Script:Files:script/vehicles/magog.script

From Mod Wiki
object vehicle_magog_npc {
    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            vSetDeploymentParms( float deploymentItemIndex, float playerIndex, vector target, float rotation ) {
        OnSetDeploymentParms( deploymentItemIndex, playerIndex, target, rotation );
    }

    void            ClearRequest();
    void            OnItemChanged();
    void            OnStateTimeChanged();

    // utility
    void            SetupCrate();
    void            SetupCommandMap();
    boolean            PerformDrop();
    void            UpdateEffects( vector pos );

    // virtuals
    boolean            vIgnoreMagogCollsion();

    // threads
    void            UpdateRotors();
    void            KillAllInBoundingBox();
    void            RopeThread();
//	void			CheckDeployable();

    vector            GetItemAttachPoint();

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

    // local data	
    float            maxHealth;
    float            commandMapHandle;

    // rotor blades
    float            rearUpperRotorJoint;
    float            rearLowerRotorJoint;
    float            leftUpperRotorJoint;
    float            leftLowerRotorJoint;
    float            rightUpperRotorJoint;
    float            rightLowerRotorJoint;
    float            bladeRotationSpeed;

    // rope stuff
    float            attachJoint;
    float            ropeJoint;
    float            hatchJoint;
    float            itemAttachJoint;

    // physics
    float            maxZAccel;
    float            maxZVel;

    boolean            cancelDrop;
    boolean            dropped;

    vector            renderMins;
    vector            renderMaxs;

    // rotor ground fx	
    boolean            groundEffects;
    float            groundEffectsThreshhold;
    float            lastGroundEffectsTime;

    entity            vGetItem() {
        return item;
    }        
}


void vehicle_magog_npc::SetupCommandMap() {
    commandMapHandle = sys.allocCMIcon( self, 1 );

    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.setCMIconUnknownMaterial( commandMapHandle, GetMaterial( getKey( "mtr_commandmap_unknown" ) ) );
    sys.setCMIconUnknownSize( commandMapHandle, getFloatKeyWithDefault( "icon_unknown_size_cm", commandMapSize / 2.0f ) );

    sys.setCMIconFlag( commandMapHandle, CMF_FOLLOWROTATION );
}

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

void vehicle_magog_npc::OnItemChanged() {
    SetupCrate();
}

void vehicle_magog_npc::OnStateTimeChanged() {
    if ( returnTime != -1 ) {
        setState( "Return" );
    } else if ( dropTime != -1 ) {
        setState( "Drop" );
    } else if ( startTime != -1 ) {
        setState( "Deliver" );
    }
}

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

    SetupCommandMap();

    rearUpperRotorJoint = getJointHandle( "rear_upper_rotor" );
    rearLowerRotorJoint = getJointHandle( "rear_lower_rotor" );

    leftUpperRotorJoint = getJointHandle( "front_left_upper_rotor" );
    leftLowerRotorJoint = getJointHandle( "front_left_lower_rotor" );

    rightUpperRotorJoint = getJointHandle( "front_right_upper_rotor" );
    rightLowerRotorJoint = getJointHandle( "front_right_lower_rotor" );

    bladeRotationSpeed    = getFloatKey( "blade_speed" );
    if ( bladeRotationSpeed <= 0 ) {
        bladeRotationSpeed = 1440.f;
    }

    // this is to account for rotor spooling etc
    maxZAccel = getFloatKey( "max_thrust" );
    maxZVel = getFloatKey( "max_z_vel" );

    gdfCorner = g_vectorZero;

    cancelDrop        = false;
    dropped         = false;

    // stop the comedy knock back from the deployable exploding when the magog is delivering it
    disableKnockback();

    startSound( "snd_rotor", SND_VEHICLE_START );

    attachJoint        = getJointHandle( getKey( "joint_attach" ) );
    ropeJoint        = getJointHandle( getKey( "joint_rope" ) );
    hatchJoint        = getJointHandle( getKey( "joint_hatch" ) );
    itemAttachJoint    = INVALID_JOINT;

    groundEffects    = false;
    groundEffectsThreshhold = getFloatKey( "groundeffects_threshhold" );
    lastGroundEffectsTime = 0.f;

    setCoverage( 0.f );

    thread UpdateRotors();
    thread KillAllInBoundingBox();
    thread RopeThread();

    renderMins = getMins();
    renderMaxs = getMaxs();
}

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

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

void vehicle_magog_npc::destroy() {
    if ( item != $null_entity && !sys.isClient() ) {
        item.remove();
    }

    sys.freeCMIcon( self, commandMapHandle );

    ClearRequest();
}

#define HOVER_HEIGHT_MIN        400
#define HOVER_HEIGHT_AIM        2300

void vehicle_magog_npc::Deliver() {
    if ( sys.isClient() ) {
        pathFind( getKey( "path_type" ), targetPos, startTime, 1.0f, gdfCorner_x, gdfCorner_y, 0, false );
        pathLevel( 20, -1, -1 );
        pathStraighten();
    }

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

//	thread CheckDeployable();

    float pathSpeed = getFloatKeyWithDefault( "path_speed", 1024 );
    float pathLength = pathGetLength();

    vector endPoint = pathGetPoint( numPoints - 1 );
    endPoint_z += HOVER_HEIGHT_MIN;
    vector endAngles = g_vectorZero;
    endAngles_y = sys.angleNormalize180( itemRotation );

    // find the actual end point
    float fraction = 1.0f;
    while ( fraction == 1.0f ) {
        fraction = sys.traceOriented( endPoint, endPoint - '0 0 3000', renderMins, renderMaxs, endAngles, MASK_SOLID | MASK_OPAQUE, self );
        endPoint = sys.getTraceEndPos();
    }
    endPoint_z += HOVER_HEIGHT_AIM;
    matchPosition = endPoint;

    startMagogDelivery( startTime, pathSpeed, 2.0f, endPoint, itemRotation );

    float coverage = 0.f;
    while ( true ) {
        if ( sys.isServer() ) {
            if ( item == $null_entity ) {
                sys.warning( "DEPLOYABLE GONE" );
            }
        }

        float frameTime = sys.getFrameTime();

        setCoverage( coverage );
        coverage = coverage + frameTime;

        vector origin = getWorldOrigin();            
        if ( !sys.isClient() ) {
            float distToEnd = sys.vecLength( origin - endPoint );
            if ( sys.vecLength( origin - endPoint ) < 64.0f ) {
                vector velocity = getLinearVelocity();
                velocity_z = 0.0f;
                if ( sys.vecLength( velocity ) < 50.0f ) {
                    setState( "Drop" );
                }
            }

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

        sys.waitFrame();
    }
}

void vehicle_magog_npc::Drop() {
    if( !sys.isClient() ) {
        if ( !PerformDrop() ) {
            // the player has switched sides or class since requesting the deployable
            ClearRequest();        
            setState( "Return" );
        }
    }

    if ( !sys.isClient() ) {
        dropTime = sys.getTime();
    } else {
        while ( dropTime == -1 ) {
            sys.waitFrame();
        }
    }

    vector origin = getWorldOrigin();    
    vector endAngles = g_vectorZero;
    endAngles_y = sys.angleNormalize180( itemRotation );

    item.unbind();
    item.vStartMagogDrop();

    if ( item.getFloatKey( "no_drop" ) == 0.f ) {

        float timeToDrop = 4.0f;
        vector dropOrigin = item.getWorldOrigin();
        vector dropDelta = targetPos - dropOrigin;

        vector dropAngles = item.getAngles();
        vector dropAngleDelta = endAngles - dropAngles;
        dropAngleDelta_y = sys.angleNormalize180( dropAngleDelta_y );


        float time;
        float frameTime;
        float doneNess;
        vector spot;
        vector angles;
        vector ropeModelSpot;
        vector hookModelSpot;

        // expand the bounds while dropping the item
        vector mins = getRenderMins();
        vector maxs = getRenderMaxs();
        mins_z += dropDelta_z;
        setRenderBounds( mins, maxs );

        if ( !sys.isClient() ) {
            item.vStartBoundsKillThread();
        }

        vector hookStartSpot = getJointPos( attachJoint );
        vector ropeStartSpot = getJointPos( ropeJoint );
        vector hookStartModelSpot = worldToModelSpace( hookStartSpot - origin );
        vector ropeStartModelSpot = worldToModelSpace( ropeStartSpot - origin );

        while ( true ) {
            time = sys.getTime() - dropTime;
            frameTime = sys.getFrameTime();
            doneNess = time / timeToDrop;
            if ( doneNess > 1.0f ) {
                doneNess = 1.0f;
            }

            doneNess = 1 - ( sys.cos( doneNess * 180 ) + 1 ) * 0.5;

            spot = dropOrigin + dropDelta * doneNess;        
            angles = dropAngles + dropAngleDelta * doneNess;    

            item.setWorldOrigin( spot );
            item.setAngles( angles );
            item.setLinearVelocity( vec3_origin );
            item.setAngularVelocity( vec3_origin );

            // use the joint position to position the hook
            vector hookSpot = GetItemAttachPoint(); //item.getJointPos( itemAttachJoint );
            vector ropeSpot = ( hookSpot + dropOrigin ) * 0.5f;

            origin = getWorldOrigin();
            hookModelSpot = worldToModelSpace( hookSpot - origin );
            ropeModelSpot = worldToModelSpace( ropeSpot - origin );

            setJointPos( attachJoint, JOINTMOD_WORLD_OVERRIDE, hookModelSpot );
            setJointPos( ropeJoint, JOINTMOD_WORLD_OVERRIDE, ropeModelSpot );

            vector resultHookSpot = getJointPos( attachJoint );

            sys.waitFrame();
            if ( doneNess == 1.0f ) {
                break;        
            }
        }

        if ( !sys.isClient() ) {
            ClearRequest();

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

        // bring the hook back up
        vector hookEndSpot = getJointPos( attachJoint );
        vector ropeEndSpot = getJointPos( ropeJoint );

        float raiseTime = sys.getTime() + 1;
        while ( true ) {
            time = sys.getTime() - raiseTime;
            frameTime = sys.getFrameTime();
            doneNess = time / ( timeToDrop * 0.75f );

            CLAMP( doneNess, 0.0f, 1.0f );
            doneNess = 1 - ( sys.cos( doneNess * 180 ) + 1 ) * 0.5;

            origin = getWorldOrigin();
            ropeStartSpot = sys.toWorldSpace( ropeStartModelSpot, self );
            hookStartSpot = sys.toWorldSpace( hookStartModelSpot, self );
            ropeModelSpot = worldToModelSpace( ( ropeStartSpot - ropeEndSpot ) * doneNess + ropeEndSpot - origin );
            hookModelSpot = worldToModelSpace( ( hookStartSpot - hookEndSpot ) * doneNess + hookEndSpot - origin );

            setJointPos( attachJoint, JOINTMOD_WORLD_OVERRIDE, hookModelSpot );
            setJointPos( ropeJoint, JOINTMOD_WORLD_OVERRIDE, ropeModelSpot );

            vector resultHookSpot2 = getJointPos( attachJoint );
            vector hookPooSpot = ( hookStartSpot - hookEndSpot ) * doneNess + hookEndSpot - origin;

            sys.waitFrame();
            if ( doneNess == 1.0f ) {
                break;
            }
        }

        // restore the original render bounds		
        mins_z -= dropDelta_z;
        setRenderBounds( mins, maxs );
    }

    dropped = true;

    if ( !sys.isClient() ) {
        setState( "Return" );
    }
}

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

    pathFind( getKey( "path_type" ), targetPos, returnTime, -1.0f, gdfCorner_x, gdfCorner_y, 0, false );
    float numPoints = pathGetNumPoints();
    if ( numPoints < 2 ) {
        return;
    }

    pathLevel( 20, -1, -1 );
    pathStraighten();

    float pathSpeed = getFloatKeyWithDefault( "path_speed", 1024 );
    float pathLength = pathGetLength();

    vector hoverOffset = g_vectorZero;
    hoverOffset_z = HOVER_HEIGHT_MIN;

    vector endPoint = pathGetPoint( numPoints - 1 ) + hoverOffset;

    startMagogReturn( returnTime, pathSpeed, 2.0f, endPoint );

    while ( true ) {
        float time = sys.getTime() - returnTime;
        float frameTime = sys.getFrameTime();

        float position = time * pathSpeed;
        float coverage = ( pathLength - 1 - position ) / pathSpeed;
        setCoverage( coverage );

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

        sys.waitFrame();
    }
}

void vehicle_magog_npc::UpdateRotors() {
    vector ang;
    ang_y = 0.f;

    while ( true ) {
        sys.waitFrame();

        ang_y += bladeRotationSpeed * sys.getFrameTime();

        setJointAngle( rearUpperRotorJoint, JOINTMOD_WORLD, ang );
        setJointAngle( rearLowerRotorJoint, JOINTMOD_WORLD, ang );

        setJointAngle( leftUpperRotorJoint, JOINTMOD_WORLD, ang * 0.99f );
        setJointAngle( leftLowerRotorJoint, JOINTMOD_WORLD, ang * 0.99f );

        setJointAngle( rightUpperRotorJoint, JOINTMOD_WORLD, ang * 1.01f );
        setJointAngle( rightLowerRotorJoint, JOINTMOD_WORLD, ang * 1.01f );

        vector absMins = getAbsMins();
        vector traceOrg = ( absMins + getAbsMaxs() ) * 0.5f;

        UpdateEffects( traceOrg );
    }
}

void vehicle_magog_npc::OnSetDeploymentParms( float deploymentItemIndex, float playerIndex, vector target, float rotation ) {    
    deployPlayerIndex    = playerIndex;
    targetPos            = target;
    itemRotation        = sys.angleNormalize180( rotation );

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

    player p = sys.getClient( playerIndex );
    item = sys.spawnType( deploymentItemIndex );
    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 );
    }

    SetupCrate();

    // setup the delivery path
    startTime = sys.getTime();
    gdfCorner = objManager.GetGDFBasePosition();

    pathFind( getKey( "path_type" ), targetPos, startTime, 1.0f, gdfCorner_x, gdfCorner_y, 0, false );
    pathLevel( 20, -1, -1 );
    pathStraighten();

    vector startPoint = pathGetPoint( 0 );
    startPoint_z += HOVER_HEIGHT_MIN;
    vector startDir = pathGetDirection( 0 );
    startDir = sys.vecNormalize( startDir );
    vector startAngles = sys.vecToAngles( startDir );

    setWorldOrigin( startPoint );
    setAngles( startAngles );

    setState( "Deliver" );
}

void vehicle_magog_npc::SetupCrate() {
    if ( item == $null_entity ) {
        return;
    }

    vector org;

    itemAttachJoint = item.getJointHandle( item.getKey( "joint_attach" ) );
    org = GetItemAttachPoint();
    org = item.worldToModelSpace( org - item.getWorldOrigin() );

    if ( item != $null_entity ) {
        item.setOrigin( getLocalJointPos( attachJoint ) - org );
        item.bindToJoint( self, getKey( "joint_attach" ), 1 );
        item.setGameTeam( getGameTeam() );
        item.vSetManualDeploy();
    }
}

vector vehicle_magog_npc::GetItemAttachPoint() {
    if ( item == $null_entity ) {
        return '0 0 0';
    }

    vector org;

    if ( itemAttachJoint != -1 ) {
        org = item.getJointPos( itemAttachJoint );
    } else {
        org = item.getWorldOrigin();
        org_z += item.getFloatKey( "attach_height" );
    }

    return org;
}

void vehicle_magog_npc::KillAllInBoundingBox() {
    sys.wait( 3 );

    eachFrame {

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

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

        entitiesInBounds( mins, maxs, MASK_ALL, 1 );
        float 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() ) {
                if ( touchesBounds( ent ) ) {
                    ent.applyDamage( $null_entity, self, '0 1 0', GetDamage( "damage_magog_npc_collide" ), 60000.f, $null_entity );
                }
            }
        }
    }
}

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

#define MAGOC_NPC_SWINGLENGTHSCALE    2
#define MAGOC_NPC_SWINGMASS            1000
#define MAGOC_NPC_SWINGMASSEMPTY    1000
#define MAGOC_NPC_SWINGSCALE        75
#define MAGOC_NPC_SWINGSCALEEMPTY    75
#define MAGOC_NPC_SWINGFRICTION        1

void vehicle_magog_npc::RopeThread() {
    vector dist = getLocalJointPos( hatchJoint ) - getLocalJointPos( ropeJoint );
    vector pos = dist;
    float length = sys.vecLength( dist );
    vector momentum = g_vectorZero;

//	sys.print( "dist:" + dist + "\n" );

    while ( true ) {
        sys.waitFrame();

        float mass = MAGOC_NPC_SWINGMASS;
        float swingScale = MAGOC_NPC_SWINGSCALE;
        if ( item == $null_entity ) {
            mass        = MAGOC_NPC_SWINGMASSEMPTY;
            swingScale    = MAGOC_NPC_SWINGSCALEEMPTY;
        }

        float inverseMass = 1 / mass;
        float gravity = 1066 * mass;

        float len = length * MAGOC_NPC_SWINGLENGTHSCALE;
        len = len * len;

        float frameTime = sys.getFrameTime();

        vector selfV = getLinearVelocity();
        selfV_z = 0;

        vector force = selfV * swingScale;
        force_z += gravity;

        vector path = sys.vecNormalize( pos );
        path = path * ( -force_z / path_z );

        force += path;

        momentum += ( ( force - ( momentum * MAGOC_NPC_SWINGFRICTION ) ) * frameTime );
        vector v = momentum * inverseMass;

        pos_x = pos_x + ( v_x * frameTime );
        pos_y = pos_y + ( v_y * frameTime );
        pos_z = sys.sqrt( len - ( ( pos_x * pos_x ) + ( pos_y * pos_y ) ) );

//		sys.print( "pos_x: " + pos_x + "\n" );
//		sys.print( "v: " + v + "\n" );

        vector modelPos = worldToModelSpace( pos );
        setJointPos( ropeJoint, JOINTMOD_WORLD_OVERRIDE, getLocalJointPos( hatchJoint ) - modelPos );
        modelPos = sys.vecNormalize( modelPos );

        vector forward = '1 0 0' - ( modelPos_x * modelPos );
        forward = sys.vecNormalize( forward );
        forward = sys.vecToAngles( forward );

//		objectStable = sys.vecLength( v ) < 0.5f && sys.fabs( pos_z - ( length * MAGOC_NPC_SWINGLENGTHSCALE ) ) < 0.5f;

        setJointAngle( attachJoint, JOINTMOD_WORLD, forward );

//		sys.debugLine( g_colorRed, getJointPos( hatchJoint ), getJointPos( hatchJoint ) - pos, 0.f );
    }
}

boolean vehicle_magog_npc::PerformDrop() {
    // special cases
    if ( deployPlayerIndex == -1 ) {
        return true;
    }

    if ( item != $null_entity ) {
        if ( item.getIntKey( "always_drop" ) == 1 ) {
            return true;
        }
    }

    player p = sys.getClient( deployPlayerIndex );
    if ( getEntityAllegiance( p ) != TA_FRIEND ) {
        return false;
    }

    if( sys.getTerritoryForPoint( targetPos, getGameTeam(), 1.f, 0.f ) == $null_entity ) {
        return false;
    }

    return true;
}

void vehicle_magog_npc::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();
    if ( !sys.isClient() ) {
        setState( "Return" );
    }
}

void vehicle_magog_npc::UpdateEffects( vector pos ) {
    groundEffects = true;//( absMins.z - traceObject.endpos.z ) < groundEffectsThreshhold;

    vector end = pos;
    end_z -= groundEffectsThreshhold;

    sys.tracePoint( pos, end, MASK_SOLID | MASK_OPAQUE, self );

    if ( sys.getTime() >= ( lastGroundEffectsTime + 0.1f )
        && groundEffects && sys.getTraceFraction() < 1.0f ) {

        string surfaceTypeName = sys.getTraceSurfaceType();

        playOriginEffect( "fx_groundeffect", surfaceTypeName, sys.getTraceEndPos(), sys.getTraceNormal(), 0 );
        lastGroundEffectsTime = sys.getTime();
    }
}