How to properly override ThingComp?

Started by Jaitnium, September 30, 2019, 10:11:33 PM

Previous topic - Next topic

Jaitnium

I'm new to modding and C#, so I've been reading a bunch of tutorials. I also successfully followed the Plague Gun mod tutorial:
https://rimworldwiki.com/wiki/Plague_Gun/Introduction

The big issue that is stumping me is that my class that is derived from ThingComp doesn't work at all. I've created a sample mod that uses the exact code listed on the wiki (except I added tickerType to the xml), added the mod to the game, but CompTick doesn't seem to execute at all: https://rimworldwiki.com/wiki/Modding_Tutorials/ThingComp

How can I properly override ThingComp so CompTick will execute?

TickerMod.xml:
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<ThingDef>
<tickerType>Normal</tickerType>
        <comps>
            <li Class="MyExampleNamespace.MyExampleCompProperties">
                <myExampleBool>false</myExampleBool>
                <myExampleFloat>0.005</myExampleFloat>
            </li>
        </comps>
    </ThingDef>
</Defs>


MyExampleNamespace.cs:
using Verse;
using System;

namespace MyExampleNamespace
{
    public class MyExampleComp : ThingComp
    {
        public override void CompTick()
        {
            Log.Error("Let's error on every tick!");
        }

        /// <summary>
        /// By default props returns the base CompProperties class.
        /// You can get props and cast it everywhere you use it,
        /// or you create a Getter like this, which casts once and returns it.
        /// Careful of case sensitivity!
        /// </summary>
        public MyExampleCompProperties Props => (MyExampleCompProperties)this.props;

        public bool ExampleBool => Props.myExampleBool;
    }

    public class MyExampleCompProperties : CompProperties
    {
        public bool myExampleBool;
        public float myExampleFloat;

        /// <summary>
        /// These constructors aren't strictly required if the compClass is set in the XML.
        /// </summary>
        public MyExampleCompProperties()
        {
            this.compClass = typeof(MyExampleComp);
        }

        public MyExampleCompProperties(Type compClass) : base(compClass)
        {
            this.compClass = compClass;
        }
    }
}



K

Your def has the wrong class. Since you didn't specify a class the def defaults to having the generic Thing class, which doesn't support comps or tickers. You need to add a line to your def specifying the class:

<thingClass>ThingWithComps</thingClass>

ThingWithComps is the most general thing class that supports comps and tickers. If your def is more specialized, like a building, it would be a good idea to use a more specialized thing class.

Jaitnium

I've updated the def to specify the class:

<?xml version="1.0" encoding="utf-8"?>
<Defs>
<ThingDef>
<defName>Ticker</defName>
<tickerType>Normal</tickerType>
<thingClass>MyExampleNamespace.MyExampleComp</thingClass>
        <comps>
            <li Class="MyExampleNamespace.MyExampleCompProperties">
                <myExampleBool>false</myExampleBool>
                <myExampleFloat>0.005</myExampleFloat>
            </li>
        </comps>
    </ThingDef>
</Defs>


Unfortunately I'm still not seeing anything in the dev logs, indicating the ticker isn't working, so I feel like I'm misunderstanding you.

K

The thingClass is completely different from your comp class. Your overall thingDef has a class, such as Thing, ThingWithComps, Builing, ect. There's a lot of thingClasses available in the base game's files. They also all have different behaviors, with Thing being the most general and default class, and derived classes like ThingWithComps having more specialized behaviors. In this case, you should use the ThingWithComps class at the very least because it implements comps, which the default Thing class does not.

Jaitnium

Okay I believe I understand thingClass better, but specifying thingClass as ThingWithComps doesn't make the ticker run either:

<?xml version="1.0" encoding="utf-8"?>
<Defs>
<ThingDef>
<defName>Ticker</defName>
<tickerType>Normal</tickerType>
<thingClass>ThingWithComps</thingClass>
        <comps>
            <li Class="MyExampleNamespace.MyExampleCompProperties">
                <myExampleBool>false</myExampleBool>
                <myExampleFloat>0.005</myExampleFloat>
            </li>
        </comps>
    </ThingDef>
</Defs>

Red192

The problem for me is that the thing is not instantiated, try to put the component into a building and than spawn it in the map

Jaitnium

Could you give me an example that would look?

Red192

I'm newb as well, so i could be wrong, but for example you can copy and paste the thingDef of, i don't know, the fire camp, in a xml inside your mod folder and than add your new component to it. Go in game, build a campfire and see if it works

LWM

Red192 has it right. The XML for a ThingDef gives a definition of some thing in the game.  The associated (via thingClass) code defines how a thing that is in the game with that definition will behave.  So until you have an actual "instance" of your thing (that uses your def, etc), your code will never* be touched.

So if you're just playing around with making things happen, adding your stuff to a vanilla object - like a campfire - is an excellent idea.

I found that for me, having a behavior I wanted to see in the game and then working towards how to make it happen gave me a framework for learning the code much better.  It also meant I made the wrong design decision at first, but that wasn't too bad to fix.  You may learn better a different way :)

In a related way, specific questions of "how do I make X happen" will probably get more concrete answers (from me) than "it doesn't work"   ;)  (For example, "I want to make X bullet do Y, and I'm trying to use comps, but it's not working for me when someone fires the gun...etc")

--LWM

* - there are, of course, ways to make exceptions

Goldscuttle

#9
Had the same issue until I put my ThingWithComps overrides in Pawn class, like so...
D:\SteamLibrary\steamapps\common\RimWorld\Mods\RimTales\Defs\PawnDesc.xml:
...
   <ThingDef Name="TestPawn" Abstract="True">
      <thingClass>Pawn</thingClass>
      <category>Pawn</category>
      <selectable>true</selectable>
      <tickerType>Normal</tickerType>
      <altitudeLayer>Pawn</altitudeLayer>
      <useHitPoints>false</useHitPoints>
      <hasTooltip>true</hasTooltip>
      <soundImpactDefault>BulletImpact_Flesh</soundImpactDefault>
      <statBases>
         <Mass>70</Mass>
      </statBases>
      <comps>
         <li Class="RimTales.PawnDescCompProperties">
            <data>Test</data>

            <compClass>CompAttachBase</compClass>
         </li>
      </comps>
...

The two lines in yellow are the only changes to Core xml, I just copied it so I can derive my own class without changing Core.
Then I used Dev mode to spawn a TestPawn which is derived from Pawn, which in turn is a ThingWithComps.

In C#:

            public override void CompTick()
            {
                base.CompTick();
                //Log.Message("Tick.");
               
                MoteMaker.ThrowText(this.parent.PositionHeld.ToVector3(), this.parent.MapHeld, this.Data, 12f);
            }