123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681 |
- // Copyright (c) ZeniMax Media Inc.
- // Licensed under the GNU General Public License 2.0.
- // dm_ball.c
- // pmack
- // june 98
- #include "g_local.h"
- // defines
- #define DBALL_GOAL_TEAM1 0x0001
- #define DBALL_GOAL_TEAM2 0x0002
- // globals
- edict_t *dball_ball_entity = NULL;
- int dball_ball_startpt_count;
- int dball_team1_goalscore;
- int dball_team2_goalscore;
- cvar_t *dball_team1_skin;
- cvar_t *dball_team2_skin;
- cvar_t *goallimit;
- // prototypes
- extern void EndDMLevel (void);
- extern void ClientUserinfoChanged (edict_t *ent, char *userinfo);
- extern void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles);
- extern float PlayersRangeFromSpot (edict_t *spot);
- void DBall_BallDie (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
- void DBall_BallRespawn (edict_t *self);
- // **************************
- // Game rules
- // **************************
- int DBall_CheckDMRules (void)
- {
- if(goallimit && goallimit->value)
- {
- if(dball_team1_goalscore >= goallimit->value)
- gi.bprintf (PRINT_HIGH, "Team 1 Wins.\n");
- else if(dball_team2_goalscore >= goallimit->value)
- gi.bprintf (PRINT_HIGH, "Team 2 Wins.\n");
- else
- return 0;
- EndDMLevel ();
- return 1;
- }
- return 0;
- }
- //==================
- //==================
- void DBall_ClientBegin (edict_t *ent)
- {
- int team1, team2, unassigned;
- edict_t *other;
- char *p;
- static char value[512];
- int j;
- team1 = 0;
- team2 = 0;
- unassigned = 0;
- for (j = 1; j <= game.maxclients; j++)
- {
- other = &g_edicts[j];
- if (!other->inuse)
- continue;
- if (!other->client)
- continue;
- if (other == ent) // don't count the new player
- continue;
-
- strcpy(value, Info_ValueForKey (other->client->pers.userinfo, "skin"));
- p = strchr(value, '/');
- if (p)
- {
- if(!strcmp(dball_team1_skin->string, value))
- team1++;
- else if(!strcmp(dball_team2_skin->string, value))
- team2++;
- else
- unassigned++;
- }
- else
- unassigned++;
- }
- if(team1 > team2)
- {
- gi.dprintf("assigned to team 2\n");
- Info_SetValueForKey(ent->client->pers.userinfo, "skin", dball_team2_skin->string);
- }
- else
- {
- gi.dprintf("assigned to team 1\n");
- Info_SetValueForKey(ent->client->pers.userinfo, "skin", dball_team1_skin->string);
- }
-
- ClientUserinfoChanged(ent, ent->client->pers.userinfo);
- if(unassigned)
- gi.dprintf("%d unassigned players present!\n", unassigned);
- }
- //==================
- //==================
- void DBall_SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles)
- {
- edict_t *bestspot;
- float bestdistance, bestplayerdistance;
- edict_t *spot;
- char *spottype;
- char skin[512];
- strcpy(skin, Info_ValueForKey (ent->client->pers.userinfo, "skin"));
- if(!strcmp(dball_team1_skin->string, skin))
- spottype = "dm_dball_team1_start";
- else if(!strcmp(dball_team2_skin->string, skin))
- spottype = "dm_dball_team2_start";
- else
- spottype = "info_player_deathmatch";
- spot = NULL;
- bestspot = NULL;
- bestdistance = 0;
- while ((spot = G_Find (spot, FOFS(classname), spottype)) != NULL)
- {
- bestplayerdistance = PlayersRangeFromSpot (spot);
- if (bestplayerdistance > bestdistance)
- {
- bestspot = spot;
- bestdistance = bestplayerdistance;
- }
- }
- if (bestspot)
- {
- VectorCopy (bestspot->s.origin, origin);
- origin[2] += 9;
- VectorCopy (bestspot->s.angles, angles);
- return;
- }
- // if we didn't find an appropriate spawnpoint, just
- // call the standard one.
- SelectSpawnPoint(ent, origin, angles);
- }
- //==================
- //==================
- void DBall_GameInit (void)
- {
- // we don't want a minimum speed for friction to take effect.
- // this will allow any knockback to move stuff.
- sv_stopspeed->value = 0;
- dball_team1_goalscore = 0;
- dball_team2_goalscore = 0;
- dmflags->value = (int)dmflags->value | DF_NO_MINES | DF_NO_NUKES | DF_NO_STACK_DOUBLE |
- DF_NO_FRIENDLY_FIRE | DF_SKINTEAMS;
-
- dball_team1_skin = gi.cvar ("dball_team1_skin", "male/ctf_r", 0);
- dball_team2_skin = gi.cvar ("dball_team2_skin", "male/ctf_b", 0);
- goallimit = gi.cvar ("goallimit", "0", 0);
- }
- //==================
- //==================
- void DBall_PostInitSetup (void)
- {
- edict_t *e;
- e=NULL;
- // turn teleporter destinations nonsolid.
- while(e = G_Find (e, FOFS(classname), "misc_teleporter_dest"))
- {
- e->solid = SOLID_NOT;
- gi.linkentity (e);
- }
- // count the ball start points
- dball_ball_startpt_count = 0;
- e=NULL;
- while(e = G_Find (e, FOFS(classname), "dm_dball_ball_start"))
- {
- dball_ball_startpt_count++;
- }
- if(dball_ball_startpt_count == 0)
- gi.dprintf("No Deathball start points!\n");
- }
- //==================
- // DBall_ChangeDamage - half damage between players. full if it involves
- // the ball entity
- //==================
- int DBall_ChangeDamage (edict_t *targ, edict_t *attacker, int damage, int mod)
- {
- // cut player -> ball damage to 1
- if (targ == dball_ball_entity)
- return 1;
- // damage player -> player is halved
- if (attacker != dball_ball_entity)
- return damage / 2;
- return damage;
- }
- //==================
- //==================
- int DBall_ChangeKnockback (edict_t *targ, edict_t *attacker, int knockback, int mod)
- {
- if(targ != dball_ball_entity)
- return knockback;
-
- if(knockback < 1)
- {
- // FIXME - these don't account for quad/double
- if(mod == MOD_ROCKET) // rocket
- knockback = 70;
- else if(mod == MOD_BFG_EFFECT) // bfg
- knockback = 90;
- else
- gi.dprintf ("zero knockback, mod %d\n", mod);
- }
- else
- {
- // FIXME - change this to an array?
- switch(mod)
- {
- case MOD_BLASTER:
- knockback *= 3;
- break;
- case MOD_SHOTGUN:
- knockback = (knockback * 3) / 8;
- break;
- case MOD_SSHOTGUN:
- knockback = knockback / 3;
- break;
- case MOD_MACHINEGUN:
- knockback = (knockback * 3) / 2;
- break;
- case MOD_HYPERBLASTER:
- knockback *= 4;
- break;
- case MOD_GRENADE:
- case MOD_HANDGRENADE:
- case MOD_PROX:
- case MOD_G_SPLASH:
- case MOD_HG_SPLASH:
- case MOD_HELD_GRENADE:
- case MOD_TRACKER:
- case MOD_DISINTEGRATOR:
- knockback /= 2;
- break;
- case MOD_R_SPLASH:
- knockback = (knockback * 3) / 2;
- break;
- case MOD_RAILGUN:
- case MOD_HEATBEAM:
- knockback /= 3;
- break;
- }
- }
- // gi.dprintf("mod: %d knockback: %d\n", mod, knockback);
- return knockback;
- }
- // **************************
- // Goals
- // **************************
- void DBall_GoalTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
- {
- int team_score;
- int scorechange;
- int j;
- char value[512];
- char *p;
- edict_t *ent;
- if(other != dball_ball_entity)
- return;
- self->health = self->max_health;
- // determine which team scored, and bump the team score
- if(self->spawnflags & DBALL_GOAL_TEAM1)
- {
- dball_team1_goalscore += self->wait;
- team_score = 1;
- }
- else
- {
- dball_team2_goalscore += self->wait;
- team_score = 2;
- }
- // bump the score for everyone on the correct team.
- for (j = 1; j <= game.maxclients; j++)
- {
- ent = &g_edicts[j];
- if (!ent->inuse)
- continue;
- if (!ent->client)
- continue;
-
- if (ent == other->enemy)
- scorechange = self->wait + 5;
- else
- scorechange = self->wait;
- strcpy(value, Info_ValueForKey (ent->client->pers.userinfo, "skin"));
- p = strchr(value, '/');
- if (p)
- {
- if(!strcmp(dball_team1_skin->string, value))
- {
- if(team_score == 1)
- ent->client->resp.score += scorechange;
- else if(other->enemy == ent)
- ent->client->resp.score -= scorechange;
- }
- else if(!strcmp(dball_team2_skin->string, value))
- {
- if(team_score == 2)
- ent->client->resp.score += scorechange;
- else if(other->enemy == ent)
- ent->client->resp.score -= scorechange;
- }
- else
- gi.dprintf("unassigned player!!!!\n");
- }
- }
- if(other->enemy)
- gi.dprintf("score for team %d by %s\n", team_score, other->enemy->client->pers.netname);
- else
- gi.dprintf("score for team %d by someone\n", team_score);
- DBall_BallDie (other, other->enemy, other->enemy, 0, vec3_origin);
- G_UseTargets (self, other);
- }
- // **************************
- // Ball
- // **************************
- edict_t *PickBallStart (edict_t *ent)
- {
- int which, current;
- edict_t *e;
- which = ceil(random() * dball_ball_startpt_count);
- e = NULL;
- current = 0;
- while(e = G_Find (e, FOFS(classname), "dm_dball_ball_start"))
- {
- current++;
- if(current == which)
- return e;
- }
- if(current == 0)
- gi.dprintf("No ball start points found!\n");
- return G_Find(NULL, FOFS(classname), "dm_dball_ball_start");
- }
- //==================
- // DBall_BallTouch - if the ball hit another player, hurt them
- //==================
- void DBall_BallTouch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
- {
- vec3_t dir;
- float dot;
- float speed;
- if(other->takedamage == DAMAGE_NO)
- return;
- // hit a player
- if(other->client)
- {
- if(ent->velocity[0] || ent->velocity[1] || ent->velocity[2])
- {
- speed = VectorLength(ent->velocity);
- VectorSubtract(ent->s.origin, other->s.origin, dir);
- dot = DotProduct(dir, ent->velocity);
- if(dot > 0.7)
- {
- T_Damage (other, ent, ent, vec3_origin, ent->s.origin, vec3_origin,
- speed/10, speed/10, 0, MOD_DBALL_CRUSH);
- }
- }
- }
- }
- //==================
- // DBall_BallPain
- //==================
- void DBall_BallPain (edict_t *self, edict_t *other, float kick, int damage)
- {
- self->enemy = other;
- self->health = self->max_health;
- // if(other->classname)
- // gi.dprintf("hurt by %s -- %d\n", other->classname, self->health);
- }
- void DBall_BallDie (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
- {
- // do the splash effect
- gi.WriteByte (svc_temp_entity);
- gi.WriteByte (TE_DBALL_GOAL);
- gi.WritePosition (self->s.origin);
- gi.multicast (self->s.origin, MULTICAST_PVS);
- VectorClear(self->s.angles);
- VectorClear(self->velocity);
- VectorClear(self->avelocity);
- // make it invisible and desolid until respawn time
- self->solid = SOLID_NOT;
- // self->s.modelindex = 0;
- self->think = DBall_BallRespawn;
- self->nextthink = level.time + 2;
- gi.linkentity(self);
- }
- void DBall_BallRespawn (edict_t *self)
- {
- edict_t *start;
- // do the splash effect
- gi.WriteByte (svc_temp_entity);
- gi.WriteByte (TE_DBALL_GOAL);
- gi.WritePosition (self->s.origin);
- gi.multicast (self->s.origin, MULTICAST_PVS);
- // move the ball and stop it
- start = PickBallStart(self);
- if(start)
- {
- VectorCopy(start->s.origin, self->s.origin);
- VectorCopy(start->s.origin, self->s.old_origin);
- }
- VectorClear(self->s.angles);
- VectorClear(self->velocity);
- VectorClear(self->avelocity);
-
- self->solid = SOLID_BBOX;
- self->s.modelindex = gi.modelindex ("models/objects/dball/tris.md2");
- self->s.event = EV_PLAYER_TELEPORT;
- self->groundentity = NULL;
- // kill anything at the destination
- KillBox (self);
- gi.linkentity (self);
- }
- // ************************
- // SPEED CHANGES
- // ************************
- #define DBALL_SPEED_ONEWAY 1
- void DBall_SpeedTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
- {
- float dot;
- vec3_t vel;
- if(other != dball_ball_entity)
- return;
- if(self->timestamp >= level.time)
- return;
- if(VectorLength(other->velocity) < 1)
- return;
- if(self->spawnflags & DBALL_SPEED_ONEWAY)
- {
- VectorCopy (other->velocity, vel);
- VectorNormalize (vel);
- dot = DotProduct (vel, self->movedir);
- if(dot < 0.8)
- return;
- }
- self->timestamp = level.time + self->delay;
- VectorScale (other->velocity, self->speed, other->velocity);
- }
- // ************************
- // SPAWN FUNCTIONS
- // ************************
- /*QUAKED dm_dball_ball (1 .5 .5) (-48 -48 -48) (48 48 48)
- Deathball Ball
- */
- void SP_dm_dball_ball (edict_t *self)
- {
- if(!(deathmatch->value))
- {
- G_FreeEdict(self);
- return;
- }
- if(gamerules && (gamerules->value != RDM_DEATHBALL))
- {
- G_FreeEdict(self);
- return;
- }
- dball_ball_entity = self;
- // VectorCopy (self->s.origin, dball_ball_startpt);
- self->s.modelindex = gi.modelindex ("models/objects/dball/tris.md2");
- VectorSet (self->mins, -32, -32, -32);
- VectorSet (self->maxs, 32, 32, 32);
- self->solid = SOLID_BBOX;
- self->movetype = MOVETYPE_NEWTOSS;
- self->clipmask = MASK_MONSTERSOLID;
- self->takedamage = DAMAGE_YES;
- self->mass = 50;
- self->health = 50000;
- self->max_health = 50000;
- self->pain = DBall_BallPain;
- self->die = DBall_BallDie;
- self->touch = DBall_BallTouch;
- gi.linkentity (self);
- }
- /*QUAKED dm_dball_team1_start (1 .5 .5) (-16 -16 -24) (16 16 32)
- Deathball team 1 start point
- */
- void SP_dm_dball_team1_start (edict_t *self)
- {
- if (!deathmatch->value)
- {
- G_FreeEdict (self);
- return;
- }
- if(gamerules && (gamerules->value != RDM_DEATHBALL))
- {
- G_FreeEdict(self);
- return;
- }
- }
- /*QUAKED dm_dball_team2_start (1 .5 .5) (-16 -16 -24) (16 16 32)
- Deathball team 2 start point
- */
- void SP_dm_dball_team2_start (edict_t *self)
- {
- if (!deathmatch->value)
- {
- G_FreeEdict (self);
- return;
- }
- if(gamerules && (gamerules->value != RDM_DEATHBALL))
- {
- G_FreeEdict(self);
- return;
- }
- }
- /*QUAKED dm_dball_ball_start (1 .5 .5) (-48 -48 -48) (48 48 48)
- Deathball ball start point
- */
- void SP_dm_dball_ball_start (edict_t *self)
- {
- if (!deathmatch->value)
- {
- G_FreeEdict (self);
- return;
- }
- if(gamerules && (gamerules->value != RDM_DEATHBALL))
- {
- G_FreeEdict(self);
- return;
- }
- }
- /*QUAKED dm_dball_speed_change (1 .5 .5) ? ONEWAY
- Deathball ball speed changing field.
- speed: multiplier for speed (.5 = half, 2 = double, etc) (default = double)
- angle: used with ONEWAY so speed change is only one way.
- delay: time between speed changes (default: 0.2 sec)
- */
- void SP_dm_dball_speed_change (edict_t *self)
- {
- if (!deathmatch->value)
- {
- G_FreeEdict (self);
- return;
- }
- if(gamerules && (gamerules->value != RDM_DEATHBALL))
- {
- G_FreeEdict(self);
- return;
- }
- if(!self->speed)
- self->speed = 2;
- if(!self->delay)
- self->delay = 0.2;
- self->touch = DBall_SpeedTouch;
- self->solid = SOLID_TRIGGER;
- self->movetype = MOVETYPE_NONE;
- self->svflags |= SVF_NOCLIENT;
- if (!VectorCompare(self->s.angles, vec3_origin))
- G_SetMovedir (self->s.angles, self->movedir);
- else
- VectorSet (self->movedir, 1, 0, 0);
- gi.setmodel (self, self->model);
- gi.linkentity (self);
- }
- /*QUAKED dm_dball_goal (1 .5 .5) ? TEAM1 TEAM2
- Deathball goal
- Team1/Team2 - beneficiary of this goal. when the ball enters this goal, the beneficiary team will score.
- "wait": score to be given for this goal (default 10) player gets score+5.
- */
- void SP_dm_dball_goal (edict_t *self)
- {
- if(!(deathmatch->value))
- {
- G_FreeEdict(self);
- return;
- }
- if(gamerules && (gamerules->value != RDM_DEATHBALL))
- {
- G_FreeEdict(self);
- return;
- }
- if(!self->wait)
- self->wait = 10;
- self->touch = DBall_GoalTouch;
- self->solid = SOLID_TRIGGER;
- self->movetype = MOVETYPE_NONE;
- self->svflags |= SVF_NOCLIENT;
- if (!VectorCompare(self->s.angles, vec3_origin))
- G_SetMovedir (self->s.angles, self->movedir);
- gi.setmodel (self, self->model);
- gi.linkentity (self);
- }
|