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