1 #define GAMEWORLD 1 2 #include "game.h" 3 namespace game 4 { 5 int nextmode = -1, nextmuts = -1, gamemode = -1, mutators = -1, maptime = 0, minremain = 0, 6 lastcamera = 0, lasttvcam = 0, lasttvchg = 0, lastzoom = 0, lastmousetype = 0, liquidchan = -1, fogdist = 0; 7 bool intermission = false, prevzoom = false, zooming = false; 8 float swayfade = 0, swayspeed = 0, swaydist = 0; 9 vec swaydir(0, 0, 0), swaypush(0, 0, 0); 10 string clientmap = ""; 11 12 gameent *player1 = new gameent(); 13 vector<gameent *> players; 14 dynent fpsmodel; 15 16 ICOMMANDG(resetvars, "", (), return); // server side 17 18 VARW(numplayers, 0, 0, MAXCLIENTS); 19 SVARW(obitwater, ""); 20 SVARW(obitdeath, ""); 21 SVARW(mapmusic, ""); 22 23 VARP(mouseinvert, 0, 0, 1); 24 VARP(mouseabsolute, 0, 0, 1); 25 VARP(mousetype, 0, 0, 2); 26 VARP(mousedeadzone, 0, 10, 100); 27 VARP(mousepanspeed, 1, 30, INT_MAX-1); 28 29 VARP(thirdperson, 0, 0, 1); 30 VARP(dynlightents, 0, 2, 2); 31 FVARP(playerblend, 0, 1, 1); 32 33 VARP(thirdpersonmodel, 0, 1, 1); 34 VARP(thirdpersonfov, 90, 120, 150); 35 FVARP(thirdpersonblend, 0, 0.5f, 1); 36 FVARP(thirdpersondist, -100, 1.f, 100); 37 38 VARP(firstpersonmodel, 0, 1, 1); 39 VARP(firstpersonfov, 90, 100, 150); 40 VARP(firstpersonsway, 0, 1, 1); 41 FVARP(firstpersonswaystep, 1, 18.0f, 100); 42 FVARP(firstpersonswayside, 0, 0.04f, 1); 43 FVARP(firstpersonswayup, 0, 0.05f, 1); 44 FVARP(firstpersonblend, 0, 1, 1); 45 FVARP(firstpersondist, -10000, -0.25f, 10000); 46 FVARP(firstpersonshift, -10000, 0.3f, 10000); 47 FVARP(firstpersonadjust, -10000, -0.07f, 10000); 48 49 VARP(editfov, 1, 120, 179); 50 VARP(specfov, 1, 120, 179); 51 VARP(specmode, 0, 1, 1); // 0 = float, 1 = tv 52 VARP(spectvtime, 1000, 10000, INT_MAX-1); 53 FVARP(spectvspeed, 0, 1, 1000); 54 FVARP(spectvpitch, 0, 1, 1000); 55 VARP(waitmode, 0, 1, 1); // 0 = float, 1 = tv 56 VARP(waittvtime, 1000, 5000, INT_MAX-1); 57 FVARP(waittvspeed, 0, 1, 1000); 58 FVARP(waittvpitch, 0, 1, 1000); 59 VARP(deathcamstyle, 0, 1, 2); // 0 = no follow, 1 = follow attacker, 2 = follow self 60 FVARP(deathcamspeed, 0, 2.f, 1000); 61 62 FVARP(sensitivity, 1e-3f, 10.0f, 1000); 63 FVARP(yawsensitivity, 1e-3f, 10.0f, 1000); 64 FVARP(pitchsensitivity, 1e-3f, 7.5f, 1000); 65 FVARP(mousesensitivity, 1e-3f, 1.0f, 1000); 66 FVARP(zoomsensitivity, 0, 0.75f, 1); 67 68 VARP(zoommousetype, 0, 0, 2); 69 VARP(zoommousedeadzone, 0, 25, 100); 70 VARP(zoommousepanspeed, 1, 10, INT_MAX-1); 71 VARP(zoomfov, 1, 10, 150); 72 VARP(zoomtime, 1, 100, 10000); 73 74 VARFP(zoomlevel, 1, 4, 10, checkzoom()); 75 VARP(zoomlevels, 1, 5, 10); 76 VARP(zoomdefault, 0, 0, 10); // 0 = last used, else defines default level 77 VARP(zoomscroll, 0, 0, 1); // 0 = stop at min/max, 1 = go to opposite end 78 79 VARP(shownamesabovehead, 0, 2, 2); 80 VARP(showstatusabovehead, 0, 2, 2); 81 VARP(showteamabovehead, 0, 1, 3); 82 VARP(showdamageabovehead, 0, 0, 3); 83 FVARP(aboveheadblend, 0.f, 0.75f, 1.f); 84 FVAR(aboveheadsmooth, 0, 0.5f, 1); 85 VAR(aboveheadsmoothmillis, 1, 200, 10000); 86 VARP(aboveheadfade, 500, 5000, INT_MAX-1); 87 88 VARP(showobituaries, 0, 4, 5); // 0 = off, 1 = only me, 2 = 1 + announcements, 3 = 2 + but dying bots, 4 = 3 + but bot vs bot, 5 = all 89 VARP(showobitdists, 0, 0, 1); 90 VARP(showplayerinfo, 0, 2, 2); // 0 = none, 1 = CON_MESG, 2 = CON_EVENT 91 VARP(playdamagetones, 0, 1, 3); 92 93 VARP(quakefade, 0, 100, INT_MAX-1); 94 VARP(ragdolls, 0, 1, 1); 95 FVARP(bloodscale, 0, 1, 1000); 96 VARP(bloodfade, 1, 10000, INT_MAX-1); 97 FVARP(gibscale, 0, 1, 1000); 98 VARP(gibfade, 1, 10000, INT_MAX-1); 99 VARP(fireburnfade, 0, 75, INT_MAX-1); 100 FVARP(impulsescale, 0, 1, 1000); 101 VARP(impulsefade, 0, 200, INT_MAX-1); 102 103 ICOMMAND(gamemode, "", (), intret(gamemode)); 104 ICOMMAND(mutators, "", (), intret(mutators)); 105 ICOMMAND(getintermission, "", (), intret(intermission ? 1 : 0)); 106 start()107 void start() { } 108 gametitle()109 const char *gametitle() { return connected() ? server::gamename(gamemode, mutators) : "ready"; } gametext()110 const char *gametext() { return connected() ? mapname : "not connected"; } 111 thirdpersonview(bool viewonly)112 bool thirdpersonview(bool viewonly) 113 { 114 if(!viewonly && (player1->state == CS_DEAD || player1->state == CS_WAITING)) return true; 115 if(!thirdperson) return false; 116 if(player1->state == CS_EDITING) return false; 117 if(player1->state == CS_SPECTATOR) return false; 118 if(inzoom()) return false; 119 return true; 120 } 121 ICOMMAND(isthirdperson, "i", (int *viewonly), intret(thirdpersonview(*viewonly ? true : false) ? 1 : 0)); 122 mousestyle()123 int mousestyle() 124 { 125 if(inzoom()) return zoommousetype; 126 return mousetype; 127 } 128 deadzone()129 int deadzone() 130 { 131 if(inzoom()) return zoommousedeadzone; 132 return mousedeadzone; 133 } 134 panspeed()135 int panspeed() 136 { 137 if(inzoom()) return zoommousepanspeed; 138 return mousepanspeed; 139 } 140 fov()141 int fov() 142 { 143 if(player1->state == CS_EDITING) return editfov; 144 if(player1->state == CS_SPECTATOR) return specfov; 145 if(thirdpersonview(true)) return thirdpersonfov; 146 return firstpersonfov; 147 } 148 checkzoom()149 void checkzoom() 150 { 151 if(zoomdefault > zoomlevels) zoomdefault = zoomlevels; 152 if(zoomlevel < 0) zoomlevel = zoomdefault ? zoomdefault : zoomlevels; 153 if(zoomlevel > zoomlevels) zoomlevel = zoomlevels; 154 } 155 setzoomlevel(int level)156 void setzoomlevel(int level) 157 { 158 checkzoom(); 159 zoomlevel += level; 160 if(zoomlevel > zoomlevels) zoomlevel = zoomscroll ? 1 : zoomlevels; 161 else if(zoomlevel < 1) zoomlevel = zoomscroll ? zoomlevels : 1; 162 } 163 ICOMMAND(setzoom, "i", (int *level), setzoomlevel(*level)); 164 zoomset(bool on,int millis)165 void zoomset(bool on, int millis) 166 { 167 if(on != zooming) 168 { 169 resetcursor(); 170 lastzoom = millis-max(zoomtime-(millis-lastzoom), 0); 171 prevzoom = zooming; 172 if(zoomdefault && on) zoomlevel = zoomdefault; 173 } 174 checkzoom(); 175 zooming = on; 176 } 177 zoomallow()178 bool zoomallow() 179 { 180 if(allowmove(player1) && weaptype[player1->weapselect].zooms) return true; 181 zoomset(false, 0); 182 return false; 183 } 184 inzoom()185 bool inzoom() 186 { 187 if(zoomallow() && lastzoom && (zooming || lastmillis-lastzoom <= zoomtime)) 188 return true; 189 return false; 190 } 191 ICOMMAND(iszooming, "", (), intret(inzoom() ? 1 : 0)); 192 inzoomswitch()193 bool inzoomswitch() 194 { 195 if(zoomallow() && lastzoom && ((zooming && lastmillis-lastzoom >= zoomtime/2) || (!zooming && lastmillis-lastzoom <= zoomtime/2))) 196 return true; 197 return false; 198 } 199 resetsway()200 void resetsway() 201 { 202 swaydir = swaypush = vec(0, 0, 0); 203 swayfade = swayspeed = swaydist = 0; 204 } 205 addsway(gameent * d)206 void addsway(gameent *d) 207 { 208 if(firstpersonsway) 209 { 210 float maxspeed = physics::movevelocity(d); 211 if(d->physstate >= PHYS_SLOPE) 212 { 213 swayspeed = min(sqrtf(d->vel.x*d->vel.x + d->vel.y*d->vel.y), maxspeed); 214 swaydist += swayspeed*curtime/1000.0f; 215 swaydist = fmod(swaydist, 2*firstpersonswaystep); 216 swayfade = 1; 217 } 218 else if(swayfade > 0) 219 { 220 swaydist += swayspeed*swayfade*curtime/1000.0f; 221 swaydist = fmod(swaydist, 2*firstpersonswaystep); 222 swayfade -= 0.5f*(curtime*maxspeed)/(firstpersonswaystep*1000.0f); 223 } 224 225 float k = pow(0.7f, curtime/25.0f); 226 swaydir.mul(k); 227 vec vel = vec(d->vel).add(d->falling).mul(FWV(impulsestyle) && d->action[AC_IMPULSE] && (d->move || d->strafe) ? 5 : 1); 228 float speedscale = max(vel.magnitude(), maxspeed); 229 if(speedscale > 0) swaydir.add(vec(vel).mul((1-k)/(15*speedscale))); 230 swaypush.mul(pow(0.5f, curtime/25.0f)); 231 } 232 else resetsway(); 233 } 234 announce(int idx,int targ,gameent * d,const char * msg,...)235 void announce(int idx, int targ, gameent *d, const char *msg, ...) 236 { 237 if(targ >= 0 && msg && *msg) 238 { 239 defvformatstring(text, msg, msg); 240 conoutft(targ, "%s", text); 241 } 242 if(idx >= 0) 243 { 244 if(d && issound(d->aschan)) removesound(d->aschan); 245 physent *t = !d || d == player1 ? camera1 : d; 246 playsound(idx, t->o, t, t == camera1 ? SND_FORCED : 0, 255, getworldsize()/2, 0, d ? &d->aschan : NULL); 247 } 248 } 249 ICOMMAND(announce, "iis", (int *idx, int *targ, char *s), announce(*idx, *targ, NULL, "\fw%s", s)); 250 tvmode()251 bool tvmode() 252 { 253 if(!m_edit(gamemode)) switch(player1->state) 254 { 255 case CS_SPECTATOR: if(specmode) return true; break; 256 case CS_WAITING: if(waitmode && (!player1->lastdeath || lastmillis-player1->lastdeath >= 500)) return true; break; 257 default: break; 258 } 259 return false; 260 } 261 262 ICOMMAND(specmodeswitch, "", (), specmode = specmode ? 0 : 1); 263 ICOMMAND(waitmodeswitch, "", (), waitmode = waitmode ? 0 : 1); 264 allowmove(physent * d)265 bool allowmove(physent *d) 266 { 267 if(d == player1) 268 { 269 if(UI::hascursor(true)) return false; 270 if(tvmode()) return false; 271 } 272 if(d->type == ENT_PLAYER || d->type == ENT_AI) 273 { 274 if(d->state == CS_DEAD || d->state == CS_WAITING || d->state == CS_SPECTATOR || intermission) 275 return false; 276 } 277 return true; 278 } 279 choosearenaweap(gameent * d,const char * s)280 void choosearenaweap(gameent *d, const char *s) 281 { 282 if(m_arena(gamemode, mutators)) 283 { 284 int weap = -1; 285 if(*s >= '0' && *s <= '9') weap = atoi(s); 286 else 287 { 288 loopi(WEAP_SUPER) if(!strcasecmp(weaptype[i].name, s)) 289 { 290 weap = i; 291 break; 292 } 293 } 294 if(weap < WEAP_OFFSET || weap >= WEAP_SUPER || weap == WEAP_GRENADE) weap = WEAP_MELEE; 295 client::addmsg(SV_ARENAWEAP, "ri2", d->clientnum, weap); 296 conoutft(CON_SELF, "\fwyou will spawn with: %s%s", weaptype[weap].text, (weap >= WEAP_OFFSET ? weaptype[weap].name : "random weapons")); 297 } 298 else conoutft(CON_MESG, "\foweapon selection is only available in arena"); 299 } 300 ICOMMAND(arenaweap, "s", (char *s), choosearenaweap(player1, s)); 301 respawn(gameent * d)302 void respawn(gameent *d) 303 { 304 if(d->state == CS_DEAD && d->respawned < 0 && (!d->lastdeath || lastmillis-d->lastdeath >= 500)) 305 { 306 client::addmsg(SV_TRYSPAWN, "ri", d->clientnum); 307 d->respawned = lastmillis; 308 } 309 } 310 deadscale(gameent * d,float amt=1,bool timechk=false)311 float deadscale(gameent *d, float amt = 1, bool timechk = false) 312 { 313 float total = amt; 314 if(d->state == CS_DEAD || d->state == CS_WAITING) 315 { 316 int len = m_delay(gamemode, mutators); 317 if(!len && d->aitype >= AI_START) len = ai::aideadfade; 318 if(len && (!timechk || len > 1000)) 319 { 320 int interval = min(len/3, 1000), over = max(len-interval, 500), millis = lastmillis-d->lastdeath; 321 if(millis <= len) { if(millis >= over) total *= 1.f-(float(millis-over)/float(interval)); } 322 else total = 0; 323 } 324 } 325 return total; 326 } 327 transscale(gameent * d,bool third=true)328 float transscale(gameent *d, bool third = true) 329 { 330 float total = d == player1 ? (third ? thirdpersonblend : firstpersonblend) : playerblend; 331 if(d->state == CS_ALIVE) 332 { 333 int prot = m_protect(gamemode, mutators), millis = d->protect(lastmillis, prot); // protect returns time left 334 if(millis > 0) total *= 1.f-(float(millis)/float(prot)); 335 if(d == player1 && inzoom()) 336 { 337 int frame = lastmillis-lastzoom; 338 float pc = frame <= zoomtime ? float(frame)/float(zoomtime) : 1.f; 339 total *= zooming ? 1.f-pc : pc; 340 } 341 } 342 else total = deadscale(d, total); 343 return total; 344 } 345 adddynlights()346 void adddynlights() 347 { 348 if(dynlightents) 349 { 350 projs::adddynlights(); 351 entities::adddynlights(); 352 if(dynlightents > 1) 353 { 354 if(m_ctf(gamemode)) ctf::adddynlights(); 355 if(m_stf(gamemode)) stf::adddynlights(); 356 } 357 if(fireburntime) 358 { 359 gameent *d = NULL; 360 loopi(numdynents()) if((d = (gameent *)iterdynents(i)) && d->lastfire && lastmillis-d->lastfire <= fireburntime) 361 { 362 float pc = float((lastmillis-d->lastfire)%fireburndelay)/float(fireburndelay/2); pc = deadscale(d, pc > 1.f ? 2.f-pc : pc); 363 adddynlight(d->headpos(-d->height*0.5f), d->height*(1.f+(pc*0.5f)+(rnd(50)/100.f)), vec(1.1f*max(pc,0.5f), 0.5f*max(pc,0.2f), 0.125f*pc)); 364 } 365 } 366 } 367 } 368 spawneffect(int type,const vec & o,int colour,int radius,float vel,int fade,float size,float blend)369 void spawneffect(int type, const vec &o, int colour, int radius, float vel, int fade, float size, float blend) 370 { 371 regularshape(type, radius, colour, 21, 25, fade, o, size, blend, -vel, 0, vel*2); 372 adddynlight(vec(o).add(vec(0, 0, radius)), radius*2, vec(colour>>16, (colour>>8)&0xFF, colour&0xFF).mul(2.f/0xFF).mul(blend), fade, fade/3); 373 } 374 impulseeffect(gameent * d,bool effect)375 void impulseeffect(gameent *d, bool effect) 376 { 377 if(effect || (FWV(impulsestyle) && d->state == CS_ALIVE && (d->turnside || (d->action[AC_IMPULSE] && (!d->ai || d->move || d->strafe))))) 378 { 379 int num = int((effect ? 25 : 5)*impulsescale), len = effect ? impulsefade : impulsefade/5; 380 if(num > 0 && len > 0) 381 { 382 if(d->type == ENT_PLAYER || d->type == ENT_AI) 383 { 384 regularshape(PART_FIREBALL, int(d->radius), firecols[effect ? 0 : rnd(FIRECOLOURS)], 21, num, len, d->lfoot, 1, 0.5f, -15, 0, 5); 385 regularshape(PART_FIREBALL, int(d->radius), firecols[effect ? 0 : rnd(FIRECOLOURS)], 21, num, len, d->rfoot, 1, 0.5f, -15, 0, 5); 386 } 387 else regularshape(PART_FIREBALL, int(d->radius)*2, firecols[effect ? 0 : rnd(FIRECOLOURS)], 21, num, len, d->feetpos(), 1, 0.5f, -15, 0, 5); 388 } 389 } 390 } 391 fireeffect(gameent * d)392 void fireeffect(gameent *d) 393 { 394 if(fireburntime && d->lastfire && (d != player1 || thirdpersonview()) && lastmillis-d->lastfire <= fireburntime) 395 { 396 float pc = lastmillis-d->lastfire >= fireburntime-500 ? 1.f-((lastmillis-d->lastfire-(fireburntime-500))/500.f) : 1.f; 397 regular_part_create(PART_FIREBALL_SOFT, max(int(fireburnfade*pc),1), d->headpos(-d->height*0.35f), firecols[rnd(FIRECOLOURS)], d->height*deadscale(d, 0.65f), 0.75f, -15, 0); 398 } 399 } 400 pointatplayer()401 gameent *pointatplayer() 402 { 403 loopv(players) 404 { 405 gameent *o = players[i]; 406 if(!o) continue; 407 vec pos = player1->headpos(); 408 float dist; 409 if(intersect(o, pos, worldpos, dist)) return o; 410 } 411 return NULL; 412 } 413 setmode(int nmode,int nmuts)414 void setmode(int nmode, int nmuts) 415 { 416 nextmode = nmode; nextmuts = nmuts; 417 server::modecheck(&nextmode, &nextmuts); 418 } 419 ICOMMAND(mode, "ii", (int *val, int *mut), setmode(*val, *mut)); 420 checkcamera()421 void checkcamera() 422 { 423 camera1 = &camera; 424 if(camera1->type != ENT_CAMERA) 425 { 426 camera1->reset(); 427 camera1->type = ENT_CAMERA; 428 camera1->collidetype = COLLIDE_AABB; 429 camera1->state = CS_ALIVE; 430 camera1->height = camera1->zradius = camera1->radius = camera1->xradius = camera1->yradius = 2; 431 } 432 if((player1->state != CS_WAITING && player1->state != CS_SPECTATOR) || tvmode()) 433 { 434 camera1->vel = vec(0, 0, 0); 435 camera1->move = camera1->strafe = 0; 436 } 437 } 438 resetcamera()439 void resetcamera() 440 { 441 lastcamera = 0; 442 zoomset(false, 0); 443 resetcursor(); 444 checkcamera(); 445 camera1->o = player1->o; 446 camera1->yaw = player1->yaw; 447 camera1->pitch = player1->pitch; 448 camera1->roll = player1->calcroll(false); 449 camera1->resetinterp(); 450 player1->resetinterp(); 451 } 452 resetworld()453 void resetworld() 454 { 455 hud::sb.showscores(false); 456 cleargui(); 457 } 458 resetstate()459 void resetstate() 460 { 461 resetworld(); 462 resetcamera(); 463 } 464 heightoffset(gameent * d,bool local)465 void heightoffset(gameent *d, bool local) 466 { 467 d->o.z -= d->height; 468 if(d->state == CS_ALIVE) 469 { 470 if(physics::iscrouching(d)) 471 { 472 bool crouching = d->action[AC_CROUCH]; 473 float crouchoff = 1.f-CROUCHHEIGHT; 474 if(!crouching) 475 { 476 float z = d->o.z, zoff = d->zradius*crouchoff, zrad = d->zradius-zoff, frac = zoff/10.f; 477 d->o.z += zrad; 478 loopi(10) 479 { 480 d->o.z += frac; 481 if(!collide(d, vec(0, 0, 1), 0.f, false)) 482 { 483 crouching = true; 484 break; 485 } 486 } 487 if(crouching) 488 { 489 if(d->actiontime[AC_CROUCH] >= 0) d->actiontime[AC_CROUCH] = max(PHYSMILLIS-(lastmillis-d->actiontime[AC_CROUCH]), 0)-lastmillis; 490 } 491 else if(d->actiontime[AC_CROUCH] < 0) 492 d->actiontime[AC_CROUCH] = lastmillis-max(PHYSMILLIS-(lastmillis+d->actiontime[AC_CROUCH]), 0); 493 d->o.z = z; 494 } 495 if(d->type == ENT_PLAYER || d->type == ENT_AI) 496 { 497 int crouchtime = abs(d->actiontime[AC_CROUCH]); 498 float amt = lastmillis-crouchtime <= PHYSMILLIS ? clamp(float(lastmillis-crouchtime)/PHYSMILLIS, 0.f, 1.f) : 1.f; 499 if(!crouching) amt = 1.f-amt; 500 crouchoff *= amt; 501 } 502 d->height = d->zradius-(d->zradius*crouchoff); 503 } 504 else d->height = d->zradius; 505 } 506 else d->height = d->zradius; 507 d->o.z += d->height; 508 } 509 checkoften(gameent * d,bool local)510 void checkoften(gameent *d, bool local) 511 { 512 d->checktags(); 513 adjustscaled(int, d->quake, quakefade); 514 if(d->aitype <= AI_BOT) heightoffset(d, local); 515 loopi(WEAP_MAX) if(d->weapstate[i] != WEAP_S_IDLE) 516 { 517 if(d->state != CS_ALIVE || (d->weapstate[i] != WEAP_S_POWER && lastmillis-d->weaplast[i] >= d->weapwait[i])) 518 d->setweapstate(i, WEAP_S_IDLE, 0, lastmillis); 519 } 520 if(d->respawned > 0 && lastmillis-d->respawned >= PHYSMILLIS*4) d->respawned = -1; 521 if(d->suicided > 0 && lastmillis-d->suicided >= PHYSMILLIS*4) d->suicided = -1; 522 if(d->lastfire > 0) 523 { 524 if(lastmillis-d->lastfire >= fireburntime-500) 525 { 526 if(lastmillis-d->lastfire >= fireburntime) 527 { 528 if(issound(d->fschan)) removesound(d->fschan); 529 d->fschan = -1; d->lastfire = 0; 530 } 531 else if(issound(d->fschan)) sounds[d->fschan].vol = int((d != player1 ? 128 : 224)*(1.f-(lastmillis-d->lastfire-(fireburntime-500))/500.f)); 532 } 533 } 534 else if(issound(d->fschan)) 535 { 536 removesound(d->fschan); 537 d->fschan = -1; 538 } 539 } 540 541 otherplayers()542 void otherplayers() 543 { 544 loopv(players) if(players[i]) 545 { 546 gameent *d = players[i]; 547 const int lagtime = lastmillis-d->lastupdate; 548 if(d->ai || !lagtime || intermission) continue; 549 //else if(lagtime > 1000) continue; 550 physics::smoothplayer(d, 1, false); 551 } 552 } 553 quake(const vec & o,int damage,int radius)554 void quake(const vec &o, int damage, int radius) 555 { 556 gameent *d; 557 loopi(numdynents()) if((d = (gameent *)iterdynents(i))) 558 d->quake = clamp(d->quake+max(int(damage*(1.f-d->o.dist(o)/EXPLOSIONSCALE/radius)*(m_insta(gamemode, mutators) ? 0.25f : 1.f)), 1), 0, 1000); 559 } 560 fireburn(gameent * d,int weap,int flags)561 bool fireburn(gameent *d, int weap, int flags) 562 { 563 if(fireburntime && (doesburn(weap, flags) || flags&HIT_MELT || (weap == -1 && flags&HIT_BURN))) 564 { 565 if(!issound(d->fschan)) playsound(S_BURNFIRE, d->o, d, SND_LOOP, d != player1 ? 128 : 224, -1, -1, &d->fschan); 566 if(flags&HIT_FULL) d->lastfire = lastmillis; 567 else return true; 568 } 569 return false; 570 } 571 572 struct damagetone 573 { 574 enum { BURN = 1<<0 }; 575 576 gameent *actor; 577 int damage, flags; 578 damagetonegame::damagetone579 damagetone() {} damagetonegame::damagetone580 damagetone(gameent *actor, int damage, int flags) : actor(actor), damage(damage), flags(flags) {} 581 mergegame::damagetone582 bool merge(const damagetone &m) 583 { 584 if(actor != m.actor || flags != m.flags) return false; 585 damage += m.damage; 586 return true; 587 } 588 playgame::damagetone589 void play() 590 { 591 int snd = 0; 592 if(flags & BURN) snd = 8; 593 else if(damage >= 200) snd = 7; 594 else if(damage >= 150) snd = 6; 595 else if(damage >= 100) snd = 5; 596 else if(damage >= 75) snd = 4; 597 else if(damage >= 50) snd = 3; 598 else if(damage >= 25) snd = 2; 599 else if(damage >= 10) snd = 1; 600 playsound(S_DAMAGE1+snd, actor->o, actor, SND_FORCED, -1, -1, -1); 601 } 602 }; 603 vector<damagetone> damagetones; 604 removedamagetones(gameent * actor)605 void removedamagetones(gameent *actor) 606 { 607 loopvrev(damagetones) if(damagetones[i].actor == actor) damagetones.removeunordered(i); 608 } 609 mergedamagetone(gameent * actor,int damage,int flags)610 void mergedamagetone(gameent *actor, int damage, int flags) 611 { 612 damagetone dt(actor, damage, flags); 613 loopv(damagetones) if(damagetones[i].merge(dt)) return; 614 damagetones.add(dt); 615 } 616 flushdamagetones()617 void flushdamagetones() 618 { 619 loopv(damagetones) damagetones[i].play(); 620 damagetones.setsizenodelete(0); 621 } 622 623 static int alarmchan = -1; hiteffect(int weap,int flags,int damage,gameent * d,gameent * actor,vec & dir,bool local)624 void hiteffect(int weap, int flags, int damage, gameent *d, gameent *actor, vec &dir, bool local) 625 { 626 bool burning = fireburn(d, weap, flags); 627 if(!local || burning) 628 { 629 if(hithurts(flags)) 630 { 631 if(d == player1) hud::damage(damage, actor->o, actor, weap); 632 if(d->type == ENT_PLAYER || d->type == ENT_AI) 633 { 634 vec p = d->headpos(); 635 p.z += 0.6f*(d->height + d->aboveeye) - d->height; 636 if(!isaitype(d->aitype) || aistyle[d->aitype].maxspeed) 637 { 638 if(!kidmode && bloodscale > 0) 639 part_splash(PART_BLOOD, int(clamp(damage/2, 2, 10)*bloodscale), bloodfade, p, 0x88FFFF, 1.5f, 1, 100, DECAL_BLOOD, int(d->radius*4)); 640 else part_splash(PART_HINT, int(clamp(damage/2, 2, 10)), bloodfade, p, 0xFFFF88, 1.5f, 1, 50, DECAL_STAIN, int(d->radius*4)); 641 } 642 if(showdamageabovehead > (d != player1 ? 0 : 1)) 643 { 644 string ds; 645 if(showdamageabovehead > 2) formatstring(ds)("<sub>-%d (%d%%)", damage, flags&HIT_HEAD ? 100 : (flags&HIT_TORSO ? 50 : 25)); 646 else formatstring(ds)("<sub>-%d", damage); 647 part_textcopy(d->abovehead(), ds, PART_TEXT, aboveheadfade, 0x888888, 3, 1, -10, 0, d); 648 } 649 if(d->aitype < AI_START && !issound(d->vschan)) playsound(S_PAIN1+rnd(5), d->o, d, 0, -1, -1, -1, &d->vschan); 650 if(!burning) d->quake = clamp(d->quake+max(damage/2, 1), 0, 1000); 651 d->lastpain = lastmillis; 652 } 653 if(d != actor) 654 { 655 bool sameteam = m_team(gamemode, mutators) && d->team == actor->team; 656 if(sameteam) { if(actor == player1 && !burning && !issound(alarmchan)) playsound(S_ALARM, actor->o, actor, 0, -1, -1, -1, &alarmchan); } 657 else if(playdamagetones >= (actor == player1 ? 1 : (d == player1 ? 2 : 3))) mergedamagetone(actor, damage, burning ? damagetone::BURN : 0); 658 if(!burning && !sameteam) actor->lasthit = lastmillis; 659 } 660 } 661 if(isweap(weap) && !burning && (d == player1 || (d->ai && aistyle[d->aitype].maxspeed))) 662 { 663 float force = (float(damage)/float(weaptype[weap].damage[flags&HIT_ALT ? 1 : 0]))*(100.f/d->weight)*weaptype[weap].hitpush[flags&HIT_ALT ? 1 : 0]; 664 if(flags&HIT_WAVE || !hithurts(flags)) force *= wavepushscale; 665 else if(d->health <= 0) force *= deadpushscale; 666 else force *= hitpushscale; 667 vec push = dir; push.z += 0.125f; push.mul(force); 668 d->vel.add(push); 669 if(flags&HIT_WAVE || flags&HIT_EXPLODE || weap == WEAP_MELEE) d->lastpush = lastmillis; 670 } 671 } 672 } 673 damaged(int weap,int flags,int damage,int health,gameent * d,gameent * actor,int millis,vec & dir)674 void damaged(int weap, int flags, int damage, int health, gameent *d, gameent *actor, int millis, vec &dir) 675 { 676 if(d->state != CS_ALIVE || intermission) return; 677 if(hithurts(flags)) 678 { 679 d->dodamage(health); 680 if(actor->type == ENT_PLAYER || actor->type == ENT_AI) actor->totaldamage += damage; 681 ai::damaged(d, actor); 682 } 683 hiteffect(weap, flags, damage, d, actor, dir, actor == player1 || actor->ai); 684 } 685 killed(int weap,int flags,int damage,gameent * d,gameent * actor,int style)686 void killed(int weap, int flags, int damage, gameent *d, gameent *actor, int style) 687 { 688 if(d->type != ENT_PLAYER && d->type != ENT_AI) return; 689 bool burning = fireburn(d, weap, flags); 690 d->lastregen = 0; 691 d->lastpain = lastmillis; 692 d->state = CS_DEAD; 693 d->deaths++; 694 int anc = -1, dth = d->aitype >= AI_START || style&FRAG_OBLITERATE ? S_SPLOSH : S_DIE1+rnd(2); 695 if(d == player1) anc = !m_duke(gamemode, mutators) && !m_trial(gamemode) ? S_V_FRAGGED : -1; 696 else d->resetinterp(); 697 formatstring(d->obit)("%s ", colorname(d)); 698 if(d != actor && actor->lastattacker == d->clientnum) actor->lastattacker = -1; 699 d->lastattacker = actor->clientnum; 700 if(d == actor) 701 { 702 if(d->aitype == AI_TURRET) concatstring(d->obit, "was destroyed"); 703 else if(flags&HIT_DEATH) concatstring(d->obit, *obitdeath ? obitdeath : "died"); 704 else if(flags&HIT_WATER) concatstring(d->obit, *obitwater ? obitwater : "died"); 705 else if(flags&HIT_MELT) concatstring(d->obit, "melted into a ball of fire"); 706 else if(flags&HIT_SPAWN) concatstring(d->obit, "tried to spawn inside solid matter"); 707 else if(flags&HIT_LOST) concatstring(d->obit, "got very, very lost"); 708 else if(flags && isweap(weap) && !burning) 709 { 710 static const char *suicidenames[WEAP_MAX] = { 711 "punched themself", 712 "ate a bullet", 713 "discovered buckshot bounces", 714 "got caught in their own crossfire", 715 "spontaneously combusted", 716 "tried to make out with plasma", 717 "got a good shock", 718 "kicked it, kamikaze style", 719 "pulled off an insta-stunt", 720 "was gibbed" 721 }; 722 concatstring(d->obit, suicidenames[weap]); 723 } 724 else if(flags&HIT_BURN || burning) concatstring(d->obit, "burned up"); 725 else if(style&FRAG_OBLITERATE) concatstring(d->obit, "was obliterated"); 726 else concatstring(d->obit, "suicided"); 727 } 728 else 729 { 730 concatstring(d->obit, "was "); 731 if(d->aitype == AI_TURRET) concatstring(d->obit, "destroyed by"); 732 else 733 { 734 static const char *obitnames[4][WEAP_MAX] = { 735 { 736 "smacked down by", 737 "pierced by", 738 "sprayed with buckshot by", 739 "riddled with holes by", 740 "char-grilled by", 741 "plasmified by", 742 "laser shocked by", 743 "blown to pieces by", 744 "lasered by", 745 "gibbed" 746 }, 747 { 748 "smacked down by", 749 "pierced by", 750 "filled with lead by", 751 "spliced apart by", 752 "fireballed by", 753 "shown the light by", 754 "was given laser burn by", 755 "blown to pieces by", 756 "was given laser burn by", 757 "gibbed" 758 }, 759 { 760 "smacked down by", 761 "capped by", 762 "scrambled by", 763 "air conditioned courtesy of", 764 "char-grilled by", 765 "plasmafied by", 766 "expertly sniped by", 767 "blown to pieces by", 768 "expertly sniped by", 769 "gibbed" 770 }, 771 { 772 "knocked into next week by", 773 "skewered by", 774 "turned into little chunks by", 775 "swiss-cheesed by", 776 "barbequed by chef", 777 "reduced to ooze by", 778 "given laser shock treatment by", 779 "obliterated by", 780 "lasered in half by", 781 "gibbed" 782 } 783 }; 784 785 int o = style&FRAG_OBLITERATE ? 3 : (style&FRAG_HEADSHOT ? 2 : (flags&HIT_ALT ? 1 : 0)); 786 concatstring(d->obit, burning ? "set ablaze by" : (isweap(weap) ? obitnames[o][weap] : "killed by")); 787 } 788 bool override = false; 789 vec az = actor->abovehead(), dz = d->abovehead(); 790 if(!m_fight(gamemode) || actor->aitype >= AI_START) 791 { 792 concatstring(d->obit, " "); 793 concatstring(d->obit, colorname(actor)); 794 } 795 else if(m_team(gamemode, mutators) && d->team == actor->team) 796 { 797 concatstring(d->obit, " \fs\fzawteam-mate\fS "); 798 concatstring(d->obit, colorname(actor)); 799 if(actor == player1) { anc = S_ALARM; override = true; } 800 } 801 else 802 { 803 if(style&FRAG_REVENGE) 804 { 805 concatstring(d->obit, " \fs\fzoyvengeful\fS"); 806 part_text(az, "<super>\fzoyAVENGED", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4; 807 part_text(dz, "<super>\fzoyREVENGE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, d); dz.z += 4; 808 if(actor == player1) d->dominated = false; 809 else if(d == player1) actor->dominating = false; 810 anc = S_V_REVENGE; override = true; 811 } 812 else if(style&FRAG_DOMINATE) 813 { 814 concatstring(d->obit, " \fs\fzoydominating\fS"); 815 part_text(az, "<super>\fzoyDOMINATING", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4; 816 part_text(dz, "<super>\fzoyDOMINATED", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, d); dz.z += 4; 817 if(actor == player1) d->dominating = true; 818 else if(d == player1) actor->dominated = true; 819 anc = S_V_DOMINATE; override = true; 820 } 821 concatstring(d->obit, " "); 822 concatstring(d->obit, colorname(actor)); 823 824 if(style&FRAG_MKILL1) 825 { 826 concatstring(d->obit, " \fs\fzRedouble-killing\fS"); 827 part_text(az, "<super>\fzvrDOUBLE-KILL", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4; 828 if(actor == player1) { part_text(dz, "<super>\fzvrDOUBLE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, d); dz.z += 4; } 829 if(!override) anc = S_V_MKILL1; 830 } 831 else if(style&FRAG_MKILL2) 832 { 833 concatstring(d->obit, " \fs\fzRetriple-killing\fS"); 834 part_text(az, "<super>\fzvrTRIPLE-KILL", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4; 835 if(actor == player1) { part_text(dz, "<super>\fzvrTRIPLE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, d); dz.z += 4; } 836 if(!override) anc = S_V_MKILL1; 837 } 838 else if(style&FRAG_MKILL3) 839 { 840 concatstring(d->obit, " \fs\fzRemulti-killing\fS"); 841 part_text(az, "<super>\fzvrMULTI-KILL", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4; 842 if(actor == player1) { part_text(dz, "<super>\fzvrMULTI", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, d); dz.z += 4; } 843 if(!override) anc = S_V_MKILL1; 844 } 845 } 846 847 if(style&FRAG_HEADSHOT) 848 { 849 part_text(az, "<super>\fzcwHEADSHOT", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4; 850 if(!override) anc = S_V_HEADSHOT; 851 } 852 853 if(style&FRAG_SPREE1) 854 { 855 concatstring(d->obit, " in total \fs\fzcgcarnage\fS"); 856 part_text(az, "<super>\fzcgCARNAGE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4; 857 if(!override) anc = S_V_SPREE1; 858 override = true; 859 } 860 else if(style&FRAG_SPREE2) 861 { 862 concatstring(d->obit, " on a \fs\fzcgslaughter\fS"); 863 part_text(az, "<super>\fzcgSLAUGHTER", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4; 864 if(!override) anc = S_V_SPREE2; 865 override = true; 866 } 867 else if(style&FRAG_SPREE3) 868 { 869 concatstring(d->obit, " on a \fs\fzcgmassacre\fS"); 870 part_text(az, "<super>\fzcgMASSACRE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4; 871 if(!override) anc = S_V_SPREE3; 872 override = true; 873 } 874 else if(style&FRAG_SPREE4) 875 { 876 concatstring(d->obit, " in a \fs\fzcgbloodbath\fS"); 877 part_text(az, "<super>\fzcgBLOODBATH", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4; 878 if(!override) anc = S_V_SPREE4; 879 override = true; 880 } 881 else if(style&FRAG_SPREE5) 882 { 883 concatstring(d->obit," on a \fs\fzcgrampage\fS"); 884 part_text(az, "<super>\fzcgRAMPAGE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4; 885 if(!override) anc = S_V_SPREE5; 886 override = true; 887 } 888 else if(style&FRAG_SPREE6) 889 { 890 concatstring(d->obit, " who seems \fs\fzcgunstoppable\fS"); 891 part_text(az, "<super>\fzcgUNSTOPPABLE", PART_TEXT, aboveheadfade, 0xFFFFFF, 4, 1, -10, 0, actor); az.z += 4; 892 if(!override) anc = S_V_SPREE6; 893 override = true; 894 } 895 } 896 if(d != actor) 897 { 898 if(actor->state == CS_ALIVE) copystring(actor->obit, d->obit); 899 actor->lastkill = lastmillis; 900 } 901 if(dth >= 0) 902 { 903 if(issound(d->vschan)) removesound(d->vschan); 904 playsound(dth, d->o, d, 0, -1, -1, -1, &d->vschan); 905 } 906 if(showobituaries && (d->aitype < AI_START || actor->aitype < (d->aitype >= AI_START ? AI_BOT : AI_START))) 907 { 908 bool isme = (d == player1 || actor == player1), show = false; 909 if(((!m_fight(gamemode) && !isme) || actor->aitype >= AI_START) && anc >= 0) anc = -1; 910 if(flags&HIT_LOST) show = true; 911 else switch(showobituaries) 912 { 913 case 1: if(isme || m_duke(gamemode, mutators)) show = true; break; 914 case 2: if(isme || anc >= 0 || m_duke(gamemode, mutators)) show = true; break; 915 case 3: if(isme || d->aitype < 0 || anc >= 0 || m_duke(gamemode, mutators)) show = true; break; 916 case 4: if(isme || d->aitype < 0 || actor->aitype < 0 || anc >= 0 || m_duke(gamemode, mutators)) show = true; break; 917 case 5: default: show = true; break; 918 } 919 int target = show ? (isme ? CON_SELF : CON_INFO) : -1; 920 if(showobitdists) announce(anc, target, d, "\fs\fw%s\fS (@\fs\fc%.2f\fSm)", d->obit, actor->o.dist(d->o)/8.f); 921 else announce(anc, target, d, "\fw%s", d->obit); 922 } 923 if(gibscale > 0) 924 { 925 vec pos = vec(d->o).sub(vec(0, 0, d->height*0.5f)); 926 int gibs = clamp(max(damage,5)/5, 1, 10), amt = int((rnd(gibs)+gibs+1)*gibscale); 927 loopi(amt) 928 projs::create(pos, vec(pos).add(d->vel), true, d, !isaitype(d->aitype) || aistyle[d->aitype].maxspeed ? PRJ_GIBS : PRJ_DEBRIS, (gibfade ? rnd(gibfade)+(gibfade/10) : 1000), 0, rnd(500)+1, 50); 929 } 930 if(m_team(gamemode, mutators) && d->team == actor->team && d != actor && actor == player1) 931 { 932 hud::teamkills.add(lastmillis); 933 if(hud::numteamkills() >= hud::teamkillnum) hud::lastteam = lastmillis; 934 } 935 ai::killed(d, actor); 936 } 937 timeupdate(int timeremain)938 void timeupdate(int timeremain) 939 { 940 minremain = timeremain; 941 if(!timeremain && !intermission) 942 { 943 player1->stopmoving(true); 944 hud::sb.showscores(true, true); 945 intermission = true; 946 smartmusic(true, false); 947 } 948 } 949 newclient(int cn)950 gameent *newclient(int cn) 951 { 952 if(cn < 0 || cn >= MAXPLAYERS) 953 { 954 defformatstring(cnmsg)("clientnum [%d]", cn); 955 neterr(cnmsg); 956 return NULL; 957 } 958 959 if(cn == player1->clientnum) return player1; 960 961 while(cn >= players.length()) players.add(NULL); 962 963 if(!players[cn]) 964 { 965 gameent *d = new gameent(); 966 d->clientnum = cn; 967 players[cn] = d; 968 } 969 970 return players[cn]; 971 } 972 getclient(int cn)973 gameent *getclient(int cn) 974 { 975 if(cn == player1->clientnum) return player1; 976 if(players.inrange(cn)) return players[cn]; 977 return NULL; 978 } 979 clientdisconnected(int cn)980 void clientdisconnected(int cn) 981 { 982 if(!players.inrange(cn)) return; 983 gameent *d = players[cn]; 984 if(!d) return; 985 if(d->name[0] && showplayerinfo && (d->aitype < 0 || ai::showaiinfo)) 986 conoutft(showplayerinfo > 1 ? int(CON_EVENT) : int(CON_MESG), "\fo%s left the game", colorname(d)); 987 loopv(client::mapvotes) if(client::mapvotes[i].player == d) { client::mapvotes.remove(i); break; } 988 projs::remove(d); 989 removedamagetones(d); 990 if(m_ctf(gamemode)) ctf::removeplayer(d); 991 if(m_stf(gamemode)) stf::removeplayer(d); 992 DELETEP(players[cn]); 993 players[cn] = NULL; 994 cleardynentcache(); 995 } 996 preload()997 void preload() 998 { 999 maskpackagedirs(~PACKAGEDIR_OCTA); 1000 int n = m_fight(gamemode) && m_team(gamemode, mutators) ? numteams(gamemode, mutators)+1 : 1; 1001 loopi(n) 1002 { 1003 loadmodel(teamtype[i].tpmdl, -1, true); 1004 loadmodel(teamtype[i].fpmdl, -1, true); 1005 } 1006 weapons::preload(); 1007 projs::preload(); 1008 entities::preload(); 1009 if(m_edit(gamemode) || m_stf(gamemode)) stf::preload(); 1010 if(m_edit(gamemode) || m_ctf(gamemode)) ctf::preload(); 1011 maskpackagedirs(~0); 1012 } 1013 resetmap(bool empty)1014 void resetmap(bool empty) // called just before a map load 1015 { 1016 if(!empty) smartmusic(true, false); 1017 } 1018 startmap(const char * name,const char * reqname,bool empty)1019 void startmap(const char *name, const char *reqname, bool empty) // called just after a map load 1020 { 1021 intermission = false; 1022 maptime = 0; 1023 projs::reset(); 1024 resetworld(); 1025 if(*name) 1026 { 1027 conoutft(CON_MESG, "\fs\fw%s by %s [\fa%s\fS]", *maptitle ? maptitle : "Untitled", *mapauthor ? mapauthor : "Unknown", server::gamename(gamemode, mutators)); 1028 preload(); 1029 } 1030 // reset perma-state 1031 gameent *d; 1032 loopi(numdynents()) if((d = (gameent *)iterdynents(i)) && (d->type == ENT_PLAYER || d->type == ENT_AI)) 1033 d->mapchange(lastmillis, m_health(gamemode, mutators)); 1034 entities::spawnplayer(player1, -1, false); // prevent the player from being in the middle of nowhere 1035 resetcamera(); 1036 if(!empty) client::sendinfo = client::sendcrc = true; 1037 fogdist = max(getvar("fog")-16, 64); 1038 copystring(clientmap, reqname ? reqname : (name ? name : "")); 1039 resetsway(); 1040 } 1041 intersectclosest(vec & from,vec & to,gameent * at)1042 gameent *intersectclosest(vec &from, vec &to, gameent *at) 1043 { 1044 gameent *best = NULL, *o; 1045 float bestdist = 1e16f; 1046 loopi(numdynents()) if((o = (gameent *)iterdynents(i))) 1047 { 1048 if(!o || o==at || o->state!=CS_ALIVE || !physics::issolid(o)) continue; 1049 float dist; 1050 if(intersect(o, from, to, dist) && dist < bestdist) 1051 { 1052 best = o; 1053 bestdist = dist; 1054 } 1055 } 1056 return best; 1057 } 1058 numdynents()1059 int numdynents() { return 1+players.length(); } iterdynents(int i)1060 dynent *iterdynents(int i) 1061 { 1062 if(!i) return player1; 1063 i--; 1064 if(i<players.length()) return players[i]; 1065 i -= players.length(); 1066 return NULL; 1067 } 1068 duplicatename(gameent * d,char * name=NULL)1069 bool duplicatename(gameent *d, char *name = NULL) 1070 { 1071 if(!name) name = d->name; 1072 if(d!=player1 && !strcmp(name, player1->name)) return true; 1073 loopv(players) if(players[i] && d!=players[i] && !strcmp(name, players[i]->name)) return true; 1074 return false; 1075 } 1076 colorname(gameent * d,char * name,const char * prefix,bool team,bool dupname)1077 char *colorname(gameent *d, char *name, const char *prefix, bool team, bool dupname) 1078 { 1079 if(!name) name = d->name; 1080 static string cname; 1081 formatstring(cname)("%s\fs%s%s", *prefix ? prefix : "", teamtype[d->team].chat, name); 1082 if(!name[0] || d->aitype >= 0 || (dupname && duplicatename(d, name))) 1083 { 1084 defformatstring(s)(" [\fs\fc%s%d\fS]", d->aitype >= 0 ? "\fe" : "", d->clientnum); 1085 concatstring(cname, s); 1086 } 1087 concatstring(cname, "\fS"); 1088 return cname; 1089 } 1090 suicide(gameent * d,int flags)1091 void suicide(gameent *d, int flags) 1092 { 1093 if((d == player1 || d->ai) && d->state == CS_ALIVE && d->suicided < 0) 1094 { 1095 fireburn(d, -1, flags); 1096 client::addmsg(SV_SUICIDE, "ri2", d->clientnum, flags); 1097 d->suicided = lastmillis; 1098 } 1099 } 1100 ICOMMAND(kill, "", (), { suicide(player1, 0); }); 1101 lighteffects(dynent * e,vec & color,vec & dir)1102 void lighteffects(dynent *e, vec &color, vec &dir) { } 1103 particletrack(particle * p,uint type,int & ts,bool lastpass)1104 void particletrack(particle *p, uint type, int &ts, bool lastpass) 1105 { 1106 if(!p || !p->owner || (p->owner->type != ENT_PLAYER && p->owner->type != ENT_AI)) return; 1107 gameent *d = (gameent *)p->owner; 1108 switch(type&0xFF) 1109 { 1110 case PT_TEXT: case PT_ICON: 1111 { 1112 vec q = p->owner->abovehead(); q.z = p->o.z; 1113 float k = pow(aboveheadsmooth, float(curtime)/float(aboveheadsmoothmillis)); 1114 p->o.mul(k).add(q.mul(1-k)); 1115 break; 1116 } 1117 case PT_TAPE: case PT_LIGHTNING: 1118 { 1119 float dist = p->o.dist(p->d); 1120 p->d = p->o = d->muzzlepos(d->weapselect); 1121 vec dir; vecfromyawpitch(d->yaw, d->pitch, 1, 0, dir); 1122 p->d.add(dir.mul(dist)); 1123 break; 1124 } 1125 case PT_PART: case PT_FIREBALL: case PT_FLARE: 1126 { 1127 p->o = d->muzzlepos(d->weapselect); 1128 break; 1129 } 1130 default: break; 1131 } 1132 } 1133 dynlighttrack(physent * owner,vec & o)1134 void dynlighttrack(physent *owner, vec &o) { } 1135 newmap(int size)1136 void newmap(int size) { client::addmsg(SV_NEWMAP, "ri", size); } 1137 loadworld(stream * f,int maptype)1138 void loadworld(stream *f, int maptype) { } saveworld(stream * f)1139 void saveworld(stream *f) { } 1140 fixfullrange(float & yaw,float & pitch,float & roll,bool full)1141 void fixfullrange(float &yaw, float &pitch, float &roll, bool full) 1142 { 1143 if(full) 1144 { 1145 while(pitch < -180.0f) pitch += 360.0f; 1146 while(pitch >= 180.0f) pitch -= 360.0f; 1147 while(roll < -180.0f) roll += 360.0f; 1148 while(roll >= 180.0f) roll -= 360.0f; 1149 } 1150 else 1151 { 1152 if(pitch > 89.9f) pitch = 89.9f; 1153 if(pitch < -89.9f) pitch = -89.9f; 1154 if(roll > 89.9f) roll = 89.9f; 1155 if(roll < -89.9f) roll = -89.9f; 1156 } 1157 while(yaw < 0.0f) yaw += 360.0f; 1158 while(yaw >= 360.0f) yaw -= 360.0f; 1159 } 1160 fixrange(float & yaw,float & pitch)1161 void fixrange(float &yaw, float &pitch) 1162 { 1163 float r = 0.f; 1164 fixfullrange(yaw, pitch, r, false); 1165 } 1166 fixview(int w,int h)1167 void fixview(int w, int h) 1168 { 1169 if(inzoom()) 1170 { 1171 int frame = lastmillis-lastzoom, f = zoomfov, t = zoomtime; 1172 checkzoom(); 1173 if(zoomlevels > 1 && zoomlevel < zoomlevels) f = fov()-(((fov()-zoomfov)/zoomlevels)*zoomlevel); 1174 float diff = float(fov()-f), amt = frame < t ? clamp(float(frame)/float(t), 0.f, 1.f) : 1.f; 1175 if(!zooming) amt = 1.f-amt; 1176 curfov = fov()-(amt*diff); 1177 } 1178 else curfov = float(fov()); 1179 } 1180 mousemove(int dx,int dy,int x,int y,int w,int h)1181 bool mousemove(int dx, int dy, int x, int y, int w, int h) 1182 { 1183 bool hascursor = UI::hascursor(true); 1184 #define mousesens(a,b,c) ((float(a)/float(b))*c) 1185 if(hascursor || (mousestyle() >= 1 && player1->state != CS_WAITING && player1->state != CS_SPECTATOR)) 1186 { 1187 if(mouseabsolute) // absolute positions, unaccelerated 1188 { 1189 cursorx = clamp(float(x)/float(w), 0.f, 1.f); 1190 cursory = clamp(float(y)/float(h), 0.f, 1.f); 1191 return false; 1192 } 1193 else 1194 { 1195 cursorx = clamp(cursorx+mousesens(dx, w, mousesensitivity), 0.f, 1.f); 1196 cursory = clamp(cursory+mousesens(dy, h, mousesensitivity*(!hascursor && mouseinvert ? -1.f : 1.f)), 0.f, 1.f); 1197 return true; 1198 } 1199 } 1200 else if(!tvmode()) 1201 { 1202 physent *target = player1->state == CS_WAITING || player1->state == CS_SPECTATOR ? camera1 : (allowmove(player1) ? player1 : NULL); 1203 if(target) 1204 { 1205 float scale = (inzoom() && zoomsensitivity > 0 && zoomsensitivity < 1 ? 1.f-(zoomlevel/float(zoomlevels+1)*zoomsensitivity) : 1.f)*sensitivity; 1206 target->yaw += mousesens(dx, w, yawsensitivity*scale); 1207 target->pitch -= mousesens(dy, h, pitchsensitivity*scale*(!hascursor && mouseinvert ? -1.f : 1.f)); 1208 fixfullrange(target->yaw, target->pitch, target->roll, false); 1209 } 1210 return true; 1211 } 1212 return false; 1213 } 1214 project(int w,int h)1215 void project(int w, int h) 1216 { 1217 int style = UI::hascursor() ? -1 : mousestyle(); 1218 if(style != lastmousetype) 1219 { 1220 resetcursor(); 1221 lastmousetype = style; 1222 } 1223 if(style >= 0) vecfromcursor(cursorx, cursory, 1.f, cursordir); 1224 } 1225 1226 struct camstate 1227 { 1228 int ent, idx; 1229 vec pos, dir; 1230 vector<int> cansee; 1231 float mindist, maxdist, score; 1232 bool alter; 1233 camstategame::camstate1234 camstate() : idx(-1), mindist(32.f), maxdist(512.f), alter(false) { reset(); } ~camstategame::camstate1235 ~camstate() {} 1236 resetgame::camstate1237 void reset() 1238 { 1239 cansee.setsize(0); 1240 dir = vec(0, 0, 0); 1241 score = 0.f; 1242 alter = false; 1243 } 1244 camsortgame::camstate1245 static int camsort(const camstate *a, const camstate *b) 1246 { 1247 int asee = a->cansee.length(), bsee = b->cansee.length(); 1248 if(a->alter && asee) asee = 1; 1249 if(b->alter && bsee) bsee = 1; 1250 if(asee > bsee) return -1; 1251 if(asee < bsee) return 1; 1252 if(a->score < b->score) return -1; 1253 if(a->score > b->score) return 1; 1254 return 0; 1255 } 1256 }; 1257 vector<camstate> cameras; 1258 getyawpitch(const vec & from,const vec & pos,float & yaw,float & pitch)1259 void getyawpitch(const vec &from, const vec &pos, float &yaw, float &pitch) 1260 { 1261 float dist = from.dist(pos); 1262 yaw = -(float)atan2(pos.x-from.x, pos.y-from.y)/PI*180+180; 1263 pitch = asin((pos.z-from.z)/dist)/RAD; 1264 } 1265 scaleyawpitch(float & yaw,float & pitch,float targyaw,float targpitch,float frame,float scale)1266 void scaleyawpitch(float &yaw, float &pitch, float targyaw, float targpitch, float frame, float scale) 1267 { 1268 if(yaw < targyaw-180.0f) yaw += 360.0f; 1269 if(yaw > targyaw+180.0f) yaw -= 360.0f; 1270 float offyaw = fabs(targyaw-yaw)*frame, offpitch = fabs(targpitch-pitch)*frame*scale; 1271 if(targyaw > yaw) 1272 { 1273 yaw += offyaw; 1274 if(targyaw < yaw) yaw = targyaw; 1275 } 1276 else if(targyaw < yaw) 1277 { 1278 yaw -= offyaw; 1279 if(targyaw > yaw) yaw = targyaw; 1280 } 1281 if(targpitch > pitch) 1282 { 1283 pitch += offpitch; 1284 if(targpitch < pitch) pitch = targpitch; 1285 } 1286 else if(targpitch < pitch) 1287 { 1288 pitch -= offpitch; 1289 if(targpitch > pitch) pitch = targpitch; 1290 } 1291 fixrange(yaw, pitch); 1292 } 1293 cameraplayer()1294 void cameraplayer() 1295 { 1296 if(player1->state != CS_WAITING && player1->state != CS_SPECTATOR && player1->state != CS_DEAD && !tvmode()) 1297 { 1298 player1->aimyaw = camera1->yaw; 1299 player1->aimpitch = camera1->pitch; 1300 fixrange(player1->aimyaw, player1->aimpitch); 1301 if(lastcamera && mousestyle() >= 1 && !UI::hascursor()) 1302 { 1303 physent *d = mousestyle() != 2 ? player1 : camera1; 1304 float amt = clamp(float(lastmillis-lastcamera)/100.f, 0.f, 1.f)*panspeed(); 1305 float zone = float(deadzone())/200.f, cx = cursorx-0.5f, cy = 0.5f-cursory; 1306 if(cx > zone || cx < -zone) d->yaw += ((cx > zone ? cx-zone : cx+zone)/(1.f-zone))*amt; 1307 if(cy > zone || cy < -zone) d->pitch += ((cy > zone ? cy-zone : cy+zone)/(1.f-zone))*amt; 1308 fixfullrange(d->yaw, d->pitch, d->roll, false); 1309 } 1310 } 1311 } 1312 cameratv()1313 void cameratv() 1314 { 1315 bool isspec = player1->state == CS_SPECTATOR; 1316 if(cameras.empty()) loopk(2) 1317 { 1318 physent d = *player1; 1319 d.radius = d.height = 4.f; 1320 d.state = CS_ALIVE; 1321 loopv(entities::ents) if(entities::ents[i]->type == CAMERA || (k && !enttype[entities::ents[i]->type].noisy)) 1322 { 1323 gameentity &e = *(gameentity *)entities::ents[i]; 1324 vec pos(e.o); 1325 if(e.type == MAPMODEL) 1326 { 1327 mapmodelinfo &mmi = getmminfo(e.attrs[0]); 1328 vec center, radius; 1329 mmi.m->collisionbox(0, center, radius); 1330 if(!mmi.m->ellipsecollide) rotatebb(center, radius, int(e.attrs[1])); 1331 pos.z += ((center.z-radius.z)+radius.z*2*mmi.m->height)*3.f; 1332 } 1333 else if(enttype[e.type].radius) pos.z += enttype[e.type].radius; 1334 d.o = pos; 1335 if(physics::entinmap(&d, false)) 1336 { 1337 camstate &c = cameras.add(); 1338 c.pos = pos; 1339 c.ent = i; 1340 if(!k) 1341 { 1342 c.idx = e.attrs[0]; 1343 if(e.attrs[1]) c.mindist = e.attrs[1]; 1344 if(e.attrs[2]) c.maxdist = e.attrs[2]; 1345 } 1346 } 1347 } 1348 lasttvcam = lasttvchg = 0; 1349 if(!cameras.empty()) break; 1350 } 1351 #define unsettvmode(q) \ 1352 { \ 1353 if(q) \ 1354 { \ 1355 camera1->o.x = camera1->o.y = camera1->o.z = getworldsize(); \ 1356 camera1->o.x *= 0.5f; camera1->o.y *= 0.5f; \ 1357 } \ 1358 camera1->resetinterp(); \ 1359 setvar(isspec ? "specmode" : "waitmode", 0, true); \ 1360 return; \ 1361 } 1362 1363 if(!cameras.empty()) 1364 { 1365 camstate *cam = &cameras[0]; 1366 int entidx = cam->ent, len = (isspec ? spectvtime : waittvtime); 1367 bool alter = cam->alter, renew = !lasttvcam || lastmillis-lasttvcam >= len, 1368 override = renew || !lasttvcam || lastmillis-lasttvcam >= max(len/10, 1500); 1369 #define addcamentity(q,p) \ 1370 { \ 1371 vec trg, pos = p; \ 1372 float dist = c.pos.dist(pos); \ 1373 if(dist >= c.mindist && dist <= min(c.maxdist, float(fogdist)) && raycubelos(c.pos, pos, trg)) \ 1374 { \ 1375 c.cansee.add(q); \ 1376 avg.add(pos); \ 1377 } \ 1378 } 1379 #define updatecamorient \ 1380 { \ 1381 if((k || j) && c.cansee.length()) \ 1382 { \ 1383 vec dir = vec(avg).div(c.cansee.length()).sub(c.pos).normalize(); \ 1384 vectoyawpitch(dir, yaw, pitch); \ 1385 } \ 1386 } 1387 #define dircamentity(q,p) \ 1388 { \ 1389 vec trg, pos = p; \ 1390 if(getsight(c.pos, yaw, pitch, pos, trg, min(c.maxdist, float(fogdist)), curfov, fovy)) \ 1391 { \ 1392 c.dir.add(pos); \ 1393 c.score += c.alter ? rnd(32) : c.pos.dist(pos); \ 1394 } \ 1395 else \ 1396 { \ 1397 avg.sub(pos); \ 1398 c.cansee.remove(q); \ 1399 updatecamorient; \ 1400 } \ 1401 } 1402 loopk(2) 1403 { 1404 int found = 0; 1405 loopvj(cameras) 1406 { 1407 camstate &c = cameras[j]; 1408 vec avg(0, 0, 0); 1409 c.reset(); 1410 switch(k) 1411 { 1412 case 0: default: 1413 { 1414 gameent *d; 1415 loopi(numdynents()) if((d = (gameent *)iterdynents(i)) && d->aitype < AI_START && (d->state == CS_ALIVE || d->state == CS_DEAD || d->state == CS_WAITING)) 1416 addcamentity(i, d->feetpos()); 1417 break; 1418 } 1419 case 1: 1420 { 1421 c.alter = true; 1422 loopv(entities::ents) if(entities::ents[i]->type == WEAPON || entities::ents[i]->type == FLAG) 1423 addcamentity(i, entities::ents[i]->o); 1424 break; 1425 } 1426 } 1427 float yaw = camera1->yaw, pitch = camera1->pitch; 1428 updatecamorient; 1429 switch(k) 1430 { 1431 case 0: default: 1432 { 1433 gameent *d; 1434 loopvrev(c.cansee) if((d = (gameent *)iterdynents(c.cansee[i]))) 1435 dircamentity(i, d->feetpos()); 1436 break; 1437 } 1438 case 1: 1439 { 1440 loopvrev(c.cansee) if(entities::ents.inrange(c.cansee[i])) 1441 dircamentity(i, entities::ents[c.cansee[i]]->o); 1442 break; 1443 } 1444 } 1445 if(!c.cansee.empty()) 1446 { 1447 float amt = float(c.cansee.length()); 1448 c.dir.div(amt); 1449 c.score /= amt; 1450 found++; 1451 } 1452 else 1453 { 1454 c.score = 0; 1455 if(override && !k && !j && !alter) renew = true; // quick scotty, get a new cam 1456 } 1457 if(!renew || !override) break; 1458 } 1459 if(override && !found && (k || !alter)) 1460 { 1461 if(!k) renew = true; 1462 else unsettvmode(lasttvcam ? false : true); 1463 } 1464 else break; 1465 } 1466 if(renew) 1467 { 1468 cameras.sort(camstate::camsort); 1469 cam = &cameras[0]; 1470 lasttvcam = lastmillis; 1471 if(!lasttvchg || cam->ent != entidx) lasttvchg = lastmillis; 1472 } 1473 else if(alter && !cam->cansee.length()) cam->alter = true; 1474 camera1->o = cam->pos; 1475 if(cam->ent != entidx || !cam->alter) 1476 { 1477 vec dir = vec(cam->dir).sub(camera1->o).normalize(); 1478 vectoyawpitch(dir, camera1->aimyaw, camera1->aimpitch); 1479 } 1480 if(cam->ent != entidx || cam->alter) { camera1->yaw = camera1->aimyaw; camera1->pitch = camera1->aimpitch; } 1481 else 1482 { 1483 float speed = isspec ? spectvspeed : waittvspeed, scale = isspec ? spectvpitch : waittvpitch; 1484 if(speed > 0) scaleyawpitch(camera1->yaw, camera1->pitch, camera1->aimyaw, camera1->aimpitch, (float(curtime)/1000.f)*speed, scale); 1485 } 1486 camera1->resetinterp(); 1487 } 1488 else unsettvmode(true); 1489 } 1490 updateworld()1491 void updateworld() // main game update loop 1492 { 1493 if(connected()) 1494 { 1495 if(!maptime) { maptime = -1; return; } // skip the first loop 1496 else if(maptime < 0) 1497 { 1498 maptime = lastmillis; 1499 //if(m_lobby(gamemode)) smartmusic(true, false); 1500 //else 1501 if(*mapmusic && (!music || !Mix_PlayingMusic() || strcmp(mapmusic, musicfile))) playmusic(mapmusic, ""); 1502 else musicdone(false); 1503 RUNWORLD("on_start"); 1504 return; 1505 } 1506 } 1507 if(!curtime) { gets2c(); if(player1->clientnum >= 0) client::c2sinfo(); return; } 1508 1509 if(!*player1->name && !menuactive()) showgui("name"); 1510 if(connected()) 1511 { 1512 player1->conopen = commandmillis > 0 || UI::hascursor(true); 1513 // do shooting/projectile update here before network update for greater accuracy with what the player sees 1514 if(allowmove(player1)) cameraplayer(); 1515 else player1->stopmoving(player1->state != CS_WAITING && player1->state != CS_SPECTATOR); 1516 1517 gameent *d = NULL; 1518 loopi(numdynents()) if((d = (gameent *)iterdynents(i)) != NULL && (d->type == ENT_PLAYER || d->type == ENT_AI)) 1519 { 1520 checkoften(d, d == player1 || d->ai); 1521 if(d == player1) 1522 { 1523 int state = d->weapstate[d->weapselect]; 1524 if(weaptype[d->weapselect].zooms) 1525 { 1526 if(state == WEAP_S_SHOOT || (state == WEAP_S_RELOAD && lastmillis-d->weaplast[d->weapselect] >= max(d->weapwait[d->weapselect]-zoomtime, 1))) 1527 state = WEAP_S_IDLE; 1528 } 1529 if(zooming && (!weaptype[d->weapselect].zooms || state != WEAP_S_IDLE)) zoomset(false, lastmillis); 1530 else if(weaptype[d->weapselect].zooms && state == WEAP_S_IDLE && zooming != d->action[AC_ALTERNATE]) 1531 zoomset(d->action[AC_ALTERNATE], lastmillis); 1532 } 1533 } 1534 1535 physics::update(); 1536 projs::update(); 1537 ai::update(); 1538 if(!intermission) 1539 { 1540 entities::update(); 1541 if(player1->state == CS_ALIVE) weapons::shoot(player1, worldpos); 1542 } 1543 otherplayers(); 1544 } 1545 else if(!menuactive()) showgui("main"); 1546 1547 gets2c(); 1548 adjustscaled(int, hud::damageresidue, hud::damageresiduefade); 1549 if(connected()) 1550 { 1551 flushdamagetones(); 1552 if(player1->state == CS_DEAD || player1->state == CS_WAITING) 1553 { 1554 if(player1->ragdoll) moveragdoll(player1, true); 1555 else if(lastmillis-player1->lastpain <= 2000) 1556 physics::move(player1, 10, false); 1557 } 1558 else 1559 { 1560 if(player1->ragdoll) cleanragdoll(player1); 1561 if(player1->state == CS_EDITING) physics::move(player1, 10, true); 1562 else if(!intermission && player1->state == CS_ALIVE) 1563 { 1564 physics::move(player1, 10, true); 1565 addsway(player1); 1566 entities::checkitems(player1); 1567 weapons::reload(player1); 1568 } 1569 } 1570 checkcamera(); 1571 if(player1->state == CS_DEAD) 1572 { 1573 gameent *a = deathcamstyle ? (deathcamstyle == 2 ? player1 : getclient(player1->lastattacker)) : NULL; 1574 if(a) 1575 { 1576 vec dir = vec(a->headpos(-a->height*0.5f)).sub(camera1->o).normalize(); 1577 float yaw = camera1->yaw, pitch = camera1->pitch; 1578 vectoyawpitch(dir, yaw, pitch); 1579 if(deathcamspeed > 0) scaleyawpitch(camera1->yaw, camera1->pitch, yaw, pitch, (float(curtime)/1000.f)*deathcamspeed, 4.f); 1580 camera1->aimyaw = camera1->yaw; 1581 camera1->aimpitch = camera1->pitch; 1582 } 1583 } 1584 else if(tvmode()) cameratv(); 1585 else if(player1->state == CS_WAITING || player1->state == CS_SPECTATOR) 1586 { 1587 camera1->move = player1->move; 1588 camera1->strafe = player1->strafe; 1589 physics::move(camera1, 10, true); 1590 } 1591 if(player1->state == CS_SPECTATOR) 1592 { 1593 player1->aimyaw = player1->yaw = camera1->yaw; 1594 player1->aimpitch = player1->pitch = camera1->pitch; 1595 player1->o = camera1->o; 1596 player1->resetinterp(); 1597 } 1598 if(hud::sb.canshowscores()) hud::sb.showscores(true); 1599 } 1600 1601 if(player1->clientnum >= 0) client::c2sinfo(); 1602 } 1603 recomputecamera(int w,int h)1604 void recomputecamera(int w, int h) 1605 { 1606 fixview(w, h); 1607 checkcamera(); 1608 if(client::ready()) 1609 { 1610 if(!lastcamera) 1611 { 1612 resetcursor(); 1613 cameras.setsize(0); 1614 if(mousestyle() == 2 && player1->state != CS_WAITING && player1->state != CS_SPECTATOR) 1615 { 1616 camera1->yaw = player1->aimyaw = player1->yaw; 1617 camera1->pitch = player1->aimpitch = player1->pitch; 1618 } 1619 } 1620 1621 if(player1->state == CS_DEAD || player1->state == CS_WAITING || player1->state == CS_SPECTATOR) 1622 { 1623 camera1->aimyaw = camera1->yaw; 1624 camera1->aimpitch = camera1->pitch; 1625 } 1626 else 1627 { 1628 camera1->o = player1->headpos(); 1629 if(mousestyle() <= 1) 1630 findorientation(camera1->o, player1->yaw, player1->pitch, worldpos); 1631 1632 camera1->aimyaw = mousestyle() <= 1 ? player1->yaw : player1->aimyaw; 1633 camera1->aimpitch = mousestyle() <= 1 ? player1->pitch : player1->aimpitch; 1634 if(thirdpersonview(true) && thirdpersondist) 1635 { 1636 vec dir; 1637 vecfromyawpitch(camera1->aimyaw, camera1->aimpitch, thirdpersondist > 0 ? -1 : 1, 0, dir); 1638 physics::movecamera(camera1, dir, fabs(thirdpersondist), 1.0f); 1639 } 1640 camera1->resetinterp(); 1641 1642 switch(mousestyle()) 1643 { 1644 case 0: 1645 case 1: 1646 { 1647 camera1->yaw = player1->yaw; 1648 camera1->pitch = player1->pitch; 1649 if(mousestyle()) 1650 { 1651 camera1->aimyaw = camera1->yaw; 1652 camera1->aimpitch = camera1->pitch; 1653 } 1654 break; 1655 } 1656 case 2: 1657 { 1658 float yaw, pitch; 1659 vectoyawpitch(cursordir, yaw, pitch); 1660 fixrange(yaw, pitch); 1661 findorientation(camera1->o, yaw, pitch, worldpos); 1662 if(allowmove(player1)) 1663 { 1664 player1->yaw = yaw; 1665 player1->pitch = pitch; 1666 } 1667 break; 1668 } 1669 } 1670 fixfullrange(camera1->yaw, camera1->pitch, camera1->roll, false); 1671 fixrange(camera1->aimyaw, camera1->aimpitch); 1672 } 1673 camera1->roll = player1->calcroll(physics::iscrouching(player1)); 1674 vecfromyawpitch(camera1->yaw, camera1->pitch, 1, 0, camdir); 1675 vecfromyawpitch(camera1->yaw, 0, 0, -1, camright); 1676 vecfromyawpitch(camera1->yaw, camera1->pitch+90, 1, 0, camup); 1677 1678 camera1->inmaterial = lookupmaterial(camera1->o); 1679 camera1->inliquid = isliquid(camera1->inmaterial&MATF_VOLUME); 1680 1681 switch(camera1->inmaterial) 1682 { 1683 case MAT_WATER: 1684 { 1685 if(!issound(liquidchan)) 1686 playsound(S_UNDERWATER, camera1->o, camera1, SND_LOOP|SND_NOATTEN|SND_NODELAY|SND_NOCULL, -1, -1, -1, &liquidchan); 1687 break; 1688 } 1689 default: 1690 { 1691 if(issound(liquidchan)) removesound(liquidchan); 1692 liquidchan = -1; 1693 break; 1694 } 1695 } 1696 1697 lastcamera = lastmillis; 1698 } 1699 } 1700 1701 VAR(animoverride, -1, 0, ANIM_MAX-1); 1702 VAR(testanims, 0, 0, 1); 1703 numanims()1704 int numanims() { return ANIM_MAX; } 1705 findanims(const char * pattern,vector<int> & anims)1706 void findanims(const char *pattern, vector<int> &anims) 1707 { 1708 loopi(sizeof(animnames)/sizeof(animnames[0])) 1709 if(*animnames[i] && matchanim(animnames[i], pattern)) 1710 anims.add(i); 1711 } 1712 renderclient(gameent * d,bool third,float trans,float size,int team,modelattach * attachments,bool secondary,int animflags,int animdelay,int lastaction,bool early)1713 void renderclient(gameent *d, bool third, float trans, float size, int team, modelattach *attachments, bool secondary, int animflags, int animdelay, int lastaction, bool early) 1714 { 1715 const char *mdl = ""; 1716 if(d->aitype <= AI_BOT) 1717 { 1718 if(third) mdl = teamtype[team].tpmdl; 1719 else mdl = teamtype[team].fpmdl; 1720 } 1721 else if(d->aitype < AI_MAX) mdl = aistyle[d->aitype].mdl; 1722 else return; 1723 1724 float yaw = d->yaw, pitch = d->pitch, roll = d->calcroll(physics::iscrouching(d)); 1725 vec o = vec(third ? d->feetpos() : d->headpos()); 1726 if(!third) 1727 { 1728 vec dir; 1729 if(firstpersonsway) 1730 { 1731 vecfromyawpitch(d->yaw, 0, 0, 1, dir); 1732 float steps = swaydist/firstpersonswaystep*M_PI; 1733 dir.mul(firstpersonswayside*cosf(steps)); 1734 dir.z = firstpersonswayup*(fabs(sinf(steps)) - 1); 1735 o.add(dir).add(swaydir).add(swaypush); 1736 } 1737 if(firstpersondist != 0.f) 1738 { 1739 vecfromyawpitch(yaw, pitch, 1, 0, dir); 1740 dir.mul(player1->radius*firstpersondist); 1741 o.add(dir); 1742 } 1743 if(firstpersonshift != 0.f) 1744 { 1745 vecfromyawpitch(yaw, pitch, 0, -1, dir); 1746 dir.mul(player1->radius*firstpersonshift); 1747 o.add(dir); 1748 } 1749 if(firstpersonadjust != 0.f) 1750 { 1751 vecfromyawpitch(yaw, pitch+90.f, 1, 0, dir); 1752 dir.mul(player1->height*firstpersonadjust); 1753 o.add(dir); 1754 } 1755 } 1756 1757 int anim = animflags, basetime = lastaction, basetime2 = 0; 1758 if(animoverride) 1759 { 1760 anim = (animoverride<0 ? ANIM_ALL : animoverride)|ANIM_LOOP; 1761 basetime = 0; 1762 } 1763 else 1764 { 1765 if(secondary && (d->aitype <= AI_BOT || aistyle[d->aitype].maxspeed)) 1766 { 1767 if(physics::liquidcheck(d) && d->physstate <= PHYS_FALL) 1768 anim |= (((allowmove(d) && (d->move || d->strafe)) || d->vel.z+d->falling.z>0 ? int(ANIM_SWIM) : int(ANIM_SINK))|ANIM_LOOP)<<ANIM_SECONDARY; 1769 else if(d->physstate == PHYS_FALL && !d->onladder && FWV(impulsestyle) && d->impulse[IM_TYPE] != IM_T_NONE && lastmillis-d->impulse[IM_TIME] <= 1000) { anim |= ANIM_IMPULSE_DASH<<ANIM_SECONDARY; basetime2 = d->impulse[IM_TIME]; } 1770 else if(d->physstate == PHYS_FALL && !d->onladder && d->actiontime[AC_JUMP] && lastmillis-d->actiontime[AC_JUMP] <= 1000) { anim |= ANIM_JUMP<<ANIM_SECONDARY; basetime2 = d->actiontime[AC_JUMP]; } 1771 else if(d->physstate == PHYS_FALL && !d->onladder && d->timeinair >= 1000) anim |= (ANIM_JUMP|ANIM_END)<<ANIM_SECONDARY; 1772 else if(FWV(impulsestyle) && d->action[AC_IMPULSE] && (d->move || d->strafe)) 1773 { 1774 if(d->move>0) anim |= (ANIM_IMPULSE_FORWARD|ANIM_LOOP)<<ANIM_SECONDARY; 1775 else if(d->strafe) anim |= ((d->strafe>0 ? ANIM_IMPULSE_LEFT : ANIM_IMPULSE_RIGHT)|ANIM_LOOP)<<ANIM_SECONDARY; 1776 else if(d->move<0) anim |= (ANIM_IMPULSE_BACKWARD|ANIM_LOOP)<<ANIM_SECONDARY; 1777 } 1778 else if(d->action[AC_CROUCH] || d->actiontime[AC_CROUCH]<0) 1779 { 1780 if(d->move>0) anim |= (ANIM_CRAWL_FORWARD|ANIM_LOOP)<<ANIM_SECONDARY; 1781 else if(d->strafe) anim |= ((d->strafe>0 ? ANIM_CRAWL_LEFT : ANIM_CRAWL_RIGHT)|ANIM_LOOP)<<ANIM_SECONDARY; 1782 else if(d->move<0) anim |= (ANIM_CRAWL_BACKWARD|ANIM_LOOP)<<ANIM_SECONDARY; 1783 else anim |= (ANIM_CROUCH|ANIM_LOOP)<<ANIM_SECONDARY; 1784 } 1785 else if(d->move>0) anim |= (ANIM_FORWARD|ANIM_LOOP)<<ANIM_SECONDARY; 1786 else if(d->strafe) anim |= ((d->strafe>0 ? ANIM_LEFT : ANIM_RIGHT)|ANIM_LOOP)<<ANIM_SECONDARY; 1787 else if(d->move<0) anim |= (ANIM_BACKWARD|ANIM_LOOP)<<ANIM_SECONDARY; 1788 } 1789 1790 if((anim>>ANIM_SECONDARY)&ANIM_INDEX) switch(anim&ANIM_INDEX) 1791 { 1792 case ANIM_IDLE: case ANIM_MELEE: case ANIM_PISTOL: case ANIM_SHOTGUN: case ANIM_SMG: 1793 case ANIM_GRENADE: case ANIM_FLAMER: case ANIM_PLASMA: case ANIM_RIFLE: 1794 { 1795 anim = (anim>>ANIM_SECONDARY) | ((anim&((1<<ANIM_SECONDARY)-1))<<ANIM_SECONDARY); 1796 swap(basetime, basetime2); 1797 break; 1798 } 1799 default: break; 1800 } 1801 } 1802 1803 if(third && testanims && d == player1) yaw = 0; else yaw += 90; 1804 if(anim == ANIM_DYING) pitch *= max(1.f-(lastmillis-basetime)/500.f, 0.f); 1805 1806 if(d->ragdoll && (!ragdolls || anim!=ANIM_DYING)) cleanragdoll(d); 1807 1808 if(!((anim>>ANIM_SECONDARY)&ANIM_INDEX)) anim |= (ANIM_IDLE|ANIM_LOOP)<<ANIM_SECONDARY; 1809 1810 int flags = MDL_LIGHT; 1811 #if 0 // breaks linkpos 1812 if(d != player1 && !(anim&ANIM_RAGDOLL)) flags |= MDL_CULL_VFC|MDL_CULL_OCCLUDED|MDL_CULL_QUERY; 1813 #endif 1814 if(d->type == ENT_PLAYER) 1815 { 1816 if(!early && third) flags |= MDL_FULLBRIGHT; 1817 } 1818 else flags |= MDL_CULL_DIST; 1819 if(early) flags |= MDL_NORENDER; 1820 else if(third && (anim&ANIM_INDEX)!=ANIM_DEAD) flags |= MDL_DYNSHADOW; 1821 dynent *e = third ? (dynent *)d : (dynent *)&fpsmodel; 1822 rendermodel(NULL, mdl, anim, o, yaw, pitch, roll, flags, e, attachments, basetime, basetime2, trans, size); 1823 } 1824 renderplayer(gameent * d,bool third,float trans,float size,bool early=false)1825 void renderplayer(gameent *d, bool third, float trans, float size, bool early = false) 1826 { 1827 if(d->state == CS_SPECTATOR) return; 1828 if(trans <= 0.f || (d == player1 && (third ? thirdpersonmodel : firstpersonmodel) < 1)) 1829 { 1830 if(d->state == CS_ALIVE && rendernormally && (early || d != player1)) 1831 trans = 1e-16f; // we need tag_muzzle/tag_waist 1832 else return; // screw it, don't render them 1833 } 1834 1835 modelattach a[8]; 1836 int ai = 0, team = m_fight(gamemode) && m_team(gamemode, mutators) ? d->team : TEAM_NEUTRAL, 1837 weap = d->weapselect, lastaction = 0, animflags = ANIM_IDLE|ANIM_LOOP, animdelay = 0; 1838 bool secondary = false, showweap = d->aitype <= AI_BOT ? isweap(weap) : aistyle[d->aitype].useweap; 1839 1840 if(d->state == CS_DEAD || d->state == CS_WAITING) 1841 { 1842 showweap = false; 1843 animflags = ANIM_DYING; 1844 lastaction = d->lastpain; 1845 if(ragdolls) 1846 { 1847 if(!validragdoll(d, lastaction)) animflags |= ANIM_RAGDOLL; 1848 } 1849 else 1850 { 1851 int t = lastmillis-lastaction; 1852 if(t < 0) return; 1853 if(t > 1000) animflags = ANIM_DEAD|ANIM_LOOP; 1854 } 1855 } 1856 else if(d->state == CS_EDITING) 1857 { 1858 animflags = ANIM_EDIT|ANIM_LOOP; 1859 showweap = false; 1860 } 1861 #if 0 1862 else if(intermission) 1863 { 1864 lastaction = lastmillis; 1865 animflags = ANIM_LOSE|ANIM_LOOP; 1866 animdelay = 1000; 1867 if(m_fight(gamemode) && m_team(gamemode, mutators)) 1868 { 1869 loopv(bestteams) if(bestteams[i] == d->team) 1870 { 1871 animflags = ANIM_WIN|ANIM_LOOP; 1872 break; 1873 } 1874 } 1875 else if(bestplayers.find(d) >= 0) animflags = ANIM_WIN|ANIM_LOOP; 1876 } 1877 #endif 1878 else if(third && lastmillis-d->lastpain <= 300) 1879 { 1880 secondary = third; 1881 lastaction = d->lastpain; 1882 animflags = ANIM_PAIN; 1883 animdelay = 300; 1884 } 1885 else 1886 { 1887 secondary = third; 1888 if(showweap) 1889 { 1890 lastaction = d->weaplast[weap]; 1891 animdelay = d->weapwait[weap]; 1892 switch(d->weapstate[weap]) 1893 { 1894 case WEAP_S_SWITCH: 1895 case WEAP_S_PICKUP: 1896 { 1897 if(lastmillis-d->weaplast[weap] <= d->weapwait[weap]/3) 1898 { 1899 if(!d->hasweap(d->lastweap, m_weapon(gamemode, mutators))) showweap = false; 1900 else weap = d->lastweap; 1901 } 1902 else if(!d->hasweap(weap, m_weapon(gamemode, mutators))) showweap = false; 1903 animflags = ANIM_SWITCH+(d->weapstate[weap]-WEAP_S_SWITCH); 1904 break; 1905 } 1906 case WEAP_S_POWER: 1907 { 1908 if(weaptype[weap].power) animflags = weaptype[weap].anim+d->weapstate[weap]; 1909 else animflags = weaptype[weap].anim|ANIM_LOOP; 1910 break; 1911 } 1912 case WEAP_S_SHOOT: 1913 { 1914 if(!d->hasweap(weap, m_weapon(gamemode, mutators)) || (!weaptype[weap].reloads && lastmillis-d->weaplast[weap] <= d->weapwait[weap]/3)) 1915 showweap = false; 1916 animflags = weaptype[weap].anim+d->weapstate[weap]; 1917 break; 1918 } 1919 case WEAP_S_RELOAD: 1920 { 1921 if(weap != WEAP_MELEE) 1922 { 1923 if(!d->hasweap(weap, m_weapon(gamemode, mutators)) || (!weaptype[weap].reloads && lastmillis-d->weaplast[weap] <= d->weapwait[weap]/3)) 1924 showweap = false; 1925 animflags = weaptype[weap].anim+d->weapstate[weap]; 1926 break; 1927 } 1928 } 1929 case WEAP_S_IDLE: case WEAP_S_WAIT: default: 1930 { 1931 if(!d->hasweap(weap, m_weapon(gamemode, mutators))) showweap = false; 1932 animflags = weaptype[weap].anim|ANIM_LOOP; 1933 break; 1934 } 1935 } 1936 } 1937 } 1938 1939 if(third && d->type == ENT_PLAYER && !shadowmapping && !envmapping && trans > 1e-16f && d->o.squaredist(camera1->o) <= maxparticledistance*maxparticledistance) 1940 { 1941 vec pos = d->abovehead(2); 1942 float blend = aboveheadblend*trans; 1943 if(shownamesabovehead > (d != player1 ? 0 : 1)) 1944 { 1945 const char *name = colorname(d, NULL, d->aitype < 0 ? "<super>" : "<default>"); 1946 if(name && *name) 1947 { 1948 part_textcopy(pos, name, PART_TEXT, 1, 0xFFFFFF, 2, blend); 1949 pos.z += 2; 1950 } 1951 } 1952 if(showstatusabovehead > (d != player1 ? 0 : 1)) 1953 { 1954 Texture *t = NULL; 1955 if(d->state == CS_DEAD || d->state == CS_WAITING) t = textureload(hud::deadtex, 3); 1956 else if(d->state == CS_ALIVE) 1957 { 1958 if(d->conopen) t = textureload(hud::conopentex, 3); 1959 else if(m_team(gamemode, mutators) && showteamabovehead > (d != player1 ? (d->team != player1->team ? 1 : 0) : 2)) 1960 t = textureload(hud::teamtex(d->team), 3); 1961 else if(d->dominating) t = textureload(hud::dominatingtex, 3); 1962 else if(d->dominated) t = textureload(hud::dominatedtex, 3); 1963 } 1964 if(t) 1965 { 1966 part_icon(pos, t, 2, blend); 1967 pos.z += 2; 1968 } 1969 } 1970 } 1971 bool hasweapon = showweap && *weaptype[weap].vwep; 1972 if(hasweapon) a[ai++] = modelattach("tag_weapon", weaptype[weap].vwep, ANIM_VWEP|ANIM_LOOP, 0); // we could probably animate this too now.. 1973 if(rendernormally && (early || d != player1)) 1974 { 1975 const char *muzzle = hasweapon ? "tag_muzzle" : "tag_weapon"; 1976 //if(d->aitype == AI_TURRET && (d->ammo[d->weapselect]+(d->weapstate[d->weapselect] == WEAP_S_SHOOT ? 1 : 0))%2) muzzle = "tag_muzzle2"; 1977 a[ai++] = modelattach(muzzle, &d->muzzle); 1978 if(third && (d->type == ENT_PLAYER || (d->type == ENT_AI && (!isaitype(d->aitype) || aistyle[d->aitype].maxspeed)))) 1979 { 1980 a[ai++] = modelattach("tag_head", &d->head); 1981 a[ai++] = modelattach("tag_torso", &d->torso); 1982 a[ai++] = modelattach("tag_waist", &d->waist); 1983 a[ai++] = modelattach("tag_lfoot", &d->lfoot); 1984 a[ai++] = modelattach("tag_rfoot", &d->rfoot); 1985 } 1986 } 1987 renderclient(d, third, trans, size, team, a[0].tag ? a : NULL, secondary, animflags, animdelay, lastaction, early); 1988 } 1989 rendercheck(gameent * d)1990 void rendercheck(gameent *d) 1991 { 1992 d->checktags(); 1993 impulseeffect(d, false); 1994 fireeffect(d); 1995 } 1996 render()1997 void render() 1998 { 1999 startmodelbatches(); 2000 gameent *d; 2001 loopi(numdynents()) if((d = (gameent *)iterdynents(i)) && d != player1) renderplayer(d, true, transscale(d, true), deadscale(d, 1, true)); 2002 entities::render(); 2003 projs::render(); 2004 if(m_stf(gamemode)) stf::render(); 2005 if(m_ctf(gamemode)) ctf::render(); 2006 ai::render(); 2007 if(rendernormally) loopi(numdynents()) if((d = (gameent *)iterdynents(i)) && d != player1) d->cleartags(); 2008 endmodelbatches(); 2009 if(rendernormally) loopi(numdynents()) if((d = (gameent *)iterdynents(i)) && d != player1) rendercheck(d); 2010 } 2011 renderavatar(bool early)2012 void renderavatar(bool early) 2013 { 2014 if(rendernormally && early) player1->cleartags(); 2015 if((thirdpersonview() || !rendernormally)) 2016 renderplayer(player1, true, transscale(player1, thirdpersonview(true)), deadscale(player1, 1, true), early); 2017 else if(!thirdpersonview() && player1->state == CS_ALIVE) 2018 renderplayer(player1, false, transscale(player1, false), deadscale(player1, 1, true), early); 2019 if(rendernormally && early) rendercheck(player1); 2020 } 2021 clientoption(char * arg)2022 bool clientoption(char *arg) { return false; } 2023 } 2024 #undef GAMEWORLD 2025