Script:Files:script/weapons/clipweapon.script

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

weapon_clip

 This is a base class for weapons that use ammo from clips with a finite number
 of bullets

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

object weapon_clip : weapon_base {

    void        preinit();
    void        init();
    void        destroy();
    void        Raise();
    void        Lower();

    //
    // General stuff
    //
    void        Idle();
    void        IdleInit();
    void        IdleClipWeapon();

    boolean        DisableSpread();

    boolean        hasIronSights;
    boolean        hasIronSightsLock;
    boolean        hasTargetLock;
    boolean        hasScope;
    boolean        hasScopeSway;
    boolean        hasScopeZoom;
    boolean        hasScopeLock;
    boolean        hasEndFireAnim;
    boolean        usesStroyent;
    boolean        hasHeat;
    boolean        infiniteAmmo;

    boolean        localScopeEffectsActive;    // Effects should be active, if this is the local view
    boolean        localScopeEffectsEnabled;    // Effects are actually active

    float        burstCount;

    //
    // Firing stuff
    //
    boolean        CanFire() { return true; }
    void        DoFire();
    void        FireBurst( float maxBurst );
    void        FireCommon();
    void        FireAuto();
    void        FireSingle();
    void        EndFire();
    void        PlayBrassSound();
    void        StopBrassSound();
    void        PlayFireAnim();

    boolean        AllowSprint();

    float        refireTime;
    float        fireRateSingle;
    float        fireRateAuto;
    boolean        playingBrassSound;
    boolean        playingFireAnim;

    //
    // Reloading stuff
    //
    boolean        CanReload();
    boolean        NeedsReload();
    void        Reload();
    float        DoReload( boolean wasScoped );

    void        BeginFireSingleReloadTime();
    void        EndFireSingleReloadTime();

    boolean        dryfireAttack;

    //
    // Heat
    //
    void        Overheat();

    //
    // Callbacks
    //
    boolean        IsScopeZoomed();
    float        OnActivate( entity p, float distance );
    float        OnActivateHeld( entity p, float distance );
    float        OnUsed( entity p, float distance );
    boolean        OnWeapNext();
    boolean        OnWeapPrev();
    void        Cleanup();

    void        OnIronSightsEnabled();
    void        OnIronSightsDisabled();

    //
    // Scope stuff
    //
    void        CheckScope();
    void        CycleZoom();
    void        ScopeUp();
    void        ScopeDown( boolean allowBlocking );
    void        LoadScopeGUI();
    void        FreeScopeGUI();
    void        UpdateScopeScale();
    void        OnScopeZoomCycle();
    void        vSetScopeState( boolean up );

    void        RemoveLocalScopeEffects();
    void        AddLocalScopeEffects();

    void        OnBecomeViewWeapon();
    void        OnFinishViewWeapon();

    void        ToolTipThread_Raise();
    void        ToolTipThread_Scope();

    boolean        sniperScopeUp;
    boolean        zoomKeyDown;
    boolean        changingScopeStatus;
    zoomWidget    zoomer;
    float        scopeGUIHandle;

    boolean        scopeThreadDone;

    boolean        showSingleReloadTime;
    boolean        fireSingleReloadTimeActive;
}

void weapon_clip::preinit() {
    showSingleReloadTime = getFloatKeyWithDefault( "show_single_reload_time", 0 ) != 0;

    hasIronSights    = true;
    hasTargetLock    = false;
    hasScope        = false;
    hasScopeSway    = true;
    hasScopeZoom    = false;
    hasScopeLock    = false;
    hasEndFireAnim    = false;
    usesStroyent    = false;
    infiniteAmmo    = false;

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

    scopeGUIHandle    = -1;

    fireRateAuto    = getFloatKeyWithDefault( "fire_rate", 0.2f );
    fireRateSingle    = 0.15f;

    refireTime        = -1.0f;
}

boolean weapon_clip::DisableSpread() {
    if ( hasScopeSway ) {
        if ( myPlayer != $null_entity ) {
            return myPlayer.IsSniperScopeUp();
        }
    }
    return false;
}

void weapon_clip::init() {
    myPlayer.SetSniperScopeUp( false );
    setBlendFrames( ANIMCHANNEL_ALL, 4 );

    if ( hasTargetLock ) {
        enableTargetLock( 1.0f );
    }

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

    weaponState( "Raise", 0 );
}

void weapon_clip::destroy() {
    if ( hasScope ) {
        ScopeDown( false );
    }

    RemoveLocalScopeEffects();

    if ( ShouldRunGuis() ) {
        KillUpdateHeat();
    }

    delete zoomer;

    StopBrassSound();
}

void weapon_clip::Raise() {
    if ( hasHeat && ShouldRunGuis() ) {
        CreateUpdateHeat();
    }

    Base_Raise();
}

void weapon_clip::Lower() {
    if ( ShouldRunGuis() ) {
        KillUpdateHeat();
    }

    Cleanup();
    Base_Lower();
}

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

General stuff

=====================================================================
*/
void weapon_clip::Idle() {
    IdleInit();
    while ( true ) {
        IdleClipWeapon();
        sys.waitFrame();
    }
}

void weapon_clip::IdleInit() {
    weaponReady();

    PlayIdleAnim();

    playingFireAnim = false;
    altFireDown = WEAPON_ALTFIRE;
}

void weapon_clip::IdleClipWeapon() {
    if ( !myPlayer.IsSniperScopeUp() ) {
        // if its not already playing the idle effect it'll start playing it
        StartIdleEffect();
    }

    float time = sys.getTime();

    if ( hasIronSights ) {
        CheckIronSights();
    } else if ( hasScope ) {
        CheckScope();
    }

    if ( WEAPON_LOWERWEAPON ) {
        StopIdleEffect();
        weaponState( "Lower", 4 );
    }
    if ( WEAPON_RELOAD && CanReload() ) {
        StopIdleEffect();
        weaponState( "Reload", 4 );
    }

    if ( WEAPON_ATTACK ) {
        myPlayer.disableSprint( 1.f );

        if ( CanFire() && !NeedsReload() ) {
            dryfireAttack = false;
            if ( time >= refireTime ) {
                DoFire();
                mainFireDown = true;
            }
        } else {
            if ( !dryfireAttack ) {
                startSound( "snd_dryfire", SND_WEAPON_DRYFIRE );
                dryfireAttack = true;
            }
            if ( CanReload() ) {
                StopIdleEffect();
                weaponState( "Reload", 4 );
            }
        }
    } else {
        if ( myPlayer.getButton( PK_ATTACK ) ) {
            if ( !dryfireAttack ) {
                startSound( "snd_dryfire", SND_WEAPON_DRYFIRE );
                dryfireAttack = true;
            }
        } else {
            dryfireAttack = false;
        }
        burstCount = 0;
        mainFireDown = false;

        if ( AllowSprint() ) {
            myPlayer.disableSprint( 0.f );

            if ( myPlayer.AI_SPRINT ) {
                if ( hasScope ) {
                    ScopeDown( true );
                }
                StopIdleEffect();
                weaponState( "Sprint", 4 );
            }
        } else {
            myPlayer.disableSprint( 1.f );
        }
    }

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

    if ( !sys.isClient() ) {
        if ( IsOverheated() ) {
            StopIdleEffect();
            weaponState( "Overheat", 4 );
        }
    }


    if ( time > refireTime && refireTime != -1.0f ) {
        EndFire();
        refireTime = -1.0f;
    }

    if ( playingFireAnim && refireTime == -1.0f ) {
        if ( animDone( ANIMCHANNEL_ALL, 0 ) ) {
            PlayIdleAnim();
            playingFireAnim = false;
        }
    }
}

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

Firing functions

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

void weapon_clip::DoFire() {
    if ( myPlayer.IsSniperScopeUp() ) {
        if ( !mainFireDown ) {
            FireSingle();
        }
    } else {
        FireAuto();
    }
}

void weapon_clip::PlayFireAnim() {
    if ( ironSightsEnabled || myPlayer.IsSniperScopeUp() ) {
        playAnim( ANIMCHANNEL_ALL, "fire_zoom" );
    } else {
        playAnim( ANIMCHANNEL_ALL, "fire" );
    }
}

void weapon_clip::FireBurst( float maxBurst ) {
    if ( burstCount >= maxBurst ) {
        return;
    }

    FireAuto();
}

void weapon_clip::FireCommon() {
    PlayFireEffect();
    PlayFireSound();
    PlayBrassSound();
    PlayFireAnim();

    burstCount++;
    playingFireAnim = true;
    launchProjectiles( numProjectiles, 0, getCurrentSpread(), 0, 1, 1 );
    increaseSpread();

    if ( !infiniteAmmo ) {
        if ( !usesStroyent ) {
            UseAmmo( 0 );
            AmmoCheckClip( 0 );
        } else {
            UseAmmo_Stroyent( 0 );
            AmmoCheck( 0 );
        }
    }

    if ( hasHeat ) {
        AddHeat();
    }
}

void weapon_clip::FireAuto() {
    FireCommon();

    refireTime = sys.getTime() + fireRateAuto;
}

void weapon_clip::FireSingle() {
    FireCommon();

    playEffect( "fx_tracer", "muzzle", 0 );

    refireTime = sys.getTime() + fireRateSingle;

    if ( showSingleReloadTime ) {
        BeginFireSingleReloadTime();
    }
}

void weapon_clip::EndFire() {
    StopBrassSound();
    resetTracerCounter();

    if ( fireSingleReloadTimeActive ) {
        EndFireSingleReloadTime();
    }

    if ( hasEndFireAnim ) {
        if ( !ironSightsEnabled ) {
            playAnim( ANIMCHANNEL_ALL, "fire_end" );
            waitUntil( animDone( ANIMCHANNEL_ALL, 1 ) );
            playingFireAnim = true;
        }
    }
}

void weapon_clip::PlayBrassSound() {
    if ( !playingBrassSound ) {
        if ( sys.getLocalPlayer() == myPlayer ) {
            playingBrassSound = true;
            startSound( "snd_brass_loop", SND_WEAPON_BRASS );
        }
    }
}

void weapon_clip::StopBrassSound() {
    if ( playingBrassSound ) {
        playingBrassSound = false;
        startSound( "snd_brass_stop", SND_WEAPON_BRASS );
    }
}

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

Reloading functions

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

boolean weapon_clip::CanReload() {
    if ( !usesStroyent && !infiniteAmmo ) {
        if ( ( ammoAvailable( 0 ) > ammoInClip( 0 ) ) && ( ammoInClip( 0 ) < clipSize( 0 ) ) ) {
            return true;
        }
    }

    return false;
}

boolean weapon_clip::NeedsReload() {
    if ( !infiniteAmmo ) {
        if ( !usesStroyent ) {
            if ( ammoInClip( 0 ) <= 0 ) {
                return true;
            }
        } else {
            if ( ammoAvailable( 0 ) <= 0 ) {
                return true;
            }
        }
    }

    return false;
}

void weapon_clip::Reload() {
    myPlayer.disableSprint( 1.f );

    StopBrassSound();

    Base_BeginReload();

    boolean wasEnabled = false;
    float lastZoomState;
    if ( hasIronSights ) {
        wasEnabled = wantsIronSights;
        if ( wasEnabled ) {
            DisableIronSights();
            waitUntil( !ironSightsThreadActive );
        }
    } else if ( hasScope ) {
        wasEnabled = sniperScopeUp;
        if ( wasEnabled ) {
            lastZoomState = zoomer.GetZoomState();
            ScopeDown( true );
        }
    }

    float stayEnabled = DoReload( wasEnabled );

    if ( stayEnabled != 0.0f ) {
        if ( hasIronSights ) {
            EnableIronSights();
            waitUntil( !ironSightsThreadActive );
        } else if ( hasScope ) {
            ScopeUp();
            zoomer.Zoom( lastZoomState );
            UpdateScopeScale();
        }
    }

    Base_EndReload();
    weaponState( "Idle", 4 );
}

float weapon_clip::DoReload( boolean wasScoped ) {
    weaponReloading();
    playAnim( ANIMCHANNEL_ALL, "reload" );

    wasScoped = false; // disabled for now

    boolean altKeyDown;
    float stayEnabled = wasScoped;
    while ( !animDone( ANIMCHANNEL_ALL, 4 ) ) {
        sys.waitFrame();

        if ( WEAPON_HIDE ) {
            Base_EndReload();
            weaponState( "Idle", 4 );            
        }

        if ( WEAPON_LOWERWEAPON ) {
            Base_EndReload();
            weaponState( "Lower", 4 );
        }

        if ( WEAPON_ALTFIRE ) {
            if ( !altKeyDown ) {
                altKeyDown = true;
                stayEnabled = 1 - stayEnabled;
            }
        } else {
            altKeyDown = false;
        }
    }

    addToClip( 0, clipSize( 0 ) );

    return stayEnabled;
}

void weapon_clip::BeginFireSingleReloadTime() {
    if ( ShouldRunGuis() ) {
        if ( fireSingleReloadTimeActive ) {
            EndFireSingleReloadTime();
        }

        fireSingleReloadTimeActive = true;
        sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.weaponReloadTime", fireRateSingle );
    }
}

void weapon_clip::EndFireSingleReloadTime() {
    if ( ShouldRunGuis() ) {
        fireSingleReloadTimeActive = false;
        sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.weaponReloadTime", 0 );
    }
}

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

Heat

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

void weapon_clip::Overheat() {
    resetTracerCounter();

    boolean wasEnabled = false;
    float lastZoomState;
    if ( hasIronSights ) {
        wasEnabled = wantsIronSights;
        if ( wasEnabled ) {
            DisableIronSights();
            waitUntil( !ironSightsThreadActive );
        }
    } else if ( hasScope ) {
        wasEnabled = sniperScopeUp;
        if ( wasEnabled ) {
            lastZoomState = zoomer.GetZoomState();
            ScopeDown( true );
        }
    }

    weaponReloading();
    StartCooling();

    if ( sys.getLocalViewPlayer() != myPlayer || pm_thirdperson.getBoolValue() ) {
        entity worldModel = getWorldModel( 0 ); // FIXME
        worldModel.playEffect( "fx_overheat_world", "muzzle", 0.0f );
    }

    playAnim( ANIMCHANNEL_ALL, "overheat" );
    boolean altKeyDown;
    float stayEnabled = false;//wasEnabled;
    while ( !animDone( ANIMCHANNEL_ALL, 4 ) ) {
        if ( WEAPON_ALTFIRE ) {
            if ( !altKeyDown ) {
                altKeyDown = true;
                stayEnabled = 1 - stayEnabled;
            }
        } else {
            altKeyDown = false;
        }

        sys.waitFrame();
    }

    FinishCooling();

    if ( stayEnabled != 0.0f ) {
        if ( hasIronSights ) {
            EnableIronSights();
            waitUntil( !ironSightsThreadActive );
        } else if ( hasScope ) {
            ScopeUp();
            zoomer.Zoom( lastZoomState );
            UpdateScopeScale();
        }
    }

    weaponState( "Idle", 4 );
}


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

Callbacks

=====================================================================
*/
boolean weapon_clip::IsScopeZoomed() {
    if ( hasScope && hasScopeZoom && ( myPlayer.IsSniperScopeUp() ) && zoomer.IsEnabled() ) {
        return true;
    }
    return false;
}

float weapon_clip::OnActivate( entity p, float distance ) {
    return IsScopeZoomed();
}

float weapon_clip::OnActivateHeld( entity p, float distance ) {
    return IsScopeZoomed();
}

float weapon_clip::OnUsed( entity p, float distance ) {
    return IsScopeZoomed();
}

boolean weapon_clip::OnWeapNext() {
    if ( IsScopeZoomed() ) {
        if ( !zoomer.IsFullyZoomedOut() ) {
            startSound( "snd_zoomout", SND_WEAPON_MODE );
        }

        zoomer.ZoomOut();
        UpdateScopeScale();
        return true;
    }

    return false;
}

boolean weapon_clip::OnWeapPrev() {
    if ( IsScopeZoomed() ) {
        if ( !zoomer.IsFullyZoomedIn() ) {
            startSound( "snd_zoomin", SND_WEAPON_MODE );
        }

        zoomer.ZoomIn();
        UpdateScopeScale();
        return true;
    }

    return false;
}

void weapon_clip::Cleanup() {
    if ( hasIronSights ) {
        if ( ironSightsThreadActive || ironSightsEnabled ) {
            DisableIronSights_Private( false );
        }
        StopIronSightsThread();
        ironSightsThreadActive = false;
    } else if ( hasScope ) {
        ScopeDown( false );
    }

    StopBrassSound();
}


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

Zoom functions

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

void weapon_clip::vSetScopeState( boolean up ) {
    if ( up ) {
        thread ScopeUp();
    } else {
        thread ScopeDown( true );
    }
}

void weapon_clip::CheckScope() {
    if ( WEAPON_ALTFIRE ) {
        if ( !altFireDown  ) {

            // toggle the scope
            if ( !sys.isClient() ) {
                if ( myPlayer.IsSniperScopeUp() ) {
                    thread ScopeDown( true );
                } else {
                    thread ScopeUp();
                }
            }

            altFireDown = true;
        }
    } else {
        altFireDown = false;
    }
}

void weapon_clip::CycleZoom() {
    if ( !myPlayer.IsSniperScopeUp() || !zoomer.IsEnabled() ) {
        return;
    }

    sys.assert( zoomer.enabled );

    startSound( "snd_zoomin", SND_WEAPON_MODE );

    zoomer.Cycle();

    UpdateScopeScale();

    OnScopeZoomCycle();
}

void weapon_clip::LoadScopeGUI() {
    FreeScopeGUI();

    string key = getKey( "gui_sniper_scope" );
    if( sys.strLength( key ) > 0 ) {
        scopeGUIHandle = sys.allocHudModule( getKey( "gui_sniper_scope" ), getFloatKeyWithDefault( "hud_sort", 0 ), false );
    }
}

void weapon_clip::FreeScopeGUI() {
    if ( scopeGUIHandle != -1 ) {
        sys.freeHudModule( scopeGUIHandle );
        scopeGUIHandle = -1;
    }
}

void weapon_clip::UpdateScopeScale() {
    if ( scopeGUIHandle == -1 ) {
        return;
    }

    float state = zoomer.GetZoomState();

    sys.setGUIFloat( scopeGUIHandle, "zoomState", state );

    if ( state == 0 ) {
        sys.setGUIFloat( scopeGUIHandle, "zoomCycleTransition", -( zoomer.GetNumZoomStates() - 1 ) );
    } else {
        sys.setGUIFloat( scopeGUIHandle, "zoomCycleTransition", 1 );
    }
}

void weapon_clip::OnScopeZoomCycle() {
    if ( scopeGUIHandle == -1 ) {
        return;
    }

    sys.guiPostNamedEvent( scopeGUIHandle, "", "onZoomCycle" );
}

void weapon_clip::OnBecomeViewWeapon() {
    if ( localScopeEffectsActive ) {
        AddLocalScopeEffects();
    }
    if ( localIronSightsEffectsActive ) {
        AddLocalIronSightsEffects();
    }
    if ( hasHeat && ShouldRunGuis() ) {
        CreateUpdateHeat();
    }
    UpdateCrosshair();
}

void weapon_clip::OnFinishViewWeapon() {
    KillUpdateHeat();
    sys.setGUIFloat( GUI_GLOBALS_HANDLE, "gameHud.weaponReloadTime", 0 );
    sys.setGUIFloat( GUI_GLOBALS_HANDLE, "weapons.cooling", 0.f );

    RemoveLocalScopeEffects();
    RemoveLocalIronSightsEffects();
}

void weapon_clip::AddLocalScopeEffects() {
    if ( localScopeEffectsEnabled ) {
        return;
    }

    if ( myPlayer == $null_entity ) {
        return;
    }

    if ( myPlayer == sys.getLocalViewPlayer() ) {
        localScopeEffectsEnabled = true;

        HideSights();
        string modelKey = getKey( "def_scopemodel" );
        if ( modelKey != "" ) {
            myPlayer.enableClientModelSights( modelKey );
        }
        LoadScopeGUI();
    }
}

void weapon_clip::ScopeUp() {
    while ( changingScopeStatus ) {
        sys.waitFrame();
    }

    if ( sniperScopeUp ) {
        UpdateCrosshair();
        return;
    }

    changingScopeStatus = true;
    sniperScopeUp = true;
    myPlayer.SetSniperScopeUp( true );

    StopIdleEffect();
    playAnim( ANIMCHANNEL_ALL, "zoomin" );
    sys.waitFrame();

    while ( !animDone( ANIMCHANNEL_ALL, 4 ) ) {
        sys.waitFrame();

        // there is an off chance that during this wait, a non-blocking ScopeDown gets called,
        // which will have hijacked the purpose of this since it was called AFTER ScopeUp
        // the end result desired is for the scope to be down anyway, so we might as well abort out of here
        if ( !sniperScopeUp ) {
            return;
        }        
    }

    setBlendFrames( ANIMCHANNEL_ALL, 4 );
    playCycle( ANIMCHANNEL_ALL, "idle_zoom" );
    myPlayer.setSpeedModifier( speedMod * fovSpeed );

    hide();

    localScopeEffectsActive = true;
    AddLocalScopeEffects();

    UpdateScopeScale();

    zoomer.Enable();
    startSound( "snd_scopeup", SND_WEAPON_SIG );
    myPlayer.setSniperAOR( 1.f );

    if ( hasScopeSway ) {
        enableSway( 1.f );
    }

    UpdateSpreadModifiers();

    changingScopeStatus = false;

    if ( hasScopeLock ) {
        enableTargetLock( 1 );
    }

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

    UpdateCrosshair();
}

void weapon_clip::RemoveLocalScopeEffects() {
    if ( !localScopeEffectsEnabled ) {
        return;
    }
    localScopeEffectsEnabled = false;

    ShowSights();
    myPlayer.disableClientModelSights();
    FreeScopeGUI();
}

void weapon_clip::ScopeDown( boolean allowBlocking ) {
    if ( allowBlocking ) {
        while ( changingScopeStatus ) {
            sys.waitFrame();
        }
    }

    if ( !sniperScopeUp ) {
        return;
    }

    if ( !hasTargetLock ) {
        enableTargetLock( 0 );
    }

    changingScopeStatus = true;

    sniperScopeUp = false;
    if ( myPlayer != $null_entity ) {
        myPlayer.SetSniperScopeUp( false );
        myPlayer.setSpeedModifier( speedMod );
    }

    localScopeEffectsActive = false;
    RemoveLocalScopeEffects();

    show();

    zoomer.Disable();
    startSound( "snd_scopedown", SND_WEAPON_SIG );

    if ( allowBlocking ) {
        playAnim( ANIMCHANNEL_ALL, "zoomout" );
        waitUntil( animDone( ANIMCHANNEL_ALL, 4 ) );
    }

    setBlendFrames( ANIMCHANNEL_ALL, 4 );
    PlayIdleAnim();

    if ( myPlayer != $null_entity ) {
        myPlayer.setSniperAOR( 0.f );
    }

    if ( hasScopeSway ) {
        enableSway( 0.f );
    }

    UpdateSpreadModifiers();

    changingScopeStatus = false;

    UpdateCrosshair();
}

void weapon_clip::OnIronSightsDisabled() {
    if ( !hasTargetLock ) {
        enableTargetLock( 0 );
    }
    UpdateCrosshair();
}

void weapon_clip::OnIronSightsEnabled() {
    if ( hasIronSightsLock ) {
        enableTargetLock( 1 );
    }
    UpdateCrosshair();
}

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

    WAIT_FOR_TOOLTIP;
    myPlayer.sendToolTip( GetToolTip( getKey( "tt_intro_1" ) ) );

    WAIT_FOR_TOOLTIP;
    myPlayer.sendToolTip( GetToolTip( getKey( "tt_intro_2" ) ) );

    if ( hasIronSights ) {
        WAIT_FOR_TOOLTIP;
        myPlayer.sendToolTip( GetToolTip( getKey( "tt_intro_ironsights_1" ) ) );

        WAIT_FOR_TOOLTIP;
        myPlayer.sendToolTip( GetToolTip( getKey( "tt_intro_ironsights_2" ) ) );
    }

    if ( hasScope ) {
        WAIT_FOR_TOOLTIP;
        myPlayer.sendToolTip( GetToolTip( getKey( "tt_intro_scope" ) ) );
    }

    if ( hasHeat ) {
        WAIT_FOR_TOOLTIP;
        myPlayer.sendToolTip( GetToolTip( getKey( "tt_intro_heat" ) ) );
    } else {
        WAIT_FOR_TOOLTIP;
        myPlayer.sendToolTip( GetToolTip( getKey( "tt_intro_reload" ) ) );
    }
}

void weapon_clip::ToolTipThread_Scope() {
    if ( scopeThreadDone ) {
        return;
    }
    scopeThreadDone = true;

    WAIT_FOR_TOOLTIP;
    myPlayer.sendToolTip( GetToolTip( getKey( "tt_intro_scope_up_1" ) ) );

    WAIT_FOR_TOOLTIP;
    myPlayer.sendToolTip( GetToolTip( getKey( "tt_intro_scope_up_2" ) ) );
}

boolean weapon_clip::AllowSprint() {
    return !myPlayer.IsSniperScopeUp() && sys.getTime() > refireTime;
}