Gizmo error with Shield Belt code

Started by Rah, August 27, 2021, 04:30:52 PM

Previous topic - Next topic

Rah

Hi, I made some armor with the shield belt feature on it and pawns being able to shoot with it. Everything works except for the shield energy display in-game when drafted. The "Gizmo" code for the vanilla shield belt is the code responsible for showing the energy in-game. I have to remove that code to be error free, but then I can't see how much shield the pawn has.

It gives all kinds of errors, like "IteratorStateMachine" cant be found, d 26 cannot be found, etc. Anyone know how to fix that error? Thanks.

using System;
using UnityEngine;
using Verse;
using Verse.Sound;

namespace RimWorld
{
    [StaticConstructorOnStartup]
    public class HellfireBattleArmor : Apparel
    {
        private float energy;

        private int ticksToReset = -1;

        private int lastKeepDisplayTick = -9999;

        private Vector3 impactAngleVect;

        private int lastAbsorbDamageTick = -9999;

        private const float MinDrawSize = 1.2f;

        private const float MaxDrawSize = 1.55f;

        private const float MaxDamagedJitterDist = 0.05f;

        private const int JitterDurationTicks = 8;

        private int StartingTicksToReset = 3200;

        private float EnergyOnReset = 0.2f;

        private float EnergyLossPerDamage = 0.033f;

        private int KeepDisplayingTicks = 1000;

        private float ApparelScorePerEnergyMax = 0.25f;

        private static readonly Material BubbleMat = MaterialPool.MatFrom("Other/ShieldBubble", ShaderDatabase.Transparent);

        private float EnergyMax
        {
            get
            {
                return this.GetStatValue(StatDefOf.EnergyShieldEnergyMax, true);
            }
        }

        private float EnergyGainPerTick
        {
            get
            {
                return this.GetStatValue(StatDefOf.EnergyShieldRechargeRate, true) / 60f;
            }
        }

        public float Energy
        {
            get
            {
                return this.energy;
            }
        }

        public ShieldState ShieldState
        {
            get
            {
                bool flag = this.ticksToReset > 0;
                ShieldState result;
                if (flag)
                {
                    result = ShieldState.Resetting;
                }
                else
                {
                    result = ShieldState.Active;
                }
                return result;
            }
        }

        private bool ShouldDisplay
        {
            get
            {
                Pawn wearer = base.Wearer;
                return wearer.Spawned && !wearer.Dead && !wearer.Downed && (wearer.InAggroMentalState || wearer.Drafted || (wearer.Faction.HostileTo(Faction.OfPlayer) && !wearer.IsPrisoner) || Find.TickManager.TicksGame < this.lastKeepDisplayTick + this.KeepDisplayingTicks);
            }
        }

        public override void ExposeData()
        {
            base.ExposeData();
            Scribe_Values.Look<float>(ref this.energy, "energy", 0f, false);
            Scribe_Values.Look<int>(ref this.ticksToReset, "ticksToReset", -1, false);
            Scribe_Values.Look<int>(ref this.lastKeepDisplayTick, "lastKeepDisplayTick", 0, false);
        }

        [IteratorStateMachine(typeof(ShieldBelt.< GetWornGizmos > d__26))]
        public override IEnumerable<Gizmo> GetWornGizmos()
        {
            while (true)
            {
                int num;
                IEnumerator<Gizmo> enumerator;
                switch (num)
                {
                    case 0:
                        enumerator = this.<> n__0().GetEnumerator();
                        goto Block_2;
                    case 1:
                        goto IL_67;
                    case 2:
                        goto IL_BD;
                }
                break;
                Block_2:
                try
                {
                    IL_6F:
                    if (!enumerator.MoveNext())
                    {
                        goto JumpOutOfTryFinally-3;
                    }
                    Gizmo current = enumerator.Current;
                    yield return current;
                    continue;
                    IL_67:
                    goto IL_6F;
                }
                finally
                {
                    if (enumerator != null)
                    {
                        enumerator.Dispose();
                    }
                }
                JumpOutOfTryFinally - 3:
enumerator = null;
                if (Find.Selector.SingleSelectedThing != base.Wearer)
                {
                    goto IL_C4;
                }
                yield return new Gizmo_EnergyShieldStatus
                {
                    shield = this
                };
            }
            yield break;
            IL_BD:
            IL_C4:
            yield break;
        }

        public override float GetSpecialApparelScoreOffset()
        {
            return this.EnergyMax * this.ApparelScorePerEnergyMax;
        }

        public override void Tick()
        {
            base.Tick();
            bool flag = base.Wearer == null;
            if (flag)
            {
                this.energy = 0f;
            }
            else
            {
                bool flag2 = this.ShieldState == ShieldState.Resetting;
                if (flag2)
                {
                    this.ticksToReset--;
                    bool flag3 = this.ticksToReset <= 0;
                    if (flag3)
                    {
                        this.Reset();
                    }
                }
                else
                {
                    bool flag4 = this.ShieldState == ShieldState.Active;
                    if (flag4)
                    {
                        this.energy += this.EnergyGainPerTick;
                        bool flag5 = this.energy > this.EnergyMax;
                        if (flag5)
                        {
                            this.energy = this.EnergyMax;
                        }
                    }
                }
            }
        }

        public override bool CheckPreAbsorbDamage(DamageInfo dinfo)
        {
            bool flag = this.ShieldState > ShieldState.Active;
            bool result;
            if (flag)
            {
                result = false;
            }
            else
            {
                bool flag2 = dinfo.Def == DamageDefOf.EMP;
                if (flag2)
                {
                    this.energy = 0f;
                    this.Break();
                    result = false;
                }
                else
                {
                    bool flag3 = dinfo.Def.isRanged || dinfo.Def.isExplosive;
                    if (flag3)
                    {
                        this.energy -= dinfo.Amount * this.EnergyLossPerDamage;
                        bool flag4 = this.energy < 0f;
                        if (flag4)
                        {
                            this.Break();
                        }
                        else
                        {
                            this.AbsorbedDamage(dinfo);
                        }
                        result = true;
                    }
                    else
                    {
                        result = false;
                    }
                }
            }
            return result;
        }

        public void KeepDisplaying()
        {
            this.lastKeepDisplayTick = Find.TickManager.TicksGame;
        }

        private void AbsorbedDamage(DamageInfo dinfo)
        {
            SoundDefOf.EnergyShield_AbsorbDamage.PlayOneShot(new TargetInfo(base.Wearer.Position, base.Wearer.Map, false));
            this.impactAngleVect = Vector3Utility.HorizontalVectorFromAngle(dinfo.Angle);
            Vector3 loc = base.Wearer.TrueCenter() + this.impactAngleVect.RotatedBy(180f) * 0.5f;
            float num = Mathf.Min(10f, 2f + dinfo.Amount / 10f);
            FleckMaker.Static(loc, base.Wearer.Map, FleckDefOf.ExplosionFlash, num);
            int num2 = (int)num;
            for (int i = 0; i < num2; i++)
            {
                FleckMaker.ThrowDustPuff(loc, base.Wearer.Map, Rand.Range(0.8f, 1.2f));
            }
            this.lastAbsorbDamageTick = Find.TickManager.TicksGame;
            this.KeepDisplaying();
        }

        private void Break()
        {
            SoundDefOf.EnergyShield_Broken.PlayOneShot(new TargetInfo(base.Wearer.Position, base.Wearer.Map, false));
            FleckMaker.Static(base.Wearer.TrueCenter(), base.Wearer.Map, FleckDefOf.ExplosionFlash, 12f);
            for (int i = 0; i < 6; i++)
            {
                FleckMaker.ThrowDustPuff(base.Wearer.TrueCenter() + Vector3Utility.HorizontalVectorFromAngle((float)Rand.Range(0, 360)) * Rand.Range(0.3f, 0.6f), base.Wearer.Map, Rand.Range(0.8f, 1.2f));
            }
            this.energy = 0f;
            this.ticksToReset = this.StartingTicksToReset;
        }

        private void Reset()
        {
            bool spawned = base.Wearer.Spawned;
            if (spawned)
            {
                SoundDefOf.EnergyShield_Reset.PlayOneShot(new TargetInfo(base.Wearer.Position, base.Wearer.Map, false));
                FleckMaker.ThrowLightningGlow(base.Wearer.TrueCenter(), base.Wearer.Map, 3f);
            }
            this.ticksToReset = -1;
            this.energy = this.EnergyOnReset;
        }

        public override void DrawWornExtras()
        {
            bool flag = this.ShieldState == ShieldState.Active && this.ShouldDisplay;
            if (flag)
            {
                float num = Mathf.Lerp(1.2f, 1.55f, this.energy);
                Vector3 vector = base.Wearer.Drawer.DrawPos;
                vector.y = AltitudeLayer.MoteOverhead.AltitudeFor();
                int num2 = Find.TickManager.TicksGame - this.lastAbsorbDamageTick;
                bool flag2 = num2 < 8;
                if (flag2)
                {
                    float num3 = (float)(8 - num2) / 8f * 0.05f;
                    vector += this.impactAngleVect * num3;
                    num -= num3;
                }
                float angle = (float)Rand.Range(0, 360);
                Vector3 s = new Vector3(num, 1f, num);
                Matrix4x4 matrix = default(Matrix4x4);
                matrix.SetTRS(vector, Quaternion.AngleAxis(angle, Vector3.up), s);
                Graphics.DrawMesh(MeshPool.plane10, matrix, HellfireBattleArmor.BubbleMat, 0);
            }
        }

        public bool AllowVerbCast(IntVec3 root, Map map, LocalTargetInfo targ, Verb verb)
        {
            return !(verb is Verb_LaunchProjectile);
        }
    }
}




Rah

I thought once you have the dll references it should always work without errors, but apparently not. I'm not much into C coding, so this is a bit greek to me. Could you explain a bit further?

RawCode

please read entire msdoc by that link, from very start to very end, it should take just few minutes.

Rah

I understand that it is some sort of intermediate language from VB to C# or whatever, and that some of the translation has to be removed / redone for it work properly, most likely the bracketed part, and some more. That's my understanding of it. Still getting some errors after that.

        public override IEnumerable<Gizmo> GetWornGizmos()
        {
            while (true)
            {
                int num; // num declared but never used
                IEnumerator<Gizmo> enumerator;
                break;
                try // unreachable code
                {
                    if (!enumerator.MoveNext())
                    continue;
                }
                finally
                {
                    if (enumerator != null)
                    {
                        enumerator.Dispose();
                    }
                }
enumerator = null;
                if (Find.Selector.SingleSelectedThing != base.Wearer)
                yield return new Gizmo_EnergyShieldStatus
                {
                    shield = this // cant implicitly convert hellfirebattlearmor to shieldbelt
                };
            }
            yield break;
        }

RawCode

code you see is result of syntax sugar "compilation" produced by "yield return"

implementation of yield return is "IteratorStateMachine" where hidden compiler generated class holds current state and actual output is provided by "movenext" method.

decompilation provided by dnspy looks this way:

    // Token: 0x060068B6 RID: 26806 RVA: 0x0023930D File Offset: 0x0023750D
public override global::System.Collections.Generic.IEnumerable<global::Verse.Gizmo> GetWornGizmos()
{
foreach (global::Verse.Gizmo gizmo in base.GetWornGizmos())
{
yield return gizmo;
}
global::System.Collections.Generic.IEnumerator<global::Verse.Gizmo> enumerator = null;
if (global::Verse.Find.Selector.SingleSelectedThing == base.Wearer)
{
yield return new global::RimWorld.Gizmo_EnergyShieldStatus
{
shield = this
};
}
yield break;
yield break;
}


to get better understanding of yield return and syntax sugar in general, you can compile simple method, like multiple yield returns, use strings for easy tracking, compile it and then decompile.

you will see how it actually looks like.

it's possible to implement exactly same logic without using yield and ever without using iterators, native implementation of everything is single dimension array.

Rah

thanks for the help. that is hilariously weird. I guess I have to do some more studying.