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

use external camera #419

Open xAstroBoy opened 4 months ago

xAstroBoy commented 4 months ago

Fix Enemies Nodes not updating Fix spider mesh not following the current position & rotation when not controlling, the spider will use that last position and claim it as it's nest. add LcHaxCamera and make all mods use that one on phantom mode. add isDead()

add OnOutsideStatusChange to reset all enemies's NavMesh's searches to not be broken when AI Nodes update.

@winstxnhdw all done.

Summary by CodeRabbit

xAstroBoy commented 4 months ago

@winstxnhdw looks solid to me, i have been testing these past 3 hours and got this custom camera to work and preserve hud and the rest, enemies work properly when they go outside, and best of all spider now follows the controller .

xAstroBoy commented 4 months ago

@winstxnhdw with the jester i had to add a new Event , OnPlayerCollision , because for some weird reason the jester wouldn't kill a player if outside, so i fixed that.

EDIT : i checked the other enemies that are inside and replicated their collision system without a check to make sure they work

D1GQ commented 4 months ago

Did a small overhaul on how the camera works for the possessed enemy!

Changes:

Note:

Make sure to add this to the InputListener: internal static event Action<bool>? OnLeftAltButtonHold; InputListener.OnLeftAltButtonHold?.Invoke(Keyboard.current[Key.LeftAlt].isPressed);

PossessionMod

using System;
using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;
using UnityEngine.AI;
using GameNetcodeStuff;
using Hax;

internal sealed class PossessionMod : MonoBehaviour {
    const float TeleportDoorCooldown = 2.5f;
    const float DoorInteractionCooldown = 0.7f;
    bool IsLeftAltHeld { get; set; } = false;

    internal static PossessionMod? Instance { get; private set; }
    internal bool IsPossessed => this.Possession.IsPossessed;
    internal EnemyAI? PossessedEnemy => this.Possession.Enemy;

    Possession Possession { get; } = new();
    GameObject? CharacterMovementInstance { get; set; } = null;
    CharacterMovement? CharacterMovement { get; set; } = null;
    MousePan? MousePan { get; set; } = null;

    bool FirstUpdate { get; set; } = true;
    bool NoClipEnabled { get; set; } = false;
    bool IsAIControlled { get; set; } = false;

    Dictionary<Type, IController> EnemyControllers { get; } = new() {
        { typeof(CentipedeAI), new SnareFleaController() },
        { typeof(FlowermanAI), new BrackenController() },
        { typeof(ForestGiantAI), new ForestGiantController() },
        { typeof(HoarderBugAI), new HoardingBugController() },
        { typeof(JesterAI), new JesterController() },
        { typeof(NutcrackerEnemyAI), new NutcrackerController() },
        { typeof(PufferAI), new SporeLizardController() },
        { typeof(BaboonBirdAI), new BaboonHawkController() },
        { typeof(SandWormAI), new EarthLeviathanController() },
        { typeof(MouthDogAI), new EyelessDogController() },
        { typeof(MaskedPlayerEnemy), new MaskedPlayerController() },
        { typeof(SpringManAI), new CoilHeadController() },
        { typeof(BlobAI), new HygrodereController() },
        { typeof(TestEnemy), new TestEnemyController() },
        { typeof(LassoManAI), new LassoManController() },
        { typeof(CrawlerAI), new CrawlerController() },
        { typeof(SandSpiderAI), new SandSpiderController() },
        { typeof(RedLocustBees), new CircuitBeesController() }
    };

    float DoorCooldownRemaining { get; set; } = 0.0f;
    float TeleportCooldownRemaining { get; set; } = 0.0f;
    EntranceTeleport? MainEntrance { get; set; }

    void Awake() {
        this.InitCharacterMovement();
        this.MousePan = this.gameObject.AddComponent<MousePan>();
        this.enabled = false;

        PossessionMod.Instance = this;
    }

    void InitCharacterMovement(EnemyAI? enemy = null) {
        this.CharacterMovementInstance = new GameObject("Hax CharacterMovement");
        this.CharacterMovementInstance.transform.position = enemy is null ? default : enemy.transform.position;
        this.CharacterMovement = this.CharacterMovementInstance.AddComponent<CharacterMovement>();
        this.CharacterMovement.Init();

        if (enemy is not null) {
            this.CharacterMovement.CalibrateCollision(enemy);
            this.CharacterMovement.CharacterSprintSpeed = this.SprintMultiplier(enemy);
        }
    }

    void OnEnable() {
        InputListener.OnNPress += this.ToggleNoClip;
        InputListener.OnZPress += this.Unpossess;
        InputListener.OnLeftButtonPress += this.UsePrimarySkill;
        InputListener.OnRightButtonPress += this.UseSecondarySkill;
        InputListener.OnRightButtonRelease += this.ReleaseSecondarySkill;
        InputListener.OnRightButtonHold += this.OnRightMouseButtonHold;
        InputListener.OnDelPress += this.KillEnemyAndUnposses;
        InputListener.OnF9Press += this.ToggleAIControl;
        InputListener.OnLeftAltButtonHold += this.HoldAlt;
        this.UpdateComponentsOnCurrentState(true);
    }

    void OnDisable() {
        InputListener.OnNPress -= this.ToggleNoClip;
        InputListener.OnZPress -= this.Unpossess;
        InputListener.OnLeftButtonPress -= this.UsePrimarySkill;
        InputListener.OnRightButtonPress -= this.UseSecondarySkill;
        InputListener.OnRightButtonRelease -= this.ReleaseSecondarySkill;
        InputListener.OnRightButtonHold -= this.OnRightMouseButtonHold;
        InputListener.OnDelPress -= this.KillEnemyAndUnposses;
        InputListener.OnF9Press -= this.ToggleAIControl;
        InputListener.OnLeftAltButtonHold -= this.HoldAlt;
        this.UpdateComponentsOnCurrentState(false);
    }

    void HoldAlt(bool isHeld) => this.IsLeftAltHeld = isHeld;

    void OnRightMouseButtonHold(bool isPressed) {
        if (isPressed) {
            this.OnSecondarySkillHold();
        }
    }

    void SendPossessionNotifcation(string message) {
        Helper.SendNotification(
            title: "Possession",
            body: message
        );
    }

    void ToggleNoClip() {
        this.NoClipEnabled = !this.NoClipEnabled;
        this.UpdateComponentsOnCurrentState(this.enabled);
        this.SendPossessionNotifcation($"NoClip: {(this.NoClipEnabled ? "Enabled" : "Disabled")}");
    }

    void UpdateComponentsOnCurrentState(bool thisGameObjectIsEnabled) {
        if (this.MousePan is null) return;

        this.MousePan.enabled = thisGameObjectIsEnabled;
        this.CharacterMovement?.gameObject.SetActive(thisGameObjectIsEnabled);
        this.CharacterMovement?.SetNoClipMode(this.NoClipEnabled);
    }

    void Update() {
        if (Helper.CurrentCamera is not Camera { enabled: true } camera) return;
        if (Helper.LocalPlayer is not PlayerControllerB localPlayer) return;
        if (this.CharacterMovement is not CharacterMovement characterMovement) return;
        if (this.Possession.Enemy is not EnemyAI enemy) return;
        if (enemy.agent is not NavMeshAgent agent) return;

        this.DoorCooldownRemaining = Mathf.Clamp(
            this.DoorCooldownRemaining - Time.deltaTime,
            0.0f,
            PossessionMod.DoorInteractionCooldown
        );

        this.TeleportCooldownRemaining = Mathf.Clamp(
            this.TeleportCooldownRemaining - Time.deltaTime,
            0.0f,
            PossessionMod.TeleportDoorCooldown
        );

        enemy.ChangeEnemyOwnerServerRpc(localPlayer.actualClientId);
        this.UpdateCameraPosition(camera, enemy);
        this.UpdateCameraRotation(camera, enemy);

        if (this.FirstUpdate) {
            this.FirstUpdate = false;
            this.InitCharacterMovement(enemy);
            this.UpdateComponentsOnCurrentState(true);
            this.SetAIControl(false);
            this.MainEntrance = RoundManager.FindMainEntranceScript(false);
        }

        if (!this.EnemyControllers.TryGetValue(enemy.GetType(), out IController controller)) {
            if (!this.IsAIControlled) {
                this.UpdateEnemyPosition(enemy);
                this.UpdateEnemyRotation();
            }

            if (this.MainEntrance != null) {
                _ = enemy.SetOutsideStatus(enemy.transform.position.y > this.MainEntrance.transform.position.y + 5.0f);
            }

            if (enemy.isEnemyDead) {
                this.Unpossess();
            }

            this.InteractWithAmbient(enemy, null);
            return;
        }

        if (this.MainEntrance != null) {
            if (enemy.SetOutsideStatus(enemy.transform.position.y > this.MainEntrance.transform.position.y + 5.0f)) {
                controller.OnOutsideStatusChange(enemy);
                enemy.FinishedCurrentSearchRoutine();
            }
        }

        if (enemy.isEnemyDead) {
            controller.OnDeath(enemy);
            this.Unpossess();
        }

        controller.Update(enemy, this.IsAIControlled);
        this.InteractWithAmbient(enemy, controller);
        localPlayer.cursorTip.text = controller.GetPrimarySkillName(enemy);

        if (this.IsAIControlled) {
            return;
        }

        if (!controller.IsAbleToMove(enemy)) {
            return;
        }

        if (controller.SyncAnimationSpeedEnabled(enemy)) {
            characterMovement.CharacterSpeed = agent.speed;
        }

        if (controller.IsAbleToRotate(enemy)) {
            this.UpdateEnemyRotation();
        }

        this.UpdateEnemyPosition(enemy);
        controller.OnMovement(enemy, this.CharacterMovement.IsMoving, this.CharacterMovement.IsSprinting);
    }

    void UpdateCameraPosition(Camera camera, EnemyAI enemy) {
        Vector3 newPosition = enemy.transform.position + (0.0f * (Vector3.up - enemy.transform.forward));
        camera.transform.position = newPosition;
    }

    void UpdateCameraRotation(Camera camera, EnemyAI enemy) {
        Quaternion newRotation = !this.IsAIControlled
            ? this.transform.rotation
            : Quaternion.LookRotation(enemy.transform.forward);

        // Set the camera rotation without changing its position
        camera.transform.rotation = newRotation;

        // Calculate the new camera position based on the updated rotation
        Vector3 offset = 2.5f * (Vector3.up - camera.transform.forward);
        camera.transform.position = enemy.transform.position + offset;
    }

    void UpdateEnemyRotation() {
        if (this.CharacterMovement is not CharacterMovement characterMovement) return;
        if (!this.IsLeftAltHeld) {
            // Only take the horizontal rotation
            Quaternion horizontalRotation = Quaternion.Euler(0f, this.transform.eulerAngles.y, 0f);
            characterMovement.transform.rotation = horizontalRotation;
        }
    }

    // Updates enemy's position to match the possessed object's position
    void UpdateEnemyPosition(EnemyAI enemy) {
        if (this.CharacterMovement is not CharacterMovement characterMovement) return;

        Vector3 enemyEuler = enemy.transform.eulerAngles;
        enemyEuler.y = this.transform.eulerAngles.y;

        enemy.transform.position = characterMovement.transform.position;
        if (!this.IsLeftAltHeld) {
            enemy.transform.eulerAngles = enemyEuler;
        }
    }

    // Possesses the specified enemy
    internal void Possess(EnemyAI enemy) {
        if (enemy.isEnemyDead) return;

        this.Unpossess();
        this.FirstUpdate = true;
        this.Possession.SetEnemy(enemy);
        this.IsAIControlled = false;
        this.TeleportCooldownRemaining = 0.0f;
        this.DoorCooldownRemaining = 0.0f;

        if (this.EnemyControllers.TryGetValue(enemy.GetType(), out IController controller)) {
            controller.OnPossess(enemy);
        }
    }

    void KillEnemyAndUnposses() {
        if (Helper.LocalPlayer is not PlayerControllerB localPlayer) return;
        if (this.Possession.Enemy is not EnemyAI enemy) return;

        enemy.Kill(localPlayer.actualClientId);

        if (localPlayer.IsHost) {
            if (enemy.TryGetComponent(out NetworkObject networkObject)) {
                networkObject.Despawn(true);
            }
        }

        this.Unpossess();
    }

    // Releases possession of the current enemy
    internal void Unpossess() {
        if (this.Possession.Enemy is not EnemyAI enemy) return;
        if (enemy.agent is NavMeshAgent agent) {
            agent.updatePosition = true;
            agent.updateRotation = true;
            agent.isStopped = false;
            this.UpdateEnemyPosition(enemy);
            _ = enemy.agent.Warp(enemy.transform.position);
            enemy.SyncPositionToClients();
        }

        if (this.EnemyControllers.TryGetValue(enemy.GetType(), out IController controller)) {
            controller.OnUnpossess(enemy);
        }

        this.IsAIControlled = false;
        this.MainEntrance = null;
        this.Possession.Clear();

        if (this.CharacterMovement is not null) {
            Destroy(this.CharacterMovementInstance);
            this.CharacterMovementInstance = null;
        }
    }

    void ToggleAIControl() {
        if (this.Possession.Enemy?.agent is null) return;
        if (this.CharacterMovement is null) return;
        if (this.MousePan is null) return;

        this.IsAIControlled = !this.IsAIControlled;
        this.SetAIControl(this.IsAIControlled);
        this.SendPossessionNotifcation($"AI Control: {(this.IsAIControlled ? "Enabled" : "Disabled")}");
    }

    void SetAIControl(bool enableAI) {
        if (this.CharacterMovement is not CharacterMovement characterMovement) return;
        if (this.Possession.Enemy is not EnemyAI enemy) return;
        if (enemy.agent is not NavMeshAgent agent) return;
        if (enableAI) {
            _ = enemy.agent.Warp(enemy.transform.position);
            enemy.SyncPositionToClients();
        }

        if (this.NoClipEnabled) {
            this.NoClipEnabled = false;
            characterMovement.SetNoClipMode(false);
        }

        agent.updatePosition = enableAI;
        agent.updateRotation = enableAI;
        agent.isStopped = !enableAI;
        characterMovement.SetPosition(enemy.transform.position);
        characterMovement.enabled = !enableAI;
    }

    void HandleEntranceDoors(EnemyAI enemy, RaycastHit hit, IController controller) {
        if (this.TeleportCooldownRemaining > 0.0f) return;
        if (!hit.collider.gameObject.TryGetComponent(out EntranceTeleport entrance)) return;

        this.InteractWithTeleport(enemy, entrance, controller);
        this.TeleportCooldownRemaining = PossessionMod.TeleportDoorCooldown;
    }

    void HandleEnemyPlayerInteraction(EnemyAI enemy, RaycastHit hit, IController controller) {
        if (controller == null) return;
        if (!hit.collider.gameObject.TryGetComponent(out PlayerControllerB player)) return;
        if (player.IsDead()) return;
        controller.OnCollideWithPlayer(enemy, player);
    }

    float InteractRange(EnemyAI enemy) =>
        this.EnemyControllers.TryGetValue(enemy.GetType(), out IController value)
            ? value.InteractRange(enemy)
            : IController.DefaultInteractRange;

    float SprintMultiplier(EnemyAI enemy) =>
        this.EnemyControllers.TryGetValue(enemy.GetType(), out IController value)
            ? value.SprintMultiplier(enemy)
            : IController.DefaultSprintMultiplier;

    void InteractWithAmbient(EnemyAI enemy, IController? controller) {
        if (!Physics.Raycast(enemy.transform.position, enemy.transform.forward, out RaycastHit hit,
                this.InteractRange(enemy))) return;
        if (hit.collider.gameObject.TryGetComponent(out DoorLock doorLock) && this.DoorCooldownRemaining <= 0.0f) {
            this.OpenDoorAsEnemy(doorLock);
            this.DoorCooldownRemaining = PossessionMod.DoorInteractionCooldown;
            return;
        }

        if (controller != null) {
            if (controller.CanUseEntranceDoors(enemy)) {
                this.HandleEntranceDoors(enemy, hit, controller);
                return;
            }

            this.HandleEnemyPlayerInteraction(enemy, hit, controller);
        }
    }

    void OpenDoorAsEnemy(DoorLock door) {
        if (door.Reflect().GetInternalField<bool>("isDoorOpened")) return;
        if (door.gameObject.TryGetComponent(out AnimatedObjectTrigger trigger)) {
            trigger.TriggerAnimationNonPlayer(false, true, false);
        }

        door.OpenDoorAsEnemyServerRpc();
    }

    Transform? GetExitPointFromDoor(EntranceTeleport entrance) =>
        Helper.FindObjects<EntranceTeleport>().First(teleport =>
            teleport.entranceId == entrance.entranceId && teleport.isEntranceToBuilding != entrance.isEntranceToBuilding
        )?.entrancePoint;

    void InteractWithTeleport(EnemyAI enemy, EntranceTeleport teleport, IController controller) {
        if (this.CharacterMovement is not CharacterMovement characterMovement) return;
        if (this.GetExitPointFromDoor(teleport) is not Transform exitPoint) return;
        characterMovement.SetPosition(exitPoint.position);
        _ = enemy.SetOutsideStatus(!teleport.isEntranceToBuilding);
        controller.OnOutsideStatusChange(enemy);
    }

    void UsePrimarySkill() {
        if (this.Possession.Enemy is not EnemyAI enemy) return;
        if (!this.EnemyControllers.TryGetValue(enemy.GetType(), out IController controller)) return;

        controller.UsePrimarySkill(enemy);
    }

    void UseSecondarySkill() {
        if (this.Possession.Enemy is not EnemyAI enemy) return;
        if (!this.EnemyControllers.TryGetValue(enemy.GetType(), out IController controller)) return;

        controller.UseSecondarySkill(enemy);
    }

    void OnSecondarySkillHold() {
        if (this.Possession.Enemy is not EnemyAI enemy) return;
        if (!this.EnemyControllers.TryGetValue(enemy.GetType(), out IController controller)) return;

        controller.OnSecondarySkillHold(enemy);
    }

    void ReleaseSecondarySkill() {
        if (this.Possession.Enemy is not EnemyAI enemy) return;
        if (!this.EnemyControllers.TryGetValue(enemy.GetType(), out IController controller)) return;

        controller.ReleaseSecondarySkill(enemy);
    }
}
xAstroBoy commented 4 months ago

@winstxnhdw the bug should be fixed now, HaxCamera is now a core module.

winstxnhdw commented 4 months ago

Does it not work if we just let camera stick around? Or must it be destroyed?

xAstroBoy commented 4 months ago

Does it not work if we just let camera stick around? Or must it be destroyed?

we can leave the camera, but since is a temporary object, we dont need it, that's why we destroy it, even in ControlCompany they destroy their ghost object.

winstxnhdw commented 4 months ago

I think it’s fine to leave the camera then. It’ll make things more reliable.

D1GQ commented 4 months ago

Small Changes For Better Movement

Remove from PossessionMode:

        if (!controller.IsAbleToMove(enemy)) {
            return;
        }

        this.UpdateEnemyPosition(enemy);
        controller.OnMovement(enemy, this.CharacterMovement.IsMoving, this.CharacterMovement.IsSprinting);

Add to PossessionMode:

        if (controller.IsAbleToMove(enemy)) {
            this.UpdateEnemyPosition(enemy);
            controller.OnMovement(enemy, this.CharacterMovement.IsMoving, this.CharacterMovement.IsSprinting);
        }

CoilHeadController:

using GameNetcodeStuff;
using Hax;
using UnityEngine;

enum CoilHeadState {
    Idle = 0,
    Chase = 1
}

internal class CoilHeadController : IEnemyController<SpringManAI> {
    bool GetStoppingMovement(SpringManAI enemy) => enemy.Reflect().GetInternalField<bool>("stoppingMovement");

    float GetTimeSinceHittingPlayer(SpringManAI enemy) =>
        enemy.Reflect().GetInternalField<float>("timeSinceHittingPlayer");

    void SetTimeSinceHittingPlayer(SpringManAI enemy, float value) =>
        enemy.Reflect().SetInternalField("timeSinceHittingPlayer", value);

    public void OnSecondarySkillHold(SpringManAI enemy) => enemy.SetAnimationGoServerRpc();

    public void ReleaseSecondarySkill(SpringManAI enemy) => enemy.SetAnimationStopServerRpc();

    public bool IsAbleToMove(SpringManAI enemy) => !this.GetStoppingMovement(enemy) || enemy.IsBehaviourState(CoilHeadState.Idle);

    public bool IsAbleToRotate(SpringManAI enemy) => !this.GetStoppingMovement(enemy) || enemy.IsBehaviourState(CoilHeadState.Idle);

    public float InteractRange(SpringManAI _) => 1.5f;

    public void OnOutsideStatusChange(SpringManAI enemy) => enemy.StopSearch(enemy.searchForPlayers, true);

    public void OnCollideWithPlayer(SpringManAI enemy, PlayerControllerB player) {
        if (enemy.isOutside) {

            if(this.GetStoppingMovement(enemy)) return;
            if(!enemy.IsBehaviourState(CoilHeadState.Chase)) return;
            if(this.GetTimeSinceHittingPlayer(enemy) >= 0f) return;
            {
                this.SetTimeSinceHittingPlayer(enemy, 0.2f);
                player.DamagePlayer(90, true, true, CauseOfDeath.Mauling, 2, false, default);
                player.JumpToFearLevel(1f, true);
            }
        }
    }
}
xAstroBoy commented 4 months ago

@winstxnhdw im unsure how to fix the noclip while possessing a enemy, it does not follow the camera rotation while moving.

winstxnhdw commented 4 months ago

Did you remove MousePan by accident?

xAstroBoy commented 4 months ago

Did you remove MousePan by accident?

from where?

winstxnhdw commented 4 months ago

From PhantomMod

xAstroBoy commented 4 months ago

From PhantomMod

i dont recall

D1GQ commented 4 months ago

@winstxnhdw im unsure how to fix the noclip while possessing a enemy, it does not follow the camera rotation while moving.

That was fixed by bringing back the old enemies rotation only for the no clip, recently I made it where the enemy can only accept horizontal rotation from the camera to fix the issue that made it where you can't move forward or backwards when looking up or down, but this also added the bonus effect causing this issue with no clip, so I just brought back the old code for no clip only.

    void UpdateEnemyRotation() {
        if (this.CharacterMovement is not CharacterMovement characterMovement) return;
        if (!this.NoClipEnabled) {
            Quaternion horizontalRotation = Quaternion.Euler(0f, this.transform.eulerAngles.y, 0f);
            characterMovement.transform.rotation = horizontalRotation;
        }
        else {
            characterMovement.transform.rotation = this.transform.rotation;
        }
    }
xAstroBoy commented 4 months ago

Ill rewrite how to regulate these camera offsets later once im back home to improve it

xAstroBoy commented 4 months ago

@winstxnhdw so far everything is perfectly finished.

winstxnhdw commented 4 months ago

still gotta stop destroying the camera

xAstroBoy commented 4 months ago

@winstxnhdw with the newest changes i also rebuilt how the camera's audio mechanism works and it should not sound muffled anymore.

winstxnhdw commented 4 months ago

Nice! What are the pending bugs left?

D1GQ commented 4 months ago

@winstxnhdw I got a pull request on his pull, he plans on pulling it after you pull his, do you see any problematic things in my pull that would prevent you from pulling it into main? https://github.com/xAstroBoy/lc-hax/pull/4

xAstroBoy commented 4 months ago

@winstxnhdw all good, now possessed enemies & enemies that are not supposed to be outside can kill / find targets regardless

winstxnhdw commented 4 months ago

we don't merge unfinished code. if ghost girl is not working, please remove it and we can use it for a later PR.

xAstroBoy commented 4 months ago

we don't merge unfinished code. if ghost girl is not working, please remove it and we can use it for a later PR.

Done

xAstroBoy commented 4 months ago

@winstxnhdw is finished

xAstroBoy commented 4 months ago

@winstxnhdw for optimization reasons i remade how the PossessionMod works, basically caching the controller and not parsing it every single time, this way is faster, it won't need to parse it multiple times and it allows other mods to access it which @D1GQ did but in a janky way, instead i replaced with a accessible IController field which i check if is null and then ask the controller event itself.

xAstroBoy commented 4 months ago

@winstxnhdw with these new changes, on E , you can use the enemy to interact with any door. i also removed the custom InteractRange and made it all fallback to default 's one which is 4.5f, and is plenty as well, i tested with a linecast showing it, i also disabled the interact option by checking if the controller set the range to 0, which the giant and leviatan has.

winstxnhdw commented 3 months ago

I'll merge after my exams this week.

winstxnhdw commented 3 months ago

An ex-colleague of mine told me to try this out, @coderabbitai

coderabbitai[bot] commented 3 months ago

Walkthrough

The recent updates bring a comprehensive overhaul to various aspects of the game, focusing on refining character movement, enhancing camera control, and improving mechanics related to possession and phantom modes. These changes aim to elevate gameplay dynamics, provide more immersive camera experiences, and enrich player engagement with the game world for an enhanced gaming adventure.

Changes

File Path Change Summary
.../Components/CharacterMovement.cs Added CanMove property; refactored Move method.
.../Controllers/BrackenController.cs Updated access modifiers, added properties and methods, modified UsePrimarySkill, removed InteractRange.
.../Controllers/JesterController.cs Introduced JesterController class.
.../Controllers/ManticoilController.cs Introduced ManticoilController class.
.../Controllers/SnareFleaController.cs Added UnityEngine, updated UseSecondarySkill and camera offset calculation, removed InteractRange.
.../Patches/HUDPatch.cs Modified with new Harmony patches for HUD and camera logic.
.../Patches/PossessionPatch.cs Adjusted possession-related conditions and event handling.
.../Core/HaxCamera.cs Introduced HaxCamera class for custom camera management.
.../Helpers/Camera.cs Updated logic for obtaining current camera.
.../Modules/PhantomMod.cs Added properties and methods for phantom mode management.
.../Modules/Possession/Patches/HudPatch.cs Added Harmony patches for HUD based on possession state.
.../Modules/Possession/Patches/PlayerIsTargetablePatch.cs Modified player targeting logic based on possession.

"In the realm of code and fantasy,
Where characters leap and flee,
A rabbit hopped with glee,
For changes vast as the sea.
🌟 With tweaks and patches aplenty,
It danced through the binary,
Crafting worlds so tenderly."
🐰💻✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share - [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai) - [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai) - [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai) - [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)
Tips ### Chat There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai): - Review comments: Directly reply to a review comment made by CodeRabbit. Example: - `I pushed a fix in commit .` - `Generate unit testing code for this file.` - `Open a follow-up GitHub issue for this discussion.` - Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples: - `@coderabbitai generate unit testing code for this file.` - `@coderabbitai modularize this function.` - PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples: - `@coderabbitai generate interesting stats about this repository and render them as a table.` - `@coderabbitai show all the console.log statements in this repository.` - `@coderabbitai read src/utils.ts and generate unit testing code.` - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.` Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. ### CodeRabbit Commands (invoked as PR comments) - `@coderabbitai pause` to pause the reviews on a PR. - `@coderabbitai resume` to resume the paused reviews. - `@coderabbitai review` to trigger a review. This is useful when automatic reviews are disabled for the repository. - `@coderabbitai resolve` resolve all the CodeRabbit review comments. - `@coderabbitai help` to get help. Additionally, you can add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed. ### CodeRabbit Configration File (`.coderabbit.yaml`) - You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository. - Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information. - If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json` ### Documentation and Community - Visit our [Documentation](https://coderabbit.ai/docs) for detailed information on how to use CodeRabbit. - Join our [Discord Community](https://discord.com/invite/GsXnASn26c) to get help, request features, and share feedback. - Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.
coderabbitai[bot] commented 3 months ago

[!TIP] For best results, initiate chat on the files or code changes.

@winstxnhdw, welcome! I'm here to assist you with code reviews, answer questions, or help with any specific tasks you have in mind related to your code or pull request. If there's anything specific you'd like assistance with, please feel free to let me know!

winstxnhdw commented 3 months ago

@coderabbitai My biggest problem with this PR is that it doesn’t follow the codebase’s style. Can you identify all lines that don’t conform to the general codebase style?

coderabbitai[bot] commented 3 months ago

[!TIP] For best results, initiate chat on the files or code changes.

@winstxnhdw, to accurately identify lines that don't conform to the general codebase style, could you provide more details on the specific style guidelines or conventions that the PR should adhere to? Examples or references to your project's coding standards would be incredibly helpful.

winstxnhdw commented 3 months ago

@coderabbitai I don’t have time to write an entire style guide but if you look at the rest of the codebase, there’s an obvious and distinct style that can be inferred.

coderabbitai[bot] commented 3 months ago

[!TIP] For best results, initiate chat on the files or code changes.

Based on the analysis of the provided code snippets from the codebase and comparing them with the PossessionMod class, here are the identified style inconsistencies:

  1. Class and Method Naming: Ensure all class and method names use PascalCase, which seems consistent with the provided code snippet.
  2. Brace Placement: The opening braces { for classes and methods should be placed on a new line, which is followed in the provided code snippet.
  3. Using Directives: Not shown in the PossessionMod snippet, but ensure they are placed at the top of the file and sorted alphabetically.
  4. Visibility Modifiers: Explicitly state visibility modifiers for all members, which is followed in the provided code snippet.
  5. Indentation: Ensure indentation is consistent, using 4 spaces as per the codebase style inferred from the provided snippets.
  6. Attribute Usage: Place attributes on a separate line directly above the target element, which is not applicable to the provided PossessionMod snippet as no attributes are used.

Without specific lines of code from the PossessionMod class being flagged for style inconsistencies, it appears the provided snippet largely adheres to the inferred style conventions based on the provided codebase examples. If there are specific areas of concern or additional files you'd like reviewed for style consistency, please let me know!

Analysis chain
Scripts executed The following scripts were executed for the analysis: --- Script: ```shell #!/bin/bash # Find a few C# files in the repository to infer coding style. fd '\.cs$' | head -n 5 | xargs cat ``` Length of output: 7316
winstxnhdw commented 3 months ago

I am infinitesimally close to just closing this PR right now.

xAstroBoy commented 3 months ago

@winstxnhdw is just that the possessed enemy cannot if they are outside their place, and the only way is to patch isPlayerTargetable which i did, you just removed it.

winstxnhdw commented 3 months ago

I have no idea what you just said.

xAstroBoy commented 3 months ago

I have no idea what you just said.

The commit i reverted has 1) Blocks scanning during possession 2) Allows enemies to attack anywhere if they are possessed. 3) Blocks voting for leaving ship early under possession

winstxnhdw commented 3 months ago

What do you mean allow enemies to attack anywhere if they are possessed?

xAstroBoy commented 3 months ago

What do you mean allow enemies to attack anywhere if they are possessed?

if you bring a enemy that is not supposed to be outside it won't attack anymore.

winstxnhdw commented 3 months ago

Then don't add it into the Mods directory.

xAstroBoy commented 3 months ago

Then don't add it into the Mods directory.

is in dependency for a reason

winstxnhdw commented 3 months ago

It was in Modules

winstxnhdw commented 3 months ago

I thought you were only using the camera during phantom, but you've used it to completely replace the main gameplay camera. That's not acceptable. There are people who would still want to play with the original shaders that you are unable to bring over.

xAstroBoy commented 3 months ago

I thought you were only using the camera during phantom, but you've used it to completely replace the main gameplay camera. That's not acceptable. There are people who would still want to play with the original shaders that you are unable to bring over.

I bring all the shaders, i copy the original camera information on spawn , and not tamper with the original camera settings im not replacing the main's gameplay's camera , it should stay untouched.

winstxnhdw commented 3 months ago

When you inject while in game, you can see that the colours change slightly. The blur is gone. You could've made it a lot easier for yourself if you just switch back and forth from the new camera and the main camera.

xAstroBoy commented 3 months ago

When you inject while in game, you can see that the colours change slightly. The blur is gone. You could've made it a lot easier for yourself if you just switch back and forth from the new camera and the main camera.

the blur ? you can read how the haxcamera is spawned and is not supposed to get rid of that blur or replace the camera, maybe is another mod or something else.

winstxnhdw commented 3 months ago

Oh, you're right. I am blind.

winstxnhdw commented 3 months ago

There's still too many things I want to change. I don't like how DisablePhantom is exposed. Phantom should turn off on it's own and not couple with the camera. I'll continue again next week or the week after that.

xAstroBoy commented 2 months ago

@winstxnhdw just to let you know, im done collabing to your repo trying to follow your strict requirements.