#using scripts\codescripts\struct; #using scripts\shared\_oob; #using scripts\shared\clientfield_shared; #using scripts\shared\killstreaks_shared; #using scripts\shared\util_shared; #using scripts\mp\_util; #using scripts\mp\killstreaks\_killstreaks; #using scripts\mp\killstreaks\_killstreak_detect; #namespace placeables; function SpawnPlaceable( killstreakRef, killstreakId, onPlaceCallback, onCancelCallback, onMoveCallback, onShutdownCallback, onDeathCallback, onEmpCallback, model, validModel, invalidModel, spawnsVehicle, pickupString, timeout, health, empDamage, placeHintString, invalidLocationHintString ) { player = self; self killstreaks::switch_to_last_non_killstreak_weapon(); placeable = spawn( "script_model", player.origin ); placeable.cancelable = true; placeable.held = false; placeable.validModel = validModel; placeable.invalidModel = invalidModel; placeable.killstreakId = killstreakId; placeable.killstreakRef = killstreakRef; placeable.onCancel = onCancelCallback; placeable.onEmp = onEmpCallback; placeable.onMove = onMoveCallback; placeable.onPlace = onPlaceCallback; placeable.onShutdown = onShutdownCallback; placeable.onDeath = onDeathCallback; placeable.owner = player; placeable.originalOwner = player; placeable.ownerEntNum = player.entNum; placeable.originalOwnerEntNum = player.entNum; placeable.pickupString = pickupString; placeable.placedModel = model; placeable.spawnsVehicle = spawnsVehicle; placeable.originalTeam = player.team; placeable.timedOut = false; placeable.timeout = timeout; placeable.timeoutStarted = false; placeable.angles = ( 0, player.angles[1], 0 ); placeable.placeHintString = placeHintString; placeable.invalidLocationHintString = invalidLocationHintString; if(!isdefined(placeable.placeHintString))placeable.placeHintString=""; if(!isdefined(placeable.invalidLocationHintString))placeable.invalidLocationHintString=""; placeable NotSolid(); if ( isdefined( placeable.vehicle ) ) placeable.vehicle NotSolid(); placeable.otherModel = spawn( "script_model", player.origin ); placeable.otherModel SetModel( placeable.placedModel ); placeable.otherModel SetInvisibleToPlayer( player ); placeable.otherModel NotSolid(); placeable.otherModel clientfield::set( "enemyvehicle", 1 ); placeable killstreaks::configure_team( killstreakRef, killstreakId, player ); if( isdefined( health ) && health > 0 ) { placeable.health = health; placeable SetCanDamage( false ); placeable thread killstreaks::MonitorDamage( killstreakRef, health, &OnDeath, 0, undefined, empDamage, &OnEMP, true ); } player thread CarryPlaceable( placeable ); level thread CancelOnGameEnd( placeable ); player thread ShutdownOnCancelEvent( placeable ); player thread CancelOnPlayerDisconnect( placeable ); placeable thread WatchOwnerGameEvents(); return placeable; } function UpdatePlacementModels( model, validModel, invalidModel ) { placeable = self; placeable.placedModel = model; placeable.validModel = validModel; placeable.invalidModel = invalidModel; } function CarryPlaceable( placeable ) { player = self; placeable Show(); placeable NotSolid(); if ( isdefined( placeable.vehicle ) ) placeable.vehicle NotSolid(); if ( isdefined( placeable.otherModel ) ) { placeable thread util::ghost_wait_show_to_player( player, 0.05, "abort_ghost_wait_show" ); placeable.otherModel thread util::ghost_wait_show_to_others( player, 0.05, "abort_ghost_wait_show" ); placeable.otherModel NotSolid(); } placeable.held = true; player.holding_placeable = placeable; player CarryTurret( placeable, ( 40, 0, 0 ), ( 0, 0, 0 ) ); player util::_disableWeapon(); player thread WatchPlacement( placeable ); } function InNoPlacementTrigger() { placeable = self; if( isdefined( level.noTurretPlacementTriggers ) ) { for( i = 0; i < level.noTurretPlacementTriggers.size; i++ ) { if( placeable IsTouching( level.noTurretPlacementTriggers[i] ) ) { return true; } } } if( isdefined( level.fatal_triggers ) ) { for( i = 0; i < level.fatal_triggers.size; i++ ) { if( placeable IsTouching( level.fatal_triggers[i] ) ) { return true; } } } if( placeable oob::IsTouchingAnyOOBTrigger() ) { return true; } return false; } function WatchPlacement( placeable ) { player = self; player endon( "disconnect" ); player endon( "death" ); placeable endon( "placed" ); placeable endon( "cancelled" ); player thread WatchCarryCancelEvents( placeable ); lastAttempt = -1; placeable.canBePlaced = false; waitingForAttackButtonRelease = true; while( true ) { placement = player CanPlayerPlaceTurret(); placeable.origin = placement["origin"]; placeable.angles = placement["angles"]; placeable.canBePlaced = placement["result"] && !( placeable InNoPlacementTrigger() ); if ( player.laststand === true ) { placeable.canBePlaced = false; } if ( isdefined( placeable.otherModel ) ) { placeable.otherModel.origin = placement["origin"]; placeable.otherModel.angles = placement["angles"]; } if( placeable.canBePlaced != lastAttempt ) { if( placeable.canBePlaced ) { placeable SetModel( placeable.validModel ); player SetHintString( IString( placeable.placeHintString ) ); } else { placeable SetModel( placeable.invalidModel ); player SetHintString( IString( placeable.invalidLocationHintString ) ); } lastAttempt = placeable.canBePlaced; } while( waitingForAttackButtonRelease && !player AttackButtonPressed() ) { waitingForAttackButtonRelease = false; } if( !waitingForAttackButtonRelease && placeable.canBePlaced && player AttackButtonPressed() ) { if( placement["result"] ) { placeable.origin = placement["origin"]; placeable.angles = placement["angles"]; player SetHintString( "" ); player StopCarryTurret( placeable ); if( !player util::isWeaponEnabled() ) { player util::_enableWeapon(); } placeable.held = false; player.holding_placeable = undefined; placeable.cancelable = false; if( ( isdefined( placeable.health ) && placeable.health ) ) { placeable SetCanDamage( true ); placeable Solid(); } if ( isdefined( placeable.vehicle ) ) { placeable.vehicle SetCanDamage( true ); placeable.vehicle Solid(); } if( isdefined( placeable.placedModel ) && !placeable.spawnsVehicle ) { placeable SetModel( placeable.placedModel ); } else { placeable notify( "abort_ghost_wait_show" ); placeable.abort_ghost_wait_show_to_player = true; placeable.abort_ghost_wait_show_to_others = true; placeable Ghost(); // need to ghost instead of hide because of resulting issue with client side animations if ( isdefined( placeable.otherModel ) ) { placeable.otherModel notify( "abort_ghost_wait_show" ); placeable.otherModel.abort_ghost_wait_show_to_player = true; placeable.otherModel.abort_ghost_wait_show_to_others = true; placeable.otherModel Ghost(); // need to ghost instead of hide because of resulting issue with client side animations } } if( isdefined( placeable.timeout ) ) { if( !placeable.timeoutStarted ) { placeable.timeoutStarted = true; placeable thread killstreaks::WaitForTimeout( placeable.killstreakRef, placeable.timeout, &OnTimeout, "death", "cancelled" ); } else if( placeable.timedOut ) { placeable thread killstreaks::WaitForTimeout( placeable.killstreakRef, 5000, &OnTimeout, "cancelled" ); } } if( isdefined( placeable.onPlace ) ) { player [[ placeable.onPlace ]]( placeable ); if( isdefined( placeable.onMove ) && !placeable.timedOut ) { SpawnMoveTrigger( placeable, player ); } } placeable notify( "placed" ); } } if( placeable.cancelable && player ActionSlotFourButtonPressed() ) { placeable notify( "cancelled" ); } {wait(.05);}; } } function WatchCarryCancelEvents( placeable ) { player = self; assert( IsPlayer( player ) ); placeable endon( "cancelled" ); placeable endon( "placed" ); player util::waittill_any( "death", "emp_jammed", "emp_grenaded", "disconnect", "joined_team" ); placeable notify( "cancelled" ); } function OnTimeout() { placeable = self; if( ( isdefined( placeable.held ) && placeable.held ) ) { placeable.timedOut = true; return; } placeable notify( "delete_placeable_trigger" ); placeable thread killstreaks::WaitForTimeout( placeable.killstreakRef, 5000, &ForceShutdown, "cancelled" ); } function OnDeath( attacker, weapon ) { placeable = self; if( isdefined( placeable.onDeath ) ) { [[ placeable.onDeath ]]( attacker, weapon ); } placeable notify( "cancelled" ); } function OnEMP( attacker ) { placeable = self; if( isdefined( placeable.onEmp ) ) { placeable [[ placeable.onEmp ]]( attacker ); } } function CancelOnPlayerDisconnect( placeable ) { placeable endon( "hacked" ); player = self; assert( IsPlayer( player ) ); placeable endon( "cancelled" ); placeable endon( "death" ); player util::waittill_any( "disconnect", "joined_team" ); placeable notify( "cancelled" ); } function CancelOnGameEnd( placeable ) { placeable endon( "cancelled" ); placeable endon( "death" ); level waittill( "game_ended" ); placeable notify( "cancelled" ); } function SpawnMoveTrigger( placeable, player ) { pos = placeable.origin + ( 0, 0, 15 ); placeable.pickupTrigger = spawn( "trigger_radius_use", pos ); placeable.pickupTrigger SetCursorHint( "HINT_NOICON", placeable ); placeable.pickupTrigger SetHintString( placeable.pickupString ); placeable.pickupTrigger SetTeamForTrigger( player.team ); player ClientClaimTrigger( placeable.pickupTrigger ); placeable thread WatchPickup( player); placeable.pickupTrigger thread WatchMoveTriggerShutdown( placeable ); } function WatchMoveTriggerShutdown( placeable ) { trigger = self; placeable util::waittill_any( "cancelled", "picked_up", "death", "delete_placeable_trigger", "hacker_delete_placeable_trigger" ); placeable.pickupTrigger delete(); } function WatchPickup( player ) { placeable = self; placeable endon( "death" ); placeable endon( "cancelled" ); assert( isdefined( placeable.pickupTrigger ) ); trigger = placeable.pickupTrigger; while ( true ) { trigger waittill( "trigger", player ); if( !isAlive( player ) ) { continue; } if( player isUsingOffhand() ) { continue; } if( !player isOnGround() ) { continue; } if ( isdefined( placeable.vehicle ) && ( placeable.vehicle.control_initiated === true ) ) { continue; } if ( isdefined( player.carryObject ) && player.carryObject.disallowPlaceablePickup === true ) { continue; } if( isdefined( trigger.triggerTeam ) && ( player.team != trigger.triggerTeam ) ) { continue; } if( isdefined( trigger.claimedBy ) && ( player != trigger.claimedBy ) ) { continue; } if( player useButtonPressed() && !player.throwingGrenade && !player meleeButtonPressed() && !player attackButtonPressed() && !( isdefined( player.isPlanting ) && player.isPlanting ) && !( isdefined( player.isDefusing ) && player.isDefusing ) && !player IsRemoteControlling() && !isdefined( player.holding_placeable ) ) { placeable notify( "picked_up" ); placeable.held = true; placeable SetCanDamage( false ); assert( isdefined( placeable.onMove ) ); player [[ placeable.onMove ]]( placeable ); player thread CarryPlaceable( placeable ); return; } } } function ForceShutdown() { placeable = self; placeable.cancelable = false; placeable notify( "cancelled" ); } function WatchOwnerGameEvents() { self notify("WatchOwnerGameEvents_singleton"); self endon ("WatchOwnerGameEvents_singleton"); placeable = self; placeable endon( "cancelled" ); placeable.owner util::waittill_any( "joined_team", "disconnect", "joined_spectators" ); if ( isdefined( placeable ) ) { placeable.abandoned = true; placeable ForceShutdown(); } } function ShutdownOnCancelEvent( placeable ) { placeable endon( "hacked" ); player = self; assert( IsPlayer( player ) ); placeable util::waittill_any( "cancelled", "death" ); if( isdefined( player ) && isdefined( placeable ) && ( placeable.held === true ) ) { player SetHintString( "" ); player StopCarryTurret( placeable ); if( !player util::isWeaponEnabled() ) { player util::_enableWeapon(); } } if( isdefined( placeable ) ) { if( placeable.cancelable ) { if( isdefined( placeable.onCancel ) ) { [[ placeable.onCancel ]]( placeable ); } } else { if( isdefined( placeable.onShutdown ) ) { [[ placeable.onShutdown ]]( placeable ); } } if ( isdefined( placeable ) ) { if ( isdefined( placeable.vehicle ) ) { vehicle_to_kill = placeable.vehicle; vehicle_to_kill.selfDestruct = true; vehicle_to_kill Kill(); // need to kill the vehicle before placeable is deleted placeable.vehicle = undefined; placeable.otherModel delete(); placeable delete(); } else { placeable.otherModel delete(); placeable delete(); } } } }