Script:Files:script/items/third eye.script

From Mod Wiki
/***********************************************************************

item_third_eye.script

***********************************************************************/

// blend times
#define THIRD_EYE_IDLE_TO_LOWER        4
#define THIRD_EYE_IDLE_TO_PUNCH        0
#define THIRD_EYE_RAISE_TO_IDLE        4
#define THIRD_EYE_PUNCH_TO_IDLE        1

object item_third_eye : weapon_base {
    void            preinit();
    void            init();
    void            destroy();

    void            Raise();
    void            Idle();
    void            DeployedIdle();
    void            DeployedRaise();
    void            DeployedLower();
    void            Throw();

    void            Cleanup();

    boolean            InRemoteView();
    void            ToggleCamera();
    void            ClearCamera();
    entity            FindCamera();

    boolean            OnWeapNext();
    boolean            OnWeapPrev();
    void            OnZoomChanged();

    void            vCheckProficiency();

    float            throwTime;
    float            maxRange;
    float            meleeDistance;
    float            baseChargePerUse;

    void            CycleZoom();

    void            ToolTipThread_Raise();
    void            ToolTipThread_Deployed();

    zoomWidget        zoomer;

    boolean            deployedTipThreadActive;

    float            toolTipOutOfRange;
    boolean            replaceCamera;
}

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

    void            Idle();

    void            FreeGui();

    void            Fizzle();

    float            OnCollide( object traceObject, vector velocity, float bodyId );
    float            OnCollide_Private( entity collisionEnt, vector normal, float surfaceFlags, string joint );
    void            OnKilled( entity inflictor, entity attacker, float damage, vector dir, float location );
    void            OnTouch( entity other, object traceObject );
    void            OnPostThink();
    float            OnUpdateCrosshairInfo( entity p );

    void            OnBecomeRemoteCamera();
    void            OnFinishRemoteCamera();

    void            vOnBindMasterDestroyed() { remove(); }    

    void            vGiveSpotProficiency();

    void            vRemoveObject() { remove(); }

    boolean            PendingRemoval();
    void            ScheduleRemoval( float delay );
    void            MakeInactive();
    void            SetupContents();
    void            RemoveThread( float delay );

    void            OnTriggered();
    void            SelfDestructThread();
    void            vSetOwner( entity o );

    void            ClearCamera();

    void            UpdateCameraAngles();

    void            DoWaterExplosion( vector position, string surfaceType, vector normal );
    void            OnExploded();
    void            OnRadarStarted();
    void            OnUnbind();

    void            ShowIcon();
    void            FreeIcon();

    void            CancelMonitorTrailUnderWater();
    void            MonitorTrailUnderWater();

    float            removeThread;

    boolean            stuck;

    float            commandMapRadarHandle;
    float            commandMapIcon;

    float            splashDamageIndex;

    float            radarRange;
    float            radarMask;

    boolean            radarStarted;

    float            cameraGUIHandle;

    vector            endAngles;

    entity            owner;

    float            cameraJoint;

    boolean            triggered;
    float            detonateDelay;
    boolean            trailUnderWater;

}


/***********************************************************************

item_third_eye Implementation

***********************************************************************/

void item_third_eye::preinit() {
    baseChargePerUse = chargePerUse;

    throwTime            = getFloatKeyWithDefault( "throw_time", 0.2 );
    maxRange            = MetresToInches( getFloatKeyWithDefault( "max_range", 160 ) );
    meleeDistance        = getFloatKey( "melee_distance" );

    zoomer = new zoomWidget;
    zoomer.Init( self );

    toolTipOutOfRange    = GetToolTip( getKey( "tt_outofrange" ) );
}

void item_third_eye::init() {
    projectile_camera camera = FindCamera();

    if ( camera == $null_entity ) {
        if ( myPlayer.isLocalPlayer() ) {
            thread ToolTipThread_Raise();
        }
        weaponState( "Raise", 0 );
    } else {
        weaponState( "DeployedRaise", 0 );
    }
}

void item_third_eye::vCheckProficiency() {
    WeaponBase_CheckProficiency();

    UpdateCrosshair();
}

void item_third_eye::destroy() {
    ClearCamera();

    delete zoomer;
}

void item_third_eye::Cleanup() {    
    ClearCamera();

    // NB: we don't delete the zoomer as the weapon isn't actually destroyed
}

void item_third_eye::ClearCamera() {
    entity camera = FindCamera();
    if ( myPlayer != $null_entity && camera != $null_entity ) {
        if ( myPlayer.getRemoteCamera() == camera ) {
            myPlayer.setRemoteCamera( $null_entity );
        }
    }
}

void item_third_eye::ToggleCamera() {
    entity camera = FindCamera();
    if ( myPlayer != $null_entity && camera != $null_entity ) {
        if ( myPlayer.getRemoteCamera() == camera ) {
            myPlayer.setRemoteCamera( $null_entity );
        } else {
            myPlayer.setRemoteCamera( camera );
        }
    }
}

boolean item_third_eye::InRemoteView() {
    entity camera = FindCamera();
    if ( camera == $null_entity ) {
        return false;
    }
    return myPlayer.getRemoteCamera() == camera;
}

entity item_third_eye::FindCamera() {
    if ( myPlayer == $null_entity ) {
        return $null_entity;
    }

    float i;
    float num = myPlayer.binGetSize();

    for ( i = 0; i < num; i++ ) {
        projectile_camera other = myPlayer.binGet( i );
        if ( other != $null_entity ) {
            return other;
        }
    }

    return $null_entity;
}

void item_third_eye::Raise() {
    setupAnimClass( "anim_prefix" );

    UpdateCharge();

    Base_Raise();
}

void item_third_eye::Idle() {
    weaponReady();
    playCycle( ANIMCHANNEL_ALL, "idle" );

    projectile_camera camera;

    while ( true ) {
        if ( WEAPON_LOWERWEAPON ) {
            weaponState( "Lower", THIRD_EYE_IDLE_TO_LOWER );
        }
        if ( WEAPON_ATTACK ) {
            if ( !mainFireDown ) {
                mainFireDown = true;

                camera = FindCamera();
                if ( camera == $null_entity ) {
                    if ( CanRemove( chargePerUse ) ) {
                        weaponState( "Throw", THIRD_EYE_IDLE_TO_PUNCH );
                    } else {
                        if ( myPlayer.isLocalPlayer() ) {
                            myPlayer.sendToolTip( GetToolTip( getKey( "tt_need_charge" ) ) );
                            sys.startSoundDirect( getKey( "snd_need_charge" ), SND_WEAPON_FIRE_LOCAL );
                            G_NotifyNoCharge( myPlayer );
                        }
                    }
                } else {
                    // Gordon: panic?
                    // not so mysterious guy: ya!
                }
            }
        } else {
            mainFireDown = false;
        }

        UpdateCharge();

        sys.waitFrame();
    }
}

void item_third_eye::Throw() {
    playAnim( ANIMCHANNEL_ALL, "fire" );
    sys.wait( throwTime );

    if ( !sys.isClient() ) {
        projectile_camera camera = sys.spawn( getKey( "def_camera" ) );

        boolean placedOnWall = false;
        vector origin = myPlayer.getViewOrigin();
        vector forward = sys.angToForward( myPlayer.getViewAngles() );
        float throwDistance = 32.0f;
        vector throwPos = origin + forward * throwDistance;

        if( melee( MASK_SHOT_BOUNDINGBOX | MASK_PROJECTILE, meleeDistance, true, false ) ) {
            float traceFraction = getMeleeFraction();
            float traceDistance = traceFraction * meleeDistance;
            vector normal = getMeleeNormal();
            float surfaceFlags = getMeleeSurfaceFlags();
            entity meleeEntity = getMeleeEntity();
            string meleeJoint = getMeleeJoint();

            if ( !( surfaceFlags & SURF_NOPLANT ) ) {
                placedOnWall = true;
                if ( meleeEntity != $null_entity ) {
                    player meleePlayer = meleeEntity;
                    vehicle_base meleeVehicle = meleeEntity;
                    if ( meleeVehicle != $null_entity || meleePlayer != $null_entity ) {
                        placedOnWall = false;
                    }
                }

                if ( placedOnWall ) {
                    camera.setOrigin( getMeleeEndPos() );
                    camera.OnCollide_Private( meleeEntity, normal, surfaceFlags, meleeJoint );
                }
            }

            if ( !placedOnWall ) {
                vector size = camera.getSize();
                float pullOut = sys.vecLength( size ) * 0.6f;
                if ( traceDistance < throwDistance + pullOut ) {
                    throwPos = origin + forward * ( traceDistance - pullOut );
                }
            }
        }

        if ( !placedOnWall ) {
            camera.setOrigin( throwPos );
            vector velocity = camera.getVectorKeyWithDefault( "velocity", '400 0 0' );
            velocity = forward * velocity_x;
            camera.setLinearVelocity( velocity );
        }

        camera.vSetOwner( myPlayer );
        myPlayer.binAdd( camera );

        myPlayer.setPlayerCovertToolState( camera, false ); //mal: let the bots know theres a 3rd eye in the world.
    }

    Remove( chargePerUse );
    UpdateCharge();

    waitUntil( animDone( ANIMCHANNEL_ALL, 0 ) );

    // raise the deployed model
    weaponState( "DeployedRaise", 0 );
}

void item_third_eye::DeployedRaise() {
    setupAnimClass( "anim_prefix_alt" );

    if ( myPlayer.isLocalPlayer() ) {
        thread ToolTipThread_Deployed();
    }

    weaponRising();
    playAnim( ANIMCHANNEL_ALL, "pda_raise" );
    waitUntil( animDone( ANIMCHANNEL_ALL, THIRD_EYE_RAISE_TO_IDLE ) );
    weaponState( "DeployedIdle", THIRD_EYE_RAISE_TO_IDLE );
}

void item_third_eye::DeployedLower() {
    ClearCamera();

    weaponLowering();
    playAnim( ANIMCHANNEL_ALL, "pda_lower" );
    waitUntil( animDone( ANIMCHANNEL_ALL, 0 ) );
    weaponHolstered();
    waitUntil( WEAPON_RAISEWEAPON );
    weaponState( "DeployedRaise", 0 );
}

void item_third_eye::DeployedIdle() {
    boolean mainFireDown = false;
    boolean playingViewerSound = false;
    boolean zoomKeyDown = false;

    weaponReady();
    playCycle( ANIMCHANNEL_ALL, "pda_idle" );
    float gui = getGUI( "0" );

    projectile_camera camera = FindCamera();
    while ( true ) {
        boolean cameraEnabled = true;
        boolean inRemoteView = InRemoteView();

        if ( camera == $null_entity ) {    
            // put away the deployed model and raise the normal model
            playAnim( ANIMCHANNEL_ALL, "pda_lower" );
            waitUntil( animDone( ANIMCHANNEL_ALL, 0 ) );
            weaponState( "Raise", 0 );
        } else {
            if ( inRemoteView ) {
                myPlayer.setRemoteCameraViewOffset( camera.getWorldAxis( Z_AXIS ) * 8 );
            }

            // update the range value
            float range = sys.vecLength( myPlayer.getWorldOrigin() - camera.getWorldOrigin() );

            float currentMaxRange = maxRange;

            if ( camera.getMaskedEntityContents( CONTENTS_EXPLOSIONSOLID ) != 0 ) {
                currentMaxRange = 0;
            }

            float signalStrength = 0;
            if ( range >= currentMaxRange ) {
                cameraEnabled = false;

                if ( inRemoteView ) {
                    ClearCamera();
                    startSound( "snd_stop", SND_WEAPON_IDLE );
                }
            } else {
                signalStrength = ( currentMaxRange - range ) / currentMaxRange;
            }

            if ( sys.getLocalViewPlayer() == myPlayer ) {
                sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.signalStrength", signalStrength );
            }

            if ( inRemoteView ) {
                camera.endAngles = camera.getRemoteViewAngles( myPlayer );
                camera.UpdateCameraAngles();
            }
        }

        if ( WEAPON_LOWERWEAPON ) {
            weaponState( "DeployedLower", THIRD_EYE_IDLE_TO_LOWER );
        }

        if ( WEAPON_ALTFIRE ) {
            if ( !mainFireDown ) {
                mainFireDown = true;
                if ( cameraEnabled ) {
                    replaceCamera = false;

                    playAnim( ANIMCHANNEL_ALL, "pda_select1" );

                    if ( !inRemoteView ) {
                        sys.wait( 0.4 );
                    }

                    ToggleCamera();
                    inRemoteView = InRemoteView();

                    if ( inRemoteView ) {
                        // set up the camera's delta view axes properly
                        camera.setRemoteViewAngles( camera.endAngles, myPlayer );
                    }
                } else {
                    if ( replaceCamera ) {
                        replaceCamera = false;
                        if ( !sys.isClient() ) {
                            camera.vRemoveObject();
                        }
                    } else {
                        replaceCamera = true;
                        if ( myPlayer.isLocalPlayer() ) {
                            sys.startSoundDirect( getKey( "snd_invalid" ), SND_WEAPON_FIRE_LOCAL );
                            myPlayer.cancelToolTips();
                            sys.wait( 0.1 );
                            myPlayer.sendToolTip( toolTipOutOfRange );
                        }
                    }
                }
            }
        } else if ( WEAPON_ATTACK ) {
            if ( !mainFireDown ) {
                mainFireDown = true;
                if( cameraEnabled ) {
                    playAnim( ANIMCHANNEL_ALL, "pda_select2" );
                    sys.wait( 0.4 );

                    if ( !sys.isClient() ) {
                        if ( camera != $null_entity ) {
                            camera.OnTriggered();
                        }
                    }
                } else {
                    replaceCamera = true;
                    if ( myPlayer == sys.getLocalViewPlayer() ) {
                        sys.startSoundDirect( getKey( "snd_invalid" ), SND_WEAPON_FIRE_LOCAL );
                        myPlayer.cancelToolTips();
                        sys.wait( 0.1 );
                        myPlayer.sendToolTip( toolTipOutOfRange );
                    }
                }
            }
        } else {
            mainFireDown = false;
        }

        if ( inRemoteView ) {
            if ( myPlayer.getButton( PK_ACTIVATE ) ) {
                if ( !zoomKeyDown ) {
                    zoomKeyDown = true;
                    CycleZoom();
                }
            } else {
                zoomKeyDown = false;
            }
        } else {
            zoomKeyDown = false;
        }

        UpdateCharge();

        sys.waitFrame();
    }
}

void item_third_eye::CycleZoom() {
    if ( !InRemoteView() || !zoomer.IsEnabled() ) {
        return;
    }

    zoomer.Cycle();
    OnZoomChanged();
}

void item_third_eye::ToolTipThread_Raise() {
    myPlayer.cancelToolTips();
    sys.wait( myPlayer.CalcTooltipWait() );

    WAIT_FOR_TOOLTIP;
    if ( FindCamera() == $null_entity ) {
        myPlayer.sendToolTip( GetToolTip( getKey( "tt_intro_1" ) ) );
    }

    WAIT_FOR_TOOLTIP;
    if ( FindCamera() == $null_entity ) {
        myPlayer.sendToolTip( GetToolTip( getKey( "tt_intro_2" ) ) );
    }
}

void item_third_eye::ToolTipThread_Deployed() {
    if ( deployedTipThreadActive ) {
        return;
    }
    deployedTipThreadActive = true;

    WAIT_FOR_TOOLTIP;
    if ( FindCamera() != $null_entity ) {
        myPlayer.sendToolTip( GetToolTip( getKey( "tt_deployed_intro_1" ) ) );
    }

    WAIT_FOR_TOOLTIP;
    if ( FindCamera() != $null_entity ) {
        myPlayer.sendToolTip( GetToolTip( getKey( "tt_deployed_intro_2" ) ) );
    }

    deployedTipThreadActive = false;
}

boolean item_third_eye::OnWeapNext() {
    if ( InRemoteView() && zoomer.IsEnabled() ) {
        if ( !zoomer.IsFullyZoomedOut() ) {
            zoomer.ZoomOut();
            OnZoomChanged();
        }

        return true;
    }

    return false;
}

boolean item_third_eye::OnWeapPrev() {
    if ( InRemoteView() && zoomer.IsEnabled() ) {
        if ( !zoomer.IsFullyZoomedIn() ) {
            zoomer.ZoomIn();
            OnZoomChanged();
        }
        return true;
    }

    return false;
}

void item_third_eye::OnZoomChanged() {
    startSound( "snd_zoom", SND_WEAPON_SIG );

    projectile_camera camera = FindCamera();
    if ( camera != $null_entity ) {
        if ( camera.cameraGUIHandle != -1 ) {
            sys.guiPostNamedEvent( camera.cameraGUIHandle, "", "onZoomCycle" );
        }
    }
}

/***********************************************************************

projectile_camera Implementation

***********************************************************************/

void projectile_camera::preinit() {
    radarRange = getFloatKey( "range" );
    radarMask = getFloatKey( "mask" );

    detonateDelay = getFloatKey( "detonate_delay" );
    trailUnderWater        = getIntKey( "trailUnderWater" );

    commandMapRadarHandle        = sys.allocCMIcon( self, 100 );

    sys.setCMIconSizeMode( commandMapRadarHandle, SM_WORLD );
    sys.setCMIconMaterial( commandMapRadarHandle, GetMaterial( getKey( "mtr_radar" ) ) );
    sys.setCMIconUnknownMaterial( commandMapRadarHandle, GetMaterial( getKey( "mtr_radar" ) ) );
    sys.setCMIconSize( commandMapRadarHandle, radarRange * 2.f );
    sys.setCMIconUnknownSize( commandMapRadarHandle, radarRange * 2.f );
    sys.setCMIconFlag( commandMapRadarHandle, CMF_TEAMONLY );
    sys.hideCMIcon( commandMapRadarHandle );


    commandMapIcon = -1;

    ShowIcon();

    removeThread = -1;
    radarStarted = false;

    SetupContents();
}

void projectile_camera::SetupContents() {
    setContents( CONTENTS_PROJECTILE );
    setClipmask( MASK_SHOT_BOUNDINGBOX | MASK_PROJECTILE );
}

void projectile_camera::init() {

    float damageDeclIndex = sys.getDeclType( "damageDef" );
    splashDamageIndex    = sys.getDeclIndex( damageDeclIndex, getKey( "dmg_self_destruct" ) );

    string cameraJointName = getKey( "joint_camera" );
    if ( cameraJointName == "" ) {
        sys.error( "projectile_camera: No camera joint name" );
    }
    cameraJoint = getJointHandle( cameraJointName );

    setNetworkSynced( true );

    endAngles = owner.getViewAngles();

    setState( "Idle" );
}

void projectile_camera::syncFields() {
    syncBroadcast( "owner" );
    syncBroadcast( "radarStarted" );
    syncBroadcast( "triggered" );
    syncCallback( "triggered", "OnTriggered" );
    syncCallback( "radarStarted", "OnRadarStarted" );

    syncBroadcast( "endAngles" );
}

void projectile_camera::CancelMonitorTrailUnderWater() {
    sys.killThread( "MonitorTrailUnderWater_" + getName() );
}

void projectile_camera::MonitorTrailUnderWater() {
    while ( true ) {
        if ( isInWater() > 0.5f ) {
            stopEffect( "fx_trail" );
        }
        sys.waitFrame();
    }
}


void projectile_camera::Idle() {
    startSound( "snd_start", SND_WEAPON_IDLE );
    if ( trailUnderWater || isInWater() < 0.5f ) {
        playEffect( "fx_trail", "", 1 );
        if ( !trailUnderWater ) {
            CancelMonitorTrailUnderWater();
            thread MonitorTrailUnderWater();
        }
    }

    if ( !sys.isClient() ) {
        sys.wait( 2.f );
        radarStarted = true;
        OnRadarStarted();
    }
}

void projectile_camera::destroy() {
    sys.freeCMIcon( self, commandMapRadarHandle );
    FreeIcon();
    stopEffect( "fx_trail" );

    CancelMonitorTrailUnderWater();

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

    FreeGui();
}

// NOTE: If this returns true, all momentum on the object will be cleared, otherwise, it will bounce
float projectile_camera::OnCollide( object traceObject, vector velocity, float bodyId ) {
    return OnCollide_Private( traceObject.getTraceEntity(), traceObject.getTraceNormal(), traceObject.getTraceSurfaceFlags(), traceObject.getTraceJoint() );
}

float projectile_camera::OnCollide_Private( entity collisionEnt, vector normal, float surfaceFlags, string joint ) {
    float shaderFlags;
    vector dir;

    if ( stuck ) {
        return true;
    }

    if ( surfaceFlags & SURF_NOIMPACT ) {
        ScheduleRemoval( 0 );
        return true;
    }
    if ( surfaceFlags & SURF_NOPLANT ) {
        return false;
    }

    if ( PendingRemoval() ) {
        return true;
    }

    player pEnt = collisionEnt;
    if ( pEnt != $null_entity ) {
        return false;
    }

    if ( collisionEnt.vDisablePlantCharge() ) {
        return false;
    }

    // push the view out of the surface a bit
    vector origin = getWorldOrigin();
    origin += normal * getFloatKey( "push_out_scale" );
    setWorldOrigin( origin );

    // align to the surface normal
    alignToAxis( normal, Z_AXIS );

    if ( !sys.isClient() ) {
        stuck = true;
        freeze( 1.f );
        clearContacts();
        putToRest();
        if ( collisionEnt != $null_entity ) {
            float jointHandle = collisionEnt.getJointHandle( joint );
            if ( jointHandle != INVALID_JOINT ) {
                bindToJoint( collisionEnt, joint, 1 );
            } else {
                bind( collisionEnt );
            }
        }
    }

    return true;
}

void projectile_camera::MakeInactive() {
    unbind();
    hide();
    clearContacts();
    putToRest();
    forceDisableClip();
    setTakesDamage( false );
    stuck = true;
    freeze( 1.f );
}

boolean projectile_camera::PendingRemoval() {
    return removeThread != -1;
}

void projectile_camera::RemoveThread( float delay ) {
    if ( delay <= 0 ) {
        delay = sys.getFrameTime();
    }
    sys.wait( delay );
    if ( !sys.isClient() ) {
        remove();
    }
}

void projectile_camera::ScheduleRemoval( float delay ) {
    removeThread = thread RemoveThread( delay );
}

void projectile_camera::ClearCamera() {
    if ( owner != $null_entity ) {
        if ( owner.getRemoteCamera() == self ) {
            owner.setRemoteCamera( $null_entity );
        }
    }
}

void projectile_camera::UpdateCameraAngles() {
    setJointAngle( cameraJoint, JOINTMOD_LOCAL, endAngles );
}
void projectile_camera::OnKilled( entity inflictor, entity attacker, float damage, vector dir, float location ) {
    if ( PendingRemoval() ) {
        return;
    }

    ScheduleRemoval( 0 );
    ClearCamera();
}

void projectile_camera::OnTouch( entity other, object traceObject ) {
}

void projectile_camera::OnTriggered() {
    if ( PendingRemoval() ) {
        return;
    }

    triggered = true;
    thread SelfDestructThread();
}

void projectile_camera::SelfDestructThread() {
    startSound( "snd_trigger", SND_WEAPON_ARM );
    sys.wait( detonateDelay );

    if ( !sys.isClient() ) {
        if ( splashDamageIndex != -1 ) {
            sys.applyRadiusDamage( getWorldOrigin(), self, owner, self, self, splashDamageIndex, 1.f, 1.f );
        }
    }

    OnExploded();
    ClearCamera();
}

void projectile_camera::Fizzle() {
    if ( PendingRemoval() ) {
        return;
    }
    ScheduleRemoval( 0 );
}

void projectile_camera::vSetOwner( entity o ) {
    owner = o;
    setGameTeam( owner.getGameTeam() );
}

void projectile_camera::OnExploded() {
    if ( !sys.isClient() ) {
        player p = owner;
        if ( p != $null_entity ) {
            p.setPlayerCovertToolState( self, true );
        }
    }

    if ( isInWater() > 0.5f ) {
        vector newOrg = getWorldOrigin();
        DoWaterExplosion( newOrg, "", '0 0 1' );
    } else {
        playMaterialEffect( "fx_explode", g_colorWhite, "", "", 0 );
    }
    ScheduleRemoval( 0 );
}

void projectile_camera::DoWaterExplosion( vector position, string surfaceType, vector normal ) {
    entitiesOfClass( sys.getTypeHandle( "sdLiquid" ), 0 );
    float count = filterEntitiesByTouching( 1 );

    if ( count > 0 ) {
        entity other = getBoundsCacheEntity( 0 );
        vector top = other.getAbsMaxs();

        position_z = top_z;
    }

    playOriginEffect( "fx_explode_water", surfaceType, position, normal, 0 );
}


void projectile_camera::OnRadarStarted() {
    freeLayers();

    if ( radarStarted ) {
        // start the radar
        sys.showCMIcon( commandMapRadarHandle );

        float radarLayer = allocRadarLayer();                
        radarSetLayerRange( radarLayer, radarRange );
        radarSetLayerMask( radarLayer, radarMask );
    }
}

void projectile_camera::FreeGui() {
    if ( cameraGUIHandle != -1 ) {
        sys.freeHudModule( cameraGUIHandle );
        cameraGUIHandle = -1;
    }
}

void projectile_camera::OnBecomeRemoteCamera() {
    FreeGui();

    cameraGUIHandle = sys.allocHudModule( getKey( "gui_camera_view" ), getFloatKeyWithDefault( "hud_sort", 0 ), false );

    hide();

    item_third_eye controller = owner.getWeaponEntity();
    if ( controller != $null_entity ) {
        controller.startSound( "snd_start", SND_WEAPON_IDLE );
        controller.myPlayer.inhibitGuis( true );
        controller.zoomer.Enable();
    }
}

void projectile_camera::OnFinishRemoteCamera() {
    FreeGui();

    show();

    item_third_eye controller = owner.getWeaponEntity();
    if ( controller != $null_entity ) {
        controller.myPlayer.inhibitGuis( false );
        controller.startSound( "snd_stop", SND_WEAPON_IDLE );
        controller.zoomer.Disable();
    }
}

void projectile_camera::vGiveSpotProficiency() {
    if ( owner == $null_entity ) {
        return;
    }

    team_base team = getGameTeam();
    if ( team != $null ) {
        team.GiveRadarSpottingProficiency( owner );
    }
}

void projectile_camera::OnUnbind() {
    Fizzle();
}

void projectile_camera::OnPostThink() {
    vector velocity = getLinearVelocity();
    float velSqr = sys.vecLengthSquared( velocity );

    if ( velSqr < 10.f ) {
        stopEffect( "fx_trail" );
    }
    if ( isBound() ) {
        forceRunPhysics();
    }
}

float projectile_camera::OnUpdateCrosshairInfo( entity p ) {
    if ( sys.getLocalPlayer() == $null_entity ) {
        return 1.f;
    }

    chSetNumLines( 0 );

    float distance = chGetDistance();
    float range = InchesToMetres( distance );
    float allegiance = getEntityAllegiance( p );
    vector color = GetAllegianceColor( allegiance );
    float index;

    if ( range < 25 ) {
        index = chAddLine();
        chSetLineText( index, sys.localizeStringArgs( "game/weapons/third_eye_camera" ) );
        chSetLineColor( index, color, 1.f );
        chSetLineType( index, CI_TEXT );
        chSetLineSize( index, 0, 0 );
    }

    return 1.f;
}

/*
=====================
projectile_camera::ShowIcon
=====================
*/
void projectile_camera::ShowIcon() {
    if ( commandMapIcon == -1 ) {
        commandMapIcon = sys.allocCMIcon( self, getFloatKey( "icon_sort_cm" ) );
        sys.setCMIconUnknownMaterial( commandMapIcon, GetMaterial( getKey( "mtr_commandmapicon" ) ) );
        sys.setCMIconMaterial( commandMapIcon, GetMaterial( getKey( "mtr_commandmapicon" ) ) );
        sys.setCMIconDrawMode( commandMapIcon, DM_MATERIAL );
        sys.setCMIconSize( commandMapIcon, 8.0f );
        sys.setCMIconFlag( commandMapIcon, CMF_TEAMONLY );
    }
}

/*
=====================
projectile_camera::FreeIcon
=====================
*/
void projectile_camera::FreeIcon() {
    if( commandMapIcon != -1 ) {
        sys.freeCMIcon( self, commandMapIcon );
        commandMapIcon = -1;
    }
}