Script:Files:script/vehicles/jotun.script

From Mod Wiki
#define DROP_DIST '0 0 32768'

void G_Jotun_DropItem( float requestId, entity item, vector targetPos ) {
    if ( sys.isClient() ) {
        return;
    }

    sys.waitFrame();

    vector dropOrigin = item.getWorldOrigin();
    dropOrigin_x = targetPos_x;
    dropOrigin_y = targetPos_y;

    entity parachute;    

    vector down    = dropOrigin - DROP_DIST;
    float frac;
    if ( item.vDropItemTrace( dropOrigin, down, item, targetPos ) ) {
        if ( !item.vAllowDrop() ) {
            if ( requestId != -1 ) {
                sys.clearDeployRequest( requestId );
                requestId = -1;
            }
            item.remove();
            return;
        }
        frac = sys.getTraceFraction();
    } else {
        frac = sys.tracePoint( dropOrigin, down, CONTENTS_SOLID | CONTENTS_MOVEABLECLIP, item );
    }

    if ( frac == 1.f ) {
        sys.warning( "G_Jotun_DropItem did not find the ground" );
    }

    vector finalDestinationII = sys.getTraceEndPos();
    item.setComeToRest( false );
    item.setGroundPosition( finalDestinationII );
    item.disableKnockback();

    entity traceEnt = sys.getTraceEntity();
    if ( traceEnt != $null_entity && traceEnt != sys.getEntity( "worldspawn" ) ) {
        item.vBindToEntity( traceEnt );
    }

    float parachuteDelay = item.getFloatKeyWithDefault( "parachute_deploy_delay", 3.f );

    float parachuteTime = sys.getTime() + parachuteDelay;
    float parachuteZ = finalDestinationII_z + 4196;

    while( 1 ) {
        sys.waitFrame();

        if ( item == $null_entity ) { // Gordon: map restart may have happened, or something else that removes the entity, don't want to spin forever
            return;
        }

        vector origin = item.getWorldOrigin();
        vector velocity = item.getLinearVelocity();
        vector delta = origin - finalDestinationII;

        if ( parachuteTime != -1 && sys.getTime() > parachuteTime && origin_z < parachuteZ ) {
            // spawn a parachute on the dropped vehicle
            string paraDef = item.getKeyWithDefault( "def_parachute", "round_parachute" );

            if ( paraDef != "" ) {
                parachute = sys.spawn( paraDef );
                if ( parachute != $null_entity ) {
                    parachute.vSetOwner( item );
                    parachute.vForceStayOpen( true );
                }
            }

            parachuteTime = -1;        

            if ( item.vSkipDeployDrop() ) {
                break;
            }
        }

        float count = item.entitiesInBounds( item.getAbsMins(), item.getAbsMaxs(), -1, 0 );
        float index;
        for ( index = 0; index < count; index++ ) {
            entity other = item.getBoundsCacheEntity( index );
            other.applyDamage( $null_entity, item, down, GetDamage( "damage_magog_npc_collide" ), 1.f, $null_entity ); // FIXME: Get damage decl
        }

        if ( delta_z <= 1 ) {
            break;
        }

        if ( velocity_z != 0.f ) {
            // drift the item towards its destination to account for error in launch origin
            float estTimeToHit = sys.fabs( delta_z / velocity_z );
            vector newVelocity = delta * ( velocity_z / delta_z );
            newVelocity_z = velocity_z;
            item.setLinearVelocity( newVelocity );
        }
    }

    item.enableKnockback();
    if ( parachute != $null_entity ) {
        parachute.vForceStayOpen( false );
    }

    if ( requestId != -1 ) {
        sys.clearDeployRequest( requestId );
        requestId = -1;
    }

    if ( item != $null_entity ) {
        item.vOnDeploy();
        item = $null_entity;
    }
}

object vehicle_jotun {
    void            preinit();
    void            init();
    void            destroy();
    void            syncFields();

    // states
    void            Deliver();
    void            Drop();
    void            Return();

    // callbacks
    void            OnSetDeploymentParms( float deploymentItemIndex, float playerIndex, vector target, float rotation );
    void            OnKilled( entity inflictor, entity attacker, float damage, vector direction, float location );
    void            ClearRequest();
    void            OnItemChanged();
    void            OnStartTimeChanged();

    // utility
    void            DoSound();
    void            SetupCommandMap();
    boolean            PerformDrop();

    // virtuals
    boolean            vIgnoreMagogCollsion();

    // threads
    void            KillAllInBoundingBox();

    // deployment stuff
    float            deployIndex;
    float            deployPlayerIndex;
    float            itemIndex;
    entity            item;
    vector            targetPos;
    float            itemRotation;
    float            startTime;
    float            dropTime;
    float            returnTime;
    vector            gdfCorner;

    float            deliveryPathLength;
    float            returnPathLength;

    // local data	
    float            maxHealth;
    float            damageIndex;

    boolean            cancelDrop;
    boolean            dropped;

    boolean            playedFlyBySound;
    boolean            playingFlySound;
    float            flyOverSoundRange;
};


void vehicle_jotun::SetupCommandMap() {

}

void vehicle_jotun::syncFields() {
    syncBroadcast( "item" );
    syncBroadcast( "dropTime" );
    syncBroadcast( "returnTime" );
    syncBroadcast( "targetPos" );
    syncBroadcast( "itemRotation" );
    syncBroadcast( "gdfCorner" );
    syncBroadcast( "startTime" );
    syncCallback( "item", "OnItemChanged" );
    syncCallback( "startTime", "OnStartTimeChanged" );
}

void vehicle_jotun::OnItemChanged() {
}

void vehicle_jotun::OnStartTimeChanged() {
    if ( startTime != -1 ) {
        setState( "Deliver" );
    }
}

void vehicle_jotun::preinit() {
    deployPlayerIndex = -1;
    startTime = -1;
    dropTime = -1;
    returnTime = -1;
    maxHealth                = getFloatKey( "health" );

    SetupCommandMap();

    float damageDeclIndex        = sys.getDeclType( "damageDef" );
    damageIndex                    = sys.getDeclIndex( damageDeclIndex, getKey( "dmg_drop_damage" ) );

    gdfCorner = '0 0 0';

    cancelDrop        = false;
    dropped         = false;

    playedFlyBySound    = false;
    playingFlySound        = false;

    flyOverSoundRange    = getFloatKey( "flyover_sndrange" );
    flyOverSoundRange    = flyOverSoundRange * flyOverSoundRange;

    setCoverage( 0.f );

    disableKnockback();
    hide();
}

void vehicle_jotun::init() {
    thread KillAllInBoundingBox();
}

void vehicle_jotun::ClearRequest() {
    if ( sys.isClient() ) {
        return;
    }

    if ( deployPlayerIndex != -1 ) {
        sys.clearDeployRequest( deployPlayerIndex );
        deployPlayerIndex = -1;
    }
}

void vehicle_jotun::destroy() {    
    sys.killThread( "KillAllInBoundingBox_" + getName() );

    ClearRequest();
}

#define JOTUN_FLY_HEIGHT            4096

void vehicle_jotun::Deliver() {
    show();

    if ( !sys.isClient() ) {
        startTime = sys.getTime();
        gdfCorner = objManager.GetGDFBasePosition();
    }

    float pathSpeed = getFloatKeyWithDefault( "path_speed", 1024 );
    string pathType = getKey( "path_type" );

    // find the delivery path & level it out
    pathFind( pathType, targetPos, startTime, 1.0f, gdfCorner_x, gdfCorner_y, 256.0f, false );
    pathLevel( 13, -1, -1 );
    float numPoints = pathGetNumPoints();
    if ( numPoints < 2 ) {
        return;
    }

    deliveryPathLength = pathGetLength();

    // get the return path & level that out
    pathFind( pathType, targetPos, startTime + deliveryPathLength / pathSpeed, -1.0f, gdfCorner_x, gdfCorner_y, 1024.0f, true );
    float newNumPoints = pathGetNumPoints();
    pathLevel( 13, numPoints - 1, newNumPoints - 1 );

    returnPathLength = pathGetLength() - deliveryPathLength;

    float pathLength = deliveryPathLength;
    float leadTime = 2.0f;

    vector flyOffset = '0 0 0';
    flyOffset_z = JOTUN_FLY_HEIGHT;

    // setup the start
    vector startPoint = pathGetPoint( 0 ) + flyOffset;
    vector endPoint = pathGetPoint( numPoints - 1 ) + flyOffset;
    vector startDir = sys.vecNormalize( endPoint - startPoint );
    vector startAngles = sys.vecToAngles( startDir );

    setWorldOrigin( startPoint );
    setAngles( startAngles );

    startJotunDelivery( startTime, pathSpeed, leadTime );

    float minDistance = 9999999999.0f;
    float coverage = 0.f;
    while ( true ) {
        float time = sys.getTime() - startTime;
        float frameTime = sys.getFrameTime();

        setCoverage( coverage );
        coverage = coverage + frameTime;

//sys.debugCircle( '1 0 0', startPoint, '0 0 1', 256.0f, 16, 0 );
//sys.debugCircle( '1 0 0', endPoint, '0 0 1', 128.0f, 16, 0 );

        float aheadPosition = ( time + leadTime )* pathSpeed;

        DoSound();

        if ( !sys.isClient() ) {
            vector origin = getWorldOrigin();
            vector delta = origin - endPoint;
            delta_z = 0.0f;
            float distance = sys.vecLength( delta );

            if ( distance < minDistance ) {
                minDistance = distance;
            } else if ( aheadPosition > pathLength ) {
                Drop();
            }
        } else if ( returnTime != -1 ) {
            setState( "Return" );
        }

        if ( cancelDrop ) {
            ClearRequest();
            setState( "Return" );
        }

        sys.waitFrame();
    }
}

void vehicle_jotun::Drop() {

    player p = sys.getClient( deployPlayerIndex );
    item = sys.spawnType( itemIndex );
    item.vSetDeployableOwner( p );

    deployable_base deployable = item;
    if ( deployable != $null_entity ) {
        p.SetTargetingItem( item );
    }    

    string statName = item.getKey( "stat_name" );
    if ( statName != "" ) {
        sys.increaseStatInt( sys.allocStatInt( statName + "_deployed" ), deployPlayerIndex, 1 );
    }

    if( PerformDrop() ) {
        vector temp = getWorldOrigin();

        vector angles;
        angles_y = itemRotation;

        item.setOrigin( temp - ( g_vectorDown * 256 ) );
        item.setAngles( angles );
        item.setGameTeam( getGameTeam() );
        item.vSetManualDeploy();
        thread G_Jotun_DropItem( deployPlayerIndex, item, targetPos );
    } else {                
        item.remove();
        ClearRequest();
    }

    deployPlayerIndex = -1;
    dropped = true;

    setState( "Return" );
}

void vehicle_jotun::Return() {
    if ( !sys.isClient() ) {
        returnTime = sys.getTime();
    } else {
        while ( returnTime == -1 ) {
            sys.waitFrame();
        }
    }

    float numPoints = pathGetNumPoints();
    if ( numPoints < 2 ) {
        return;
    }

    float pathSpeed = getFloatKeyWithDefault( "path_speed", 1024 );
    float pathLength = returnPathLength;
    float leadTime = 2.0f;

    vector flyOffset = '0 0 0';
    flyOffset_z = JOTUN_FLY_HEIGHT;

    vector endPoint = pathGetPoint( numPoints - 1 ) + flyOffset;
    startJotunReturn( returnTime, pathSpeed, leadTime );

    while ( true ) {    
        DoSound();

        float time = sys.getTime() - returnTime;
        float position = time * pathSpeed;
        setCoverage( ( pathLength - 1 - position ) / pathSpeed );

        if ( !sys.isClient() ) {
            if ( position > pathLength ) {
                remove();
            }
        }

        sys.waitFrame();
    }
}

void vehicle_jotun::DoSound() {
    if( !playingFlySound ) {
        playingFlySound = true;
        fadeSound( SND_VEHICLE_IDLE, -60.f, 0.f );
        startSound( "snd_flyby_far", SND_VEHICLE_IDLE );
        fadeSound( SND_VEHICLE_IDLE, 0.f, 2.f );
    }

    // check distance to local player
    if( !playedFlyBySound ) {
        entity p = sys.getLocalPlayer();
        if( p != $null_entity ) {
            vector playerOrigin = p.getWorldOrigin();
            playerOrigin -= getWorldOrigin();
            playerOrigin_z = 0.f;

            if( sys.vecLengthSquared( playerOrigin ) < flyOverSoundRange ) {
                sys.startSoundDirect( getKey( "snd_flyby" ), SND_VEHICLE_IDLE );
                playedFlyBySound = true;
            }
        }
    }
}

void vehicle_jotun::OnSetDeploymentParms( float deploymentItemIndex, float playerIndex, vector target, float rotation ) {    

    deployPlayerIndex    = playerIndex;
    targetPos            = target;
    itemRotation        = sys.angleNormalize180( rotation );
    itemIndex            = deploymentItemIndex;

    if( !PerformDrop() ) {
        // player has switched sides since calling in the deployable
        ClearRequest();
        remove();
        return;
    }

    player p = sys.getClient( playerIndex );

    setState( "Deliver" );
}

void vehicle_jotun::OnKilled( entity inflictor, entity attacker, float damage, vector direction, float location ) {
}

void vehicle_jotun::KillAllInBoundingBox() {
    sys.threadName( "KillAllInBoundingBox_" + getName() );

    sys.wait( 3 );

    eachFrame {

        vector mins = getAbsMins();
        vector maxs = getAbsMaxs();

//		sys.debugBounds( '1 1 1', mins, maxs, 0.f );

        float count = entitiesInBounds( mins, maxs, MASK_ALL, 1 );
        count = filterEntitiesByClass( "sdTransport", 1 );

        float i;
        for( i = 0; i < count; i++ ) {
            entity ent = getBoundsCacheEntity( i );
            if( ent == self || ent == item || ent == $null_entity ) {
                continue;
            }

            if( !ent.vIgnoreMagogCollsion() ) {
                ent.applyDamage( $null_entity, self, '0 1 0', GetDamage( "damage_magog_npc_collide" ), 60000.f, $null_entity );
            }
        }
    }
}

boolean vehicle_jotun::vIgnoreMagogCollsion() {
    return true;
}

boolean vehicle_jotun::PerformDrop() {
    return true;
}

void vehicle_jotun::vCancelDeployForPlayer( float playerIndex ) {
    if ( playerIndex != deployPlayerIndex ) {
        return;
    }

    // deployables already gone
    if ( dropped ) {
        return;
    }

    player p = sys.getClient( deployPlayerIndex );
    objManager.PlaySoundForPlayer( getKey( "snd_cancel_deploy" ), p );

    ClearRequest();
    setState( "Return" );
}