]> git.jsancho.org Git - lugaru.git/blob - Source/Tutorial.cpp
In case the resolution settings in the config file have wrong values,
[lugaru.git] / Source / Tutorial.cpp
1 /*
2 Copyright (C) 2003, 2010 - Wolfire Games
3 Copyright (C) 2010-2017 - 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]->hasWeapon()) {
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]->hasWeapon() && 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 ") + Input::keyToChar(Game::forwardkey);
570             string += std::string(", ") + Input::keyToChar(Game::leftkey);
571             string += std::string(", ") + Input::keyToChar(Game::backkey);
572             string += std::string(" and ") + Input::keyToChar(Game::rightkey) + " keys to move around.";
573             string2 = "All movement is relative to the camera.";
574             break;
575         case 5:
576             string = std::string("Please press ") + Input::keyToChar(Game::jumpkey) + " to jump.";
577             string2 = "You can hold it longer to jump higher.";
578             break;
579         case 6:
580             string = std::string("You can press ") + Input::keyToChar(Game::crouchkey) + " to crouch.";
581             string2 = "You can jump higher from a crouching position.";
582             break;
583         case 7:
584             string = std::string("While running, you can press ") + Input::keyToChar(Game::crouchkey) + " to roll.";
585             break;
586         case 8:
587             string = "While crouching, you can sneak around silently";
588             string2 = "using the movement keys.";
589             break;
590         case 9:
591             string = "Release the crouch key while sneaking and hold the movement keys";
592             string2 = "to run animal-style.";
593             break;
594         case 10:
595             string = "ADVANCED MOVEMENT:";
596             break;
597         case 11:
598             string = std::string("When you jump at a wall, you can hold ") + Input::keyToChar(Game::jumpkey) + " again";
599             string2 = "during impact to perform a walljump.";
600             string3 = "Be sure to use the movement keys to press against the wall";
601             break;
602         case 12:
603             string = "While in the air, you can press crouch to flip.";
604             string2 = "Walljumps and flips confuse enemies and give you more control.";
605             break;
606         case 13:
607             string = "BASIC COMBAT:";
608             break;
609         case 14:
610             string = "There is now an imaginary enemy";
611             string2 = "in the middle of the training area.";
612             break;
613         case 15:
614             string = std::string("Press ") + Input::keyToChar(Game::attackkey) + " to attack when you are near an enemy.";
615             string2 = "You can punch by standing still near an enemy and attacking.";
616             break;
617         case 16:
618             string = "If you are close, you will perform a weak punch.";
619             string2 = "The weak punch is excellent for starting attack combinations.";
620             break;
621         case 17:
622             string = "Attacking while running results in a spin kick.";
623             string2 = "This is one of your most powerful ground attacks.";
624             break;
625         case 18:
626             string = "Sweep the enemy's legs out by attacking while crouched.";
627             string2 = "This is a very fast attack, and easy to follow up.";
628             break;
629         case 19:
630             string = "When an enemy is on the ground, you can deal some extra";
631             string2 = "damage by running up and drop-kicking him.";
632             string3 = "(Try knocking them down with a sweep first)";
633             break;
634         case 20:
635             string = "Your most powerful individual attack is the rabbit kick.";
636             string2 = std::string("Run at the enemy while holding ") + Input::keyToChar(Game::attackkey) + ", and press";
637             string3 = std::string("the jump key (") + Input::keyToChar(Game::jumpkey) + ") to attack.";
638             break;
639         case 21:
640             string = "This attack is devastating if timed correctly.";
641             string2 = "Even if timed incorrectly, it will knock the enemy over.";
642             if (againbonus) {
643                 string3 = "Try rabbit-kicking the imaginary enemy again.";
644             } else {
645                 string3 = "Try rabbit-kicking the imaginary enemy.";
646             }
647             break;
648         case 22:
649             string = "If you sneak behind an enemy unnoticed, you can kill";
650             string2 = "him instantly. Move close behind this enemy";
651             string3 = "and attack.";
652             break;
653         case 23:
654             string = "Another important attack is the wall kick. When an enemy";
655             string2 = "is near a wall, perform a walljump nearby and hold";
656             string3 = "the attack key during impact with the wall.";
657             break;
658         case 24:
659             string = "You can tackle enemies by running at them animal-style";
660             string2 = std::string("and pressing jump (") + Input::keyToChar(Game::jumpkey) + ") or attack (" + Input::keyToChar(Game::attackkey) + ").";
661             string3 = "This is especially useful when they are running away.";
662             break;
663         case 25:
664             string = "Dodge by pressing back and attack. Dodging is essential";
665             string2 = "against enemies with swords or other long weapons.";
666             break;
667         case 26:
668             string = "REVERSALS AND COUNTER-REVERSALS";
669             break;
670         case 27:
671             string = "The enemy can now reverse your attacks.";
672             break;
673         case 28:
674             string = "If you attack, you will notice that the enemy now sometimes";
675             string2 = "catches your attack and uses it against you. Hold";
676             string3 = std::string("crouch (") + Input::keyToChar(Game::crouchkey) + ") after attacking to escape from reversals.";
677             break;
678         case 29:
679             string = "Try escaping from two more reversals in a row.";
680             break;
681         case 30:
682             string = "Good!";
683             break;
684         case 31:
685             string = std::string("To reverse an attack, you must tap crouch (") + Input::keyToChar(Game::crouchkey) + ") during the";
686             string2 = "enemy's attack. You must also be close to the enemy;";
687             string3 = "this is especially important against armed opponents.";
688             break;
689         case 32:
690             string = "The enemy can attack in " + to_string(int(maxtime - stagetime)) + " seconds.";
691             string2 = "This imaginary opponents attacks will be highlighted";
692             string3 = "to make this easier.";
693             break;
694         case 33:
695             string = "Reverse three enemy attacks!";
696             break;
697         case 34:
698             string = "Reverse two more enemy attacks!";
699             break;
700         case 35:
701             string = "Reverse one more enemy attack!";
702             break;
703         case 36:
704             string = "Excellent!";
705             break;
706         case 37:
707             string = "Now spar with the enemy for " + to_string(int(maxtime - stagetime)) + " more seconds.";
708             string2 = "Damage dealt: " + to_string(int(damagedealt));
709             string3 = "Damage taken: " + to_string(int(damagetaken));
710             break;
711         case 38:
712             string = "WEAPONS:";
713             break;
714         case 39:
715             string = "There is now an imaginary knife";
716             string2 = "in the center of the training area.";
717             break;
718         case 40:
719             string = "Stand, roll or handspring over the knife";
720             string2 = std::string("while pressing ") + Input::keyToChar(Game::throwkey) + " to pick it up.";
721             string3 = "You can crouch and press the same key to drop it again.";
722             break;
723         case 41:
724             string = std::string("You can equip and unequip weapons using the ") + Input::keyToChar(Game::drawkey) + " key.";
725             string2 = "Sometimes it is best to keep them unequipped to";
726             string3 = "prevent enemies from taking them. ";
727             break;
728         case 42:
729             string = "The knife is the smallest weapon and the least encumbering.";
730             string2 = "You can equip or unequip it while standing, crouching,";
731             string3 = "running or flipping.";
732             break;
733         case 43:
734             string = "You perform weapon attacks the same way as unarmed attacks,";
735             string2 = "but sharp weapons cause permanent damage, instead of the";
736             string3 = "temporary trauma from blunt weapons, fists and feet.";
737             break;
738         case 44:
739             string = "The enemy now has your knife!";
740             string2 = "Please reverse two of his knife attacks.";
741             break;
742         case 45:
743             string = "Please reverse one more of his knife attacks.";
744             break;
745         case 46:
746             string = "Now he has a sword!";
747             string2 = "The sword has longer reach than your arms, so you";
748             string3 = "must move close to reverse the sword slash.";
749             break;
750         case 47:
751             string = "Long weapons like the sword and staff are also useful for defense;";
752             string2 = "you can parry enemy weapon attacks by pressing the attack key";
753             string3 = "at the right time. Please try parrying the enemy's attacks!";
754             break;
755         case 48:
756             string = "The staff is like the sword, but has two main attacks.";
757             string2 = "The standing smash is fast and effective, and the running";
758             string3 = "spin smash is slower and more powerful.";
759             break;
760         case 49:
761             string = std::string("When facing an enemy, you can throw the knife with ") + Input::keyToChar(Game::throwkey) + ".";
762             string2 = "It is possible to throw the knife while flipping,";
763             string3 = "but it is very inaccurate.";
764             break;
765         case 50:
766             string = "You now know everything you can learn from training.";
767             string2 = "Everything else you must learn from experience!";
768             break;
769         case 51:
770             string = "Walk out of the training area to return to the main menu.";
771             break;
772     }
773
774     float opacity = maxtime - stagetime;
775
776     if (opacity > 1) {
777         opacity = 1;
778     }
779     if (opacity < 0) {
780         opacity = 0;
781     }
782
783     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);
784     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);
785     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);
786
787     string = "Press 'tab' to skip to the next item.";
788     string2 = "Press escape at any time to";
789     string3 = "pause or exit the tutorial.";
790
791     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);
792     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);
793     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);
794 }
795
796 void Tutorial::DoStuff(float multiplier)
797 {
798     XYZ temp;
799     XYZ temp2;
800     XYZ temp3;
801     XYZ oldtemp;
802     XYZ oldtemp2;
803     temp.x = 1011;
804     temp.y = 84;
805     temp.z = 491;
806     temp2.x = 1025;
807     temp2.y = 75;
808     temp2.z = 447;
809     temp3.x = 1038;
810     temp3.y = 76;
811     temp3.z = 453;
812     oldtemp = temp;
813     oldtemp2 = temp2;
814     if (stage >= 51) {
815         if (distsq(&temp, &Person::players[0]->coords) >= distsq(&temp, &temp2) - 1 || distsq(&temp3, &Person::players[0]->coords) < 4) {
816             OPENAL_StopSound(OPENAL_ALL); // hack...OpenAL renderer isn't stopping music after tutorial goes to level menu...
817             OPENAL_SetFrequency(OPENAL_ALL);
818
819             emit_stream_np(stream_menutheme);
820
821             Game::gameon = 0;
822             mainmenu = 5;
823
824             Game::fireSound();
825
826             Game::flash();
827         }
828     } else {
829         if (distsq(&temp, &Person::players[0]->coords) >= distsq(&temp, &temp2) - 1 || distsq(&temp3, &Person::players[0]->coords) < 4) {
830             emit_sound_at(fireendsound, Person::players[0]->coords);
831
832             Person::players[0]->coords = (oldtemp + oldtemp2) / 2;
833
834             Game::flash();
835         }
836     }
837     if (stage >= 14 && stage < 50) {
838         if (distsq(&temp, &Person::players[1]->coords) >= distsq(&temp, &temp2) - 1 || distsq(&temp3, &Person::players[1]->coords) < 4) {
839             emit_sound_at(fireendsound, Person::players[1]->coords);
840
841             for (unsigned i = 0; i < Person::players[1]->skeleton.joints.size(); i++) {
842                 if (Random() % 2 == 0) {
843                     if (!Person::players[1]->skeleton.free) {
844                         temp2 = (Person::players[1]->coords - Person::players[1]->oldcoords) / multiplier / 2; //velocity/2;
845                     }
846                     if (Person::players[1]->skeleton.free) {
847                         temp2 = Person::players[1]->skeleton.joints[i].velocity * Person::players[1]->scale / 2;
848                     }
849                     if (!Person::players[1]->skeleton.free) {
850                         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;
851                     }
852                     if (Person::players[1]->skeleton.free) {
853                         temp = Person::players[1]->skeleton.joints[i].position * Person::players[1]->scale + Person::players[1]->coords;
854                     }
855                     Sprite::MakeSprite(breathsprite, temp, temp2, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1);
856                 }
857             }
858
859             Person::players[1]->coords = (oldtemp + oldtemp2) / 2;
860             for (unsigned i = 0; i < Person::players[1]->skeleton.joints.size(); i++) {
861                 Person::players[1]->skeleton.joints[i].velocity = 0;
862                 if (Random() % 2 == 0) {
863                     if (!Person::players[1]->skeleton.free) {
864                         temp2 = (Person::players[1]->coords - Person::players[1]->oldcoords) / multiplier / 2; //velocity/2;
865                     }
866                     if (Person::players[1]->skeleton.free) {
867                         temp2 = Person::players[1]->skeleton.joints[i].velocity * Person::players[1]->scale / 2;
868                     }
869                     if (!Person::players[1]->skeleton.free) {
870                         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;
871                     }
872                     if (Person::players[1]->skeleton.free) {
873                         temp = Person::players[1]->skeleton.joints[i].position * Person::players[1]->scale + Person::players[1]->coords;
874                     }
875                     Sprite::MakeSprite(breathsprite, temp, temp2, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1);
876                 }
877             }
878         }
879     }
880 }