1 // g_newdm.c
2 // pmack
3 // june 1998
4
5 #include "g_local.h"
6 #include "m_player.h"
7
8 dm_game_rt DMGame;
9
10 // ****************************
11 // General DM Stuff
12 // ****************************
13
InitGameRules(void)14 void InitGameRules(void)
15 {
16 int gameNum;
17
18 // clear out the game rule structure before we start
19 memset(&DMGame, 0, sizeof(dm_game_rt));
20
21 if(gamerules && gamerules->value)
22 {
23 gameNum = gamerules->value;
24 switch(gameNum)
25 {
26 case RDM_TAG:
27 DMGame.GameInit = Tag_GameInit;
28 DMGame.PostInitSetup = Tag_PostInitSetup;
29 DMGame.PlayerDeath = Tag_PlayerDeath;
30 DMGame.Score = Tag_Score;
31 DMGame.PlayerEffects = Tag_PlayerEffects;
32 DMGame.DogTag = Tag_DogTag;
33 DMGame.PlayerDisconnect = Tag_PlayerDisconnect;
34 DMGame.ChangeDamage = Tag_ChangeDamage;
35 break;
36 /*
37 case RDM_DEATHBALL:
38 DMGame.GameInit = DBall_GameInit;
39 DMGame.ChangeKnockback = DBall_ChangeKnockback;
40 DMGame.ChangeDamage = DBall_ChangeDamage;
41 DMGame.ClientBegin = DBall_ClientBegin;
42 DMGame.SelectSpawnPoint = DBall_SelectSpawnPoint;
43 DMGame.PostInitSetup = DBall_PostInitSetup;
44 DMGame.CheckDMRules = DBall_CheckDMRules;
45 break;
46 */
47 // reset gamerules if it's not a valid number
48 default:
49 gamerules->value = 0;
50 break;
51 }
52 }
53
54 // if we're set up to play, initialize the game as needed.
55 if(DMGame.GameInit)
56 DMGame.GameInit();
57 }
58
59 //=================
60 //=================
61 #define IT_TYPE_MASK (IT_WEAPON|IT_AMMO|IT_POWERUP|IT_ARMOR|IT_KEY)
62
63 extern void ED_CallSpawn (edict_t *ent);
64 extern qboolean Pickup_Health (edict_t *ent, edict_t *other);
65 extern qboolean Pickup_Adrenaline (edict_t *ent, edict_t *other);
66 extern qboolean Pickup_Armor (edict_t *ent, edict_t *other);
67 extern qboolean Pickup_PowerArmor (edict_t *ent, edict_t *other);
68
FindSubstituteItem(edict_t * ent)69 char *FindSubstituteItem (edict_t *ent)
70 {
71 int i;
72 int itflags, myflags;
73 float rnd;
74 int count;
75 int pick;
76 gitem_t *it;
77
78 // there are only two classes of power armor, and we don't want
79 // to give out power screens. therefore, power shields should
80 // remain power shields. (powerscreens shouldn't be there at all...)
81 if (ent->item->pickup == Pickup_PowerArmor)
82 return NULL;
83
84 // health is special case
85 if ((ent->item->pickup == Pickup_Health) || (ent->item->pickup == Pickup_Adrenaline))
86 {
87 // health pellets stay health pellets
88 if(!strcmp(ent->classname, "item_health_small"))
89 return NULL;
90
91 rnd = random();
92 if(rnd < 0.6)
93 return "item_health";
94 else if(rnd < 0.9)
95 return "item_health_large";
96 else if(rnd < 0.99)
97 return "item_adrenaline";
98 else
99 return "item_health_mega";
100 }
101 // armor is also special case
102 else if(ent->item->pickup == Pickup_Armor)
103 {
104 // armor shards stay armor shards
105 if (ent->item->tag == ARMOR_SHARD)
106 return NULL;
107
108 rnd = random();
109 if(rnd < 0.6)
110 return "item_armor_jacket";
111 else if(rnd < 0.9)
112 return "item_armor_combat";
113 else
114 return "item_armor_body";
115 }
116
117
118 // we want to stay within the item class
119 myflags = ent->item->flags & IT_TYPE_MASK;
120 if ((myflags & IT_AMMO) && (myflags & IT_WEAPON))
121 myflags = IT_AMMO;
122
123 count = 0;
124
125 // first pass, count the matching items
126 it = itemlist;
127 for (i=0 ; i<game.num_items ; i++, it++)
128 {
129 itflags = it->flags;
130
131 if (!itflags || (itflags & IT_NOT_GIVEABLE))
132 continue;
133
134 // prox,grenades,etc should count as ammo.
135 if ((itflags & IT_AMMO) && (itflags & IT_WEAPON))
136 itflags = IT_AMMO;
137
138 // don't respawn spheres if they're dmflag disabled.
139 if ( (int)dmflags->value & DF_NO_SPHERES )
140 {
141 if (!strcmp (ent->classname, "item_sphere_vengeance") ||
142 !strcmp (ent->classname, "item_sphere_hunter") ||
143 !strcmp (ent->classname, "item_spehre_defender"))
144 {
145 continue;
146 }
147 }
148
149 if ( ((int)dmflags->value & DF_NO_NUKES) && !strcmp(ent->classname, "ammo_nuke") )
150 continue;
151
152 if ( ((int)dmflags->value & DF_NO_MINES) &&
153 (!strcmp(ent->classname, "ammo_prox") || !strcmp(ent->classname, "ammo_tesla")))
154 continue;
155
156 if ((itflags & IT_TYPE_MASK) == (myflags & IT_TYPE_MASK))
157 count++;
158 }
159
160 if(!count)
161 return NULL;
162
163 pick = ceil(random() * count);
164 count = 0;
165
166 // second pass, pick one.
167 it = itemlist;
168 for (i=0 ; i<game.num_items ; i++, it++)
169 {
170 itflags = it->flags;
171
172 if (!itflags || (itflags & IT_NOT_GIVEABLE))
173 continue;
174
175 // prox,grenades,etc should count as ammo.
176 if ((itflags & IT_AMMO) && (itflags & IT_WEAPON))
177 itflags = IT_AMMO;
178
179 if ( ((int)dmflags->value & DF_NO_NUKES) && !strcmp(ent->classname, "ammo_nuke") )
180 continue;
181
182 if ( ((int)dmflags->value & DF_NO_MINES) &&
183 (!strcmp(ent->classname, "ammo_prox") || !strcmp(ent->classname, "ammo_tesla")))
184 continue;
185
186 if ((itflags & IT_TYPE_MASK) == (myflags & IT_TYPE_MASK))
187 {
188 count++;
189 if(pick == count)
190 return it->classname;
191 }
192 }
193
194 return NULL;
195 }
196
197 //=================
198 //=================
DoRandomRespawn(edict_t * ent)199 edict_t *DoRandomRespawn (edict_t *ent)
200 {
201 edict_t *newEnt;
202 char *classname;
203
204 classname = FindSubstituteItem (ent);
205 if (classname == NULL)
206 return NULL;
207
208 gi.unlinkentity (ent);
209
210 newEnt = G_Spawn();
211 newEnt->classname = classname;
212 VectorCopy (ent->s.origin, newEnt->s.origin);
213 VectorCopy (ent->s.old_origin, newEnt->s.old_origin);
214 VectorCopy (ent->mins, newEnt->mins);
215 VectorCopy (ent->maxs, newEnt->maxs);
216
217 VectorSet (newEnt->gravityVector, 0, 0, -1);
218
219 ED_CallSpawn (newEnt);
220
221 newEnt->s.renderfx |= RF_IR_VISIBLE;
222
223 return newEnt;
224 }
225
226 //=================
227 //=================
PrecacheForRandomRespawn(void)228 void PrecacheForRandomRespawn (void)
229 {
230 gitem_t *it;
231 int i;
232 int itflags;
233
234 it = itemlist;
235 for (i=0 ; i<game.num_items ; i++, it++)
236 {
237 itflags = it->flags;
238
239 if (!itflags || (itflags & IT_NOT_GIVEABLE))
240 continue;
241
242 PrecacheItem(it);
243 }
244 }
245
246 // ***************************
247 // DOPPLEGANGER
248 // ***************************
249
250 extern edict_t *Sphere_Spawn (edict_t *owner, int spawnflags);
251
252 void fire_doppleganger (edict_t *ent, vec3_t start, vec3_t aimdir);
253 void doppleganger_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
254
doppleganger_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)255 void doppleganger_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
256 {
257 edict_t *sphere;
258 float dist;
259 vec3_t dir;
260
261 if((self->enemy) && (self->enemy != self->teammaster))
262 {
263 VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
264 dist = VectorLength(dir);
265
266 if(dist > 768)
267 {
268 sphere = Sphere_Spawn (self, SPHERE_HUNTER | SPHERE_DOPPLEGANGER);
269 sphere->pain(sphere, attacker, 0, 0);
270 }
271 else //if(dist > 256)
272 {
273 sphere = Sphere_Spawn (self, SPHERE_VENGEANCE | SPHERE_DOPPLEGANGER);
274 sphere->pain(sphere, attacker, 0, 0);
275 }
276 // else
277 // {
278 // T_RadiusClassDamage (self, self->teammaster, 175, "doppleganger", 384, MOD_DOPPLE_EXPLODE);
279 // }
280 }
281
282 if(self->teamchain)
283 BecomeExplosion1(self->teamchain);
284 BecomeExplosion1(self);
285 }
286
doppleganger_pain(edict_t * self,edict_t * other,float kick,int damage)287 void doppleganger_pain (edict_t *self, edict_t *other, float kick, int damage)
288 {
289 self->enemy = other;
290 }
291
doppleganger_timeout(edict_t * self)292 void doppleganger_timeout (edict_t *self)
293 {
294 // T_RadiusClassDamage (self, self->teammaster, 140, "doppleganger", 256, MOD_DOPPLE_EXPLODE);
295
296 if(self->teamchain)
297 BecomeExplosion1(self->teamchain);
298 BecomeExplosion1(self);
299 }
300
body_think(edict_t * self)301 void body_think (edict_t *self)
302 {
303 float r;
304
305 if(abs(self->ideal_yaw - anglemod(self->s.angles[YAW])) < 2)
306 {
307 if(self->timestamp < level.time)
308 {
309 r = random();
310 if(r < 0.10)
311 {
312 self->ideal_yaw = random() * 350.0;
313 self->timestamp = level.time + 1;
314 }
315 }
316 }
317 else
318 M_ChangeYaw(self);
319
320 self->s.frame ++;
321 if (self->s.frame > FRAME_stand40)
322 self->s.frame = FRAME_stand01;
323
324 self->nextthink = level.time + 0.1;
325 }
326
fire_doppleganger(edict_t * ent,vec3_t start,vec3_t aimdir)327 void fire_doppleganger (edict_t *ent, vec3_t start, vec3_t aimdir)
328 {
329 edict_t *base;
330 edict_t *body;
331 vec3_t dir;
332 vec3_t forward, right, up;
333 int number;
334
335 vectoangles2 (aimdir, dir);
336 AngleVectors (dir, forward, right, up);
337
338 base = G_Spawn();
339 VectorCopy (start, base->s.origin);
340 VectorCopy (dir, base->s.angles);
341 VectorClear (base->velocity);
342 VectorClear (base->avelocity);
343 base->movetype = MOVETYPE_TOSS;
344 base->solid = SOLID_BBOX;
345 base->s.renderfx |= RF_IR_VISIBLE;
346 base->s.angles[PITCH]=0;
347 VectorSet (base->mins, -16, -16, -24);
348 VectorSet (base->maxs, 16, 16, 32);
349 // base->s.modelindex = gi.modelindex ("models/objects/dopplebase/tris.md2");
350 base->s.modelindex = 0;
351 base->teammaster = ent;
352 base->svflags |= SVF_DAMAGEABLE;
353 base->takedamage = DAMAGE_AIM;
354 base->health = 30;
355 base->pain = doppleganger_pain;
356 base->die = doppleganger_die;
357
358 // FIXME - remove with style
359 base->nextthink = level.time + 30;
360 base->think = doppleganger_timeout;
361
362 base->classname = "doppleganger";
363
364 gi.linkentity (base);
365
366 body = G_Spawn();
367 number = body->s.number;
368 body->s = ent->s;
369 body->s.sound = 0;
370 body->s.event = 0;
371 // body->s.modelindex2 = 0; // no attached items (CTF flag, etc)
372 body->s.number = number;
373 body->yaw_speed = 30;
374 body->ideal_yaw = 0;
375 VectorCopy (start, body->s.origin);
376 body->s.origin[2] += 8;
377 body->think = body_think;
378 body->nextthink = level.time + FRAMETIME;
379 gi.linkentity (body);
380
381 base->teamchain = body;
382 body->teammaster = base;
383 }
384
385