-
Notifications
You must be signed in to change notification settings - Fork 20
Enhance Grapple Gun with unhook functionality and modular improvements #198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
OpenTools-run
wants to merge
27
commits into
cortex-command-community:development
Choose a base branch
from
OpenTools-run:improve-grappling-gun
base: development
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 20 commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
a5abad0
Implement unhook functionality and pie menu integration for Grapple Gun
OpenTools-run 9b4a8f0
Implement Grapple Gun Enhancements
OpenTools-run 9e0e520
Refactor module paths and enhance grapple robustness
OpenTools-run 10300a8
Enhance RopePhysics with improved collision handling and constraint a…
OpenTools-run 169cb05
Refactor grapple to use rigid, unbreakable Verlet rope physics
OpenTools-run 55fbce4
Enhance grapple flight physics and max shoot distance
OpenTools-run f842201
Refactor: Document rope physics, tune iterations
OpenTools-run 2e3f722
Refines Grapple Gun mechanics and rope physics
OpenTools-run 487cfa0
Refactor grapple rope physics for improved realism and control
OpenTools-run fb203ab
Refine Grapple Gun mechanics for enhanced stability and rope physics
OpenTools-run f13173c
Refine grapple physics, collision detection, and controls
OpenTools-run 4bad057
Refine grapple controls and enable background functionality
OpenTools-run 9132a76
Refines grapple gun mechanics and input handling
OpenTools-run 83d4c6c
Refine grapple gun logic for improved ownership checks and background…
OpenTools-run 6488b40
Add logging functionality to GrappleGun's RopeStateManager
OpenTools-run c85575d
Improves grapple gun magazine reset and sounds
OpenTools-run 8caba67
Refines Grapple Gun Shift+Wheel control and prevents weapon switch
OpenTools-run 9fe0d84
Enhance input settings and improve UI stability
OpenTools-run 84b68ff
Refactor logging to use Logger module
OpenTools-run 264b042
Merge branch 'development' into improve-grappling-gun
OpenTools-run 624897e
Update Source/Lua/LuaBindingsInput.cpp
OpenTools-run 81a918a
GrappleGun.lua nitpicking
OpenTools-run ee4131d
Refactor logging and adjust shift key input handling
OpenTools-run db856e7
Refactor Logger module usage and improve shift key input handling in …
OpenTools-run c1009b3
Removes unused shift input and control state
OpenTools-run b63b247
Removes obsolete "Shift" key binding
OpenTools-run a6dd3dd
Update .gitignore to include imgui.ini and fix SHIFT key state retrie…
OpenTools-run File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
1,436 changes: 952 additions & 484 deletions
1,436
Data/Base.rte/Devices/Tools/GrappleGun/Grapple.lua
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,89 +1,201 @@ | ||
---@diagnostic disable: undefined-global | ||
-- Localize Cortex Command globals | ||
local Timer = Timer | ||
local PresetMan = PresetMan | ||
local CreateMOSRotating = CreateMOSRotating | ||
local IsActor = IsActor | ||
local Actor = Actor | ||
local ToMOSParticle = ToMOSParticle | ||
local ToMOSprite = ToMOSprite | ||
local PrimitiveMan = PrimitiveMan | ||
local ActivityMan = ActivityMan | ||
local MovableMan = MovableMan | ||
local Vector = Vector | ||
local Controller = Controller -- For Controller.BODY_PRONE etc. | ||
local rte = rte | ||
|
||
function Create(self) | ||
self.tapTimerAim = Timer(); | ||
self.tapTimerJump = Timer(); | ||
self.tapCounter = 0; | ||
self.didTap = false; | ||
self.canTap = false; | ||
-- Timers and counters for tap-based controls (e.g., double-tap to retrieve hook) | ||
self.tapTimerAim = Timer() -- Unused? Or intended for a different tap action. | ||
self.tapTimerJump = Timer() -- Used for crouch-tap detection. | ||
self.tapCounter = 0 | ||
-- self.didTap = false -- Seems unused, consider removing. | ||
self.canTap = false -- Flag to register the first tap in a sequence. | ||
|
||
self.tapTime = 200 -- Max milliseconds between taps for them to count as a sequence. | ||
self.tapAmount = 2 -- Number of taps required. | ||
|
||
self.guide = false -- Whether to show the aiming guide arrow. | ||
|
||
self.tapTime = 200; | ||
self.tapAmount = 2; | ||
self.guide = false; | ||
-- Create the guide arrow MOSRotating. This is a visual aid. | ||
-- Ensure "Grapple Gun Guide Arrow" preset exists and is a MOSRotating. | ||
local arrowPreset = PresetMan:GetPreset("Grapple Gun Guide Arrow", "MOSRotating", "Grapple Gun Guide Arrow") | ||
if arrowPreset and arrowPreset.ClassName == "MOSRotating" then | ||
self.arrow = CreateMOSRotating("Grapple Gun Guide Arrow") | ||
if self.arrow then | ||
self.arrow.GlobalAccurateDelete = true -- Ensure it cleans up properly | ||
end | ||
else | ||
self.arrow = nil -- Preset not found or incorrect type | ||
-- Log an error or warning if preset is missing/incorrect | ||
-- print("Warning: Grapple Gun Guide Arrow preset not found or incorrect type.") | ||
end | ||
|
||
self.arrow = CreateMOSRotating("Grapple Gun Guide Arrow"); | ||
self.originalRoundCount = 1 | ||
self.hasGrappleActive = false | ||
end | ||
|
||
function Update(self) | ||
local parent = self:GetRootParent(); | ||
if parent and IsActor(parent) then | ||
if IsAHuman(parent) then | ||
parent = ToAHuman(parent); | ||
elseif IsACrab(parent) then | ||
parent = ToACrab(parent); | ||
else | ||
parent = ToActor(parent); | ||
end | ||
if parent:IsPlayerControlled() and parent.Status < Actor.DYING then | ||
local controller = parent:GetController(); | ||
local mouse = controller:IsMouseControlled(); | ||
-- Deactivate when equipped in BG arm to allow FG arm shooting | ||
if parent.EquippedBGItem and parent.EquippedItem then | ||
if parent.EquippedBGItem.ID == self.ID then | ||
self:Deactivate(); | ||
end | ||
end | ||
|
||
if self.Magazine then | ||
-- Double tapping crouch retrieves the hook | ||
if self.Magazine.Scale == 1 then | ||
self.StanceOffset = Vector(ToMOSprite(self:GetParent()):GetSpriteWidth(), 1); | ||
self.SharpStanceOffset = Vector(ToMOSprite(self:GetParent()):GetSpriteWidth(), 1); | ||
if controller and controller:IsState(Controller.BODY_PRONE) then | ||
if self.canTap then | ||
controller:SetState(Controller.BODY_PRONE, false); | ||
self.tapTimerJump:Reset(); | ||
self.didTap = true; | ||
self.canTap = false; | ||
self.tapCounter = self.tapCounter + 1; | ||
end | ||
else | ||
self.canTap = true; | ||
end | ||
|
||
if self.tapTimerJump:IsPastSimMS(self.tapTime) then | ||
self.tapCounter = 0; | ||
else | ||
if self.tapCounter >= self.tapAmount then | ||
self:Activate(); | ||
self.tapCounter = 0; | ||
end | ||
end | ||
end | ||
|
||
-- A guide arrow appears at higher speeds | ||
if (self.Magazine.Scale == 0 and not controller:IsState(Controller.AIM_SHARP)) or parent.Vel:MagnitudeIsGreaterThan(6) then | ||
self.guide = true; | ||
else | ||
self.guide = false; | ||
end | ||
end | ||
|
||
if self.guide then | ||
local frame = 0; | ||
if parent.Vel:MagnitudeIsGreaterThan(12) then | ||
frame = 1; | ||
end | ||
local startPos = (parent.Pos + parent.EyePos + self.Pos)/3; | ||
local guidePos = startPos + Vector(parent.AimDistance + (parent.Vel.Magnitude), 0):RadRotate(parent:GetAimAngle(true)); | ||
PrimitiveMan:DrawBitmapPrimitive(ActivityMan:GetActivity():ScreenOfPlayer(controller.Player), guidePos, self.arrow, parent:GetAimAngle(true), frame); | ||
end | ||
else | ||
self:Deactivate(); | ||
end | ||
|
||
if self.Magazine then | ||
self.Magazine.RoundCount = 1; | ||
self.Magazine.Scale = 1; | ||
self.Magazine.Frame = 0; | ||
end | ||
end | ||
local parent = self:GetRootParent() | ||
|
||
-- Ensure the gun is held by a valid, player-controlled Actor. | ||
if not parent or not IsActor(parent) then | ||
self:Deactivate() -- If not held by an actor, deactivate. | ||
return | ||
end | ||
|
||
local parentActor = ToActor(parent) -- Cast to Actor base type | ||
-- Specific casting to AHuman or ACrab can be done if needed for type-specific logic | ||
|
||
if not parentActor:IsPlayerControlled() or parentActor.Status >= Actor.DYING then | ||
self:Deactivate() -- Deactivate if not player controlled or if player is dying. | ||
return | ||
end | ||
|
||
local controller = parentActor:GetController() | ||
if not controller then | ||
self:Deactivate() -- Should not happen if IsPlayerControlled is true, but good check. | ||
return | ||
end | ||
|
||
-- REMOVE/COMMENT OUT this section that deactivates in background: | ||
--[[ | ||
if parentActor.EquippedBGItem and parentActor.EquippedBGItem.ID == self.ID and parentActor.EquippedItem then | ||
self:Deactivate() | ||
// Potentially return here if no further logic should run for a BG equipped grapple gun. | ||
end | ||
--]] | ||
|
||
-- Allow gun to stay active in background for rope functionality | ||
|
||
-- Magazine handling (visual representation of the hook's availability) | ||
if self.Magazine and MovableMan:IsParticle(self.Magazine) then | ||
local magazineParticle = ToMOSParticle(self.Magazine) | ||
|
||
-- Double tapping crouch retrieves the hook (if a grapple is active) | ||
-- This logic seems to be for initiating a retrieve action from the gun itself. | ||
-- The actual unhooking is handled by the Grapple.lua script's tap detection. | ||
-- This section might be redundant if Grapple.lua's tap detection is comprehensive. | ||
if magazineParticle.Scale == 1 then -- Assuming Scale 1 means hook is "loaded" / available to fire | ||
-- The following stance offsets seem to be for when the hook is *not* fired yet. | ||
-- Consider if this is the correct condition. | ||
local parentSprite = ToMOSprite(self:GetParent()) -- Assuming self:GetParent() is the gun's sprite component | ||
if parentSprite then | ||
local spriteWidth = parentSprite:GetSpriteWidth() or 0 | ||
self.StanceOffset = Vector(spriteWidth, 1) | ||
self.SharpStanceOffset = Vector(spriteWidth, 1) | ||
end | ||
|
||
-- REMOVE the entire crouch-tap section from the gun - it should only be in the hook | ||
-- The gun should NOT handle unhooking directly | ||
|
||
-- Only keep this for other gun functionality, NOT for unhooking: | ||
if controller:IsState(Controller.WEAPON_RELOAD) then | ||
-- Gun's own reload logic here (if any) | ||
-- Do NOT send unhook signals from here | ||
end | ||
|
||
end | ||
|
||
-- Guide arrow visibility logic | ||
-- Show if magazine scale is 0 (hook is fired) AND not sharp aiming, OR if parent is moving fast. | ||
local shouldShowGuide = false | ||
if magazineParticle.Scale == 0 and not controller:IsState(Controller.AIM_SHARP) then | ||
shouldShowGuide = true | ||
elseif parentActor.Vel and parentActor.Vel:MagnitudeIsGreaterThan(6) then | ||
shouldShowGuide = true | ||
end | ||
self.guide = shouldShowGuide | ||
else | ||
self.guide = false -- No magazine or not a particle, so no guide based on it. | ||
end | ||
|
||
-- Draw the guide arrow if enabled and valid | ||
if self.guide and self.arrow and self.arrow.ID ~= rte.NoMOID then | ||
local frame = 0 | ||
if parentActor.Vel and parentActor.Vel:MagnitudeIsGreaterThan(12) then | ||
frame = 1 -- Use a different arrow frame for higher speeds | ||
end | ||
|
||
-- Calculate positions for drawing the arrow | ||
-- EyePos might not exist on all Actor types, ensure parentActor has it or use a fallback. | ||
local eyePos = parentActor.EyePos or Vector(0,0) | ||
local startPos = (parentActor.Pos + eyePos + self.Pos)/3 -- Averaged position | ||
local aimAngle = parentActor:GetAimAngle(true) | ||
local aimDistance = parentActor.AimDistance or 50 -- Default AimDistance if not present | ||
local guidePos = startPos + Vector(aimDistance + (parentActor.Vel and parentActor.Vel.Magnitude or 0), 0):RadRotate(aimAngle) | ||
|
||
-- Ensure the arrow MO still exists before trying to draw with it | ||
if MovableMan:IsValid(self.arrow) then | ||
PrimitiveMan:DrawBitmapPrimitive(ActivityMan:GetActivity():ScreenOfPlayer(controller.Player), guidePos, self.arrow, aimAngle, frame) | ||
else | ||
self.arrow = nil -- Arrow MO was deleted, nullify reference | ||
end | ||
end | ||
|
||
-- Check if we have an active grapple | ||
local hasActiveGrapple = false | ||
for mo in MovableMan.AddedActors do | ||
if mo and mo.PresetName == "Grapple Gun Claw" and mo.parentGun and mo.parentGun.ID == self.ID then | ||
hasActiveGrapple = true | ||
break | ||
end | ||
end | ||
|
||
-- Update magazine based on grapple state | ||
if self.Magazine and MovableMan:IsParticle(self.Magazine) then | ||
local mag = ToMOSParticle(self.Magazine) | ||
if hasActiveGrapple then | ||
mag.RoundCount = 0 -- Empty when grapple is out | ||
self.hasGrappleActive = true | ||
elseif self.hasGrappleActive and not hasActiveGrapple then | ||
-- Grapple just returned, restore ammo | ||
mag.RoundCount = 1 | ||
self.hasGrappleActive = false | ||
end | ||
end | ||
|
||
-- Ensure magazine is visually "full" and ready if no grapple is active. | ||
-- This assumes the HDFirearm's standard magazine logic handles firing. | ||
-- If a grapple claw MO (the projectile) is active, Grapple.lua will hide the magazine. | ||
-- This section ensures it's visible when no grapple is out. | ||
if self.Magazine and MovableMan:IsParticle(self.Magazine) then | ||
local magParticle = ToMOSParticle(self.Magazine) | ||
local isActiveGrapple = false | ||
-- Check if there's an active grapple associated with this gun | ||
for mo_instance in MovableMan:GetMOsByPreset("Grapple Gun Claw") do | ||
if mo_instance and mo_instance.parentGun and mo_instance.parentGun.ID == self.ID then | ||
isActiveGrapple = true | ||
break | ||
end | ||
end | ||
|
||
if not isActiveGrapple then | ||
magParticle.RoundCount = 1 -- Visually full | ||
magParticle.Scale = 1 -- Visible | ||
magParticle.Frame = 0 -- Standard frame | ||
else | ||
magParticle.Scale = 0 -- Hidden by active grapple (Grapple.lua also does this) | ||
magParticle.RoundCount = 0 -- Visually empty | ||
|
||
end | ||
end | ||
end | ||
|
||
function Destroy(self) | ||
-- Clean up the guide arrow if it exists | ||
if self.arrow and self.arrow.ID ~= rte.NoMOID and MovableMan:IsValid(self.arrow) then | ||
MovableMan:RemoveMO(self.arrow) | ||
self.arrow = nil | ||
end | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,53 @@ | ||
-- Load required modules | ||
-- RopeStateManager might not be directly needed here if we only set GrappleMode on the gun. | ||
-- local RopeStateManager = require("Devices.Tools.GrappleGun.Scripts.RopeStateManager") | ||
|
||
-- Utility function to safely check if an object has a specific property (key) in its Lua script table. | ||
-- This is useful for checking if a script-defined variable exists on an MO. | ||
function HasScriptProperty(obj, propName) | ||
OpenTools-run marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if type(obj) ~= "table" or type(propName) ~= "string" then | ||
return false | ||
end | ||
-- pcall to safely access potentially non-existent script members. | ||
-- This is more about checking Lua script-defined members rather than engine properties. | ||
local status, result = pcall(function() return rawget(obj, propName) ~= nil end) | ||
return status and result | ||
end | ||
|
||
-- Helper function to validate grapple gun | ||
local function ValidateGrappleGun(pieMenuOwner) | ||
if not pieMenuOwner or not pieMenuOwner.EquippedItem then | ||
return nil | ||
end | ||
|
||
local gun = ToMOSRotating(pieMenuOwner.EquippedItem) | ||
if gun and gun.PresetName == "Grapple Gun" then | ||
return gun | ||
end | ||
|
||
return nil | ||
end | ||
|
||
-- Action for Retract slice in the pie menu. | ||
function GrapplePieRetract(pieMenuOwner, pieMenu, pieSlice) | ||
local gun = pieMenuOwner.EquippedItem; | ||
if gun then | ||
ToMOSRotating(gun):SetNumberValue("GrappleMode", 1); | ||
end | ||
local gun = ValidateGrappleGun(pieMenuOwner) | ||
if gun then | ||
gun:SetNumberValue("GrappleMode", 1) -- 1 signifies Retract | ||
end | ||
end | ||
|
||
-- Action for Extend slice in the pie menu. | ||
function GrapplePieExtend(pieMenuOwner, pieMenu, pieSlice) | ||
local gun = pieMenuOwner.EquippedItem; | ||
if gun then | ||
ToMOSRotating(gun):SetNumberValue("GrappleMode", 2); | ||
end | ||
local gun = ValidateGrappleGun(pieMenuOwner) | ||
if gun then | ||
gun:SetNumberValue("GrappleMode", 2) -- 2 signifies Extend | ||
end | ||
end | ||
|
||
-- Action for Unhook slice in the pie menu. | ||
function GrapplePieUnhook(pieMenuOwner, pieMenu, pieSlice) | ||
local gun = ValidateGrappleGun(pieMenuOwner) | ||
if gun then | ||
gun:SetNumberValue("GrappleMode", 3) -- 3 signifies Unhook | ||
end | ||
end |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.