#Starlancer AIFix v3.13.2 | EnemyEscape v3.0.0

1 messages ยท Page 7 of 1

gleaming robin
#

what am I lookin at bruh

twilit drift
#

the emit delegate stuff

gleaming robin
#

is my understanding correct then?

faint copper
#

mrov didn't highlight the ldloc_1 Susge

twilit drift
#

๐Ÿฅบ

faint copper
#

feels weird to me to have self be the last argument but I'm sure that's because they emit the ldarg.0 immediately before the call

#

personally I would just emit the ldarg.0 myself to make it the first one lol

faint copper
#

wait who are you talking about? did you look at the MonoMod source?

gleaming robin
#

oh sorry, the example code I shared had
c.Emit(OpCodes.Ldarg_0); // load argument 0 'this' onto stack
right before the EmitDelegate

faint copper
#

wot

#

@twilit drift you didn't load arg 0 in the code you linked theinking

#

is it automatic or not??

twilit drift
#

๐Ÿ˜ณ

#

umm

#

it's working?

#

I don't know ๐Ÿ˜ญ

gleaming robin
faint copper
#

thank you

#

it's much easier to tell what's going on with context lol

gleaming robin
#

yea lol

#

if I'm understanding things correctly (probably not) then I just need to load arg.0 and i4.0 on the stack, not emit a ldsfld, and from there let the EmitDelegate handle the method?

#

๐Ÿ‘€

#

idk if it works, but I loaded up with no error this time

twilit drift
#

๐Ÿ‘€๐Ÿ‘€

faint copper
#

what's the constant 0 for?

#

can you post a diff of what you're trying to do to the vanilla method?

#

from what I saw earlier, you only had a self argument to your delegate

faint copper
twilit drift
#

I am not sure as well

faint copper
#

what's loc 1?

twilit drift
#

I'm planning to change it to a harmonytranspiler instead of ILmanipulator

faint copper
#

(where's the comment about what the load is for Susge )

twilit drift
#

๐Ÿ˜ณ

#
    // // to be completely honest, I have no idea what I'm doing
    // // https://github.com/SylviBlossom/LC-SimpleWeatherDisplay/blob/2d252b92dcd4d8ef259b8072d9339ff5ccdc4d0b/src/Plugin.cs#L127-L153
    // // this is just this, but repurposed
#

here

faint copper
#

also looking at EmitDelegate it seems like I was right not to want to use something like that

#

it stores a reference to the delegate in a list to keep it alive for the duration of the program, whereas if you just emit a call yourself you can avoid any indirection

gleaming robin
#

man, it might help if I turn warning logs back on in bepinex

faint copper
#

WHAt

#

turning off logging...

#

why are y'all making me judge you so much today

twilit drift
#

๐Ÿ˜ณ๐Ÿ˜ญ

#

it's a bowl of spaghetti

gleaming robin
#

prior to this I was trying to test something I needed loginfo for, and meaningless vanilla warnings (failed to init audio spatializer) were spamming

#

so I just wanted peace

rocky fable
#

mrov needs lotsa spaghetti!!!!

faint copper
#

true those ones are very annoying

#

I won't ever turn off warnings though, I don't trust myself to remember to turn them on again

#

I don't mind copying logs into npp and scanning them

gleaming robin
#

okay, well I load in with no IL error, but my code isn't firing

#

I was really hoping to see logger.LogWarning("Baboon hawk is trying to carry scrap back to the nest!"); flooding in ; w ;

twilit drift
#

๐Ÿฅบ

#

so at least you're targeting correctly? ๐Ÿฅบ

gleaming robin
faint copper
#

@twilit drift I understand why it works now, ldloc.1 isn't loading the string, it's loading this but within a generated Enumerator.MoveNext()

#

silly enumerators

twilit drift
#

oh gosh

faint copper
twilit drift
#

I'm not surprised I didn't understand what I was doing ๐Ÿ˜†

faint copper
#

also why are you planning on changing to a transpiler?

twilit drift
#

because I don't like the current implementation ๐Ÿฅฒ

#

it's yoinked

faint copper
#

hmm I see

#

if I were you I would stick with ILCursor

twilit drift
#

it's better for the stuff I'm doing?

faint copper
#

well, I haven't directly compared them, but I would imagine ILCursor is a nicer experience than CodeMatcher

gleaming robin
faint copper
#

you can emit the same code with ILCursor that you can with transpilers

twilit drift
#

I know, but I've played with CodeMatcher and it was easier to understand then that

faint copper
#

oh? huh

faint copper
#

you're not popping it there

gleaming robin
#

to me it seems like the basics of IL are super unintuitive (alphabet soup nonsense) but I feel like I'm getting somewhere

faint copper
#

I would've expected this not to pass validation

gleaming robin
#

ยฏ_(ใƒ„)_/ยฏ

faint copper
#

you only have one argument to your delegate but you're emitting two instructions that push to the stack

#

I'm not sure if MonoMod validates the IL, but if it was doing so I would've expected it would not be very happy about that

gleaming robin
#

well removing the ldc didn't throw any errors so... idk

#

I'm just trying to figure out the process lol

faint copper
#

I guess having an extra value on the stack isn't necessarily fatal unless the VM doesn't like it when ret happens with a non-empty stack

#

and I don't know if that's valid or not

gleaming robin
#

well it's gone now anyways

faint copper
#

yeah, I'm mainly trying to think through whether it should've detected that after the patch ran

#

wait

#

you don't even have those in the order it would need to be in, that should crash

#

so maybe that's one point for Harmony

#

I'm pretty sure it would've seen that the call is trying to pop a value of the wrong type off the stack and complained

#

yeah, looks like a transpiler explodes when I do that

#

so maybe you should switch over to a transpiler and use CodeMatcher instead

#

it'll be much easier for you to start out if it actually tells you when your code doesn't make sense

#

it's not the best at telling you why, but you'll at least see an error on startup instead of it happening when your function is finally called

#

the CodeMatcher functionality seems quite similar to what you're working with right now as well

gleaming robin
#

what order should it be in?

faint copper
#

so

#

you had

ldarg.0
ldc.i4.0
call delegate void(BaboonBirdAI)
#

if you go through that in order, it would:

  • push BaboonBirdAI this
  • push i32 0
  • pop BaboonBirdAI arg0, receive i32 instead
#

it can detect that the call is trying to pop a BaboonBirdAI instance and getting an i32 instead, and say "mmm I think this isn't quite right theinking "

#

but it didn't

#

transpilers would

gleaming robin
#

huh, why didn't MM explode

faint copper
#

so they are better

gleaming robin
#

or at least return an error

faint copper
#

it's because transpilers specifically validate the IL they generate after they finish

gleaming robin
#

ah

faint copper
#

it may also apply to ILManipulators but I'm not sure

gleaming robin
#

well even after removing the ldc it still doesn't work

faint copper
#

yeah

#

if it hit your code you would've gotten a weird error

#

did you print a message when it runs your patch so you know that it's actually running it?

golden basin
#

Print the il to see if it's actually changing at the end

faint copper
#

not the delegate, but the code that generates the IL

golden basin
#

:p

faint copper
#

is it possible for it not to change it?

#

if the ILCursor is invalid and you try to emit, does it eat the error?

golden basin
#

It shouldn't eat any errors afaik

#

Here lemme bring the expert

#

@rustic needle

faint copper
#

uh oh

#

Hamunii isn't gonna be happy to see my hating

rustic needle
#

I'm gaming, what

golden basin
#

Lmao

#

I dont think ive ever seen u game

#

They're doing il hooking stuff

rustic needle
#

lol

faint copper
#

would it be possible for an ILCursor to not modify things if it's told to emit? I assume it wouldn't eat errors

golden basin
#

Yeah that

#

Also what're u playing

rustic needle
gleaming robin
faint copper
#

is that printed inside your ILContext function? if not, then it's not really a good test

gleaming robin
#

oh I can put a normal log in there? I assumed it had to be all IL stuff only

faint copper
#

you're sending a delegate to MonoMod and then assuming it's running it

#

it's just any other function, yeah

gleaming robin
#

oh, well then

#

lemme try

faint copper
#

you could do all kinds of messed up stuff in there

golden basin
gleaming robin
#

@faint copper hoping this is what you meant lol

faint copper
#

ah yes

rustic plume
#

the enemyescape mod

faint copper
#

hopefully with enough IL context to be able to see if it worked

#

although now what I wonder is, have you given it time in-game to actually have a little guy pick up an item?

#

you'd have to scatter a lot of items around for them to find one I think

gleaming robin
gleaming robin
faint copper
#

ah ok hmm

gleaming robin
#

last test I made it real quick and spawned an item for it to pick up lol

faint copper
#

did you check that the behavior state corresponds to the one you're injecting into?

gleaming robin
#

lemme doublecheck

faint copper
#

on next run it would be good to get the final IL you generate by printing the ILCursor and then check with UnityExplorer whether the baboon hawk is in the correct state

gleaming robin
#

Imperium shows me a neat little box above the enemy with the info

#

How would I print the ILCursor? Just log $"{ILCursor}"?

faint copper
#

print your local variable instance of it, but yes

gleaming robin
#
logger.LogInfo(c);```
faint copper
#

indeed

#

also... aren't you injecting into behavior state 0 and not behavior state 1 where it tries to navigate to the camp? can you show me the code you're trying to inject into in context?

golden basin
faint copper
#

what's wrong with the current update anyways lol

#

are people just complaining for no reason?

gleaming robin
#

nothing's "wrong" with it necessarily

faint copper
#

I figured

#

classic

gleaming robin
#

just compared to where I'm currently headed

#

it's very crude lol

faint copper
#

yeah

#

it happens

golden basin
faint copper
#

not you, but [this person](#1206494982521753620 message) has asked a similar question at least once before iirc

#

if not more than twice already

gleaming robin
faint copper
#

yes

golden basin
#

Though tbf users are usually split up into two categories, the constant askers, and the constant supporters who are low key askers

gleaming robin
#

it's supposed to be in behaviour state 1 when it's carrying the scrap to the camp

faint copper
gleaming robin
#

but my code shouldn't care for the purpose of what we're trying to do rn

golden basin
faint copper
#

but yeah lemme know when you have that printout of the generated IL

gleaming robin
#
IL_0000: ldarg.0```
faint copper
#

uhhh

#

is that at the start or the end?

gleaming robin
#

yes

#

; w ;

faint copper
#

wot

gleaming robin
faint copper
#

if the answer is that it's at the start then you should move it to the end

#

we need to see it after you make changes

gleaming robin
#

OH okay I totally misunderstood the assignment

#

me rn ^

faint copper
#

understoodable

golden basin
#

It's okay, il hooks killed me today too

faint copper
#

I just hope that it's gonna print out more than one instruction

golden basin
#

I couldn't even manage to do the simple il hook I was doing lol

faint copper
#

the MonoMod docs say that it should print "surrounding instructions" so I'm not sure why you only saw one

golden basin
#

Whatd you print specifically

faint copper
#

the cursor

#

which will use ToString()

golden basin
#

I dont think you print the cursor

#

Aren't u supposed to print the param il

gleaming robin
#
[Info   :Starlancer EnemyEscape] // ILCursor: System.Void DMD<BaboonBirdAI::DoAIInterval>?-812375320::BaboonBirdAI::DoAIInterval(BaboonBirdAI), 369, None
IL_0000: call System.Void MonoMod.Cil.RuntimeILReferenceBag/FastDelegateInvokers::Invoke<BaboonBirdAI>(T1,MonoMod.Cil.RuntimeILReferenceBag/FastDelegateInvokers/Action`1<T1>)
IL_0485: ldarg.0```
faint copper
#

you mean the context?

#

perhaps I suppose

golden basin
#

Ye

#

Pretty sure u are

gleaming robin
#

okay I'll try that

golden basin
#

It should give you a large text dump

#

Its what I logged to check changes

faint copper
gleaming robin
#

nope here we go, one sec

golden basin
#

Yep he's got it lol

faint copper
#

it's gonna be a big print for this method which is why I wanted ILCursor to work

gleaming robin
golden basin
#

I usually just screenshotted the relevant parts

#

Otherwise it just looks like a huge mess

#

But idk what you're looking for specifically

gleaming robin
#

I wanna let Zaggy decide what's relevant

golden basin
#

Aren't you changing something in this method?

gleaming robin
#

yea I just felt like presenting the whole log rather than risk somehow leaving something relevant out

golden basin
#

That's fair

#

Good luck zaggy

#

(Its not too hard just annoying to read at first lol)

#

I'm glad I have a second monitor

faint copper
#

it's fine

golden basin
#

And here I was being told I learned nothing from the il hooking I did today ๐Ÿฅบ

faint copper
#

does GotoNext() put you at the index of the first predicate instead of after the last??

golden basin
#

I'm gonna say yes because I think i know what you're talking about but idk what predicate is

gleaming robin
#

the code should be at IL_0485, which right before that is some IL_0000 stuff which I'm assuming is supposed to be me?

golden basin
#

I had to do moveType.after and then - index by 1

faint copper
golden basin
#

Ah icic

gleaming robin
#

wait I think I goofed, lemme check something

faint copper
#

yeah I'm gonna be honest the fact that the MonoMod docs don't say where the cursor ends up for a predicate array with length > 1 is kinda annoying

#

I'm gonna assume that it puts it at the start and you need to += predicates.Length; which isn't exactly ideal since that means you have to toss a magic number or local array in there

#

it looks like your Index-- is going here and then deleting this instruction:

gleaming robin
#

Yea I was dumb and meant to get rid of that attempted removal

faint copper
#

well

#

the removal is fine if you pop twice three times

#

actually, since it expects it to push a value two pops should do

#

lol

gleaming robin
#

regardless, removing the removal didn't change any of the surrounding code (except for not removing 0483)

faint copper
#

yeah it wouldn't

#

you were deleting the branch instruction I showed above

#

if you want to keep using ILCursor, you would need to c.Index += [number of predicates]

#

but... I'm gonna be honest, it seems like CodeMatcher's MatchEndForward function would be a lot smoother for this

#

I actually had thought that ILCursor had similar functionality, but the docs don't mention anything that I can find

#

if you want to use CodeMatcher, you can just give your function a [HarmonyTranspiler][HarmonyPatch(Type, string)] and then construct the CodeMatcher(IEnumerable<CodeInstruction>, ILGenerator) from your transpiler's parameters of the same type

#

then harmony.PatchAll(Type) as usual for prefixes and postfixes

gleaming robin
#

At this point I might jump ship yea @_@

gleaming robin
#

I remember trying to use CodeMatcher and something was going wrong tho

faint copper
#

I'm sure we can figure it out

gleaming robin
#

and I think it was somehow mrov's fault?

faint copper
#

uh oh

#

thrown under the bus yet again

#

at this point mrov might as well just become the bus's skid plates

gleaming robin
#

ah yes, this was the issue

twilit drift
#

oopsie

gleaming robin
#

yea that

faint copper
#

so does that mean you already know the solution?

gleaming robin
#

nope

faint copper
#

what does your csproj look like?

gleaming robin
#

I think that's what lead to me going to MM

#

sec

#

@faint copper

#

lemme just paste it

#

or not, it's too long lol

#

there we go

#

I'm a fool that's the old one

#

aspodfijasopdfij

faint copper
#

try this for your packages and remove the references to BepInEx, Harmony, HarmonyX dlls (and the MonoMod ones if you don't need them)

    <ItemGroup>
        <PackageReference Include="Mono.Cecil" Version="0.11.5.0"/>
        <PackageReference Include="BepInEx.BaseLib" Version="5.4.21.0"/>
        <PackageReference Include="BepInEx.Core" Version="5.4.21.0"/>
        <PackageReference Include="BepInEx.AssemblyPublicizer.MSBuild" Version="0.4.1" PrivateAssets="all"/>
    </ItemGroup>
#

ope one of the lines changed color when I didn't mean it to lol

gleaming robin
#

no warnings or errors!

#

and a much cleaner dependency list lol

#

If I ever hit the lottery @faint copper , you're getting at least ten dollars of it

faint copper
#

I'll buy a half a jalapeno burger with it

gleaming robin
#

๐Ÿคญ now to figure out what I'm doing here

#

Okay, so it seems like opcode matching is still pretty simple, but for ldsfld I'm not sure of the syntax. The example I'm looking at on BepInEx's github wiki has this:

new CodeMatch(i => i.opcode == OpCodes.Ldfld && ((FieldInfo)i.operand).Name == "test"))```
#

To which I assume I can swap Ldfld with Ldsfld, and FieldInfo with BaboonBirdAI

#

but then I run into not being able to directly reference baboonCampPosition like I was previously

faint copper
#

FieldInfo is a type that represents the field that the instruction is loading

#

what you could use is i => i.LoadsField(AccessTools.Field(typeof(BaboonBirdAI), nameof(BaboonBirdAI.baboonCampPosition)))

#

or better yet, put the field in a variable so that you're not requesting the field from AccessTools repeatedly:

FieldInfo baboonCampPositionField = AccessTools.Field(typeof(BaboonBirdAI), nameof(BaboonBirdAI.baboonCampPosition));
matcher.MatchEndForward([i => i.LoadsField(baboonCampPositionField)]);
#

you can also get the field by doing typeof(BaboonBirdAI).GetField(nameof(BaboonBirdAI.baboonCampPosition)), and there is a similar method for getting methods on a type

gleaming robin
#

Would I store the variable within the code instruction?

faint copper
#

what do you mean?

gleaming robin
#

like should it be declared within the class or the transpiler itself?

faint copper
#

the transpiler is fine, you would only need to put it outside the function if you're going to use it elsewhere

gleaming robin
#

kk

#

btw you've got MatchEndForward, but mine is MatchForward and I have to declare false/true for start/end

faint copper
#

and actually, it looks like CodeMatch can take an opcode and operand argument, so you can do this instead:

matcher.MatchEndForward([new CodeMatch(OpCodes.Ldsfld, typeof(BaboonBirdAI).GetField(nameof(BaboonBirdAI.baboonCampPosition))
)]);

this will pass the FieldInfo instance into the CodeMatch so that it's cached and then it will just compare after that instance each time

#

if you were to do a predicate instead like i => i.LoadsField([..]) it would evaluate [..] every time, which if you get the field in there can be costly

#

hopefully that makes sense

gleaming robin
#

I think so, that's what I was trying to do for a bit but I didn't know the syntax of it :P

#

Alright, so once I've matched the code I want to transpile, do I make an OpCode.call? Also, it's still there from the tutorial example here, but what is InstructionEnumeration()?

IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
    return new CodeMatcher(instructions)
        .MatchForward(false,
        new CodeMatch(OpCodes.Ldarg_0),
        new CodeMatch(OpCodes.Ldsfld, typeof(BaboonBirdAI).GetField(nameof(BaboonBirdAI.baboonCampPosition))),
        new CodeMatch(OpCodes.Ldc_I4_0))
                 .InstructionEnumeration();
}```
faint copper
#

you said false means start and true means end, didn't you?

#

if so, you'll want to pass it true

#

and InstructionEnumeration() just returns the IEnumerable<CodeInstruction> of the modified method body

#

to make your call, you would want to call Insert(CodeInstruction[]) and pass it an array of CodeInstructions that do what you want

#

that would go after you find the injection point with MatchForward()

#

so i.e.

.Insert(new CodeInstruction(OpCodes.Ldarg_0),
  CodeInstruction.Call(typeof(YourType), "YourMethodNameOnYourType", new Type[] { typeof(ParameterType1) }))
gleaming robin
rustic needle
rustic needle
distant bay
#

v50

faint copper
rustic needle
#

Yeah, I should really work on improving the (unofficial) documentation on lethal.wiki. And perhaps talk with the MonoMod devs about it too, as it could be nice to have better official documentation

faint copper
#

Oh, I was looking at the MonoMod docs actually

#

the table of contents doesn't let you scroll through it so none of the GotoNext methods appear there lol

faint copper
#

as far as the ILCursor, the documentation also doesn't really make that clear, but I would've thought of its current index as referring to the instruction in the list at its index rather than between them

#

I suppose it makes sense, but I think it would be nice if it printed more than two instructions, or at least if the documentation made it clear that's what it meant

rustic needle
#

well, that's why you should generally just print the ILContext il instead

faint copper
#

sure, but then you get 10 times as many instructions as you actually need

#

(in functions that are large obviously)

#

I'm not sure what the use case for printing two instructions at the position of the ILCursor is

#

seems like it should be configurable by an argument or just do a wider range

rustic needle
#

true. How does HarmonyX deal with printing the position of the CodeMatcher or whatever?

faint copper
#

that I don't know tbh

#

I don't use either lol

rustic needle
faint copper
#

that seems so situational to the point of being useless, but maybe I'm wrong

#

I guess it prints the offset..

rustic needle
#

It's useful if you don't know how the cursor works I guess

faint copper
#

right

#

I am wondering while you're here though, am I right that MonoMod IL manipulators (or whatever you would call them) don't validate the generated instructions? it seems like earlier Audio Knight was generating some invalid IL and it wasn't printing any errors despite that

gleaming robin
faint copper
#

I would've expected the code to cause undefined behavior or exceptions when it ran due to popping values of the wrong type off the stack

#

but transpilers seem to validate that before it gets to running it

rustic needle
faint copper
#

oh, that's odd

#

hard to say what was going on for him then

rustic needle
#

ye

faint copper
#

do you know if they both use some standard way of validating or their own differing implementations of that?

rustic needle
#

I guess feeding the arguments to methods isn't validated then?

#

No idea about the validation stuff

faint copper
#

I tried it on one of my transpilers and it caught it, but maybe it depends

golden basin
gleaming robin
rustic needle
#

HarmonyX borrows quite a lot of stuff from MonoMod with IL stuff

#

well, at least the ILContext/ILCursor

faint copper
twilit drift
#

monomod supremacy mentioned

faint copper
#

up to you though, it does make the code a little prettier to use that indirection

faint copper
#

because from what I saw of the code, it has to statically store a reference to the delegates that are used so they don't get GCed, then make a separate function call just to get it later when it needs to be called

rustic needle
#

huh

faint copper
#

it's probably not super inefficient since it's in an indexable list but I'm a bit of a purist wrt my code injection

#

if I can avoid overhead I do

rustic needle
#

saving the precious nanoseconds or something ๐Ÿ™

faint copper
#

yeah

#

premature optimization is the root of all evil but I can't help it

#

it probably adds up but if other people are doing it, I'm a drop in the ocean

rustic needle
#

idk, people doing inefficient stuff is what makes performance worse, like getting some gameobject in an Update method or something lol

faint copper
#

yeah

#

that's probably a whole lot more significant than a table lookup

gleaming robin
faint copper
#

fair enough

faint copper
#

I won't fault you for that too much

golden basin
#

hamunii has 0 faith smh

#

i was kidding

#

though i still do something a bit weird in update

rustic needle
#

at least the sizing up part

golden basin
faint copper
#

I also haven't looked at how MonoMod's mmhook works, but I'm a little concerned that adding wrappers for a bunch of functions adds a lot of overhead

#

since those are more common it would be more of an issue than any IL manipulation

rustic needle
gleaming robin
#

Okay, so now I need to register the transpiler, yea?

faint copper
gleaming robin
#

Oh yea

faint copper
#

Harmony.PatchAll(typeof(PatchClass))

rustic needle
#

so if there is any perfomance cost with using MMHOOK, it's nothing compared to that ^

faint copper
#

but applying/undoing would only be done once, right?

#

my concern is about the cost at the call site, since a fair amount of mods I think use MMHOOK to hook into Update() methods

gleaming robin
#

Harmony didn't scream at me ๐Ÿ‘€

faint copper
#

it looks like HookEndpointManager just holds a couple of dictionaries, not sure if I'm missing some references to those dictionaries in the rest of the MonoMod codebase though Hmm

carmine shale
#

Enemy escape?

#

That must be new

#

I wanna see an old bird go ham on a coil head

rustic needle
rustic needle
gleaming robin
#

OKAY so its not overriding it, but my thing is being called!!!!!

gleaming robin
rustic needle
gleaming robin
#

Alright, now that I've got the code actually firing, I'm not sure why the destination isn't getting properly overridden

#

also I'm renaming self to hawk to that I don't get put in jail again if I share the code

golden basin
#

the naughty word included self?

warm nexus
faint copper
faint copper
gleaming robin
golden basin
#

lol bet

faint copper
#

I'm having trouble figuring out where the hooks are actually injected, even looking at a decomp of MMHOOK

faint copper
#

right, I was in that file and I didn't see any noteworthy references to the dictionaries it stores

#

oh wait, does the magic happen in the Hook constructor? lemme check

rustic needle
#

there is definitely stuff in the constructors

gleaming robin
#

@faint copper I've tried a few iterations of my code, but it seems like I need to completely replace the original call for this to work

faint copper
#

you can add branches if you need to skip vanilla code

#

but...I really think this would be easier if you just injected into SetDestinationToPosition...

faint copper
#

if you do it in SetDestinationToPosition, you can just modify the destination as you see fit since it's an argument instead of a static field

#

and it'll take care of more AI than just the baboon hawks

gleaming robin
#

true

#

Should I just put in a catch-all of "if (pathIsInvalid) {return;}"?

faint copper
#

oh, that's not what I meant

#

you should change the destination there, not skip the function

gleaming robin
#

I'm unsure what to change the destination to tho

faint copper
#

well, if an AI wants to path to a location that is outside of its current domain, it needs to find an exit, yes?

#

you were trying to do something similar in the baboon hawk's DoAIInterval I believe

gleaming robin
#

ah, so maybe something like this?

bool nodeInOtherArea;
if (isOutside) 
}
  //loop through inside nodes and if any of them are within, let's say 20 units of Vector3.Distance(), set nodeInOtherArea = true and break the loop
}
if (nodeInOtherArea)
{
  //loop through outside teleports, pick the closest one, set position to it instead of the original position passed to SDTP
}
else
{
  //keep original position
}```
faint copper
#

for checking which area you're in, you'd be better off checking the nodes of both areas to see which one has a node closest to the enemy, but otherwise yeah

#

there's no real guarantee that a node will be within 20 meters, but you can be more sure that the closest one will be in the area that the enemy is in

#

if you cache an array of Vector3 for each area then you can check that very quickly

gleaming robin
#

oh fair, yea checking the closest node's tag would be way better

faint copper
#

I would keep a separate array of Vector3 for each area so that you can just loop through them really fast

gleaming robin
#

I could certainly make an array in StarlancerAIFix when I run my FindNodes method and cache it

faint copper
#

contiguous memory helps a lot for stuff like this

#

exactly

#

you already are caching the node objects, so you could probably get away with caching their locations as well

#

at least assuming we aren't going to see mods moving AI nodes around (which would be very strange imo)

gleaming robin
#

that would be incredibly weird lol

#

would it be better to make a dictionary for this?

faint copper
#

a dictionary? how would you use a dictionary?

gleaming robin
#

so that I could cache a node's position with its tag?

#

I'm not 100% sure of the logistics lol

faint copper
#
Vector3[] outsideNodePositions;
Vector3[] insideNodePositions;
#

with this, you don't need to care about tags

#

tags are inefficient

gleaming robin
#

yea u right

faint copper
#

getting anything from the Unity runtime is slow

#

.transform is slow, .position is slow, .rotation is slow, .gameObject is slow, etc

#

if you're doing those in a loop 200 times, it adds up

#

if you have an array of Vector3, all that gets put in your CPU cache and then it just blazes through the calculations

#

if you use a Dictionary, you're splitting those up into separate buckets which can result in blank spaces that have to be checked, or even indirection, which isn't great for CPU cache

#

(speaking about iterating a Dictionary that is, but getting values from every key in the Dictionary is also similarly inefficient)

#

if you absolutely needed to keep track of the tags (like if there were an unknown number of AI node tags), you would be better off doing either

(Vector3, Tag)[] nodePositionsAndTags;

or

Vector3[] nodePositions;
Tag[] nodeTags;

not sure which would be more efficient though

gleaming robin
#

Understood ๐Ÿซก I'll keep it simple then lol

for (int i = 0; i < insideAINodes.Length; i++)
{
    insideNodePositions[i] = insideAINodes[i].transform.position;
}```
faint copper
gleaming robin
#

Alright, the original code is:

public bool SetDestinationToPosition(Vector3 position, bool checkForPath = false)
{
    if (checkForPath)
    {
        position = RoundManager.Instance.GetNavMeshPosition(position, RoundManager.Instance.navHit, 1.75f);
        path1 = new NavMeshPath();
        if (!agent.CalculatePath(position, path1))
        {
            return false;
        }
        if (Vector3.Distance(path1.corners[path1.corners.Length - 1], RoundManager.Instance.GetNavMeshPosition(position, RoundManager.Instance.navHit, 2.7f)) > 1.55f)
        {
            return false;
        }
    }
    moveTowardsDestination = true;
    movingTowardsTargetPlayer = false;
    destination = RoundManager.Instance.GetNavMeshPosition(position, RoundManager.Instance.navHit, -1f);
    return true;
}```
Would I want to insert my code before, and if it determines the position is in another area it returns true earlier than the original code and thus takes precedence?
#

(I'd also need to toggle those two bools toward the end)

#

Also, am I able/would I need to ref the original position and checkForPath?

gleaming robin
faint copper
#

oh yeah, true

#

whoops

gleaming robin
#

that's why I was initally talking about "within a distance"

faint copper
faint copper
gleaming robin
#

Alternatively, I could check the node nearest the position and see which nodePositions array it exists in

#

I think there's a method for that at least

faint copper
#

that sounds like what I suggested but without the early exit

gleaming robin
#

actually it has a break/return

faint copper
#

nonononono

#

that one will calculate a path

#

you don't need to check paths for this, that will be way slower than iterating over every node's position in a Vector3 array

gleaming robin
#

kk, I'm just trying to think of a more accurate way to determine it than a magic number distance

faint copper
#

the magic number distance would just be a shortcut, there's nothing particularly inaccurate about it unless the interior literally clips into the moon

gleaming robin
#

true

#

which LLL will be preventing anyways iirc

faint copper
#

hopefully, eventually

#

but even if it doesn't, if those AI nodes are closer than 10 meters from each other then we probably have bigger problems than an interior being on the same height as the moon

gleaming robin
faint copper
#

it certainly might make sense to do that, yeah

#

on most maps that would be 2-4 paths, I suppose, so it shouldn't be a huge deal

#

not ideal if there's another solution, though

gleaming robin
#

true, but it needs to have a valid path if I wanna set it as a destination

faint copper
#

not really

#

vanilla code doesn't check that most of the time

gleaming robin
#

yea but vanilla code doesn't really account for enemies being both inside and outside, except for Masked and snakes

faint copper
#

if you wanted to, you could check NavMeshAgent.pathStatus to react to a path being invalid

faint copper
gleaming robin
# faint copper if you wanted to, you could check NavMeshAgent.pathStatus to react to a path bei...

So instead of

NavMesh.CalculatePath(enemy.transform.position, teleport.transform.position, enemy.agent.areaMask, pathToTeleport);
if (pathToTeleport.status != NavMeshPathStatus.PathComplete && Vector3.Distance(enemy.transform.position, teleport.transform.position) < closestTeleportDistance)
{
    position = teleport.transform.position;
}```
I would set it and then check if the path was invalid without calculating it myself?
`if (enemy.agent.path.status != NavMeshPathStatus.PathComplete) { }`
faint copper
#

it's asynchronous, so not immediately

#

pathPending has to be false

#

this would be more a thing to check on the next AI interval

turbid wave
#

You have some great stuff cooking over here. Can't wait for it to release

gleaming robin
#

Ah okay. I have to go to dinner and movie, so I'll be back later to hammer away lol

gleaming robin
turbid wave
#

When this code is triggered by an attempt to path to another area.
You'll need to record the original destination.
Slip a call to the rest of the mod to schedule the teleport after reaching the exit.
And restore the original destination pathing after the teleport happens.

#

With those steps, the areas would become fully seamless

faint copper
#

I don't think storing the original destination should be necessary, since the behavior state repeatedly sets the destination

#

if the AI is forced to update immediately after a teleport, which I believe he already made them do, then they should immediately start moving to wherever they wanted to go outside

#

or inside in the reverse scenario

turbid wave
faint copper
#

it's just a common pattern

#

we could definitely verify that it's consistent, but I don't think that would be an issue

turbid wave
#

Seems inefficient to spam pathing attempts, when the destination is stationary. The first pathing could be the only pathing

faint copper
#

I kinda doubt it is

#

I'm sure that the Unity runtime tracks whether the destination or the navmesh are modified

#

it would be kinda silly if it didn't

#

even if not, it's a vanilla thing, it's not like we can easily change that

turbid wave
#

I have assumed that enemyAI would call SetDestination on the event. And then wait for the agent to reach the destination.

#

If the enemyAI class stores the desired destination and calls SetDestination on every update. Then yes we don't need to store it.

#

But if that is done by a behavior of each enemy, and not the class, then future/ modded enemies could break that assumption

gleaming robin
#

SetDestinationToPosition is a method of the EnemyAI class

#

Or type, whatever the terminology is lol

turbid wave
#

Then yeah we don't need to save it.

ruby bobcat
#

||Are outside enemies supposed to travel inside with EnemyEscape? xD||

autumn panther
#

Ye

gleaming robin
#

Is there something causing a problem?

ruby bobcat
faint copper
#

the only time I know of that it clearly doesn't is if an invalid target location is set, because when that happens, they reset their target to some default location

gleaming robin
#

Sorry, I think I'm missing something, the function recognizes if the position being fed to it is the same as the previous position?

faint copper
#

not the vanilla function, the Unity runtime

#

if you were to tell an agent to go to the same place twice, I would expect it to not cancel any existing pathing

gleaming robin
#

ah

#

neat

#

that would line up with the blob's debug nav-line ceasing its flickering after I tell it to go somewhere I guess

#

@faint copper @copper yew @twilit drift idk what time zones everyone is in, so none of y'all might be up/available, but if I want to insert my code at the beginning of the method, would I just:

return new CodeMatcher(instructions)
    .MatchForward([useEnd = false],
    new CodeMatch(OpCodes.Ldarg_2)

Or is there some other logic I have to grab for this?

copper yew
#

if you're just running code before the original, couldn't you just do a prefix patch

gleaming robin
#

I don't know anymore ; w ;

tulip vault
#

return false prefixes still run other patches applied to that function

#

which sometimes can be problematic

rocky fable
#

XD

gleaming robin
#

Like fr I don't know what the right answer is. I've been focused on learning transpiling bc originally I was gonna insert the code into the BaboonBirdAI.DoAIInterval(), but Zaggy suggested just preventing issues in EnemyAI.SetDestinationToPosition() in the first place, which I agree with, but we never discussed switching from transpiling

gleaming robin
tulip vault
#

it is

gleaming robin
rocky fable
#

Earlier I went into a Fire Exit and there was a dog that went inside that was instantly aggro'd and it attacked me

#

XD

#

I was like "Bruh all I did was walk inside why was that dog so angry?"

#

โ˜ ๏ธ

gleaming robin
#

Fair point, I'll change the default to 0 bc it is a one-hit-kill after all

gleaming robin
tulip vault
#

if your never returning false the function can just be void

rocky fable
#

Are you planning to add the functionality for some enemies like Hoarding Bugs and Brackens to be able to spawn outside btw?

gleaming robin
#

Wasn't planning on it really, but I guess it could be within the scope. I'd probably default that to off if I implemented it

#

but that's a future consideration

#

If I can get this working, I can start tidying up for release lol

#

Prefixing might actually be better in this case since I could ref and manipulate the initial variable calls too, which CodeInstruction wouldn't let me do

gleaming robin
#

Running into a new error this time. The only code that's new is the caching of the positions, and the IL error points to:

// for (int i = 0; i < outsideAINodes.Length; i++)```
Is FGOWT too slow for it to correctly get a length in time?
tulip vault
gleaming robin
#

so the game doesn't calculate the length until after the if statement is done? I thought that it'd be usable after assigning the new value to the array

tulip vault
#

or means if any of the three are true it runs

#

which means it checks all three

#

and if that first one is null..

gleaming robin
#

I do and don't understand. The code runs top-down right? So it shouldn't be null when it gets to the loop

tulip vault
#

but if it is null

#

your checking it's length and first index inside of it

#

in the actual if statement

#

i think

gleaming robin
tulip vault
#

im doubting myself a little now

gleaming robin
#

let me do a quick log test

tulip vault
#

is outsidenodePositions null?

gleaming robin
#

ohhh maybe that's it

#

but brb

gleaming robin
#

added outsideNodePositions = new Vector3[outsideAINodes.Length]; to the code and it was able to log all the positions

#

Code still isn't where I want it, but no more errors at least ๐Ÿ˜“

faint copper
#

if you use a transpiler, I think that CodeMatcher would just start at the beginning of the function, so you can immediately begin inserting instructions without calling MatchForward

gleaming robin
#

copy that

#

Like many programmers, I feel like my current code should be doing what I want, but somehow the initial position is still briefly getting thrown in as an actual position assignment

#
[Warning:Starlancer EnemyEscape] Original Position: (-35.09, -2.43, -11.97) //The position passed by the call to SDTP (baboonCampPosition)
[Warning:Starlancer EnemyEscape] Target position is outside, checking for reachable teleport.
[Warning:Starlancer EnemyEscape] Teleport found, changing destination to (-26.26, -224.23, 46.90).
[Warning:Starlancer EnemyEscape] Final Position: (-26.26, -224.23, 46.90) //The position of the teleport found by my code```
faint copper
gleaming robin
#

The hawk is still trying briefly to go towards the camp while its inside, which makes it path away from the teleport for half a sec, path towards for half a sec, ad infinitum

#

so it's like the destination is still being set to the camp somehow

faint copper
#

hmm

gleaming robin
faint copper
#

yeah your phrasing didn't really make it clear what was going on

gleaming robin
#

fair lol

faint copper
#

now might be a good time to install your mod into the asset rip project to see the AI debug gizmos

#

but if you look the decomp, it uses EnemyAI.destination in the EnemyAI.DoAIInterval() function in the base class, which means that it sets the destination before your code takes effect every interval

gleaming robin
#

oh dammit

faint copper
#

which means that if anything sets the destination to somewhere else in the meantime that'll take effect instead

#

it doesn't appear that anything else assigns to destination which would interfere with it, so it should work

#

you should see if the destination is being set to somewhere else

gleaming robin
#

alrighty
any specific gizmo that you're thinking of in the debug thing? bc I'm using Imperium which gives me a full navmeshpath drawn out and tells me the behaviour state

faint copper
#

as in it shows the agent's path? or does it listen for some EnemyAI function and generate a separate path to display?

#

if you were in the editor it would execute this

public virtual void OnDrawGizmos()
{
    if (base.IsOwner && debugEnemyAI)
    {
        Gizmos.DrawSphere(destination, 0.5f);
        Gizmos.DrawLine(base.transform.position, destination);
    }
}
#

I don't know what Imperium uses for that so I couldn't tell you whether that is a useful debugging tool

tulip vault
#

@faint copperbtw NavMeshAIPath or whatevs its called provides .corners[] which is an array of positions used for the path

#

which you can use for a mroe accurate debug path

faint copper
#

yeah, certainly

#

that could be used to debug what the actual agent is doing, but that above code is vanilla so it would have to be injected in there

gleaming robin
gleaming robin
# faint copper you should see if the destination is being set to somewhere else

Yea I'm gonna try setting the agent.destination in my thing as well. I wasn't expecting the final destination to change in this, since I didn't set it, but I figured I'd log it anyways lol

[Warning:Starlancer EnemyEscape] Original position argument: (-54.48, -3.85, -6.51)
[Warning:Starlancer EnemyEscape] Original destination: (-54.48, -3.85, -6.51)
[Warning:Starlancer EnemyEscape] Target position is outside, checking for reachable teleport.
[Warning:Starlancer EnemyEscape] Teleport found, changing destination to (2.11, -211.73, 65.89).
[Warning:Starlancer EnemyEscape] Final Position argument: (2.11, -211.73, 65.89)
[Warning:Starlancer EnemyEscape] Final destination: (-54.48, -3.85, -6.51)```
faint copper
#

I didn't mean agent.destination, I meant EnemyAI.destination

#

it'd be good to figure out why the AI isn't going where you expect, since it should be going to your overridden location after 200ms

#

the reason I say you should look at it in the editor is then you can see specifically whether EnemyAI.destination is changing

#

unless you want to look at the Imperium source code to figure out what path it's visualizing exactly

#

what I would want to verify is that EnemyAI.destination remains what you set it to until the next call to EnemyAI.DoAIInterval

gleaming robin
# faint copper I didn't mean agent.destination, I meant EnemyAI.destination

that's actually what I did instead, but still no luck, so I guess I'll try the editor

[Warning:Starlancer EnemyEscape] Original position argument: (22.27, -2.96, 37.38)
[Warning:Starlancer EnemyEscape] Original destination argument: (-113.19, 3.04, -17.65)
[Warning:Starlancer EnemyEscape] Final Position argument: (22.27, -2.96, 37.38)
[Warning:Starlancer EnemyEscape] Final destination argument: (-113.19, 3.04, -17.65)```
faint copper
#

wait what? then what were you doing before?

gleaming robin
gleaming robin
faint copper
#

you're being very confusing with the order in which you tried things and what exactly they did

#

pls post code for what you had before and/or now

gleaming robin
#

yea sorry, I'm throwing things at the wall and seeing what sticks lol

faint copper
#

it's very difficult to guess what's going on without knowing what precisely you're doing

#

there are so many ways it could do what you said without knowing that it's written the way I expect

gleaming robin
#

okay well the context of that last block is that the hawk was on its way to the main entrance teleport when it decided to switch to state 1 and then the position argument and enemy.destination ended up mismatching, so the code that was affecting the pathing was this
edit: This is in my escape component Update() triggered on a 1s interval

gleaming robin
# gleaming robin Yea I'm gonna try setting the agent.destination in my thing as well. I wasn't ex...

This block was created by this:

logger.LogWarning("SetDestinationToPositionPrefix is trying to do something!");
logger.LogWarning($"Original position argument: {position}");
logger.LogWarning($"Original destination argument: {__instance.destination}");
bool destinationInOtherArea = false;
bool teleportFound = false;
NavMeshPath pathToTeleport = new NavMeshPath();

if (!__instance.isOutside)
{
    foreach (Vector3 node in AIFix.insideNodePositions)
    {
        if (Vector3.Distance(node, position) < 10)
        {
            destinationInOtherArea = false;
            break;
        }
        else { destinationInOtherArea = true; }
    }
    if (destinationInOtherArea)
    {
        logger.LogWarning("Target position is outside, checking for reachable teleport.");
        float closestTeleportDistance = float.PositiveInfinity;
        foreach (EntranceTeleport teleport in insideTeleports)
        {
            NavMesh.CalculatePath(__instance.transform.position, teleport.transform.position, __instance.agent.areaMask, pathToTeleport);
            if (pathToTeleport.status != NavMeshPathStatus.PathComplete && Vector3.Distance(__instance.transform.position, teleport.transform.position) < closestTeleportDistance)
            {
                checkForPath = false;
                teleportFound = true;
                position = teleport.transform.position;
                __instance.destination = RoundManager.Instance.GetNavMeshPosition(position, RoundManager.Instance.navHit, -1f);
            }
        }
        if (teleportFound) { logger.LogWarning($"Teleport found, changing destination to {position}."); }
    }
}
logger.LogWarning($"Final Position argument: {position}");
logger.LogWarning($"Final destination argument: {__instance.destination}");```
faint copper
#

where are the methods' declarations?

gleaming robin
#

Sorry, edited that first thing to say it was my escape component's Update() on a 1s interval
Second thing is the SDTP prefix

faint copper
#

can you paste the prefix declaration?

gleaming robin
#
[HarmonyPatch(typeof(EnemyAI), nameof(EnemyAI.SetDestinationToPosition))]
[HarmonyPrefix]

private static void SetDestinationToPositionPrefix(EnemyAI __instance, Vector3 position, bool checkForPath)```
faint copper
#

you didn't make your position parameter ref, it won't modify the value in the caller

gleaming robin
#

I tried that before, but I'll try it again

#

alright reduce me to atoms

#

it's sort of working

#

I swear I tried the ref thing before, ya gotta believe me D:

faint copper
#

it might've had some other issue keeping it from working then

#

it definitely has to be ref, if it isn't ref it just modifies your local argument and then tosses it

gleaming robin
#

alright well the screwiness I currently have I know the reason for, so I'll report back in a few

#

there's still some optimizations to be done as well, but for now my goal is functioning

faint copper
#

I wanna say your check for whether the node is inside could sometimes fail since the destination could end up being further than 10 meters from a node, why not check which set of nodes contains the closest node as well as exiting early if one is within that distance?

#

not immediately urgent obviously but it would probably be good to keep that in mind

gleaming robin
#

yee, good idea

gleaming robin
#

ZAGGY

#

ZAGGYYYY

#

VIDEO INCOMING IN A MIN

tulip vault
#

oh god

#

adamance is gonna be hell

gleaming robin
#

I could cry rn

autumn panther
#

peak

gleaming robin
gleaming robin
#

I'm gonna go to bed now, and in the morning I'll do a brief test of the other enemies and make sure I didn't break any of them horrendously. From there I'll optimize/tidy up the code and then update \[T]/

#

and yes I have committed this to my local repo

turbid wave
#

Amazing dish chef, cooking well done โœ…

faint copper
faint copper
turbid wave
#

Although zaggy,, I thought we wanted to keep the original destination in EnemyAI.destination so that the enemy remembered where it was going even when it's not spamming EnemyAI.setDestinationToPosition()

#

And instead change the call that is in EnemyAI.DoAllInterval so that calling agent.SetDestination() would lead to the door

turbid wave
#

Or is there yet another location that saves our original destination?

faint copper
#

the logic that set the destination will re-run and set the same destination in almost all if not all cases, like I mentioned before, so that shouldn't be necessary

#

the AI interval function runs on an interval, it doesn't stop after it sets a destination

turbid wave
#

Oh so the call to SetDestinationToPosition() comes from doAllInterval()?

faint copper
#

that's where it's almost always called, yes

#

or in functions called from there

turbid wave
#

alright.
SetDestinationToPosition() seemed like the kind of function that an enemy mod would call once to order a pathing. And then doAllInterval would use the stored destination from EnemyAI to direct the agent

#

Which if there is a modded enemy that does it that way. It would get lost after returning to the intended area

faint copper
#

yeah, it's certainly possible for mods to do things differently

turbid wave
#

SetDestinationToPosition doesn't seem to be intended to be spammed every interval.

faint copper
#

what do you mean?

#

I saw no indication that it's not

turbid wave
#

I don't mean that it's too costly.
But it just sets the EnemyAI.destination
You'd expect that to be done to handle an event in the behavior. Like picking up scrap -> go to nest.
Then the EnemyAI can handle going there without reminders on every interval. Cuz the destination was stored in EnemyAI.destination

faint copper
#

there's no reason not to set it every frame, let alone every interval

#

it doesn't matter what it could be used for, it matters what it is used for

#

I don't know why Zeekers made it work this way, but it does

#

maybe there were issues with desync

turbid wave
faint copper
#

obviously we could store the previous destination to make an attempt to make it foolproof, but making a component for something to store that when we can't even test it seems a little pointless

turbid wave
faint copper
#

if there's no bug, we can't verify the fix

#

it's not "properly" if the agent already does no work when setting the destination to the same location

#

perhaps it would work fine to do it that way, but I don't see any big problem with how it works now

turbid wave
#

It's properly, because repeating the call is not required

faint copper
#

for one thing, that would mean you would have to write a bunch of code to verify that the asynchronously generated path was valid, and if it isn't, either keep spamming it or try a different one

#

I've observed agents that have no valid destination continue moving, which means they have a chance of getting unstuck

turbid wave
#

I'm just predicting a bug when a custom enemy doesn't spam that method. That's all

faint copper
#

but I don't know that there's any way to recover if you detect an issue with pathing, because there is probably a root cause that needs to be addressed before writing a bunch of code in the hopes that it will allow AI to leave a stuck state that they probably entered for unforseen reasons

#

what I'm saying is, why should we predict a bug and fix it when we can't even verify that it will work?

#

the baboon hawk immediately started pathing to its nest after leaving the interior, so there is no way to know if that happened because of a "bug fix" or because it already worked

#

if the default settings of the mod don't make any modded enemies path between zones then it's not necessarily "supported" to enable that

#

so until someone reports such an issue, it shouldn't be a concern

#

also @gleaming robin not sure if I missed it in the code before, but are you checking whether the enemies have an escape chance before letting them change zones?

turbid wave
turbid wave
faint copper
faint copper
#

all I'm saying is that reworking to fix some hypothetical bug is both a waste of time and a way to introduce more issues

turbid wave
#

Is the decompiled code for LC available somewhere?

faint copper
#

it's available in a decompiler, of which there are several

copper yew
#

you just need ILSpy or DNSpy

turbid wave
#

So that I don't need to setup the modding environment to look at that stuff

faint copper
#

most people don't use an asset rip for that

spiral ermine
#

wow

#

yall have been suffering over here havent u

twilit drift
#

you did it!!!!!!!

spiral ermine
#

HOLY SHIT

#

THATS HOT AS FUCK LETS GO!!!!!!!!

twilit drift
#

make yippees and baboons fight for their loot nests ๐Ÿคญ๐Ÿคญ

turbid wave
golden basin
#

They'd try to grab eachothers loot if encountered, and probably bug out

#

But won't attack eachother

#

I don't think zeekers intended for outside going inside and other way around

ruby bobcat
golden basin
ruby bobcat
#

Agreed

golden basin
#

It might just be that collision = damage

ruby bobcat
#

Ill try to force it by testing just to confirm

ruby bobcat
gleaming robin
turbid wave
#

hmm, shouldn't chance to escape be applied only when randomly deciding to go to the other area?
When they have a reason to path to the other area, failing this check would just break the ai.

#

and if chanceToEscape is 0 indeed then they shouldn't be in the wrong area to begin with...
Ah but they could've been spawned into the wrong area by the moon author

#

Maybe if they get spawned into the wrong area, then their nest is also in the wrong area?

#

so they don't try to change area anymore

gleaming robin
#

That's a fair point

#

I may have misunderstood the question, but my own statement still stands that I need to implement a check in case an enemy happens to wander close enough to the entrance/exit

golden basin
#

Btw since you're including it in this thread are you making it part of ai fix?

gleaming robin
#

It's dependent on AIFix

#

A rudimentary 1.0 is already out

gleaming robin
reef salmon
#

do old birds attack outside inside enemies?

gleaming robin
#

I don't think so. That's something I wanna do for the next big AIFix update tho

reef salmon
#

oh yeah they chill

#

spot the thumpers, camoflauged

gleaming robin
#

Alright so there's still a thing or two that needs ironing out, but I verified that the Hawk behavior is at least mostly consistent, AND a hoarding bug that leaves and finds scrap outside will absolutely take it back into the facility

reef salmon
gleaming robin
#

I'd have to hook into it directly, so idk if it'll be this next update, but yea I was planning on it.

reef salmon
#

very epic

rustic plume
#

enough

#

please update it

reef salmon
#

for some reason the individual enemy chance configs are gone

gleaming robin
gleaming robin
#

huh, that would imply that they all failed to register

#

did you have any errors?

rustic plume
#

thats why

#

or its a thunderstore issue

reef salmon
rustic plume
#

refresh the config page

reef salmon
rustic plume
#

delete the config

#

and open the game again

reef salmon
#

i did

#

didn't work

rustic plume
#

weird, for me it worked

rustic plume
#

release the update already @gleaming robin

#

๐Ÿ˜ญ

twilit drift
#

๐Ÿค”

faint copper
faint copper
# reef salmon gone

did that happen after deleting the config? BepInEx configs are not supposed to clear out unused config options

also, a log would be useful, I believe the hook to register them was in GameNetworkManager.Start() or something similarly required for the game to function

reef salmon
faint copper
#

gotcha, that makes sense

#

would've been good to know first of all if the game was actually functioning, and second of all whether the enemies' configs are registered when they spawned

copper yew
faint copper
#

because I believe Audio Knight put in a way for the enemy configs to be bound when they spawned as well (I hope)

gleaming robin
reef salmon
#

hmm, will enemyescape allow masked to exit through fire exits aswell instead of just main?

gleaming robin
#

They can't?

reef salmon
#

they can only go through main

#

also what in the world

#

when did the configs start existing

#

and why does only the flowerman one exist now

#

also i believe i've found an incompatibility between this and snatchinbracken, if the bracken drags you inside, the game still thinks the player is outside so you get outside lighting and the clock and stuff

ripe flax
#

does this mod effect enemy spawn rate?

gleaming robin
faint copper
faint copper
#

they'll need to override SetEnemyOutside and update the flag of the player when dragging them

faint copper
#

what do you mean by outside lighting then

reef salmon
#

the sunlight clips through the walls

faint copper
#

hmm lemme check something

#

wow, I didn't know the sun turned off when you entered the interior

#

but I suppose that makes sense considering that it has a non-shadowed light for indirect lighting contribution

faint copper
gleaming robin
faint copper
#

Snatchin Bracken does

#

you can't know that the bracken is teleporting a player, unless you make a soft dependency on their mod

gleaming robin
#

I will say convincing a bracken to leave the facility is harder than it would seem. I might have to transpile it lol

#

later tho

faint copper
#

trying to make it randomly do so you mean?

#

or I guess you mentioned making it leave if no players are inside

#

definitely seems like something you'd have to do a little IL injection for, yeah

gleaming robin
#

Yea it's glued to its favorite spot while no player is inside. I tried changing the favorite spot to a teleport, but that didn't seem to do anything. I'll work at it more later. For now...

#

โš™๏ธ โฌ†๏ธ

faint copper
#

hmm, are you sure it was trying to go to its favorite spot and not just finding the farthest node from the entrance? I forget which it does in that situation but it does like to do that

gleaming robin
#

oh maybe that's it

#

regardless lol

gleaming robin
#

Starlancer AIFix v3.6.0 | EnemyEscape v2.0.0

#
  • StarlancerAIFix v3.6.0

    • When finding the AI nodes in the level, AIFix now also caches their locations. This was done primarily for EnemyEscape, but it offers a non-zero performance boost for AIFix as well.
  • StarlancerEnemyEscape v2.0.0

    • Almost a complete overhaul from v1.0.0
    • Instead of only allowing enemies a chance to use the EntranceTeleports if they happen to wander close enough, a system has been implemented to make an enemy path directly to an EntranceTeleport and warp to its match on the other half of the level. The range at which they may search for a teleport and the cooldown for an attempted search are configurable per enemy.
      • "Chance To Escape" now represents the chance for them to initiate pathing to the teleport.
    • Implemented a HarmonyPrefix to EnemyAI.SetDestinationToPosition() in order to prevent enemies from getting stuck if the position they want to path to is on the other half of the level.
#

AIFix's source code is so tiny in comparison to EnemyEscape ๐Ÿ˜…

#

Don't Touch Me is compatible now btw. I don't know if it can actually move, but at least it doesn't break EnemyEscape now ๐Ÿคญ
@spice kindle @tired ore @edgy topaz

gleaming robin
#

you get to see how my fevered mind codes

copper yew
edgy topaz
#

Yippie

tired ore
dusty jewel
golden basin
#

Last I checked I think he did it based on their behaviour, which is when they're not chasing you, but I could be wrong it's been a while + I can imagine them chasing, you leaving, losing aggro, then they leave, see u again, chase again

gleaming robin
spiral ermine
#

Yall have done it

#

Truly the opps can catch you lackin anywhere

gleaming robin
short lagoon
gleaming robin
short lagoon
#

Nice!!

#

Dude Imma start using this

#

The fiend outside would be so mortifying

gleaming robin
#

every enemy is registered by my mod, just some like the lassoman and red pill unimplemented vanilla enemies are blacklisted

#

even though in my code I was silly and called it a whitelist lol

short lagoon
#

So we can put monsters on a blacklist and whitelist too

#

I fucking love this

gleaming robin
short lagoon
#

Real

gleaming robin
#

the interior/exterior range and cooldown time are also configurable per enemy

short lagoon
#

Ooooo

gleaming robin
#

read the readme ๐Ÿคญ

short lagoon
#

I will

reef salmon
#

my config is still broken with 2.0.0 ๐Ÿฅน give me my config back

reef salmon
#

also, how does chance to escape work? like, when does it roll the chance?

golden basin
#

When I saw it being discuss, it was a dice roll once every cooldown of 10 seconds when it qualifies

reef salmon
#

every 10 seconds is actually pretty frequently

gleaming robin
#

pathing range and cooldown is configurable per enemy

#

try deleting your config

#

I've tested multiple times and it should actually re-check the config each load

#

idk why yours is busted

reef salmon
gleaming robin
#

go into your profile, go into the bepinex folder, into the config folder, and make sure it's absolutely gone

#

actually

#

what it looks like is something is preventing it from registering

#

are you getting an error after you click into the main menu?

#

or do you have a fast startup mod on?

reef salmon
reef salmon
#
[16:22:52.0191936] [Info   :Starlancer EnemyEscape] Starlancer is allowing enemies to roam.```
these are the only two logs that mention enemyescape
gleaming robin
#

nothing errors after clicking Online?

reef salmon
gleaming robin
#

send me your profile code

rustic plume
#

a enemy need to escape only once to the full config appear

gleaming robin
#

nope, it registers all enemies after clicking Online

rustic plume
#

then idk

reef salmon
gleaming robin
#

I've tested it with LLL v50 and it worked

#

well

#

lemme test it with a deleted config and make sure it regens

#

I just made sure it didn't crash ๐Ÿคญ

gleaming robin
#

well it's not LLL at least

#

gonna test your profile

rustic plume
#

@gleaming robin

#

yo

#

the hoarding bug isn't giving any damage to non-hosts

#

(only the host is with the mod)

reef salmon
#

hm, that's an issue i know lethalescape had (hoarding bugs only deal damage to host)

gleaming robin
#

why does only the host have it? I never said anything about it being server-side only

rustic plume
#

and this is really similiar

gleaming robin
#

that's a bold assumption to make

rustic plume
#

well, you can add support to serverside

gleaming robin
#

no

#

just have everyone download it

#

and if the issue persists I'll look at it

rustic plume
#

k, i'm gonna stay with lethal escape then

gleaming robin
#

okay bye

reef salmon
rustic plume
#

lethal escape is serverside

#

and it takes the ai ownership

reef salmon
#

lethal escape just teleports enemies around

#

it doesn't modify ai

rustic plume
#

k

rustic plume
gleaming robin
# reef salmon it doesn't modify ai

ehhh yes and no, it does set them outside, which is why AIFix never really touched it, but I can't say for sure exactly if/how it modifies things

rustic plume
#

the mod is semi-serverside

short lagoon
#

I thought it was host only

rustic plume
#

it just doesn't give damage

gleaming robin
#

regardless, unless a mod author says explicitly that a mod works when only the host has it, I wouldn't assume that it is. It's a 30kb file, there is no harm in everyone having it

rustic plume
gleaming robin
#

goodbye then

#

You've been very impolite here

rustic plume
#

cya

gleaming robin
#

and I'm rather tired of you making demands and telling people you know what my mod does as factual information

short lagoon
#

Send him to the backrooms

gleaming robin
short lagoon
#

Understandable

#

Im naturally not a patient person but Iโ€™ve been trying really hard

#

Iโ€™ve been doing pretty good

#

Simply asking questions about mods so I can know itโ€™s progress

#

It helps a lot

#

Youโ€™re doin great Audio Knight

#

Donโ€™t worry bout them haters

spiral ermine
#

Dickhead on my block?

short lagoon
spiral ermine
#

What we don't tolerate around here is unneeded rudeness to devs like Knight

short lagoon
#

Yeah

stiff pulsar
#

Here if you need me beevil

tired ore
#

bailiff, whack his pp

gleaming robin
#

@reef salmon
try disabling Rosie's moons

#

_>

reef salmon
gleaming robin
#

@ruby bobcat WALL TURR- I mean, your moons break my mod

reef salmon
#

i can only guess it's because of the fancy subspecies or whatever (i think rosie's moons has those), but i think those are just texture changes to map prefabs

gleaming robin
#

I think it must be her custom enemy types which are modifications of vanilla enemies?

#

ye

#

at least we found the issue

reef salmon
#

why would a texture change to a prefab break it though

gleaming robin
#

it's something in the scriptable object I think, but we'll wait for her before further speculation

reef salmon
#

ohh i see how it works, every 10 seconds while they're in a RANGE of the entrance, not always

#

that makes alot more sense

#

oh wait no cooldown time depends on enemy

#

it can vary!

gleaming robin
#

Yep! Fully configurable

#

@turbid wave I forgot to tag you, but you'll be happy to hear that it's all config'd

reef salmon
#

default 0 chance for mouthdogs to go inside, cowardice

gleaming robin
#

only because they're a one-hit-kill lol

#

but that's the beauty of a config, you can jack it up to 100 and give it 9999 units of pathfinding range

reef salmon
#

actually if a bracken is chasing someone but in range of teleport detection, will it go for the player or the teleport?

gleaming robin
#

bracken doesn't do random pathfinding yet

#

player

#

you should read the readme for the known issues btw

#

(everyone should read the readme)

stiff pulsar
#

It's in the name

gleaming robin
#

?

stiff pulsar
#

Read me!

gleaming robin
#

oh

#

yee

#

I'll have to transpile the bracken code to get it to allow EnemyEscape to tell it what to do

#

it's real stubborn

reef salmon
gleaming robin
#

exactly what it says on the tin

#

it can still potentially follow you out

#

but like I said, it's stubborn

reef salmon
#

oh if it rolls the chance even with 9999 range, it'll just not go there?

#

is that what it means?

gleaming robin
#

currently, my code just ignores Bracken for the random pathfinding, before the diceroll, to not waste any computational effort on it
bc even when I tell it to do something, it doesn't

reef salmon
#

then transpile it! i want the bracken to be a fiend

gleaming robin
reef salmon
#

now!!!!

gleaming robin
#

I just spent 4 days on this, lemme rest ๐Ÿ˜ญ

reef salmon
gleaming robin
#

alternatively

reef salmon
#

also from what i've heard, hoarding bugs and baboon hawks, if they go inside/outside, they bring stuff to their respective nests?

gleaming robin
#

yep

reef salmon
#

so it goes

gleaming robin
#

vid there of a hawk doing such

reef salmon
#

i like how he sleeps at the end of the clip, job well done

#

will you perhaps consider modifying the snare flea code to make them actually work outside?

gleaming robin
#

Wdym? In what way do they not work?

faint copper
#

also if the author of LethalEscape said only the host needed it that's putting a lot of faith in the networking code of LC lmao

#

all enemies that I've looked at in the code deal damage on the client side based on the target they have chosen there, so if the target is mismatched between host and client they become useless

gleaming robin
faint copper
#

ohh

#

that makes sense, I guess that's partly my sleepy brain's fault

gleaming robin
#

Lol no worries. The description of each enemy config escape chance says it again in what might be slightly clearer terms

turbid wave
#

Oh one thing that didn't end up being configurable. I wanted to give enemies a higher chance of return than that of escape. And currently there is a single value for both.

#

You deserve a rest tho. Job well done with this mod. You can just stash that suggestion for later.

gleaming robin
golden basin
opal bane
#

[19:12:13.6619438] [Error : Unity Log] NullReferenceException: Object reference not set to an instance of an object
Stack trace:
EnemyEscape.StarlancerEscapeComponent.BindRegisteredEnemies () (at <a5a07d1df715484e849d3d540a9d2ca0>:IL_0016)
(wrapper dynamic-method) GameNetworkManager.DMDGameNetworkManager::Start(GameNetworkManager)
(wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.TrampolineGameNetworkManager::Start?-1330587096(GameNetworkManager)
LethalLib.Modules.NetworkPrefabs.GameNetworkManager_Start (On.GameNetworkManager+orig_Start orig, GameNetworkManager self) (at <c68aa40cbbae4bd69cd2fcd50c8f1ae1>:IL_0000)
(wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.HookGameNetworkManager::Start?1654550456(GameNetworkManager)
(wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.TrampolineGameNetworkManager::Start?-367031872(GameNetworkManager)
LethalBestiary.Modules.NetworkPrefabs.GameNetworkManager_Start (On.GameNetworkManager+orig_Start orig, GameNetworkManager self) (at <0d184de3cadb4a4a9b8ad443576d9910>:IL_0000)
(wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.HookGameNetworkManager::Start?942834480(GameNetworkManager)

[19:12:13.6619438] [Error : Unity Log] NullReferenceException: Object reference not set to an instance of an object
Stack trace:
EnemyEscape.StarlancerEscapeComponent.BindRegisteredEnemies () (at <a5a07d1df715484e849d3d540a9d2ca0>:IL_0016)
(wrapper dynamic-method) GameNetworkManager.DMDGameNetworkManager::Start(GameNetworkManager)
(wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.TrampolineGameNetworkManager::Start?-1330587096(GameNetworkManager)
LethalLib.Modules.NetworkPrefabs.GameNetworkManager_Start (On.GameNetworkManager+orig_Start orig, GameNetworkManager self) (at <c68aa40cbbae4bd69cd2fcd50c8f1ae1>:IL_0000)
(wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.HookGameNetworkManager::Start?1654550456(GameNetworkManager)
(wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.TrampolineGameNetworkManager::Start?-367031872(GameNetworkManager)
LethalBestiary.Modules.NetworkPrefabs.GameNetworkManager_Start (On.GameNetworkManager+orig_Start orig, GameNetworkManager self) (at <0d184de3cadb4a4a9b8ad443576d9910>:IL_0000)
(wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.HookGameNetworkManager::Start?942834480(GameNetworkManager)

ruby bobcat
#

Like what even broke?

gleaming robin
gleaming robin
opal bane