|
|
1.1 root 1: // dm_ball.c
2: // pmack
3: // june 98
4:
5: #include "g_local.h"
6:
7: // defines
8:
9: #define DBALL_GOAL_TEAM1 0x0001
10: #define DBALL_GOAL_TEAM2 0x0002
11:
12: // globals
13:
14: edict_t *dball_ball_entity = NULL;
15: int dball_ball_startpt_count;
16: int dball_team1_goalscore;
17: int dball_team2_goalscore;
18:
19: cvar_t *dball_team1_skin;
20: cvar_t *dball_team2_skin;
21: cvar_t *goallimit;
22:
23: // prototypes
24:
25: extern void EndDMLevel (void);
26: extern void ClientUserinfoChanged (edict_t *ent, char *userinfo);
27: extern void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles);
28: extern float PlayersRangeFromSpot (edict_t *spot);
29:
30: void DBall_BallDie (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
31: void DBall_BallRespawn (edict_t *self);
32:
33: // **************************
34: // Game rules
35: // **************************
36:
37: int DBall_CheckDMRules (void)
38: {
39: if(goallimit && goallimit->value)
40: {
41: if(dball_team1_goalscore >= goallimit->value)
42: gi.bprintf (PRINT_HIGH, "Team 1 Wins.\n");
43: else if(dball_team2_goalscore >= goallimit->value)
44: gi.bprintf (PRINT_HIGH, "Team 2 Wins.\n");
45: else
46: return 0;
47:
48: EndDMLevel ();
49: return 1;
50: }
51:
52: return 0;
53: }
54:
55: //==================
56: //==================
57: void DBall_ClientBegin (edict_t *ent)
58: {
59: int team1, team2, unassigned;
60: edict_t *other;
61: char *p;
62: static char value[512];
63: int j;
64:
65: team1 = 0;
66: team2 = 0;
67: unassigned = 0;
68:
69: for (j = 1; j <= game.maxclients; j++)
70: {
71: other = &g_edicts[j];
72: if (!other->inuse)
73: continue;
74: if (!other->client)
75: continue;
76: if (other == ent) // don't count the new player
77: continue;
78:
79: strcpy(value, Info_ValueForKey (other->client->pers.userinfo, "skin"));
80: p = strchr(value, '/');
81: if (p)
82: {
83: if(!strcmp(dball_team1_skin->string, value))
84: team1++;
85: else if(!strcmp(dball_team2_skin->string, value))
86: team2++;
87: else
88: unassigned++;
89: }
90: else
91: unassigned++;
92: }
93:
94: if(team1 > team2)
95: {
96: gi.dprintf("assigned to team 2\n");
97: Info_SetValueForKey(ent->client->pers.userinfo, "skin", dball_team2_skin->string);
98: }
99: else
100: {
101: gi.dprintf("assigned to team 1\n");
102: Info_SetValueForKey(ent->client->pers.userinfo, "skin", dball_team1_skin->string);
103: }
104:
105: ClientUserinfoChanged(ent, ent->client->pers.userinfo);
106:
107: if(unassigned)
108: gi.dprintf("%d unassigned players present!\n", unassigned);
109: }
110:
111: //==================
112: //==================
113: void DBall_SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles)
114: {
115: edict_t *bestspot;
116: float bestdistance, bestplayerdistance;
117: edict_t *spot;
118: char *spottype;
119: char skin[512];
120:
121: strcpy(skin, Info_ValueForKey (ent->client->pers.userinfo, "skin"));
122: if(!strcmp(dball_team1_skin->string, skin))
123: spottype = "dm_dball_team1_start";
124: else if(!strcmp(dball_team2_skin->string, skin))
125: spottype = "dm_dball_team2_start";
126: else
127: spottype = "info_player_deathmatch";
128:
129: spot = NULL;
130: bestspot = NULL;
131: bestdistance = 0;
132: while ((spot = G_Find (spot, FOFS(classname), spottype)) != NULL)
133: {
134: bestplayerdistance = PlayersRangeFromSpot (spot);
135:
136: if (bestplayerdistance > bestdistance)
137: {
138: bestspot = spot;
139: bestdistance = bestplayerdistance;
140: }
141: }
142:
143: if (bestspot)
144: {
145: VectorCopy (bestspot->s.origin, origin);
146: origin[2] += 9;
147: VectorCopy (bestspot->s.angles, angles);
148: return;
149: }
150:
151: // if we didn't find an appropriate spawnpoint, just
152: // call the standard one.
153: SelectSpawnPoint(ent, origin, angles);
154: }
155:
156: //==================
157: //==================
158: void DBall_GameInit (void)
159: {
160: // we don't want a minimum speed for friction to take effect.
161: // this will allow any knockback to move stuff.
162: sv_stopspeed->value = 0;
163: dball_team1_goalscore = 0;
164: dball_team2_goalscore = 0;
165:
166: dmflags->value = (int)dmflags->value | DF_NO_MINES | DF_NO_NUKES | DF_NO_STACK_DOUBLE |
167: DF_NO_FRIENDLY_FIRE | DF_SKINTEAMS;
168:
169: dball_team1_skin = gi.cvar ("dball_team1_skin", "male/ctf_r", 0);
170: dball_team2_skin = gi.cvar ("dball_team2_skin", "male/ctf_b", 0);
171: goallimit = gi.cvar ("goallimit", "0", 0);
172: }
173:
174: //==================
175: //==================
176: void DBall_PostInitSetup (void)
177: {
178: edict_t *e;
179:
180: e=NULL;
181: // turn teleporter destinations nonsolid.
182: while(e = G_Find (e, FOFS(classname), "misc_teleporter_dest"))
183: {
184: e->solid = SOLID_NOT;
185: gi.linkentity (e);
186: }
187:
188: // count the ball start points
189: dball_ball_startpt_count = 0;
190: e=NULL;
191: while(e = G_Find (e, FOFS(classname), "dm_dball_ball_start"))
192: {
193: dball_ball_startpt_count++;
194: }
195:
196: if(dball_ball_startpt_count == 0)
197: gi.dprintf("No Deathball start points!\n");
198: }
199:
200: //==================
201: // DBall_ChangeDamage - half damage between players. full if it involves
202: // the ball entity
203: //==================
204: int DBall_ChangeDamage (edict_t *targ, edict_t *attacker, int damage, int mod)
205: {
206: // cut player -> ball damage to 1
207: if (targ == dball_ball_entity)
208: return 1;
209:
210: // damage player -> player is halved
211: if (attacker != dball_ball_entity)
212: return damage / 2;
213:
214: return damage;
215: }
216:
217: //==================
218: //==================
219: int DBall_ChangeKnockback (edict_t *targ, edict_t *attacker, int knockback, int mod)
220: {
221: if(targ != dball_ball_entity)
222: return knockback;
223:
224: if(knockback < 1)
225: {
226: // FIXME - these don't account for quad/double
227: if(mod == MOD_ROCKET) // rocket
228: knockback = 70;
229: else if(mod == MOD_BFG_EFFECT) // bfg
230: knockback = 90;
231: else
232: gi.dprintf ("zero knockback, mod %d\n", mod);
233: }
234: else
235: {
236: // FIXME - change this to an array?
237: switch(mod)
238: {
239: case MOD_BLASTER:
240: knockback *= 3;
241: break;
242: case MOD_SHOTGUN:
243: knockback = (knockback * 3) / 8;
244: break;
245: case MOD_SSHOTGUN:
246: knockback = knockback / 3;
247: break;
248: case MOD_MACHINEGUN:
249: knockback = (knockback * 3) / 2;
250: break;
251: case MOD_HYPERBLASTER:
252: knockback *= 4;
253: break;
254: case MOD_GRENADE:
255: case MOD_HANDGRENADE:
256: case MOD_PROX:
257: case MOD_G_SPLASH:
258: case MOD_HG_SPLASH:
259: case MOD_HELD_GRENADE:
260: case MOD_TRACKER:
261: case MOD_DISINTEGRATOR:
262: knockback /= 2;
263: break;
264: case MOD_R_SPLASH:
265: knockback = (knockback * 3) / 2;
266: break;
267: case MOD_RAILGUN:
268: case MOD_HEATBEAM:
269: knockback /= 3;
270: break;
271: }
272: }
273:
274: // gi.dprintf("mod: %d knockback: %d\n", mod, knockback);
275: return knockback;
276: }
277:
278: // **************************
279: // Goals
280: // **************************
281:
282: void DBall_GoalTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
283: {
284: int team_score;
285: int scorechange;
286: int j;
287: char value[512];
288: char *p;
289: edict_t *ent;
290:
291: if(other != dball_ball_entity)
292: return;
293:
294: self->health = self->max_health;
295:
296: // determine which team scored, and bump the team score
297: if(self->spawnflags & DBALL_GOAL_TEAM1)
298: {
299: dball_team1_goalscore += self->wait;
300: team_score = 1;
301: }
302: else
303: {
304: dball_team2_goalscore += self->wait;
305: team_score = 2;
306: }
307:
308: // bump the score for everyone on the correct team.
309: for (j = 1; j <= game.maxclients; j++)
310: {
311: ent = &g_edicts[j];
312: if (!ent->inuse)
313: continue;
314: if (!ent->client)
315: continue;
316:
317: if (ent == other->enemy)
318: scorechange = self->wait + 5;
319: else
320: scorechange = self->wait;
321:
322: strcpy(value, Info_ValueForKey (ent->client->pers.userinfo, "skin"));
323: p = strchr(value, '/');
324: if (p)
325: {
326: if(!strcmp(dball_team1_skin->string, value))
327: {
328: if(team_score == 1)
329: ent->client->resp.score += scorechange;
330: else if(other->enemy == ent)
331: ent->client->resp.score -= scorechange;
332: }
333: else if(!strcmp(dball_team2_skin->string, value))
334: {
335: if(team_score == 2)
336: ent->client->resp.score += scorechange;
337: else if(other->enemy == ent)
338: ent->client->resp.score -= scorechange;
339: }
340: else
341: gi.dprintf("unassigned player!!!!\n");
342: }
343: }
344:
345: if(other->enemy)
346: gi.dprintf("score for team %d by %s\n", team_score, other->enemy->client->pers.netname);
347: else
348: gi.dprintf("score for team %d by someone\n", team_score);
349:
350: DBall_BallDie (other, other->enemy, other->enemy, 0, vec3_origin);
351:
352: G_UseTargets (self, other);
353: }
354:
355: // **************************
356: // Ball
357: // **************************
358:
359: edict_t *PickBallStart (edict_t *ent)
360: {
361: int which, current;
362: edict_t *e;
363:
364: which = ceil(random() * dball_ball_startpt_count);
365: e = NULL;
366: current = 0;
367:
368: while(e = G_Find (e, FOFS(classname), "dm_dball_ball_start"))
369: {
370: current++;
371: if(current == which)
372: return e;
373: }
374:
375: if(current == 0)
376: gi.dprintf("No ball start points found!\n");
377:
378: return G_Find(NULL, FOFS(classname), "dm_dball_ball_start");
379: }
380:
381: //==================
382: // DBall_BallTouch - if the ball hit another player, hurt them
383: //==================
384: void DBall_BallTouch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
385: {
386: vec3_t dir;
387: float dot;
388: float speed;
389:
390: if(other->takedamage == DAMAGE_NO)
391: return;
392:
393: // hit a player
394: if(other->client)
395: {
396: if(ent->velocity[0] || ent->velocity[1] || ent->velocity[2])
397: {
398: speed = VectorLength(ent->velocity);
399:
400: VectorSubtract(ent->s.origin, other->s.origin, dir);
401: dot = DotProduct(dir, ent->velocity);
402:
403: if(dot > 0.7)
404: {
405: T_Damage (other, ent, ent, vec3_origin, ent->s.origin, vec3_origin,
406: speed/10, speed/10, 0, MOD_DBALL_CRUSH);
407: }
408: }
409: }
410: }
411:
412: //==================
413: // DBall_BallPain
414: //==================
415: void DBall_BallPain (edict_t *self, edict_t *other, float kick, int damage)
416: {
417: self->enemy = other;
418: self->health = self->max_health;
419: // if(other->classname)
420: // gi.dprintf("hurt by %s -- %d\n", other->classname, self->health);
421: }
422:
423: void DBall_BallDie (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
424: {
425: // do the splash effect
426: gi.WriteByte (svc_temp_entity);
427: gi.WriteByte (TE_DBALL_GOAL);
428: gi.WritePosition (self->s.origin);
429: gi.multicast (self->s.origin, MULTICAST_PVS);
430:
431: VectorClear(self->s.angles);
432: VectorClear(self->velocity);
433: VectorClear(self->avelocity);
434:
435: // make it invisible and desolid until respawn time
436: self->solid = SOLID_NOT;
437: // self->s.modelindex = 0;
438: self->think = DBall_BallRespawn;
439: self->nextthink = level.time + 2;
440: gi.linkentity(self);
441:
442: }
443:
444: void DBall_BallRespawn (edict_t *self)
445: {
446: edict_t *start;
447:
448: // do the splash effect
449: gi.WriteByte (svc_temp_entity);
450: gi.WriteByte (TE_DBALL_GOAL);
451: gi.WritePosition (self->s.origin);
452: gi.multicast (self->s.origin, MULTICAST_PVS);
453:
454: // move the ball and stop it
455: start = PickBallStart(self);
456: if(start)
457: {
458: VectorCopy(start->s.origin, self->s.origin);
459: VectorCopy(start->s.origin, self->s.old_origin);
460: }
461:
462: VectorClear(self->s.angles);
463: VectorClear(self->velocity);
464: VectorClear(self->avelocity);
465:
466: self->solid = SOLID_BBOX;
467: self->s.modelindex = gi.modelindex ("models/objects/dball/tris.md2");
468: self->s.event = EV_PLAYER_TELEPORT;
469: self->groundentity = NULL;
470:
471: // kill anything at the destination
472: KillBox (self);
473:
474: gi.linkentity (self);
475: }
476:
477: // ************************
478: // SPEED CHANGES
479: // ************************
480:
481: #define DBALL_SPEED_ONEWAY 1
482:
483: void DBall_SpeedTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
484: {
485: float dot;
486: vec3_t vel;
487:
488: if(other != dball_ball_entity)
489: return;
490:
491: if(self->timestamp >= level.time)
492: return;
493:
494: if(VectorLength(other->velocity) < 1)
495: return;
496:
497: if(self->spawnflags & DBALL_SPEED_ONEWAY)
498: {
499: VectorCopy (other->velocity, vel);
500: VectorNormalize (vel);
501: dot = DotProduct (vel, self->movedir);
502: if(dot < 0.8)
503: return;
504: }
505:
506: self->timestamp = level.time + self->delay;
507: VectorScale (other->velocity, self->speed, other->velocity);
508: }
509:
510: // ************************
511: // SPAWN FUNCTIONS
512: // ************************
513:
514: /*QUAKED dm_dball_ball (1 .5 .5) (-48 -48 -48) (48 48 48)
515: Deathball Ball
516: */
517: void SP_dm_dball_ball (edict_t *self)
518: {
519: if(!(deathmatch->value))
520: {
521: G_FreeEdict(self);
522: return;
523: }
524:
525: if(gamerules && (gamerules->value != RDM_DEATHBALL))
526: {
527: G_FreeEdict(self);
528: return;
529: }
530:
531: dball_ball_entity = self;
532: // VectorCopy (self->s.origin, dball_ball_startpt);
533:
534: self->s.modelindex = gi.modelindex ("models/objects/dball/tris.md2");
535: VectorSet (self->mins, -32, -32, -32);
536: VectorSet (self->maxs, 32, 32, 32);
537: self->solid = SOLID_BBOX;
538: self->movetype = MOVETYPE_NEWTOSS;
539: self->clipmask = MASK_MONSTERSOLID;
540:
541: self->takedamage = DAMAGE_YES;
542: self->mass = 50;
543: self->health = 50000;
544: self->max_health = 50000;
545: self->pain = DBall_BallPain;
546: self->die = DBall_BallDie;
547: self->touch = DBall_BallTouch;
548:
549: gi.linkentity (self);
550: }
551:
552: /*QUAKED dm_dball_team1_start (1 .5 .5) (-16 -16 -24) (16 16 32)
553: Deathball team 1 start point
554: */
555: void SP_dm_dball_team1_start (edict_t *self)
556: {
557: if (!deathmatch->value)
558: {
559: G_FreeEdict (self);
560: return;
561: }
562: if(gamerules && (gamerules->value != RDM_DEATHBALL))
563: {
564: G_FreeEdict(self);
565: return;
566: }
567: }
568:
569: /*QUAKED dm_dball_team2_start (1 .5 .5) (-16 -16 -24) (16 16 32)
570: Deathball team 2 start point
571: */
572: void SP_dm_dball_team2_start (edict_t *self)
573: {
574: if (!deathmatch->value)
575: {
576: G_FreeEdict (self);
577: return;
578: }
579: if(gamerules && (gamerules->value != RDM_DEATHBALL))
580: {
581: G_FreeEdict(self);
582: return;
583: }
584: }
585:
586: /*QUAKED dm_dball_ball_start (1 .5 .5) (-48 -48 -48) (48 48 48)
587: Deathball ball start point
588: */
589: void SP_dm_dball_ball_start (edict_t *self)
590: {
591: if (!deathmatch->value)
592: {
593: G_FreeEdict (self);
594: return;
595: }
596: if(gamerules && (gamerules->value != RDM_DEATHBALL))
597: {
598: G_FreeEdict(self);
599: return;
600: }
601: }
602:
603: /*QUAKED dm_dball_speed_change (1 .5 .5) ? ONEWAY
604: Deathball ball speed changing field.
605:
606: speed: multiplier for speed (.5 = half, 2 = double, etc) (default = double)
607: angle: used with ONEWAY so speed change is only one way.
608: delay: time between speed changes (default: 0.2 sec)
609: */
610: void SP_dm_dball_speed_change (edict_t *self)
611: {
612: if (!deathmatch->value)
613: {
614: G_FreeEdict (self);
615: return;
616: }
617: if(gamerules && (gamerules->value != RDM_DEATHBALL))
618: {
619: G_FreeEdict(self);
620: return;
621: }
622:
623: if(!self->speed)
624: self->speed = 2;
625:
626: if(!self->delay)
627: self->delay = 0.2;
628:
629: self->touch = DBall_SpeedTouch;
630: self->solid = SOLID_TRIGGER;
631: self->movetype = MOVETYPE_NONE;
632: self->svflags |= SVF_NOCLIENT;
633:
634: if (!VectorCompare(self->s.angles, vec3_origin))
635: G_SetMovedir (self->s.angles, self->movedir);
636: else
637: VectorSet (self->movedir, 1, 0, 0);
638:
639: gi.setmodel (self, self->model);
640: gi.linkentity (self);
641: }
642:
643: /*QUAKED dm_dball_goal (1 .5 .5) ? TEAM1 TEAM2
644: Deathball goal
645:
646: Team1/Team2 - beneficiary of this goal. when the ball enters this goal, the beneficiary team will score.
647:
648: "wait": score to be given for this goal (default 10) player gets score+5.
649: */
650: void SP_dm_dball_goal (edict_t *self)
651: {
652: if(!(deathmatch->value))
653: {
654: G_FreeEdict(self);
655: return;
656: }
657:
658: if(gamerules && (gamerules->value != RDM_DEATHBALL))
659: {
660: G_FreeEdict(self);
661: return;
662: }
663:
664: if(!self->wait)
665: self->wait = 10;
666:
667: self->touch = DBall_GoalTouch;
668: self->solid = SOLID_TRIGGER;
669: self->movetype = MOVETYPE_NONE;
670: self->svflags |= SVF_NOCLIENT;
671:
672: if (!VectorCompare(self->s.angles, vec3_origin))
673: G_SetMovedir (self->s.angles, self->movedir);
674:
675: gi.setmodel (self, self->model);
676: gi.linkentity (self);
677:
678: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.