]> git.jsancho.org Git - lugaru.git/blob - Source/GameTick.cpp
BEAUTIFIED ALL SOURCE CODE
[lugaru.git] / Source / GameTick.cpp
1 /*
2 Copyright (C) 2003, 2010 - Wolfire Games
3
4 This file is part of Lugaru.
5
6 Lugaru is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21
22 #if PLATFORM_UNIX
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #else
27 #include <direct.h>
28 #endif
29
30 #include <limits>
31 #include <ctime>
32 #include <dirent.h>
33 #include "Game.h"
34 #include "openal_wrapper.h"
35 #include "Settings.h"
36 #include "Input.h"
37 #include "Animation.h"
38 #include "Awards.h"
39 #include "Menu.h"
40
41 #include <algorithm>
42
43 using namespace std;
44 using namespace Game;
45
46 // Added more evilness needed for MSVC
47 #ifdef _MSC_VER
48 #define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n)
49 #define snprintf(buf, size, format, ...) _sprintf_p(buf, size, format)
50 #endif
51
52
53 extern float multiplier;
54 extern XYZ viewer;
55 extern int environment;
56 extern Terrain terrain;
57 extern float screenwidth, screenheight;
58 extern float gravity;
59 extern int detail;
60 extern float texdetail;
61 extern Objects objects;
62 extern int slomo;
63 extern float slomodelay;
64 extern bool floatjump;
65 extern float volume;
66 extern Light light;
67 extern float camerashake;
68 extern float woozy;
69 extern float blackout;
70 extern bool cellophane;
71 extern bool musictoggle;
72 extern int difficulty;
73 extern int bloodtoggle;
74 extern bool invertmouse;
75 extern float windvar;
76 extern float precipdelay;
77 extern XYZ viewerfacing;
78 extern bool ambientsound;
79 extern bool mousejump;
80 extern float viewdistance;
81 extern bool freeze;
82 extern bool keyboardfrozen;
83 extern bool loadingstuff;
84 extern XYZ windvector;
85 extern bool debugmode;
86 static int leveltheme;
87 extern int mainmenu;
88 extern int oldmainmenu;
89 extern bool visibleloading;
90 extern XYZ envsound[30];
91 extern float envsoundvol[30];
92 extern int numenvsounds;
93 extern float envsoundlife[30];
94 extern float usermousesensitivity;
95 extern bool ismotionblur;
96 extern bool showdamagebar; // (des)activate the damage bar
97 extern bool decals;
98 extern float tintr, tintg, tintb;
99 extern bool skyboxtexture;
100 extern float skyboxr;
101 extern float skyboxg;
102 extern float skyboxb;
103 extern float skyboxlightr;
104 extern float skyboxlightg;
105 extern float skyboxlightb;
106 extern float fadestart;
107 extern float slomospeed;
108 extern float slomofreq;
109 extern int tutoriallevel;
110 extern float smoketex;
111 extern float tutorialstagetime;
112 extern int tutorialstage;
113 extern float tutorialmaxtime;
114 extern float tutorialsuccess;
115 extern bool againbonus;
116 extern bool reversaltrain;
117 extern bool canattack;
118 extern bool cananger;
119 extern float damagedealt;
120 extern int maptype;
121 extern int editoractive;
122 extern int editorpathtype;
123 extern TGAImageRec texture;
124
125 extern float hostiletime;
126
127 extern bool gamestarted;
128
129 extern int numhotspots;
130 extern int killhotspot;
131 extern XYZ hotspot[40];
132 extern int hotspottype[40];
133 extern float hotspotsize[40];
134 extern char hotspottext[40][256];
135 extern int currenthotspot;
136
137 extern int hostile;
138
139 extern bool stillloading;
140 extern bool winfreeze;
141
142 extern bool campaign;
143
144
145
146 void Loadlevel(int which);
147 void Loadlevel(const char *name);
148
149
150
151 class CampaignLevel
152 {
153 private:
154     int width;
155     struct Position {
156         int x, y;
157     };
158 public:
159     std::string mapname;
160     std::string description;
161     int choosenext;
162     /*
163     0 = Immediately load next level at the end of this one.
164     1 = Go back to the world map.
165     2 = Don't bring up the Fiery loading screen. Maybe other things, I've not investigated.
166     */
167     //int numnext; // 0 on final level. As David said: he meant to add story branching, but he eventually hadn't.
168     std::vector<int> nextlevel;
169     Position location;
170     CampaignLevel() : width(10) {
171         choosenext = 1;
172         location.x = 0;
173         location.y = 0;
174     }
175     int getStartX() {
176         return 30 + 120 + location.x * 400 / 512;
177     }
178     int getStartY() {
179         return 30 + 30 + (512 - location.y) * 400 / 512;
180     }
181     int getEndX() {
182         return getStartX() + width;
183     }
184     int getEndY() {
185         return getStartY() + width;
186     }
187     XYZ getCenter() {
188         XYZ center;
189         center.x = getStartX() + width / 2;
190         center.y = getStartY() + width / 2;
191         return center;
192     }
193     int getWidth() {
194         return width;
195     }
196     istream& operator<< (istream& is) {
197         is.ignore(256, ':');
198         is.ignore(256, ':');
199         is.ignore(256, ' ');
200         is >> mapname;
201         is.ignore(256, ':');
202         is >> description;
203         for (int pos = description.find('_'); pos != string::npos; pos = description.find('_', pos)) {
204             description.replace(pos, 1, 1, ' ');
205         }
206         is.ignore(256, ':');
207         is >> choosenext;
208         is.ignore(256, ':');
209         int numnext, next;
210         is >> numnext;
211         for (int j = 0; j < numnext; j++) {
212             is.ignore(256, ':');
213             is >> next;
214             nextlevel.push_back(next - 1);
215         }
216         is.ignore(256, ':');
217         is >> location.x;
218         is.ignore(256, ':');
219         is >> location.y;
220         return is;
221     }
222     friend istream& operator>> (istream& is, CampaignLevel& cl) {
223         return cl << is;
224     }
225 };
226
227 int indemo = 0;
228 bool won = false;
229 int entername = 0;
230 vector<CampaignLevel> campaignlevels;
231 int whichchoice = 0;
232 int actuallevel = 0;
233 bool winhotspot = false;
234 bool windialogue = false;
235 bool realthreat = 0;
236 XYZ cameraloc;
237 float cameradist = 0;
238 bool oldattackkey = 0;
239 int whichlevel = 0;
240 float musicvolume[4] = {};
241 float oldmusicvolume[4] = {};
242 int musicselected = 0;
243
244
245
246 static const char *rabbitskin[] = {
247     ":Data:Textures:Fur3.jpg",
248     ":Data:Textures:Fur.jpg",
249     ":Data:Textures:Fur2.jpg",
250     ":Data:Textures:Lynx.jpg",
251     ":Data:Textures:Otter.jpg",
252     ":Data:Textures:Opal.jpg",
253     ":Data:Textures:Sable.jpg",
254     ":Data:Textures:Chocolate.jpg",
255     ":Data:Textures:BW2.jpg",
256     ":Data:Textures:WB2.jpg"
257 };
258
259 static const char *wolfskin[] = {
260     ":Data:Textures:Wolf.jpg",
261     ":Data:Textures:Darkwolf.jpg",
262     ":Data:Textures:Snowwolf.jpg"
263 };
264
265 #define STATIC_ASSERT(x) extern int s_a_dummy[2 * (!!(x)) - 1];
266 STATIC_ASSERT (rabbittype == 0 && wolftype == 1)
267
268 static const char **creatureskin[] = {rabbitskin, wolfskin};
269
270 /* Return true if PFX is a prefix of STR (case-insensitive).  */
271 static bool stripfx(const char *str, const char *pfx)
272 {
273     return !strncasecmp(str, pfx, strlen(pfx));
274 }
275
276 static const char *cmd_names[] = {
277 #define DECLARE_COMMAND(cmd) #cmd,
278 #include "ConsoleCmds.h"
279 #undef  DECLARE_COMMAND
280 };
281
282 typedef void (*console_handler)(const char *args);
283
284 #define DECLARE_COMMAND(cmd) static void ch_##cmd(const char *args);
285 #include "ConsoleCmds.h"
286 #undef  DECLARE_COMMAND
287
288 static console_handler cmd_handlers[] = {
289 #define DECLARE_COMMAND(cmd) ch_##cmd,
290 #include "ConsoleCmds.h"
291 #undef  DECLARE_COMMAND
292 };
293
294
295
296 // utility functions
297
298 // TODO: this is slightly incorrect
299 inline float roughDirection(XYZ vec)
300 {
301     Normalise(&vec);
302     float angle = -asin(-vec.x) * 180 / M_PI;
303     if (vec.z < 0)
304         angle = 180 - angle;
305     return angle;
306 }
307 inline float roughDirectionTo(XYZ start, XYZ end)
308 {
309     return roughDirection(end - start);
310 }
311 inline float pitchOf(XYZ vec)
312 {
313     Normalise(&vec);
314     return -asin(vec.y) * 180 / M_PI;
315 }
316 inline float pitchTo(XYZ start, XYZ end)
317 {
318     return pitchOf(end - start);
319 }
320 inline float sq(float n)
321 {
322     return n * n;
323 }
324 inline float stepTowardf(float from, float to, float by)
325 {
326     if (fabs(from - to) < by) return to;
327     else if (from > to) return from - by;
328     else return from + by;
329 }
330
331 void playdialogueboxsound()
332 {
333     XYZ temppos;
334     temppos = player[participantfocus[whichdialogue][indialogue]].coords;
335     temppos = temppos - viewer;
336     Normalise(&temppos);
337     temppos += viewer;
338
339     int sound = -1;
340     switch (dialogueboxsound[whichdialogue][indialogue]) {
341     case -6:
342         sound = alarmsound;
343         break;
344     case -4:
345         sound = consolefailsound;
346         break;
347     case -3:
348         sound = consolesuccesssound;
349         break;
350     case -2:
351         sound = firestartsound;
352         break;
353     case -1:
354         sound = fireendsound;
355         break;
356     case 1:
357         sound = rabbitchitter;
358         break;
359     case 2:
360         sound = rabbitchitter2;
361         break;
362     case 3:
363         sound = rabbitpainsound;
364         break;
365     case 4:
366         sound = rabbitpain1sound;
367         break;
368     case 5:
369         sound = rabbitattacksound;
370         break;
371     case 6:
372         sound = rabbitattack2sound;
373         break;
374     case 7:
375         sound = rabbitattack3sound;
376         break;
377     case 8:
378         sound = rabbitattack4sound;
379         break;
380     case 9:
381         sound = growlsound;
382         break;
383     case 10:
384         sound = growl2sound;
385         break;
386     case 11:
387         sound = snarlsound;
388         break;
389     case 12:
390         sound = snarl2sound;
391         break;
392     case 13:
393         sound = barksound;
394         break;
395     case 14:
396         sound = bark2sound;
397         break;
398     case 15:
399         sound = bark3sound;
400         break;
401     case 16:
402         sound = barkgrowlsound;
403         break;
404     default:
405         break;
406     }
407     if (sound != -1)
408         emit_sound_at(sound, temppos);
409 }
410
411 // ================================================================
412
413 bool AddClothes(const char *fileName, GLubyte *array)
414 {
415     LOGFUNC;
416     //Load Image
417     unsigned char fileNamep[256];
418     CopyCStringToPascal(fileName, fileNamep);
419     bool opened;
420     opened = upload_image( fileNamep , 1);
421
422     float alphanum;
423     //Is it valid?
424     if (opened) {
425         if (tintr > 1)tintr = 1;
426         if (tintg > 1)tintg = 1;
427         if (tintb > 1)tintb = 1;
428
429         if (tintr < 0)tintr = 0;
430         if (tintg < 0)tintg = 0;
431         if (tintb < 0)tintb = 0;
432
433         int bytesPerPixel = texture.bpp / 8;
434
435         int tempnum = 0;
436         alphanum = 255;
437         for (int i = 0; i < (int)(texture.sizeY * texture.sizeX * bytesPerPixel); i++) {
438             if (bytesPerPixel == 3)alphanum = 255;
439             else if ((i + 1) % 4 == 0)alphanum = texture.data[i];
440             //alphanum/=2;
441             if ((i + 1) % 4 || bytesPerPixel == 3) {
442                 if ((i % 4) == 0)texture.data[i] *= tintr;
443                 if ((i % 4) == 1)texture.data[i] *= tintg;
444                 if ((i % 4) == 2)texture.data[i] *= tintb;
445                 array[tempnum] = (float)array[tempnum] * (1 - alphanum / 255) + (float)texture.data[i] * (alphanum / 255);
446                 tempnum++;
447             }
448         }
449     } else return 0;
450     return 1;
451 }
452
453
454
455 static void ch_quit(const char *args)
456 {
457     tryquit = 1;
458 }
459
460 static void ch_map(const char *args)
461 {
462     Loadlevel(args);
463     whichlevel = -2;
464     campaign = 0;
465 }
466
467 static void ch_save(const char *args)
468 {
469     char buf[64];
470     snprintf(buf, 63, ":Data:Maps:%s", args);
471
472     int mapvers = 12;
473
474     FILE *tfile;
475     tfile = fopen( ConvertFileName(buf), "wb" );
476     fpackf(tfile, "Bi", mapvers);
477     fpackf(tfile, "Bi", maptype);
478     fpackf(tfile, "Bi", hostile);
479     fpackf(tfile, "Bf Bf", viewdistance, fadestart);
480     fpackf(tfile, "Bb Bf Bf Bf", skyboxtexture, skyboxr, skyboxg, skyboxb);
481     fpackf(tfile, "Bf Bf Bf", skyboxlightr, skyboxlightg, skyboxlightb);
482     fpackf(tfile, "Bf Bf Bf Bf Bf Bi", player[0].coords.x, player[0].coords.y, player[0].coords.z,
483            player[0].yaw, player[0].targetyaw, player[0].num_weapons);
484     if (player[0].num_weapons > 0 && player[0].num_weapons < 5)
485         for (int j = 0; j < player[0].num_weapons; j++)
486             fpackf(tfile, "Bi", weapons[player[0].weaponids[j]].getType());
487
488     fpackf(tfile, "Bf Bf Bf", player[0].armorhead, player[0].armorhigh, player[0].armorlow);
489     fpackf(tfile, "Bf Bf Bf", player[0].protectionhead, player[0].protectionhigh, player[0].protectionlow);
490     fpackf(tfile, "Bf Bf Bf", player[0].metalhead, player[0].metalhigh, player[0].metallow);
491     fpackf(tfile, "Bf Bf", player[0].power, player[0].speedmult);
492
493     fpackf(tfile, "Bi", player[0].numclothes);
494
495     fpackf(tfile, "Bi Bi", player[0].whichskin, player[0].creature);
496
497     fpackf(tfile, "Bi", numdialogues);
498
499     for (int k = 0; k < numdialogues; k++) {
500         fpackf(tfile, "Bi", numdialogueboxes[k]);
501         fpackf(tfile, "Bi", dialoguetype[k]);
502         for (int l = 0; l < 10; l++) {
503             fpackf(tfile, "Bf Bf Bf", participantlocation[k][l].x, participantlocation[k][l].y, participantlocation[k][l].z);
504             fpackf(tfile, "Bf", participantyaw[k][l]);
505         }
506         for (int l = 0; l < numdialogueboxes[k]; l++) {
507             fpackf(tfile, "Bi", dialogueboxlocation[k][l]);
508             fpackf(tfile, "Bf", dialogueboxcolor[k][l][0]);
509             fpackf(tfile, "Bf", dialogueboxcolor[k][l][1]);
510             fpackf(tfile, "Bf", dialogueboxcolor[k][l][2]);
511             fpackf(tfile, "Bi", dialogueboxsound[k][l]);
512
513             int templength = strlen(dialoguetext[k][l]);
514             fpackf(tfile, "Bi", (templength));
515             for (int m = 0; m < templength; m++) {
516                 fpackf(tfile, "Bb", dialoguetext[k][l][m]);
517                 if (dialoguetext[k][l][m] == '\0')
518                     break;
519             }
520
521             templength = strlen(dialoguename[k][l]);
522             fpackf(tfile, "Bi", templength);
523             for (int m = 0; m < templength; m++) {
524                 fpackf(tfile, "Bb", dialoguename[k][l][m]);
525                 if (dialoguename[k][l][m] == '\0')
526                     break;
527             }
528
529             fpackf(tfile, "Bf Bf Bf", dialoguecamera[k][l].x, dialoguecamera[k][l].y, dialoguecamera[k][l].z);
530             fpackf(tfile, "Bi", participantfocus[k][l]);
531             fpackf(tfile, "Bi", participantaction[k][l]);
532
533             for (int m = 0; m < 10; m++)
534                 fpackf(tfile, "Bf Bf Bf", participantfacing[k][l][m].x, participantfacing[k][l][m].y, participantfacing[k][l][m].z);
535
536             fpackf(tfile, "Bf Bf", dialoguecamerayaw[k][l], dialoguecamerapitch[k][l]);
537         }
538     }
539
540     for (int k = 0; k < player[0].numclothes; k++) {
541         int templength = strlen(player[0].clothes[k]);
542         fpackf(tfile, "Bi", templength);
543         for (int l = 0; l < templength; l++)
544             fpackf(tfile, "Bb", player[0].clothes[k][l]);
545         fpackf(tfile, "Bf Bf Bf", player[0].clothestintr[k], player[0].clothestintg[k], player[0].clothestintb[k]);
546     }
547
548     fpackf(tfile, "Bi", environment);
549
550     fpackf(tfile, "Bi", objects.numobjects);
551
552     for (int k = 0; k < objects.numobjects; k++)
553         fpackf(tfile, "Bi Bf Bf Bf Bf Bf Bf", objects.type[k], objects.yaw[k], objects.pitch[k],
554                objects.position[k].x, objects.position[k].y, objects.position[k].z, objects.scale[k]);
555
556     fpackf(tfile, "Bi", numhotspots);
557     for (int i = 0; i < numhotspots; i++) {
558         fpackf(tfile, "Bi Bf Bf Bf Bf", hotspottype[i], hotspotsize[i], hotspot[i].x, hotspot[i].y, hotspot[i].z);
559         int templength = strlen(hotspottext[i]);
560         fpackf(tfile, "Bi", templength);
561         for (int l = 0; l < templength; l++)
562             fpackf(tfile, "Bb", hotspottext[i][l]);
563     }
564
565     fpackf(tfile, "Bi", numplayers);
566     if (numplayers < maxplayers)
567         for (int j = 1; j < numplayers; j++) {
568             fpackf(tfile, "Bi Bi Bf Bf Bf Bi Bi Bf Bb Bf", player[j].whichskin, player[j].creature,
569                    player[j].coords.x, player[j].coords.y, player[j].coords.z,
570                    player[j].num_weapons, player[j].howactive, player[j].scale, player[j].immobile, player[j].yaw);
571             if (player[j].num_weapons < 5)
572                 for (int k = 0; k < player[j].num_weapons; k++)
573                     fpackf(tfile, "Bi", weapons[player[j].weaponids[k]].getType());
574             if (player[j].numwaypoints < 30) {
575                 fpackf(tfile, "Bi", player[j].numwaypoints);
576                 for (int k = 0; k < player[j].numwaypoints; k++) {
577                     fpackf(tfile, "Bf", player[j].waypoints[k].x);
578                     fpackf(tfile, "Bf", player[j].waypoints[k].y);
579                     fpackf(tfile, "Bf", player[j].waypoints[k].z);
580                     fpackf(tfile, "Bi", player[j].waypointtype[k]);
581                 }
582                 fpackf(tfile, "Bi", player[j].waypoint);
583             } else {
584                 player[j].numwaypoints = 0;
585                 player[j].waypoint = 0;
586                 fpackf(tfile, "Bi Bi Bi", player[j].numwaypoints, player[j].waypoint, player[j].waypoint);
587             }
588
589             fpackf(tfile, "Bf Bf Bf", player[j].armorhead, player[j].armorhigh, player[j].armorlow);
590             fpackf(tfile, "Bf Bf Bf", player[j].protectionhead, player[j].protectionhigh, player[j].protectionlow);
591             fpackf(tfile, "Bf Bf Bf", player[j].metalhead, player[j].metalhigh, player[j].metallow);
592             fpackf(tfile, "Bf Bf", player[j].power, player[j].speedmult);
593
594             float headprop, bodyprop, armprop, legprop;
595             if (player[j].creature == wolftype) {
596                 headprop = player[j].proportionhead.x / 1.1;
597                 bodyprop = player[j].proportionbody.x / 1.1;
598                 armprop = player[j].proportionarms.x / 1.1;
599                 legprop = player[j].proportionlegs.x / 1.1;
600             } else if (player[j].creature == rabbittype) {
601                 headprop = player[j].proportionhead.x / 1.2;
602                 bodyprop = player[j].proportionbody.x / 1.05;
603                 armprop = player[j].proportionarms.x / 1.00;
604                 legprop = player[j].proportionlegs.x / 1.1;
605             }
606
607             fpackf(tfile, "Bf Bf Bf Bf", headprop, bodyprop, armprop, legprop);
608
609             fpackf(tfile, "Bi", player[j].numclothes);
610             if (player[j].numclothes)
611                 for (int k = 0; k < player[j].numclothes; k++) {
612                     int templength;
613                     templength = strlen(player[j].clothes[k]);
614                     fpackf(tfile, "Bi", templength);
615                     for (int l = 0; l < templength; l++)
616                         fpackf(tfile, "Bb", player[j].clothes[k][l]);
617                     fpackf(tfile, "Bf Bf Bf", player[j].clothestintr[k], player[j].clothestintg[k], player[j].clothestintb[k]);
618                 }
619         }
620
621     fpackf(tfile, "Bi", numpathpoints);
622     for (int j = 0; j < numpathpoints; j++) {
623         fpackf(tfile, "Bf Bf Bf Bi", pathpoint[j].x, pathpoint[j].y, pathpoint[j].z, numpathpointconnect[j]);
624         for (int k = 0; k < numpathpointconnect[j]; k++)
625             fpackf(tfile, "Bi", pathpointconnect[j][k]);
626     }
627
628     fpackf(tfile, "Bf Bf Bf Bf", mapcenter.x, mapcenter.y, mapcenter.z, mapradius);
629
630     fclose(tfile);
631 }
632
633 static void ch_cellar(const char *args)
634 {
635     player[0].skeleton.drawmodel.textureptr.load(":Data:Textures:Furdarko.jpg", 1, &player[0].skeleton.skinText[0], &player[0].skeleton.skinsize);
636 }
637
638 static void ch_tint(const char *args)
639 {
640     sscanf(args, "%f%f%f", &tintr, &tintg, &tintb);
641 }
642
643 static void ch_tintr(const char *args)
644 {
645     tintr = atof(args);
646 }
647
648 static void ch_tintg(const char *args)
649 {
650     tintg = atof(args);
651 }
652
653 static void ch_tintb(const char *args)
654 {
655     tintb = atof(args);
656 }
657
658 static void ch_speed(const char *args)
659 {
660     player[0].speedmult = atof(args);
661 }
662
663 static void ch_strength(const char *args)
664 {
665     player[0].power = atof(args);
666 }
667
668 static void ch_power(const char *args)
669 {
670     player[0].power = atof(args);
671 }
672
673 static void ch_size(const char *args)
674 {
675     player[0].scale = atof(args) * .2;
676 }
677
678 static int findClosestPlayer()
679 {
680     int closest = -1;
681     float closestdist = std::numeric_limits<float>::max();
682
683     for (int i = 1; i < numplayers; i++) {
684         float distance = distsq(&player[i].coords, &player[0].coords);
685         if (distance < closestdist) {
686             closestdist = distance;
687             closest = i;
688         }
689     }
690     return closest;
691 }
692
693 static int findClosestObject()
694 {
695     int closest = -1;
696     float closestdist = std::numeric_limits<float>::max();
697
698     for (int i = 0; i < objects.numobjects; i++) {
699         float distance = distsq(&objects.position[i], &player[0].coords);
700         if (distance < closestdist) {
701             closestdist = distance;
702             closest = i;
703         }
704     }
705     return closest;
706 }
707
708 static void ch_sizenear(const char *args)
709 {
710     int closest = findClosestPlayer();
711     if (closest >= 0)
712         player[closest].scale = atof(args) * .2;
713 }
714
715 static void set_proportion(int pnum, const char *args)
716 {
717     float headprop, bodyprop, armprop, legprop;
718
719     sscanf(args, "%f%f%f%f", &headprop, &bodyprop, &armprop, &legprop);
720
721     if (player[pnum].creature == wolftype) {
722         player[pnum].proportionhead = 1.1 * headprop;
723         player[pnum].proportionbody = 1.1 * bodyprop;
724         player[pnum].proportionarms = 1.1 * armprop;
725         player[pnum].proportionlegs = 1.1 * legprop;
726     } else if (player[pnum].creature == rabbittype) {
727         player[pnum].proportionhead = 1.2 * headprop;
728         player[pnum].proportionbody = 1.05 * bodyprop;
729         player[pnum].proportionarms = 1.00 * armprop;
730         player[pnum].proportionlegs = 1.1 * legprop;
731         player[pnum].proportionlegs.y = 1.05 * legprop;
732     }
733 }
734
735 static void ch_proportion(const char *args)
736 {
737     set_proportion(0, args);
738 }
739
740 static void ch_proportionnear(const char *args)
741 {
742     int closest = findClosestPlayer();
743     if (closest >= 0)
744         set_proportion(closest, args);
745 }
746
747 static void set_protection(int pnum, const char *args)
748 {
749     float head, high, low;
750     sscanf(args, "%f%f%f", &head, &high, &low);
751
752     player[pnum].protectionhead = head;
753     player[pnum].protectionhigh = high;
754     player[pnum].protectionlow  = low;
755 }
756
757 static void ch_protection(const char *args)
758 {
759     set_protection(0, args);
760 }
761
762 static void ch_protectionnear(const char *args)
763 {
764     int closest = findClosestPlayer();
765     if (closest >= 0)
766         set_protection(closest, args);
767 }
768
769 static void set_armor(int pnum, const char *args)
770 {
771     float head, high, low;
772     sscanf(args, "%f%f%f", &head, &high, &low);
773
774     player[pnum].armorhead = head;
775     player[pnum].armorhigh = high;
776     player[pnum].armorlow  = low;
777 }
778
779 static void ch_armor(const char *args)
780 {
781     set_armor(0, args);
782 }
783
784 static void ch_armornear(const char *args)
785 {
786     int closest = findClosestPlayer();
787     if (closest >= 0)
788         set_armor(closest, args);
789 }
790
791 static void ch_protectionreset(const char *args)
792 {
793     set_protection(0, "1 1 1");
794     set_armor(0, "1 1 1");
795 }
796
797 static void set_metal(int pnum, const char *args)
798 {
799     float head, high, low;
800     sscanf(args, "%f%f%f", &head, &high, &low);
801
802     player[pnum].metalhead = head;
803     player[pnum].metalhigh = high;
804     player[pnum].metallow  = low;
805 }
806
807 static void ch_metal(const char *args)
808 {
809     set_metal(0, args);
810 }
811
812 static void set_noclothes(int pnum, const char *args)
813 {
814     player[pnum].numclothes = 0;
815     player[pnum].skeleton.drawmodel.textureptr.load(
816         creatureskin[player[pnum].creature][player[pnum].whichskin], 1,
817         &player[pnum].skeleton.skinText[0], &player[pnum].skeleton.skinsize);
818 }
819
820 static void ch_noclothes(const char *args)
821 {
822     set_noclothes(0, args);
823 }
824
825 static void ch_noclothesnear(const char *args)
826 {
827     int closest = findClosestPlayer();
828     if (closest >= 0)
829         set_noclothes(closest, args);
830 }
831
832
833 static void set_clothes(int pnum, const char *args)
834 {
835     char buf[64];
836     snprintf(buf, 63, ":Data:Textures:%s.png", args);
837
838     if (!AddClothes(buf, &player[pnum].skeleton.skinText[pnum]))
839         return;
840
841     player[pnum].DoMipmaps();
842     strcpy(player[pnum].clothes[player[pnum].numclothes], buf);
843     player[pnum].clothestintr[player[pnum].numclothes] = tintr;
844     player[pnum].clothestintg[player[pnum].numclothes] = tintg;
845     player[pnum].clothestintb[player[pnum].numclothes] = tintb;
846     player[pnum].numclothes++;
847 }
848
849 static void ch_clothes(const char *args)
850 {
851     set_clothes(0, args);
852 }
853
854 static void ch_clothesnear(const char *args)
855 {
856     int closest = findClosestPlayer();
857     if (closest >= 0)
858         set_clothes(closest, args);
859 }
860
861 static void ch_belt(const char *args)
862 {
863     player[0].skeleton.clothes = !player[0].skeleton.clothes;
864 }
865
866
867 static void ch_cellophane(const char *args)
868 {
869     cellophane = !cellophane;
870     float mul = cellophane ? 0 : 1;
871
872     for (int i = 0; i < numplayers; i++) {
873         player[i].proportionhead.z = player[i].proportionhead.x * mul;
874         player[i].proportionbody.z = player[i].proportionbody.x * mul;
875         player[i].proportionarms.z = player[i].proportionarms.x * mul;
876         player[i].proportionlegs.z = player[i].proportionlegs.x * mul;
877     }
878 }
879
880 static void ch_funnybunny(const char *args)
881 {
882     player[0].skeleton.id = 0;
883     player[0].skeleton.Load(":Data:Skeleton:Basic Figure", ":Data:Skeleton:Basic Figurelow",
884                             ":Data:Skeleton:Rabbitbelt", ":Data:Models:Body.solid",
885                             ":Data:Models:Body2.solid", ":Data:Models:Body3.solid",
886                             ":Data:Models:Body4.solid", ":Data:Models:Body5.solid",
887                             ":Data:Models:Body6.solid", ":Data:Models:Body7.solid",
888                             ":Data:Models:Bodylow.solid", ":Data:Models:Belt.solid", 1);
889     player[0].skeleton.drawmodel.textureptr.load(":Data:Textures:fur3.jpg", 1, &player[0].skeleton.skinText[0], &player[0].skeleton.skinsize);
890     player[0].creature = rabbittype;
891     player[0].scale = .2;
892     player[0].headless = 0;
893     player[0].damagetolerance = 200;
894     set_proportion(0, "1 1 1 1");
895 }
896
897 static void ch_wolfie(const char *args)
898 {
899     player[0].skeleton.id = 0;
900     player[0].skeleton.Load(":Data:Skeleton:Basic Figure Wolf", ":Data:Skeleton:Basic Figure Wolf Low",
901                             ":Data:Skeleton:Rabbitbelt", ":Data:Models:Wolf.solid",
902                             ":Data:Models:Wolf2.solid", ":Data:Models:Wolf3.solid",
903                             ":Data:Models:Wolf4.solid", ":Data:Models:Wolf5.solid",
904                             ":Data:Models:Wolf6.solid", ":Data:Models:Wolf7.solid",
905                             ":Data:Models:Wolflow.solid", ":Data:Models:Belt.solid", 0);
906     player[0].skeleton.drawmodel.textureptr.load(":Data:Textures:Wolf.jpg", 1, &player[0].skeleton.skinText[0], &player[0].skeleton.skinsize);
907     player[0].creature = wolftype;
908     player[0].damagetolerance = 300;
909     set_proportion(0, "1 1 1 1");
910 }
911
912 static void ch_wolfieisgod(const char *args)
913 {
914     ch_wolfie(args);
915 }
916
917 static void ch_wolf(const char *args)
918 {
919     player[0].skeleton.drawmodel.textureptr.load(":Data:Textures:Wolf.jpg", 1, &player[0].skeleton.skinText[0], &player[0].skeleton.skinsize);
920 }
921
922 static void ch_snowwolf(const char *args)
923 {
924     player[0].skeleton.drawmodel.textureptr.load(":Data:Textures:SnowWolf.jpg", 1, &player[0].skeleton.skinText[0], &player[0].skeleton.skinsize);
925 }
926
927 static void ch_darkwolf(const char *args)
928 {
929     player[0].skeleton.drawmodel.textureptr.load(":Data:Textures:DarkWolf.jpg", 1, &player[0].skeleton.skinText[0], &player[0].skeleton.skinsize);
930 }
931
932 static void ch_lizardwolf(const char *args)
933 {
934     player[0].skeleton.drawmodel.textureptr.load(":Data:Textures:Lizardwolf.jpg", 1, &player[0].skeleton.skinText[0], &player[0].skeleton.skinsize);
935 }
936
937 static void ch_white(const char *args)
938 {
939     player[0].skeleton.drawmodel.textureptr.load(":Data:Textures:fur.jpg", 1, &player[0].skeleton.skinText[0], &player[0].skeleton.skinsize);
940 }
941
942 static void ch_brown(const char *args)
943 {
944     player[0].skeleton.drawmodel.textureptr.load(":Data:Textures:fur3.jpg", 1, &player[0].skeleton.skinText[0], &player[0].skeleton.skinsize);
945 }
946
947 static void ch_black(const char *args)
948 {
949     player[0].skeleton.drawmodel.textureptr.load(":Data:Textures:fur2.jpg", 1, &player[0].skeleton.skinText[0], &player[0].skeleton.skinsize);
950 }
951
952 static void ch_sizemin(const char *args)
953 {
954     for (int i = 1; i < numplayers; i++)
955         if (player[i].scale < 0.8 * 0.2)
956             player[i].scale = 0.8 * 0.2;
957 }
958
959 static void ch_tutorial(const char *args)
960 {
961     tutoriallevel = atoi(args);
962 }
963
964 static void ch_hostile(const char *args)
965 {
966     hostile = atoi(args);
967 }
968
969 static void ch_indemo(const char *args)
970 {
971     indemo = 1;
972     hotspot[numhotspots] = player[0].coords;
973     hotspotsize[numhotspots] = 0;
974     hotspottype[numhotspots] = -111;
975     strcpy(hotspottext[numhotspots], "mapname");
976     numhotspots++;
977 }
978
979 static void ch_notindemo(const char *args)
980 {
981     indemo = 0;
982     numhotspots--;
983 }
984
985 static void ch_type(const char *args)
986 {
987     int n = sizeof(editortypenames) / sizeof(editortypenames[0]);
988     for (int i = 0; i < n; i++)
989         if (stripfx(args, editortypenames[i])) {
990             editoractive = i;
991             break;
992         }
993 }
994
995 static void ch_path(const char *args)
996 {
997     int n = sizeof(pathtypenames) / sizeof(pathtypenames[0]);
998     for (int i = 0; i < n; i++)
999         if (stripfx(args, pathtypenames[i])) {
1000             editorpathtype = i;
1001             break;
1002         }
1003 }
1004
1005 static void ch_hs(const char *args)
1006 {
1007     hotspot[numhotspots] = player[0].coords;
1008
1009     float size;
1010     int type, shift;
1011     sscanf(args, "%f%d %n", &size, &type, &shift);
1012
1013     hotspotsize[numhotspots] = size;
1014     hotspottype[numhotspots] = type;
1015
1016     strcpy(hotspottext[numhotspots], args + shift);
1017     strcat(hotspottext[numhotspots], "\n");
1018
1019     numhotspots++;
1020 }
1021
1022 static void ch_dialogue(const char *args)
1023 {
1024     int dlg;
1025     char buf1[32], buf2[64];
1026
1027     sscanf(args, "%d %31s", &dlg, buf1);
1028     snprintf(buf2, 63, ":Data:Dialogues:%s.txt", buf1);
1029
1030     dialoguetype[numdialogues] = dlg;
1031
1032     memset(dialoguetext[numdialogues], 0, sizeof(dialoguetext[numdialogues]));
1033     memset(dialoguename[numdialogues], 0, sizeof(dialoguename[numdialogues]));
1034
1035     ifstream ipstream(ConvertFileName(buf2));
1036     ipstream.ignore(256, ':');
1037     ipstream >> numdialogueboxes[numdialogues];
1038     for (int i = 0; i < numdialogueboxes[numdialogues]; i++) {
1039         ipstream.ignore(256, ':');
1040         ipstream.ignore(256, ':');
1041         ipstream.ignore(256, ' ');
1042         ipstream >> dialogueboxlocation[numdialogues][i];
1043         ipstream.ignore(256, ':');
1044         ipstream >> dialogueboxcolor[numdialogues][i][0];
1045         ipstream >> dialogueboxcolor[numdialogues][i][1];
1046         ipstream >> dialogueboxcolor[numdialogues][i][2];
1047         ipstream.ignore(256, ':');
1048         ipstream.getline(dialoguename[numdialogues][i], 64);
1049         ipstream.ignore(256, ':');
1050         ipstream.ignore(256, ' ');
1051         ipstream.getline(dialoguetext[numdialogues][i], 128);
1052         for (int j = 0; j < 128; j++) {
1053             if (dialoguetext[numdialogues][i][j] == '\\')dialoguetext[numdialogues][i][j] = '\n';
1054         }
1055         ipstream.ignore(256, ':');
1056         ipstream >> dialogueboxsound[numdialogues][i];
1057     }
1058
1059     for (int i = 0; i < numdialogueboxes[numdialogues]; i++) {
1060         for (int j = 0; j < numplayers; j++) {
1061             participantfacing[numdialogues][i][j] = player[j].facing;
1062         }
1063     }
1064     ipstream.close();
1065
1066     directing = 1;
1067     indialogue = 0;
1068     whichdialogue = numdialogues;
1069
1070     numdialogues++;
1071 }
1072
1073 static void ch_fixdialogue(const char *args)
1074 {
1075     char buf1[32], buf2[64];
1076     int whichdi;
1077
1078     sscanf(args, "%d %31s", &whichdi, buf1);
1079     snprintf(buf2, 63, ":Data:Dialogues:%s.txt", buf1);
1080
1081     memset(dialoguetext[whichdi], 0, sizeof(dialoguetext[whichdi]));
1082     memset(dialoguename[whichdi], 0, sizeof(dialoguename[whichdi]));
1083
1084     ifstream ipstream(ConvertFileName(buf2));
1085     ipstream.ignore(256, ':');
1086     ipstream >> numdialogueboxes[whichdi];
1087     for (int i = 0; i < numdialogueboxes[whichdi]; i++) {
1088         ipstream.ignore(256, ':');
1089         ipstream.ignore(256, ':');
1090         ipstream.ignore(256, ' ');
1091         ipstream >> dialogueboxlocation[whichdi][i];
1092         ipstream.ignore(256, ':');
1093         ipstream >> dialogueboxcolor[whichdi][i][0];
1094         ipstream >> dialogueboxcolor[whichdi][i][1];
1095         ipstream >> dialogueboxcolor[whichdi][i][2];
1096         ipstream.ignore(256, ':');
1097         ipstream.getline(dialoguename[whichdi][i], 64);
1098         ipstream.ignore(256, ':');
1099         ipstream.ignore(256, ' ');
1100         ipstream.getline(dialoguetext[whichdi][i], 128);
1101         for (int j = 0; j < 128; j++) {
1102             if (dialoguetext[whichdi][i][j] == '\\')dialoguetext[whichdi][i][j] = '\n';
1103         }
1104         ipstream.ignore(256, ':');
1105         ipstream >> dialogueboxsound[whichdi][i];
1106     }
1107
1108     ipstream.close();
1109 }
1110
1111 static void ch_fixtype(const char *args)
1112 {
1113     int dlg;
1114     sscanf(args, "%d", &dlg);
1115     dialoguetype[0] = dlg;
1116 }
1117
1118 static void ch_fixrotation(const char *args)
1119 {
1120     participantyaw[whichdialogue][participantfocus[whichdialogue][indialogue]] = player[participantfocus[whichdialogue][indialogue]].yaw;
1121 }
1122
1123 static void ch_ddialogue(const char *args)
1124 {
1125     if (numdialogues)
1126         numdialogues--;
1127 }
1128
1129 static void ch_dhs(const char *args)
1130 {
1131     if (numhotspots)
1132         numhotspots--;
1133 }
1134
1135 static void ch_immobile(const char *args)
1136 {
1137     player[0].immobile = 1;
1138 }
1139
1140 static void ch_allimmobile(const char *args)
1141 {
1142     for (int i = 1; i < numplayers; i++)
1143         player[i].immobile = 1;
1144 }
1145
1146 static void ch_mobile(const char *args)
1147 {
1148     player[0].immobile = 0;
1149 }
1150
1151 static void ch_default(const char *args)
1152 {
1153     player[0].armorhead = 1;
1154     player[0].armorhigh = 1;
1155     player[0].armorlow = 1;
1156     player[0].protectionhead = 1;
1157     player[0].protectionhigh = 1;
1158     player[0].protectionlow = 1;
1159     player[0].metalhead = 1;
1160     player[0].metalhigh = 1;
1161     player[0].metallow = 1;
1162     player[0].power = 1;
1163     player[0].speedmult = 1;
1164     player[0].scale = 1;
1165
1166     if (player[0].creature == wolftype) {
1167         player[0].proportionhead = 1.1;
1168         player[0].proportionbody = 1.1;
1169         player[0].proportionarms = 1.1;
1170         player[0].proportionlegs = 1.1;
1171     } else if (player[0].creature == rabbittype) {
1172         player[0].proportionhead = 1.2;
1173         player[0].proportionbody = 1.05;
1174         player[0].proportionarms = 1.00;
1175         player[0].proportionlegs = 1.1;
1176         player[0].proportionlegs.y = 1.05;
1177     }
1178
1179     player[0].numclothes = 0;
1180     player[0].skeleton.drawmodel.textureptr.load(
1181         creatureskin[player[0].creature][player[0].whichskin], 1,
1182         &player[0].skeleton.skinText[0], &player[0].skeleton.skinsize);
1183
1184     editoractive = typeactive;
1185     player[0].immobile = 0;
1186 }
1187
1188 static void ch_play(const char *args)
1189 {
1190     int dlg;
1191     sscanf(args, "%d", &dlg);
1192     whichdialogue = dlg;
1193
1194     if (whichdialogue >= numdialogues)
1195         return;
1196
1197     for (int i = 0; i < numdialogueboxes[whichdialogue]; i++) {
1198         player[participantfocus[whichdialogue][i]].coords = participantlocation[whichdialogue][participantfocus[whichdialogue][i]];
1199         player[participantfocus[whichdialogue][i]].yaw = participantyaw[whichdialogue][participantfocus[whichdialogue][i]];
1200         player[participantfocus[whichdialogue][i]].targetyaw = participantyaw[whichdialogue][participantfocus[whichdialogue][i]];
1201         player[participantfocus[whichdialogue][i]].velocity = 0;
1202         player[participantfocus[whichdialogue][i]].animTarget = player[participantfocus[whichdialogue][i]].getIdle();
1203         player[participantfocus[whichdialogue][i]].frameTarget = 0;
1204     }
1205
1206     directing = 0;
1207     indialogue = 0;
1208
1209     playdialogueboxsound();
1210 }
1211
1212 static void ch_mapkilleveryone(const char *args)
1213 {
1214     maptype = mapkilleveryone;
1215 }
1216
1217 static void ch_mapkillmost(const char *args)
1218 {
1219     maptype = mapkillmost;
1220 }
1221
1222 static void ch_mapkillsomeone(const char *args)
1223 {
1224     maptype = mapkillsomeone;
1225 }
1226
1227 static void ch_mapgosomewhere(const char *args)
1228 {
1229     maptype = mapgosomewhere;
1230 }
1231
1232 static void ch_viewdistance(const char *args)
1233 {
1234     viewdistance = atof(args) * 100;
1235 }
1236
1237 static void ch_fadestart(const char *args)
1238 {
1239     fadestart = atof(args);
1240 }
1241
1242 static void ch_slomo(const char *args)
1243 {
1244     slomospeed = atof(args);
1245     slomo = !slomo;
1246     slomodelay = 1000;
1247 }
1248
1249 static void ch_slofreq(const char *args)
1250 {
1251     slomofreq = atof(args);
1252 }
1253
1254 static void ch_skytint(const char *args)
1255 {
1256     sscanf(args, "%f%f%f", &skyboxr, &skyboxg, &skyboxb);
1257
1258     skyboxlightr = skyboxr;
1259     skyboxlightg = skyboxg;
1260     skyboxlightb = skyboxb;
1261
1262     SetUpLighting();
1263
1264     terrain.DoShadows();
1265     objects.DoShadows();
1266 }
1267
1268 static void ch_skylight(const char *args)
1269 {
1270     sscanf(args, "%f%f%f", &skyboxlightr, &skyboxlightg, &skyboxlightb);
1271
1272     SetUpLighting();
1273
1274     terrain.DoShadows();
1275     objects.DoShadows();
1276 }
1277
1278 static void ch_skybox(const char *args)
1279 {
1280     skyboxtexture = !skyboxtexture;
1281
1282     SetUpLighting();
1283
1284     terrain.DoShadows();
1285     objects.DoShadows();
1286 }
1287
1288 static void cmd_dispatch(const string cmd)
1289 {
1290     int i, n_cmds = sizeof(cmd_names) / sizeof(cmd_names[0]);
1291
1292     for (i = 0; i < n_cmds; i++)
1293         if (cmd.substr(0, cmd.find(' ')) == string(cmd_names[i])) {
1294             cout << "|" << cmd.substr(cmd.find(' ') + 1) << "|" << endl;
1295             cmd_handlers[i](cmd.substr(cmd.find(' ') + 1).c_str());
1296             break;
1297         }
1298     emit_sound_np(i < n_cmds ? consolesuccesssound : consolefailsound);
1299 }
1300
1301 /********************> Tick() <*****/
1302 extern bool save_image(const char * fname);
1303 void Screenshot (void)
1304 {
1305     char temp[1024];
1306     time_t      t = time(NULL);
1307     struct      tm *tme = localtime(&t);
1308     sprintf(temp, "Screenshots/Screenshot_%04d_%02d_%02d--%02d_%02d_%02d.png", tme->tm_year + 1900, tme->tm_mon + 1, tme->tm_mday, tme->tm_hour, tme->tm_min, tme->tm_sec);
1309
1310 #if defined(_WIN32)
1311     mkdir("Screenshots");
1312 #else
1313     mkdir("Screenshots", S_IRWXU);
1314 #endif
1315
1316     save_image(temp);
1317 }
1318
1319 void Game::SetUpLighting()
1320 {
1321     if (environment == snowyenvironment)
1322         light.setColors(.65, .65, .7, .4, .4, .44);
1323     if (environment == desertenvironment)
1324         light.setColors(.95, .95, .95, .4, .35, .3);
1325     if (environment == grassyenvironment)
1326         light.setColors(.95, .95, 1, .4, .4, .44);
1327     if (!skyboxtexture)
1328         light.setColors(1, 1, 1, .4, .4, .4);
1329     float average;
1330     average = (skyboxlightr + skyboxlightg + skyboxlightb) / 3;
1331     light.color[0] *= (skyboxlightr + average) / 2;
1332     light.color[1] *= (skyboxlightg + average) / 2;
1333     light.color[2] *= (skyboxlightb + average) / 2;
1334     light.ambient[0] *= (skyboxlightr + average) / 2;
1335     light.ambient[1] *= (skyboxlightg + average) / 2;
1336     light.ambient[2] *= (skyboxlightb + average) / 2;
1337 }
1338
1339 int findPathDist(int start, int end)
1340 {
1341     int smallestcount, count, connected;
1342     int last, last2, last3, last4;
1343     int closest;
1344
1345     smallestcount = 1000;
1346     for (int i = 0; i < 50; i++) {
1347         count = 0;
1348         last = start;
1349         last2 = -1;
1350         last3 = -1;
1351         last4 = -1;
1352         while (last != end && count < 30) {
1353             closest = -1;
1354             for (int j = 0; j < numpathpoints; j++) {
1355                 if (j != last && j != last2 && j != last3 && j != last4) {
1356                     connected = 0;
1357                     if (numpathpointconnect[j])
1358                         for (int k = 0; k < numpathpointconnect[j]; k++) {
1359                             if (pathpointconnect[j][k] == last)connected = 1;
1360                         }
1361                     if (!connected)
1362                         if (numpathpointconnect[last])
1363                             for (int k = 0; k < numpathpointconnect[last]; k++) {
1364                                 if (pathpointconnect[last][k] == j)connected = 1;
1365                             }
1366                     if (connected)
1367                         if (closest == -1 || Random() % 2 == 0) {
1368                             closest = j;
1369                         }
1370                 }
1371             }
1372             last4 = last3;
1373             last3 = last2;
1374             last2 = last;
1375             last = closest;
1376             count++;
1377         }
1378         if (count < smallestcount)smallestcount = count;
1379     }
1380     return smallestcount;
1381 }
1382
1383 int Game::checkcollide(XYZ startpoint, XYZ endpoint)
1384 {
1385     static XYZ colpoint, colviewer, coltarget;
1386     static float minx, minz, maxx, maxz, miny, maxy;
1387
1388     minx = min(startpoint.x, endpoint.x) - 1;
1389     miny = min(startpoint.y, endpoint.y) - 1;
1390     minz = min(startpoint.z, endpoint.z) - 1;
1391     maxx = max(startpoint.x, endpoint.x) + 1;
1392     maxy = max(startpoint.y, endpoint.y) + 1;
1393     maxz = max(startpoint.z, endpoint.z) + 1;
1394
1395     for (int i = 0; i < objects.numobjects; i++) {
1396         if (     objects.position[i].x > minx - objects.model[i].boundingsphereradius &&
1397                  objects.position[i].x < maxx + objects.model[i].boundingsphereradius &&
1398                  objects.position[i].y > miny - objects.model[i].boundingsphereradius &&
1399                  objects.position[i].y < maxy + objects.model[i].boundingsphereradius &&
1400                  objects.position[i].z > minz - objects.model[i].boundingsphereradius &&
1401                  objects.position[i].z < maxz + objects.model[i].boundingsphereradius) {
1402             if (     objects.type[i] != treeleavestype &&
1403                      objects.type[i] != bushtype &&
1404                      objects.type[i] != firetype) {
1405                 colviewer = startpoint;
1406                 coltarget = endpoint;
1407                 if (objects.model[i].LineCheck(&colviewer, &coltarget, &colpoint, &objects.position[i], &objects.yaw[i]) != -1)return i;
1408             }
1409         }
1410     }
1411
1412     //if(terrain.lineTerrain(startpoint,endpoint,&colpoint)!=-1)return 1000;
1413
1414     return -1;
1415 }
1416
1417 int Game::checkcollide(XYZ startpoint, XYZ endpoint, int what)
1418 {
1419     static XYZ colpoint, colviewer, coltarget;
1420     static float minx, minz, maxx, maxz, miny, maxy;
1421     static int i; //FIXME: see below
1422
1423     minx = min(startpoint.x, endpoint.x) - 1;
1424     miny = min(startpoint.y, endpoint.y) - 1;
1425     minz = min(startpoint.z, endpoint.z) - 1;
1426     maxx = max(startpoint.x, endpoint.x) + 1;
1427     maxy = max(startpoint.y, endpoint.y) + 1;
1428     maxz = max(startpoint.z, endpoint.z) + 1;
1429
1430     if (what != 1000) {
1431         if (     objects.position[what].x > minx - objects.model[what].boundingsphereradius &&
1432                  objects.position[what].x < maxx + objects.model[what].boundingsphereradius &&
1433                  objects.position[what].y > miny - objects.model[what].boundingsphereradius &&
1434                  objects.position[what].y < maxy + objects.model[what].boundingsphereradius &&
1435                  objects.position[what].z > minz - objects.model[what].boundingsphereradius &&
1436                  objects.position[what].z < maxz + objects.model[what].boundingsphereradius) {
1437             if (     objects.type[what] != treeleavestype &&
1438                      objects.type[what] != bushtype &&
1439                      objects.type[what] != firetype) {
1440                 colviewer = startpoint;
1441                 coltarget = endpoint;
1442                 //FIXME: i/what
1443                 if (objects.model[what].LineCheck(&colviewer, &coltarget, &colpoint, &objects.position[what], &objects.yaw[what]) != -1)return i;
1444             }
1445         }
1446     }
1447
1448     if (what == 1000)if (terrain.lineTerrain(startpoint, endpoint, &colpoint) != -1)return 1000;
1449
1450     return -1;
1451 }
1452
1453 void Setenvironment(int which)
1454 {
1455     LOGFUNC;
1456
1457     LOG(" Setting environment...");
1458
1459     float temptexdetail;
1460     environment = which;
1461
1462     pause_sound(stream_snowtheme);
1463     pause_sound(stream_grasstheme);
1464     pause_sound(stream_deserttheme);
1465     pause_sound(stream_wind);
1466     pause_sound(stream_desertambient);
1467
1468
1469     if (environment == snowyenvironment) {
1470         windvector = 0;
1471         windvector.z = 3;
1472         if (ambientsound)
1473             emit_stream_np(stream_wind);
1474
1475         objects.treetextureptr.load(":Data:Textures:snowtree.png", 0, 1);
1476         objects.bushtextureptr.load(":Data:Textures:bushsnow.png", 0, 1);
1477         objects.rocktextureptr.load(":Data:Textures:bouldersnow.jpg", 1, 0);
1478         objects.boxtextureptr.load(":Data:Textures:snowbox.jpg", 1, 0);
1479
1480         footstepsound = footstepsn1;
1481         footstepsound2 = footstepsn2;
1482         footstepsound3 = footstepst1;
1483         footstepsound4 = footstepst2;
1484
1485         terraintexture.load(":Data:Textures:snow.jpg", 1, 0);
1486         terraintexture2.load(":Data:Textures:rock.jpg", 1, 0);
1487
1488         //LoadTexture(":Data:Textures:detailgrain.png",&terraintexture3,1);
1489
1490
1491
1492
1493         temptexdetail = texdetail;
1494         if (texdetail > 1)texdetail = 4;
1495         skybox->load(   ":Data:Textures:Skybox(snow):Front.jpg",
1496                         ":Data:Textures:Skybox(snow):Left.jpg",
1497                         ":Data:Textures:Skybox(snow):Back.jpg",
1498                         ":Data:Textures:Skybox(snow):Right.jpg",
1499                         ":Data:Textures:Skybox(snow):Up.jpg",
1500                         ":Data:Textures:Skybox(snow):Down.jpg");
1501
1502
1503
1504
1505         texdetail = temptexdetail;
1506     } else if (environment == desertenvironment) {
1507         windvector = 0;
1508         windvector.z = 2;
1509         objects.treetextureptr.load(":Data:Textures:deserttree.png", 0, 1);
1510         objects.bushtextureptr.load(":Data:Textures:bushdesert.png", 0, 1);
1511         objects.rocktextureptr.load(":Data:Textures:boulderdesert.jpg", 1, 0);
1512         objects.boxtextureptr.load(":Data:Textures:desertbox.jpg", 1, 0);
1513
1514
1515         if (ambientsound)
1516             emit_stream_np(stream_desertambient);
1517
1518         footstepsound = footstepsn1;
1519         footstepsound2 = footstepsn2;
1520         footstepsound3 = footstepsn1;
1521         footstepsound4 = footstepsn2;
1522
1523         terraintexture.load(":Data:Textures:sand.jpg", 1, 0);
1524         terraintexture2.load(":Data:Textures:sandslope.jpg", 1, 0);
1525
1526         //LoadTexture(":Data:Textures:detailgrain.png",&terraintexture3,1);
1527
1528
1529
1530         temptexdetail = texdetail;
1531         if (texdetail > 1)texdetail = 4;
1532         skybox->load(   ":Data:Textures:Skybox(sand):Front.jpg",
1533                         ":Data:Textures:Skybox(sand):Left.jpg",
1534                         ":Data:Textures:Skybox(sand):Back.jpg",
1535                         ":Data:Textures:Skybox(sand):Right.jpg",
1536                         ":Data:Textures:Skybox(sand):Up.jpg",
1537                         ":Data:Textures:Skybox(sand):Down.jpg");
1538
1539
1540
1541
1542         texdetail = temptexdetail;
1543     } else if (environment == grassyenvironment) {
1544         windvector = 0;
1545         windvector.z = 2;
1546         objects.treetextureptr.load(":Data:Textures:tree.png", 0, 1);
1547         objects.bushtextureptr.load(":Data:Textures:bush.png", 0, 1);
1548         objects.rocktextureptr.load(":Data:Textures:boulder.jpg", 1, 0);
1549         objects.boxtextureptr.load(":Data:Textures:grassbox.jpg", 1, 0);
1550
1551         if (ambientsound)
1552             emit_stream_np(stream_wind, 100.);
1553
1554         footstepsound = footstepgr1;
1555         footstepsound2 = footstepgr2;
1556         footstepsound3 = footstepst1;
1557         footstepsound4 = footstepst2;
1558
1559         terraintexture.load(":Data:Textures:grassdirt.jpg", 1, 0);
1560         terraintexture2.load(":Data:Textures:mossrock.jpg", 1, 0);
1561
1562         //LoadTexture(":Data:Textures:detail.png",&terraintexture3,1);
1563
1564
1565
1566         temptexdetail = texdetail;
1567         if (texdetail > 1)texdetail = 4;
1568         skybox->load(   ":Data:Textures:Skybox(grass):Front.jpg",
1569                         ":Data:Textures:Skybox(grass):Left.jpg",
1570                         ":Data:Textures:Skybox(grass):Back.jpg",
1571                         ":Data:Textures:Skybox(grass):Right.jpg",
1572                         ":Data:Textures:Skybox(grass):Up.jpg",
1573                         ":Data:Textures:Skybox(grass):Down.jpg");
1574
1575
1576
1577         texdetail = temptexdetail;
1578     }
1579     temptexdetail = texdetail;
1580     texdetail = 1;
1581     terrain.load(":Data:Textures:heightmap.png");
1582
1583     texdetail = temptexdetail;
1584 }
1585
1586 void LoadCampaign()
1587 {
1588     if (!accountactive)
1589         return;
1590     ifstream ipstream(ConvertFileName((":Data:Campaigns:" + accountactive->getCurrentCampaign() + ".txt").c_str()));
1591     ipstream.ignore(256, ':');
1592     int numlevels;
1593     ipstream >> numlevels;
1594     campaignlevels.clear();
1595     for (int i = 0; i < numlevels; i++) {
1596         CampaignLevel cl;
1597         ipstream >> cl;
1598         campaignlevels.push_back(cl);
1599     }
1600     ipstream.close();
1601
1602     ifstream test(ConvertFileName((":Data:Textures:" + accountactive->getCurrentCampaign() + ":World.png").c_str()));
1603     if (test.good()) {
1604         Mainmenuitems[7].load((":Data:Textures:" + accountactive->getCurrentCampaign() + ":World.png").c_str(), 0, 0);
1605     } else {
1606         Mainmenuitems[7].load(":Data:Textures:World.png", 0, 0);
1607     }
1608
1609     if (accountactive->getCampaignChoicesMade() == 0) {
1610         accountactive->setCampaignScore(0);
1611         accountactive->resetFasttime();
1612     }
1613 }
1614
1615 vector<string> ListCampaigns()
1616 {
1617     DIR *campaigns = opendir(ConvertFileName(":Data:Campaigns"));
1618     struct dirent *campaign = NULL;
1619     if (!campaigns) {
1620         perror("Problem while loading campaigns");
1621         cerr << "campaign folder was : " << ConvertFileName(":Data:Campaigns") << endl;
1622         exit(EXIT_FAILURE);
1623     }
1624     vector<string> campaignNames;
1625     while ((campaign = readdir(campaigns)) != NULL) {
1626         string name(campaign->d_name);
1627         if (name.length() < 5)
1628             continue;
1629         if (!name.compare(name.length() - 4, 4, ".txt")) {
1630             campaignNames.push_back(name.substr(0, name.length() - 4));
1631         }
1632     }
1633     closedir(campaigns);
1634     return campaignNames;
1635 }
1636
1637 void Loadlevel(int which)
1638 {
1639     stealthloading = 0;
1640     whichlevel = which;
1641
1642     if (which == -1) {
1643         tutoriallevel = -1;
1644         Loadlevel("tutorial");
1645     } else if (which >= 0 && which <= 15) {
1646         char buf[32];
1647         snprintf(buf, 32, "map%d", which + 1); // challenges
1648         Loadlevel(buf);
1649     } else
1650         Loadlevel("mapsave");
1651 }
1652
1653 void Loadlevel(const char *name)
1654 {
1655     int templength;
1656     float lamefloat;
1657     static const char *pfx = ":Data:Maps:";
1658     char *buf;
1659
1660     float headprop, legprop, armprop, bodyprop;
1661
1662     LOGFUNC;
1663
1664     LOG(std::string("Loading level...") + name);
1665
1666     if (!gameon)
1667         visibleloading = 1;
1668     if (stealthloading)
1669         visibleloading = 0;
1670     if (!stillloading)
1671         loadtime = 0;
1672     gamestarted = 1;
1673
1674     numenvsounds = 0;
1675
1676     if (tutoriallevel != -1)
1677         tutoriallevel = 0;
1678     else
1679         tutoriallevel = 1;
1680
1681     if (tutoriallevel == 1)
1682         tutorialstage = 0;
1683     if (tutorialstage == 0) {
1684         tutorialstagetime = 0;
1685         tutorialmaxtime = 1;
1686     }
1687     loadingstuff = 1;
1688     pause_sound(whooshsound);
1689     pause_sound(stream_firesound);
1690
1691     // Change the map filename into something that is os specific
1692     buf = (char*) alloca(strlen(pfx) + strlen(name) + 1);
1693     sprintf(buf, "%s%s", pfx, name);
1694     const char *FixedFN = ConvertFileName(buf);
1695
1696     int mapvers;
1697     FILE *tfile;
1698     //~ char* buff=getcwd(NULL,0);
1699     //~ cout << buff << " " << FixedFN << endl;
1700     //~ free(buff);
1701     tfile = fopen( FixedFN, "rb" );
1702     if (tfile) {
1703         pause_sound(stream_firesound);
1704         scoreadded = 0;
1705         windialogue = false;
1706         hostiletime = 0;
1707         won = 0;
1708
1709         animation[bounceidleanim].Load((char *)"Idle", middleheight, neutral);
1710
1711         numdialogues = 0;
1712
1713         for (int i = 0; i < 20; i++)
1714             dialoguegonethrough[i] = 0;
1715
1716         indialogue = -1;
1717         cameramode = 0;
1718
1719         damagedealt = 0;
1720         damagetaken = 0;
1721
1722         if (accountactive)
1723             difficulty = accountactive->getDifficulty();
1724
1725         numhotspots = 0;
1726         currenthotspot = -1;
1727         bonustime = 1;
1728
1729         skyboxtexture = 1;
1730         skyboxr = 1;
1731         skyboxg = 1;
1732         skyboxb = 1;
1733
1734         freeze = 0;
1735         winfreeze = 0;
1736
1737         for (int i = 0; i < 100; i++)
1738             bonusnum[i] = 0;
1739
1740         numfalls = 0;
1741         numflipfail = 0;
1742         numseen = 0;
1743         numstaffattack = 0;
1744         numswordattack = 0;
1745         numknifeattack = 0;
1746         numunarmedattack = 0;
1747         numescaped = 0;
1748         numflipped = 0;
1749         numwallflipped = 0;
1750         numthrowkill = 0;
1751         numafterkill = 0;
1752         numreversals = 0;
1753         numattacks = 0;
1754         maxalarmed = 0;
1755         numresponded = 0;
1756
1757         bonustotal = startbonustotal;
1758         bonus = 0;
1759         gameon = 1;
1760         changedelay = 0;
1761         if (console) {
1762             emit_sound_np(consolesuccesssound);
1763             freeze = 0;
1764             console = false;
1765         }
1766
1767         if (!stealthloading) {
1768             terrain.numdecals = 0;
1769             Sprite::deleteSprites();
1770             for (int i = 0; i < objects.numobjects; i++)
1771                 objects.model[i].numdecals = 0;
1772
1773             int j = objects.numobjects;
1774             for (int i = 0; i < j; i++) {
1775                 objects.DeleteObject(0);
1776                 if (visibleloading)
1777                     LoadingScreen();
1778             }
1779
1780             for (int i = 0; i < subdivision; i++)
1781                 for (int j = 0; j < subdivision; j++)
1782                     terrain.patchobjectnum[i][j] = 0;
1783             if (visibleloading)
1784                 LoadingScreen();
1785         }
1786
1787         weapons.clear();
1788
1789         funpackf(tfile, "Bi", &mapvers);
1790         if (mapvers >= 15)
1791             funpackf(tfile, "Bi", &indemo);
1792         else
1793             indemo = 0;
1794         if (mapvers >= 5)
1795             funpackf(tfile, "Bi", &maptype);
1796         else
1797             maptype = mapkilleveryone;
1798         if (mapvers >= 6)
1799             funpackf(tfile, "Bi", &hostile);
1800         else
1801             hostile = 1;
1802         if (mapvers >= 4)
1803             funpackf(tfile, "Bf Bf", &viewdistance, &fadestart);
1804         else {
1805             viewdistance = 100;
1806             fadestart = .6;
1807         }
1808         if (mapvers >= 2)
1809             funpackf(tfile, "Bb Bf Bf Bf", &skyboxtexture, &skyboxr, &skyboxg, &skyboxb);
1810         else {
1811             skyboxtexture = 1;
1812             skyboxr = 1;
1813             skyboxg = 1;
1814             skyboxb = 1;
1815         }
1816         if (mapvers >= 10)
1817             funpackf(tfile, "Bf Bf Bf", &skyboxlightr, &skyboxlightg, &skyboxlightb);
1818         else {
1819             skyboxlightr = skyboxr;
1820             skyboxlightg = skyboxg;
1821             skyboxlightb = skyboxb;
1822         }
1823         if (!stealthloading)
1824             funpackf(tfile, "Bf Bf Bf Bf Bf Bi", &player[0].coords.x, &player[0].coords.y, &player[0].coords.z, &player[0].yaw, &player[0].targetyaw, &player[0].num_weapons);
1825         if (stealthloading)
1826             funpackf(tfile, "Bf Bf Bf Bf Bf Bi", &lamefloat, &lamefloat, &lamefloat, &lamefloat, &lamefloat, &player[0].num_weapons);
1827         player[0].originalcoords = player[0].coords;
1828         if (player[0].num_weapons > 0 && player[0].num_weapons < 5)
1829             for (int j = 0; j < player[0].num_weapons; j++) {
1830                 player[0].weaponids[j] = weapons.size();
1831                 int type;
1832                 funpackf(tfile, "Bi", &type);
1833                 weapons.push_back(Weapon(type, 0));
1834             }
1835
1836         if (visibleloading)
1837             LoadingScreen();
1838
1839         funpackf(tfile, "Bf Bf Bf", &player[0].armorhead, &player[0].armorhigh, &player[0].armorlow);
1840         funpackf(tfile, "Bf Bf Bf", &player[0].protectionhead, &player[0].protectionhigh, &player[0].protectionlow);
1841         funpackf(tfile, "Bf Bf Bf", &player[0].metalhead, &player[0].metalhigh, &player[0].metallow);
1842         funpackf(tfile, "Bf Bf", &player[0].power, &player[0].speedmult);
1843
1844         funpackf(tfile, "Bi", &player[0].numclothes);
1845
1846         if (mapvers >= 9)
1847             funpackf(tfile, "Bi Bi", &player[0].whichskin, &player[0].creature);
1848         else {
1849             player[0].whichskin = 0;
1850             player[0].creature = rabbittype;
1851         }
1852
1853         player[0].lastattack = -1;
1854         player[0].lastattack2 = -1;
1855         player[0].lastattack3 = -1;
1856
1857         //dialogues
1858         if (mapvers >= 8) {
1859             funpackf(tfile, "Bi", &numdialogues);
1860             for (int k = 0; k < numdialogues; k++) {
1861                 funpackf(tfile, "Bi", &numdialogueboxes[k]);
1862                 funpackf(tfile, "Bi", &dialoguetype[k]);
1863                 for (int l = 0; l < 10; l++) {
1864                     funpackf(tfile, "Bf Bf Bf", &participantlocation[k][l].x, &participantlocation[k][l].y, &participantlocation[k][l].z);
1865                     funpackf(tfile, "Bf", &participantyaw[k][l]);
1866                 }
1867                 for (int l = 0; l < numdialogueboxes[k]; l++) {
1868                     funpackf(tfile, "Bi", &dialogueboxlocation[k][l]);
1869                     funpackf(tfile, "Bf", &dialogueboxcolor[k][l][0]);
1870                     funpackf(tfile, "Bf", &dialogueboxcolor[k][l][1]);
1871                     funpackf(tfile, "Bf", &dialogueboxcolor[k][l][2]);
1872                     funpackf(tfile, "Bi", &dialogueboxsound[k][l]);
1873
1874                     funpackf(tfile, "Bi", &templength);
1875                     if (templength > 128 || templength <= 0)
1876                         templength = 128;
1877                     int m;
1878                     for (m = 0; m < templength; m++) {
1879                         funpackf(tfile, "Bb", &dialoguetext[k][l][m]);
1880                         if (dialoguetext[k][l][m] == '\0')
1881                             break;
1882                     }
1883                     dialoguetext[k][l][m] = 0;
1884
1885                     funpackf(tfile, "Bi", &templength);
1886                     if (templength > 64 || templength <= 0)templength = 64;
1887                     for (m = 0; m < templength; m++) {
1888                         funpackf(tfile, "Bb", &dialoguename[k][l][m]);
1889                         if (dialoguename[k][l][m] == '\0')
1890                             break;
1891                     }
1892                     dialoguename[k][l][m] = 0;
1893                     funpackf(tfile, "Bf Bf Bf", &dialoguecamera[k][l].x, &dialoguecamera[k][l].y, &dialoguecamera[k][l].z);
1894                     funpackf(tfile, "Bi", &participantfocus[k][l]);
1895                     funpackf(tfile, "Bi", &participantaction[k][l]);
1896
1897                     for (m = 0; m < 10; m++)
1898                         funpackf(tfile, "Bf Bf Bf", &participantfacing[k][l][m].x, &participantfacing[k][l][m].y, &participantfacing[k][l][m].z);
1899
1900                     funpackf(tfile, "Bf Bf", &dialoguecamerayaw[k][l], &dialoguecamerapitch[k][l]);
1901                 }
1902             }
1903         } else
1904             numdialogues = 0;
1905
1906         for (int k = 0; k < player[0].numclothes; k++) {
1907             funpackf(tfile, "Bi", &templength);
1908             for (int l = 0; l < templength; l++)
1909                 funpackf(tfile, "Bb", &player[0].clothes[k][l]);
1910             player[0].clothes[k][templength] = '\0';
1911             funpackf(tfile, "Bf Bf Bf", &player[0].clothestintr[k], &player[0].clothestintg[k], &player[0].clothestintb[k]);
1912         }
1913
1914         funpackf(tfile, "Bi", &environment);
1915
1916         funpackf(tfile, "Bi", &objects.numobjects);
1917         for (int i = 0; i < objects.numobjects; i++) {
1918             funpackf(tfile, "Bi Bf Bf Bf Bf Bf Bf", &objects.type[i], &objects.yaw[i], &objects.pitch[i], &objects.position[i].x, &objects.position[i].y, &objects.position[i].z, &objects.scale[i]);
1919             if (objects.type[i] == treeleavestype)
1920                 objects.scale[i] = objects.scale[i - 1];
1921         }
1922
1923         if (mapvers >= 7) {
1924             funpackf(tfile, "Bi", &numhotspots);
1925             for (int i = 0; i < numhotspots; i++) {
1926                 funpackf(tfile, "Bi Bf Bf Bf Bf", &hotspottype[i], &hotspotsize[i], &hotspot[i].x, &hotspot[i].y, &hotspot[i].z);
1927                 funpackf(tfile, "Bi", &templength);
1928                 if (templength)
1929                     for (int l = 0; l < templength; l++)
1930                         funpackf(tfile, "Bb", &hotspottext[i][l]);
1931                 hotspottext[i][templength] = '\0';
1932                 if (hotspottype[i] == -111)
1933                     indemo = 1;
1934             }
1935         } else
1936             numhotspots = 0;
1937
1938         if (visibleloading)
1939             LoadingScreen();
1940
1941         if (!stealthloading) {
1942             objects.center = 0;
1943             for (int i = 0; i < objects.numobjects; i++)
1944                 objects.center += objects.position[i];
1945             objects.center /= objects.numobjects;
1946
1947
1948             if (visibleloading)
1949                 LoadingScreen();
1950
1951             float maxdistance = 0;
1952             float tempdist;
1953             //~ int whichclosest;
1954             for (int i = 0; i < objects.numobjects; i++) {
1955                 tempdist = distsq(&objects.center, &objects.position[i]);
1956                 if (tempdist > maxdistance) {
1957                     //~ whichclosest=i;
1958                     maxdistance = tempdist;
1959                 }
1960             }
1961             objects.radius = fast_sqrt(maxdistance);
1962         }
1963
1964         if (visibleloading)
1965             LoadingScreen();
1966         //mapcenter=objects.center;
1967         //mapradius=objects.radius;
1968
1969         funpackf(tfile, "Bi", &numplayers);
1970         int howmanyremoved = 0;
1971         bool removeanother = 0;
1972         if (numplayers > 1 && numplayers < maxplayers) {
1973             for (int i = 1; i < numplayers; i++) {
1974                 if (visibleloading)
1975                     LoadingScreen();
1976                 removeanother = 0;
1977
1978                 funpackf(tfile, "Bi Bi Bf Bf Bf Bi", &player[i - howmanyremoved].whichskin, &player[i - howmanyremoved].creature, &player[i - howmanyremoved].coords.x, &player[i - howmanyremoved].coords.y, &player[i - howmanyremoved].coords.z, &player[i - howmanyremoved].num_weapons);
1979                 if (mapvers >= 5)
1980                     funpackf(tfile, "Bi", &player[i - howmanyremoved].howactive);
1981                 else
1982                     player[i - howmanyremoved].howactive = typeactive;
1983                 if (mapvers >= 3)
1984                     funpackf(tfile, "Bf", &player[i - howmanyremoved].scale);
1985                 else
1986                     player[i - howmanyremoved].scale = -1;
1987                 if (mapvers >= 11)
1988                     funpackf(tfile, "Bb", &player[i - howmanyremoved].immobile);
1989                 else
1990                     player[i - howmanyremoved].immobile = 0;
1991                 if (mapvers >= 12)
1992                     funpackf(tfile, "Bf", &player[i - howmanyremoved].yaw);
1993                 else
1994                     player[i - howmanyremoved].yaw = 0;
1995                 player[i - howmanyremoved].targetyaw = player[i - howmanyremoved].yaw;
1996                 if (player[i - howmanyremoved].num_weapons < 0 || player[i - howmanyremoved].num_weapons > 5) {
1997                     removeanother = 1;
1998                     howmanyremoved++;
1999                 }
2000                 if (!removeanother) {
2001                     if (player[i - howmanyremoved].num_weapons > 0 && player[i - howmanyremoved].num_weapons < 5) {
2002                         for (int j = 0; j < player[i - howmanyremoved].num_weapons; j++) {
2003                             player[i - howmanyremoved].weaponids[j] = weapons.size();
2004                             int type;
2005                             funpackf(tfile, "Bi", &type);
2006                             weapons.push_back(Weapon(type, i));
2007                         }
2008                     }
2009                     funpackf(tfile, "Bi", &player[i - howmanyremoved].numwaypoints);
2010                     //player[i-howmanyremoved].numwaypoints=10;
2011                     for (int j = 0; j < player[i - howmanyremoved].numwaypoints; j++) {
2012                         funpackf(tfile, "Bf", &player[i - howmanyremoved].waypoints[j].x);
2013                         funpackf(tfile, "Bf", &player[i - howmanyremoved].waypoints[j].y);
2014                         funpackf(tfile, "Bf", &player[i - howmanyremoved].waypoints[j].z);
2015                         if (mapvers >= 5)
2016                             funpackf(tfile, "Bi", &player[i - howmanyremoved].waypointtype[j]);
2017                         else
2018                             player[i - howmanyremoved].waypointtype[j] = wpkeepwalking;
2019                     }
2020
2021                     funpackf(tfile, "Bi", &player[i - howmanyremoved].waypoint);
2022                     if (player[i - howmanyremoved].waypoint > player[i - howmanyremoved].numwaypoints - 1)
2023                         player[i - howmanyremoved].waypoint = 0;
2024
2025                     funpackf(tfile, "Bf Bf Bf", &player[i - howmanyremoved].armorhead, &player[i - howmanyremoved].armorhigh, &player[i - howmanyremoved].armorlow);
2026                     funpackf(tfile, "Bf Bf Bf", &player[i - howmanyremoved].protectionhead, &player[i - howmanyremoved].protectionhigh, &player[i - howmanyremoved].protectionlow);
2027                     funpackf(tfile, "Bf Bf Bf", &player[i - howmanyremoved].metalhead, &player[i - howmanyremoved].metalhigh, &player[i - howmanyremoved].metallow);
2028                     funpackf(tfile, "Bf Bf", &player[i - howmanyremoved].power, &player[i - howmanyremoved].speedmult);
2029
2030                     if (mapvers >= 4)
2031                         funpackf(tfile, "Bf Bf Bf Bf", &headprop, &bodyprop, &armprop, &legprop);
2032                     else {
2033                         headprop = 1;
2034                         bodyprop = 1;
2035                         armprop = 1;
2036                         legprop = 1;
2037                     }
2038                     if (player[i - howmanyremoved].creature == wolftype) {
2039                         player[i - howmanyremoved].proportionhead = 1.1 * headprop;
2040                         player[i - howmanyremoved].proportionbody = 1.1 * bodyprop;
2041                         player[i - howmanyremoved].proportionarms = 1.1 * armprop;
2042                         player[i - howmanyremoved].proportionlegs = 1.1 * legprop;
2043                     }
2044
2045                     if (player[i - howmanyremoved].creature == rabbittype) {
2046                         player[i - howmanyremoved].proportionhead = 1.2 * headprop;
2047                         player[i - howmanyremoved].proportionbody = 1.05 * bodyprop;
2048                         player[i - howmanyremoved].proportionarms = 1.00 * armprop;
2049                         player[i - howmanyremoved].proportionlegs = 1.1 * legprop;
2050                         player[i - howmanyremoved].proportionlegs.y = 1.05 * legprop;
2051                     }
2052
2053                     funpackf(tfile, "Bi", &player[i - howmanyremoved].numclothes);
2054                     if (player[i - howmanyremoved].numclothes) {
2055                         for (int k = 0; k < player[i - howmanyremoved].numclothes; k++) {
2056                             int templength;
2057                             funpackf(tfile, "Bi", &templength);
2058                             for (int l = 0; l < templength; l++)
2059                                 funpackf(tfile, "Bb", &player[i - howmanyremoved].clothes[k][l]);
2060                             player[i - howmanyremoved].clothes[k][templength] = '\0';
2061                             funpackf(tfile, "Bf Bf Bf", &player[i - howmanyremoved].clothestintr[k], &player[i - howmanyremoved].clothestintg[k], &player[i - howmanyremoved].clothestintb[k]);
2062                         }
2063                     }
2064                 }
2065             }
2066         }
2067         if (visibleloading)
2068             LoadingScreen();
2069
2070         numplayers -= howmanyremoved;
2071         funpackf(tfile, "Bi", &numpathpoints);
2072         if (numpathpoints > 30 || numpathpoints < 0)
2073             numpathpoints = 0;
2074         for (int j = 0; j < numpathpoints; j++) {
2075             funpackf(tfile, "Bf Bf Bf Bi", &pathpoint[j].x, &pathpoint[j].y, &pathpoint[j].z, &numpathpointconnect[j]);
2076             for (int k = 0; k < numpathpointconnect[j]; k++) {
2077                 funpackf(tfile, "Bi", &pathpointconnect[j][k]);
2078             }
2079         }
2080         if (visibleloading)
2081             LoadingScreen();
2082
2083         funpackf(tfile, "Bf Bf Bf Bf", &mapcenter.x, &mapcenter.y, &mapcenter.z, &mapradius);
2084
2085         SetUpLighting();
2086         if (environment != oldenvironment)
2087             Setenvironment(environment);
2088         oldenvironment = environment;
2089
2090         if (!stealthloading) {
2091             int j = objects.numobjects;
2092             objects.numobjects = 0;
2093             for (int i = 0; i < j; i++) {
2094                 objects.MakeObject(objects.type[i], objects.position[i], objects.yaw[i], objects.pitch[i], objects.scale[i]);
2095                 if (visibleloading)
2096                     LoadingScreen();
2097             }
2098
2099             terrain.DoShadows();
2100             if (visibleloading)
2101                 LoadingScreen();
2102             objects.DoShadows();
2103             if (visibleloading)
2104                 LoadingScreen();
2105         }
2106
2107         fclose(tfile);
2108
2109         if (numplayers > maxplayers - 1)
2110             numplayers = maxplayers - 1;
2111         for (int i = 0; i < numplayers; i++) {
2112             if (visibleloading)
2113                 LoadingScreen();
2114             player[i].burnt = 0;
2115             player[i].bled = 0;
2116             player[i].onfire = 0;
2117             if (i == 0 || player[i].scale < 0)
2118                 player[i].scale = .2;
2119             player[i].skeleton.free = 0;
2120             player[i].skeleton.id = i;
2121             if (i == 0 && mapvers < 9)
2122                 player[i].creature = rabbittype;
2123             if (player[i].creature != wolftype) {
2124                 player[i].skeleton.Load(
2125                     (char *)":Data:Skeleton:Basic Figure",
2126                     (char *)":Data:Skeleton:Basic Figurelow",
2127                     (char *)":Data:Skeleton:Rabbitbelt",
2128                     (char *)":Data:Models:Body.solid",
2129                     (char *)":Data:Models:Body2.solid",
2130                     (char *)":Data:Models:Body3.solid",
2131                     (char *)":Data:Models:Body4.solid",
2132                     (char *)":Data:Models:Body5.solid",
2133                     (char *)":Data:Models:Body6.solid",
2134                     (char *)":Data:Models:Body7.solid",
2135                     (char *)":Data:Models:Bodylow.solid",
2136                     (char *)":Data:Models:Belt.solid", 0);
2137             } else {
2138                 if (player[i].creature != wolftype) {
2139                     player[i].skeleton.Load(
2140                         (char *)":Data:Skeleton:Basic Figure",
2141                         (char *)":Data:Skeleton:Basic Figurelow",
2142                         (char *)":Data:Skeleton:Rabbitbelt",
2143                         (char *)":Data:Models:Body.solid",
2144                         (char *)":Data:Models:Body2.solid",
2145                         (char *)":Data:Models:Body3.solid",
2146                         (char *)":Data:Models:Body4.solid",
2147                         (char *)":Data:Models:Body5.solid",
2148                         (char *)":Data:Models:Body6.solid",
2149                         (char *)":Data:Models:Body7.solid",
2150                         (char *)":Data:Models:Bodylow.solid",
2151                         (char *)":Data:Models:Belt.solid", 1);
2152                     player[i].skeleton.drawmodelclothes.textureptr.load(":Data:Textures:Belt.png", 1, 1);
2153                 }
2154                 if (player[i].creature == wolftype) {
2155                     player[i].skeleton.Load(
2156                         (char *)":Data:Skeleton:Basic Figure Wolf",
2157                         (char *)":Data:Skeleton:Basic Figure Wolf Low",
2158                         (char *)":Data:Skeleton:Rabbitbelt",
2159                         (char *)":Data:Models:Wolf.solid",
2160                         (char *)":Data:Models:Wolf2.solid",
2161                         (char *)":Data:Models:Wolf3.solid",
2162                         (char *)":Data:Models:Wolf4.solid",
2163                         (char *)":Data:Models:Wolf5.solid",
2164                         (char *)":Data:Models:Wolf6.solid",
2165                         (char *)":Data:Models:Wolf7.solid",
2166                         (char *)":Data:Models:Wolflow.solid",
2167                         (char *)":Data:Models:Belt.solid", 0);
2168                 }
2169             }
2170
2171
2172             //~ int texsize;
2173             //~ texsize=512*512*3/texdetail/texdetail;
2174
2175             player[i].skeleton.drawmodel.textureptr.load(creatureskin[player[i].creature][player[i].whichskin], 1, &player[i].skeleton.skinText[0], &player[i].skeleton.skinsize);
2176
2177             if (player[i].numclothes) {
2178                 for (int j = 0; j < player[i].numclothes; j++) {
2179                     tintr = player[i].clothestintr[j];
2180                     tintg = player[i].clothestintg[j];
2181                     tintb = player[i].clothestintb[j];
2182                     AddClothes((char *)player[i].clothes[j], &player[i].skeleton.skinText[0]);
2183                 }
2184                 player[i].DoMipmaps();
2185             }
2186
2187             player[i].animCurrent = bounceidleanim;
2188             player[i].animTarget = bounceidleanim;
2189             player[i].frameCurrent = 0;
2190             player[i].frameTarget = 1;
2191             player[i].target = 0;
2192             player[i].speed = 1 + (float)(Random() % 100) / 1000;
2193             if (difficulty == 0)
2194                 player[i].speed -= .2;
2195             if (difficulty == 1)
2196                 player[i].speed -= .1;
2197
2198             player[i].velocity = 0;
2199             player[i].oldcoords = player[i].coords;
2200             player[i].realoldcoords = player[i].coords;
2201
2202             player[i].id = i;
2203             player[i].skeleton.id = i;
2204             player[i].updatedelay = 0;
2205             player[i].normalsupdatedelay = 0;
2206
2207             player[i].aitype = passivetype;
2208             player[i].madskills = 0;
2209
2210             if (i == 0) {
2211                 player[i].proportionhead = 1.2;
2212                 player[i].proportionbody = 1.05;
2213                 player[i].proportionarms = 1.00;
2214                 player[i].proportionlegs = 1.1;
2215                 player[i].proportionlegs.y = 1.05;
2216             }
2217             player[i].headless = 0;
2218             player[i].currentoffset = 0;
2219             player[i].targetoffset = 0;
2220
2221             player[i].damagetolerance = 200;
2222
2223             if (player[i].creature == wolftype) {
2224                 if (i == 0 || player[i].scale < 0)
2225                     player[i].scale = .23;
2226                 player[i].damagetolerance = 300;
2227             }
2228
2229             if (visibleloading)
2230                 LoadingScreen();
2231             if (cellophane) {
2232                 player[i].proportionhead.z = 0;
2233                 player[i].proportionbody.z = 0;
2234                 player[i].proportionarms.z = 0;
2235                 player[i].proportionlegs.z = 0;
2236             }
2237
2238             player[i].tempanimation.Load((char *)"Tempanim", 0, 0);
2239
2240             player[i].headmorphness = 0;
2241             player[i].targetheadmorphness = 1;
2242             player[i].headmorphstart = 0;
2243             player[i].headmorphend = 0;
2244
2245             player[i].pausetime = 0;
2246
2247             player[i].dead = 0;
2248             player[i].jumppower = 5;
2249             player[i].damage = 0;
2250             player[i].permanentdamage = 0;
2251             player[i].superpermanentdamage = 0;
2252
2253             player[i].forwardkeydown = 0;
2254             player[i].leftkeydown = 0;
2255             player[i].backkeydown = 0;
2256             player[i].rightkeydown = 0;
2257             player[i].jumpkeydown = 0;
2258             player[i].crouchkeydown = 0;
2259             player[i].throwkeydown = 0;
2260
2261             player[i].collided = -10;
2262             player[i].loaded = 1;
2263             player[i].bloodloss = 0;
2264             player[i].weaponactive = -1;
2265             player[i].weaponstuck = -1;
2266             player[i].bleeding = 0;
2267             player[i].deathbleeding = 0;
2268             player[i].stunned = 0;
2269             player[i].hasvictim = 0;
2270             player[i].wentforweapon = 0;
2271         }
2272
2273         player[0].aitype = playercontrolled;
2274         player[0].weaponactive = -1;
2275
2276         if (difficulty == 1)
2277             player[0].power = 1 / .9;
2278
2279         if (difficulty == 0)
2280             player[0].power = 1 / .8;
2281
2282         if (difficulty == 1)
2283             player[0].damagetolerance = 250;
2284         if (difficulty == 0)
2285             player[0].damagetolerance = 300;
2286         if (difficulty == 0)
2287             player[0].armorhead *= 1.5;
2288         if (difficulty == 0)
2289             player[0].armorhigh *= 1.5;
2290         if (difficulty == 0)
2291             player[0].armorlow *= 1.5;
2292         cameraloc = player[0].coords;
2293         cameraloc.y += 5;
2294         yaw = player[0].yaw;
2295
2296         hawkcoords = player[0].coords;
2297         hawkcoords.y += 30;
2298
2299         if (visibleloading)
2300             LoadingScreen();
2301         //~ for(int i=0;i<weapons.size();i++){
2302         //~ }
2303
2304         LOG("Starting background music...");
2305
2306         OPENAL_StopSound(OPENAL_ALL);
2307         if (environment == snowyenvironment) {
2308             if (ambientsound)
2309                 emit_stream_np(stream_wind);
2310         } else if (environment == desertenvironment) {
2311             if (ambientsound)
2312                 emit_stream_np(stream_desertambient);
2313         } else if (environment == grassyenvironment) {
2314             if (ambientsound)
2315                 emit_stream_np(stream_wind, 100.);
2316         }
2317         oldmusicvolume[0] = 0;
2318         oldmusicvolume[1] = 0;
2319         oldmusicvolume[2] = 0;
2320         oldmusicvolume[3] = 0;
2321
2322         if (!firstload)
2323             firstload = 1;
2324     } else {
2325         perror("Problem");
2326     }
2327     leveltime = 0;
2328     loadingstuff = 0;
2329     visibleloading = 0;
2330 }
2331
2332 void doTutorial()
2333 {
2334     if (tutorialstagetime > tutorialmaxtime) {
2335         tutorialstage++;
2336         tutorialsuccess = 0;
2337         if (tutorialstage <= 1) {
2338             canattack = 0;
2339             cananger = 0;
2340             reversaltrain = 0;
2341         }
2342         switch (tutorialstage) {
2343         case 1:
2344             tutorialmaxtime = 5;
2345             break;
2346         case 2:
2347             tutorialmaxtime = 2;
2348             break;
2349         case 3:
2350             tutorialmaxtime = 600;
2351             break;
2352         case 4:
2353             tutorialmaxtime = 1000;
2354             break;
2355         case 5:
2356             tutorialmaxtime = 600;
2357             break;
2358         case 6:
2359             tutorialmaxtime = 600;
2360             break;
2361         case 7:
2362             tutorialmaxtime = 600;
2363             break;
2364         case 8:
2365             tutorialmaxtime = 600;
2366             break;
2367         case 9:
2368             tutorialmaxtime = 600;
2369             break;
2370         case 10:
2371             tutorialmaxtime = 2;
2372             break;
2373         case 11:
2374             tutorialmaxtime = 1000;
2375             break;
2376         case 12:
2377             tutorialmaxtime = 1000;
2378             break;
2379         case 13:
2380             tutorialmaxtime = 2;
2381             break;
2382         case 14: {
2383             tutorialmaxtime = 3;
2384
2385             XYZ temp, temp2;
2386
2387             temp.x = 1011;
2388             temp.y = 84;
2389             temp.z = 491;
2390             temp2.x = 1025;
2391             temp2.y = 75;
2392             temp2.z = 447;
2393
2394             player[1].coords = (temp + temp2) / 2;
2395
2396             emit_sound_at(fireendsound, player[1].coords);
2397
2398             for (int i = 0; i < player[1].skeleton.num_joints; i++) {
2399                 if (Random() % 2 == 0) {
2400                     if (!player[1].skeleton.free)temp2 = (player[1].coords - player[1].oldcoords) / multiplier / 2; //velocity/2;
2401                     if (player[1].skeleton.free)temp2 = player[1].skeleton.joints[i].velocity * player[1].scale / 2;
2402                     if (!player[1].skeleton.free)temp = DoRotation(DoRotation(DoRotation(player[1].skeleton.joints[i].position, 0, 0, player[1].tilt), player[1].tilt2, 0, 0), 0, player[1].yaw, 0) * player[1].scale + player[1].coords;
2403                     if (player[1].skeleton.free)temp = player[1].skeleton.joints[i].position * player[1].scale + player[1].coords;
2404                     Sprite::MakeSprite(breathsprite, temp, temp2, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1);
2405                 }
2406             }
2407         }
2408         break;
2409         case 15:
2410             tutorialmaxtime = 500;
2411             break;
2412         case 16:
2413             tutorialmaxtime = 500;
2414             break;
2415         case 17:
2416             tutorialmaxtime = 500;
2417             break;
2418         case 18:
2419             tutorialmaxtime = 500;
2420             break;
2421         case 19:
2422             tutorialstage = 20;
2423             //tutorialmaxtime=500;
2424             break;
2425         case 20:
2426             tutorialmaxtime = 500;
2427             break;
2428         case 21:
2429             tutorialmaxtime = 500;
2430             if (bonus == cannon) {
2431                 bonus = Slicebonus;
2432                 againbonus = 1;
2433             } else againbonus = 0;
2434             break;
2435         case 22:
2436             tutorialmaxtime = 500;
2437             break;
2438         case 23:
2439             tutorialmaxtime = 500;
2440             break;
2441         case 24:
2442             tutorialmaxtime = 500;
2443             break;
2444         case 25:
2445             tutorialmaxtime = 500;
2446             break;
2447         case 26:
2448             tutorialmaxtime = 2;
2449             break;
2450         case 27:
2451             tutorialmaxtime = 4;
2452             reversaltrain = 1;
2453             cananger = 1;
2454             player[1].aitype = attacktypecutoff;
2455             break;
2456         case 28:
2457             tutorialmaxtime = 400;
2458             break;
2459         case 29:
2460             tutorialmaxtime = 400;
2461             player[0].escapednum = 0;
2462             break;
2463         case 30:
2464             tutorialmaxtime = 4;
2465             reversaltrain = 0;
2466             cananger = 0;
2467             player[1].aitype = passivetype;
2468             break;
2469         case 31:
2470             tutorialmaxtime = 13;
2471             break;
2472         case 32:
2473             tutorialmaxtime = 8;
2474             break;
2475         case 33:
2476             tutorialmaxtime = 400;
2477             cananger = 1;
2478             canattack = 1;
2479             player[1].aitype = attacktypecutoff;
2480             break;
2481         case 34:
2482             tutorialmaxtime = 400;
2483             break;
2484         case 35:
2485             tutorialmaxtime = 400;
2486             break;
2487         case 36:
2488             tutorialmaxtime = 2;
2489             reversaltrain = 0;
2490             cananger = 0;
2491             player[1].aitype = passivetype;
2492             break;
2493         case 37:
2494             damagedealt = 0;
2495             damagetaken = 0;
2496             tutorialmaxtime = 50;
2497             cananger = 1;
2498             canattack = 1;
2499             player[1].aitype = attacktypecutoff;
2500             break;
2501         case 38:
2502             tutorialmaxtime = 4;
2503             canattack = 0;
2504             cananger = 0;
2505             player[1].aitype = passivetype;
2506             break;
2507         case 39: {
2508             XYZ temp, temp2;
2509
2510             temp.x = 1011;
2511             temp.y = 84;
2512             temp.z = 491;
2513             temp2.x = 1025;
2514             temp2.y = 75;
2515             temp2.z = 447;
2516
2517             Weapon w(knife, -1);
2518             w.position = (temp + temp2) / 2;
2519             w.tippoint = (temp + temp2) / 2;
2520
2521             w.velocity = 0.1;
2522             w.tipvelocity = 0.1;
2523             w.missed = 1;
2524             w.hitsomething = 0;
2525             w.freetime = 0;
2526             w.firstfree = 1;
2527             w.physics = 1;
2528
2529             weapons.push_back(w);
2530         }
2531         break;
2532         case 40:
2533             tutorialmaxtime = 300;
2534             break;
2535         case 41:
2536             tutorialmaxtime = 300;
2537             break;
2538         case 42:
2539             tutorialmaxtime = 8;
2540             break;
2541         case 43:
2542             tutorialmaxtime = 300;
2543             break;
2544         case 44:
2545             weapons[0].owner = 1;
2546             player[0].weaponactive = -1;
2547             player[0].num_weapons = 0;
2548             player[1].weaponactive = 0;
2549             player[1].num_weapons = 1;
2550             player[1].weaponids[0] = 0;
2551
2552             cananger = 1;
2553             canattack = 1;
2554             player[1].aitype = attacktypecutoff;
2555
2556             tutorialmaxtime = 300;
2557             break;
2558         case 45:
2559             weapons[0].owner = 1;
2560             player[0].weaponactive = -1;
2561             player[0].num_weapons = 0;
2562             player[1].weaponactive = 0;
2563             player[1].num_weapons = 1;
2564             player[1].weaponids[0] = 0;
2565
2566             tutorialmaxtime = 300;
2567             break;
2568         case 46:
2569             weapons[0].owner = 1;
2570             player[0].weaponactive = -1;
2571             player[0].num_weapons = 0;
2572             player[1].weaponactive = 0;
2573             player[1].num_weapons = 1;
2574             player[1].weaponids[0] = 0;
2575
2576             weapons[0].setType(sword);
2577
2578             tutorialmaxtime = 300;
2579             break;
2580         case 47: {
2581             tutorialmaxtime = 10;
2582
2583             XYZ temp, temp2;
2584
2585             temp.x = 1011;
2586             temp.y = 84;
2587             temp.z = 491;
2588             temp2.x = 1025;
2589             temp2.y = 75;
2590             temp2.z = 447;
2591
2592             Weapon w(sword, -1);
2593             w.position = (temp + temp2) / 2;
2594             w.tippoint = (temp + temp2) / 2;
2595
2596             w.velocity = 0.1;
2597             w.tipvelocity = 0.1;
2598             w.missed = 1;
2599             w.hitsomething = 0;
2600             w.freetime = 0;
2601             w.firstfree = 1;
2602             w.physics = 1;
2603
2604             weapons.push_back(w);
2605
2606             weapons[0].owner = 1;
2607             weapons[1].owner = 0;
2608             player[0].weaponactive = 0;
2609             player[0].num_weapons = 1;
2610             player[0].weaponids[0] = 1;
2611             player[1].weaponactive = 0;
2612             player[1].num_weapons = 1;
2613             player[1].weaponids[0] = 0;
2614
2615         }
2616         break;
2617         case 48:
2618             canattack = 0;
2619             cananger = 0;
2620             player[1].aitype = passivetype;
2621
2622             tutorialmaxtime = 15;
2623
2624             weapons[0].owner = 1;
2625             weapons[1].owner = 0;
2626             player[0].weaponactive = 0;
2627             player[0].num_weapons = 1;
2628             player[0].weaponids[0] = 1;
2629             player[1].weaponactive = 0;
2630             player[1].num_weapons = 1;
2631             player[1].weaponids[0] = 0;
2632
2633             if (player[0].weaponactive != -1)
2634                 weapons[player[0].weaponids[player[0].weaponactive]].setType(staff);
2635             else
2636                 weapons[0].setType(staff);
2637             break;
2638         case 49:
2639             canattack = 0;
2640             cananger = 0;
2641             player[1].aitype = passivetype;
2642
2643             tutorialmaxtime = 200;
2644
2645             weapons[1].position = 1000;
2646             weapons[1].tippoint = 1000;
2647
2648             weapons[0].setType(knife);
2649
2650             weapons[0].owner = 0;
2651             player[1].weaponactive = -1;
2652             player[1].num_weapons = 0;
2653             player[0].weaponactive = 0;
2654             player[0].num_weapons = 1;
2655             player[0].weaponids[0] = 0;
2656
2657             break;
2658         case 50: {
2659             tutorialmaxtime = 8;
2660
2661             XYZ temp, temp2;
2662             emit_sound_at(fireendsound, player[1].coords);
2663
2664             for (int i = 0; i < player[1].skeleton.num_joints; i++) {
2665                 if (Random() % 2 == 0) {
2666                     if (!player[1].skeleton.free)temp2 = (player[1].coords - player[1].oldcoords) / multiplier / 2; //velocity/2;
2667                     if (player[1].skeleton.free)temp2 = player[1].skeleton.joints[i].velocity * player[1].scale / 2;
2668                     if (!player[1].skeleton.free)temp = DoRotation(DoRotation(DoRotation(player[1].skeleton.joints[i].position, 0, 0, player[1].tilt), player[1].tilt2, 0, 0), 0, player[1].yaw, 0) * player[1].scale + player[1].coords;
2669                     if (player[1].skeleton.free)temp = player[1].skeleton.joints[i].position * player[1].scale + player[1].coords;
2670                     Sprite::MakeSprite(breathsprite, temp, temp2, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1);
2671                 }
2672             }
2673
2674             player[1].num_weapons = 0;
2675             player[1].weaponstuck = -1;
2676             player[1].weaponactive = -1;
2677
2678             weapons.clear();
2679         }
2680         break;
2681         case 51:
2682             tutorialmaxtime = 80000;
2683             break;
2684         default:
2685             break;
2686         }
2687         if (tutorialstage <= 51)tutorialstagetime = 0;
2688     }
2689
2690     //Tutorial success
2691     if (tutorialstagetime < tutorialmaxtime - 3) {
2692         switch (tutorialstage) {
2693         case 3:
2694             if (deltah || deltav)tutorialsuccess += multiplier;
2695             break;
2696         case 4:
2697             if (player[0].forwardkeydown || player[0].backkeydown || player[0].leftkeydown || player[0].rightkeydown)tutorialsuccess += multiplier;
2698             break;
2699         case 5:
2700             if (player[0].jumpkeydown)tutorialsuccess = 1;
2701             break;
2702         case 6:
2703             if (player[0].isCrouch())tutorialsuccess = 1;
2704             break;
2705         case 7:
2706             if (player[0].animTarget == rollanim)tutorialsuccess = 1;
2707             break;
2708         case 8:
2709             if (player[0].animTarget == sneakanim)tutorialsuccess += multiplier;
2710             break;
2711         case 9:
2712             if (player[0].animTarget == rabbitrunninganim || player[0].animTarget == wolfrunninganim)tutorialsuccess += multiplier;
2713             break;
2714         case 11:
2715             if (player[0].isWallJump())tutorialsuccess = 1;
2716             break;
2717         case 12:
2718             if (player[0].animTarget == flipanim)tutorialsuccess = 1;
2719             break;
2720         case 15:
2721             if (player[0].animTarget == upunchanim || player[0].animTarget == winduppunchanim)tutorialsuccess = 1;
2722             break;
2723         case 16:
2724             if (player[0].animTarget == winduppunchanim)tutorialsuccess = 1;
2725             break;
2726         case 17:
2727             if (player[0].animTarget == spinkickanim)tutorialsuccess = 1;
2728             break;
2729         case 18:
2730             if (player[0].animTarget == sweepanim)tutorialsuccess = 1;
2731             break;
2732         case 19:
2733             if (player[0].animTarget == dropkickanim)tutorialsuccess = 1;
2734             break;
2735         case 20:
2736             if (player[0].animTarget == rabbitkickanim)tutorialsuccess = 1;
2737             break;
2738         case 21:
2739             if (bonus == cannon)tutorialsuccess = 1;
2740             break;
2741         case 22:
2742             if (bonus == spinecrusher)tutorialsuccess = 1;
2743             break;
2744         case 23:
2745             if (player[0].animTarget == walljumprightkickanim || player[0].animTarget == walljumpleftkickanim)tutorialsuccess = 1;
2746             break;
2747         case 24:
2748             if (player[0].animTarget == rabbittacklinganim)tutorialsuccess = 1;
2749             break;
2750         case 25:
2751             if (player[0].animTarget == backhandspringanim)tutorialsuccess = 1;
2752             break;
2753         case 28:
2754             if (animation[player[0].animTarget].attack == reversed && player[0].feint)tutorialsuccess = 1;
2755             break;
2756         case 29:
2757             if (player[0].escapednum == 2) {
2758                 tutorialsuccess = 1;
2759                 reversaltrain = 0;
2760                 cananger = 0;
2761                 player[1].aitype = passivetype;
2762             }
2763             break;
2764         case 33:
2765             if (animation[player[0].animTarget].attack == reversal)tutorialsuccess = 1;
2766             break;
2767         case 34:
2768             if (animation[player[0].animTarget].attack == reversal)tutorialsuccess = 1;
2769             break;
2770         case 35:
2771             if (animation[player[0].animTarget].attack == reversal) {
2772                 tutorialsuccess = 1;
2773                 reversaltrain = 0;
2774                 cananger = 0;
2775                 player[1].aitype = passivetype;
2776             }
2777             break;
2778         case 40:
2779             if (player[0].num_weapons > 0)tutorialsuccess = 1;
2780             break;
2781         case 41:
2782             if (player[0].weaponactive == -1 && player[0].num_weapons > 0)tutorialsuccess = 1;
2783             break;
2784         case 43:
2785             if (player[0].animTarget == knifeslashstartanim)tutorialsuccess = 1;
2786             break;
2787         case 44:
2788             if (animation[player[0].animTarget].attack == reversal)tutorialsuccess = 1;
2789             break;
2790         case 45:
2791             if (animation[player[0].animTarget].attack == reversal)tutorialsuccess = 1;
2792             break;
2793         case 46:
2794             if (animation[player[0].animTarget].attack == reversal)tutorialsuccess = 1;
2795             break;
2796         case 49:
2797             if (player[1].weaponstuck != -1)tutorialsuccess = 1;
2798             break;
2799         default:
2800             break;
2801         }
2802         if (tutorialsuccess >= 1)tutorialstagetime = tutorialmaxtime - 3;
2803
2804
2805         if (tutorialstagetime == tutorialmaxtime - 3) {
2806             emit_sound_np(consolesuccesssound);
2807         }
2808
2809         if (tutorialsuccess >= 1) {
2810             if (tutorialstage == 34 || tutorialstage == 35)
2811                 tutorialstagetime = tutorialmaxtime - 1;
2812         }
2813     }
2814
2815     if (tutorialstage < 14 || tutorialstage >= 50) {
2816         player[1].coords.y = 300;
2817         player[1].velocity = 0;
2818     }
2819 }
2820
2821 void doDebugKeys()
2822 {
2823     float headprop, bodyprop, armprop, legprop;
2824     if (debugmode) {
2825         if (Input::isKeyPressed(SDLK_h)) {
2826             player[0].damagetolerance = 200000;
2827             player[0].damage = 0;
2828             player[0].burnt = 0;
2829             player[0].permanentdamage = 0;
2830             player[0].superpermanentdamage = 0;
2831         }
2832
2833         if (Input::isKeyPressed(SDLK_j)) {
2834             environment++;
2835             if (environment > 2)
2836                 environment = 0;
2837             Setenvironment(environment);
2838         }
2839
2840         if (Input::isKeyPressed(SDLK_c)) {
2841             cameramode = 1 - cameramode;
2842         }
2843
2844         if (Input::isKeyPressed(SDLK_x) && !Input::isKeyDown(SDLK_LSHIFT)) {
2845             if (player[0].num_weapons > 0) {
2846                 if (weapons[player[0].weaponids[0]].getType() == sword)
2847                     weapons[player[0].weaponids[0]].setType(staff);
2848                 else if (weapons[player[0].weaponids[0]].getType() == staff)
2849                     weapons[player[0].weaponids[0]].setType(knife);
2850                 else
2851                     weapons[player[0].weaponids[0]].setType(sword);
2852             }
2853         }
2854
2855         if (Input::isKeyPressed(SDLK_x) && Input::isKeyDown(SDLK_LSHIFT)) {
2856             int closest = findClosestPlayer();
2857             if (closest >= 0) {
2858                 if (player[closest].num_weapons) {
2859                     if (weapons[player[closest].weaponids[0]].getType() == sword)
2860                         weapons[player[closest].weaponids[0]].setType(staff);
2861                     else if (weapons[player[closest].weaponids[0]].getType() == staff)
2862                         weapons[player[closest].weaponids[0]].setType(knife);
2863                     else
2864                         weapons[player[closest].weaponids[0]].setType(sword);
2865                 }
2866                 if (!player[closest].num_weapons) {
2867                     player[closest].weaponids[0] = weapons.size();
2868
2869                     weapons.push_back(Weapon(knife, closest));
2870
2871                     player[closest].num_weapons = 1;
2872                 }
2873             }
2874         }
2875
2876         if (Input::isKeyDown(SDLK_u)) {
2877             int closest = findClosestPlayer();
2878             if (closest >= 0) {
2879                 player[closest].yaw += multiplier * 50;
2880                 player[closest].targetyaw = player[closest].yaw;
2881             }
2882         }
2883
2884
2885         if (Input::isKeyPressed(SDLK_o) && !Input::isKeyDown(SDLK_LSHIFT)) {
2886             int closest = findClosestPlayer();
2887             if (Input::isKeyDown(SDLK_LCTRL))
2888                 closest = 0;
2889
2890             if (closest >= 0) {
2891                 player[closest].whichskin++;
2892                 if (player[closest].whichskin > 9)
2893                     player[closest].whichskin = 0;
2894                 if (player[closest].whichskin > 2 && player[closest].creature == wolftype)
2895                     player[closest].whichskin = 0;
2896
2897                 player[closest].skeleton.drawmodel.textureptr.load(creatureskin[player[closest].creature][player[closest].whichskin], 1,
2898                         &player[closest].skeleton.skinText[0], &player[closest].skeleton.skinsize);
2899             }
2900
2901             if (player[closest].numclothes) {
2902                 for (int i = 0; i < player[closest].numclothes; i++) {
2903                     tintr = player[closest].clothestintr[i];
2904                     tintg = player[closest].clothestintg[i];
2905                     tintb = player[closest].clothestintb[i];
2906                     AddClothes((char *)player[closest].clothes[i], &player[closest].skeleton.skinText[0]);
2907                 }
2908                 player[closest].DoMipmaps();
2909             }
2910         }
2911
2912         if (Input::isKeyPressed(SDLK_o) && Input::isKeyDown(SDLK_LSHIFT)) {
2913             int closest = findClosestPlayer();
2914             if (closest >= 0) {
2915                 if (player[closest].creature == wolftype) {
2916                     headprop = player[closest].proportionhead.x / 1.1;
2917                     bodyprop = player[closest].proportionbody.x / 1.1;
2918                     armprop = player[closest].proportionarms.x / 1.1;
2919                     legprop = player[closest].proportionlegs.x / 1.1;
2920                 }
2921
2922                 if (player[closest].creature == rabbittype) {
2923                     headprop = player[closest].proportionhead.x / 1.2;
2924                     bodyprop = player[closest].proportionbody.x / 1.05;
2925                     armprop = player[closest].proportionarms.x / 1.00;
2926                     legprop = player[closest].proportionlegs.x / 1.1;
2927                 }
2928
2929
2930                 if (player[closest].creature == rabbittype) {
2931                     player[closest].skeleton.id = closest;
2932                     player[closest].skeleton.Load((char *)":Data:Skeleton:Basic Figure Wolf", (char *)":Data:Skeleton:Basic Figure Wolf Low", (char *)":Data:Skeleton:Rabbitbelt", (char *)":Data:Models:Wolf.solid", (char *)":Data:Models:Wolf2.solid", (char *)":Data:Models:Wolf3.solid", (char *)":Data:Models:Wolf4.solid", (char *)":Data:Models:Wolf5.solid", (char *)":Data:Models:Wolf6.solid", (char *)":Data:Models:Wolf7.solid", (char *)":Data:Models:Wolflow.solid", (char *)":Data:Models:Belt.solid", 0);
2933                     player[closest].skeleton.drawmodel.textureptr.load(":Data:Textures:Wolf.jpg", 1, &player[closest].skeleton.skinText[closest], &player[closest].skeleton.skinsize);
2934                     player[closest].whichskin = 0;
2935                     player[closest].creature = wolftype;
2936
2937                     player[closest].proportionhead = 1.1;
2938                     player[closest].proportionbody = 1.1;
2939                     player[closest].proportionarms = 1.1;
2940                     player[closest].proportionlegs = 1.1;
2941                     player[closest].proportionlegs.y = 1.1;
2942                     player[closest].scale = .23 * 5 * player[0].scale;
2943
2944                     player[closest].damagetolerance = 300;
2945                 } else {
2946                     player[closest].skeleton.id = closest;
2947                     player[closest].skeleton.Load((char *)":Data:Skeleton:Basic Figure", (char *)":Data:Skeleton:Basic Figurelow", (char *)":Data:Skeleton:Rabbitbelt", (char *)":Data:Models:Body.solid", (char *)":Data:Models:Body2.solid", (char *)":Data:Models:Body3.solid", (char *)":Data:Models:Body4.solid", (char *)":Data:Models:Body5.solid", (char *)":Data:Models:Body6.solid", (char *)":Data:Models:Body7.solid", (char *)":Data:Models:Bodylow.solid", (char *)":Data:Models:Belt.solid", 1);
2948                     player[closest].skeleton.drawmodel.textureptr.load(":Data:Textures:Fur3.jpg", 1, &player[closest].skeleton.skinText[0], &player[closest].skeleton.skinsize);
2949                     player[closest].whichskin = 0;
2950                     player[closest].creature = rabbittype;
2951
2952                     player[closest].proportionhead = 1.2;
2953                     player[closest].proportionbody = 1.05;
2954                     player[closest].proportionarms = 1.00;
2955                     player[closest].proportionlegs = 1.1;
2956                     player[closest].proportionlegs.y = 1.05;
2957                     player[closest].scale = .2 * 5 * player[0].scale;
2958
2959                     player[closest].damagetolerance = 200;
2960                 }
2961
2962                 if (player[closest].creature == wolftype) {
2963                     player[closest].proportionhead = 1.1 * headprop;
2964                     player[closest].proportionbody = 1.1 * bodyprop;
2965                     player[closest].proportionarms = 1.1 * armprop;
2966                     player[closest].proportionlegs = 1.1 * legprop;
2967                 }
2968
2969                 if (player[closest].creature == rabbittype) {
2970                     player[closest].proportionhead = 1.2 * headprop;
2971                     player[closest].proportionbody = 1.05 * bodyprop;
2972                     player[closest].proportionarms = 1.00 * armprop;
2973                     player[closest].proportionlegs = 1.1 * legprop;
2974                     player[closest].proportionlegs.y = 1.05 * legprop;
2975                 }
2976
2977             }
2978         }
2979
2980         if (Input::isKeyPressed(SDLK_b) && !Input::isKeyDown(SDLK_LSHIFT)) {
2981             slomo = 1 - slomo;
2982             slomodelay = 1000;
2983         }
2984
2985
2986         if (((Input::isKeyPressed(SDLK_i) && !Input::isKeyDown(SDLK_LSHIFT)))) {
2987             int closest = -1;
2988             float closestdist = std::numeric_limits<float>::max();
2989
2990             for (int i = 1; i < numplayers; i++) {
2991                 float distance = distsq(&player[i].coords, &player[0].coords);
2992                 if (!player[i].headless)
2993                     if (distance < closestdist) {
2994                         closestdist = distance;
2995                         closest = i;
2996                     }
2997             }
2998
2999             XYZ flatfacing2, flatvelocity2;
3000             XYZ blah;
3001             if (closest != -1 && distsq(&player[closest].coords, &player[0].coords) < 144) {
3002                 blah = player[closest].coords;
3003                 XYZ headspurtdirection;
3004                 //int i = player[closest].skeleton.jointlabels[head];
3005                 Joint& headjoint = player[closest].joint(head);
3006                 for (int k = 0; k < player[closest].skeleton.num_joints; k++) {
3007                     if (!player[closest].skeleton.free)
3008                         flatvelocity2 = player[closest].velocity;
3009                     if (player[closest].skeleton.free)
3010                         flatvelocity2 = headjoint.velocity;
3011                     if (!player[closest].skeleton.free)
3012                         flatfacing2 = DoRotation(DoRotation(DoRotation(headjoint.position, 0, 0, player[closest].tilt), player[closest].tilt2, 0, 0), 0, player[closest].yaw, 0) * player[closest].scale + player[closest].coords;
3013                     if (player[closest].skeleton.free)
3014                         flatfacing2 = headjoint.position * player[closest].scale + player[closest].coords;
3015                     flatvelocity2.x += (float)(abs(Random() % 100) - 50) / 10;
3016                     flatvelocity2.y += (float)(abs(Random() % 100) - 50) / 10;
3017                     flatvelocity2.z += (float)(abs(Random() % 100) - 50) / 10;
3018                     headspurtdirection = headjoint.position - player[closest].jointPos(neck);
3019                     Normalise(&headspurtdirection);
3020                     Sprite::MakeSprite(bloodflamesprite, flatfacing2, flatvelocity2, 1, 1, 1, .6, 1);
3021                     flatvelocity2 += headspurtdirection * 8;
3022                     Sprite::MakeSprite(bloodsprite, flatfacing2, flatvelocity2 / 2, 1, 1, 1, .16, 1);
3023                 }
3024                 Sprite::MakeSprite(cloudsprite, flatfacing2, flatvelocity2 * 0, .6, 0, 0, 1, .5);
3025
3026                 emit_sound_at(splattersound, blah);
3027                 emit_sound_at(breaksound2, blah, 100.);
3028
3029                 if (player[closest].skeleton.free == 2)player[closest].skeleton.free = 0;
3030                 player[closest].RagDoll(0);
3031                 player[closest].dead = 2;
3032                 player[closest].headless = 1;
3033                 player[closest].DoBloodBig(3, 165);
3034
3035                 camerashake += .3;
3036             }
3037         }
3038
3039         if (((Input::isKeyPressed(SDLK_i) && Input::isKeyDown(SDLK_LSHIFT)))) {
3040             int closest = findClosestPlayer();
3041             XYZ flatfacing2, flatvelocity2;
3042             XYZ blah;
3043             if (closest >= 0 && distsq(&player[closest].coords, &player[0].coords) < 144) {
3044                 blah = player[closest].coords;
3045                 emit_sound_at(splattersound, blah);
3046                 emit_sound_at(breaksound2, blah);
3047
3048                 for (int i = 0; i < player[closest].skeleton.num_joints; i++) {
3049                     if (!player[closest].skeleton.free)flatvelocity2 = player[closest].velocity;
3050                     if (player[closest].skeleton.free)flatvelocity2 = player[closest].skeleton.joints[i].velocity;
3051                     if (!player[closest].skeleton.free)flatfacing2 = DoRotation(DoRotation(DoRotation(player[closest].skeleton.joints[i].position, 0, 0, player[closest].tilt), player[closest].tilt2, 0, 0), 0, player[closest].yaw, 0) * player[closest].scale + player[closest].coords;
3052                     if (player[closest].skeleton.free)flatfacing2 = player[closest].skeleton.joints[i].position * player[closest].scale + player[closest].coords;
3053                     flatvelocity2.x += (float)(abs(Random() % 100) - 50) / 10;
3054                     flatvelocity2.y += (float)(abs(Random() % 100) - 50) / 10;
3055                     flatvelocity2.z += (float)(abs(Random() % 100) - 50) / 10;
3056                     Sprite::MakeSprite(bloodflamesprite, flatfacing2, flatvelocity2, 1, 1, 1, 3, 1);
3057                     Sprite::MakeSprite(bloodsprite, flatfacing2, flatvelocity2, 1, 1, 1, .3, 1);
3058                     Sprite::MakeSprite(cloudsprite, flatfacing2, flatvelocity2 * 0, .6, 0, 0, 1, .5);
3059                 }
3060
3061                 for (int i = 0; i < player[closest].skeleton.num_joints; i++) {
3062                     if (!player[closest].skeleton.free)flatvelocity2 = player[closest].velocity;
3063                     if (player[closest].skeleton.free)flatvelocity2 = player[closest].skeleton.joints[i].velocity;
3064                     if (!player[closest].skeleton.free)flatfacing2 = DoRotation(DoRotation(DoRotation(player[closest].skeleton.joints[i].position, 0, 0, player[closest].tilt), player[closest].tilt2, 0, 0), 0, player[closest].yaw, 0) * player[closest].scale + player[closest].coords;
3065                     if (player[closest].skeleton.free)flatfacing2 = player[closest].skeleton.joints[i].position * player[closest].scale + player[closest].coords;
3066                     flatvelocity2.x += (float)(abs(Random() % 100) - 50) / 10;
3067                     flatvelocity2.y += (float)(abs(Random() % 100) - 50) / 10;
3068                     flatvelocity2.z += (float)(abs(Random() % 100) - 50) / 10;
3069                     Sprite::MakeSprite(bloodflamesprite, flatfacing2, flatvelocity2, 1, 1, 1, 3, 1);
3070                     Sprite::MakeSprite(bloodsprite, flatfacing2, flatvelocity2, 1, 1, 1, .4, 1);
3071                 }
3072
3073                 for (int i = 0; i < player[closest].skeleton.num_joints; i++) {
3074                     if (!player[closest].skeleton.free)flatvelocity2 = player[closest].velocity;
3075                     if (player[closest].skeleton.free)flatvelocity2 = player[closest].skeleton.joints[i].velocity;
3076                     if (!player[closest].skeleton.free)flatfacing2 = DoRotation(DoRotation(DoRotation(player[closest].skeleton.joints[i].position, 0, 0, player[closest].tilt), player[closest].tilt2, 0, 0), 0, player[closest].yaw, 0) * player[closest].scale + player[closest].coords;
3077                     if (player[closest].skeleton.free)flatfacing2 = player[closest].skeleton.joints[i].position * player[closest].scale + player[closest].coords;
3078                     flatvelocity2.x += (float)(abs(Random() % 100) - 50) / 10;
3079                     flatvelocity2.y += (float)(abs(Random() % 100) - 50) / 10;
3080                     flatvelocity2.z += (float)(abs(Random() % 100) - 50) / 10;
3081                     Sprite::MakeSprite(bloodflamesprite, flatfacing2, flatvelocity2 * 2, 1, 1, 1, 3, 1);
3082                     Sprite::MakeSprite(bloodsprite, flatfacing2, flatvelocity2 * 2, 1, 1, 1, .4, 1);
3083                 }
3084
3085                 for (int i = 0; i < player[closest].skeleton.num_joints; i++) {
3086                     if (!player[closest].skeleton.free)flatvelocity2 = player[closest].velocity;
3087                     if (player[closest].skeleton.free)flatvelocity2 = player[closest].skeleton.joints[i].velocity;
3088                     if (!player[closest].skeleton.free)flatfacing2 = DoRotation(DoRotation(DoRotation(player[closest].skeleton.joints[i].position, 0, 0, player[closest].tilt), player[closest].tilt2, 0, 0), 0, player[closest].yaw, 0) * player[closest].scale + player[closest].coords;
3089                     if (player[closest].skeleton.free)flatfacing2 = player[closest].skeleton.joints[i].position * player[closest].scale + player[closest].coords;
3090                     flatvelocity2.x += (float)(abs(Random() % 100) - 50) / 10;
3091                     flatvelocity2.y += (float)(abs(Random() % 100) - 50) / 10;
3092                     flatvelocity2.z += (float)(abs(Random() % 100) - 50) / 10;
3093                     Sprite::MakeSprite(bloodflamesprite, flatfacing2, flatvelocity2 * 2, 1, 1, 1, 3, 1);
3094                     Sprite::MakeSprite(bloodsprite, flatfacing2, flatvelocity2 * 2, 1, 1, 1, .4, 1);
3095                 }
3096
3097                 XYZ temppos;
3098                 for (int j = 0; j < numplayers; j++) {
3099                     if (j != closest) {
3100                         if (distsq(&player[j].coords, &player[closest].coords) < 25) {
3101                             player[j].DoDamage((25 - distsq(&player[j].coords, &player[closest].coords)) * 60);
3102                             if (player[j].skeleton.free == 2)
3103                                 player[j].skeleton.free = 1;
3104                             player[j].skeleton.longdead = 0;
3105                             player[j].RagDoll(0);
3106                             for (int i = 0; i < player[j].skeleton.num_joints; i++) {
3107                                 temppos = player[j].skeleton.joints[i].position + player[j].coords;
3108                                 if (distsq(&temppos, &player[closest].coords) < 25) {
3109                                     flatvelocity2 = temppos - player[closest].coords;
3110                                     Normalise(&flatvelocity2);
3111                                     player[j].skeleton.joints[i].velocity += flatvelocity2 * ((20 - distsq(&temppos, &player[closest].coords)) * 20);
3112                                 }
3113                             }
3114                         }
3115                     }
3116                 }
3117
3118                 player[closest].DoDamage(10000);
3119                 player[closest].RagDoll(0);
3120                 player[closest].dead = 2;
3121                 player[closest].coords = 20;
3122                 player[closest].skeleton.free = 2;
3123
3124                 camerashake += .6;
3125
3126             }
3127         }
3128
3129         if (Input::isKeyPressed(SDLK_f)) {
3130             player[0].onfire = 1 - player[0].onfire;
3131             if (player[0].onfire) {
3132                 player[0].CatchFire();
3133             }
3134             if (!player[0].onfire) {
3135                 emit_sound_at(fireendsound, player[0].coords);
3136                 pause_sound(stream_firesound);
3137             }
3138         }
3139
3140         if (Input::isKeyPressed(SDLK_n) && !Input::isKeyDown(SDLK_LCTRL)) {
3141             //if(!player[0].skeleton.free)player[0].damage+=500;
3142             player[0].RagDoll(0);
3143             //player[0].spurt=1;
3144             //player[0].DoDamage(1000);
3145
3146             emit_sound_at(whooshsound, player[0].coords, 128.);
3147         }
3148
3149         if (Input::isKeyPressed(SDLK_n) && Input::isKeyDown(SDLK_LCTRL)) {
3150             for (int i = 0; i < objects.numobjects; i++) {
3151                 if (objects.type[i] == treeleavestype) {
3152                     objects.scale[i] *= .9;
3153                 }
3154             }
3155         }
3156
3157         if (Input::isKeyPressed(SDLK_m) && Input::isKeyDown(SDLK_LSHIFT)) {
3158             editorenabled = 1 - editorenabled;
3159             if (editorenabled) {
3160                 player[0].damagetolerance = 100000;
3161             } else {
3162                 player[0].damagetolerance = 200;
3163             }
3164             player[0].damage = 0; // these lines were in both if and else, but I think they would better fit in the if
3165             player[0].permanentdamage = 0;
3166             player[0].superpermanentdamage = 0;
3167             player[0].bloodloss = 0;
3168             player[0].deathbleeding = 0;
3169         }
3170
3171         //skip level
3172         if (whichlevel != -2 && Input::isKeyPressed(SDLK_k) && Input::isKeyDown(SDLK_LSHIFT) && !editorenabled) {
3173             targetlevel++;
3174             if (targetlevel > numchallengelevels - 1)
3175                 targetlevel = 0;
3176             loading = 1;
3177             leveltime = 5;
3178         }
3179
3180         if (editorenabled) {
3181             if (Input::isKeyPressed(SDLK_DELETE) && Input::isKeyDown(SDLK_LSHIFT)) {
3182                 int closest = findClosestPlayer();
3183                 if (closest >= 0) {
3184                     //player[closest]=player[numplayers-1];
3185                     //player[closest].skeleton=player[numplayers-1].skeleton;
3186                     numplayers--;
3187                 }
3188             }
3189
3190             if (Input::isKeyPressed(SDLK_DELETE) && Input::isKeyDown(SDLK_LCTRL)) {
3191                 int closest = findClosestObject();
3192                 if (closest >= 0)
3193                     objects.position[closest].y -= 500;
3194             }
3195
3196             if (Input::isKeyPressed(SDLK_m) && Input::isKeyDown(SDLK_LSHIFT)) {
3197                 //drawmode++;
3198                 //if(drawmode>2)drawmode=0;
3199                 if (objects.numobjects < max_objects - 1) {
3200                     XYZ boxcoords;
3201                     boxcoords.x = player[0].coords.x;
3202                     boxcoords.z = player[0].coords.z;
3203                     boxcoords.y = player[0].coords.y - 3;
3204                     if (editortype == bushtype)boxcoords.y = player[0].coords.y - .5;
3205                     if (editortype == firetype)boxcoords.y = player[0].coords.y - .5;
3206                     //objects.MakeObject(abs(Random()%3),boxcoords,Random()%360);
3207                     float temprotat, temprotat2;
3208                     temprotat = editoryaw;
3209                     temprotat2 = editorpitch;
3210                     if (temprotat < 0 || editortype == bushtype)temprotat = Random() % 360;
3211                     if (temprotat2 < 0)temprotat2 = Random() % 360;
3212
3213                     objects.MakeObject(editortype, boxcoords, (int)temprotat - ((int)temprotat) % 30, (int)temprotat2, editorsize);
3214                     if (editortype == treetrunktype)
3215                         objects.MakeObject(treeleavestype, boxcoords, Random() % 360 * (temprotat2 < 2) + (int)editoryaw - ((int)editoryaw) % 30, editorpitch, editorsize);
3216                 }
3217             }
3218
3219             if (Input::isKeyPressed(SDLK_p) && Input::isKeyDown(SDLK_LSHIFT) && !Input::isKeyDown(SDLK_LCTRL)) {
3220                 if (numplayers < maxplayers - 1) {
3221                     player[numplayers].scale = .2 * 5 * player[0].scale;
3222                     player[numplayers].creature = rabbittype;
3223                     player[numplayers].howactive = editoractive;
3224                     player[numplayers].skeleton.id = numplayers;
3225                     player[numplayers].skeleton.Load((char *)":Data:Skeleton:Basic Figure", (char *)":Data:Skeleton:Basic Figurelow", (char *)":Data:Skeleton:Rabbitbelt", (char *)":Data:Models:Body.solid", (char *)":Data:Models:Body2.solid", (char *)":Data:Models:Body3.solid", (char *)":Data:Models:Body4.solid", (char *)":Data:Models:Body5.solid", (char *)":Data:Models:Body6.solid", (char *)":Data:Models:Body7.solid", (char *)":Data:Models:Bodylow.solid", (char *)":Data:Models:Belt.solid", 1);
3226
3227                     //texsize=512*512*3/texdetail/texdetail;
3228                     //if(!player[numplayers].loaded)player[numplayers].skeleton.skinText = new GLubyte[texsize];
3229                     //player[numplayers].skeleton.skinText.resize(texsize);
3230
3231                     int k = abs(Random() % 2) + 1;
3232                     if (k == 0) {
3233                         player[numplayers].skeleton.drawmodel.textureptr.load(":Data:Textures:Fur3.jpg", 1, &player[numplayers].skeleton.skinText[0], &player[numplayers].skeleton.skinsize);
3234                         player[numplayers].whichskin = 0;
3235                     } else if (k == 1) {
3236                         player[numplayers].skeleton.drawmodel.textureptr.load(":Data:Textures:Fur.jpg", 1, &player[numplayers].skeleton.skinText[0], &player[numplayers].skeleton.skinsize);
3237                         player[numplayers].whichskin = 1;
3238                     } else {
3239                         player[numplayers].skeleton.drawmodel.textureptr.load(":Data:Textures:Fur2.jpg", 1, &player[numplayers].skeleton.skinText[0], &player[numplayers].skeleton.skinsize);
3240                         player[numplayers].whichskin = 2;
3241                     }
3242
3243                     player[numplayers].skeleton.drawmodelclothes.textureptr.load(":Data:Textures:Belt.png", 1, 1);
3244                     player[numplayers].power = 1;
3245                     player[numplayers].speedmult = 1;
3246                     player[numplayers].animCurrent = bounceidleanim;
3247                     player[numplayers].animTarget = bounceidleanim;
3248                     player[numplayers].frameCurrent = 0;
3249                     player[numplayers].frameTarget = 1;
3250                     player[numplayers].target = 0;
3251                     player[numplayers].bled = 0;
3252                     player[numplayers].speed = 1 + (float)(Random() % 100) / 1000;
3253
3254                     player[numplayers].targetyaw = player[0].targetyaw;
3255                     player[numplayers].yaw = player[0].yaw;
3256
3257                     player[numplayers].velocity = 0;
3258                     player[numplayers].coords = player[0].coords;
3259                     player[numplayers].oldcoords = player[numplayers].coords;
3260                     player[numplayers].realoldcoords = player[numplayers].coords;
3261
3262                     player[numplayers].id = numplayers;
3263                     player[numplayers].skeleton.id = numplayers;
3264                     player[numplayers].updatedelay = 0;
3265                     player[numplayers].normalsupdatedelay = 0;
3266
3267                     player[numplayers].aitype = passivetype;
3268
3269                     if (player[0].creature == wolftype) {
3270                         headprop = player[0].proportionhead.x / 1.1;
3271                         bodyprop = player[0].proportionbody.x / 1.1;
3272                         armprop = player[0].proportionarms.x / 1.1;
3273                         legprop = player[0].proportionlegs.x / 1.1;
3274                     }
3275
3276                     if (player[0].creature == rabbittype) {
3277                         headprop = player[0].proportionhead.x / 1.2;
3278                         bodyprop = player[0].proportionbody.x / 1.05;
3279                         armprop = player[0].proportionarms.x / 1.00;
3280                         legprop = player[0].proportionlegs.x / 1.1;
3281                     }
3282
3283                     if (player[numplayers].creature == wolftype) {
3284                         player[numplayers].proportionhead = 1.1 * headprop;
3285                         player[numplayers].proportionbody = 1.1 * bodyprop;
3286                         player[numplayers].proportionarms = 1.1 * armprop;
3287                         player[numplayers].proportionlegs = 1.1 * legprop;
3288                     }
3289
3290                     if (player[numplayers].creature == rabbittype) {
3291                         player[numplayers].proportionhead = 1.2 * headprop;
3292                         player[numplayers].proportionbody = 1.05 * bodyprop;
3293                         player[numplayers].proportionarms = 1.00 * armprop;
3294                         player[numplayers].proportionlegs = 1.1 * legprop;
3295                         player[numplayers].proportionlegs.y = 1.05 * legprop;
3296                     }
3297
3298                     player[numplayers].headless = 0;
3299                     player[numplayers].onfire = 0;
3300
3301                     if (cellophane) {
3302                         player[numplayers].proportionhead.z = 0;
3303                         player[numplayers].proportionbody.z = 0;
3304                         player[numplayers].proportionarms.z = 0;
3305                         player[numplayers].proportionlegs.z = 0;
3306                     }
3307
3308                     player[numplayers].tempanimation.Load((char *)"Tempanim", 0, 0);
3309
3310                     player[numplayers].damagetolerance = 200;
3311
3312                     player[numplayers].protectionhead = player[0].protectionhead;
3313                     player[numplayers].protectionhigh = player[0].protectionhigh;
3314                     player[numplayers].protectionlow = player[0].protectionlow;
3315                     player[numplayers].armorhead = player[0].armorhead;
3316                     player[numplayers].armorhigh = player[0].armorhigh;
3317                     player[numplayers].armorlow = player[0].armorlow;
3318                     player[numplayers].metalhead = player[0].metalhead;
3319                     player[numplayers].metalhigh = player[0].metalhigh;
3320                     player[numplayers].metallow = player[0].metallow;
3321
3322                     player[numplayers].immobile = player[0].immobile;
3323
3324                     player[numplayers].numclothes = player[0].numclothes;
3325                     if (player[numplayers].numclothes)
3326                         for (int i = 0; i < player[numplayers].numclothes; i++) {
3327                             strcpy(player[numplayers].clothes[i], player[0].clothes[i]);
3328                             player[numplayers].clothestintr[i] = player[0].clothestintr[i];
3329                             player[numplayers].clothestintg[i] = player[0].clothestintg[i];
3330                             player[numplayers].clothestintb[i] = player[0].clothestintb[i];
3331                             tintr = player[numplayers].clothestintr[i];
3332                             tintg = player[numplayers].clothestintg[i];
3333                             tintb = player[numplayers].clothestintb[i];
3334                             AddClothes((char *)player[numplayers].clothes[i], &player[numplayers].skeleton.skinText[0]);
3335                         }
3336                     if (player[numplayers].numclothes) {
3337                         player[numplayers].DoMipmaps();
3338                     }
3339
3340                     player[numplayers].power = player[0].power;
3341                     player[numplayers].speedmult = player[0].speedmult;
3342
3343                     player[numplayers].damage = 0;
3344                     player[numplayers].permanentdamage = 0;
3345                     player[numplayers].superpermanentdamage = 0;
3346                     player[numplayers].deathbleeding = 0;
3347                     player[numplayers].bleeding = 0;
3348                     player[numplayers].numwaypoints = 0;
3349                     player[numplayers].waypoint = 0;
3350                     player[numplayers].jumppath = 0;
3351                     player[numplayers].weaponstuck = -1;
3352                     player[numplayers].weaponactive = -1;
3353                     player[numplayers].num_weapons = 0;
3354                     player[numplayers].bloodloss = 0;
3355                     player[numplayers].dead = 0;
3356
3357                     player[numplayers].loaded = 1;
3358
3359                     numplayers++;
3360                 }
3361             }
3362
3363             if (Input::isKeyPressed(SDLK_p) && Input::isKeyDown(SDLK_LSHIFT)) {
3364                 if (player[numplayers - 1].numwaypoints < 90) {
3365                     player[numplayers - 1].waypoints[player[numplayers - 1].numwaypoints] = player[0].coords;
3366                     player[numplayers - 1].waypointtype[player[numplayers - 1].numwaypoints] = editorpathtype;
3367                     player[numplayers - 1].numwaypoints++;
3368                 }
3369             }
3370
3371             if (Input::isKeyPressed(SDLK_p) && Input::isKeyDown(SDLK_LCTRL)) {
3372                 if (numpathpoints < 30) {
3373                     bool connected, alreadyconnected;
3374                     connected = 0;
3375                     if (numpathpoints > 1)
3376                         for (int i = 0; i < numpathpoints; i++) {
3377                             if (distsq(&pathpoint[i], &player[0].coords) < .5 && i != pathpointselected && !connected) {
3378                                 alreadyconnected = 0;
3379                                 for (int j = 0; j < numpathpointconnect[pathpointselected]; j++) {
3380                                     if (pathpointconnect[pathpointselected][j] == i)alreadyconnected = 1;
3381                                 }
3382                                 if (!alreadyconnected) {
3383                                     numpathpointconnect[pathpointselected]++;
3384                                     connected = 1;
3385                                     pathpointconnect[pathpointselected][numpathpointconnect[pathpointselected] - 1] = i;
3386                                 }
3387                             }
3388                         }
3389                     if (!connected) {
3390                         numpathpoints++;
3391                         pathpoint[numpathpoints - 1] = player[0].coords;
3392                         numpathpointconnect[numpathpoints - 1] = 0;
3393                         if (numpathpoints > 1 && pathpointselected != -1) {
3394                             numpathpointconnect[pathpointselected]++;
3395                             pathpointconnect[pathpointselected][numpathpointconnect[pathpointselected] - 1] = numpathpoints - 1;
3396                         }
3397                         pathpointselected = numpathpoints - 1;
3398                     }
3399                 }
3400             }
3401
3402             if (Input::isKeyPressed(SDLK_PERIOD)) {
3403                 pathpointselected++;
3404                 if (pathpointselected >= numpathpoints)
3405                     pathpointselected = -1;
3406             }
3407             if (Input::isKeyPressed(SDLK_COMMA) && !Input::isKeyDown(SDLK_LSHIFT)) {
3408                 pathpointselected--;
3409                 if (pathpointselected <= -2)
3410                     pathpointselected = numpathpoints - 1;
3411             }
3412             if (Input::isKeyPressed(SDLK_COMMA) && Input::isKeyDown(SDLK_LSHIFT)) {
3413                 if (pathpointselected != -1) {
3414                     numpathpoints--;
3415                     pathpoint[pathpointselected] = pathpoint[numpathpoints];
3416                     numpathpointconnect[pathpointselected] = numpathpointconnect[numpathpoints];
3417                     for (int i = 0; i < numpathpointconnect[pathpointselected]; i++) {
3418                         pathpointconnect[pathpointselected][i] = pathpointconnect[numpathpoints][i];
3419                     }
3420                     for (int i = 0; i < numpathpoints; i++) {
3421                         for (int j = 0; j < numpathpointconnect[i]; j++) {
3422                             if (pathpointconnect[i][j] == pathpointselected) {
3423                                 pathpointconnect[i][j] = pathpointconnect[i][numpathpointconnect[i] - 1];
3424                                 numpathpointconnect[i]--;
3425                             }
3426                             if (pathpointconnect[i][j] == numpathpoints) {
3427                                 pathpointconnect[i][j] = pathpointselected;
3428                             }
3429                         }
3430                     }
3431                     pathpointselected = numpathpoints - 1;
3432                 }
3433             }
3434
3435             if (Input::isKeyPressed(SDLK_LEFT) && Input::isKeyDown(SDLK_LSHIFT) && !Input::isKeyDown(SDLK_LCTRL)) {
3436                 editortype--;
3437                 if (editortype == treeleavestype || editortype == 10)editortype--;
3438                 if (editortype < 0)editortype = firetype;
3439             }
3440
3441             if (Input::isKeyPressed(SDLK_RIGHT) && Input::isKeyDown(SDLK_LSHIFT) && !Input::isKeyDown(SDLK_LCTRL)) {
3442                 editortype++;
3443                 if (editortype == treeleavestype || editortype == 10)editortype++;
3444                 if (editortype > firetype)editortype = 0;
3445             }
3446
3447             if (Input::isKeyDown(SDLK_LEFT) && !Input::isKeyDown(SDLK_LSHIFT) && !Input::isKeyDown(SDLK_LCTRL)) {
3448                 editoryaw -= multiplier * 100;
3449                 if (editoryaw < -.01)editoryaw = -.01;
3450             }
3451
3452             if (Input::isKeyDown(SDLK_RIGHT) && !Input::isKeyDown(SDLK_LSHIFT) && !Input::isKeyDown(SDLK_LCTRL)) {
3453                 editoryaw += multiplier * 100;
3454             }
3455
3456             if (Input::isKeyDown(SDLK_UP) && !Input::isKeyDown(SDLK_LCTRL)) {
3457                 editorsize += multiplier;
3458             }
3459
3460             if (Input::isKeyDown(SDLK_DOWN) && !Input::isKeyDown(SDLK_LCTRL)) {
3461                 editorsize -= multiplier;
3462                 if (editorsize < .1)editorsize = .1;
3463             }
3464
3465
3466             if (Input::isKeyPressed(SDLK_LEFT) && Input::isKeyDown(SDLK_LSHIFT) && Input::isKeyDown(SDLK_LCTRL)) {
3467                 mapradius -= multiplier * 10;
3468             }
3469
3470             if (Input::isKeyPressed(SDLK_RIGHT) && Input::isKeyDown(SDLK_LSHIFT) && Input::isKeyDown(SDLK_LCTRL)) {
3471                 mapradius += multiplier * 10;
3472             }
3473             if (Input::isKeyDown(SDLK_UP) && Input::isKeyDown(SDLK_LCTRL)) {
3474                 editorpitch += multiplier * 100;
3475             }
3476
3477             if (Input::isKeyDown(SDLK_DOWN) && Input::isKeyDown(SDLK_LCTRL)) {
3478                 editorpitch -= multiplier * 100;
3479                 if (editorpitch < -.01)editorpitch = -.01;
3480             }
3481             if (Input::isKeyPressed(SDLK_DELETE) && objects.numobjects && Input::isKeyDown(SDLK_LSHIFT)) {
3482                 int closest = findClosestObject();
3483                 if (closest >= 0)
3484                     objects.DeleteObject(closest);
3485             }
3486         }
3487     }
3488 }
3489
3490 void doJumpReversals()
3491 {
3492     for (int k = 0; k < numplayers; k++)
3493         for (int i = k; i < numplayers; i++) {
3494             if (i == k)continue;
3495             if (     player[k].skeleton.free == 0 &&
3496                      player[i].skeleton.oldfree == 0 &&
3497                      (player[i].animTarget == jumpupanim ||
3498                       player[k].animTarget == jumpupanim) &&
3499                      (player[i].aitype == playercontrolled ||
3500                       player[k].aitype == playercontrolled) &&
3501                      (player[i].aitype == attacktypecutoff && player[i].stunned <= 0 ||
3502                       player[k].aitype == attacktypecutoff && player[k].stunned <= 0)) {
3503                 if (     distsq(&player[i].coords, &player[k].coords) < 10 * sq((player[i].scale + player[k].scale) * 2.5) &&
3504                          distsqflat(&player[i].coords, &player[k].coords) < 2 * sq((player[i].scale + player[k].scale) * 2.5)) {
3505                     //TODO: refactor two huge similar ifs
3506                     if (player[i].animTarget == jumpupanim &&
3507                             player[k].animTarget != getupfrombackanim &&
3508                             player[k].animTarget != getupfromfrontanim &&
3509                             animation[player[k].animTarget].height == middleheight &&
3510                             normaldotproduct(player[i].velocity, player[k].coords - player[i].coords) < 0 &&
3511                             (player[k].aitype == playercontrolled && player[k].attackkeydown ||
3512                              player[k].aitype != playercontrolled)) {
3513                         player[i].victim = &player[k];
3514                         player[i].velocity = 0;
3515                         player[i].animCurrent = jumpreversedanim;
3516                         player[i].animTarget = jumpreversedanim;
3517                         player[i].frameCurrent = 0;
3518                         player[i].frameTarget = 1;
3519                         player[i].targettilt2 = 0;
3520                         player[k].victim = &player[i];
3521                         player[k].velocity = 0;
3522                         player[k].animCurrent = jumpreversalanim;
3523                         player[k].animTarget = jumpreversalanim;
3524                         player[k].frameCurrent = 0;
3525                         player[k].frameTarget = 1;
3526                         player[k].targettilt2 = 0;
3527                         if (player[i].coords.y < player[k].coords.y + 1) {
3528                             player[i].animCurrent = rabbitkickreversedanim;
3529                             player[i].animTarget = rabbitkickreversedanim;
3530                             player[i].frameCurrent = 1;
3531                             player[i].frameTarget = 2;
3532                             player[k].animCurrent = rabbitkickreversalanim;
3533                             player[k].animTarget = rabbitkickreversalanim;
3534                             player[k].frameCurrent = 1;
3535                             player[k].frameTarget = 2;
3536                         }
3537                         player[i].target = 0;
3538                         player[k].oldcoords = player[k].coords;
3539                         player[i].coords = player[k].coords;
3540                         player[k].targetyaw = player[i].targetyaw;
3541                         player[k].yaw = player[i].targetyaw;
3542                         if (player[k].aitype == attacktypecutoff)
3543                             player[k].stunned = .5;
3544                     }
3545                     if (player[k].animTarget == jumpupanim &&
3546                             player[i].animTarget != getupfrombackanim &&
3547                             player[i].animTarget != getupfromfrontanim &&
3548                             animation[player[i].animTarget].height == middleheight &&
3549                             normaldotproduct(player[k].velocity, player[i].coords - player[k].coords) < 0 &&
3550                             ((player[i].aitype == playercontrolled && player[i].attackkeydown) ||
3551                              player[i].aitype != playercontrolled)) {
3552                         player[k].victim = &player[i];
3553                         player[k].velocity = 0;
3554                         player[k].animCurrent = jumpreversedanim;
3555                         player[k].animTarget = jumpreversedanim;
3556                         player[k].frameCurrent = 0;
3557                         player[k].frameTarget = 1;
3558                         player[k].targettilt2 = 0;
3559                         player[i].victim = &player[k];
3560                         player[i].velocity = 0;
3561                         player[i].animCurrent = jumpreversalanim;
3562                         player[i].animTarget = jumpreversalanim;
3563                         player[i].frameCurrent = 0;
3564                         player[i].frameTarget = 1;
3565                         player[i].targettilt2 = 0;
3566                         if (player[k].coords.y < player[i].coords.y + 1) {
3567                             player[k].animTarget = rabbitkickreversedanim;
3568                             player[k].animCurrent = rabbitkickreversedanim;
3569                             player[i].animCurrent = rabbitkickreversalanim;
3570                             player[i].animTarget = rabbitkickreversalanim;
3571                             player[k].frameCurrent = 1;
3572                             player[k].frameTarget = 2;
3573                             player[i].frameCurrent = 1;
3574                             player[i].frameTarget = 2;
3575                         }
3576                         player[k].target = 0;
3577                         player[i].oldcoords = player[i].coords;
3578                         player[k].coords = player[i].coords;
3579                         player[i].targetyaw = player[k].targetyaw;
3580                         player[i].yaw = player[k].targetyaw;
3581                         if (player[i].aitype == attacktypecutoff)
3582                             player[i].stunned = .5;
3583                     }
3584                 }
3585             }
3586         }
3587 }
3588
3589 void doAerialAcrobatics()
3590 {
3591     static XYZ facing, flatfacing;
3592     for (int k = 0; k < numplayers; k++) {
3593         player[k].turnspeed = 500;
3594
3595         if ((player[k].isRun() &&
3596                 ((player[k].targetyaw != rabbitrunninganim &&
3597                   player[k].targetyaw != wolfrunninganim) ||
3598                  player[k].frameTarget == 4)) ||
3599                 player[k].animTarget == removeknifeanim ||
3600                 player[k].animTarget == crouchremoveknifeanim ||
3601                 player[k].animTarget == flipanim ||
3602                 player[k].animTarget == fightsidestep ||
3603                 player[k].animTarget == walkanim) {
3604             player[k].yaw = stepTowardf(player[k].yaw, player[k].targetyaw, multiplier * player[k].turnspeed);
3605         }
3606
3607
3608         if (player[k].isStop() ||
3609                 player[k].isLanding() ||
3610                 player[k].animTarget == staggerbackhighanim ||
3611                 (player[k].animTarget == sneakanim && player[k].animCurrent == sneakanim) ||
3612                 player[k].animTarget == staggerbackhardanim ||
3613                 player[k].animTarget == backhandspringanim ||
3614                 player[k].animTarget == dodgebackanim ||
3615                 player[k].animTarget == rollanim ||
3616                 (animation[player[k].animTarget].attack &&
3617                  player[k].animTarget != rabbitkickanim &&
3618                  (player[k].animTarget != crouchstabanim || player[k].hasvictim) &&
3619                  (player[k].animTarget != swordgroundstabanim || player[k].hasvictim))) {
3620             player[k].yaw = stepTowardf(player[k].yaw, player[k].targetyaw, multiplier * player[k].turnspeed * 2);
3621         }
3622
3623         if (player[k].animTarget == sneakanim && player[k].animCurrent != sneakanim) {
3624             player[k].yaw = stepTowardf(player[k].yaw, player[k].targetyaw, multiplier * player[k].turnspeed * 4);
3625         }
3626
3627         /*if(player[k].aitype!=passivetype||(distsq(&player[k].coords,&viewer)<viewdistance*viewdistance))*/
3628         player[k].DoStuff();
3629         if (player[k].immobile && k != 0)
3630             player[k].coords = player[k].realoldcoords;
3631
3632         //if player's position has changed (?)
3633         if (distsq(&player[k].coords, &player[k].realoldcoords) > 0 &&
3634                 !player[k].skeleton.free &&
3635                 player[k].animTarget != climbanim &&
3636                 player[k].animTarget != hanganim) {
3637             XYZ lowpoint, lowpointtarget, lowpoint2, lowpointtarget2, lowpoint3, lowpointtarget3, lowpoint4, lowpointtarget4, lowpoint5, lowpointtarget5, lowpoint6, lowpointtarget6, lowpoint7, lowpointtarget7, colpoint, colpoint2;
3638             int whichhit;
3639             bool tempcollide = 0;
3640
3641             if (player[k].collide < -.3)
3642                 player[k].collide = -.3;
3643             if (player[k].collide > 1)
3644                 player[k].collide = 1;
3645             player[k].collide -= multiplier * 30;
3646
3647             //clip to terrain
3648             player[k].coords.y = max(player[k].coords.y, terrain.getHeight(player[k].coords.x, player[k].coords.z));
3649
3650             for (int l = 0; l < terrain.patchobjectnum[player[k].whichpatchx][player[k].whichpatchz]; l++) {
3651                 int i = terrain.patchobjects[player[k].whichpatchx][player[k].whichpatchz][l];
3652                 if (objects.type[i] != rocktype ||
3653                         objects.scale[i] > .5 && player[k].aitype == playercontrolled ||
3654                         objects.position[i].y > player[k].coords.y) {
3655                     lowpoint = player[k].coords;
3656                     if (player[k].animTarget != jumpupanim &&
3657                             player[k].animTarget != jumpdownanim &&
3658                             !player[k].isFlip())
3659                         lowpoint.y += 1.25;
3660                     else
3661                         lowpoint.y += 1.3;
3662                     if (     player[k].coords.y < terrain.getHeight(player[k].coords.x, player[k].coords.z) &&
3663                              player[k].coords.y > terrain.getHeight(player[k].coords.x, player[k].coords.z) - .1)
3664                         player[k].coords.y = terrain.getHeight(player[k].coords.x, player[k].coords.z);
3665                     if (player[k].SphereCheck(&lowpoint, 1.3, &colpoint, &objects.position[i], &objects.yaw[i], &objects.model[i]) != -1) {
3666                         flatfacing = lowpoint - player[k].coords;
3667                         player[k].coords = lowpoint;
3668                         player[k].coords.y -= 1.3;
3669                         player[k].collide = 1;
3670                         tempcollide = 1;
3671                         //wall jumps
3672                         //TODO: refactor four similar blocks
3673                         if (player[k].aitype == playercontrolled &&
3674                                 (player[k].animTarget == jumpupanim ||
3675                                  player[k].animTarget == jumpdownanim ||
3676                                  player[k].isFlip()) &&
3677                                 !player[k].jumptogglekeydown &&
3678                                 player[k].jumpkeydown) {
3679                             lowpointtarget = lowpoint + DoRotation(player[k].facing, 0, -90, 0) * 1.5;
3680                             XYZ tempcoords1 = lowpoint;
3681                             whichhit = objects.model[i].LineCheck(&lowpoint, &lowpointtarget, &colpoint, &objects.position[i], &objects.yaw[i]);
3682                             if (whichhit != -1 && fabs(objects.model[i].facenormals[whichhit].y) < .3) {
3683                                 player[k].setAnimation(walljumpleftanim);
3684                                 emit_sound_at(movewhooshsound, player[k].coords);
3685                                 if (k == 0)
3686                                     pause_sound(whooshsound);
3687
3688                                 lowpointtarget = DoRotation(objects.model[i].facenormals[whichhit], 0, objects.yaw[i], 0);
3689                                 player[k].yaw = -asin(0 - lowpointtarget.x) * 180 / M_PI;
3690                                 if (lowpointtarget.z < 0)
3691                                     player[k].yaw = 180 - player[k].yaw;
3692                                 player[k].targetyaw = player[k].yaw;
3693                                 player[k].lowyaw = player[k].yaw;
3694                                 if (k == 0)
3695                                     numwallflipped++;
3696                             } else {
3697                                 lowpoint = tempcoords1;
3698                                 lowpointtarget = lowpoint + DoRotation(player[k].facing, 0, 90, 0) * 1.5;
3699                                 whichhit = objects.model[i].LineCheck(&lowpoint, &lowpointtarget, &colpoint, &objects.position[i], &objects.yaw[i]);
3700                                 if (whichhit != -1 && fabs(objects.model[i].facenormals[whichhit].y) < .3) {
3701                                     player[k].setAnimation(walljumprightanim);
3702                                     emit_sound_at(movewhooshsound, player[k].coords);
3703                                     if (k == 0)pause_sound(whooshsound);
3704
3705                                     lowpointtarget = DoRotation(objects.model[i].facenormals[whichhit], 0, objects.yaw[i], 0);
3706                                     player[k].yaw = -asin(0 - lowpointtarget.x) * 180 / M_PI;
3707                                     if (lowpointtarget.z < 0)player[k].yaw = 180 - player[k].yaw;
3708                                     player[k].targetyaw = player[k].yaw;
3709                                     player[k].lowyaw = player[k].yaw;
3710                                     if (k == 0)numwallflipped++;
3711                                 } else {
3712                                     lowpoint = tempcoords1;
3713                                     lowpointtarget = lowpoint + player[k].facing * 2;
3714                                     whichhit = objects.model[i].LineCheck(&lowpoint, &lowpointtarget, &colpoint, &objects.position[i], &objects.yaw[i]);
3715                                     if (whichhit != -1 && fabs(objects.model[i].facenormals[whichhit].y) < .3) {
3716                                         player[k].setAnimation(walljumpbackanim);
3717                                         emit_sound_at(movewhooshsound, player[k].coords);
3718                                         if (k == 0)pause_sound(whooshsound);
3719
3720                                         lowpointtarget = DoRotation(objects.model[i].facenormals[whichhit], 0, objects.yaw[i], 0);
3721                                         player[k].yaw = -asin(0 - lowpointtarget.x) * 180 / M_PI;
3722                                         if (lowpointtarget.z < 0)player[k].yaw = 180 - player[k].yaw;
3723                                         player[k].targetyaw = player[k].yaw;
3724                                         player[k].lowyaw = player[k].yaw;
3725                                         if (k == 0)numwallflipped++;
3726                                     } else {
3727                                         lowpoint = tempcoords1;
3728                                         lowpointtarget = lowpoint - player[k].facing * 2;
3729                                         whichhit = objects.model[i].LineCheck(&lowpoint, &lowpointtarget, &colpoint, &objects.position[i], &objects.yaw[i]);
3730                                         if (whichhit != -1 && fabs(objects.model[i].facenormals[whichhit].y) < .3) {
3731                                             player[k].setAnimation(walljumpfrontanim);
3732                                             emit_sound_at(movewhooshsound, player[k].coords);
3733                                             if (k == 0)pause_sound(whooshsound);
3734
3735                                             lowpointtarget = DoRotation(objects.model[i].facenormals[whichhit], 0, objects.yaw[i], 0);
3736                                             player[k].yaw = -asin(0 - lowpointtarget.x) * 180 / M_PI;
3737                                             if (lowpointtarget.z < 0)player[k].yaw = 180 - player[k].yaw;
3738                                             player[k].yaw += 180;
3739                                             player[k].targetyaw = player[k].yaw;
3740                                             player[k].lowyaw = player[k].yaw;
3741                                             if (k == 0)numwallflipped++;
3742                                         }
3743                                     }
3744                                 }
3745                             }
3746                         }
3747                     }
3748                 } else if (objects.type[i] == rocktype) {
3749                     lowpoint2 = player[k].coords;
3750                     lowpoint = player[k].coords;
3751                     lowpoint.y += 2;
3752                     if (objects.model[i].LineCheck(&lowpoint, &lowpoint2, &colpoint, &objects.position[i], &objects.yaw[i]) != -1) {
3753                         player[k].coords = colpoint;
3754                         player[k].collide = 1;
3755                         tempcollide = 1;
3756
3757                         if (player[k].animTarget == jumpdownanim || player[k].isFlip()) {
3758                             //flipped into a rock
3759                             if (player[k].isFlip() && animation[player[k].animTarget].label[player[k].frameTarget] == 7)
3760                                 player[k].RagDoll(0);
3761
3762                             if (player[k].animTarget == jumpupanim) {
3763                                 player[k].jumppower = -4;
3764                                 player[k].animTarget = player[k].getIdle();
3765                             }
3766                             player[k].target = 0;
3767                             player[k].frameTarget = 0;
3768                             player[k].onterrain = 1;
3769
3770                             if (player[k].id == 0) {
3771                                 pause_sound(whooshsound);
3772                                 OPENAL_SetVolume(channels[whooshsound], 0);
3773                             }
3774
3775                             //landing
3776                             if ((player[k].animTarget == jumpdownanim || player[k].isFlip()) && !player[k].wasLanding()) {
3777                                 if (player[k].isFlip())
3778                                     player[k].jumppower = -4;
3779                                 player[k].animTarget = player[k].getLanding();
3780                                 emit_sound_at(landsound, player[k].coords, 128.);
3781                                 if (k == 0) {
3782                                     envsound[numenvsounds] = player[k].coords;
3783                                     envsoundvol[numenvsounds] = 16;
3784                                     envsoundlife[numenvsounds] = .4;
3785                                     numenvsounds++;
3786                                 }
3787
3788                             }
3789                         }
3790                     }
3791                 }
3792             }
3793
3794             if (tempcollide && (/*player[k].jumptogglekeydown*/1 == 1 || player[k].aitype != playercontrolled))
3795                 for (int l = 0; l < terrain.patchobjectnum[player[k].whichpatchx][player[k].whichpatchz]; l++) {
3796                     int i = terrain.patchobjects[player[k].whichpatchx][player[k].whichpatchz][l];
3797                     lowpoint = player[k].coords;
3798                     lowpoint.y += 1.35;
3799                     if (objects.type[i] != rocktype)
3800                         if (player[k].SphereCheck(&lowpoint, 1.33, &colpoint, &objects.position[i], &objects.yaw[i], &objects.model[i]) != -1) {
3801                             if (player[k].animTarget != jumpupanim &&
3802                                     player[k].animTarget != jumpdownanim &&
3803                                     player[k].onterrain)
3804                                 player[k].avoidcollided = 1;
3805                             player[k].coords = lowpoint;
3806                             player[k].coords.y -= 1.35;
3807                             player[k].collide = 1;
3808
3809                             if ((player[k].grabdelay <= 0 || player[k].aitype != playercontrolled) &&
3810                                     (player[k].animCurrent != climbanim &&
3811                                      player[k].animCurrent != hanganim &&
3812                                      !player[k].isWallJump() ||
3813                                      player[k].animTarget == jumpupanim ||
3814                                      player[k].animTarget == jumpdownanim)) {
3815                                 lowpoint = player[k].coords;
3816                                 objects.model[i].SphereCheckPossible(&lowpoint, 1.5, &objects.position[i], &objects.yaw[i]);
3817                                 lowpoint = player[k].coords;
3818                                 lowpoint.y += .05;
3819                                 facing = 0;
3820                                 facing.z = -1;
3821                                 facing = DoRotation(facing, 0, player[k].targetyaw + 180, 0);
3822                                 lowpointtarget = lowpoint + facing * 1.4;
3823                                 whichhit = objects.model[i].LineCheckPossible(&lowpoint, &lowpointtarget, &colpoint, &objects.position[i], &objects.yaw[i]);
3824                                 if (whichhit != -1) {
3825                                     lowpoint = player[k].coords;
3826                                     lowpoint.y += .1;
3827                                     lowpointtarget = lowpoint + facing * 1.4;
3828                                     lowpoint2 = lowpoint;
3829                                     lowpointtarget2 = lowpointtarget;
3830                                     lowpoint3 = lowpoint;
3831                                     lowpointtarget3 = lowpointtarget;
3832                                     lowpoint4 = lowpoint;
3833                                     lowpointtarget4 = lowpointtarget;
3834                                     lowpoint5 = lowpoint;
3835                                     lowpointtarget5 = lowpointtarget;
3836                                     lowpoint6 = lowpoint;
3837                                     lowpointtarget6 = lowpointtarget;
3838                                     lowpoint7 = lowpoint;
3839                                     lowpointtarget7 = lowpoint;
3840                                     lowpoint2.x += .1;
3841                                     lowpointtarget2.x += .1;
3842                                     lowpoint3.z += .1;
3843                                     lowpointtarget3.z += .1;
3844                                     lowpoint4.x -= .1;
3845                                     lowpointtarget4.x -= .1;
3846                                     lowpoint5.z -= .1;
3847                                     lowpointtarget5.z -= .1;
3848                                     lowpoint6.y += 45 / 13;
3849                                     lowpointtarget6.y += 45 / 13;
3850                                     lowpointtarget6 += facing * .6;
3851                                     lowpointtarget7.y += 90 / 13;
3852                                     whichhit = objects.model[i].LineCheckPossible(&lowpoint, &lowpointtarget, &colpoint, &objects.position[i], &objects.yaw[i]);
3853                                     if (objects.friction[i] > .5)
3854                                         if (whichhit != -1) {
3855                                             if (whichhit != -1 && player[k].animTarget != jumpupanim && player[k].animTarget != jumpdownanim)
3856                                                 player[k].collided = 1;
3857                                             if (checkcollide(lowpoint7, lowpointtarget7) == -1)
3858                                                 if (checkcollide(lowpoint6, lowpointtarget6) == -1)
3859                                                     if (     objects.model[i].LineCheckPossible(&lowpoint2, &lowpointtarget2,
3860                                                              &colpoint, &objects.position[i], &objects.yaw[i]) != -1 &&
3861                                                              objects.model[i].LineCheckPossible(&lowpoint3, &lowpointtarget3,
3862                                                                      &colpoint, &objects.position[i], &objects.yaw[i]) != -1 &&
3863                                                              objects.model[i].LineCheckPossible(&lowpoint4, &lowpointtarget4,
3864                                                                      &colpoint, &objects.position[i], &objects.yaw[i]) != -1 &&
3865                                                              objects.model[i].LineCheckPossible(&lowpoint5, &lowpointtarget5,
3866                                                                      &colpoint, &objects.position[i], &objects.yaw[i]) != -1)
3867                                                         for (int j = 0; j < 45; j++) {
3868                                                             lowpoint = player[k].coords;
3869                                                             lowpoint.y += (float)j / 13;
3870                                                             lowpointtarget = lowpoint + facing * 1.4;
3871                                                             if (objects.model[i].LineCheckPossible(&lowpoint, &lowpointtarget,
3872                                                                                                    &colpoint2, &objects.position[i], &objects.yaw[i]) == -1) {
3873                                                                 if (j <= 6 || j <= 25 && player[k].animTarget == jumpdownanim)
3874                                                                     break;
3875                                                                 if (player[k].animTarget == jumpupanim || player[k].animTarget == jumpdownanim) {
3876                                                                     lowpoint = DoRotation(objects.model[i].facenormals[whichhit], 0, objects.yaw[k], 0);
3877                                                                     lowpoint = player[k].coords;
3878                                                                     lowpoint.y += (float)j / 13;
3879                                                                     lowpointtarget = lowpoint + facing * 1.3;
3880                                                                     flatfacing = player[k].coords;
3881                                                                     player[k].coords = colpoint - DoRotation(objects.model[i].facenormals[whichhit], 0, objects.yaw[k], 0) * .01;
3882                                                                     player[k].coords.y = lowpointtarget.y - .07;
3883                                                                     player[k].currentoffset = (flatfacing - player[k].coords) / player[k].scale;
3884
3885                                                                     if (j > 10 || !player[k].isRun()) {
3886                                                                         if (player[k].animTarget == jumpdownanim || player[k].animTarget == jumpupanim) {
3887                                                                             if (k == 0)
3888                                                                                 pause_sound(whooshsound);
3889                                                                         }
3890                                                                         emit_sound_at(jumpsound, player[k].coords, 128.);
3891
3892                                                                         lowpointtarget = DoRotation(objects.model[i].facenormals[whichhit], 0, objects.yaw[i], 0);
3893                                                                         player[k].yaw = -asin(0 - lowpointtarget.x) * 180 / M_PI;
3894                                                                         if (lowpointtarget.z < 0)
3895                                                                             player[k].yaw = 180 - player[k].yaw;
3896                                                                         player[k].targetyaw = player[k].yaw;
3897                                                                         player[k].lowyaw = player[k].yaw;
3898
3899                                                                         //player[k].velocity=lowpointtarget*.03;
3900                                                                         player[k].velocity = 0;
3901
3902                                                                         //climb ledge (?)
3903                                                                         if (player[k].animTarget == jumpupanim) {
3904                                                                             player[k].animTarget = climbanim;
3905                                                                             player[k].jumppower = 0;
3906                                                                             player[k].jumpclimb = 1;
3907                                                                         }
3908                                                                         player[k].transspeed = 6;
3909                                                                         player[k].target = 0;
3910                                                                         player[k].frameTarget = 1;
3911                                                                         //hang ledge (?)
3912                                                                         if (j > 25) {
3913                                                                             player[k].setAnimation(hanganim);
3914                                                                             player[k].jumppower = 0;
3915                                                                         }
3916                                                                     }
3917                                                                     break;
3918                                                                 }
3919                                                             }
3920                                                         }
3921                                         }
3922                                 }
3923                             }
3924                         }
3925                 }
3926             if (player[k].collide <= 0) {
3927                 //in the air
3928                 if (!player[k].onterrain &&
3929                         player[k].animTarget != jumpupanim &&
3930                         player[k].animTarget != jumpdownanim &&
3931                         player[k].animTarget != climbanim &&
3932                         player[k].animTarget != hanganim &&
3933                         !player[k].isWallJump() &&
3934                         !player[k].isFlip()) {
3935                     if (player[k].animCurrent != climbanim &&
3936                             player[k].animCurrent != tempanim &&
3937                             player[k].animTarget != backhandspringanim &&
3938                             (player[k].animTarget != rollanim ||
3939                              player[k].frameTarget < 2 ||
3940                              player[k].frameTarget > 6)) {
3941                         //stagger off ledge (?)
3942                         if (player[k].animTarget == staggerbackhighanim || player[k].animTarget == staggerbackhardanim)
3943                             player[k].RagDoll(0);
3944                         player[k].setAnimation(jumpdownanim);
3945
3946                         if (!k)
3947                             emit_sound_at(whooshsound, player[k].coords, 128.);
3948                     }
3949                     //gravity
3950                     player[k].velocity.y += gravity;
3951                 }
3952             }
3953         }
3954         player[k].realoldcoords = player[k].coords;
3955     }
3956 }
3957
3958 void doAttacks()
3959 {
3960     static XYZ relative;
3961     static int randattack;
3962     static bool playerrealattackkeydown = 0;
3963
3964     if (!Input::isKeyDown(attackkey))
3965         oldattackkey = 0;
3966     if (oldattackkey)
3967         player[0].attackkeydown = 0;
3968     if (oldattackkey)
3969         playerrealattackkeydown = 0;
3970     if (!oldattackkey)
3971         playerrealattackkeydown = Input::isKeyDown(attackkey);
3972     if ((player[0].parriedrecently <= 0 ||
3973             player[0].weaponactive == -1) &&
3974             (!oldattackkey ||
3975              (realthreat &&
3976               player[0].lastattack != swordslashanim &&
3977               player[0].lastattack != knifeslashstartanim &&
3978               player[0].lastattack != staffhitanim &&
3979               player[0].lastattack != staffspinhitanim)))
3980         player[0].attackkeydown = Input::isKeyDown(attackkey);
3981     if (Input::isKeyDown(attackkey) &&
3982             !oldattackkey &&
3983             !player[0].backkeydown) {
3984         for (int k = 0; k < numplayers; k++) {
3985             if ((player[k].animTarget == swordslashanim ||
3986                     player[k].animTarget == staffhitanim ||
3987                     player[k].animTarget == staffspinhitanim) &&
3988                     player[0].animCurrent != dodgebackanim &&
3989                     !player[k].skeleton.free)
3990                 player[k].Reverse();
3991         }
3992     }
3993
3994     if (!hostile || indialogue != -1)player[0].attackkeydown = 0;
3995
3996     for (int k = 0; k < numplayers; k++) {
3997         if (indialogue != -1)player[k].attackkeydown = 0;
3998         if (player[k].animTarget != rabbitrunninganim && player[k].animTarget != wolfrunninganim) {
3999             if (player[k].aitype != playercontrolled)
4000                 player[k].victim = &player[0];
4001             //attack key pressed
4002             if (player[k].attackkeydown) {
4003                 //dodge backward
4004                 if (player[k].backkeydown &&
4005                         player[k].animTarget != backhandspringanim &&
4006                         (player[k].isIdle() ||
4007                          player[k].isStop() ||
4008                          player[k].isRun() ||
4009                          player[k].animTarget == walkanim)) {
4010                     if (player[k].jumppower <= 1) {
4011                         player[k].jumppower -= 2;
4012                     } else {
4013                         for (int i = 0; i < numplayers; i++) {
4014                             if (i == k)continue;
4015                             if (player[i].animTarget == swordslashanim ||
4016                                     player[i].animTarget == knifeslashstartanim ||
4017                                     player[i].animTarget == staffhitanim ||
4018                                     player[i].animTarget == staffspinhitanim)
4019                                 if (distsq(&player[k].coords, &player[i].coords) < 6.5 && !player[i].skeleton.free) {
4020                                     player[k].setAnimation(dodgebackanim);
4021                                     player[k].targetyaw = roughDirectionTo(player[k].coords, player[i].coords);
4022                                     player[k].targettilt2 = pitchTo(player[k].coords, player[i].coords);
4023                                 }
4024                         }
4025                         if (player[k].animTarget != dodgebackanim) {
4026                             if (k == 0)numflipped++;
4027                             player[k].setAnimation(backhandspringanim);
4028                             player[k].targetyaw = -yaw + 180;
4029                             if (player[k].leftkeydown)
4030                                 player[k].targetyaw -= 45;
4031                             if (player[k].rightkeydown)
4032                                 player[k].targetyaw += 45;
4033                             player[k].yaw = player[k].targetyaw;
4034                             player[k].jumppower -= 2;
4035                         }
4036                     }
4037                 }
4038                 //attack
4039                 if (!animation[player[k].animTarget].attack &&
4040                         !player[k].backkeydown &&
4041                         (player[k].isIdle() ||
4042                          player[k].isRun() ||
4043                          player[k].animTarget == walkanim ||
4044                          player[k].animTarget == sneakanim ||
4045                          player[k].isCrouch())) {
4046                     const int attackweapon = player[k].weaponactive == -1 ? 0 : weapons[player[k].weaponids[player[k].weaponactive]].getType();
4047                     //normal attacks (?)
4048                     player[k].hasvictim = 0;
4049                     if (numplayers > 1)
4050                         for (int i = 0; i < numplayers; i++) {
4051                             if (i == k || !(k == 0 || i == 0))continue;
4052                             if (!player[k].hasvictim)
4053                                 if (animation[player[k].animTarget].attack != reversal) {
4054                                     //choose an attack
4055                                     const float distance = distsq(&player[k].coords, &player[i].coords);
4056                                     if (distance < 4.5 &&
4057                                             !player[i].skeleton.free &&
4058                                             player[i].howactive < typedead1 &&
4059                                             player[i].animTarget != jumpreversedanim &&
4060                                             player[i].animTarget != rabbitkickreversedanim &&
4061                                             player[i].animTarget != rabbitkickanim &&
4062                                             player[k].animTarget != rabbitkickanim &&
4063                                             player[i].animTarget != getupfrombackanim &&
4064                                             (player[i].animTarget != staggerbackhighanim &&
4065                                              (player[i].animTarget != staggerbackhardanim ||
4066                                               animation[staggerbackhardanim].label[player[i].frameTarget] == 6)) &&
4067                                             player[i].animTarget != jumpdownanim &&
4068                                             player[i].animTarget != jumpupanim &&
4069                                             player[i].animTarget != getupfromfrontanim) {
4070                                         player[k].victim = &player[i];
4071                                         player[k].hasvictim = 1;
4072                                         if (player[k].aitype == playercontrolled) { //human player
4073                                             //sweep
4074                                             if (distance < 2.5 * sq(player[k].scale * 5) &&
4075                                                     player[k].crouchkeydown &&
4076                                                     animation[player[i].animTarget].height != lowheight)
4077                                                 player[k].animTarget = sweepanim;
4078                                             //winduppunch
4079                                             else if (distance < 1.5 * sq(player[k].scale * 5) &&
4080                                                      animation[player[i].animTarget].height != lowheight &&
4081                                                      !player[k].forwardkeydown &&
4082                                                      !player[k].leftkeydown &&
4083                                                      !player[k].rightkeydown &&
4084                                                      !player[k].crouchkeydown &&
4085                                                      !attackweapon &&
4086                                                      !reversaltrain)
4087                                                 player[k].animTarget = winduppunchanim;
4088                                             //upunch
4089                                             else if (distance < 2.5 * sq(player[k].scale * 5) &&
4090                                                      animation[player[i].animTarget].height != lowheight &&
4091                                                      !player[k].forwardkeydown &&
4092                                                      !player[k].leftkeydown &&
4093                                                      !player[k].rightkeydown &&
4094                                                      !player[k].crouchkeydown &&
4095                                                      !attackweapon)
4096                                                 player[k].animTarget = upunchanim;
4097                                             //knifefollow
4098                                             else if (distance < 2.5 * sq(player[k].scale * 5) &&
4099                                                      player[i].staggerdelay > 0 &&
4100                                                      attackweapon == knife &&
4101                                                      player[i].bloodloss > player[i].damagetolerance / 2)
4102                                                 player[k].animTarget = knifefollowanim;
4103                                             //knifeslashstart
4104                                             else if (distance < 2.5 * sq(player[k].scale * 5) &&
4105                                                      animation[player[i].animTarget].height != lowheight &&
4106                                                      !player[k].forwardkeydown &&
4107                                                      !player[k].leftkeydown &&
4108                                                      !player[k].rightkeydown &&
4109                                                      !player[k].crouchkeydown &&
4110                                                      attackweapon == knife &&
4111                                                      player[k].weaponmissdelay <= 0)
4112                                                 player[k].animTarget = knifeslashstartanim;
4113                                             //swordslash
4114                                             else if (distance < 4.5 * sq(player[k].scale * 5) &&
4115                                                      animation[player[i].animTarget].height != lowheight &&
4116                                                      !player[k].crouchkeydown &&
4117                                                      attackweapon == sword &&
4118                                                      player[k].weaponmissdelay <= 0)
4119                                                 player[k].animTarget = swordslashanim;
4120                                             //staffhit
4121                                             else if (distance < 4.5 * sq(player[k].scale * 5) &&
4122                                                      animation[player[i].animTarget].height != lowheight &&
4123                                                      !player[k].crouchkeydown &&
4124                                                      attackweapon == staff &&
4125                                                      player[k].weaponmissdelay <= 0 &&
4126                                                      !player[k].leftkeydown &&
4127                                                      !player[k].rightkeydown &&
4128                                                      !player[k].forwardkeydown)
4129                                                 player[k].animTarget = staffhitanim;
4130                                             //staffspinhit
4131                                             else if (distance < 4.5 * sq(player[k].scale * 5) &&
4132                                                      animation[player[i].animTarget].height != lowheight &&
4133                                                      !player[k].crouchkeydown &&
4134                                                      attackweapon == staff &&
4135                                                      player[k].weaponmissdelay <= 0)
4136                                                 player[k].animTarget = staffspinhitanim;
4137                                             //spinkick
4138                                             else if (distance < 2.5 * sq(player[k].scale * 5) &&
4139                                                      animation[player[i].animTarget].height != lowheight)
4140                                                 player[k].animTarget = spinkickanim;
4141                                             //lowkick
4142                                             else if (distance < 2.5 * sq(player[k].scale * 5) &&
4143                                                      animation[player[i].animTarget].height == lowheight &&
4144                                                      animation[player[k].animTarget].attack != normalattack)
4145                                                 player[k].animTarget = lowkickanim;
4146                                         } else { //AI player
4147                                             if (distance < 4.5 * sq(player[k].scale * 5)) {
4148                                                 randattack = abs(Random() % 5);
4149                                                 if (!attackweapon && distance < 2.5 * sq(player[k].scale * 5)) {
4150                                                     //sweep
4151                                                     if (randattack == 0 && animation[player[i].animTarget].height != lowheight)
4152                                                         player[k].animTarget = sweepanim;
4153                                                     //upunch
4154                                                     else if (randattack == 1 && animation[player[i].animTarget].height != lowheight &&
4155                                                              !attackweapon)
4156                                                         player[k].animTarget = upunchanim;
4157                                                     //spinkick
4158                                                     else if (randattack == 2 && animation[player[i].animTarget].height != lowheight)
4159                                                         player[k].animTarget = spinkickanim;
4160                                                     //lowkick
4161                                                     else if (animation[player[i].animTarget].height == lowheight)
4162                                                         player[k].animTarget = lowkickanim;
4163                                                 }
4164                                                 if (attackweapon) {
4165                                                     //sweep
4166                                                     if ((tutoriallevel != 1 || !attackweapon) &&
4167                                                             distance < 2.5 * sq(player[k].scale * 5) &&
4168                                                             randattack == 0 &&
4169                                                             animation[player[i].animTarget].height != lowheight)
4170                                                         player[k].animTarget = sweepanim;
4171                                                     //knifeslashstart
4172                                                     else if (distance < 2.5 * sq(player[k].scale * 5) &&
4173                                                              attackweapon == knife &&
4174                                                              player[k].weaponmissdelay <= 0)
4175                                                         player[k].animTarget = knifeslashstartanim;
4176                                                     //swordslash
4177                                                     else if (!(player[0].victim == &player[i] &&
4178                                                                player[0].hasvictim &&
4179                                                                player[0].animTarget == swordslashanim) &&
4180                                                              attackweapon == sword &&
4181                                                              player[k].weaponmissdelay <= 0)
4182                                                         player[k].animTarget = swordslashanim;
4183                                                     //staffhit
4184                                                     else if (!(player[0].victim == &player[i] &&
4185                                                                player[0].hasvictim &&
4186                                                                player[0].animTarget == swordslashanim) &&
4187                                                              attackweapon == staff &&
4188                                                              player[k].weaponmissdelay <= 0 &&
4189                                                              randattack < 3)
4190                                                         player[k].animTarget = staffhitanim;
4191                                                     //staffspinhit
4192                                                     else if (!(player[0].victim == &player[i] &&
4193                                                                player[0].hasvictim &&
4194                                                                player[0].animTarget == swordslashanim) &&
4195                                                              attackweapon == staff &&
4196                                                              player[k].weaponmissdelay <= 0 &&
4197                                                              randattack >= 3)
4198                                                         player[k].animTarget = staffspinhitanim;
4199                                                     //spinkick
4200                                                     else if ((tutoriallevel != 1 || !attackweapon) &&
4201                                                              distance < 2.5 * sq(player[k].scale * 5) &&
4202                                                              randattack == 1 &&
4203                                                              animation[player[i].animTarget].height != lowheight)
4204                                                         player[k].animTarget = spinkickanim;
4205                                                     //lowkick
4206                                                     else if (distance < 2.5 * sq(player[k].scale * 5) &&
4207                                                              animation[player[i].animTarget].height == lowheight &&
4208                                                              animation[player[k].animTarget].attack != normalattack)
4209                                                         player[k].animTarget = lowkickanim;
4210                                                 }
4211                                             }
4212                                         }
4213                                         //upunch becomes wolfslap
4214                                         if (player[k].animTarget == upunchanim && player[k].creature == wolftype)
4215                                             player[k].animTarget = wolfslapanim;
4216                                     }
4217                                     //sneak attacks
4218                                     if ((k == 0) && (tutoriallevel != 1 || tutorialstage == 22) &&
4219                                             player[i].howactive < typedead1 &&
4220                                             distance < 1.5 * sq(player[k].scale * 5) &&
4221                                             !player[i].skeleton.free &&
4222                                             player[i].animTarget != getupfrombackanim &&
4223                                             player[i].animTarget != getupfromfrontanim &&
4224                                             (player[i].stunned > 0 && player[k].madskills ||
4225                                              player[i].surprised > 0 ||
4226                                              player[i].aitype == passivetype ||
4227                                              attackweapon && player[i].stunned > 0) &&
4228                                             normaldotproduct(player[i].facing, player[i].coords - player[k].coords) > 0) {
4229                                         //sneakattack
4230                                         if (!attackweapon) {
4231                                             player[k].animCurrent = sneakattackanim;
4232                                             player[k].animTarget = sneakattackanim;
4233                                             player[i].animCurrent = sneakattackedanim;
4234                                             player[i].animTarget = sneakattackedanim;
4235                                             player[k].oldcoords = player[k].coords;
4236                                             player[k].coords = player[i].coords;
4237                                         }
4238                                         //knifesneakattack
4239                                         if (attackweapon == knife) {
4240                                             player[k].animCurrent = knifesneakattackanim;
4241                                             player[k].animTarget = knifesneakattackanim;
4242                                             player[i].animCurrent = knifesneakattackedanim;
4243                                             player[i].animTarget = knifesneakattackedanim;
4244                                             player[i].oldcoords = player[i].coords;
4245                                             player[i].coords = player[k].coords;
4246                                         }
4247                                         //swordsneakattack
4248                                         if (attackweapon == sword) {
4249                                             player[k].animCurrent = swordsneakattackanim;
4250                                             player[k].animTarget = swordsneakattackanim;
4251                                             player[i].animCurrent = swordsneakattackedanim;
4252                                             player[i].animTarget = swordsneakattackedanim;
4253                                             player[i].oldcoords = player[i].coords;
4254                                             player[i].coords = player[k].coords;
4255                                         }
4256                                         if (attackweapon != staff) {
4257                                             player[k].victim = &player[i];
4258                                             player[k].hasvictim = 1;
4259                                             player[i].targettilt2 = 0;
4260                                             player[i].frameTarget = 1;
4261                                             player[i].frameCurrent = 0;
4262                                             player[i].target = 0;
4263                                             player[i].velocity = 0;
4264                                             player[k].targettilt2 = player[i].targettilt2;
4265                                             player[k].frameCurrent = player[i].frameCurrent;
4266                                             player[k].frameTarget = player[i].frameTarget;
4267                                             player[k].target = player[i].target;
4268                                             player[k].velocity = 0;
4269                                             player[k].targetyaw = player[i].yaw;
4270                                             player[k].yaw = player[i].yaw;
4271                                             player[i].targetyaw = player[i].yaw;
4272                                         }
4273                                     }
4274                                     if (animation[player[k].animTarget].attack == normalattack &&
4275                                             player[k].victim == &player[i] &&
4276                                             (!player[i].skeleton.free)) {
4277                                         oldattackkey = 1;
4278                                         player[k].frameTarget = 0;
4279                                         player[k].target = 0;
4280
4281                                         player[k].targetyaw = roughDirectionTo(player[k].coords, player[i].coords);
4282                                         player[k].targettilt2 = pitchTo(player[k].coords, player[i].coords);
4283                                         player[k].lastattack3 = player[k].lastattack2;
4284                                         player[k].lastattack2 = player[k].lastattack;
4285                                         player[k].lastattack = player[k].animTarget;
4286                                     }
4287                                     if (player[k].animTarget == knifefollowanim &&
4288                                             player[k].victim == &player[i]) {
4289                                         oldattackkey = 1;
4290                                         player[k].targetyaw = roughDirectionTo(player[k].coords, player[i].coords);
4291                                         player[k].targettilt2 = pitchTo(player[k].coords, player[i].coords);
4292                                         player[k].victim = &player[i];
4293                                         player[k].hasvictim = 1;
4294                                         player[i].animTarget = knifefollowedanim;
4295                                         player[i].animCurrent = knifefollowedanim;
4296                                         player[i].targettilt2 = 0;
4297                                         player[i].targettilt2 = player[k].targettilt2;
4298                                         player[i].frameTarget = 1;
4299                                         player[i].frameCurrent = 0;
4300                                         player[i].target = 0;
4301                                         player[i].velocity = 0;
4302                                         player[k].animCurrent = knifefollowanim;
4303                                         player[k].animTarget = knifefollowanim;
4304                                         player[k].targettilt2 = player[i].targettilt2;
4305                                         player[k].frameCurrent = player[i].frameCurrent;
4306                                         player[k].frameTarget = player[i].frameTarget;
4307                                         player[k].target = player[i].target;
4308                                         player[k].velocity = 0;
4309                                         player[k].oldcoords = player[k].coords;
4310                                         player[i].coords = player[k].coords;
4311                                         player[i].targetyaw = player[k].targetyaw;
4312                                         player[i].yaw = player[k].targetyaw;
4313                                         player[k].yaw = player[k].targetyaw;
4314                                         player[i].yaw = player[k].targetyaw;
4315                                     }
4316                                 }
4317                         }
4318                     const bool hasstaff = attackweapon == staff;
4319                     if (k == 0 && numplayers > 1)
4320                         for (int i = 0; i < numplayers; i++) {
4321                             if (i == k)continue;
4322                             if ((playerrealattackkeydown || player[i].dead || !hasstaff) &&
4323                                     animation[player[k].animTarget].attack == neutral) {
4324                                 const float distance = distsq(&player[k].coords, &player[i].coords);
4325                                 if (!player[i].dead || !realthreat || (!attackweapon && player[k].crouchkeydown))
4326                                     if (player[i].skeleton.free)
4327                                         if (distance < 3.5 * sq(player[k].scale * 5) &&
4328                                                 (player[i].dead ||
4329                                                  player[i].skeleton.longdead > 1000 ||
4330                                                  player[k].isRun() ||
4331                                                  hasstaff ||
4332                                                  (attackweapon &&
4333                                                   (player[i].skeleton.longdead > 2000 ||
4334                                                    player[i].damage > player[i].damagetolerance / 8 ||
4335                                                    player[i].bloodloss > player[i].damagetolerance / 2) &&
4336                                                   distance < 1.5 * sq(player[k].scale * 5)))) {
4337                                             player[k].victim = &player[i];
4338                                             player[k].hasvictim = 1;
4339                                             if (attackweapon && tutoriallevel != 1) {
4340                                                 //crouchstab
4341                                                 if (player[k].crouchkeydown && attackweapon == knife && distance < 1.5 * sq(player[k].scale * 5))
4342                                                     player[k].animTarget = crouchstabanim;
4343                                                 //swordgroundstab
4344                                                 if (player[k].crouchkeydown && distance < 1.5 * sq(player[k].scale * 5) && attackweapon == sword)
4345                                                     player[k].animTarget = swordgroundstabanim;
4346                                                 //staffgroundsmash
4347                                                 if (distance < 3.5 * sq(player[k].scale * 5) && attackweapon == staff)
4348                                                     player[k].animTarget = staffgroundsmashanim;
4349                                             }
4350                                             if (distance < 2.5 &&
4351                                                     player[k].crouchkeydown &&
4352                                                     player[k].animTarget != crouchstabanim &&
4353                                                     !attackweapon &&
4354                                                     player[i].dead &&
4355                                                     player[i].skeleton.free &&
4356                                                     player[i].skeleton.longdead > 1000) {
4357                                                 player[k].animTarget = killanim;
4358                                                 //TODO: refactor this out, what does it do?
4359                                                 for (int j = 0; j < terrain.numdecals; j++) {
4360                                                     if ((terrain.decaltype[j] == blooddecal || terrain.decaltype[j] == blooddecalslow) &&
4361                                                             terrain.decalalivetime[j] < 2)
4362                                                         terrain.DeleteDecal(j);
4363                                                 }
4364                                                 for (int l = 0; l < objects.numobjects; l++) {
4365                                                     if (objects.model[l].type == decalstype)
4366                                                         for (int j = 0; j < objects.model[l].numdecals; j++) {
4367                                                             if ((objects.model[l].decaltype[j] == blooddecal ||
4368                                                                     objects.model[l].decaltype[j] == blooddecalslow) &&
4369                                                                     objects.model[l].decalalivetime[j] < 2)
4370                                                                 objects.model[l].DeleteDecal(j);
4371                                                         }
4372                                                 }
4373                                             }
4374                                             if (!player[i].dead || musictype != 2)
4375                                                 if (distance < 3.5 &&
4376                                                         (player[k].isRun() || player[k].isIdle() && player[k].attackkeydown) &&
4377                                                         player[k].staggerdelay <= 0 &&
4378                                                         (player[i].dead ||
4379                                                          player[i].skeleton.longdead < 300 &&
4380                                                          player[k].lastattack != spinkickanim &&
4381                                                          player[i].skeleton.free) &&
4382                                                         (!player[i].dead || musictype != stream_fighttheme)) {
4383                                                     player[k].animTarget = dropkickanim;
4384                                                     for (int j = 0; j < terrain.numdecals; j++) {
4385                                                         if ((terrain.decaltype[j] == blooddecal || terrain.decaltype[j] == blooddecalslow) &&
4386                                                                 terrain.decalalivetime[j] < 2) {
4387                                                             terrain.DeleteDecal(j);
4388                                                         }
4389                                                     }
4390                                                     for (int l = 0; l < objects.numobjects; l++) {
4391                                                         if (objects.model[l].type == decalstype)
4392                                                             for (int j = 0; j < objects.model[l].numdecals; j++) {
4393                                                                 if ((objects.model[l].decaltype[j] == blooddecal ||
4394                                                                         objects.model[l].decaltype[j] == blooddecalslow) &&
4395                                                                         objects.model[l].decalalivetime[j] < 2) {
4396                                                                     objects.model[l].DeleteDecal(j);
4397                                                                 }
4398                                                             }
4399                                                     }
4400                                                 }
4401                                         }
4402                                 if (animation[player[k].animTarget].attack == normalattack &&
4403                                         player[k].victim == &player[i] &&
4404                                         (!player[i].skeleton.free ||
4405                                          player[k].animTarget == killanim ||
4406                                          player[k].animTarget == crouchstabanim ||
4407                                          player[k].animTarget == swordgroundstabanim ||
4408                                          player[k].animTarget == staffgroundsmashanim ||
4409                                          player[k].animTarget == dropkickanim)) {
4410                                     oldattackkey = 1;
4411                                     player[k].frameTarget = 0;
4412                                     player[k].target = 0;
4413
4414                                     XYZ targetpoint = player[i].coords;
4415                                     if (player[k].animTarget == crouchstabanim ||
4416                                             player[k].animTarget == swordgroundstabanim ||
4417                                             player[k].animTarget == staffgroundsmashanim) {
4418                                         targetpoint += (player[i].jointPos(abdomen) +
4419                                                         player[i].jointPos(neck)) / 2 *
4420                                                        player[i].scale;
4421                                     }
4422                                     player[k].targetyaw = roughDirectionTo(player[k].coords, targetpoint);
4423                                     player[k].targettilt2 = pitchTo(player[k].coords, targetpoint);
4424
4425                                     if (player[k].animTarget == crouchstabanim || player[k].animTarget == swordgroundstabanim) {
4426                                         player[k].targetyaw += (float)(abs(Random() % 100) - 50) / 4;
4427                                     }
4428
4429                                     if (player[k].animTarget == staffgroundsmashanim)
4430                                         player[k].targettilt2 += 10;
4431
4432                                     player[k].lastattack3 = player[k].lastattack2;
4433                                     player[k].lastattack2 = player[k].lastattack;
4434                                     player[k].lastattack = player[k].animTarget;
4435
4436                                     if (player[k].animTarget == swordgroundstabanim) {
4437                                         player[k].targetyaw += 30;
4438                                     }
4439                                 }
4440                             }
4441                         }
4442                     if (!player[k].hasvictim) {
4443                         //find victim
4444                         for (int i = 0; i < numplayers; i++) {
4445                             if (i == k || !(i == 0 || k == 0))continue;
4446                             if (!player[i].skeleton.free) {
4447                                 if (player[k].hasvictim) {
4448                                     if (distsq(&player[k].coords, &player[i].coords) <
4449                                             distsq(&player[k].coords, &player[k].victim->coords))
4450                                         player[k].victim = &player[i];
4451                                 } else {
4452                                     player[k].victim = &player[i];
4453                                     player[k].hasvictim = 1;
4454                                 }
4455                             }
4456                         }
4457                     }
4458                     if (player[k].aitype == playercontrolled)
4459                         //rabbit kick
4460                         if (player[k].attackkeydown &&
4461                                 player[k].isRun() &&
4462                                 player[k].wasRun() &&
4463                                 ((player[k].hasvictim &&
4464                                   distsq(&player[k].coords, &player[k].victim->coords) < 12 * sq(player[k].scale * 5) &&
4465                                   distsq(&player[k].coords, &player[k].victim->coords) > 7 * sq(player[k].scale * 5) &&
4466                                   !player[k].victim->skeleton.free &&
4467                                   player[k].victim->animTarget != getupfrombackanim &&
4468                                   player[k].victim->animTarget != getupfromfrontanim &&
4469                                   animation[player[k].victim->animTarget].height != lowheight &&
4470                                   player[k].aitype != playercontrolled && //wat???
4471                                   normaldotproduct(player[k].facing, player[k].victim->coords - player[k].coords) > 0 &&
4472                                   player[k].rabbitkickenabled) ||
4473                                  player[k].jumpkeydown)) {
4474                             oldattackkey = 1;
4475                             player[k].setAnimation(rabbitkickanim);
4476                         }
4477                     //update counts
4478                     if (animation[player[k].animTarget].attack && k == 0) {
4479                         numattacks++;
4480                         switch (attackweapon) {
4481                         case 0:
4482                             numunarmedattack++;
4483                             break;
4484                         case knife:
4485                             numknifeattack++;
4486                             break;
4487                         case sword:
4488                             numswordattack++;
4489                             break;
4490                         case staff:
4491                             numstaffattack++;
4492                             break;
4493                         }
4494                     }
4495                 }
4496             }
4497         }
4498     }
4499 }
4500
4501 void doPlayerCollisions()
4502 {
4503     static XYZ rotatetarget;
4504     static float collisionradius;
4505     if (numplayers > 1)
4506         for (int k = 0; k < numplayers; k++)
4507             for (int i = k + 1; i < numplayers; i++) {
4508                 //neither player is part of a reversal
4509                 if ((animation[player[i].animTarget].attack != reversed &&
4510                         animation[player[i].animTarget].attack != reversal &&
4511                         animation[player[k].animTarget].attack != reversed &&
4512                         animation[player[k].animTarget].attack != reversal) || (i != 0 && k != 0))
4513                     if ((animation[player[i].animCurrent].attack != reversed &&
4514                             animation[player[i].animCurrent].attack != reversal &&
4515                             animation[player[k].animCurrent].attack != reversed &&
4516                             animation[player[k].animCurrent].attack != reversal) || (i != 0 && k != 0))
4517                         //neither is sleeping
4518                         if (player[i].howactive <= typesleeping && player[k].howactive <= typesleeping)
4519                             if (player[i].howactive != typesittingwall && player[k].howactive != typesittingwall)
4520                                 //in same patch, neither is climbing
4521                                 if (player[i].whichpatchx == player[k].whichpatchx &&
4522                                         player[i].whichpatchz == player[k].whichpatchz &&
4523                                         player[k].skeleton.oldfree == player[k].skeleton.free &&
4524                                         player[i].skeleton.oldfree == player[i].skeleton.free &&
4525                                         player[i].animTarget != climbanim &&
4526                                         player[i].animTarget != hanganim &&
4527                                         player[k].animTarget != climbanim &&
4528                                         player[k].animTarget != hanganim)
4529                                     //players are close (bounding box test)
4530                                     if (player[i].coords.y > player[k].coords.y - 3)
4531                                         if (player[i].coords.y < player[k].coords.y + 3)
4532                                             if (player[i].coords.x > player[k].coords.x - 3)
4533                                                 if (player[i].coords.x < player[k].coords.x + 3)
4534                                                     if (player[i].coords.z > player[k].coords.z - 3)
4535                                                         if (player[i].coords.z < player[k].coords.z + 3) {
4536                                                             //spread fire from player to player
4537                                                             if (distsq(&player[i].coords, &player[k].coords)
4538                                                                     < 3 * sq((player[i].scale + player[k].scale) * 2.5)) {
4539                                                                 if (player[i].onfire || player[k].onfire) {
4540                                                                     if (!player[i].onfire)player[i].CatchFire();
4541                                                                     if (!player[k].onfire)player[k].CatchFire();
4542                                                                 }
4543                                                             }
4544
4545                                                             XYZ tempcoords1 = player[i].coords;
4546                                                             XYZ tempcoords2 = player[k].coords;
4547                                                             if (!player[i].skeleton.oldfree)
4548                                                                 tempcoords1.y += player[i].jointPos(abdomen).y * player[i].scale;
4549                                                             if (!player[k].skeleton.oldfree)
4550                                                                 tempcoords2.y += player[k].jointPos(abdomen).y * player[k].scale;
4551                                                             collisionradius = 1.2 * sq((player[i].scale + player[k].scale) * 2.5);
4552                                                             if (player[0].hasvictim)
4553                                                                 if (player[0].animTarget == rabbitkickanim && (k == 0 || i == 0) && !player[0].victim->skeleton.free)
4554                                                                     collisionradius = 3;
4555                                                             if ((!player[i].skeleton.oldfree || !player[k].skeleton.oldfree) &&
4556                                                                     (distsq(&tempcoords1, &tempcoords2) < collisionradius ||
4557                                                                      distsq(&player[i].coords, &player[k].coords) < collisionradius)) {
4558                                                                 //jump down on a dead body
4559                                                                 if (k == 0 || i == 0) {
4560                                                                     int l = i ? i : k;
4561                                                                     if (player[0].animTarget == jumpdownanim &&
4562                                                                             !player[0].skeleton.oldfree &&
4563                                                                             !player[0].skeleton.free &&
4564                                                                             player[l].skeleton.oldfree &&
4565                                                                             player[l].skeleton.free &&
4566                                                                             player[l].dead &&
4567                                                                             player[0].lastcollide <= 0 &&
4568                                                                             fabs(player[l].coords.y - player[0].coords.y) < .2 &&
4569                                                                             distsq(&player[0].coords, &player[l].coords) < .7 * sq((player[l].scale + player[0].scale) * 2.5)) {
4570                                                                         player[0].coords.y = player[l].coords.y;
4571                                                                         player[l].velocity = player[0].velocity;
4572                                                                         player[l].skeleton.free = 0;
4573                                                                         player[l].yaw = 0;
4574                                                                         player[l].RagDoll(0);
4575                                                                         player[l].DoDamage(20);
4576                                                                         camerashake += .3;
4577                                                                         player[l].skeleton.longdead = 0;
4578                                                                         player[0].lastcollide = 1;
4579                                                                     }
4580                                                                 }
4581
4582                                                                 if (     (player[i].skeleton.oldfree == 1 && findLengthfast(&player[i].velocity) > 1) ||
4583                                                                          (player[k].skeleton.oldfree == 1 && findLengthfast(&player[k].velocity) > 1) ||
4584                                                                          (player[i].skeleton.oldfree == 0 && player[k].skeleton.oldfree == 0)) {
4585                                                                     rotatetarget = player[k].velocity - player[i].velocity;
4586                                                                     if ((player[i].animTarget != getupfrombackanim && player[i].animTarget != getupfromfrontanim ||
4587                                                                             player[i].skeleton.free) &&
4588                                                                             (player[k].animTarget != getupfrombackanim && player[k].animTarget != getupfromfrontanim ||
4589                                                                              player[k].skeleton.free))
4590                                                                         if ((((k != 0 && findLengthfast(&rotatetarget) > 150 ||
4591                                                                                 k == 0 && findLengthfast(&rotatetarget) > 50 && player[0].rabbitkickragdoll) &&
4592                                                                                 normaldotproduct(rotatetarget, player[k].coords - player[i].coords) > 0) &&
4593                                                                                 (k == 0 ||
4594                                                                                  k != 0 && player[i].skeleton.oldfree == 1 && animation[player[k].animCurrent].attack == neutral ||
4595                                                                                  /*i!=0&&*/player[k].skeleton.oldfree == 1 && animation[player[i].animCurrent].attack == neutral)) ||
4596                                                                                 (player[i].animTarget == jumpupanim || player[i].animTarget == jumpdownanim || player[i].isFlip()) &&
4597                                                                                 (player[k].animTarget == jumpupanim || player[k].animTarget == jumpdownanim || player[k].isFlip()) &&
4598                                                                                 k == 0 && !player[i].skeleton.oldfree && !player[k].skeleton.oldfree) {
4599                                                                             //If hit by body
4600                                                                             if (     (i != 0 || player[i].skeleton.free) &&
4601                                                                                      (k != 0 || player[k].skeleton.free) ||
4602                                                                                      (animation[player[i].animTarget].height == highheight &&
4603                                                                                       animation[player[k].animTarget].height == highheight)) {
4604                                                                                 if (tutoriallevel != 1) {
4605                                                                                     emit_sound_at(heavyimpactsound, player[i].coords);
4606                                                                                 }
4607
4608                                                                                 player[i].RagDoll(0);
4609                                                                                 if (player[i].damage > player[i].damagetolerance - findLengthfast(&rotatetarget) / 4 && !player[i].dead) {
4610                                                                                     award_bonus(0, aimbonus);
4611                                                                                 }
4612                                                                                 player[i].DoDamage(findLengthfast(&rotatetarget) / 4);
4613                                                                                 player[k].RagDoll(0);
4614                                                                                 if (player[k].damage > player[k].damagetolerance - findLengthfast(&rotatetarget) / 4 && !player[k].dead) {
4615                                                                                     award_bonus(0, aimbonus); // Huh, again?
4616                                                                                 }
4617                                                                                 player[k].DoDamage(findLengthfast(&rotatetarget) / 4);
4618
4619                                                                                 for (int j = 0; j < player[i].skeleton.num_joints; j++) {
4620                                                                                     player[i].skeleton.joints[j].velocity = player[i].skeleton.joints[j].velocity / 5 + player[k].velocity;
4621                                                                                 }
4622                                                                                 for (int j = 0; j < player[k].skeleton.num_joints; j++) {
4623                                                                                     player[k].skeleton.joints[j].velocity = player[k].skeleton.joints[j].velocity / 5 + player[i].velocity;
4624                                                                                 }
4625
4626                                                                             }
4627                                                                         }
4628                                                                     if (     (animation[player[i].animTarget].attack == neutral ||
4629                                                                               animation[player[i].animTarget].attack == normalattack) &&
4630                                                                              (animation[player[k].animTarget].attack == neutral ||
4631                                                                               animation[player[k].animTarget].attack == normalattack)) {
4632                                                                         //If bumped
4633                                                                         if (player[i].skeleton.oldfree == 0 && player[k].skeleton.oldfree == 0) {
4634                                                                             if (distsq(&player[k].coords, &player[i].coords) < .5 * sq((player[i].scale + player[k].scale) * 2.5)) {
4635                                                                                 rotatetarget = player[k].coords - player[i].coords;
4636                                                                                 Normalise(&rotatetarget);
4637                                                                                 player[k].coords = (player[k].coords + player[i].coords) / 2;
4638                                                                                 player[i].coords = player[k].coords - rotatetarget * fast_sqrt(.6) / 2
4639                                                                                                    * sq((player[i].scale + player[k].scale) * 2.5);
4640                                                                                 player[k].coords += rotatetarget * fast_sqrt(.6) / 2 * sq((player[i].scale + player[k].scale) * 2.5);
4641                                                                                 if (player[k].howactive == typeactive || hostile)
4642                                                                                     if (player[k].isIdle()) {
4643                                                                                         if (player[k].howactive < typesleeping)
4644                                                                                             player[k].setAnimation(player[k].getStop());
4645                                                                                         else if (player[k].howactive == typesleeping)
4646                                                                                             player[k].setAnimation(getupfromfrontanim);
4647                                                                                         if (!editorenabled)
4648                                                                                             player[k].howactive = typeactive;
4649                                                                                     }
4650                                                                                 if (player[i].howactive == typeactive || hostile)
4651                                                                                     if (player[i].isIdle()) {
4652                                                                                         if (player[i].howactive < typesleeping)
4653                                                                                             player[i].setAnimation(player[k].getStop());
4654                                                                                         else
4655                                                                                             player[i].setAnimation(getupfromfrontanim);
4656                                                                                         if (!editorenabled)
4657                                                                                             player[i].howactive = typeactive;
4658                                                                                     }
4659                                                                             }
4660                                                                             //jump down on player
4661                                                                             if (hostile) {
4662                                                                                 if (k == 0 && i != 0 && player[k].animTarget == jumpdownanim &&
4663                                                                                         !player[i].isCrouch() &&
4664                                                                                         player[i].animTarget != rollanim &&
4665                                                                                         !player[k].skeleton.oldfree && !
4666                                                                                         player[k].skeleton.free &&
4667                                                                                         player[k].lastcollide <= 0 &&
4668                                                                                         player[k].velocity.y < -10) {
4669                                                                                     player[i].velocity = player[k].velocity;
4670                                                                                     player[k].velocity = player[k].velocity * -.5;
4671                                                                                     player[k].velocity.y = player[i].velocity.y;
4672                                                                                     player[i].DoDamage(20);
4673                                                                                     player[i].RagDoll(0);
4674                                                                                     player[k].lastcollide = 1;
4675                                                                                     award_bonus(k, AboveBonus);
4676                                                                                 }
4677                                                                                 if (i == 0 && k != 0 && player[i].animTarget == jumpdownanim &&
4678                                                                                         !player[k].isCrouch() &&
4679                                                                                         player[k].animTarget != rollanim &&
4680                                                                                         !player[i].skeleton.oldfree &&
4681                                                                                         !player[i].skeleton.free &&
4682                                                                                         player[i].lastcollide <= 0 &&
4683                                                                                         player[i].velocity.y < -10) {
4684                                                                                     player[k].velocity = player[i].velocity;
4685                                                                                     player[i].velocity = player[i].velocity * -.3;
4686                                                                                     player[i].velocity.y = player[k].velocity.y;
4687                                                                                     player[k].DoDamage(20);
4688                                                                                     player[k].RagDoll(0);
4689                                                                                     player[i].lastcollide = 1;
4690                                                                                     award_bonus(i, AboveBonus);
4691                                                                                 }
4692                                                                             }
4693                                                                         }
4694                                                                     }
4695                                                                 }
4696                                                                 player[i].CheckKick();
4697                                                                 player[k].CheckKick();
4698                                                             }
4699                                                         }
4700             }
4701 }
4702
4703 void doAI(int i)
4704 {
4705     static bool connected;
4706     if (player[i].aitype != playercontrolled && indialogue == -1) {
4707         player[i].jumpclimb = 0;
4708         //disable movement in editor
4709         if (editorenabled)
4710             player[i].stunned = 1;
4711
4712         player[i].pause = 0;
4713         if (distsqflat(&player[0].coords, &player[i].coords) < 30 &&
4714                 player[0].coords.y > player[i].coords.y + 2 &&
4715                 !player[0].onterrain)
4716             player[i].pause = 1;
4717
4718         //pathfinding
4719         if (player[i].aitype == pathfindtype) {
4720             if (player[i].finalpathfindpoint == -1) {
4721                 float closestdistance;
4722                 float tempdist;
4723                 int closest;
4724                 XYZ colpoint;
4725                 closest = -1;
4726                 closestdistance = -1;
4727                 for (int j = 0; j < numpathpoints; j++)
4728                     if (closest == -1 || distsq(&player[i].finalfinaltarget, &pathpoint[j]) < closestdistance) {
4729                         closestdistance = distsq(&player[i].finalfinaltarget, &pathpoint[j]);
4730                         closest = j;
4731                         player[i].finaltarget = pathpoint[j];
4732                     }
4733                 player[i].finalpathfindpoint = closest;
4734                 for (int j = 0; j < numpathpoints; j++)
4735                     for (int k = 0; k < numpathpointconnect[j]; k++) {
4736                         DistancePointLine(&player[i].finalfinaltarget, &pathpoint[j], &pathpoint[pathpointconnect[j][k]], &tempdist, &colpoint );
4737                         if (sq(tempdist) < closestdistance)
4738                             if (findDistance(&colpoint, &pathpoint[j]) + findDistance(&colpoint, &pathpoint[pathpointconnect[j][k]]) <
4739                                     findDistance(&pathpoint[j], &pathpoint[pathpointconnect[j][k]]) + .1) {
4740                                 closestdistance = sq(tempdist);
4741                                 closest = j;
4742                                 player[i].finaltarget = colpoint;
4743                             }
4744                     }
4745                 player[i].finalpathfindpoint = closest;
4746
4747             }
4748             if (player[i].targetpathfindpoint == -1) {
4749                 float closestdistance;
4750                 float tempdist;
4751                 int closest;
4752                 XYZ colpoint;
4753                 closest = -1;
4754                 closestdistance = -1;
4755                 if (player[i].lastpathfindpoint == -1) {
4756                     for (int j = 0; j < numpathpoints; j++) {
4757                         if (j != player[i].lastpathfindpoint)
4758                             if (closest == -1 || (distsq(&player[i].coords, &pathpoint[j]) < closestdistance)) {
4759                                 closestdistance = distsq(&player[i].coords, &pathpoint[j]);
4760                                 closest = j;
4761                             }
4762                     }
4763                     player[i].targetpathfindpoint = closest;
4764                     for (int j = 0; j < numpathpoints; j++)
4765                         if (j != player[i].lastpathfindpoint)
4766                             for (int k = 0; k < numpathpointconnect[j]; k++) {
4767                                 DistancePointLine(&player[i].coords, &pathpoint[j], &pathpoint[pathpointconnect[j][k]], &tempdist, &colpoint );
4768                                 if (sq(tempdist) < closestdistance) {
4769                                     if (findDistance(&colpoint, &pathpoint[j]) + findDistance(&colpoint, &pathpoint[pathpointconnect[j][k]]) <
4770                                             findDistance(&pathpoint[j], &pathpoint[pathpointconnect[j][k]]) + .1) {
4771                                         closestdistance = sq(tempdist);
4772                                         closest = j;
4773                                     }
4774                                 }
4775                             }
4776                     player[i].targetpathfindpoint = closest;
4777                 } else {
4778                     for (int j = 0; j < numpathpoints; j++)
4779                         if (j != player[i].lastpathfindpoint &&
4780                                 j != player[i].lastpathfindpoint2 &&
4781                                 j != player[i].lastpathfindpoint3 &&
4782                                 j != player[i].lastpathfindpoint4) {
4783                             connected = 0;
4784                             if (numpathpointconnect[j])
4785                                 for (int k = 0; k < numpathpointconnect[j]; k++)
4786                                     if (pathpointconnect[j][k] == player[i].lastpathfindpoint)
4787                                         connected = 1;
4788                             if (!connected)
4789                                 if (numpathpointconnect[player[i].lastpathfindpoint])
4790                                     for (int k = 0; k < numpathpointconnect[player[i].lastpathfindpoint]; k++)
4791                                         if (pathpointconnect[player[i].lastpathfindpoint][k] == j)
4792                                             connected = 1;
4793                             if (connected) {
4794                                 tempdist = findPathDist(j, player[i].finalpathfindpoint);
4795                                 if (closest == -1 || tempdist < closestdistance) {
4796                                     closestdistance = tempdist;
4797                                     closest = j;
4798                                 }
4799                             }
4800                         }
4801                     player[i].targetpathfindpoint = closest;
4802                 }
4803             }
4804             player[i].losupdatedelay -= multiplier;
4805
4806             player[i].targetyaw = roughDirectionTo(player[i].coords, pathpoint[player[i].targetpathfindpoint]);
4807             player[i].lookyaw = player[i].targetyaw;
4808
4809             //reached target point
4810             if (distsqflat(&player[i].coords, &pathpoint[player[i].targetpathfindpoint]) < .6) {
4811                 player[i].lastpathfindpoint4 = player[i].lastpathfindpoint3;
4812                 player[i].lastpathfindpoint3 = player[i].lastpathfindpoint2;
4813                 player[i].lastpathfindpoint2 = player[i].lastpathfindpoint;
4814                 player[i].lastpathfindpoint = player[i].targetpathfindpoint;
4815                 if (player[i].lastpathfindpoint2 == -1)
4816                     player[i].lastpathfindpoint2 = player[i].lastpathfindpoint;
4817                 if (player[i].lastpathfindpoint3 == -1)
4818                     player[i].lastpathfindpoint3 = player[i].lastpathfindpoint2;
4819                 if (player[i].lastpathfindpoint4 == -1)
4820                     player[i].lastpathfindpoint4 = player[i].lastpathfindpoint3;
4821                 player[i].targetpathfindpoint = -1;
4822             }
4823             if (     distsqflat(&player[i].coords, &player[i].finalfinaltarget) <
4824                      distsqflat(&player[i].coords, &player[i].finaltarget) ||
4825                      distsqflat(&player[i].coords, &player[i].finaltarget) < .6 * sq(player[i].scale * 5) ||
4826                      player[i].lastpathfindpoint == player[i].finalpathfindpoint) {
4827                 player[i].aitype = passivetype;
4828             }
4829
4830             player[i].forwardkeydown = 1;
4831             player[i].leftkeydown = 0;
4832             player[i].backkeydown = 0;
4833             player[i].rightkeydown = 0;
4834             player[i].crouchkeydown = 0;
4835             player[i].attackkeydown = 0;
4836             player[i].throwkeydown = 0;
4837
4838             if (player[i].avoidcollided > .8 && !player[i].jumpkeydown && player[i].collided < .8)
4839                 player[i].targetyaw += 90 * (player[i].whichdirection * 2 - 1);
4840
4841             if (player[i].collided < 1 || player[i].animTarget != jumpupanim)
4842                 player[i].jumpkeydown = 0;
4843             if ((player[i].collided > .8 && player[i].jumppower >= 5))
4844                 player[i].jumpkeydown = 1;
4845
4846             if ((tutoriallevel != 1 || cananger) &&
4847                     hostile &&
4848                     !player[0].dead &&
4849                     distsq(&player[i].coords, &player[0].coords) < 400 &&
4850                     player[i].occluded < 25) {
4851                 if (distsq(&player[i].coords, &player[0].coords) < 12 &&
4852                         animation[player[0].animTarget].height != lowheight &&
4853                         !editorenabled &&
4854                         (player[0].coords.y < player[i].coords.y + 5 || player[0].onterrain))
4855                     player[i].aitype = attacktypecutoff;
4856                 if (distsq(&player[i].coords, &player[0].coords) < 30 &&
4857                         animation[player[0].animTarget].height == highheight &&
4858                         !editorenabled)
4859                     player[i].aitype = attacktypecutoff;
4860
4861                 if (player[i].losupdatedelay < 0 && !editorenabled && player[i].occluded < 2) {
4862                     player[i].losupdatedelay = .2;
4863                     for (int j = 0; j < numplayers; j++)
4864                         if (j == 0 || player[j].skeleton.free || player[j].aitype != passivetype)
4865                             if (abs(Random() % 2) || animation[player[j].animTarget].height != lowheight || j != 0)
4866                                 if (distsq(&player[i].coords, &player[j].coords) < 400)
4867                                     if (normaldotproduct(player[i].facing, player[j].coords - player[i].coords) > 0)
4868                                         if (player[j].coords.y < player[i].coords.y + 5 || player[j].onterrain)
4869                                             if (!player[j].isWallJump() && -1 == checkcollide(
4870                                                         DoRotation(player[i].jointPos(head), 0, player[i].yaw, 0)
4871                                                         *player[i].scale + player[i].coords,
4872                                                         DoRotation(player[j].jointPos(head), 0, player[j].yaw, 0)
4873                                                         *player[j].scale + player[j].coords) ||
4874                                                     (player[j].animTarget == hanganim &&
4875                                                      normaldotproduct(player[j].facing, player[i].coords - player[j].coords) < 0)) {
4876                                                 player[i].aitype = searchtype;
4877                                                 player[i].lastchecktime = 12;
4878                                                 player[i].lastseen = player[j].coords;
4879                                                 player[i].lastseentime = 12;
4880                                             }
4881                 }
4882             }
4883             if (player[i].aitype == attacktypecutoff && musictype != 2)
4884                 if (player[i].creature != wolftype) {
4885                     player[i].stunned = .6;
4886                     player[i].surprised = .6;
4887                 }
4888         }
4889
4890         if (player[i].aitype != passivetype && leveltime > .5)
4891             player[i].howactive = typeactive;
4892
4893         if (player[i].aitype == passivetype) {
4894             player[i].aiupdatedelay -= multiplier;
4895             player[i].losupdatedelay -= multiplier;
4896             player[i].lastseentime += multiplier;
4897             player[i].pausetime -= multiplier;
4898             if (player[i].lastseentime > 1)
4899                 player[i].lastseentime = 1;
4900
4901             if (player[i].aiupdatedelay < 0) {
4902                 if (player[i].numwaypoints > 1 && player[i].howactive == typeactive && player[i].pausetime <= 0) {
4903                     player[i].targetyaw = roughDirectionTo(player[i].coords, player[i].waypoints[player[i].waypoint]);
4904                     player[i].lookyaw = player[i].targetyaw;
4905                     player[i].aiupdatedelay = .05;
4906
4907                     if (distsqflat(&player[i].coords, &player[i].waypoints[player[i].waypoint]) < 1) {
4908                         if (player[i].waypointtype[player[i].waypoint] == wppause)
4909                             player[i].pausetime = 4;
4910                         player[i].waypoint++;
4911                         if (player[i].waypoint > player[i].numwaypoints - 1)
4912                             player[i].waypoint = 0;
4913
4914                     }
4915                 }
4916
4917                 if (player[i].numwaypoints > 1 && player[i].howactive == typeactive && player[i].pausetime <= 0)
4918                     player[i].forwardkeydown = 1;
4919                 else
4920                     player[i].forwardkeydown = 0;
4921                 player[i].leftkeydown = 0;
4922                 player[i].backkeydown = 0;
4923                 player[i].rightkeydown = 0;
4924                 player[i].crouchkeydown = 0;
4925                 player[i].attackkeydown = 0;
4926                 player[i].throwkeydown = 0;
4927
4928                 if (player[i].avoidcollided > .8 && !player[i].jumpkeydown && player[i].collided < .8) {
4929                     if (!player[i].avoidsomething)
4930                         player[i].targetyaw += 90 * (player[i].whichdirection * 2 - 1);
4931                     else {
4932                         XYZ leftpos, rightpos;
4933                         float leftdist, rightdist;
4934                         leftpos = player[i].coords + DoRotation(player[i].facing, 0, 90, 0);
4935                         rightpos = player[i].coords - DoRotation(player[i].facing, 0, 90, 0);
4936                         leftdist = distsq(&leftpos, &player[i].avoidwhere);
4937                         rightdist = distsq(&rightpos, &player[i].avoidwhere);
4938                         if (leftdist < rightdist)
4939                             player[i].targetyaw += 90;
4940                         else
4941                             player[i].targetyaw -= 90;
4942                     }
4943                 }
4944             }
4945             if (player[i].collided < 1 || player[i].animTarget != jumpupanim)
4946                 player[i].jumpkeydown = 0;
4947             if ((player[i].collided > .8 && player[i].jumppower >= 5))
4948                 player[i].jumpkeydown = 1;
4949
4950
4951             //hearing sounds
4952             if (!editorenabled) {
4953                 if (player[i].howactive <= typesleeping)
4954                     if (numenvsounds > 0 && (tutoriallevel != 1 || cananger) && hostile)
4955                         for (int j = 0; j < numenvsounds; j++) {
4956                             float vol = player[i].howactive == typesleeping ? envsoundvol[j] - 14 : envsoundvol[j];
4957                             if (vol > 0 && distsq(&player[i].coords, &envsound[j]) <
4958                                     2 * (vol + vol * (player[i].creature == rabbittype) * 3))
4959                                 player[i].aitype = attacktypecutoff;
4960                         }
4961
4962                 if (player[i].aitype != passivetype) {
4963                     if (player[i].howactive == typesleeping)
4964                         player[i].setAnimation(getupfromfrontanim);
4965                     player[i].howactive = typeactive;
4966                 }
4967             }
4968
4969             if (player[i].howactive < typesleeping &&
4970                     ((tutoriallevel != 1 || cananger) && hostile) &&
4971                     !player[0].dead &&
4972                     distsq(&player[i].coords, &player[0].coords) < 400 &&
4973                     player[i].occluded < 25) {
4974                 if (distsq(&player[i].coords, &player[0].coords) < 12 &&
4975                         animation[player[0].animTarget].height != lowheight && !editorenabled)
4976                     player[i].aitype = attacktypecutoff;
4977                 if (distsq(&player[i].coords, &player[0].coords) < 30 &&
4978                         animation[player[0].animTarget].height == highheight && !editorenabled)
4979                     player[i].aitype = attacktypecutoff;
4980
4981                 //wolf smell
4982                 if (player[i].creature == wolftype) {
4983                     XYZ windsmell;
4984                     for (int j = 0; j < numplayers; j++) {
4985                         if (j == 0 || (player[j].dead && player[j].bloodloss > 0)) {
4986                             float smelldistance = 50;
4987                             if (j == 0 && player[j].num_weapons > 0) {
4988                                 if (weapons[player[j].weaponids[0]].bloody)
4989                                     smelldistance = 100;
4990                                 if (player[j].num_weapons == 2)
4991                                     if (weapons[player[j].weaponids[1]].bloody)
4992                                         smelldistance = 100;
4993                             }
4994                             if (j != 0)
4995                                 smelldistance = 100;
4996                             windsmell = windvector;
4997                             Normalise(&windsmell);
4998                             windsmell = windsmell * 2 + player[j].coords;
4999                             if (distsq(&player[i].coords, &windsmell) < smelldistance && !editorenabled)
5000                                 player[i].aitype = attacktypecutoff;
5001                         }
5002                     }
5003                 }
5004
5005                 if (player[i].howactive < typesleeping && player[i].losupdatedelay < 0 && !editorenabled && player[i].occluded < 2) {
5006                     player[i].losupdatedelay = .2;
5007                     for (int j = 0; j < numplayers; j++) {
5008                         if (j == 0 || player[j].skeleton.free || player[j].aitype != passivetype) {
5009                             if (abs(Random() % 2) || animation[player[j].animTarget].height != lowheight || j != 0)
5010                                 if (distsq(&player[i].coords, &player[j].coords) < 400)
5011                                     if (normaldotproduct(player[i].facing, player[j].coords - player[i].coords) > 0)
5012                                         if ((-1 == checkcollide(
5013                                                     DoRotation(player[i].jointPos(head), 0, player[i].yaw, 0)*
5014                                                     player[i].scale + player[i].coords,
5015                                                     DoRotation(player[j].jointPos(head), 0, player[j].yaw, 0)*
5016                                                     player[j].scale + player[j].coords) &&
5017                                                 !player[j].isWallJump()) ||
5018                                                 (player[j].animTarget == hanganim &&
5019                                                  normaldotproduct(player[j].facing, player[i].coords - player[j].coords) < 0)) {
5020                                             player[i].lastseentime -= .2;
5021                                             if (j == 0 && animation[player[j].animTarget].height == lowheight)
5022                                                 player[i].lastseentime -= .4;
5023                                             else
5024                                                 player[i].lastseentime -= .6;
5025                                         }
5026                             if (player[i].lastseentime <= 0) {
5027                                 player[i].aitype = searchtype;
5028                                 player[i].lastchecktime = 12;
5029                                 player[i].lastseen = player[j].coords;
5030                                 player[i].lastseentime = 12;
5031                             }
5032                         }
5033                     }
5034                 }
5035             }
5036             //alerted surprise
5037             if (player[i].aitype == attacktypecutoff && musictype != 2) {
5038                 if (player[i].creature != wolftype) {
5039                     player[i].stunned = .6;
5040                     player[i].surprised = .6;
5041                 }
5042                 if (player[i].creature == wolftype) {
5043                     player[i].stunned = .47;
5044                     player[i].surprised = .47;
5045                 }
5046                 numseen++;
5047             }
5048         }
5049
5050         //search for player
5051         int j;
5052         if (player[i].aitype == searchtype) {
5053             player[i].aiupdatedelay -= multiplier;
5054             player[i].losupdatedelay -= multiplier;
5055             if (!player[i].pause)
5056                 player[i].lastseentime -= multiplier;
5057             player[i].lastchecktime -= multiplier;
5058
5059             if (player[i].isRun() && !player[i].onground) {
5060                 if (player[i].coords.y > terrain.getHeight(player[i].coords.x, player[i].coords.z) + 10) {
5061                     XYZ test2 = player[i].coords + player[i].facing;
5062                     test2.y += 5;
5063                     XYZ test = player[i].coords + player[i].facing;
5064                     test.y -= 10;
5065                     j = checkcollide(test2, test, player[i].laststanding);
5066                     if (j == -1)
5067                         j = checkcollide(test2, test);
5068                     if (j == -1) {
5069                         player[i].velocity = 0;
5070                         player[i].setAnimation(player[i].getStop());
5071                         player[i].targetyaw += 180;
5072                         player[i].stunned = .5;
5073                         //player[i].aitype=passivetype;
5074                         player[i].aitype = pathfindtype;
5075                         player[i].finalfinaltarget = player[i].waypoints[player[i].waypoint];
5076                         player[i].finalpathfindpoint = -1;
5077                         player[i].targetpathfindpoint = -1;
5078                         player[i].lastpathfindpoint = -1;
5079                         player[i].lastpathfindpoint2 = -1;
5080                         player[i].lastpathfindpoint3 = -1;
5081                         player[i].lastpathfindpoint4 = -1;
5082                     } else player[i].laststanding = j;
5083                 }
5084             }
5085             //check out last seen location
5086             if (player[i].aiupdatedelay < 0) {
5087                 player[i].targetyaw = roughDirectionTo(player[i].coords, player[i].lastseen);
5088                 player[i].lookyaw = player[i].targetyaw;
5089                 player[i].aiupdatedelay = .05;
5090                 player[i].forwardkeydown = 1;
5091
5092                 if (distsqflat(&player[i].coords, &player[i].lastseen) < 1 * sq(player[i].scale * 5) || player[i].lastchecktime < 0) {
5093                     player[i].forwardkeydown = 0;
5094                     player[i].aiupdatedelay = 1;
5095                     player[i].lastseen.x += (float(Random() % 100) - 50) / 25;
5096                     player[i].lastseen.z += (float(Random() % 100) - 50) / 25;
5097                     player[i].lastchecktime = 3;
5098                 }
5099
5100                 player[i].leftkeydown = 0;
5101                 player[i].backkeydown = 0;
5102                 player[i].rightkeydown = 0;
5103                 player[i].crouchkeydown = 0;
5104                 player[i].attackkeydown = 0;
5105                 player[i].throwkeydown = 0;
5106
5107                 if (player[i].avoidcollided > .8 && !player[i].jumpkeydown && player[i].collided < .8) {
5108                     if (!player[i].avoidsomething)player[i].targetyaw += 90 * (player[i].whichdirection * 2 - 1);
5109                     else {
5110                         XYZ leftpos, rightpos;
5111                         float leftdist, rightdist;
5112                         leftpos = player[i].coords + DoRotation(player[i].facing, 0, 90, 0);
5113                         rightpos = player[i].coords - DoRotation(player[i].facing, 0, 90, 0);
5114                         leftdist = distsq(&leftpos, &player[i].avoidwhere);
5115                         rightdist = distsq(&rightpos, &player[i].avoidwhere);
5116                         if (leftdist < rightdist)player[i].targetyaw += 90;
5117                         else player[i].targetyaw -= 90;
5118                     }
5119                 }
5120             }
5121             if (player[i].collided < 1 || player[i].animTarget != jumpupanim)
5122                 player[i].jumpkeydown = 0;
5123             if ((player[i].collided > .8 && player[i].jumppower >= 5))
5124                 player[i].jumpkeydown = 1;
5125
5126             if (numenvsounds > 0 && ((tutoriallevel != 1 || cananger) && hostile))
5127                 for (int k = 0; k < numenvsounds; k++) {
5128                     if (distsq(&player[i].coords, &envsound[k]) < 2 * (envsoundvol[k] + envsoundvol[k] * (player[i].creature == rabbittype) * 3)) {
5129                         player[i].aitype = attacktypecutoff;
5130                     }
5131                 }
5132
5133             if (!player[0].dead &&
5134                     player[i].losupdatedelay < 0 &&
5135                     !editorenabled &&
5136                     player[i].occluded < 2 &&
5137                     ((tutoriallevel != 1 || cananger) && hostile)) {
5138                 player[i].losupdatedelay = .2;
5139                 if (distsq(&player[i].coords, &player[0].coords) < 4 && animation[player[i].animTarget].height != lowheight) {
5140                     player[i].aitype = attacktypecutoff;
5141                     player[i].lastseentime = 1;
5142                 }
5143                 if (abs(Random() % 2) || animation[player[i].animTarget].height != lowheight)
5144                     //TODO: factor out canSeePlayer()
5145                     if (distsq(&player[i].coords, &player[0].coords) < 400)
5146                         if (normaldotproduct(player[i].facing, player[0].coords - player[i].coords) > 0)
5147                             if ((checkcollide(
5148                                         DoRotation(player[i].jointPos(head), 0, player[i].yaw, 0)*
5149                                         player[i].scale + player[i].coords,
5150                                         DoRotation(player[0].jointPos(head), 0, player[0].yaw, 0)*
5151                                         player[0].scale + player[0].coords) == -1) ||
5152                                     (player[0].animTarget == hanganim && normaldotproduct(
5153                                          player[0].facing, player[i].coords - player[0].coords) < 0)) {
5154                                 /* //TODO: changed j to 0 on a whim, make sure this is correct
5155                                 (player[j].animTarget==hanganim&&normaldotproduct(
5156                                     player[j].facing,player[i].coords-player[j].coords)<0)
5157                                 */
5158                                 player[i].aitype = attacktypecutoff;
5159                                 player[i].lastseentime = 1;
5160                             }
5161             }
5162             //player escaped
5163             if (player[i].lastseentime < 0) {
5164                 //player[i].aitype=passivetype;
5165                 numescaped++;
5166                 player[i].aitype = pathfindtype;
5167                 player[i].finalfinaltarget = player[i].waypoints[player[i].waypoint];
5168                 player[i].finalpathfindpoint = -1;
5169                 player[i].targetpathfindpoint = -1;
5170                 player[i].lastpathfindpoint = -1;
5171                 player[i].lastpathfindpoint2 = -1;
5172                 player[i].lastpathfindpoint3 = -1;
5173                 player[i].lastpathfindpoint4 = -1;
5174             }
5175         }
5176
5177         if (player[i].aitype != gethelptype)
5178             player[i].runninghowlong = 0;
5179
5180         //get help from buddies
5181         if (player[i].aitype == gethelptype) {
5182             player[i].runninghowlong += multiplier;
5183             player[i].aiupdatedelay -= multiplier;
5184
5185             if (player[i].aiupdatedelay < 0 || player[i].ally == 0) {
5186                 player[i].aiupdatedelay = .2;
5187
5188                 //find closest ally
5189                 //TODO: factor out closest search somehow
5190                 if (!player[i].ally) {
5191                     int closest = -1;
5192                     float closestdist = -1;
5193                     for (int k = 0; k < numplayers; k++) {
5194                         if (k != i && k != 0 && !player[k].dead &&
5195                                 player[k].howactive < typedead1 &&
5196                                 !player[k].skeleton.free &&
5197                                 player[k].aitype == passivetype) {
5198                             float distance = distsq(&player[i].coords, &player[k].coords);
5199                             if (closestdist == -1 || distance < closestdist) {
5200                                 closestdist = distance;
5201                                 closest = k;
5202                             }
5203                             closest = k;
5204                         }
5205                     }
5206                     if (closest != -1)
5207                         player[i].ally = closest;
5208                     else
5209                         player[i].ally = 0;
5210                     player[i].lastseen = player[0].coords;
5211                     player[i].lastseentime = 12;
5212                 }
5213
5214
5215                 player[i].lastchecktime = 12;
5216
5217                 XYZ facing = player[i].coords;
5218                 XYZ flatfacing = player[player[i].ally].coords;
5219                 facing.y += player[i].jointPos(head).y * player[i].scale;
5220                 flatfacing.y += player[player[i].ally].jointPos(head).y * player[player[i].ally].scale;
5221                 if (-1 != checkcollide(facing, flatfacing))
5222                     player[i].lastseentime -= .1;
5223
5224                 //no available ally, run back to player
5225                 if (player[i].ally <= 0 ||
5226                         player[player[i].ally].skeleton.free ||
5227                         player[player[i].ally].aitype != passivetype ||
5228                         player[i].lastseentime <= 0) {
5229                     player[i].aitype = searchtype;
5230                     player[i].lastseentime = 12;
5231                 }
5232
5233                 //seek out ally
5234                 if (player[i].ally > 0) {
5235                     player[i].targetyaw = roughDirectionTo(player[i].coords, player[player[i].ally].coords);
5236                     player[i].lookyaw = player[i].targetyaw;
5237                     player[i].aiupdatedelay = .05;
5238                     player[i].forwardkeydown = 1;
5239
5240                     if (distsqflat(&player[i].coords, &player[player[i].ally].coords) < 3) {
5241                         player[i].aitype = searchtype;
5242                         player[i].lastseentime = 12;
5243                         player[player[i].ally].aitype = searchtype;
5244                         if (player[player[i].ally].lastseentime < player[i].lastseentime) {
5245                             player[player[i].ally].lastseen = player[i].lastseen;
5246                             player[player[i].ally].lastseentime = player[i].lastseentime;
5247                             player[player[i].ally].lastchecktime = player[i].lastchecktime;
5248                         }
5249                     }
5250
5251                     if (player[i].avoidcollided > .8 && !player[i].jumpkeydown && player[i].collided < .8) {
5252                         if (!player[i].avoidsomething)
5253                             player[i].targetyaw += 90 * (player[i].whichdirection * 2 - 1);
5254                         else {
5255                             XYZ leftpos, rightpos;
5256                             float leftdist, rightdist;
5257                             leftpos = player[i].coords + DoRotation(player[i].facing, 0, 90, 0);
5258                             rightpos = player[i].coords - DoRotation(player[i].facing, 0, 90, 0);
5259                             leftdist = distsq(&leftpos, &player[i].avoidwhere);
5260                             rightdist = distsq(&rightpos, &player[i].avoidwhere);
5261                             if (leftdist < rightdist)
5262                                 player[i].targetyaw += 90;
5263                             else
5264                                 player[i].targetyaw -= 90;
5265                         }
5266                     }
5267                 }
5268
5269                 player[i].leftkeydown = 0;
5270                 player[i].backkeydown = 0;
5271                 player[i].rightkeydown = 0;
5272                 player[i].crouchkeydown = 0;
5273                 player[i].attackkeydown = 0;
5274             }
5275             if (player[i].collided < 1 || player[i].animTarget != jumpupanim)
5276                 player[i].jumpkeydown = 0;
5277             if (player[i].collided > .8 && player[i].jumppower >= 5)
5278                 player[i].jumpkeydown = 1;
5279         }
5280
5281         //retreiving a weapon on the ground
5282         if (player[i].aitype == getweapontype) {
5283             player[i].aiupdatedelay -= multiplier;
5284             player[i].lastchecktime -= multiplier;
5285
5286             if (player[i].aiupdatedelay < 0) {
5287                 player[i].aiupdatedelay = .2;
5288
5289                 //ALLY IS WEPON
5290                 if (player[i].ally < 0) {
5291                     int closest = -1;
5292                     float closestdist = -1;
5293                     for (int k = 0; k < weapons.size(); k++)
5294                         if (weapons[k].owner == -1) {
5295                             float distance = distsq(&player[i].coords, &weapons[k].position);
5296                             if (closestdist == -1 || distance < closestdist) {
5297                                 closestdist = distance;
5298                                 closest = k;
5299                             }
5300                             closest = k;
5301                         }
5302                     if (closest != -1)
5303                         player[i].ally = closest;
5304                     else
5305                         player[i].ally = -1;
5306                 }
5307
5308                 player[i].lastseentime = 12;
5309
5310                 if (!player[0].dead && ((tutoriallevel != 1 || cananger) && hostile))
5311                     if (player[i].ally < 0 || player[i].weaponactive != -1 || player[i].lastchecktime <= 0) {
5312                         player[i].aitype = attacktypecutoff;
5313                         player[i].lastseentime = 1;
5314                     }
5315                 if (!player[0].dead)
5316                     if (player[i].ally >= 0) {
5317                         if (weapons[player[i].ally].owner != -1 ||
5318                                 distsq(&player[i].coords, &weapons[player[i].ally].position) > 16) {
5319                             player[i].aitype = attacktypecutoff;
5320                             player[i].lastseentime = 1;
5321                         }
5322                         //TODO: factor these out as moveToward()
5323                         player[i].targetyaw = roughDirectionTo(player[i].coords, weapons[player[i].ally].position);
5324                         player[i].lookyaw = player[i].targetyaw;
5325                         player[i].aiupdatedelay = .05;
5326                         player[i].forwardkeydown = 1;
5327
5328
5329                         if (player[i].avoidcollided > .8 && !player[i].jumpkeydown && player[i].collided < .8) {
5330                             if (!player[i].avoidsomething)
5331                                 player[i].targetyaw += 90 * (player[i].whichdirection * 2 - 1);
5332                             else {
5333                                 XYZ leftpos, rightpos;
5334                                 float leftdist, rightdist;
5335                                 leftpos = player[i].coords + DoRotation(player[i].facing, 0, 90, 0);
5336                                 rightpos = player[i].coords - DoRotation(player[i].facing, 0, 90, 0);
5337                                 leftdist = distsq(&leftpos, &player[i].avoidwhere);
5338                                 rightdist = distsq(&rightpos, &player[i].avoidwhere);
5339                                 if (leftdist < rightdist)
5340                                     player[i].targetyaw += 90;
5341                                 else
5342                                     player[i].targetyaw -= 90;
5343                             }
5344                         }
5345                     }
5346
5347                 player[i].leftkeydown = 0;
5348                 player[i].backkeydown = 0;
5349                 player[i].rightkeydown = 0;
5350                 player[i].attackkeydown = 0;
5351                 player[i].throwkeydown = 1;
5352                 player[i].crouchkeydown = 0;
5353                 if (player[i].animTarget != crouchremoveknifeanim &&
5354                         player[i].animTarget != removeknifeanim)
5355                     player[i].throwtogglekeydown = 0;
5356                 player[i].drawkeydown = 0;
5357             }
5358             if (player[i].collided < 1 || player[i].animTarget != jumpupanim)
5359                 player[i].jumpkeydown = 0;
5360             if ((player[i].collided > .8 && player[i].jumppower >= 5))
5361                 player[i].jumpkeydown = 1;
5362         }
5363
5364         if (player[i].aitype == attacktypecutoff) {
5365             player[i].aiupdatedelay -= multiplier;
5366             //dodge or reverse rabbit kicks, knife throws, flips
5367             if (player[i].damage < player[i].damagetolerance * 2 / 3)
5368                 if ((player[0].animTarget == rabbitkickanim ||
5369                         player[0].animTarget == knifethrowanim ||
5370                         (player[0].isFlip() &&
5371                          normaldotproduct(player[0].facing, player[0].coords - player[i].coords) < 0)) &&
5372                         !player[0].skeleton.free &&
5373                         (player[i].aiupdatedelay < .1)) {
5374                     player[i].attackkeydown = 0;
5375                     if (player[i].isIdle())
5376                         player[i].crouchkeydown = 1;
5377                     if (player[0].animTarget != rabbitkickanim && player[0].weaponactive != -1) {
5378                         if (weapons[player[0].weaponids[0]].getType() == knife) {
5379                             if (player[i].isIdle() || player[i].isCrouch() || player[i].isRun() || player[i].isFlip()) {
5380                                 if (abs(Random() % 2 == 0))
5381                                     player[i].setAnimation(backhandspringanim);
5382                                 else
5383                                     player[i].setAnimation(rollanim);
5384                                 player[i].targetyaw += 90 * (abs(Random() % 2) * 2 - 1);
5385                                 player[i].wentforweapon = 0;
5386                             }
5387                             if (player[i].animTarget == jumpupanim || player[i].animTarget == jumpdownanim)
5388                                 player[i].setAnimation(flipanim);
5389                         }
5390                     }
5391                     player[i].forwardkeydown = 0;
5392                     player[i].aiupdatedelay = .02;
5393                 }
5394             //get confused by flips
5395             if (player[0].isFlip() &&
5396                     !player[0].skeleton.free &&
5397                     player[0].animTarget != walljumprightkickanim &&
5398                     player[0].animTarget != walljumpleftkickanim) {
5399                 if (distsq(&player[0].coords, &player[i].coords) < 25)
5400                     if ((1 - player[i].damage / player[i].damagetolerance) > .5)
5401                         player[i].stunned = 1;
5402             }
5403             //go for weapon on the ground
5404             if (player[i].wentforweapon < 3)
5405                 for (int k = 0; k < weapons.size(); k++)
5406                     if (player[i].creature != wolftype)
5407                         if (player[i].num_weapons == 0 &&
5408                                 weapons[k].owner == -1 &&
5409                                 weapons[i].velocity.x == 0 &&
5410                                 weapons[i].velocity.z == 0 &&
5411                                 weapons[i].velocity.y == 0) {
5412                             if (distsq(&player[i].coords, &weapons[k].position) < 16) {
5413                                 player[i].wentforweapon++;
5414                                 player[i].lastchecktime = 6;
5415                                 player[i].aitype = getweapontype;
5416                                 player[i].ally = -1;
5417                             }
5418                         }
5419             //dodge/reverse walljump kicks
5420             if (player[i].damage < player[i].damagetolerance / 2)
5421                 if (animation[player[i].animTarget].height != highheight)
5422                     if (player[i].damage < player[i].damagetolerance * .5 &&
5423                             ((player[0].animTarget == walljumprightkickanim ||
5424                               player[0].animTarget == walljumpleftkickanim) &&
5425                              ((player[i].aiupdatedelay < .15 &&
5426                                difficulty == 2) ||
5427                               (player[i].aiupdatedelay < .08 &&
5428                                difficulty != 2)))) {
5429                         player[i].crouchkeydown = 1;
5430                     }
5431             //walked off a ledge (?)
5432             if (player[i].isRun() && !player[i].onground)
5433                 if (player[i].coords.y > terrain.getHeight(player[i].coords.x, player[i].coords.z) + 10) {
5434                     XYZ test2 = player[i].coords + player[i].facing;
5435                     test2.y += 5;
5436                     XYZ test = player[i].coords + player[i].facing;
5437                     test.y -= 10;
5438                     j = checkcollide(test2, test, player[i].laststanding);
5439                     if (j == -1)
5440                         j = checkcollide(test2, test);
5441                     if (j == -1) {
5442                         player[i].velocity = 0;
5443                         player[i].setAnimation(player[i].getStop());
5444                         player[i].targetyaw += 180;
5445                         player[i].stunned = .5;
5446                         player[i].aitype = pathfindtype;
5447                         player[i].finalfinaltarget = player[i].waypoints[player[i].waypoint];
5448                         player[i].finalpathfindpoint = -1;
5449                         player[i].targetpathfindpoint = -1;
5450                         player[i].lastpathfindpoint = -1;
5451                         player[i].lastpathfindpoint2 = -1;
5452                         player[i].lastpathfindpoint3 = -1;
5453                         player[i].lastpathfindpoint4 = -1;
5454                     } else
5455                         player[i].laststanding = j;
5456                 }
5457             //lose sight of player in the air (?)
5458             if (player[0].coords.y > player[i].coords.y + 5 &&
5459                     animation[player[0].animTarget].height != highheight &&
5460                     !player[0].onterrain) {
5461                 player[i].aitype = pathfindtype;
5462                 player[i].finalfinaltarget = player[i].waypoints[player[i].waypoint];
5463                 player[i].finalpathfindpoint = -1;
5464                 player[i].targetpathfindpoint = -1;
5465                 player[i].lastpathfindpoint = -1;
5466                 player[i].lastpathfindpoint2 = -1;
5467                 player[i].lastpathfindpoint3 = -1;
5468                 player[i].lastpathfindpoint4 = -1;
5469             }
5470             //it's time to think (?)
5471             if (player[i].aiupdatedelay < 0 &&
5472                     !animation[player[i].animTarget].attack &&
5473                     player[i].animTarget != staggerbackhighanim &&
5474                     player[i].animTarget != staggerbackhardanim &&
5475                     player[i].animTarget != backhandspringanim &&
5476                     player[i].animTarget != dodgebackanim) {
5477                 //draw weapon
5478                 if (player[i].weaponactive == -1 && player[i].num_weapons > 0)
5479                     player[i].drawkeydown = Random() % 2;
5480                 else
5481                     player[i].drawkeydown = 0;
5482                 player[i].rabbitkickenabled = Random() % 2;
5483                 //chase player
5484                 XYZ rotatetarget = player[0].coords + player[0].velocity;
5485                 XYZ targetpoint = player[0].coords;
5486                 if (distsq(&player[0].coords, &player[i].coords) <
5487                         distsq(&rotatetarget, &player[i].coords))
5488                     targetpoint += player[0].velocity *
5489                                    findDistance(&player[0].coords, &player[i].coords) / findLength(&player[i].velocity);
5490                 player[i].targetyaw = roughDirectionTo(player[i].coords, targetpoint);
5491                 player[i].lookyaw = player[i].targetyaw;
5492                 player[i].aiupdatedelay = .2 + fabs((float)(Random() % 100) / 1000);
5493
5494                 if (distsq(&player[i].coords, &player[0].coords) > 5 && (player[0].weaponactive == -1 || player[i].weaponactive != -1))
5495                     player[i].forwardkeydown = 1;
5496                 else if ((distsq(&player[i].coords, &player[0].coords) > 16 ||
5497                           distsq(&player[i].coords, &player[0].coords) < 9) &&
5498                          player[0].weaponactive != -1)
5499                     player[i].forwardkeydown = 1;
5500                 else if (Random() % 6 == 0 || (player[i].creature == wolftype && Random() % 3 == 0))
5501                     player[i].forwardkeydown = 1;
5502                 else
5503                     player[i].forwardkeydown = 0;
5504                 //chill out around the corpse
5505                 if (player[0].dead) {
5506                     player[i].forwardkeydown = 0;
5507                     if (Random() % 10 == 0)
5508                         player[i].forwardkeydown = 1;
5509                     if (Random() % 100 == 0) {
5510                         player[i].aitype = pathfindtype;
5511                         player[i].finalfinaltarget = player[i].waypoints[player[i].waypoint];
5512                         player[i].finalpathfindpoint = -1;
5513                         player[i].targetpathfindpoint = -1;
5514                         player[i].lastpathfindpoint = -1;
5515                         player[i].lastpathfindpoint2 = -1;
5516                         player[i].lastpathfindpoint3 = -1;
5517                         player[i].lastpathfindpoint4 = -1;
5518                     }
5519                 }
5520                 player[i].leftkeydown = 0;
5521                 player[i].backkeydown = 0;
5522                 player[i].rightkeydown = 0;
5523                 player[i].crouchkeydown = 0;
5524                 player[i].throwkeydown = 0;
5525
5526                 if (player[i].avoidcollided > .8 && !player[i].jumpkeydown && player[i].collided < .8)
5527                     player[i].targetyaw += 90 * (player[i].whichdirection * 2 - 1);
5528                 //attack!!!
5529                 if (Random() % 2 == 0 || player[i].weaponactive != -1 || player[i].creature == wolftype)
5530                     player[i].attackkeydown = 1;
5531                 else
5532                     player[i].attackkeydown = 0;
5533                 if (player[i].isRun() && Random() % 6 && distsq(&player[i].coords, &player[0].coords) > 7)
5534                     player[i].attackkeydown = 0;
5535
5536                 //TODO: wat
5537                 if (player[i].aitype != playercontrolled &&
5538                         (player[i].isIdle() ||
5539                          player[i].isCrouch() ||
5540                          player[i].isRun())) {
5541                     int target = -2;
5542                     for (int j = 0; j < numplayers; j++)
5543                         if (j != i && !player[j].skeleton.free &&
5544                                 player[j].hasvictim &&
5545                                 (tutoriallevel == 1 && reversaltrain ||
5546                                  Random() % 2 == 0 && difficulty == 2 ||
5547                                  Random() % 4 == 0 && difficulty == 1 ||
5548                                  Random() % 8 == 0 && difficulty == 0 ||
5549                                  player[j].lastattack2 == player[j].animTarget &&
5550                                  player[j].lastattack3 == player[j].animTarget &&
5551                                  (Random() % 2 == 0 || difficulty == 2) ||
5552                                  (player[i].isIdle() || player[i].isRun()) &&
5553                                  player[j].weaponactive != -1 ||
5554                                  player[j].animTarget == swordslashanim &&
5555                                  player[i].weaponactive != -1 ||
5556                                  player[j].animTarget == staffhitanim ||
5557                                  player[j].animTarget == staffspinhitanim))
5558                             if (distsq(&player[j].coords, &player[j].victim->coords) < 4 &&
5559                                     player[j].victim == &player[i] &&
5560                                     (player[j].animTarget == sweepanim ||
5561                                      player[j].animTarget == spinkickanim ||
5562                                      player[j].animTarget == staffhitanim ||
5563                                      player[j].animTarget == staffspinhitanim ||
5564                                      player[j].animTarget == winduppunchanim ||
5565                                      player[j].animTarget == upunchanim ||
5566                                      player[j].animTarget == wolfslapanim ||
5567                                      player[j].animTarget == knifeslashstartanim ||
5568                                      player[j].animTarget == swordslashanim &&
5569                                      (distsq(&player[j].coords, &player[i].coords) < 2 ||
5570                                       player[i].weaponactive != -1))) {
5571                                 if (target >= 0)
5572                                     target = -1;
5573                                 else
5574                                     target = j;
5575                             }
5576                     if (target >= 0)
5577                         player[target].Reverse();
5578                 }
5579
5580                 if (player[i].collided < 1)
5581                     player[i].jumpkeydown = 0;
5582                 if (player[i].collided > .8 && player[i].jumppower >= 5 ||
5583                         distsq(&player[i].coords, &player[0].coords) > 400 &&
5584                         player[i].onterrain &&
5585                         player[i].creature == rabbittype)
5586                     player[i].jumpkeydown = 1;
5587                 //TODO: why are we controlling the human?
5588                 if (normaldotproduct(player[i].facing, player[0].coords - player[i].coords) > 0)
5589                     player[0].jumpkeydown = 0;
5590                 if (player[0].animTarget == jumpdownanim &&
5591                         distsq(&player[0].coords, &player[i].coords) < 40)
5592                     player[i].crouchkeydown = 1;
5593                 if (player[i].jumpkeydown)
5594                     player[i].attackkeydown = 0;
5595
5596                 if (tutoriallevel == 1)
5597                     if (!canattack)
5598                         player[i].attackkeydown = 0;
5599
5600
5601                 XYZ facing = player[i].coords;
5602                 XYZ flatfacing = player[0].coords;
5603                 facing.y += player[i].jointPos(head).y * player[i].scale;
5604                 flatfacing.y += player[0].jointPos(head).y * player[0].scale;
5605                 if (player[i].occluded >= 2)
5606                     if (-1 != checkcollide(facing, flatfacing)) {
5607                         if (!player[i].pause)
5608                             player[i].lastseentime -= .2;
5609                         if (player[i].lastseentime <= 0 &&
5610                                 (player[i].creature != wolftype ||
5611                                  player[i].weaponstuck == -1)) {
5612                             player[i].aitype = searchtype;
5613                             player[i].lastchecktime = 12;
5614                             player[i].lastseen = player[0].coords;
5615                             player[i].lastseentime = 12;
5616                         }
5617                     } else
5618                         player[i].lastseentime = 1;
5619             }
5620         }
5621         if (animation[player[0].animTarget].height == highheight &&
5622                 (player[i].aitype == attacktypecutoff ||
5623                  player[i].aitype == searchtype))
5624             if (player[0].coords.y > terrain.getHeight(player[0].coords.x, player[0].coords.z) + 10) {
5625                 XYZ test = player[0].coords;
5626                 test.y -= 40;
5627                 if (-1 == checkcollide(player[0].coords, test))
5628                     player[i].stunned = 1;
5629             }
5630         //stunned
5631         if (player[i].aitype == passivetype && !(player[i].numwaypoints > 1) ||
5632                 player[i].stunned > 0 ||
5633                 player[i].pause && player[i].damage > player[i].superpermanentdamage) {
5634             if (player[i].pause)
5635                 player[i].lastseentime = 1;
5636             player[i].targetyaw = player[i].yaw;
5637             player[i].forwardkeydown = 0;
5638             player[i].leftkeydown = 0;
5639             player[i].backkeydown = 0;
5640             player[i].rightkeydown = 0;
5641             player[i].jumpkeydown = 0;
5642             player[i].attackkeydown = 0;
5643             player[i].crouchkeydown = 0;
5644             player[i].throwkeydown = 0;
5645         }
5646
5647
5648         XYZ facing;
5649         facing = 0;
5650         facing.z = -1;
5651
5652         XYZ flatfacing = DoRotation(facing, 0, player[i].yaw + 180, 0);
5653         facing = flatfacing;
5654
5655         if (player[i].aitype == attacktypecutoff) {
5656             player[i].targetheadyaw = 180 - roughDirectionTo(player[i].coords, player[0].coords);
5657             player[i].targetheadpitch = pitchTo(player[i].coords, player[0].coords);
5658         } else if (player[i].howactive >= typesleeping) {
5659             player[i].targetheadyaw = player[i].targetyaw;
5660             player[i].targetheadpitch = 0;
5661         } else {
5662             if (player[i].interestdelay <= 0) {
5663                 player[i].interestdelay = .7 + (float)(abs(Random() % 100)) / 100;
5664                 player[i].headtarget = player[i].coords;
5665                 player[i].headtarget.x += (float)(abs(Random() % 200) - 100) / 100;
5666                 player[i].headtarget.z += (float)(abs(Random() % 200) - 100) / 100;
5667                 player[i].headtarget.y += (float)(abs(Random() % 200) - 100) / 300;
5668                 player[i].headtarget += player[i].facing * 1.5;
5669             }
5670             player[i].targetheadyaw = 180 - roughDirectionTo(player[i].coords, player[i].headtarget);
5671             player[i].targetheadpitch = pitchTo(player[i].coords, player[i].headtarget);
5672         }
5673     }
5674 }
5675
5676
5677
5678 void updateSettingsMenu()
5679 {
5680     char sbuf[256];
5681     if ((float)newscreenwidth > (float)newscreenheight * 1.61 || (float)newscreenwidth < (float)newscreenheight * 1.59)
5682         sprintf (sbuf, "Resolution: %d*%d", (int)newscreenwidth, (int)newscreenheight);
5683     else
5684         sprintf (sbuf, "Resolution: %d*%d (widescreen)", (int)newscreenwidth, (int)newscreenheight);
5685     Menu::setText(0, sbuf);
5686     if (newdetail == 0) Menu::setText(1, "Detail: Low");
5687     if (newdetail == 1) Menu::setText(1, "Detail: Medium");
5688     if (newdetail == 2) Menu::setText(1, "Detail: High");
5689     if (bloodtoggle == 0) Menu::setText(2, "Blood: Off");
5690     if (bloodtoggle == 1) Menu::setText(2, "Blood: On, low detail");
5691     if (bloodtoggle == 2) Menu::setText(2, "Blood: On, high detail (slower)");
5692     if (difficulty == 0) Menu::setText(3, "Difficulty: Easier");
5693     if (difficulty == 1) Menu::setText(3, "Difficulty: Difficult");
5694     if (difficulty == 2) Menu::setText(3, "Difficulty: Insane");
5695     Menu::setText(4, ismotionblur ? "Blur Effects: Enabled (less compatible)" : "Blur Effects: Disabled (more compatible)");
5696     Menu::setText(5, decals ? "Decals: Enabled (slower)" : "Decals: Disabled");
5697     Menu::setText(6, musictoggle ? "Music: Enabled" : "Music: Disabled");
5698     Menu::setText(9, invertmouse ? "Invert mouse: Yes" : "Invert mouse: No");
5699     sprintf (sbuf, "Mouse Speed: %d", (int)(usermousesensitivity * 5));
5700     Menu::setText(10, sbuf);
5701     sprintf (sbuf, "Volume: %d%%", (int)(volume * 100));
5702     Menu::setText(11, sbuf);
5703     Menu::setText(13, showdamagebar ? "Damage Bar: On" : "Damage Bar: Off");
5704     if (newdetail == detail && newscreenheight == (int)screenheight && newscreenwidth == (int)screenwidth)
5705         sprintf (sbuf, "Back");
5706     else
5707         sprintf (sbuf, "Back (some changes take effect next time Lugaru is opened)");
5708     Menu::setText(8, sbuf);
5709 }
5710
5711 void updateStereoConfigMenu()
5712 {
5713     char sbuf[256];
5714     sprintf(sbuf, "Stereo mode: %s", StereoModeName(newstereomode));
5715     Menu::setText(0, sbuf);
5716     sprintf(sbuf, "Stereo separation: %.3f", stereoseparation);
5717     Menu::setText(1, sbuf);
5718     sprintf(sbuf, "Reverse stereo: %s", stereoreverse ? "Yes" : "No");
5719     Menu::setText(2, sbuf);
5720 }
5721
5722 void updateControlsMenu()
5723 {
5724     Menu::setText(0, (string)"Forwards: " + (keyselect == 0 ? "_" : Input::keyToChar(forwardkey)));
5725     Menu::setText(1, (string)"Back: "    + (keyselect == 1 ? "_" : Input::keyToChar(backkey)));
5726     Menu::setText(2, (string)"Left: "    + (keyselect == 2 ? "_" : Input::keyToChar(leftkey)));
5727     Menu::setText(3, (string)"Right: "   + (keyselect == 3 ? "_" : Input::keyToChar(rightkey)));
5728     Menu::setText(4, (string)"Crouch: "  + (keyselect == 4 ? "_" : Input::keyToChar(crouchkey)));
5729     Menu::setText(5, (string)"Jump: "    + (keyselect == 5 ? "_" : Input::keyToChar(jumpkey)));
5730     Menu::setText(6, (string)"Draw: "    + (keyselect == 6 ? "_" : Input::keyToChar(drawkey)));
5731     Menu::setText(7, (string)"Throw: "   + (keyselect == 7 ? "_" : Input::keyToChar(throwkey)));
5732     Menu::setText(8, (string)"Attack: "  + (keyselect == 8 ? "_" : Input::keyToChar(attackkey)));
5733     if (debugmode)
5734         Menu::setText(9, (string)"Console: " + (keyselect == 9 ? "_" : Input::keyToChar(consolekey)));
5735 }
5736
5737 /*
5738 Values of mainmenu :
5739 1 Main menu
5740 2 Menu pause (resume/end game)
5741 3 Option menu
5742 4 Controls configuration menu
5743 5 Main game menu (choose level or challenge)
5744 6 Deleting user menu
5745 7 User managment menu (select/add)
5746 8 Choose difficulty menu
5747 9 Challenge level selection menu
5748 10 End of the campaign congratulation (is that really a menu?)
5749 11 Same that 9 ??? => unused
5750 18 stereo configuration
5751 */
5752
5753 void Game::LoadMenu()
5754 {
5755     Menu::clearMenu();
5756     switch (mainmenu) {
5757     case 1:
5758     case 2:
5759         Menu::addImage(0, Mainmenuitems[0], 150, 480 - 128, 256, 128);
5760         Menu::addButtonImage(1, Mainmenuitems[mainmenu == 1 ? 1 : 5], 18, 480 - 152 - 32, 128, 32);
5761         Menu::addButtonImage(2, Mainmenuitems[2], 18, 480 - 228 - 32, 112, 32);
5762         Menu::addButtonImage(3, Mainmenuitems[mainmenu == 1 ? 3 : 6], 18, 480 - 306 - 32, mainmenu == 1 ? 68 : 132, 32);
5763         break;
5764     case 3:
5765         Menu::addButton( 0, "", 10 + 20, 440);
5766         Menu::addButton( 1, "", 10 + 60, 405);
5767         Menu::addButton( 2, "", 10 + 70, 370);
5768         Menu::addButton( 3, "", 10 + 20 - 1000, 335 - 1000);
5769         Menu::addButton( 4, "", 10   , 335);
5770         Menu::addButton( 5, "", 10 + 60, 300);
5771         Menu::addButton( 6, "", 10 + 70, 265);
5772         Menu::addButton( 9, "", 10   , 230);
5773         Menu::addButton(10, "", 20   , 195);
5774         Menu::addButton(11, "", 10 + 60, 160);
5775         Menu::addButton(13, "", 30   , 125);
5776         Menu::addButton( 7, "-Configure Controls-", 10 + 15, 90);
5777         Menu::addButton(12, "-Configure Stereo -", 10 + 15, 55);
5778         Menu::addButton(8, "Back", 10, 10);
5779         updateSettingsMenu();
5780         break;
5781     case 4:
5782         Menu::addButton(0, "", 10   , 400);
5783         Menu::addButton(1, "", 10 + 40, 360);
5784         Menu::addButton(2, "", 10 + 40, 320);
5785         Menu::addButton(3, "", 10 + 30, 280);
5786         Menu::addButton(4, "", 10 + 20, 240);
5787         Menu::addButton(5, "", 10 + 40, 200);
5788         Menu::addButton(6, "", 10 + 40, 160);
5789         Menu::addButton(7, "", 10 + 30, 120);
5790         Menu::addButton(8, "", 10 + 20, 80);
5791         if (debugmode)
5792             Menu::addButton(9, "", 10 + 10, 40);
5793         Menu::addButton(debugmode ? 10 : 9, "Back", 10, 10);
5794         updateControlsMenu();
5795         break;
5796     case 5: {
5797         LoadCampaign();
5798         Menu::addLabel(-1, accountactive->getName(), 5, 400);
5799         Menu::addButton(1, "Tutorial", 5, 300);
5800         Menu::addButton(2, "Challenge", 5, 240);
5801         Menu::addButton(3, "Delete User", 400, 10);
5802         Menu::addButton(4, "Main Menu", 5, 10);
5803         Menu::addButton(5, "Change User", 5, 180);
5804         Menu::addButton(6, "Campaign : " + accountactive->getCurrentCampaign(), 200, 420);
5805
5806         //show campaign map
5807         //with (2,-5) offset from old code
5808         Menu::addImage(-1, Mainmenuitems[7], 150 + 2, 60 - 5, 400, 400);
5809         //show levels
5810         int numlevels = accountactive->getCampaignChoicesMade();
5811         numlevels += numlevels > 0 ? campaignlevels[numlevels - 1].nextlevel.size() : 1;
5812         for (int i = 0; i < numlevels; i++) {
5813             XYZ midpoint = campaignlevels[i].getCenter();
5814             float itemsize = campaignlevels[i].getWidth();
5815             const bool active = i >= accountactive->getCampaignChoicesMade();
5816             if (!active)
5817                 itemsize /= 2;
5818
5819             if (i >= 1) {
5820                 XYZ start = campaignlevels[i - 1].getCenter();
5821                 Menu::addMapLine(start.x, start.y, midpoint.x - start.x, midpoint.y - start.y, 0.5, active ? 1 : 0.5, active ? 1 : 0.5, 0, 0);
5822             }
5823             Menu::addMapMarker(NB_CAMPAIGN_MENU_ITEM + i, Mapcircletexture,
5824                                midpoint.x - itemsize / 2, midpoint.y - itemsize / 2, itemsize, itemsize, active ? 1 : 0.5, 0, 0);
5825
5826             if (active) {
5827                 Menu::addMapLabel(-2, campaignlevels[i].description,
5828                                   campaignlevels[i].getStartX() + 10,
5829                                   campaignlevels[i].getStartY() - 4);
5830             }
5831         }
5832     }
5833     break;
5834     case 6:
5835         Menu::addLabel(-1, "Are you sure you want to delete this user?", 10, 400);
5836         Menu::addButton(1, "Yes", 10, 360);
5837         Menu::addButton(2, "No", 10, 320);
5838         break;
5839     case 7:
5840         if (Account::getNbAccounts() < 8)
5841             Menu::addButton(0, "New User", 10, 400);
5842         else
5843             Menu::addLabel(0, "No More Users", 10, 400);
5844         Menu::addLabel(-2, "", 20, 400);
5845         Menu::addButton(Account::getNbAccounts() + 1, "Back", 10, 10);
5846         for (int i = 0; i < Account::getNbAccounts(); i++)
5847             Menu::addButton(i + 1, Account::get(i)->getName(), 10, 340 - 20 * (i + 1));
5848         break;
5849     case 8:
5850         Menu::addButton(0, "Easier", 10, 400);
5851         Menu::addButton(1, "Difficult", 10, 360);
5852         Menu::addButton(2, "Insane", 10, 320);
5853         break;
5854     case 9:
5855         for (int i = 0; i < numchallengelevels; i++) {
5856             char temp[255];
5857             string name = "";
5858             sprintf (temp, "Level %d", i + 1);
5859             for (int j = strlen(temp); j < 17; j++)
5860                 strcat(temp, " ");
5861             name += temp;
5862             sprintf (temp, "%d", (int)accountactive->getHighScore(i));
5863             for (int j = strlen(temp); j < (32 - 17); j++)
5864                 strcat(temp, " ");
5865             name += temp;
5866             sprintf (temp, "%d:", (int)(((int)accountactive->getFastTime(i) - (int)(accountactive->getFastTime(i)) % 60) / 60));
5867             if ((int)(accountactive->getFastTime(i)) % 60 < 10)strcat(temp, "0");
5868             name += temp;
5869             sprintf (temp, "%d", (int)(accountactive->getFastTime(i)) % 60);
5870             name += temp;
5871
5872             Menu::addButton(i, name, 10, 400 - i * 25, i > accountactive->getProgress() ? 0.5 : 1, 0, 0);
5873         }
5874
5875         Menu::addButton(-1, "             High Score      Best Time", 10, 440);
5876         Menu::addButton(numchallengelevels, "Back", 10, 10);
5877         break;
5878     case 10: {
5879         Menu::addLabel(0, "Congratulations!", 220, 330);
5880         Menu::addLabel(1, "You have avenged your family and", 140, 300);
5881         Menu::addLabel(2, "restored peace to the island of Lugaru.", 110, 270);
5882         Menu::addButton(3, "Back", 10, 10);
5883         char sbuf[256];
5884         sprintf(sbuf, "Your score:         %d", (int)accountactive->getCampaignScore());
5885         Menu::addLabel(4, sbuf, 190, 200);
5886         sprintf(sbuf, "Highest score:      %d", (int)accountactive->getCampaignHighScore());
5887         Menu::addLabel(5, sbuf, 190, 180);
5888     }
5889     break;
5890     case 18:
5891         Menu::addButton(0, "", 70, 400);
5892         Menu::addButton(1, "", 10, 360);
5893         Menu::addButton(2, "", 40, 320);
5894         Menu::addButton(3, "Back", 10, 10);
5895         updateStereoConfigMenu();
5896         break;
5897     }
5898 }
5899
5900 extern SDL_Rect **resolutions;
5901
5902 void MenuTick()
5903 {
5904     //menu buttons
5905     selected = Menu::getSelected(mousecoordh * 640 / screenwidth, 480 - mousecoordv * 480 / screenheight);
5906
5907     // some specific case where we do something even if the left mouse button is not pressed.
5908     if ((mainmenu == 5) && (endgame == 2)) {
5909         accountactive->endGame();
5910         endgame = 0;
5911     }
5912     if (mainmenu == 10)
5913         endgame = 2;
5914     if (mainmenu == 18 && Input::isKeyPressed(MOUSEBUTTON2) && selected == 1) {
5915         stereoseparation -= 0.001;
5916         updateStereoConfigMenu();
5917     }
5918
5919     static int oldmainmenu = mainmenu;
5920
5921     char sbuf[256];
5922
5923     if (Input::MouseClicked() && (selected >= 0)) { // handling of the left mouse clic in menus
5924         switch (mainmenu) {
5925         case 1:
5926         case 2:
5927             switch (selected) {
5928             case 1:
5929                 if (gameon) { //resume
5930                     mainmenu = 0;
5931                     pause_sound(stream_menutheme);
5932                     resume_stream(leveltheme);
5933                 } else { //new game
5934                     fireSound(firestartsound);
5935                     flash();
5936                     mainmenu = (accountactive ? 5 : 7);
5937                     selected = -1;
5938                 }
5939                 break;
5940             case 2: //options
5941                 fireSound();
5942                 flash();
5943                 mainmenu = 3;
5944                 if (newdetail > 2) newdetail = detail;
5945                 if (newdetail < 0) newdetail = detail;
5946                 if (newscreenwidth > 3000) newscreenwidth = screenwidth;
5947                 if (newscreenwidth < 0) newscreenwidth = screenwidth;
5948                 if (newscreenheight > 3000) newscreenheight = screenheight;
5949                 if (newscreenheight < 0) newscreenheight = screenheight;
5950                 break;
5951             case 3:
5952                 fireSound();
5953                 flash();
5954                 if (gameon) { //end game
5955                     gameon = 0;
5956                     mainmenu = 1;
5957                 } else { //quit
5958                     tryquit = 1;
5959                     pause_sound(stream_menutheme);
5960                 }
5961                 break;
5962             }
5963             break;
5964         case 3:
5965             fireSound();
5966             bool isCustomResolution, found;
5967             switch (selected) {
5968             case 0:
5969                 isCustomResolution = true;
5970                 found = false;
5971                 for (int i = 0; (!found) && (resolutions[i]); i++) {
5972                     if ((resolutions[i]->w == screenwidth) && (resolutions[i]->h == screenwidth))
5973                         isCustomResolution = false;
5974
5975                     if ((resolutions[i]->w == newscreenwidth) && (resolutions[i]->h == newscreenheight)) {
5976                         i++;
5977                         if (resolutions[i] != NULL) {
5978                             newscreenwidth = (int) resolutions[i]->w;
5979                             newscreenheight = (int) resolutions[i]->h;
5980                         } else if (isCustomResolution) {
5981                             if ((screenwidth == newscreenwidth) && (screenheight == newscreenheight)) {
5982                                 newscreenwidth = (int) resolutions[0]->w;
5983                                 newscreenheight = (int) resolutions[0]->h;
5984                             } else {
5985                                 newscreenwidth = screenwidth;
5986                                 newscreenheight = screenheight;
5987                             }
5988                         } else {
5989                             newscreenwidth = (int) resolutions[0]->w;
5990                             newscreenheight = (int) resolutions[0]->h;
5991                         }
5992                         found = true;
5993                     }
5994                 }
5995
5996                 if (!found) {
5997                     newscreenwidth = (int) resolutions[0]->w;
5998                     newscreenheight = (int) resolutions[0]->h;
5999                 }
6000                 break;
6001             case 1:
6002                 newdetail++;
6003                 if (newdetail > 2) newdetail = 0;
6004                 break;
6005             case 2:
6006                 bloodtoggle++;
6007                 if (bloodtoggle > 2) bloodtoggle = 0;
6008                 break;
6009             case 3:
6010                 difficulty++;
6011                 if (difficulty > 2) difficulty = 0;
6012                 break;
6013             case 4:
6014                 ismotionblur = !ismotionblur;
6015                 break;
6016             case 5:
6017                 decals = !decals;
6018                 break;
6019             case 6:
6020                 musictoggle = !musictoggle;
6021                 if (musictoggle) {
6022                     emit_stream_np(stream_menutheme);
6023                 } else {
6024                     pause_sound(leveltheme);
6025                     pause_sound(stream_fighttheme);
6026                     pause_sound(stream_menutheme);
6027
6028                     for (int i = 0; i < 4; i++) {
6029                         oldmusicvolume[i] = 0;
6030                         musicvolume[i] = 0;
6031                     }
6032                 }
6033                 break;
6034             case 7: // controls
6035                 flash();
6036                 mainmenu = 4;
6037                 selected = -1;
6038                 keyselect = -1;
6039                 break;
6040             case 8:
6041                 flash();
6042                 SaveSettings();
6043                 mainmenu = gameon ? 2 : 1;
6044                 break;
6045             case 9:
6046                 invertmouse = !invertmouse;
6047                 break;
6048             case 10:
6049                 usermousesensitivity += .2;
6050                 if (usermousesensitivity > 2)
6051                     usermousesensitivity = .2;
6052                 break;
6053             case 11:
6054                 volume += .1f;
6055                 if (volume > 1.0001f)
6056                     volume = 0;
6057                 OPENAL_SetSFXMasterVolume((int)(volume * 255));
6058                 break;
6059             case 12:
6060                 flash();
6061                 newstereomode = stereomode;
6062                 mainmenu = 18;
6063                 keyselect = -1;
6064                 break;
6065             case 13:
6066                 showdamagebar = !showdamagebar;
6067                 break;
6068             }
6069             updateSettingsMenu();
6070             break;
6071         case 4:
6072             if (!waiting) {
6073                 fireSound();
6074                 if (selected < (debugmode ? 10 : 9) && keyselect == -1)
6075                     keyselect = selected;
6076                 if (keyselect != -1)
6077                     setKeySelected();
6078                 if (selected == (debugmode ? 10 : 9)) {
6079                     flash();
6080                     mainmenu = 3;
6081                 }
6082             }
6083             updateControlsMenu();
6084             break;
6085         case 5:
6086             fireSound();
6087             flash();
6088             if ((selected - NB_CAMPAIGN_MENU_ITEM >= accountactive->getCampaignChoicesMade())) {
6089                 startbonustotal = 0;
6090
6091                 loading = 2;
6092                 loadtime = 0;
6093                 targetlevel = 7;
6094                 if (firstload)
6095                     TickOnceAfter();
6096                 else
6097                     LoadStuff();
6098                 whichchoice = selected - NB_CAMPAIGN_MENU_ITEM - accountactive->getCampaignChoicesMade();
6099                 actuallevel = (accountactive->getCampaignChoicesMade() > 0 ? campaignlevels[accountactive->getCampaignChoicesMade() - 1].nextlevel[whichchoice] : 0);
6100                 visibleloading = 1;
6101                 stillloading = 1;
6102                 Loadlevel(campaignlevels[actuallevel].mapname.c_str());
6103                 campaign = 1;
6104                 mainmenu = 0;
6105                 gameon = 1;
6106                 pause_sound(stream_menutheme);
6107             }
6108             switch (selected) {
6109             case 1:
6110                 startbonustotal = 0;
6111
6112                 loading = 2;
6113                 loadtime = 0;
6114                 targetlevel = -1;
6115                 if (firstload) {
6116                     TickOnceAfter();
6117                 } else
6118                     LoadStuff();
6119                 Loadlevel(-1);
6120
6121                 mainmenu = 0;
6122                 gameon = 1;
6123                 pause_sound(stream_menutheme);
6124                 break;
6125             case 2:
6126                 mainmenu = 9;
6127                 break;
6128             case 3:
6129                 mainmenu = 6;
6130                 break;
6131             case 4:
6132                 mainmenu = (gameon ? 2 : 1);
6133                 break;
6134             case 5:
6135                 mainmenu = 7;
6136                 break;
6137             case 6:
6138                 vector<string> campaigns = ListCampaigns();
6139                 vector<string>::iterator c;
6140                 if ((c = find(campaigns.begin(), campaigns.end(), accountactive->getCurrentCampaign())) == campaigns.end()) {
6141                     if (!campaigns.empty())
6142                         accountactive->setCurrentCampaign(campaigns.front());
6143                 } else {
6144                     c++;
6145                     if (c == campaigns.end())
6146                         c = campaigns.begin();
6147                     accountactive->setCurrentCampaign(*c);
6148                 }
6149                 LoadMenu();
6150                 break;
6151             }
6152             break;
6153         case 6:
6154             fireSound();
6155             if (selected == 1) {
6156                 flash();
6157                 accountactive = Account::destroy(accountactive);
6158                 mainmenu = 7;
6159             } else if (selected == 2) {
6160                 flash();
6161                 mainmenu = 5;
6162             }
6163             break;
6164         case 7:
6165             fireSound();
6166             if (selected == 0 && Account::getNbAccounts() < 8) {
6167                 entername = 1;
6168             } else if (selected < Account::getNbAccounts() + 1) {
6169                 flash();
6170                 mainmenu = 5;
6171                 accountactive = Account::get(selected - 1);
6172             } else if (selected == Account::getNbAccounts() + 1) {
6173                 flash();
6174                 if (accountactive)
6175                     mainmenu = 5;
6176                 else
6177                     mainmenu = 1;
6178                 for (int j = 0; j < 255; j++) {
6179                     displaytext[0][j] = 0;
6180                 }
6181                 displaychars[0] = 0;
6182                 displayselected = 0;
6183                 entername = 0;
6184             }
6185             break;
6186         case 8:
6187             fireSound();
6188             flash();
6189             if (selected <= 2)
6190                 accountactive->setDifficulty(selected);
6191             mainmenu = 5;
6192             break;
6193         case 9:
6194             if (selected < numchallengelevels && selected <= accountactive->getProgress()) {
6195                 fireSound();
6196                 flash();
6197
6198                 startbonustotal = 0;
6199
6200                 loading = 2;
6201                 loadtime = 0;
6202                 targetlevel = selected;
6203                 if (firstload)
6204                     TickOnceAfter();
6205                 else
6206                     LoadStuff();
6207                 Loadlevel(selected);
6208                 campaign = 0;
6209
6210                 mainmenu = 0;
6211                 gameon = 1;
6212                 pause_sound(stream_menutheme);
6213             }
6214             if (selected == numchallengelevels) {
6215                 fireSound();
6216                 flash();
6217                 mainmenu = 5;
6218             }
6219             break;
6220         case 10:
6221             if (selected == 3) {
6222                 fireSound();
6223                 flash();
6224                 mainmenu = 5;
6225             }
6226             break;
6227         case 18:
6228             if (selected == 1)
6229                 stereoseparation += 0.001;
6230             else {
6231                 fireSound();
6232                 if (selected == 0) {
6233                     newstereomode = (StereoMode)(newstereomode + 1);
6234                     while (!CanInitStereo(newstereomode)) {
6235                         printf("Failed to initialize mode %s (%i)\n", StereoModeName(newstereomode), newstereomode);
6236                         newstereomode = (StereoMode)(newstereomode + 1);
6237                         if (newstereomode >= stereoCount)
6238                             newstereomode = stereoNone;
6239                     }
6240                 } else if (selected == 2) {
6241                     stereoreverse = !stereoreverse;
6242                 } else if (selected == 3) {
6243                     flash();
6244                     mainmenu = 3;
6245
6246                     stereomode = newstereomode;
6247                     InitStereo(stereomode);
6248                 }
6249             }
6250             updateStereoConfigMenu();
6251             break;
6252         }
6253     }
6254
6255     if (Input::isKeyDown(SDLK_q) && Input::isKeyDown(SDLK_LMETA)) {
6256         tryquit = 1;
6257         if (mainmenu == 3) {
6258             SaveSettings();
6259         }
6260     }
6261
6262     OPENAL_SetFrequency(channels[stream_menutheme], 22050);
6263
6264     if (entername) {
6265         inputText(displaytext[0], &displayselected, &displaychars[0]);
6266         if (!waiting) { // the input as finished
6267             if (displaychars[0]) { // with enter
6268                 accountactive = Account::add(string(displaytext[0]));
6269
6270                 mainmenu = 8;
6271
6272                 flash();
6273
6274                 fireSound(firestartsound);
6275
6276                 for (int i = 0; i < 255; i++) {
6277                     displaytext[0][i] = 0;
6278                 }
6279                 displaychars[0] = 0;
6280
6281                 displayselected = 0;
6282             }
6283             entername = 0;
6284             LoadMenu();
6285         }
6286
6287         displayblinkdelay -= multiplier;
6288         if (displayblinkdelay <= 0) {
6289             displayblinkdelay = .3;
6290             displayblink = 1 - displayblink;
6291         }
6292     }
6293
6294     if (entername) {
6295         Menu::setText(0, displaytext[0], 20, 400, -1, -1);
6296         Menu::setText(-2, displayblink ? "_" : "", 20 + displayselected * 10, 400, -1, -1);
6297     }
6298
6299     if (oldmainmenu != mainmenu)
6300         LoadMenu();
6301     oldmainmenu = mainmenu;
6302
6303 }
6304
6305 void Game::Tick()
6306 {
6307     static XYZ facing, flatfacing;
6308     static int target;
6309
6310     for (int i = 0; i < 15; i++) {
6311         displaytime[i] += multiplier;
6312     }
6313
6314     keyboardfrozen = false;
6315     Input::Tick();
6316
6317     if (Input::isKeyPressed(SDLK_F6)) {
6318         if (Input::isKeyDown(SDLK_LSHIFT))
6319             stereoreverse = true;
6320         else
6321             stereoreverse = false;
6322
6323         if (stereoreverse)
6324             printf("Stereo reversed\n");
6325         else
6326             printf("Stereo unreversed\n");
6327     }
6328
6329     if (Input::isKeyDown(SDLK_F7)) {
6330         if (Input::isKeyDown(SDLK_LSHIFT))
6331             stereoseparation -= 0.001;
6332         else
6333             stereoseparation -= 0.010;
6334         printf("Stereo decreased increased to %f\n", stereoseparation);
6335     }
6336
6337     if (Input::isKeyDown(SDLK_F8)) {
6338         if (Input::isKeyDown(SDLK_LSHIFT))
6339             stereoseparation += 0.001;
6340         else
6341             stereoseparation += 0.010;
6342         printf("Stereo separation increased to %f\n", stereoseparation);
6343     }
6344
6345
6346     if (Input::isKeyPressed(SDLK_TAB) && tutoriallevel) {
6347         if (tutorialstage != 51)
6348             tutorialstagetime = tutorialmaxtime;
6349         emit_sound_np(consolefailsound, 128.);
6350     }
6351
6352     /*
6353     Values of mainmenu :
6354     1 Main menu
6355     2 Menu pause (resume/end game)
6356     3 Option menu
6357     4 Controls configuration menu
6358     5 Main game menu (choose level or challenge)
6359     6 Deleting user menu
6360     7 User managment menu (select/add)
6361     8 Choose difficulty menu
6362     9 Challenge level selection menu
6363     10 End of the campaign congratulation (is that really a menu?)
6364     11 Same that 9 ??? => unused
6365     18 stereo configuration
6366     */
6367
6368     if (!console) {
6369         //campaign over?
6370         if (mainmenu && endgame == 1)
6371             mainmenu = 10;
6372         //go to level select after completing a campaign level
6373         if (campaign && winfreeze && mainmenu == 0 && campaignlevels[actuallevel].choosenext == 1) {
6374             mainmenu = 5;
6375             gameon = 0;
6376             winfreeze = 0;
6377             fireSound();
6378             flash();
6379             if (musictoggle) {
6380                 OPENAL_SetFrequency(OPENAL_ALL, 0.001);
6381                 emit_stream_np(stream_menutheme);
6382                 pause_sound(leveltheme);
6383             }
6384             LoadMenu();
6385         }
6386         //escape key pressed
6387         if (Input::isKeyPressed(SDLK_ESCAPE) &&
6388                 (gameon || mainmenu == 0 || (mainmenu >= 3 && mainmenu != 8 && !(mainmenu == 7 && entername)))) {
6389             selected = -1;
6390             if (mainmenu == 0 && !winfreeze)
6391                 mainmenu = 2; //pause
6392             else if (mainmenu == 1 || mainmenu == 2) {
6393                 mainmenu = 0; //unpause
6394             }
6395             //play menu theme
6396             if (musictoggle && (mainmenu == 1 || mainmenu == 2)) {
6397                 OPENAL_SetFrequency(OPENAL_ALL, 0.001);
6398                 emit_stream_np(stream_menutheme);
6399                 pause_sound(leveltheme);
6400             }
6401             //on resume, play level music
6402             if (!mainmenu) {
6403                 pause_sound(stream_menutheme);
6404                 resume_stream(leveltheme);
6405             }
6406             //finished with settings menu
6407             if (mainmenu == 3) {
6408                 SaveSettings();
6409             }
6410             //effects
6411             if (mainmenu >= 3 && mainmenu != 8) {
6412                 fireSound();
6413                 flash();
6414             }
6415             //go back
6416             switch (mainmenu) {
6417             case 3:
6418             case 5:
6419                 mainmenu = gameon ? 2 : 1;
6420                 break;
6421             case 4:
6422             case 18:
6423                 mainmenu = 3;
6424                 break;
6425             case 6:
6426             case 7:
6427             case 9:
6428             case 10:
6429                 mainmenu = 5;
6430                 break;
6431             }
6432         }
6433     }
6434
6435     if (mainmenu) {
6436         MenuTick();
6437     }
6438
6439     if (!mainmenu) {
6440         if (hostile == 1)hostiletime += multiplier;
6441         else hostiletime = 0;
6442         if (!winfreeze)leveltime += multiplier;
6443
6444         //keys
6445         if (Input::isKeyPressed(SDLK_v) && debugmode) {
6446             freeze = 1 - freeze;
6447             if (freeze) {
6448                 OPENAL_SetFrequency(OPENAL_ALL, 0.001);
6449             }
6450         }
6451
6452         if (Input::isKeyPressed(chatkey) && !console && !chatting && debugmode)
6453             chatting = 1;
6454
6455         if (chatting) {
6456             inputText(displaytext[0], &displayselected, &displaychars[0]);
6457             if (!waiting) {
6458                 if (displaychars[0]) {
6459                     for (int j = 0; j < 255; j++)
6460                         displaytext[0][j] = 0;
6461                     displaychars[0] = 0;
6462                     displayselected = 0;
6463                 }
6464                 chatting = 0;
6465             }
6466
6467             displayblinkdelay -= multiplier;
6468             if (displayblinkdelay <= 0) {
6469                 displayblinkdelay = .3;
6470                 displayblink = 1 - displayblink;
6471             }
6472         }
6473         if (chatting)
6474             keyboardfrozen = true;
6475
6476         if (Input::isKeyPressed(consolekey) && debugmode) {
6477             console = !console;
6478             if (console) {
6479                 OPENAL_SetFrequency(OPENAL_ALL, 0.001);
6480             } else {
6481                 freeze = 0;
6482                 waiting = false;
6483             }
6484         }
6485
6486         if (console)
6487             freeze = 1;
6488         if (console && !Input::isKeyDown(SDLK_LMETA)) {
6489             inputText(consoletext[0], &consoleselected, &consolechars[0]);
6490             if (!waiting) {
6491                 if (consolechars[0] > 0) {
6492                     consoletext[0][consolechars[0]] = '\0';
6493                     cmd_dispatch(consoletext[0]);
6494                     for (int k = 14; k >= 1; k--) {
6495                         for (int j = 0; j < 255; j++)
6496                             consoletext[k][j] = consoletext[k - 1][j];
6497                         consolechars[k] = consolechars[k - 1];
6498                     }
6499                     for (int j = 0; j < 255; j++)
6500                         consoletext[0][j] = 0;
6501                     consolechars[0] = 0;
6502                     consoleselected = 0;
6503                 }
6504             }
6505
6506             consoleblinkdelay -= multiplier;
6507             if (consoleblinkdelay <= 0) {
6508                 consoleblinkdelay = .3;
6509                 consoleblink = 1 - consoleblink;
6510             }
6511         }
6512
6513
6514
6515         if (Input::isKeyDown(SDLK_q) && Input::isKeyDown(SDLK_LMETA)) {
6516             tryquit = 1;
6517             if (mainmenu == 3) {
6518                 SaveSettings();
6519             }
6520         }
6521
6522         static int oldwinfreeze;
6523         if (winfreeze && !oldwinfreeze) {
6524             OPENAL_SetFrequency(OPENAL_ALL, 0.001);
6525             emit_sound_np(consolesuccesssound);
6526         }
6527         if (winfreeze == 0)
6528             oldwinfreeze = winfreeze;
6529         else
6530             oldwinfreeze++;
6531
6532         if ((Input::isKeyPressed(jumpkey) || Input::isKeyPressed(SDLK_SPACE)) && !campaign)
6533             if (winfreeze)
6534                 winfreeze = 0;
6535         if ((Input::isKeyDown(SDLK_ESCAPE)) && !campaign && gameon) {
6536             if (console) {
6537                 console = false;
6538                 freeze = 0;
6539             } else if (winfreeze) {
6540                 mainmenu = 9;
6541                 gameon = 0;
6542             }
6543         }
6544
6545
6546
6547         if (!freeze && !winfreeze && !(mainmenu && gameon) && (gameon || !gamestarted)) {
6548
6549             //dialogues
6550             static float talkdelay = 0;
6551
6552             if (indialogue != -1)
6553                 talkdelay = 1;
6554             talkdelay -= multiplier;
6555
6556             if (talkdelay <= 0 && indialogue == -1 && animation[player[0].animTarget].height != highheight)
6557                 for (int i = 0; i < numdialogues; i++) {
6558                     int realdialoguetype;
6559                     bool special;
6560                     if (dialoguetype[i] > 49) {
6561                         realdialoguetype = dialoguetype[i] - 50;
6562                         special = 1;
6563                     } else if (dialoguetype[i] > 39) {
6564                         realdialoguetype = dialoguetype[i] - 40;
6565                         special = 1;
6566                     } else if (dialoguetype[i] > 29) {
6567                         realdialoguetype = dialoguetype[i] - 30;
6568                         special = 1;
6569                     } else if (dialoguetype[i] > 19) {
6570                         realdialoguetype = dialoguetype[i] - 20;
6571                         special = 1;
6572                     } else if (dialoguetype[i] > 9) {
6573                         realdialoguetype = dialoguetype[i] - 10;
6574                         special = 1;
6575                     } else {
6576                         realdialoguetype = dialoguetype[i];
6577                         special = 0;
6578                     }
6579                     if ((!hostile || dialoguetype[i] > 40 && dialoguetype[i] < 50) &&
6580                             realdialoguetype < numplayers &&
6581                             realdialoguetype > 0 &&
6582                             (dialoguegonethrough[i] == 0 || !special) &&
6583                             (special || Input::isKeyPressed(attackkey))) {
6584                         if (distsq(&player[0].coords, &player[realdialoguetype].coords) < 6 ||
6585                                 player[realdialoguetype].howactive >= typedead1 ||
6586                                 dialoguetype[i] > 40 && dialoguetype[i] < 50) {
6587                             whichdialogue = i;
6588                             for (int j = 0; j < numdialogueboxes[whichdialogue]; j++) {
6589                                 player[participantfocus[whichdialogue][j]].coords = participantlocation[whichdialogue][participantfocus[whichdialogue][j]];
6590                                 player[participantfocus[whichdialogue][j]].yaw = participantyaw[whichdialogue][participantfocus[whichdialogue][j]];
6591                                 player[participantfocus[whichdialogue][j]].targetyaw = participantyaw[whichdialogue][participantfocus[whichdialogue][j]];
6592                                 player[participantfocus[whichdialogue][j]].velocity = 0;
6593                                 player[participantfocus[whichdialogue][j]].animTarget = player[participantfocus[whichdialogue][j]].getIdle();
6594                                 player[participantfocus[whichdialogue][j]].frameTarget = 0;
6595                             }
6596                             directing = 0;
6597                             indialogue = 0;
6598                             dialoguetime = 0;
6599                             dialoguegonethrough[i]++;
6600                             if (dialogueboxsound[whichdialogue][indialogue] != 0) {
6601                                 playdialogueboxsound();
6602                             }
6603                         }
6604                     }
6605                 }
6606
6607             windvar += multiplier;
6608             smoketex += multiplier;
6609             tutorialstagetime += multiplier;
6610
6611             //hotspots
6612             static float hotspotvisual[40];
6613             if (numhotspots) {
6614                 XYZ hotspotsprite;
6615                 if (editorenabled)
6616                     for (int i = 0; i < numhotspots; i++)
6617                         hotspotvisual[i] -= multiplier / 320;
6618
6619                 for (int i = 0; i < numhotspots; i++) {
6620                     //if(hotspottype[i]<=10)
6621                     while (hotspotvisual[i] < 0) {
6622                         hotspotsprite = 0;
6623                         hotspotsprite.x = float(abs(Random() % 100000)) / 100000 * hotspotsize[i];
6624                         hotspotsprite = DoRotation(hotspotsprite, 0, 0, Random() % 360);
6625                         hotspotsprite = DoRotation(hotspotsprite, 0, Random() % 360, 0);
6626                         hotspotsprite += hotspot[i];
6627                         Sprite::MakeSprite(breathsprite, hotspotsprite, hotspotsprite * 0, 1, 0.5, 0, 7, 0.4);
6628                         hotspotvisual[i] += 0.1 / hotspotsize[i] / hotspotsize[i] / hotspotsize[i];
6629                     }
6630                 }
6631
6632                 for (int i = 0; i < numhotspots; i++) {
6633                     if (hotspottype[i] <= 10 && hotspottype[i] > 0) {
6634                         hotspot[i] = player[hotspottype[i]].coords;
6635                     }
6636                 }
6637             }
6638
6639             //Tutorial
6640             if (tutoriallevel) {
6641                 doTutorial();
6642             }
6643
6644             //bonuses
6645             if (tutoriallevel != 1) {
6646                 if (bonustime == 0 &&
6647                         bonus != solidhit &&
6648                         bonus != spinecrusher &&
6649                         bonus != tracheotomy &&
6650                         bonus != backstab &&
6651                         bonusvalue > 10) {
6652                     emit_sound_np(consolesuccesssound);
6653                 }
6654             } else if (bonustime == 0) {
6655                 emit_sound_np(fireendsound);
6656             }
6657             if (bonustime == 0) {
6658                 if (bonus != solidhit &&
6659                         bonus != twoxcombo &&
6660                         bonus != threexcombo &&
6661                         bonus != fourxcombo &&
6662                         bonus != megacombo)
6663                     bonusnum[bonus]++;
6664                 else
6665                     bonusnum[bonus] += 0.15;
6666                 if (tutoriallevel)
6667                     bonusvalue = 0;
6668                 bonusvalue /= bonusnum[bonus];
6669                 bonustotal += bonusvalue;
6670             }
6671             bonustime += multiplier;
6672
6673             //snow effects
6674             if (environment == snowyenvironment) {
6675                 precipdelay -= multiplier;
6676                 while (precipdelay < 0) {
6677                     precipdelay += .04;
6678                     if (!detail)
6679                         precipdelay += .04;
6680                     XYZ footvel, footpoint;
6681
6682                     footvel = 0;
6683                     footpoint = viewer + viewerfacing * 6;
6684                     footpoint.y += ((float)abs(Random() % 1200)) / 100 - 6;
6685                     footpoint.x += ((float)abs(Random() % 1200)) / 100 - 6;
6686                     footpoint.z += ((float)abs(Random() % 1200)) / 100 - 6;
6687                     Sprite::MakeSprite(snowsprite, footpoint, footvel, 1, 1, 1, .1, 1);
6688                 }
6689             }
6690
6691
6692             doAerialAcrobatics();
6693
6694
6695             static XYZ oldviewer;
6696
6697             //control keys
6698             if (indialogue == -1) {
6699                 player[0].forwardkeydown = Input::isKeyDown(forwardkey);
6700                 player[0].leftkeydown = Input::isKeyDown(leftkey);
6701                 player[0].backkeydown = Input::isKeyDown(backkey);
6702                 player[0].rightkeydown = Input::isKeyDown(rightkey);
6703                 player[0].jumpkeydown = Input::isKeyDown(jumpkey);
6704                 player[0].crouchkeydown = Input::isKeyDown(crouchkey);
6705                 player[0].drawkeydown = Input::isKeyDown(drawkey);
6706                 player[0].throwkeydown = Input::isKeyDown(throwkey);
6707             } else {
6708                 player[0].forwardkeydown = 0;
6709                 player[0].leftkeydown = 0;
6710                 player[0].backkeydown = 0;
6711                 player[0].rightkeydown = 0;
6712                 player[0].jumpkeydown = 0;
6713                 player[0].crouchkeydown = 0;
6714                 player[0].drawkeydown = 0;
6715                 player[0].throwkeydown = 0;
6716             }
6717
6718             if (!player[0].jumpkeydown)
6719                 player[0].jumpclimb = 0;
6720
6721
6722             if (indialogue != -1) {
6723                 cameramode = 1;
6724                 if (directing) {
6725                     facing = 0;
6726                     facing.z = -1;
6727
6728                     facing = DoRotation(facing, -pitch, 0, 0);
6729                     facing = DoRotation(facing, 0, 0 - yaw, 0);
6730
6731                     flatfacing = 0;
6732                     flatfacing.z = -1;
6733
6734                     flatfacing = DoRotation(flatfacing, 0, -yaw, 0);
6735
6736                     if (Input::isKeyDown(forwardkey))
6737                         viewer += facing * multiplier * 4;
6738                     if (Input::isKeyDown(backkey))
6739                         viewer -= facing * multiplier * 4;
6740                     if (Input::isKeyDown(leftkey))
6741                         viewer += DoRotation(flatfacing * multiplier, 0, 90, 0) * 4;
6742                     if (Input::isKeyDown(rightkey))
6743                         viewer += DoRotation(flatfacing * multiplier, 0, -90, 0) * 4;
6744                     if (Input::isKeyDown(jumpkey))
6745                         viewer.y += multiplier * 4;
6746                     if (Input::isKeyDown(crouchkey))
6747                         viewer.y -= multiplier * 4;
6748                     if (     Input::isKeyPressed(SDLK_1) ||
6749                              Input::isKeyPressed(SDLK_2) ||
6750                              Input::isKeyPressed(SDLK_3) ||
6751                              Input::isKeyPressed(SDLK_4) ||
6752                              Input::isKeyPressed(SDLK_5) ||
6753                              Input::isKeyPressed(SDLK_6) ||
6754                              Input::isKeyPressed(SDLK_7) ||
6755                              Input::isKeyPressed(SDLK_8) ||
6756                              Input::isKeyPressed(SDLK_9) ||
6757                              Input::isKeyPressed(SDLK_0) ||
6758                              Input::isKeyPressed(SDLK_MINUS)) {
6759                         int whichend;
6760                         if (Input::isKeyPressed(SDLK_1))whichend = 1;
6761                         if (Input::isKeyPressed(SDLK_2))whichend = 2;
6762                         if (Input::isKeyPressed(SDLK_3))whichend = 3;
6763                         if (Input::isKeyPressed(SDLK_4))whichend = 4;
6764                         if (Input::isKeyPressed(SDLK_5))whichend = 5;
6765                         if (Input::isKeyPressed(SDLK_6))whichend = 6;
6766                         if (Input::isKeyPressed(SDLK_7))whichend = 7;
6767                         if (Input::isKeyPressed(SDLK_8))whichend = 8;
6768                         if (Input::isKeyPressed(SDLK_9))whichend = 9;
6769                         if (Input::isKeyPressed(SDLK_0))whichend = 0;
6770                         if (Input::isKeyPressed(SDLK_MINUS))
6771                             whichend = -1;
6772                         if (whichend != -1) {
6773                             participantfocus[whichdialogue][indialogue] = whichend;
6774                             participantlocation[whichdialogue][whichend] = player[whichend].coords;
6775                             participantyaw[whichdialogue][whichend] = player[whichend].yaw;
6776                         }
6777                         if (whichend == -1) {
6778                             participantfocus[whichdialogue][indialogue] = -1;
6779                         }
6780                         if (player[participantfocus[whichdialogue][indialogue]].dead) {
6781                             indialogue = -1;
6782                             directing = 0;
6783                             cameramode = 0;
6784                         }
6785                         dialoguecamera[whichdialogue][indialogue] = viewer;
6786                         dialoguecamerayaw[whichdialogue][indialogue] = yaw;
6787                         dialoguecamerapitch[whichdialogue][indialogue] = pitch;
6788                         indialogue++;
6789                         if (indialogue < numdialogueboxes[whichdialogue]) {
6790                             if (dialogueboxsound[whichdialogue][indialogue] != 0) {
6791                                 playdialogueboxsound();
6792                             }
6793                         }
6794
6795                         for (int j = 0; j < numplayers; j++) {
6796                             participantfacing[whichdialogue][indialogue][j] = participantfacing[whichdialogue][indialogue - 1][j];
6797                         }
6798                     }
6799                     //TODO: should these be KeyDown or KeyPressed?
6800                     if (     Input::isKeyDown(SDLK_KP1) ||
6801                              Input::isKeyDown(SDLK_KP2) ||
6802                              Input::isKeyDown(SDLK_KP3) ||
6803                              Input::isKeyDown(SDLK_KP4) ||
6804                              Input::isKeyDown(SDLK_KP5) ||
6805                              Input::isKeyDown(SDLK_KP6) ||
6806                              Input::isKeyDown(SDLK_KP7) ||
6807                              Input::isKeyDown(SDLK_KP8) ||
6808                              Input::isKeyDown(SDLK_KP9) ||
6809                              Input::isKeyDown(SDLK_KP0)) {
6810                         int whichend;
6811                         if (Input::isKeyDown(SDLK_KP1))whichend = 1;
6812                         if (Input::isKeyDown(SDLK_KP2))whichend = 2;
6813                         if (Input::isKeyDown(SDLK_KP3))whichend = 3;
6814                         if (Input::isKeyDown(SDLK_KP4))whichend = 4;
6815                         if (Input::isKeyDown(SDLK_KP5))whichend = 5;
6816                         if (Input::isKeyDown(SDLK_KP6))whichend = 6;
6817                         if (Input::isKeyDown(SDLK_KP7))whichend = 7;
6818                         if (Input::isKeyDown(SDLK_KP8))whichend = 8;
6819                         if (Input::isKeyDown(SDLK_KP9))whichend = 9;
6820                         if (Input::isKeyDown(SDLK_KP0))whichend = 0;
6821                         participantfacing[whichdialogue][indialogue][whichend] = facing;
6822                     }
6823                     if (indialogue >= numdialogueboxes[whichdialogue]) {
6824                         indialogue = -1;
6825                         directing = 0;
6826                         cameramode = 0;
6827                     }
6828                 }
6829                 if (!directing) {
6830                     pause_sound(whooshsound);
6831                     viewer = dialoguecamera[whichdialogue][indialogue];
6832                     viewer.y = max((double)viewer.y, terrain.getHeight(viewer.x, viewer.z) + .1);
6833                     yaw = dialoguecamerayaw[whichdialogue][indialogue];
6834                     pitch = dialoguecamerapitch[whichdialogue][indialogue];
6835                     if (dialoguetime > 0.5)
6836                         if (     Input::isKeyPressed(SDLK_1) ||
6837                                  Input::isKeyPressed(SDLK_2) ||
6838                                  Input::isKeyPressed(SDLK_3) ||
6839                                  Input::isKeyPressed(SDLK_4) ||
6840                                  Input::isKeyPressed(SDLK_5) ||
6841                                  Input::isKeyPressed(SDLK_6) ||
6842                                  Input::isKeyPressed(SDLK_7) ||
6843                                  Input::isKeyPressed(SDLK_8) ||
6844                                  Input::isKeyPressed(SDLK_9) ||
6845                                  Input::isKeyPressed(SDLK_0) ||
6846                                  Input::isKeyPressed(SDLK_MINUS) ||
6847                                  Input::isKeyPressed(attackkey)) {
6848                             indialogue++;
6849                             if (indialogue < numdialogueboxes[whichdialogue]) {
6850                                 if (dialogueboxsound[whichdialogue][indialogue] != 0) {
6851                                     playdialogueboxsound();
6852                                     if (dialogueboxsound[whichdialogue][indialogue] == -5) {
6853                                         hotspot[numhotspots] = player[0].coords;
6854                                         hotspotsize[numhotspots] = 10;
6855                                         hotspottype[numhotspots] = -1;
6856
6857                                         numhotspots++;
6858                                     }
6859                                     if (dialogueboxsound[whichdialogue][indialogue] == -6) {
6860                                         hostile = 1;
6861                                     }
6862
6863                                     if (player[participantfocus[whichdialogue][indialogue]].dead) {
6864                                         indialogue = -1;
6865                                         directing = 0;
6866                                         cameramode = 0;
6867                                     }
6868                                 }
6869                             }
6870                         }
6871                     if (indialogue >= numdialogueboxes[whichdialogue]) {
6872                         indialogue = -1;
6873                         directing = 0;
6874                         cameramode = 0;
6875                         if (dialoguetype[whichdialogue] > 19 && dialoguetype[whichdialogue] < 30) {
6876                             hostile = 1;
6877                         }
6878                         if (dialoguetype[whichdialogue] > 29 && dialoguetype[whichdialogue] < 40) {
6879                             windialogue = true;
6880                         }
6881                         if (dialoguetype[whichdialogue] > 49 && dialoguetype[whichdialogue] < 60) {
6882                             hostile = 1;
6883                             for (int i = 1; i < numplayers; i++) {
6884                                 player[i].aitype = attacktypecutoff;
6885                             }
6886                         }
6887                     }
6888                 }
6889             }
6890
6891             if (!player[0].jumpkeydown) {
6892                 player[0].jumptogglekeydown = 0;
6893             }
6894             if (player[0].jumpkeydown &&
6895                     player[0].animTarget != jumpupanim &&
6896                     player[0].animTarget != jumpdownanim &&
6897                     !player[0].isFlip())
6898                 player[0].jumptogglekeydown = 1;
6899
6900
6901             dialoguetime += multiplier;
6902             hawkyaw += multiplier * 25;
6903             realhawkcoords = 0;
6904             realhawkcoords.x = 25;
6905             realhawkcoords = DoRotation(realhawkcoords, 0, hawkyaw, 0) + hawkcoords;
6906             hawkcalldelay -= multiplier / 2;
6907
6908             if (hawkcalldelay <= 0) {
6909                 emit_sound_at(hawksound, realhawkcoords);
6910
6911                 hawkcalldelay = 16 + abs(Random() % 8);
6912             }
6913
6914             doDebugKeys();
6915
6916             doAttacks();
6917
6918             doPlayerCollisions();
6919
6920             doJumpReversals();
6921
6922             for (int k = 0; k < numplayers; k++)
6923                 if (k != 0 && player[k].immobile)
6924                     player[k].coords = player[k].realoldcoords;
6925
6926             for (int k = 0; k < numplayers; k++) {
6927                 if (!isnormal(player[k].coords.x) || !isnormal(player[k].coords.y) || !isnormal(player[k].coords.z)) {
6928                     if (!isnormal(player[k].coords.x) || !isnormal(player[k].coords.y) || !isnormal(player[k].coords.z)) {
6929                         player[k].DoDamage(1000);
6930                     }
6931                 }
6932             }
6933
6934             //respawn
6935             static bool respawnkeydown;
6936             if (!editorenabled &&
6937                     (whichlevel != -2 &&
6938                      (Input::isKeyDown(SDLK_z) &&
6939                       Input::isKeyDown(SDLK_LMETA) &&
6940                       debugmode) ||
6941                      (Input::isKeyDown(jumpkey) &&
6942                       !respawnkeydown &&
6943                       !oldattackkey &&
6944                       player[0].dead))) {
6945                 targetlevel = whichlevel;
6946                 loading = 1;
6947                 leveltime = 5;
6948             }
6949             if (!Input::isKeyDown(jumpkey))
6950                 respawnkeydown = 0;
6951             if (Input::isKeyDown(jumpkey))
6952                 respawnkeydown = 1;
6953
6954
6955
6956
6957             static bool movekey;
6958
6959             //?
6960             for (int i = 0; i < numplayers; i++) {
6961                 static float oldtargetyaw;
6962                 if (!player[i].skeleton.free) {
6963                     oldtargetyaw = player[i].targetyaw;
6964                     if (i == 0 && indialogue == -1) {
6965                         //TODO: refactor repetitive code
6966                         if (!animation[player[0].animTarget].attack &&
6967                                 player[0].animTarget != staggerbackhighanim &&
6968                                 player[0].animTarget != staggerbackhardanim &&
6969                                 player[0].animTarget != crouchremoveknifeanim &&
6970                                 player[0].animTarget != removeknifeanim &&
6971                                 player[0].animTarget != backhandspringanim &&
6972                                 player[0].animTarget != dodgebackanim &&
6973                                 player[0].animTarget != walljumprightkickanim &&
6974                                 player[0].animTarget != walljumpleftkickanim) {
6975                             if (cameramode)
6976                                 player[0].targetyaw = 0;
6977                             else
6978                                 player[0].targetyaw = -yaw + 180;
6979                         }
6980
6981                         facing = 0;
6982                         facing.z = -1;
6983
6984                         flatfacing = DoRotation(facing, 0, player[i].yaw + 180, 0);
6985                         if (cameramode) {
6986                             facing = flatfacing;
6987                         } else {
6988                             facing = DoRotation(facing, -pitch, 0, 0);
6989                             facing = DoRotation(facing, 0, 0 - yaw, 0);
6990                         }
6991
6992                         player[0].lookyaw = -yaw;
6993
6994                         player[i].targetheadyaw = yaw;
6995                         player[i].targetheadpitch = pitch;
6996                     }
6997                     if (i != 0 && player[i].aitype == playercontrolled && indialogue == -1) {
6998                         if (!animation[player[i].animTarget].attack &&
6999                                 player[i].animTarget != staggerbackhighanim &&
7000                                 player[i].animTarget != staggerbackhardanim &&
7001                                 player[i].animTarget != crouchremoveknifeanim &&
7002                                 player[i].animTarget != removeknifeanim &&
7003                                 player[i].animTarget != backhandspringanim &&
7004                                 player[i].animTarget != dodgebackanim &&
7005                                 player[i].animTarget != walljumprightkickanim &&
7006                                 player[i].animTarget != walljumpleftkickanim) {
7007                             player[i].targetyaw = -player[i].lookyaw + 180;
7008                         }
7009
7010                         facing = 0;
7011                         facing.z = -1;
7012
7013                         flatfacing = DoRotation(facing, 0, player[i].yaw + 180, 0);
7014
7015                         facing = DoRotation(facing, -player[i].lookpitch, 0, 0);
7016                         facing = DoRotation(facing, 0, 0 - player[i].lookyaw, 0);
7017
7018                         player[i].targetheadyaw = player[i].lookyaw;
7019                         player[i].targetheadpitch = player[i].lookpitch;
7020                     }
7021                     if (indialogue != -1) {
7022                         player[i].targetheadyaw = 180 - roughDirection(participantfacing[whichdialogue][indialogue][i]);
7023                         player[i].targetheadpitch = pitchOf(participantfacing[whichdialogue][indialogue][i]);
7024                     }
7025
7026                     if (leveltime < .5)
7027                         numenvsounds = 0;
7028
7029                     player[i].avoidsomething = 0;
7030
7031                     //avoid flaming things
7032                     for (int j = 0; j < objects.numobjects; j++)
7033                         if (objects.onfire[j])
7034                             if (distsq(&player[i].coords, &objects.position[j]) < sq(objects.scale[j]) * 200)
7035                                 if (     distsq(&player[i].coords, &objects.position[j]) <
7036                                          distsq(&player[i].coords, &player[0].coords)) {
7037                                     player[i].collided = 0;
7038                                     player[i].avoidcollided = 1;
7039                                     if (player[i].avoidsomething == 0 ||
7040                                             distsq(&player[i].coords, &objects.position[j]) <
7041                                             distsq(&player[i].coords, &player[i].avoidwhere)) {
7042                                         player[i].avoidwhere = objects.position[j];
7043                                         player[i].avoidsomething = 1;
7044                                     }
7045                                 }
7046
7047                     //avoid flaming players
7048                     for (int j = 0; j < numplayers; j++)
7049                         if (player[j].onfire)
7050                             if (distsq(&player[j].coords, &player[i].coords) < sq(0.3) * 200)
7051                                 if (     distsq(&player[i].coords, &player[j].coords) <
7052                                          distsq(&player[i].coords, &player[0].coords)) {
7053                                     player[i].collided = 0;
7054                                     player[i].avoidcollided = 1;
7055                                     if (player[i].avoidsomething == 0 ||
7056                                             distsq(&player[i].coords, &player[j].coords) <
7057                                             distsq(&player[i].coords, &player[i].avoidwhere)) {
7058                                         player[i].avoidwhere = player[j].coords;
7059                                         player[i].avoidsomething = 1;
7060                                     }
7061                                 }
7062
7063                     if (player[i].collided > .8)
7064                         player[i].avoidcollided = 0;
7065
7066                     doAI(i);
7067
7068                     if (animation[player[i].animTarget].attack == reversed) {
7069                         //player[i].targetyaw=player[i].yaw;
7070                         player[i].forwardkeydown = 0;
7071                         player[i].leftkeydown = 0;
7072                         player[i].backkeydown = 0;
7073                         player[i].rightkeydown = 0;
7074                         player[i].jumpkeydown = 0;
7075                         player[i].attackkeydown = 0;
7076                         //player[i].crouchkeydown=0;
7077                         player[i].throwkeydown = 0;
7078                     }
7079
7080                     if (indialogue != -1) {
7081                         player[i].forwardkeydown = 0;
7082                         player[i].leftkeydown = 0;
7083                         player[i].backkeydown = 0;
7084                         player[i].rightkeydown = 0;
7085                         player[i].jumpkeydown = 0;
7086                         player[i].crouchkeydown = 0;
7087                         player[i].drawkeydown = 0;
7088                         player[i].throwkeydown = 0;
7089                     }
7090
7091                     if (player[i].collided < -.3)
7092                         player[i].collided = -.3;
7093                     if (player[i].collided > 1)
7094                         player[i].collided = 1;
7095                     player[i].collided -= multiplier * 4;
7096                     player[i].whichdirectiondelay -= multiplier;
7097                     if (player[i].avoidcollided < -.3 || player[i].whichdirectiondelay <= 0) {
7098                         player[i].avoidcollided = -.3;
7099                         player[i].whichdirection = abs(Random() % 2);
7100                         player[i].whichdirectiondelay = .4;
7101                     }
7102                     if (player[i].avoidcollided > 1)
7103                         player[i].avoidcollided = 1;
7104                     player[i].avoidcollided -= multiplier / 4;
7105                     if (!player[i].skeleton.free) {
7106                         player[i].stunned -= multiplier;
7107                         player[i].surprised -= multiplier;
7108                     }
7109                     if (i != 0 && player[i].surprised <= 0 &&
7110                             player[i].aitype == attacktypecutoff &&
7111                             !player[i].dead &&
7112                             !player[i].skeleton.free &&
7113                             animation[player[i].animTarget].attack == neutral)
7114                         numresponded = 1;
7115
7116                     if (!player[i].throwkeydown)
7117                         player[i].throwtogglekeydown = 0;
7118
7119                     //pick up weapon
7120                     if (player[i].throwkeydown && !player[i].throwtogglekeydown) {
7121                         if (player[i].weaponactive == -1 &&
7122                                 player[i].num_weapons < 2 &&
7123                                 (player[i].isIdle() ||
7124                                  player[i].isCrouch() ||
7125                                  player[i].animTarget == sneakanim ||
7126                                  player[i].animTarget == rollanim ||
7127                                  player[i].animTarget == backhandspringanim ||
7128                                  player[i].isFlip() ||
7129                                  player[i].isFlip() ||
7130                                  player[i].aitype != playercontrolled)) {
7131                             for (int j = 0; j < weapons.size(); j++) {
7132                                 if ((weapons[j].velocity.x == 0 && weapons[j].velocity.y == 0 && weapons[j].velocity.z == 0 ||
7133                                         player[i].aitype == playercontrolled) &&
7134                                         weapons[j].owner == -1 &&
7135                                         player[i].weaponactive == -1)
7136                                     if (distsqflat(&player[i].coords, &weapons[j].position) < 2) {
7137                                         if (distsq(&player[i].coords, &weapons[j].position) < 2) {
7138                                             if (player[i].isCrouch() ||
7139                                                     player[i].animTarget == sneakanim ||
7140                                                     player[i].isRun() ||
7141                                                     player[i].isIdle() ||
7142                                                     player[i].aitype != playercontrolled) {
7143                                                 player[i].throwtogglekeydown = 1;
7144                                                 player[i].setAnimation(crouchremoveknifeanim);
7145                                                 player[i].targetyaw = roughDirectionTo(player[i].coords, weapons[j].position);
7146                                                 player[i].hasvictim = 0;
7147                                             }
7148                                             if (player[i].animTarget == rollanim || player[i].animTarget == backhandspringanim) {
7149                                                 player[i].throwtogglekeydown = 1;
7150                                                 player[i].hasvictim = 0;
7151
7152                                                 if ((weapons[j].velocity.x == 0 && weapons[j].velocity.y == 0 && weapons[j].velocity.z == 0 ||
7153                                                         player[i].aitype == playercontrolled) &&
7154                                                         weapons[j].owner == -1 ||
7155                                                         player[i].victim &&
7156                                                         weapons[j].owner == player[i].victim->id)
7157                                                     if (distsqflat(&player[i].coords, &weapons[j].position) < 2 && player[i].weaponactive == -1)
7158                                                         if (distsq(&player[i].coords, &weapons[j].position) < 1 || player[i].victim) {
7159                                                             if (weapons[j].getType() != staff)
7160                                                                 emit_sound_at(knifedrawsound, player[i].coords, 128.);
7161
7162                                                             player[i].weaponactive = 0;
7163                                                             weapons[j].owner = player[i].id;
7164                                                             if (player[i].num_weapons > 0)
7165                                                                 player[i].weaponids[player[i].num_weapons] = player[i].weaponids[0];
7166                                                             player[i].num_weapons++;
7167                                                             player[i].weaponids[0] = j;
7168                                                         }
7169                                             }
7170                                         } else if ((player[i].isIdle() ||
7171                                                     player[i].isFlip() ||
7172                                                     player[i].aitype != playercontrolled) &&
7173                                                    distsq(&player[i].coords, &weapons[j].position) < 5 &&
7174                                                    player[i].coords.y < weapons[j].position.y) {
7175                                             if (!player[i].isFlip()) {
7176                                                 player[i].throwtogglekeydown = 1;
7177                                                 player[i].setAnimation(removeknifeanim);
7178                                                 player[i].targetyaw = roughDirectionTo(player[i].coords, weapons[j].position);
7179                                             }
7180                                             if (player[i].isFlip()) {
7181                                                 player[i].throwtogglekeydown = 1;
7182                                                 player[i].hasvictim = 0;
7183
7184                                                 for (int k = 0; k < weapons.size(); k++) {
7185                                                     if (player[i].weaponactive == -1)
7186                                                         if ((weapons[k].velocity.x == 0 && weapons[k].velocity.y == 0 && weapons[k].velocity.z == 0 ||
7187                                                                 player[i].aitype == playercontrolled) &&
7188                                                                 weapons[k].owner == -1 ||
7189                                                                 player[i].victim &&
7190                                                                 weapons[k].owner == player[i].victim->id)
7191                                                             if (distsqflat(&player[i].coords, &weapons[k].position) < 3 &&
7192                                                                     player[i].weaponactive == -1) {
7193                                                                 if (weapons[k].getType() != staff)
7194                                                                     emit_sound_at(knifedrawsound, player[i].coords, 128.);
7195
7196                                                                 player[i].weaponactive = 0;
7197                                                                 weapons[k].owner = player[i].id;
7198                                                                 if (player[i].num_weapons > 0)
7199                                                                     player[i].weaponids[player[i].num_weapons] = player[i].weaponids[0];
7200                                                                 player[i].num_weapons++;
7201                                                                 player[i].weaponids[0] = k;
7202                                                             }
7203                                                 }
7204                                             }
7205                                         }
7206                                     }
7207                             }
7208                             if (player[i].isCrouch() ||
7209                                     player[i].animTarget == sneakanim ||
7210                                     player[i].isRun() ||
7211                                     player[i].isIdle() || player[i].animTarget == rollanim ||
7212                                     player[i].animTarget == backhandspringanim) {
7213                                 if (numplayers > 1)
7214                                     for (int j = 0; j < numplayers; j++) {
7215                                         if (player[i].weaponactive == -1)
7216                                             if (j != i)
7217                                                 if (player[j].num_weapons &&
7218                                                         player[j].skeleton.free &&
7219                                                         distsq(&player[i].coords, &player[j].coords) < 2/*&&player[j].dead*/ &&
7220                                                         (((player[j].skeleton.forward.y < 0 &&
7221                                                            player[j].weaponstuckwhere == 0) ||
7222                                                           (player[j].skeleton.forward.y > 0 &&
7223                                                            player[j].weaponstuckwhere == 1)) ||
7224                                                          player[j].weaponstuck == -1 ||
7225                                                          player[j].num_weapons > 1)) {
7226                                                     if (player[i].animTarget != rollanim && player[i].animTarget != backhandspringanim) {
7227                                                         player[i].throwtogglekeydown = 1;
7228                                                         player[i].victim = &player[j];
7229                                                         player[i].hasvictim = 1;
7230                                                         player[i].setAnimation(crouchremoveknifeanim);
7231                                                         player[i].targetyaw = roughDirectionTo(player[i].coords, player[j].coords);
7232                                                     }
7233                                                     if (player[i].animTarget == rollanim || player[i].animTarget == backhandspringanim) {
7234                                                         player[i].throwtogglekeydown = 1;
7235                                                         player[i].victim = &player[j];
7236                                                         player[i].hasvictim = 1;
7237                                                         int k = player[j].weaponids[0];
7238                                                         if (player[i].hasvictim) {
7239                                                             bool fleshstuck;
7240                                                             fleshstuck = 0;
7241                                                             if (player[i].victim->weaponstuck != -1) {
7242                                                                 if (player[i].victim->weaponids[player[i].victim->weaponstuck] == k) {
7243                                                                     fleshstuck = 1;
7244                                                                 }
7245                                                             }
7246                                                             if (!fleshstuck) {
7247                                                                 if (weapons[k].getType() != staff)
7248                                                                     emit_sound_at(knifedrawsound, player[i].coords, 128.);
7249                                                             }
7250                                                             if (fleshstuck)
7251                                                                 emit_sound_at(fleshstabremovesound, player[i].coords, 128.);
7252
7253                                                             player[i].weaponactive = 0;
7254                                                             if (weapons[k].owner != -1) {
7255                                                                 if (player[i].victim->num_weapons == 1)player[i].victim->num_weapons = 0;
7256                                                                 else player[i].victim->num_weapons = 1;
7257
7258                                                                 player[i].victim->skeleton.longdead = 0;
7259                                                                 player[i].victim->skeleton.free = 1;
7260                                                                 player[i].victim->skeleton.broken = 0;
7261
7262                                                                 for (int l = 0; l < player[i].victim->skeleton.num_joints; l++) {
7263                                                                     player[i].victim->skeleton.joints[l].velchange = 0;
7264                                                                     player[i].victim->skeleton.joints[l].locked = 0;
7265                                                                 }
7266
7267                                                                 XYZ relative;
7268                                                                 relative = 0;
7269                                                                 relative.y = 10;
7270                                                                 Normalise(&relative);
7271                                                                 XYZ footvel, footpoint;
7272                                                                 footvel = 0;
7273                                                                 footpoint = weapons[k].position;
7274                                                                 if (player[i].victim->weaponstuck != -1) {
7275                                                                     if (player[i].victim->weaponids[player[i].victim->weaponstuck] == k) {
7276                                                                         if (bloodtoggle)Sprite::MakeSprite(cloudimpactsprite, footpoint, footvel, 1, 0, 0, .8, .3);
7277                                                                         weapons[k].bloody = 2;
7278                                                                         weapons[k].blooddrip = 5;
7279                                                                         player[i].victim->weaponstuck = -1;
7280                                                                         player[i].victim->bloodloss += 2000;
7281                                                                         player[i].victim->DoDamage(2000);
7282                                                                     }
7283                                                                 }
7284                                                                 if (player[i].victim->num_weapons > 0) {
7285                                                                     if (player[i].victim->weaponstuck != 0 && player[i].victim->weaponstuck != -1)player[i].victim->weaponstuck = 0;
7286                                                                     if (player[i].victim->weaponids[0] == k)
7287                                                                         player[i].victim->weaponids[0] = player[i].victim->weaponids[player[i].victim->num_weapons];
7288                                                                 }
7289
7290                                                                 player[i].victim->weaponactive = -1;
7291
7292                                                                 player[i].victim->jointVel(abdomen) += relative * 6;
7293                                                                 player[i].victim->jointVel(neck) += relative * 6;
7294                                                                 player[i].victim->jointVel(rightshoulder) += relative * 6;
7295                                                                 player[i].victim->jointVel(leftshoulder) += relative * 6;
7296                                                             }
7297                                                             weapons[k].owner = i;
7298                                                             if (player[i].num_weapons > 0) {
7299                                                                 player[i].weaponids[player[i].num_weapons] = player[i].weaponids[0];
7300                                                             }
7301                                                             player[i].num_weapons++;
7302                                                             player[i].weaponids[0] = k;
7303                                                         }
7304                                                     }
7305                                                 }
7306                                     }
7307                             }
7308                         }
7309                         if (player[i].weaponactive != -1 && player[i].aitype == playercontrolled) {
7310                             if (weapons[player[i].weaponids[0]].getType() == knife) {
7311                                 if (player[i].isIdle() ||
7312                                         player[i].isRun() ||
7313                                         player[i].isCrouch() ||
7314                                         player[i].animTarget == sneakanim ||
7315                                         player[i].isFlip())
7316                                     if (numplayers > 1)
7317                                         for (int j = 0; j < numplayers; j++) {
7318                                             if (i != j)
7319                                                 if (tutoriallevel != 1 || tutorialstage == 49)
7320                                                     if (hostile)
7321                                                         if (normaldotproduct(player[i].facing, player[i].coords - player[j].coords) < 0 &&
7322                                                                 distsq(&player[i].coords, &player[j].coords) < 100 &&
7323                                                                 distsq(&player[i].coords, &player[j].coords) > 1.5 &&
7324                                                                 !player[j].skeleton.free &&
7325                                                                 -1 == checkcollide(DoRotation(player[j].jointPos(head), 0, player[j].yaw, 0)*player[j].scale + player[j].coords, DoRotation(player[i].jointPos(head), 0, player[i].yaw, 0)*player[i].scale + player[i].coords)) {
7326                                                             if (!player[i].isFlip()) {
7327                                                                 player[i].throwtogglekeydown = 1;
7328                                                                 player[i].victim = &player[j];
7329                                                                 player[i].setAnimation(knifethrowanim);
7330                                                                 player[i].targetyaw = roughDirectionTo(player[i].coords, player[j].coords);
7331                                                                 player[i].targettilt2 = pitchTo(player[i].coords, player[j].coords);
7332                                                             }
7333                                                             if (player[i].isFlip()) {
7334                                                                 if (player[i].weaponactive != -1) {
7335                                                                     player[i].throwtogglekeydown = 1;
7336                                                                     player[i].victim = &player[j];
7337                                                                     XYZ aim;
7338                                                                     weapons[player[i].weaponids[0]].owner = -1;
7339                                                                     aim = player[i].victim->coords + DoRotation(player[i].victim->jointPos(abdomen), 0, player[i].victim->yaw, 0) * player[i].victim->scale + player[i].victim->velocity * findDistance(&player[i].victim->coords, &player[i].coords) / 50 - (player[i].coords + DoRotation(player[i].jointPos(righthand), 0, player[i].yaw, 0) * player[i].scale);
7340                                                                     Normalise(&aim);
7341
7342                                                                     aim = DoRotation(aim, (float)abs(Random() % 30) - 15, (float)abs(Random() % 30) - 15, 0);
7343
7344                                                                     weapons[player[i].weaponids[0]].velocity = aim * 50;
7345                                                                     weapons[player[i].weaponids[0]].tipvelocity = aim * 50;
7346                                                                     weapons[player[i].weaponids[0]].missed = 0;
7347                                                                     weapons[player[i].weaponids[0]].freetime = 0;
7348                                                                     weapons[player[i].weaponids[0]].firstfree = 1;
7349                                                                     weapons[player[i].weaponids[0]].physics = 0;
7350                                                                     player[i].num_weapons--;
7351                                                                     if (player[i].num_weapons) {
7352                                                                         player[i].weaponids[0] = player[i].weaponids[player[i].num_weapons];
7353                                                                     }
7354                                                                     player[i].weaponactive = -1;
7355                                                                 }
7356                                                             }
7357                                                         }
7358                                         }
7359                             }
7360                         }
7361                         if (player[i].weaponactive != -1 && player[i].aitype == playercontrolled) {
7362                             if (player[i].isCrouch() || player[i].animTarget == sneakanim) {
7363                                 player[i].throwtogglekeydown = 1;
7364                                 weapons[player[i].weaponids[0]].owner = -1;
7365                                 weapons[player[i].weaponids[0]].velocity = player[i].velocity * .2;
7366                                 if (weapons[player[i].weaponids[0]].velocity.x == 0)weapons[player[i].weaponids[0]].velocity.x = .1;
7367                                 weapons[player[i].weaponids[0]].tipvelocity = weapons[player[i].weaponids[0]].velocity;
7368                                 weapons[player[i].weaponids[0]].missed = 1;
7369                                 weapons[player[i].weaponids[0]].freetime = 0;
7370                                 weapons[player[i].weaponids[0]].firstfree = 1;
7371                                 weapons[player[i].weaponids[0]].physics = 1;
7372                                 player[i].num_weapons--;
7373                                 if (player[i].num_weapons) {
7374                                     player[i].weaponids[0] = player[i].weaponids[player[i].num_weapons];
7375                                     if (player[i].weaponstuck == player[i].num_weapons)player[i].weaponstuck = 0;
7376                                 }
7377
7378                                 player[i].weaponactive = -1;
7379                                 for (int j = 0; j < numplayers; j++) {
7380                                     player[j].wentforweapon = 0;
7381                                 }
7382                             }
7383                         }
7384
7385                     }
7386
7387                     //draw weapon
7388                     if (i == 0 || !player[0].dead || player[i].weaponactive != -1) {
7389                         if (player[i].drawkeydown && !player[i].drawtogglekeydown ||
7390                                 player[i].num_weapons == 2 &&
7391                                 player[i].weaponactive == -1 &&
7392                                 player[i].isIdle() ||
7393                                 player[0].dead &&
7394                                 player[i].weaponactive != -1 &&
7395                                 i != 0) {
7396                             bool isgood = true;
7397                             if (player[i].weaponactive != -1)
7398                                 if (weapons[player[i].weaponids[player[i].weaponactive]].getType() == staff)
7399                                     isgood = false;
7400                             if (isgood && player[i].creature != wolftype) {
7401                                 if (player[i].isIdle() && player[i].num_weapons && weapons[player[i].weaponids[0]].getType() == knife) {
7402                                     player[i].setAnimation(drawrightanim);
7403                                     player[i].drawtogglekeydown = 1;
7404                                 }
7405                                 if ((player[i].isIdle() ||
7406                                         (player[i].aitype != playercontrolled &&
7407                                          player[0].weaponactive != -1 &&
7408                                          player[i].isRun())) &&
7409                                         player[i].num_weapons &&
7410                                         weapons[player[i].weaponids[0]].getType() == sword) {
7411                                     player[i].setAnimation(drawleftanim);
7412                                     player[i].drawtogglekeydown = 1;
7413                                 }
7414                                 if (player[i].isCrouch() && player[i].num_weapons && weapons[player[i].weaponids[0]].getType() == knife) {
7415                                     player[i].setAnimation(crouchdrawrightanim);
7416                                     player[i].drawtogglekeydown = 1;
7417                                 }
7418                             }
7419                         }
7420                     }
7421
7422                     //clean weapon
7423                     if (player[i].weaponactive != -1) {
7424                         if (player[i].isCrouch() &&
7425                                 weapons[player[i].weaponids[player[i].weaponactive]].bloody &&
7426                                 bloodtoggle &&
7427                                 player[i].onterrain &&
7428                                 player[i].num_weapons &&
7429                                 player[i].attackkeydown &&
7430                                 musictype != stream_fighttheme) {
7431                             if (weapons[player[i].weaponids[player[i].weaponactive]].getType() == knife)
7432                                 player[i].setAnimation(crouchstabanim);
7433                             if (weapons[player[i].weaponids[player[i].weaponactive]].getType() == sword)
7434                                 player[i].setAnimation(swordgroundstabanim);
7435                             player[i].hasvictim = 0;
7436                         }
7437                     }
7438
7439                     if (!player[i].drawkeydown)
7440                         player[i].drawtogglekeydown = 0;
7441
7442                     XYZ absflatfacing;
7443                     if (i == 0) {
7444                         absflatfacing = 0;
7445                         absflatfacing.z = -1;
7446
7447                         absflatfacing = DoRotation(absflatfacing, 0, -yaw, 0);
7448                     } else
7449                         absflatfacing = flatfacing;
7450
7451                     if (indialogue != -1) {
7452                         player[i].forwardkeydown = 0;
7453                         player[i].leftkeydown = 0;
7454                         player[i].backkeydown = 0;
7455                         player[i].rightkeydown = 0;
7456                         player[i].jumpkeydown = 0;
7457                         player[i].crouchkeydown = 0;
7458                         player[i].drawkeydown = 0;
7459                         player[i].throwkeydown = 0;
7460                     }
7461                     movekey = 0;
7462                     //Do controls
7463                     if (!animation[player[i].animTarget].attack &&
7464                             player[i].animTarget != staggerbackhighanim &&
7465                             player[i].animTarget != staggerbackhardanim &&
7466                             player[i].animTarget != backhandspringanim &&
7467                             player[i].animTarget != dodgebackanim) {
7468                         if (!player[i].forwardkeydown)
7469                             player[i].forwardstogglekeydown = 0;
7470                         if (player[i].crouchkeydown) {
7471                             //Crouch
7472                             target = -2;
7473                             if (i == 0) {
7474                                 player[i].superruntoggle = 1;
7475                                 if (numplayers > 1)
7476                                     for (int j = 0; j < numplayers; j++)
7477                                         if (j != i && !player[j].skeleton.free && player[j].aitype == passivetype)
7478                                             if (distsq(&player[j].coords, &player[i].coords) < 16)
7479                                                 player[i].superruntoggle = 0;
7480                             }
7481
7482                             if (numplayers > 1)
7483                                 for (int j = 0; j < numplayers; j++) {
7484                                     if (j != i && !player[j].skeleton.free && player[j].victim && player[i].lowreversaldelay <= 0) {
7485                                         if (distsq(&player[j].coords, &player[j].victim->coords) < 3 &&
7486                                                 player[j].victim == &player[i] &&
7487                                                 (player[j].animTarget == sweepanim ||
7488                                                  player[j].animTarget == upunchanim ||
7489                                                  player[j].animTarget == wolfslapanim ||
7490                                                  ((player[j].animTarget == swordslashanim ||
7491                                                    player[j].animTarget == knifeslashstartanim ||
7492                                                    player[j].animTarget == staffhitanim ||
7493                                                    player[j].animTarget == staffspinhitanim) &&
7494                                                   distsq(&player[j].coords, &player[i].coords) < 2))) {
7495                                             if (target >= 0)
7496                                                 target = -1;
7497                                             else
7498                                                 target = j;
7499                                         }
7500                                     }
7501                                 }
7502                             if (target >= 0)
7503                                 player[target].Reverse();
7504                             player[i].lowreversaldelay = .5;
7505
7506                             if (player[i].isIdle()) {
7507                                 player[i].setAnimation(player[i].getCrouch());
7508                                 player[i].transspeed = 10;
7509                             }
7510                             if (player[i].isRun() ||
7511                                     (player[i].isStop() &&
7512                                      (player[i].leftkeydown ||
7513                                       player[i].rightkeydown ||
7514                                       player[i].forwardkeydown ||
7515                                       player[i].backkeydown))) {
7516                                 player[i].setAnimation(rollanim);
7517                                 player[i].transspeed = 20;
7518                             }
7519                         }
7520                         if (!player[i].crouchkeydown) {
7521                             //Uncrouch
7522                             if (!player[i].isRun() && player[i].animTarget != sneakanim && i == 0)player[i].superruntoggle = 0;
7523                             target = -2;
7524                             if (player[i].isCrouch()) {
7525                                 if (numplayers > 1)
7526                                     for (int j = 0; j < numplayers; j++) {
7527                                         if (j != i &&
7528                                                 !player[j].skeleton.free &&
7529                                                 player[j].victim &&
7530                                                 player[i].highreversaldelay <= 0) {
7531                                             if (distsq(&player[j].coords, &player[j].victim->coords) < 3 &&
7532                                                     player[j].victim == &player[i] &&
7533                                                     (player[j].animTarget == spinkickanim) &&
7534                                                     player[i].isCrouch()) {
7535                                                 if (target >= 0)
7536                                                     target = -1;
7537                                                 else
7538                                                     target = j;
7539                                             }
7540                                         }
7541                                     }
7542                                 if (target >= 0)
7543                                     player[target].Reverse();
7544                                 player[i].highreversaldelay = .5;
7545
7546                                 if (player[i].isCrouch()) {
7547                                     if (!player[i].wasCrouch()) {
7548                                         player[i].animCurrent = player[i].getCrouch();
7549                                         player[i].frameCurrent = 0;
7550                                     }
7551                                     player[i].setAnimation(player[i].getIdle());
7552                                     player[i].transspeed = 10;
7553                                 }
7554                             }
7555                             if (player[i].animTarget == sneakanim) {
7556                                 player[i].setAnimation(player[i].getIdle());
7557                                 player[i].transspeed = 10;
7558                             }
7559                         }
7560                         if (player[i].forwardkeydown) {
7561                             if (player[i].isIdle() ||
7562                                     (player[i].isStop() &&
7563                                      player[i].targetyaw == player[i].yaw) ||
7564                                     (player[i].isLanding() &&
7565                                      player[i].frameTarget > 0 &&
7566                                      !player[i].jumpkeydown) ||
7567                                     (player[i].isLandhard() &&
7568                                      player[i].frameTarget > 0 &&
7569                                      !player[i].jumpkeydown &&
7570                                      player[i].crouchkeydown)) {
7571                                 if (player[i].aitype == passivetype)
7572                                     player[i].setAnimation(walkanim);
7573                                 else
7574                                     player[i].setAnimation(player[i].getRun());
7575                             }
7576                             if (player[i].isCrouch()) {
7577                                 player[i].animTarget = sneakanim;
7578                                 if (player[i].wasCrouch())
7579                                     player[i].target = 0;
7580                                 player[i].frameTarget = 0;
7581                             }
7582                             if (player[i].animTarget == hanganim/*&&(!player[i].forwardstogglekeydown||player[i].aitype!=playercontrolled)*/) {
7583                                 player[i].setAnimation(climbanim);
7584                                 player[i].frameTarget = 1;
7585                                 player[i].jumpclimb = 1;
7586                             }
7587                             if (player[i].animTarget == jumpupanim || player[i].animTarget == jumpdownanim || player[i].isFlip()) {
7588                                 player[i].velocity += absflatfacing * 5 * multiplier;
7589                             }
7590                             player[i].forwardstogglekeydown = 1;
7591                             movekey = 1;
7592                         }
7593                         if (player[i].rightkeydown) {
7594                             if (player[i].isIdle() ||
7595                                     (player[i].isStop() &&
7596                                      player[i].targetyaw == player[i].yaw) ||
7597                                     (player[i].isLanding() &&
7598                                      player[i].frameTarget > 0 &&
7599                                      !player[i].jumpkeydown) ||
7600                                     (player[i].isLandhard() &&
7601                                      player[i].frameTarget > 0 &&
7602                                      !player[i].jumpkeydown &&
7603                                      player[i].crouchkeydown)) {
7604                                 player[i].setAnimation(player[i].getRun());
7605                             }
7606                             if (player[i].isCrouch()) {
7607                                 player[i].animTarget = sneakanim;
7608                                 if (player[i].wasCrouch())
7609                                     player[i].target = 0;
7610                                 player[i].frameTarget = 0;
7611                             }
7612                             if (player[i].animTarget == jumpupanim || player[i].animTarget == jumpdownanim || player[i].isFlip()) {
7613                                 player[i].velocity += DoRotation(absflatfacing * 5 * multiplier, 0, -90, 0);
7614                             }
7615                             player[i].targetyaw -= 90;
7616                             if (player[i].forwardkeydown)player[i].targetyaw += 45;
7617                             if (player[i].backkeydown)player[i].targetyaw -= 45;
7618                             movekey = 1;
7619                         }
7620                         if ( player[i].leftkeydown) {
7621                             if (player[i].isIdle() ||
7622                                     (player[i].isStop() &&
7623                                      player[i].targetyaw == player[i].yaw) ||
7624                                     (player[i].isLanding() &&
7625                                      player[i].frameTarget > 0 &&
7626                                      !player[i].jumpkeydown) ||
7627                                     (player[i].isLandhard() &&
7628                                      player[i].frameTarget > 0 &&
7629                                      !player[i].jumpkeydown &&
7630                                      player[i].crouchkeydown)) {
7631                                 player[i].setAnimation(player[i].getRun());
7632                             }
7633                             if (player[i].isCrouch()) {
7634                                 player[i].animTarget = sneakanim;
7635                                 if (player[i].wasCrouch())
7636                                     player[i].target = 0;
7637                                 player[i].frameTarget = 0;
7638                             }
7639                             if (player[i].animTarget == jumpupanim || player[i].animTarget == jumpdownanim || player[i].isFlip()) {
7640                                 player[i].velocity -= DoRotation(absflatfacing * 5 * multiplier, 0, -90, 0);
7641                             }
7642                             player[i].targetyaw += 90;
7643                             if (player[i].forwardkeydown)player[i].targetyaw -= 45;
7644                             if (player[i].backkeydown)player[i].targetyaw += 45;
7645                             movekey = 1;
7646                         }
7647                         if (player[i].backkeydown) {
7648                             if (player[i].isIdle() ||
7649                                     (player[i].isStop() &&
7650                                      player[i].targetyaw == player[i].yaw) ||
7651                                     (player[i].isLanding() &&
7652                                      player[i].frameTarget > 0 &&
7653                                      !player[i].jumpkeydown) ||
7654                                     (player[i].isLandhard() &&
7655                                      player[i].frameTarget > 0 &&
7656                                      !player[i].jumpkeydown &&
7657                                      player[i].crouchkeydown)) {
7658                                 player[i].setAnimation(player[i].getRun());
7659                             }
7660                             if (player[i].isCrouch()) {
7661                                 player[i].animTarget = sneakanim;
7662                                 if (player[i].wasCrouch())
7663                                     player[i].target = 0;
7664                                 player[i].frameTarget = 0;
7665                             }
7666                             if (player[i].animTarget == jumpupanim || player[i].animTarget == jumpdownanim || player[i].isFlip()) {
7667                                 player[i].velocity -= absflatfacing * 5 * multiplier;
7668                             }
7669                             if (player[i].animTarget == hanganim) {
7670                                 player[i].animCurrent = jumpdownanim;
7671                                 player[i].animTarget = jumpdownanim;
7672                                 player[i].target = 0;
7673                                 player[i].frameCurrent = 0;
7674                                 player[i].frameTarget = 1;
7675                                 player[i].velocity = 0;
7676                                 player[i].velocity.y += gravity;
7677                                 player[i].coords.y -= 1.4;
7678                                 player[i].grabdelay = 1;
7679                             }
7680                             if ( !player[i].leftkeydown && !player[i].rightkeydown)
7681                                 player[i].targetyaw += 180;
7682                             movekey = 1;
7683                         }
7684                         if ((player[i].jumpkeydown && !player[i].jumpclimb) || player[i].jumpstart) {
7685                             if ((((player[i].isLanding() && player[i].frameTarget >= 3) ||
7686                                     player[i].isRun() ||
7687                                     player[i].animTarget == walkanim ||
7688                                     player[i].isCrouch() ||
7689                                     player[i].animTarget == sneakanim) &&
7690                                     player[i].jumppower > 1) &&
7691                                     ((player[i].animTarget != rabbitrunninganim &&
7692                                       player[i].animTarget != wolfrunninganim) || i != 0)) {
7693                                 player[i].jumpstart = 0;
7694                                 player[i].setAnimation(jumpupanim);
7695                                 player[i].yaw = player[i].targetyaw;
7696                                 player[i].transspeed = 20;
7697                                 player[i].FootLand(0, 1);
7698                                 player[i].FootLand(1, 1);
7699
7700                                 facing = 0;
7701                                 facing.z = -1;
7702                                 flatfacing = DoRotation(facing, 0, player[i].targetyaw + 180, 0);
7703
7704                                 if (movekey)player[i].velocity = flatfacing * player[i].speed * 45 * player[i].scale;
7705                                 if (!movekey)player[i].velocity = 0;
7706
7707                                 //Dodge sweep?
7708                                 target = -2;
7709                                 if (numplayers > 1)
7710                                     for (int j = 0; j < numplayers; j++) {
7711                                         if (j != i && !player[j].skeleton.free && player[j].victim) {
7712                                             if (distsq(&player[j].coords, &player[j].victim->coords) < 3 &&
7713                                                     player[j].victim == &player[i] &&
7714                                                     (player[j].animTarget == sweepanim)) {
7715                                                 if (target >= 0)target = -1;
7716                                                 else target = j;
7717                                             }
7718                                         }
7719                                     }
7720                                 if (target >= 0)player[i].velocity.y = 1;
7721                                 else if (player[i].crouchkeydown || player[i].aitype != playercontrolled) {
7722                                     player[i].velocity.y = 7;
7723                                     player[i].crouchtogglekeydown = 1;
7724                                 } else player[i].velocity.y = 5;
7725
7726                                 if (mousejump && i == 0 && debugmode) {
7727                                     if (!player[i].isLanding())player[i].tempdeltav = deltav;
7728                                     if (player[i].tempdeltav < 0)player[i].velocity.y -= (float)(player[i].tempdeltav) / multiplier / 1000;
7729                                 }
7730
7731                                 player[i].coords.y += .2;
7732                                 player[i].jumppower -= 1;
7733
7734                                 if (!i)
7735                                     emit_sound_at(whooshsound, player[i].coords, 128.);
7736
7737                                 emit_sound_at(jumpsound, player[i].coords, 128.);
7738                             }
7739                             if ((player[i].isIdle()) && player[i].jumppower > 1) {
7740                                 player[i].setAnimation(player[i].getLanding());
7741                                 player[i].frameTarget = 2;
7742                                 player[i].landhard = 0;
7743                                 player[i].jumpstart = 1;
7744                                 player[i].tempdeltav = deltav;
7745                             }
7746                             if (player[i].animTarget == jumpupanim &&
7747                                     (((!floatjump &&
7748                                        !editorenabled) ||
7749                                       !debugmode) ||
7750                                      player[i].aitype != playercontrolled)) {
7751                                 if (player[i].jumppower > multiplier * 6) {
7752                                     player[i].velocity.y += multiplier * 6;
7753                                     player[i].jumppower -= multiplier * 6;
7754                                 }
7755                                 if (player[i].jumppower <= multiplier * 6) {
7756                                     player[i].velocity.y += player[i].jumppower;
7757                                     player[i].jumppower = 0;
7758                                 }
7759                             }
7760                             if (((floatjump || editorenabled) && debugmode) && i == 0)player[i].velocity.y += multiplier * 30;
7761                         }
7762
7763                         if (!movekey) {
7764                             if (player[i].isRun() || player[i].animTarget == walkanim)
7765                                 player[i].setAnimation(player[i].getStop());
7766                             if (player[i].animTarget == sneakanim) {
7767                                 player[i].animTarget = player[i].getCrouch();
7768                                 if (player[i].animCurrent == sneakanim)
7769                                     player[i].target = 0;
7770                                 player[i].frameTarget = 0;
7771                             }
7772                         }
7773                         if (player[i].animTarget == walkanim &&
7774                                 (player[i].aitype == attacktypecutoff ||
7775                                  player[i].aitype == searchtype ||
7776                                  (player[i].aitype == passivetype &&
7777                                   player[i].numwaypoints <= 1)))
7778                             player[i].setAnimation(player[i].getStop());
7779                         if (player[i].isRun() && (player[i].aitype == passivetype))
7780                             player[i].setAnimation(player[i].getStop());
7781                     }
7782                 }
7783                 if (player[i].animTarget == rollanim)
7784                     player[i].targetyaw = oldtargetyaw;
7785             }
7786
7787             //Rotation
7788             for (int k = 0; k < numplayers; k++) {
7789                 if (fabs(player[k].yaw - player[k].targetyaw) > 180) {
7790                     if (player[k].yaw > player[k].targetyaw)
7791                         player[k].yaw -= 360;
7792                     else
7793                         player[k].yaw += 360;
7794                 }
7795
7796                 //stop to turn in right direction
7797                 if (fabs(player[k].yaw - player[k].targetyaw) > 90 && (player[k].isRun() || player[k].animTarget == walkanim))
7798                     player[k].setAnimation(player[k].getStop());
7799
7800                 if (player[k].animTarget == backhandspringanim || player[k].animTarget == dodgebackanim)
7801                     player[k].targettilt = 0;
7802
7803                 if (player[k].animTarget != jumpupanim &&
7804                         player[k].animTarget != backhandspringanim &&
7805                         player[k].animTarget != jumpdownanim &&
7806                         !player[k].isFlip()) {
7807                     player[k].targettilt = 0;
7808                     if (player[k].jumppower < 0 && !player[k].jumpkeydown)
7809                         player[k].jumppower = 0;
7810                     player[k].jumppower += multiplier * 7;
7811                     if (player[k].isCrouch())
7812                         player[k].jumppower += multiplier * 7;
7813                     if (player[k].jumppower > 5)
7814                         player[k].jumppower = 5;
7815                 }
7816
7817                 if (player[k].isRun())
7818                     player[k].targettilt = (player[k].yaw - player[k].targetyaw) / 4;
7819
7820                 player[k].tilt = stepTowardf(player[k].tilt, player[k].targettilt, multiplier * 150);
7821                 player[k].grabdelay -= multiplier;
7822             }
7823
7824             //do animations
7825             for (int k = 0; k < numplayers; k++) {
7826                 player[k].DoAnimations();
7827                 player[k].whichpatchx = player[k].coords.x / (terrain.size / subdivision * terrain.scale);
7828                 player[k].whichpatchz = player[k].coords.z / (terrain.size / subdivision * terrain.scale);
7829             }
7830
7831             //do stuff
7832             objects.DoStuff();
7833
7834             for (int j = numenvsounds - 1; j >= 0; j--) {
7835                 envsoundlife[j] -= multiplier;
7836                 if (envsoundlife[j] < 0) {
7837                     numenvsounds--;
7838                     envsoundlife[j] = envsoundlife[numenvsounds];
7839                     envsound[j] = envsound[numenvsounds];
7840                 }
7841             }
7842             if (slomo)
7843                 OPENAL_SetFrequency(OPENAL_ALL, slomofreq);
7844             else
7845                 OPENAL_SetFrequency(OPENAL_ALL, 22050);
7846
7847             if (tutoriallevel == 1) {
7848                 XYZ temp;
7849                 XYZ temp2;
7850                 XYZ temp3;
7851                 XYZ oldtemp;
7852                 XYZ oldtemp2;
7853                 temp.x = 1011;
7854                 temp.y = 84;
7855                 temp.z = 491;
7856                 temp2.x = 1025;
7857                 temp2.y = 75;
7858                 temp2.z = 447;
7859                 temp3.x = 1038;
7860                 temp3.y = 76;
7861                 temp3.z = 453;
7862                 oldtemp = temp;
7863                 oldtemp2 = temp2;
7864                 if (tutorialstage >= 51)
7865                     if (distsq(&temp, &player[0].coords) >= distsq(&temp, &temp2) - 1 || distsq(&temp3, &player[0].coords) < 4) {
7866                         OPENAL_StopSound(OPENAL_ALL);  // hack...OpenAL renderer isn't stopping music after tutorial goes to level menu...
7867                         OPENAL_SetFrequency(OPENAL_ALL, 0.001);
7868
7869                         emit_stream_np(stream_menutheme);
7870
7871                         gameon = 0;
7872                         mainmenu = 5;
7873
7874                         fireSound();
7875
7876                         flash();
7877                     }
7878                 if (tutorialstage < 51)
7879                     if (distsq(&temp, &player[0].coords) >= distsq(&temp, &temp2) - 1 || distsq(&temp3, &player[0].coords) < 4) {
7880                         emit_sound_at(fireendsound, player[0].coords);
7881
7882                         player[0].coords = (oldtemp + oldtemp2) / 2;
7883
7884                         flash();
7885                     }
7886                 if (tutorialstage >= 14 && tutorialstage < 50)
7887                     if (distsq(&temp, &player[1].coords) >= distsq(&temp, &temp2) - 1 || distsq(&temp3, &player[1].coords) < 4) {
7888                         emit_sound_at(fireendsound, player[1].coords);
7889
7890                         for (int i = 0; i < player[1].skeleton.num_joints; i++) {
7891                             if (Random() % 2 == 0) {
7892                                 if (!player[1].skeleton.free)temp2 = (player[1].coords - player[1].oldcoords) / multiplier / 2; //velocity/2;
7893                                 if (player[1].skeleton.free)temp2 = player[1].skeleton.joints[i].velocity * player[1].scale / 2;
7894                                 if (!player[1].skeleton.free)temp = DoRotation(DoRotation(DoRotation(player[1].skeleton.joints[i].position, 0, 0, player[1].tilt), player[1].tilt2, 0, 0), 0, player[1].yaw, 0) * player[1].scale + player[1].coords;
7895                                 if (player[1].skeleton.free)temp = player[1].skeleton.joints[i].position * player[1].scale + player[1].coords;
7896                                 Sprite::MakeSprite(breathsprite, temp, temp2, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1);
7897                             }
7898                         }
7899
7900                         player[1].coords = (oldtemp + oldtemp2) / 2;
7901                         for (int i = 0; i < player[1].skeleton.num_joints; i++) {
7902                             player[1].skeleton.joints[i].velocity = 0;
7903                             if (Random() % 2 == 0) {
7904                                 if (!player[1].skeleton.free)temp2 = (player[1].coords - player[1].oldcoords) / multiplier / 2; //velocity/2;
7905                                 if (player[1].skeleton.free)temp2 = player[1].skeleton.joints[i].velocity * player[1].scale / 2;
7906                                 if (!player[1].skeleton.free)temp = DoRotation(DoRotation(DoRotation(player[1].skeleton.joints[i].position, 0, 0, player[1].tilt), player[1].tilt2, 0, 0), 0, player[1].yaw, 0) * player[1].scale + player[1].coords;
7907                                 if (player[1].skeleton.free)temp = player[1].skeleton.joints[i].position * player[1].scale + player[1].coords;
7908                                 Sprite::MakeSprite(breathsprite, temp, temp2, 1, 1, 1, .6 + (float)abs(Random() % 100) / 200 - .25, 1);
7909                             }
7910                         }
7911                     }
7912             }
7913
7914
7915             //3d sound
7916             static float gLoc[3];
7917             gLoc[0] = viewer.x;
7918             gLoc[1] = viewer.y;
7919             gLoc[2] = viewer.z;
7920             static float vel[3];
7921             vel[0] = (viewer.x - oldviewer.x) / multiplier;
7922             vel[1] = (viewer.y - oldviewer.y) / multiplier;
7923             vel[2] = (viewer.z - oldviewer.z) / multiplier;
7924
7925             //Set orientation with forward and up vectors
7926             static XYZ upvector;
7927             upvector = 0;
7928             upvector.z = -1;
7929
7930             upvector = DoRotation(upvector, -pitch + 90, 0, 0);
7931             upvector = DoRotation(upvector, 0, 0 - yaw, 0);
7932
7933             facing = 0;
7934             facing.z = -1;
7935
7936             facing = DoRotation(facing, -pitch, 0, 0);
7937             facing = DoRotation(facing, 0, 0 - yaw, 0);
7938
7939
7940             static float ori[6];
7941             ori[0] = -facing.x;
7942             ori[1] = facing.y;
7943             ori[2] = -facing.z;
7944             ori[3] = -upvector.x;
7945             ori[4] = upvector.y;
7946             ori[5] = -upvector.z;
7947
7948             OPENAL_3D_Listener_SetAttributes(&gLoc[0], &vel[0], ori[0], ori[1], ori[2], ori[3], ori[4], ori[5]);
7949             OPENAL_Update();
7950
7951             oldviewer = viewer;
7952         }
7953     }
7954
7955     if (Input::isKeyPressed(SDLK_F1))
7956         Screenshot();
7957 }
7958
7959 void Game::TickOnce()
7960 {
7961     if (mainmenu)
7962         yaw += multiplier * 5;
7963     else if (directing || indialogue == -1) {
7964         yaw += deltah * .7;
7965         if (!invertmouse)
7966             pitch += deltav * .7;
7967         if (invertmouse)
7968             pitch -= deltav * .7;
7969         if (pitch > 90)
7970             pitch = 90;
7971         if (pitch < -70)
7972             pitch = -70;
7973     }
7974 }
7975
7976 void Game::TickOnceAfter()
7977 {
7978     static XYZ colviewer;
7979     static XYZ coltarget;
7980     static XYZ target;
7981     static XYZ col;
7982     static XYZ facing;
7983     static float changedelay;
7984     static bool alldead;
7985     static float unseendelay;
7986     static float cameraspeed;
7987
7988     if (!mainmenu) {
7989         static int oldmusictype = musictype;
7990
7991         if (environment == snowyenvironment)
7992             leveltheme = stream_snowtheme;
7993         if (environment == grassyenvironment)
7994             leveltheme = stream_grasstheme;
7995         if (environment == desertenvironment)
7996             leveltheme = stream_deserttheme;
7997
7998         realthreat = 0;
7999
8000         musictype = leveltheme;
8001         for (int i = 0; i < numplayers; i++) {
8002             if ((player[i].aitype == attacktypecutoff ||
8003                     player[i].aitype == getweapontype ||
8004                     player[i].aitype == gethelptype ||
8005                     player[i].aitype == searchtype) &&
8006                     !player[i].dead/*&&player[i].surprised<=0*/ &&
8007                     (player[i].animTarget != sneakattackedanim &&
8008                      player[i].animTarget != knifesneakattackedanim &&
8009                      player[i].animTarget != swordsneakattackedanim)) {
8010                 musictype = stream_fighttheme;
8011                 realthreat = 1;
8012             }
8013         }
8014         if (player[0].dead)
8015             musictype = stream_menutheme;
8016
8017
8018         if (musictype == stream_fighttheme)
8019             unseendelay = 1;
8020
8021         if (oldmusictype == stream_fighttheme && musictype != stream_fighttheme) {
8022             unseendelay -= multiplier;
8023             if (unseendelay > 0)
8024                 musictype = stream_fighttheme;
8025         }
8026
8027
8028         if (loading == 2) {
8029             musictype = stream_menutheme;
8030             musicvolume[2] = 512;
8031             musicvolume[0] = 0;
8032             musicvolume[1] = 0;
8033             musicvolume[3] = 0;
8034         }
8035
8036         if (musictoggle)
8037             if (musictype != oldmusictype && musictype == stream_fighttheme)
8038                 emit_sound_np(alarmsound);
8039         musicselected = musictype;
8040
8041         if (musicselected == leveltheme)
8042             musicvolume[0] += multiplier * 450;
8043         else
8044             musicvolume[0] -= multiplier * 450;
8045         if (musicselected == stream_fighttheme)
8046             musicvolume[1] += multiplier * 450;
8047         else
8048             musicvolume[1] -= multiplier * 450;
8049         if (musicselected == stream_menutheme)
8050             musicvolume[2] += multiplier * 450;
8051         else
8052             musicvolume[2] -= multiplier * 450;
8053
8054         for (int i = 0; i < 3; i++) {
8055             if (musicvolume[i] < 0)
8056                 musicvolume[i] = 0;
8057             if (musicvolume[i] > 512)
8058                 musicvolume[i] = 512;
8059         }
8060
8061         if (musicvolume[2] > 128 && !loading && !mainmenu)
8062             musicvolume[2] = 128;
8063
8064         if (musictoggle) {
8065             if (musicvolume[0] > 0 && oldmusicvolume[0] <= 0)
8066                 emit_stream_np(leveltheme, musicvolume[0]);
8067             if (musicvolume[1] > 0 && oldmusicvolume[1] <= 0)
8068                 emit_stream_np(stream_fighttheme, musicvolume[1]);
8069             if (musicvolume[2] > 0 && oldmusicvolume[2] <= 0)
8070                 emit_stream_np(stream_menutheme, musicvolume[2]);
8071             if (musicvolume[0] <= 0 && oldmusicvolume[0] > 0)
8072                 pause_sound(leveltheme);
8073             if (musicvolume[1] <= 0 && oldmusicvolume[1] > 0)
8074                 pause_sound(stream_fighttheme);
8075             if (musicvolume[2] <= 0 && oldmusicvolume[2] > 0)
8076                 pause_sound(stream_menutheme);
8077
8078             if (musicvolume[0] != oldmusicvolume[0])
8079                 OPENAL_SetVolume(channels[leveltheme], musicvolume[0]);
8080             if (musicvolume[1] != oldmusicvolume[1])
8081                 OPENAL_SetVolume(channels[stream_fighttheme], musicvolume[1]);
8082             if (musicvolume[2] != oldmusicvolume[2])
8083                 OPENAL_SetVolume(channels[stream_menutheme], musicvolume[2]);
8084
8085             for (int i = 0; i < 3; i++)
8086                 oldmusicvolume[i] = musicvolume[i];
8087         } else {
8088             pause_sound(leveltheme);
8089             pause_sound(stream_fighttheme);
8090             pause_sound(stream_menutheme);
8091
8092             for (int i = 0; i < 4; i++) {
8093                 oldmusicvolume[i] = 0;
8094                 musicvolume[i] = 0;
8095             }
8096         }
8097
8098         killhotspot = 2;
8099         for (int i = 0; i < numhotspots; i++) {
8100             if (hotspottype[i] > 10 && hotspottype[i] < 20) {
8101                 if (player[hotspottype[i] - 10].dead == 0)
8102                     killhotspot = 0;
8103                 else if (killhotspot == 2)
8104                     killhotspot = 1;
8105             }
8106         }
8107         if (killhotspot == 2)
8108             killhotspot = 0;
8109
8110
8111         winhotspot = false;
8112         for (int i = 0; i < numhotspots; i++)
8113             if (hotspottype[i] == -1)
8114                 if (distsq(&player[0].coords, &hotspot[i]) < hotspotsize[i])
8115                     winhotspot = true;
8116
8117         int numalarmed = 0;
8118         for (int i = 1; i < numplayers; i++)
8119             if (!player[i].dead && player[i].aitype == attacktypecutoff && player[i].surprised <= 0)
8120                 numalarmed++;
8121         if (numalarmed > maxalarmed)
8122             maxalarmed = numalarmed;
8123
8124         if (changedelay <= 0 && !loading && !editorenabled && gameon && !tutoriallevel && changedelay != -999 && !won) {
8125             if (player[0].dead && changedelay <= 0) {
8126                 changedelay = 1;
8127                 targetlevel = whichlevel;
8128             }
8129             alldead = true;
8130             for (int i = 1; i < numplayers; i++) {
8131                 if (!player[i].dead && player[i].howactive < typedead1) {
8132                     alldead = false;
8133                     break;
8134                 }
8135             }
8136
8137
8138             if (alldead && !player[0].dead && maptype == mapkilleveryone) {
8139                 changedelay = 1;
8140                 targetlevel = whichlevel + 1;
8141                 if (targetlevel > numchallengelevels - 1)targetlevel = 0;
8142             }
8143             if (winhotspot || windialogue) {
8144                 changedelay = 0.1;
8145                 targetlevel = whichlevel + 1;
8146                 if (targetlevel > numchallengelevels - 1)targetlevel = 0;
8147             }
8148
8149
8150             if (killhotspot) {
8151                 changedelay = 1;
8152                 targetlevel = whichlevel + 1;
8153                 if (targetlevel > numchallengelevels - 1)targetlevel = 0;
8154             }
8155
8156             if (changedelay > 0 && !player[0].dead && !won) {
8157                 //high scores, awards, win
8158                 if (campaign) {
8159                     accountactive->winCampaignLevel(whichchoice, bonustotal, leveltime);
8160                     scoreadded = 1;
8161                 } else {
8162                     accountactive->winLevel(whichlevel, bonustotal - startbonustotal, leveltime);
8163                 }
8164                 won = 1;
8165             }
8166         }
8167
8168         if (!winfreeze) {
8169
8170             if (leveltime < 1) {
8171                 loading = 0;
8172                 changedelay = .1;
8173                 alldead = false;
8174                 winhotspot = false;
8175                 killhotspot = 0;
8176             }
8177
8178             if (!editorenabled && gameon && !mainmenu) {
8179                 if (changedelay != -999)
8180                     changedelay -= multiplier / 7;
8181                 if (player[0].dead)
8182                     targetlevel = whichlevel;
8183                 if (loading == 2 && !campaign) {
8184                     flash();
8185
8186                     fireSound(firestartsound);
8187
8188                     if (!player[0].dead && targetlevel != whichlevel)
8189                         startbonustotal = bonustotal;
8190                     if (player[0].dead)
8191                         Loadlevel(whichlevel);
8192                     else
8193                         Loadlevel(targetlevel);
8194
8195                     fireSound();
8196
8197                     loading = 3;
8198                 }
8199                 if (loading == 2 && targetlevel == whichlevel) {
8200                     flash();
8201                     loadtime = 0;
8202
8203                     fireSound(firestartsound);
8204
8205                     Loadlevel(campaignlevels[accountactive->getCampaignChoicesMade()].mapname.c_str());
8206
8207                     fireSound();
8208
8209                     loading = 3;
8210                 }
8211                 if (changedelay <= -999 &&
8212                         whichlevel != -2 &&
8213                         !loading &&
8214                         (player[0].dead ||
8215                          (alldead && maptype == mapkilleveryone) ||
8216                          (winhotspot) ||
8217                          (killhotspot)))
8218                     loading = 1;
8219                 if ((player[0].dead ||
8220                         (alldead && maptype == mapkilleveryone) ||
8221                         (winhotspot) ||
8222                         (windialogue) ||
8223                         (killhotspot)) &&
8224                         changedelay <= 0) {
8225                     if (whichlevel != -2 && !loading && !player[0].dead) {
8226                         winfreeze = true;
8227                         changedelay = -999;
8228                     }
8229                     if (player[0].dead)
8230                         loading = 1;
8231                 }
8232             }
8233
8234             if (campaign) {
8235                 // campaignchoosenext determines what to do when the level is complete:
8236                 // 0 = load next level
8237                 // 1 = go back to level select screen
8238                 // 2 = stealthload next level
8239                 if (mainmenu == 0 && winfreeze && (campaignlevels[actuallevel].choosenext) == 1) {
8240                     if (campaignlevels[actuallevel].nextlevel.empty())
8241                         endgame = 1;
8242                 } else if (mainmenu == 0 && winfreeze) {
8243                     stealthloading = (campaignlevels[actuallevel].choosenext == 2);
8244
8245                     if (!stealthloading) {
8246                         fireSound(firestartsound);
8247
8248                         flash();
8249                     }
8250
8251                     startbonustotal = 0;
8252
8253                     LoadCampaign();
8254
8255                     loading = 2;
8256                     loadtime = 0;
8257                     targetlevel = 7;
8258                     if (!firstload)
8259                         LoadStuff();
8260                     whichchoice = 0;
8261                     actuallevel = campaignlevels[actuallevel].nextlevel.front();
8262                     visibleloading = 1;
8263                     stillloading = 1;
8264                     Loadlevel(campaignlevels[actuallevel].mapname.c_str());
8265                     campaign = 1;
8266                     mainmenu = 0;
8267                     gameon = 1;
8268                     pause_sound(stream_menutheme);
8269
8270                     stealthloading = 0;
8271                 }
8272             }
8273
8274             if (loading == 3)
8275                 loading = 0;
8276
8277         }
8278
8279         oldmusictype = musictype;
8280     }
8281
8282     facing = 0;
8283     facing.z = -1;
8284
8285     facing = DoRotation(facing, -pitch, 0, 0);
8286     facing = DoRotation(facing, 0, 0 - yaw, 0);
8287     viewerfacing = facing;
8288
8289     if (!cameramode) {
8290         if ((animation[player[0].animTarget].attack != 3 && animation[player[0].animCurrent].attack != 3) || player[0].skeleton.free)target = player[0].coords + player[0].currentoffset * (1 - player[0].target) * player[0].scale + player[0].targetoffset * player[0].target * player[0].scale - player[0].facing * .05;
8291         else target = player[0].oldcoords + player[0].currentoffset * (1 - player[0].target) * player[0].scale + player[0].targetoffset * player[0].target * player[0].scale - player[0].facing * .05;
8292         target.y += .1;
8293         if (player[0].skeleton.free) {
8294             for (int i = 0; i < player[0].skeleton.num_joints; i++) {
8295                 if (player[0].skeleton.joints[i].position.y * player[0].scale + player[0].coords.y > target.y)
8296                     target.y = player[0].skeleton.joints[i].position.y * player[0].scale + player[0].coords.y;
8297             }
8298             target.y += .1;
8299         }
8300         if (player[0].skeleton.free != 2/*&&!autocam*/) {
8301             cameraspeed = 20;
8302             if (findLengthfast(&player[0].velocity) > 400) {
8303                 cameraspeed = 20 + (findLength(&player[0].velocity) - 20) * .96;
8304             }
8305             if (player[0].skeleton.free == 0 && player[0].animTarget != hanganim && player[0].animTarget != climbanim)target.y += 1.4;
8306             coltarget = target - cameraloc;
8307             if (findLengthfast(&coltarget) < multiplier * multiplier * 400)cameraloc = target;
8308             else {
8309                 Normalise(&coltarget);
8310                 if (player[0].animTarget != hanganim && player[0].animTarget != climbanim && player[0].animCurrent != climbanim && player[0].currentoffset.x == 0)cameraloc = cameraloc + coltarget * multiplier * cameraspeed;
8311                 else cameraloc = cameraloc + coltarget * multiplier * 8;
8312             }
8313             if (editorenabled)cameraloc = target;
8314             cameradist += multiplier * 5;
8315             if (cameradist > 2.3)cameradist = 2.3;
8316             viewer = cameraloc - facing * cameradist;
8317             colviewer = viewer;
8318             coltarget = cameraloc;
8319             objects.SphereCheckPossible(&colviewer, findDistance(&colviewer, &coltarget));
8320             if (terrain.patchobjectnum[player[0].whichpatchx][player[0].whichpatchz])
8321                 for (int j = 0; j < terrain.patchobjectnum[player[0].whichpatchx][player[0].whichpatchz]; j++) {
8322                     int i = terrain.patchobjects[player[0].whichpatchx][player[0].whichpatchz][j];
8323                     colviewer = viewer;
8324                     coltarget = cameraloc;
8325                     if (objects.model[i].LineCheckPossible(&colviewer, &coltarget, &col, &objects.position[i], &objects.yaw[i]) != -1)viewer = col;
8326                 }
8327             if (terrain.patchobjectnum[player[0].whichpatchx][player[0].whichpatchz])
8328                 for (int j = 0; j < terrain.patchobjectnum[player[0].whichpatchx][player[0].whichpatchz]; j++) {
8329                     int i = terrain.patchobjects[player[0].whichpatchx][player[0].whichpatchz][j];
8330                     colviewer = viewer;
8331                     if (objects.model[i].SphereCheck(&colviewer, .15, &col, &objects.position[i], &objects.yaw[i]) != -1) {
8332                         viewer = colviewer;
8333                     }
8334                 }
8335             cameradist = findDistance(&viewer, &target);
8336             viewer.y = max((double)viewer.y, terrain.getHeight(viewer.x, viewer.z) + .6);
8337             if (cameraloc.y < terrain.getHeight(cameraloc.x, cameraloc.z)) {
8338                 cameraloc.y = terrain.getHeight(cameraloc.x, cameraloc.z);
8339             }
8340         }
8341         /*
8342         //what did autocam do?
8343         if(player[0].skeleton.free!=2&&autocam){
8344                 cameraspeed=20;
8345                 if(findLengthfast(&player[0].velocity)>400){
8346                         cameraspeed=20+(findLength(&player[0].velocity)-20)*.96;
8347                 }
8348                 if(player[0].skeleton.free==0&&player[0].animTarget!=hanganim&&player[0].animTarget!=climbanim)target.y+=1.4;
8349                 cameradist+=multiplier*5;
8350                 if(cameradist>3.3)cameradist=3.3;
8351                 coltarget=target-cameraloc;
8352                 if(findLengthfast(&coltarget)<multiplier*multiplier*400)cameraloc=target;
8353                 else if(findLengthfast(&coltarget)>1)
8354                 {
8355                         Normalise(&coltarget);
8356                         if(player[0].animTarget!=hanganim&&player[0].animTarget!=climbanim&&player[0].animCurrent!=climbanim&&player[0].currentoffset.x==0)cameraloc=cameraloc+coltarget*multiplier*cameraspeed;
8357                         else cameraloc=cameraloc+coltarget*multiplier*8;
8358                 }
8359                 if(editorenabled)cameraloc=target;
8360                 viewer=cameraloc;
8361                 colviewer=viewer;
8362                 coltarget=cameraloc;
8363                 objects.SphereCheckPossible(&colviewer, findDistance(&colviewer,&coltarget));
8364                 if(terrain.patchobjectnum[player[0].whichpatchx][player[0].whichpatchz])
8365                         for(int j=0;j<terrain.patchobjectnum[player[0].whichpatchx][player[0].whichpatchz];j++){
8366                                 int i=terrain.patchobjects[player[0].whichpatchx][player[0].whichpatchz][j];
8367                                 colviewer=viewer;
8368                                 coltarget=cameraloc;
8369                                 if(objects.model[i].LineCheckPossible(&colviewer,&coltarget,&col,&objects.position[i],&objects.yaw[i])!=-1)viewer=col;
8370                         }
8371             if(terrain.patchobjectnum[player[0].whichpatchx][player[0].whichpatchz])
8372                 for(int j=0;j<terrain.patchobjectnum[player[0].whichpatchx][player[0].whichpatchz];j++){
8373                     int i=terrain.patchobjects[player[0].whichpatchx][player[0].whichpatchz][j];
8374                     colviewer=viewer;
8375                     if(objects.model[i].SphereCheck(&colviewer,.15,&col,&objects.position[i],&objects.yaw[i])!=-1){
8376                         viewer=colviewer;
8377                     }
8378                 }
8379             cameradist=findDistance(&viewer,&target);
8380             viewer.y=max((double)viewer.y,terrain.getHeight(viewer.x,viewer.z)+.6);
8381             if(cameraloc.y<terrain.getHeight(cameraloc.x,cameraloc.z)){
8382                 cameraloc.y=terrain.getHeight(cameraloc.x,cameraloc.z);
8383             }
8384         }
8385         */
8386         if (camerashake > .8)camerashake = .8;
8387         //if(woozy>10)woozy=10;
8388         //woozy+=multiplier;
8389         woozy += multiplier;
8390         if (player[0].dead)camerashake = 0;
8391         if (player[0].dead)woozy = 0;
8392         camerashake -= multiplier * 2;
8393         blackout -= multiplier * 2;
8394         //if(player[0].isCrouch())woozy-=multiplier*8;
8395         if (camerashake < 0)camerashake = 0;
8396         if (blackout < 0)blackout = 0;
8397         //if(woozy<0)woozy=0;
8398         if (camerashake) {
8399             viewer.x += (float)(Random() % 100) * .0005 * camerashake;
8400             viewer.y += (float)(Random() % 100) * .0005 * camerashake;
8401             viewer.z += (float)(Random() % 100) * .0005 * camerashake;
8402         }
8403     }
8404 }
8405