winstxnhdw / lc-hax

A powerful, feature-rich and highly performant internal cheat for the co-op indie horror video game, Lethal Company.
77 stars 25 forks source link

Add Enemy Control #352

Closed xAstroBoy closed 4 months ago

xAstroBoy commented 4 months ago

Closes #250

winstxnhdw commented 4 months ago

Oh dear, there's a lot more problems than I thought.

    public EnemyIdentity IdentifyEnemy(EnemyAI enemy) {
        return enemy is null
            ? EnemyIdentity.None
            : enemy switch {
                CentipedeAI => EnemyIdentity.Centipede,
                BaboonBirdAI => EnemyIdentity.Baboon,
                FlowermanAI => EnemyIdentity.Flowerman,
                ForestGiantAI => EnemyIdentity.ForestGiant,
                HoarderBugAI => EnemyIdentity.HoarderBug,
                JesterAI => EnemyIdentity.Jester,
                MaskedPlayerEnemy => EnemyIdentity.Masked,
                MouthDogAI => EnemyIdentity.MouthDog,
                NutcrackerEnemyAI => EnemyIdentity.Nutcracker,
                PufferAI => EnemyIdentity.Puffer,
                SandWormAI => EnemyIdentity.Sandworm,
                SpringManAI => EnemyIdentity.Springman,
                _ => EnemyIdentity.Default,
            };
    }

Why did you not just return the enemy instead? You are making a lot of unnecessary indirections, which you later have to indirect back again.

Please be patient while I fix this.

xAstroBoy commented 4 months ago

Oh dear, there's a lot more problems than I thought.

    public EnemyIdentity IdentifyEnemy(EnemyAI enemy) {
        return enemy is null
            ? EnemyIdentity.None
            : enemy switch {
                CentipedeAI => EnemyIdentity.Centipede,
                BaboonBirdAI => EnemyIdentity.Baboon,
                FlowermanAI => EnemyIdentity.Flowerman,
                ForestGiantAI => EnemyIdentity.ForestGiant,
                HoarderBugAI => EnemyIdentity.HoarderBug,
                JesterAI => EnemyIdentity.Jester,
                MaskedPlayerEnemy => EnemyIdentity.Masked,
                MouthDogAI => EnemyIdentity.MouthDog,
                NutcrackerEnemyAI => EnemyIdentity.Nutcracker,
                PufferAI => EnemyIdentity.Puffer,
                SandWormAI => EnemyIdentity.Sandworm,
                SpringManAI => EnemyIdentity.Springman,
                _ => EnemyIdentity.Default,
            };
    }

Why did you not just return the enemy instead? You are making a lot of unnecessary indirections, which you later have to indirect back again.

Please be patient while I fix this.

that is how to identify which enemy class is to specifically control without casting the enemy over and over?

winstxnhdw commented 4 months ago

You could probably just do this.

T IdentifyEnemy(EnemyAI enemy) where T : EnemyAI {
   ...
}

without casting the enemy over and over?

Also, you are doing a lot of casting anyways.

            case EnemyIdentity.Centipede:
                return ((CentipedeAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.Baboon:
                return ((BaboonBirdAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.HoarderBug:
                return ((HoarderBugAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.Jester:
                return ((JesterAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.Nutcracker:
                return ((NutcrackerEnemyAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.Puffer:
                return ((PufferAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.Sandworm:
                return ((SandWormAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.MouthDog:
                return ((MouthDogAI)this.EnemyToPossess).GetSecondarySkillName();
winstxnhdw commented 4 months ago

I am still looking really superficially. Give me a while to dig into it.

winstxnhdw commented 4 months ago

It would've probably been better to use a IController interface since every extension has a common method. We'll see.

xAstroBoy commented 4 months ago

You could probably just do this.

T IdentifyEnemy(EnemyAI enemy) where T : EnemyAI {
   ...
}

without casting the enemy over and over?

Also, you are doing a lot of casting anyways.

            case EnemyIdentity.Centipede:
                return ((CentipedeAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.Baboon:
                return ((BaboonBirdAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.HoarderBug:
                return ((HoarderBugAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.Jester:
                return ((JesterAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.Nutcracker:
                return ((NutcrackerEnemyAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.Puffer:
                return ((PufferAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.Sandworm:
                return ((SandWormAI)this.EnemyToPossess).GetSecondarySkillName();
            case EnemyIdentity.MouthDog:
                return ((MouthDogAI)this.EnemyToPossess).GetSecondarySkillName();

yes, but the switch case casts only based off which enemy is, and the enum acts as a identity

winstxnhdw commented 4 months ago

Have you seen IEnemyPrompter?

xAstroBoy commented 4 months ago

Have you seen IEnemyPrompter?

nvm

xAstroBoy commented 4 months ago

@winstxnhdw another issue is seconday attack while you are dead and controlling a enemy you risk of starting a vote for the ship to leave early

winstxnhdw commented 4 months ago

@winstxnhdw another issue is seconday attack while you are dead and controlling a enemy you risk of starting a vote for the ship to leave early

Good catch. We can patch that out.

xAstroBoy commented 4 months ago

@winstxnhdw i want to use Update() and add a better primary and secondary since with your input system sometimes it fails and it does not hold on the attacks. Also a way to set the enemy walk/run speed would be handy as well, since controlcompany does that to make it as natural as possible.

winstxnhdw commented 4 months ago

i want to use Update() and add a better primary and secondary since with your input system sometimes it fails and it does not hold on the attacks.

Can you tell me how it fails? You might be implementing it wrongly. Hold commands must be set within the update loop like this.

    void Update() {
        InputListener.onShiftButtonHold?.Invoke(Keyboard.current[Key.LeftShift].isPressed);
        InputListener.onFButtonHold?.Invoke(Keyboard.current[Key.F].isPressed);
        InputListener.onRButtonHold?.Invoke(Keyboard.current[Key.R].isPressed);
        InputListener.onEButtonHold?.Invoke(Keyboard.current[Key.E].isPressed);

        foreach ((Func<bool> keyPressed, Action eventAction) in this.InputActions) {
            if (!keyPressed()) continue;
            eventAction();
        }
    }
xAstroBoy commented 4 months ago

i want to use Update() and add a better primary and secondary since with your input system sometimes it fails and it does not hold on the attacks.

Can you tell me how it fails? You might be implementing it wrongly. Hold commands must be set within the update loop like this.

    void Update() {
        InputListener.onShiftButtonHold?.Invoke(Keyboard.current[Key.LeftShift].isPressed);
        InputListener.onFButtonHold?.Invoke(Keyboard.current[Key.F].isPressed);
        InputListener.onRButtonHold?.Invoke(Keyboard.current[Key.R].isPressed);
        InputListener.onEButtonHold?.Invoke(Keyboard.current[Key.E].isPressed);

        foreach ((Func<bool> keyPressed, Action eventAction) in this.InputActions) {
            if (!keyPressed()) continue;
            eventAction();
        }
    }

yeah, the secondary needs to be a hold attack, because you need to keep pressing the button to make the jester crank and when it pops out the secondary has to be released, ill investigate better, but im not sure about that, as well we need to disable ping scan until we stop possessing the enemy

winstxnhdw commented 4 months ago

Also a way to set the enemy walk/run speed would be handy as well, since controlcompany does that to make it as natural as possible.

Would subscribing to the onLeftBracketPress and onRightBracketPress events to change speed work?

xAstroBoy commented 4 months ago

Also a way to set the enemy walk/run speed would be handy as well, since controlcompany does that to make it as natural as possible.

Would subscribing to the onLeftBracketPress and onRightBracketPress events to change speed work?

No need, we need just to change the jester speed to make it faster when it pops out, not all enemies needs that. same for coilhead when pressing shift it needs to play it's accelerated animation

winstxnhdw commented 4 months ago

yeah, the secondary needs to be a hold attack, because you need to keep pressing the button to make the jester crank and when it pops out the secondary has to be released, ill investigate better, but im not sure about that, as well we need to disable ping scan until we stop possessing the enemy

Yup so what you need to do is something like this.

    public static event Action? onLeftButtonHold;

    void Update() {
        InputListener.onShiftButtonHold?.Invoke(Keyboard.current[Key.LeftShift].isPressed);
        InputListener.onFButtonHold?.Invoke(Keyboard.current[Key.F].isPressed);
        InputListener.onRButtonHold?.Invoke(Keyboard.current[Key.R].isPressed);
        InputListener.onEButtonHold?.Invoke(Keyboard.current[Key.E].isPressed);
        InputListener.onLeftButtonHold?.Invoke(Mouse.current.leftButton.isPressed);

        foreach ((Func<bool> keyPressed, Action eventAction) in this.InputActions) {
            if (!keyPressed()) continue;
            eventAction();
        }
    }
winstxnhdw commented 4 months ago

No need, we need just to change the jester speed to make it faster when it pops out, not all enemies needs that. same for coilhead when pressing shift it needs to play it's accelerated animation

Can't we do this programmatically? Like if jester is not in box -> set enemy speed to higher?

xAstroBoy commented 4 months ago

yeah, the secondary needs to be a hold attack, because you need to keep pressing the button to make the jester crank and when it pops out the secondary has to be released, ill investigate better, but im not sure about that, as well we need to disable ping scan until we stop possessing the enemy

Yup so what you need to do is something like this.

    public static event Action? onLeftButtonHold;

    void Update() {
        InputListener.onShiftButtonHold?.Invoke(Keyboard.current[Key.LeftShift].isPressed);
        InputListener.onFButtonHold?.Invoke(Keyboard.current[Key.F].isPressed);
        InputListener.onRButtonHold?.Invoke(Keyboard.current[Key.R].isPressed);
        InputListener.onEButtonHold?.Invoke(Keyboard.current[Key.E].isPressed);
        InputListener.onLeftButtonHold?.Invoke(Mouse.current.leftButton.isPressed);

        foreach ((Func<bool> keyPressed, Action eventAction) in this.InputActions) {
            if (!keyPressed()) continue;
            eventAction();
        }
    }

Go ahead and apply it, im working to figure how to disable the ping scan input until we stop the enemy posession.

xAstroBoy commented 4 months ago

No need, we need just to change the jester speed to make it faster when it pops out, not all enemies needs that. same for coilhead when pressing shift it needs to play it's accelerated animation

Can't we do this programmatically? Like if jester is not in box -> set enemy speed to higher?

Exactly , is just that i cant figure how to do it with the possession mod. EDIT : a way to block rotation would be handy as well since some enemies are not supposed to rotate when are frozen lol

winstxnhdw commented 4 months ago

No need, we need just to change the jester speed to make it faster when it pops out, not all enemies needs that. same for coilhead when pressing shift it needs to play it's accelerated animation

Can't we do this programmatically? Like if jester is not in box -> set enemy speed to higher?

Exactly , is just that i cant figure how to do it with the possession mod.

Something like this?

    public static void ReleaseSecondarySkill(this JesterAI instance) {
        if (!instance.IsInState(JesterState.Cranking)) return;
        instance.SetState(JesterState.PopOut);
        PossessionMod.Instance.RigidbodyKeyboard.baseSpeed = 999.9f;
    }

EDIT : a way to block rotation would be handy as well since some enemies are not supposed to rotate when are frozen lol

Since we are using a rigidbody controller, IIRC there's a boolean we can flip to disable rotations.

xAstroBoy commented 4 months ago

No need, we need just to change the jester speed to make it faster when it pops out, not all enemies needs that. same for coilhead when pressing shift it needs to play it's accelerated animation

Can't we do this programmatically? Like if jester is not in box -> set enemy speed to higher?

Exactly , is just that i cant figure how to do it with the possession mod.

Something like this?

    public static void ReleaseSecondarySkill(this JesterAI instance) {
        if (!instance.IsInState(JesterState.Cranking)) return;
        instance.SetState(JesterState.PopOut);
        PossessionMod.Instance.RigidbodyKeyboard.baseSpeed = 999.9f;
    }

EDIT : a way to block rotation would be handy as well since some enemies are not supposed to rotate when are frozen lol

Since we are using a rigidbody controller, IIRC there's a boolean we can flip to disable rotations.

you can check how controlcompany by reading the dll with dnspy does, they set the PlayerControl speed by multiplying it by 16, and yeah 999.9f is over exagerated.

D1GQ commented 4 months ago

Possibly add a Patch that disables the UI when in possession.

xAstroBoy commented 4 months ago

Possibly add a Patch that disables the UI when in possession.

already did by disabling the early vote hud.

xAstroBoy commented 4 months ago

@winstxnhdw so far it can be merged, i fixed the issue with grabbing scrap and now we can control both Hoarding Bug and the monkey to grab the items and drop them without any issues.

winstxnhdw commented 4 months ago

I think @D1GQ wants all the HUDs to be disabled during possession?

xAstroBoy commented 4 months ago

I think @D1GQ wants all the HUDs to be disabled during possession?

that can be added later, but the main functionality is completed.

xAstroBoy commented 4 months ago

@winstxnhdw everything is ready to be merged, only spring's control needs to be actually implemented to make it walk and not slide.

winstxnhdw commented 4 months ago

Huh? What happened to all my changes?

winstxnhdw commented 4 months ago

Okay, nvm. Some are still there. I'll finish the rest tomorrow.

Totoqoe commented 4 months ago

There's a bug where if an enemy despawns or you get kicked while possessing it, then you're unable to to possess any other enemies until you restart.

D1GQ commented 4 months ago

There's also issue where the door gets absolutely spammed trying to open and close it as an enemy. And it seems disabling The death HUD doesn't work when possessing.

xAstroBoy commented 4 months ago

@winstxnhdw a health checker to see if a possessed enemy is dead to eject PossessionMod and a way to give AI the enemy control and override it would be nice. Haven't figured how to fix the door issue .

winstxnhdw commented 4 months ago

AI the enemy control and override it would be nice

What?

winstxnhdw commented 4 months ago

Haven't figured how to fix the door issue .

Just use TriggerMod interact??

xAstroBoy commented 4 months ago

Haven't figured how to fix the door issue .

Just use TriggerMod interact??

yeah triggermod can do it, but when you are dead is not working to open/close doors.

winstxnhdw commented 4 months ago

Nope, it still works.

xAstroBoy commented 4 months ago

Nope, it still works.

not dedicated to the enemy itself tho? for example if you possess a enemy and you want to make it leave the facility that wouldn't work.

winstxnhdw commented 4 months ago

Oh, you mean the entrances? Honestly, there is nothing wrong with noclipping outside. Let's figure it out next time.

winstxnhdw commented 4 months ago

I just spent 7 hours on this. This is the last time I will be accepting a PR of such dubious quality. The next time I see a PR that will take more more than 2 hours to fix, I am just closing it. I can't waste so much time anymore.