Skip to content

Commit 83d4c6c

Browse files
committed
Refine grapple gun logic for improved ownership checks and background functionality
1 parent 9132a76 commit 83d4c6c

File tree

3 files changed

+297
-201
lines changed

3 files changed

+297
-201
lines changed

Data/Base.rte/Devices/Tools/GrappleGun/Grapple.lua

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,43 @@ function Update(self)
192192

193193
local parentActor = self.parent -- self.parent is already an Actor type from the setup block
194194

195-
-- Remove the HasObject check - allow grapple to persist even when gun is not equipped
195+
-- Check if grapple gun still exists - either equipped or in inventory
196196
if not self.parentGun or self.parentGun.ID == rte.NoMOID then
197197
self.ToDelete = true
198198
return
199199
end
200+
201+
-- Check if the gun still belongs to the parent actor
202+
local shouldDelete = false
203+
204+
if self.parentGun.RootID == parentActor.ID then
205+
-- Gun is equipped by our parent - all good
206+
elseif self.parentGun.RootID == rte.NoMOID then
207+
-- Gun is unequipped - check if it's in our parent's inventory
208+
local gunInInventory = false
209+
if parentActor.Inventory then
210+
for item in parentActor.Inventory do
211+
if item and item.ID == self.parentGun.ID then
212+
gunInInventory = true
213+
break
214+
end
215+
end
216+
end
217+
if not gunInInventory then
218+
shouldDelete = true
219+
end
220+
else
221+
-- Gun is equipped by someone else
222+
local currentOwner = MovableMan:GetMOFromID(self.parentGun.RootID)
223+
if currentOwner and IsActor(currentOwner) and currentOwner.ID ~= parentActor.ID then
224+
shouldDelete = true
225+
end
226+
end
227+
228+
if shouldDelete then
229+
self.ToDelete = true
230+
return
231+
end
200232

201233
-- Standard update flags
202234
self.ToSettle = false -- Grapple claw should not settle
@@ -320,34 +352,43 @@ function Update(self)
320352
self.Pos.Y = self.apy[self.currentSegments]
321353
end
322354

323-
-- Aim the gun
355+
-- Aim the gun only if it's currently equipped
324356
if self.parentGun and self.parentGun.ID ~= rte.NoMOID then
325-
local flipAng = parentActor.HFlipped and math.pi or 0
326-
self.parentGun.RotAngle = self.lineVec.AbsRadAngle + flipAng
327-
if MovableMan:IsParticle(self.parentGun.Magazine) then -- Check if Magazine is a particle
328-
ToMOSParticle(self.parentGun.Magazine).Scale = 0 -- Hide magazine when grapple is active
329-
end
330-
331-
-- Handle unhooking from firing the gun again
332-
if self.parentGun.FiredFrame then
333-
if self.actionMode == 1 then -- If flying, just delete
357+
local gunIsEquipped = (self.parentGun.RootID == parentActor.ID)
358+
359+
if gunIsEquipped then
360+
local flipAng = parentActor.HFlipped and math.pi or 0
361+
self.parentGun.RotAngle = self.lineVec.AbsRadAngle + flipAng
362+
363+
-- Handle unhooking from firing the gun again - ONLY when gun is equipped
364+
if self.parentGun.FiredFrame then
365+
if self.actionMode == 1 then -- If flying, just delete
366+
self.ToDelete = true
367+
elseif self.actionMode > 1 then -- If attached, mark as ready to release
368+
self.canRelease = true
369+
end
370+
end
371+
-- If marked ready and gun is fired again (or activated for some guns)
372+
if self.canRelease and self.parentGun.FiredFrame and
373+
(self.parentGun.Vel.Y ~= -1 or self.parentGun:IsActivated()) then
334374
self.ToDelete = true
335-
elseif self.actionMode > 1 then -- If attached, mark as ready to release
336-
self.canRelease = true
337375
end
338376
end
339-
-- If marked ready and gun is fired again (or activated for some guns)
340-
if self.canRelease and self.parentGun.FiredFrame and
341-
(self.parentGun.Vel.Y ~= -1 or self.parentGun:IsActivated()) then -- Original logic for release condition
342-
self.ToDelete = true
377+
378+
-- Always hide magazine when grapple is active, regardless of equipped status
379+
if MovableMan:IsParticle(self.parentGun.Magazine) then -- Check if Magazine is a particle
380+
ToMOSParticle(self.parentGun.Magazine).Scale = 0 -- Hide magazine when grapple is active
343381
end
344382
end
345383

346384
-- Player-specific controls and unhooking mechanisms
347385
if IsAHuman(parentActor) or IsACrab(parentActor) then
348386
if parentActor:IsPlayerControlled() then
349387
local controller = self.parent:GetController()
350-
if controller then
388+
local gunIsEquipped = self.parentGun and (self.parentGun.RootID == parentActor.ID)
389+
390+
if controller and gunIsEquipped then
391+
-- Only handle unhook inputs when gun is equipped
351392
-- 1. Handle R key (reload) to unhook - use the module function
352393
if RopeInputController.handleReloadKeyUnhook(self, controller) then
353394
self.ToDelete = true
@@ -371,8 +412,10 @@ function Update(self)
371412
self.parentGun.Magazine.RoundCount = 0
372413
self.parentGun.Magazine.Scale = 0 -- Hide the magazine
373414
end
374-
375-
-- 4. Other rope controls (climbing, length adjustment)
415+
end
416+
417+
if controller then
418+
-- Always allow rope movement controls regardless of gun equipped status
376419
RopeInputController.handleRopePulling(self)
377420
RopeInputController.handleAutoRetraction(self, false)
378421
end

Data/Base.rte/Devices/Tools/GrappleGun/GrappleGun.lua

Lines changed: 91 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -50,60 +50,65 @@ function Update(self)
5050

5151
-- Ensure the gun is held by a valid, player-controlled Actor.
5252
if not parent or not IsActor(parent) then
53-
self:Deactivate()
53+
self:Deactivate() -- If not held by an actor, deactivate.
5454
return
5555
end
5656

57-
local parentActor = ToActor(parent)
57+
local parentActor = ToActor(parent) -- Cast to Actor base type
58+
-- Specific casting to AHuman or ACrab can be done if needed for type-specific logic
5859

5960
if not parentActor:IsPlayerControlled() or parentActor.Status >= Actor.DYING then
60-
self:Deactivate()
61+
self:Deactivate() -- Deactivate if not player controlled or if player is dying.
6162
return
6263
end
6364

6465
local controller = parentActor:GetController()
6566
if not controller then
66-
self:Deactivate()
67+
self:Deactivate() -- Should not happen if IsPlayerControlled is true, but good check.
6768
return
6869
end
6970

71+
-- REMOVE/COMMENT OUT this section that deactivates in background:
72+
--[[
73+
if parentActor.EquippedBGItem and parentActor.EquippedBGItem.ID == self.ID and parentActor.EquippedItem then
74+
self:Deactivate()
75+
// Potentially return here if no further logic should run for a BG equipped grapple gun.
76+
end
77+
--]]
78+
79+
-- Allow gun to stay active in background for rope functionality
80+
7081
-- Magazine handling (visual representation of the hook's availability)
7182
if self.Magazine and MovableMan:IsParticle(self.Magazine) then
7283
local magazineParticle = ToMOSParticle(self.Magazine)
7384

74-
-- Check if we have an active grapple
75-
local hasActiveGrapple = false
76-
for mo in MovableMan.AddedActors do
77-
if mo and mo.PresetName == "Grapple Gun Claw" and mo.parentGun and mo.parentGun.ID == self.ID then
78-
hasActiveGrapple = true
79-
break
80-
end
81-
end
82-
83-
-- Update magazine based on grapple state
84-
if hasActiveGrapple then
85-
magazineParticle.RoundCount = 0 -- Empty when grapple is out
86-
magazineParticle.Scale = 0 -- Hidden
87-
self.hasGrappleActive = true
88-
elseif self.hasGrappleActive and not hasActiveGrapple then
89-
-- Grapple just returned, restore ammo
90-
magazineParticle.RoundCount = 1
91-
magazineParticle.Scale = 1
92-
magazineParticle.Frame = 0
93-
self.hasGrappleActive = false
94-
end
95-
96-
-- Set stance offset when hook is loaded
97-
if magazineParticle.Scale == 1 then
98-
local parentSprite = ToMOSprite(self:GetParent())
85+
-- Double tapping crouch retrieves the hook (if a grapple is active)
86+
-- This logic seems to be for initiating a retrieve action from the gun itself.
87+
-- The actual unhooking is handled by the Grapple.lua script's tap detection.
88+
-- This section might be redundant if Grapple.lua's tap detection is comprehensive.
89+
if magazineParticle.Scale == 1 then -- Assuming Scale 1 means hook is "loaded" / available to fire
90+
-- The following stance offsets seem to be for when the hook is *not* fired yet.
91+
-- Consider if this is the correct condition.
92+
local parentSprite = ToMOSprite(self:GetParent()) -- Assuming self:GetParent() is the gun's sprite component
9993
if parentSprite then
10094
local spriteWidth = parentSprite:GetSpriteWidth() or 0
10195
self.StanceOffset = Vector(spriteWidth, 1)
10296
self.SharpStanceOffset = Vector(spriteWidth, 1)
10397
end
98+
99+
-- REMOVE the entire crouch-tap section from the gun - it should only be in the hook
100+
-- The gun should NOT handle unhooking directly
101+
102+
-- Only keep this for other gun functionality, NOT for unhooking:
103+
if controller:IsState(Controller.WEAPON_RELOAD) then
104+
-- Gun's own reload logic here (if any)
105+
-- Do NOT send unhook signals from here
106+
end
107+
104108
end
105109

106110
-- Guide arrow visibility logic
111+
-- Show if magazine scale is 0 (hook is fired) AND not sharp aiming, OR if parent is moving fast.
107112
local shouldShowGuide = false
108113
if magazineParticle.Scale == 0 and not controller:IsState(Controller.AIM_SHARP) then
109114
shouldShowGuide = true
@@ -112,26 +117,77 @@ function Update(self)
112117
end
113118
self.guide = shouldShowGuide
114119
else
115-
self.guide = false
120+
self.guide = false -- No magazine or not a particle, so no guide based on it.
116121
end
117122

118123
-- Draw the guide arrow if enabled and valid
119124
if self.guide and self.arrow and self.arrow.ID ~= rte.NoMOID then
120125
local frame = 0
121126
if parentActor.Vel and parentActor.Vel:MagnitudeIsGreaterThan(12) then
122-
frame = 1
127+
frame = 1 -- Use a different arrow frame for higher speeds
123128
end
124129

125-
local eyePos = parentActor.EyePos or Vector(0,0)
126-
local startPos = (parentActor.Pos + eyePos + self.Pos)/3
130+
-- Calculate positions for drawing the arrow
131+
-- EyePos might not exist on all Actor types, ensure parentActor has it or use a fallback.
132+
local eyePos = parentActor.EyePos or Vector(0,0)
133+
local startPos = (parentActor.Pos + eyePos + self.Pos)/3 -- Averaged position
127134
local aimAngle = parentActor:GetAimAngle(true)
128-
local aimDistance = parentActor.AimDistance or 50
135+
local aimDistance = parentActor.AimDistance or 50 -- Default AimDistance if not present
129136
local guidePos = startPos + Vector(aimDistance + (parentActor.Vel and parentActor.Vel.Magnitude or 0), 0):RadRotate(aimAngle)
130137

138+
-- Ensure the arrow MO still exists before trying to draw with it
131139
if MovableMan:IsValid(self.arrow) then
132140
PrimitiveMan:DrawBitmapPrimitive(ActivityMan:GetActivity():ScreenOfPlayer(controller.Player), guidePos, self.arrow, aimAngle, frame)
133141
else
134-
self.arrow = nil
142+
self.arrow = nil -- Arrow MO was deleted, nullify reference
143+
end
144+
end
145+
146+
-- Check if we have an active grapple
147+
local hasActiveGrapple = false
148+
for mo in MovableMan.AddedActors do
149+
if mo and mo.PresetName == "Grapple Gun Claw" and mo.parentGun and mo.parentGun.ID == self.ID then
150+
hasActiveGrapple = true
151+
break
152+
end
153+
end
154+
155+
-- Update magazine based on grapple state
156+
if self.Magazine and MovableMan:IsParticle(self.Magazine) then
157+
local mag = ToMOSParticle(self.Magazine)
158+
if hasActiveGrapple then
159+
mag.RoundCount = 0 -- Empty when grapple is out
160+
self.hasGrappleActive = true
161+
elseif self.hasGrappleActive and not hasActiveGrapple then
162+
-- Grapple just returned, restore ammo
163+
mag.RoundCount = 1
164+
self.hasGrappleActive = false
165+
end
166+
end
167+
168+
-- Ensure magazine is visually "full" and ready if no grapple is active.
169+
-- This assumes the HDFirearm's standard magazine logic handles firing.
170+
-- If a grapple claw MO (the projectile) is active, Grapple.lua will hide the magazine.
171+
-- This section ensures it's visible when no grapple is out.
172+
if self.Magazine and MovableMan:IsParticle(self.Magazine) then
173+
local magParticle = ToMOSParticle(self.Magazine)
174+
local isActiveGrapple = false
175+
-- Check if there's an active grapple associated with this gun
176+
for mo_instance in MovableMan:GetMOsByPreset("Grapple Gun Claw") do
177+
if mo_instance and mo_instance.parentGun and mo_instance.parentGun.ID == self.ID then
178+
isActiveGrapple = true
179+
break
180+
end
181+
end
182+
183+
if not isActiveGrapple then
184+
magParticle.RoundCount = 1 -- Visually full
185+
magParticle.Scale = 1 -- Visible
186+
magParticle.Frame = 0 -- Standard frame
187+
else
188+
magParticle.Scale = 0 -- Hidden by active grapple (Grapple.lua also does this)
189+
magParticle.RoundCount = 0 -- Visually empty
190+
135191
end
136192
end
137193
end

0 commit comments

Comments
 (0)