/*** * * Copyright (c) 1996-2001, Valve LLC. All rights reserved. * * This product contains software technology licensed from Id * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. * All Rights Reserved. * * Use, distribution, and modification of this source code and/or resulting * object code is restricted to non-commercial enhancements to products from * Valve LLC. All other use, distribution, or modification is prohibited * without written permission from Valve LLC. * ****/ /* ===== gearbox_triggers.cpp ======================================================== spawn and use functions for editor-placed triggers */ #include "extdll.h" #include "util.h" #include "cbase.h" #include "player.h" #include "saverestore.h" #include "trains.h" #include "gamerules.h" #include "triggers.h" #include "skill.h" //========================================================= // CTriggerXenReturn //========================================================= class CTriggerXenReturn : public CTriggerTeleport { public: void Spawn(void); void EXPORT TeleportTouch(CBaseEntity *pOther); }; LINK_ENTITY_TO_CLASS(trigger_xen_return, CTriggerXenReturn); void CTriggerXenReturn::Spawn(void) { CTriggerTeleport::Spawn(); SetTouch(&CTriggerXenReturn::TeleportTouch); } void CTriggerXenReturn::TeleportTouch(CBaseEntity* pOther) { entvars_t* pevToucher = pOther->pev; edict_t *pentTarget = NULL; // Only teleport monsters or clients if (!FBitSet(pevToucher->flags, FL_CLIENT | FL_MONSTER)) return; if (!UTIL_IsMasterTriggered(m_sMaster, pOther)) return; if (!(pev->spawnflags & SF_TRIGGER_ALLOWMONSTERS)) {// no monsters allowed! if (FBitSet(pevToucher->flags, FL_MONSTER)) { return; } } if ((pev->spawnflags & SF_TRIGGER_NOCLIENTS)) {// no clients allowed if (pOther->IsPlayer()) { return; } } pentTarget = FIND_ENTITY_BY_CLASSNAME(pentTarget, "info_displacer_earth_target"); if (FNullEnt(pentTarget)) return; Vector tmp = VARS(pentTarget)->origin; if (pOther->IsPlayer()) { tmp.z -= pOther->pev->mins.z;// make origin adjustments in case the teleportee is a player. (origin in center, not at feet) } tmp.z++; pevToucher->flags &= ~FL_ONGROUND; UTIL_SetOrigin(pevToucher, tmp); pevToucher->angles = pentTarget->v.angles; if (pOther->IsPlayer()) { pevToucher->v_angle = pentTarget->v.angles; } pevToucher->fixangle = TRUE; pevToucher->velocity = pevToucher->basevelocity = g_vecZero; if (pOther->IsPlayer()) { // Ensure the current player is marked as being // on earth. ((CBasePlayer*)pOther)->m_fInXen = FALSE; // Reset gravity to default. pOther->pev->gravity = 1.0f; } // Play teleport sound. EMIT_SOUND(ENT(pOther->pev), CHAN_STATIC, "debris/beamstart7.wav", 1, ATTN_NORM ); } //========================================================= // CTriggerGenewormHit //========================================================= #define SF_TRIGGER_HURT_TARGETONCE 1// Only fire hurt target once #define SF_TRIGGER_HURT_START_OFF 2//spawnflag that makes trigger_push spawn turned OFF #define SF_TRIGGER_HURT_NO_CLIENTS 8//spawnflag that makes trigger_push spawn turned OFF #define SF_TRIGGER_HURT_CLIENTONLYFIRE 16// trigger hurt will only fire its target if it is hurting a client #define SF_TRIGGER_HURT_CLIENTONLYTOUCH 32// only clients may touch this trigger. class CTriggerGenewormHit : public CBaseTrigger { public: void Spawn(); void Precache(); void EXPORT GeneWormTouch(CBaseEntity *pOther); static const char* pAttackSounds[]; static TYPEDESCRIPTION m_SaveData[]; virtual int Save( CSave &save ); virtual int Restore( CRestore &restore ); float m_flLastDamageTime; }; TYPEDESCRIPTION CTriggerGenewormHit::m_SaveData[] = { DEFINE_FIELD(CTriggerGenewormHit, m_flLastDamageTime, FIELD_TIME), }; IMPLEMENT_SAVERESTORE(CTriggerGenewormHit, CBaseTrigger) const char *CTriggerGenewormHit::pAttackSounds[] = { "zombie/claw_strike1.wav", "zombie/claw_strike2.wav", "zombie/claw_strike3.wav" }; void CTriggerGenewormHit::Spawn() { Precache(); InitTrigger(); SetTouch(&CTriggerGenewormHit::GeneWormTouch); if(pev->targetname) SetUse(&CBaseTrigger::ToggleUse); if(pev->spawnflags & SF_TRIGGER_HURT_START_OFF) pev->solid = SOLID_NOT; UTIL_SetOrigin(pev, pev->origin); pev->dmg = gSkillData.gwormDmgHit; m_flLastDamageTime = gpGlobals->time; } void CTriggerGenewormHit::Precache() { PRECACHE_SOUND_ARRAY(pAttackSounds); } void CTriggerGenewormHit::GeneWormTouch(CBaseEntity *pOther) { if( gpGlobals->time - m_flLastDamageTime < 2 || !pOther->pev->takedamage ) return; if( ( pev->spawnflags & SF_TRIGGER_HURT_CLIENTONLYTOUCH ) && !pOther->IsPlayer() ) { // this trigger is only allowed to touch clients, and this ain't a client. return; } if( ( pev->spawnflags & SF_TRIGGER_HURT_NO_CLIENTS ) && pOther->IsPlayer() ) return; // HACKHACK -- In multiplayer, players touch this based on packet receipt. // So the players who send packets later aren't always hurt. Keep track of // how much time has passed and whether or not you've touched that player if( g_pGameRules->IsMultiplayer() ) { if( pev->dmgtime > gpGlobals->time ) { if( gpGlobals->time != pev->pain_finished ) { // too early to hurt again, and not same frame with a different entity if( pOther->IsPlayer() ) { int playerMask = 1 << ( pOther->entindex() - 1 ); // If I've already touched this player (this time), then bail out if( pev->impulse & playerMask ) return; // Mark this player as touched // BUGBUG - There can be only 32 players! pev->impulse |= playerMask; } else { return; } } } else { // New clock, "un-touch" all players pev->impulse = 0; if( pOther->IsPlayer() ) { int playerMask = 1 << ( pOther->entindex() - 1 ); // Mark this player as touched // BUGBUG - There can be only 32 players! pev->impulse |= playerMask; } } } else // Original code -- single player { if( pev->dmgtime > gpGlobals->time && gpGlobals->time != pev->pain_finished ) { // too early to hurt again, and not same frame with a different entity return; } } // If this is time_based damage (poison, radiation), override the pev->dmg with a // default for the given damage type. Monsters only take time-based damage // while touching the trigger. Player continues taking damage for a while after // leaving the trigger pOther->TakeDamage( pev, pev, gSkillData.gwormDmgHit, m_bitsDamageInflict ); // Store pain time so we can get all of the other entities on this frame pev->pain_finished = gpGlobals->time; // Apply damage every half second pev->dmgtime = gpGlobals->time + 0.5;// half second delay until this trigger can hurt toucher again EMIT_SOUND_DYN(ENT(pev), CHAN_BODY, RANDOM_SOUND_ARRAY(pAttackSounds), VOL_NORM, 0.1, 0, 100 + RANDOM_FLOAT(-5,5)); m_flLastDamageTime = gpGlobals->time; if( pev->target ) { // trigger has a target it wants to fire. if( pev->spawnflags & SF_TRIGGER_HURT_CLIENTONLYFIRE ) { // if the toucher isn't a client, don't fire the target! if( !pOther->IsPlayer() ) { return; } } SUB_UseTargets( pOther, USE_TOGGLE, 0 ); if( pev->spawnflags & SF_TRIGGER_HURT_TARGETONCE ) pev->target = 0; } } LINK_ENTITY_TO_CLASS(trigger_geneworm_hit, CTriggerGenewormHit) //========================================================= // CPlayerFreeze //========================================================= class CTriggerPlayerFreeze : public CBaseDelay { public: void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); int ObjectCaps( void ) { return CBaseDelay::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } }; LINK_ENTITY_TO_CLASS( trigger_playerfreeze, CTriggerPlayerFreeze ) void CTriggerPlayerFreeze::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { if( !pActivator || !pActivator->IsPlayer() ) pActivator = CBaseEntity::Instance( g_engfuncs.pfnPEntityOfEntIndex( 1 ) ); if( pActivator && (pActivator->pev->flags & FL_FROZEN) ) ( (CBasePlayer *)( (CBaseEntity *)pActivator ) )->EnableControl( TRUE ); else ( (CBasePlayer *)( (CBaseEntity *)pActivator ) )->EnableControl( FALSE ); }