1 enum                            // static entity types
2 {
3     NOTUSED = 0,                // entity slot not in use in map
4     LIGHT,                      // lightsource, attr1 = radius, attr2 = intensity
5     PLAYERSTART,                // attr1 = angle, attr2 = team
6     I_CLIPS, I_AMMO, I_GRENADE,
7     I_HEALTH, I_HELMET, I_ARMOUR, I_AKIMBO,
8                                 // helmet : 2010may16 -> mapversion:8
9     MAPMODEL,                   // attr1 = angle, attr2 = idx
10     CARROT,                     // attr1 = tag, attr2 = type
11     LADDER,
12     CTF_FLAG,                   // attr1 = angle, attr2 = red/blue
13     SOUND,
14     CLIP,
15     PLCLIP,
16     MAXENTTYPES
17 };
18 
19 enum {MAP_IS_BAD, MAP_IS_EDITABLE, MAP_IS_GOOD};
20 
21 extern const char *entnames[MAXENTTYPES];
22 #define isitem(i) ((i) >= I_CLIPS && (i) <= I_AKIMBO)
23 
24 struct persistent_entity        // map entity
25 {
26     short x, y, z;              // cube aligned position
27     short attr1;
28     uchar type;                 // type is one of the above
29     uchar attr2, attr3, attr4;
persistent_entitypersistent_entity30     persistent_entity(short x, short y, short z, uchar type, short attr1, uchar attr2, uchar attr3, uchar attr4) : x(x), y(y), z(z), attr1(attr1), type(type), attr2(attr2), attr3(attr3), attr4(attr4) {}
persistent_entitypersistent_entity31     persistent_entity() {}
32 };
33 
34 struct entity : persistent_entity
35 {
36     bool spawned;               //the dynamic states of a map entity
37     int lastmillis;
entityentity38     entity(short x, short y, short z, uchar type, short attr1, uchar attr2, uchar attr3, uchar attr4) : persistent_entity(x, y, z, type, attr1, attr2, attr3, attr4), spawned(false) {}
entityentity39     entity() {}
fitsmodeentity40     bool fitsmode(int gamemode) { return !m_noitems && isitem(type) && !(m_noitemsnade && type!=I_GRENADE) && !(m_pistol && type==I_AMMO); }
transformtypeentity41     void transformtype(int gamemode)
42     {
43         if(m_noitemsnade && type==I_CLIPS) type = I_GRENADE;
44         else if(m_pistol && ( type==I_AMMO || type==I_GRENADE )) type = I_CLIPS;
45     }
46 };
47 
48 enum { GUN_KNIFE = 0, GUN_PISTOL, GUN_CARBINE, GUN_SHOTGUN, GUN_SUBGUN, GUN_SNIPER, GUN_ASSAULT, GUN_CPISTOL, GUN_GRENADE, GUN_AKIMBO, NUMGUNS };
49 #define reloadable_gun(g) (g != GUN_KNIFE && g != GUN_GRENADE)
50 
51 #define SGRAYS 21
52 #define SGDMGTOTAL 90
53 
54 #define SGDMGBONUS 65
55 #define SGDMGDISTB 50
56 
57 #define SGCCdmg 500
58 #define SGCCbase 0
59 #define SGCCrange 40
60 
61 #define SGCMdmg 375
62 #define SGCMbase 25
63 #define SGCMrange 60
64 
65 #define SGCOdmg 125
66 #define SGCObase 45
67 #define SGCOrange 75
68 
69 #define SGMAXDMGABS 105
70 #define SGMAXDMGLOC 84
71 #define SGBONUSDIST 80
72 #define SGSEGDMG_O 3
73 #define SGSEGDMG_M 6
74 #define SGSEGDMG_C 4
75 #define SGSPREAD 2.25
76 #define EXPDAMRAD 10
77 
78 struct itemstat { int add, start, max, sound; };
79 extern itemstat ammostats[NUMGUNS];
80 extern itemstat powerupstats[I_ARMOUR-I_HEALTH+1];
81 
82 struct guninfo { string modelname; short sound, reload, reloadtime, attackdelay, damage, piercing, projspeed, part, spread, recoil, magsize, mdl_kick_rot, mdl_kick_back, recoilincrease, recoilbase, maxrecoil, recoilbackfade, pushfactor; bool isauto; };
83 extern guninfo guns[NUMGUNS];
84 
reloadtime(int gun)85 static inline int reloadtime(int gun) { return guns[gun].reloadtime; }
attackdelay(int gun)86 static inline int attackdelay(int gun) { return guns[gun].attackdelay; }
magsize(int gun)87 static inline int magsize(int gun) { return guns[gun].magsize; }
88 
89 /** roseta stone:
90        0000,         0001,      0010,           0011,            0100,       0101,     0110 */
91 enum { TEAM_CLA = 0, TEAM_RVSF, TEAM_CLA_SPECT, TEAM_RVSF_SPECT, TEAM_SPECT, TEAM_NUM, TEAM_ANYACTIVE };
92 extern const char *teamnames[TEAM_NUM+1];
93 extern const char *teamnames_s[TEAM_NUM+1];
94 
95 #define TEAM_VOID TEAM_NUM
96 #define isteam(a,b)   (m_teammode && (a) == (b))
97 #define team_opposite(o) (team_isvalid(o) && (o) < TEAM_SPECT ? (o) ^ 1 : TEAM_SPECT)
98 #define team_base(t) ((t) & 1)
99 #define team_basestring(t) ((t) == 1 ? teamnames[1] : ((t) == 0 ? teamnames[0] : "SPECT"))
100 #define team_isvalid(t) ((int(t)) >= 0 && (t) < TEAM_NUM)
101 #define team_isactive(t) ((t) == TEAM_CLA || (t) == TEAM_RVSF)
102 #define team_isspect(t) ((t) > TEAM_RVSF && (t) < TEAM_VOID)
103 #define team_group(t) ((t) == TEAM_SPECT ? TEAM_SPECT : team_base(t))
104 #define team_tospec(t) ((t) == TEAM_SPECT ? TEAM_SPECT : team_base(t) + TEAM_CLA_SPECT - TEAM_CLA)
105 // note: team_isactive and team_base can/should be used to check the limits for arrays of size '2'
106 static inline const char *team_string(int t, bool abbr = false) { const char **n = abbr ? teamnames_s : teamnames; return team_isvalid(t) ? n[t] : n[TEAM_NUM]; }
107 
108 enum { ENT_PLAYER = 0, ENT_BOT, ENT_CAMERA, ENT_BOUNCE };
109 enum { CS_ALIVE = 0, CS_DEAD, CS_SPAWNING, CS_LAGGED, CS_EDITING, CS_SPECTATE };
110 enum { CR_DEFAULT = 0, CR_ADMIN };
111 enum { SM_NONE = 0, SM_DEATHCAM, SM_FOLLOW1ST, SM_FOLLOW3RD, SM_FOLLOW3RD_TRANSPARENT, SM_FLY, SM_OVERVIEW, SM_NUM };
112 enum { FPCN_VOID = -4, FPCN_DEATHCAM = -2, FPCN_FLY = -2, FPCN_OVERVIEW = -1 };
113 
114 class worldobject
115 {
116 public:
~worldobject()117     virtual ~worldobject() {};
118 };
119 
120 class physent : public worldobject
121 {
122 public:
123     vec o, vel, vel_t;                         // origin, velocity
124     vec deltapos, newpos;                       // movement interpolation
125     float yaw, pitch, roll;             // used as vec in one place
126     float pitchvel;
127     float maxspeed;                     // cubes per second, 24 for player
128     int timeinair;                      // used for fake gravity
129     float radius, eyeheight, maxeyeheight, aboveeye;  // bounding box size
130     bool inwater;
131     bool onfloor, onladder, jumpnext, crouching, crouchedinair, trycrouch, cancollide, stuck, scoping, shoot;
132     int lastjump;
133     float lastjumpheight;
134     int lastsplash;
135     char move, strafe;
136     uchar state, type;
137     float eyeheightvel;
138     int last_pos;
139 
physent()140     physent() : o(0, 0, 0), deltapos(0, 0, 0), newpos(0, 0, 0), yaw(270), pitch(0), roll(0), pitchvel(0),
141             crouching(false), crouchedinair(false), trycrouch(false), cancollide(true), stuck(false), scoping(false), shoot(false), lastjump(0), lastjumpheight(0), lastsplash(0), state(CS_ALIVE), last_pos(0)
142     {
143         reset();
144     }
~physent()145     virtual ~physent() {}
146 
resetinterp()147     void resetinterp()
148     {
149         newpos = o;
150         newpos.z -= eyeheight;
151         deltapos = vec(0, 0, 0);
152     }
153 
reset()154     void reset()
155     {
156         vel.x = vel.y = vel.z = eyeheightvel = vel_t.x = vel_t.y = vel_t.z = 0.0f;
157         move = strafe = 0;
158         timeinair = lastjump = lastsplash = 0;
159         onfloor = onladder = inwater = jumpnext = crouching = crouchedinair = trycrouch = stuck = false;
160         last_pos = 0;
161     }
162 
oncollision()163     virtual void oncollision() {}
onmoved(const vec & dist)164     virtual void onmoved(const vec &dist) {}
165 };
166 
167 class dynent : public physent                 // animated ent
168 {
169 public:
170     bool k_left, k_right, k_up, k_down;         // see input code
171 
172     animstate prev[2], current[2];              // md2's need only [0], md3's need both for the lower&upper model
173     int lastanimswitchtime[2];
174     void *lastmodel[2];
175     int lastrendered;
176 
stopmoving()177     void stopmoving()
178     {
179         k_left = k_right = k_up = k_down = jumpnext = false;
180         move = strafe = 0;
181     }
182 
resetanim()183     void resetanim()
184     {
185         loopi(2)
186         {
187             prev[i].reset();
188             current[i].reset();
189             lastanimswitchtime[i] = -1;
190             lastmodel[i] = NULL;
191         }
192         lastrendered = 0;
193     }
194 
reset()195     void reset()
196     {
197         physent::reset();
198         stopmoving();
199     }
200 
dynent()201     dynent() { reset(); resetanim(); }
~dynent()202     virtual ~dynent() {}
203 };
204 
205 #define MAXNAMELEN 15
206 
207 class bounceent;
208 
209 #define POSHIST_SIZE 7
210 
211 struct poshist
212 {
213     int nextupdate, curpos, numpos;
214     vec pos[POSHIST_SIZE];
215 
poshistposhist216     poshist() : nextupdate(0) { reset(); }
217 
sizeposhist218     const int size() const { return numpos; }
219 
resetposhist220     void reset()
221     {
222         curpos = 0;
223         numpos = 0;
224     }
225 
addposposhist226     void addpos(const vec &o)
227     {
228         pos[curpos] = o;
229         curpos++;
230         if(curpos>=POSHIST_SIZE) curpos = 0;
231         if(numpos<POSHIST_SIZE) numpos++;
232     }
233 
getposposhist234     const vec &getpos(int i) const
235     {
236         i = curpos-1-i;
237         if(i < 0) i += POSHIST_SIZE;
238         return pos[i];
239     }
240 
updateposhist241     void update(const vec &o, int lastmillis)
242     {
243         if(lastmillis<nextupdate) return;
244         if(o.dist(pos[0]) >= 4.0f) addpos(o);
245         nextupdate = lastmillis + 100;
246     }
247 };
248 
249 class playerstate
250 {
251 public:
252     int health, armour;
253     int primary, nextprimary;
254     int gunselect;
255     bool akimbo;
256     int ammo[NUMGUNS], mag[NUMGUNS], gunwait[NUMGUNS];
257     int pstatshots[NUMGUNS], pstatdamage[NUMGUNS];
258 
playerstate()259     playerstate() : armour(0), primary(GUN_ASSAULT), nextprimary(GUN_ASSAULT), akimbo(false) {}
~playerstate()260     virtual ~playerstate() {}
261 
resetstats()262     void resetstats() { loopi(NUMGUNS) pstatshots[i] = pstatdamage[i] = 0; }
263 
itemstats(int type)264     itemstat &itemstats(int type)
265     {
266         switch(type)
267         {
268             case I_CLIPS: return ammostats[GUN_PISTOL];
269             case I_AMMO: return ammostats[primary];
270             case I_GRENADE: return ammostats[GUN_GRENADE];
271             case I_AKIMBO: return ammostats[GUN_AKIMBO];
272             case I_HEALTH:
273             case I_HELMET:
274             case I_ARMOUR:
275                 return powerupstats[type-I_HEALTH];
276             default:
277                 return *(itemstat *)0;
278         }
279     }
280 
canpickup(int type)281     bool canpickup(int type)
282     {
283         switch(type)
284         {
285             case I_CLIPS: return ammo[akimbo ? GUN_AKIMBO : GUN_PISTOL]<ammostats[akimbo ? GUN_AKIMBO : GUN_PISTOL].max;
286             case I_AMMO: return ammo[primary]<ammostats[primary].max;
287             case I_GRENADE: return mag[GUN_GRENADE]<ammostats[GUN_GRENADE].max;
288             case I_HEALTH: return health<powerupstats[type-I_HEALTH].max;
289             case I_HELMET:
290             case I_ARMOUR: return armour<powerupstats[type-I_HEALTH].max;
291             case I_AKIMBO: return !akimbo;
292             default: return false;
293         }
294     }
295 
additem(itemstat & is,int & v)296     void additem(itemstat &is, int &v)
297     {
298         v += is.add;
299         if(v > is.max) v = is.max;
300     }
301 
pickup(int type)302     void pickup(int type)
303     {
304         switch(type)
305         {
306             case I_CLIPS:
307                 additem(ammostats[GUN_PISTOL], ammo[GUN_PISTOL]);
308                 additem(ammostats[GUN_AKIMBO], ammo[GUN_AKIMBO]);
309                 break;
310             case I_AMMO: additem(ammostats[primary], ammo[primary]); break;
311             case I_GRENADE: additem(ammostats[GUN_GRENADE], mag[GUN_GRENADE]); break;
312             case I_HEALTH: additem(powerupstats[type-I_HEALTH], health); break;
313             case I_HELMET:
314             case I_ARMOUR:
315                 additem(powerupstats[type-I_HEALTH], armour); break;
316             case I_AKIMBO:
317                 akimbo = true;
318                 mag[GUN_AKIMBO] = guns[GUN_AKIMBO].magsize;
319                 additem(ammostats[GUN_AKIMBO], ammo[GUN_AKIMBO]);
320                 break;
321         }
322     }
323 
respawn()324     void respawn()
325     {
326         health = 100;
327         armour = 0;
328         gunselect = GUN_PISTOL;
329         akimbo = false;
330         loopi(NUMGUNS) ammo[i] = mag[i] = gunwait[i] = 0;
331         ammo[GUN_KNIFE] = mag[GUN_KNIFE] = 1;
332     }
333 
spawnstate(int gamemode)334     virtual void spawnstate(int gamemode)
335     {
336         if(m_pistol) primary = GUN_PISTOL;
337         else if(m_osok) primary = GUN_SNIPER;
338         else if(m_lss) primary = GUN_KNIFE;
339         else primary = nextprimary;
340 
341         if(!m_nopistol)
342         {
343             ammo[GUN_PISTOL] = ammostats[GUN_PISTOL].start-magsize(GUN_PISTOL);//ammostats[GUN_PISTOL].max-magsize(GUN_PISTOL);
344             mag[GUN_PISTOL] = magsize(GUN_PISTOL);
345         }
346 
347         if(!m_noprimary)
348         {
349             ammo[primary] = ammostats[primary].start-magsize(primary);
350             mag[primary] = magsize(primary);
351         }
352 
353         gunselect = primary;
354 
355         if(m_osok) health = 1;
356         if(m_lms) // Survivor && Team-Survivor : 2010nov19
357         {
358             health = 100;
359             armour = 100;
360             ammo[GUN_GRENADE] = 2;
361         }
362     }
363 
364     // just subtract damage here, can set death, etc. later in code calling this
dodamage(int damage,int gun)365     int dodamage(int damage, int gun)
366     {
367         guninfo gi = guns[gun];
368         if(damage == INT_MAX)
369         {
370             damage = health;
371             armour = health = 0;
372             return damage;
373         }
374 
375         // 4-level armour - tiered approach: 16%, 33%, 37%, 41%
376         // Please update ./ac_website/htdocs/docs/introduction.html if this changes.
377         int armoursection = 0;
378         int ad = damage;
379         if(armour > 25) armoursection = 1;
380         if(armour > 50) armoursection = 2;
381         if(armour > 75) armoursection = 3;
382         switch(armoursection)
383         {
384             case 0: ad = (int) (16.0f/25.0f * armour); break;             // 16
385             case 1: ad = (int) (17.0f/25.0f * armour) - 1; break;         // 33
386             case 2: ad = (int) (4.0f/25.0f * armour) + 25; break;         // 37
387             case 3: ad = (int) (4.0f/25.0f * armour) + 25; break;         // 41
388             default: break;
389         }
390 
391         //ra - reduced armor
392         //rd - reduced damage
393         int ra = (int) (ad * damage/100.0f);
394         int rd = ra-(ra*(gi.piercing/100.0f)); //Who cares about rounding errors anyways?
395 
396         armour -= ra;
397         damage -= rd;
398 
399         health -= damage;
400         return damage;
401     }
402 };
403 
404 #ifndef STANDALONE
405 #define HEADSIZE 0.4f
406 
407 class playerent : public dynent, public playerstate
408 {
409 private:
410     int curskin, nextskin[2];
411 public:
412     int clientnum, lastupdate, plag, ping;
413     enet_uint32 address;
414     int lifesequence;                   // sequence id for each respawn, used in damage test
415     int frags, flagscore, deaths, points, tks;
416     int lastaction, lastmove, lastpain, lastvoicecom;
417     int clientrole;
418     bool attacking;
419     string name;
420     int team;
421     int weaponchanging;
422     int nextweapon; // weapon we switch to
423     int spectatemode, followplayercn;
424     int eardamagemillis;
425     int respawnoffset;
allowmove()426     bool allowmove() { return state!=CS_DEAD || spectatemode==SM_FLY; }
427 
428     weapon *weapons[NUMGUNS];
429     weapon *prevweaponsel, *weaponsel, *nextweaponsel, *primweap, *nextprimweap, *lastattackweapon;
430 
431     poshist history; // Previous stored locations of this player
432 
433     const char *skin_noteam, *skin_cla, *skin_rvsf;
434 
435     float deltayaw, deltapitch, newyaw, newpitch;
436     int smoothmillis;
437 
438     vec head;
439 
440     bool ignored, muted;
441 
playerent()442     playerent() : curskin(0), clientnum(-1), lastupdate(0), plag(0), ping(0), address(0), lifesequence(0), frags(0), flagscore(0), deaths(0), points(0), tks(0), lastpain(0), lastvoicecom(0), clientrole(CR_DEFAULT),
443                   team(TEAM_SPECT), spectatemode(SM_NONE), followplayercn(FPCN_VOID), eardamagemillis(0), respawnoffset(0),
444                   prevweaponsel(NULL), weaponsel(NULL), nextweaponsel(NULL), primweap(NULL), nextprimweap(NULL), lastattackweapon(NULL),
445                   smoothmillis(-1),
446                   head(-1, -1, -1), ignored(false), muted(false)
447     {
448         type = ENT_PLAYER;
449         name[0] = 0;
450         maxeyeheight = 4.5f;
451         aboveeye = 0.7f;
452         radius = 1.1f;
453         maxspeed = 16.0f;
454         skin_noteam = skin_cla = skin_rvsf = NULL;
455         loopi(2) nextskin[i] = 0;
456         respawn();
457     }
458 
~playerent()459     virtual ~playerent()
460     {
461         extern void removebounceents(playerent *owner);
462         extern void removedynlights(physent *owner);
463         extern void zapplayerflags(playerent *owner);
464         extern void cleanplayervotes(playerent *owner);
465         extern physent *camera1;
466         extern void togglespect();
467         removebounceents(this);
468         audiomgr.detachsounds(this);
469         removedynlights(this);
470         zapplayerflags(this);
471         cleanplayervotes(this);
472         if(this==camera1) togglespect();
473     }
474 
damageroll(float damage)475     void damageroll(float damage)
476     {
477         extern void clamproll(physent *pl);
478         float damroll = 2.0f*damage;
479         roll += roll>0 ? damroll : (roll<0 ? -damroll : (rnd(2) ? damroll : -damroll)); // give player a kick
480         clamproll(this);
481     }
482 
hitpush(int damage,const vec & dir,playerent * actor,int gun)483     void hitpush(int damage, const vec &dir, playerent *actor, int gun)
484     {
485         if(gun<0 || gun>NUMGUNS) return;
486         vec push(dir);
487         push.mul(damage/100.0f*guns[gun].pushfactor);
488         vel.add(push);
489         extern int lastmillis;
490         if(gun==GUN_GRENADE && damage > 50) eardamagemillis = lastmillis+damage*100;
491     }
492 
resetspec()493     void resetspec()
494     {
495         spectatemode = SM_NONE;
496         followplayercn = FPCN_VOID;
497     }
498 
respawn()499     void respawn()
500     {
501         dynent::reset();
502         playerstate::respawn();
503         history.reset();
504         if(weaponsel) weaponsel->reset();
505         lastaction = 0;
506         lastattackweapon = NULL;
507         attacking = false;
508         extern int lastmillis;
509         weaponchanging = lastmillis - weapons[gunselect]->weaponchangetime/2; // 2011jan16:ft: for a little no-shoot after spawn
510         resetspec();
511         eardamagemillis = 0;
512         eyeheight = maxeyeheight;
513         curskin = nextskin[team_base(team)];
514     }
515 
spawnstate(int gamemode)516     void spawnstate(int gamemode)
517     {
518         playerstate::spawnstate(gamemode);
519         prevweaponsel = weaponsel = weapons[gunselect];
520         primweap = weapons[primary];
521         nextprimweap = weapons[nextprimary];
522         curskin = nextskin[team_base(team)];
523     }
524 
selectweapon(int w)525     void selectweapon(int w) { if (weaponsel) prevweaponsel = weaponsel; weaponsel = weapons[(gunselect = w)]; if (!prevweaponsel) prevweaponsel = weaponsel; }
setprimary(int w)526     void setprimary(int w) { primweap = weapons[(primary = w)]; }
setnextprimary(int w)527     void setnextprimary(int w) { nextprimweap = weapons[(nextprimary = w)]; }
isspectating()528     bool isspectating() { return state==CS_SPECTATE || (state==CS_DEAD && spectatemode > SM_NONE); }
weaponswitch(weapon * w)529     void weaponswitch(weapon *w)
530     {
531         if(!w) return;
532         extern int lastmillis;
533         weaponsel->ondeselecting();
534         weaponchanging = lastmillis;
535         nextweaponsel = w;
536         w->onselecting();
537     }
538     int skin(int t = -1) { return nextskin[team_base(t < 0 ? team : t)]; }
setskin(int t,int s)539     void setskin(int t, int s)
540     {
541         const int maxskin[2] = { 4, 6 };
542         t = team_base(t < 0 ? team : t);
543         nextskin[t] = iabs(s) % maxskin[t];
544     }
545 };
546 
547 
548 
549 class CBot;
550 
551 class botent : public playerent
552 {
553 public:
554     // Added by Rick
555     CBot *pBot; // Only used if this is a bot, points to the bot class if we are the host,
556                 // for other clients its NULL
557     // End add by Rick
558 
559     playerent *enemy;                      // monster wants to kill this entity
560     // Added by Rick: targetpitch
561     float targetpitch;                    // monster wants to look in this direction
562     // End add
563     float targetyaw;                    // monster wants to look in this direction
564 
botent()565     botent() : pBot(NULL), enemy(NULL) { type = ENT_BOT; }
~botent()566     ~botent() { }
567 
deaths()568     int deaths() { return lifesequence; }
569 };
570 #endif //#ifndef STANDALONE
571 
572 // flag-mode entities
573 
574 enum { CTFF_INBASE = 0, CTFF_STOLEN, CTFF_DROPPED, CTFF_IDLE };
575 
576 struct flaginfo
577 {
578     int team;
579     entity *flagent;
580     int actor_cn;
581     playerent *actor;
582     vec pos;
583     int state; // one of CTFF_*
584     bool ack;
flaginfoflaginfo585     flaginfo() : flagent(0), actor(0), state(CTFF_INBASE), ack(false) {}
586 };
587 
588 // nades, gibs
589 
590 enum { BT_NONE, BT_NADE, BT_GIB };
591 
592 class bounceent : public physent
593 {
594 public:
595     int millis, timetolive, bouncetype; // see enum above
596     float rotspeed;
597     bool plclipped;
598     playerent *owner;
599 
bounceent()600     bounceent() : bouncetype(BT_NONE), rotspeed(1.0f), plclipped(false), owner(NULL)
601     {
602         type = ENT_BOUNCE;
603         maxspeed = 40;
604         radius = 0.2f;
605         eyeheight = maxeyeheight = 0.3f;
606         aboveeye = 0.0f;
607     }
608 
~bounceent()609     virtual ~bounceent() {}
610 
isalive(int lastmillis)611     bool isalive(int lastmillis) { return lastmillis - millis < timetolive; }
destroy()612     virtual void destroy() {}
applyphysics()613     virtual bool applyphysics() { return true; }
614 };
615 
616 struct hitmsg
617 {
618     int target, lifesequence, info;
619     ivec dir;
620 };
621 
622 class grenadeent : public bounceent
623 {
624 public:
625     bool local;
626     int nadestate;
627     float distsincebounce;
628     grenadeent(playerent *owner, int millis = 0);
629     ~grenadeent();
630     void activate(const vec &from, const vec &to);
631     void _throw(const vec &from, const vec &vel);
632     void explode();
633     void splash();
634     virtual void destroy();
635     virtual bool applyphysics();
636     void moveoutsidebbox(const vec &direction, playerent *boundingbox);
637     void oncollision();
638     void onmoved(const vec &dist);
639 };
640 
641 enum {MD_FRAGS = 0, MD_DEATHS, END_MDS};
642 struct medalsst {bool assigned; int cn; int item;};
643 
644 #define MAXKILLMSGLEN 16
645 extern char killmessages[2][NUMGUNS][MAXKILLMSGLEN];
646 inline const char *killmessage(int gun, bool gib = false)
647 {
648     if(gun<0 || gun>=NUMGUNS) return "";
649 
650     return killmessages[gib?1:0][gun];
651 }
652 
653 #ifndef STANDALONE
654 struct pckserver
655 {
656     char *addr;
657     bool pending, responsive;
658     int ping;
659 
pckserverpckserver660     pckserver() : addr(NULL), pending(false), responsive(true), ping(-1) {}
661 };
662 
663 enum { PCK_TEXTURE, PCK_SKYBOX, PCK_MAPMODEL, PCK_AUDIO, PCK_MAP, PCK_NUM };
664 
665 struct package
666 {
667     char *name;
668     int type, number;
669     bool pending;
670     pckserver *source;
671     CURL *curl;
672 
packagepackage673     package() : name(NULL), type(-1), number(0), pending(false), source(NULL), curl(NULL) {}
674 };
675 #endif