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