Script:Files:script/projectiles/landmine.script

From Mod Wiki
Revision as of 09:52, 5 November 2007 by Wizz (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
/***********************************************************************

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        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();

    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::OnTriggerTimeChanged() {
    if ( triggerTime != 0 ) {
        if ( mode == LM_PROX ) {
            // slow beep
            if( playTriggerEffect ) {
                startSound( "snd_trigger", SND_WEAPON_ARM );
                playEffect( "fx_trigger", "", false );
                playTriggerEffect = false;
            }
         } 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( "" );
    }

    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 );
        }
    }
}

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.getLocalPlayer() == $null_entity ) {
        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;

    // 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 ) {
        return AK_NONE;
    }

    if ( p.getHealth() <= 0 ) {
        return AK_NONE;
    }

    if ( !stuck ) {
        return AK_NONE;
    }

    float allegiance = getEntityAllegiance( p );

    if ( allegiance == TA_ENEMY ) {
        if ( distance > 192.0f ) {
            return AK_NONE;
        }

        if ( armTime == 0 ) {
            return AK_NONE;
        }

        return AK_ARM;
    } else if ( p == owner ) {
        if ( distance > DISTANCE_FOR_ACTION ) {
            return AK_NONE;
        }

        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 );
    }
}