Difference between revisions of "Modding Tutorials/Custom Comp Classes"

From RimWorld Wiki
Jump to navigation Jump to search
(More complicated example...)
m
Line 1: Line 1:
 +
{{BackToTutorials}}
 
Creating a custom comp class is a convenient way to add new functionality to RimWorld.
 
Creating a custom comp class is a convenient way to add new functionality to RimWorld.
  

Revision as of 16:32, 28 January 2019

Modding Tutorials
Creating a custom comp class is a convenient way to add new functionality to RimWorld.

Prerequisites

def (xml) and C# structure

Setup, Defs, and Classes

You will have some custom def and it will have something like this:

Defs (xml)

 <ThingDef ...>
   ...
   ...
   <comps>
     <li Class="MyNamespace.MyCompProperties">
       <myCustomCompProperty>some value</myCustomCompProperty>
       <mySecondCompProp>4</mySecondCompProp>
     </li>
     <li>
       <!-- this is kind of like <tag>MN.MyCustomTag</tag>:-->
       <compClass>MyNamespace.MyCustomThingComp</compClass>
     </li>
   </comps>
 </ThingDef>

C#

 namespace MyNamespace // For example, LWM.ModName - by using your 
                       //   handle/name/etc, you almost certainly guarantee uniqueness
 {
 //////////// <li Class="MyNamespace.MyCompProperties"> ////////////
 public class MyCompProperties : CompProperties // Name this as you wish, of course
 {
   public Properties() {
     this.compClass = typeof(MyNamespace.MyLinkedCompThing); // rename as appropriate
   }
   public string myCustomCompProperty; // Name matches def, of course
   public int mySecondCompProp = 1; // Can set default values
 }
 
 // this ThingComp is used to actually access the comp property defined above
 // this is not "<compClass>MyNamespace.MyCustomThingComp</compClass>"
 public class MyLinkedCompThing : ThingComp
 {
   public string myCustomCompProperty
   {
     get
     {
       return ((MyCompProperties)this.props).myCustomCompProperty;
     }
   }
   public int mySecondCompProperty // Have to get all the names right
   { get  { return ((MyCompProperties)this.props).mySecondCompProperty; } } //etc
 }
 //////////// <compClass>MyNamespace.MyCustomThingComp</compClass> ////////////
 public class MyCustomThingComp : ThingComp
 {
   public override void CompTick()
   {
     // do stuff
   }
   public override void CompTickRare() //etc
   public override ... // Check out Verse/ThingComp.cs for more ideas
 }
 } // end MyNamespace

Accessing your Comps

Just setting up your custom comps doesn't do you a lot of good if you can't access them!

Accessing CompProperty directly

This is the "hard" way, but doable if you want:

    int importantProperty = ((MyCompProperties)((ThingWithComps)myObject)
                             .GetComp<MyLinkedCompThing>().props).mySecondCompProp;
    //     The "(ThingWithComps)" cast may or may not be needed

Use the "get" method we created

Easier approach:

    // real world example:
    SlotGroup slotGroup = GetSlotGroupInEarlierCode();
    MyLinkedCompThing comp = ((ThingWithComps)slotGroup.parent).GetComp<MyLinkedCompThing>();
    if (comp != null) {
        string s = comp.myCustomCompProperty;
        int x = otherObject.GetComp<MyLinkedCompThing>().mySecondCompProp; // be sure it exists, etc
    } else { /* not a thing with my comp, etc */ }

More Complicated

You can do a lot of complicated things...

    <comps>
      <li Class="MyNS.Properties>
        <myThingFilters>
          <li><!-- These are a list, see? -->
            <filter>
              <categories>
                <li>Apparel</li>
              </categories>
            </filter>
          </li>
          <li>
            <filter>
              <categories>
                <li>Drugs</li>
              </categories>
              <thingDefs>
                <li>ChunkSlagSteel</li>
              </thingDefs>
            </filter>
          </li>
        </myThingFilters>
      </li>
    </comps>

And then

    namespace MyNS {
    public class Properties : CompProperties {
      public Properties() { this.compClass=typeof(ComplicatedComp); }
      public List<ThingFilter> myThingFilters = new List<ThingFilter>;
    }
    public class ComplicatedComp : ThingComp {
      // set up getter as before:
      public List<ThingFilter> myThingFilters { get { return ((Properties)this.props).myThingFilters;
    
    public class MyWeirdCode {
      // etc
        List<ThingFilter> listOfThingFilters = (ThingWithComps)thing.GetComp<ComplicatedComp>().myThingFilters;
        // why you would want a list of thing filters is beyond me?
        // what could you even do with it?
        // but you can have it.
    }

Cautions, traps, etc

If you have the same comp in an abstract (XML) def and attempt to redefine it in a (XML) child def, it will get counted twice. It's possible to get around this in the code, if you want to have default comps:

 <ThingDef Name=ParentWithDefault ... Abstract=true>
   ...
   <comps>
     <li Class="MyCompPropertiesWithDefault">
       <myValue>3</myValue>
     </li>
   </comps>
 </ThingDef>
 <ThingDef ParentName="ParentWihtDefault">
   ...
   <comps>
     <li Class="MyCompPropertiesWithDefault">
       <myValue>5</myValue>
     </li>
   </comps>
 </ThingDef>


 public class MyLinkedCompThing : ThingComp
 {
   public string myCustomCompProperty //etc
   
   public override void Initialize (CompProperties props) {
     base.Initialize(props);
     // Remove duplicate entries and ensure the last entry is the only one left
     //   This allows a default abstract def with the comp
     //   and child def to change the comp value:
     MyCompProprtiesWithDefault[] list = this.parent.GetComps<MyCompPropertiesWithDefault>().ToArray();
     // Remove everything but the last entry; harmless if only one entry:
     for (var i = 0; i < list.Length-1; i++)
     {
       this.parent.AllComps.Remove(list[i]);
     }
   }
 
 ///etc
 }