From 4ac04d3a97f16f6db3ca32f93d6a15a960394c3b Mon Sep 17 00:00:00 2001 From: ChronoVortex Date: Wed, 10 May 2023 01:48:08 -0700 Subject: [PATCH 1/8] added deep table copy util function --- data/vertex_module/util.lua | 64 ++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/data/vertex_module/util.lua b/data/vertex_module/util.lua index 3dd27e7..6f17e43 100644 --- a/data/vertex_module/util.lua +++ b/data/vertex_module/util.lua @@ -10,7 +10,7 @@ local INT_MAX = 2147483647 ---------------------------- -- Generic iterator for C vectors function mods.vertexutil.vter(cvec) - local i = -1 --so the first returned value is indexed at zero + local i = -1 -- so the first returned value is indexed at zero local n = cvec:size() return function() i = i + 1 @@ -19,6 +19,68 @@ function mods.vertexutil.vter(cvec) end local vter = mods.vertexutil.vter +-- Copy a table recursively +--[[ taken from +https://stackoverflow.com/questions/42178768/lua-copying-a-table-efficiently-deep-copy#answer-45867020 +https://gist.github.com/cpeosphoros/0aa286c6b39c1e452d9aa15d7537ac95 +--]] +function mods.vertexutil.table_copy_deep(value, cache, promises, copies) + cache = cache or {} + promises = promises or {} + copies = copies or {} + local copy + if type(value) == 'table' then + if (cache[value]) then + copy = cache[value] + else + promises[value] = promises[value] or {} + copy = {} + for k, v in next, value, nil do + local nKey = promises[k] or mods.vertexutil.table_copy_deep(k, cache, promises, copies) + local nValue = promises[v] or mods.vertexutil.table_copy_deep(v, cache, promises, copies) + copies[nKey] = type(k) == "table" and k or nil + copies[nValue] = type(v) == "table" and v or nil + copy[nKey] = nValue + end + local mt = getmetatable(value) + if mt then + setmetatable(copy, mt.__immutable and mt or mods.vertexutil.table_copy_deep(mt, cache, promises, copies)) + end + cache[value] = copy + end + else -- number, string, boolean, etc + copy = value + end + for k, v in pairs(copies) do + if k == cache[v] then + copies[k] = nil + end + end + local function correctRec(tbl) + if type(tbl) ~= "table" then return tbl end + if copies[tbl] and cache[copies[tbl]] then + return cache[copies[tbl]] + end + local new = {} + for k, v in pairs(tbl) do + local oldK = k + k, v = correctRec(k), correctRec(v) + if k ~= oldK then + tbl[oldK] = nil + new[k] = v + else + tbl[k] = v + end + end + for k, v in pairs(new) do + tbl[k] = v + end + return tbl + end + correctRec(copy) + return copy +end + -- Check if a given crew member is being mind controlled by a ship system function mods.vertexutil.under_mind_system(crewmem) local controlledCrew = nil From 5d3c444d600eb2597d855342d7ec822514a8430c Mon Sep 17 00:00:00 2001 From: ChronoVortex Date: Wed, 10 May 2023 01:48:59 -0700 Subject: [PATCH 2/8] fixed error levels --- data/vertex_module/tags/augEffects.lua | 6 +++--- data/vertex_module/tags/hack.lua | 6 +++--- data/vertex_module/tags/lockdownBeam.lua | 6 +++--- data/vertex_module/tags/mindControl.lua | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/data/vertex_module/tags/augEffects.lua b/data/vertex_module/tags/augEffects.lua index ebb77a2..623b4a3 100644 --- a/data/vertex_module/tags/augEffects.lua +++ b/data/vertex_module/tags/augEffects.lua @@ -27,9 +27,9 @@ local function parser(node) local augEffects = {} for augEffectNode in Children(node) do local augEffect = {} - if not augEffectNode:first_attribute("effect") then error("augEffect tag requires an effect!", 2) - elseif not augEffectNode:first_attribute("amount") then error("augEffect tag requires an amount!", 2) - elseif not tonumber(augEffectNode:first_attribute("amount"):value()) then error("Invalid number for augEffect 'amount' attribute!", 2) + if not augEffectNode:first_attribute("effect") then error("augEffect tag requires an effect!") + elseif not augEffectNode:first_attribute("amount") then error("augEffect tag requires an amount!") + elseif not tonumber(augEffectNode:first_attribute("amount"):value()) then error("Invalid number for augEffect 'amount' attribute!") end augEffect.effect = augEffectNode:first_attribute("effect"):value() diff --git a/data/vertex_module/tags/hack.lua b/data/vertex_module/tags/hack.lua index d2564d1..b751c14 100644 --- a/data/vertex_module/tags/hack.lua +++ b/data/vertex_module/tags/hack.lua @@ -26,14 +26,14 @@ local crew_data = mods.vertexutil.crew_data local function parser(node) local hack = {} - if not node:first_attribute("duration") then error("hack tag requires a duration!", 2) end + if not node:first_attribute("duration") then error("hack tag requires a duration!") end hack.duration = tonumber(node:first_attribute("duration"):value()) - if not hack.duration then error("Invalid number for hack 'duration' attribute!", 2) end + if not hack.duration then error("Invalid number for hack 'duration' attribute!") end if node:first_attribute("hitShieldDuration") then hack.hitShieldDuration = tonumber(node:first_attribute("hitShieldDuration"):value()) if not hack.hitShieldDuration then - error("Invalid number for hack 'hitShieldDuration' attribute!", 2) + error("Invalid number for hack 'hitShieldDuration' attribute!") end end diff --git a/data/vertex_module/tags/lockdownBeam.lua b/data/vertex_module/tags/lockdownBeam.lua index b67b7b4..5a706d7 100644 --- a/data/vertex_module/tags/lockdownBeam.lua +++ b/data/vertex_module/tags/lockdownBeam.lua @@ -30,17 +30,17 @@ local function parser(node) if node:first_attribute("chance") then lockdown.chance = tonumber(node:first_attribute("chance"):value()) if not lockdown.chance then - error("Invalid number for lockdown 'chance' attribute!", 2) + error("Invalid number for lockdown 'chance' attribute!") end end lockdown.sounds = {} for sound in Children(node) do if sound:name() ~= "sound" then - error("Invalid child tag '"..sound:name().."' for 'lockdownBeam'!", 2) + error("Invalid child tag '"..sound:name().."' for 'lockdownBeam'!") end if not sound:value() then - error("Invalid value for 'sound' child of 'lockdownBeam' tag!", 2) + error("Invalid value for 'sound' child of 'lockdownBeam' tag!") end table.insert(lockdown.sounds, sound:value()) end diff --git a/data/vertex_module/tags/mindControl.lua b/data/vertex_module/tags/mindControl.lua index 422e288..e3a7c1d 100644 --- a/data/vertex_module/tags/mindControl.lua +++ b/data/vertex_module/tags/mindControl.lua @@ -28,23 +28,23 @@ local function parser(node) mindControl.duration = node:value() if not mindControl.duration then - error("mindControl tag requires a value for duration!", 2) + error("mindControl tag requires a value for duration!") elseif not tonumber(mindControl.duration) then - error("Invalid number for mindControl tag!", 2) + error("Invalid number for mindControl tag!") end mindControl.duration = tonumber(mindControl.duration) if node:first_attribute("limit") then mindControl.limit = tonumber(node:first_attribute("limit"):value()) if not mindControl.limit then - error("Invalid number for mindControl 'limit' attribute!", 2) + error("Invalid number for mindControl 'limit' attribute!") end end if node:first_attribute("endSound") then mindControl.endSound = node:first_attribute("endSound"):value() if not mindControl.endSound then - error("Invalid mindControl 'endSound' attribute!", 2) + error("Invalid mindControl 'endSound' attribute!") end end From 35cfa36eaf5e4d3a917704e93c08011246ede04a Mon Sep 17 00:00:00 2001 From: ChronoVortex Date: Sun, 28 May 2023 08:44:19 -0700 Subject: [PATCH 3/8] initial particle system implementation --- data/hyperspace.xml.append | 4 + data/vertex_module/callbacks.lua | 124 ++++++ data/vertex_module/particlesInit.lua | 406 +++++++++++++++++++ data/vertex_module/tags/particleEmitters.lua | 69 ++++ data/vertex_module/xmlDataRead.lua | 8 +- 5 files changed, 607 insertions(+), 4 deletions(-) create mode 100644 data/vertex_module/callbacks.lua create mode 100644 data/vertex_module/particlesInit.lua create mode 100644 data/vertex_module/tags/particleEmitters.lua diff --git a/data/hyperspace.xml.append b/data/hyperspace.xml.append index 09a3d2b..59586fc 100644 --- a/data/hyperspace.xml.append +++ b/data/hyperspace.xml.append @@ -4,13 +4,17 @@ data/vertex_module/util.lua + data/vertex_module/callbacks.lua data/vertex_module/xmlDataInit.lua + data/vertex_module/particlesInit.lua + data/vertex_module/tags/augEffects.lua data/vertex_module/tags/hack.lua data/vertex_module/tags/lockdownBeam.lua data/vertex_module/tags/mindControl.lua data/vertex_module/tags/noPowerFix.lua + data/vertex_module/tags/particleEmitters.lua data/vertex_module/tags/preignited.lua data/vertex_module/xmlDataRead.lua diff --git a/data/vertex_module/callbacks.lua b/data/vertex_module/callbacks.lua new file mode 100644 index 0000000..45a30c7 --- /dev/null +++ b/data/vertex_module/callbacks.lua @@ -0,0 +1,124 @@ +local vter = mods.vertexutil.vter + +-- Make sure that inferno core was patched before this mod if it was patched +local infernoInstalled = false +if mods.inferno then infernoInstalled = true end +script.on_load(function() + if not infernoInstalled and mods.inferno then + Hyperspace.ErrorMessage("Vertex Tags and Utility Functions was patched before Inferno-Core! Please re-patch your mods, and make sure to put Inferno-Core first!") + end +end) + +-- Implement the inferno weapon fire callbacks ourselves if inferno isn't patched +if not infernoInstalled then + +local callback_runner = { + identifier = "", + + __call = function(self, ...) + for key, functable in ipairs(self) do + for _, func in ipairs(functable) do + local success, res = pcall(func, ...) + if not success then + log(string.format("Failed to call function in callback '%s' due to error:\n %s", self.identifier, res)) + elseif res then + return + end + end + end + end, + + add = function(self, func, priority) + local priority = priority or 0 + if type(priority) ~= 'number' or math.floor(priority) ~= priority then + error("Priority argument must be an integer!", 3) + end + local priority = priority or 0 + local ptab = nil + for _,v in ipairs(self) do + if getmetatable(v).priority == priority then + ptab = v break + end + end + if not ptab then + ptab = setmetatable({}, {priority = priority}) + table.insert(self, ptab) + end + if type(func) ~= 'function' then + error("Second argument must be a function!", 3) + end + table.insert(ptab, func) + table.sort(self, function(lesser,greater) + return getmetatable(lesser).priority > getmetatable(greater).priority -- larger numbers come first + end) + end, + + new = function(self, o) + o = o or {} + self.__index = self + setmetatable(o, self) + return o + end, +} + +Defines.FireEvents = { + WEAPON_FIRE = callback_runner:new({identifier = "Defines.FireEvents.WEAPON_FIRE"}), + ARTILLERY_FIRE = callback_runner:new({identifier = "Defines.FireEvents.ARTILLERY_FIRE"}), +} + +script.on_internal_event(Defines.InternalEvents.ON_TICK, function() + for i = 0, 1 do + local weapons = nil + local ship = Hyperspace.Global.GetInstance():GetShipManager(i) + pcall(function() weapons = ship.weaponSystem.weapons end) + if weapons and ship.weaponSystem:Powered() then + for weapon in vter(weapons) do + while true do + local projectile = weapon:GetProjectile() + if projectile then + Hyperspace.Global.GetInstance():GetCApp().world.space.projectiles:push_back(projectile) + Defines.FireEvents.WEAPON_FIRE(ship, weapon, projectile) + else + break + end + end + end + end + end +end, 1000) + +script.on_internal_event(Defines.InternalEvents.ON_TICK, function() + for i = 0, 1 do + local artilleries = nil + local ship = Hyperspace.Global.GetInstance():GetShipManager(i) + pcall(function() artilleries = ship.artillerySystems end) + if artilleries then + for artillery in vter(artilleries) do + while true do + local weapon = artillery.projectileFactory + local projectile = weapon:GetProjectile() + if projectile then + Hyperspace.Global.GetInstance():GetCApp().world.space.projectiles:push_back(projectile) + Defines.FireEvents.ARTILLERY_FIRE(ship, artillery, projectile) + else + break + end + end + end + end + end +end, 1000) + +function script.on_fire_event(FireEvent, func, priority) + local validEvent = false + for _, v in pairs(Defines.FireEvents) do + if v == FireEvent then validEvent = true break end + end + if not validEvent then + log("\n\nValid FireEvents:\nWEAPON_FIRE\nARTILLERY_FIRE") + error("First argument of function 'script.on_fire_event' must be a valid FireEvent! Check the FTL_HS.log file for more information.", 2) + end + FireEvent:add(func, priority) +end + +end diff --git a/data/vertex_module/particlesInit.lua b/data/vertex_module/particlesInit.lua new file mode 100644 index 0000000..f4fa351 --- /dev/null +++ b/data/vertex_module/particlesInit.lua @@ -0,0 +1,406 @@ +mods.vertexparts = {} + +local vter = mods.vertexutil.vter +local Children = mods.vertexdata.Children +local parse_xml_bool = mods.vertexdata.parse_xml_bool +local INT_MAX = 2147483647 + +---------------------- +-- HELPER FUNCTIONS -- +---------------------- +local function seconds_per_tick() + return Hyperspace.FPS.SpeedFactor/16 +end + +local function rand_range(min, max) + return (Hyperspace.random32()/INT_MAX)*(max - min) + min +end + +local function lerp(a, b, amount) + return a + (b - a)*amount +end + +local function node_get_value(node, errorMsg) + if not node then error(errorMsg, 2) end + local ret = node:value() + if not ret then error(errorMsg, 2) end + return ret +end + +local function node_get_number(node, errorMsg) + if not node then error(errorMsg, 2) end + local ret = tonumber(node:value()) + if not ret then error(errorMsg, 2) end + return ret +end + +local function node_get_number_default(node, default) + if not node then return default end + local ret = tonumber(node:value()) + if not ret then return default end + return ret +end + +local function node_get_bool_default(node, default) + if not node then return default end + local ret = node:value() + if not ret then return default end + return parse_xml_bool(ret) +end + +----------------- +-- DEFENITIONS -- +----------------- +mods.vertexparts.particleLayers = {} +local particleLayers = mods.vertexparts.particleLayers + +mods.vertexparts.particles = {} +local particles = mods.vertexparts.particles + +mods.vertexparts.particleTypes = {} +local particleTypes = mods.vertexparts.particleTypes + +mods.vertexparts.emitterEvents = {} +local emitterEvents = mods.vertexparts.emitterEvents + +mods.vertexparts.particleEmitters = {} +local particleEmitters = mods.vertexparts.particleEmitters +particleEmitters.activeEmitters = {} + +---------------------- +-- PARTICLE TRACKER -- +---------------------- +particleLayers.TOP = 0 +particleLayers.BOTTOM = 1 + +function particles:Create(typeName, x, y, space, layer, rotate, mirror) + local particle = {} + table.insert(self, particle) + particle.type = particleTypes[typeName] + if not particle.type then error(tostring(typeName).." particle type does not exist!") end + particle.x = x + particle.y = y + particle.space = space + particle.layer = layer + particle.rotate = rotate + particle.mirror = mirror + particle.anim = Hyperspace.Global.GetInstance():GetAnimationControl():GetAnimation(particle.type.anim.name) + particle.anim.position.x = -particle.anim.info.frameWidth/2 + particle.anim.position.y = -particle.anim.info.frameHeight/2 + if particle.type.anim.random then + particle.anim:SetCurrentFrame(Hyperspace.random32()%particle.anim.info.numFrames) + end + if particle.type.anim.animated then + particle.anim.tracker.loop = true + particle.anim:Start(false) + end + local col = particle.type.colors[1] + particle.color = Graphics.GL_Color(col.r, col.g, col.b, col.a) + particle.lifetime = rand_range(particle.type.lifetime.min, particle.type.lifetime.max) + particle.lifeRemaining = particle.lifetime + particle.speed = rand_range(particle.type.speed.min, particle.type.speed.max) + if mirror then + particle.angleSign = -1 + if particle.rotate then + particle.xscaleSign = 1 + particle.yscaleSign = -1 + else + particle.xscaleSign = -1 + particle.yscaleSign = 1 + end + else + particle.angleSign = 1 + particle.xscaleSign = 1 + particle.yscaleSign = 1 + end + particle.direction = particle.angleSign*rand_range(particle.type.direction.min, particle.type.direction.max) + particle.orientation = particle.angleSign*rand_range(particle.type.orientation.min, particle.type.orientation.max) + if rotate then + particle.direction = particle.direction - particle.angleSign*90 + particle.orientation = particle.orientation - particle.angleSign*90 + particle.orientationOffset = 0 + else + particle.orientationOffset = 90 + -- for some reason the direction needs an offset of 180 degrees if we're mirrored but not rotated + -- angleSign math is a hacky way to avoid checking if mirror + particle.direction = particle.direction + (particle.angleSign/2 + 1.5)*180 + end + particle.scale = rand_range(particle.type.scale.min, particle.type.scale.max) +end + +function particles:Update() + for index, particle in ipairs(self) do + particle.lifeRemaining = particle.lifeRemaining - seconds_per_tick() + if particle.lifeRemaining <= 0 then + table.remove(self, index) + else + local lifeProgress = 1 - particle.lifeRemaining/particle.lifetime + if particle.type.anim.animated then + if particle.type.anim.lifetime then + particle.anim:SetProgress(lifeProgress) + else + particle.anim:Update() + end + end + local numColors = #(particle.type.colors) + if numColors > 1 then + local colorProgress = (numColors - 1)*lifeProgress + local firstColIndex = math.floor(colorProgress) + 1 + colorProgress = colorProgress%1 + local col1 = particle.type.colors[firstColIndex] + local col2 = particle.type.colors[firstColIndex + 1] + particle.color.r = lerp(col1.r, col2.r, colorProgress) + particle.color.g = lerp(col1.g, col2.g, colorProgress) + particle.color.b = lerp(col1.b, col2.b, colorProgress) + particle.color.a = lerp(col1.a, col2.a, colorProgress) + end + local dirRadians = math.rad(particle.direction) + particle.x = particle.x + math.cos(dirRadians)*particle.speed*seconds_per_tick() + particle.y = particle.y + -math.sin(dirRadians)*particle.speed*seconds_per_tick() + particle.speed = particle.speed + particle.type.speed.increment*seconds_per_tick() + particle.direction = particle.direction + particle.type.direction.increment*particle.angleSign*seconds_per_tick() + particle.orientation = particle.orientation + particle.type.orientation.increment*particle.angleSign*seconds_per_tick() + particle.scale = particle.scale + particle.type.scale.increment*seconds_per_tick() + end + end +end + +function particles:Render(space, layer) + for index, particle in ipairs(self) do + if space == particle.space and layer == particle.layer then + Graphics.CSurface.GL_PushMatrix() + Graphics.CSurface.GL_Translate(particle.x, particle.y) + local angle = particle.orientation + if particle.type.orientation.relative then + angle = angle + particle.direction - particle.orientationOffset + end + Graphics.CSurface.GL_Rotate(-angle, 0, 0) + Graphics.CSurface.GL_Scale(particle.xscaleSign*particle.scale, particle.yscaleSign*particle.scale, 1) + particle.anim:OnRender(1, particle.color, false) + Graphics.CSurface.GL_PopMatrix() + end + end +end + +-------------------- +-- PARTICLE TYPES -- +-------------------- +function particleTypes:ParseNew(particleTypeNode) + local partTypeName = node_get_value(particleTypeNode:first_attribute("name"), "particleType requires a name!") + log("Parsing particleType tag "..partTypeName) + local partType = {} + self[partTypeName] = partType + + -- Animation + partType.anim = {} + local animNode = particleTypeNode:first_node("anim") + if not animNode then error("particleType requires an anim tag!") end + partType.anim.name = node_get_value(animNode, "particleType anim tag requires a value!") + partType.anim.animated = node_get_bool_default(animNode:first_attribute("animated"), false) + partType.anim.random = node_get_bool_default(animNode:first_attribute("random"), false) + partType.anim.lifetime = node_get_bool_default(animNode:first_attribute("lifetime"), false) + + -- Colors + partType.colors = {} + local colorsNode = particleTypeNode:first_node("colors") + if not colorsNode then + table.insert(partType.colors, {r = 1, g = 1, b = 1, a = 1}) + else + for colorNode in Children(colorsNode) do + local rVal = node_get_number(colorNode:first_attribute("r"), "particleType color tag requires a red value!")/255 + local gVal = node_get_number(colorNode:first_attribute("g"), "particleType color tag requires a green value!")/255 + local bVal = node_get_number(colorNode:first_attribute("b"), "particleType color tag requires a blue value!")/255 + local aVal = node_get_number(colorNode:first_attribute("a"), "particleType color tag requires an alpha value!") + table.insert(partType.colors, {r = rVal, g = gVal, b = bVal, a = aVal}) + end + if #(partType.colors) <= 0 then error("particleType colors tag requires at least one color!") end + end + + -- Lifetime + partType.lifetime = {} + local lifetimeNode = particleTypeNode:first_node("lifetime") + if not lifetimeNode then error("particleType requires an lifetime tag!") end + partType.lifetime.min = node_get_number(lifetimeNode:first_attribute("min"), "particleType lifetime tag requires a valid minimum!") + if partType.lifetime.min < 0 then error("particleType lifetime minimum must be positive!") end + partType.lifetime.max = node_get_number(lifetimeNode:first_attribute("max"), "particleType lifetime tag requires a valid maximum!") + if partType.lifetime.min > partType.lifetime.max then error("particleType lifetime maximum must be greater than or equal to minimum!") end + + -- Speed + partType.speed = {} + local speedNode = particleTypeNode:first_node("speed") + if not speedNode then + partType.speed.min = 0 + partType.speed.max = 0 + partType.speed.increment = 0 + else + partType.speed.min = node_get_number(speedNode:first_attribute("min"), "particleType speed tag requires a valid minimum!") + partType.speed.max = node_get_number(speedNode:first_attribute("max"), "particleType speed tag requires a valid maximum!") + if partType.speed.min > partType.speed.max then error("particleType speed maximum must be greater than or equal to minimum!") end + partType.speed.increment = node_get_number_default(speedNode:first_attribute("increment"), 0) + end + + -- Direction + partType.direction = {} + local directionNode = particleTypeNode:first_node("direction") + if not directionNode then + if speedNode then error("particleType with speed tag also requires a valid direction tag!") end + partType.direction.min = 0 + partType.direction.max = 0 + partType.direction.increment = 0 + else + partType.direction.min = node_get_number(directionNode:first_attribute("min"), "particleType direction tag requires a valid minimum!") + partType.direction.max = node_get_number(directionNode:first_attribute("max"), "particleType direction tag requires a valid maximum!") + if partType.direction.min > partType.direction.max then error("particleType direction maximum must be greater than or equal to minimum!") end + partType.direction.increment = node_get_number_default(directionNode:first_attribute("increment"), 0) + end + + -- Orientation + partType.orientation = {} + local orientationNode = particleTypeNode:first_node("orientation") + if not orientationNode then + partType.orientation.min = 0 + partType.orientation.max = 0 + partType.orientation.increment = 0 + partType.orientation.relative = false + else + partType.orientation.min = node_get_number(orientationNode:first_attribute("min"), "particleType orientation tag requires a valid minimum!") + partType.orientation.max = node_get_number(orientationNode:first_attribute("max"), "particleType orientation tag requires a valid maximum!") + if partType.orientation.min > partType.orientation.max then error("particleType orientation maximum must be greater than or equal to minimum!") end + partType.orientation.increment = node_get_number_default(orientationNode:first_attribute("increment"), 0) + partType.orientation.relative = node_get_bool_default(orientationNode:first_attribute("relative"), false) + end + + -- Scale + partType.scale = {} + local scaleNode = particleTypeNode:first_node("scale") + if not scaleNode then + partType.scale.min = 1 + partType.scale.max = 1 + partType.scale.increment = 0 + else + partType.scale.min = node_get_number(scaleNode:first_attribute("min"), "particleType scale tag requires a valid minimum!") + partType.scale.max = node_get_number(scaleNode:first_attribute("max"), "particleType scale tag requires a valid maximum!") + if partType.scale.min > partType.scale.max then error("particleType scale maximum must be greater than or equal to minimum!") end + partType.scale.increment = node_get_number_default(scaleNode:first_attribute("increment"), 0) + end +end + +----------------------- +-- PARTICLE EMITTERS -- +----------------------- +emitterEvents.FIRE = 0 +emitterEvents.EXPLOSION = 1 + +-- TODO: add shapes, add EXPLOSION event +function particleEmitters:ParseNew(particleEmitterNode, weaponName) + local partEmitter = {} + local baseEmitterName = nil + if weaponName then + log("Parsing particleEmitter tag in weapon "..weaponName) + baseEmitterName = particleEmitterNode:first_attribute("name") + if baseEmitterName then -- Give tables with parents a metatable that makes them draw values from their parent as defaults + baseEmitterName = baseEmitterName:value() + local baseEmitter = self[baseEmitterName] + if not baseEmitter then error("particleEmitter "..baseEmitterName.." does not exist in animations xml!") end + setmetatable(partEmitter, { + __index = function(t, k) + return baseEmitter[k] + end + }) + end + local weaponEmitters = self.activeEmitters[weaponName] + if not weaponEmitters then + weaponEmitters = {} + self.activeEmitters[weaponName] = weaponEmitters + end + table.insert(weaponEmitters, partEmitter) + else + local partEmitterName = node_get_value(particleEmitterNode:first_attribute("name"), "particleEmitter requires a name!") + log("Parsing particleEmitter tag "..partEmitterName) + if partEmitterName == "activeEmitters" then error("particleEmitter name must not be activeEmitters!") end + self[partEmitterName] = partEmitter + end + + -- Type + local function get_type() + partEmitter.type = node_get_value(particleEmitterNode:first_node("type"), "particleEmitter requires a valid type!") + end + if baseEmitterName then pcall(get_type) else get_type() end + + -- Event + local function get_event() + partEmitter.on = emitterEvents[node_get_value(particleEmitterNode:first_node("on"), "particleEmitter requires a valid event!")] + if not partEmitter.on then error("particleEmitter requires a valid event!") end + end + if baseEmitterName then pcall(get_event) else get_event() end + + -- Location + local default = nil + if not baseEmitterName then default = 0 end + partEmitter.x = node_get_number_default(particleEmitterNode:first_node("x"), default) + partEmitter.y = node_get_number_default(particleEmitterNode:first_node("y"), default) + + -- Count + if not baseEmitterName then default = 1 end + partEmitter.count = node_get_number_default(particleEmitterNode:first_node("count"), default) + + -- Layer + if not baseEmitterName then default = false end + local renderUnderShip = node_get_bool_default(particleEmitterNode:first_node("renderUnderShip"), default) + if renderUnderShip ~= nil then + if renderUnderShip then + partEmitter.layer = particleLayers.BOTTOM + else + partEmitter.layer = particleLayers.TOP + end + end +end + +-- TODO: add shapes, add EXPLOSION event +function particleEmitters:Emit(emitter, event, weapon) + if event == emitter.on then + -- Calculate weapon coodinates with emitter offset + local weaponAnim = weapon.weaponVisual + local emitPointX, emitPointY + do + local ship = Hyperspace.Global.GetInstance():GetShipManager(weapon.iShipId).ship + local shipGraph = Hyperspace.ShipGraph.GetShipInfo(weapon.iShipId) + local slideOffset = weaponAnim:GetSlide() + emitPointX = ship.shipImage.x + shipGraph.shipBox.x + weaponAnim.renderPoint.x + slideOffset.x + emitPointY = ship.shipImage.y + shipGraph.shipBox.y + weaponAnim.renderPoint.y + slideOffset.y + end + local vertMod = 1 + if weapon.mount.mirror then vertMod = -1 end + if weapon.mount.rotate then + emitPointX = emitPointX - emitter.y + weaponAnim.mountPoint.y + emitPointY = emitPointY + (emitter.x - weaponAnim.mountPoint.x)*vertMod + else + emitPointX = emitPointX + (emitter.x - weaponAnim.mountPoint.x)*vertMod + emitPointY = emitPointY + emitter.y - weaponAnim.mountPoint.y + end + + -- Emit particles + for i = 1, emitter.count do + particles:Create(emitter.type, emitPointX, emitPointY, weapon.iShipId, emitter.layer, weapon.mount.rotate, weapon.mount.mirror) + end + end +end + +-------------------------- +-- PARSE ANIMATIONS XML -- +-------------------------- +local animationFiles = { + "data/animations.xml", + "data/dlcAnimations.xml", +} +for _, file in ipairs(animationFiles) do + local doc = RapidXML.xml_document(file) + for node in Children(doc:first_node("FTL")) do + if node:name() == "particleType" then + particleTypes:ParseNew(node) + elseif node:name() == "particleEmitter" then + particleEmitters:ParseNew(node) + end + end + doc:clear() +end diff --git a/data/vertex_module/tags/particleEmitters.lua b/data/vertex_module/tags/particleEmitters.lua new file mode 100644 index 0000000..b921fac --- /dev/null +++ b/data/vertex_module/tags/particleEmitters.lua @@ -0,0 +1,69 @@ +------------- +-- IMPORTS -- +------------- +local weaponInfo = mods.vertexdata.weaponInfo +local droneInfo = mods.vertexdata.droneInfo +local customTagsAll = mods.vertexdata.customTagsAll +local customTagsWeapons = mods.vertexdata.customTagsWeapons +local customTagsDrones = mods.vertexdata.customTagsDrones +local Children = mods.vertexdata.Children +local parse_xml_bool = mods.vertexdata.parse_xml_bool +local tag_add_all = mods.vertexdata.tag_add_all +local tag_add_weapons = mods.vertexdata.tag_add_weapons +local tag_add_drones = mods.vertexdata.tag_add_drones + +local vter = mods.vertexutil.vter +local under_mind_system = mods.vertexutil.under_mind_system +local can_be_mind_controlled = mods.vertexutil.can_be_mind_controlled +local get_ship_crew_point = mods.vertexutil.get_ship_crew_point +local get_adjacent_rooms = mods.vertexutil.get_adjacent_rooms +local get_room_at_location = mods.vertexutil.get_room_at_location +local crew_data = mods.vertexutil.crew_data + +local particleLayers = mods.vertexparts.particleLayers +local particles = mods.vertexparts.particles +local particleTypes = mods.vertexparts.particleTypes +local emitterEvents = mods.vertexparts.emitterEvents +local particleEmitters = mods.vertexparts.particleEmitters + +------------ +-- PARSER -- +------------ +local function parser(node, weaponName) + for particleEmitterNode in Children(node) do + particleEmitters:ParseNew(particleEmitterNode, weaponName) + end +end + +----------- +-- LOGIC -- +----------- +local function logic() + -- Render and update particles + script.on_render_event(Defines.RenderEvents.SHIP, function(ship) + particles:Render(ship.iShipId, particleLayers.BOTTOM) + end, function(ship) + particles:Render(ship.iShipId, particleLayers.TOP) + end) + script.on_internal_event(Defines.InternalEvents.ON_TICK, function(ship) + if not Hyperspace.Global.GetInstance():GetCApp().world.space.gamePaused then + particles:Update() + end + end) + + -- Emitter fire event + local function emitter_fire(ship, weapon, projectile) + local weaponEmitters = particleEmitters.activeEmitters[weapon.blueprint.name] + if weaponEmitters then + for i, emitter in ipairs(weaponEmitters) do + particleEmitters:Emit(emitter, emitterEvents.FIRE, weapon) + end + end + end + script.on_fire_event(Defines.FireEvents.WEAPON_FIRE, emitter_fire) + script.on_fire_event(Defines.FireEvents.ARTILLERY_FIRE, function(ship, artillery, projectile) + emitter_fire(ship, artillery.projectileFactory, projectile) + end) +end + +tag_add_weapons("particleEmitters", parser, logic) diff --git a/data/vertex_module/xmlDataRead.lua b/data/vertex_module/xmlDataRead.lua index b2bf2a4..7317863 100644 --- a/data/vertex_module/xmlDataRead.lua +++ b/data/vertex_module/xmlDataRead.lua @@ -28,7 +28,7 @@ for _, file in ipairs(blueprintFiles) do customTagsAll[tag].hooked = true end log("Found "..tag.." tag for weapon "..thisWeaponName) - thisWeaponInfo[tag] = customTagsAll[tag].parser(wepNode) + thisWeaponInfo[tag] = customTagsAll[tag].parser(wepNode, thisWeaponName) end if customTagsWeapons[tag] then if not customTagsWeapons[tag].hooked then @@ -37,7 +37,7 @@ for _, file in ipairs(blueprintFiles) do customTagsWeapons[tag].hooked = true end log("Found "..tag.." tag for weapon "..thisWeaponName) - thisWeaponInfo[tag] = customTagsWeapons[tag].parser(wepNode) + thisWeaponInfo[tag] = customTagsWeapons[tag].parser(wepNode, thisWeaponName) end end for tag in pairs(customTagsAll) do @@ -63,7 +63,7 @@ for _, file in ipairs(blueprintFiles) do customTagsAll[tag].hooked = true end log("Found "..tag.." tag for drone "..thisDroneName) - thisDroneInfo[tag] = customTagsAll[tag].parser(droneNode) + thisDroneInfo[tag] = customTagsAll[tag].parser(droneNode, thisDroneName) end if customTagsDrones[tag] then if not customTagsDrones[tag].hooked then @@ -72,7 +72,7 @@ for _, file in ipairs(blueprintFiles) do customTagsDrones[tag].hooked = true end log("Found "..tag.." tag for drone "..thisDroneName) - thisDroneInfo[tag] = customTagsDrones[tag].parser(droneNode) + thisDroneInfo[tag] = customTagsDrones[tag].parser(droneNode, thisDroneName) end end for tag in pairs(customTagsAll) do From a036d75f0153b4e2a252c87ffad0320f8148802b Mon Sep 17 00:00:00 2001 From: ChronoVortex Date: Sun, 28 May 2023 08:44:50 -0700 Subject: [PATCH 4/8] updated no power fix for inferno compatibility --- data/vertex_module/tags/noPowerFix.lua | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/data/vertex_module/tags/noPowerFix.lua b/data/vertex_module/tags/noPowerFix.lua index 5fe4be8..33a6147 100644 --- a/data/vertex_module/tags/noPowerFix.lua +++ b/data/vertex_module/tags/noPowerFix.lua @@ -31,14 +31,19 @@ end -- LOGIC -- ----------- local function logic() - local function fix_no_power_projectiles(weapons) - for weapon in vter(weapons) do - local noPowerFix = weaponInfo[weapon.blueprint.name]["noPowerFix"] - if noPowerFix and noPowerFix.doFix then - local projectile = weapon:GetProjectile() - while projectile do - Hyperspace.Global.GetInstance():GetCApp().world.space.projectiles:push_back(projectile) - projectile = weapon:GetProjectile() + local function fix_no_power_projectiles(weapons, ship) + if not ship.weaponSystem:Powered() then + for weapon in vter(weapons) do + if weapon.requiredPower <= 0 then + local noPowerFix = weaponInfo[weapon.blueprint.name]["noPowerFix"] + if noPowerFix and noPowerFix.doFix then + local projectile = weapon:GetProjectile() + while projectile do + Hyperspace.Global.GetInstance():GetCApp().world.space.projectiles:push_back(projectile) + Defines.FireEvents.WEAPON_FIRE(ship, weapon, projectile) + projectile = weapon:GetProjectile() + end + end end end end @@ -46,11 +51,11 @@ local function logic() script.on_internal_event(Defines.InternalEvents.ON_TICK, function() local weaponsPlayer = nil pcall(function() weaponsPlayer = Hyperspace.ships.player.weaponSystem.weapons end) - if weaponsPlayer then fix_no_power_projectiles(weaponsPlayer) end + if weaponsPlayer then fix_no_power_projectiles(weaponsPlayer, Hyperspace.ships.player) end local weaponsEnemy = nil pcall(function() weaponsEnemy = Hyperspace.ships.enemy.weaponSystem.weapons end) - if weaponsEnemy then fix_no_power_projectiles(weaponsEnemy) end + if weaponsEnemy then fix_no_power_projectiles(weaponsEnemy, Hyperspace.ships.enemy) end end) end From 2e14777c419413f81272fbba2c37c4de7da82648 Mon Sep 17 00:00:00 2001 From: ChronoVortex Date: Wed, 14 Jun 2023 22:33:34 -0700 Subject: [PATCH 5/8] implemented emitter shapes --- data/vertex_module/particlesInit.lua | 77 ++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/data/vertex_module/particlesInit.lua b/data/vertex_module/particlesInit.lua index f4fa351..62c4bbc 100644 --- a/data/vertex_module/particlesInit.lua +++ b/data/vertex_module/particlesInit.lua @@ -63,6 +63,9 @@ local particleTypes = mods.vertexparts.particleTypes mods.vertexparts.emitterEvents = {} local emitterEvents = mods.vertexparts.emitterEvents +mods.vertexparts.emitterShapes = {} +local emitterShapes = mods.vertexparts.emitterShapes + mods.vertexparts.particleEmitters = {} local particleEmitters = mods.vertexparts.particleEmitters particleEmitters.activeEmitters = {} @@ -291,7 +294,10 @@ end emitterEvents.FIRE = 0 emitterEvents.EXPLOSION = 1 --- TODO: add shapes, add EXPLOSION event +emitterShapes.LINE = 0 +emitterShapes.RECT = 1 +emitterShapes.ELLIPSE = 2 + function particleEmitters:ParseNew(particleEmitterNode, weaponName) local partEmitter = {} local baseEmitterName = nil @@ -354,9 +360,58 @@ function particleEmitters:ParseNew(particleEmitterNode, weaponName) partEmitter.layer = particleLayers.TOP end end + + -- Shape + local shapeNode = particleEmitterNode:first_node("shape") + if shapeNode then + partEmitter.shape = emitterShapes[node_get_value(shapeNode, "particleEmitter shape tag requires a valid value!")] + end + + -- Shape size + if baseEmitterName or not partEmitter.shape then + partEmitter.width = node_get_number_default(particleEmitterNode:first_node("w"), nil) + partEmitter.height = node_get_number_default(particleEmitterNode:first_node("h"), nil) + else + partEmitter.width = node_get_number(particleEmitterNode:first_node("w"), "particleEmitter with shape tag requires a valid width!") + partEmitter.height = node_get_number(particleEmitterNode:first_node("h"), "particleEmitter with shape tag requires a valid height!") + end + if partEmitter.width then + if partEmitter.shape == emitterShapes.LINE then + if partEmitter.width == 0 and partEmitter.width == 0 then error("particleEmitter with LINE shape must have non-zero width or height!") end + else + if partEmitter.width == 0 or partEmitter.width == 0 then error("particleEmitter with non-LINE shape must have non-zero width and height!") end + end + end +end + +local function generate_shape_offset(emitter) + local offsetX, offsetY + if emitter.shape == emitterShapes.LINE then + local offsetMagnitude = Hyperspace.random32()/INT_MAX + offsetX = offsetMagnitude*emitter.width + offsetY = offsetMagnitude*emitter.height + elseif emitter.shape == emitterShapes.RECT then + if emitter.width < 0 then + offsetX = rand_range(emitter.width, 0) + else + offsetX = rand_range(0, emitter.width) + end + if emitter.height < 0 then + offsetY = rand_range(emitter.height, 0) + else + offsetY = rand_range(0, emitter.height) + end + elseif emitter.shape == emitterShapes.ELLIPSE then + local halfWidth = emitter.width/2 + local r = halfWidth*math.sqrt(Hyperspace.random32()/INT_MAX) + local theta = 2*math.pi*(Hyperspace.random32()/INT_MAX) + offsetX = halfWidth + r*math.cos(theta) + offsetY = (halfWidth + r*math.sin(theta))*(emitter.height/emitter.width) + end + return offsetX, offsetY end --- TODO: add shapes, add EXPLOSION event +-- TODO: add EXPLOSION event function particleEmitters:Emit(emitter, event, weapon) if event == emitter.on then -- Calculate weapon coodinates with emitter offset @@ -380,8 +435,22 @@ function particleEmitters:Emit(emitter, event, weapon) end -- Emit particles - for i = 1, emitter.count do - particles:Create(emitter.type, emitPointX, emitPointY, weapon.iShipId, emitter.layer, weapon.mount.rotate, weapon.mount.mirror) + if emitter.shape then + if weapon.mount.rotate then + for i = 1, emitter.count do + local offsetX, offsetY = generate_shape_offset(emitter) + particles:Create(emitter.type, emitPointX - offsetY, emitPointY + offsetX*vertMod, weapon.iShipId, emitter.layer, weapon.mount.rotate, weapon.mount.mirror) + end + else + for i = 1, emitter.count do + local offsetX, offsetY = generate_shape_offset(emitter) + particles:Create(emitter.type, emitPointX + offsetX*vertMod, emitPointY + offsetY, weapon.iShipId, emitter.layer, weapon.mount.rotate, weapon.mount.mirror) + end + end + else + for i = 1, emitter.count do + particles:Create(emitter.type, emitPointX, emitPointY, weapon.iShipId, emitter.layer, weapon.mount.rotate, weapon.mount.mirror) + end end end end From e637ecc6c16718c153d0fdea36c0191e61a67641 Mon Sep 17 00:00:00 2001 From: ChronoVortex Date: Wed, 14 Jun 2023 23:50:42 -0700 Subject: [PATCH 6/8] added explosion event --- data/vertex_module/particlesInit.lua | 54 ++++++++++++-------- data/vertex_module/tags/particleEmitters.lua | 30 +++++++++-- 2 files changed, 60 insertions(+), 24 deletions(-) diff --git a/data/vertex_module/particlesInit.lua b/data/vertex_module/particlesInit.lua index 62c4bbc..fcdcb96 100644 --- a/data/vertex_module/particlesInit.lua +++ b/data/vertex_module/particlesInit.lua @@ -411,45 +411,57 @@ local function generate_shape_offset(emitter) return offsetX, offsetY end --- TODO: add EXPLOSION event -function particleEmitters:Emit(emitter, event, weapon) +function particleEmitters:Emit(emitter, event, weapon, posX, posY, shipId) if event == emitter.on then - -- Calculate weapon coodinates with emitter offset - local weaponAnim = weapon.weaponVisual - local emitPointX, emitPointY - do + local emitPointX = posX or 0 + local emitPointY = posY or 0 + local rotate = false + local mirror = false + local vertMod = 1 + if weapon then + rotate = weapon.mount.rotate + mirror = weapon.mount.mirror + if mirror then vertMod = -1 end + + -- Calculate weapon coodinates + local weaponAnim = weapon.weaponVisual local ship = Hyperspace.Global.GetInstance():GetShipManager(weapon.iShipId).ship local shipGraph = Hyperspace.ShipGraph.GetShipInfo(weapon.iShipId) local slideOffset = weaponAnim:GetSlide() - emitPointX = ship.shipImage.x + shipGraph.shipBox.x + weaponAnim.renderPoint.x + slideOffset.x - emitPointY = ship.shipImage.y + shipGraph.shipBox.y + weaponAnim.renderPoint.y + slideOffset.y - end - local vertMod = 1 - if weapon.mount.mirror then vertMod = -1 end - if weapon.mount.rotate then - emitPointX = emitPointX - emitter.y + weaponAnim.mountPoint.y - emitPointY = emitPointY + (emitter.x - weaponAnim.mountPoint.x)*vertMod + emitPointX = emitPointX + ship.shipImage.x + shipGraph.shipBox.x + weaponAnim.renderPoint.x + slideOffset.x + emitPointY = emitPointY + ship.shipImage.y + shipGraph.shipBox.y + weaponAnim.renderPoint.y + slideOffset.y + + -- Add emitter and mount point offset + if rotate then + emitPointX = emitPointX - emitter.y + weaponAnim.mountPoint.y + emitPointY = emitPointY + (emitter.x - weaponAnim.mountPoint.x)*vertMod + else + emitPointX = emitPointX + (emitter.x - weaponAnim.mountPoint.x)*vertMod + emitPointY = emitPointY + emitter.y - weaponAnim.mountPoint.y + end else - emitPointX = emitPointX + (emitter.x - weaponAnim.mountPoint.x)*vertMod - emitPointY = emitPointY + emitter.y - weaponAnim.mountPoint.y + emitPointX = emitPointX + emitter.x + emitPointY = emitPointY + emitter.y end - + -- Emit particles if emitter.shape then - if weapon.mount.rotate then + if rotate then for i = 1, emitter.count do local offsetX, offsetY = generate_shape_offset(emitter) - particles:Create(emitter.type, emitPointX - offsetY, emitPointY + offsetX*vertMod, weapon.iShipId, emitter.layer, weapon.mount.rotate, weapon.mount.mirror) + particles:Create(emitter.type, emitPointX - offsetY, emitPointY + offsetX*vertMod, shipId or weapon.iShipId, + emitter.layer, rotate, mirror) end else for i = 1, emitter.count do local offsetX, offsetY = generate_shape_offset(emitter) - particles:Create(emitter.type, emitPointX + offsetX*vertMod, emitPointY + offsetY, weapon.iShipId, emitter.layer, weapon.mount.rotate, weapon.mount.mirror) + particles:Create(emitter.type, emitPointX + offsetX*vertMod, emitPointY + offsetY, shipId or weapon.iShipId, + emitter.layer, rotate, mirror) end end else for i = 1, emitter.count do - particles:Create(emitter.type, emitPointX, emitPointY, weapon.iShipId, emitter.layer, weapon.mount.rotate, weapon.mount.mirror) + particles:Create(emitter.type, emitPointX, emitPointY, shipId or weapon.iShipId, emitter.layer, rotate, mirror) end end end diff --git a/data/vertex_module/tags/particleEmitters.lua b/data/vertex_module/tags/particleEmitters.lua index b921fac..3b054e6 100644 --- a/data/vertex_module/tags/particleEmitters.lua +++ b/data/vertex_module/tags/particleEmitters.lua @@ -52,7 +52,7 @@ local function logic() end) -- Emitter fire event - local function emitter_fire(ship, weapon, projectile) + local function emitter_fire(weapon) local weaponEmitters = particleEmitters.activeEmitters[weapon.blueprint.name] if weaponEmitters then for i, emitter in ipairs(weaponEmitters) do @@ -60,9 +60,33 @@ local function logic() end end end - script.on_fire_event(Defines.FireEvents.WEAPON_FIRE, emitter_fire) + script.on_fire_event(Defines.FireEvents.WEAPON_FIRE, function(ship, weapon, projectile) + emitter_fire(weapon) + end) script.on_fire_event(Defines.FireEvents.ARTILLERY_FIRE, function(ship, artillery, projectile) - emitter_fire(ship, artillery.projectileFactory, projectile) + emitter_fire(artillery.projectileFactory) + end) + + -- Emitter explosion event + local function emitter_explosion(projectile) + local weaponEmitters = particleEmitters.activeEmitters[projectile.extend.name] + if weaponEmitters then + for i, emitter in ipairs(weaponEmitters) do + particleEmitters:Emit(emitter, emitterEvents.EXPLOSION, nil, projectile.position.x, projectile.position.y, projectile.currentSpace) + end + end + end + script.on_internal_event(Defines.InternalEvents.DRONE_COLLISION, function(drone, projectile, damage, response) + emitter_explosion(projectile) + end) + script.on_internal_event(Defines.InternalEvents.PROJECTILE_COLLISION, function(thisProjectile, otherProjectile, damage, response) + emitter_explosion(thisProjectile) + end) + script.on_internal_event(Defines.InternalEvents.SHIELD_COLLISION, function(ship, projectile, damage, response) + emitter_explosion(projectile) + end) + script.on_internal_event(Defines.InternalEvents.DAMAGE_AREA_HIT, function(ship, projectile, damage, response) + emitter_explosion(projectile) end) end From 30ab65469318ec50c42fcfcd46b0407136c49738 Mon Sep 17 00:00:00 2001 From: ChronoVortex Date: Mon, 26 Jun 2023 07:44:10 -0700 Subject: [PATCH 7/8] updated for HS 1.4 --- data/hyperspace.xml.append | 1 - data/vertex_module/callbacks.lua | 124 ------------------- data/vertex_module/tags/particleEmitters.lua | 28 ++--- data/vertex_module/util.lua | 4 +- mod-appendix/metadata.xml | 2 +- 5 files changed, 16 insertions(+), 143 deletions(-) delete mode 100644 data/vertex_module/callbacks.lua diff --git a/data/hyperspace.xml.append b/data/hyperspace.xml.append index 59586fc..d612369 100644 --- a/data/hyperspace.xml.append +++ b/data/hyperspace.xml.append @@ -4,7 +4,6 @@ data/vertex_module/util.lua - data/vertex_module/callbacks.lua data/vertex_module/xmlDataInit.lua data/vertex_module/particlesInit.lua diff --git a/data/vertex_module/callbacks.lua b/data/vertex_module/callbacks.lua deleted file mode 100644 index 45a30c7..0000000 --- a/data/vertex_module/callbacks.lua +++ /dev/null @@ -1,124 +0,0 @@ -local vter = mods.vertexutil.vter - --- Make sure that inferno core was patched before this mod if it was patched -local infernoInstalled = false -if mods.inferno then infernoInstalled = true end -script.on_load(function() - if not infernoInstalled and mods.inferno then - Hyperspace.ErrorMessage("Vertex Tags and Utility Functions was patched before Inferno-Core! Please re-patch your mods, and make sure to put Inferno-Core first!") - end -end) - --- Implement the inferno weapon fire callbacks ourselves if inferno isn't patched -if not infernoInstalled then - -local callback_runner = { - identifier = "", - - __call = function(self, ...) - for key, functable in ipairs(self) do - for _, func in ipairs(functable) do - local success, res = pcall(func, ...) - if not success then - log(string.format("Failed to call function in callback '%s' due to error:\n %s", self.identifier, res)) - elseif res then - return - end - end - end - end, - - add = function(self, func, priority) - local priority = priority or 0 - if type(priority) ~= 'number' or math.floor(priority) ~= priority then - error("Priority argument must be an integer!", 3) - end - local priority = priority or 0 - local ptab = nil - for _,v in ipairs(self) do - if getmetatable(v).priority == priority then - ptab = v break - end - end - if not ptab then - ptab = setmetatable({}, {priority = priority}) - table.insert(self, ptab) - end - if type(func) ~= 'function' then - error("Second argument must be a function!", 3) - end - table.insert(ptab, func) - table.sort(self, function(lesser,greater) - return getmetatable(lesser).priority > getmetatable(greater).priority -- larger numbers come first - end) - end, - - new = function(self, o) - o = o or {} - self.__index = self - setmetatable(o, self) - return o - end, -} - -Defines.FireEvents = { - WEAPON_FIRE = callback_runner:new({identifier = "Defines.FireEvents.WEAPON_FIRE"}), - ARTILLERY_FIRE = callback_runner:new({identifier = "Defines.FireEvents.ARTILLERY_FIRE"}), -} - -script.on_internal_event(Defines.InternalEvents.ON_TICK, function() - for i = 0, 1 do - local weapons = nil - local ship = Hyperspace.Global.GetInstance():GetShipManager(i) - pcall(function() weapons = ship.weaponSystem.weapons end) - if weapons and ship.weaponSystem:Powered() then - for weapon in vter(weapons) do - while true do - local projectile = weapon:GetProjectile() - if projectile then - Hyperspace.Global.GetInstance():GetCApp().world.space.projectiles:push_back(projectile) - Defines.FireEvents.WEAPON_FIRE(ship, weapon, projectile) - else - break - end - end - end - end - end -end, 1000) - -script.on_internal_event(Defines.InternalEvents.ON_TICK, function() - for i = 0, 1 do - local artilleries = nil - local ship = Hyperspace.Global.GetInstance():GetShipManager(i) - pcall(function() artilleries = ship.artillerySystems end) - if artilleries then - for artillery in vter(artilleries) do - while true do - local weapon = artillery.projectileFactory - local projectile = weapon:GetProjectile() - if projectile then - Hyperspace.Global.GetInstance():GetCApp().world.space.projectiles:push_back(projectile) - Defines.FireEvents.ARTILLERY_FIRE(ship, artillery, projectile) - else - break - end - end - end - end - end -end, 1000) - -function script.on_fire_event(FireEvent, func, priority) - local validEvent = false - for _, v in pairs(Defines.FireEvents) do - if v == FireEvent then validEvent = true break end - end - if not validEvent then - log("\n\nValid FireEvents:\nWEAPON_FIRE\nARTILLERY_FIRE") - error("First argument of function 'script.on_fire_event' must be a valid FireEvent! Check the FTL_HS.log file for more information.", 2) - end - FireEvent:add(func, priority) -end - -end diff --git a/data/vertex_module/tags/particleEmitters.lua b/data/vertex_module/tags/particleEmitters.lua index 3b054e6..47e5945 100644 --- a/data/vertex_module/tags/particleEmitters.lua +++ b/data/vertex_module/tags/particleEmitters.lua @@ -52,27 +52,25 @@ local function logic() end) -- Emitter fire event - local function emitter_fire(weapon) - local weaponEmitters = particleEmitters.activeEmitters[weapon.blueprint.name] - if weaponEmitters then - for i, emitter in ipairs(weaponEmitters) do - particleEmitters:Emit(emitter, emitterEvents.FIRE, weapon) + script.on_internal_event(Defines.InternalEvents.PROJECTILE_FIRE, function(projectile, weapon) + if weapon then + local weaponEmitters = particleEmitters.activeEmitters[weapon.blueprint.name] + if weaponEmitters then + for i, emitter in ipairs(weaponEmitters) do + particleEmitters:Emit(emitter, emitterEvents.FIRE, weapon) + end end end - end - script.on_fire_event(Defines.FireEvents.WEAPON_FIRE, function(ship, weapon, projectile) - emitter_fire(weapon) - end) - script.on_fire_event(Defines.FireEvents.ARTILLERY_FIRE, function(ship, artillery, projectile) - emitter_fire(artillery.projectileFactory) end) -- Emitter explosion event local function emitter_explosion(projectile) - local weaponEmitters = particleEmitters.activeEmitters[projectile.extend.name] - if weaponEmitters then - for i, emitter in ipairs(weaponEmitters) do - particleEmitters:Emit(emitter, emitterEvents.EXPLOSION, nil, projectile.position.x, projectile.position.y, projectile.currentSpace) + if projectile then + local weaponEmitters = particleEmitters.activeEmitters[projectile.extend.name] + if weaponEmitters then + for i, emitter in ipairs(weaponEmitters) do + particleEmitters:Emit(emitter, emitterEvents.EXPLOSION, nil, projectile.position.x, projectile.position.y, projectile.currentSpace) + end end end end diff --git a/data/vertex_module/util.lua b/data/vertex_module/util.lua index 6f17e43..71e7b69 100644 --- a/data/vertex_module/util.lua +++ b/data/vertex_module/util.lua @@ -1,5 +1,5 @@ -if not (Hyperspace.version and Hyperspace.version.major == 1 and Hyperspace.version.minor >= 3) then - error("Incorrect Hyperspace version detected! Vertex Tags and Utility Functions requires Hyperspace 1.3+") +if not (Hyperspace.version and Hyperspace.version.major == 1 and Hyperspace.version.minor >= 4) then + error("Incorrect Hyperspace version detected! Vertex Tags and Utility Functions requires Hyperspace 1.4+") end mods.vertexutil = {} diff --git a/mod-appendix/metadata.xml b/mod-appendix/metadata.xml index 2dcdf6f..578166f 100644 --- a/mod-appendix/metadata.xml +++ b/mod-appendix/metadata.xml @@ -2,7 +2,7 @@ <![CDATA[ Vertex Tags and Utility Functions ]]> - + Date: Mon, 26 Jun 2023 07:51:03 -0700 Subject: [PATCH 8/8] revert changes to noPowerFix --- data/vertex_module/tags/noPowerFix.lua | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/data/vertex_module/tags/noPowerFix.lua b/data/vertex_module/tags/noPowerFix.lua index 33a6147..5fe4be8 100644 --- a/data/vertex_module/tags/noPowerFix.lua +++ b/data/vertex_module/tags/noPowerFix.lua @@ -31,19 +31,14 @@ end -- LOGIC -- ----------- local function logic() - local function fix_no_power_projectiles(weapons, ship) - if not ship.weaponSystem:Powered() then - for weapon in vter(weapons) do - if weapon.requiredPower <= 0 then - local noPowerFix = weaponInfo[weapon.blueprint.name]["noPowerFix"] - if noPowerFix and noPowerFix.doFix then - local projectile = weapon:GetProjectile() - while projectile do - Hyperspace.Global.GetInstance():GetCApp().world.space.projectiles:push_back(projectile) - Defines.FireEvents.WEAPON_FIRE(ship, weapon, projectile) - projectile = weapon:GetProjectile() - end - end + local function fix_no_power_projectiles(weapons) + for weapon in vter(weapons) do + local noPowerFix = weaponInfo[weapon.blueprint.name]["noPowerFix"] + if noPowerFix and noPowerFix.doFix then + local projectile = weapon:GetProjectile() + while projectile do + Hyperspace.Global.GetInstance():GetCApp().world.space.projectiles:push_back(projectile) + projectile = weapon:GetProjectile() end end end @@ -51,11 +46,11 @@ local function logic() script.on_internal_event(Defines.InternalEvents.ON_TICK, function() local weaponsPlayer = nil pcall(function() weaponsPlayer = Hyperspace.ships.player.weaponSystem.weapons end) - if weaponsPlayer then fix_no_power_projectiles(weaponsPlayer, Hyperspace.ships.player) end + if weaponsPlayer then fix_no_power_projectiles(weaponsPlayer) end local weaponsEnemy = nil pcall(function() weaponsEnemy = Hyperspace.ships.enemy.weaponSystem.weapons end) - if weaponsEnemy then fix_no_power_projectiles(weaponsEnemy, Hyperspace.ships.enemy) end + if weaponsEnemy then fix_no_power_projectiles(weaponsEnemy) end end) end