Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Topics - Knight

#1
Releases / [1.0] Medical IV's - Update 1.4.1
June 09, 2019, 10:46:08 PM

[Description]
Medical IV's aims to further improve your healthcare quality by adding IV drips (Intraveneous Therapy).
So far the mod adds 6 different IV's; a blood transfusion that reduces the bleed rate of a colonist, a nutritional drip which provides an immobile colonist with constant food,
a painkiller drip which reduces a colonist's pain significantly, an antibiotic drip which improves a colonist's blood filtration/immunity gain speed,
an anesthetic drip which keeps a pawn in a medically induced coma and finally a glitterworld drip that combines the functionality of the painkiller, antibiotic and blood drip.
Additionally, each drip will provide boosts to both medical tend quality and surgery success chance when placed adjacent to a hospital bed.


- Powered by Harmony and HugsLib

https://steamcommunity.com/sharedfiles/filedetails/?id=1424438685

[How to Use]
All of the IV's will function when placed adjacent to any bed or sleeping spot. They will provide the health benefits to all pawns that are in a bed and directly above, below, left or right of the IV but not if they're diagonal from it. The 6 drips are based on the Vitals Monitor and as such each machine should be placed adjacent to any hospital bed for the medical offsets to work - this means that tend quality and other bed quality benefits will only work for hospital beds.

[Medical IV's](The Big Update)
Medical IV's introduces a new, key piece of technology from the modern era of medicine - intravaneous therapy or "medical drips". In real life, these drips contain fluids such as antibiotics, painkillers or any other medication and make use of tubes to directly inject the fluid into a person's bloodstream. This update for Medical IV's sees the majority of code rewritten, the addition of 2 extra machines and dozens of bug fixes or general QoL improvements or balancing. Minor changes include a new logo (Made by me, Knight) and implementation of coveted features/suggestions put forward by others.

[Features]
Currently includes 6 machines:
- A blood IV for reducing blood loss by 75%
- A painkiller IV to block all pain
- An antibiotic IV to speed up recovery from infection and disease
- A nutritional IV to keep bed ridden colonist's fed
- An anesthetic IV to induce a medical coma in a pawn
- A glitterworld IV that combines the functions of the painkiller, blood and antibiotic IV

[Additional Features]
Other features are bundled with the drips to add to the level of realism and improve gameplay. These include:
- Addition of craftable/consumable painkillers
- Addition of craftable/consumable antibiotics
- Addition of blood bags
- A new plant with interesting qualities
- An overhauled refill system
- Ability to upgrade to more convenient machines requiring no refill through research
- The ability to draw blood from colonists / bleed a prisoner to death
- An updated research tab for the mod

[Credits]
Knight: Code & XML ; Minor graphics and Logo
Kotobuki Mugi: Graphics & Large assets
ilikegoodfood: For his BleedRate hediff code

Common FAQ

  • Can I make suggestions? Sure. As a matter of fact, I encourage it! I'd like to push my abilities whilst making the game as fun as possible.
  • Is there an impact on performance? It's highly unlikely you'll experience performance problems with my mod! It only updates every 60 ticks and should absolutely be fine as long as you don't have thousands of the IV drips on the map :)
  • Is it safe to add to existing saves? Yes, it should be absolutely fine to add to existing saves. Nothing has been edited, removed or changed that's related to Rimworld or any other mod.
  • How about removing it? I imagine it wouldn't cause significant issues but obviously be cautious of the fact that placing down objects and then removing the mod associated with those objects could be problematic. It's always best to remove the objects first before uninstalling.
  • Permissions? Don't reupload it anywhere and don't blatantly copy the code - feel free to make patches and compatibility edits (if needed).
  • How do I get blood bags? You can get blood bags by either drawing blood from colonists/prisoners or by growing bloodleaf, crushing it into a powder and then synthesising it into blood bags at a drug lab. You'll require Penyoxycilin production and bloodstem infusion research unlocked though.



Support
If you like or use my mods, consider buying me a coffee on ko-fi. It'll speed up the delivery of updates, any future mods I've got planned and generally motivate me to make more!


https://ko-fi.com/knight_#


[attachment deleted due to age]
#2
With the About.xml file within a mod you can use <![CDATA[ ..... ]]> to implement tags such as <size=23> and <color=#fffffff>, etc. I tried doing this with a LanguageData file within Language/English/Keyed when an incident pops up / letter is received. Unfortunately, it seems like Rimworld only uses the basic 'string' datatype with these dialogue boxes and nothing more unlike the About.xml which uses, correct me if I'm wrong, Rich Text Format.

Any way around this? Or, rather, anything I can do about it? A brief search here got me to a comment saying that most of the text in-game can actually be altered and stylised through 'control codes' and that images can actually be inserted too. Outside of the about.xml, I just can't see where.

Cheers
#3
Is there a technical way of seeing which mod is causing lag spikes? I don't want to have to manually go through each of my mods and check, surely there's a mod tool or even another mod altogether that can help here? From what I've briefly tested, no Dev setting is capable of this.

Any help? Thanks.
#4
I'm trying to make a script run the instant a pawn wears a specific piece of apparel. I've written the script but there's nothing inside the Apparel class that can be overridden that allows for scripts to be run when worn. I tried to create a constructor but this just returns the following error once being spawned into the world:

Root level exception in OnGUI(): System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object

Any suggestions? Does "ExposeData()" run once an item is worn? Maybe I could call upon a method there?

Thanks.
#5
A mod I've been making for a while now needs to be able to differentiate between left/right bones or organs. This includes left radius, left femur, left lung, right lung, right radius, right femur... etc. You get the idea... In B19 this, AFAIK, is no longer possible without some extra trickery. Before you used to be able to simply call on the bodypart's .def and it'd return "LeftLung" for example. The code was along the lines of:

part.def.ToString()
> LeftLung


Now that'd actually just return:

> Lung

The new workaround is far more complex and doesn't always work. You'd have to actually call upon the BodyPartDef's customLabel and then convert that to a string before checking it with .Contains(). For example:

if (part.customLabel.ToString().Contains("left lung")
{
    doThing();
}


The issue with this is that sometimes the code, at least for me, would go wrong and would look at the space as two different words rather than trying to match the whole statement. So I'd actually have doThing() be called when the bodypart in question was "left" anything rather than "left lung" specifically.

Is this intended behaviour? I'm not entirely sure why this'd need to be changed from what it was, if I'm honest. It seems more annoying to get around and BodyPartRecord's now are less detailed and more vague actually returning things like: BodyPartRecord(Lung, ticksSinceCreation=1) and not specifying which lung like it used to in B18. I'm hoping this is a bug because I don't want to have to add several new checks and if statements inside of my code just to check (sometimes unsuccessfully) what side the part/bone/organ is on.
#6
Previously Rimworld just accepted "MinifiedDef" and that was that but now it takes "MinifiedThing" and it throws the error:

{Building} is minifiable but not in any thing category

I tried adding the tag <category>Building</category> but that didn't work.

Any help?
#7
public static bool Patch_Prefix(ref Pawn pawn, ref Bill bill, ref IBillGiver giver)
    {
        Type billType = bill.GetType();
        if (billType == Bill_Medical)
        {
            doThing();
        }


This is quite self-explanatory, right?

I'm trying to check to see if the type of a bill is Bill_Medical and, if it is, doThing(). Unfortunately, VS is telling me that I can't see if something is a type because it's a type... yeah.

Although, it has literally just occurred to me that this might not work because the thing it's checking is already type "Bill". So, are there better ways of doing this? I need to check if the given bill is a medical bill - "Bill_Medical" - rather than a normal bill. Given that this is a patch, I can't just change the type in the method parameter.

I'm assuming that I can check to see if a Bill is a Bill_Medical because Bill_Medical inherits and is a type of Bill, right? If not, how else can I check to see if a bill is medical only?

Sorry if this is confusing and thanks.
#8
I'd like to give a hediff hand-in-hand with a trait. Basically, if a pawn has trait X then I'd like to give them a hediff literally at game start (or before-hand in the colony setup screen).

Is there any way to do this using .XML or a really simple way to do it using scripting that has as little impact possible?

Cheers
#9
EDIT: Solution at bottom

Is there a way to only patch something on a certain event using Harmony as opposed to patching it always?

I'd like to patch a method using Harmony only when, for example, a medical bill is called "Medical_Bill_123" rather than instantly, always and when the method is called. So, normally Harmony would always run my Prefix patch whenever the original method is called no matter what. I'd like it to be something like this:

if (Bill_Medical.Contains("Medical_Bill_123")
{
    RunPatch(HarmonyPatch2);
}

HarmonyPatch2
{

    static void Postfix(ref bool __result)
    {
        __result = true;
    }
}


Any help would be appreciated, thanks!

SOLUTION:

Harmony has the ability to manually patch each method you define. To do this you need to remove the .PatchAll() line and instead do .Patch(targetmethod [original method], [prefix method], [postfix method]); You can keep .PatchAll() but remove the [HarmonyPatch] annotation on the patch you'd like to NOT be enabled at startup. Therefore, when you reached the condition you want, you can call .Patch() and control the method you'd like to patch more easily. For example:

If (number == 2)
{
    harmony.Patch(original, method1, null)
}


The code above would ONLY patch when the number is equal to 2.
#10
EDIT: Solution at bottom!

I need to get the billDoer for a medical bill (read: Surgeon). How could I do this in the simplest way? I thought that a simple Harmony patch to return the current billDoer would work but no class or method explicitly defines or returns the pawn that is currently doing the job, they usually only take the billDoer as a parameter and that comes with its own issues as shown below. I think I need a method with the billDoer parameter that is called before work has begun on a job although I'm not sure what method does that. Maybe a different, lower-level function will achieve what I need?

This is what I've got so far and the code works but it doesn't do what I need it to do:

[HarmonyPatch(typeof(Bill_Medical))]
[HarmonyPatch("Notify_DoBillStarted")]
public class GetSurgeonPatch
{
    public static Pawn Surgeon;

    static void Postfix(ref Pawn billDoer)
    {
        Pawn Surgeon = billDoer;
        Log.Message("Surgeon: " + Surgeon);
    }
}


The reason the above doesn't work is because it returns the surgeon for the medical bill but only when the work has begun. For example, when the 'yellow progress bar' begins ticking up beneath the Pawn. This isn't what I want, I need the surgeon to be returned before the work has begun / when the job has been assigned to that particular pawn - both will work. Essentially I need this:

1) Medical Bill assigned to Patient
2) Surgeon assigned to Medical Bill
2.5) Surgeon name given to my mod
3) Work begins
4) Work complete

So, is there something I'm missing? Perhaps there's a PostFix or Prefix patch using Harmony that'd return the billDoer or maybe a new method I can write that'd provide that information to me? Any help would be appreciated.

Cheers.

Solution:

The Harmony patch works but, as I thought, I was using the wrong method to implement the patch in-game. A few minor edits and information allowed me to get what I wanted working. I began using a Prefix patch to tap into TryStartNewDoBillJob and then do some checks before the original method was allowed to run:

[HarmonyPatch(typeof(WorkGiver_DoBill))]
[HarmonyPatch("TryStartNewDoBillJob")]
public class GiverAndDoer
{
    static bool Prefix(ref Pawn pawn, ref Bill bill, ref IBillGiver giver)
    {
        if (bill.ToString().Contains("Bill_Administer_SerumOne")) // This checks if the bill is the medical bill I don't want being done by the patient
        {
            if (pawn.ToString() == giver.ToString()) // This checks if the patient is the same as the surgeon
            {
                Log.Message("Patient + Surgeon the same for Bill_Administer_SerumOne");
                return false; // This prevents the original method from running if the above statement is true. Essentially stopping the job from being assigned
            }
            return true;
        }
        return true; // The above statements were false so the original method can be run without issue now!
    }
}


If you only want the name of both the billDoer and billGiver then simply Prefix patch the same method but get the value of Pawn and Giver and then compare. Giver is billGiver and Pawn is billDoer.
#11
Help / How do I edit the game code in my mod?
July 20, 2018, 09:07:36 AM
I'd like to use Harmony to patch a single field on the game start. For example, I want to patch the bool:

public bool requiresBedForSurgery = true; to false. I've tried using Harmony but it hasn't worked so far because I'm fairly certain you can only patch methods with it. Here's what I tried:

[HarmonyPatch(typeof(FleshTypeDef))]
[HarmonyPatch("requiresBedForSurgery")]
static class SurgeryBedPatch
{
    static void Postfix(ref bool __result)
    {
        __result = false;
        Log.Message("Patched SurgeryBed Bool");
    }
}


And this is the error that appears in the console:

Could not execute post-long-event action. Exception: System.TypeInitializationException:
An exception was thrown by the type initializer for Main ---> System.ArgumentException: No target method specified for class SurgeryBedPatch


Am I overlooking something simple or is this not possible? I'd like to universally change this bool at the game start and not just in one particular scenario so I can't just use code to make this happen (I already tried that anyway). Thanks!
#12
I'd like to be able to change the Breathing stat percentage every time X thing is consumed. As far as I'm aware, Rimworld only offers the ability to offset a stat without them stacking so consuming 10 of X would still only have the effects of 1 of X.

Is there a workaround for this? Or something that I'm missing?

Thanks.
#13
I'm extremely new to .DLL modding and C# so bare with me and please ask if you need any other information to help!

So, I've created a piece of code which I'm fairly certain will work if it were not for this error:

Exception loading def from file KN_Machine.xml: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidCastException: Cannot cast from source type to destination type.
  at (wrapper managed-to-native) object:__icall_wrapper_mono_object_castclass (object,intptr)
  at Verse.DirectXmlToObject.ObjectFromXml[ThingDef] (System.Xml.XmlNode xmlRoot, Boolean doPostLoad) [0x00000] in <filename unknown>:0
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---


Here's my code and the prediction I have as to why I'm getting this error:

namespace KN
{
    public class Building_HediffMachine : Building
    {
        public static HediffDef tempHediff;
        public override void Tick()
        {
            Log.Message("[Knight's Mod] Part 1");
            this.Tick();
           
            if (Gen.IsHashIntervalTick(this, 10))
            {
                Log.Message("[Knight's Mod] Part 3");
                IntVec3[] cells = GenAdj.AdjacentCells;
                foreach (IntVec3 item in cells)
                {
                    Log.Message("[Knight's Mod] Part 4");
                    foreach (Pawn pawn in this.Map.thingGrid.ThingsAt(item))
                    {
                        Log.Message("[Knight's Mod] Trying to add Hediff to pawn");
                        pawn.health.AddHediff(tempHediff);
                    }
                }
            }
        }
        public Building_HediffMachine()
        {
            Log.Message("[Knight's Mod] Initialised Scripts"); // This gets printed to the log
        }
    }
}


As you can see I've used the Log to determine exactly where the error was / where the code was stopping. In the log it only prints out "Initialised Scripts" but doesn't print "Part 1". I've got a feeling that this is because the game is trying to search for the BuildingDef despite it being on the main menu and there being no buildings (duh) but I don't know how to only call the function once the object is placed. That's my prediction anyways, I can't be certain obviously. I literally began learning C# about a week ago. Also, it's very unlikely this mod will be posted because I've stolen code from others whilst I still learn - not that this is important information but hey, why not.

Any help would be appreciated! Please keep it simple too, I'm a massive beginner! Thanks.

EDIT: Oh and if someone could tell me how to properly reference a Hediff added by .XML files inside of my .DLL I'd appreciate it!

EDIT 2:

I've managed to fix the original error I was having by moving a line of code (As seen below). However, now I've got a different issue - the log is telling me the following:

Exception loading def from file KN_Machine.xml: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.NullReferenceException: Object reference not set to an instance of an object
  at Verse.Gen.HashOffsetTicks (Verse.Thing t) [0x00000] in <filename unknown>:0
  at Verse.Gen.IsHashIntervalTick (Verse.Thing t, Int32 interval) [0x00000] in <filename unknown>:0
  at KN.Building_HediffMachine.Tick () [0x00000] in <filename unknown>:0
  at KN.Building_HediffMachine.Tick..ctor () [0x00000] in <filename unknown>:0
  at (wrapper managed-to-native) System.Reflection.MonoCMethod:InternalInvoke (object,object[],System.Exception&)
  at System.Reflection.MonoCMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
  --- End of inner exception stack trace ---


Fix to old error below:

namespace KN
{
    public class Building_HediffMachine : Building
    {
        public static HediffDef tempHediff;
        public override void Tick()
        {
            Log.Message("[Knight's Mod] Part 1");
            // removed this.Tick() to stop overflow error

            if (Gen.IsHashIntervalTick(this, 10))
            {
                Log.Message("[Knight's Mod] Part 3");
                IntVec3[] cells = GenAdj.AdjacentCells;
                foreach (IntVec3 item in cells)
                {
                    Log.Message("[Knight's Mod] Part 4");
                    foreach (Pawn pawn in this.Map.thingGrid.ThingsAt(item))
                    {
                        Log.Message("[Knight's Mod] Trying to add Hediff to pawn");
                        pawn.health.AddHediff(tempHediff);
                    }
                }
            }
        }
        public Building_HediffMachine()
        {
            Log.Message("[Knight's Mod] Initialised Scripts"); // This gets printed to the log
            this.Tick() // Added line to call the function
        }
    }
}


Now I believe it's to do with the HashIntervalTick function. Apologies for the flip-flopping and mistakes.
#14
I'm making a variation of the vitals monitor that will provide greater benefits to colonists when they're sleeping in a hospital bed. It works the same in the sense that it must be adjacent to the bed for the benefits to be active. However, the difference is I'd like to check if the bed is adjacent to the machine, a pawn is using it and I'd like to actually apply a Hediff to that specific pawn.

I'm COMPLETELY new to RimWorld modding and am relatively new to C# so go easy on me. That's not to say that I don't know what I'm doing though, I pickup things quite easily.

( I've already created the machine, the Hediff and everything else - I just need to write the code that'd apply it to the pawn sleeping next to it )

Thanks.