#Starlancer AIFix v3.13.2 | EnemyEscape v3.0.0
1 messages · Page 6 of 1
I'm guessing that EnemyEscape is separate to not break AIFix since there are currently problems with the AI switching and behaviour. Because AIFix is the base for the AI of inside enemies to work outside and vice versa.
unless I'm missing something, it's not required for AI to work outside in EnemyEscape
SetEnemyOutside sets the AI nodes so that they can function in the opposite environment
Without the AIFix, the inside enemies just stood in place and did nothing. That's why AIFix was made after all
AIFix initially was set up to just set the AI nodes based on where the enemy is when it was spawned, then it was changed to update that every interval to allow LethalEscape to work, from my understanding
but LethalEscape didn't call SetEnemyOutside, so the AI nodes were incorrect, and that was what AIFix changed
it doesn't apply to EnemyEscape
see this message #1206494982521753620 message
There might be a slight issue with some enemies in current Enemy escape, but AIFix fixes certain issues that arise from enemies being in the wrong place, like the outside coilhead slide, sandworms always resetting to the same node after attacking indoors (still need to figure out why they only work in the factory, but that's a later problem), and the Jester not functioning correctly outside
And aren't the nodes depandant on the type of AI used? I'm pretty sure there is separate category for the nodes inside and outside
Rather than put those fixes into a new mechanics mod, I'd rather just make the new mechanics depend on the fixes
EnemyEscape handles the teleport inverting the ai, but not the spawn-in-non-native-area AI
ah, that makes sense, thanks
Np :3
They get initially set based on the class of the AI, sure, but EnemyEscape doesn't change where they spawn, only where they move to. When they move between indoors and outdoors, it calls the function that sets whether the enemy is indoors or outdoors, which also sets the AI nodes that the AI uses. This means that the AI has the waypoints it needs to not get stuck.
You were mentioned up here, so I just thought I'd ask
? Which part? We make lethal casino
Yea sorry a few messages down from that
Oh yeah, I see. Hmmm yeah I dont think that has anything to do with us
mmk, it should be fixed next update regardless I hope
Would you really want it configurable per enemy, or a global inside and global outside range?
I mostly just don't want to set up more stuff for presets ; w ;
though I guess I could just set them to 50 inside 200 outside as defaults and let people do w/e from there without presetting things
plus it'd be funny to have the Chaos preset default to 1000, but that's something again that people can choose to do if they set it to Chaos I suppose
A global is better than nothing.
But that's what I thought you'd do
mmk, I'll whip up a quick config binding for it, I'm almost done with a massive improvement to the AI of EnemyEscape lmao
Much like the AIFix, it was relatively simple to make a Proof-of-Concept, but this is taking some COOKING to get good
Alright, it's unfortunately gotten really late, so I'm not gonna release the update tonight. Tomorrow I'll add the range configs and probably some extra logic for specific enemies, most notably the Hoarding Bugs and Baboon Hawks, since they need to dynamically create a second nest when they either go in or come out of the facility.
I've done so much today ; w ;
and no, the picture is not meant to be legible
you been grinding
majorly lol, this is practically an overhaul from initial release already
lll be like
at some point you'd still totally crush lethaltoolbox related stuff i bet
editor tooling is also really fun to play with
Planning on looking into it more once v50 LLL drops proper
I was playin with the mod today N forgot that dogs can go into the building, it scared the shit out of me
Wish I had a clip of it
Holy fucking shit
I just discovered something radical
Fuck
its
not 100%
hold this
oK
The relationship between SnatchinBracken and EnemyEscape is strong
But
there are
issues
using EnemyEscape the bracken can sucessfully path from outside to inside the facility to the bracken room
however
it uh
get stuck whenver trying to go through the fire exit
specifically
the one on experimentation
where it tries to go through it
but cannot touch it
because its holding you inbetween it and the fire exit
OH WAIT A SECOND
ITS BECAUSE HE STILL HAS THE CHACNE TO USE THE TELEPORT
Ey @gleaming robin found a bug with the starlanceAi 3.5.1 (probably 3.5.0 too) were slimes got compact into a ball and did no damage. Downgraded to 3.4.1whatever and seems to work fine. I'm playing on v49 so that may be it
didn't take a screenshot unfortunately
I'm really surprised that it can do this pathing. The bracken room is not available from the outside navmesh.
I would expect it to fail to find a path and stand in place.
Maybe that does happen, but then he changes state and independently makes a decision to return to facility
Im surprised too
It can path to the inside facility room
However
its teleportation of that pathing sucks
But if that was the case, then he would not be stopped at the door. The random chance happens before deciding to path towards the door
From the second he grabs me he paths towards the fire exit closest to the bracken room
Watch kidnapped and millions of other Lethal Company videos on Medal, the largest Game Clip Platform.
wacky am i rite @wintry wind @gleaming robin
going to try one more time with aipathfinding lag fix
hey do you by any chance know if it kills bracken in v50
esp in diversity
damn
I am not educated in the full changes of AIPathfindingLagfix
Also 900% sure diversity is borked in v50
yeah you're probably right
That's a lotta percent
there's a lot to break
HOLY FUCKING SHIT
Watch LETS GO????? and millions of other Lethal Company videos on Medal, the largest Game Clip Platform.
SUCCESSFULL TEST????
Current release doesn't even have the "chance to path" thing
ikr. How does this happen?
maybe zeekers cooked something into the SetEnemyOutside methods, such that they realize that they need to path inside first?
We need to get to the bottom of this. Maybe there's some system that we have overlooked and broken in the process
Do note that Mi6k is spawning the bracken outside. It is not a bracken that has left the facility using the mod
Maybe the game has hidden support for enemies pathing between the outside and inside? Have you tried to tell an enemy to path outside, without the mod?
Or maybe this is code from SnatchingBracken, that has been created to support brackens that have been spawned outside?
I don't see anything in it
Hell no
I spawned a bracken outside
And uh
So that would also confirm that it's not the vanilla game doing this...
And even with Snatchin + AiFix
Bracken didnt path inside
Or towards the facility
rather a corner of the map somewhere
That is what i would expect to happen
Its only with Snatchin + AIFix + EnemyEscape + Pathfinding fix (maybe) everything worked in that test
it's trying to get closer to the real location within the scene
well, that's most of my theories on why it happens out the window
Haven't you said something about changing node tags? To make pathing better?
To which batby replied that it would be a "desctructive" change to vanilla
maybe it's able to use that node?
So at Batby's suggestion I instead made new objects with the OutsideAINode/AINode tag to allow for random pathing to an entrance, but they don't affect actual navmesh
Unless I'm incredibly mistaken, there's nothing actually linking inside to outside navmesh-wise here
hmm, and that change has made it into the version that Mi6k is using?
yea that was initial release
i guess maybe it would be worth checking the behavior without those nodes?
just to sanity check ourselves, and maybe this will lead to a better solution for everything
also rerun the test with them still. To confirm that this is not something that happens only for Mi6k
@spiral ermine idk if you've used the UnityExplorer mod, but could you do a bracken test with disabling the nodes in the scene?
Yep
i dont think im qualified for this one chief, you mau have to tell me how to do that
Go to experimentation. In game, F7 will turn the overlay on and off, on the left will be an object window, use the dropdown for switching to Experimentation scene, then use the search bar for EnemyEscape and disable the 4 objects that come up
then test the bracken again
ok
also what mod are you using to freecam and spawn and stuff?
and visualize pathing
noice ty
this would have been handy when debugging the path to door code
yea ; w ;
@turbid wave I can't believe it, but yea it actually works
just checked current release
does imperium have tools to manually order pathing?
well I'm not using imperium
well if it does have those tools, then we could test without SnatchingBracken. And test on other enemies too
I'm not sure I follow
if imperium let's you order an enemy to path somewhere.
We could order a nutcracker to path somewhere on the outside, then back inside
and also eliminate the possibility that there is something special about SnatchingBracken that is influecing things
I see
alright well it gets weirder
without the nodes, the bracken doesn't take me to the experimentation fire exit lmao
Okay so I had a suspicion, and I think @golden basin will love to know that this is just a case of Experimentation being wacky. There's navmesh into the building, so what's happening is that the Bracken is trying to go as far from the main entrance as it possibly can, as per its code, so it goes to the fire exit thinking it'll go through it and follow the navmesh (which it cannot), hence why without a successful teleport roll it just mashes its face against the door
So this is just an incredible coincidence @turbid wave
@spiral ermine
This makes sense, I've seen the bracken act weird in experimentation around entrances
It becomes kinda blind
Silly Zeekers
#ZeekersMoment™️
however
that doesn't explain why it didn't happen without the nodes I placed by the door
bc it's not like it adds navmesh
idk ; w ;
AIFix doesn't touch blobs directly, so for now I'll chalk it up to v49 weirdness
sounds about right!
maybe unrelated to the nodes, during that test, you have been caught by the bracken in a different spot on the map, and there was a better option for running away from the door
I ran it a few times in different spots
although it's interesting how Mi6k said that this didn't happen at all when not using the SLEsc
Anyways, since it's an inconsistent event entirely brought on by Zeekers' toying around, I'm not gonna worry about it
i think there is a barrier that zeekers put at the door, to prevent things from randomly pathing into that navmesh. And your node somehow allows it to step over the barrier
it doesn't kill the Bracken ever
shame this this is not a stable feature. If this mod worked like that for every map reliably, it would be perfect
uh, I may have been mistaken regarding the Experimentation navmesh
I was looking at a bad angle, so I thought it was going through the fire exit door, but it's not
which means I am once again confused
Oh I meant that figuratively, and mi6k was probably right about diversity being the issue with what i encountered
you can totally add a big navmeshblocker in that room via super arbitrary code before it does the runtime bake btw
Also found some code that will allow me to calculate the actual distance of the navmesh path between the enemy's position and the entrances, so the value of "range" will be more accurate
var corners = pathToTeleport.corners;
var pathDistance = 0f;
for (int i = 1; i < corners.Length; i++)
{
pathDistance += Vector3.Distance(corners[i - 1], corners[i]);
}```
oh then you should describe it in more detail
there's a good chance that it's diversity though, I'd say, I haven't noticed any change with the lag fix in v50 yet
its just that the breaker-angered bracken sometimes just stands in one place
i dont think i can describe it in more detail tbh, it might even be some vanilla pathfinding shenanigans. happened on a mansion tileset once is all i can remember
how long does it stand still?
dont count on it being 100% reliable cause it happened like some time ago, but at least for the anger duration he was standing there
but honestly i dont think that until diversity gets a v50 patch its something worth looking into
oh, if he was in the anger state then it doesn't affect it at all afaik
the patch affects the running away state
any side effect would be due to making use of a search coroutine field that bracken wouldn't need for chasing a player because he's all-knowing
Can’t wait for @gleaming robin to fall too deep in the ai rabbit hole and think about ai teammates
Don't tempt me
Someone would need to pay me for that for the amount of time I'd probably put into it
... if I did do it, I could make a store thing for AI teammates that would just place the teammate on board like how the game does the decorations
Okay, Imperium is astounding
its literally goated
hours of bugtesting reduced to mer minutes with how much it does
@patent epoch tysm for Imperium :3
being able to SEE the current navpath is an absolute gamechanger for testing this mod ; w ;
¯_(ツ)_/¯
youre welcome! funny enough I actually tested Imperium with your mod yesterday to ensure it works and to see the changed pathfinding 
Awesome!
well if Knight doesnt know maybe @faint copper ?
for the dog thing?
I'm not sure
I never looked at the dog hitreg issue actually
it has always worked to shoot them, it's just not reliable, which makes me think it's just how the colliders are set up
@patent epoch if I may offer one suggestion, when I was invisible enemies could still hear me, so maybe disable any noise from the player while invisible?
yeah the feature is not 100% working yet, there is "Muted" for entities that detect you based on hearing but it doesn't work on all the entities yet
i noticed today how the blob's path is perma-blinking, I assume it's just recalculating the path over and over, I've seen it with the snare flea too but when the flea does it, it actually causes severe lag
yep, but once I set it on its path to the teleport it gets solid :3
hmm, interesting
I haven't actually used your mod myself yet as I don't play with mods that often
@gleaming robin
Questions as I am out of the loop.
Does this work on v49?
Will this work for modded enemies?
Will this work for all moons and modded interiors?
Maybe
Yes
Yes*
*if the moon itself was made by an intelligent creature
^
lol thanks team!
:P
Also. Thank you for allowing me to make Shattered Company even more painful. If people are not pissing or shitting their pants. Have I really done a good enough job?
keepenemypower moment
I do my best
How does it work? Does it just attach an AI path node to every door slot?
Current release, kinda
Not the best implementation
New release (coming soon™️) waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay better
Tomorrow at 10pm
Ohhhh interesting. I like the unexpected Bracken drag haha
(powered by Mi6k™️ brain ideas)
XD
Powered by something idk
I think maybe what it was, was that the node I placed at the fire exit just HAPPENS to be the farthest from the entrance path-wise on Experimentation, so it becomes the bracken's favorite spot
Okay, I keep doing that thing where I change a little bit of code, break something, and then can't figure out what exactly broke, so I've learned my lesson and I've made a backup of the working plugin on my desktop
have you tried github?
Current state of things:
When an enemy spawns, all cooldowns are set.
When the pathing cooldown reaches 0 (or less), it rolls the dice on leaving the facility.
If successful, it calculates the path to each teleport, and for any that return valid, it checks the length of the path, and if the path is close enough, it navigates directly to the teleport. (My check for the behaviour state should allow it to break out of this and go after players)
figured
I just haven't learned how to use it properly
you don't need to even use GitHub, just use a local git repository blease 
I was gonna say that before you had an issue like this but uhh too late now I guess
if you make granular changes and commit them to a git repo it makes it WAY easier to tell where you went wrong
the granular commits thing being the most important part that a lot of people skip, it's more work but it's absolutely worth it when you have some weird issue and you don't have to sift through 500 lines of code changes to find where you broke things
for example, I had someone point out a performance regression in OpenBodyCams, and I was able to bisect down to the exact commit where I broke it and that left me with iirc less than 20 lines to look at
vs probably hundreds of lines that I changed in the course of that release that broke things
if you ever want help using git, I can give you some tips
first tip: use VSCode to stage your changes, its diffing is very good
Ah, I was wondering about the differences between VSCode and VisualStudio
You did actually, but I fixed the issue
really depends on the language, for C# generally I would expect VS to be better, but I haven't tried VSCode for C#
.
I run VS for developing my mods, then open VSCode to stage and commit changes
oh yeah, that doesn't count, I could've given you reasons lol
just make it like lethal escape
it will just will follow you out the door
baby steps
Having this spam in v49
[21:24:17.5263820] [Error : Unity Log] NullReferenceException: Object reference not set to an instance of an object
Stack trace:
StarlancerAIFix.Patches.AIFix.AIUpdatePatch (EnemyAI __instance) (at <ddb6fba65f4f4f1fadedc0d7e02023e4>:IL_006F)
(wrapper dynamic-method) EnemyAI.DMDEnemyAI::DoAIInterval(EnemyAI)
MaskedPlayerEnemy.DoAIInterval () (at <af9b1eec498a45aebd42601d6ab85015>:IL_0000)
(wrapper dynamic-method) EnemyAI.DMDEnemyAI::Update(EnemyAI)
(wrapper dynamic-method) MaskedPlayerEnemy.DMDMaskedPlayerEnemy::Update(MaskedPlayerEnemy)
[21:27:35.6559542] [Error : Unity Log] MissingMethodException: Method not found: void .EnemyAI.SetEnemyOutside(bool)
Stack trace:
looks like you're trying to run it on v49 there
presumably you should downgrade to a previous version that was intended for v49
found the trouble maker for that error it's LCOFFICE mod.
i forgot that it has custom enemy sorry
darn am I using an incorrect version...
@gleaming robin
Which of the newest versions should be v49 compatible?
I am currently using 3.5.0
https://thunderstore.io/c/lethal-company/p/AudioKnight/StarlancerAIFix/changelog/
Also is this https://thunderstore.io/c/lethal-company/p/AudioKnight/StarlancerEnemyEscape/ erroring out in v49?
that definitely would, it won't work on v49 without a backport
In theory the new AIFix should still be v49 compatible, but I'd just look at the version release dates.
I can say nothing for EnemyEscape v49 compatibility :P
Huh, I didn't think the Shrimp would break it
Shrimp breaks everything afaik
Hopefully it's just special characters in its name, the next update should account for that
go make it like lethal escape (suggestion)
the only monster that actually escapes (the most chance) is thumper (in enemy escape)
and its not like lethal escape that it leaves automatically without chances
i'm saying a feedback
the way it was phrased was like a demand
why does it want to reference EnemyAI.SetEnemyOutside() then? 
yea, I did it for you lol
@faint copper any idea why I can't reference a public static variable from LC's assembly?
BaboonBirdAI.baboonCampPosition
ew why is that static
idk but I can't access it
...nvm suddenly I can access it
ope
wait
wth is going on
it disappeared xD
Okay I thought I understood how this particular aspect of coding worked but apparently not
hmm? wdym?
you set it in the line above the one you're typing
you can't access static fields by going through an instance in C#
ohhhhhh okay
Java lets you do that but C# doesn't lol
ugh
well I wanted to make it so that a new camp position got created on warp (which I would cache inside/outside)
wait
nvm, I had the thought about moving the static but then that'd mess things up if some were in and some were out
I'm not sure of the solution here 🤔
well
their nest is a physical object as I understand it, so spawning one in the interior would get a little screwy, but if you really wanted to you probably could
then you'd have to do a bit of transpiling to make them use your inside nest location instead of the outdoor one
or you could instead make a transpiler to make them path to the exit when they're inside and trying to go to the nest
silly giant spikes with the head on them
that way they can actually make it out
Transpiling's pretty tricky, yea?
it takes some learning
for this it shouldn't be super involved but you still need to know the basics of how stack machines work
and read documentation of the IL instructions in the functions you're interested in
Well before I worry about that, this is all that the nest object is for the baboon hawks
so it only serves as a home base if that makes any difference
man, this is irrelevant, nvm
a nest only even gets spawned if there are 3 hawks
that object has visible children though doesn't it?
I think it would feel kinda odd if they had an invisible nest in the interior and just ignored their outdoor one
I'm not pursuing this any longer bc my understanding of its purpose has changed, but my original idea was: If a baboon hawk warps inside, it picks a node inside to act as its camp, if it warps outside, it uses the original camp position
the issue I'm running into is that if a baboon hawk picks up scrap inside, it glitches out and gets stuck in place pretty much
but only if it was originally outside I think, unless I just fixed it
looking at their code, it seems like as soon as they pick up scrap they try to directly path to the nest, so that makes sense
if you inject a call to your own function to replace that destination with the exit if they're inside, then they should not get stuck
it's the easier solution than replacing the nest location when inside, and it would make more sense to players for them to take the scrap to the visible nest prefab than some arbitrary point inside
Where at? I was looking but I couldn't seem to find the right area
admittedly I don't know which behavior belongs to the one where they are trying to drop off scrap, but I believe it's under case 1 of the switch in DoAIInterval()
lmao I really did just miss that
if I were to prefix that and pass baboonCampPosition as a reference, would that fail from being static?
(enemy.isOutside == false)
{
baboonCampPosition = RoundManager.Instance.GetNavMeshPosition(enemy.allAINodes[random.Next(0, enemy.allAINodes.Length - 1)].transform.position);
}```
prefix which?
if you prefix DoAIInterval your destination would instantly get overridden
ahhh right
you could technically postfix it but that's kinda ugly
wait
no
ok then I don't know where you were going with that
oh wait, were you saying you'd overwrite the nest position at the start DoAIInterval and then reset it after?
then you'd probably have the baboons drop the items at the door
My thinking was that while it's inside, the DAII would refresh the position each call, and if outside replace it with a cached original, but looking at it now that would fall apart if the hawk starts inside
so I need something to just reassign the nest position after warping, that adapts to whether the hawk spawned inside or outside
reassigning it isn't a good idea
you gotta look at all the references to that field
it checks the distance to the nest to determine whether to drop items among other things
ah
so the solution is to change the destination while an item is held instead
which would be, take the item to the exit, leave and bring it to camp
prefixing DAII won't work, postfixing is ugly, idk how to transpile, so I might have to postfix
but I've also thought of a niche case that my mods enable
you need to inject into DoAIInterval to modify what it sends to SetDestination
if the baboon is holding the item and the destination is the door, it'll drop it at the door
it only drops the item if its within x units of the camp
modifying it in postfix might work, but that means you have to integrate every existing condition in the postfix to make sure you don't overwrite the destination at the wrong time and make it do weird things
which is why I'd recommend trying to learn to make a transpiler for it
it's really not that bad a way to start with transpilers, this isn't a difficult one like other ones I've looked at
just wanted to ref the code snippet rq
should I just peep the harmony docs, or do you have a better resource/wanna teach me yourself?
I think the best approach would be to take a look at others' transpilers
I don't think mine are the best examples because mine get a bit hairy usually, but if you look maybe one of Sylvi's like https://discord.com/channels/1168655651455639582/1210303734618390579 that would be good
she uses MonoMod's ILManipulator which is easier to use, but there's also CodeMatcher in HarmonyX which doesn't require the patcher
basically though, you just need to look at ILSpy in with the IL view (probably want the combined IL and C# view to make it easier to understand)
that way you can figure out what instructions line up with the function call you want to hook into
if you hover over each instruction it has a little explanation of what it does
you can start by making your transpiler/ILManipulator do nothing and just check if you can match the instruction you want
(I'm playing a game rn so I'll try to get some resources later if you'd like to pursue it, I mostly just got started with pre-existing knowledge and experimentation)
oh I've been using IL with c# for debugging lately lol
would the check just be like a debug log?
depending on how you're matching the code, but yeah, generally I would just print it out to verify it
from what I saw, CodeMatcher lets you print an error if it fails to find a position
welp, when is the enemyescape mod updating
https://lethal.wiki/dev/fundamentals/patching-code/monomod-examples#ilhook-examples
@gleaming robin this may help
ILCursor should work with the latest BepInEx through the HarmonyILManipulator annotation like in the code here, but as you can also see in that linked issue, it will have issues if there's a transpiler and a manipulator on the same function when using the version of BepInEx on Thunderstore
As an alternative, there's this that explains a bit about how you can use CodeMatcher in a Harmony transpiler: https://github.com/BepInEx/HarmonyX/wiki/Transpiler-helpers#codematcher
I write mine pretty manually with some utility functions so I wouldn't recommend my own code as a reference for this
Cool, ty. I'll take a look in a bit. Good news is that I realized I had my CalculatePath() backwards, so fixing fixed my Thumper issue. I also managed to get the HoarderBug to reliably switch its list of unsearched nodes from inside to outside, which should help out its AI for its search routine.
@faint copper I figured out the issue with the blob I think. It can't climb or jump, so on experimentation when it left (which was where the screenshots were taken) all of the AI nodes are past the ladders, which only have jump and climb links 🤭
So I think that's why its blobby body didn't want to cooperate
oh, you mean it wouldn't path anywhere? or was it an issue with its edges?
it wouldn't path, which I think prevented it from gathering itself properly
I tested it on March and it came right out of the door
I see you're trying to fix this for baboon Hawks specifically. But I've just realized this could be fixed in general for every enemy
The underlying issue is that the baboon hawk being inside tried to pathfind to the nest, which is on the outside navmesh
The same issue would be valid for snatching bracken. When trying to pathfind to the favorite room with a body snatched outside
We can describe those issues as an enemy trying to pathfind to a location on the other navmesh.
Inside to outside. And outside to inside
Maybe it could be caught after the base class enemyAI tried to navigate somewhere and failed.
Then we just compare the height of the AI agent and that of the destination.
If this comparison gives us a high difference in height we can determine that the enemy has failed to find a path to the other domain
Then from this we can call a function of yours to let your code handle returning to the original domain
Something like AIwantsOutside
AIwantsInside
Those functions would skip the cooldown and random chance code. And force cause the AI to escape / return
Idk how this is handled in code. But maybe it could also capture the failed destination and attempt to restore that destination pathfinding after the enemy has made it to the correct domain
If this works it would solve the problem for every enemy. Future vanilla enemies, and custom modded enemies alike.
Also it would make it possible to create a modded enemy that pathfinds between outside and inside destinations, as a part of its mechanic.
Such a mod would only need to depend on this mod, and set the destination from the other domain. Then let this mod handle transferring to the other domain
Oh yea
@gleaming robin
Reguarding your music mod
first couple times you turn on the boombox there is slight stuttering
not a bad perspective actually
this could be a hook in SetDestination, if that's the only point at which enemies set a destination outside their current domain
Also wow how deep have yall been studying the transitioning of pathfinding
I would tend to say the best way to check that would be what Audio Knight is already doing, checking distance to each node, but it would be good to profile the cost
if it's too bad there are shortcuts
This has been my life for two days
🤭
Also I'll give a proper look in a bit at what all you've sent @turbid wave . I also need to figure out how to deal with the bracken not wanting to leave its spot and a weird issue with the spider that I don't know how to describe
Take your time.
Also I think I'm gonna put manticoils and circuit bees on the "ignore" list for the mod, they don't roam normally like other enemies
bees? if they can't get to the hive they're perma-aggroed 🤭
and sometimes they wander after players 🤭🤭
and you can hide inside to get rid of them 🤭🤭🤭
True, I can leave the bees in for that scenario lol
are the chances in the config change to teleport outside or chance to wander to the door?
@faint copper @tulip vault
Ooo
Wait hold on what's inside that folder
This looks suspiciously close to the folder that contains dll and assets
I've entered the bronze age
you can't handle what's inside
Open it :3
Oh wow so actually did do it right
yep, and bin/obj/editorconfig are in the gitignore
my coding is separate from unity
NO way
but yea
Is it a public repository?
the entirety of the referenced dlls and stuff are ignored
not yet, just local that'll be pushed to my Starlancer repo
and I'll pull out the plugin dll once I plan to push
Gotcha
I decided to stop tormenting them
"oh no, the last version of this file worked! how can i restore it?"
like literally it's that easy
🥺
.
dum-dum
ye
no lazy 😦
i remember the time i've used github only to get the sweet education edition perks
and there my entire project went missing
and i'm using git lol
I'm using github desktop cuz it was easy to setup
same
i was about to recommend it to you 😅
cause it it easier than cli
yee, easy integration to the repo was the main thing
it's good for the simple stuff
but when you need to do anything advanced you're out of luck
but adding commits, changing branches and reverting stuff is trivial
okay wow
joke's on y'all
I made a mistake and github desktop deleted my plugin file, BUT GUESS WHO SAVED HIS DLL AND PDB LAST NIGHT
yayy
DON'T TOUCH THAT! >.<
wait so you're gonna use a decompiled version of your plugin? lol
I hope GitHub desktop warns you when you're about to discard a bunch of changes
I pushed a branch as a test and then deleted that branch, not realizing that it would remove it from my local repo as well
welcome to git
sooo kinda freaking out a bit
you either delete it locally or delete it on the remote, not both
unless Git desktop is stupid or something
it's not there is all I know
is your remote GitHub?
you can use git reflog to get back your branch
as long as the changes were committed anyway
oh wait I didn't delete my old local non-git, I swear I couldn't find it before
ughhhhhhhhhhhhhhh thank god
you mean your old backups?
literally just what I had last night before I shut down
so everything is fine (?)
if you ever have this kind of thing happen again, as long as you didn't discard uncommitted changes, you can recover
run git reflog in your terminal and you'll see every change you've made
when you find the commit you lost in there, you can just do git checkout [hash] and you'll have a detached head of the changes at the point where you committed
then you can git checkout -b [branch name] to create a branch from it
with this power you can mess around with git without fear
oh sweet
git makes it really difficult to accidentally lose progress. If you know how to use it you can almost always recover
It's almost an issue, when you end up pushing some confidential password into the internet. And you need it to disappear. But git won't let you remove it form history
To prevent this from happening in the future, should what I do be work on stuff saved in a separate folder and just put stuff in my git when I want to do some version control?
oh also if you want to restore your changes onto an existing branch, you'd do
git reflog, find commit hash that you lostgit checkout [branch to restore to]git reset --hard [hash]
this is obviously a little "dangerous" because you're overwriting that branch completely, but if you know what you're doing with it it's totally fine and useful
NO lmao
git is your safety net not some random folder
just commit often
and be careful with the discard changes button like mrov mentioned
mmk
using git to look over your changes before you commit them is also a very good habit to have so that you can make sure you didn't miss something
reviewing your own code isn't nearly as effective as someone else doing it, but you will still catch plenty of bugs that way
the discard changes is like reloading a checkpoint in a game. It returns you to the state of your last commit.
So committing often, is similar to saving often
except here you give each commit a short description of what you changed.
(preferably making each commit one granular change that compiles and ideally runs without breaking anything)
so that you can then look at the history of savepoints, and see what changes in each savepoint
then cherry-picking/reverting stuff also has a less chance of breaking stuff
indeed
because less files affected each commit == easier manipulating later
bisecting for bugs is much easier that way too
if you have half your commits not compiling or running it becomes very difficult
oh also @gleaming robin prefer stashing over discarding if you want to be safe
thanks so much for this, I really was freaking out at first 😓
got it!
once I seriously clean this mess up, I'll happily share the source repo

tempting to follow up on what Batby told me and make public fields into properties, but I haven't really done get/set stuff (nor do I know exactly when to use them)
Imo, if you don't know why you should be using something, then don't use it.
Just leads to misusing the thing, and to a worse overall situation
I would only worry about that for things that you expect other mods to need to use that you may need to react to changes to
and generally you could prefer internal over public if you don't want something to be treated as public API
If you're pushed into using something, then research why it would be a good idea first before committing to it
most of my fields are internal or private lol
hmm, which fields were you thinking needed to be properties then?
well that's just it, I'm not really expecting other mods to do things to EnemyEscape so idk
oh, then don't do it
Despite my ambitions, my serious coding only starting in January lol
before then I had only taken 1 java class 10+ years ago and a python class like 8 years ago
If you implemented the AIwantsOutside AIwantsInside functions I've talked about, those could be used by another mod.
Btw have you gotten around to reading that stuff?
ah not yet, I was too busy freaking out about my git stupidity 🤭 I'll check it out now
The main difficulty is calculating the fact that the desired position is on the other half of the level. Height is sort of okay, but without re-implementing the calculations I did away with in previous versions of AIFix, I'd have to use a "magic number" to set a division height.
I think for the short term I might patch the BaboonHawk to just not pick up items while inside
I'm not sure what do about the bracken tho, bc its DoAIInterval tells it to path to its favorite spot while passive, so my component can't effectively override that
@faint copper I know that I should generally avoid prefixing, but when would you say prefixing is okay?
bc in theory I could prefix the hawks InteractWithScrap() method to just say if (!isOutside) { return; }
(as a temp measure until I figure out how to get it back to its original location)
Yeah, that's the magic differentiator. The assumption that interiors are always below the exterior.
I've seen that the new sfDesat moon "the spacestation" breaks this assumption and spawns the interior on the same level, for you to see as the exterior view of the station
ughhhh this wouldn't work either actually, because the inverse situation (hawk spawn inside, find scrap outside) would still break it
But Batby said that this also breaks assumptions that LLL is making, so i guess it would be save to pick such a "magic number".
Maybe you could even soft dep on LLL to get that "magic number" dynamically
Possibly? idk exactly how all that will end up lol
and yea the Spacestation was what lead to me making AIFix more accurate, so that above-exterior dungeons would still function with it
Alright I'm taking matters into my own hands
[HarmonyPatch(typeof(BaboonBirdAI), "Start")]
[HarmonyPrefix]
[HarmonyPriority(1000000)]
private static void takeCareOfThoseLittleBastards(EnemyAI __instance)
{
__instance.KillEnemy();
}```
😳
Just thought of another way to distinguish which domain the location is in.
I'm assuming that the interior navmesh and exterior navmesh can be easily differentiated by their reference.
You could calculate their bounds after scene loads. The highest and lowest value for each axis, that is still within that navmesh.
Then you only check if the location is within bounds of one navmesh or the other. No magic numbers included.
The exterior navmesh gets rebaked at runtime, so I think all the navmesh in the scene falls under one reference unfortunately
Or you could use the bounds of the interior directly, i recall those were mentioned in the Mimics thread
Hm... I wonder if postfixing DoAIInterval() with if (current path is unreachable) { drop held item and sync } would work
would that do anything? I don't see any calls to SetDestination there
unless LLL patches all the vanilla magic numbers, there's no reason to
the magic number is -80f
Nothing to do with the destination, just prevents them from picking up scrap. The idea would be fruitless though
#1181019013409677312 message
that does sound like a way to discover whatever other ways that the AI wants to break lol
I think dungen calculates a bounding box for the interior, but I'm not sure
I personally haven't had a reason to use it so I wouldn't necessarily trust it
I think the options are:
- magic number
- find closest node among both sets
I know it doesn't do that currently, but there has been talk of LLL supporting multiple dungeons per scene and dynamically placing their roots at modified elevations.
Also iirc LC_Office interior comes dangerously close to that -80f, there have been issues because of how tall it is
to both things, unless LLL starts patching the vanilla checks, there's no reason to behave differently
LC_Office extending too far up is an issue with the interior that Batby seeks to fix eventually by shifting the dungeon root after generation
at least as far as I know
future proofing, sure
that will also make Space Station conform, to its detriment, but there will have to be a lot more work to make something like that function the way it should
perhaps work that only Zeekers can do
which means I don't really see it happening without some trickery to make those assumptions still true
that's what i mean here #1206494982521753620 message, we can depend on -80f, because batby is going to enforce it anyway.
As interiors that violate this number also break other assumptions in LLL and in the game itself.
But star wanted to support those interiors with the aifix, so a decision needs to be made here
star wanted to support those interiors with the aifix
it was more that Spacestation gave me a push to do what I needed to do anyway
the interior bounding box could be a way to determine the domain, without dropping support for interiors above -80f
But that would depend on authors of custom interiors correctly setting those up. I could see that happening, if those bounding boxes started causing errors.
or if all else fails, maybe differentiating the navmeshes would be a feature for LLL to handle after loading the scene.
the tiles themselves have bounds that are calculated based on their transforms, you can just take the union of those
but if we're trying to account for moon/interior authors doing screwy things, then I think the only solution is to scan all the AI nodes
unless the NavMeshAgents have some way of indicating what object they're on
If i understood star correctly, it seems that the outside and inside are in the same navmesh object. So scanning all nodes would not give us what we want.
AI nodes != navmesh
oh you mean start the scan on the root, and walk all the connected nodes?
no, I mean the waypoints that Zeekers and mod authors create
pretty much what I did for AIFix initially was for each ainode just log the max and min of inside vs outside and find a midpoint between the two to act as the divider
also @faint copper I just tested in Lan and I think you'll be happy to hear that the special nodes I instantiate do indeed register for both host and client
nice that's good
pretty much what I did for AIFix initially was for each ainode just log the max and min of inside vs outside and find a midpoint between the two to act as the divider
I thought you had some code at some point that found the closest AI node to an enemy to determine which they were on?
that's what I'm suggesting here, although it's obviously not the most efficient method
I would honestly just suggest that you make the same assumption vanilla does
you know, the easiest way to do all of this would be to place offmeshlink's for the entrance teleports.
Enemies don't need to actually walk through the links. But those would allow the agent to realize the best path, including the domain transfer.
Then we just need to detect the attempt at using the link, and handle the TP.
going through your configs and found enemies i havent heard of who is maggie? and whats the crystal ray?
I was confused about them as well, found them in the config for FairAI.
Those are unfinished enemies from LethalThings
damn was hoping i found new enemies
should i still use fair ai if im using starlancers ai fix?
And to enforce the chances for escaping from the facility from your config. The cost of pathing through the Link could handle that
it's not a bad idea, but it would take some testing to determine what cost to give it so that enemies won't path to it unless they are told to
also, I'm not sure how possible it would be to place those links for modded moons / interiors
Those mods do different things. They don't replace each other.
ai fix makes AI's functional when spawned in the wrong place.
FairAI handles interaction between hazards and enemies
That's current AIFix
was something wrong with that method?
with the max/min?
also @faint copper with transpiling, I can alter specific lines of code yes? So if I transpiled the Bracken's AI interval, I could have it check for my own condition and tell it do or do not do what it was originally going to do?
indeed
alright time to become transpile-pilled
there's really no other way to fix these inconsistencies lol
no, with saying that a point is in or out of the interior based on which node is closest to it
indeed
which thing are you looking at with Brackens?
Sorry if I'm misunderstanding, but it's what AIFix is doing currently, and it stores them in a static that gets overwritten each round
the DoAIInterval tells it to stay put at its favorite position which is overriding me telling it to path to an exit
looks like it's using SetDestinationToPosition there to me
why not inject into there like Piotr suggested?
so if (pathingToTeleport) change that line to the teleport location?
that looks like just the caching code, I'm talking about determining where a vector is, in interior vs exterior
I could (in the same function) node.transform.position and store it in a separate array
if (isOutside != PointIsOutside(destination)) destination = GetTeleportPosition(isOutside);
unrelated to current conversation,, I found this https://docs.unity3d.com/ScriptReference/AI.NavMeshAgent.CompleteOffMeshLink.html
Could be used to turn the offmeshlink's into teleports. All that would be left is to figure out the cooldown/chance code
Maybe that's ok? The bracken AI just doesn't want it to go outside, unless provoked. Because of how a bracken behaves...
It'd require a big shift in the code, but that's an intriguing idea
it would certainly be the ideal solution if it's possible to set up at runtime
this chance of escaping seems to be messing with the intended behavior of enemies.
And using OffMeshLinks would allow the enemy behavior to have proper control of the situation. But then we lose the ability to config the chances
In my mind it's a trade-off. And like Piotrenewicz says, I'm not sure I could prevent them from deciding to path outside of their own accord without some really ugly (probably breaking) code
However, that sheer freedom might be what people would prefer
Hmm, if we could determine between the enemy randomly wandering and actually pathing to a specific destination.
during random wander we could roll the chance and on the result disable the link, so that the enemy doesn't have that option during random wander.
But when pathing with intent, we leave the link enabled, and it finds the route correctly
Just set this bool to false, when enemy state is idle, and it didn't make the last dice roll it did.
Set it back to true as soon as it is done pathfinding. To let other enemies use it separately.
This would require a transpile on the EnemyAI class, before and after calling SetDestination()
and an assumption that state idle is a good way to determine random walk
we'd need a way for each agent to have its own link then
agent pathfinding is done asynchronously
I'm trying to cook up a dumb idea
Can someone tell me what layer NavigationSurface is on?
I don't wanna open unity lol
UnityExplorer can tell you too
Ah
I would assume
Do you mean that you assume the async pathfinding or the UnityExplorer?
wait how does the random wandering code work for LC enemies?
can it even consider a location for SetDestination that is through an OffMeshLink?
or is that something that would have to happen when another situation is causing it to consider that location
UnityExplorer
the Unity documentation specifically says that it runs pathfinding async from what I remember
with that we would be limited to 32 enemies
Wait can you elaborate why that is? I haven't found this limit in that thread..
this isn't about using a different area bit for every enemy.
Each enemy has it's own area mask, that we could set one bit in to tell the agent if that enemy is allowed to consider the link for this SetDestination call
it would be a prefix on SetDestination, that checks if enemy state is idle, and checks the result of last roll of the dice to disable the mask bit.
else enable the mask bit.
and re-roll the dice every cooldown
hmm, but if the random wandering code is capable of choosing locations through that link, it could still pick that location. And disabling it last minute would make it temporarily unable to find path
So with this mod you can configure what goes in and what goes out?
yeah, there are chances in the config, and if you set it to 0 it will never attempt
Nice
oh wait, good point
You can do this for specific monsters?
I was still thinking about duplicate links
Like if you want just dogs to go in and out
yes
Oh nice
I know nothing about how enemies pick locations to wander to. Is there a method that they call to figure out where they could move?
Prefixing a method like that would be even better, since it won't affect pathing attempts to a deliberate location.
And it would prevent them from considering a location before they even try to go there
And this is host only?
Honestly not sure! 🤭
Maybe we can just set the cost for that OffMeshLink to +Inf,
such that it would be over budget for the random walk.
That would still allow deliberate pathing through it.
And on a successful roll to escape we can use SetDestination to make the escape a deliberate move.
I don't really want to rewrite everything, but I did a little attempt of this, and the main issue I ran into is that navmesh bakes at runtime (and I'm not sure exactly when) and I couldn't figure out what method to hook into to instantiate the links.
ooh I might've found it actually, gonna test again
yeah i get that. Shame that we're only figuring this out now, and not before commiting to the other solution
So that test is something that you should make a branch for in your git repo
but i doubt you'd want to deal with it
The test would really just be to see if it could work, but I think the method I have going already is a little more controllable
And rather than spending more time on the OML attempt, I'm gonna get back to the working code :P
But I appreciate the idea!
Well if this works like i expect it to. We wouldn't need to use the cost for chance control.
We would max out the cost to prevent it from being chosen randomly.
Then force it to path through when your random escape logic says so.
This would be the same level of 'controllable'
@faint copper is CodeMatcher something I have access to natively in my current setup, or do I need to install something? And I'm guessing the lethal wiki link you sent me would require me to use depend on MMHOOK?
I guess just disable the enemies that do this deliberate pathing in the preset. And put a warning in the config that their AI may break when they escape
haaaaave you imported HarmonyLib (using HarmonyLib;)?
ye
umm
this is my code:
using System.Collections.Generic;
using System.Reflection.Emit;
using HarmonyLib;
[HarmonyTranspiler]
[HarmonyPatch]
static IEnumerable<CodeInstruction> PatchPatch(IEnumerable<CodeInstruction> instructions)
{
CodeMatcher codeMatcher = new CodeMatcher(instructions);
...
return codeMatcher.InstructionEnumeration();
}
and it's working 🤔
¯_(ツ)_/¯
Nice
if CodeMatcher isn't there, perhaps the BepInEx versions you have differ?
If I put in @twilit drift's code it shows I can create a CodeMatcher, but not a CodeMatcher that utilizes 'instructions'
How's your Harmony defined in .csproj?
I'm using
<PackageReference Include="Lib.Harmony" Version="2.2.2" />
from nuget
which i don't know if it's bad or not but it works 😆
I'm not using a package reference, I was referencing the included 0Harmony.dll
I tried switching the ref to 5.4.22 with no luck
which says it's version 2.9.0 btw
lemme try just doing a ref like that
@faint copper it was all @twilit drift's fault
using Lib.Harmony 2.3.3 instead fixed it
throwing me under the bus lol
Monomod wen
We have good docs
In the lethal wiki
the docs certainly are nice
and being able to use it without the patcher is nice (if BepInEx updates there'll even be no downside!)
Oh I thought Monomod required the hook patcher
It does I think but monomod better :3
Zaggy just said
and being able to use it without the patcher is nice (if BepInEx updates there'll even be no downside!)
😭
Plus it'll get better as time goes on, hamuniis cooking stuff up to make it better
I'm not that knowledgeable with monomod
Whoops * doesn't work when multiple
lmao
Well if MM is significantly easier as the wiki suggests, I haven't really gotten started on the Harmony transpiling so maybe I'll just do MM instead and add hookgenpatcher as a dependency
I would need it as a dependency yeah?
#1203485438497787954 & #1204237353670148137 might be worth reading @gleaming robin
xilo running me through my first couple transpilers step by step with little to no offtopic talk
oh cool
except I'm being pulled towards two transpile methods and I have no idea which would be better for me 🤭
im always team harmony but
wanna make your case against Monomod?
Less intuitive by a long shot
Plus I can autocomplete what I'm looking for in patches with intellisense and it'll auto make the method including parameters
So as long as I know what I want it'll set it up for me in a couple seconds
Also we really do just have a lot more docs for monomod here
Like we have a lot hamunii made lol
oh neat
Sorry @tulip vault, looks like I'll be Monomod-pilled (at least for transpiling)
mmk, time to figure out how to actually set the patch up :P
welp I'm already confused, I just wanted to copy over an example from the page so I could just see how it looked, and I don't know if these packages are actually required or if I've done something wrong before even starting
why u dont get ai to help you
not to be a skid, but instead, use ai to find errors in ur code and correct it for you
Because I'm trying to learn it somewhat on my own first. I don't mind nudges (or in this case just an answer to if these packages are what I'm actually looking for), but I don't want to just be outright given the code
Don't you just need to reference MMHOOK assemblies?
ahhhh that'd be where I'm going wrong probably xD
it's possible to use ILCursor in Harmony with a HarmonyILManipulator
the downside is that if you do that to the same method as a HarmonyTranspiler function did, then it'll fail
ahh okay
so safer to just use monomod in a general sense
though it probably wouldn't matter for this particular case since who else would modify the hawk returning to camp with scrap 🤭
fr fr
Well I have the example mostly working except it says this variable doesn't exist. It's a private variable, but I thought since it was in the tutorial it was somehow exposed due to MMHook or something
playerSlidingTimer is a private field in zeeker's code itself
have you publicized the assembly before? if not, add this:
<PackageReference Include="BepInEx.AssemblyPublicizer.MSBuild" Version="0.4.1" PrivateAssets="all" />
ah, never have lol
yeah that's why then
didn't know that was a thing
and for the LC package reference, make sure it has Publicize="true"
<PackageReference Include="LethalCompany.GameLibs.Steam" Version="50.0.1-ngd.0" Publicize="true" />
Where does this come from exactly? As in, was this something that should've already been in my project? 😭
inside your .csproj
nah I know where to put them (or in the nuget package manager which is what I'm doing)
I just thought maybe I should have already been having them
ohhh thats what you meant
are you using a local copy or something?
it's a package from lordfirespeed
(which imo you should be relying on nuget packages when possible anyways)
i guess thats something the wiki should ideally mention
it probably does and I just didn't see it
eyyy it works, thankee
:D
yeee stuff like this is gonna make all the little dev workflow cracks pop up
can be overwelming to look into them all at once but their all pretty tame
Seems it broke something else tho @copper yew
not yet lol, I haven't gotten it to a state worth publishing
idk, it threw some little warning icons on some of my manual refs which seemed related to the packages I installed so I thought they were duplicates, but I can't see how it'd break a System thing
I checked and I did, but changing it didn't fix the error
it might still be lurking in your usings up the top?
it was just in my transpiler cs I had created to do transpiling things
even if I comment out everything in my other cs's the issue persists
I am so confused
@copper yew solved it, I needed to add HarmonyX
I love wikipedia, I love knowing why something is called what it is
st = store makes a ton of these make immediate sense
ld = load
conv = convert (a bit more obvious, but still)
IL shenanigans 😭
I'm heading to bed, but @faint copper I guess I managed to get the code transpiled in the right place. It didn't really do what I needed it to, but it didn't throw a bunch of errors at me at least.
oh no 😭
It's fine, I'm on version control now :P
nice! getting it to compile is a good first step at least
if you want me to have a look at the code or generated IL I could let you know if I see anything weird
I didn't even think to look at the decompiled code of it lol
Is this saying that the reason its not doing anything is that it's just immediately popped off the stack without running?
oh, I didn't mean the IL of your IL manipulator
I meant the IL that it generates
I'm not sure what that pop is for, you probably are calling a function above that that returns something that you're ignoring
(probably an ILCursor function that is supposed to allow chaining?)
uh
@gleaming robin I let you out of jail
Your code had an unfortunate run of letters that spelled out a naughty word with a dot in it
private static void HawkScrapDestinationChanger(ILContext il)
{
IL.BaboonBirdAI.DoAIInterval += HawkScrapDestinationChanger;
ILCursor c = new(il);
c.GotoNext(
x => x.MatchLdarg(0),
x => x.MatchLdcI4(0),
x => x.MatchCall<EnemyAI>(nameof(EnemyAI.SetDestinationToPosition))
);
c.Emit(OpCodes.Ldarg_0);
c.EmitDelegate<Action<BaboonBirdAI>>((self) =>
{
logger.LogWarning("Baboon hawk is trying to carry scrap back to the nest!");
var pathToTeleport = new NavMeshPath();
foreach (EntranceTeleport teleport in StarlancerEscapeComponent.entranceTeleports)
{
NavMesh.CalculatePath(self.transform.position, teleport.entrancePoint.transform.position, self.agent.areaMask, pathToTeleport); //Check for a valid path to this entrance.
if (pathToTeleport.status == NavMeshPathStatus.PathComplete)
{
self.SetDestinationToPosition(teleport.transform.position);
break;
}
}
});
}```
I'll just send it for you
Thanks! And thank you for freedom :3
wait, are you supposed to be registering your manipulator inside there?
probably not
xD
That would make sense as to why nothing really happens I suppose
yeah that needs to be called somewhere else I would imagine
I don't use MonoMod though so I couldn't say for sure
I'm pretty sure you're right
I was just rushing to get the tutorial code to not throw an error at me and that worked :P
I think I need to register it on enemy start?
Oh, so I could just run it alongside all my registering of enemies then?
(I should say, I don't know when it needs to be registered but it definitely only needs to be done once)
that would probably work
I'm sure the normal way to do it would be during Plugin.Awake() like for Harmony patches
🥷
I forget if you answered this before, but is there a reason why you're not trying to patch this in SetDestinationToPosition()?
just because you don't trust the ways you've used to detect which navmesh a position is on?
one other potential option that I don't think was mentioned is to check that there is no direct path, then test a path from the exit point of an EntranceTeleport from wherever the enemy currently resides, and if that path is valid, send the enemy to that EntranceTeleport
not the most efficient way probably, since testing paths isn't super fast, but it's at least foolproof I think
you mean transpile the base method, rather than the hawk's call to it?
that's essentially how my code tests the paths lol
@faint copper okay, I'm actually glad to get an error bc that means my code is trying to do something at least
That's on attempting to register it on GameNetworkManager.Start()
hmm, are you using .NET Standard 2.1? you should put your pdb in the mod folder so you can actually get line numbers if so
it doesn't look like you're checking that there's a valid direct path in the code that Robin posted above
is that not what my foreach is doing?
oh I see what you mean
a valid path to the original destination?
by direct path I mean a path from the agent's position to the destination that was passed to SetDestinationToPosition
from what I can tell from above, it looks like you're just making them drop items at the entrance regardless of whether they can go to the nest
gotcha gotcha
well for now I'm just trying to successfully override their behavior
also, why check all entrances instead of just the ones that you know are in the AI's current area?
once I properly have my hook in, then I can fine tune it
at this moment, the only way I can think of to differentiate is just to check the game object name for the existence of "(Clone)" lol
also, the reason I ask about getting line numbers is just because I'm assuming that error occurs when it can't find a match for what you're searching for, but it is suspicious that it's not a custom error
I'll throw the pdb in with the next build
you should be able to check whether the EntranceTeleport is in the DungeonRoot I would think
unless they're not in that hierarchy
I'd have to check whether the interior ones spawn through SpawnSyncedObject or something, but I wouldn't expect they would
they do
`// SetDestinationToPosition(baboonCampPosition);
IL_0483: br.s IL_0492
IL_0485: ldarg.0
IL_0486: ldsfld valuetype [UnityEngine.CoreModule]UnityEngine.Vector3 BaboonBirdAI::baboonCampPosition
IL_048b: ldc.i4.0
IL_048c: call instance bool EnemyAI::SetDestinationToPosition(valuetype [UnityEngine.CoreModule]UnityEngine.Vector3, bool)
IL_0491: pop`
okay so that's the IL I'm attempting to access
do I need to match that ldsfld?
yes
Star you've been messing with the navmesh for a while
Is there a way to make an enemy pick a further away node
When wandering
c.GotoNext(
x => x.MatchLdarg(0),
x => x.MatchLdcI4(0),
x => x.MatchCall<EnemyAI>(nameof(EnemyAI.SetDestinationToPosition))
);
this piece of code specifically looks for
ldarg.0
ldc.i4.0
call instance bool EnemyAI::SetDestinationToPosition(valuetype [UnityEngine.CoreModule]UnityEngine.Vector3, bool)
if it sees that ldsfld in the middle it won't match
uhhh iirc there's a method for ChooseFurthestNodeFromPosition
Ehh furthest node isn't that great, I just wanna like increase the range because the Redwood can stay in one spot too much
with the search coroutine?
yea I'm not sure lol, sorry
Okay, then my current issue is figuring out the syntax of MatchLdsfld. Is it as simple as
x => x.MatchLdsfld<Vector3>("baboonCampPosition"),?
if that compiles, that seems right
oh wait
never mind, that type parameter is probably the type that field lives on
you didn't specify where the field is
Not sure I understand
what class defines "baboonCampPosition"? and where is it in that function call?
Well since MatchLdsfld requires a string, would I just use BaboonBirdAI.baboonCampPosition.ToString()?
or do I need to use the overloaded method and do nameof(BaboonBirdAI)
nono
x.MatchLdsfld<BaboonBirdAI>("baboonCampPosition")
that's what I meant by specifying the type it lives on
ohh
you gave it the type of the field and the name of the field, but not the type it is on, so it would have to scan all loaded types for that field if it was going to do what you wanted
gotcha gotcha
if you want to use nameof, though, which you should, you would do x.MatchLdsfld<BaboonBirdAI>(nameof(BaboonBirdAI.baboonCampPosition))
it's much better to reference things that way so that you can use IDE tools to find that again if you ever need to
which is why using the publicizer is also a very good thing to do
So once I've matched it, the MM examples do some OpCode emitting, what is that really for?
c.Emit(OpCodes.Ldarg_0);
c.Emit(OpCodes.Ldc_I4_0);
reflection that is asking for a field or method that doesn't exist will fail at compile time instead of runtime when using nameof
fair point
it lets you load values from the function you're injecting to, like your example here does
that will load the first argument and then load a constant value of 0
(Ldc = load constant, i4 = 4-byte integer, 0 = constant zero)
So with that, is the 0 being used for false?
arg 0 is the position, yes
er, whoops
no, arg 0 would be this in an instance function
for a static method it would be position
okay so this is where the visualization of the stack comes in I guess
the main thing you have to keep in mind is that things are popped from the stack in the same order they are pushed to it
in the case of a method call, it pops in the order you would expect given the parameter ordering
last push is last arg etc
so arg.0 is "this" (meaning the hawk in this instance?), the fld is the position, the i4.0 is false, which are then called with SetDestinationToPosition()?
which are then popped by the call to SetDestinationToPosition()
, yes
oh also, this is incorrect, ldsfld means "load static field", as in it doesn't require an instance and therefore doesn't pop anything off the stack
IL_0485: ldarg.0
IL_0486: ldsfld valuetype [UnityEngine.CoreModule]UnityEngine.Vector3 BaboonBirdAI::baboonCampPosition
IL_048b: ldc.i4.0
after these three instructions, the stack is 3 items
if it was ldfld, it would pop one item off the stack
would ldfld pop the field that it loads off the stack?
if you check the documentation you can see the stack behavior
ldfld pops one value off the stack and then loads a field from it and pushes that onto the stack
do I need to emit any opcodes, or do I just EmitDelegate?
depends on the case
yea it was just the version of the page :P
Google doesn't know how to remove queries from URLs because it's dumb
anyway
if you need a value that exists in the function I believe you probably do need to emit opcodes, unless MonoMod does some magic to detect variable names or something
if it doesn't, just load the things you need from the place you're injecting in the order you define them in the delegate
it looks like I can use EmitDelegate and then just use regular code
hm?
it doesn't say anything about that there
all it does is emit a call or callvirt to your function
the example code was this, so that was my understanding
c.EmitDelegate<Action<PlayerControllerB>>((self) =>
{
logger.LogInfo("Hello from C# code in IL!");
if (self.isSprinting)
self.jumpForce = 30f;
else
self.jumpForce = 13f; // this is the default value of jumpForce
})```
I thought we had a conundrum on our hands

