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