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