//=========== (C) Copyright 1996-2002, Valve, L.L.C. All rights reserved. =========== // // The copyright to the contents herein is the property of Valve, L.L.C. // The contents may be used and/or copied only with the written permission of // Valve, L.L.C., or in accordance with the terms and conditions stipulated in // the agreement/contract under which the contents have been supplied. // // Purpose: Quake world items // // $Workfile: $ // $Date: $ // //----------------------------------------------------------------------------- // $Log: $ // // $NoKeywords: $ //============================================================================= #include "extdll.h" #include "util.h" #include "cbase.h" #include "player.h" #include "shake.h" #include "../engine/studio.h" #include "weapons.h" #include "quake_gun.h" #include "hltv.h" extern unsigned short g_usPowerUp; class CQuakeItem : public CBaseEntity { public: void Spawn( void ); // Respawning void EXPORT Materialize( void ); void Respawn( float flTime ); virtual void SetObjectCollisionBox ( void ); // Touch void EXPORT ItemTouch( CBaseEntity *pOther ); virtual BOOL MyTouch( CBasePlayer *pOther ) { return FALSE; }; float m_flRespawnTime; }; //----------------------------------------------------------------------------- // Purpose: Spawn and drop to the floor //----------------------------------------------------------------------------- void CQuakeItem :: SetObjectCollisionBox( void ) { pev->absmin = pev->origin + Vector(-32, -32, 0); pev->absmax = pev->origin + Vector(32, 32, 56); } void CQuakeItem::Spawn() { pev->movetype = MOVETYPE_TOSS; pev->solid = SOLID_TRIGGER; SetTouch( &CQuakeItem::ItemTouch ); if (DROP_TO_FLOOR(ENT(pev)) == 0) { ALERT(at_error, "Item %s fell out of level at %f,%f,%f", STRING( pev->classname ), pev->origin.x, pev->origin.y, pev->origin.z); UTIL_Remove( this ); return; } //UTIL_SetOrigin( pev, pev->origin + Vector(0,0,16) ); if (!m_flRespawnTime) m_flRespawnTime = 20; } //----------------------------------------------------------------------------- // Purpose: Bring the item back //----------------------------------------------------------------------------- void CQuakeItem::Materialize() { // Become visible and touchable pev->effects &= ~EF_NODRAW; SetTouch( &CQuakeItem::ItemTouch ); // Play respawn sound EMIT_SOUND( ENT(pev), CHAN_WEAPON, "items/itembk2.wav", 1, ATTN_NORM ); } //----------------------------------------------------------------------------- // Purpose: Setup the item's respawn in the time set //----------------------------------------------------------------------------- void CQuakeItem::Respawn( float flTime ) { pev->effects |= EF_NODRAW; SetTouch( NULL ); // Come back in time SetThink( &CQuakeItem::Materialize ); pev->nextthink = gpGlobals->time + flTime; } //----------------------------------------------------------------------------- // Purpose: Touch function that calls the virtual touch function //----------------------------------------------------------------------------- void CQuakeItem::ItemTouch( CBaseEntity *pOther ) { // if it's not a player, ignore if ( !pOther->IsPlayer() ) return; //Dead? if (pOther->pev->health <= 0) return; CBasePlayer *pPlayer = (CBasePlayer *)pOther; // Call the virtual touch function if ( MyTouch( pPlayer ) ) { SUB_UseTargets( pOther, USE_TOGGLE, 0 ); // Respawn if it's not DM==2 if (gpGlobals->deathmatch != 2) { Respawn( m_flRespawnTime ); } else { UTIL_Remove( this ); } } } //====================================================================================== // HEALTH ITEMS //====================================================================================== #define H_ROTTEN 1 #define H_MEGA 2 class CItemHealth : public CQuakeItem { public: void Spawn( void ); void Precache( void ); BOOL MyTouch( CBasePlayer *pPlayer ); void EXPORT MegahealthRot( void ); EHANDLE m_hRotTarget; int m_iHealAmount; int m_iHealType; }; LINK_ENTITY_TO_CLASS(item_health, CItemHealth); //-------------------------------------------- // Spawn void CItemHealth::Spawn( void ) { Precache(); // Setup healing method if (pev->spawnflags & H_ROTTEN) { SET_MODEL(ENT(pev), "models/w_medkits.mdl"); pev->noise = MAKE_STRING( "items/r_item1.wav" ); m_iHealAmount = 15; m_iHealType = H_ROTTEN; } else if (pev->spawnflags & H_MEGA) { SET_MODEL(ENT(pev), "models/w_medkitl.mdl"); pev->noise = MAKE_STRING( "items/r_item2.wav" ); m_iHealAmount = 100; m_iHealType = H_MEGA; } else { SET_MODEL(ENT(pev), "models/w_medkit.mdl"); pev->noise = MAKE_STRING( "items/health1.wav" ); m_iHealAmount = 25; m_iHealType = H_ROTTEN; } CQuakeItem::Spawn(); } //-------------------------------------------- // Precache void CItemHealth::Precache() { PRECACHE_MODEL("models/w_medkitl.mdl"); PRECACHE_MODEL("models/w_medkits.mdl"); PRECACHE_MODEL("models/w_medkit.mdl"); PRECACHE_SOUND("items/r_item1.wav"); PRECACHE_SOUND("items/r_item2.wav"); PRECACHE_SOUND("items/health1.wav"); } //-------------------------------------------- // Health Touch BOOL CItemHealth::MyTouch( CBasePlayer *pPlayer ) { // Don't heal in DM==4 if they're invincible if (gpGlobals->deathmatch == 4 && pPlayer->m_flInvincibleFinished > 0) return FALSE; if (pPlayer->pev->health <= 0) return FALSE; if (m_iHealType == H_MEGA) { if (pPlayer->pev->health >= 250) return FALSE; if ( !pPlayer->TakeHealth( m_iHealAmount, DMG_GENERIC | DMG_IGNORE_MAXHEALTH) ) return FALSE; } else { // Heal the Player if ( !pPlayer->TakeHealth( m_iHealAmount, DMG_GENERIC ) ) return FALSE; } ClientPrint( pPlayer->pev, HUD_PRINTNOTIFY, "#Get_Health", UTIL_dtos1(m_iHealAmount) ); EMIT_SOUND( ENT(pev), CHAN_ITEM, STRING(pev->noise), 1, ATTN_NORM ); // Setup for respawn if (m_iHealType == H_MEGA) { // Go invisible and fire targets pev->effects |= EF_NODRAW; SetTouch( NULL ); SUB_UseTargets( pPlayer, USE_TOGGLE, 0 ); pPlayer->m_iQuakeItems |= IT_SUPERHEALTH; if (gpGlobals->deathmatch != 4) { SetThink( &CItemHealth::MegahealthRot ); pev->nextthink = gpGlobals->time + 5; } m_hRotTarget = pPlayer; // Return FALSE, because we want to handle our respawn ourselves return FALSE; } // Respawn as normal return TRUE; } //-------------------------------------------- // Megahealth Rot function. Reduce player's health until it's below 100. Then respawn. void CItemHealth::MegahealthRot( void ) { if (m_hRotTarget) { CBasePlayer *pPlayer = ((CBasePlayer *)((CBaseEntity *)m_hRotTarget)); if( pPlayer->pev->health > pPlayer->pev->max_health && pPlayer->m_iRuneStatus != ITEM_RUNE4_FLAG ) { pPlayer->pev->health--; pev->nextthink = gpGlobals->time + 1; return; } pPlayer->m_iQuakeItems &= ~IT_SUPERHEALTH; } // Respawn if it's not DM==2 if (gpGlobals->deathmatch != 2) { SetThink( &CQuakeItem::Materialize ); pev->nextthink = gpGlobals->time + 20; } else { UTIL_Remove( this ); } } //====================================================================================== // ARMOR ITEMS //====================================================================================== class CItemArmor : public CQuakeItem { public: BOOL MyTouch( CBasePlayer *pPlayer ); float m_flArmorValue; float m_flArmorType; int m_iArmorBit; }; // Armor Touch BOOL CItemArmor::MyTouch( CBasePlayer *pPlayer ) { if (pPlayer->pev->health <= 0) return FALSE; // Don't pickup in DM==4 if they're invincible if (gpGlobals->deathmatch == 4 && pPlayer->m_flInvincibleFinished > 0) return FALSE; // Don't pickup if this armor isn't as good as the stuff we've got if ( (pPlayer->pev->armortype * pPlayer->pev->armorvalue) >= (m_flArmorType * m_flArmorValue) ) return FALSE; pPlayer->pev->armortype = m_flArmorType; pPlayer->pev->armorvalue = m_flArmorValue; pPlayer->m_iQuakeItems &= ~(IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3); pPlayer->m_iQuakeItems |= m_iArmorBit; EMIT_SOUND( ENT( pPlayer->pev ), CHAN_ITEM, "items/armor1.wav", 1, ATTN_NORM ); return TRUE; } //=============== // Green Armor class CItemArmorGreen : public CItemArmor { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(item_armor1, CItemArmorGreen); // Spawn void CItemArmorGreen::Spawn( void ) { Precache(); SET_MODEL(ENT(pev), "models/armour_g.mdl"); CItemArmor::Spawn(); m_flArmorValue = 100; m_flArmorType = 0.3; m_iArmorBit = IT_ARMOR1; } // Precache void CItemArmorGreen::Precache( void ) { PRECACHE_MODEL( "models/armour_g.mdl" ); PRECACHE_SOUND( "items/armor1.wav" ); } //=============== // Yellow Armor class CItemArmorYellow : public CItemArmor { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(item_armor2, CItemArmorYellow); // Spawn void CItemArmorYellow::Spawn( void ) { Precache(); SET_MODEL(ENT(pev), "models/armour_y.mdl"); CItemArmor::Spawn(); m_flArmorValue = 150; m_flArmorType = 0.6; m_iArmorBit = IT_ARMOR2; } // Precache void CItemArmorYellow::Precache( void ) { PRECACHE_MODEL( "models/armour_y.mdl" ); PRECACHE_SOUND( "items/armor1.wav" ); } //=============== // Red Armor class CItemArmorRed : public CItemArmor { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(item_armor3, CItemArmorRed); LINK_ENTITY_TO_CLASS(item_armorInv, CItemArmorRed); // Spawn void CItemArmorRed::Spawn( void ) { Precache(); SET_MODEL(ENT(pev), "models/armour_r.mdl"); CItemArmor::Spawn(); m_flArmorValue = 200; m_flArmorType = 0.8; m_iArmorBit = IT_ARMOR3; } // Precache void CItemArmorRed::Precache( void ) { PRECACHE_MODEL( "models/armour_r.mdl" ); PRECACHE_SOUND( "items/armor1.wav" ); } //====================================================================================== // WEAPON ITEMS //====================================================================================== void CBasePlayer::CheckAmmo() { if (m_iAmmoShells > 100) m_iAmmoShells = 100; if (m_iAmmoNails > 200) m_iAmmoNails = 200; if (m_iAmmoRockets > 100) m_iAmmoRockets = 100; if (m_iAmmoCells > 100) m_iAmmoCells = 100; } int RankForWeapon(int iWeapon) { switch (iWeapon) { case IT_LIGHTNING: return 1; break; case IT_ROCKET_LAUNCHER: return 2; break; case IT_SUPER_NAILGUN: return 3; break; case IT_GRENADE_LAUNCHER: return 4; break; case IT_SUPER_SHOTGUN: return 5; break; case IT_NAILGUN: return 6; break; default: break; } return 7; } int WeaponCode(int iWeapon) { switch (iWeapon) { case IT_SUPER_SHOTGUN: return 3; break; case IT_NAILGUN: return 4; break; case IT_SUPER_NAILGUN: return 5; break; case IT_GRENADE_LAUNCHER: return 6; break; case IT_ROCKET_LAUNCHER: return 7; break; case IT_LIGHTNING: return 8; break; default: break; } return 1; } int GetWeaponValue ( int iWeapon ) { int iWepValue; switch ( iWeapon ) { case IT_AXE: iWepValue = 1; break; case IT_SHOTGUN: iWepValue = 2; break; case IT_SUPER_SHOTGUN: iWepValue = 3; break; case IT_NAILGUN: iWepValue = 4; break; case IT_SUPER_NAILGUN: iWepValue = 5; break; case IT_GRENADE_LAUNCHER: iWepValue = 6; break; case IT_ROCKET_LAUNCHER: iWepValue = 7; break; case IT_LIGHTNING: iWepValue = 8; break; } return iWepValue; } // Change weapon only if the new one's better void CBasePlayer::Deathmatch_Weapon(int iOldWeapon, int iNewWeapon) { int iPickedWep = GetWeaponValue( iNewWeapon ); int iOldWep = GetWeaponValue( m_iQuakeWeapon ); switch ( m_iAutoWepSwitch ) { case 0: return; break; case 1: W_ChangeWeapon( iPickedWep ); break; case 2: if ( ( iPickedWep == 8 && !FBitSet(pev->flags, FL_INWATER) ) || iPickedWep > iOldWep ) W_ChangeWeapon( iPickedWep ); break; } } //----------------------------------------------- // Base Quake Weapon object class CItemWeapon : public CQuakeItem { public: BOOL MyTouch( CBasePlayer *pPlayer ); int m_iWeapon; }; BOOL CItemWeapon::MyTouch( CBasePlayer *pPlayer ) { BOOL bLeaveWeapon = FALSE; if (gpGlobals->deathmatch == 2 || gpGlobals->deathmatch == 3 || gpGlobals->deathmatch == 5 || CVAR_GET_FLOAT("mp_weaponstay") > 0 ) bLeaveWeapon = TRUE; // Leave the weapon if the player's already got it if ( bLeaveWeapon && (pPlayer->m_iQuakeItems & m_iWeapon) ) return FALSE; if ( pPlayer->pev->health <= 0) return FALSE; // Give the player some ammo switch (m_iWeapon) { case IT_NAILGUN: pPlayer->m_iAmmoNails += 30; break; case IT_SUPER_NAILGUN: pPlayer->m_iAmmoNails += 30; break; case IT_SUPER_SHOTGUN: pPlayer->m_iAmmoShells += 5; break; case IT_ROCKET_LAUNCHER: pPlayer->m_iAmmoRockets += 5; break; case IT_GRENADE_LAUNCHER: pPlayer->m_iAmmoRockets += 5; break; case IT_LIGHTNING: pPlayer->m_iAmmoCells += 15; break; default: break; } pPlayer->CheckAmmo(); EMIT_SOUND( ENT(pev), CHAN_ITEM, "weapons/pkup.wav", 1, ATTN_NORM ); // Change to new weapon? int iOldItems = pPlayer->m_iQuakeWeapon; pPlayer->m_iQuakeItems |= m_iWeapon; pPlayer->Deathmatch_Weapon(iOldItems, m_iWeapon); // Update HUD pPlayer->W_SetCurrentAmmo(); pPlayer->m_iClientQuakeWeapon = -1; pPlayer->m_fWeapon = FALSE; pPlayer->m_fKnownItem = FALSE; pPlayer->UpdateClientData(); if (bLeaveWeapon) return FALSE; // Respawn m_flRespawnTime = 30; return TRUE; } //=============== // Super Shotgun class CItemWeaponSuperShotgun : public CItemWeapon { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(weapon_supershotgun, CItemWeaponSuperShotgun); // Spawn void CItemWeaponSuperShotgun::Spawn( void ) { if ( gpGlobals->deathmatch > 3) { UTIL_Remove(this); return; } Precache(); SET_MODEL(ENT(pev), "models/g_shot2.mdl"); CItemWeapon::Spawn(); m_iWeapon = IT_SUPER_SHOTGUN; pev->netname = MAKE_STRING("Double-barrelled Shotgun"); } // Precache void CItemWeaponSuperShotgun::Precache( void ) { PRECACHE_MODEL( "models/g_shot2.mdl" ); } //=============== // Nailgun class CItemWeaponNailgun : public CItemWeapon { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(weapon_nailgun, CItemWeaponNailgun); // Spawn void CItemWeaponNailgun::Spawn( void ) { if ( gpGlobals->deathmatch > 3) { UTIL_Remove(this); return; } Precache(); SET_MODEL(ENT(pev), "models/g_nail.mdl"); CItemWeapon::Spawn(); m_iWeapon = IT_NAILGUN; pev->netname = MAKE_STRING("Nailgun"); } // Precache void CItemWeaponNailgun::Precache( void ) { PRECACHE_MODEL( "models/g_nail.mdl" ); } //=============== // Super Nailgun class CItemWeaponSuperNailgun : public CItemWeapon { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(weapon_supernailgun, CItemWeaponSuperNailgun); // Spawn void CItemWeaponSuperNailgun::Spawn( void ) { if ( gpGlobals->deathmatch > 3) { UTIL_Remove(this); return; } Precache(); SET_MODEL(ENT(pev), "models/g_nail2.mdl"); CItemWeapon::Spawn(); m_iWeapon = IT_SUPER_NAILGUN; pev->netname = MAKE_STRING("Super Nailgun"); } // Precache void CItemWeaponSuperNailgun::Precache( void ) { PRECACHE_MODEL( "models/g_nail2.mdl" ); } //=============== // Grenade Launcher class CItemWeaponGrenadeLauncher : public CItemWeapon { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(weapon_grenadelauncher, CItemWeaponGrenadeLauncher); // Spawn void CItemWeaponGrenadeLauncher::Spawn( void ) { if ( gpGlobals->deathmatch > 3) { UTIL_Remove(this); return; } Precache(); SET_MODEL(ENT(pev), "models/g_rock.mdl"); CItemWeapon::Spawn(); m_iWeapon = IT_GRENADE_LAUNCHER; pev->netname = MAKE_STRING("Grenade Launcher"); } // Precache void CItemWeaponGrenadeLauncher::Precache( void ) { PRECACHE_MODEL( "models/g_rock.mdl" ); } //=============== // Rocket Launcher class CItemWeaponRocketLauncher : public CItemWeapon { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(weapon_rocketlauncher, CItemWeaponRocketLauncher); // Spawn void CItemWeaponRocketLauncher::Spawn( void ) { if ( gpGlobals->deathmatch > 3) { UTIL_Remove(this); return; } Precache(); SET_MODEL(ENT(pev), "models/g_rock2.mdl"); CItemWeapon::Spawn(); m_iWeapon = IT_ROCKET_LAUNCHER; pev->netname = MAKE_STRING("Rocket Launcher"); } // Precache void CItemWeaponRocketLauncher::Precache( void ) { PRECACHE_MODEL( "models/g_rock2.mdl" ); } //=============== // Lightning Gun class CItemWeaponLightning : public CItemWeapon { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(weapon_lightning, CItemWeaponLightning); // Spawn void CItemWeaponLightning::Spawn( void ) { if ( gpGlobals->deathmatch > 3) { UTIL_Remove(this); return; } Precache(); SET_MODEL(ENT(pev), "models/g_light.mdl"); CItemWeapon::Spawn(); m_iWeapon = IT_LIGHTNING; pev->netname = MAKE_STRING("Thunderbolt"); } // Precache void CItemWeaponLightning::Precache( void ) { PRECACHE_MODEL( "models/g_light.mdl" ); } //====================================================================================== // AMMO ITEMS //====================================================================================== #define BIG_AMMOBOX 1 class CItemAmmo : public CQuakeItem { public: void Spawn( void ); void Precache( void ); BOOL MyTouch( CBasePlayer *pPlayer ); int m_isSmallBox; int m_isLargeBox; int ammo_shells; int ammo_nails; int ammo_rockets; int ammo_cells; }; // Spawn void CItemAmmo::Spawn( void ) { Precache(); // Set the box size if (pev->spawnflags & BIG_AMMOBOX) { SET_MODEL( ENT(pev), STRING(m_isLargeBox) ); ammo_shells *= 2; ammo_nails *= 2; ammo_rockets *= 2; ammo_cells *= 2; } else { SET_MODEL( ENT(pev), STRING(m_isSmallBox) ); } // Halve respawn times in DM==3 and DM==5 if (gpGlobals->deathmatch == 3 || gpGlobals->deathmatch == 5) m_flRespawnTime = 15; else m_flRespawnTime = 30; CQuakeItem::Spawn(); } // Precache void CItemAmmo::Precache( void ) { if (pev->spawnflags & BIG_AMMOBOX) PRECACHE_MODEL( STRING(m_isLargeBox) ); else PRECACHE_MODEL( STRING(m_isSmallBox) ); } BOOL CItemAmmo::MyTouch( CBasePlayer *pPlayer ) { if (pPlayer->pev->health <= 0) return FALSE; // Find the player's best weapon int iBestWeapon = pPlayer->W_BestWeapon(); // Return if the player can't carry if (ammo_shells && pPlayer->m_iAmmoShells >= 100) return FALSE; if (ammo_nails && pPlayer->m_iAmmoNails >= 200) return FALSE; if (ammo_rockets && pPlayer->m_iAmmoRockets >= 100) return FALSE; if (ammo_cells && pPlayer->m_iAmmoCells >= 100) return FALSE; pPlayer->m_iAmmoShells += ammo_shells; pPlayer->m_iAmmoNails += ammo_nails; pPlayer->m_iAmmoRockets += ammo_rockets; pPlayer->m_iAmmoCells += ammo_cells; pPlayer->CheckAmmo(); EMIT_SOUND( ENT(pev), CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM ); // Change to a better weapon if possible if ( pPlayer->m_iQuakeWeapon == iBestWeapon ) { pPlayer->m_iQuakeWeapon = pPlayer->W_BestWeapon(); } pPlayer->W_SetCurrentAmmo(); return TRUE; } //=============== // Shells class CItemAmmoShells : public CItemAmmo { public: void Spawn( void ); }; LINK_ENTITY_TO_CLASS(item_shells, CItemAmmoShells); // Spawn void CItemAmmoShells::Spawn( void ) { if ( gpGlobals->deathmatch == 4) { UTIL_Remove(this); return; } m_isSmallBox = MAKE_STRING("models/w_shotbox.mdl"); m_isLargeBox = MAKE_STRING("models/w_shotbox_big.mdl"); pev->netname = MAKE_STRING("shells"); ammo_shells = 20; CItemAmmo::Spawn(); } //=============== // Spikes class CItemAmmoSpikes : public CItemAmmo { public: void Spawn(); }; LINK_ENTITY_TO_CLASS(item_spikes, CItemAmmoSpikes); // Spawn void CItemAmmoSpikes::Spawn( void ) { if ( gpGlobals->deathmatch == 4) { UTIL_Remove(this); return; } m_isSmallBox = MAKE_STRING("models/b_nail0.mdl"); m_isLargeBox = MAKE_STRING("models/b_nail1.mdl"); pev->netname = MAKE_STRING("nails"); ammo_nails = 25; CItemAmmo::Spawn(); } //=============== // Rockets class CItemAmmoRockets : public CItemAmmo { public: void Spawn( void ); }; LINK_ENTITY_TO_CLASS(item_rockets, CItemAmmoRockets); // Spawn void CItemAmmoRockets::Spawn( void ) { if ( gpGlobals->deathmatch == 4) { UTIL_Remove(this); return; } m_isSmallBox = MAKE_STRING("models/w_rpgammo.mdl"); m_isLargeBox = MAKE_STRING("models/w_rpgammo_big.mdl"); pev->netname = MAKE_STRING("rockets"); ammo_rockets = 5; CItemAmmo::Spawn(); } //=============== // Cells class CItemAmmoCells : public CItemAmmo { public: void Spawn( void ); }; LINK_ENTITY_TO_CLASS(item_cells, CItemAmmoCells); // Spawn void CItemAmmoCells::Spawn( void ) { if ( gpGlobals->deathmatch == 4) { UTIL_Remove(this); return; } m_isSmallBox = MAKE_STRING("models/w_battery.mdl"); m_isLargeBox = MAKE_STRING("models/w_battery.mdl"); pev->netname = MAKE_STRING("cells"); ammo_cells = 6; CItemAmmo::Spawn(); } //=============== // Weapon ammo // Another method of placing ammo. Quake still uses it in some maps. #define AW_SHOTGUN 1 #define AW_ROCKET 2 #define AW_SPIKES 4 #define AW_BIG 8 class CItemAmmoWeapon : public CItemAmmo { public: void Spawn( void ); }; LINK_ENTITY_TO_CLASS(item_weapon, CItemAmmoWeapon); // Spawn void CItemAmmoWeapon::Spawn( void ) { if ( gpGlobals->deathmatch == 4) { UTIL_Remove(this); return; } // Shells if (pev->spawnflags & AW_SHOTGUN) { m_isSmallBox = MAKE_STRING("models/w_shotbox.mdl"); m_isLargeBox = MAKE_STRING("models/w_shotbox_big.mdl"); pev->netname = MAKE_STRING("shells"); ammo_shells = 20; } // Nails if (pev->spawnflags & AW_SPIKES) { m_isSmallBox = MAKE_STRING("models/b_nail0.mdl"); m_isLargeBox = MAKE_STRING("models/b_nail1.mdl"); pev->netname = MAKE_STRING("nails"); ammo_nails = 25; } // Rockets if (pev->spawnflags & AW_ROCKET) { m_isSmallBox = MAKE_STRING("models/w_rpgammo.mdl"); m_isLargeBox = MAKE_STRING("models/w_rpgammo_big.mdl"); pev->netname = MAKE_STRING("rockets"); ammo_rockets = 5; } // Big? if (pev->spawnflags & AW_BIG) pev->spawnflags = BIG_AMMOBOX; else pev->spawnflags = 0; CItemAmmo::Spawn(); } //=============================================================================== // POWERUPS //=============================================================================== class CItemPowerup : public CQuakeItem { public: BOOL MyTouch( CBasePlayer *pPlayer ); int m_iPowerupBit; float invincible_finished; float radsuit_finished; float invisible_finished; float super_damage_finished; }; // Powerup Touch BOOL CItemPowerup::MyTouch( CBasePlayer *pPlayer ) { if (pPlayer->pev->health <= 0) return FALSE; EMIT_SOUND( ENT(pev), CHAN_ITEM, STRING(pev->noise), 1, ATTN_NORM ); pPlayer->m_iQuakeItems |= m_iPowerupBit; int iPowerUp = 0; // Invincibility if (invincible_finished) { // Make them glow red if ( pPlayer->m_iQuakeItems & IT_QUAD ) { pPlayer->pev->renderfx = kRenderFxGlowShell; pPlayer->pev->rendercolor = Vector( 255, 125, 255 ); // RGB pPlayer->pev->renderamt = 100; // Shell size iPowerUp = 3; } else { pPlayer->pev->renderfx = kRenderFxGlowShell; pPlayer->pev->rendercolor = Vector( 255, 128, 0 ); // RGB pPlayer->pev->renderamt = 100; // Shell size iPowerUp = 2; } if ( pPlayer->m_iQuakeItems & IT_INVISIBILITY ) { pPlayer->pev->rendermode = kRenderTransColor; pPlayer->pev->renderamt = 1; } pPlayer->m_flInvincibleFinished = gpGlobals->time + invincible_finished; } // Quad Damage if (super_damage_finished) { // Make them glow blue if ( pPlayer->m_iQuakeItems & IT_INVULNERABILITY ) { pPlayer->pev->renderfx = kRenderFxGlowShell; pPlayer->pev->rendercolor = Vector( 255, 125, 255 ); // RGB pPlayer->pev->renderamt = 100; // Shell size iPowerUp = 3; } else { pPlayer->pev->renderfx = kRenderFxGlowShell; pPlayer->pev->rendercolor = Vector( 128, 128, 255 ); // RGB pPlayer->pev->renderamt = 100; // Shell size iPowerUp = 1; } if ( pPlayer->m_iQuakeItems & IT_INVISIBILITY ) { pPlayer->pev->rendermode = kRenderTransColor; pPlayer->pev->renderamt = 1; } pPlayer->m_flSuperDamageFinished = gpGlobals->time + super_damage_finished; // Remove armor and cells if DM==4 if (gpGlobals->deathmatch == 4) { pPlayer->pev->armortype = 0; pPlayer->pev->armorvalue = 0; pPlayer->m_iAmmoCells = 0; } } // Radiation suit if (radsuit_finished) pPlayer->m_flRadsuitFinished = gpGlobals->time + radsuit_finished; // Invisibility if (invisible_finished) { pPlayer->m_flInvisibleFinished = gpGlobals->time + invisible_finished; pPlayer->pev->renderfx = kRenderFxGlowShell; pPlayer->pev->rendercolor = Vector( 128, 128, 128 ); // RGB pPlayer->pev->renderamt = 5; // Shell size } // tell director about it MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR ); WRITE_BYTE ( 9 ); // command length in bytes WRITE_BYTE ( DRC_CMD_EVENT ); // powerup pickup WRITE_SHORT( ENTINDEX(pPlayer->edict()) ); // player is primary target WRITE_SHORT( ENTINDEX(this->edict()) ); // powerup as second target WRITE_LONG( 9 ); // highst prio in game MESSAGE_END(); pPlayer->W_SetCurrentAmmo(); PLAYBACK_EVENT_FULL( FEV_GLOBAL | FEV_RELIABLE, pPlayer->edict(), g_usPowerUp, 0, (float *)&g_vecZero, (float *)&g_vecZero, (float)iPowerUp, 0.0, pPlayer->entindex(), pPlayer->pev->team, 0, 0 ); return TRUE; } //=============== // Pentagram class CItemPowerupInvincible : public CItemPowerup { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(item_artifact_invulnerability, CItemPowerupInvincible); // Spawn void CItemPowerupInvincible::Spawn( void ) { Precache(); CQuakeItem::Spawn(); m_flRespawnTime = 300; invincible_finished = 30; SET_MODEL(ENT(pev), "models/pow_invuln.mdl"); pev->netname = MAKE_STRING("Pentagram of Protection"); pev->noise = MAKE_STRING("items/protect.wav"); m_iPowerupBit = IT_INVULNERABILITY; // Make it glow red pev->renderfx = kRenderFxGlowShell; pev->rendercolor = Vector( 255, 128, 0 ); // RGB pev->renderamt = 100; // Shellsize } // Precache void CItemPowerupInvincible::Precache( void ) { PRECACHE_MODEL("models/pow_invuln.mdl"); PRECACHE_SOUND("items/protect.wav"); PRECACHE_SOUND("items/protect2.wav"); PRECACHE_SOUND("items/protect3.wav"); } //=============== // Radiation Suit class CItemPowerupRadsuit : public CItemPowerup { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(item_artifact_envirosuit, CItemPowerupRadsuit); // Spawn void CItemPowerupRadsuit::Spawn( void ) { Precache(); CQuakeItem::Spawn(); m_flRespawnTime = 60; radsuit_finished = 30; SET_MODEL(ENT(pev), "models/suit.mdl"); pev->netname = MAKE_STRING("Biosuit"); pev->noise = MAKE_STRING("items/suit.wav"); m_iPowerupBit = IT_SUIT; } // Precache void CItemPowerupRadsuit::Precache( void ) { PRECACHE_MODEL("models/suit.mdl"); PRECACHE_SOUND("items/suit.wav"); PRECACHE_SOUND("items/suit2.wav"); } //=============== // Ring of Invisibility class CItemPowerupInvisibility : public CItemPowerup { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(item_artifact_invisibility, CItemPowerupInvisibility); // Spawn void CItemPowerupInvisibility::Spawn( void ) { Precache(); CQuakeItem::Spawn(); m_flRespawnTime = 300; invisible_finished = 30; SET_MODEL(ENT(pev), "models/pow_invis.mdl"); pev->netname = MAKE_STRING("Ring of Shadows"); pev->noise = MAKE_STRING("items/inv1.wav"); m_iPowerupBit = IT_INVISIBILITY; pev->renderfx = kRenderFxGlowShell; pev->rendercolor = Vector( 128, 128, 128 ); // RGB pev->renderamt = 25; // Shell size pev->rendermode = kRenderTransColor; pev->renderamt = 30; } // Precache void CItemPowerupInvisibility::Precache( void ) { PRECACHE_MODEL("models/pow_invis.mdl"); PRECACHE_SOUND("items/inv1.wav"); PRECACHE_SOUND("items/inv2.wav"); PRECACHE_SOUND("items/inv3.wav"); } //=============== // Quad Damage class CItemPowerupQuad : public CItemPowerup { public: void Spawn( void ); void Precache( void ); }; LINK_ENTITY_TO_CLASS(item_artifact_super_damage, CItemPowerupQuad); // Spawn void CItemPowerupQuad::Spawn( void ) { Precache(); CQuakeItem::Spawn(); m_flRespawnTime = 60; super_damage_finished = 30; SET_MODEL(ENT(pev), "models/pow_quad.mdl"); pev->netname = MAKE_STRING("Quad Damage"); pev->noise = MAKE_STRING("items/damage.wav"); m_iPowerupBit = IT_QUAD; // Make it glow blue pev->renderfx = kRenderFxGlowShell; pev->rendercolor = Vector( 128, 128, 255 ); // RGB pev->renderamt = 100; // Shell size } // Precache void CItemPowerupQuad::Precache( void ) { PRECACHE_MODEL("models/pow_quad.mdl"); PRECACHE_SOUND("items/damage.wav"); PRECACHE_SOUND("items/damage2.wav"); PRECACHE_SOUND("items/damage3.wav"); } //=============================================================================== // PLAYER BACKPACKS //=============================================================================== class CItemBackpack : public CQuakeItem { public: void Spawn( void ); // void SetBox ( void ); virtual void SetObjectCollisionBox ( void ); BOOL MyTouch( CBasePlayer *pPlayer ); int m_iItems; int ammo_shells; int ammo_nails; int ammo_rockets; int ammo_cells; }; LINK_ENTITY_TO_CLASS(item_backpack, CItemBackpack); void CItemBackpack :: SetObjectCollisionBox( void ) { pev->absmin = pev->origin + Vector(-32, -32, 0); pev->absmax = pev->origin + Vector(32, 32, 56); } // Spawn void CItemBackpack::Spawn() { pev->movetype = MOVETYPE_TOSS; pev->solid = SOLID_TRIGGER; UTIL_SetOrigin( pev, pev->origin ); SET_MODEL(ENT(pev), "models/backpack.mdl"); SetTouch( &CItemBackpack::ItemTouch ); } // Drop a backpack containing this player's ammo/weapons void CBasePlayer::DropBackpack() { // Any ammo to drop? if ( !(m_iAmmoShells + m_iAmmoNails + m_iAmmoRockets + m_iAmmoCells) ) return; // Create the pack CItemBackpack *pPack = (CItemBackpack *)CBaseEntity::Create( "item_backpack", pev->origin - Vector(0, 0, 24), g_vecZero, edict() ); pPack->pev->velocity = Vector( RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), 300 ); pPack->Spawn(); // Put the player's weapon in the pack pPack->m_iItems = m_iQuakeWeapon; switch (pPack->m_iItems) { case IT_AXE: pPack->pev->netname = MAKE_STRING("Crowbar"); break; case IT_SHOTGUN: pPack->pev->netname = MAKE_STRING("Shotgun"); break; case IT_SUPER_SHOTGUN: pPack->pev->netname = MAKE_STRING("Double-barrelled Shotgun"); break; case IT_NAILGUN: pPack->pev->netname = MAKE_STRING("Nailgun"); break; case IT_SUPER_NAILGUN: pPack->pev->netname = MAKE_STRING("Super Nailgun"); break; case IT_GRENADE_LAUNCHER: pPack->pev->netname = MAKE_STRING("Grenade Launcher"); break; case IT_ROCKET_LAUNCHER: pPack->pev->netname = MAKE_STRING("Rocket Launcher"); break; case IT_LIGHTNING: pPack->pev->netname = MAKE_STRING("Thunderbolt"); break; default: pPack->pev->netname = MAKE_STRING("Invalid weapon."); break; } // Put the ammo in pPack->ammo_shells = m_iAmmoShells; pPack->ammo_nails = m_iAmmoNails; pPack->ammo_rockets = m_iAmmoRockets; pPack->ammo_cells = m_iAmmoCells; //Remove them from the player m_iAmmoShells = m_iAmmoNails = m_iAmmoRockets = m_iAmmoCells = 0; // Remove after 2 mins pPack->pev->nextthink = gpGlobals->time + 120; pPack->SetThink( &CBasePlayer::SUB_Remove ); // Remove all weapons m_iQuakeItems = 0; m_iQuakeWeapon = 0; } // Pickup backpack BOOL CItemBackpack::MyTouch( CBasePlayer *pPlayer ) { if (pPlayer->pev->health <= 0) return FALSE; if (gpGlobals->deathmatch == 4 && pPlayer->m_flInvincibleFinished > 0) return FALSE; if (gpGlobals->deathmatch == 4) { pPlayer->pev->health += 10; ClientPrint( pPlayer->pev, HUD_PRINTNOTIFY, "#Additional_Health" ); if ((pPlayer->pev->health > 250) && (pPlayer->pev->health < 300)) EMIT_SOUND( ENT(pPlayer->pev), CHAN_ITEM, "items/protect3.wav", 1, ATTN_NORM ); else EMIT_SOUND( ENT(pPlayer->pev), CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM ); // Become invulnerable if the player's reached 300 health if (pPlayer->pev->health > 299) { if (pPlayer->m_flInvincibleFinished == 0) { // Give player invincibility and quad pPlayer->m_flInvincibleFinished = gpGlobals->time + 30; pPlayer->m_flSuperDamageFinished = gpGlobals->time + 30; pPlayer->m_iQuakeItems |= (IT_INVULNERABILITY | IT_QUAD); pPlayer->m_iAmmoCells = 0; // Make player glow red pPlayer->pev->renderfx = kRenderFxGlowShell; pPlayer->pev->rendercolor = Vector( 255, 128, 0 ); // RGB pPlayer->pev->renderamt = 100; // Shell size EMIT_SOUND( ENT(pPlayer->pev), CHAN_VOICE, "items/sight1.wav", 1, ATTN_NORM ); UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "#Bonus_Power", STRING(pPlayer->pev->netname) ); } } UTIL_Remove( this ); // We've removed ourself, so don't let CQuakeItem handle respawn return FALSE; } BOOL bPrintComma = FALSE; // Get the weapon from the pack if (m_iItems) { if ( !(pPlayer->m_iQuakeItems & m_iItems) ) { bPrintComma = TRUE; switch ( m_iItems ) { case IT_SUPER_SHOTGUN: ClientPrint( pPlayer->pev, HUD_PRINTNOTIFY, "#You_Get_SS", UTIL_dtos1( ammo_shells ), UTIL_dtos2 ( ammo_nails ), UTIL_dtos3 ( ammo_rockets ), UTIL_dtos4 ( ammo_cells ) ); break; case IT_NAILGUN: ClientPrint( pPlayer->pev, HUD_PRINTNOTIFY, "#You_Get_NG", UTIL_dtos1( ammo_shells ), UTIL_dtos2 ( ammo_nails ), UTIL_dtos3 ( ammo_rockets ), UTIL_dtos4 ( ammo_cells ) ); break; case IT_SUPER_NAILGUN: ClientPrint( pPlayer->pev, HUD_PRINTNOTIFY, "#You_Get_SG", UTIL_dtos1( ammo_shells ), UTIL_dtos2 ( ammo_nails ), UTIL_dtos3 ( ammo_rockets ), UTIL_dtos4 ( ammo_cells ) ); break; case IT_GRENADE_LAUNCHER: ClientPrint( pPlayer->pev, HUD_PRINTNOTIFY, "#You_Get_GL", UTIL_dtos1( ammo_shells ), UTIL_dtos2 ( ammo_nails ), UTIL_dtos3 ( ammo_rockets ), UTIL_dtos4 ( ammo_cells ) ); break; case IT_ROCKET_LAUNCHER: ClientPrint( pPlayer->pev, HUD_PRINTNOTIFY, "#You_Get_RL", UTIL_dtos1( ammo_shells ), UTIL_dtos2 ( ammo_nails ), UTIL_dtos3 ( ammo_rockets ), UTIL_dtos4 ( ammo_cells ) ); break; case IT_LIGHTNING: ClientPrint( pPlayer->pev, HUD_PRINTNOTIFY, "#You_Get_LG", UTIL_dtos1( ammo_shells ), UTIL_dtos2 ( ammo_nails ), UTIL_dtos3 ( ammo_rockets ), UTIL_dtos4 ( ammo_cells ) ); break; } } else ClientPrint( pPlayer->pev, HUD_PRINTNOTIFY, "#You_Get_NoGun", UTIL_dtos1( ammo_shells ), UTIL_dtos2 ( ammo_nails ), UTIL_dtos3 ( ammo_rockets ), UTIL_dtos4 ( ammo_cells ) ); } // Get ammo from pack pPlayer->m_iAmmoShells += ammo_shells; pPlayer->m_iAmmoNails += ammo_nails; pPlayer->m_iAmmoRockets += ammo_rockets; pPlayer->m_iAmmoCells += ammo_cells; pPlayer->CheckAmmo(); int iNewWeapon = m_iItems; if (!iNewWeapon) iNewWeapon = pPlayer->m_iQuakeWeapon; int iOldWeapon = pPlayer->m_iQuakeItems; pPlayer->m_iQuakeItems |= m_iItems; // Give them at least 5 rockets in DM==3 and DM==5 if ( (gpGlobals->deathmatch==3 || gpGlobals->deathmatch == 5) & ( (WeaponCode(iNewWeapon)==6) || (WeaponCode(iNewWeapon)==7) ) & (pPlayer->m_iAmmoRockets < 5) ) pPlayer->m_iAmmoRockets = 5; EMIT_SOUND( ENT(pPlayer->pev), CHAN_ITEM, "weapons/lock4.wav", 1, ATTN_NORM ); // Switch to a better weapon if ( WeaponCode(iNewWeapon) <= pPlayer->m_iBackpackSwitch ) { if (pPlayer->pev->flags & FL_INWATER) { if (iNewWeapon != IT_LIGHTNING) { pPlayer->Deathmatch_Weapon(iOldWeapon, iNewWeapon); } } else { pPlayer->Deathmatch_Weapon(iOldWeapon, iNewWeapon); } } pPlayer->W_SetCurrentAmmo(); pPlayer->m_iClientQuakeWeapon = -1; pPlayer->m_fWeapon = FALSE; pPlayer->m_fKnownItem = FALSE; pPlayer->UpdateClientData(); UTIL_Remove( this ); // We've removed ourself, so don't let CQuakeItem handle respawn return FALSE; } #if 0 /* =============================================================================== KEYS =============================================================================== */ void() key_touch = { local entity stemp; local float best; if (other.classname != "player") return; if (other.health <= 0) return; if (other.items & self.items) return; sprint (other, PRINT_LOW, "You got the "); sprint (other, PRINT_LOW, self.netname); sprint (other,PRINT_LOW, "\n"); sound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM); stuffcmd (other, "bf\n"); other.items = other.items | self.items; self.solid = SOLID_NOT; self.model = string_null; activator = other; SUB_UseTargets(); // fire all targets / killtargets }; void() key_setsounds = { if (world.worldtype == 0) { precache_sound ("misc/medkey.wav"); self.noise = "misc/medkey.wav"; } if (world.worldtype == 1) { precache_sound ("misc/runekey.wav"); self.noise = "misc/runekey.wav"; } if (world.worldtype == 2) { precache_sound2 ("misc/basekey.wav"); self.noise = "misc/basekey.wav"; } }; /*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32) SILVER key In order for keys to work you MUST set your maps worldtype to one of the following: 0: medieval 1: metal 2: base */ void() item_key1 = { if (world.worldtype == 0) { precache_model ("progs/w_s_key.mdl"); setmodel (self, "progs/w_s_key.mdl"); self.netname = "silver key"; } else if (world.worldtype == 1) { precache_model ("progs/m_s_key.mdl"); setmodel (self, "progs/m_s_key.mdl"); self.netname = "silver runekey"; } else if (world.worldtype == 2) { precache_model2 ("progs/b_s_key.mdl"); setmodel (self, "progs/b_s_key.mdl"); self.netname = "silver keycard"; } key_setsounds(); self.touch = key_touch; self.items = IT_KEY1; setsize (self, '-16 -16 -24', '16 16 32'); StartItem (); }; /*QUAKED item_key2 (0 .5 .8) (-16 -16 -24) (16 16 32) GOLD key In order for keys to work you MUST set your maps worldtype to one of the following: 0: medieval 1: metal 2: base */ void() item_key2 = { if (world.worldtype == 0) { precache_model ("progs/w_g_key.mdl"); setmodel (self, "progs/w_g_key.mdl"); self.netname = "gold key"; } if (world.worldtype == 1) { precache_model ("progs/m_g_key.mdl"); setmodel (self, "progs/m_g_key.mdl"); self.netname = "gold runekey"; } if (world.worldtype == 2) { precache_model2 ("progs/b_g_key.mdl"); setmodel (self, "progs/b_g_key.mdl"); self.netname = "gold keycard"; } key_setsounds(); self.touch = key_touch; self.items = IT_KEY2; setsize (self, '-16 -16 -24', '16 16 32'); StartItem (); }; /* =============================================================================== END OF LEVEL RUNES =============================================================================== */ void() sigil_touch = { local entity stemp; local float best; if (other.classname != "player") return; if (other.health <= 0) return; centerprint (other, "You got the rune!"); sound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM); stuffcmd (other, "bf\n"); self.solid = SOLID_NOT; self.model = string_null; serverflags = serverflags | (self.spawnflags & 15); self.classname = ""; // so rune doors won't find it activator = other; SUB_UseTargets(); // fire all targets / killtargets }; /*QUAKED item_sigil (0 .5 .8) (-16 -16 -24) (16 16 32) E1 E2 E3 E4 End of level sigil, pick up to end episode and return to jrstart. */ void() item_sigil = { if (!self.spawnflags) objerror ("no spawnflags"); precache_sound ("misc/runekey.wav"); self.noise = "misc/runekey.wav"; if (self.spawnflags & 1) { precache_model ("progs/end1.mdl"); setmodel (self, "progs/end1.mdl"); } if (self.spawnflags & 2) { precache_model2 ("progs/end2.mdl"); setmodel (self, "progs/end2.mdl"); } if (self.spawnflags & 4) { precache_model2 ("progs/end3.mdl"); setmodel (self, "progs/end3.mdl"); } if (self.spawnflags & 8) { precache_model2 ("progs/end4.mdl"); setmodel (self, "progs/end4.mdl"); } self.touch = sigil_touch; setsize (self, '-16 -16 -24', '16 16 32'); StartItem (); }; void() q_touch = { local entity stemp; local float best; local string s; if (other.classname != "player") return; if (other.health <= 0) return; self.mdl = self.model; sound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM); stuffcmd (other, "bf\n"); self.solid = SOLID_NOT; other.items = other.items | IT_QUAD; self.model = string_null; if (deathmatch == 4) { other.armortype = 0; other.armorvalue = 0 * 0.01; other.ammo_cells = 0; } // do the apropriate action other.super_time = 1; other.super_damage_finished = self.cnt; s=ftos(rint(other.super_damage_finished - time)); bprint (PRINT_LOW, other.netname); if (deathmatch == 4) bprint (PRINT_LOW, " recovered an OctaPower with "); else bprint (PRINT_LOW, " recovered a Quad with "); bprint (PRINT_LOW, s); bprint (PRINT_LOW, " seconds remaining!\n"); activator = other; SUB_UseTargets(); // fire all targets / killtargets }; void(float timeleft) DropQuad = { local entity item; item = spawn(); item.origin = self.origin - '0 0 24'; item.velocity_z = 300; item.velocity_x = -100 + (random() * 200); item.velocity_y = -100 + (random() * 200); item.flags = FL_ITEM; item.solid = SOLID_TRIGGER; item.movetype = MOVETYPE_TOSS; item.noise = "items/damage.wav"; setmodel (item, "progs/quaddama.mdl"); setsize (item, '-16 -16 -24', '16 16 32'); item.cnt = time + timeleft; item.touch = q_touch; item.nextthink = time + timeleft; // remove it with the time left on it item.think = SUB_Remove; }; void() r_touch; void() r_touch = { local entity stemp; local float best; local string s; if (other.classname != "player") return; if (other.health <= 0) return; self.mdl = self.model; sound (other, CHAN_VOICE, self.noise, 1, ATTN_NORM); stuffcmd (other, "bf\n"); self.solid = SOLID_NOT; other.items = other.items | IT_INVISIBILITY; self.model = string_null; // do the apropriate action other.invisible_time = 1; other.invisible_finished = self.cnt; s=ftos(rint(other.invisible_finished - time)); bprint (PRINT_LOW, other.netname); bprint (PRINT_LOW, " recovered a Ring with "); bprint (PRINT_LOW, s); bprint (PRINT_LOW, " seconds remaining!\n"); activator = other; SUB_UseTargets(); // fire all targets / killtargets }; void(float timeleft) DropRing = { local entity item; item = spawn(); item.origin = self.origin - '0 0 24'; item.velocity_z = 300; item.velocity_x = -100 + (random() * 200); item.velocity_y = -100 + (random() * 200); item.flags = FL_ITEM; item.solid = SOLID_TRIGGER; item.movetype = MOVETYPE_TOSS; item.noise = "items/inv1.wav"; setmodel (item, "progs/invisibl.mdl"); setsize (item, '-16 -16 -24', '16 16 32'); item.cnt = time + timeleft; item.touch = r_touch; item.nextthink = time + timeleft; // remove after 30 seconds item.think = SUB_Remove; }; #endif