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