]> git.jsancho.org Git - lugaru.git/blob - Source/Tutorial.cpp
Merged identical switch cases in Tutorial
[lugaru.git] / Source / Tutorial.cpp
1 /*
2 Copyright (C) 2003, 2010 - Wolfire Games
3 Copyright (C) 2010-2016 - Lugaru contributors (see AUTHORS file)
4
5 This file is part of Lugaru.
6
7 Lugaru is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 Lugaru is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Lugaru.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "Tutorial.hpp"
22 #include "Game.hpp"
23 #include "Audio/Sounds.hpp"
24 #include "Audio/openal_wrapper.hpp"
25 #include "Level/Awards.hpp"
26 #include "Objects/Person.hpp"
27 #include "Utils/Input.hpp"
28
29 extern bool reversaltrain;
30 extern bool canattack;
31 extern bool cananger;
32 extern int bonus;
33 extern float damagedealt;
34 extern bool againbonus;
35 extern float screenwidth, screenheight;
36 extern int mainmenu;
37
38 bool Tutorial::active = false;
39 int Tutorial::stage = 0;
40 float Tutorial::stagetime = 0;
41 float Tutorial::maxtime = 0;
42 float Tutorial::success = 0;
43
44 void Tutorial::Do(float multiplier)
45 {
46     if (stagetime > maxtime) {
47         stage++;
48         success = 0;
49         if (stage <= 1) {
50             canattack = 0;
51             cananger = 0;
52             reversaltrain = 0;
53         }
54         switch (stage) {
55             case 1:
56                 maxtime = 5;
57                 break;
58             case 2:
59             case 10:
60             case 13:
61             case 26:
62                 maxtime = 2;
63                 break;
64             case 3:
65             case 5:
66             case 6:
67             case 7:
68             case 8:
69             case 9:
70                 maxtime = 600;
71                 break;
72             case 4:
73             case 11:
74             case 12:
75                 maxtime = 1000;
76                 break;
77             case 14: {
78                 maxtime = 3;
79
80                 XYZ temp, temp2;
81
82                 temp.x = 1011;
83                 temp.y = 84;
84                 temp.z = 491;
85                 temp2.x = 1025;
86                 temp2.y = 75;
87                 temp2.z = 447;
88
89                 Person::players[1]->coords = (temp + temp2) / 2;
90
91                 emit_sound_at(fireendsound, Person::players[1]->coords);
92
93                 for (unsigned i = 0; i < Person::players[1]->skeleton.joints.size(); i++) {
94                     if (Random() % 2 == 0) {
95                         if (!Person::players[1]->skeleton.free)
96                             temp2 = (Person::players[1]->coords - Person::players[1]->oldcoords) / multiplier / 2; //velocity/2;
97                         if (Person::players[1]->skeleton.free)
98                             temp2 = Person::players[1]->skeleton.joints[i].velocity * Person::players[1]->scale / 2;
99                         if (!Person::players[1]->skeleton.free)
100                             temp = DoRotation(DoRotation(DoRotation(Person::players[1]->skeleton.joints[i].position, 0, 0, Person::players[1]->tilt), Person::players[1]->tilt2, 0, 0), 0, Person::players[1]->yaw, 0) * Person::players[1]->scale + Person::players[1]->coords;
101                         if (Person::players[1]->skeleton.free)
102                             temp = Person::players[1]->skeleton.joints[i].position * Person::players[1]->scale + Person::players[1]->coords;
103                         Sprite::MakeSprite(breathsprite, temp, temp2, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1);
104                     }
105                 }
106             }
107             break;
108             case 15:
109             case 16:
110             case 17:
111             case 18:
112             case 20:
113             case 22:
114             case 23:
115             case 24:
116             case 25:
117                 maxtime = 500;
118                 break;
119             case 19:
120                 stage = 20;
121                 break;
122             case 21:
123                 maxtime = 500;
124                 if (bonus == cannon) {
125                     bonus = Slicebonus;
126                     againbonus = 1;
127                 } else
128                     againbonus = 0;
129                 break;
130             case 27:
131                 maxtime = 4;
132                 reversaltrain = 1;
133                 cananger = 1;
134                 Person::players[1]->aitype = attacktypecutoff;
135                 break;
136             case 28:
137             case 34:
138             case 35:
139                 maxtime = 400;
140                 break;
141             case 29:
142                 maxtime = 400;
143                 Person::players[0]->escapednum = 0;
144                 break;
145             case 30:
146                 maxtime = 4;
147                 reversaltrain = 0;
148                 cananger = 0;
149                 Person::players[1]->aitype = passivetype;
150                 break;
151             case 31:
152                 maxtime = 13;
153                 break;
154             case 32:
155             case 42:
156                 maxtime = 8;
157                 break;
158             case 33:
159                 maxtime = 400;
160                 cananger = 1;
161                 canattack = 1;
162                 Person::players[1]->aitype = attacktypecutoff;
163                 break;
164             case 36:
165                 maxtime = 2;
166                 reversaltrain = 0;
167                 cananger = 0;
168                 Person::players[1]->aitype = passivetype;
169                 break;
170             case 37:
171                 damagedealt = 0;
172                 damagetaken = 0;
173                 maxtime = 50;
174                 cananger = 1;
175                 canattack = 1;
176                 Person::players[1]->aitype = attacktypecutoff;
177                 break;
178             case 38:
179                 maxtime = 4;
180                 canattack = 0;
181                 cananger = 0;
182                 Person::players[1]->aitype = passivetype;
183                 break;
184             case 39: {
185                 XYZ temp, temp2;
186
187                 temp.x = 1011;
188                 temp.y = 84;
189                 temp.z = 491;
190                 temp2.x = 1025;
191                 temp2.y = 75;
192                 temp2.z = 447;
193
194                 Weapon w(knife, -1);
195                 w.position = (temp + temp2) / 2;
196                 w.tippoint = (temp + temp2) / 2;
197
198                 w.velocity = 0.1;
199                 w.tipvelocity = 0.1;
200                 w.missed = 1;
201                 w.hitsomething = 0;
202                 w.freetime = 0;
203                 w.firstfree = 1;
204                 w.physics = 1;
205
206                 weapons.push_back(w);
207             }
208             break;
209             case 40:
210             case 41:
211             case 43:
212                 maxtime = 300;
213                 break;
214             case 44:
215                 weapons[0].owner = 1;
216                 Person::players[0]->weaponactive = -1;
217                 Person::players[0]->num_weapons = 0;
218                 Person::players[1]->weaponactive = 0;
219                 Person::players[1]->num_weapons = 1;
220                 Person::players[1]->weaponids[0] = 0;
221
222                 cananger = 1;
223                 canattack = 1;
224                 Person::players[1]->aitype = attacktypecutoff;
225
226                 maxtime = 300;
227                 break;
228             case 45:
229                 weapons[0].owner = 1;
230                 Person::players[0]->weaponactive = -1;
231                 Person::players[0]->num_weapons = 0;
232                 Person::players[1]->weaponactive = 0;
233                 Person::players[1]->num_weapons = 1;
234                 Person::players[1]->weaponids[0] = 0;
235
236                 maxtime = 300;
237                 break;
238             case 46:
239                 weapons[0].owner = 1;
240                 Person::players[0]->weaponactive = -1;
241                 Person::players[0]->num_weapons = 0;
242                 Person::players[1]->weaponactive = 0;
243                 Person::players[1]->num_weapons = 1;
244                 Person::players[1]->weaponids[0] = 0;
245
246                 weapons[0].setType(sword);
247
248                 maxtime = 300;
249                 break;
250             case 47: {
251                 maxtime = 10;
252
253                 XYZ temp, temp2;
254
255                 temp.x = 1011;
256                 temp.y = 84;
257                 temp.z = 491;
258                 temp2.x = 1025;
259                 temp2.y = 75;
260                 temp2.z = 447;
261
262                 Weapon w(sword, -1);
263                 w.position = (temp + temp2) / 2;
264                 w.tippoint = (temp + temp2) / 2;
265
266                 w.velocity = 0.1;
267                 w.tipvelocity = 0.1;
268                 w.missed = 1;
269                 w.hitsomething = 0;
270                 w.freetime = 0;
271                 w.firstfree = 1;
272                 w.physics = 1;
273
274                 weapons.push_back(w);
275
276                 weapons[0].owner = 1;
277                 weapons[1].owner = 0;
278                 Person::players[0]->weaponactive = 0;
279                 Person::players[0]->num_weapons = 1;
280                 Person::players[0]->weaponids[0] = 1;
281                 Person::players[1]->weaponactive = 0;
282                 Person::players[1]->num_weapons = 1;
283                 Person::players[1]->weaponids[0] = 0;
284
285             }
286             break;
287             case 48:
288                 canattack = 0;
289                 cananger = 0;
290                 Person::players[1]->aitype = passivetype;
291
292                 maxtime = 15;
293
294                 weapons[0].owner = 1;
295                 weapons[1].owner = 0;
296                 Person::players[0]->weaponactive = 0;
297                 Person::players[0]->num_weapons = 1;
298                 Person::players[0]->weaponids[0] = 1;
299                 Person::players[1]->weaponactive = 0;
300                 Person::players[1]->num_weapons = 1;
301                 Person::players[1]->weaponids[0] = 0;
302
303                 if (Person::players[0]->weaponactive != -1)
304                     weapons[Person::players[0]->weaponids[Person::players[0]->weaponactive]].setType(staff);
305                 else
306                     weapons[0].setType(staff);
307                 break;
308             case 49:
309                 canattack = 0;
310                 cananger = 0;
311                 Person::players[1]->aitype = passivetype;
312
313                 maxtime = 200;
314
315                 weapons[1].position = 1000;
316                 weapons[1].tippoint = 1000;
317
318                 weapons[0].setType(knife);
319
320                 weapons[0].owner = 0;
321                 Person::players[1]->weaponactive = -1;
322                 Person::players[1]->num_weapons = 0;
323                 Person::players[0]->weaponactive = 0;
324                 Person::players[0]->num_weapons = 1;
325                 Person::players[0]->weaponids[0] = 0;
326
327                 break;
328             case 50: {
329                 maxtime = 8;
330
331                 XYZ temp, temp2;
332                 emit_sound_at(fireendsound, Person::players[1]->coords);
333
334                 for (unsigned i = 0; i < Person::players[1]->skeleton.joints.size(); i++) {
335                     if (Random() % 2 == 0) {
336                         if (!Person::players[1]->skeleton.free)
337                             temp2 = (Person::players[1]->coords - Person::players[1]->oldcoords) / multiplier / 2; //velocity/2;
338                         if (Person::players[1]->skeleton.free)
339                             temp2 = Person::players[1]->skeleton.joints[i].velocity * Person::players[1]->scale / 2;
340                         if (!Person::players[1]->skeleton.free)
341                             temp = DoRotation(DoRotation(DoRotation(Person::players[1]->skeleton.joints[i].position, 0, 0, Person::players[1]->tilt), Person::players[1]->tilt2, 0, 0), 0, Person::players[1]->yaw, 0) * Person::players[1]->scale + Person::players[1]->coords;
342                         if (Person::players[1]->skeleton.free)
343                             temp = Person::players[1]->skeleton.joints[i].position * Person::players[1]->scale + Person::players[1]->coords;
344                         Sprite::MakeSprite(breathsprite, temp, temp2, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1);
345                     }
346                 }
347
348                 Person::players[1]->num_weapons = 0;
349                 Person::players[1]->weaponstuck = -1;
350                 Person::players[1]->weaponactive = -1;
351
352                 weapons.clear();
353             }
354             break;
355             case 51:
356                 maxtime = 80000;
357                 break;
358             default:
359                 break;
360         }
361         if (stage <= 51)
362             stagetime = 0;
363     }
364
365     //Tutorial success
366     if (stagetime < maxtime - 3) {
367         switch (stage) {
368             case 3:
369                 if (Game::deltah || Game::deltav)
370                     success += multiplier;
371                 break;
372             case 4:
373                 if (Person::players[0]->forwardkeydown || Person::players[0]->backkeydown || Person::players[0]->leftkeydown || Person::players[0]->rightkeydown)
374                     success += multiplier;
375                 break;
376             case 5:
377                 if (Person::players[0]->jumpkeydown)
378                     success = 1;
379                 break;
380             case 6:
381                 if (Person::players[0]->isCrouch())
382                     success = 1;
383                 break;
384             case 7:
385                 if (Person::players[0]->animTarget == rollanim)
386                     success = 1;
387                 break;
388             case 8:
389                 if (Person::players[0]->animTarget == sneakanim)
390                     success += multiplier;
391                 break;
392             case 9:
393                 if (Person::players[0]->animTarget == rabbitrunninganim || Person::players[0]->animTarget == wolfrunninganim)
394                     success += multiplier;
395                 break;
396             case 11:
397                 if (Person::players[0]->isWallJump())
398                     success = 1;
399                 break;
400             case 12:
401                 if (Person::players[0]->animTarget == flipanim)
402                     success = 1;
403                 break;
404             case 15:
405                 if (Person::players[0]->animTarget == upunchanim || Person::players[0]->animTarget == winduppunchanim)
406                     success = 1;
407                 break;
408             case 16:
409                 if (Person::players[0]->animTarget == winduppunchanim)
410                     success = 1;
411                 break;
412             case 17:
413                 if (Person::players[0]->animTarget == spinkickanim)
414                     success = 1;
415                 break;
416             case 18:
417                 if (Person::players[0]->animTarget == sweepanim)
418                     success = 1;
419                 break;
420             case 19:
421                 if (Person::players[0]->animTarget == dropkickanim)
422                     success = 1;
423                 break;
424             case 20:
425                 if (Person::players[0]->animTarget == rabbitkickanim)
426                     success = 1;
427                 break;
428             case 21:
429                 if (bonus == cannon)
430                     success = 1;
431                 break;
432             case 22:
433                 if (bonus == spinecrusher)
434                     success = 1;
435                 break;
436             case 23:
437                 if (Person::players[0]->animTarget == walljumprightkickanim || Person::players[0]->animTarget == walljumpleftkickanim)
438                     success = 1;
439                 break;
440             case 24:
441                 if (Person::players[0]->animTarget == rabbittacklinganim)
442                     success = 1;
443                 break;
444             case 25:
445                 if (Person::players[0]->animTarget == backhandspringanim)
446                     success = 1;
447                 break;
448             case 28:
449                 if (Animation::animations[Person::players[0]->animTarget].attack == reversed && Person::players[0]->feint)
450                     success = 1;
451                 break;
452             case 29:
453                 if (Person::players[0]->escapednum == 2) {
454                     success = 1;
455                     reversaltrain = 0;
456                     cananger = 0;
457                     Person::players[1]->aitype = passivetype;
458                 }
459                 break;
460             case 33:
461             case 34:
462             case 44:
463             case 45:
464             case 46:
465                 if (Animation::animations[Person::players[0]->animTarget].attack == reversal) {
466                     success = 1;
467                 }
468                 break;
469             case 35:
470                 if (Animation::animations[Person::players[0]->animTarget].attack == reversal) {
471                     success = 1;
472                     reversaltrain = 0;
473                     cananger = 0;
474                     Person::players[1]->aitype = passivetype;
475                 }
476                 break;
477             case 40:
478                 if (Person::players[0]->num_weapons > 0) {
479                     success = 1;
480                 }
481                 break;
482             case 41:
483                 if (Person::players[0]->weaponactive == -1 && Person::players[0]->num_weapons > 0) {
484                     success = 1;
485                 }
486                 break;
487             case 43:
488                 if (Person::players[0]->animTarget == knifeslashstartanim) {
489                     success = 1;
490                 }
491                 break;
492             case 49:
493                 if (Person::players[1]->weaponstuck != -1) {
494                     success = 1;
495                 }
496                 break;
497             default:
498                 break;
499         }
500         if (success >= 1) {
501             stagetime = maxtime - 3;
502         }
503
504         if (stagetime == maxtime - 3) {
505             emit_sound_np(consolesuccesssound);
506         }
507
508         if (success >= 1) {
509             if (stage == 34 || stage == 35) {
510                 stagetime = maxtime - 1;
511             }
512         }
513     }
514
515     if (stage < 14 || stage >= 50) {
516         Person::players[1]->coords.y = 300;
517         Person::players[1]->velocity = 0;
518     }
519 }
520
521 void Tutorial::DrawText()
522 {
523     std::string string;
524     std::string string2;
525     std::string string3;
526     float tutorialopac = maxtime - stagetime;
527     if (tutorialopac > 1)
528         tutorialopac = 1;
529     if (tutorialopac < 0)
530         tutorialopac = 0;
531
532     string = " ";
533     string2 = " ";
534     string3 = " ";
535     if (stage == 0) {
536         string = " ";
537         string2 = " ";
538         string3 = " ";
539     }
540     if (stage == 1) {
541         string = "Welcome to the Lugaru training level!";
542         string2 = " ";
543         string3 = " ";
544     }
545     if (stage == 2) {
546         string = "BASIC MOVEMENT:";
547         string2 = " ";
548         string3 = " ";
549     }
550     if (stage == 3) {
551         string = "You can move the mouse to rotate the camera.";
552         string2 = " ";
553         string3 = " ";
554     }
555     if (stage == 4) {
556         string = std::string("Try using the ") +
557                 Input::keyToChar(Game::forwardkey) + ", " +
558                 Input::keyToChar(Game::leftkey) + ", " +
559                 Input::keyToChar(Game::backkey) + " and " +
560                 Input::keyToChar(Game::rightkey) + " keys to move around.";
561         string2 = "All movement is relative to the camera.";
562         string3 = " ";
563     }
564     if (stage == 5) {
565         string = std::string("Please press ") + Input::keyToChar(Game::jumpkey) + " to jump.";
566         string2 = "You can hold it longer to jump higher.";
567         string3 = " ";
568     }
569     if (stage == 6) {
570         string = std::string("You can press ") + Input::keyToChar(Game::crouchkey) + " to crouch.";
571         string2 = "You can jump higher from a crouching position.";
572         string3 = " ";
573     }
574     if (stage == 7) {
575         string = std::string("While running, you can press ") + Input::keyToChar(Game::crouchkey) + " to roll.";
576         string2 = " ";
577         string3 = " ";
578     }
579     if (stage == 8) {
580         string = "While crouching, you can sneak around silently";
581         string2 = "using the movement keys.";
582         string3 = " ";
583     }
584     if (stage == 9) {
585         string = "Release the crouch key while sneaking and hold the movement keys";
586         string2 = "to run animal-style.";
587         string3 = " ";
588     }
589     if (stage == 10) {
590         string = "ADVANCED MOVEMENT:";
591         string2 = " ";
592         string3 = " ";
593     }
594     if (stage == 11) {
595         string = std::string("When you jump at a wall, you can hold ") + Input::keyToChar(Game::jumpkey) + " again";
596         string2 = "during impact to perform a walljump.";
597         string3 = "Be sure to use the movement keys to press against the wall";
598     }
599     if (stage == 12) {
600         string = "While in the air, you can press crouch to flip.";
601         string2 = "Walljumps and flips confuse enemies and give you more control.";
602         string3 = " ";
603     }
604     if (stage == 13) {
605         string = "BASIC COMBAT:";
606         string2 = " ";
607         string3 = " ";
608     }
609     if (stage == 14) {
610         string = "There is now an imaginary enemy";
611         string2 = "in the middle of the training area.";
612         string3 = " ";
613     }
614     if (stage == 15) {
615         if (Game::attackkey == MOUSEBUTTON1) {
616             string = "Click to attack when you are near an enemy.";
617         } else {
618             string = std::string("Press ") + Input::keyToChar(Game::attackkey) + " to attack when you are near an enemy.";
619         }
620         string2 = "You can punch by standing still near an enemy and attacking.";
621         string3 = " ";
622     }
623     if (stage == 16) {
624         string = "If you are close, you will perform a weak punch.";
625         string2 = "The weak punch is excellent for starting attack combinations.";
626         string3 = " ";
627     }
628     if (stage == 17) {
629         string = "Attacking while running results in a spin kick.";
630         string2 = "This is one of your most powerful ground attacks.";
631         string3 = " ";
632     }
633     if (stage == 18) {
634         string = "Sweep the enemy's legs out by attacking while crouched.";
635         string2 = "This is a very fast attack, and easy to follow up.";
636         string3 = " ";
637     }
638     if (stage == 19) {
639         string = "When an enemy is on the ground, you can deal some extra";
640         string2 = "damage by running up and drop-kicking him.";
641         string3 = "(Try knocking them down with a sweep first)";
642     }
643     if (stage == 20) {
644         string = "Your most powerful individual attack is the rabbit kick.";
645         if (Game::attackkey == MOUSEBUTTON1) {
646             string2 = "Run at the enemy while holding the mouse button, and press";
647         } else {
648             string2 = std::string("Run at the enemy while holding ") + Input::keyToChar(Game::attackkey) + ", and press";
649         }
650         string3 = std::string("the jump key (") + Input::keyToChar(Game::jumpkey) + ") to attack.";
651     }
652     if (stage == 21) {
653         string = "This attack is devastating if timed correctly.";
654         string2 = "Even if timed incorrectly, it will knock the enemy over.";
655         if (againbonus)
656             string3 = "Try rabbit-kicking the imaginary enemy again.";
657         else
658             string3 = "Try rabbit-kicking the imaginary enemy.";
659     }
660     if (stage == 22) {
661         string = "If you sneak behind an enemy unnoticed, you can kill";
662         string2 = "him instantly. Move close behind this enemy";
663         string3 = "and attack.";
664     }
665     if (stage == 23) {
666         string = "Another important attack is the wall kick. When an enemy";
667         string2 = "is near a wall, perform a walljump nearby and hold";
668         string3 = "the attack key during impact with the wall.";
669     }
670     if (stage == 24) {
671         string = "You can tackle enemies by running at them animal-style";
672         if (Game::attackkey == MOUSEBUTTON1) {
673             string2 = std::string("and pressing jump (") + Input::keyToChar(Game::jumpkey) + ") or attack(mouse button).";
674         } else {
675             string2 = std::string("and pressing jump (") + Input::keyToChar(Game::jumpkey) + ") or attack(" + Input::keyToChar(Game::attackkey) + ").";
676         }
677         string3 = "This is especially useful when they are running away.";
678     }
679     if (stage == 25) {
680         string = "Dodge by pressing back and attack. Dodging is essential";
681         string2 = "against enemies with swords or other long weapons.";
682         string3 = " ";
683     }
684     if (stage == 26) {
685         string = "REVERSALS AND COUNTER-REVERSALS";
686         string2 = " ";
687         string3 = " ";
688     }
689     if (stage == 27) {
690         string = "The enemy can now reverse your attacks.";
691         string2 = " ";
692         string3 = " ";
693     }
694     if (stage == 28) {
695         string = "If you attack, you will notice that the enemy now sometimes";
696         string2 = "catches your attack and uses it against you. Hold";
697         string3 = std::string("crouch (") + Input::keyToChar(Game::crouchkey) + ") after attacking to escape from reversals.";
698     }
699     if (stage == 29) {
700         string = "Try escaping from two more reversals in a row.";
701         string2 = " ";
702         string3 = " ";
703     }
704     if (stage == 30) {
705         string = "Good!";
706         string2 = " ";
707         string3 = " ";
708     }
709     if (stage == 31) {
710         string = std::string("To reverse an attack, you must tap crouch (") + Input::keyToChar(Game::crouchkey) + ") during the";
711         string2 = "enemy's attack. You must also be close to the enemy;";
712         string3 = "this is especially important against armed opponents.";
713     }
714     if (stage == 32) {
715         string = "The enemy can attack in " + to_string(int(maxtime - stagetime)) + " seconds.";
716         string2 = "This imaginary opponents attacks will be highlighted";
717         string3 = "to make this easier.";
718     }
719     if (stage == 33) {
720         string = "Reverse three enemy attacks!";
721         string2 = " ";
722         string3 = " ";
723     }
724     if (stage == 34) {
725         string = "Reverse two more enemy attacks!";
726         string2 = " ";
727         string3 = " ";
728     }
729     if (stage == 35) {
730         string = "Reverse one more enemy attack!";
731         string2 = " ";
732         string3 = " ";
733     }
734     if (stage == 36) {
735         string = "Excellent!";
736         string2 = " ";
737         string3 = " ";
738     }
739     if (stage == 37) {
740         string = "Now spar with the enemy for " + to_string(int(maxtime - stagetime)) + " more seconds.";
741         string2 = "Damage dealt: " + to_string(int(damagedealt));
742         string3 = "Damage taken: " + to_string(int(damagetaken));
743     }
744     if (stage == 38) {
745         string = "WEAPONS:";
746         string2 = " ";
747         string3 = " ";
748     }
749     if (stage == 39) {
750         string = "There is now an imaginary knife";
751         string2 = "in the center of the training area.";
752         string3 = " ";
753     }
754     if (stage == 40) {
755         string = "Stand, roll or handspring over the knife";
756         string2 = std::string("while pressing ") + Input::keyToChar(Game::throwkey) + " to pick it up.";
757         string3 = "You can crouch and press the same key to drop it again.";
758     }
759     if (stage == 41) {
760         string = std::string("You can equip and unequip weapons using the ") + Input::keyToChar(Game::drawkey) + " key.";
761         string2 = "Sometimes it is best to keep them unequipped to";
762         string3 = "prevent enemies from taking them. ";
763     }
764     if (stage == 42) {
765         string = "The knife is the smallest weapon and the least encumbering.";
766         string2 = "You can equip or unequip it while standing, crouching,";
767         string3 = "running or flipping.";
768     }
769     if (stage == 43) {
770         string = "You perform weapon attacks the same way as unarmed attacks,";
771         string2 = "but sharp weapons cause permanent damage, instead of the";
772         string3 = "temporary trauma from blunt weapons, fists and feet.";
773     }
774     if (stage == 44) {
775         string = "The enemy now has your knife!";
776         string2 = "Please reverse two of his knife attacks.";
777         string3 = " ";
778     }
779     if (stage == 45) {
780         string = "Please reverse one more of his knife attacks.";
781         string2 = " ";
782         string3 = " ";
783     }
784     if (stage == 46) {
785         string = "Now he has a sword!";
786         string2 = "The sword has longer reach than your arms, so you";
787         string3 = "must move close to reverse the sword slash.";
788     }
789     if (stage == 47) {
790         string = "Long weapons like the sword and staff are also useful for defense;";
791         string2 = "you can parry enemy weapon attacks by pressing the attack key";
792         string3 = "at the right time. Please try parrying the enemy's attacks!";
793     }
794     if (stage == 48) {
795         string = "The staff is like the sword, but has two main attacks.";
796         string2 = "The standing smash is fast and effective, and the running";
797         string3 = "spin smash is slower and more powerful.";
798     }
799     if (stage == 49) {
800         string = std::string("When facing an enemy, you can throw the knife with ") + Input::keyToChar(Game::throwkey) + ".";
801         string2 = "It is possible to throw the knife while flipping,";
802         string3 = "but it is very inaccurate.";
803     }
804     if (stage == 50) {
805         string = "You now know everything you can learn from training.";
806         string2 = "Everything else you must learn from experience!";
807         string3 = " ";
808     }
809     if (stage == 51) {
810         string = "Walk out of the training area to return to the main menu.";
811         string2 = " ";
812         string3 = " ";
813     }
814
815     Game::text->glPrintOutlined(1, 1, 1, tutorialopac, screenwidth / 2 - 7.6 * string.size()*screenwidth / 1024, screenheight / 16 + screenheight * 4 / 5, string, 1, 1.5 * screenwidth / 1024, screenwidth, screenheight);
816     Game::text->glPrintOutlined(1, 1, 1, tutorialopac, screenwidth / 2 - 7.6 * string2.size()*screenwidth / 1024, screenheight / 16 + screenheight * 4 / 5 - 20 * screenwidth / 1024, string2, 1, 1.5 * screenwidth / 1024, screenwidth, screenheight);
817     Game::text->glPrintOutlined(1, 1, 1, tutorialopac, screenwidth / 2 - 7.6 * string3.size()*screenwidth / 1024, screenheight / 16 + screenheight * 4 / 5 - 40 * screenwidth / 1024, string3, 1, 1.5 * screenwidth / 1024, screenwidth, screenheight);
818
819     string = "Press 'tab' to skip to the next item.";
820     string2 = "Press escape at any time to";
821     string3 = "pause or exit the tutorial.";
822
823     Game::text->glPrintOutlined(0.5, 0.5, 0.5, 1, screenwidth / 2 - 7.6 * string.size()*screenwidth / 1024 * .8, 0 + screenheight * 1 / 10, string, 1, 1.5 * screenwidth / 1024 * .8, screenwidth, screenheight);
824     Game::text->glPrintOutlined(0.5, 0.5, 0.5, 1, screenwidth / 2 - 7.6 * string2.size()*screenwidth / 1024 * .8, 0 + screenheight * 1 / 10 - 20 * .8 * screenwidth / 1024, string2, 1, 1.5 * screenwidth / 1024 * .8, screenwidth, screenheight);
825     Game::text->glPrintOutlined(0.5, 0.5, 0.5, 1, screenwidth / 2 - 7.6 * string3.size()*screenwidth / 1024 * .8, 0 + screenheight * 1 / 10 - 40 * .8 * screenwidth / 1024, string3, 1, 1.5 * screenwidth / 1024 * .8, screenwidth, screenheight);
826 }
827
828 void Tutorial::DoStuff(float multiplier)
829 {
830     XYZ temp;
831     XYZ temp2;
832     XYZ temp3;
833     XYZ oldtemp;
834     XYZ oldtemp2;
835     temp.x = 1011;
836     temp.y = 84;
837     temp.z = 491;
838     temp2.x = 1025;
839     temp2.y = 75;
840     temp2.z = 447;
841     temp3.x = 1038;
842     temp3.y = 76;
843     temp3.z = 453;
844     oldtemp = temp;
845     oldtemp2 = temp2;
846     if (stage >= 51) {
847         if (distsq(&temp, &Person::players[0]->coords) >= distsq(&temp, &temp2) - 1 || distsq(&temp3, &Person::players[0]->coords) < 4) {
848             OPENAL_StopSound(OPENAL_ALL);  // hack...OpenAL renderer isn't stopping music after tutorial goes to level menu...
849             OPENAL_SetFrequency(OPENAL_ALL);
850
851             emit_stream_np(stream_menutheme);
852
853             Game::gameon = 0;
854             mainmenu = 5;
855
856             Game::fireSound();
857
858             Game::flash();
859         }
860     } else {
861         if (distsq(&temp, &Person::players[0]->coords) >= distsq(&temp, &temp2) - 1 || distsq(&temp3, &Person::players[0]->coords) < 4) {
862             emit_sound_at(fireendsound, Person::players[0]->coords);
863
864             Person::players[0]->coords = (oldtemp + oldtemp2) / 2;
865
866             Game::flash();
867         }
868     }
869     if (stage >= 14 && stage < 50) {
870         if (distsq(&temp, &Person::players[1]->coords) >= distsq(&temp, &temp2) - 1 || distsq(&temp3, &Person::players[1]->coords) < 4) {
871             emit_sound_at(fireendsound, Person::players[1]->coords);
872
873             for (unsigned i = 0; i < Person::players[1]->skeleton.joints.size(); i++) {
874                 if (Random() % 2 == 0) {
875                     if (!Person::players[1]->skeleton.free)
876                         temp2 = (Person::players[1]->coords - Person::players[1]->oldcoords) / multiplier / 2; //velocity/2;
877                     if (Person::players[1]->skeleton.free)
878                         temp2 = Person::players[1]->skeleton.joints[i].velocity * Person::players[1]->scale / 2;
879                     if (!Person::players[1]->skeleton.free)
880                         temp = DoRotation(DoRotation(DoRotation(Person::players[1]->skeleton.joints[i].position, 0, 0, Person::players[1]->tilt), Person::players[1]->tilt2, 0, 0), 0, Person::players[1]->yaw, 0) * Person::players[1]->scale + Person::players[1]->coords;
881                     if (Person::players[1]->skeleton.free)
882                         temp = Person::players[1]->skeleton.joints[i].position * Person::players[1]->scale + Person::players[1]->coords;
883                     Sprite::MakeSprite(breathsprite, temp, temp2, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1);
884                 }
885             }
886
887             Person::players[1]->coords = (oldtemp + oldtemp2) / 2;
888             for (unsigned i = 0; i < Person::players[1]->skeleton.joints.size(); i++) {
889                 Person::players[1]->skeleton.joints[i].velocity = 0;
890                 if (Random() % 2 == 0) {
891                     if (!Person::players[1]->skeleton.free)
892                         temp2 = (Person::players[1]->coords - Person::players[1]->oldcoords) / multiplier / 2; //velocity/2;
893                     if (Person::players[1]->skeleton.free)
894                         temp2 = Person::players[1]->skeleton.joints[i].velocity * Person::players[1]->scale / 2;
895                     if (!Person::players[1]->skeleton.free)
896                         temp = DoRotation(DoRotation(DoRotation(Person::players[1]->skeleton.joints[i].position, 0, 0, Person::players[1]->tilt), Person::players[1]->tilt2, 0, 0), 0, Person::players[1]->yaw, 0) * Person::players[1]->scale + Person::players[1]->coords;
897                     if (Person::players[1]->skeleton.free)
898                         temp = Person::players[1]->skeleton.joints[i].position * Person::players[1]->scale + Person::players[1]->coords;
899                     Sprite::MakeSprite(breathsprite, temp, temp2, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1);
900                 }
901             }
902         }
903     }
904 }