1 #include "game.h"
2 
3 namespace game
4 {
5     vector<gameent *> bestplayers;
6     vector<int> bestteams;
7 
8     VARP(ragdoll, 0, 1, 1);
9     VARP(ragdollmillis, 0, 10000, 300000);
10     VARP(ragdollfade, 0, 100, 5000);
11     VARP(forceplayermodels, 0, 0, 1);
12     VARP(hidedead, 0, 0, 1);
13 
14     extern int playermodel;
15 
16     vector<gameent *> ragdolls;
17 
saveragdoll(gameent * d)18     void saveragdoll(gameent *d)
19     {
20         if(!d->ragdoll || !ragdollmillis || (!ragdollfade && lastmillis > d->lastpain + ragdollmillis)) return;
21         gameent *r = new gameent(*d);
22         r->lastupdate = ragdollfade && lastmillis > d->lastpain + max(ragdollmillis - ragdollfade, 0) ? lastmillis - max(ragdollmillis - ragdollfade, 0) : d->lastpain;
23         r->edit = NULL;
24         r->ai = NULL;
25         if(d==player1) r->playermodel = playermodel;
26         ragdolls.add(r);
27         d->ragdoll = NULL;
28     }
29 
clearragdolls()30     void clearragdolls()
31     {
32         ragdolls.deletecontents();
33     }
34 
moveragdolls()35     void moveragdolls()
36     {
37         loopv(ragdolls)
38         {
39             gameent *d = ragdolls[i];
40             if(lastmillis > d->lastupdate + ragdollmillis)
41             {
42                 delete ragdolls.remove(i--);
43                 continue;
44             }
45             moveragdoll(d);
46         }
47     }
48 
49     static const int playercolors[] =
50     {
51         0xAC2C2A,
52         0xAD6932,
53         0xBFAA56,
54         0x4C7A3D,
55         0x3F748C,
56         0x27508A,
57         0xB8658C,
58         0x5A3B80,
59         0xB8B1A5
60     };
61 
62     static const int playercolorsazul[] =
63     {
64         0x27508A,
65         0x3F748C,
66         0x3B3B80,
67         0x5364B5
68     };
69 
70     static const int playercolorsrojo[] =
71     {
72         0xAC2C2A,
73         0x992417,
74         0x802438,
75         0xA3435B
76     };
77 
78     extern void changedplayercolor();
79     VARFP(playercolor, 0, 4, sizeof(playercolors)/sizeof(playercolors[0])-1, changedplayercolor());
80     VARFP(playercolorazul, 0, 0, sizeof(playercolorsazul)/sizeof(playercolorsazul[0])-1, changedplayercolor());
81     VARFP(playercolorrojo, 0, 0, sizeof(playercolorsrojo)/sizeof(playercolorsrojo[0])-1, changedplayercolor());
82 
83     static const playermodelinfo playermodels[] =
84     {
85         { { "player/bones", "player/bones", "player/bones" }, { "hudgun", "hudgun", "hudgun" }, { "player", "player_azul", "player_rojo" }, true }
86     };
87 
88     extern void changedplayermodel();
89     VARFP(playermodel, 0, 0, sizeof(playermodels)/sizeof(playermodels[0])-1, changedplayermodel());
90 
chooserandomplayermodel(int seed)91     int chooserandomplayermodel(int seed)
92     {
93         return (seed&0xFFFF)%(sizeof(playermodels)/sizeof(playermodels[0]));
94     }
95 
getplayermodelinfo(int n)96     const playermodelinfo *getplayermodelinfo(int n)
97     {
98         if(size_t(n) >= sizeof(playermodels)/sizeof(playermodels[0])) return NULL;
99         return &playermodels[n];
100     }
101 
getplayermodelinfo(gameent * d)102     const playermodelinfo &getplayermodelinfo(gameent *d)
103     {
104         const playermodelinfo *mdl = getplayermodelinfo(d==player1 || forceplayermodels ? playermodel : d->playermodel);
105         if(!mdl) mdl = getplayermodelinfo(playermodel);
106         return *mdl;
107     }
108 
getplayercolor(int team,int color)109     int getplayercolor(int team, int color)
110     {
111         #define GETPLAYERCOLOR(playercolors) \
112             return playercolors[color%(sizeof(playercolors)/sizeof(playercolors[0]))];
113         switch(team)
114         {
115             case 1: GETPLAYERCOLOR(playercolorsazul)
116             case 2: GETPLAYERCOLOR(playercolorsrojo)
117             default: GETPLAYERCOLOR(playercolors)
118         }
119     }
120 
121     ICOMMAND(getplayercolor, "ii", (int *color, int *team), intret(getplayercolor(*team, *color)));
122 
getplayercolor(gameent * d,int team)123     int getplayercolor(gameent *d, int team)
124     {
125         if(d==player1) switch(team)
126         {
127             case 1: return getplayercolor(1, playercolorazul);
128             case 2: return getplayercolor(2, playercolorrojo);
129             default: return getplayercolor(0, playercolor);
130         }
131         else return getplayercolor(team, (d->playercolor>>(5*team))&0x1F);
132     }
133 
changedplayermodel()134     void changedplayermodel()
135     {
136         if(player1->clientnum < 0) player1->playermodel = playermodel;
137         if(player1->ragdoll) cleanragdoll(player1);
138         loopv(ragdolls)
139         {
140             gameent *d = ragdolls[i];
141             if(!d->ragdoll) continue;
142             if(!forceplayermodels)
143             {
144                 const playermodelinfo *mdl = getplayermodelinfo(d->playermodel);
145                 if(mdl) continue;
146             }
147             cleanragdoll(d);
148         }
149         loopv(players)
150         {
151             gameent *d = players[i];
152             if(d == player1 || !d->ragdoll) continue;
153             if(!forceplayermodels)
154             {
155                 const playermodelinfo *mdl = getplayermodelinfo(d->playermodel);
156                 if(mdl) continue;
157             }
158             cleanragdoll(d);
159         }
160     }
161 
changedplayercolor()162     void changedplayercolor()
163     {
164         if(player1->clientnum < 0) player1->playercolor = playercolor | (playercolorazul<<5) | (playercolorrojo<<10);
165     }
166 
syncplayer()167     void syncplayer()
168     {
169         if(player1->playermodel != playermodel)
170         {
171             player1->playermodel = playermodel;
172             addmsg(N_SWITCHMODEL, "ri", player1->playermodel);
173         }
174 
175         int col = playercolor | (playercolorazul<<5) | (playercolorrojo<<10);
176         if(player1->playercolor != col)
177         {
178             player1->playercolor = col;
179             addmsg(N_SWITCHCOLOR, "ri", player1->playercolor);
180         }
181     }
182 
preloadplayermodel()183     void preloadplayermodel()
184     {
185         loopi(sizeof(playermodels)/sizeof(playermodels[0]))
186         {
187             const playermodelinfo *mdl = getplayermodelinfo(i);
188             if(!mdl) break;
189             if(i != playermodel && (!multiplayer(false) || forceplayermodels)) continue;
190             if(m_teammode)
191             {
192                 loopj(MAXTEAMS) preloadmodel(mdl->model[1+j]);
193             }
194             else preloadmodel(mdl->model[0]);
195         }
196     }
197 
numanims()198     int numanims() { return NUMANIMS; }
199 
findanims(const char * pattern,vector<int> & anims)200     void findanims(const char *pattern, vector<int> &anims)
201     {
202         loopi(sizeof(animnames)/sizeof(animnames[0])) if(matchanim(animnames[i], pattern)) anims.add(i);
203     }
204 
205     VAR(animoverride, -1, 0, NUMANIMS-1);
206     VAR(testanims, 0, 0, 1);
207     VAR(testpitch, -90, 0, 90);
208 
renderplayer(gameent * d,const playermodelinfo & mdl,int color,int team,float fade,int flags=0,bool mainpass=true)209     void renderplayer(gameent *d, const playermodelinfo &mdl, int color, int team, float fade, int flags = 0, bool mainpass = true)
210     {
211         int lastaction = d->lastaction, anim = ANIM_IDLE|ANIM_LOOP, attack = 0, delay = 0;
212         if(d->lastattack >= 0)
213         {
214             attack = attacks[d->lastattack].anim;
215             delay = attacks[d->lastattack].attackdelay+50;
216         }
217         if(intermission && d->state!=CS_DEAD)
218         {
219             anim = attack = ANIM_LOSE|ANIM_LOOP;
220             if(validteam(team) ? bestteams.htfind(team)>=0 : bestplayers.find(d)>=0) anim = attack = ANIM_WIN|ANIM_LOOP;
221         }
222         else if(d->state==CS_ALIVE && d->lasttaunt && lastmillis-d->lasttaunt<1000 && lastmillis-d->lastaction>delay)
223         {
224             lastaction = d->lasttaunt;
225             anim = attack = ANIM_TAUNT;
226             delay = 1000;
227         }
228         modelattach a[5];
229         int ai = 0;
230         if(guns[d->gunselect].vwep)
231         {
232             int vanim = ANIM_VWEP_IDLE|ANIM_LOOP, vtime = 0;
233             if(lastaction && d->lastattack >= 0 && attacks[d->lastattack].gun==d->gunselect && lastmillis < lastaction + delay)
234             {
235                 vanim = attacks[d->lastattack].vwepanim;
236                 vtime = lastaction;
237             }
238             a[ai++] = modelattach("tag_weapon", guns[d->gunselect].vwep, vanim, vtime);
239         }
240         if(mainpass && !(flags&MDL_ONLYSHADOW))
241         {
242             d->muzzle = vec(-1, -1, -1);
243             if(guns[d->gunselect].vwep) a[ai++] = modelattach("tag_muzzle", &d->muzzle);
244         }
245         const char *mdlname = mdl.model[validteam(team) ? team : 0];
246         float yaw = testanims && d==player1 ? 0 : d->yaw,
247               pitch = testpitch && d==player1 ? testpitch : d->pitch;
248         vec o = d->feetpos();
249         int basetime = 0;
250         if(animoverride) anim = (animoverride<0 ? ANIM_ALL : animoverride)|ANIM_LOOP;
251         else if(d->state==CS_DEAD)
252         {
253             anim = ANIM_DYING|ANIM_NOPITCH;
254             basetime = d->lastpain;
255             if(ragdoll && mdl.ragdoll) anim |= ANIM_RAGDOLL;
256             else if(lastmillis-basetime>1000) anim = ANIM_DEAD|ANIM_LOOP|ANIM_NOPITCH;
257         }
258         else if(d->state==CS_EDITING || d->state==CS_SPECTATOR) anim = ANIM_EDIT|ANIM_LOOP;
259         else if(d->state==CS_LAGGED)                            anim = ANIM_LAG|ANIM_LOOP;
260         else if(!intermission)
261         {
262             if(lastmillis-d->lastpain < 300)
263             {
264                 anim = ANIM_PAIN;
265                 basetime = d->lastpain;
266             }
267             else if(d->lastpain < lastaction && lastmillis-lastaction < delay)
268             {
269                 anim = attack;
270                 basetime = lastaction;
271             }
272 
273             if(d->inwater && d->physstate<=PHYS_FALL) anim |= (((game::allowmove(d) && (d->move || d->strafe)) || d->vel.z+d->falling.z>0 ? ANIM_SWIM : ANIM_SINK)|ANIM_LOOP)<<ANIM_SECONDARY;
274             else
275             {
276                 static const int dirs[9] =
277                 {
278                     ANIM_RUN_SE, ANIM_RUN_S, ANIM_RUN_SW,
279                     ANIM_RUN_E,  0,          ANIM_RUN_W,
280                     ANIM_RUN_NE, ANIM_RUN_N, ANIM_RUN_NW
281                 };
282                 int dir = dirs[(d->move+1)*3 + (d->strafe+1)];
283                 if(d->timeinair>100) anim |= ((dir ? dir+ANIM_JUMP_N-ANIM_RUN_N : ANIM_JUMP) | ANIM_END) << ANIM_SECONDARY;
284                 else if(dir && game::allowmove(d)) anim |= (dir | ANIM_LOOP) << ANIM_SECONDARY;
285             }
286 
287             if(d->crouching) switch((anim>>ANIM_SECONDARY)&ANIM_INDEX)
288             {
289                 case ANIM_IDLE: anim &= ~(ANIM_INDEX<<ANIM_SECONDARY); anim |= ANIM_CROUCH<<ANIM_SECONDARY; break;
290                 case ANIM_JUMP: anim &= ~(ANIM_INDEX<<ANIM_SECONDARY); anim |= ANIM_CROUCH_JUMP<<ANIM_SECONDARY; break;
291                 case ANIM_SWIM: anim &= ~(ANIM_INDEX<<ANIM_SECONDARY); anim |= ANIM_CROUCH_SWIM<<ANIM_SECONDARY; break;
292                 case ANIM_SINK: anim &= ~(ANIM_INDEX<<ANIM_SECONDARY); anim |= ANIM_CROUCH_SINK<<ANIM_SECONDARY; break;
293                 case 0: anim |= (ANIM_CROUCH|ANIM_LOOP)<<ANIM_SECONDARY; break;
294                 case ANIM_RUN_N: case ANIM_RUN_NE: case ANIM_RUN_E: case ANIM_RUN_SE: case ANIM_RUN_S: case ANIM_RUN_SW: case ANIM_RUN_W: case ANIM_RUN_NW:
295                     anim += (ANIM_CROUCH_N - ANIM_RUN_N) << ANIM_SECONDARY;
296                     break;
297                 case ANIM_JUMP_N: case ANIM_JUMP_NE: case ANIM_JUMP_E: case ANIM_JUMP_SE: case ANIM_JUMP_S: case ANIM_JUMP_SW: case ANIM_JUMP_W: case ANIM_JUMP_NW:
298                     anim += (ANIM_CROUCH_JUMP_N - ANIM_JUMP_N) << ANIM_SECONDARY;
299                     break;
300             }
301 
302             if((anim&ANIM_INDEX)==ANIM_IDLE && (anim>>ANIM_SECONDARY)&ANIM_INDEX) anim >>= ANIM_SECONDARY;
303         }
304         if(!((anim>>ANIM_SECONDARY)&ANIM_INDEX)) anim |= (ANIM_IDLE|ANIM_LOOP)<<ANIM_SECONDARY;
305         if(d!=player1) flags |= MDL_CULL_VFC | MDL_CULL_OCCLUDED | MDL_CULL_QUERY;
306         if(d->type==ENT_PLAYER) flags |= MDL_FULLBRIGHT;
307         else flags |= MDL_CULL_DIST;
308         if(!mainpass) flags &= ~(MDL_FULLBRIGHT | MDL_CULL_VFC | MDL_CULL_OCCLUDED | MDL_CULL_QUERY | MDL_CULL_DIST);
309         float trans = d->state == CS_LAGGED ? 0.3f : 1.0f;
310         rendermodel(mdlname, anim, o, yaw, pitch, 0, flags, d, a[0].tag ? a : NULL, basetime, 0, fade, vec4(vec::hexcolor(color), trans));
311     }
312 
renderplayer(gameent * d,float fade=1,int flags=0)313     static inline void renderplayer(gameent *d, float fade = 1, int flags = 0)
314     {
315         int team = m_teammode && validteam(d->team) ? d->team : 0;
316         renderplayer(d, getplayermodelinfo(d), getplayercolor(d, team), team, fade, flags);
317     }
318 
rendergame()319     void rendergame()
320     {
321         ai::render();
322 
323         if(intermission)
324         {
325             bestteams.shrink(0);
326             bestplayers.shrink(0);
327             if(m_teammode) getbestteams(bestteams);
328             else getbestplayers(bestplayers);
329         }
330 
331         bool third = isthirdperson();
332         gameent *f = followingplayer(), *exclude = third ? NULL : f;
333         loopv(players)
334         {
335             gameent *d = players[i];
336             if(d == player1 || d->state==CS_SPECTATOR || d->state==CS_SPAWNING || d->lifesequence < 0 || d == exclude || (d->state==CS_DEAD && hidedead)) continue;
337             renderplayer(d);
338             copystring(d->info, colorname(d));
339             if(d->state!=CS_DEAD)
340             {
341                 int team = m_teammode && validteam(d->team) ? d->team : 0;
342                 particle_text(d->abovehead(), d->info, PART_TEXT, 1, teamtextcolor[team], 2.0f);
343             }
344         }
345         loopv(ragdolls)
346         {
347             gameent *d = ragdolls[i];
348             float fade = 1.0f;
349             if(ragdollmillis && ragdollfade)
350                 fade -= clamp(float(lastmillis - (d->lastupdate + max(ragdollmillis - ragdollfade, 0)))/min(ragdollmillis, ragdollfade), 0.0f, 1.0f);
351             renderplayer(d, fade);
352         }
353         if(exclude)
354             renderplayer(exclude, 1, MDL_ONLYSHADOW);
355         else if(!f && (player1->state==CS_ALIVE || (player1->state==CS_EDITING && third) || (player1->state==CS_DEAD && !hidedead)))
356             renderplayer(player1, 1, third ? 0 : MDL_ONLYSHADOW);
357         entities::renderentities();
358         renderbouncers();
359         renderprojectiles();
360         if(cmode) cmode->rendergame();
361     }
362 
363     VARP(hudgun, 0, 1, 1);
364     VARP(hudgunsway, 0, 1, 1);
365 
366     FVAR(swaystep, 1, 35.0f, 100);
367     FVAR(swayside, 0, 0.10f, 1);
368     FVAR(swayup, -1, 0.15f, 1);
369 
370     float swayfade = 0, swayspeed = 0, swaydist = 0;
371     vec swaydir(0, 0, 0);
372 
swayhudgun(int curtime)373     void swayhudgun(int curtime)
374     {
375         gameent *d = hudplayer();
376         if(d->state != CS_SPECTATOR)
377         {
378             if(d->physstate >= PHYS_SLOPE)
379             {
380                 swayspeed = min(sqrtf(d->vel.x*d->vel.x + d->vel.y*d->vel.y), d->maxspeed);
381                 swaydist += swayspeed*curtime/1000.0f;
382                 swaydist = fmod(swaydist, 2*swaystep);
383                 swayfade = 1;
384             }
385             else if(swayfade > 0)
386             {
387                 swaydist += swayspeed*swayfade*curtime/1000.0f;
388                 swaydist = fmod(swaydist, 2*swaystep);
389                 swayfade -= 0.5f*(curtime*d->maxspeed)/(swaystep*1000.0f);
390             }
391 
392             float k = pow(0.7f, curtime/10.0f);
393             swaydir.mul(k);
394             vec vel(d->vel);
395             vel.add(d->falling);
396             swaydir.add(vec(vel).mul((1-k)/(15*max(vel.magnitude(), d->maxspeed))));
397         }
398     }
399 
400     struct hudent : dynent
401     {
hudentgame::hudent402         hudent() { type = ENT_CAMERA; }
403     } guninterp;
404 
drawhudmodel(gameent * d,int anim,int basetime)405     void drawhudmodel(gameent *d, int anim, int basetime)
406     {
407         const char *file = guns[d->gunselect].file;
408         if(!file) return;
409 
410         vec sway;
411         vecfromyawpitch(d->yaw, 0, 0, 1, sway);
412         float steps = swaydist/swaystep*M_PI;
413         sway.mul(swayside*cosf(steps));
414         sway.z = swayup*(fabs(sinf(steps)) - 1);
415         sway.add(swaydir).add(d->o);
416         if(!hudgunsway) sway = d->o;
417 
418         const playermodelinfo &mdl = getplayermodelinfo(d);
419         int team = m_teammode && validteam(d->team) ? d->team : 0,
420             color = getplayercolor(d, team);
421         defformatstring(gunname, "%s/%s", mdl.hudguns[team], file);
422         modelattach a[2];
423         d->muzzle = vec(-1, -1, -1);
424         a[0] = modelattach("tag_muzzle", &d->muzzle);
425         rendermodel(gunname, anim, sway, d->yaw, d->pitch, 0, MDL_NOBATCH, NULL, a, basetime, 0, 1, vec4(vec::hexcolor(color), 1));
426         if(d->muzzle.x >= 0) d->muzzle = calcavatarpos(d->muzzle, 12);
427     }
428 
drawhudgun()429     void drawhudgun()
430     {
431         gameent *d = hudplayer();
432         if(d->state==CS_SPECTATOR || d->state==CS_EDITING || !hudgun || editmode)
433         {
434             d->muzzle = player1->muzzle = vec(-1, -1, -1);
435             return;
436         }
437 
438         int anim = ANIM_GUN_IDLE|ANIM_LOOP, basetime = 0;
439         if(d->lastaction && d->lastattack >= 0 && attacks[d->lastattack].gun==d->gunselect && lastmillis-d->lastaction<attacks[d->lastattack].attackdelay)
440         {
441             anim = attacks[d->lastattack].hudanim;
442             basetime = d->lastaction;
443         }
444         drawhudmodel(d, anim, basetime);
445     }
446 
renderavatar()447     void renderavatar()
448     {
449         drawhudgun();
450     }
451 
renderplayerpreview(int model,int color,int team,int weap)452     void renderplayerpreview(int model, int color, int team, int weap)
453     {
454         static gameent *previewent = NULL;
455         if(!previewent)
456         {
457             previewent = new gameent;
458             loopi(NUMGUNS) previewent->ammo[i] = 1;
459         }
460         float height = previewent->eyeheight + previewent->aboveeye,
461               zrad = height/2;
462         vec2 xyrad = vec2(previewent->xradius, previewent->yradius).max(height/4);
463         previewent->o = calcmodelpreviewpos(vec(xyrad, zrad), previewent->yaw).addz(previewent->eyeheight - zrad);
464         previewent->gunselect = validgun(weap) ? weap : GUN_RAIL;
465         const playermodelinfo *mdlinfo = getplayermodelinfo(model);
466         if(!mdlinfo) return;
467         renderplayer(previewent, *mdlinfo, getplayercolor(team, color), team, 1, 0, false);
468     }
469 
hudgunorigin(int gun,const vec & from,const vec & to,gameent * d)470     vec hudgunorigin(int gun, const vec &from, const vec &to, gameent *d)
471     {
472         if(d->muzzle.x >= 0) return d->muzzle;
473         vec offset(from);
474         if(d!=hudplayer() || isthirdperson())
475         {
476             vec front, right;
477             vecfromyawpitch(d->yaw, d->pitch, 1, 0, front);
478             offset.add(front.mul(d->radius));
479             offset.z += (d->aboveeye + d->eyeheight)*0.75f - d->eyeheight;
480             vecfromyawpitch(d->yaw, 0, 0, -1, right);
481             offset.add(right.mul(0.5f*d->radius));
482             offset.add(front);
483             return offset;
484         }
485         offset.add(vec(to).sub(from).normalize().mul(2));
486         if(hudgun)
487         {
488             offset.sub(vec(camup).mul(1.0f));
489             offset.add(vec(camright).mul(0.8f));
490         }
491         else offset.sub(vec(camup).mul(0.8f));
492         return offset;
493     }
494 
preloadweapons()495     void preloadweapons()
496     {
497         const playermodelinfo &mdl = getplayermodelinfo(player1);
498         loopi(NUMGUNS)
499         {
500             const char *file = guns[i].file;
501             if(!file) continue;
502             string fname;
503             if(m_teammode)
504             {
505                 loopj(MAXTEAMS)
506                 {
507                     formatstring(fname, "%s/%s", mdl.hudguns[1+j], file);
508                     preloadmodel(fname);
509                 }
510             }
511             else
512             {
513                 formatstring(fname, "%s/%s", mdl.hudguns[0], file);
514                 preloadmodel(fname);
515             }
516             formatstring(fname, "worldgun/%s", file);
517             preloadmodel(fname);
518         }
519     }
520 
preloadsounds()521     void preloadsounds()
522     {
523         for(int i = S_JUMP; i <= S_DIE2; i++) preloadsound(i);
524     }
525 
preload()526     void preload()
527     {
528         if(hudgun) preloadweapons();
529         preloadbouncers();
530         preloadplayermodel();
531         preloadsounds();
532         entities::preloadentities();
533     }
534 
535 }
536 
537