// RAVEN BEGIN // bdube: note that this file is no longer merged with Doom3 updates // // MERGE_DATE 09/30/2004 #include "../idlib/precompiled.h" #pragma hdrstop #include "Game_local.h" /* =============================================================================== idItem =============================================================================== */ const idEventDef EV_DropToFloor( "" ); const idEventDef EV_RespawnItem( "respawn" ); const idEventDef EV_RespawnFx( "" ); const idEventDef EV_GetPlayerPos( "" ); const idEventDef EV_HideObjective( "", "e" ); const idEventDef EV_CamShot( "" ); // RAVEN BEGIN // abahr: const idEventDef EV_SetGravity( "" ); // RAVEN END CLASS_DECLARATION( idEntity, idItem ) EVENT( EV_DropToFloor, idItem::Event_DropToFloor ) EVENT( EV_Touch, idItem::Event_Touch ) EVENT( EV_Activate, idItem::Event_Trigger ) EVENT( EV_RespawnItem, idItem::Event_Respawn ) EVENT( EV_RespawnFx, idItem::Event_RespawnFx ) // RAVEN BEGIN // abahr EVENT( EV_SetGravity, idItem::Event_SetGravity ) // RAVEN END END_CLASS /* ================ idItem::idItem ================ */ idItem::idItem() { spin = false; inView = false; skin = NULL; pickupSkin = NULL; inViewTime = 0; lastCycle = 0; lastRenderViewTime = -1; itemShellHandle = -1; shellMaterial = NULL; orgOrigin.Zero(); canPickUp = true; fl.networkSync = true; trigger = NULL; syncPhysics = false; srvReady = -1; clReady = -1; effectIdle = NULL; itemPVSArea = 0; effectIdle = NULL; simpleItem = false; pickedUp = false; } /* ================ idItem::~idItem ================ */ idItem::~idItem() { // remove the highlight shell if ( itemShellHandle != -1 ) { gameRenderWorld->FreeEntityDef( itemShellHandle ); } if ( trigger ) { delete trigger; } SetPhysics( NULL ); } /* ================ idItem::Save ================ */ void idItem::Save( idSaveGame *savefile ) const { savefile->WriteClipModel( trigger ); savefile->WriteBool( spin ); savefile->WriteSkin( skin ); savefile->WriteSkin( pickupSkin ); savefile->WriteVec3( orgOrigin ); savefile->WriteBool( pulse ); savefile->WriteBool( canPickUp ); savefile->WriteStaticObject( physicsObj ); // savefile->WriteInt(itemShellHandle); // cnicholson: Set at end of Restore, do not save savefile->WriteMaterial( shellMaterial ); savefile->WriteBool( inView ); savefile->WriteInt( inViewTime ); savefile->WriteInt( lastCycle ); savefile->WriteInt( lastRenderViewTime ); } /* ================ idItem::Restore ================ */ void idItem::Restore( idRestoreGame *savefile ) { savefile->ReadClipModel( trigger ); savefile->ReadBool( spin ); savefile->ReadSkin( skin ); savefile->ReadSkin( pickupSkin ); savefile->ReadVec3( orgOrigin ); savefile->ReadBool( pulse ); savefile->ReadBool( canPickUp ); savefile->ReadStaticObject ( physicsObj ); // savefile->ReadInt(itemShellHandle); // cnicholson: Set at end of function, do not restore savefile->ReadMaterial( shellMaterial ); savefile->ReadBool( inView ); savefile->ReadInt( inViewTime ); savefile->ReadInt( lastCycle ); savefile->ReadInt( lastRenderViewTime ); RestorePhysics( &physicsObj ); physicsObj.SetSelf( this ); itemShellHandle = -1; } /* ================ idItem::UpdateRenderEntity ================ */ bool idItem::UpdateRenderEntity( renderEntity_s *renderEntity, const renderView_t *renderView ) const { if( simpleItem ) { return false; } if ( lastRenderViewTime == renderView->time ) { return false; } lastRenderViewTime = renderView->time; // check for glow highlighting if near the center of the view idVec3 dir = renderEntity->origin - renderView->vieworg; dir.Normalize(); float d = dir * renderView->viewaxis[0]; // two second pulse cycle float cycle = ( renderView->time - inViewTime ) / 2000.0f; if ( d > 0.94f ) { if ( !inView ) { inView = true; if ( cycle > lastCycle ) { // restart at the beginning inViewTime = renderView->time; cycle = 0.0f; } } } else { if ( inView ) { inView = false; lastCycle = ceil( cycle ); } } // fade down after the last pulse finishes if ( !inView && cycle > lastCycle ) { renderEntity->shaderParms[4] = 0.0f; } else { // pulse up in 1/4 second cycle -= (int)cycle; if ( cycle < 0.1f ) { renderEntity->shaderParms[4] = cycle * 10.0f; } else if ( cycle < 0.2f ) { renderEntity->shaderParms[4] = 1.0f; } else if ( cycle < 0.3f ) { renderEntity->shaderParms[4] = 1.0f - ( cycle - 0.2f ) * 10.0f; } else { // stay off between pulses renderEntity->shaderParms[4] = 0.0f; } } // update every single time this is in view return true; } /* ================ idItem::UpdateTrigger ================ */ void idItem::UpdateTrigger ( void ) { // update trigger position // RAVEN BEGIN // ddynerman: multiple clip worlds trigger->Link( this, 0, GetPhysics()->GetOrigin(), mat3_identity ); // RAVEN END } /* ================ idItem::ModelCallback ================ */ bool idItem::ModelCallback( renderEntity_t *renderEntity, const renderView_t *renderView ) { const idItem *ent; // this may be triggered by a model trace or other non-view related source if ( !renderView ) { return false; } ent = static_cast(gameLocal.entities[ renderEntity->entityNum ]); if ( !ent ) { gameLocal.Error( "idItem::ModelCallback: callback with NULL game entity" ); } return ent->UpdateRenderEntity( renderEntity, renderView ); } /* ================ idItem::GetPhysicsToVisualTransform ================ */ bool idItem::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) { if( simpleItem ) { if ( gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->GetRenderView() ) { if( gameLocal.GetLocalPlayer()->spectating ) { idPlayer* spec = (idPlayer*)gameLocal.entities[ gameLocal.GetLocalPlayer()->spectator ]; if( spec && spec->GetRenderView() ) { axis = spec->GetRenderView()->viewaxis; } } else { axis = gameLocal.GetLocalPlayer()->GetRenderView()->viewaxis; } } else { // dedicated server for instance axis = mat3_identity; } origin = idVec3( 0.0f, 0.0f, 32.0f ); return true; } if( !spin || (gameLocal.isServer && !gameLocal.isListenServer) ) { return false; } idAngles ang; ang.pitch = ang.roll = 0.0f; ang.yaw = ( gameLocal.time & 4095 ) * 360.0f / -4096.0f; axis = ang.ToMat3() * GetPhysics()->GetAxis(); float scale = 0.005f; float offset = entityNumber * 0.685145f; // rjohnson: just a random number here to shift the cos curve origin.Zero(); origin += GetPhysics()->GetAxis()[2] * (4.0f + idMath::Cos( ( ( gameLocal.time + 1000 ) * scale ) + offset ) * 4.0f); return true; } // RAVEN BEGIN // mekberg: added /* ================ idItem::Collide ================ */ bool idItem::Collide( const trace_t &collision, const idVec3 &velocity ) { idEntity* lol = gameLocal.entities[ collision.c.entityNum ]; if ( gameLocal.isMultiplayer && collision.c.contents & CONTENTS_ITEMCLIP && lol && !lol->IsType( idItem::GetClassType() ) ) { PostEventMS( &EV_Remove, 0 ); } return false; } // RAVEN END /* ================ idItem::Think ================ */ void idItem::Think( void ) { if ( thinkFlags & TH_PHYSICS ) { RunPhysics(); UpdateTrigger(); } if ( gameLocal.IsMultiplayer() && g_skipItemShadowsMP.GetBool() ) { renderEntity.suppressShadowInViewID = gameLocal.localClientNum + 1; } else { renderEntity.suppressShadowInViewID = 0; } if( !(simpleItem && pickedUp) ) { UpdateVisuals(); Present(); } } /* ================ idItem::Present ================ */ void idItem::Present( void ) { idEntity::Present(); if ( !fl.hidden && pulse ) { // also add a highlight shell model renderEntity_t shell; shell = renderEntity; // we will mess with shader parms when the item is in view // to give the "item pulse" effect shell.callback = idItem::ModelCallback; shell.entityNum = entityNumber; shell.customShader = shellMaterial; if ( itemShellHandle == -1 ) { itemShellHandle = gameRenderWorld->AddEntityDef( &shell ); } else { gameRenderWorld->UpdateEntityDef( itemShellHandle, &shell ); } } } /* ================ idItem::InstanceJoin ================ */ void idItem::InstanceJoin( void ) { idEntity::InstanceJoin(); UpdateModelTransform(); if ( !simpleItem && spawnArgs.GetString( "fx_idle" ) ) { PlayEffect( "fx_idle", renderEntity.origin, renderEntity.axis, true ); } } /* ================ idItem::InstanceLeave ================ */ void idItem::InstanceLeave( void ) { idEntity::InstanceLeave(); StopEffect( "fx_idle", true ); } /* ================ idItem::Spawn ================ */ void idItem::Spawn( void ) { idStr giveTo; idEntity * ent; idVec3 vSize; idBounds bounds(vec3_origin); // check for triggerbounds, which allows for non-square triggers (useful for, say, a CTF flag) if ( spawnArgs.GetVector( "triggerbounds", "16 16 16", vSize )) { bounds.AddPoint(idVec3( vSize.x*0.5f, vSize.y*0.5f, 0.0f)); bounds.AddPoint(idVec3(-vSize.x*0.5f, -vSize.y*0.5f, vSize.z)); } else { // create a square trigger for item pickup float tsize; spawnArgs.GetFloat( "triggersize", "16.0", tsize ); bounds.ExpandSelf( tsize ); } // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_PUSH_HEAP_MEM(this); // RAVEN END trigger = new idClipModel( idTraceModel( bounds )); // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_POP_HEAP(); // RAVEN END // RAVEN BEGIN // ddynerman: multiple clip worlds trigger->Link( this, 0, GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() ); // RAVEN END physicsObj.SetSelf ( this ); // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_PUSH_HEAP_MEM(this); // RAVEN END physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f ); // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_POP_HEAP(); // RAVEN END physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); physicsObj.SetAxis( GetPhysics()->GetAxis() ); physicsObj.SetGravity( gameLocal.GetGravity() ); physicsObj.SetContents( 0 ); physicsObj.SetClipMask( MASK_SOLID ); physicsObj.SetFriction( 0.0f, 0.0f, 6.0f ); SetPhysics( &physicsObj ); if ( spawnArgs.GetBool( "start_off" ) ) { trigger->SetContents( 0 ); Hide(); } else { trigger->SetContents( CONTENTS_TRIGGER ); } giveTo = spawnArgs.GetString( "owner" ); if ( giveTo.Length() ) { ent = gameLocal.FindEntity( giveTo ); if ( !ent ) { gameLocal.Error( "Item couldn't find owner '%s'", giveTo.c_str() ); } PostEventMS( &EV_Touch, 0, ent, NULL ); } if ( spawnArgs.GetBool( "spin" ) || gameLocal.isMultiplayer ) { spin = true; BecomeActive( TH_THINK ); } // pulse ( and therefore itemShellHandle ) was taken out and shot. do not sync //pulse = !spawnArgs.GetBool( "nopulse" ); pulse = false; orgOrigin = GetPhysics()->GetOrigin(); canPickUp = !( spawnArgs.GetBool( "triggerFirst" ) || spawnArgs.GetBool( "no_touch" ) ); inViewTime = -1000; lastCycle = -1; itemShellHandle = -1; // RAVEN BEGIN // abahr: move texture to def file for precaching shellMaterial = declManager->FindMaterial( spawnArgs.GetString("mtr_highlight", "_default") ); PostEventMS( &EV_SetGravity, 0 ); // RAVEN END if ( spawnArgs.GetString( "skin", NULL ) ) { skin = declManager->FindSkin( spawnArgs.GetString( "skin" ), false ); if( skin ) { SetSkin( skin ); srvReady = 1; } } else { skin = NULL; } if ( spawnArgs.GetString( "skin_pickup", NULL ) ) { pickupSkin = declManager->FindSkin( spawnArgs.GetString( "skin_pickup" ), false ); } else { pickupSkin = NULL; } syncPhysics = spawnArgs.GetBool( "net_syncPhysics", "0" ); if ( srvReady == -1 ) { srvReady = IsHidden() ? 0 : 1; } // RAVEN BEGIN // mekberg: added for removing pickups in mp in pits if ( gameLocal.isMultiplayer ) { trigger->SetContents( trigger->GetContents() | CONTENTS_ITEMCLIP ); } // RAVEN END if( gameLocal.isMultiplayer ) { itemPVSArea = gameLocal.pvs.GetPVSArea( GetPhysics()->GetOrigin() ); } else { itemPVSArea = 0; } simpleItem = g_simpleItems.GetBool() && gameLocal.isMultiplayer && !IsType( rvItemCTFFlag::GetClassType() ); if( simpleItem ) { memset( &renderEntity, 0, sizeof( renderEntity ) ); renderEntity.axis = mat3_identity; renderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f; renderEntity.shaderParms[ SHADERPARM_GREEN ] = 1.0f; renderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f; renderEntity.shaderParms[ SHADERPARM_ALPHA ] = 1.0f; renderEntity.shaderParms[ SHADERPARM_SPRITE_WIDTH ] = 32.0f; renderEntity.shaderParms[ SHADERPARM_SPRITE_HEIGHT ] = 32.0f; renderEntity.hModel = renderModelManager->FindModel( "_sprite" ); renderEntity.callback = NULL; renderEntity.numJoints = 0; renderEntity.joints = NULL; renderEntity.customSkin = 0; renderEntity.noShadow = true; renderEntity.noSelfShadow = true; renderEntity.customShader = declManager->FindMaterial( spawnArgs.GetString( "mtr_simple_icon" ) ); renderEntity.referenceShader = 0; renderEntity.bounds = renderEntity.hModel->Bounds( &renderEntity ); SetAxis( mat3_identity ); } else { if ( spawnArgs.GetString( "fx_idle" ) ) { UpdateModelTransform(); effectIdle = PlayEffect( "fx_idle", renderEntity.origin, renderEntity.axis, true ); } } GetPhysics( )->SetClipMask( GetPhysics( )->GetClipMask( ) | CONTENTS_ITEMCLIP ); pickedUp = false; } /* ================ idItem::Event_SetGravity ================ */ void idItem::Event_SetGravity() { // If the item isnt a dropped item then see if it should settle itself // to the floor or not if ( !spawnArgs.GetBool( "dropped" ) ) { if ( spawnArgs.GetBool( "nodrop" ) ) { physicsObj.PutToRest(); } else { PostEventMS( &EV_DropToFloor, 0 ); } } } // RAVEN END /* ================ idItem::GetAttributes ================ */ void idItem::GetAttributes( idDict &attributes ) { int i; const idKeyValue *arg; for( i = 0; i < spawnArgs.GetNumKeyVals(); i++ ) { arg = spawnArgs.GetKeyVal( i ); if ( arg->GetKey().Left( 4 ) == "inv_" ) { attributes.Set( arg->GetKey().Right( arg->GetKey().Length() - 4 ), arg->GetValue() ); } } } /* ================ idItem::GiveToPlayer ================ */ bool idItem::GiveToPlayer( idPlayer *player ) { if ( player == NULL ) { return false; } if ( spawnArgs.GetBool( "inv_carry" ) ) { return player->GiveInventoryItem( &spawnArgs ); } // Handle the special ammo pickup that gives ammo for the weapon the player currently has if ( spawnArgs.GetBool( "item_currentWeaponAmmo" ) ) { const char *ammoName = player->weapon->GetAmmoNameForIndex(player->weapon->GetAmmoType()); if ( player->weapon->TotalAmmoCount() != player->weapon->maxAmmo && player->weapon->AmmoRequired() ) { player->GiveItem(ammoName); player->GiveInventoryItem( &spawnArgs ); return true; } return false; } return player->GiveItem( this ); } /* =============== idItem::SendPickupMsg =============== */ void idItem::SendPickupMsg( int clientNum ) { idBitMsg msg; byte msgBuf[MAX_GAME_MESSAGE_SIZE]; msg.Init( msgBuf, sizeof( msgBuf ) ); msg.WriteByte( GAME_UNRELIABLE_MESSAGE_EVENT ); msg.WriteBits( gameLocal.GetSpawnId( this ), 32 ); msg.WriteByte( EVENT_PICKUP ); msg.WriteByte( clientNum ); // send as unreliable to client picking up the item, so it can play HUD things gameLocal.SendUnreliableMessagePVS( msg, this, itemPVSArea ); } /* ================ idItem::Pickup ================ */ bool idItem::Pickup( idPlayer *player ) { //dropped weapon? bool dropped = spawnArgs.GetBool( "dropped" ); if ( gameLocal.isMultiplayer && !dropped && spawnArgs.FindKey( "weaponclass" ) && gameLocal.IsWeaponsStayOn() && gameLocal.time > player->lastPickupTime + 1000 ) { idDict attr; GetAttributes( attr ); const idKeyValue* arg = attr.FindKey( "weapon" ); if ( arg ) { if ( !player->inventory.Give( player, player->spawnArgs, arg->GetKey(), arg->GetValue(), NULL, false, dropped, true ) ) { StartSound( "snd_noacquire", SND_CHANNEL_ITEM, 0, false, NULL ); } } } // only predict noacquire on client if ( gameLocal.isClient ) { return false; } int givenToPlayer = spawnArgs.GetInt( "givenToPlayer", "-1" ); if ( player == NULL || ( givenToPlayer != -1 && givenToPlayer != player->entityNumber ) ) { // idPlayer::GiveItem spawns an idItem for pickup, which appears at the origin before being picked // and could sometimes be picked by someone else, particularly in buy mode when there is a play spawn sitting on the origin ;-) return false; } if ( !GiveToPlayer( player ) ) { return false; } if ( gameLocal.isServer ) { SendPickupMsg( player->entityNumber ); } // Check for global acquire sounds in multiplayer if ( gameLocal.isMultiplayer && spawnArgs.GetBool( "globalAcquireSound" ) ) { gameLocal.mpGame.PlayGlobalItemAcquireSound( entityDefNumber ); } else { StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL ); } // trigger our targets ActivateTargets( player ); player->lastPickupTime = gameLocal.time; //if a placed item and si_weaponStay is on and we're a weapon, don't remove and respawn if ( gameLocal.IsMultiplayer() ) { if ( !dropped ) { if ( spawnArgs.FindKey( "weaponclass" ) ) { if ( gameLocal.IsWeaponsStayOn() ) { return true; } } } } // clear our contents so the object isn't picked up twice GetPhysics()->SetContents( 0 ); // hide the model, or switch to the pickup skin if ( pickupSkin ) { SetSkin( pickupSkin ); srvReady = 0; } else { Hide(); BecomeInactive( TH_THINK ); } pickedUp = true; // allow SetSkin or Hide() to get called regardless of simpleitem mode if( simpleItem ) { FreeModelDef(); UpdateVisuals(); } // remove the highlight shell if ( itemShellHandle != -1 ) { gameRenderWorld->FreeEntityDef( itemShellHandle ); itemShellHandle = -1; } // asalmon: Added option for a differnt respawn rate based on gametype. float respawn = spawnArgs.GetFloat(va("respawn_%s",gameLocal.serverInfo.GetString( "si_gameType" )), "-1.0"); if( respawn == -1.0f ) { respawn = spawnArgs.GetFloat( "respawn", "5.0" ); } bool no_respawn = spawnArgs.GetBool( "no_respawn" ); if ( !gameLocal.isMultiplayer ) { respawn = 0.0f; } else if ( gameLocal.mpGame.IsBuyingAllowedInTheCurrentGameMode() ) { if ( givenToPlayer != -1 ) { respawn = 0.0f; } } if ( respawn && !dropped && !no_respawn ) { const char *sfx = spawnArgs.GetString( "fx_Respawn" ); if ( sfx && *sfx ) { PostEventSec( &EV_RespawnFx, respawn - 0.5f ); } PostEventSec( &EV_RespawnItem, respawn ); } else if ( !spawnArgs.GetBool( "inv_objective" ) && !no_respawn ) { // give some time for the pickup sound to play // FIXME: Play on the owner if ( !spawnArgs.GetBool( "inv_carry" ) ) { PostEventMS( &EV_Remove, 5000 ); } } trigger->SetContents( 0 ); StopEffect( "fx_idle" ); return true; } /* ================ idItem::Hide ================ */ void idItem::Hide( void ) { srvReady = 0; idEntity::Hide( ); trigger->SetContents( 0 ); } /* ================ idItem::Show ================ */ void idItem::Show( void ) { srvReady = 1; idEntity::Show( ); trigger->SetContents( CONTENTS_TRIGGER ); } /* ================ idItem::ClientStale ================ */ bool idItem::ClientStale( void ) { idEntity::ClientStale(); StopEffect( "fx_idle" ); return false; } /* ================ idItem::ClientUnstale ================ */ void idItem::ClientUnstale( void ) { idEntity::ClientUnstale(); UpdateModelTransform(); if ( !simpleItem && spawnArgs.GetString( "fx_idle" ) ) { PlayEffect( "fx_idle", renderEntity.origin, renderEntity.axis, true ); } } /* ================ idItem::ClientPredictionThink ================ */ void idItem::ClientPredictionThink( void ) { // only think forward because the state is not synced through snapshots if ( !gameLocal.isNewFrame ) { return; } Think(); } /* ================ idItem::WriteFromSnapshot ================ */ void idItem::WriteToSnapshot( idBitMsgDelta &msg ) const { if ( syncPhysics ) { physicsObj.WriteToSnapshot( msg ); } assert( srvReady != -1 ); msg.WriteBits( ( srvReady == 1 ), 1 ); } /* ================ idItem::ReadFromSnapshot ================ */ void idItem::ReadFromSnapshot( const idBitMsgDelta &msg ) { if ( syncPhysics ) { physicsObj.ReadFromSnapshot( msg ); } int newReady = ( msg.ReadBits( 1 ) != 0 ); idVec3 resetOrigin( 0, 0, 0 ); // client spawns the ent with ready == -1 so the state set happens at least once if ( newReady != clReady ) { if ( newReady ) { // g_simpleItems might force a hide even with a pickup skin if ( pickupSkin ) { SetSkin( skin ); } else { SetSkin( skin ); Show(); } if( simpleItem ) { UpdateVisuals(); } pickedUp = false; if ( effectIdle.GetEntity( ) ) { UpdateModelTransform(); effectIdle->SetOrigin( resetOrigin ); } else if ( spawnArgs.GetString( "fx_idle" ) && !simpleItem ) { UpdateModelTransform(); effectIdle = PlayEffect( "fx_idle", renderEntity.origin, renderEntity.axis, true ); } } else { if ( pickupSkin ) { SetSkin( pickupSkin ); } else { Hide(); } if( simpleItem ) { FreeModelDef(); UpdateVisuals(); } pickedUp = true; StopEffect( "fx_idle" ); effectIdle = NULL; } } clReady = newReady; } /* ================ idItem::Event_Pickup ================ */ void idItem::Event_Pickup( int clientNum ) { idPlayer *player; assert( gameLocal.isClient ); // play pickup sound if ( !spawnArgs.GetBool( "globalAcquireSound" ) ) { StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL ); } if( clientNum == gameLocal.localClientNum ) { player = (idPlayer*)gameLocal.entities[ clientNum ]; player->lastPickupTime = gameLocal.time; if ( player ) { player->GiveItem( this ); } } } /* ================ idItem::ClientReceiveEvent ================ */ bool idItem::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) { switch( event ) { case EVENT_PICKUP: { int clientNum = msg.ReadByte(); if( clientNum >= 0 && clientNum < MAX_CLIENTS ) { Event_Pickup( clientNum ); } return true; } case EVENT_RESPAWNFX: { Event_RespawnFx(); return true; } default: { return idEntity::ClientReceiveEvent( event, time, msg ); } } //unreachable // return false; } /* ================ idItem::Event_DropToFloor ================ */ void idItem::Event_DropToFloor( void ) { // don't drop the floor if bound to another entity if ( GetBindMaster() != NULL && GetBindMaster() != this ) { return; } physicsObj.DropToFloor( ); } /* ================ idItem::Event_Touch ================ */ void idItem::Event_Touch( idEntity *other, trace_t *trace ) { // RAVEN BEGIN // jnewquist: Use accessor for static class type if ( !other->IsType( idPlayer::GetClassType() ) ) { // RAVEN END return; } if ( !canPickUp ) { return; } Pickup( static_cast(other) ); } /* ================ idItem::Event_Trigger ================ */ void idItem::Event_Trigger( idEntity *activator ) { if ( !canPickUp && spawnArgs.GetBool( "triggerFirst" ) ) { canPickUp = true; return; } // RAVEN BEGIN // jnewquist: Use accessor for static class type if ( activator && activator->IsType( idPlayer::GetClassType() ) ) { // RAVEN END Pickup( static_cast( activator ) ); } } /* ================ idItem::Event_Respawn ================ */ void idItem::Event_Respawn( void ) { // with simple items, re-show the item, but still set srvReady to true to let clients // know how to deal if ( pickupSkin ) { srvReady = true; if( !simpleItem ) { SetSkin( skin ); } } if( !pickupSkin ) { BecomeActive( TH_THINK ); Show(); } if( simpleItem ) { Show(); } pickedUp = false; inViewTime = -1000; lastCycle = -1; trigger->SetContents ( CONTENTS_TRIGGER ); SetOrigin( orgOrigin ); StartSound( "snd_respawn", SND_CHANNEL_ITEM, 0, false, NULL ); PostEventMS( &EV_SetGravity, 0 ); CancelEvents( &EV_RespawnItem ); // don't double respawn if ( !simpleItem && spawnArgs.GetString( "fx_idle" ) ) { UpdateModelTransform(); PlayEffect( "fx_idle", renderEntity.origin, renderEntity.axis, true ); } } /* ================ idItem::Event_RespawnFx ================ */ void idItem::Event_RespawnFx( void ) { idBitMsg msg; byte msgBuf[MAX_GAME_MESSAGE_SIZE]; if ( gameLocal.isServer ) { msg.Init( msgBuf, sizeof( msgBuf ) ); msg.WriteByte( GAME_UNRELIABLE_MESSAGE_EVENT ); msg.WriteBits( gameLocal.GetSpawnId( this ), 32 ); msg.WriteByte( EVENT_RESPAWNFX ); // send unreliable PVS-sensitive gameLocal.SendUnreliableMessagePVS( msg, this, gameLocal.pvs.GetPVSArea( GetPhysics()->GetOrigin() ) ); } if( !simpleItem ) { gameLocal.PlayEffect( spawnArgs, "fx_respawn", GetPhysics()->GetOrigin(), idVec3(0,0,1).ToMat3(), false, vec3_origin ); } } /* =============================================================================== idItemPowerup =============================================================================== */ /* =============== idItemPowerup =============== */ CLASS_DECLARATION( idItem, idItemPowerup ) END_CLASS /* ================ idItemPowerup::idItemPowerup ================ */ idItemPowerup::idItemPowerup() { time = 0; type = 0; droppedTime = 0; unique = false; } /* ================ idItemPowerup::Save ================ */ void idItemPowerup::Save( idSaveGame *savefile ) const { savefile->WriteInt( time ); savefile->WriteInt( type ); savefile->WriteInt( droppedTime ); // cnicholson: Added unsaved var } /* ================ idItemPowerup::Restore ================ */ void idItemPowerup::Restore( idRestoreGame *savefile ) { savefile->ReadInt( time ); savefile->ReadInt( type ); savefile->ReadInt( droppedTime ); // cnicholson: Added unrestored var } /* ================ idItemPowerup::Spawn ================ */ void idItemPowerup::Spawn( void ) { time = SEC2MS( spawnArgs.GetInt( "time", "30" ) ); // SEC2MS screws up when we want -1 time (no expiration) if( spawnArgs.GetInt( "time" ) == -1 ) { time = -1; } type = spawnArgs.GetInt( "type", "0" ); // If the powerup was dropped then make it dissapear using its remaining time. if ( spawnArgs.GetBool( "dropped" ) && time != -1 ) { droppedTime = gameLocal.time + time; PostEventMS( &EV_Remove, time ); } // unique powerpus won't respawn while a player has them unique = spawnArgs.GetBool( "unique", "0" ); if( unique ) { spawnArgs.SetBool( "no_respawn", true ); } if ( !idStr::Icmp( spawnArgs.GetString( "team" ), "strogg" ) ) { team = TEAM_STROGG; } else if( !idStr::Icmp( spawnArgs.GetString( "team" ), "marine" ) ) { team = TEAM_MARINE; } else { team = -1; } } /* ================ idItemPowerup::GiveToPlayer ================ */ bool idItemPowerup::GiveToPlayer( idPlayer *player ) { if ( player == NULL || player->spectating ) { return false; } // only one arena CTF powerup at a time if ( type >= POWERUP_AMMOREGEN && type <= POWERUP_SCOUT ) { if ( ( player->inventory.powerups & ARENA_POWERUP_MASK ) != 0 ) { return false; } } // in flavours of arena CTF (or are idItemPowerups only used in Arena? or even, are idItemPowerups MP only?), // ensure that items with a team can only be picked up by members of that team if ( gameLocal.IsMultiplayer() && gameLocal.IsTeamGame() && team >= 0 ) { if( team != player->team ) { return false; } } if ( droppedTime > 0 ) { player->GivePowerUp( type, droppedTime - gameLocal.time ); } else { player->GivePowerUp( type, time ); } // also call idItem::GiveToPlayer so any inv_* keywords get applied idItem::GiveToPlayer( player ); return true; } /* ================ idItemPowerup::Think ================ */ void idItemPowerup::Think( void ) { int i; // idItem::Think() only needs to be called if we're spawned in if( !IsHidden() || (pickupSkin && GetSkin() != pickupSkin ) ) { // only get here if spawned in idItem::Think(); return; } if( !unique ) { // non-unique despawned powerups don't need to think return; } for( i = 0; i < gameLocal.numClients; i++ ) { idPlayer* p = (idPlayer*)gameLocal.entities[ i ]; if( p == NULL ) { continue; } // only spawn back in if noone on your team has the powerup if( p->PowerUpActive( type ) && p->team == team ) { break; } } if( i >= gameLocal.numClients ) { PostEventMS( &EV_RespawnItem, 0 ); } } /* ================ idItemPowerup::Pickup ================ */ bool idItemPowerup::Pickup( idPlayer* player ) { // regular pickup routine, but unique items need to think to know when to respawn bool pickup; if( gameLocal.isClient ) { // no client-side powerup prediction return false; } pickup = idItem::Pickup( player ); if( unique ) { BecomeActive( TH_THINK ); } return pickup; } /* =============================================================================== idObjective =============================================================================== */ CLASS_DECLARATION( idItem, idObjective ) EVENT( EV_Activate, idObjective::Event_Trigger ) EVENT( EV_HideObjective, idObjective::Event_HideObjective ) EVENT( EV_GetPlayerPos, idObjective::Event_GetPlayerPos ) EVENT( EV_CamShot, idObjective::Event_CamShot ) END_CLASS /* ================ idObjective::idObjective ================ */ idObjective::idObjective() { playerPos.Zero(); // RAVEN BEGIN // mekberg: store triggered time for timed removal. triggerTime = 0; // RAVEN END } /* ================ idObjective::Save ================ */ void idObjective::Save( idSaveGame *savefile ) const { savefile->WriteVec3( playerPos ); } /* ================ idObjective::Restore ================ */ void idObjective::Restore( idRestoreGame *savefile ) { savefile->ReadVec3( playerPos ); // RAVEN BEGIN // jnewquist: Don't do this on Xenon, we want prebuilt textures #ifndef _XENON PostEventMS( &EV_CamShot, 250 ); #endif // RAVEN END } /* ================ idObjective::Spawn ================ */ void idObjective::Spawn( void ) { Hide(); // RAVEN BEGIN // jnewquist: Only post a camshot event if the spawn args request it #ifndef _XENON const char *camName; if ( spawnArgs.GetString( "camShot", "", &camName ) ) { common->Warning( "SpawnArg camShot on %s is not recommended.", spawnArgs.GetString( "name" ) ); PostEventMS( &EV_CamShot, 250 ); } #endif // RAVEN END } /* ================ idObjective::Event_Screenshot ================ */ void idObjective::Event_CamShot( ) { const char *camName; // RAVEN BEGIN // bdube: changed screenshot location idStr shotName = "gfx/objectives/"; shotName += spawnArgs.GetString( "screenshot" ); shotName.SetFileExtension( ".tga" ); // RAVEN END if ( spawnArgs.GetString( "camShot", "", &camName ) ) { idEntity *ent = gameLocal.FindEntity( camName ); if ( ent && ent->cameraTarget ) { const renderView_t *view = ent->cameraTarget->GetRenderView(); renderView_t fullView = *view; fullView.width = SCREEN_WIDTH; fullView.height = SCREEN_HEIGHT; // draw a view to a texture renderSystem->CropRenderSize( 256, 256, true ); gameRenderWorld->RenderScene( &fullView ); // TTimo: I don't think this jives with SMP code, but I grepped through the final maps and didn't see any using it renderSystem->CaptureRenderToFile( shotName ); renderSystem->UnCrop(); } } } /* ================ idObjective::Event_Trigger ================ */ void idObjective::Event_Trigger( idEntity *activator ) { idPlayer *player = gameLocal.GetLocalPlayer(); if ( player ) { if ( spawnArgs.GetString( "inv_objective", NULL ) ) { // RAVEN BEGIN // abahr: changed player->hud to player->GetHud so when in a vehicle we update the vehicle hud // twhitaker: changed player->hud to player->GetObjectiveHud(), to resolve all issues if ( player && player->GetObjectiveHud() ) { // bdube: changed screenshot location idStr shotName = spawnArgs.GetString( "screenshot" ); player->GetObjectiveHud()->SetStateString( "screenshot", shotName ); player->GetObjectiveHud()->SetStateString( "objective", "1" ); player->GetObjectiveHud()->SetStateString( "objectivetext", common->GetLocalizedString( spawnArgs.GetString( "objectivetext" ) ) ); player->GetObjectiveHud()->SetStateString( "objectivetitle", common->GetLocalizedString( spawnArgs.GetString( "objectivetitle" ) ) ); // RAVEN END player->GiveObjective( spawnArgs.GetString( "objectivetitle" ), spawnArgs.GetString( "objectivetext" ), shotName ); // a tad slow but keeps from having to update all objectives in all maps with a name ptr for( int i = 0; i < gameLocal.num_entities; i++ ) { // RAVEN BEGIN // jnewquist: Use accessor for static class type if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idObjectiveComplete::GetClassType() ) ) { // RAVEN END if ( idStr::Icmp( spawnArgs.GetString( "objectivetitle" ), gameLocal.entities[ i ]->spawnArgs.GetString( "objectivetitle" ) ) == 0 ){ gameLocal.entities[ i ]->spawnArgs.SetBool( "objEnabled", true ); break; } } } PostEventMS( &EV_GetPlayerPos, 2000 ); // RAVEN BEGIN // mekberg: store triggered time for timed removal. triggerTime = gameLocal.time; // RAVEN END } } } } /* ================ idObjective::Event_GetPlayerPos ================ */ void idObjective::Event_GetPlayerPos() { idPlayer *player = gameLocal.GetLocalPlayer(); if ( player ) { playerPos = player->GetPhysics()->GetOrigin(); PostEventMS( &EV_HideObjective, 100, player ); } } /* ================ idObjective::Event_HideObjective ================ */ void idObjective::Event_HideObjective(idEntity *e) { idPlayer *player = gameLocal.GetLocalPlayer(); if ( player ) { idVec3 v = player->GetPhysics()->GetOrigin() - playerPos; // RAVEN BEGIN // mekberg: hide time done internally now if ( v.Length() > 64.0f || gameLocal.time > triggerTime + 5000 ) { // RAVEN END player->HideObjective ( ); PostEventMS( &EV_Remove, 0 ); } else { PostEventMS( &EV_HideObjective, 100, player ); } } } /* =============================================================================== idMoveableItem =============================================================================== */ CLASS_DECLARATION( idItem, idMoveableItem ) EVENT( EV_Gib, idMoveableItem::Event_Gib ) END_CLASS /* ================ idMoveableItem::idMoveableItem ================ */ idMoveableItem::idMoveableItem() { } /* ================ idMoveableItem::~idMoveableItem ================ */ idMoveableItem::~idMoveableItem() { // If this entity has been allocated, but not spawned, the physics object will // not have a self pointer, and will not unregister itself from the entity. SetPhysics( NULL ); } /* ================ idMoveableItem::Save ================ */ void idMoveableItem::Save( idSaveGame *savefile ) const { savefile->WriteStaticObject( physicsObj ); } /* ================ idMoveableItem::Restore ================ */ void idMoveableItem::Restore( idRestoreGame *savefile ) { savefile->ReadStaticObject ( physicsObj ); RestorePhysics ( &physicsObj ); } /* ================ idMoveableItem::Spawn ================ */ void idMoveableItem::Spawn( void ) { idTraceModel trm; float density, friction, bouncyness; idStr clipModelName; idBounds bounds; // if the model should be shrinked if ( spawnArgs.GetBool( "clipshrink" ) ) { trm.Shrink( CM_CLIP_EPSILON ); } // get rigid body properties spawnArgs.GetFloat( "density", "0.5", density ); density = idMath::ClampFloat( 0.001f, 1000.0f, density ); spawnArgs.GetFloat( "friction", "0.05", friction ); friction = idMath::ClampFloat( 0.0f, 1.0f, friction ); spawnArgs.GetFloat( "bouncyness", "0.6", bouncyness ); bouncyness = idMath::ClampFloat( 0.0f, 1.0f, bouncyness ); // setup the physics physicsObj.SetSelf( this ); // RAVEN BEGIN // mwhitlock: Dynamic memory consolidation RV_PUSH_HEAP_MEM(this); // check if a clip model is set spawnArgs.GetString( "itemclipmodel", "", clipModelName ); if ( clipModelName[0] ) { if ( collisionModelManager->TrmFromModel( gameLocal.GetMapName(), clipModelName, trm ) ) { physicsObj.SetClipModel( new idClipModel( trm ), density ); } else { // fallback physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), density ); } } else { // fallback physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), density ); } // mwhitlock: Dynamic memory consolidation RV_POP_HEAP(); // RAVEN END physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); physicsObj.SetAxis( GetPhysics()->GetAxis() ); physicsObj.SetBouncyness( bouncyness ); physicsObj.SetFriction( 0.6f, 0.6f, friction ); physicsObj.SetGravity( gameLocal.GetGravity() ); physicsObj.SetContents( CONTENTS_RENDERMODEL ); physicsObj.SetClipMask( MASK_SOLID | CONTENTS_MOVEABLECLIP ); SetPhysics( &physicsObj ); // RAVEN BEGIN // mekberg: added if ( spawnArgs.GetBool( "noimpact" ) || spawnArgs.GetBool( "notPushable" ) ) { physicsObj.DisableImpact(); } // RAVEN END } /* ================ idMoveableItem::Think ================ */ void idMoveableItem::Think( void ) { RunPhysics(); UpdateTrigger( ); Present(); } /* ================ idMoveableItem::DropItem ================ */ idEntity *idMoveableItem::DropItem( const char *classname, const idVec3 &origin, const idMat3 &axis, const idVec3 &velocity, int activateDelay, int removeDelay ) { idDict args; idEntity *item; args.Set( "classname", classname ); args.Set( "dropped", "1" ); // we sometimes drop idMoveables here, so set 'nodrop' to 1 so that it doesn't get put on the floor args.Set( "nodrop", "1" ); if ( activateDelay ) { args.SetBool( "triggerFirst", true ); } gameLocal.SpawnEntityDef( args, &item ); if ( item ) { // set item position item->GetPhysics()->SetOrigin( origin ); item->GetPhysics()->SetAxis( axis ); item->GetPhysics()->SetLinearVelocity( velocity ); item->UpdateVisuals(); if ( activateDelay ) { item->PostEventMS( &EV_Activate, activateDelay, item ); } if ( !removeDelay ) { removeDelay = 5 * 60 * 1000; } // always remove a dropped item after 5 minutes in case it dropped to an unreachable location item->PostEventMS( &EV_Remove, removeDelay ); } return item; } /* ================ idMoveableItem::DropItems The entity should have the following key/value pairs set: "def_dropItem" "item def" "dropItemJoint" "joint name" "dropItemRotation" "pitch yaw roll" "dropItemOffset" "x y z" "skin_drop" "skin name" To drop multiple items the following key/value pairs can be used: "def_dropItem" "item def" "dropItemJoint" "joint name" "dropItemRotation" "pitch yaw roll" "dropItemOffset" "x y z" where is an aribtrary string. ================ */ void idMoveableItem::DropItems( idAnimatedEntity *ent, const char *type, idList *list ) { const idKeyValue *kv; const char *skinName, *c, *jointName; idStr key, key2; idVec3 origin; idMat3 axis; idAngles angles; const idDeclSkin *skin; jointHandle_t joint; idEntity *item; // drop all items kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), NULL ); while ( kv ) { c = kv->GetKey().c_str() + kv->GetKey().Length(); if ( idStr::Icmp( c - 5, "Joint" ) != 0 && idStr::Icmp( c - 8, "Rotation" ) != 0 ) { key = kv->GetKey().c_str() + 4; key2 = key; key += "Joint"; key2 += "Offset"; jointName = ent->spawnArgs.GetString( key ); joint = ent->GetAnimator()->GetJointHandle( jointName ); if ( !ent->GetJointWorldTransform( joint, gameLocal.time, origin, axis ) ) { gameLocal.Warning( "%s refers to invalid joint '%s' on entity '%s'\n", key.c_str(), jointName, ent->name.c_str() ); origin = ent->GetPhysics()->GetOrigin(); axis = ent->GetPhysics()->GetAxis(); } if ( g_dropItemRotation.GetString()[0] ) { angles.Zero(); sscanf( g_dropItemRotation.GetString(), "%f %f %f", &angles.pitch, &angles.yaw, &angles.roll ); } else { key = kv->GetKey().c_str() + 4; key += "Rotation"; ent->spawnArgs.GetAngles( key, "0 0 0", angles ); } axis *= angles.ToMat3() * axis; origin += ent->spawnArgs.GetVector( key2, "0 0 0" ); item = DropItem( kv->GetValue(), origin, axis, vec3_origin, 0, 0 ); if ( list && item ) { list->Append( item ); } } kv = ent->spawnArgs.MatchPrefix( va( "def_drop%sItem", type ), kv ); } // change the skin to hide all items skinName = ent->spawnArgs.GetString( va( "skin_drop%s", type ) ); if ( skinName[0] ) { skin = declManager->FindSkin( skinName ); ent->SetSkin( skin ); } } /* ====================== idMoveableItem::WriteToSnapshot ====================== */ void idMoveableItem::WriteToSnapshot( idBitMsgDelta &msg ) const { physicsObj.WriteToSnapshot( msg ); } /* ====================== idMoveableItem::ReadFromSnapshot ====================== */ void idMoveableItem::ReadFromSnapshot( const idBitMsgDelta &msg ) { physicsObj.ReadFromSnapshot( msg ); if ( msg.HasChanged() ) { UpdateVisuals(); } } /* ============ idMoveableItem::Gib ============ */ void idMoveableItem::Gib( const idVec3 &dir, const char *damageDefName ) { // remove the entity PostEventMS( &EV_Remove, 0 ); } /* ============ idMoveableItem::Event_Gib ============ */ void idMoveableItem::Event_Gib( const char *damageDefName ) { Gib( idVec3( 0, 0, 1 ), damageDefName ); } /* =============================================================================== idItemRemover =============================================================================== */ CLASS_DECLARATION( idEntity, idItemRemover ) EVENT( EV_Activate, idItemRemover::Event_Trigger ) END_CLASS /* ================ idItemRemover::Spawn ================ */ void idItemRemover::Spawn( void ) { } /* ================ idItemRemover::RemoveItem ================ */ void idItemRemover::RemoveItem( idPlayer *player ) { const char *remove; remove = spawnArgs.GetString( "remove" ); player->RemoveInventoryItem( remove ); } /* ================ idItemRemover::Event_Trigger ================ */ void idItemRemover::Event_Trigger( idEntity *activator ) { // RAVEN BEGIN // jnewquist: Use accessor for static class type if ( activator->IsType( idPlayer::GetClassType() ) ) { // RAVEN END RemoveItem( static_cast(activator) ); } } /* =============================================================================== idObjectiveComplete =============================================================================== */ CLASS_DECLARATION( idItemRemover, idObjectiveComplete ) EVENT( EV_Activate, idObjectiveComplete::Event_Trigger ) EVENT( EV_HideObjective, idObjectiveComplete::Event_HideObjective ) EVENT( EV_GetPlayerPos, idObjectiveComplete::Event_GetPlayerPos ) END_CLASS /* ================ idObjectiveComplete::idObjectiveComplete ================ */ idObjectiveComplete::idObjectiveComplete() { playerPos.Zero(); // RAVEN BEGIN // mekberg: store triggered time for timed removal. triggerTime = 0; // RAVEN END } /* ================ idObjectiveComplete::Save ================ */ void idObjectiveComplete::Save( idSaveGame *savefile ) const { savefile->WriteVec3( playerPos ); } /* ================ idObjectiveComplete::Restore ================ */ void idObjectiveComplete::Restore( idRestoreGame *savefile ) { savefile->ReadVec3( playerPos ); } /* ================ idObjectiveComplete::Spawn ================ */ void idObjectiveComplete::Spawn( void ) { spawnArgs.SetBool( "objEnabled", false ); Hide(); } /* ================ idObjectiveComplete::Event_Trigger ================ */ void idObjectiveComplete::Event_Trigger( idEntity *activator ) { if ( !spawnArgs.GetBool( "objEnabled" ) ) { return; } idPlayer *player = gameLocal.GetLocalPlayer(); if ( player ) { RemoveItem( player ); if ( spawnArgs.GetString( "inv_objective", NULL ) ) { // RAVEN BEGIN // abahr: changed player->hud to player->GetHud so if in a vehicle we update that hud // twhitaker: moved objective system to it's own hud, to solve vehicle hud issues. if ( player->GetObjectiveHud() ) { player->GetObjectiveHud()->SetStateString( "objective", "2" ); player->GetObjectiveHud()->SetStateString( "objectivetext", common->GetLocalizedString( spawnArgs.GetString( "objectivetext" ) ) ); player->GetObjectiveHud()->SetStateString( "objectivetitle", common->GetLocalizedString( spawnArgs.GetString( "objectivetitle" ) ) ); // RAVEN END player->CompleteObjective( spawnArgs.GetString( "objectivetitle" ) ); PostEventMS( &EV_GetPlayerPos, 2000 ); // RAVEN BEGIN // mekberg: store triggered time for timed removal. triggerTime = gameLocal.time; // RAVEN END } } } } /* ================ idObjectiveComplete::Event_GetPlayerPos ================ */ void idObjectiveComplete::Event_GetPlayerPos() { idPlayer *player = gameLocal.GetLocalPlayer(); if ( player ) { playerPos = player->GetPhysics()->GetOrigin(); PostEventMS( &EV_HideObjective, 100, player ); } } /* ================ idObjectiveComplete::Event_HideObjective ================ */ void idObjectiveComplete::Event_HideObjective( idEntity *e ) { idPlayer *player = gameLocal.GetLocalPlayer(); if ( player && player->GetObjectiveHud() ) { idVec3 v = player->GetPhysics()->GetOrigin(); v -= playerPos; // RAVEN BEGIN // mekberg: hide time done internally now if ( v.Length() > 64.0f || gameLocal.time > triggerTime + 5000 ) { // RAVEN END player->HideObjective ( ); PostEventMS( &EV_Remove, 0 ); } else { PostEventMS( &EV_HideObjective, 100, player ); } } } /* =============================================================================== rvObjectiveFailed =============================================================================== */ CLASS_DECLARATION( idItemRemover, rvObjectiveFailed ) EVENT( EV_Activate, rvObjectiveFailed::Event_Trigger ) END_CLASS rvObjectiveFailed::rvObjectiveFailed ( void ) { } /* ================ rvObjectiveFailed::Event_Trigger ================ */ void rvObjectiveFailed::Event_Trigger( idEntity *activator ) { idPlayer *player = gameLocal.GetLocalPlayer(); if ( !player || !player->GetObjectiveHud() ) { return; } if ( !spawnArgs.GetString( "inv_objective", NULL ) ) { return; } player->GetObjectiveHud()->SetStateString( "objective", "2" ); player->GetObjectiveHud()->SetStateString( "objectivetext", common->GetLocalizedString( spawnArgs.GetString( "objectivetext" ) ) ); player->GetObjectiveHud()->SetStateString( "objectivetitle", common->GetLocalizedString( spawnArgs.GetString( "objectivetitle" ) ) ); player->FailObjective( spawnArgs.GetString( "objectivetitle" ) ); } /* =============================================================================== rvItemCTFFlag =============================================================================== */ const idEventDef EV_ResetFlag ( "" ); const idEventDef EV_LinkTrigger( "" ); CLASS_DECLARATION( idItem, rvItemCTFFlag ) EVENT( EV_ResetFlag, rvItemCTFFlag::Event_ResetFlag ) EVENT( EV_LinkTrigger, rvItemCTFFlag::Event_LinkTrigger ) END_CLASS /* ================ rvItemCTFFlag::rvItemCTFFlag ================ */ rvItemCTFFlag::rvItemCTFFlag() { } /* ================ rvItemCTFFlag::Spawn ================ */ void rvItemCTFFlag::Spawn () { /* rjohnson: I fixed the crash so we don't need to remove them... //don't spawn outside of CTF games if( (gameLocal.gameType != GAME_CTF) && (gameLocal.gameType != GAME_1F_CTF) && (gameLocal.gameType != GAME_ARENA_CTF) && (gameLocal.gameType != GAME_ARENA_1F_CTF) ){ //don't spawn! gameLocal.Error("Flag spawn on in non-CTF gametype."); PostEventMS ( &EV_Remove, 0 ); return; } */ spawnArgs.GetBool ( "dropped", "0", dropped ); spawnArgs.GetInt ( "team", "0", team ); bool reset = false; spawnArgs.GetBool ( "reset", "0", reset ); switch ( team ) { case TEAM_MARINE: { powerup = POWERUP_CTF_MARINEFLAG; gameLocal.mpGame.SetFlagEntity( this, TEAM_MARINE ); break; } case TEAM_STROGG: { powerup = POWERUP_CTF_STROGGFLAG; gameLocal.mpGame.SetFlagEntity( this, TEAM_STROGG ); break; } case TEAM_MAX: { powerup = POWERUP_CTF_ONEFLAG; break; } default: gameLocal.Warning ( "Unknown ctf flag team '%d' on entity '%s'", team, name.c_str() ); PostEventMS ( &EV_Remove, 0 ); return; } if ( dropped ) { if ( !gameLocal.isClient ) { ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->SetFlagState( team, FS_DROPPED ); } if ( reset ) { //PostEventSec( &EV_ResetFlag, 1 ); } else { PostEventSec( &EV_ResetFlag, 30 ); } // Let powerups settle for a frame before allowing them to be picked up - we need to // make sure we hit the dropped state for VO, etc to work properly trigger->SetContents( 0 ); PostEventSec( &EV_LinkTrigger, 0 ); } else { if ( !gameLocal.isClient ) { ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->SetFlagState( team, FS_AT_BASE ); } } GetPhysics( )->SetClipMask( GetPhysics( )->GetClipMask( ) | CONTENTS_ITEMCLIP ); spin = false; } /* ================ rvItemCTFFlag::Event_LinkTrigger ================ */ void rvItemCTFFlag::Event_LinkTrigger( void ) { trigger->SetContents( CONTENTS_TRIGGER | CONTENTS_ITEMCLIP ); } /* ================ rvItemCTFFlag::GiveToPlayer ================ */ bool rvItemCTFFlag::GiveToPlayer( idPlayer* player ) { if ( !gameLocal.IsMultiplayer() ) { return false; } if ( player->spectating || ((gameLocal.mpGame.GetGameState())->GetMPGameState() != GAMEON && (gameLocal.mpGame.GetGameState())->GetMPGameState() != SUDDENDEATH && !cvarSystem->GetCVarBool( "g_testCTF" )) ) { return false; } int teamPowerup; int enemyPowerup; bool canPickup = ( team == player->team ); switch ( player->team ) { default: case TEAM_MARINE: teamPowerup = POWERUP_CTF_MARINEFLAG; enemyPowerup = POWERUP_CTF_STROGGFLAG; break; case TEAM_STROGG: teamPowerup = POWERUP_CTF_STROGGFLAG; enemyPowerup = POWERUP_CTF_MARINEFLAG; break; } if( gameLocal.gameType == GAME_1F_CTF || gameLocal.gameType == GAME_ARENA_1F_CTF ) { // in one flag CTF, we score touching the enemy's flag canPickup = ( team == gameLocal.mpGame.OpposingTeam( player->team ) ); enemyPowerup = POWERUP_CTF_ONEFLAG; } // If the player runs over their own flag the only thing they // can do is score or return it. // when the player touches the one flag, he always gets it if ( canPickup ) { // If the flag was dropped, return it if ( dropped ) { gameLocal.mpGame.AddPlayerTeamScore( player, 2 ); statManager->FlagReturned( player ); ResetFlag ( teamPowerup ); // RITUAL BEGIN // squirrel: Mode-agnostic buymenus player->GiveCash( (float)gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerCashAward_flagReturned", 0 ) ); // RITUAL END } else if ( player->PowerUpActive ( enemyPowerup ) ) { // If they have the enemy flag then they score if ( !gameLocal.mpGame.CanCapture ( player->team ) ) { return false; } ResetFlag( enemyPowerup ); gameLocal.mpGame.FlagCaptured( player ); } return false; } // only pickup one flag in arena CTF if( ( gameLocal.gameType == GAME_1F_CTF || gameLocal.gameType == GAME_ARENA_1F_CTF ) && team != TEAM_MAX ) { return false; } player->GivePowerUp( enemyPowerup, -1 ); // RITUAL BEGIN // squirrel: Mode-agnostic buymenus player->GiveCash( (float)gameLocal.mpGame.mpBuyingManager.GetIntValueForKey( "playerCashAward_flagStolen", 0 ) ); // RITUAL END return true; } /* ================ rvItemCTFFlag::Pickup ================ */ bool rvItemCTFFlag::Pickup( idPlayer *player ) { if( gameLocal.isClient ) { // no client-side CTF flag prediction return false; } if ( !GiveToPlayer( player ) ) { return false; } // ServerSendEvent( EVENT_PICKUP, NULL, false, -1 ); if ( gameLocal.isServer ) { SendPickupMsg( player->entityNumber ); } // Check for global acquire sounds in multiplayer if ( gameLocal.isMultiplayer && spawnArgs.GetBool( "globalAcquireSound" ) ) { gameLocal.mpGame.PlayGlobalItemAcquireSound( entityDefNumber ); } else { StartSound( "snd_acquire", SND_CHANNEL_ITEM, 0, false, NULL ); } // trigger our targets ActivateTargets( player ); // clear our contents so the object isn't picked up twice GetPhysics()->SetContents( 0 ); // hide the model Hide(); gameLocal.mpGame.SetFlagEntity( NULL, team ); gameLocal.mpGame.AddPlayerTeamScore( player, 1 ); if( gameLocal.gameType == GAME_CTF || gameLocal.gameType == GAME_ARENA_CTF ) { ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->SetFlagState( team, FS_TAKEN ); } else if( gameLocal.gameType == GAME_1F_CTF || gameLocal.gameType == GAME_ARENA_1F_CTF ) { ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->SetFlagState( team, (player->team == TEAM_MARINE ? FS_TAKEN_MARINE : FS_TAKEN_STROGG) ); } ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->SetFlagCarrier( team, player->entityNumber ); if ( spawnArgs.GetBool( "dropped" ) ) { PostEventMS( &EV_Remove, 0 ); } BecomeInactive( TH_THINK ); return true; } /* ================ rvItemCTFFlag::ResetFlag ================ */ void rvItemCTFFlag::ResetFlag( int powerup ) { idEntity* ent; for ( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { // Make sure no players have the flag anymore // RAVEN BEGIN // jnewquist: Use accessor for static class type if ( ent->IsType ( idPlayer::GetClassType() ) ) { // RAVEN END static_cast(ent)->ClearPowerup ( powerup ); continue; } // If its not a CTF flag item then skip it if ( !ent->IsType( rvItemCTFFlag::Type ) ) { continue; } // Make sure its the right type first rvItemCTFFlag* flag; flag = static_cast(ent); if ( flag->powerup != powerup ) { continue; } if ( flag->dropped ) { flag->PostEventMS( &EV_Remove, 0 ); } else { flag->PostEventMS( &EV_RespawnItem, 0 ); gameLocal.mpGame.SetFlagEntity( flag, flag->team ); } } if ( !gameLocal.isClient ) { int team = -1; if ( powerup == POWERUP_CTF_MARINEFLAG ) { team = TEAM_MARINE; } else if ( powerup == POWERUP_CTF_STROGGFLAG ) { team = TEAM_STROGG; } else if ( powerup == POWERUP_CTF_ONEFLAG ) { team = TEAM_MAX; } ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->SetFlagState( team, FS_AT_BASE ); } } /* ================ rvItemCTFFlag::Event_ResetFlag ================ */ void rvItemCTFFlag::Event_ResetFlag( void ) { ResetFlag( powerup ); } void rvItemCTFFlag::Think( void ) { idItem::Think(); } /* ================ rvItemCTFFlag::Collide ================ */ bool rvItemCTFFlag::Collide( const trace_t &collision, const idVec3 &velocity ) { idEntity* lol = gameLocal.entities[ collision.c.entityNum ]; if ( collision.c.contents & CONTENTS_ITEMCLIP && lol && !lol->IsType( idItem::GetClassType() ) ) { ResetFlag( powerup ); } return false; } // RAVEN END const idEventDef EV_ResetSpawn ( "" ); CLASS_DECLARATION( idItemPowerup, riDeadZonePowerup ) EVENT( EV_ResetSpawn, riDeadZonePowerup::Event_ResetSpawn ) END_CLASS /* ================ riDeadZonePowerup::idItemPowerup ================ */ riDeadZonePowerup::riDeadZonePowerup() { } /* ================ riDeadZonePowerup::Save ================ */ void riDeadZonePowerup::Save( idSaveGame *savefile ) const { } /* ================ riDeadZonePowerup::Restore ================ */ void riDeadZonePowerup::Restore( idRestoreGame *savefile ) { } /* ================ riDeadZonePowerup::Show ================ */ void riDeadZonePowerup::Show() { idItem::Show(); } /* ================ riDeadZonePowerup::Spawn ================ */ void riDeadZonePowerup::Spawn( void ) { powerup = POWERUP_DEADZONE; time = SEC2MS( gameLocal.serverInfo.GetInt("si_deadZonePowerupTime") ); if ( spawnArgs.GetBool( "dropped" ) ) { time = SEC2MS( spawnArgs.GetInt( "time", "10" ) ); if ( time > SEC2MS(10) ) time = SEC2MS(10); Show(); CancelEvents(&EV_Remove); PostEventSec( &EV_ResetSpawn, MS2SEC(time) ); } else Hide(); } /* ================ riDeadZonePowerup::Pickup ================ */ bool riDeadZonePowerup::Pickup( idPlayer* player ) { // regular pickup routine, but unique items need to think to know when to respawn bool pickup; if ( player->PowerUpActive(POWERUP_DEADZONE) ) return false; pickup = idItemPowerup::Pickup( player ); if ( spawnArgs.GetBool( "dropped" ) ) { // Cancel the respawn so the powerup doesn't get removed // from the player that just picked it up. PostEventMS( &EV_Remove, 0 ); CancelEvents( &EV_ResetSpawn ); } return pickup; } /* ================ riDeadZonePowerup::ResetSpawn ================ */ void riDeadZonePowerup::ResetSpawn( int powerup ) { int count = 1; idEntity* ent; riDeadZonePowerup* spawnSpot = 0; for ( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { // If its not a DeadZone powerup then skip it if ( !ent->IsType( riDeadZonePowerup::Type ) ) { continue; } // Make sure its the right type first riDeadZonePowerup* flag; flag = static_cast(ent); if ( flag->powerup != powerup ) { continue; } if ( flag->spawnArgs.GetBool("dropped", "0") && flag == this ) { flag->PostEventMS( &EV_Remove, 0 ); } else { if ( !flag->IsVisible() ) { if ( !(rand()%count) ) { spawnSpot = flag; if ( flag->spawnArgs.GetBool("dropped", "0") ) gameLocal.DPrintf("WARNING: Trying to spawn a powerup at a DROPPED location!"); } count++; } } } if ( spawnSpot ) { spawnSpot->Show(); spawnSpot->PostEventMS( &EV_RespawnItem, 0 ); } else { gameLocal.DPrintf("WARNING: Failed to find a valid spawn spot!"); } } /* ================ riDeadZonePowerup::Collide ================ */ bool riDeadZonePowerup::Collide( const trace_t &collision, const idVec3 &velocity ) { idEntity* lol = gameLocal.entities[ collision.c.entityNum ]; if ( gameLocal.isMultiplayer && collision.c.contents & CONTENTS_ITEMCLIP && lol && !lol->IsType( idItem::GetClassType() ) ) { PostEventMS( &EV_ResetSpawn, 0 ); // Just respawn it. } return false; } /* ================ riDeadZonePowerup::Event_ResetFlag ================ */ void riDeadZonePowerup::Event_ResetSpawn( void ) { ResetSpawn( POWERUP_DEADZONE ); }