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