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