object scud_trigger : hackable_objective { void syncFields(); void OnHacked( entity p ); void vSetActive( boolean value ); boolean TryFire( entity firer ); void FireThread( entity firer ); void FireThreadFake(); void TrackScud(); void vSetTargetEntity( entity ent ); void vSetOwner( entity ent ); void vStartObjective(); void OnActiveChanged(); void OnPostMapSpawn(); void OnObjectiveIndexChanged(); void OnScudChanged(); void DestroyTarget( entity targetEnt ); void SpawnDestroyEffect( vector position ); entity vGetSpectateEntity(); boolean vIsObjectiveComplete(); entity targetEnt; entity owner; entity trackingScud; boolean active; boolean AllowHack() { return active; } } void scud_trigger::syncFields() { syncBroadcast( "objectiveIndex" ); syncCallback( "objectiveIndex", "OnObjectiveIndexChanged" ); syncBroadcast( "trackingScud" ); syncCallback( "trackingScud", "OnScudChanged" ); syncBroadcast( "active" ); syncCallback( "active", "OnActiveChanged" ); syncBroadcast( "owner" ); } void scud_trigger::OnObjectiveIndexChanged() { SetObjectiveIndex( objectiveIndex ); } void scud_trigger::OnScudChanged() { if ( trackingScud != $null_entity ) { thread TrackScud(); } } void scud_trigger::OnPostMapSpawn() { // TEMP: for old style shields targetEnt = getEntityKey( "target" ); } void scud_trigger::OnActiveChanged() { if ( active ) { setShaderParm( 7, 1 ); } else { setShaderParm( 7, 0 ); } } void scud_trigger::vSetActive( boolean value ) { if ( active == value ) { return; } active = value; OnActiveChanged(); } void scud_trigger::OnHacked( entity p ) { owner.vSetActive( true ); vSetActive( false ); object oldTeam = getGameTeam(); object newTeam = p.getGameTeam(); setGameTeam( newTeam ); if ( p != $null_entity ) { string statName = newTeam.getName() + "_primary_objective_hacked"; sys.increaseStatInt( sys.allocStatInt( statName ), p.getEntityNumber(), 1 ); statName = newTeam.getName() + "_hack_uses"; sys.increaseStatInt( sys.allocStatInt( statName ), p.getEntityNumber(), 1 ); sys.increaseStatInt( sys.allocStatInt( "total_objectives_completed" ), p.getEntityNumber(), 1 ); handle completeMessage = sys.localizeString( getKeyWithDefault( "complete_message", "maps/generic/obj_hacked" ) ); objManager.PushCPrintString( p.getUserName() ); objManager.CPrintEvent( completeMessage, $null ); } objManager.OnHackComplete( self ); objManager.PlaySound( getKey( "snd_hacked_strogg" ), stroggTeam ); objManager.PlaySound( getKey( "snd_hacked_gdf" ), gdfTeam ); thread FireThread( p ); KillFizzleThread(); } boolean scud_trigger::TryFire( entity firer ) { // set any MCP up to fire if ( G_TryFireScud( firer, targetEnt ) ) { return true; } return false; } void scud_trigger::FireThread( entity firer ) { sys.wait( 5.f ); if ( !TryFire( firer ) ) { objManager.CPrintEvent( sys.localizeString( "game/misc/shield_generator_hacked_reenable" ), gdfTeam ); objManager.CPrintEvent( sys.localizeString( "game/misc/shield_generator_hacked_attack" ), stroggTeam ); while ( true ) { if ( TryFire( firer ) ) { break; } sys.wait( 0.2f ); } } objManager.PlaySound( getKey( "snd_launched_strogg" ), stroggTeam ); objManager.PlaySound( getKey( "snd_launched_gdf" ), gdfTeam ); } void scud_trigger::FireThreadFake() { sys.wait( 5.f ); while ( true ) { if ( TryFire( $null_entity ) ) { break; } sys.wait( 0.2f ); } objManager.PlaySound( getKey( "snd_launched_strogg" ), stroggTeam ); objManager.PlaySound( getKey( "snd_launched_gdf" ), gdfTeam ); while ( true ) { float count = entitiesOfCollection( "nuke" ); if ( count > 0 ) { trackingScud = getBoundsCacheEntity( 0 ); break; } sys.waitFrame(); } TrackScud(); } void scud_trigger::TrackScud() { while ( true ) { sys.waitFrame(); if ( trackingScud == $null_entity ) { return; } float range = sys.vecLength( trackingScud.getWorldOrigin() - getWorldOrigin() ); if ( range > 6144.f ) { continue; } DestroyTarget( trackingScud ); if ( !sys.isClient() ) { objManager.PlaySound( getKey( "snd_stopped_strogg" ), stroggTeam ); objManager.PlaySound( getKey( "snd_stopped_gdf" ), gdfTeam ); objManager.OnShieldDestroyedScud( self ); } break; } } void scud_trigger::DestroyTarget( entity targetEnt ) { if ( targetEnt.vGetDestroyed() ) { return; } SpawnDestroyEffect( targetEnt.getWorldOrigin() ); targetEnt.vSetDestroyed(); } void scud_trigger::SpawnDestroyEffect( vector position ) { vector startPos = owner.getJointPos( owner.getJointHandle( "top_comprezzor_joint" ) ); // start at the roof of the deployable vector diff = startPos - position; vector forward = '1 0 0'; diff = sys.vecNormalize( diff ); vector angles = sys.vecToAngles( diff ); // Charge up and cool down effect owner.playOriginEffect( "fx_charge", "", startPos, forward, false ); // The electrial beam and shockwave object effect = owner.spawnClientEffect( "fx_hit" ); if ( effect != $null_entity ) { effect.setEffectLooping( 0 ); effect.setOrigin( position ); effect.setEffectEndOrigin( startPos ); effect.setAngles( angles ); } } void scud_trigger::vSetOwner( entity ent ) { owner = ent; } void scud_trigger::vSetTargetEntity( entity ent ) { targetEnt = ent; } void scud_trigger::vStartObjective() { thread FireThreadFake(); } entity scud_trigger::vGetSpectateEntity() { float time = sys.getTime(); if ( lastHackTime >= time - 0.5f ) { return hacker; } return $null_entity; } boolean scud_trigger::vIsObjectiveComplete() { return hackCount >= maxHackCount; } object scud_trigger_structure { void preinit(); void syncFields(); void vOnDeploy(); void vSetOwner( entity ent ); void vSetActive( boolean value ); void OnDeadChanged(); void BoundsKillThread(); void InitEffectThread(); string vGetQuickChatString( entity p ); scud_trigger triggerEnt; entity targetEnt; float objectiveIndex; boolean dead; vector effectOffset; } void scud_trigger_structure::preinit() { effectOffset = getVectorKey( "effect_offset" ); thread InitEffectThread(); if ( !sys.isClient() ) { thread BoundsKillThread(); } } void scud_trigger_structure::syncFields() { syncBroadcast( "dead" ); syncCallback( "dead", "OnDeadChanged" ); } void scud_trigger_structure::OnDeadChanged() { if ( dead ) { playAnim( ANIMCHANNEL_ALL, "destroyed" ); stopEffect( "fx_active" ); } else { playCycle( ANIMCHANNEL_ALL, "idle" ); playOriginEffect( "fx_active", "", getWorldOrigin() + effectOffset, '1 0 0', true ); } } void scud_trigger_structure::BoundsKillThread() { float damageIndex = GetDamage( getKey( "dmg_crush" ) ); while ( true ) { BoundsDamage( self, damageIndex ); sys.waitFrame(); } } void scud_trigger_structure::vSetActive( boolean value ) { if ( dead == value ) { return; } dead = value; OnDeadChanged(); } void scud_trigger_structure::vOnDeploy() { if ( !sys.isClient() ) { triggerEnt = sys.spawn( getKey( "def_trigger" ) ); triggerEnt.setOrigin( getWorldOrigin() ); triggerEnt.setAngles( getAngles() ); triggerEnt.setGameTeam( getGameTeam() ); triggerEnt.vSetTargetEntity( targetEnt ); triggerEnt.vSetOwner( self ); triggerEnt.SetObjectiveIndex( objectiveIndex ); objManager.OnShieldDeployed( self, triggerEnt ); sys.killThread( "BoundsKillThread_" + getName() ); } sys.killThread( "InitEffectThread_" + getName() ); if ( !dead ) { playOriginEffect( "fx_active", "", getWorldOrigin() + effectOffset, '1 0 0', true ); } } void scud_trigger_structure::vSetOwner( entity ent ) { targetEnt = ent.getEntityKey( "target" ); objectiveIndex = ent.getIntKey( "objective_index" ); } void scud_trigger_structure::InitEffectThread() { sys.wait( 5 ); OnDeadChanged(); } string scud_trigger_structure::vGetQuickChatString( entity p ) { return triggerEnt.vGetQuickChatString( p ); }