1 #include "game.h" 2 3 namespace entities 4 { 5 using namespace game; 6 extraentinfosize()7 int extraentinfosize() { return 0; } // size in bytes of what the 2 methods below read/write... so it can be skipped by other games 8 writeent(entity & e,char * buf)9 void writeent(entity &e, char *buf) // write any additional data to disk (except for ET_ ents) 10 { 11 } 12 readent(entity & e,char * buf,int ver)13 void readent(entity &e, char *buf, int ver) // read from disk, and init 14 { 15 if(ver <= 30) switch(e.type) 16 { 17 case FLAG: 18 case MONSTER: 19 case TELEDEST: 20 case RESPAWNPOINT: 21 case BOX: 22 case BARREL: 23 case PLATFORM: 24 case ELEVATOR: 25 e.attr1 = (int(e.attr1)+180)%360; 26 break; 27 } 28 if(ver <= 31) switch(e.type) 29 { 30 case BOX: 31 case BARREL: 32 case PLATFORM: 33 case ELEVATOR: 34 int yaw = (int(e.attr1)%360 + 360)%360 + 7; 35 e.attr1 = yaw - yaw%15; 36 break; 37 } 38 } 39 40 #ifndef STANDALONE 41 vector<extentity *> ents; 42 getents()43 vector<extentity *> &getents() { return ents; } 44 mayattach(extentity & e)45 bool mayattach(extentity &e) { return false; } attachent(extentity & e,extentity & a)46 bool attachent(extentity &e, extentity &a) { return false; } 47 itemname(int i)48 const char *itemname(int i) 49 { 50 int t = ents[i]->type; 51 if(t<I_SHELLS || t>I_QUAD) return NULL; 52 return itemstats[t-I_SHELLS].name; 53 } 54 itemicon(int i)55 int itemicon(int i) 56 { 57 int t = ents[i]->type; 58 if(t<I_SHELLS || t>I_QUAD) return -1; 59 return itemstats[t-I_SHELLS].icon; 60 } 61 entmdlname(int type)62 const char *entmdlname(int type) 63 { 64 static const char * const entmdlnames[] = 65 { 66 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 67 "ammo/shells", "ammo/bullets", "ammo/rockets", "ammo/rrounds", "ammo/grenades", "ammo/cartridges", 68 "health", "boost", "armor/green", "armor/yellow", "quad", "teleporter", 69 NULL, NULL, 70 "carrot", 71 NULL, NULL, 72 "checkpoint", 73 NULL, NULL, 74 NULL, NULL, 75 NULL 76 }; 77 return entmdlnames[type]; 78 } 79 entmodel(const entity & e)80 const char *entmodel(const entity &e) 81 { 82 if(e.type == TELEPORT) 83 { 84 if(e.attr2 > 0) return mapmodelname(e.attr2); 85 if(e.attr2 < 0) return NULL; 86 } 87 return e.type < MAXENTTYPES ? entmdlname(e.type) : NULL; 88 } 89 preloadentities()90 void preloadentities() 91 { 92 loopi(MAXENTTYPES) 93 { 94 switch(i) 95 { 96 case I_SHELLS: case I_BULLETS: case I_ROCKETS: case I_ROUNDS: case I_GRENADES: case I_CARTRIDGES: 97 if(m_noammo) continue; 98 break; 99 case I_HEALTH: case I_BOOST: case I_GREENARMOUR: case I_YELLOWARMOUR: case I_QUAD: 100 if(m_noitems) continue; 101 break; 102 case CARROT: case RESPAWNPOINT: 103 if(!m_classicsp) continue; 104 break; 105 } 106 const char *mdl = entmdlname(i); 107 if(!mdl) continue; 108 preloadmodel(mdl); 109 } 110 loopv(ents) 111 { 112 extentity &e = *ents[i]; 113 switch(e.type) 114 { 115 case TELEPORT: 116 if(e.attr2 > 0) preloadmodel(mapmodelname(e.attr2)); 117 case JUMPPAD: 118 if(e.attr4 > 0) preloadmapsound(e.attr4); 119 break; 120 } 121 } 122 } 123 renderentities()124 void renderentities() 125 { 126 loopv(ents) 127 { 128 extentity &e = *ents[i]; 129 int revs = 10; 130 switch(e.type) 131 { 132 case CARROT: 133 case RESPAWNPOINT: 134 if(e.attr2) revs = 1; 135 break; 136 case TELEPORT: 137 if(e.attr2 < 0) continue; 138 break; 139 default: 140 if(!e.spawned() || e.type < I_SHELLS || e.type > I_QUAD) continue; 141 } 142 const char *mdlname = entmodel(e); 143 if(mdlname) 144 { 145 vec p = e.o; 146 p.z += 1+sinf(lastmillis/100.0+e.o.x+e.o.y)/20; 147 rendermodel(&e.light, mdlname, ANIM_MAPMODEL|ANIM_LOOP, p, lastmillis/(float)revs, 0, MDL_SHADOW | MDL_CULL_VFC | MDL_CULL_DIST | MDL_CULL_OCCLUDED); 148 } 149 } 150 } 151 addammo(int type,int & v,bool local)152 void addammo(int type, int &v, bool local) 153 { 154 itemstat &is = itemstats[type-I_SHELLS]; 155 v += is.add; 156 if(v>is.max) v = is.max; 157 if(local) msgsound(is.sound); 158 } 159 repammo(fpsent * d,int type,bool local)160 void repammo(fpsent *d, int type, bool local) 161 { 162 addammo(type, d->ammo[type-I_SHELLS+GUN_SG], local); 163 } 164 165 // these two functions are called when the server acknowledges that you really 166 // picked up the item (in multiplayer someone may grab it before you). 167 pickupeffects(int n,fpsent * d)168 void pickupeffects(int n, fpsent *d) 169 { 170 if(!ents.inrange(n)) return; 171 extentity *e = ents[n]; 172 int type = e->type; 173 if(type<I_SHELLS || type>I_QUAD) return; 174 e->clearspawned(); 175 e->clearnopickup(); 176 if(!d) return; 177 itemstat &is = itemstats[type-I_SHELLS]; 178 fpsent *h = followingplayer(player1); 179 if(d!=h || isthirdperson()) 180 { 181 //particle_text(d->abovehead(), is.name, PART_TEXT, 2000, 0xFFC864, 4.0f, -8); 182 particle_icon(d->abovehead(), is.icon%4, is.icon/4, PART_HUD_ICON_GREY, 2000, 0xFFFFFF, 2.0f, -8); 183 } 184 playsound(itemstats[type-I_SHELLS].sound, d!=h ? &d->o : NULL, NULL, 0, 0, 0, -1, 0, 1500); 185 d->pickup(type); 186 if(d==h) switch(type) 187 { 188 case I_BOOST: 189 conoutf(CON_GAMEINFO, "\f2you got the health boost!"); 190 playsound(S_V_BOOST, NULL, NULL, 0, 0, 0, -1, 0, 3000); 191 break; 192 193 case I_QUAD: 194 conoutf(CON_GAMEINFO, "\f2you got the quad!"); 195 playsound(S_V_QUAD, NULL, NULL, 0, 0, 0, -1, 0, 3000); 196 break; 197 } 198 } 199 200 // these functions are called when the client touches the item 201 teleporteffects(fpsent * d,int tp,int td,bool local)202 void teleporteffects(fpsent *d, int tp, int td, bool local) 203 { 204 if(ents.inrange(tp) && ents[tp]->type == TELEPORT) 205 { 206 extentity &e = *ents[tp]; 207 if(e.attr4 >= 0) 208 { 209 int snd = S_TELEPORT, flags = 0; 210 if(e.attr4 > 0) { snd = e.attr4; flags = SND_MAP; } 211 fpsent *h = followingplayer(player1); 212 playsound(snd, d==h ? NULL : &e.o, NULL, flags); 213 if(d!=h && ents.inrange(td) && ents[td]->type == TELEDEST) playsound(snd, &ents[td]->o, NULL, flags); 214 } 215 } 216 if(local && d->clientnum >= 0) 217 { 218 sendposition(d); 219 packetbuf p(32, ENET_PACKET_FLAG_RELIABLE); 220 putint(p, N_TELEPORT); 221 putint(p, d->clientnum); 222 putint(p, tp); 223 putint(p, td); 224 sendclientpacket(p.finalize(), 0); 225 flushclient(); 226 } 227 } 228 jumppadeffects(fpsent * d,int jp,bool local)229 void jumppadeffects(fpsent *d, int jp, bool local) 230 { 231 if(ents.inrange(jp) && ents[jp]->type == JUMPPAD) 232 { 233 extentity &e = *ents[jp]; 234 if(e.attr4 >= 0) 235 { 236 int snd = S_JUMPPAD, flags = 0; 237 if(e.attr4 > 0) { snd = e.attr4; flags = SND_MAP; } 238 playsound(snd, d == followingplayer(player1) ? NULL : &e.o, NULL, flags); 239 } 240 } 241 if(local && d->clientnum >= 0) 242 { 243 sendposition(d); 244 packetbuf p(16, ENET_PACKET_FLAG_RELIABLE); 245 putint(p, N_JUMPPAD); 246 putint(p, d->clientnum); 247 putint(p, jp); 248 sendclientpacket(p.finalize(), 0); 249 flushclient(); 250 } 251 } 252 teleport(int n,fpsent * d)253 void teleport(int n, fpsent *d) // also used by monsters 254 { 255 int e = -1, tag = ents[n]->attr1, beenhere = -1; 256 for(;;) 257 { 258 e = findentity(TELEDEST, e+1); 259 if(e==beenhere || e<0) { conoutf(CON_WARN, "no teleport destination for tag %d", tag); return; } 260 if(beenhere<0) beenhere = e; 261 if(ents[e]->attr2==tag) 262 { 263 teleporteffects(d, n, e, true); 264 d->o = ents[e]->o; 265 d->yaw = ents[e]->attr1; 266 if(ents[e]->attr3 > 0) 267 { 268 vec dir; 269 vecfromyawpitch(d->yaw, 0, 1, 0, dir); 270 float speed = d->vel.magnitude2(); 271 d->vel.x = dir.x*speed; 272 d->vel.y = dir.y*speed; 273 } 274 else d->vel = vec(0, 0, 0); 275 entinmap(d); 276 updatedynentcache(d); 277 ai::inferwaypoints(d, ents[n]->o, ents[e]->o, 16.f); 278 break; 279 } 280 } 281 } 282 trypickup(int n,fpsent * d)283 void trypickup(int n, fpsent *d) 284 { 285 extentity *e = ents[n]; 286 switch(e->type) 287 { 288 default: 289 if(d->canpickup(e->type)) 290 { 291 addmsg(N_ITEMPICKUP, "rci", d, n); 292 e->setnopickup(); // even if someone else gets it first 293 } 294 break; 295 296 case TELEPORT: 297 { 298 if(d->lastpickup==e->type && lastmillis-d->lastpickupmillis<500) break; 299 if(e->attr3 > 0) 300 { 301 defformatstring(hookname, "can_teleport_%d", e->attr3); 302 if(!execidentbool(hookname, true)) break; 303 } 304 d->lastpickup = e->type; 305 d->lastpickupmillis = lastmillis; 306 teleport(n, d); 307 break; 308 } 309 310 case RESPAWNPOINT: 311 if(!m_classicsp || d!=player1 || n==respawnent) break; 312 respawnent = n; 313 conoutf(CON_GAMEINFO, "\f2respawn point set!"); 314 playsound(S_V_RESPAWNPOINT); 315 break; 316 317 case JUMPPAD: 318 { 319 if(d->lastpickup==e->type && lastmillis-d->lastpickupmillis<300) break; 320 d->lastpickup = e->type; 321 d->lastpickupmillis = lastmillis; 322 jumppadeffects(d, n, true); 323 vec v((int)(char)e->attr3*10.0f, (int)(char)e->attr2*10.0f, e->attr1*12.5f); 324 if(d->ai) d->ai->becareful = true; 325 d->falling = vec(0, 0, 0); 326 d->physstate = PHYS_FALL; 327 d->timeinair = 1; 328 d->vel = v; 329 break; 330 } 331 } 332 } 333 checkitems(fpsent * d)334 void checkitems(fpsent *d) 335 { 336 if(d->state!=CS_ALIVE) return; 337 vec o = d->feetpos(); 338 loopv(ents) 339 { 340 extentity &e = *ents[i]; 341 if(e.type==NOTUSED) continue; 342 if((!e.spawned() || e.nopickup()) && e.type!=TELEPORT && e.type!=JUMPPAD && e.type!=RESPAWNPOINT) continue; 343 float dist = e.o.dist(o); 344 if(dist<(e.type==TELEPORT ? 16 : 12)) trypickup(i, d); 345 } 346 } 347 checkquad(int time,fpsent * d)348 void checkquad(int time, fpsent *d) 349 { 350 if(d->quadmillis && (d->quadmillis -= time)<=0) 351 { 352 d->quadmillis = 0; 353 fpsent *h = followingplayer(player1); 354 playsound(S_PUPOUT, d==h ? NULL : &d->o); 355 if(d==h) conoutf(CON_GAMEINFO, "\f2quad damage is over"); 356 } 357 } 358 putitems(packetbuf & p)359 void putitems(packetbuf &p) // puts items in network stream and also spawns them locally 360 { 361 putint(p, N_ITEMLIST); 362 loopv(ents) if(ents[i]->type>=I_SHELLS && ents[i]->type<=I_QUAD && (!m_noammo || ents[i]->type<I_SHELLS || ents[i]->type>I_CARTRIDGES)) 363 { 364 putint(p, i); 365 putint(p, ents[i]->type); 366 } 367 putint(p, -1); 368 } 369 resetspawns()370 void resetspawns() { loopv(ents) { extentity *e = ents[i]; e->clearspawned(); e->clearnopickup(); } } 371 spawnitems(bool force)372 void spawnitems(bool force) 373 { 374 if(m_noitems) return; 375 loopv(ents) 376 { 377 extentity *e = ents[i]; 378 if(e->type>=I_SHELLS && e->type<=I_QUAD && (!m_noammo || e->type<I_SHELLS || e->type>I_CARTRIDGES)) 379 { 380 e->setspawned(force || m_sp || !server::delayspawn(e->type)); 381 e->clearnopickup(); 382 } 383 } 384 } 385 setspawn(int i,bool on)386 void setspawn(int i, bool on) { if(ents.inrange(i)) { extentity *e = ents[i]; e->setspawned(on); e->clearnopickup(); } } 387 newentity()388 extentity *newentity() { return new fpsentity(); } deleteentity(extentity * e)389 void deleteentity(extentity *e) { delete (fpsentity *)e; } 390 clearents()391 void clearents() 392 { 393 while(ents.length()) deleteentity(ents.pop()); 394 } 395 396 enum 397 { 398 TRIG_COLLIDE = 1<<0, 399 TRIG_TOGGLE = 1<<1, 400 TRIG_ONCE = 0<<2, 401 TRIG_MANY = 1<<2, 402 TRIG_DISAPPEAR = 1<<3, 403 TRIG_AUTO_RESET = 1<<4, 404 TRIG_RUMBLE = 1<<5, 405 TRIG_LOCKED = 1<<6, 406 TRIG_ENDSP = 1<<7 407 }; 408 409 static const int NUMTRIGGERTYPES = 32; 410 411 static const int triggertypes[NUMTRIGGERTYPES] = 412 { 413 -1, 414 TRIG_ONCE, // 1 415 TRIG_RUMBLE, // 2 416 TRIG_TOGGLE, // 3 417 TRIG_TOGGLE | TRIG_RUMBLE, // 4 418 TRIG_MANY, // 5 419 TRIG_MANY | TRIG_RUMBLE, // 6 420 TRIG_MANY | TRIG_TOGGLE, // 7 421 TRIG_MANY | TRIG_TOGGLE | TRIG_RUMBLE, // 8 422 TRIG_COLLIDE | TRIG_TOGGLE | TRIG_RUMBLE, // 9 423 TRIG_COLLIDE | TRIG_TOGGLE | TRIG_AUTO_RESET | TRIG_RUMBLE, // 10 424 TRIG_COLLIDE | TRIG_TOGGLE | TRIG_LOCKED | TRIG_RUMBLE, // 11 425 TRIG_DISAPPEAR, // 12 426 TRIG_DISAPPEAR | TRIG_RUMBLE, // 13 427 TRIG_DISAPPEAR | TRIG_COLLIDE | TRIG_LOCKED, // 14 428 -1 /* reserved 15 */, 429 -1 /* reserved 16 */, 430 -1 /* reserved 17 */, 431 -1 /* reserved 18 */, 432 -1 /* reserved 19 */, 433 -1 /* reserved 20 */, 434 -1 /* reserved 21 */, 435 -1 /* reserved 22 */, 436 -1 /* reserved 23 */, 437 -1 /* reserved 24 */, 438 -1 /* reserved 25 */, 439 -1 /* reserved 26 */, 440 -1 /* reserved 27 */, 441 -1 /* reserved 28 */, 442 TRIG_DISAPPEAR | TRIG_RUMBLE | TRIG_ENDSP, // 29 443 -1 /* reserved 30 */, 444 -1 /* reserved 31 */, 445 }; 446 447 #define validtrigger(type) (triggertypes[(type) & (NUMTRIGGERTYPES-1)]>=0) 448 #define checktriggertype(type, flag) (triggertypes[(type) & (NUMTRIGGERTYPES-1)] & (flag)) 449 cleartriggerflags(extentity & e)450 static inline void cleartriggerflags(extentity &e) 451 { 452 e.flags &= ~(EF_ANIM | EF_NOVIS | EF_NOSHADOW | EF_NOCOLLIDE); 453 } 454 setuptriggerflags(fpsentity & e)455 static inline void setuptriggerflags(fpsentity &e) 456 { 457 cleartriggerflags(e); 458 e.flags |= EF_ANIM; 459 if(checktriggertype(e.attr3, TRIG_COLLIDE|TRIG_DISAPPEAR)) e.flags |= EF_NOSHADOW; 460 if(!checktriggertype(e.attr3, TRIG_COLLIDE)) e.flags |= EF_NOCOLLIDE; 461 switch(e.triggerstate) 462 { 463 case TRIGGERING: 464 if(checktriggertype(e.attr3, TRIG_COLLIDE) && lastmillis-e.lasttrigger >= 500) e.flags |= EF_NOCOLLIDE; 465 break; 466 case TRIGGERED: 467 if(checktriggertype(e.attr3, TRIG_COLLIDE)) e.flags |= EF_NOCOLLIDE; 468 break; 469 case TRIGGER_DISAPPEARED: 470 e.flags |= EF_NOVIS | EF_NOCOLLIDE; 471 break; 472 } 473 } 474 resettriggers()475 void resettriggers() 476 { 477 loopv(ents) 478 { 479 fpsentity &e = *(fpsentity *)ents[i]; 480 if(e.type != ET_MAPMODEL || !validtrigger(e.attr3)) continue; 481 e.triggerstate = TRIGGER_RESET; 482 e.lasttrigger = 0; 483 setuptriggerflags(e); 484 } 485 } 486 unlocktriggers(int tag,int oldstate=TRIGGER_RESET,int newstate=TRIGGERING)487 void unlocktriggers(int tag, int oldstate = TRIGGER_RESET, int newstate = TRIGGERING) 488 { 489 loopv(ents) 490 { 491 fpsentity &e = *(fpsentity *)ents[i]; 492 if(e.type != ET_MAPMODEL || !validtrigger(e.attr3)) continue; 493 if(e.attr4 == tag && e.triggerstate == oldstate && checktriggertype(e.attr3, TRIG_LOCKED)) 494 { 495 if(newstate == TRIGGER_RESETTING && checktriggertype(e.attr3, TRIG_COLLIDE) && overlapsdynent(e.o, 20)) continue; 496 e.triggerstate = newstate; 497 e.lasttrigger = lastmillis; 498 if(checktriggertype(e.attr3, TRIG_RUMBLE)) playsound(S_RUMBLE, &e.o); 499 } 500 } 501 } 502 503 ICOMMAND(trigger, "ii", (int *tag, int *state), 504 { 505 if(*state) unlocktriggers(*tag); 506 else unlocktriggers(*tag, TRIGGERED, TRIGGER_RESETTING); 507 }); 508 509 VAR(triggerstate, -1, 0, 1); 510 doleveltrigger(int trigger,int state)511 void doleveltrigger(int trigger, int state) 512 { 513 defformatstring(aliasname, "level_trigger_%d", trigger); 514 if(identexists(aliasname)) 515 { 516 triggerstate = state; 517 execident(aliasname); 518 } 519 } 520 checktriggers()521 void checktriggers() 522 { 523 if(player1->state != CS_ALIVE) return; 524 vec o = player1->feetpos(); 525 loopv(ents) 526 { 527 fpsentity &e = *(fpsentity *)ents[i]; 528 if(e.type != ET_MAPMODEL || !validtrigger(e.attr3)) continue; 529 switch(e.triggerstate) 530 { 531 case TRIGGERING: 532 case TRIGGER_RESETTING: 533 if(lastmillis-e.lasttrigger>=1000) 534 { 535 if(e.attr4) 536 { 537 if(e.triggerstate == TRIGGERING) unlocktriggers(e.attr4); 538 else unlocktriggers(e.attr4, TRIGGERED, TRIGGER_RESETTING); 539 } 540 if(checktriggertype(e.attr3, TRIG_DISAPPEAR)) e.triggerstate = TRIGGER_DISAPPEARED; 541 else if(e.triggerstate==TRIGGERING && checktriggertype(e.attr3, TRIG_TOGGLE)) e.triggerstate = TRIGGERED; 542 else e.triggerstate = TRIGGER_RESET; 543 } 544 setuptriggerflags(e); 545 break; 546 case TRIGGER_RESET: 547 if(e.lasttrigger) 548 { 549 if(checktriggertype(e.attr3, TRIG_AUTO_RESET|TRIG_MANY|TRIG_LOCKED) && e.o.dist(o)-player1->radius>=(checktriggertype(e.attr3, TRIG_COLLIDE) ? 20 : 12)) 550 e.lasttrigger = 0; 551 break; 552 } 553 else if(e.o.dist(o)-player1->radius>=(checktriggertype(e.attr3, TRIG_COLLIDE) ? 20 : 12)) break; 554 else if(checktriggertype(e.attr3, TRIG_LOCKED)) 555 { 556 if(!e.attr4) break; 557 doleveltrigger(e.attr4, -1); 558 e.lasttrigger = lastmillis; 559 break; 560 } 561 e.triggerstate = TRIGGERING; 562 e.lasttrigger = lastmillis; 563 setuptriggerflags(e); 564 if(checktriggertype(e.attr3, TRIG_RUMBLE)) playsound(S_RUMBLE, &e.o); 565 if(checktriggertype(e.attr3, TRIG_ENDSP)) endsp(false); 566 if(e.attr4) doleveltrigger(e.attr4, 1); 567 break; 568 case TRIGGERED: 569 if(e.o.dist(o)-player1->radius<(checktriggertype(e.attr3, TRIG_COLLIDE) ? 20 : 12)) 570 { 571 if(e.lasttrigger) break; 572 } 573 else if(checktriggertype(e.attr3, TRIG_AUTO_RESET)) 574 { 575 if(lastmillis-e.lasttrigger<6000) break; 576 } 577 else if(checktriggertype(e.attr3, TRIG_MANY)) 578 { 579 e.lasttrigger = 0; 580 break; 581 } 582 else break; 583 if(checktriggertype(e.attr3, TRIG_COLLIDE) && overlapsdynent(e.o, 20)) break; 584 e.triggerstate = TRIGGER_RESETTING; 585 e.lasttrigger = lastmillis; 586 setuptriggerflags(e); 587 if(checktriggertype(e.attr3, TRIG_RUMBLE)) playsound(S_RUMBLE, &e.o); 588 if(checktriggertype(e.attr3, TRIG_ENDSP)) endsp(false); 589 if(e.attr4) doleveltrigger(e.attr4, 0); 590 break; 591 } 592 } 593 } 594 animatemapmodel(const extentity & e,int & anim,int & basetime)595 void animatemapmodel(const extentity &e, int &anim, int &basetime) 596 { 597 const fpsentity &f = (const fpsentity &)e; 598 if(validtrigger(f.attr3)) switch(f.triggerstate) 599 { 600 case TRIGGER_RESET: anim = ANIM_TRIGGER|ANIM_START; break; 601 case TRIGGERING: anim = ANIM_TRIGGER; basetime = f.lasttrigger; break; 602 case TRIGGERED: anim = ANIM_TRIGGER|ANIM_END; break; 603 case TRIGGER_RESETTING: anim = ANIM_TRIGGER|ANIM_REVERSE; basetime = f.lasttrigger; break; 604 } 605 } 606 fixentity(extentity & e)607 void fixentity(extentity &e) 608 { 609 switch(e.type) 610 { 611 case FLAG: 612 case BOX: 613 case BARREL: 614 case PLATFORM: 615 case ELEVATOR: 616 e.attr5 = e.attr4; 617 e.attr4 = e.attr3; 618 case TELEDEST: 619 e.attr3 = e.attr2; 620 case MONSTER: 621 e.attr2 = e.attr1; 622 case RESPAWNPOINT: 623 e.attr1 = (int)player1->yaw; 624 break; 625 } 626 } 627 entradius(extentity & e,bool color)628 void entradius(extentity &e, bool color) 629 { 630 switch(e.type) 631 { 632 case TELEPORT: 633 loopv(ents) if(ents[i]->type == TELEDEST && e.attr1==ents[i]->attr2) 634 { 635 renderentarrow(e, vec(ents[i]->o).sub(e.o).normalize(), e.o.dist(ents[i]->o)); 636 break; 637 } 638 break; 639 640 case JUMPPAD: 641 renderentarrow(e, vec((int)(char)e.attr3*10.0f, (int)(char)e.attr2*10.0f, e.attr1*12.5f).normalize(), 4); 642 break; 643 644 case FLAG: 645 case MONSTER: 646 case TELEDEST: 647 case RESPAWNPOINT: 648 case BOX: 649 case BARREL: 650 case PLATFORM: 651 case ELEVATOR: 652 { 653 vec dir; 654 vecfromyawpitch(e.attr1, 0, 1, 0, dir); 655 renderentarrow(e, dir, 4); 656 break; 657 } 658 case MAPMODEL: 659 if(validtrigger(e.attr3)) renderentring(e, checktriggertype(e.attr3, TRIG_COLLIDE) ? 20 : 12); 660 break; 661 } 662 } 663 printent(extentity & e,char * buf,int len)664 bool printent(extentity &e, char *buf, int len) 665 { 666 return false; 667 } 668 entnameinfo(entity & e)669 const char *entnameinfo(entity &e) { return ""; } entname(int i)670 const char *entname(int i) 671 { 672 static const char * const entnames[] = 673 { 674 "none?", "light", "mapmodel", "playerstart", "envmap", "particles", "sound", "spotlight", 675 "shells", "bullets", "rockets", "riflerounds", "grenades", "cartridges", 676 "health", "healthboost", "greenarmour", "yellowarmour", "quaddamage", 677 "teleport", "teledest", 678 "monster", "carrot", "jumppad", 679 "base", "respawnpoint", 680 "box", "barrel", 681 "platform", "elevator", 682 "flag", 683 "", "", "", "", 684 }; 685 return i>=0 && size_t(i)<sizeof(entnames)/sizeof(entnames[0]) ? entnames[i] : ""; 686 } 687 editent(int i,bool local)688 void editent(int i, bool local) 689 { 690 extentity &e = *ents[i]; 691 if(e.type == ET_MAPMODEL && validtrigger(e.attr3)) 692 { 693 fpsentity &f = (fpsentity &)e; 694 f.triggerstate = TRIGGER_RESET; 695 f.lasttrigger = 0; 696 setuptriggerflags(f); 697 } 698 else cleartriggerflags(e); 699 if(local) addmsg(N_EDITENT, "rii3ii5", i, (int)(e.o.x*DMF), (int)(e.o.y*DMF), (int)(e.o.z*DMF), e.type, e.attr1, e.attr2, e.attr3, e.attr4, e.attr5); 700 } 701 dropheight(entity & e)702 float dropheight(entity &e) 703 { 704 if(e.type==BASE || e.type==FLAG) return 0.0f; 705 return 4.0f; 706 } 707 #endif 708 } 709 710