/* * ============================================================================== * * trigger_teleport * * ============================================================================== */ void trigger_teleporter_touch(gentity_t *self, gentity_t *other, trace_t *trace) { gentity_t *dest; if (!other->client) { return; } if (other->client->ps.pm_type == PM_DEAD) { return; } // Spectators only? if ((self->spawnflags & 1) && other->client->sess.sessionTeam != TEAM_SPECTATOR) { return; } dest = G_PickTarget(self->target); if (!dest) { G_Printf("Couldn't find teleporter destination\n"); return; } TeleportPlayer(other, dest->s.origin, dest->s.angles); }
/*QUAKED target_push (.5 .5 .5) (-8 -8 -8) (8 8 8) bouncepad * Pushes the activator in the direction.of angle, or towards a target apex. * "speed" defaults to 1000 * if "bouncepad", play bounce noise instead of windfly */ void SP_target_push(gentity_t *self) { if (!self->speed) { self->speed = 1000; } G_SetMovedir(self->s.angles, self->s.origin2); VectorScale(self->s.origin2, self->speed, self->s.origin2); if (self->spawnflags & 1) { self->noise_index = G_SoundIndex("sound/world/jumppad.wav"); } else { self->noise_index = G_SoundIndex("sound/misc/windfly.wav"); } if (self->target) { VectorCopy(self->s.origin, self->r.absmin); VectorCopy(self->s.origin, self->r.absmax); self->think = AimAtTarget; self->nextthink = level.time + FRAMETIME; } self->use = Use_target_push; }
void SP_trigger_hurt(gentity_t *self) { InitTrigger(self); self->noise_index = G_SoundIndex("sound/world/electro.wav"); self->touch = hurt_touch; if (!self->damage) { self->damage = 5; } self->r.contents = CONTENTS_TRIGGER; if (self->spawnflags & 2) { self->use = hurt_use; } // link in to the world if starting active if (!(self->spawnflags & 1)) { trap_LinkEntity(self); } }
void Use_target_push(gentity_t *self, gentity_t *other, gentity_t *activator) { if (!activator->client) { return; } if (activator->client->ps.pm_type != PM_NORMAL) { return; } if (activator->client->ps.powerups[PW_FLIGHT]) { return; } VectorCopy(self->s.origin2, activator->client->ps.velocity); // play fly sound every 1.5 seconds if (activator->fly_sound_debounce_time < level.time) { activator->fly_sound_debounce_time = level.time + 1500; G_Sound(activator, CHAN_AUTO, self->noise_index); } }
//====================================================================== /* * =============== * LogAccuracyHit * =============== */ bool LogAccuracyHit(gentity_t *target, gentity_t *attacker) { if (!target->takedamage) { return(false); } if (target == attacker) { return(false); } if (!target->client) { return(false); } if (!attacker->client) { return(false); } if (target->client->ps.stats[STAT_HEALTH] <= 0) { return(false); } if (OnSameTeam(target, attacker)) { return(false); } return(true); }
/*QUAKED target_give (1 0 0) (-8 -8 -8) (8 8 8) * Gives the activator all the items pointed to. */ void Use_Target_Give(gentity_t *ent, gentity_t *other, gentity_t *activator) { gentity_t *t; trace_t trace; if (!activator->client) { return; } if (!ent->target) { return; } memset(&trace, 0, sizeof(trace)); t = NULL; while ((t = G_Find(t, FOFS(targetname), ent->target)) != NULL) { if (!t->item) { continue; } Touch_Item(t, activator, &trace); // make sure it isn't going to respawn or show any events t->nextthink = 0; trap_UnlinkEntity(t); } }
/* * ================== * LookAtKiller * ================== */ void LookAtKiller(gentity_t *self, gentity_t *inflictor, gentity_t *attacker) { vec3_t dir; vec3_t angles; if (attacker && attacker != self) { VectorSubtract(attacker->s.pos.trBase, self->s.pos.trBase, dir); } else if (inflictor && inflictor != self) { VectorSubtract(inflictor->s.pos.trBase, self->s.pos.trBase, dir); } else { self->client->ps.stats[STAT_DEAD_YAW] = self->s.angles[YAW]; return; } self->client->ps.stats[STAT_DEAD_YAW] = vectoyaw(dir); angles[YAW] = vectoyaw(dir); angles[PITCH] = 0; angles[ROLL] = 0; }
//============================================================================= /* * ================= * fire_rocket * ================= */ gentity_t *fire_rocket(gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *bolt; VectorNormalize(dir); bolt = G_Spawn(); bolt->classname = "rocket"; bolt->nextthink = level.time + 15000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_ROCKET_LAUNCHER; bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = 100; bolt->splashDamage = 100; bolt->splashRadius = 120; bolt->methodOfDeath = MOD_ROCKET; bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy(start, bolt->s.pos.trBase); VectorScale(dir, 900, bolt->s.pos.trDelta); SnapVector(bolt->s.pos.trDelta); // save net bandwidth VectorCopy(start, bolt->r.currentOrigin); return(bolt); }
/* * ================ * G_ExplodeMissile * * Explode a missile without an impact * ================ */ void G_ExplodeMissile(gentity_t *ent) { vec3_t dir; vec3_t origin; BG_EvaluateTrajectory(&ent->s.pos, level.time, origin); SnapVector(origin); G_SetOrigin(ent, origin); // we don't have a valid direction, so just point straight up dir[0] = dir[1] = 0; dir[2] = 1; ent->s.eType = ET_GENERAL; G_AddEvent(ent, EV_MISSILE_MISS, DirToByte(dir)); ent->freeAfterEvent = true; // splash damage if (ent->splashDamage) { if (G_RadiusDamage(ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent , ent->splashMethodOfDeath)) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } trap_LinkEntity(ent); }
/* * ================= * fire_grapple * ================= */ gentity_t *fire_grapple(gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *hook; VectorNormalize(dir); hook = G_Spawn(); hook->classname = "hook"; hook->nextthink = level.time + 10000; hook->think = Weapon_HookFree; hook->s.eType = ET_MISSILE; hook->r.svFlags = SVF_USE_CURRENT_ORIGIN; hook->s.weapon = WP_GRAPPLING_HOOK; hook->r.ownerNum = self->s.number; hook->methodOfDeath = MOD_GRAPPLE; hook->clipmask = MASK_SHOT; hook->parent = self; hook->target_ent = NULL; hook->s.pos.trType = TR_LINEAR; hook->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame hook->s.otherEntityNum = self->s.number; // use to match beam in client VectorCopy(start, hook->s.pos.trBase); VectorScale(dir, 800, hook->s.pos.trDelta); SnapVector(hook->s.pos.trDelta); // save net bandwidth VectorCopy(start, hook->r.currentOrigin); self->client->hook = hook; return(hook); }
/* * ====================================================================== * * SHOTGUN * * ====================================================================== */ // DEFAULT_SHOTGUN_SPREAD and DEFAULT_SHOTGUN_COUNT are in bg_public.h, because // client predicts same spreads #define DEFAULT_SHOTGUN_DAMAGE 10 bool ShotgunPellet(vec3_t start, vec3_t end, gentity_t *ent) { trace_t tr; int damage, i, passent; gentity_t *traceEnt; vec3_t tr_start, tr_end; passent = ent->s.number; VectorCopy(start, tr_start); VectorCopy(end, tr_end); for (i = 0; i < 10; i++) { trap_Trace(&tr, tr_start, NULL, NULL, tr_end, passent, MASK_SHOT); traceEnt = &g_entities[tr.entityNum]; // send bullet impact if (tr.surfaceFlags & SURF_NOIMPACT) { return(false); } if (traceEnt->takedamage) { damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor; G_Damage(traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_SHOTGUN); if (LogAccuracyHit(traceEnt, ent)) { return(true); } } return(false); } return(false); }
// this should match CG_ShotgunPattern void ShotgunPattern(vec3_t origin, vec3_t origin2, int seed, gentity_t *ent) { int i; float r, u; vec3_t end; vec3_t forward, right, up; int oldScore; bool hitClient = false; // derive the right and up vectors from the forward vector, because // the client won't have any other information VectorNormalize2(origin2, forward); PerpendicularVector(right, forward); CrossProduct(forward, right, up); oldScore = ent->client->ps.persistant[PERS_SCORE]; // generate the "random" spread pattern for (i = 0; i < DEFAULT_SHOTGUN_COUNT; i++) { r = Q_crandom(&seed) * DEFAULT_SHOTGUN_SPREAD * 16; u = Q_crandom(&seed) * DEFAULT_SHOTGUN_SPREAD * 16; VectorMA(origin, 8192 * 16, forward, end); VectorMA(end, r, right, end); VectorMA(end, u, up, end); if (ShotgunPellet(origin, end, ent) && !hitClient) { hitClient = true; ent->client->accuracy_hits++; } } }
//========================================================== /*QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off global activator * "noise" wav file to play * * A global sound will play full volume throughout the level. * Activator sounds will play on the player that activated the target. * Global and activator sounds can't be combined with looping. * Normal sounds play each time the target is used. * Looped sounds will be toggled by use functions. * Multiple identical looping sounds will just increase volume without any speed cost. * "wait" : Seconds between auto triggerings, 0 = don't auto trigger * "random" wait variance, default is 0 */ void Use_Target_Speaker(gentity_t *ent, gentity_t *other, gentity_t *activator) { if (ent->spawnflags & 3) // looping sound toggles { if (ent->s.loopSound) { ent->s.loopSound = 0; // turn it off } else { ent->s.loopSound = ent->noise_index; // start it } } else // normal sound { if (ent->spawnflags & 8) { G_AddEvent(activator, EV_GENERAL_SOUND, ent->noise_index); } else if (ent->spawnflags & 4) { G_AddEvent(ent, EV_GLOBAL_SOUND, ent->noise_index); } else { G_AddEvent(ent, EV_GENERAL_SOUND, ent->noise_index); } } }
/* * =============== * G_DamageFeedback * * Called just before a snapshot is sent to the given player. * Totals up all damage and generates both the player_state_t * damage values to that client for pain blends and kicks, and * global pain sound events for all clients. * =============== */ void P_DamageFeedback(gentity_t *player) { gclient_t *client; float count; vec3_t angles; client = player->client; if (client->ps.pm_type == PM_DEAD) { return; } // total points of damage shot at the player this frame count = client->damage_blood + client->damage_armor; if (count == 0) { return; // didn't take any damage } if (count > 255) { count = 255; } // send the information to the client // world damage (falling, slime, etc) uses a special code // to make the blend blob centered instead of positional if (client->damage_fromWorld) { client->ps.damagePitch = 255; client->ps.damageYaw = 255; client->damage_fromWorld = false; } else { vectoangles(client->damage_from, angles); client->ps.damagePitch = angles[PITCH] / 360.0 * 256; client->ps.damageYaw = angles[YAW] / 360.0 * 256; } // play an apropriate pain sound if ((level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE)) { player->pain_debounce_time = level.time + 700; G_AddEvent(player, EV_PAIN, player->health); client->ps.damageEvent++; } client->ps.damageCount = count; // // clear totals // client->damage_blood = 0; client->damage_armor = 0; client->damage_knockback = 0; }
//============================================================== /* * ============== * ClientImpacts * ============== */ void ClientImpacts(gentity_t *ent, pmove_t *pm) { int i, j; trace_t trace; gentity_t *other; memset(&trace, 0, sizeof(trace)); for (i = 0; i < pm->numtouch; i++) { for (j = 0; j < i; j++) { if (pm->touchents[j] == pm->touchents[i]) { break; } } if (j != i) { continue; // duplicated } other = &g_entities[pm->touchents[i]]; if ((ent->r.svFlags & SVF_BOT) && (ent->touch)) { ent->touch(ent, other, &trace); } if (!other->touch) { continue; } other->touch(other, ent, &trace); } }
/* * ================ * G_BounceMissile * * ================ */ void G_BounceMissile(gentity_t *ent, trace_t *trace) { vec3_t velocity; float dot; int hitTime; // reflect the velocity on the trace plane hitTime = level.previousTime + (level.time - level.previousTime) * trace->fraction; BG_EvaluateTrajectoryDelta(&ent->s.pos, hitTime, velocity); dot = DotProduct(velocity, trace->plane.normal); VectorMA(velocity, -2 * dot, trace->plane.normal, ent->s.pos.trDelta); if (ent->s.eFlags & EF_BOUNCE_HALF) { VectorScale(ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta); // check for stop if (trace->plane.normal[2] > 0.2 && VectorLength(ent->s.pos.trDelta) < 40) { G_SetOrigin(ent, trace->endpos); return; } } VectorAdd(ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin); VectorCopy(ent->r.currentOrigin, ent->s.pos.trBase); ent->s.pos.trTime = level.time; }
/* * ================== * GibEntity * ================== */ void GibEntity(gentity_t *self, int killer) { gentity_t *ent; int i; //if this entity still has kamikaze if (self->s.eFlags & EF_KAMIKAZE) { // check if there is a kamikaze timer around for this owner for (i = 0; i < MAX_GENTITIES; i++) { ent = &g_entities[i]; if (!ent->inuse) { continue; } if (ent->activator != self) { continue; } if (strcmp(ent->classname, "kamikaze timer")) { continue; } G_FreeEntity(ent); break; } } G_AddEvent(self, EV_GIB_PLAYER, killer); self->takedamage = false; self->s.eType = ET_INVISIBLE; self->r.contents = 0; }
/* * =============== * CalcMuzzlePointOrigin * * set muzzle location relative to pivoting eye * =============== */ void CalcMuzzlePointOrigin(gentity_t *ent, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint) { VectorCopy(ent->s.pos.trBase, muzzlePoint); muzzlePoint[2] += ent->client->ps.viewheight; VectorMA(muzzlePoint, 14, forward, muzzlePoint); // snap to integer coordinates for more efficient network bandwidth usage SnapVector(muzzlePoint); }
void Touch_Multi(gentity_t *self, gentity_t *other, trace_t *trace) { if (!other->client) { return; } multi_trigger(self, other); }
//====================================================================== void Add_Ammo(gentity_t *ent, int weapon, int count) { ent->client->ps.ammo[weapon] += count; if (ent->client->ps.ammo[weapon] > 200) { ent->client->ps.ammo[weapon] = 200; } }
void SP_target_score(gentity_t *ent) { if (!ent->count) { ent->count = 1; } ent->use = Use_Target_Score; }
void Bullet_Fire(gentity_t *ent, float spread, int damage) { trace_t tr; vec3_t end; float r; float u; gentity_t *tent; gentity_t *traceEnt; int i, passent; damage *= s_quadFactor; r = random() * M_PI * 2.0f; u = sin(r) * crandom() * spread * 16; r = cos(r) * crandom() * spread * 16; VectorMA(muzzle, 8192 * 16, forward, end); VectorMA(end, r, right, end); VectorMA(end, u, up, end); passent = ent->s.number; for (i = 0; i < 10; i++) { trap_Trace(&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT); if (tr.surfaceFlags & SURF_NOIMPACT) { return; } traceEnt = &g_entities[tr.entityNum]; // snap the endpos to integers, but nudged towards the line SnapVectorTowards(tr.endpos, muzzle); // send bullet impact if (traceEnt->takedamage && traceEnt->client) { tent = G_TempEntity(tr.endpos, EV_BULLET_HIT_FLESH); tent->s.eventParm = traceEnt->s.number; if (LogAccuracyHit(traceEnt, ent)) { ent->client->accuracy_hits++; } } else { tent = G_TempEntity(tr.endpos, EV_BULLET_HIT_WALL); tent->s.eventParm = DirToByte(tr.plane.normal); } tent->s.otherEntityNum = ent->s.number; if (traceEnt->takedamage) { G_Damage(traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_MACHINEGUN); } break; } }
/* * ====================================================================== * * GRAPPLING HOOK * * ====================================================================== */ void Weapon_GrapplingHook_Fire(gentity_t *ent) { if (!ent->client->fireHeld && !ent->client->hook) { fire_grapple(ent, muzzle, forward); } ent->client->fireHeld = true; }
/* * ============================================================================== * * trigger_push * * ============================================================================== */ void trigger_push_touch(gentity_t *self, gentity_t *other, trace_t *trace) { if (!other->client) { return; } BG_TouchJumpPad(&other->client->ps, &self->s); }
/* * ================ * G_RunMissile * ================ */ void G_RunMissile(gentity_t *ent) { vec3_t origin; trace_t tr; int passent; // get current position BG_EvaluateTrajectory(&ent->s.pos, level.time, origin); // if this missile bounced off an invulnerability sphere if (ent->target_ent) { passent = ent->target_ent->s.number; } else { // ignore interactions with the missile owner passent = ent->r.ownerNum; } // trace a line from the previous position to the current position trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask); if (tr.startsolid || tr.allsolid) { // make sure the tr.entityNum is set to the entity we're stuck in trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask); tr.fraction = 0; } else { VectorCopy(tr.endpos, ent->r.currentOrigin); } trap_LinkEntity(ent); if (tr.fraction != 1) { // never explode or bounce on sky if (tr.surfaceFlags & SURF_NOIMPACT) { // If grapple, reset owner if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) { ent->parent->client->hook = NULL; } G_FreeEntity(ent); return; } G_MissileImpact(ent, &tr); if (ent->s.eType != ET_MISSILE) { return; // exploded } } // check think function after bouncing G_RunThink(ent); }
//====================================================================== int Pickup_Armor(gentity_t *ent, gentity_t *other) { other->client->ps.stats[STAT_ARMOR] += ent->item->quantity; if (other->client->ps.stats[STAT_ARMOR] > other->client->ps.stats[STAT_MAX_HEALTH] * 2) { other->client->ps.stats[STAT_ARMOR] = other->client->ps.stats[STAT_MAX_HEALTH] * 2; } return(RESPAWN_ARMOR); }
/* * ====================================================================== * * PLASMA GUN * * ====================================================================== */ void Weapon_Plasmagun_Fire(gentity_t *ent) { gentity_t *m; m = fire_plasma(ent, muzzle, forward); m->damage *= s_quadFactor; m->splashDamage *= s_quadFactor; // VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics }
//=========================================================== void locateCamera(gentity_t *ent) { vec3_t dir; gentity_t *target; gentity_t *owner; owner = G_PickTarget(ent->target); if (!owner) { G_Printf("Couldn't find target for misc_partal_surface\n"); G_FreeEntity(ent); return; } ent->r.ownerNum = owner->s.number; // frame holds the rotate speed if (owner->spawnflags & 1) { ent->s.frame = 25; } else if (owner->spawnflags & 2) { ent->s.frame = 75; } // swing camera ? if (owner->spawnflags & 4) { // set to 0 for no rotation at all ent->s.powerups = 0; } else { ent->s.powerups = 1; } // clientNum holds the rotate offset ent->s.clientNum = owner->s.clientNum; VectorCopy(owner->s.origin, ent->s.origin2); // see if the portal_camera has a target target = G_PickTarget(owner->target); if (target) { VectorSubtract(target->s.origin, owner->s.origin, dir); VectorNormalize(dir); } else { G_SetMovedir(owner->s.angles, dir); } ent->s.eventParm = DirToByte(dir); }
/* * =============== * G_SetClientSound * =============== */ void G_SetClientSound(gentity_t *ent) { if (ent->waterlevel && (ent->watertype & (CONTENTS_LAVA | CONTENTS_SLIME))) { ent->client->ps.loopSound = level.snd_fry; } else { ent->client->ps.loopSound = 0; } }
//====================================================================== int Pickup_Holdable(gentity_t *ent, gentity_t *other) { other->client->ps.stats[STAT_HOLDABLE_ITEM] = ent->item - bg_itemlist; if (ent->item->giTag == HI_KAMIKAZE) { other->client->ps.eFlags |= EF_KAMIKAZE; } return(RESPAWN_HOLDABLE); }