/* * ================ * 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; }
/* * ============================================================================== * * 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); }
/* * ================ * CG_ReflectVelocity * ================ */ void CG_ReflectVelocity(localEntity_t *le, trace_t *trace) { vec3_t velocity; float dot; int hitTime; // reflect the velocity on the trace plane hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction; BG_EvaluateTrajectoryDelta(&le->pos, hitTime, velocity); dot = DotProduct(velocity, trace->plane.normal); VectorMA(velocity, -2 * dot, trace->plane.normal, le->pos.trDelta); VectorScale(le->pos.trDelta, le->bounceFactor, le->pos.trDelta); VectorCopy(trace->endpos, le->pos.trBase); le->pos.trTime = cg.time; // check for stop, making sure that even on low FPS systems it doesn't bobble if (trace->allsolid || (trace->plane.normal[2] > 0 && (le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2]))) { le->pos.trType = TR_STATIONARY; } else { } }
/* * ================ * CG_FragmentBounceSound * ================ */ void CG_FragmentBounceSound(localEntity_t *le, trace_t *trace) { if (le->leBounceSoundType == LEBS_BLOOD) { // half the gibs will make splat sounds if (rand() & 1) { int r = rand() & 3; sfxHandle_t s; if (r == 0) { s = cgs.media.gibBounce1Sound; } else if (r == 1) { s = cgs.media.gibBounce2Sound; } else { s = cgs.media.gibBounce3Sound; } trap_S_StartSound(trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s); } } else if (le->leBounceSoundType == LEBS_BRASS) { } // don't allow a fragment to make multiple bounce sounds, // or it gets too noisy as they settle le->leBounceSoundType = LEBS_NONE; }
void Touch_Multi(gentity_t *self, gentity_t *other, trace_t *trace) { if (!other->client) { return; } multi_trigger(self, other); }
/* * ============================================================================== * * 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); }
void hurt_touch(gentity_t *self, gentity_t *other, trace_t *trace) { int dflags; if (!other->takedamage) { return; } if (self->timestamp > level.time) { return; } if (self->spawnflags & 16) { self->timestamp = level.time + 1000; } else { self->timestamp = level.time + FRAMETIME; } // play sound if (!(self->spawnflags & 4)) { G_Sound(other, CHAN_AUTO, self->noise_index); } if (self->spawnflags & 8) { dflags = DAMAGE_NO_PROTECTION; } else { dflags = 0; } G_Damage(other, self, self, NULL, NULL, self->damage, dflags, MOD_TRIGGER_HURT); }
/* * ================ * CG_FragmentBounceMark * ================ */ void CG_FragmentBounceMark(localEntity_t *le, trace_t *trace) { int radius; if (le->leMarkType == LEMT_BLOOD) { radius = 16 + (rand() & 31); CG_ImpactMark(cgs.media.bloodMarkShader, trace->endpos, trace->plane.normal, random() * 360, 1, 1, 1, 1, true, radius, false); } else if (le->leMarkType == LEMT_BURN) { radius = 8 + (rand() & 15); CG_ImpactMark(cgs.media.burnMarkShader, trace->endpos, trace->plane.normal, random() * 360, 1, 1, 1, 1, true, radius, false); } // don't allow a fragment to make multiple marks, or they // pile up while settling le->leMarkType = LEMT_NONE; }
/* * ================ * G_MissileImpact * ================ */ void G_MissileImpact(gentity_t *ent, trace_t *trace) { gentity_t *other; bool hitClient = false; other = &g_entities[trace->entityNum]; // check for bounce if (!other->takedamage && (ent->s.eFlags & (EF_BOUNCE | EF_BOUNCE_HALF))) { G_BounceMissile(ent, trace); G_AddEvent(ent, EV_GRENADE_BOUNCE, 0); return; } // impact damage if (other->takedamage) { // FIXME: wrong damage direction? if (ent->damage) { vec3_t velocity; if (LogAccuracyHit(other, &g_entities[ent->r.ownerNum])) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = true; } BG_EvaluateTrajectoryDelta(&ent->s.pos, level.time, velocity); if (VectorLength(velocity) == 0) { velocity[2] = 1; // stepped on a grenade } G_Damage(other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); } } if (!strcmp(ent->classname, "hook")) { gentity_t *nent; vec3_t v; nent = G_Spawn(); if (other->takedamage && other->client) { G_AddEvent(nent, EV_MISSILE_HIT, DirToByte(trace->plane.normal)); nent->s.otherEntityNum = other->s.number; ent->enemy = other; v[0] = other->r.currentOrigin[0] + (other->r.mins[0] + other->r.maxs[0]) * 0.5; v[1] = other->r.currentOrigin[1] + (other->r.mins[1] + other->r.maxs[1]) * 0.5; v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5; SnapVectorTowards(v, ent->s.pos.trBase); // save net bandwidth } else { VectorCopy(trace->endpos, v); G_AddEvent(nent, EV_MISSILE_MISS, DirToByte(trace->plane.normal)); ent->enemy = NULL; } SnapVectorTowards(v, ent->s.pos.trBase); // save net bandwidth nent->freeAfterEvent = true; // change over to a normal entity right at the point of impact nent->s.eType = ET_GENERAL; ent->s.eType = ET_GRAPPLE; G_SetOrigin(ent, v); G_SetOrigin(nent, v); ent->think = Weapon_HookThink; ent->nextthink = level.time + FRAMETIME; ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL; VectorCopy(ent->r.currentOrigin, ent->parent->client->ps.grapplePoint); trap_LinkEntity(ent); trap_LinkEntity(nent); return; } // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? if (other->takedamage && other->client) { G_AddEvent(ent, EV_MISSILE_HIT, DirToByte(trace->plane.normal)); ent->s.otherEntityNum = other->s.number; } else if (trace->surfaceFlags & SURF_METALSTEPS) { G_AddEvent(ent, EV_MISSILE_MISS_METAL, DirToByte(trace->plane.normal)); } else { G_AddEvent(ent, EV_MISSILE_MISS, DirToByte(trace->plane.normal)); } ent->freeAfterEvent = true; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; SnapVectorTowards(trace->endpos, ent->s.pos.trBase); // save net bandwidth G_SetOrigin(ent, trace->endpos); // splash damage (doesn't apply to person directly hit) if (ent->splashDamage) { if (G_RadiusDamage(trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath)) { if (!hitClient) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } trap_LinkEntity(ent); }
/* * =============== * Touch_Item * =============== */ void Touch_Item(gentity_t *ent, gentity_t *other, trace_t *trace) { int respawn; bool predict; if (!other->client) { return; } if (other->health < 1) { return; // dead people can't pickup } // the same pickup rules are used for client side and server side if (!BG_CanItemBeGrabbed(g_gametype.integer, &ent->s, &other->client->ps)) { return; } G_LogPrintf("Item: %i %s\n", other->s.number, ent->item->classname); predict = other->client->pers.predictItemPickup; // call the item-specific pickup function switch (ent->item->giType) { case IT_WEAPON: respawn = Pickup_Weapon(ent, other); // predict = false; break; case IT_AMMO: respawn = Pickup_Ammo(ent, other); // predict = false; break; case IT_ARMOR: respawn = Pickup_Armor(ent, other); break; case IT_HEALTH: respawn = Pickup_Health(ent, other); break; case IT_POWERUP: respawn = Pickup_Powerup(ent, other); predict = false; break; case IT_TEAM: respawn = Pickup_Team(ent, other); break; case IT_HOLDABLE: respawn = Pickup_Holdable(ent, other); break; default: return; } if (!respawn) { return; } // play the normal pickup sound if (predict) { G_AddPredictableEvent(other, EV_ITEM_PICKUP, ent->s.modelindex); } else { G_AddEvent(other, EV_ITEM_PICKUP, ent->s.modelindex); } // powerup pickups are global broadcasts if (ent->item->giType == IT_POWERUP || ent->item->giType == IT_TEAM) { // if we want the global sound to play if (!ent->speed) { gentity_t *te; te = G_TempEntity(ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP); te->s.eventParm = ent->s.modelindex; te->r.svFlags |= SVF_BROADCAST; } else { gentity_t *te; te = G_TempEntity(ent->s.pos.trBase, EV_GLOBAL_ITEM_PICKUP); te->s.eventParm = ent->s.modelindex; // only send this temp entity to a single client te->r.svFlags |= SVF_SINGLECLIENT; te->r.singleClient = other->s.number; } } // fire item targets G_UseTargets(ent, other); // wait of -1 will not respawn if (ent->wait == -1) { ent->r.svFlags |= SVF_NOCLIENT; ent->s.eFlags |= EF_NODRAW; ent->r.contents = 0; ent->unlinkAfterEvent = true; return; } // non zero wait overrides respawn time if (ent->wait) { respawn = ent->wait; } // random can be used to vary the respawn time if (ent->random) { respawn += crandom() * ent->random; if (respawn < 1) { respawn = 1; } } // dropped items will not respawn if (ent->flags & FL_DROPPED_ITEM) { ent->freeAfterEvent = true; } // picked up items still stay around, they just don't // draw anything. This allows respawnable items // to be placed on movers. ent->r.svFlags |= SVF_NOCLIENT; ent->s.eFlags |= EF_NODRAW; ent->r.contents = 0; // ZOID // A negative respawn times means to never respawn this item (but don't // delete it). This is used by items that are respawned by third party // events such as ctf flags if (respawn <= 0) { ent->nextthink = 0; ent->think = 0; } else { ent->nextthink = level.time + respawn * 1000; ent->think = RespawnItem; } trap_LinkEntity(ent); }