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