Initial sauer
[SauerbratenRemote.git] / src / fpsgame / entities.h
blobd4460a400ba81c60bf70d08cfab3963410b8a7e8
2 struct entities : icliententities
4 fpsclient &cl;
6 vector<extentity *> ents;
8 entities(fpsclient &_cl) : cl(_cl)
12 vector<extentity *> &getents() { return ents; }
14 const char *itemname(int i)
16 int t = ents[i]->type;
17 if(t<I_SHELLS || t>I_QUAD) return NULL;
18 return itemstats[t-I_SHELLS].name;
21 const char *entmdlname(int type)
23 static const char *entmdlnames[] =
25 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
26 "ammo/shells", "ammo/bullets", "ammo/rockets", "ammo/rrounds", "ammo/grenades", "ammo/cartridges",
27 "health", "boost", "armor/green", "armor/yellow", "quad", "teleporter",
28 NULL, NULL,
29 "carrot",
30 NULL, NULL,
31 "checkpoint",
32 NULL, NULL,
33 NULL, NULL
35 return entmdlnames[type];
38 void preloadentities()
40 loopi(MAXENTTYPES)
42 const char *mdl = entmdlname(i);
43 if(!mdl) continue;
44 loadmodel(mdl, -1, true);
48 void renderent(extentity &e, int type, float z, float yaw, int anim = ANIM_MAPMODEL|ANIM_LOOP, int basetime = 0, float speed = 10.0f)
50 const char *mdlname = entmdlname(type);
51 if(!mdlname) return;
52 rendermodel(e.color, e.dir, mdlname, anim, 0, 0, vec(e.o).add(vec(0, 0, z)), yaw, 0, speed, basetime, NULL, MDL_SHADOW | MDL_CULL_VFC | MDL_CULL_DIST | MDL_CULL_OCCLUDED);
55 void renderentities()
57 loopv(ents)
59 extentity &e = *ents[i];
60 if(e.type==CARROT || e.type==RESPAWNPOINT)
62 renderent(e, e.type, (float)(1+sin(cl.lastmillis/100.0+e.o.x+e.o.y)/20), cl.lastmillis/(e.attr2 ? 1.0f : 10.0f));
63 continue;
65 if(!e.spawned && e.type!=TELEPORT) continue;
66 if(e.type<I_SHELLS || e.type>TELEPORT) continue;
67 renderent(e, e.type, (float)(1+sin(cl.lastmillis/100.0+e.o.x+e.o.y)/20), cl.lastmillis/10.0f);
71 void rumble(const extentity &e)
73 playsound(S_RUMBLE, &e.o);
76 void trigger(extentity &e)
78 switch(e.attr3)
80 case 29:
81 cl.ms.endsp(false);
82 break;
86 void addammo(int type, int &v, bool local = true)
88 itemstat &is = itemstats[type-I_SHELLS];
89 v += is.add;
90 if(v>is.max) v = is.max;
91 if(local) cl.playsoundc(is.sound);
94 void repammo(fpsent *d, int type)
96 addammo(type, d->ammo[type-I_SHELLS+GUN_SG]);
99 // these two functions are called when the server acknowledges that you really
100 // picked up the item (in multiplayer someone may grab it before you).
102 void pickupeffects(int n, fpsent *d)
104 if(!ents.inrange(n)) return;
105 int type = ents[n]->type;
106 if(type<I_SHELLS || type>I_QUAD) return;
107 ents[n]->spawned = false;
108 if(!d) return;
109 itemstat &is = itemstats[type-I_SHELLS];
110 if(d!=cl.player1 || isthirdperson()) particle_text(d->abovehead(), is.name, 15);
111 playsound(itemstats[type-I_SHELLS].sound, d!=cl.player1 ? &d->o : NULL);
112 if(d!=cl.player1) return;
113 d->pickup(type);
114 switch(type)
116 case I_BOOST:
117 conoutf("\f2you have a permanent +10 health bonus! (%d)", d->maxhealth);
118 playsound(S_V_BOOST);
119 break;
121 case I_QUAD:
122 conoutf("\f2you got the quad!");
123 playsound(S_V_QUAD);
124 break;
128 // these functions are called when the client touches the item
130 void teleport(int n, fpsent *d) // also used by monsters
132 int e = -1, tag = ents[n]->attr1, beenhere = -1;
133 for(;;)
135 e = findentity(TELEDEST, e+1);
136 if(e==beenhere || e<0) { conoutf("no teleport destination for tag %d", tag); return; }
137 if(beenhere<0) beenhere = e;
138 if(ents[e]->attr2==tag)
140 d->o = ents[e]->o;
141 d->yaw = ents[e]->attr1;
142 d->pitch = 0;
143 d->vel = vec(0, 0, 0);//vec(cosf(RAD*(d->yaw-90)), sinf(RAD*(d->yaw-90)), 0);
144 entinmap(d);
145 cl.playsoundc(S_TELEPORT, d);
146 break;
151 void trypickup(int n, fpsent *d)
153 switch(ents[n]->type)
155 default:
156 if(d->canpickup(ents[n]->type))
158 cl.cc.addmsg(SV_ITEMPICKUP, "ri", n);
159 ents[n]->spawned = false; // even if someone else gets it first
161 break;
163 case TELEPORT:
165 if(d->lastpickup==ents[n]->type && cl.lastmillis-d->lastpickupmillis<500) break;
166 d->lastpickup = ents[n]->type;
167 d->lastpickupmillis = cl.lastmillis;
168 teleport(n, d);
169 break;
172 case RESPAWNPOINT:
173 if(d!=cl.player1) break;
174 if(n==cl.respawnent) break;
175 cl.respawnent = n;
176 conoutf("\f2respawn point set!");
177 playsound(S_V_RESPAWNPOINT);
178 break;
180 case JUMPPAD:
182 if(d->lastpickup==ents[n]->type && cl.lastmillis-d->lastpickupmillis<300) break;
183 d->lastpickup = ents[n]->type;
184 d->lastpickupmillis = cl.lastmillis;
185 vec v((int)(char)ents[n]->attr3*10.0f, (int)(char)ents[n]->attr2*10.0f, ents[n]->attr1*12.5f);
186 d->timeinair = 0;
187 d->gravity = vec(0, 0, 0);
188 d->vel = v;
189 // d->vel.z = 0;
190 // d->vel.add(v);
191 cl.playsoundc(S_JUMPPAD, d);
192 break;
197 void checkitems(fpsent *d)
199 if(d==cl.player1 && (editmode || cl.cc.spectator)) return;
200 vec o = d->o;
201 o.z -= d->eyeheight;
202 loopv(ents)
204 extentity &e = *ents[i];
205 if(e.type==NOTUSED) continue;
206 if(!e.spawned && e.type!=TELEPORT && e.type!=JUMPPAD && e.type!=RESPAWNPOINT) continue;
207 float dist = e.o.dist(o);
208 if(dist<(e.type==TELEPORT ? 16 : 12)) trypickup(i, d);
212 void checkquad(int time, fpsent *d)
214 if(d->quadmillis && (d->quadmillis -= time)<=0)
216 d->quadmillis = 0;
217 cl.playsoundc(S_PUPOUT, d);
218 if(d==cl.player1) conoutf("\f2quad damage is over");
222 void putitems(ucharbuf &p, int gamemode) // puts items in network stream and also spawns them locally
224 loopv(ents) if(ents[i]->type>=I_SHELLS && ents[i]->type<=I_QUAD && (!m_capture || ents[i]->type<I_SHELLS || ents[i]->type>I_CARTRIDGES))
226 putint(p, i);
227 putint(p, ents[i]->type);
229 ents[i]->spawned = (m_sp || (ents[i]->type!=I_QUAD && ents[i]->type!=I_BOOST));
233 void resetspawns() { loopv(ents) ents[i]->spawned = false; }
234 void setspawn(int i, bool on) { if(ents.inrange(i)) ents[i]->spawned = on; }
236 extentity *newentity() { return new fpsentity(); }
238 void fixentity(extentity &e)
240 switch(e.type)
242 case BOX:
243 case BARREL:
244 case PLATFORM:
245 case ELEVATOR:
246 e.attr4 = e.attr3;
247 e.attr3 = e.attr2;
248 case MONSTER:
249 case TELEDEST:
250 e.attr2 = e.attr1;
251 case RESPAWNPOINT:
252 e.attr1 = (int)cl.player1->yaw;
256 void entradius(extentity &e, float &radius, float &angle, vec &dir)
258 switch(e.type)
260 case TELEPORT:
261 loopv(ents) if(ents[i]->type == TELEDEST && e.attr1==ents[i]->attr2)
263 radius = e.o.dist(ents[i]->o);
264 dir = vec(ents[i]->o).sub(e.o).normalize();
265 break;
267 break;
269 case JUMPPAD:
270 radius = 4;
271 dir = vec((int)(char)e.attr3*10.0f, (int)(char)e.attr2*10.0f, e.attr1*12.5f).normalize();
272 break;
274 case MONSTER:
275 case TELEDEST:
276 case MAPMODEL:
277 case RESPAWNPOINT:
278 case BOX:
279 case BARREL:
280 case PLATFORM:
281 case ELEVATOR:
282 radius = 4;
283 vecfromyawpitch(e.attr1, 0, 1, 0, dir);
284 break;
288 const char *entnameinfo(entity &e) { return ""; }
289 const char *entname(int i)
291 static const char *entnames[] =
293 "none?", "light", "mapmodel", "playerstart", "envmap", "particles", "sound", "spotlight",
294 "shells", "bullets", "rockets", "riflerounds", "grenades", "cartridges",
295 "health", "healthboost", "greenarmour", "yellowarmour", "quaddamage",
296 "teleport", "teledest",
297 "monster", "carrot", "jumppad",
298 "base", "respawnpoint",
299 "box", "barrel",
300 "platform", "elevator",
301 "", "", "", "",
303 return i>=0 && size_t(i)<sizeof(entnames)/sizeof(entnames[0]) ? entnames[i] : "";
306 int extraentinfosize() { return 0; } // size in bytes of what the 2 methods below read/write... so it can be skipped by other games
308 void writeent(entity &e, char *buf) // write any additional data to disk (except for ET_ ents)
312 void readent(entity &e, char *buf) // read from disk, and init
314 int ver = getmapversion();
315 if(ver <= 10)
317 if(e.type >= 7) e.type++;
319 if(ver <= 12)
321 if(e.type >= 8) e.type++;
325 void editent(int i)
327 extentity &e = *ents[i];
328 cl.cc.addmsg(SV_EDITENT, "ri9", 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);
331 float dropheight(entity &e)
333 if(e.type==MAPMODEL || e.type==BASE) return 0.0f;
334 return 4.0f;