Script:Files:script/deployables/antipersonnel.script

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

deployable_antipersonnel - anti-personnel turret

===============================================================================
*/

object deployable_antipersonnel : deployable_turret {
    entity        OnAcquireTarget();

    boolean        TargetIsValid( entity targetEnt );

    void        preinit();
    void        syncFields();

    void        OnPostDamage_AP( entity attacker, float oldHealth, float newHealth );
    void        OnPostDamage( entity attacker, float oldHealth, float newHealth ) {
        OnPostDamage_AP( attacker, oldHealth, newHealth );
    }
    void        CheckRetribution( entity attacker );

    float        playerClass;

    void        OnFullChargeTimeChanged();

    boolean        ShouldSpinBarrel();

    void        OnBeginAttack();
    void        OnEndAttack();
    void        SpinBarrelThread();

    boolean        CanFire();
    void        Fired();
    void        FireMissile();

    void        UpdateCharge();

    float        fullChargeTime;
    float        cooldownTime;
    float        chargeTime;
    float        chargePerShot;
    float        overheatPenalty;

    float        spinBarrelJoint;
    vector        barrelAngles;

    float        tracerCounter;
    float        tracerInterval;

    boolean        barrelThreadActive;

    boolean        updateChargeThreadActive;
}

void deployable_antipersonnel::preinit() {
    playerClass = sys.getTypeHandle( "idPlayer" );

    fullChargeTime = -1;
    cooldownTime = -1;
    chargeTime = getFloatKeyWithDefault( "charge_time", 10 );
    chargePerShot = getFloatKeyWithDefault( "charge_per_shot", 0.7 );
    overheatPenalty = getFloatKeyWithDefault( "overheat_penalty", 5 );

    spinBarrelJoint = -1;

    string barrelJointName = getKey( "joint_spin_barrel" );
    if ( barrelJointName != "" ) {
        spinBarrelJoint = getJointHandle( barrelJointName );
    }

    tracerCounter            = 0;
    tracerInterval            = sys.getEntityDefIntKey( projectileIndex, "tracer_interval" );
}

void deployable_antipersonnel::syncFields() {
    syncBroadcast( "cooldownTime" );

    sync( "fullChargeTime" );
    syncCallback( "fullChargeTime", "OnFullChargeTimeChanged" );
}

void deployable_antipersonnel::OnFullChargeTimeChanged() {
    if ( !updateChargeThreadActive ) {
        if ( myUser == sys.getLocalPlayer() ) {
            thread UpdateCharge();
        }
    }
}

void deployable_antipersonnel::CheckRetribution( entity attacker ) {
    if ( !TargetIsValid( attacker ) ) {
        return;
    }

    if ( !InRange( attacker.getWorldOrigin() ) ) {
        return;
    }

    setEnemy( attacker );
}

void deployable_antipersonnel::OnPostDamage_AP( entity attacker, float oldHealth, float newHealth ) {
    boolean oldDisabled = disabledState;
    UpdateState( newHealth );

    if ( !disabledState ) {
        if ( getEnemy() == $null_entity && newHealth < oldHealth ) {
            CheckRetribution( attacker );
        }
    }

    UpdateDisabledStats( attacker, oldDisabled );
}

boolean deployable_antipersonnel::TargetIsValid( entity targetEnt ) {
    player testPlayer = targetEnt;

    if ( testPlayer == $null_entity ) {
        return false;
    }

    if ( testPlayer.getHealth() <= 0 ) {
        return false;
    }

    if ( testPlayer.getVehicle() != $null_entity ) {
        return false;
    }

    if ( testPlayer.isDisguised() ) {
        return false;
    }

    if ( getEntityAllegiance( testPlayer ) != TA_ENEMY ) {
        return false;
    }

    if ( testPlayer.isInvulnerable() ) {
        return false;
    }

    if ( !TraceCheck( testPlayer ) ) {
        return false;
    }

    return true;
}

entity deployable_antipersonnel::OnAcquireTarget() {
    if ( !finishedDeploying || disabledState ) {
        return $null_entity;
    }

    float i;
    float count;
    entity ent;
    entity targetingRangeEnt;
    vector targetDir;
    vector selfDir;

    entitiesOfClass( playerClass, 0 );
    filterEntitiesByDisguiseAllegiance( TA_FLAG_ENEMY, 1 );                // only damage enemies
    filterEntitiesByRadius( getWorldOrigin(), maxRange, 1 );        // find entities within maxRange radius

    selfDir = getWorldAxis( 0 );
    selfDir_z = 0;

    count = getBoundsCacheCount(); // number of remaining entities
    for ( i = 0; i < count; i++ ) {
        ent = getBoundsCacheEntity( i );

        targetDir = sys.vecNormalize( ent.getWorldOrigin() - getWorldOrigin() );
        targetDir_z = 0;

        if ( targetDir * selfDir < angleRange ) {
            continue;
        }

        if ( !TargetIsValid( ent ) ) {
            continue;
        }

        if ( !InRange( ent.getWorldOrigin() ) ) {
            continue;
        }
        if ( InFiringRange( ent.getWorldOrigin() ) ) {
            return ent;
        }

        if ( targetingRangeEnt == $null_entity ) {
            targetingRangeEnt = ent;
        }
    }

    return targetingRangeEnt;
}

void deployable_antipersonnel::UpdateCharge() {
    sys.assert( false );

    updateChargeThreadActive = true;

    while ( true ) {
        if ( myUser != sys.getLocalPlayer() ) {
            break;
        }

        float time = sys.getTime();

        float chargeAmount = ( chargeTime - ( fullChargeTime - time ) ) / chargeTime;
        if ( chargeAmount > 1 ) {
            chargeAmount = 1;
        }

        sys.setGUIFloat( GUI_GLOBALS_HANDLE, "weapons.energyBarCharge", chargeAmount );
        sys.setGUIFloat( GUI_GLOBALS_HANDLE, "weapons.energyAvailable", time >= cooldownTime );

        if ( chargeAmount == 1.f ) {
            break;
        }

        sys.waitFrame();
    }

    updateChargeThreadActive = false;
}

boolean deployable_antipersonnel::CanFire() {
    if ( myUser == $null_entity ) {
        return true;
    }

    float time = sys.getTime();
    float chargeAmount = ( chargeTime - ( fullChargeTime - time ) ) / chargeTime;
    if ( chargeAmount >= chargePerShot && time > cooldownTime ) {
        return true;
    }

    return false;
}

void deployable_antipersonnel::Fired() {
    if ( myUser == $null_entity ) {
        return;
    }

    if ( !sys.isClient() ) {
        float timePerShot = chargeTime * chargePerShot;

        if ( fullChargeTime < sys.getTime() ) {
            fullChargeTime = sys.getTime();
        }
        fullChargeTime = fullChargeTime + timePerShot;

        float time = sys.getTime();
        if ( fullChargeTime >= time + chargeTime - timePerShot ) {
            cooldownTime = sys.getTime() + overheatPenalty;
        }
    }

    if ( !updateChargeThreadActive ) {
        if ( myUser == sys.getLocalPlayer() ) {
            thread UpdateCharge();
        }
    }
}

void deployable_antipersonnel::FireMissile() {
    float muzzleJoint;

    float forceTracer = TRACER_CHANCE;
    if ( tracerInterval > 0 ) {
        if ( tracerCounter % tracerInterval == 0 ) {
            forceTracer = TRACER_FORCE;
        }
    }

    entity controller = owner;
    if ( controller == $null_entity ) {
        controller = self;
    }

    launchBullet( controller, self, projectileIndex, spread, getJointPos( jointBarrel ), getJointAxis( jointBarrel, 0 ), forceTracer, false ); // hit scan
    tracerCounter++;

    playJointEffect( "fx_fire", jointBarrel, 0 );

    if( numTracerJoints > 1 ) {
        muzzleJoint = int( sys.random( numTracerJoints ) ) + 1;
    } else {
        muzzleJoint = numTracerJoints;
    }
    playEffect( "fx_muzzle", getKey( "joint_tracer" + muzzleJoint ), 0 );

    Fired();
}

void deployable_antipersonnel::OnBeginAttack() {
    KillAttackThread();
    thread AttackThread();

    attacking = true;

    if ( spinBarrelJoint != -1 && !barrelThreadActive ) {
        thread SpinBarrelThread();
    }

    playCycle( ANIMCHANNEL_ALL, "fire" );
}

void deployable_antipersonnel::OnEndAttack() {
    attacking = false;

    playAnim( ANIMCHANNEL_ALL, "base" );

    KillAttackThread();
}

boolean deployable_antipersonnel::ShouldSpinBarrel() {
    return attacking && sys.getTime() > cooldownTime;
}

void deployable_antipersonnel::SpinBarrelThread() {
    barrelThreadActive = true;

    float speed = 0.f;
    float maxSpeed = 900;

    float frameTime;

    while ( attacking ) {
        if ( ShouldSpinBarrel() ) {
            startSound( "snd_fire_start", SND_DEPLOYABLE_FIRE );
            startSound( "snd_fire_far", SND_DEPLOYABLE_FIRE_FAR );
            startSound( "snd_brass", SND_DEPLOYABLE_BRASS );

            while ( ShouldSpinBarrel() ) {
                frameTime = sys.getFrameTime();

                speed = speed + ( frameTime * 600 );
                if ( speed > maxSpeed ) {
                    speed = maxSpeed;
                }

                barrelAngles_y = ( barrelAngles_y + ( frameTime * speed ) ) % 360;
                setJointAngle( spinBarrelJoint, JOINTMOD_LOCAL, barrelAngles );

                sys.waitFrame();
            }

            startSound( "snd_brass_stop", SND_DEPLOYABLE_BRASS );
            startSound( "snd_fire_stop", SND_DEPLOYABLE_FIRE );
            startSound( "snd_fire_far_stop", SND_DEPLOYABLE_FIRE_FAR );
        }

        while ( !ShouldSpinBarrel() ) {
            sys.waitFrame();

            frameTime = sys.getFrameTime();

            speed = speed - ( frameTime * 600 );
            if ( speed < 0 ) {
                speed = 0;
                break;
            }

            barrelAngles_y = ( barrelAngles_y + ( frameTime * speed ) ) % 360;
            setJointAngle( spinBarrelJoint, JOINTMOD_LOCAL, barrelAngles );
        }
    }

    barrelThreadActive = false;
}

/*
===============================================================================

deployable_antipersonnel_salvage - anti-personnel turret that can't be destroyed
									starts with zero health though

===============================================================================
*/
object deployable_antipersonnel_salvage : deployable_antipersonnel {
    void    init();

    void    UpdateState( float health );
    void    CreateDestroyThread() { }
    void    OnPostDamage( entity attacker, float oldHealth, float newHealth );
};

void deployable_antipersonnel_salvage::init() {
    setHealth( 1 );
    UpdateState( 1 );
}

void deployable_antipersonnel_salvage::OnPostDamage( entity attacker, float oldHealth, float newHealth ) {
    if ( newHealth <= 0 ) {
        setHealth( 1 );
        newHealth = 1;
    }

    OnPostDamage_AP( attacker, oldHealth, newHealth );
}

void deployable_antipersonnel_salvage::UpdateState( float health ) {
    float frac = health / getMaxHealth();

    float newState;

    player p = sys.getLocalPlayer();

    boolean showHealthStatus = false;
    if ( p != $null_entity ) {
        team_base team = p.getGameTeam();
        float allegiance = getEntityAllegiance( p );
        if ( allegiance == TA_FRIEND ) {
            showHealthStatus = true;
        }
    }

    float healthStatusParm = 0;

    if ( frac < ( 2.f / 3.f ) ) {
        newState = DS_DAMAGED;
        healthStatusParm = frac * 2;
    } else {
        newState = DS_NORMAL;
    }

    if ( showHealthStatus && !disabledState ) {
        sys.setCMIconShaderParm( commandMapHandle, 5, healthStatusParm );
    }

    SetDeployableState( newState );
}