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 } 16 17 #ifndef STANDALONE 18 vector<extentity *> ents; 19 getents()20 vector<extentity *> &getents() { return ents; } 21 mayattach(extentity & e)22 bool mayattach(extentity &e) { return false; } attachent(extentity & e,extentity & a)23 bool attachent(extentity &e, extentity &a) { return false; } 24 itemname(int i)25 const char *itemname(int i) 26 { 27 return NULL; 28 #if 0 29 int t = ents[i]->type; 30 if(!validitem(t)) return NULL; 31 return itemstats[t-I_FIRST].name; 32 #endif 33 } 34 itemicon(int i)35 int itemicon(int i) 36 { 37 return -1; 38 #if 0 39 int t = ents[i]->type; 40 if(!validitem(t)) return -1; 41 return itemstats[t-I_FIRST].icon; 42 #endif 43 } 44 entmdlname(int type)45 const char *entmdlname(int type) 46 { 47 static const char * const entmdlnames[MAXENTTYPES] = 48 { 49 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 50 "game/teleport", NULL, NULL, 51 NULL 52 }; 53 return entmdlnames[type]; 54 } 55 entmodel(const entity & e)56 const char *entmodel(const entity &e) 57 { 58 if(e.type == TELEPORT) 59 { 60 if(e.attr2 > 0) return mapmodelname(e.attr2); 61 if(e.attr2 < 0) return NULL; 62 } 63 return e.type < MAXENTTYPES ? entmdlname(e.type) : NULL; 64 } 65 preloadentities()66 void preloadentities() 67 { 68 loopi(MAXENTTYPES) 69 { 70 const char *mdl = entmdlname(i); 71 if(!mdl) continue; 72 preloadmodel(mdl); 73 } 74 loopv(ents) 75 { 76 extentity &e = *ents[i]; 77 switch(e.type) 78 { 79 case TELEPORT: 80 if(e.attr2 > 0) preloadmodel(mapmodelname(e.attr2)); 81 case JUMPPAD: 82 if(e.attr4 > 0) preloadmapsound(e.attr4); 83 break; 84 } 85 } 86 } 87 renderentities()88 void renderentities() 89 { 90 loopv(ents) 91 { 92 extentity &e = *ents[i]; 93 int revs = 10; 94 switch(e.type) 95 { 96 case TELEPORT: 97 if(e.attr2 < 0) continue; 98 break; 99 default: 100 if(!e.spawned() || !validitem(e.type)) continue; 101 break; 102 } 103 const char *mdlname = entmodel(e); 104 if(mdlname) 105 { 106 vec p = e.o; 107 p.z += 1+sinf(lastmillis/100.0+e.o.x+e.o.y)/20; 108 rendermodel(mdlname, ANIM_MAPMODEL|ANIM_LOOP, p, lastmillis/(float)revs, 0, 0, MDL_CULL_VFC | MDL_CULL_DIST | MDL_CULL_OCCLUDED); 109 } 110 } 111 } 112 addammo(int type,int & v,bool local)113 void addammo(int type, int &v, bool local) 114 { 115 #if 0 116 itemstat &is = itemstats[type-I_FIRST]; 117 v += is.add; 118 if(v>is.max) v = is.max; 119 if(local) msgsound(is.sound); 120 #endif 121 } 122 123 // these two functions are called when the server acknowledges that you really 124 // picked up the item (in multiplayer someone may grab it before you). 125 pickupeffects(int n,gameent * d)126 void pickupeffects(int n, gameent *d) 127 { 128 #if 0 129 if(!ents.inrange(n)) return; 130 int type = ents[n]->type; 131 if(!validitem(type)) return; 132 ents[n]->clearspawned(); 133 if(!d) return; 134 itemstat &is = itemstats[type-I_FIRST]; 135 if(d!=player1 || isthirdperson()) 136 { 137 //particle_text(d->abovehead(), is.name, PART_TEXT, 2000, 0xFFC864, 4.0f, -8); 138 particle_icon(d->abovehead(), is.icon%4, is.icon/4, PART_HUD_ICON_GREY, 2000, 0xFFFFFF, 2.0f, -8); 139 } 140 playsound(itemstats[type-I_FIRST].sound, d!=player1 ? &d->o : NULL, NULL, 0, 0, 0, -1, 0, 1500); 141 d->pickup(type); 142 if(d==player1) switch(type) 143 { 144 } 145 #endif 146 } 147 148 // these functions are called when the client touches the item 149 teleporteffects(gameent * d,int tp,int td,bool local)150 void teleporteffects(gameent *d, int tp, int td, bool local) 151 { 152 if(ents.inrange(tp) && ents[tp]->type == TELEPORT) 153 { 154 extentity &e = *ents[tp]; 155 if(e.attr4 >= 0) 156 { 157 int snd = S_TELEPORT, flags = 0; 158 if(e.attr4 > 0) { snd = e.attr4; flags = SND_MAP; } 159 if(d == player1) playsound(snd, NULL, NULL, flags); 160 else 161 { 162 playsound(snd, &e.o, NULL, flags); 163 if(ents.inrange(td) && ents[td]->type == TELEDEST) playsound(snd, &ents[td]->o, NULL, flags); 164 } 165 } 166 } 167 if(local && d->clientnum >= 0) 168 { 169 sendposition(d); 170 packetbuf p(32, ENET_PACKET_FLAG_RELIABLE); 171 putint(p, N_TELEPORT); 172 putint(p, d->clientnum); 173 putint(p, tp); 174 putint(p, td); 175 sendclientpacket(p.finalize(), 0); 176 flushclient(); 177 } 178 } 179 jumppadeffects(gameent * d,int jp,bool local)180 void jumppadeffects(gameent *d, int jp, bool local) 181 { 182 if(ents.inrange(jp) && ents[jp]->type == JUMPPAD) 183 { 184 extentity &e = *ents[jp]; 185 if(e.attr4 >= 0) 186 { 187 int snd = S_JUMPPAD, flags = 0; 188 if(e.attr4 > 0) { snd = e.attr4; flags = SND_MAP; } 189 if(d == player1) playsound(snd, NULL, NULL, flags); 190 else playsound(snd, &e.o, NULL, flags); 191 } 192 } 193 if(local && d->clientnum >= 0) 194 { 195 sendposition(d); 196 packetbuf p(16, ENET_PACKET_FLAG_RELIABLE); 197 putint(p, N_JUMPPAD); 198 putint(p, d->clientnum); 199 putint(p, jp); 200 sendclientpacket(p.finalize(), 0); 201 flushclient(); 202 } 203 } 204 teleport(int n,gameent * d)205 void teleport(int n, gameent *d) // also used by monsters 206 { 207 int e = -1, tag = ents[n]->attr1, beenhere = -1; 208 for(;;) 209 { 210 e = findentity(TELEDEST, e+1); 211 if(e==beenhere || e<0) { conoutf(CON_WARN, "no teleport destination for tag %d", tag); return; } 212 if(beenhere<0) beenhere = e; 213 if(ents[e]->attr2==tag) 214 { 215 teleporteffects(d, n, e, true); 216 d->o = ents[e]->o; 217 d->yaw = ents[e]->attr1; 218 if(ents[e]->attr3 > 0) 219 { 220 vec dir; 221 vecfromyawpitch(d->yaw, 0, 1, 0, dir); 222 float speed = d->vel.magnitude2(); 223 d->vel.x = dir.x*speed; 224 d->vel.y = dir.y*speed; 225 } 226 else d->vel = vec(0, 0, 0); 227 entinmap(d); 228 updatedynentcache(d); 229 ai::inferwaypoints(d, ents[n]->o, ents[e]->o, 16.f); 230 if(d == player1) ovr::reset(); 231 break; 232 } 233 } 234 } 235 trypickup(int n,gameent * d)236 void trypickup(int n, gameent *d) 237 { 238 switch(ents[n]->type) 239 { 240 default: 241 if(d->canpickup(ents[n]->type)) 242 { 243 addmsg(N_ITEMPICKUP, "rci", d, n); 244 ents[n]->clearspawned(); // even if someone else gets it first 245 } 246 break; 247 248 case TELEPORT: 249 { 250 if(d->lastpickup==ents[n]->type && lastmillis-d->lastpickupmillis<500) break; 251 if(ents[n]->attr3 > 0) 252 { 253 defformatstring(hookname, "can_teleport_%d", ents[n]->attr3); 254 if(!execidentbool(hookname, true)) break; 255 } 256 d->lastpickup = ents[n]->type; 257 d->lastpickupmillis = lastmillis; 258 teleport(n, d); 259 break; 260 } 261 262 case JUMPPAD: 263 { 264 if(d->lastpickup==ents[n]->type && lastmillis-d->lastpickupmillis<300) break; 265 d->lastpickup = ents[n]->type; 266 d->lastpickupmillis = lastmillis; 267 jumppadeffects(d, n, true); 268 vec v((int)(char)ents[n]->attr3*10.0f, (int)(char)ents[n]->attr2*10.0f, ents[n]->attr1*12.5f); 269 if(d->ai) d->ai->becareful = true; 270 d->falling = vec(0, 0, 0); 271 d->physstate = PHYS_FALL; 272 d->timeinair = 1; 273 d->vel = v; 274 break; 275 } 276 } 277 } 278 checkitems(gameent * d)279 void checkitems(gameent *d) 280 { 281 if(d->state!=CS_ALIVE) return; 282 vec o = d->feetpos(); 283 loopv(ents) 284 { 285 extentity &e = *ents[i]; 286 if(e.type==NOTUSED) continue; 287 if(!e.spawned() && e.type!=TELEPORT && e.type!=JUMPPAD) continue; 288 float dist = e.o.dist(o); 289 if(dist<(e.type==TELEPORT ? 16 : 12)) trypickup(i, d); 290 } 291 } 292 putitems(packetbuf & p)293 void putitems(packetbuf &p) // puts items in network stream and also spawns them locally 294 { 295 putint(p, N_ITEMLIST); 296 loopv(ents) if(validitem(ents[i]->type)) 297 { 298 putint(p, i); 299 putint(p, ents[i]->type); 300 } 301 putint(p, -1); 302 } 303 resetspawns()304 void resetspawns() { loopv(ents) ents[i]->clearspawned(); } 305 spawnitems(bool force)306 void spawnitems(bool force) 307 { 308 loopv(ents) if(validitem(ents[i]->type)) 309 { 310 ents[i]->setspawned(force || !server::delayspawn(ents[i]->type)); 311 } 312 } 313 setspawn(int i,bool on)314 void setspawn(int i, bool on) { if(ents.inrange(i)) ents[i]->setspawned(on); } 315 newentity()316 extentity *newentity() { return new gameentity(); } deleteentity(extentity * e)317 void deleteentity(extentity *e) { delete (gameentity *)e; } 318 clearents()319 void clearents() 320 { 321 while(ents.length()) deleteentity(ents.pop()); 322 } 323 animatemapmodel(const extentity & e,int & anim,int & basetime)324 void animatemapmodel(const extentity &e, int &anim, int &basetime) 325 { 326 } 327 fixentity(extentity & e)328 void fixentity(extentity &e) 329 { 330 switch(e.type) 331 { 332 case FLAG: 333 e.attr5 = e.attr4; 334 e.attr4 = e.attr3; 335 case TELEDEST: 336 e.attr3 = e.attr2; 337 e.attr2 = e.attr1; 338 e.attr1 = (int)player1->yaw; 339 break; 340 } 341 } 342 entradius(extentity & e,bool color)343 void entradius(extentity &e, bool color) 344 { 345 switch(e.type) 346 { 347 case TELEPORT: 348 loopv(ents) if(ents[i]->type == TELEDEST && e.attr1==ents[i]->attr2) 349 { 350 renderentarrow(e, vec(ents[i]->o).sub(e.o).normalize(), e.o.dist(ents[i]->o)); 351 break; 352 } 353 break; 354 355 case JUMPPAD: 356 renderentarrow(e, vec((int)(char)e.attr3*10.0f, (int)(char)e.attr2*10.0f, e.attr1*12.5f).normalize(), 4); 357 break; 358 359 case FLAG: 360 case TELEDEST: 361 { 362 vec dir; 363 vecfromyawpitch(e.attr1, 0, 1, 0, dir); 364 renderentarrow(e, dir, 4); 365 break; 366 } 367 } 368 } 369 printent(extentity & e,char * buf,int len)370 bool printent(extentity &e, char *buf, int len) 371 { 372 return false; 373 } 374 entnameinfo(entity & e)375 const char *entnameinfo(entity &e) { return ""; } entname(int i)376 const char *entname(int i) 377 { 378 static const char * const entnames[MAXENTTYPES] = 379 { 380 "none?", "light", "mapmodel", "playerstart", "envmap", "particles", "sound", "spotlight", "decal", 381 "teleport", "teledest", "jumppad", 382 "flag" 383 }; 384 return i>=0 && size_t(i)<sizeof(entnames)/sizeof(entnames[0]) ? entnames[i] : ""; 385 } 386 editent(int i,bool local)387 void editent(int i, bool local) 388 { 389 extentity &e = *ents[i]; 390 //e.flags = 0; 391 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); 392 } 393 dropheight(entity & e)394 float dropheight(entity &e) 395 { 396 if(e.type==FLAG) return 0.0f; 397 return 4.0f; 398 } 399 #endif 400 } 401 402