etqw-sdk/base/script/projectiles/teleporter.script

584 lines
15 KiB
Plaintext

/***********************************************************************
projectile_teleporter
***********************************************************************/
object projectile_teleporter : projectile_missile {
void syncFields();
void init();
void preinit();
void destroy();
void DoExplodeEffect( entity collisionEnt );
void vActivateTeleportation();
void vCancelTeleportation();
float vGetPliersProgressBarValue( float action );
boolean vCheckActionCode( entity p, float actionCode );
void vArm( entity p );
void DoTeleportation();
float OnActivate( entity p, float distance );
float GetActivateCode( entity p, float distance );
float OnUpdateCrosshairInfo( entity p );
void SetupCommandmapIcon();
void ClearCommandmapIcon();
void OnOwnerChanged();
void DropOwnerItems();
player owner;
void vSetOwner( entity other ) { owner = other; OnOwnerChanged(); }
entity vGetOwner() { return owner; }
void OwnerCheck();
float SweepCheck( vector worldOrigin, float radius, float startAngle, float angleIncrement, vector ownerMins, vector ownerMaxs );
void FindPosition( boolean sweepRange );
void SetupContents();
vector lastValidPosition;
vector portPosition;
float portTime;
float failedPortTime;
void OnPortPositionChanged();
void OnFailedPortChanged();
float disarmCurrent;
float disarmMaxCount;
float commandmapIcon;
float destroyedToolTip;
float chargePerUse;
void vSetCharge( float amount ) { chargePerUse = amount; }
float useMeToolTip1;
float useMeToolTip2;
float badLocationToolTip;
boolean doingTeleport;
boolean vIsTeleporting() { return doingTeleport; }
boolean localIsOwner;
}
#define BAD_PORT_POSITION '-999999 -999999 -999999'
void projectile_teleporter::init() {
setNetworkSynced( true );
SetupContents();
lastValidPosition = getWorldOrigin();
failedPortTime = 0;
setState( "Idle" );
}
void projectile_teleporter::preinit() {
commandmapIcon = -1;
disarmMaxCount = getFloatKeyWithDefault( "disarm_count", 20 );
destroyedToolTip = GetToolTip( getKey( "tt_destroyed" ) );
useMeToolTip1 = GetToolTip( getKey( "tt_useMeToolTip1" ) );
useMeToolTip2 = GetToolTip( getKey( "tt_useMeToolTip2" ) );
badLocationToolTip = GetToolTip( getKey( "tt_badLocation" ) );
// crazy number it would never reach (heh, hopefully!)
portPosition = BAD_PORT_POSITION;
thread OwnerCheck();
}
void projectile_teleporter::destroy() {
ClearCommandmapIcon();
if ( owner != $null_entity ) {
if ( !sys.isClient() ) {
owner.binRemove( self );
owner.setTeleporterState( true );
sys.broadcastToolTip( destroyedToolTip, owner, wstr_empty, wstr_empty, wstr_empty, wstr_empty );
}
if ( owner == sys.getLocalPlayer() ) {
sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.teleporterActive", 0 );
}
}
}
void projectile_teleporter::syncFields() {
sync( "disarmCurrent" );
syncBroadcast( "owner" );
syncBroadcast( "lastValidPosition" );
syncBroadcast( "portTime" );
syncBroadcast( "failedPortTime" );
syncBroadcast( "portPosition" );
syncBroadcast( "chargePerUse" );
syncCallback( "owner", "OnOwnerChanged" );
syncCallback( "portPosition", "OnPortPositionChanged" );
syncCallback( "failedPortTime", "OnFailedPortChanged" );
}
void projectile_teleporter::SetupContents() {
setContents( 0 );
setClipmask( MASK_PROJECTILE | CONTENTS_BODY | CONTENTS_SLIDEMOVER );
}
void projectile_teleporter::OnFailedPortChanged() {
if ( failedPortTime > 0 ) {
// invalid position, play a sound to indicate that
if ( owner == sys.getLocalPlayer() ) {
sys.startSoundDirect( getKey( "snd_invalid" ), SND_WEAPON_FIRE_LOCAL );
if ( !owner.isToolTipPlaying() ) {
owner.sendToolTip( badLocationToolTip );
}
}
}
}
void projectile_teleporter::DoExplodeEffect( entity collisionEnt ) {
}
void projectile_teleporter::DropOwnerItems() {
if ( owner == $null_entity ) {
return;
}
float count = entitiesOfCollection( "carryables" );
float i;
for ( i = 0; i < count; i++ ) {
carryable_item other = getBoundsCacheEntity( i );
if ( other == $null_entity ) {
continue;
}
if ( other.carrier != owner ) {
continue;
}
other.Drop();
}
}
void projectile_teleporter::DoTeleportation() {
// upright the model, and make it reserve the space the player
// will need when he ports in, so things can't get in the way during the process
// I don't like snapping the origin & angles like this :/
setContents( CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY );
if ( !sys.isClient() ) {
owner.setTeleporterState( true );
setOrigin( portPosition );
setAngles( '0 0 0' );
setBoxClipModel( owner.getMins(), owner.getMaxs(), 10000.0f );
freeze( 1 );
}
doingTeleport = true;
vector pos = portPosition;
vector angles = owner.getViewAngles();
angles_x = angles_z = 0;
vector forward = sys.angToForward( angles );
// Play effects both for fading out at our current position and fading in at the destination
player p = owner;
boolean doneFX = false;
if ( p != $null_entity ) {
if ( p.AI_CROUCH ) {
sys.playWorldEffect( getKey( "fx_teleport_start_crouch" ), '1 1 1', owner.getWorldOrigin(), forward );
sys.playWorldEffect( getKey( "fx_teleport_end_crouch" ), '1 1 1', pos, forward );
doneFX = true;
} else
if ( p.AI_PRONE ) {
sys.playWorldEffect( getKey( "fx_teleport_start_prone" ), '1 1 1', owner.getWorldOrigin(), forward );
sys.playWorldEffect( getKey( "fx_teleport_end_prone" ), '1 1 1', pos, forward );
doneFX = true;
}
}
if ( !doneFX ) {
sys.playWorldEffect( getKey( "fx_teleport_start" ), '1 1 1', owner.getWorldOrigin(), forward );
sys.playWorldEffect( getKey( "fx_teleport_end" ), '1 1 1', pos, forward );
}
// Wait half a sec before teleporting
// And then another half a sec before showing the player again
// so there is some time to have fancy effects
hide();
owner.freeze( 1 );
float portEndTime = portTime + 0.5f;
if ( portEndTime > sys.getTime() ) {
sys.wait( portEndTime - sys.getTime() );
}
owner.hide();
float contents;
if ( !sys.isClient() ) {
forceDisableClip();
DropOwnerItems();
owner.setWorldOrigin( pos );
owner.setLinearVelocity( g_vectorZero );
// check to see if the player is now embedded in something
contents = sys.checkContents( pos, owner.getMins(), owner.getMaxs(), MASK_PLAYERSOLID, owner );
if ( contents ) {
// kill the owner
owner.applyDamage( self, owner, g_vectorZero, GetDamage( getKey( "dmg_blocked" ) ), 1.0f, $null_entity );
}
}
if ( owner.getHealth() > 0 ) {
portEndTime = portTime + 1.f;
if ( portEndTime > sys.getTime() ) {
sys.wait( portEndTime - sys.getTime() );
}
}
owner.show();
owner.freeze( 0 );
if ( !sys.isClient() ) {
// double-check to see if the player is now embedded in something
contents = sys.checkContents( pos, owner.getMins(), owner.getMaxs(), MASK_PLAYERSOLID, owner );
if ( contents ) {
// kill the owner
owner.applyDamage( self, owner, g_vectorZero, GetDamage( getKey( "dmg_blocked" ) ), 1.0f, $null_entity );
}
owner.binRemove( self );
owner = $null_entity;
OnOwnerChanged();
remove();
}
}
void projectile_teleporter::SetupCommandmapIcon() {
ClearCommandmapIcon();
commandmapIcon = sys.allocCMIcon( self, getFloatKey( "icon_sort_cm" ) );
sys.setCMIconMaterial( commandmapIcon, GetMaterial( getKey( "mtr_icon" ) ) );
sys.setCMIconFlag( commandmapIcon, CMF_ALWAYSKNOWN );
sys.setCMIconSize( commandmapIcon, getFloatKey( "icon_size_cm" ) );
}
void projectile_teleporter::ClearCommandmapIcon() {
if ( commandmapIcon != -1 ) {
sys.freeCMIcon( self, commandmapIcon );
commandmapIcon = -1;
}
}
void projectile_teleporter::OnOwnerChanged() {
if ( owner == sys.getLocalPlayer() ) {
SetupCommandmapIcon();
localIsOwner = true;
sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.teleporterActive", 1 );
} else {
ClearCommandmapIcon();
if ( localIsOwner ) {
sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.teleporterActive", 0 );
}
}
}
void projectile_teleporter::OnPortPositionChanged() {
if ( portPosition != BAD_PORT_POSITION ) {
if ( owner != $null_entity ) {
owner.teleportSucceeded = true;
}
owner.EnergyBar_Remove( chargePerUse );
thread DoTeleportation();
} else {
if ( !sys.isClient() ) {
// communicates failure to the client
failedPortTime = sys.getTime();
OnFailedPortChanged();
}
}
}
void projectile_teleporter::vActivateTeleportation() {
if ( !sys.isClient() ) {
// do a more thorough check to see if theres a position we can spawn to
FindPosition( true );
// ensure that the last valid position is still clear
float contents = sys.checkContents( lastValidPosition, owner.getMins(), owner.getMaxs(), MASK_PLAYERSOLID, self );
if ( !contents ) {
portPosition = lastValidPosition;
} else {
portPosition = BAD_PORT_POSITION;
}
portTime = sys.getTime();
OnPortPositionChanged();
}
}
void projectile_teleporter::vCancelTeleportation() {
if ( !doingTeleport && !sys.isClient() ) {
owner.binRemove( self );
owner = $null_entity;
OnOwnerChanged();
remove();
}
}
float projectile_teleporter::SweepCheck( vector worldOrigin, float radius, float startAngle, float angleIncrement, vector ownerMins, vector ownerMaxs ) {
float angle = startAngle;
float contents = 1;
vector testOrigin;
vector testAngles;
vector offset;
while ( contents != 0 && angle < 360.0f ) {
testAngles = g_vectorZero;
testAngles_y = angle;
offset = radius * 0.5f * sys.angToForward( testAngles );
testOrigin = worldOrigin + offset;
contents = sys.checkContents( testOrigin, ownerMins, ownerMaxs, MASK_PLAYERSOLID, self );
if ( !contents ) {
lastValidPosition = testOrigin;
}
angle += angleIncrement;
}
return contents;
}
void projectile_teleporter::FindPosition( boolean sweepRange ) {
if ( owner == $null_entity ) {
return;
}
vector ownerMins = owner.getMins();
vector ownerMaxs = owner.getMaxs();
vector worldOrigin = getWorldOrigin();
// do a "sweep around the entity" check
// checks at a few different positions to find a nearby empty point
vector myMins = owner.getMins();
vector myMaxs = owner.getMaxs();
vector mySize = myMaxs - myMins;
mySize_z = 0.0f;
float myDiagSize = sys.vecLength( mySize );
// rough check the centre
float contents = sys.checkContents( worldOrigin, ownerMins, ownerMaxs, MASK_PLAYERSOLID, self );
if ( !contents ) {
lastValidPosition = worldOrigin;
} else {
contents = SweepCheck( worldOrigin, myDiagSize, 0.0f, 120.0f, ownerMins, ownerMaxs );
if ( contents && sweepRange ) {
contents = SweepCheck( worldOrigin, myDiagSize, 0.0f, 120.0f, ownerMins, ownerMaxs );
if ( contents ) {
contents = SweepCheck( worldOrigin, myDiagSize, 60.0f, 120.0f, ownerMins, ownerMaxs );
if ( contents ) {
contents = SweepCheck( worldOrigin, myDiagSize, 30.0f, 120.0f, ownerMins, ownerMaxs );
if ( contents ) {
contents = SweepCheck( worldOrigin, myDiagSize, 90.0f, 120.0f, ownerMins, ownerMaxs );
}
}
}
}
}
}
void projectile_teleporter::OwnerCheck() {
if ( !sys.isClient() ) {
while ( owner == $null_entity ) {
sys.waitFrame();
}
owner.setTeleporterState( false );
while ( true ) {
//
// Check if a player can be spawned here
//
vector velocity = getLinearVelocity();
float velSqr = sys.vecLengthSquared( velocity );
if ( velSqr > 1500.0f ) {
FindPosition( false );
}
if ( velSqr < 10.f ) {
if ( !noTrail ) {
stopEffect( "fx_trail" );
noTrail = true;
}
}
// if the position is too far from the actual position of the object, ignore it
vector myOrigin = getWorldOrigin();
float distSq = sys.vecLengthSquared( myOrigin - lastValidPosition );
if ( distSq > 60.0f*60.0f ) {
lastValidPosition = BAD_PORT_POSITION;
}
//
// Check if the owner is still alive
//
if ( owner == $null_entity ) {
remove();
} else {
if ( owner.getHealth() <= 0.0f ) {
remove();
}
}
sys.waitFrame();
}
}
}
float projectile_teleporter::OnActivate( entity p, float distance ) {
float code = GetActivateCode( p, distance );
if ( code == AK_NONE ) {
return 0.f;
}
p.vSelectActionItem( code );
return 1.f;
}
float projectile_teleporter::GetActivateCode( entity p, float distance ) {
if ( p.getViewingEntity() != p || distance > DISTANCE_FOR_ACTION ) {
return AK_NONE;
}
if ( p.getHealth() <= 0 ) {
return AK_NONE;
}
float allegiance = getEntityAllegiance( p );
if ( allegiance == TA_ENEMY ) {
return AK_ARM;
}
return AK_NONE;
}
float projectile_teleporter::OnUpdateCrosshairInfo( entity p ) {
if ( !sys.doClientSideStuff() ) {
return 1.f;
}
float allegiance = getEntityAllegiance( p );
vector color = GetAllegianceColor( allegiance );
float distance = chGetDistance();
float range = InchesToMetres( distance );
chSetNumLines( 0 );
float index;
if ( p != $null_entity ) {
// see if theres a valid action to perform
float code = GetActivateCode( p, distance );
if ( code != AK_NONE && p.vHasActionItem( code ) ) {
index = chAddLine();
chSetLineMaterial( index, p.vGetActionIcon( code ) );
chSetLineType( index, CI_IMAGE );
chSetLineSize( index, 64, 64 );
chSetLineColor( index, g_colorWhite, 0.9f );
if ( p.isLocalPlayer() ) {
if ( !p.isToolTipPlaying() ) {
if ( sys.getTime() - p.getCrosshairStartTime() > 1.f ) {
if ( p.getCurrentWeapon() != p.vGetActionItem( code ) ) {
p.sendToolTip( useMeToolTip1 );
} else {
p.sendToolTip( useMeToolTip2 );
}
}
}
}
}
}
index = chAddLine();
chSetLineTextIndex( index, g_locStr_TeleportBeacon );
chSetLineColor( index, color, 1.f );
chSetLineType( index, CI_TEXT );
chSetLineSize( index, 0, 0 );
if ( range <= 100 ) {
index = chAddLine();
chSetLineText( index, G_BuildRangeStr( range ) );
chSetLineColor( index, color, 1.f );
chSetLineType( index, CI_TEXT );
chSetLineSize( index, 0, 0 );
}
return 1.f;
}
float projectile_teleporter::vGetPliersProgressBarValue( float action ) {
if ( action == AC_DISARM ) {
return disarmCurrent / disarmMaxCount;
}
return 0.f;
}
boolean projectile_teleporter::vCheckActionCode( entity p, float actionCode ) {
if ( actionCode == AC_DISARM ) {
if ( getEntityAllegiance( p ) != TA_ENEMY ) {
return false;
}
return disarmCurrent < disarmMaxCount;
}
return false;
}
void projectile_teleporter::vArm( entity p ) {
float count = 1;
team_base team = p.getGameTeam();
if ( team.HasDisarmBonus( p ) ) {
count = count * 1.25f;
}
disarmCurrent = disarmCurrent + count;
if ( sys.isClient() ) {
return;
}
if ( disarmCurrent >= disarmMaxCount ) {
if ( owner != $null_entity ) {
if ( !sys.isClient() ) {
sys.broadcastToolTip( destroyedToolTip, owner, wstr_empty, wstr_empty, wstr_empty, wstr_empty );
}
}
remove();
}
}