Notepad++ (https://notepad-plus-plus.org/) or Atom (https://atom.io/) or Sublimetext (https://www.sublimetext.com/) | | | Use any text editor that allows you to edit XML files and use "Find in Files" for referencing. |
Visual Studio Community (https://www.visualstudio.com/vs/community/) | | | Use this or any other C# compiler to turn scripts into .dll files that RimWorld can use. |
dnSpy (https://github.com/0xd4d/dnSpy/releases) | | | This is for referencing the game's decompiled C# scripts. |
<?xml version="1.0" encoding="utf-8"?>
<ModMetaData>
<name>Test Mod - Plague Gun</name>
<author>YourName</author>
<targetVersion>0.17.0</targetVersion>
<description>V1.0
This mod adds a plague gun, a weapon that has a chance to give your enemies the plague.
</description>
</ModMetaData>
DO NOT USE THIS -- This is an incomplete preview.
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<ThingDef ParentName="BaseBullet">
<defName>TST_PlagueGun_Bullet</defName>
<label>plague bullet</label>
...
</ThingDef>
<ThingDef ParentName="BaseHumanMakeableGun">
<defName>TST_PlagueGun_Weapon</defName>
<label>plague gun</label>
...
</ThingDef>
</Defs>
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Defs>
</Defs>
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<ThingDef ParentName="BaseBullet">
<defName>TST_Bullet_PlagueGun</defName>
<label>plague bullet</label>
<graphicData>
<texPath>Things/Projectile/Bullet_Small</texPath>
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<projectile>
<flyOverhead>false</flyOverhead>
<damageDef>Bullet</damageDef>
<damageAmountBase>9</damageAmountBase>
<speed>55</speed>
</projectile>
</ThingDef>
<ThingDef ParentName="BaseHumanMakeableGun">
<defName>TST_Gun_PlagueGun</defName>
<label>plague gun</label>
<description>A curious weapon notable for its horrible health effects.</description>
<graphicData>
<texPath>Things/Item/Equipment/WeaponRanged/Revolver</texPath>
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<soundInteract>InteractRevolver</soundInteract>
<statBases>
<WorkToMake>15000</WorkToMake>
<Mass>1.4</Mass>
<AccuracyTouch>0.91</AccuracyTouch>
<AccuracyShort>0.71</AccuracyShort>
<AccuracyMedium>0.50</AccuracyMedium>
<AccuracyLong>0.32</AccuracyLong>
<RangedWeapon_Cooldown>1.26</RangedWeapon_Cooldown>
<Weapon_Bulk>0.5</Weapon_Bulk>
</statBases>
<weaponTags>
<li>SimpleGun</li>
</weaponTags>
<costList>
<Steel>30</Steel>
<Component>2</Component>
</costList>
<verbs>
<li>
<verbClass>Verb_Shoot</verbClass>
<hasStandardCommand>true</hasStandardCommand>
<defaultProjectile>TST_Bullet_PlagueGun</defaultProjectile>
<warmupTime>0.3</warmupTime>
<range>26</range>
<soundCast>ShotRevolver</soundCast>
<soundCastTail>GunTail_Light</soundCastTail>
<muzzleFlashScale>9</muzzleFlashScale>
</li>
</verbs>
<tools>
<li>
<label>grip</label>
<capacities>
<li>Blunt</li>
</capacities>
<power>8</power>
<cooldownTime>1.6</cooldownTime>
</li>
<li>
<label>barrel</label>
<capacities>
<li>Blunt</li>
<li>Poke</li>
</capacities>
<power>8</power>
<cooldownTime>1.6</cooldownTime>
</li>
</tools>
</ThingDef>
</Defs>
<AddHediffChance>0.05</AddHediffChance>
<HediffToAdd>Plague</HediffToAdd>
<thingClass>Plague.Projectile_PlagueBullet</thingClass>
using RimWorld;
using Verse;
namespace Plague
{
public class ThingDef_PlagueBullet : ThingDef
{
public float AddHediffChance = 0.05f; //The default chance of adding a hediff.
public HediffDef HediffToAdd = HediffDefOf.Plague;
}
}
namespace Plague
{
public class Projectile_PlagueBullet: Bullet
{
#region Properties
//
public ThingDef_PlagueBullet Def
{
get
{
return this.def as ThingDef_PlagueBullet;
}
}
#endregion Properties
}
}
#region Overrides
protected override void Impact(Thing hitThing)
{
base.Impact(hitThing);
/*
* Null checking is very important in RimWorld.
* 99% of errors reported are from NullReferenceExceptions (NREs).
* Make sure your code checks if things actually exist, before they
* try to use the code that belongs to said things.
*/
if (Def != null && hitThing != null && hitThing is Pawn hitPawn) //Fancy way to declare a variable inside an if statement. - Thanks Erdelf.
{
var rand = Rand.Value; // This is a random percentage between 0% and 100%
if (rand <= Def.AddHediffChance) // If the percentage falls under the chance, success!
{
/*
* Messages.Message flashes a message on the top of the screen.
* You may be familiar with this one when a colonist dies, because
* it makes a negative sound and mentioneds "So and so has died of _____".
*
* Here, we're using the "Translate" function. More on that later in
* the localization section.
*/
Messages.Message("TST_PlagueBullet_SuccessMessage".Translate(new object[] {
this.launcher.Label, hitPawn.Label
}), MessageSound.Standard);
//This checks to see if the character has a heal differential, or hediff on them already.
var plagueOnPawn = hitPawn?.health?.hediffSet?.GetFirstHediffOfDef(Def.HediffToAdd);
var randomSeverity = Rand.Range(0.15f, 0.30f);
if (plagueOnPawn != null)
{
//If they already have plague, add a random range to its severity.
//If severity reaches 1.0f, or 100%, plague kills the target.
plagueOnPawn.Severity += randomSeverity;
}
else
{
//These three lines create a new health differential or Hediff,
//put them on the character, and increase its severity by a random amount.
Hediff hediff = HediffMaker.MakeHediff(Def.HediffToAdd, hitPawn, null);
hediff.Severity = randomSeverity;
hitPawn.health.AddHediff(hediff, null, null);
}
}
else //failure!
{
/*
* Motes handle all the smaller visual effects in RimWorld.
* Dust plumes, symbol bubbles, and text messages floating next to characters.
* This mote makes a small text message next to the character.
*/
MoteMaker.ThrowText(hitThing.PositionHeld.ToVector3(), hitThing.MapHeld, "TST_PlagueBullet_FailureMote".Translate(Def.AddHediffChance), 12f);
}
}
}
#endregion Overrides
Messages.Message("TST_PlagueBullet_SuccessMessage".Translate(new object[] {
this.launcher.Label, hitPawn.Label
}), MessageSound.Standard);
MoteMaker.ThrowText(hitThing.PositionHeld.ToVector3(), hitThing.MapHeld, "TST_PlagueBullet_FailureMote".Translate(Def.AddHediffChance), 12f);
<?xml version="1.0" encoding="utf-8" ?>
<LanguageData>
</LanguageData>
<?xml version="1.0" encoding="utf-8" ?>
<LanguageData>
<TST_PlagueBullet_FailureMote></TST_PlagueBullet_FailureMote>
<TST_PlagueBullet_SuccessMessage></TST_PlagueBullet_SuccessMessage>
</LanguageData>
<?xml version="1.0" encoding="utf-8" ?>
<LanguageData>
<TST_PlagueBullet_FailureMote>Failure: {0} chance</TST_PlagueBullet_FailureMote>
<TST_PlagueBullet_SuccessMessage>{0} infected {1} with the plague!</TST_PlagueBullet_SuccessMessage>
</LanguageData>
Quote from: Tynan on June 03, 2017, 11:17:03 PM
This is really great! But it might be even more useful on the wiki. The wiki is designed as a long-term info repository, while forums are for temporary discussion. If you put it on the wiki, others can also participate it and improve it, or add other tutorials alongside, and so on. Consider wiki-ing it!
Error CS1061 'ThingDef' does not contain a definition for 'AddHediffChance' and no extension method 'AddHediffChance' accepting a first argument of type 'ThingDef' could be found (are you missing a using directive or an assembly reference?) PlagueGun D:\SteamLibrary\SteamApps\common\RimWorld\Mods\PlagueGun\Source\PlagueGun\PlagueGun\Projectile_PlagueBullet.cs 36 Active
Error CS1061 'ThingDef' does not contain a definition for 'HediffToAdd' and no extension method 'HediffToAdd' accepting a first argument of type 'ThingDef' could be found (are you missing a using directive or an assembly reference?) PlagueGun D:\SteamLibrary\SteamApps\common\RimWorld\Mods\PlagueGun\Source\PlagueGun\PlagueGun\Projectile_PlagueBullet.cs 51 Active
Error CS1061 'ThingDef' does not contain a definition for 'HediffToAdd' and no extension method 'HediffToAdd' accepting a first argument of type 'ThingDef' could be found (are you missing a using directive or an assembly reference?) PlagueGun D:\SteamLibrary\SteamApps\common\RimWorld\Mods\PlagueGun\Source\PlagueGun\PlagueGun\Projectile_PlagueBullet.cs 63 Active
Error CS1061 'ThingDef' does not contain a definition for 'AddHediffChance' and no extension method 'AddHediffChance' accepting a first argument of type 'ThingDef' could be found (are you missing a using directive or an assembly reference?) PlagueGun D:\SteamLibrary\SteamApps\common\RimWorld\Mods\PlagueGun\Source\PlagueGun\PlagueGun\Projectile_PlagueBullet.cs 75 Active
if (Def != null && hitThing != null && hitThing is Pawn hitPawn) //Fancy way to declare a variable inside an if statement. - Thanks Erdelf.
Pawn hitPawn = hitThing as Pawn;
if (this.Def != null && hitPawn != null)
public ThingDef Def
{
get { return this.def as ThingDef_PlagueBullet; }
}
public ThingDef_PlagueBullet Def
{
get { return this.def as ThingDef_PlagueBullet; }
}
Quote from: UncleIROH on June 04, 2017, 01:13:26 AM
@DrGenghis
I was having the same issue. The forum post looks like it might have a mistake in it because it doesn't match the example C# file.
The post has this line:Code Selectpublic ThingDef Def
{
get { return this.def as ThingDef_PlagueBullet; }
}
But it should be:Code Selectpublic ThingDef_PlagueBullet Def
{
get { return this.def as ThingDef_PlagueBullet; }
}
xcopy "YourDllFile" "OutputDirectory" /i /y
xcopy "C:\Users\Mrowa\Documents\Visual Studio 2013\Projects\ClutterFurniture\ClutterFurniture\bin\Debug\ClutterFurniture.dll"
"C:\Program Files (x86)\Steam\steamapps\common\RimWorld\Mods\Clutter Furniture\Assemblies" /i /y
Quote from: UncleIROH on June 04, 2017, 11:14:16 AM
Anyone get this gun to actually give someone the plague? I've killed bunches of pawns with it, no plague.
Quote11) Repeat and copy all of the ThingDef Parents and Bases for Bullet_Pistol and Pistol to your XML file.
<AddHediffChance>0.05<AddHediffChance>
Quote from: Toby Larone on June 06, 2017, 02:57:23 PM
P.S. is it normal to get a message in the top left corner of the game telling you whether the shot infected the target or not? I am getting them even with dev mode turned off.
QuoteRimWorld 0.17.1557 rev1153
Verse.Log:Message(String)
RimWorld.VersionControl:LogVersionNumber()
Verse.Root:CheckGlobalInit()
Verse.Root:Start()
Verse.Root_Entry:Start()
Exception reading RangedWeapon_PlagueGun.xml as XML: System.Xml.XmlException: 'AddHediffChance' is expected Line 21, position 6.
at Mono.Xml2.XmlTextReader.Expect (System.String expected) [0x00000] in <filename unknown>:0
at Mono.Xml2.XmlTextReader.ReadEndTag () [0x00000] in <filename unknown>:0
at Mono.Xml2.XmlTextReader.ReadContent () [0x00000] in <filename unknown>:0
at Mono.Xml2.XmlTextReader.ReadContent () [0x00000] in <filename unknown>:0
at Mono.Xml2.XmlTextReader.Read () [0x00000] in <filename unknown>:0
at System.Xml.XmlTextReader.Read () [0x00000] in <filename unknown>:0
at System.Xml.XmlDocument.ReadNodeCore (System.Xml.XmlReader reader) [0x00000] in <filename unknown>:0
at System.Xml.XmlDocument.ReadNodeCore (System.Xml.XmlReader reader) [0x00000] in <filename unknown>:0
at System.Xml.XmlDocument.ReadNodeCore (System.Xml.XmlReader reader) [0x00000] in <filename unknown>:0
at System.Xml.XmlDocument.ReadNodeCore (System.Xml.XmlReader reader) [0x00000] in <filename unknown>:0
at System.Xml.XmlDocument.ReadNodeCore (System.Xml.XmlReader reader) [0x00000] in <filename unknown>:0
at System.Xml.XmlDocument.ReadNode (System.Xml.XmlReader reader) [0x00000] in <filename unknown>:0
at System.Xml.XmlDocument.Load (System.Xml.XmlReader xmlReader) [0x00000] in <filename unknown>:0
at System.Xml.XmlDocument.LoadXml (System.String xml) [0x00000] in <filename unknown>:0
at Verse.LoadableXmlAsset..ctor (System.String name, System.String fullFolderPath, System.String contents) [0x00000] in <filename unknown>:0
Verse.Log:Warning(String)
Verse.LoadableXmlAsset:.ctor(String, String, String)
Verse.<XmlAssetsInModFolder>c__Iterator224:MoveNext()
System.Collections.Generic.List`1:AddEnumerable(IEnumerable`1)
System.Collections.Generic.List`1:.ctor(IEnumerable`1)
System.Linq.Enumerable:ToList(IEnumerable`1)
Verse.ModContentPack:LoadDefs(IEnumerable`1)
Verse.LoadedModManager:LoadAllActiveMods()
Verse.PlayDataLoader:DoPlayLoad()
Verse.PlayDataLoader:LoadAllPlayData(Boolean)
Verse.Root:<Start>m__84E()
Verse.LongEventHandler:RunEventFromAnotherThread(Action)
Verse.LongEventHandler:<UpdateCurrentAsynchronousEvent>m__84C()
Loaded file (Scenario) is from version 0.17.1546 rev887, we are running version 0.17.1557 rev1153.
Verse.Log:Warning(String)
Verse.ScribeMetaHeaderUtility:LoadGameDataHeader(ScribeHeaderMode, Boolean)
Verse.GameDataSaveLoader:TryLoadScenario(String, ScenarioCategory, Scenario&)
RimWorld.ScenarioFiles:RecacheData()
RimWorld.ScenarioLister:RecacheData()
RimWorld.ScenarioLister:RecacheIfDirty()
RimWorld.<ScenariosInCategory>c__Iterator128:MoveNext()
System.Linq.Enumerable:FirstOrDefault(IEnumerable`1)
RimWorld.Page_SelectScenario:EnsureValidSelection()
RimWorld.Page_SelectScenario:PreOpen()
Verse.WindowStack:Add(Window)
RimWorld.MainMenuDrawer:<DoMainMenuControls>m__5FF()
Verse.ListableOption:DrawOption(Vector2, Single)
Verse.OptionListingUtility:DrawOptionListing(Rect, List`1)
RimWorld.MainMenuDrawer:DoMainMenuControls(Rect, Boolean)
RimWorld.MainMenuDrawer:MainMenuOnGUI()
Verse.UIRoot_Entry:DoMainMenu()
Verse.UIRoot_Entry:UIRootOnGUI()
Verse.Root:OnGUI()
Initializing new game with mods Core and PlagueGun
Verse.Log:Message(String)
Verse.Game:InitNewGame()
Verse.Root_Play:<Start>m__850()
Verse.LongEventHandler:RunEventFromAnotherThread(Action)
Verse.LongEventHandler:<UpdateCurrentAsynchronousEvent>m__84C()
// C:\Program Files (x86)\Steam\steamapps\common\RimWorld\RimWorldWin_Data\Managed\Assembly-CSharp.dll
// Assembly-CSharp, Version=0.17.6362.34601, Culture=neutral, PublicKeyToken=null
// Global type: <Module>
// Architecture: AnyCPU (64-bit preferred)
// Runtime: .NET 2.0
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyVersion("0.17.6362.34601")]
[assembly: AssemblyCompany("Ludeon Studios")]
[assembly: AssemblyCopyright("Copyright Ludeon Studios.")]
[assembly: AssemblyTrademark("RimWorld is a registered trademark of Ludeon Studios.")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
Quote from: arenoobies on June 09, 2017, 12:34:47 AM
Thank you for this! I've been looking for a step by step in modding and I've been rummaging through the defs in RimWorld folder trying to learn how to mod or atleast modified something on my own, with this guide it helps me even better! Please do continue and perhaps in the future add a bit more complicated one like one of your mods, factions! Really appreciated! <3
Edit: I can't find the gun in dev mode spawn weapon, it says these:
Quote from: Kappten on June 14, 2017, 12:13:53 PM
When I use ILSpy to decompile Assembly-CSharp I only get:Code Select// C:\Program Files (x86)\Steam\steamapps\common\RimWorld\RimWorldWin_Data\Managed\Assembly-CSharp.dll
// Assembly-CSharp, Version=0.17.6362.34601, Culture=neutral, PublicKeyToken=null
// Global type: <Module>
// Architecture: AnyCPU (64-bit preferred)
// Runtime: .NET 2.0
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
[assembly: AssemblyVersion("0.17.6362.34601")]
[assembly: AssemblyCompany("Ludeon Studios")]
[assembly: AssemblyCopyright("Copyright Ludeon Studios.")]
[assembly: AssemblyTrademark("RimWorld is a registered trademark of Ludeon Studios.")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
Even when I press CTRL + F and search for "Projectile_" I get no results. I used the Link in the Post to download ILSpy. What am I doing wrong?
Exception drawing PG_Gun_Plague60985: System.NullReferenceException: Object reference not set to an instance of an object
at Verse.Projectile.get_StartingTicksToImpact () [0x00000] in <filename unknown>:0
at Verse.Projectile.get_ExactPosition () [0x00000] in <filename unknown>:0
at Verse.Projectile.get_DrawPos () [0x00000] in <filename unknown>:0
at Verse.Projectile.Draw () [0x00000] in <filename unknown>:0
at Verse.DynamicDrawManager.DrawDynamicThings (Verse.DrawTargetDef drawTarget) [0x00000] in <filename unknown>:0
Verse.Log:Error(String)
Verse.DynamicDrawManager:DrawDynamicThings(DrawTargetDef)
Verse.Map:MapUpdate()
Verse.Game:UpdatePlay()
Verse.Root_Play:Update()
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RimWorld;
using Verse;
namespace Plague
{
class Projectile_PlagueBullet : Bullet
{
#region Properties
//
public ThingDef_PlagueBullet Def
{
get
{
return this.def as ThingDef_PlagueBullet;
}
}
#endregion Properties
#region Overrides
protected override void Impact(Thing hitThing)
{
base.Impact(hitThing);
/*
* Null checking is very important in RimWorld.
* 99% of errors reported are from NullReferenceExceptions (NREs).
* Make sure your code checks if things actually exist, before they
* try to use the code that belongs to said things.
*/
if (Def != null && hitThing != null && hitThing is Pawn hitPawn) //Fancy way to declare a variable inside an if statement. - Thanks Erdelf.
{
var rand = Rand.Value; // This is a random percentage between 0% and 100%
if (rand <= Def.AddHediffChance) // If the percentage falls under the chance, success!
{
/*
* Messages.Message flashes a message on the top of the screen.
* You may be familiar with this one when a colonist dies, because
* it makes a negative sound and mentioneds "So and so has died of _____".
*
* Here, we're using the "Translate" function. More on that later in
* the localization section.
*/
Messages.Message("PG_PlagueBullet_SuccessMessage".Translate(new object[] {
this.launcher.Label, hitPawn.Label
}), MessageSound.Standard);
//This checks to see if the character has a heal differential, or hediff on them already.
var plagueOnPawn = hitPawn?.health?.hediffSet?.GetFirstHediffOfDef(Def.HediffToAdd);
var randomSeverity = Rand.Range(0.15f, 0.30f);
if (plagueOnPawn != null)
{
//If they already have plague, add a random range to its severity.
//If severity reaches 1.0f, or 100%, plague kills the target.
plagueOnPawn.Severity += randomSeverity;
}
else
{
//These three lines create a new health differential or Hediff,
//put them on the character, and increase its severity by a random amount.
Hediff hediff = HediffMaker.MakeHediff(Def.HediffToAdd, hitPawn, null);
hediff.Severity = randomSeverity;
hitPawn.health.AddHediff(hediff, null, null);
}
}
else //failure!
{
/*
* Motes handle all the smaller visual effects in RimWorld.
* Dust plumes, symbol bubbles, and text messages floating next to characters.
* This mote makes a small text message next to the character.
*/
MoteMaker.ThrowText(hitThing.PositionHeld.ToVector3(), hitThing.MapHeld, "PG_PlagueBullet_FailureMote".Translate(Def.AddHediffChance), 12f);
}
}
}
#endregion Overrides
}
}
Severity Code Description Project File Line Suppression State
Error CS0103 The name 'hitPawn' does not exist in the current context PlagueGun C:\Program Files (x86)\Steam\steamapps\common\RimWorld\Mods\PlagueGun\PlagueGun\Projectile_PlagueBullet.cs 34 Active
if (Def != null && hitThing != null && hitThing is Pawn hitPawn) //Fancy way to declare a variable inside an if statement. - Thanks Erdelf.
if (Def != null && hitThing != null ) //Fancy way to declare a variable inside an if statement. - Thanks Erdelf.
{
Pawn hitPawn = hitThing as Pawn
if(hitPawn!=null)
{
var rand = Rand.Value; // This is a random percentage between 0% and 100%
if (rand <= Def.AddHediffChance) // If the percentage falls under the chance, success!
{
/*
* Messages.Message flashes a message on the top of the screen.
* You may be familiar with this one when a colonist dies, because
* it makes a negative sound and mentioneds "So and so has died of _____".
*
* Here, we're using the "Translate" function. More on that later in
* the localization section.
*/
Messages.Message("PG_PlagueBullet_SuccessMessage".Translate(new object[] {
this.launcher.Label, hitPawn.Label
}), MessageSound.Standard);
//This checks to see if the character has a heal differential, or hediff on them already.
var plagueOnPawn = hitPawn?.health?.hediffSet?.GetFirstHediffOfDef(Def.HediffToAdd);
var randomSeverity = Rand.Range(0.15f, 0.30f);
if (plagueOnPawn != null)
{
//If they already have plague, add a random range to its severity.
//If severity reaches 1.0f, or 100%, plague kills the target.
plagueOnPawn.Severity += randomSeverity;
}
else
{
//These three lines create a new health differential or Hediff,
//put them on the character, and increase its severity by a random amount.
Hediff hediff = HediffMaker.MakeHediff(Def.HediffToAdd, hitPawn, null);
hediff.Severity = randomSeverity;
hitPawn.health.AddHediff(hediff, null, null);
}
}
else //failure!
{
/*
* Motes handle all the smaller visual effects in RimWorld.
* Dust plumes, symbol bubbles, and text messages floating next to characters.
* This mote makes a small text message next to the character.
*/
MoteMaker.ThrowText(hitThing.PositionHeld.ToVector3(), hitThing.MapHeld, "PG_PlagueBullet_FailureMote".Translate(Def.AddHediffChance), 12f);
}
}
}
if (Def != null && hitThing != null ) //Fancy way to declare a variable inside an if statement. - Thanks Erdelf.
{
Pawn hitPawn = hitThing as Pawn
if(hitPawn!=null)
Quote from: steeveebr on June 04, 2017, 12:58:03 PM
Out of curiosity: Step 11 states to copy all of the ThingDef Parents and Bases to your XML...
And then your code snippet also includes BaseHumanGun. Is that inclusion necessary? I don't see it as a parent in either the Pistol Bullet or the Pistol Gun.
Quote from: Cryusaki on July 03, 2017, 04:23:21 PMWhere? I'd like to improve the tutorial. So what's confusing?
Probably the best modding tutorial for Rimworld on the internet but it is still missing so much detail that it's hard to follow
Quote from: jecrell on June 03, 2017, 05:01:31 AMJust a quick pointer (0x154ca6e7 hahahah) You may want to specify the Visual Studio Community version (2017). I see so many people asking questions because their VS is an earlier version that cannot read C# 7...Required Items
Notepad++ (https://notepad-plus-plus.org/) or
Atom (https://atom.io/) or
Sublimetext (https://www.sublimetext.com/)| Use any text editor that allows you to edit XML files and use "Find in Files" for referencing. Visual Studio Community (https://www.visualstudio.com/vs/community/) | Use this or any other C# compiler to turn scripts into .dll files that RimWorld can use. Zentar's ILSpy (https://github.com/Zhentar/ILSpy/releases) | This is for referencing the game's decompiled C# scripts. **NOTE** Regular ILSpy will not give you the cleanest code. For best results, please use Zhentar's ILSpy.
If you like this mod, why not consider looking at some of my other mods?
[b]Harsher Tundra[/b]
No trees in the tundra biome, fewer grazing animals. Ice patches.
[b]Really Toxic Fallout[/b]
Even brief exposure to toxic fallout can now cause life-threatening complications. Stay indoors!
[b]Less Annoying UI Sounds[/b]
You no longer need go berserk because of an annoying ding or boo-doop.
[b]No Doomed Friendlies[/b]
Friendlies will not show up if you haven't opened the ancient danger room.
[b]Solar Apocalypse & Rogue Planet[/b]
These planet-threatening game conditions will have you researching ship-building like a boss.
[b]Peaceful Fixes[/b]
Peaceful difficulty is new, and there are a few events which show up despite it supposedly being free of threats.
[b]Difficulty Expansion[/b]
Is Extreme not extreme enough for you? Are the easier difficultites not easy enough?
[b]Combat Readiness Check[/b]
Raids now scale mostly based on your colonists, how healthy they are, and what weapons or armour you can equip them with.
[b]No Doomed Newbies[/b]
Wanderers who join, and chased refugees who refuge, and even escape pod survivors will be more relevant to your colony.
Quote from: D Mars on August 03, 2017, 05:59:31 PM
Hey so I followed your guide, I'm able to spawn the Plague gun in game but it doesn't work when I try shooting another colonist. I downloaded all 3 files so I can visually compare them and everything seems to match up. I get this error on load though(http://imgur.com/a/T7X7i), any ideas on where I went wrong? Thanks!
Quote from: Wof on August 03, 2017, 08:36:42 PM
I had the same issue. The problem was that the .dll wasn't created in my Assemblies folder but instead in a new folder inside the Source folder. I just moved it and it was fine.
Quote from: Eck on August 06, 2017, 03:06:36 PM
Awesome tutorial, but I think there are some unnecessary steps. You don't need to (and I don't think you should) include all the parent base items in your custom RangedWeapon_PlagueGun.xml file. Those entries will already included from the Core game files so the copied entries aren't necessary (and might cause conflicts). Basically skip step 11, and remove every ThingDef in the RangedWeapon_PlagueGun.xml file after the BASES comment.
Quote20) Go to the Build tab in the project properties.
21) Change the output path to be RimWorld\Mods\PlagueGun\Assemblies
- All .dll files will go into this directory when we "build" our code library.
22) In that same window, click the Advanced... button.
23) Change Debugging information to none.
- This prevents a simple error that occurs when RimWorld doesn't know what to do with the debug .pdb files.
Quote from: Jibbles on August 23, 2017, 02:11:00 AM
Find it strange that there isn't a completed version to download so we can test and see how everything is supposed to look and work. Can someone share? I'm grateful for this tutorial but I'm not able to get it to work.
still missing the / in xml file.
I come across errors even though I've copied things word for word. Guess I'm doing something wrong when it comes building the project.
Could not find type named Plague.ThingDef_PlagueBullet from node
Could not find a type named Plague.Projectile_PlagueBullet
XML error: <AddHediffChance>0.05</AddHediffChance> doesn't correspond to any field in type ThingDef.
XML error: <HediffToAdd>Plague</HediffToAdd> doesn't correspond to any field in type ThingDef.
Config error in TST_Bullet_PlagueGun: TST_Bullet_PlagueGun has null thingClass.
Quote from: SpaceDorf on August 06, 2017, 05:57:16 AM
Thank you flying spaghetti monster for finally sending my to the right IDE
sometimes microsoft gets it right (https://sourceforge.net/projects/vscode-portable/)
it's fast, it's portable, it's beautiful ..
Severity Code Description Project File Line Suppression State
Error CS0117 'HediffDefOf' does not contain a definition for 'Petrification' BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\BasiliskVision.cs 15 Active
Error CS1061 'Projectile_PetrifyBullet' does not contain a definition for 'def' and no extension method 'def' accepting a first argument of type 'Projectile_PetrifyBullet' could be found (are you missing a using directive or an assembly reference?) BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 16 Active
Error CS1061 'Projectile_PetrifyBullet' does not contain a definition for 'launcher' and no extension method 'launcher' accepting a first argument of type 'Projectile_PetrifyBullet' could be found (are you missing a using directive or an assembly reference?) BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 30 Active
Error CS0115 'Projectile_PetrifyBullet.Impact(Thing)': no suitable method found to override BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 21 Active
Error CS1061 'string' does not contain a definition for 'Translate' and no extension method 'Translate' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 29 Active
Error CS0103 The name 'HediffMaker' does not exist in the current context BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 40 Active
Error CS0103 The name 'Messages' does not exist in the current context BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 29 Active
Error CS0103 The name 'MessageSound' does not exist in the current context BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 31 Active
Error CS0103 The name 'Rand' does not exist in the current context BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 26 Active
Error CS0103 The name 'Rand' does not exist in the current context BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 33 Active
Error CS0246 The type or namespace name 'Bullet' could not be found (are you missing a using directive or an assembly reference?) BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 8 Active
Error CS0246 The type or namespace name 'Hediff' could not be found (are you missing a using directive or an assembly reference?) BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 40 Active
Error CS0246 The type or namespace name 'Pawn' could not be found (are you missing a using directive or an assembly reference?) BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 24 Active
Error CS0234 The type or namespace name 'Tasks' does not exist in the namespace 'System.Threading' (are you missing an assembly reference?) BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\BasiliskVision.cs 5 Active
Error CS0246 The type or namespace name 'Thing' could not be found (are you missing a using directive or an assembly reference?) BasiliskEye C:\Users\USUARIO\source\repos\BasiliskEye\BasiliskEye\Petrify.cs 21 Active
Quote from: DJRockXD on October 12, 2017, 06:48:33 AMKudos to the tutorial maker and suggesting folks learn to search the Defs -- you'll be doing a ton of this when you start down your own path. But for this one, no need to search blindly for strings in files. All the weapons stuff is in Defs\ThingDefs_Misc\Weapons_Guns.xml. There Bullet_Pistol def is at line 65. There is no standalone version - although each ThingDef could probably be spread out in its own file, but that would be a nightmare in other ways.
I couldn't find the "defName" file or the "Bullet_Pistol" document using that "find in file" thing. Plus I could only find that function in Notepad++, not in Atom...
I found a "Bullet_Pistol" reference manually in the "Weapons_Guns" XML document, but not a stand alone version of it...
And I am probably an idiot for not being able to make the "Find in Files" function work even in Notepad++, as it couldn't find anything on my computer when I tested it out multiple times... Help please ):
}), MessageSound.Standard);
}), MessageTypeDefOf.NeutralEvent);
Quote from: StoneWolf on November 06, 2017, 01:51:24 PM
In your tutorial you advise on setting a prefix for the defName variable in order to increase mod compatability. Should I do the same for the file name? Or does it really not matter what its called?
if (Def != null && hitThing != null)
{
Pawn hitPawn = hitThing as Pawn;
if(hitPawn != null)
Messages.Message("TST_PlagueBullet_SuccessMessage", new MessageTypeDef());
var plagueOnPawn = hitPawn?.health?.hediffSet?.GetFirstHediffOfDef(Def.HediffToAdd);
var plagueOnPawn = hitPawn.health.hediffSet.GetFirstHediffOfDef(Def.HediffToAdd);
Quote from: tudy on December 28, 2017, 05:02:41 PMIf I'm understanding correctly, the error-checking you're thinking of is encapsulated in the is operator already. It will return false if hitThing can't be coerced into being a Pawn, which will prevent the entire block from happening at all. (If you have as for is in the if statement, then you're correct, it is possible to get a null result stored in hitPawn if given a non-pawn object.)
ThisCode Select
if (Def != null && hitThing != null)
{
Pawn hitPawn = hitThing as Pawn;
if(hitPawn != null)
helps, if you get an error in the if-clause, when initiating "hitPawn" inside of it.
QuoteI had no problems building with the ?s using the suggested IDE. It's possible it's a compatibility
Then this line prevented me from building:Code Select
var plagueOnPawn = hitPawn?.health?.hediffSet?.GetFirstHediffOfDef(Def.HediffToAdd);
I cancelled out the question-marks, resulting in the buildable version:Code Select
var plagueOnPawn = hitPawn.health.hediffSet.GetFirstHediffOfDef(Def.HediffToAdd);
Not 100% sure what these "?" are actually doing, but the result works as intended, so, if anyone else got that problem, this might be your solution as well.
QuoteBasically the class tells Rimworld that a def category exists. It's a bridge between the engine and the XML files; without that, it doesn't know how to parse the XML. It also allows other code to refer to a def class; that's not important for this specific class, but it's critical for many classes.
1) What is the first .cs file good for (Steps 31 - 35)? I can't wrap my head around, what it actually does. Isn't all of that done already in the XML-file? If it is just about making a blueprint, couldn't we put all of what's happening there just into the second .cs file created?
QuoteThe values in the .cs file are basically placeholders or defaults. You can think of it like having a default parameter for a function. For example, if a default AddHediffChance exists in the code definition for ThingDef_PlagueBullet, then if your XML happens to have a ThingDef_PlagueBullet that lacks an AddHediffChance, it will use the default. If there's no default and nothing in the XML it will give an error when loaded.
2) To test out the mod I wanted to put the chance to apply the Plague to 100%. So, I went into the first .cs file (the one I wonder what it actually is doing) and set the value to "1.0f". But it had no effect whatsoever. Only after I changed it in the XML to "1.0" it applied the Plague to each shot. Why is that? What's the difference between the value set in the .cs-file and the xml-file? Where should I actually change such values in good practice?
Quote from: tudy on December 28, 2017, 05:02:41 PM
Then this line prevented me from building:Code Select
var plagueOnPawn = hitPawn?.health?.hediffSet?.GetFirstHediffOfDef(Def.HediffToAdd);
I cancelled out the question-marks, resulting in the buildable version:Code Select
var plagueOnPawn = hitPawn.health.hediffSet.GetFirstHediffOfDef(Def.HediffToAdd);
Not 100% sure what these "?" are actually doing, but the result works as intended, so, if anyone else got that problem, this might be your solution as well.
QuoteException ticking NEGO_Bullet_PlagueGun110669: System.StackOverflowException: The requested operation caused a stack overflow.
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
.
. have to shorten it because it's too long for the post...
.
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
Verse.Log:Error(String)
Verse.TickList:Tick()
Verse.TickManager:DoSingleTick()
Verse.TickManager:TickManagerUpdate()
Verse.Game:UpdatePlay()
Verse.Root_Play:Update()
Quote<?xml version="1.0" encoding="utf-8"?>
<ThingDefs>
<ThingDef Class="Plague.ThingDef_PlagueBullet" ParentName="BaseBullet">
<defName>NEGO_Bullet_PlagueGun</defName>
<label>pistol bullet</label>
<graphicData>
<texPath>Things/Projectile/Bullet_Small</texPath>
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<projectile>
<flyOverhead>false</flyOverhead>
<damageDef>Bullet</damageDef>
<DamageAmountBase>1</DamageAmountBase>
<Speed>55</Speed>
</projectile>
<AddHediffChance>1.0</AddHediffChance>
<HediffToAdd>Plague</HediffToAdd>
<thingClass>Plague.Projectile_PlagueBullet</thingClass>
</ThingDef>
<ThingDef ParentName="BaseHumanMakeableGun">
<defName>NEGO_Gun_PlagueGun</defName>
<label>plague gun</label>
<description>A curious weapon notable for its horrible health effects.</description>
<graphicData>
<texPath>Things/Item/Equipment/WeaponRanged/Pistol</texPath>
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<soundInteract>InteractPistol</soundInteract>
<statBases>
<WorkToMake>15000</WorkToMake>
<Mass>1.2</Mass>
<AccuracyTouch>1.0</AccuracyTouch>
<AccuracyShort>1.0</AccuracyShort>
<AccuracyMedium>1.0</AccuracyMedium>
<AccuracyLong>1.0</AccuracyLong>
<RangedWeapon_Cooldown>1.26</RangedWeapon_Cooldown>
</statBases>
<costList>
<Steel>30</Steel>
<Component>2</Component>
</costList>
<verbs>
<li>
<verbClass>Verb_Shoot</verbClass>
<hasStandardCommand>true</hasStandardCommand>
<projectileDef>NEGO_Bullet_PlagueGun</projectileDef>
<warmupTime>0.3</warmupTime>
<range>24</range>
<soundCast>ShotPistol</soundCast>
<soundCastTail>GunTail_Light</soundCastTail>
<muzzleFlashScale>9</muzzleFlashScale>
</li>
</verbs>
</ThingDef>
</ThingDefs>
Quoteusing System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RimWorld;
using Verse;
namespace Plague
{
class Projectile_PlagueBullet : Bullet
{
#region Properties
public ThingDef_PlagueBullet Def
{
get
{
return this.Def as ThingDef_PlagueBullet;
}
}
#endregion
#region Overrides
protected override void Impact(Thing hitThing)
{
base.Impact(hitThing);
Pawn hitPawn = hitThing as Pawn;
float randomSeverity = Rand.Value;
if (Def != null && hitThing != null && hitPawn != null)
{
var rand = Rand.Value;
if(rand <= Def.AddHediffChance)
{
Messages.Message("NEGO_PlagueBullet_SuccessMessage".Translate(new object[]
{
this.launcher.Label, hitPawn.Label
}), MessageSound.Standard);
}
var plagueOrPawn = hitPawn?.health?.hediffSet?.GetFirstHediffOfDef(Def.HediffToAdd);
if (plagueOrPawn != null)
{
plagueOrPawn.Severity += randomSeverity;
}
else
{
Hediff hediff = HediffMaker.MakeHediff(Def.HediffToAdd, hitPawn, null);
hediff.Severity = randomSeverity;
hitPawn.health.AddHediff(hediff, null, null);
}
}
else
{
MoteMaker.ThrowText(hitThing.PositionHeld.ToVector3(), hitThing.MapHeld, "NEGO_PlagueBullet_FailureMote".Translate(Def.AddHediffChance), 12f);
}
}
#endregion
}
}
Quote from: Negomir99 on April 11, 2018, 11:43:25 AMAs a general rule, a stack overflow happens when you cause RW to try to do something recursively and end up in an infinite loop. In this case, you can see that it's in the Def getter, here:
This tutorial is great, I managed to get the gun into the game, even tweak its accuracy and other stats, but every time the bullet hits anything I get this error:QuoteException ticking NEGO_Bullet_PlagueGun110669: System.StackOverflowException: The requested operation caused a stack overflow.
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0
public ThingDef_PlagueBullet Def
{
get
{
return this.Def as ThingDef_PlagueBullet;
}
}
if (Def != null && hitThing != null && hitPawn != null)
Quote from: jamaicancastle on April 11, 2018, 01:01:34 PMQuote from: Negomir99 on April 11, 2018, 11:43:25 AMAs a general rule, a stack overflow happens when you cause RW to try to do something recursively and end up in an infinite loop. In this case, you can see that it's in the Def getter, here:
This tutorial is great, I managed to get the gun into the game, even tweak its accuracy and other stats, but every time the bullet hits anything I get this error:QuoteException ticking NEGO_Bullet_PlagueGun110669: System.StackOverflowException: The requested operation caused a stack overflow.
at Plague.Projectile_PlagueBullet.get_Def () [0x00000] in <filename unknown>:0Code Selectpublic ThingDef_PlagueBullet Def
{
get
{
return this.Def as ThingDef_PlagueBullet;
}
}
In this case it's calling this.Def - which is to say, itself, hence the infinite loop. It should be calling this.def (note the capitalization), which is a property of the base object.
The code goes along fine until it actually tries to get Def, which it does in Impact, specifically in this line:Code Selectif (Def != null && hitThing != null && hitPawn != null)
Because it runs into the error, the rest of the Impact method doesn't get run, hence why it doesn't apply its impact effects.
Quote<ThingDef Class = "PlagueGun.ThingDef_PlagueBullet" ParentName="BaseBullet">
<defName>Bullet_PlagueGun</defName>
<label>PlagueGun bullet</label>
<graphicData>
<texPath>Things/Projectile/Bullet_Small</texPath>
<graphicClass>Graphic_Single</graphicClass>
</graphicData>
<projectile>
<flyOverhead>false</flyOverhead>
<damageDef>Bullet</damageDef>
<damageAmountBase>11</damageAmountBase>
<speed>55</speed>
</projectile>
<AddHediffChance>0.5</AddHediffChance>
<HediffToAdd>Plague</HediffToAdd>
<thingClass>PlagueGun.Projectile_PlagueGun</thingClass>
</ThingDef>
Quotenamespace PlagueGunand
{
public class ThingDef_PlagueBullet : ThingDef
{
public float AddHediffChance = .5f;
public HediffDef HediffToAdd = HediffDefOf.Plague;
}
}
Quotenamespace PlagueGun
{
class Projectile_PlagueGun : Bullet
{
public ThingDef_PlagueBullet Def
{
get
{
return this.def as ThingDef_PlagueBullet;
}
}
#region Overrides
protected override void Impact(Thing hitThing)
{
Pawn hitPawn = hitThing as Pawn;
if (Def != null && hitThing != null && hitPawn != null)
{
var rand = Rand.Value;
if (rand <= Def.AddHediffChance)
{
var plagueOnPawn = hitPawn.health.hediffSet.GetFirstHediffOfDef(Def.HediffToAdd);
var randomSeverity = .3f;
if (plagueOnPawn != null)
{
plagueOnPawn.Severity += randomSeverity;
}
else
{
Hediff hediff = HediffMaker.MakeHediff(Def.HediffToAdd, hitPawn, null);
hediff.Severity = randomSeverity ;
hitPawn.health.AddHediff(hediff, null, null);
}
}
}
}
#endregion Overrides
}
}
Quote from: Eck on August 06, 2017, 03:06:36 PM
Awesome tutorial, but I think there are some unnecessary steps. You don't need to (and I don't think you should) include all the parent base items in your custom RangedWeapon_PlagueGun.xml file. Those entries will already included from the Core game files so the copied entries aren't necessary (and might cause conflicts). Basically skip step 11, and remove every ThingDef in the RangedWeapon_PlagueGun.xml file after the BASES comment.
I followed the tutorial through and got everything working even though I raised my eyebrow at the copy/pasted base classes. After that, I deleted all the base def entries, changed some other things like fire rate and range, and everything still worked.
@Tynan - Could you double check my post and make sure I'm giving good advice. I only just started messing with modding Rim World and even though I tested what I said, I wouldn't mind a sanity check. :)
- Eck