Script:Files:script/misc/supply crate.script

From Mod Wiki
object supply_crate {
    void                preinit();
    void                syncFields();
    void                init();
    void                destroy();

    float                OnUpdateCrosshairInfo( entity p );
    void                OnKilled( entity inflictor, entity attacker, float damage, vector direction, float location );

    void                vSetDeployableOwner( entity p );
    void                OnOwnerChanged();

    void                vOnDeploy();
    void                OnDeployedChanged() { vOnDeploy(); }


    void                Idle();
    void                Supply();

    void                LogLifeTime();

    void                SetupCommandMap();

    void                UpdateGUI();

    void                Fizzle();
    void                RemoveThread( float removeTime );
    void                vRemoveObject() { Fizzle(); }
    boolean                vIsSupplyDrop() { return true; }
    float                vGetSupplyDropCreationTime();

    boolean                vBlockVehicleSpawn() { return true; }

    void                vOnBindMasterDestroyed();

    void                BoundsKillThread();
    void                DropSoundThread();

    void                HideParachute();

    void                OnFizzleChanged();
    void                OnSupplyCountChanged();

    void                vBindToEntity( entity ent );

    boolean                vDropItemTrace( vector tStart, vector tEnd, entity passEntity, vector targetPos );
    boolean                ValidDropTrace( vector originalPos );
    boolean                vAllowDrop();

    float                GetSupplyCrateIndex();
    float                GetSupplyCrateCount();

    float                supplyCount;
    float                supplyMax;
    float                lastSupplyCount;

    float                creationTime;

    float                repairRange;
    float                commandMapHandle;
    float                commandMapHandleEnemy;

    string                packageName;

    player                owner;
    entity                vGetOwner() { return owner; }

    handle                objectName;

    float                usedProficiency;

    handle                statUsed;
    handle                statTimeUsed;
    handle                statNumDestroyed;

    boolean                removeThreadActive;
    boolean                deployed;
    boolean                fizzle;

    entity                bindEntity;

    boolean                allowDrop;
}

void supply_crate::syncFields() {
    syncBroadcast( "supplyCount" );
    syncBroadcast( "owner" );
    syncBroadcast( "deployed" );
    syncBroadcast( "fizzle" );

    syncCallback( "owner", "OnOwnerChanged" );
    syncCallback( "deployed", "OnDeployedChanged" );
    syncCallback( "fizzle", "OnFizzleChanged" );
    syncCallback( "supplyCount", "OnSupplyCountChanged" );
}

void supply_crate::preinit() {
    repairRange            = MetresToInches( getFloatKeyWithDefault( "repair_range", 2.5f ) );
    packageName            = getKey( "pck_items" );
    supplyMax            = getFloatKeyWithDefault( "max_supply", 50.f );
    supplyCount            = supplyMax;
    lastSupplyCount        = supplyMax;

    objectName            = sys.localizeString( getKey( "object_name" ) );

    usedProficiency        = GetProficiency( getKey( "prof_used" ) );

    creationTime        = sys.getTime();

    commandMapHandle    = -1;
    commandMapHandleEnemy = -1;

    statUsed            = -1;
    statTimeUsed        = -1;
    statNumDestroyed    = -1;

    string statName        = getKey( "stat_name" );
    if ( statName != "" ) {
        statUsed            = sys.allocStatInt( statName + "_uses" );
        statTimeUsed        = sys.allocStatInt( statName + "_time_used" );
        statNumDestroyed    = sys.allocStatInt( statName + "_killed" );
    }

    creationTime        = sys.getTime();

    setContents( CONTENTS_RENDERMODEL | CONTENTS_PLAYERCLIP | CONTENTS_VEHICLECLIP | CONTENTS_FLYERHIVECLIP );
}

void supply_crate::init() {
    SetupCommandMap();

    if ( !sys.isClient() ) {
        owner.binAdd( self );
        player p = owner;
        if ( p != $null_entity ) {
            p.setPlayerSupplyCrateState( self, false );
        }
    }

    setState( "Idle" );
}

void supply_crate::destroy() {
    sys.freeCMIcon( self, commandMapHandle );
    sys.freeCMIcon( self, commandMapHandleEnemy );

    LogLifeTime();

    sys.killThread( "RemoveThread_" + getName() );

    if ( !sys.isClient() ) {
        player p = owner;
        if ( p != $null_entity ) {
            p.setPlayerSupplyCrateState( self, true );
        }
    }

    if( owner == sys.getLocalPlayer() ) {
        sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.supplyCrateHealth0", 0 );
        sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.supplyCrateSupplies0", 0 );

        float count = GetSupplyCrateCount();
        if ( count < 2 ) {
            sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.supplyCrateHealth1", 0 );
            sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.supplyCrateSupplies1", 0 );
        }
    }

    if ( owner != $null_entity ) {
        owner.binRemove( self );
    }
}

void supply_crate::vOnDeploy() {
    if ( !sys.isClient() ) {
        deployed = true;

        G_GiveDeployBonus( owner, self );
    }

    float hitGroundEffect = getFloatKey( "play_hitground_effect" );
    if ( hitGroundEffect ) {
        startSound( "snd_drop", SND_DEPLOYABLE_IDLE );
        playEffect( "fx_hitground", "origin", false );
    }
}

void supply_crate::LogLifeTime() {
    if ( owner == $null_entity ) {
        return;
    }

    float t = sys.getTime() - creationTime;
    sys.increaseStatInt( statTimeUsed, owner.getEntityNumber(), t );
}

void supply_crate::Idle() {
    if ( sys.isClient() ) {
        return;
    }    

    sys.wait( 0.1f );

    thread BoundsKillThread();
    while ( true ) {
        if ( isAtRest() ) {
            if ( bindEntity != $null_entity ) {
                if ( !sys.isClient() ) {
                    bind( bindEntity );
                }
            }

            if ( !removeThreadActive ) {
                thread DropSoundThread();
            }
            break;
        }
        sys.waitFrame();
    }
    sys.killThread( "BoundsKillThread_" + getName() );

    while ( true ) {
        sys.wait( 1.f );
        if ( !removeThreadActive ) {
            Supply();
        }
    }
}

void supply_crate::Supply() {
    vector mins, maxs;

    mins = getAbsMins() - '32 32 0';
    maxs = getAbsMaxs() + '32 32 8';

    entitiesInBounds( mins, maxs, CONTENTS_BODY | CONTENTS_SLIDEMOVER, 1.0f );
    filterEntitiesByClass( "idPlayer", 1 );
    float count = filterEntitiesByAllegiance( TA_FLAG_FRIEND, 1 );

    float i;
    for ( i = 0; i < count; i++ ) {
        entity p = getBoundsCacheEntity( i );
        if ( p.getHealth() <= 0 ) {
            continue;
        }

        if ( p.givePackage( packageName ) ) {
            supplyCount = supplyCount - 1;
            OnSupplyCountChanged();

            if ( owner != $null_entity ) {
                if ( owner != p ) {
                    if ( usedProficiency != -1 ) {
                        owner.giveProficiency( usedProficiency, 1.f, $null, "supply crate used" );
                    }
                    sys.increaseStatInt( statUsed, owner.getEntityNumber(), 1 );
                }
            }

            if ( supplyCount == 0 ) {
                vRemoveObject();
            }
        }
    }
}

void supply_crate::SetupCommandMap() {
    commandMapHandle = sys.allocCMIcon( self, getFloatKey( "icon_sort_cm" ) );
    float commandMapSize = getFloatKeyWithDefault( "icon_size_cm", 16.f );

    sys.setCMIconDrawMode( commandMapHandle, DM_ROTATED_MATERIAL );
    sys.setCMIconSize( commandMapHandle, commandMapSize );
    sys.setCMIconColorMode( commandMapHandle, CM_ALLEGIANCE );
    sys.setCMIconMaterial( commandMapHandle, GetMaterial( getKey( "mtr_commandmap" ) ) );
    sys.setCMIconFlag( commandMapHandle, CMF_ALWAYSKNOWN | CMF_TEAMONLY );

    commandMapHandleEnemy = sys.allocCMIcon( self, getFloatKey( "icon_sort_cm" ) );
    sys.setCMIconDrawMode( commandMapHandleEnemy, DM_ROTATED_MATERIAL );
    sys.setCMIconSize( commandMapHandleEnemy, commandMapSize );
    sys.setCMIconColorMode( commandMapHandleEnemy, CM_ALLEGIANCE );
    sys.setCMIconMaterial( commandMapHandleEnemy, GetMaterial( getKey( "mtr_commandmap" ) ) );
    sys.setCMIconFlag( commandMapHandleEnemy, CMF_ENEMYONLY );
}

float supply_crate::OnUpdateCrosshairInfo( entity p ) {
    float allegiance = getEntityAllegiance( p );
    vector color = GetAllegianceColor( allegiance );

    chSetNumLines( 0 );

    float index;

    index = chAddLine();
    chSetLineColor( index, color, 1.f );
    chSetLineTextIndex( index, objectName );
    chSetLineType( index, CI_TEXT );
    chSetLineSize( index, 0, 0 );

    index = chAddLine();
    chSetLineColor( index, color, 0.5f );
    chSetLineType( index, CI_BAR );
    chSetLineFraction( index, getHealth() / getMaxHealth() );
    chSetLineSize( index, 100, CROSSHAIR_INFO_BAR_HEIGHT );

    if ( allegiance == TA_FRIEND ) {
        index = chAddLine();
        chSetLineTextIndex( index, g_locStr_Supplies );
        chSetLineColor( index, color, 1.f );
        chSetLineType( index, CI_TEXT );
        chSetLineSize( index, 0, 0 );

        index = chAddLine();
        chSetLineColor( index, color, 0.5f );
        chSetLineType( index, CI_BAR );
        chSetLineFraction( index, supplyCount / supplyMax );
        chSetLineSize( index, 100, CROSSHAIR_INFO_BAR_HEIGHT );
    }

    return 1.f;
}

void supply_crate::OnKilled( entity inflictor, entity attacker, float damage, vector direction, float location ) {
    if ( !sys.isClient() ) {
        player attackPlayer = attacker;
        if ( attackPlayer != $null_entity ) {
            if ( getEntityAllegiance( inflictor ) == TA_ENEMY ) {
                sys.increaseStatInt( statNumDestroyed, attackPlayer.getEntityNumber(), 1 );
            }
        }

        Fizzle();

        entity next;
        entity current;
        for ( current = getNextTeamEntity(); current != $null_entity; current = next ) {
            next = current.getNextTeamEntity();
            current.vOnBindMasterDestroyed();
        }
    }
}

void supply_crate::vSetDeployableOwner( entity p ) {
    owner = p;
    OnOwnerChanged();
}

void supply_crate::UpdateGUI() {
    sys.threadName( "UpdateGUI_" + getName() );
    while( true ) {
        if ( !removeThreadActive ) {
            float index = GetSupplyCrateIndex();
            if ( index == 0 ) {
                sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.supplyCrateHealth0", getHealth() / getMaxHealth() );
                sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.supplyCrateSupplies0", supplyCount / supplyMax );
                if ( GetSupplyCrateCount() == 1 ) {
                    sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.supplyCrateHealth1", 0 );
                    sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.supplyCrateSupplies1", 0 );
                }
            } else if ( index == 1 ) {
                sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.supplyCrateHealth1", getHealth() / getMaxHealth() );
                sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.supplyCrateSupplies1", supplyCount / supplyMax );
            }
        }
        sys.waitFrame();
    }
}

void supply_crate::OnOwnerChanged() {
    if ( owner == sys.getLocalPlayer() ) {
        sys.killThread( "UpdateGUI_" + getName() );
        thread UpdateGUI();    
    }
}

void supply_crate::BoundsKillThread() {
    float damageIndex = GetDamage( getKey( "dmg_crush" ) );

    while ( true ) {
        vector mins = getAbsMins();
        vector maxs = getAbsMaxs();
        mins += '8 8 -32';
        maxs -= '8 8 0';

        float count = entitiesInBounds( mins, maxs, MASK_SHOT_RENDERMODEL | MASK_SHOT_BOUNDINGBOX, 1 );
        float index;
        for ( index = 0; index < count; index++ ) {
            entity other = getBoundsCacheEntity( index );
            if ( other == self ) {
                continue;
            }
            if ( other != $null_entity ) {
                other.applyDamage( $null_entity, self, '0 0 -1', damageIndex, 1.f, $null_entity );
            }

            vehicle_base v = other;
            if ( v != $null_entity ) {
                vRemoveObject();
            }
        }

        if ( removeThreadActive ) {
            return;
        }

        sys.waitFrame();
    }
}

void supply_crate::Fizzle() {
    if ( removeThreadActive ) {
        return;
    }

    startSound( "snd_drop", SND_DEPLOYABLE );
    playEffect( "fx_fizzle", "", 0 );

    HideParachute();

    hide();
    disablePhysics();
    forceDisableClip();
    removeThreadActive = true;
    fizzle = true;

    if ( !sys.isClient() ) {
        thread RemoveThread( 2.0 );
    }
}

void supply_crate::RemoveThread( float removeTime ) {
    sys.wait( removeTime );
    remove();
}

float supply_crate::vGetSupplyDropCreationTime() {
    return creationTime;
}

void supply_crate::DropSoundThread() {
    startSound( "snd_drop", SND_DEPLOYABLE );
}

void supply_crate::HideParachute() {
    if ( !sys.isClient() ) {
        float index;
        float count = entitiesOfCollection( "parachutes" );
        for ( index = 0; index < count; index++ ) {
            entity other = getBoundsCacheEntity( index );
            if ( other.vGetOwner() == self ) {
                other.vCancel();
                return;
            }
        }
    }
}

void supply_crate::OnFizzleChanged() {
    if ( sys.isClient() ) {
        Fizzle();
    }
}

void supply_crate::OnSupplyCountChanged() {
    entity p = sys.getLocalPlayer();
    if ( p == $null_entity ) {
        return;
    }

    if ( supplyCount < lastSupplyCount ) {
        startSound( "snd_supply", SND_DEPLOYABLE_IDLE );
    }
    lastSupplyCount = supplyCount;
}

void supply_crate::vOnBindMasterDestroyed() {
    if ( !sys.isClient() ) {
        unbind();
        Fizzle();
    }
}

void supply_crate::vBindToEntity( entity ent ) {
    bindEntity = ent;
}

boolean supply_crate::vDropItemTrace( vector tStart, vector tEnd, entity passEntity, vector targetPos ) {
    allowDrop = true;

    float frac = sys.tracePoint( tStart, tEnd, CONTENTS_SOLID | CONTENTS_MOVEABLECLIP | CONTENTS_PLAYERCLIP, passEntity );
    vector pos = sys.getTraceEndPos() + '0 0 1'; // just a tiny bit off of ground as it might not be flat
    if ( frac == 1.0f || sys.getTraceNormal() * vec3_up < 0.99f ) {
        if ( sys.fabs( pos_z - targetPos_z ) > 128 ) {
            allowDrop = false;
        }
        return true;
    }


    // try to avoid clipping into buildings
    if ( !sys.checkContents( pos, getMins(), getMaxs(), CONTENTS_SOLID | CONTENTS_MOVEABLECLIP | CONTENTS_PLAYERCLIP, passEntity ) ) {
        if ( sys.fabs( pos_z - targetPos_z ) > 128 ) {
            allowDrop = false;
        }
        return true;
    }

    vector size = getSize();
    pos_x = pos_x + size_x / 2;
    pos_y = pos_y + size_y / 2;

    vector cratePos;
    cratePos_x = pos_x - size_x;
    cratePos_y = pos_y - size_y;
    cratePos_z = pos_z;
    sys.trace( cratePos + '0 0 2048', cratePos + '0 0 -128', getMins(), getMaxs(), CONTENTS_SOLID | CONTENTS_MOVEABLECLIP | CONTENTS_PLAYERCLIP, passEntity );
    if ( ValidDropTrace( cratePos ) ) {
        return true;
    }

    cratePos_x = pos_x;
    cratePos_y = pos_y - size_y;
    sys.trace( cratePos + '0 0 2048', cratePos + '0 0 -128', getMins(), getMaxs(), CONTENTS_SOLID | CONTENTS_MOVEABLECLIP | CONTENTS_PLAYERCLIP, passEntity );
    if ( ValidDropTrace( cratePos ) ) {
        return true;
    }

    cratePos_x = pos_x - size_x;
    cratePos_y = pos_y;
    sys.trace( cratePos + '0 0 2048', cratePos + '0 0 -128', getMins(), getMaxs(), CONTENTS_SOLID | CONTENTS_MOVEABLECLIP | CONTENTS_PLAYERCLIP, passEntity );
    if ( ValidDropTrace( cratePos ) ) {
        return true;
    }

    cratePos_x = pos_x;
    cratePos_y = pos_y;
    sys.trace( cratePos + '0 0 2048', cratePos + '0 0 -128', getMins(), getMaxs(), CONTENTS_SOLID | CONTENTS_MOVEABLECLIP | CONTENTS_PLAYERCLIP, passEntity );
    if ( ValidDropTrace( cratePos ) ) {
        return true;
    }

    sys.tracePoint( tStart, tEnd, CONTENTS_SOLID | CONTENTS_MOVEABLECLIP | CONTENTS_PLAYERCLIP, passEntity );
    if ( sys.fabs( pos_z - targetPos_z ) > 128 ) {
        allowDrop = false;
    }

    return true;
}

boolean supply_crate::ValidDropTrace( vector originalPos ) {
    vector pos = sys.getTraceEndPos();
    if ( sys.fabs( pos_z - originalPos_z ) > 4 ) {
        return false;
    }

    if ( sys.getTraceFraction() == 1.0f 
        || ( sys.getTraceNormal() * vec3_up < 0.99f )
        || ( sys.getTraceSurfaceFlags() & SURF_NOIMPACT )
        || ( sys.getTraceEntity() != $null_entity && sys.getTraceEntity() != sys.getEntity( "worldspawn" ) ) ) {
        return false;
    }

    return true;
}

float supply_crate::GetSupplyCrateIndex() {
    if ( owner == $null_entity ) {
        return -1;
    }

    float i;
    float num = owner.binGetSize();
    float index = 0;

    for ( i = 0; i < num; i++ ) {
        entity other = owner.binGet( i );
        if ( other.inCollection( "supply_crate" ) ) {
            if ( other == self ) {
                return index;
            } else {
                index++;
            }
        }
    }

    return -1;
}

float supply_crate::GetSupplyCrateCount() {
    if ( owner == $null_entity ) {
        return 0;
    }

    float i;
    float num = owner.binGetSize();
    float count = 0;

    for ( i = 0; i < num; i++ ) {
        entity other = owner.binGet( i );
        if ( other.inCollection( "supply_crate" ) ) {
            count++;
        }
    }

    return count;
}

boolean supply_crate::vAllowDrop() {
    return allowDrop;
}