Rimworld Mod production tutorial 1 make a pestilence gun

0. introduction

Before I tried to make mod, I saw more than N tutorials on the Internet, but I still don't know where to start. Later, I thought that I had to tie the bell to get rid of the bell. I had no choice but to read the English documents. I thought it would be more detailed.
So this article refers to wiki Previous tutorial
But even if it is a very detailed tutorial, there are still many easy places for the first attempt, so this article records the complete weapon production process and the points that need attention, as well as some translation work.

1.mod introduction

First of all, I will introduce that the mod of rimworld mainly consists of four parts: xml corresponding data, c ා corresponding logic code, texture corresponding visual effect, and sound corresponding sound effect. Because I don't know about art and sound effects, I don't want to talk about texture and sound for the time being.
Another part may be English. English foundation is very helpful to understand the meaning of data
Most of the mods are only applied to the xml part, because the threshold is very low, it is easy to learn, but the disadvantage is that the original game can only be modified, and the creation cannot be separated from the original framework. Most of the MODS in the creative workshops use this way.
Using C Chen can accomplish more creations, such as the alien race framework of Erdelf, which is inseparable from C Chen.
If you don't understand the program, the part of C can be skipped.

2. Prepare tools

An ide for writing xml, vscode recommended
An ide that writes c code. visual studio is recommended as vs for short. Version 17 and 19 can be used
A decompilation tool for reference to game source code and other mod author's code, dnspy is recommended
These can be easily found in Baidu, so no download address will be left
We need to pay attention here
When installing vs, please check. net desktop development, otherwise you can't create C assembly or package our code into dll

3. Structure of mod

  • Mod root directory
    • About (store the introduction of mod and the title picture of MOD)
    • Defs(def can't find out what it means, but in the code, def corresponds to a variety of xml data, so I understand it as data. In C, each object also corresponds to two classes, one is def data class, the other is logic class)
    • Assemblies (packaged mod code)
    • Languages (the core directory of translation)
    • Textures (textures, materials of objects in mod)
    • Sounds
    • Patches (patches for other MODS)
    • Source (the source code of mod is generally not provided by the author, but we can decompile it. There are few encrypted mod)

4.xml construction

First, we create a mod root directory under the mods directory of rimworld. In order to distinguish which variables are keywords in the game, I changed the name. Our mod is called testgun


Then build the above structure under TestGun

Then enter the About directory, drag in an image to change its name to Preview.png, create a txt file, and then change it to About.xml. If you can't see the suffix of. XML, click View - option at the top to unhide the extension of the known file type


Then open About.xml
Copy the following code, which is the name, author, version and description of the mod

<?xml version="1.0" encoding="utf-8"?>
<ModMetaData>
  <name>TestGun</name>
  <author>Shadowrabbit</author>
  <supportedVersions>
    <li>1.1</li>
  </supportedVersions>
  <description>Test creation mod Goods
  </description>
</ModMetaData>

After this step, you can see our mod in the game

Next, create a ThingDefs folder in the Defs folder, and then create an xml file called rangedweapon_testgunin ThingDefs

This is the naming rule of the original game. It can work normally if you don't follow it, but I still recommend naming carefully.
Next, change the format in xml.

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

Then we need to "copy" the xml data of a gun in the original game, so that we can know what data can be modified by default.
Let's add a directory in vscode, E:\steam\steamapps\common\RimWorld\Data\Core. This is my game directory for reference only. Please fill in the directory where your computer is installed

Core is the core of the original game. We can find that its structure is exactly the same as our definition of mod. Open Defs to find all item data definitions, but there are too many to know where to start. We search for revolver in the core and copy the pistol data


For the convenience of observation, I added some translation notes

<?xml version="1.0" encoding="utf-8"?>
<!-- Bullet data -->
<Defs>
<!-- Which item is inherited -->
  <ThingDef ParentName="BaseBullet">
    <!-- The name of the item in the code -->
    <defName>Bullet_Revolver</defName>
    <!-- The name of the item in the game -->
    <label>revolver bullet</label>
    <graphicData>
      <!-- Texture map used -->
      <texPath>Things/Projectile/Bullet_Small</texPath>
      <!-- Code of which graphics class to use -->
      <graphicClass>Graphic_Single</graphicClass>
    </graphicData>
    <projectile>
    <!-- Bullet type -->
      <damageDef>Bullet</damageDef>
      <!-- Bullet basic damage -->
      <damageAmountBase>12</damageAmountBase>
      <!-- Cancel the reaction speed of shooting -->
      <stoppingPower>1</stoppingPower>
      <!-- Bullet speed -->
      <speed>55</speed>
    </projectile>
  </ThingDef>
  <!-- Gun data -->
  <ThingDef ParentName="BaseHumanMakeableGun">
    <defName>Gun_Revolver</defName>
    <label>revolver</label>
    <!-- describe -->
    <description>An ancient pattern double-action revolver. It's not very powerful, but has a decent range for a pistol and is quick on the draw.</description>
    <graphicData>
      <texPath>Things/Item/Equipment/WeaponRanged/Revolver</texPath>
      <graphicClass>Graphic_Single</graphicClass>
    </graphicData>
    <!-- UI zoom -->
    <uiIconScale>1.4</uiIconScale>
    <soundInteract>Interact_Revolver</soundInteract>
    <!-- Personal guess is the label of art system -->
    <thingSetMakerTags><li>RewardStandardQualitySuper</li></thingSetMakerTags>
    <!-- Basic attribute -->
    <statBases>
    <!-- Production workload -->
      <WorkToMake>4000</WorkToMake>
      <!-- Item size -->
      <Mass>1.4</Mass>
      <!-- Face to face hit rate -->
      <AccuracyTouch>0.80</AccuracyTouch>
      <!-- Short distance hit rate -->
      <AccuracyShort>0.75</AccuracyShort>
      <!-- Medium distance hit rate -->
      <AccuracyMedium>0.45</AccuracyMedium>
      <!-- Long range hit rate -->
      <AccuracyLong>0.35</AccuracyLong>
      <RangedWeapon_Cooldown>1.6</RangedWeapon_Cooldown>
    </statBases>
    <!-- Weapon label -->
    <weaponTags>
      <li>SimpleGun</li>
      <li>Revolver</li>
    </weaponTags>
    <!-- Consumption list -->
    <costList>
      <Steel>30</Steel>
      <ComponentIndustrial>2</ComponentIndustrial>
    </costList>
    <recipeMaker>
    <!-- Skill demand -->
      <skillRequirements>
        <Crafting>3</Crafting>
      </skillRequirements>
    </recipeMaker>
    <!-- action -->
    <verbs>
      <li>
      <!-- Shooting -->
        <verbClass>Verb_Shoot</verbClass>
        <!-- Is there a standard order -->
        <hasStandardCommand>true</hasStandardCommand>
        <!-- Bullet type -->
        <defaultProjectile>Bullet_Revolver</defaultProjectile>
        <!-- Preheating time -->
        <warmupTime>0.3</warmupTime>
        <!-- Firing distance -->
        <range>25.9</range>
        <!-- Sound effects for shooting -->
        <soundCast>Shot_Revolver</soundCast>
        <!-- Sound effect when shot -->
        <soundCastTail>GunTail_Light</soundCastTail>
        <!-- Zoom of muzzle light effect -->
        <muzzleFlashScale>9</muzzleFlashScale>
      </li>
    </verbs>
    <tools>
      <li>
        <label>grip</label>
        <capacities>
          <li>Blunt</li>
        </capacities>
        <power>9</power>
        <cooldownTime>2</cooldownTime>
      </li>
      <li>
        <label>barrel</label>
        <capacities>
          <li>Blunt</li>
          <li>Poke</li>
        </capacities>
        <power>9</power>
        <cooldownTime>2</cooldownTime>
      </li>
    </tools>
  </ThingDef>
</Defs>

So far, you can use xml to modify various parameters of the gun, as well as what kind of bullets are fired (you need to go to the core to check what bullets are there, and then fill in the default projectile), and you can also change the map and sound effect of the gun. More item parameters need to be checked in wiki, then translated + understood by yourself, and more xml code in core.
What we are making is a pestilence gun. We need to create a new bullet, so we need to add several new parameters to the bullet's def to interact with the C code
Add to the bullet's xml data

<ThingDef Class="SR.ThingDef_TestBullet" ParentName="BaseBullet">

And three new custom properties

	<! -- probability of triggering plague -- >
    <AddHediffChance>0.5</AddHediffChance>
    [! -- hediff is a unique name in the game, which can't be found in the dictionary. It means the state of a certain part, such as infection, bionic part, Plague plague is one of the original games -- >
    <HediffToAdd>Plague</HediffToAdd>
    <! -- Logical class used in C × -- >
    <thingClass>SR.Projectile_PlagueBullet</thingClass>

Then change the name of the bullet to avoid the same as the original, and modify it as follows

<?xml version="1.0" encoding="utf-8"?>
<!-- Bullet data -->
<Defs>
<!--The item is in C#Which article does the data class used in inherit -- >
  <ThingDef Class="SR.ThingDef_TestBullet" ParentName="BaseBullet">
    <!-- The name of the item in the code -->
    <defName>Bullet_TestBullet</defName>
    <!-- The name of the item in the game -->
    <label>Test bullets</label>
    <graphicData>
      <!-- Texture map used -->
      <texPath>Things/Projectile/Bullet_Small</texPath>
      <!-- Code of which graphics class to use -->
      <graphicClass>Graphic_Single</graphicClass>
    </graphicData>
    <projectile>
    <!-- Bullet type -->
      <damageDef>Bullet</damageDef>
      <!-- Bullet basic damage -->
      <damageAmountBase>12</damageAmountBase>
      <!-- Cancel the reaction speed of shooting -->
      <stoppingPower>1</stoppingPower>
      <!-- Bullet speed -->
      <speed>55</speed>
    </projectile>
    <!-- Probability of triggering plague -->
    <addHediffChance>0.5</addHediffChance>
    <!-- Hediff This word is a unique name in the game. It can't be found in the dictionary. It means the state of a certain part, such as infection, bionic part, plague Plague It's one of the original games,The meaning of this variable we define is to store the attached hediff type -->
    <hediffToAdd>Plague</hediffToAdd>
    <!-- The item is in C#Logical classes used in -- >
    <thingClass>SR.Projectile_TestBullet</thingClass>
  </ThingDef>
  <!-- Gun data -->
  <ThingDef ParentName="BaseHumanMakeableGun">
    <defName>SR_Gun_TestGun</defName>
    <label>Shadow rabbit's test gun</label>
    <!-- describe -->
    <description>A gun for testing the effect of plague</description>
    <graphicData>
      <texPath>Things/Item/Equipment/WeaponRanged/Revolver</texPath>
      <graphicClass>Graphic_Single</graphicClass>
    </graphicData>
    <!-- UI zoom -->
    <uiIconScale>1.4</uiIconScale>
    <!-- Interactive sound -->
    <soundInteract>Interact_Revolver</soundInteract>
    <!-- Personal guess is the label of art system -->
    <thingSetMakerTags><li>RewardStandardQualitySuper</li></thingSetMakerTags>
    <!-- Basic attribute -->
    <statBases>
    <!-- Production workload -->
      <WorkToMake>4000</WorkToMake>
      <!-- Item size -->
      <Mass>1.4</Mass>
      <!-- Face to face hit rate -->
      <AccuracyTouch>0.80</AccuracyTouch>
      <!-- Short distance hit rate -->
      <AccuracyShort>0.75</AccuracyShort>
      <!-- Medium distance hit rate -->
      <AccuracyMedium>0.45</AccuracyMedium>
      <!-- Long range hit rate -->
      <AccuracyLong>0.35</AccuracyLong>
      <RangedWeapon_Cooldown>1.6</RangedWeapon_Cooldown>
    </statBases>
    <!-- Weapon label -->
    <weaponTags>
      <li>SimpleGun</li>
      <li>Revolver</li>
    </weaponTags>
    <!-- Consumption list -->
    <costList>
      <Steel>30</Steel>
      <ComponentIndustrial>2</ComponentIndustrial>
    </costList>
    <recipeMaker>
    <!-- Skill demand -->
      <skillRequirements>
        <Crafting>3</Crafting>
      </skillRequirements>
    </recipeMaker>
    <!-- action -->
    <verbs>
      <li>
      <!-- Shooting -->
        <verbClass>Verb_Shoot</verbClass>
        <!-- Is there a standard order -->
        <hasStandardCommand>true</hasStandardCommand>
        <!-- Bullet type -->
        <defaultProjectile>Bullet_TestBullet</defaultProjectile>
        <!-- Preheating time -->
        <warmupTime>0.3</warmupTime>
        <!-- Firing distance -->
        <range>25.9</range>
        <!-- Sound effects for shooting -->
        <soundCast>Shot_Revolver</soundCast>
        <!-- Sound effect when shot -->
        <soundCastTail>GunTail_Light</soundCastTail>
        <!-- Zoom of muzzle light effect -->
        <muzzleFlashScale>9</muzzleFlashScale>
      </li>
    </verbs>
    <tools>
      <li>
        <label>grip</label>
        <capacities>
          <li>Blunt</li>
        </capacities>
        <power>9</power>
        <cooldownTime>2</cooldownTime>
      </li>
      <li>
        <label>barrel</label>
        <capacities>
          <li>Blunt</li>
          <li>Poke</li>
        </capacities>
        <power>9</power>
        <cooldownTime>2</cooldownTime>
      </li>
    </tools>
  </ThingDef>
</Defs>

In order to avoid naming conflicts with other mod s, I use SR as the code namespace. SR.XX represents C ා class, and Sr ᦇ XX represents xml data. This is also the default naming specification of the original game. There are two places where SR.XX data interacts with C ා and the naming must be consistent.
This completes the xml part.

5.C code construction

We create a new class library (. net framework) on vs and change the name to namespace SR
The wiki tutorial reminds us to use the Framework version of. net framework 3.5. After I tried, I found that the original game version is higher than 3.5. So I decompiled other mod s and looked at the version used by other authors. I chose the same 4.6.1. Maybe this part of the original information is out of date.

Generate in the properties node, change the dll output path to our mod path E:\steam\steamapps\common\RimWorld\Mods\TestGun\Assemblies

Next, click Advanced and set the internal compiler error report to none. This is to avoid that we write some small bug s that cause the whole game to crash
.
Click reference, and select all DLLs and Assembly-CSharp.dll at the beginning of UnityEngine under the original game's rimworld \ rimworldwin64'data \ managed path.
wiki tells us to refer to two DLLs, UnityEngine and assembly CSharp. In fact, they may not be two. UnityEngine.XX is a part of UnityEngine, so it actually refers to a long string of DLLs. This place stuck me for an hour

Then set the "copy to local" in the dll property we just referenced to false, because these libraries already exist in the original game, and we will duplicate them again. (press shift to batch)
Then try to generate a solution to see if it can be packed normally. If there is a problem, try to delete the dll reference with a yellow exclamation mark.
I pretend that everyone can package as I do. I create two classes corresponding to sr.projectile'testbullet'and sr.ThingDef'testbullet'set in xml, and reference two classes Rimworld and Verse. Bullet class inherits bullet class and data class inherits ThingDef

using RimWorld;
using Verse;
namespace SR
{
    public class Projectile_TestBullet:Bullet
    {

    }
}

using RimWorld;
using Verse;

namespace SR
{
    public class ThingDef_TestBullet:ThingDef
    {
    }
}

We write the newly added field into the data class. The name should be the same as that in xml. We don't need to assign a default value to the new variable, because it will be overwritten by the data in xml. Therefore, the addhediffcance runtime sets 0.5 for us in xml

using RimWorld;
using Verse;

namespace SR
{
    public class ThingDef_TestBullet:ThingDef
    {
        public float addHediffChance; //The default value will be overridden by xml
        public HediffDef hediffToAdd;
    }
}

In addition, if the data of HediffDef type has a default value, an error will be reported after version 1.0, because hediffdof has not been initialized at this time. But if you want to leave the default value, there is also a solution. There is a callback function, we can reassign it when we call back.

public override void ResolveReferences()
{
    base.ResolveReferences();
    hediffToAdd = HediffDefOf.Plague;
}

After that is the logic class. First, define a variable to store its data

#region data
public ThingDef_TestBullet ThingDef_TestBullet
{
    get
    {
        //The bottom layer reads the new data in the xml format of ThingDef_TestBullet that we defined by name and stores it in this.def. We unpack this.def to get the data in the format of ThingDef_TestBullet that we defined
        return this.def as ThingDef_TestBullet
    }
}
#endregion

We need to know what process the original bullet will perform when it hits the target. Open dnspy to decompile Assembly-CSharp.dll. All the code of the original game is in Assembly-CSharp.dll, and then search the bullet bullet. You can see that there is only one overloaded method impact in the bullet's source code. According to the English meaning, we know that it is the logic executed after being shot. What's written in it for now Don't worry, we inherit the bullet class in the class of the test bullet, and then rewrite the impact method to add our plague settings after the original logic is executed

About the setting of adding plague, the code is given on the wiki. I'll explain it in detail and post it

using RimWorld;
using Verse;
namespace SR
{
    public class Projectile_TestBullet : Bullet
    {
        #region data
        public ThingDef_TestBullet ThingDef_TestBullet
        {
            get
            {
                //The bottom layer reads the new data in the xml format of ThingDef_TestBullet that we defined by name and stores it in this.def. We unpack this.def to get the data in the format of ThingDef_TestBullet that we defined
                return this.def as ThingDef_TestBullet;
            }
        }
        #endregion
        protected override void Impact(Thing hitThing)
        {
            //Under the influence of bullets, methods such as damage killing are implemented at the bottom. If you are interested, you can decompile Assembly-Csharp.dll with dnspy to study what is written in it
            base.Impact(hitThing);
            //The vast majority of mod errors are reported because they are not judged to be good or empty. It is a good habit to write comments and judge non empty
            //Big guy uses a grammar sugar here hitThing is Pawn hitPawn
            //If hitThing can be unpacked to Pawn, this value returns true and declares a variable hitPawn=hitThing as Pawn
            //Otherwise, return false hitPawn is null
            if (ThingDef_TestBullet != null && hitThing != null && hitThing is Pawn hitPawn)
            {
                var rand = Rand.Value; //This method encapsulates a function that returns 0% - 100% random numbers
                //Trigger plague
                if (rand <= ThingDef_TestBullet.addHediffChance)
                {
                    //A prompt is displayed in the upper left corner of the screen. After the translate method is used to translate different languages, MessageTypeDefOf needs to set an event
                    Messages.Message("{0}Caused by the use of test gun{1}Infect plague".Translate(this.launcher.Label,hitPawn.Label),MessageTypeDefOf.NeutralEvent);
                    //Determine if the target has triggered the plague effect
                    var plagueOnPawn = hitPawn.health?.hediffSet?.GetFirstHediffOfDef(ThingDef_TestBullet.hediffToAdd);
                    //We randomly generate a severity for this triggered plague
                    var randomSeverity = Rand.Range(0.15f, 0.30f);
                    //Plague has been triggered
                    if (plagueOnPawn != null)
                    {
                        //If the severity is superimposed, more than 100% will die
                        plagueOnPawn.Severity += randomSeverity;
                    }
                    else
                    {
                        //We call HediffMaker.MakeHediff to generate a new hediff state. The type is the hediffdeof.plague plague type we set before
                        Hediff hediff = HediffMaker.MakeHediff(ThingDef_TestBullet.hediffToAdd, hitPawn);
                        //Set the severity of this state
                        hediff.Severity = randomSeverity;
                        //Add status to the hit target
                        hitPawn.health.AddHediff(hediff);
                    }
                }
                //Not triggered this time
                else
                {
                    //This method can pop up a small line of words in a certain position (here is the side of the hit target), such as missed, hit the head, etc., and it can also
                    MoteMaker.ThrowText(hitThing.PositionHeld.ToVector3(), hitThing.MapHeld, "{0}Plague not triggered".Translate(hitPawn.Label), 12f);
                }
            }
        }
    }
}

At this point, the mod of the new weapon is finished. We choose to package and generate a new dll. According to the directory set before, it will be packaged in Assemblies.

6. test

Unfortunately, although our new weapons have data, there is no way to generate them naturally in the game. We need to test our new guns with the help of developer mode. First, open developer mode (remember to load our mod), build a new map at will, and then select open debug actions menu at the top

Find the spawn weapon – SR_Gun_TestGun in it, then drop it on the ground, let our character pick it up, and then try to shoot his own villain, which just triggers the plague effect

The non triggered log also displays normally

Stack to 100% plague will die directly, which is normal. So far, the production of new weapons has been completed.

7. localization

Localization refers to translating into various languages. We replace the content in message with a variable, so that the variable can display different words in different languages.

Messages.Message("{0}Caused by the use of test gun{1}Infect plague".Translate(this.launcher.Label,hitPawn.Label),MessageTypeDefOf.NeutralEvent);

We change to

Messages.Message("SR_Message_TestBullet_Success".Translate(this.launcher.Label,hitPawn.Label),MessageTypeDefOf.NeutralEvent);

Then create the directory of ChineseSimplified in Languages, then create the directory of Keyed in ChineseSimplified directory, and then create an XML file in Keyed directory. We name it sr_testgun_keys.xml (the name can be taken at will, but it still follows the specification). Copy the default language data structure

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

Then add two new keys, corresponding to the key name set in the previous step

<?xml version="1.0" encoding="utf-8" ?>
<LanguageData>
  <SR_Message_TestBullet_Success></SR_Message_TestBullet_Success>
  <SR_Mote_TestBullet_Fail></SR_Mote_TestBullet_Fail>
</LanguageData>

Then write in the Chinese Translation

<?xml version="1.0" encoding="utf-8" ?>
<LanguageData>
  <SR_Message_TestBullet_Success>{0}Caused by the use of test gun{1}Infect plague</SR_Message_TestBullet_Success>
  <SR_Mote_TestBullet_Fail>{0}Plague not triggered</SR_Mote_TestBullet_Fail>
</LanguageData>

At this point, the Chinese version is finished. If you want to add the English version, create the English directory under Luaguages, and so on. Then create the same file, and fill in the corresponding English text in the value. The game will automatically select the language data to load according to the language selected by the user.
Source download

78 original articles published, 6 praised, 10000 visitors+
Private letter follow

Tags: xml encoding Steam Attribute

Posted on Fri, 06 Mar 2020 04:00:37 -0800 by szalinski