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