1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "g_local.h"
25 
26 int imageindex_sbfctf1;
27 int imageindex_sbfctf2;
28 
29 /*
30 ==================
31 CTFScoreboardMessage
32 ==================
33 */
CTFScoreboardMessage(edict_t * ent,edict_t * killer,int mapvote)34 void CTFScoreboardMessage (edict_t *ent, edict_t *killer, int mapvote)
35 {
36 	char	entry[1024];
37 	char	string[1400];
38 	int		len;
39 	int		i, j, k, x, y;
40 	int		sorted[2][MAX_CLIENTS];
41 	int		sortedscores[2][MAX_CLIENTS];
42 	int		score, total[2], totalscore[2];
43 
44 	gclient_t	*cl;
45 	edict_t		*cl_ent;
46 	int team;
47 	int maxsize = 1024;
48 	gitem_t *flag1_item, *flag2_item;
49 
50 	flag1_item = FindItemByClassname("item_flag_red");
51 	flag2_item = FindItemByClassname("item_flag_blue");
52 
53 	// sort the clients by team and score
54 	total[0] = total[1] = 0;
55 	totalscore[0] = totalscore[1] = 0;
56 	for (i=0 ; i<game.maxclients ; i++)
57 	{
58 		cl_ent = g_edicts + 1 + i;
59 		if (!cl_ent->inuse)
60 			continue;
61 		if (cl_ent->dmteam == RED_TEAM)
62 			team = 0;
63 		else if (cl_ent->dmteam == BLUE_TEAM)
64 			team = 1;
65 		else
66 			continue; // unknown team?
67 
68 		score = game.clients[i].resp.score;
69 		for (j=0 ; j<total[team] ; j++)
70 		{
71 			if (score > sortedscores[team][j])
72 				break;
73 		}
74 		for (k=total[team] ; k>j ; k--)
75 		{
76 			sorted[team][k] = sorted[team][k-1];
77 			sortedscores[team][k] = sortedscores[team][k-1];
78 		}
79 		sorted[team][j] = i;
80 		sortedscores[team][j] = score;
81 		totalscore[team] += score;
82 		total[team]++;
83 	}
84 
85 	// print level name and exit rules
86 	// add the clients in sorted order
87 	*string = 0;
88 	len = 0;
89 
90 	if(ctf->value) {
91 		//do ctf
92 		sprintf(string, "newctfsb xv -16 yv -8 picn ctf1 "
93 			"xv +12 yv 4 num 3 21 "
94 			"xv 238 yv -8 picn ctf2 "
95 			"xv 264 yv 4 num 3 22 ");
96 		len = strlen(string);
97 	}
98 	else {
99 		//regular team games
100 		sprintf(string, "newctfsb xv -16 yv -8 picn team1 "
101 			"xv +12 yv 4 num 3 21 "
102 			"xv 238 yv -8 picn team2 "
103 			"xv 264 yv 4 num 3 22 ");
104 		len = strlen(string);
105 	}
106 
107 	for (i=0 ; i<16 ; i++)
108 	{
109 		if (i >= total[0] && i >= total[1])
110 			break; // we're done
111 
112 		*entry = 0;
113 
114 		// left side
115 		if (i < total[0]) {
116 			cl = &game.clients[sorted[0][i]];
117 			cl_ent = g_edicts + 1 + sorted[0][i];
118 
119 			sprintf(entry+strlen(entry),
120 				"ctf -96 %d %d %d %d ",
121 				42 + i * 16,
122 				sorted[0][i],
123 				cl->resp.score,
124 				cl->ping > 999 ? 999 : cl->ping);
125 
126 			if (cl_ent->client->pers.inventory[ITEM_INDEX(flag2_item)])
127 				sprintf(entry + strlen(entry), "xv -92 yv %d picn sbfctf2 ",
128 					43 + i * 16);
129 
130 			if (maxsize - len > strlen(entry)) {
131 				strcat(string, entry);
132 				len = strlen(string);
133 			}
134 		}
135 
136 		// right side
137 		if (i < total[1]) {
138 			cl = &game.clients[sorted[1][i]];
139 			cl_ent = g_edicts + 1 + sorted[1][i];
140 
141 			sprintf(entry+strlen(entry),
142 				"ctf 160 %d %d %d %d ",
143 				42 + i * 16,
144 				sorted[1][i],
145 				cl->resp.score,
146 				cl->ping > 999 ? 999 : cl->ping);
147 
148 			if (cl_ent->client->pers.inventory[ITEM_INDEX(flag1_item)])
149 				sprintf(entry + strlen(entry), "xv 164 yv %d picn sbfctf1 ",
150 					43 + i * 16);
151 
152 			if (maxsize - len > strlen(entry)) {
153 				strcat(string, entry);
154 				len = strlen(string);
155 			}
156 		}
157 	}
158 
159 	if(mapvote) {
160 		y = 64;
161 		x = 96;
162 		Com_sprintf(entry, sizeof(entry),
163 			"xv %i yt %i string Vote ", x, y);
164 		if(maxsize - len > strlen(entry)) {
165 			strcat(string, entry);
166 			len = strlen(string);
167 		}
168 		x = 136;
169 		Com_sprintf(entry, sizeof(entry),
170 			"xv %i yt %i string for ", x, y);
171 		if(maxsize - len > strlen(entry)) {
172 			strcat(string, entry);
173 			len = strlen(string);
174 		}
175 		x = 168;
176 		Com_sprintf(entry, sizeof(entry),
177 			"xv %i yt %i string next ", x, y);
178 		if(maxsize - len > strlen(entry)) {
179 			strcat(string, entry);
180 			len = strlen(string);
181 		}
182 		x = 208;
183 		Com_sprintf(entry, sizeof(entry),
184 			"xv %i yt %i string map: ", x, y);
185 		if(maxsize - len > strlen(entry)) {
186 			strcat(string, entry);
187 			len = strlen(string);
188 		}
189 		x = 96;
190 		for(i=0; i<4; i++) {
191 
192 			Com_sprintf(entry, sizeof(entry),
193 			"xv %i yt %i string %i.%s ", x, y+((i+1)*9)+9, i+1, votedmap[i].mapname);
194 			if(maxsize - len > strlen(entry)) {
195 				strcat(string, entry);
196 				len = strlen(string);
197 			}
198 		}
199 
200 	}
201 
202 	gi.WriteByte (svc_layout);
203 	gi.WriteString (string);
204 }
205 /*
206  * Precache CTF items
207  */
208 
CTFPrecache(void)209 void CTFPrecache(void)
210 {
211 	imageindex_sbfctf1 =  gi.imageindex("sbfctf1");
212 	imageindex_sbfctf2 =  gi.imageindex("sbfctf2");
213 }
214 
215 
216 /*------------------------------------------------------------------------*/
217 /* GRAPPLE																  */
218 /*------------------------------------------------------------------------*/
219 
220 // ent is player
CTFPlayerResetGrapple(edict_t * ent)221 void CTFPlayerResetGrapple(edict_t *ent)
222 {
223 	if (ent->client && ent->client->ctf_grapple)
224 		CTFResetGrapple(ent->client->ctf_grapple);
225 }
226 
227 // self is grapple, not player
CTFResetGrapple(edict_t * self)228 void CTFResetGrapple(edict_t *self)
229 {
230 	if (self->owner->client->ctf_grapple) {
231 		float volume = 1.0;
232 		gclient_t *cl;
233 
234 		if (self->owner->client->silencer_shots)
235 			volume = 0.2;
236 
237 		cl = self->owner->client;
238 		cl->ctf_grapple = NULL;
239 		cl->ctf_grapplereleasetime = level.time;
240 		cl->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
241 		cl->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
242 		G_FreeEdict(self);
243 	}
244 }
245 
CTFGrappleTouch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)246 void CTFGrappleTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
247 {
248 	// float volume = 1.0;
249 
250 	if (other == self->owner)
251 		return;
252 
253 	if (self->owner->client->ctf_grapplestate != CTF_GRAPPLE_STATE_FLY)
254 		return;
255 
256 	if (surf && (surf->flags & SURF_SKY))
257 	{
258 		CTFResetGrapple(self);
259 		return;
260 	}
261 
262 	VectorCopy(vec3_origin, self->velocity);
263 
264 	PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
265 
266 	if (other->takedamage) {
267 		T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, 0, MOD_GRAPPLE);
268 	}
269 
270 	self->owner->client->ctf_grapplestate = CTF_GRAPPLE_STATE_PULL; // we're on hook
271 	self->enemy = other;
272 
273 	self->solid = SOLID_NOT;
274 
275 }
276 
277 // draw beam between grapple and self
CTFGrappleDrawCable(edict_t * self)278 void CTFGrappleDrawCable(edict_t *self)
279 {
280 	vec3_t	offset, start, end, f, r;
281 	vec3_t	dir;
282 	float	distance;
283 
284 	AngleVectors (self->owner->client->v_angle, f, r, NULL);
285 	VectorSet(offset, 32, 6, self->owner->viewheight-5);
286 	P_ProjectSource (self->owner->client, self->owner->s.origin, offset, f, r, start);
287 
288 	VectorSubtract(start, self->owner->s.origin, offset);
289 
290 	VectorSubtract (start, self->s.origin, dir);
291 	distance = VectorLength(dir);
292 	// don't draw cable if close
293 	if (distance < 64)
294 		return;
295 
296 #if 0
297 	if (distance > 256)
298 		return;
299 
300 	// check for min/max pitch
301 	vectoangles (dir, angles);
302 	if (angles[0] < -180)
303 		angles[0] += 360;
304 	if (fabs(angles[0]) > 45)
305 		return;
306 
307 	trace_t	tr; //!!
308 
309 	tr = gi.trace (start, NULL, NULL, self->s.origin, self, MASK_SHOT);
310 	if (tr.ent != self) {
311 		CTFResetGrapple(self);
312 		return;
313 	}
314 #endif
315 
316 	VectorCopy (self->s.origin, end);
317 
318 	gi.WriteByte (svc_temp_entity);
319 
320 	gi.WriteByte (TE_BLASTERBEAM);
321 	gi.WritePosition (start);
322 	gi.WritePosition (end);
323 	gi.multicast (self->s.origin, MULTICAST_PVS);
324 
325 }
326 
327 void SV_AddGravity (edict_t *ent, float timespan);
328 
329 // pull the player toward the grapple
CTFGrapplePull(edict_t * self)330 void CTFGrapplePull(edict_t *self)
331 {
332 	vec3_t hookdir, v;
333 	float vlen;
334 
335 	if (strcmp(self->owner->client->pers.weapon->classname, "weapon_grapple") == 0 &&
336 		!self->owner->client->newweapon &&
337 		self->owner->client->weaponstate != WEAPON_FIRING &&
338 		self->owner->client->weaponstate != WEAPON_ACTIVATING) {
339 		CTFResetGrapple(self);
340 		return;
341 	}
342 
343 	if (self->enemy) {
344 		if (self->enemy->solid == SOLID_NOT) {
345 			CTFResetGrapple(self);
346 			return;
347 		}
348 		if (self->enemy->solid == SOLID_BBOX) {
349 			VectorScale(self->enemy->size, 0.5, v);
350 			VectorAdd(v, self->enemy->s.origin, v);
351 			VectorAdd(v, self->enemy->mins, self->s.origin);
352 			gi.linkentity (self);
353 		} else
354 			VectorCopy(self->enemy->velocity, self->velocity);
355 		if (self->enemy->takedamage) {
356 			float volume = 1.0;
357 
358 			if (self->owner->client->silencer_shots)
359 				volume = 0.2;
360 
361 			T_Damage (self->enemy, self, self->owner, self->velocity, self->s.origin, vec3_origin, 1, 1, 0, MOD_GRAPPLE);
362 		}
363 		if (self->enemy->deadflag) { // he died
364 			CTFResetGrapple(self);
365 			return;
366 		}
367 	}
368 
369 	CTFGrappleDrawCable(self);
370 
371 	if (self->owner->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY) {
372 		// pull player toward grapple
373 		// this causes icky stuff with prediction, we need to extend
374 		// the prediction layer to include two new fields in the player
375 		// move stuff: a point and a velocity.  The client should add
376 		// that velociy in the direction of the point
377 		vec3_t forward, up;
378 
379 		AngleVectors (self->owner->client->v_angle, forward, NULL, up);
380 		VectorCopy(self->owner->s.origin, v);
381 		v[2] += self->owner->viewheight;
382 		VectorSubtract (self->s.origin, v, hookdir);
383 
384 		vlen = VectorLength(hookdir);
385 
386 		if (self->owner->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL &&
387 			vlen < 64) {
388 			float volume = 1.0;
389 
390 			if (self->owner->client->silencer_shots)
391 				volume = 0.2;
392 
393 			self->owner->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
394 			gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/electroball.wav"), volume, ATTN_NORM, 0);
395 			self->owner->client->ctf_grapplestate = CTF_GRAPPLE_STATE_HANG;
396 		}
397 
398 		VectorNormalize (hookdir);
399 		VectorScale(hookdir, CTF_GRAPPLE_PULL_SPEED, hookdir);
400 		VectorCopy(hookdir, self->owner->velocity);
401 		SV_AddGravity(self->owner, FRAMETIME);
402 	}
403 }
404 
CTFFireGrapple(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,int effect)405 void CTFFireGrapple (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect)
406 {
407 	edict_t	*grapple;
408 	trace_t	tr;
409 
410 	VectorNormalize (dir);
411 
412 	grapple = G_Spawn();
413 	VectorCopy (start, grapple->s.origin);
414 	VectorCopy (start, grapple->s.old_origin);
415 	vectoangles (dir, grapple->s.angles);
416 	VectorScale (dir, speed, grapple->velocity);
417 	grapple->movetype = MOVETYPE_FLYMISSILE;
418 	grapple->clipmask = MASK_SHOT;
419 	grapple->solid = SOLID_BBOX;
420 	grapple->s.effects |= effect;
421 	VectorClear (grapple->mins);
422 	VectorClear (grapple->maxs);
423 	grapple->s.modelindex = 0;
424 	grapple->owner = self;
425 	grapple->touch = CTFGrappleTouch;
426 	grapple->dmg = damage;
427 	self->client->ctf_grapple = grapple;
428 	self->client->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
429 	gi.linkentity (grapple);
430 
431 	tr = gi.trace (self->s.origin, NULL, NULL, grapple->s.origin, grapple, MASK_SHOT);
432 	if (tr.fraction < 1.0)
433 	{
434 		VectorMA (grapple->s.origin, -10, dir, grapple->s.origin);
435 		grapple->touch (grapple, tr.ent, NULL, NULL);
436 	}
437 }
438 
CTFGrappleFire(edict_t * ent,vec3_t g_offset,int damage,int effect)439 void CTFGrappleFire (edict_t *ent, vec3_t g_offset, int damage, int effect)
440 {
441 	vec3_t	forward, right;
442 	vec3_t	start;
443 	vec3_t	offset;
444 	// float volume = 1.0;
445 
446 	if (ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY)
447 		return; // it's already out
448 
449 	AngleVectors (ent->client->v_angle, forward, right, NULL);
450 //	VectorSet(offset, 24, 16, ent->viewheight-8+2);
451 	VectorSet(offset, 24, 8, ent->viewheight-8+2);
452 	VectorAdd (offset, g_offset, offset);
453 	P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
454 
455 	VectorScale (forward, -2, ent->client->kick_origin);
456 	ent->client->kick_angles[0] = -1;
457 
458 	CTFFireGrapple (ent, start, forward, damage, CTF_GRAPPLE_SPEED, effect);
459 
460 	PlayerNoise(ent, start, PNOISE_WEAPON);
461 }
462 
463 
CTFWeapon_Grapple_Fire(edict_t * ent)464 void CTFWeapon_Grapple_Fire (edict_t *ent)
465 {
466 	int		damage;
467 
468 	damage = 15;
469 	CTFGrappleFire (ent, vec3_origin, damage, 0);
470 	ent->client->ps.gunframe++;
471 }
472 
CTFWeapon_Grapple(edict_t * ent)473 void CTFWeapon_Grapple (edict_t *ent)
474 {
475 	static int	pause_frames[]	= {10, 18, 27, 0};
476 	static int	fire_frames[]	= {6, 0};
477 	int prevstate;
478 
479 	// if the the attack button is still down, stay in the firing frame
480 	if ((ent->client->buttons & BUTTON_ATTACK) &&
481 		ent->client->weaponstate == WEAPON_FIRING &&
482 		ent->client->ctf_grapple)
483 		ent->client->ps.gunframe = 9;
484 
485 	if (!(ent->client->buttons & BUTTON_ATTACK) &&
486 		ent->client->ctf_grapple) {
487 		CTFResetGrapple(ent->client->ctf_grapple);
488 		if (ent->client->weaponstate == WEAPON_FIRING)
489 			ent->client->weaponstate = WEAPON_READY;
490 	}
491 
492 
493 	if (ent->client->newweapon &&
494 		ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY &&
495 		ent->client->weaponstate == WEAPON_FIRING) {
496 		// he wants to change weapons while grappled
497 		ent->client->weaponstate = WEAPON_DROPPING;
498 		ent->client->ps.gunframe = 32;
499 	}
500 
501 	prevstate = ent->client->weaponstate;
502 	Weapon_Generic (ent, 5, 9, 31, 36, pause_frames, fire_frames,
503 		CTFWeapon_Grapple_Fire);
504 
505 	// if we just switched back to grapple, immediately go to fire frame
506 	if (prevstate == WEAPON_ACTIVATING &&
507 		ent->client->weaponstate == WEAPON_READY &&
508 		ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY) {
509 		if (!(ent->client->buttons & BUTTON_ATTACK))
510 			ent->client->ps.gunframe = 9;
511 		else
512 			ent->client->ps.gunframe = 5;
513 		ent->client->weaponstate = WEAPON_FIRING;
514 	}
515 }
516 
CTFFlagThink(edict_t * ent)517 static void CTFFlagThink(edict_t *ent)
518 {
519 	if (ent->solid != SOLID_NOT)
520 		ent->s.frame = 173 + (((ent->s.frame - 173) + 1) % 16);
521 	ent->nextthink = level.time + FRAMETIME;
522 }
CTFFlagSetup(edict_t * ent)523 void CTFFlagSetup (edict_t *ent)
524 {
525 	trace_t		tr;
526 	vec3_t		dest;
527 	float		*v;
528 
529 	v = tv(-15,-15,-15);
530 	VectorCopy (v, ent->mins);
531 	v = tv(15,15,15);
532 	VectorCopy (v, ent->maxs);
533 
534 	if (ent->model)
535 		gi.setmodel (ent, ent->model);
536 	else
537 		gi.setmodel (ent, ent->item->world_model);
538 	ent->solid = SOLID_TRIGGER;
539 	ent->movetype = MOVETYPE_TOSS;
540 	ent->touch = Touch_Item;
541 
542 	v = tv(0,0,-128);
543 	VectorAdd (ent->s.origin, v, dest);
544 
545 	tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID);
546 	if (tr.startsolid)
547 	{
548 		gi.dprintf ("CTFFlagSetup: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
549 		G_FreeEdict (ent);
550 		return;
551 	}
552 
553 	VectorCopy (tr.endpos, ent->s.origin);
554 
555 	gi.linkentity (ent);
556 
557 	ent->nextthink = level.time + FRAMETIME;
558 	ent->think = CTFFlagThink;
559 }
CTFEffects(edict_t * player)560 void CTFEffects(edict_t *player)
561 {
562 	gitem_t *flag1_item, *flag2_item;
563 
564 	flag1_item = FindItemByClassname("item_flag_red");
565 	flag2_item = FindItemByClassname("item_flag_blue");
566 
567 	if (player->client->pers.inventory[ITEM_INDEX(flag1_item)])
568 		player->s.modelindex4 = gi.modelindex("models/items/flags/flag1.md2");
569 	else if (player->client->pers.inventory[ITEM_INDEX(flag2_item)])
570 		player->s.modelindex4 = gi.modelindex("models/items/flags/flag2.md2");
571 	else
572 		player->s.modelindex4 = 0;
573 }
CTFDrop_Flag(edict_t * ent,gitem_t * item)574 void CTFDrop_Flag(edict_t *ent, gitem_t *item)
575 {
576 	if (rand() & 1)
577 		safe_cprintf(ent, PRINT_HIGH, "Only lusers drop flags.\n");
578 	else
579 		safe_cprintf(ent, PRINT_HIGH, "Winners don't drop flags.\n");
580 }
CTFResetFlag(int ctf_team)581 void CTFResetFlag(int ctf_team)
582 {
583 	char *c;
584 	edict_t *ent;
585 
586 	switch (ctf_team) {
587 	case RED_TEAM:
588 		c = "item_flag_red";
589 		break;
590 	case BLUE_TEAM:
591 		c = "item_flag_blue";
592 		break;
593 	default:
594 		return;
595 	}
596 
597 	ent = NULL;
598 	while ((ent = G_Find (ent, FOFS(classname), c)) != NULL) {
599 		if (ent->spawnflags & DROPPED_ITEM)
600 			G_FreeEdict(ent);
601 		else {
602 			ent->svflags &= ~SVF_NOCLIENT;
603 			ent->solid = SOLID_TRIGGER;
604 			gi.linkentity(ent);
605 			ent->s.event = EV_ITEM_RESPAWN;
606 		}
607 	}
608 }
609 
CTFResetFlags(void)610 void CTFResetFlags(void)
611 {
612 	CTFResetFlag(RED_TEAM);
613 	CTFResetFlag(BLUE_TEAM);
614 }
CTFDropFlagTouch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)615 static void CTFDropFlagTouch(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
616 {
617 	//owner (who dropped us) can't touch for two secs
618 	if (other == ent->owner &&
619 		ent->nextthink - level.time > 28)
620 		return;
621 
622 	Touch_Item (ent, other, plane, surf);
623 }
CTFDropFlagThink(edict_t * ent)624 static void CTFDropFlagThink(edict_t *ent)
625 {
626 	// auto return the flag
627 	// reset flag will remove ourselves
628 	if (strcmp(ent->classname, "item_flag_red") == 0) {
629 		CTFResetFlag(RED_TEAM);
630 		safe_bprintf(PRINT_HIGH, "The %s flag has returned!\n",
631 			"Red");
632 	} else if (strcmp(ent->classname, "item_flag_blue") == 0) {
633 		CTFResetFlag(BLUE_TEAM);
634 		safe_bprintf(PRINT_HIGH, "The %s flag has returned!\n",
635 			"Blue");
636 	}
637 
638 }
CTFDeadDropFlag(edict_t * self,edict_t * other)639 void CTFDeadDropFlag(edict_t *self, edict_t *other)
640 {
641 	edict_t *dropped = NULL;
642 	gitem_t *flag1_item, *flag2_item;
643 
644 	dropped = NULL;
645 	flag1_item = flag2_item = NULL;
646 
647 	flag1_item = FindItemByClassname("item_flag_red");
648 	flag2_item = FindItemByClassname("item_flag_blue");
649 
650 	/* notes on assist bonus:
651 	 *  other can be NULL, if self is disconnecting, or self is a bot being kicked.
652 	 *  other can be self for suicides, but that is handled with team check.
653 	 */
654 	if (self->client->pers.inventory[ITEM_INDEX(flag1_item)]) {
655 
656 		if ( other != NULL && other->client
657 			&& ( (other->dmteam == RED_TEAM && self->dmteam == BLUE_TEAM)
658 				|| (other->dmteam == BLUE_TEAM && self->dmteam == RED_TEAM) ))
659 		{//kill enemy w flag; assist bonus (1 for frag + 4 = 5 total)
660 			other->client->resp.score += 4;
661 		}
662 
663 		dropped = Drop_Item(self, flag1_item);
664 		self->client->pers.inventory[ITEM_INDEX(flag1_item)] = 0;
665 		safe_bprintf(PRINT_HIGH, "%s lost the %s flag!\n",
666 			self->client->pers.netname, "Red");
667 	} else if (self->client->pers.inventory[ITEM_INDEX(flag2_item)]) {
668 
669 		if ( other != NULL && other->client
670 			&& ( (other->dmteam == RED_TEAM && self->dmteam == BLUE_TEAM)
671 				|| (other->dmteam == BLUE_TEAM && self->dmteam == RED_TEAM ) ))
672 		{ //kill enemy w flag; assist bonus (1 for frag + 4 = 5 total)
673 			other->client->resp.score += 4;
674 		}
675 
676 		dropped = Drop_Item(self, flag2_item);
677 		self->client->pers.inventory[ITEM_INDEX(flag2_item)] = 0;
678 		safe_bprintf(PRINT_HIGH, "%s lost the %s flag!\n",
679 			self->client->pers.netname, "Blue");
680 	}
681 
682 	if (dropped) {
683 		dropped->think = CTFDropFlagThink;
684 		dropped->nextthink = level.time + 30;
685 		dropped->touch = CTFDropFlagTouch;
686 		dropped->s.frame = 175;
687 		dropped->s.effects = EF_ROTATE;
688 	}
689 }
CTFPickup_Flag(edict_t * ent,edict_t * other)690 qboolean CTFPickup_Flag (edict_t *ent, edict_t *other)
691 {
692 	int ctf_team;
693 	char team_name[16] = " ";
694 	char enemy_team_name[16] = " ";
695 	gitem_t *flag_item, *enemy_flag_item, *it;
696 
697 	// figure out what team this flag is
698 	if (strcmp(ent->classname, "item_flag_red") == 0)
699 		ctf_team = RED_TEAM;
700 	else if (strcmp(ent->classname, "item_flag_blue") == 0)
701 		ctf_team = BLUE_TEAM;
702 	else {
703 		safe_cprintf(ent, PRINT_HIGH, "Don't know what team the flag is on.\n");
704 		return false;
705 	}
706 
707 // same team, if the flag at base, check to he has the enemy flag
708 	if (ctf_team == RED_TEAM) {
709 		flag_item = FindItemByClassname("item_flag_red");
710 		enemy_flag_item = FindItemByClassname("item_flag_blue");
711 		strcpy(team_name, "Red");
712 		strcpy(enemy_team_name, "Blue");
713 	} else {
714 		flag_item = FindItemByClassname("item_flag_blue");
715 		enemy_flag_item = FindItemByClassname("item_flag_red");
716 		strcpy(team_name, "Blue");
717 		strcpy(enemy_team_name, "Red");
718 	}
719 
720 	if (ctf_team == other->dmteam) {
721 
722 		if (!(ent->spawnflags & DROPPED_ITEM)) {
723 			// the flag is at home base.  if the player has the enemy
724 			// flag, he's just won!
725 
726 			if (other->client->pers.inventory[ITEM_INDEX(enemy_flag_item)]) {
727 				safe_bprintf(PRINT_HIGH, "%s captured the %s flag!\n",
728 						other->client->pers.netname, enemy_team_name);
729 				other->client->pers.inventory[ITEM_INDEX(enemy_flag_item)] = 0;
730 
731 				if (ctf_team == RED_TEAM)
732 				{
733 					red_team_score++;
734 
735 					if(red_team_score > blue_team_score){
736 						reddiff = red_team_score - blue_team_score;
737 						if(reddiff == 1){
738 							gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/red_takes.wav"), 1, ATTN_NONE, 0);
739 						}
740 						else
741 						{
742 							gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/red_increases.wav"), 1, ATTN_NONE, 0);
743 						}
744 
745 					}
746 					else
747 					{
748 						if(red_team_score == blue_team_score){
749 							gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/scores_tied.wav"), 1, ATTN_NONE, 0);
750 						}
751 						else
752 						{
753 							gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/red_scores.wav"), 1, ATTN_NONE, 0);
754 						}
755 					}
756 
757 				}
758 				else
759 				{
760 					blue_team_score++;
761 
762 					if(blue_team_score > red_team_score){
763 						bluediff = blue_team_score - red_team_score;
764 						if(bluediff == 1){
765 							gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/blue_takes.wav"), 1, ATTN_NONE, 0);
766 						}
767 						else
768 						{
769 							gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/blue_increases.wav"), 1, ATTN_NONE, 0);
770 						}
771 
772 					}
773 					else
774 					{
775 						if(red_team_score == blue_team_score){
776 							gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/scores_tied.wav"), 1, ATTN_NONE, 0);
777 						}
778 						else
779 						{
780 							gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/blue_scores.wav"), 1, ATTN_NONE, 0);
781 						}
782 					}
783 				}
784 
785 				// other gets another 10 frag bonus
786 				other->client->resp.score += 10;//CTF_CAPTURE_BONUS;
787 				//reward points bonus
788 				other->client->resp.reward_pts+=5;
789 
790 				if(other->client->resp.reward_pts >= g_reward->integer && !other->client->resp.powered) { //give them speed and invis powerups
791 					it = FindItem("Invisibility");
792 					other->client->pers.inventory[ITEM_INDEX(it)] += 1;
793 
794 					it = FindItem("Sproing");
795 					other->client->pers.inventory[ITEM_INDEX(it)] += 1;
796 
797 					it = FindItem("Haste");
798 					other->client->pers.inventory[ITEM_INDEX(it)] += 1;
799 
800 					other->client->resp.powered = true;
801 
802 					gi.sound (other, CHAN_AUTO, gi.soundindex("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
803 				}
804 
805 				CTFResetFlags();
806 				return false;
807 			}
808 			return false; // its at home base already
809 		}
810 		// hey, its not home.  return it by teleporting it back
811 		safe_bprintf(PRINT_HIGH, "%s returned the %s flag!\n",
812 			other->client->pers.netname, team_name);
813 		other->client->resp.score += 5;//CTF_RECOVERY_BONUS;
814 		if(!strcmp("Red", team_name))
815 			gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/red_returned.wav"), 1, ATTN_NONE, 0);
816 		else
817 			gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/blue_returned.wav"), 1, ATTN_NONE, 0);
818 
819 		//reward points bonus
820 		other->client->resp.reward_pts+=2;
821 
822 		if(other->client->resp.reward_pts >= g_reward->integer && !other->client->resp.powered) { //give them speed and invis powerups
823 			it = FindItem("Invisibility");
824 			other->client->pers.inventory[ITEM_INDEX(it)] += 1;
825 
826 			it = FindItem("Sproing");
827 			other->client->pers.inventory[ITEM_INDEX(it)] += 1;
828 
829 			it = FindItem("Haste");
830 			other->client->pers.inventory[ITEM_INDEX(it)] += 1;
831 
832 			other->client->resp.powered = true;
833 
834 			gi.sound (other, CHAN_AUTO, gi.soundindex("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
835 		}
836 
837 		//CTFResetFlag will remove this entity!  We must return false
838 		CTFResetFlag(ctf_team);
839 		return false;
840 	}
841 
842 	// hey, its not our flag, pick it up
843 	safe_bprintf(PRINT_HIGH, "%s got the %s flag!\n",
844 		other->client->pers.netname, team_name);
845 	other->client->resp.score += 10;//CTF_FLAG_BONUS;
846 	if(!strcmp("Red", team_name))
847 		gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/red_picked.wav"), 1, ATTN_NONE, 0);
848 	else
849 		gi.sound (ent, CHAN_AUTO, gi.soundindex("misc/blue_picked.wav"), 1, ATTN_NONE, 0);
850 
851 	other->client->resp.reward_pts+=2;
852 
853 	//if a bot, break off of path, so we can find base path
854 	if(other->is_bot)
855 	{
856 		other->state = STATE_WANDER;
857 		other->wander_timeout = level.time + 1.0;
858 	}
859 
860 	if(other->client->resp.reward_pts >= g_reward->integer && !other->client->resp.powered) { //give them speed and invis powerups
861 		it = FindItem("Invisibility");
862 		other->client->pers.inventory[ITEM_INDEX(it)] += 1;
863 
864 		it = FindItem("Sproing");
865 		other->client->pers.inventory[ITEM_INDEX(it)] += 1;
866 
867 		it = FindItem("Haste");
868 		other->client->pers.inventory[ITEM_INDEX(it)] += 1;
869 
870 		other->client->resp.powered = true;
871 
872 		gi.sound (other, CHAN_AUTO, gi.soundindex("misc/pc_up.wav"), 1, ATTN_STATIC, 0);
873 	}
874 
875 	other->client->pers.inventory[ITEM_INDEX(flag_item)] = 1;
876 
877 	// pick up the flag
878 	// if it's not a dropped flag, we just make is disappear
879 	// if it's dropped, it will be removed by the pickup caller
880 	if (!(ent->spawnflags & DROPPED_ITEM)) {
881 		ent->flags |= FL_RESPAWN;
882 		ent->svflags |= SVF_NOCLIENT;
883 		ent->solid = SOLID_NOT;
884 	}
885 	return true;
886 }
887