// ----------------------------------------------------------------------
// Gretchen Counter
// ----------------------------------------------------------------------
//
// This object clearly looks like it was recycled from a very old
// Geiger counter.
//
// Smells kind of like rotting meat.
//
// Try not to think about it.
//

#include "zscript/snektech.zs"

const HDLD_KIRI_GRETCHENCOUNTER = "kgc";

enum GretchenCounterStatus
{
    KGC_BATTERY = 1,
    KGC_ACTIVE  = 2
}

class GretchenCounter : HDWeapon
{
    default
    {
        +weapon.wimpy_weapon;
        +inventory.invbar;
        +hdweapon.droptranslation;
        +hdweapon.fitsinbackpack;
        hdweapon.barrelsize 0,0,0;
        weapon.selectionorder 1014;

        scale 0.6;
        inventory.icon "KGCPA0";
        inventory.pickupmessage "Picked up a Gretchen Counter.";
        inventory.pickupsound "derp/crawl";
        translation 0;
        tag "Gretchen Counter";

        hdweapon.refid HDLD_KIRI_GRETCHENCOUNTER;
    }

    float lastReading;
    float angularVelocity;
    float needlePosition;
    int framesSinceLastBeep;
    int lastBatteryCheckFrame;

    override bool AddSpareWeapon(actor newowner)
    {
        return AddSpareWeaponRegular(newowner);
    }

    override HDWeapon GetSpareWeapon(actor newowner, bool reverse, bool doselect)
    {
        return GetSpareWeaponRegular(newowner, reverse, doselect);
    }

    int hash(int x)
    {
        x = ((x >> 16) ^ x) * 0x45d9f3b;
        x = ((x >> 16) ^ x) * 0x45d9f3b;
        x = (x >> 16) ^ x;
        return x;
    }

    bool HasBatteryCharge()
    {
        int bat = weaponstatus[KGC_BATTERY];
        if(bat > 0) {

            int timeHash = hash(level.time / 10);
            if((timeHash % 5) < bat) {
                return true;
            }
        }
        return false;
    }

    void TickBattery()
    {
        if(weaponstatus[KGC_ACTIVE] &&
            weaponstatus[KGC_BATTERY] > 0)
        {
            // Discharge rate: 1/1000 chance per tick.
            int r = random(0, 16000);
            if(r < 2) {
                weaponstatus[KGC_BATTERY] -= 1;
            }
        }
    }

    states
    {
    spawn:
        KGCP # 1 { frame = invoker.GetIsActive(); }
        loop;

    // Needle overlay states.
    needle_indicator:
        KGCN # 1; wait;

	select0:
		KGCM # 1 {
            updateOverlay();
        }
        goto select0big;

    deselect0:
		KGCM # 1 {
            updateOverlay();
        }
        goto deselect0big;

	user3:
		#### A 0 A_MagManager("HDBattery");
		goto ready;


    unload:
        KGCM # 1 offset(0, 20) { updateOverlay(); }
        KGCM # 1 offset(0, 40) { updateOverlay(); }
        KGCM # 1 offset(0, 55) { updateOverlay(); }

        KGCM # 10 {
            if(invoker.weaponstatus[KGC_BATTERY] != -1) {
                A_StartSound("weapons/pocket", 9);
                HDMagAmmo.GiveMag(self, "HDBattery", invoker.weaponstatus[KGC_BATTERY]);
                invoker.weaponstatus[KGC_BATTERY] = -1;
            }
        }

        KGCM # 1 offset(0, 55) { updateOverlay(); }
        KGCM # 1 offset(0, 40) { updateOverlay(); }
        KGCM # 1 offset(0, 20) { updateOverlay(); }
        KGCM # 1 offset(0, 0) { updateOverlay(); }
        goto ready;

    reload:
        KGCM # 1 offset(0, 20) { updateOverlay(); }
        KGCM # 1 offset(0, 40) { updateOverlay(); }
        KGCM # 1 offset(0, 55) { updateOverlay(); }

        KGCM # 10 {
            if(CountInv("HDBattery")) {

                // Unload existing battery.
                if(invoker.weaponstatus[KGC_BATTERY] != -1) {
                    A_StartSound("weapons/pocket", 9);
                    HDMagAmmo.GiveMag(self, "HDBattery", invoker.weaponstatus[KGC_BATTERY]);
                    invoker.weaponstatus[KGC_BATTERY] = -1;
                }

                // Load new mag.
                A_StartSound("weapons/plasload", 8);
                HDMagAmmo magAmmo = HDMagAmmo(
                    FindInventory("HDBattery"));
                if(magAmmo) {
                    invoker.weaponstatus[KGC_BATTERY] = magAmmo.TakeMag(true);
                }
            }
        }

        KGCM # 1 offset(0, 55) { updateOverlay(); }
        KGCM # 1 offset(0, 40) { updateOverlay(); }
        KGCM # 1 offset(0, 20) { updateOverlay(); }
        KGCM # 1 offset(0, 0) { updateOverlay(); }
        goto ready;

	ready:
        KGCM B 1 {
            A_Overlay(355, "needle_indicator");
            A_OverlayPivotAlign(355, PSPA_CENTER, PSPA_BOTTOM);
            updateOverlay();

            // USER3 = MagManager.
            // USER4 = Unload.
            A_WeaponReady(WRF_ALLOWUSER3 | WRF_ALLOWUSER4 | WRF_ALLOWRELOAD);
        }

        goto readyend;

    fire:
        KGCM # 1 offset(0, 20) { updateOverlay(); }
        KGCM # 1 offset(0, 40) { updateOverlay(); }
        KGCM # 1 offset(0, 55) { updateOverlay(); }

        KGCM # 1 {
            invoker.weaponstatus[KGC_ACTIVE] =
                !invoker.weaponstatus[KGC_ACTIVE];
            A_StartSound("kiri/gretchencounter_onoff");
            updateOverlay();
        }

        KGCM # 1 offset(0, 55) { updateOverlay(); }
        KGCM # 1 offset(0, 40) { updateOverlay(); }
        KGCM # 1 offset(0, 20) { updateOverlay(); }
        KGCM # 1 offset(0, 0) { updateOverlay(); }
        goto waiting;

    waiting:
        KGCM # 1 { updateOverlay(); }
        KGCM # 1 { updateOverlay(); }
        KGCM # 1 { updateOverlay(); }
        KGCM # 1 { updateOverlay(); }
        KGCM # 1 { updateOverlay(); }
        KGCM # 1 A_Refire("waiting");
        goto ready;

    readyend:
		#### # 0 A_ReadyEnd();
        #### # 0 A_Jump(256,"ready");
    }

    action void updateOverlay()
    {
        float angle_min = -45.0;
        float angle_max = 45.0;
        A_OverlayRotate(355,
            angle_min + (angle_max - angle_min) * (1.0 - invoker.needlePosition));

        if(invoker.weaponstatus[KGC_ACTIVE]) {
            if(invoker.HasBatteryCharge()) {
                player.getpsprite(PSP_WEAPON).frame = 1;
            } else {
                player.getpsprite(PSP_WEAPON).frame = 2;
            }
        } else {
            player.getpsprite(PSP_WEAPON).frame = 0;
        }

        player.getpsprite(355).frame = int(invoker.GetIsActive());
    }

    float GetReadingForType(String type_name, float distance_scale)
    {
        ThinkerIterator iter = ThinkerIterator.Create(type_name);
        Actor mo;
        float totalReading = 0.0;

        Actor referenceActor = owner;
        if(!referenceActor) {
            referenceActor = self;
        }

        while(mo = Actor(iter.Next()))
        {
            if(mo.health <= 0) {
                continue;
            }

            float dist = referenceActor.Distance3D(mo);

            // Convert to "meters" (with a minimum).
            if(dist < 32) {
                dist = 32;
            }

            // Scale distance.
            dist *= distance_scale;

            // Apply inverse-square falloff.
            totalReading += 1.0 / (dist * dist);
        }

        return totalReading;
    }

    override void Tick()
    {
        super.Tick();
        UpdateDisplay();
        TickBattery();
    }

	override void DrawHUDStuff(
        HDStatusBar statusBar, HDWeapon weapon,
        HDPlayerPawn pawn)
    {
		if(statusBar.hudlevel == 1) {
			statusBar.drawbattery(
                -54, -4,
                statusBar.DI_SCREEN_CENTER_BOTTOM,
                reloadorder : true);
			statusBar.drawnum(
                pawn.countinv("HDBattery"),
                -46, -8,
                statusBar.DI_SCREEN_CENTER_BOTTOM);
		}

        int bat = weapon.weaponstatus[KGC_BATTERY];
		if(bat > 0) {

            // Draw battery bar.
            statusbar.drawwepnum(bat, 20);

        } else if(bat > -1) {

            // No battery.
            statusBar.drawstring(
                statusBar.mamountfont, "00000",
                (-16,-9),
                statusBar.DI_TEXT_ALIGN_RIGHT |
                statusBar.DI_TRANSLATABLE |
                statusBar.DI_SCREEN_CENTER_BOTTOM,
                Font.CR_DARKGRAY);
        }
    }

    bool GetIsActive()
    {
        return weaponstatus[KGC_ACTIVE] &&
            HasBatteryCharge();
    }

	override void InitializeWepStats(bool idfa)
    {
        super.InitializeWepStats(idfa);
        weaponstatus[KGC_BATTERY] = 20;
        weaponstatus[KGC_ACTIVE] = 0;
    }

	override double WeaponBulk()
    {
        int battery_weight = 0;
        if(weaponstatus[KGC_BATTERY] != -1) {
            // Battery bulk = 18, but inside this combines with its
            // bulk (takes up less room).
            battery_weight = 10;
        }

        // This item's bulk is 20. Battery adds to that.
        return battery_weight + 20;
    }

    void UpdateDisplay()
    {
        Actor referenceActor = owner;
        if(!referenceActor) {
            referenceActor = self;
        }

        // Get the actual reading.
        float totalReading = 0.0;
        if(GetIsActive()) {
            totalReading =
                GetReadingForType("BFGNecroShard", 1.0) +
                GetReadingForType("HDBarrel", 2.0);
        }

        float pct_reading = lastReading * 2000.0;

        lastReading = totalReading;

        // Make clicky noises if we're getting readings.
        float r = frandom(0.0, 1.0);
        if(r < pct_reading && GetIsActive())
        {
            referenceActor.A_StartSound("kiri/gretchencounter_click");
        }

        // Apply some physics to the needle rotation.
        angularvelocity += pct_reading - needlePosition;
        angularvelocity *= 0.97;

        // Check to see if the needle was at max last frame. If it
        // wasn't at max before, and it is now, we need to start
        // beeping.
        bool wasNeedleAtMax = (needlePosition >= 0.9);

        needlePosition += angularvelocity * 0.1;
        needlePosition = clamp(needlePosition, 0.0, 1.0);

        // Play a "beep" if the needle is at max, and it's either been
        // long enough or we weren't at max before. Make it start and
        // stop.
        framesSinceLastBeep++;
        if(!wasNeedleAtMax || framesSinceLastBeep > 15) {
            if(needlePosition >= 0.9) {
                referenceActor.A_StartSound("kiri/gretchencounter_blip");
                framesSinceLastBeep = 0;
            }
        }
    }

    override string GetHelpText()
    {
        return ""
            ..WEPHELP_FIRE.." Toggle on/off\n"
            ..WEPHELP_RELOAD.." Reload battery\n"
            ..WEPHELP_UNLOAD.." Remove battery\n"
            ..WEPHELP_MAGMANAGER;
    }
}

