Intial Commit
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
require "KLT_LAV"
|
||||
require "TimedActions/KLT_ReloadLAV"
|
||||
require "Vehicles/Papa_Chad_Tank_Cannon"
|
||||
|
||||
local F = KLTLAV
|
||||
local characterVariables = {
|
||||
"M2_idle", "M2_360Anim", "M2_045Anim", "M2_090Anim", "M2_135Anim",
|
||||
"M2_180Anim", "M2_225Anim", "M2_270Anim", "M2_315Anim",
|
||||
}
|
||||
|
||||
function F.installClient()
|
||||
if F.clientInstalled then return end
|
||||
if not weaponConfig or not weaponConfig.M41_76mm
|
||||
or not chooseWeaponPC or not tankAimSystemPC then
|
||||
print("[KI5LAVTurret] MTK turret API unavailable; controls not installed.")
|
||||
return
|
||||
end
|
||||
local profile = {}
|
||||
for key, value in pairs(weaponConfig.M41_76mm) do profile[key] = value end
|
||||
profile.requiredPart = F.weaponPart
|
||||
profile.requiredSeat = "SeatGunner" -- MTK's label for index 1, not a renamed seat.
|
||||
profile.requiredItem = F.ammoType
|
||||
profile.requiredItemHE = F.heAmmoType
|
||||
profile.coax = false
|
||||
profile.sound = "HRSys_LAV300_90mm_Fire"
|
||||
profile.soundEnd = "HRSys_LAV300_90mm_End"
|
||||
profile.rotationTime = 10
|
||||
profile.shotsPerMinute = 12
|
||||
profile.startHeight = 0.7
|
||||
profile.penetrationMM = 150
|
||||
profile.soundTurret = "Turret_Electre"
|
||||
profile.soundTurretCooldown = 0.25
|
||||
weaponConfig[F.weaponPart] = profile
|
||||
F.profile = profile
|
||||
-- Keep AP damage unchanged. HE uses precisely the tank profile's explosion
|
||||
-- radius, flash radius and damage, with MTK's existing explosion code.
|
||||
local heProfile = {}
|
||||
for key, value in pairs(profile) do heProfile[key] = value end
|
||||
heProfile.penetrationMM = weaponConfig.M41_76mm.penetrationMM
|
||||
F.heProfile = heProfile
|
||||
|
||||
local originalChoose = chooseWeaponPC
|
||||
chooseWeaponPC = function(vehicle)
|
||||
if F.isLAV(vehicle) then
|
||||
if not F.inputBlocked and F.weaponReady(vehicle) and not F.pendingReload[getPlayer()] then
|
||||
local part = vehicle:getPartById(F.weaponPart)
|
||||
local container = part and part:getItemContainer()
|
||||
if container and container:containsType(F.heAmmoType) then return heProfile end
|
||||
return profile
|
||||
end
|
||||
return nil
|
||||
end
|
||||
return originalChoose(vehicle)
|
||||
end
|
||||
|
||||
local originalAim = tankAimSystemPC
|
||||
Events.OnPlayerUpdate.Remove(originalAim)
|
||||
tankAimSystemPC = function(player)
|
||||
local vehicle = player and player:getVehicle()
|
||||
if not F.isLAV(vehicle) then return originalAim(player) end
|
||||
|
||||
local gunner = F.isGunner(vehicle, player) and player == getPlayer() and not player:isDead()
|
||||
local overInventory = UIManager and UIManager.isMouseOverInventory
|
||||
and UIManager.isMouseOverInventory()
|
||||
-- Still call MTK so its existing projectiles continue to advance. A nil
|
||||
-- weapon prevents input/state changes from driver or non-local updates.
|
||||
F.inputBlocked = not gunner or overInventory or getGameSpeed() == 0
|
||||
if F.inputBlocked then
|
||||
originalAim(player)
|
||||
F.inputBlocked = false
|
||||
return
|
||||
end
|
||||
|
||||
local oldDirection = player:getDirectionAngle()
|
||||
local oldSneaking = player:isSneaking()
|
||||
local oldVariables = {}
|
||||
for _, name in ipairs(characterVariables) do oldVariables[name] = player:getVariableString(name) end
|
||||
|
||||
local aiming = isMouseButtonDown(1) and player:getCharacterActions():isEmpty()
|
||||
if aiming then
|
||||
-- ToWorld also uses the installed Viewpoint mouse bridge when active.
|
||||
-- Scaled mouse coordinates are required by the base-game conversion.
|
||||
local x, y = ISCoordConversion.ToWorld(getMouseXScaled(), getMouseYScaled(), player:getZ() + profile.startHeight)
|
||||
if x and y then
|
||||
local dx, dy = x - player:getX(), y - player:getY()
|
||||
if dx * dx + dy * dy > 0.0001 then
|
||||
player:setDirectionAngle(math.deg(math.atan2(dy, dx)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local weapon = vehicle:getPartById(F.weaponPart)
|
||||
local container = weapon and weapon:getItemContainer()
|
||||
local round = aiming and isMouseButtonDown(0) and container and (container:FindAndReturn(F.heAmmoType) or container:FindAndReturn(F.ammoType))
|
||||
originalAim(player)
|
||||
if round and isClient() and not container:contains(round) then
|
||||
sendRemoveItemFromContainer(container, round)
|
||||
end
|
||||
|
||||
-- Keep DAMN's existing seated pose instead of MTK's exposed M2 gunner pose.
|
||||
player:setDirectionAngle(oldDirection)
|
||||
player:setSneaking(oldSneaking)
|
||||
for _, name in ipairs(characterVariables) do
|
||||
if oldVariables[name] and oldVariables[name] ~= "" then
|
||||
player:setVariable(name, oldVariables[name])
|
||||
else
|
||||
player:clearVariable(name)
|
||||
end
|
||||
end
|
||||
|
||||
if aiming and turretAngles[vehicle:getId()] and not isClient() then
|
||||
local hull = calculateVehicleRotation(vehicle:getAngleX(), vehicle:getAngleY(), vehicle:getAngleZ())
|
||||
local relative = (hull - turretAngles[vehicle:getId()] + 360) % 360
|
||||
F.savePose(vehicle, F.poseForAngle(relative))
|
||||
end
|
||||
end
|
||||
Events.OnPlayerUpdate.Add(tankAimSystemPC)
|
||||
F.clientInstalled = true
|
||||
print("[KI5LAVTurret] Gunner controls registered with Military Tool Kit.")
|
||||
end
|
||||
|
||||
Events.OnGameBoot.Add(F.installClient)
|
||||
Events.OnGameStart.Add(F.installClient)
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
require "TimedActions/ISBaseTimedAction"
|
||||
require "KLT_LAV"
|
||||
|
||||
KLT_ReloadLAV = ISBaseTimedAction:derive("KLT_ReloadLAV")
|
||||
local L = KLTLAV
|
||||
|
||||
function KLT_ReloadLAV:isValid()
|
||||
if isClient() or not L.isGunner(self.vehicle, self.character) or not L.weaponReady(self.vehicle) then return false end
|
||||
local gun = self.vehicle:getPartById(L.weaponPart)
|
||||
local storage = self.vehicle:getPartById(L.storagePart)
|
||||
return gun == self.gun and storage == self.storage and storage:getInventoryItem() ~= nil
|
||||
and storage:getCondition() > 0 and gun:getItemContainer() == self.breech
|
||||
and storage:getItemContainer() == self.source and self.breech:isEmpty()
|
||||
and self.source:contains(self.round) and L.isAmmoType(self.round:getFullType())
|
||||
end
|
||||
|
||||
function KLT_ReloadLAV:start()
|
||||
self.character:getEmitter():playSound("Turret_Reload_Open")
|
||||
end
|
||||
|
||||
function KLT_ReloadLAV:clearPending()
|
||||
if L.pendingReload[self.character] == self then L.pendingReload[self.character] = nil end
|
||||
end
|
||||
|
||||
function KLT_ReloadLAV:stop()
|
||||
self:clearPending()
|
||||
ISBaseTimedAction.stop(self)
|
||||
end
|
||||
|
||||
function KLT_ReloadLAV:forceCancel()
|
||||
self:clearPending()
|
||||
end
|
||||
|
||||
function KLT_ReloadLAV:perform()
|
||||
-- Validate again: leaving the seat, losing the round or loading another
|
||||
-- shell during the action must never create or move ammunition.
|
||||
if self:isValid() then
|
||||
self.source:Remove(self.round)
|
||||
self.breech:AddItem(self.round)
|
||||
self.character:getEmitter():playSound("Turret_Reload_Close")
|
||||
end
|
||||
self:clearPending()
|
||||
ISBaseTimedAction.perform(self)
|
||||
end
|
||||
|
||||
function KLT_ReloadLAV:new(character, vehicle, gun, storage, round)
|
||||
local o = ISBaseTimedAction.new(self, character)
|
||||
o.vehicle = vehicle
|
||||
o.gun = gun
|
||||
o.storage = storage
|
||||
o.source = storage:getItemContainer()
|
||||
o.breech = gun:getItemContainer()
|
||||
o.round = round
|
||||
o.maxTime = 300 -- about five seconds before character modifiers
|
||||
o.stopOnWalk = true
|
||||
o.stopOnRun = true
|
||||
o.stopOnAim = false -- pendingReload blocks firing while the player holds aim
|
||||
return o
|
||||
end
|
||||
|
||||
function L.queueReload(character)
|
||||
local vehicle = character and character:getVehicle()
|
||||
if not L.isGunner(vehicle, character) or not L.weaponReady(vehicle) or L.pendingReload[character] then return false end
|
||||
if isClient() then character:Say("LAV reload prototype currently supports single-player."); return false end
|
||||
local gun = vehicle:getPartById(L.weaponPart)
|
||||
local storage = vehicle:getPartById(L.storagePart)
|
||||
local source = storage and storage:getItemContainer()
|
||||
local breech = gun and gun:getItemContainer()
|
||||
if not source or not breech or not breech:isEmpty() then return false end
|
||||
-- Match MTK tank reload priority: HE first when both types are stored.
|
||||
local round = source:FindAndReturn(L.heAmmoType) or source:FindAndReturn(L.ammoType)
|
||||
if not round then character:Say("No 90 mm AP or HE shells in LAV ammunition storage."); return false end
|
||||
local action = KLT_ReloadLAV:new(character, vehicle, gun, storage, round)
|
||||
if not action:isValid() then return false end
|
||||
L.pendingReload[character] = action
|
||||
ISTimedActionQueue.add(action)
|
||||
return true
|
||||
end
|
||||
|
||||
Events.OnKeyPressed.Add(function(key)
|
||||
if key == Keyboard.KEY_R and getGameSpeed() > 0
|
||||
and not (UIManager and UIManager.isMouseOverInventory and UIManager.isMouseOverInventory()) then
|
||||
L.queueReload(getSpecificPlayer(0))
|
||||
end
|
||||
end)
|
||||
Reference in New Issue
Block a user