Modding Tutorials/Smelter

From RimWorld Wiki
Jump to navigation Jump to search

< Modding Tutorials

This tutorial introduces how to make an interactive building. Currently, only work benches can be fully modified using .xml. The Comms console and Nutrient paste dispenser have hard-coded elements. So here we will set out to make a building that follows the work bench pattern.

Concept

All mods start with a concept. Some are grandiose (adding space pirates to the game). Some, like this one, are modest. The concept of the smelter is simple. Make a building where colonists can take slag debris to melt down into usable metal.

Overview

When you set out to make a building or piece of furniture that operates as a workbench, you need to define three things:

  • 1) The building definition (ThingDef)
  • 2) The work assignment definition (WorkGiverDef)
  • 3) One or more recipe definitions (RecipeDef)

Step 3 is optional if your building duplicates the behavior of an already existing one. For example, if you are making a stone kitchen that uses stone blocks instead of metal to construct, you could skip step 3 and use the same recipes as the standard kitchen model.

Also, if you haven't already, make sure you define an About.xml for the mod. You can make a new one or copy the following:

<?xml version="1.0" encoding="utf-8"?>
<ModMetaData>
	<name>Smelter Tutorial</name>
	<author>Patrick Runyan</author>
	<url>http://www.facebook.com/patrick.runyan.90</url>
	<targetVersion>Alpha 3</targetVersion>
	<description>Tutorial mod for recipes and interactive buildings.</description>
</ModMetaData>

Create the Building Definition

This part should be easy. You already did it in the first tutorial. But here is the boilerplate for a new building which will become our smelter. Save this in a file called "Buildings_Smelter.xml" in your mod folder in the Defs/ThingDefs/ subfolder.

 <?xml version="1.0" encoding="utf-8" ?>
 <Buildings>
  
  <!-- Boilerplate --> 
  
  <ThingDef Name="BuildingBase" Abstract="True">
    <category>Building</category>
    <bulletImpactSound>BulletImpactMetal</bulletImpactSound>
    <selectable>true</selectable>
    <drawerType>MapMeshAndRealTime</drawerType>
    <surfaceNeeded>Light</surfaceNeeded>
    <constructionEffect>ConstructMetal</constructionEffect>
    <repairEffect>Repair</repairEffect>
  </ThingDef>
  
  <!-- Smelter building definition -->
  
  <ThingDef ParentName="BuildingBase">
    <DefName>BuildingSmelter</DefName>
    <EType>BuildingComplex</EType>
    <Label>Smelter</Label>
    <ThingClass>Building</ThingClass>
    <Description>A large, hot furnace used to produce metal from ore.</Description>
    <TexturePath>Things/Building/TableStonecutter</TexturePath>
    <CostList>
      <li>
        <thingDef>Metal</thingDef>
        <count>30</count>
      </li>
    </CostList>
    <AltitudeLayer>BuildingTall</AltitudeLayer>
    <WorkToBuild>600</WorkToBuild>
    <maxHealth>200</maxHealth>
    <Size>(3,2)</Size>
    <surfaceNeeded>Heavy</surfaceNeeded>
    <DesignationCategory>Building</DesignationCategory>
    <Passability>Impassable</Passability>
  </ThingDef>
 </Buildings>

Go ahead and run the game to test out the above definition. You'll see your colonists build a 3x2 structure that looks about the same as the stone cutter's table (that's because we told it to use the same texture for simplicity's sake).

Now let's add the interaction space. To do this you will need to add the following:

<hasInteractionSquare>true</hasInteractionSquare>
<interactionSquareOffset>(0,0,2)</interactionSquareOffset>

The first tag, "hasInteractionSquare" tells the game that this building has an interaction square. It's phrased sort of like a question: "The Smelter has an interaction square?" and then answers the question "Yes, that's true." The second tag, "interactionSquareOffset" tells the game where that square is. The three values refer to the x, y, and z coordinates in the game. This is a bit confusing unless you are are intimately aware of how the Unity engine works. Basically, since Unity is used for 3d games, x, y, and z are mandatory. But Rimworld is 2d so only the x and z coordinates matter. Wait, x and z?! Yes, because in Unity, the y coordinate refers to the vertical space. This value is always 0 because Rimworld is flat. It may be easier to just ignore all this and think of the value as "x, 0, y" instead. The offset is measured as distance from the origin of the object. The smelter is 3 units wide by 2 units tall. It's interaction square will be 2 units above its origin.

[ ][x][ ] x = the offset space (2 spaces above the origin)
[■][■][■] 
[■][□][■] 
    ^--------the origin.

Fire up the game and see what happens. Now when you build the smelter, you should see a yellow circle above it just like with the other interactive buildings in the game. You have successfully told Rimworld that your building has an interaction square! But so far, that's all. Your colonists still have no idea what to do with it.

The next thing to do is to tell the game that the smelter is used to make stuff from something else. This means updating the EType and ThingClass tags. We are going to tell the game that this building is a work table because that function best fits our concept. Edit the tags as follows:

<EType>Building_WorkTable</EType>
<ThingClass>Building_WorkTable</ThingClass>

This is just more boilerplate necessary to make the building function as desired. Don't worry about the nuances of Building_WorkTable vs. BuildingComplex or Building_Worktable vs. Building. Those are advanced concepts beyond the scope of this tutorial.

The next thing to add is a definition for the inspector tab. When you click on a building, the inspector tab gives you information about it. We also want to give orders for the smelter on this tab. To do so add the following:

 <inspectorTabs>
  <li>UI.ITab_Bills</li>
 </inspectorTabs>

This tells the game to use the Bills user interface in the inspection tab of this building. Launch the game again, build a smelter, then click on it to see what happens. You should notice a button called "Bills" above the inspector tab. You can press it just like you can on the kitchen or the stone cutter's table and open the bills menu! If you then click "Add bill"... nothing happens. That's because we haven't given the smelter any "recipes" yet. That is the final addition to the smelter building to make.

Recipes are listed under the recipes tag. For now, we will just add one recipe, the recipe for melting slag into metal:

 <recipes>
  <li>SmeltDebrisSlag</li>
 </recipes>

You may be asking yourself, "Where does this recipe come from?" And the answer is nowhere yet. We've told the smelter to use a recipe called "SmeltDebrisSlag" but before we launch the game again, we'll have to write the recipe! For now, though, we are done with defining the smelter building. Close the file.

Create a Recipe

So we have a building that let's your colonists cook slag into metal using a recipe called "SmeltSlagDebris". Only one problem, we haven't written the recipe next. To do that make a file called "Recipe_Smelter.xml" in your mod folder in the Defs/RecipeDefs/ subfolder. In this file, add the following boilerplate:

<?xml version="1.0" encoding="utf-8" ?>
<RecipeDefs>
 <RecipeDef>
   <defName>SmeltDebrisSlag</defName>
   <label>Smelt slag debris</label>
   <description>Decomposes slag to extract metal.</description>
   <jobString>Smelting slag.</jobString>
   <workAmount>1000</workAmount>
 <RecipeDef>
</RecipeDefs>

Now that the recipe exists, you can start up the game again. Now when you click the "Add bill" button the recipe you just defined should appear! At this point you might be getting a bit tired of waiting for your colonists to build your smelter. There are a couple things you can do. You can either change the value of the "WorkToBuild" tag in the building definition to something much lower, or you can turn on "God mode" which will let you build anything instantly. This is very useful and now might be a good time to become familiar with it if you haven't already!

You might be asking, "What does this recipe actually do?" Well, nothing yet. That's the next step and it's also where things get interesting. Recipes consist of two important parts: ingredients and products. Ingredients go in; products come out.

First, let's start with the easy part, the product. We know we want metal to come out, we tell the game this using the Products tag. Add the following to your recipe:

 <products>
  <li>
    <thingDef>Metal</thingDef>
    <count>5</count>
  </li>
 </products>

We could add even more things to be produced by adding addition li entries. But for now metal is enough. We set the count value to 5. This may not seem like much, but remember this is slag that we are smelting, which by definition shouldn't have any metal in it at all. But since slag is left in game from crashed shuttles and destroyed buildings, it's ok if we get a little out of it. But it shouldn't be more than the original metal used to make the building that blew up creating the slag. That would be an exploit. It's important to think about how your mod will affect the game's balance if you expect other people to try it out.

Next we will specify the ingredients. This requires quite a bit more boilerplate. While the recipe product only requires one tag, ingredients require three tags: Ingredients, ParentIngredientFilter, and DefaultIngredientFilter. The exact function of these three tags in an advanced concept and beyond the scope of this tutorial. For now, just remember to define all three and give it the same value, in this case "DebrisSlag". Insert the following into your recipe:

    <ingredients>
      <li>
        <filter>
          <thingDefs>
            <li>DebrisSlag</li>
          </thingDefs>
        </filter>
        <count>1</count>
      </li>
    </ingredients>
    
    <parentIngredientFilter>
      <thingDefs>
        <li>DebrisSlag</li>
      </thingDefs>
    </parentIngredientFilter>
    
    <defaultIngredientFilter>
      <thingDefs>
        <li>DebrisSlag</li>
      </thingDefs>
    </defaultIngredientFilter>

Wow. We had to write "DebrisSlag" in three different places just to get the recipe to take slag as its sole ingredient. This may seem overly complex for such a simple task, but it actually makes implementing more complex recipes much easier in the future. For now, just know that not only are we telling the recipe what to take as its ingredient but how to show and filter those ingredients in the user interface.

Launch the game and see what happens. Everything looks good, right? We added a bill and we can configure it in the bills menu. Wait... there's no slag on the screen. Activate God Mode and then choose "Tool: Spawn thing" from the menu (it's near the bottom of the second column). Then click "Slag debris" from the next menu (it's about the middle of the second column). Click around on screen and drop some slag. Right click when you've finished. Now that there's some slag lying about and a bill to melt it down at the smelter, colonists should start getting to work! Except... they aren't. They're just standing about. Selecting a colonist and right clicking on the smelter also doesn't do anything. That's because our colonists aren't aware that actually completing the bill at the smelter is, well, their job. And that is the third step in making our smelter.

Create a WorkGiverDef

To make the colonists aware of what work needs to be done, we need to write a WorkGiverDef. This will tell the game to tell them that, hey, you should do this task. Otherwise they'll ignore it. Who's to tell them that smelting slag is their job and not, say, that of the Muffalo? Eating plants is a job but no one told them to do that either.

To write the WorkGiverDef, first make a new file called "Workgiver_Smelter.xml" in your mod folder in the Defs/WorkgiverDefs/ subfolder. In it add the following:

<?xml version="1.0" encoding="utf-8" ?>
<WorkGivers>
  <WorkGiverDef>
    <DefName>DoBillsSmelter</DefName>
    <giverClass>AI.WorkGiver_DoBill</giverClass>
    <workType>Crafting</workType>
    <priorityInType>10</priorityInType>
    <WorkTableDef>BuildingSmelter</WorkTableDef>
    <verb>smelt</verb>
    <gerund>smelting</gerund>
  </WorkGiverDef>
</WorkGivers>

You can probably guess what these values mean by looking, but here's the most important thing to remember. Make sure the DefName is unique and not something used in the core or another mod. GiverClass just tells the game that the work to be done is a bill. Remember the smelter uses the bill assignment interface for smelting metal. The next most important thing is WorkTableDef. Make sure this is the definition name for the smelter. Remember way back we assigned smelter the DefName value "BuildingSmelter". WorkType simply tells the game which colonist skill is used to do the job. In this case, let's go with Crafting. But if you like, you can set it to any of the other colonist skills. If you believe in the fine art of metal smelting, you might change it to Art, for example. PriorityInType tells the game how important this job is relative to other Crafting jobs. Finally, Verb and Gerund are just the words that are used in the game text to tell the player what the colonist is doing. For example: "Prioritize smelting".

Now that the WorkGiverDef is finished, launch the game and test out the job. You should observe your colonists running off now to pick up slag and fill the bill created at your smelter!

Troubleshooting

If you are having problems, always try to reboot the game, close your text editors, and reopen the mod menu when you restart the game to make sure the data is up-to-date. If you still have problems, remember to check the modding help forum at the official site. Most likely someone else has had the same issue!

Below are the final .xml files used in this tutorial. Check to make sure your files are consistent.

Mods/SmelterTutorial/About/About.xml

<?xml version="1.0" encoding="utf-8"?>
<ModMetaData>
	<name>Smelter Tutorial</name>
	<author>Your Name</author>
	<url>http://http://rimworldwiki.com/</url>
	<targetVersion>Alpha 3</targetVersion>
	<description>Tutorial mod for recipes and interactive buildings.</description>
</ModMetaData>

Mods/SmelterTutorial/Defs/ThingDefs/Buildings_Smelter.xml

<?xml version="1.0" encoding="utf-8" ?>
<Buildings>
  
  <!-- Boilerplate -->
  
  <ThingDef Name="BuildingBase" Abstract="True">
    <category>Building</category>
    <bulletImpactSound>BulletImpactMetal</bulletImpactSound>
    <selectable>true</selectable>
    <drawerType>MapMeshAndRealTime</drawerType>
    <surfaceNeeded>Light</surfaceNeeded>
    <constructionEffect>ConstructMetal</constructionEffect>
    <repairEffect>Repair</repairEffect>
  </ThingDef>
  
  <!-- Smelter building definition -->
  
  <ThingDef ParentName="BuildingBase">
    <DefName>BuildingSmelter</DefName>
    <EType>Building_WorkTable</EType>
    <Label>Smelter</Label>
    <ThingClass>Building_WorkTable</ThingClass>
    <Description>A large, hot furnace used to produce metal from ore. The heat required consumes a great deal of electricity so it's best to turn it off when not in use.</Description>
    <TexturePath>Things/Building/TableStonecutter</TexturePath>
    <CostList>
      <li>
        <thingDef>Metal</thingDef>
        <count>10</count>
      </li>
    </CostList>
    <AltitudeLayer>BuildingTall</AltitudeLayer>
    <WorkToBuild>600</WorkToBuild>
    <maxHealth>200</maxHealth>
    <Size>(3,2)</Size>
    <surfaceNeeded>Heavy</surfaceNeeded>
    <DesignationCategory>Building</DesignationCategory>
    <Passability>Impassable</Passability>
    <hasInteractionSquare>True</hasInteractionSquare>
    <interactionSquareOffset>(0,0,2)</interactionSquareOffset>
    <inspectorTabs>
      <li>UI.ITab_Bills</li>
    </inspectorTabs>
    <recipes>
      <li>SmeltDebrisSlag</li>
      <!--li>SmeltDebrisRock</li-->
    </recipes>
  </ThingDef>
</Buildings>

Mods/SmelterTutorial/Defs/RecipeDefs/Recipe_Smelter.xml

<?xml version="1.0" encoding="utf-8" ?>
<RecipeDefs>
  <RecipeDef>
    <defName>SmeltDebrisSlag</defName>
    <label>Smelt slag debris</label>
    <description>Decomposes slag to extract metal.</description>
    <jobString>Smelting slag.</jobString>
    <workAmount>1000</workAmount>
    <products>
      <li>
        <thingDef>Metal</thingDef>
        <count>5</count>
      </li>
    </products>
    <ingredients>
      <li>
        <filter>
          <thingDefs>
            <li>DebrisSlag</li>
          </thingDefs>
        </filter>
        <count>1</count>
      </li>
    </ingredients>
    <parentIngredientFilter>
      <thingDefs>
        <li>DebrisSlag</li>
      </thingDefs>
    </parentIngredientFilter>
    <defaultIngredientFilter>
      <thingDefs>
        <li>DebrisSlag</li>
      </thingDefs>
    </defaultIngredientFilter>
  </RecipeDef>
</RecipeDefs>

Mods/SmelterTutorial/Defs/WorkGiverDefs/Workgiver_Smelter.xml

<?xml version="1.0" encoding="utf-8" ?>
<WorkGivers>

  <WorkGiverDef>
    <defName>DoBillsSmelter</defName>
    <giverClass>AI.WorkGiver_DoBill</giverClass>
    <workType>Crafting</workType>
    <priorityInType>10</priorityInType>
    <workTableDef>BuildingSmelter</workTableDef>
    <verb>smelt</verb>
    <gerund>smelting</gerund>
  </WorkGiverDef>

</WorkGivers>