Ranged Verb from hediffDef

Started by ilikegoodfood, October 10, 2018, 04:55:18 AM

Previous topic - Next topic

ilikegoodfood

To try and keep this minimal, I'm going to explain only my most immediate issue.

I am attempting to make a hediffDef that grants a ranged verb to pawns, both animals and colonists alike. In order to facilitate the use of ranged verbs in animals I am using the RangeAnimalFramework (Steam Workshop) (Forum) by BrokenValkyrie.

I have created a hediffDef, see below, that uses the standard HediffCompProperties_VerbGiver, which does not flag any errors. As far as I can tell by reading the C#, it isn't limited to tools.

<HediffDef ParentName="AddedBodyPartBase">
<defName>MM_ThermalLanceAssembly</defName>
<label>thermal lance assembly</label>
<labelNoun>a thermal lance assembly</labelNoun>
<!--<spawnThingOnRemoved>ArchotechArm</spawnThingOnRemoved>-->
<comps>
    <li Class="HediffCompProperties_VerbGiver">
<verbs>
<li>
<verbClass>Verb_Shoot</verbClass>
<accuracyTouch>0.7</accuracyTouch>
<accuracyShort>0.8</accuracyShort>
<accuracyMedium>0.9</accuracyMedium>
<accuracyLong>0.85</accuracyLong>
<hasStandardCommand>true</hasStandardCommand>
<defaultProjectile>MM_PolarColossusProjectile</defaultProjectile>
<warmupTime>3</warmupTime>
<burstShotCount>0</burstShotCount>
<minRange>6</minRange>
<range>27</range>
<soundCast>ChargeLance_Fire</soundCast>
<soundCastTail>GunTail_Light</soundCastTail>
<muzzleFlashScale>12</muzzleFlashScale>
</li>
</verbs>
    </li>
</comps>
<stages>
<li>
<statOffsets>
<ComfyTemperatureMin>-40</ComfyTemperatureMin>
<ComfyTemperatureMax>-10</ComfyTemperatureMax>
</statOffsets>
</li>
</stages>
<addedPartProps>
<solid>true</solid>
<partEfficiency>1.1</partEfficiency>
<betterThanNatural>true</betterThanNatural>
</addedPartProps>
    </HediffDef>


Now, once the hediff is added to the pawn, it should add the Verb to their list of verbs. This is where the RangeAnimalFramework comes in. It contains a function, ARA_VerbChecker_Patch, that checks non-colonists for ranged verbs and adds them to the pawn's verb list. It also contains various functions that enable animal behaviors associated with ranged weapons.

Since ARA_VerbChecker_Patch is a prefix to the core TryGetAttackVerb, it should be called each time TryGetAttackVerb is called. If I understand the code correctly, TryGetAttackVerb is called each time a pawn tries to attack a target, therefore the new verb should be added to the animal's verb list and it should use the new verb.
This appears to not be the case.

Furthermore, once I can work out that the verb is correctly being added to the pawn and how to make sure that AnimalRangeFramework allows it to be used in animals, I will then need to override the core TryGetAttackVerb in order to make colonists recognize and use it.

Is my understanding of the code, as described above, correct so far?
If not, what am I getting wrong?

I also need help with why the verb isn't being added or detected by the AnimalRangeFramework.
And then I'll need help with creating a safe, functional override for TryGetAttackVerb, assuming that is the correct thing after all, or whatever else needs to be changed.

Thank you all very much in advance.

P.S.
On a slight side-note, I noticed while poking around inside of EPOE's files that they also had an addedBodyPart hediffDef that granted a ranged verb, but it was commented out. I suspect they tried to do the same at some point and abandoned the idea for some reason.

SargBjornson

Ok, wild idea, but...

How about doing it upside-down? You modify the RangeAnimalFramework (adding new classes) to only work for a given pawn IF the pawn has a Hediff...

ilikegoodfood

As far as I can tell, I shouldn't need to add any additional code to it. It should be called, get the verb-list and then assign the behaviors that are appropriate. I don't currently know why the verb isn't working.

Is it that the HediffCompProperties_VerbGiver only works for tools and not verbs? In that case, why am I not getting an error?

Is it that the verbs from HediffCompProperties_VerbGiver are not actually added to the animal, but are instead added to a separate list? In that case, which list, and how do I access it?
Once I know which list, it should be simple enough to do as you suggest and add in a clause to both ARA_VerbChecker_Patch and TryGetAttackVerb that calls verbs from that list in addition to what it already calls.

So, as you can see, my issue is that I don't know where to look right now, even if I wanted to add the verb in code given the hediff's existence.

Finally, something I've not looked into yet, is that I would want it to show up drafted colonists as if it were a held weapon, so you get the weapon icon, the shoot commands and hold-fire button as normal. That's for after though.

ilikegoodfood

#3
Quote from: SargBjornson on October 10, 2018, 05:27:57 AM
Ok, wild idea, but...

How about doing it upside-down? You modify the RangeAnimalFramework (adding new classes) to only work for a given pawn IF the pawn has a Hediff...

That is a good pointer though. I'll go and look into where the VerbGiver places the verb in more detail. I had assumed that it would end up in the same place as the verbs attached to the raceDef, but that may not be the case.

EDIT 1: All verbs are sent to this.VerbTracker, which is a list of verbs that that pawn has access to. Equipement verbs, pawn verbs, turret verbs, it doesn't matter what, it ends up there.

ARA_VerbCheck_Patch iterates through that list and returns false if the creature has a verb with a range greater than 1.1f. Looking through the harmony wiki, this terminates the patch and prevents the original method from being run entirely, which makes sense since, as animals don't use equipment, it is unnecessary.
At this point, at least for the animals, the alterations to the behavior tree take over and the animal prioritizes the use of ranged attacks. This also explains why they can't handle multiple ranged verbs with different ranges, because a pawn can only have one primary verb.

Now, all of this new-found understanding leads me to the exact same conundrum as before: Is it the VerbGiver failing to pass the verb on, which seems unlikely, or is the verbTracker.AllVerbs failing to update the verb list, which also seems extremely unlikely.
The behaviors should simply all work as is.

ilikegoodfood

I think that I may have found the issue, at least part of it.

There is a separate function called GetHediffsVerbs that is invoked by ChooseMeleeVerb, but is not invoked by either ARA_VerbCheck_Patch or by TryGetAttackVerb.

In order to make it work, I would need to write a patch for ARA_VerbCheck_Patch that first invokes GetHediffsVerbs, then iterates through all of the verbs including those from the hediffs.
I would then also need to make an override for TryGetAttackVerb that checks for ranged attack verbs from the hediffs After checking equipment and before checking for Melee Verbs. By ordering it that way, the pawn would the equipment (gun) provided and only use their innate verb if they don't have a gun.

This does, however, lead to a question of how to handle multiple ranged verbs on a single pawn. I will eventually need to write a chooseRangedeVerb that works similarly to chooseMeleeVerb, selecting the most appropriate verb from the available list.

Now, all of that said, I have never written a patch for a patch, nor have I created a total override before. Do you have any advice on how to go about this?

SargBjornson

Why do you need to patch it? Just clone it and make a different DLL for your mod! That way you can modify it as much as you want, and as long as you use a different namespace it won't conflict with any other mod using animals and ranged attacks! It's what I'm doing with Genetic Rim and Alpha Animals

ilikegoodfood

Won't cloning it cause it to conflict with any other instance of AnimalRangeFramework?
I'm not sure how the priority patching system works and I'm not sure how that'd work, for instance, between our two mods, which both use different clones of it.
Surely using the base framework and then patching it would make compatibility easier?

SargBjornson

huh, now that I think of it, the AnimalRangeFramework isn't called by namespace... I'll have to check my own mods to see if they actually work together...

ilikegoodfood

#8
Indeed.
I'll wait on your tests before I start writing additional patches. I'll need to re-organize my assembly either way.

Also, a future project I would like to target is compatibility with your own mod. It's very cool and the more unique aspects of my creatures should work really well with your work if properly implemented.

Currently looking for an artist too. My work is passable, but not good, and I've discovered that it can be a deal-breaker for my subscribers.

ilikegoodfood

#9
I have considered the issue at hand and have come up with two viable ways of organising it:

1) Create an additional prefix patch for TryGetAttackVerb and prioritize it so that triggers before ARA_VerbCheck_Patch. The prefix I create will then perform the job of both ARA_VerbCheck_Patch and TryGetAttackVerb, including the additional behavior that I need it to perform and then it will always return false, thus preventing the original functions from being called.
This will make the mod incompatible with anything else that modifies these functions.

2) Write a transpiler patch for each of the two separate functions that behave in exactly the same way as those functions, aside from the additional check. This should better maintain compatibility, but may be significantly harder to actually pull off.

I will then need to write some varient of the chooseMeleeVerb function that acts as a chooseRangeVerb, preferably giving the player UI control over which verb is executed, in order to allow for circumstances where multiple additional ranged verbs are added to a single pawn. I have no idea how to do this bit, but one step at a time.

EDIT: Is it even possible to run a transpiler patch on a prefix patch?

ilikegoodfood

So, I have decided to go with the approach of using a transpiler patch to separately edit each of ARA_VebCheck_Patch and TryGetAttackVerb.

As such, I have begun to read into transpilers, using this tutorial and it's attached resources.

What I've created should now correctly find the required line in the code and insert a new block of code there. I have, however, no idea how to write that new block of code or what would be the most efficient form it could take.

Any and all help/advice would be appreciated.
Thanks.

Here is my current code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using Harmony;
using RimWorld;
using Verse;
using AnimalRangeAttack;

namespace MonsterMash
{
    [HarmonyPatch(typeof(HarmonyPatch), "ARA_VerbCheck_Patch", null)]
    class harmonyPatches_ARA_VerbCheck_Patch
    {
        private static IEnumerable<CodeInstruction> newCodeList;

        static IEnumerable<CodeInstruction> Transpiler(MethodBase original, IEnumerable<CodeInstruction> instructions)
        {
            int startIndex = -1, targetIndex = -1;

            var codes = new List<CodeInstruction>(instructions);
            for (int i = 0; i < codes.Count; i++)
            {
                if (codes[i].opcode == OpCodes.Callvirt && codes[i + 1].opcode == OpCodes.Clt)
                {
                    startIndex = i; //Provides the index of the callvirt before the call I need.
                    for (int j = startIndex; j < codes.Count; j++)
                    {
                        if (codes[j].opcode == OpCodes.Ldc_I4_1)
                        {
                            targetIndex = j; //Finds the first line of code that is run after the for loop.
                            break;
                        }
                    }
                    break;
                }
            }
            codes.InsertRange(targetIndex - 1, newCode);
        }
    }
}

ilikegoodfood

I ended up creating a separate help thread for the transpiler side of things, which is why this has gone quite for a while. Please feel free to check it out too.

Regardless, I felt that I should post a duplicate of this here too:

Quote from: ilikegoodfood on October 17, 2018, 09:07:09 AM
While this is a slight side-note, I have come to the realization that, even if I manage to get all these transpiler patches working, I may not want to.

The issue is that what I am working on is an implementation of pawn race and hediff verbs, so that people and animals may use ranged attacks from health effects. This will allow the implementation of body-mounted weaponry, such as an arm-mounted charge rifle, say.

While it is something that I personally would like to see the base game support, implementing it will cause a number of knock-on effects that will cause this small project to spiral out into a major framework.
A few examples of this are as follows:
The pawn UI will need to be adjusted so that you can tell a pawn with a ranged verb to hold fire.
The pawn UI will need to be adjusted so that, if the pawn has multiple ranged verbs, you can toggle between them.
Not sure if the old 'Brawler' trait is still the same, but if it is, then hediffs with ranged verbs should trigger their 'has ranged weapon equipped' bad thoughts.

If I do decide to get into this fully, and create what could be a fairly major framework, I would probably want to completely re-implement the RangeAnimalFramework into it, so that they are a singular functioning part, instead of having one framework dependent on another.
While that should make my work easier in some cases, I have no idea how code-sharing rules and re-implementations of such things work. I have no idea if BrokenValkirie would be okay with that, or, on the other hand, if they may like to help out.

I am also aware that some of the code already exists. CombatExtended uses a weapon-swapping system that I could, with their permission, either copy or re-implement (they recently gave permission for the re-use of some of their other code in a specific mod, so I don't see that being a problem).
Obviously, compatibility is of utmost importance to me. technically speaking, using transpilers, CombatExtended could be remade in a such a way that it had complete compatibility. What I definitely don't want is to exclude many mods from being used with it.
This makes fully grasping the use of transpilers of paramount importance for such a project.

If I do decide to go ahead with it, and learn everything I need to, and get all the permissions, then I would probably also ask erdelf for help directly.

So, for right now, I need to learn how to get this stuff working and knock out a prototype, After that, I need to seriously consider if I am willing and able to commit to such a project. I would kind of like to, but I wouldn't like to be the sole author...

SargBjornson

Damn, this idea turned out to be way harder than it looked!

ilikegoodfood

#13
Indeed. The base game was never designed to handle multiple ranged verbs, for both simplicity of code and gameplay. It was also, given that range combat is extremely rare in nature, never designed to implement ranged functions at the race level.

Currently, the only place it checks for ranged verbs is in the equipment. This would need to be re-written so that it looks in the pawn's race, hediffs and equipment.
Once it determines which verbs the pawn has access to, the processing would need to go in three separate directions: one for animals (inc. wild men) one for NPC equipment-capable pawns and a third for colonists. It should be possible to merge the animals and equipment-capable NPCs with a well placed IF function.
This route would have to catalog all of the available ranged verbs and select the most suitable using some kind of re-implementation of 'chooseMeleeVerb' such that they select the most suitable verb to use.
The colonist route would also need to catalog all available ranged verbs, but instead of using a 'chooseRangedVerb' function, they would need to be displayed as a stack of toggle-able weapons. The player can then use a 'swap weapon' button to switch the pawn between them.
Ideally, you would also add a UI block for once-use verbs, such that they occur only on player command, and they all have their own button. A pleasant side-effect of this is that it would allow you to add mode-switching weapons to the game.
An assault rifle that has a single-shot mode for higher accuracy and a once-use grenade launcher attachment would all become possible. It would greatly expand the tactical systems available within the game and to the modding community in general.

I would absolutely love to be able to have this to play with, and for others to play with, but I am a beginner modder of RimWorld, the only game I've ever seriously modded, with an entry-level understanding of C# and IL. I have the terrible feeling that it may well be beyond me, not because of skill, I can always learn more, but patience.
On the other hand, while digging into all this is both exhausting and exhilarating at the same time, I love doing something new and I love coding. I definitely won't be able to maintain my focus without a team though. I need people to bounce off-of and keep me going.

In fact, after having put in my first ever 80-hour work-week trying to get this working, and having made only limited progress, I am already starting to falter. I'm dreading having to take a few days to update my mod to 1.o when it comes out , just because I've over done it.

SargBjornson

Another wild and probably quite stupid idea... How about adding a "dumb" equipment item? I know animals can be given equipment trackers by code...