Help with C# building class and visual studios

Started by BasedGreg, June 12, 2019, 03:22:36 AM

Previous topic - Next topic

BasedGreg

So my ambition has quickly escalated since I started modding. I am now starting my first attempt at C# to create a buildingClass for a production building that functions similarly to how to fermenting barrel does, except instead of wort and beer, you turn cartridges into graphene.

I have two problems, the first is that my visual studios project is running on .NET framework 2.0 and I have been completely unable to get anything past 2.0 to work. Will this work fine? If not how do I install other frameworks?

My second problem is that I have only one error. 'ThingDefOf' does not contain a definition for 'Graphene'.
I haven't the faintest clue how to resolve this. The code for the fermenting barrel also doesn't have a definiton for beer yet that seems to work fine. What do?

Here's my code if you're willing to look through. I appreciate any and all help.

using RimWorld;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using Verse;

namespace Building_AdvancedPrinter
{
    [StaticConstructorOnStartup]
    public class Building_AdvancedPrinter : Building
    {
        private int GrapheneCartridgeCount;

        private float progressInt;

        private Material barFilledCachedMat;

        public const int MaxGrapheneCartridgeCapacity = 10;

        private const int BaseReactionDuration = 120000;

        public const float MinIdealTemperature = -125f;

        private static readonly Vector2 BarSize = new Vector2(0.55f, 0.1f);

        private static readonly Color BarZeroProgressColor = new Color(0.4f, 0.27f, 0.22f);

        private static readonly Color BarFermentedColor = new Color(0.6f, 0.93f, 0.96f);

        private static readonly Material BarUnfilledMat = SolidColorMaterials.SimpleSolidColorMaterial(new Color(0.3f, 0.3f, 0.3f));

        public float Progress
        {
            get
            {
                return progressInt;
            }
            set
            {
                if (value != progressInt)
                {
                    progressInt = value;
                    barFilledCachedMat = null;
                }
            }
        }

        private Material BarFilledMat
        {
            get
            {
                if (barFilledCachedMat == null)
                {
                    barFilledCachedMat = SolidColorMaterials.SimpleSolidColorMaterial(Color.Lerp(BarZeroProgressColor, BarFermentedColor, Progress));
                }
                return barFilledCachedMat;
            }
        }

        public int SpaceLeftForGrapheneCartridge
        {
            get
            {
                if (Fermented)
                {
                    return 0;
                }
                return 10 - GrapheneCartridgeCount;
            }
        }

        private bool Empty => GrapheneCartridgeCount <= 0;

        public bool Fermented => !Empty && Progress >= 1f;

        private float CurrentTempProgressSpeedFactor
        {
            get
            {
                CompProperties_TemperatureRuinable compProperties = def.GetCompProperties<CompProperties_TemperatureRuinable>();
                float ambientTemperature = base.AmbientTemperature;
                if (ambientTemperature < compProperties.minSafeTemperature)
                {
                    return 0.1f;
                }
                if (ambientTemperature < 7f)
                {
                    return GenMath.LerpDouble(compProperties.minSafeTemperature, 7f, 0.1f, 1f, ambientTemperature);
                }
                return 1f;
            }
        }

        private float ProgressPerTickAtCurrentTemp => 2.77777781E-06f * CurrentTempProgressSpeedFactor;

        private int EstimatedTicksLeft => Mathf.Max(Mathf.RoundToInt((1f - Progress) / ProgressPerTickAtCurrentTemp), 0);

        public override void ExposeData()
        {
            base.ExposeData();
            Scribe_Values.Look(ref GrapheneCartridgeCount, "GraphiteCount", 0);
            Scribe_Values.Look(ref progressInt, "progress", 0f);
        }

        public override void TickRare()
        {
            base.TickRare();
            if (!Empty)
            {
                Progress = Mathf.Min(Progress + 125f * ProgressPerTickAtCurrentTemp, 1f);
            }
        }

        public void AddGraphite(int count)
        {
            GetComp<CompTemperatureRuinable>().Reset();
            if (Fermented)
            {
                Log.Warning("Tried to add a graphene cartridge to a depositor filled with graphene sheets. Colonists should remove the graphene sheets first.");
                return;
            }
            int num = Mathf.Min(count, 10 - GrapheneCartridgeCount);
            if (num > 0)
            {
                Progress = GenMath.WeightedAverage(0f, num, Progress, GrapheneCartridgeCount);
                GrapheneCartridgeCount += num;
            }
        }

        protected override void ReceiveCompSignal(string signal)
        {
            if (signal == "RuinedByTemperature")
            {
                Reset();
            }
        }

        private void Reset()
        {
            GrapheneCartridgeCount = 0;
            Progress = 0f;
        }

        public void AddGrapheneCartridge(Thing GrapheneCartridge)
        {
            int num = Mathf.Min(GrapheneCartridge.stackCount, 10 - GrapheneCartridgeCount);
            if (num > 0)
            {
                AddGraphite(num);
                GrapheneCartridge.SplitOff(num).Destroy();
            }
        }

        public override string GetInspectString()
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(base.GetInspectString());
            if (stringBuilder.Length != 0)
            {
                stringBuilder.AppendLine();
            }
            CompTemperatureRuinable comp = GetComp<CompTemperatureRuinable>();
            if (!Empty && !comp.Ruined)
            {
                if (Fermented)
                {
                    stringBuilder.AppendLine("ContainsGraphene".Translate(GrapheneCartridgeCount, 10));
                }
                else
                {
                    stringBuilder.AppendLine("ContainsGrapheneCartridge".Translate(GrapheneCartridgeCount, 10));
                }
            }
            if (!Empty)
            {
                if (Fermented)
                {
                    stringBuilder.AppendLine("Complete".Translate());
                }
                else
                {
                    stringBuilder.AppendLine("ReactionProgress".Translate(Progress.ToStringPercent(), EstimatedTicksLeft.ToStringTicksToPeriod()));
                    if (CurrentTempProgressSpeedFactor != 1f)
                    {
                        stringBuilder.AppendLine("GrapheneVatOutOfIdealTemperature".Translate(CurrentTempProgressSpeedFactor.ToStringPercent()));
                    }
                }
            }

            stringBuilder.AppendLine("Temperature".Translate() + ": " + base.AmbientTemperature.ToStringTemperature("F0"));
            stringBuilder.AppendLine("IdealReactingTemperature".Translate() + ": " + 7f.ToStringTemperature("F0") + " ~ " + comp.Props.maxSafeTemperature.ToStringTemperature("F0"));
            return stringBuilder.ToString().TrimEndNewlines();
        }

        public Thing TakeOutGraphene()
        {
            if (!Fermented)
            {
                Log.Warning("Tried to get graphene but it's not yet complete.");
                return null;
            }
            Thing thing = ThingMaker.MakeThing(ThingDefOf.Graphene);
            thing.stackCount = GrapheneCartridgeCount;
            Reset();
            return thing;
        }

        public override void Draw()
        {
            base.Draw();
            if (!Empty)
            {
                Vector3 drawPos = DrawPos;
                drawPos.y += 3f / 64f;
                drawPos.z += 0.25f;
                GenDraw.FillableBarRequest r = default(GenDraw.FillableBarRequest);
                r.center = drawPos;
                r.size = BarSize;
                r.fillPercent = (float)GrapheneCartridgeCount / 25f;
                r.filledMat = BarFilledMat;
                r.unfilledMat = BarUnfilledMat;
                r.margin = 0.1f;
                r.rotation = Rot4.North;
                GenDraw.DrawFillableBar(r);
            }
        }

        public override IEnumerable<Gizmo> GetGizmos()
        {
            foreach (Gizmo gizmo in base.GetGizmos())
            {
                yield return gizmo;
            }
            if (Prefs.DevMode && !Empty)
            {
                yield return new Command_Action
                {
                    defaultLabel = "Debug: Set progress to 1",
                    action = delegate
                    {
                        Progress = 1f;
                    }
                };
            }
        }
    }
}


LWM

1.  Consider posting your code on something like GitHub - it would be much easier to read than here ;)

2.  What build environment are you using?  I have no idea if 2.0 will work, but RimWorld does use 3.5.

3.  The ThingDefOf for Beer IS hard-coded/defined somewhere in the game's code.  You can grab your graphene ThingDef in other ways.  E.g.,


    static ThingDef GrapheneThingDef;
    static Building_AdvancedPrinter() {  //static class constructor - this might not work
        // why not? Because ThingDefs are loaded late in the startup process, and this class might get called?
        //    But it will probably work
        GrapheneThingDef=DefGenerator<ThingDef>.GetNamed("Graphene");
        if (GrapheneThingDef==null) { Log.Error("Okay, that didn't work"); }
    }
    // then use GrapheneThingDef in the code, etc.

Hope that helps, good luck!
--LWM

Fallcoast

To add to what LWM already posted. 

It sounds to me like your project might have been created as a '.NET Standard' project instead of a '.NET Framework' project.  If your project's target framework is '.NET Standard 2.0', that's what happened.  The naming is confusing and its an easy mistake to make.  I think the simplest fix is to create a new .NET Framework project and copy your code there.

Graphene isn't available in ThingDefOf because Graphene isn't in the base game.  You can add your own ThingDefOf class for your mod like:

[DefOf]
public class AdvancedPrinter_ThingDefOf
{
    public static ThingDef Graphene;  //This needs to match the def Name in XML
    //Any other mod ThingDefs can be added to this class
    //...
}

Then in your code use that class instead of ThingDefOf to reference Graphene.

Thing thing = ThingMaker.MakeThing(AdvancedPrinter_ThingDefOf.Graphene);

There's some more about this on the wiki https://rimworldwiki.com/wiki/Modding_Tutorials/Linking_XML_and_C#Using_your_XML_from_C.23 

LWM

Oh, hey that's good to know.  Maybe I should reread some of the wiki  ;D ::)

BasedGreg

#4
Quote from: Fallcoast on June 13, 2019, 03:02:37 AM
To add to what LWM already posted. 

It sounds to me like your project might have been created as a '.NET Standard' project instead of a '.NET Framework' project.  If your project's target framework is '.NET Standard 2.0', that's what happened.  The naming is confusing and its an easy mistake to make.  I think the simplest fix is to create a new .NET Framework project and copy your code there.

Graphene isn't available in ThingDefOf because Graphene isn't in the base game.  You can add your own ThingDefOf class for your mod like:

[DefOf]
public class AdvancedPrinter_ThingDefOf
{
    public static ThingDef Graphene;  //This needs to match the def Name in XML
    //Any other mod ThingDefs can be added to this class
    //...
}

Then in your code use that class instead of ThingDefOf to reference Graphene.

Thing thing = ThingMaker.MakeThing(AdvancedPrinter_ThingDefOf.Graphene);


Yeah, took me a little longer than I'd like to admit to figure out I was using a .NET standard instead of a .NET Framework hahah

Also, after using iLSpy and poking around in the dlls I managed to figure that out. I now no longer get errors! Except now the building still won't work. It's in the game and I've made a custom workgiver class (based on the fermenting barrel), but my test pawn can't load in the cartridges or really interact with the object at all. It just sits there like a glorified end table, except less useful.

Fallcoast

Quote from: BasedGreg on June 15, 2019, 02:04:24 PM

Yeah, took me a little longer than I'd like to admit to figure out I was using a .NET standard instead of a .NET Framework hahah

Also, after using iLSpy and poking around in the dlls I managed to figure that out. I now no longer get errors! Except now the building still won't work. It's in the game and I've made a custom workgiver class (based on the fermenting barrel), but my test pawn can't load in the cartridges or really interact with the object at all. It just sits there like a glorified end table, except less useful.


No worries dude, not the first time that's tripped somebody up. 

The job not working could be a few things.  There needs to be a JobDriver class and WorkGiverDefs+JobDefs in XML too.  If the defs aren't there I don't think the game is aware that it can assign that job to pawns.  Do you get a menu when right clicking the building with a pawn selected?

I also remember a gotcha with those Def's driverClass and giverClass attributes.  Rimworld core defs use just the C# class name, but I had to use {namespace}.{class} to get modded jobs to work.

Hopefully this helps you out.

BasedGreg

#6
Quote from: Fallcoast on June 15, 2019, 09:01:14 PM
The job not working could be a few things.  There needs to be a JobDriver class and WorkGiverDefs+JobDefs in XML too.  If the defs aren't there I don't think the game is aware that it can assign that job to pawns.  Do you get a menu when right clicking the building with a pawn selected?

This was it. Didn't even know about the jobDef. Thanks a bunch!

Unfortunately, even after having added a jobGiver class and a jobDef, it still doesn't work. I do call the class correctly so that's not the problem. For reference what happens is that the object is placed down but when I right click on it no text box opens of any kind.

LWM

Sorry, which object in which part of the process?

--LWM

BasedGreg

Quote from: LWM on June 16, 2019, 12:01:29 AM
Sorry, which object in which part of the process?

--LWM


So, like the fermenting barrel my workbench takes an object (graphene cartridge) and converts it over time into its product (graphene sheets). The problem is that even though I have a jobdef, a workgiver, and the appropriate classes required, I can't get my pawn to fill up the barrel. When I right click on the barrel with a pawn to give a task, like you would with the fermenting barrel, no text box opens up. It's like I'm clicking nothing.

LWM

Don't you have to right-click on the stuff lying around?

I have the "Simple Chain" for Leathers mod, and they manage to get their tanning vats to do pretty much exactly what you want; checking it out might provide clues, if all else fails?

--LWM