/*********************************************************************** projectile_landmine.script ***********************************************************************/ #define LM_NONE 0 #define LM_TRAP 1 #define LM_PROX 2 #define BEAM_START_OFFSET -0.25f object projectile_landmine : projectile_armable { void preinit(); void syncFields(); void destroy(); void vSetOwner( entity other ); void OnTouch( entity other, object traceObject ); void OnKilled( entity inflictor, entity attacker, float damage, vector direction, float location ); float OnUpdateCrosshairInfo( entity p ); float OnActivate( entity p, float distance ); float GetActivateCode( entity p, float distance ); void OnTriggerTimeChanged(); void OnStick( entity collisionEnt, vector collisionNormal, string surfaceType, string joint ); boolean vGetIsSelfArm(); void vSetSpotTime( float time ); void vOnTargetDestroyed(); void vOnContextDisarm( entity p ); void vMakeSticky( entity collisionEnt, vector collisionNormal, string surfaceType, boolean _allowTripmine, string joint ); void CheckTrapAgainst( entity triggerer ); void OnModeChanged(); void BotOnExplode(); float vGetPliersProgressBarValue( float action ); void Idle(); void CheckTrigger(); void CheckDetonate(); void DoRadiusDamage(); void OnDisarmed( entity p ); void OnArmed( entity p ); void OnSelfArmed( entity p ); float EnemyCount(); float EnemyInFrontCount(); void ArmEffects(); void UpdateFlag(); void ShowIcon(); void HideIcon(); void Trigger( boolean newState ); float OnCollide( object traceObject, vector velocity, float bodyId ); boolean vRepairDroneIgnoreHidden(); void vApplyEmpDamage( entity attacker, float time, float weaponTime ); void DisarmFizzleThread(); void KillDisarmFizzleThread(); void SetupWorldIcon(); boolean worldIconSet; float disarmProficiency; float range; float armDelay; float mode; float damageFilterIndex; float commandMapIcon; float lastSpotTime; float beamRange; float beamLength; float beamHandle; float activateTime; float triggerTime; entity owner; entity vGetOwner() { return owner; } float showBeam; float creationTime; float vGetMineCreationTime() { return creationTime; } boolean allowTripmine; boolean playTriggerEffect; float detonateDelay; float disableProficiency; float selfArmDelay; boolean selfArmIsArming; } void projectile_landmine::syncFields() { syncBroadcast( "beamLength" ); syncBroadcast( "activateTime" ); syncBroadcast( "triggerTime" ); syncBroadcast( "mode" ); syncBroadcast( "owner" ); syncBroadcast( "selfArmIsArming" ); syncCallback( "mode", "OnModeChanged" ); syncCallback( "triggerTime", "OnTriggerTimeChanged" ); } void projectile_landmine::preinit() { mode = LM_NONE; range = getFloatKey( "range" ); ScheduleFizzle( getFloatKeyWithDefault( "fizzle_time", 30 ) ); disarmProficiency = GetProficiency( getKey( "prof_disarm" ) ); beamRange = GetGlobalFloat( "tripmine_beam_range" ); beamHandle = -1; commandMapIcon = -1; damageFilterIndex = GetTargetFilter( getKey( "ti_target_filter" ) ); creationTime = sys.getTime(); playTriggerEffect = true; detonateDelay = getFloatKey( "prox_detonate_delay" ); selfArmDelay = getFloatKey( "self_arm_delay" ); disableProficiency = GetProficiency( getKey( "prof_disable" ) ); disableKnockback(); } void projectile_landmine::destroy() { HideIcon(); if ( beamHandle != -1 ) { freeBeam( beamHandle ); beamHandle = -1; } if ( owner != $null_entity ) { owner.binRemove( self ); } KillDisarmFizzleThread(); sys.killThread( "OnSelfArmed_" + getName() ); } void projectile_landmine::Idle() { while ( true ) { sys.waitFrame(); UpdateFlag(); CheckTrigger(); CheckDetonate(); } } void projectile_landmine::OnModeChanged() { if ( mode != LM_NONE ) { stuck = true; freeze( 1.f ); clearContacts(); putToRest(); } } void projectile_landmine::SetupWorldIcon() { if ( mode == LM_PROX && !worldIconSet ) { if ( g_drawMineIcons.getBoolValue() && g_allowMineIcons.getBoolValue() ) { float size = g_mineIconSize.getFloatValue(); float cutoff = getFloatKeyWithDefault( "world_icon_cutoff_dist", 512 ); setIconEnabled( true ); setIconMaterial( "mtr_worldicon" ); setIconSize( size, size ); setIconColorMode( EI_TEAM ); setIconPosition( EI_CENTER ); setIconCutoff( cutoff ); setIconAlphaScale( g_mineIconAlphaScale.getFloatValue() ); worldIconSet = true; } } } void projectile_landmine::OnTriggerTimeChanged() { if ( triggerTime != 0 ) { if ( mode == LM_PROX ) { // slow beep if( playTriggerEffect ) { startSound( "snd_trigger", SND_WEAPON_ARM ); playEffect( "fx_trigger", "", false ); playTriggerEffect = false; player lPlayer = sys.getLocalViewPlayer(); if ( lPlayer != $null_entity ) { vector dir = lPlayer.getWorldOrigin() - getWorldOrigin(); if ( sys.vecLength( dir ) < range * 1.3 ) { lPlayer.vMineWarning( self ); } } } } else { // } } else { // stop beeping sound } } void projectile_landmine::Trigger( boolean newState ) { if ( newState ) { triggerTime = sys.getTime(); } else { triggerTime = 0; } OnTriggerTimeChanged(); } void projectile_landmine::ShowIcon() { player lPlayer; object team; lPlayer = sys.getLocalPlayer(); if ( lPlayer == $null_entity ) { return; } if( commandMapIcon == -1 ) { team = getGameTeam(); commandMapIcon = sys.allocCMIcon( self, getFloatKey( "icon_sort_cm" ) ); string mat = "mtr_icon_friendly"; if ( lPlayer.getGameTeam() != team ) { mat = "mtr_icon_enemy"; } sys.setCMIconMaterial( commandMapIcon, GetMaterial( getKey( mat ) ) ); sys.setCMIconSize( commandMapIcon, 10.f ); sys.setCMIconFlag( commandMapIcon, CMF_ALWAYSKNOWN ); } } void projectile_landmine::HideIcon() { if( commandMapIcon != -1 ) { sys.freeCMIcon( self, commandMapIcon ); commandMapIcon = -1; } } void projectile_landmine::UpdateFlag() { entity localPlayer = sys.getLocalPlayer(); if( localPlayer == $null_entity ) { return; } float oldShowBeam; boolean isFriend = getEntityAllegiance( localPlayer ) == TA_FRIEND; oldShowBeam = showBeam; if ( armTime == 0 ) { showBeam = false; show(); HideIcon(); } else { showBeam = true; show(); if ( isFriend || ( sys.getTime() - lastSpotTime ) < 1.f ) { ShowIcon(); } else { HideIcon(); } setSkin( "" ); SetupWorldIcon(); } if( oldShowBeam != showBeam ) { if ( mode == LM_PROX ) { if( showBeam ) { startSound( "snd_beep", SND_WEAPON_SIG ); } else { stopSound( SND_WEAPON_SIG ); } } } if ( showBeam ) { if ( mode == LM_TRAP ) { if ( beamHandle == -1 ) { beamHandle = allocBeam( getKey( "mtr_beam" ) ); } vector beamDir = getWorldAxis( 2 ); vector beamStartPos = getWorldOrigin() + ( beamDir * BEAM_START_OFFSET ); vector beamColor; if ( isFriend ) { beamColor = g_colorGreen; } else { beamColor = g_colorRed; } updateBeam( beamHandle, beamStartPos, beamStartPos + ( beamDir * beamLength ), beamColor, 1.f, .5f ); } } else { if ( beamHandle != -1 ) { freeBeam( beamHandle ); beamHandle = -1; } } } void projectile_landmine::ArmEffects() { if ( mode == LM_NONE ) { return; } SetupContents(); startSound( "snd_armed", SND_WEAPON_ARM ); if ( mode == LM_TRAP ) { if ( !sys.isClient() ) { setCanCollideWithTeam( 0.f ); vector beamDir = getWorldAxis( 2 ); vector beamStartPos = getWorldOrigin() + ( beamDir * BEAM_START_OFFSET ); float fraction = sys.tracePoint( beamStartPos, beamStartPos + ( beamDir * beamRange ), CONTENTS_SOLID | CONTENTS_RENDERMODEL, self ); // don't allow trip mine if the trace hit an entity // as we can't determine the actual beam length. if ( fraction == 1.f || sys.getTraceEntity() != sys.getEntity( "worldspawn" ) ) { mode = LM_PROX; } else { beamLength = fraction * beamRange; } activateTime = sys.getTime() + 2.f; setCanCollideWithTeam( 1.f ); } } SetupWorldIcon(); } float projectile_landmine::EnemyCount() { entitiesInRadius( g_vectorZero, range, -1, 0 ); filterEntitiesByFilter( damageFilterIndex, 1 ); float aliveCount = 0; float count = filterEntitiesByDisguiseAllegiance( TA_FLAG_ENEMY, 1 ); float index; for ( index = 0; index < count; index++ ) { entity e = getBoundsCacheEntity( index ); if ( e.getHealth() <= 0 ) { continue; } // don't have empty vehicles set off mines vehicle_base v = e; if ( v != $null_entity ) { if ( v.isEmpty() ) { continue; } } aliveCount++; } return aliveCount; } float projectile_landmine::EnemyInFrontCount() { entitiesInRadius( g_vectorZero, range, -1, 0 ); filterEntitiesByFilter( damageFilterIndex, 1 ); float aliveCount = 0; float count = filterEntitiesByDisguiseAllegiance( TA_FLAG_ENEMY, 1 ); float index; for ( index = 0; index < count; index++ ) { entity e = getBoundsCacheEntity( index ); if ( e.getHealth() <= 0 ) { continue; } // don't have empty vehicles set off mines vehicle_base v = e; if ( v != $null_entity ) { if ( v.isEmpty() ) { continue; } } // check if in front vector myForwards = getWorldAxis( 2 ); vector myPos = getWorldOrigin() - myForwards * 2; vector delta = sys.vecNormalize( e.getWorldOrigin() - myPos ); if ( myForwards * delta <= 0.0f ) { continue; } aliveCount++; } return aliveCount; } void projectile_landmine::CheckTrigger() { if ( triggerTime != 0 || armTime == 0 ) { return; } if ( mode == LM_PROX ) { if ( EnemyInFrontCount() == 0 ) { return; } } else if ( mode == LM_TRAP ) { if ( activateTime > sys.getTime() ) { return; } } Trigger( true ); } void projectile_landmine::CheckDetonate() { if ( triggerTime == 0 ) { return; } if ( mode == LM_PROX ) { if ( ( sys.getTime() - triggerTime ) < detonateDelay ) { return; } Explode( $null_entity, $null_entity ); Trigger( false ); return; } if ( mode == LM_TRAP ) { vector beamDir = getWorldAxis( 2 ); vector beamStartPos = getWorldOrigin() + ( beamDir * BEAM_START_OFFSET ); setCanCollideWithTeam( 0.f ); float count = entitiesInBounds( beamStartPos, beamStartPos + ( beamDir * beamLength ), CONTENTS_MONSTER | CONTENTS_VEHICLECLIP | CONTENTS_BODY | CONTENTS_SLIDEMOVER, 1.f ); setCanCollideWithTeam( 1.f ); float i; for ( i = 0; i < count; i++ ) { CheckTrapAgainst( getBoundsCacheEntity( i ) ); } } } void projectile_landmine::CheckTrapAgainst( entity triggerer ) { boolean trigger = false; player p = triggerer; vehicle_base vehicle = triggerer; if ( p == $null_entity && vehicle == $null_entity ) { return; } float allegiance = getEntityAllegiance( triggerer ); float disguiseAllegiance = allegiance; if ( triggerer != $null_entity ) { if ( triggerer.vTriggerTripmine() ) { player disguised = triggerer.getDisguiseClient(); if ( disguised != $null_entity ) { disguiseAllegiance = getEntityAllegiance( disguised ); } } } if ( allegiance == TA_ENEMY && disguiseAllegiance == TA_ENEMY ) { Explode( $null_entity, $null_entity ); Trigger( false ); } } void projectile_landmine::OnArmed( entity p ) { stopSound( SND_ITEM ); if ( !sys.isClient() ) { selfArmIsArming = false; } ProjectileArmable_OnArmed( p ); ArmEffects(); CancelFizzle(); if ( !sys.isClient() ) { if ( mode == LM_NONE ) { sys.warning( "projectile_landmine::Arm No Mode Set\n" ); mode = LM_PROX; } player myPlayer = owner; if ( myPlayer != $null_entity ) { myPlayer.binAdd( self ); myPlayer.setPlayerMineArmed( true, self, true ); //mal: let the bots know this mine has been armed, and is dangerous. } } } void projectile_landmine::OnSelfArmed( entity p ) { selfArmIsArming = true; sys.assert( selfArmDelay - 2.f > 0 ); sys.wait( 2.f ); startSound( "snd_arm_auto", SND_ITEM ); sys.wait( selfArmDelay - 2.f ); if ( armTime == 0.0f ) { doPostArmSwitch = false; OnArmed( p ); } } void projectile_landmine::OnDisarmed( entity p ) { ProjectileArmable_OnDisarmed( p ); if ( getEntityAllegiance( p ) == TA_ENEMY ) { p.giveProficiency( disarmProficiency, 1.f, $null, "disarmed mine" ); } if ( !sys.isClient() ) { player myPlayer = owner; if ( myPlayer != $null_entity ) { myPlayer.binRemove( self ); myPlayer.setPlayerMineArmed( false, self, false ); //mal: thats one less mine for the bots to worry about. } } thread DisarmFizzleThread(); } void projectile_landmine::OnTouch( entity other, object traceObject ) { } void projectile_landmine::vSetSpotTime( float time ) { lastSpotTime = time; } void projectile_landmine::vOnContextDisarm( entity p ) { InitDisarmTask(); } float projectile_landmine::vGetPliersProgressBarValue( float action ) { if ( action == AC_ARM ) { if ( armTime != 0 ) { return 1.f; } return countCurrent / targetCount; } if ( action == AC_DISARM ) { if ( disarmTime != 0 ) { return 1.f; } return countCurrent / targetCount; } return 0.f; } boolean projectile_landmine::vRepairDroneIgnoreHidden() { return true; } void projectile_landmine::vApplyEmpDamage( entity attacker, float time, float weaponTime ) { if ( armTime != 0 ) { if ( disableProficiency != -1 ) { attacker.giveProficiency( disableProficiency, 1.f, $null, "emp on mine" ); } } OnDisarmed( $null_entity ); } float projectile_landmine::OnUpdateCrosshairInfo( entity p ) { if ( !sys.doClientSideStuff() ) { return 1.f; } float allegiance = getEntityAllegiance( p ); vector color = GetAllegianceColor( allegiance ); float distance = chGetDistance(); float range = InchesToMetres( distance ); float health = getHealth(); 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 ); CheckContextToolTip( allegiance, code, p ); } } index = chAddLine(); if ( selfArmIsArming ) { if ( mode == LM_TRAP ) { chSetLineTextIndex( index, g_locStr_SelfArmingTripmine ); } else { chSetLineTextIndex( index, g_locStr_SelfArmingProxymine ); } } else if ( armTime == 0 ) { if ( mode == LM_TRAP ) { chSetLineTextIndex( index, g_locStr_UnarmedTripmine ); } else { chSetLineTextIndex( index, g_locStr_UnarmedProxymine ); } } else { if ( mode == LM_TRAP ) { chSetLineTextIndex( index, g_locStr_Tripmine ); } else /* if ( mode == LM_PROX ) */ { chSetLineTextIndex( index, g_locStr_Proxmine ); } } 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; } void projectile_landmine::OnKilled( entity inflictor, entity attacker, float damage, vector direction, float location ) { if ( owner == sys.getLocalPlayer() ) { sys.setGUIString( GUI_GLOBALS_HANDLE, "gameHud.bumpNotifyIcon", "mines" ); } Explode( $null_entity, $null_entity ); } void projectile_landmine::DoRadiusDamage() { if ( mode == LM_TRAP ) { splashDamageIndex = GetDamage( getKey( "dmg_splash_damage_trap" ) ); } else if ( mode == LM_PROX ) { splashDamageIndex = GetDamage( getKey( "dmg_splash_damage_prox" ) ); } if ( splashDamageIndex != -1 && !sys.isClient() ) { float power = 1.f; if ( armTime == 0 ) { power = 0.25f; } sys.applyRadiusDamage( getWorldOrigin(), self, owner, radiusDamageIgnoreEntity, self, splashDamageIndex, power, 1.f ); } } void projectile_landmine::vSetOwner( entity other ) { owner = other; if ( owner == $null_entity ) { Fizzle(); } } void projectile_landmine::vMakeSticky( entity collisionEnt, vector collisionNormal, string surfaceType, boolean _allowTripmine, string joint ) { allowTripmine = _allowTripmine; OnStick( collisionEnt, collisionNormal, surfaceType, joint ); } boolean projectile_landmine::vGetIsSelfArm() { if ( owner != $null_entity ) { team_base team = owner.getGameTeam(); if ( team != $null ) { if ( team.HasSelfArmingMines( owner ) ) { return true; } } } return false; } void projectile_landmine::OnStick( entity collisionEnt, vector collisionNormal, string surfaceType, string joint ) { ProjectileMissile_OnStick( collisionEnt, collisionNormal, surfaceType, joint ); if ( !sys.isClient() ) { if ( allowTripmine ) { mode = LM_TRAP; } else { mode = LM_PROX; } if ( vGetIsSelfArm() ) { thread OnSelfArmed( owner ); } setClipOriented( true ); } } float projectile_landmine::OnActivate( entity p, float distance ) { float allegiance = getEntityAllegiance( p ); team_base team = p.getGameTeam(); float code = GetActivateCode( p, distance ); if ( code == AK_NONE ) { return 0.0f; } p.vSelectActionItem( code ); return 1.f; } float projectile_landmine::GetActivateCode( entity p, float distance ) { if ( p.getViewingEntity() != p || distance > DISTANCE_FOR_ACTION ) { return AK_NONE; } if ( p.getHealth() <= 0 ) { return AK_NONE; } if ( !stuck ) { return AK_NONE; } float allegiance = getEntityAllegiance( p ); if ( allegiance == TA_ENEMY ) { if ( armTime == 0 ) { return AK_NONE; } return AK_ARM; } else if ( p == owner ) { return AK_ARM; } if ( armTime == 0 ) { return AK_ARM; } return AK_NONE; } // NOTE: If this returns true, all momentum on the object will be cleared, otherwise, it will bounce float projectile_landmine::OnCollide( object traceObject, vector velocity, float bodyId ) { float shaderFlags; entity collisionEnt; if ( stuck ) { return true; } shaderFlags = traceObject.getTraceSurfaceFlags(); if ( shaderFlags & SURF_NOIMPACT || shaderFlags & SURF_NOPLANT ) { return false; } collisionEnt = traceObject.getTraceEntity(); if ( collisionEnt.vDisablePlantCharge() ) { return false; } // push the view out of the surface a bit vector normal = traceObject.getTraceNormal(); // align to the surface normal alignToAxis( normal, Z_AXIS ); // move to surface setWorldOrigin( traceObject.getTracePoint() ); if ( !sys.isClient() ) { OnStick( collisionEnt, normal, traceObject.getTraceSurfaceType(), traceObject.getTraceJoint() ); } return true; } void projectile_landmine::DisarmFizzleThread() { playEffect( "fx_disarm", "", false ); sys.wait( startSound( "snd_disarmed", SND_WEAPON_DISARM ) ); Fizzle(); } void projectile_landmine::KillDisarmFizzleThread() { sys.killThread( "DisarmFizzleThread_" + getName() ); } void projectile_landmine::BotOnExplode() { //mal: this mine has exploded, let its owner know. player p = owner; if ( p != $null_entity ) { p.setPlayerMineState( self, true, false ); } } void projectile_landmine::vOnTargetDestroyed() { Fizzle(); }