I have this code currently on 3 types of bullets, so I can change their damage, speed, and blast radius. You can see I have a few custom places in the header where I tell which bullet this is and what bullet factory to use. But the rest of the code is the same. So should I use it like this and make 3 different scripts? Or should I rework it a bit to but all the types of bullets in one script so I only have one script to manage. Keep in mind I may be adding more bullet types in the future and those may or may not change the behavior in the script below. Like one my be a mortar type shell so it’s trajectory may be different. or one my be a straight line laser that doesn’t follow the target.
I have this same issue for the tower placement as well, but there I don’t think they’ll have different behaviors.
local entmgr = require("modules.entity_manager")
local attributes = require("modules.attributes")
local COLLISION_RESPONSE = hash("collision_response")
local TRIGGER_RESPONSE = hash("trigger_response")
local CONTACT_POINT_RESPONSE = hash("contact_point_response")
local ENEMY = hash("enemy")
local OWN_GROUP = hash("bullet")
local BULLET_NAME = "twin_rocket"
local DAMAGE = attributes.get_attribute(BULLET_NAME, "damage")
local SPEED = attributes.get_attribute(BULLET_NAME, "speed")
local function target_enemy(self, enemy_id)
self.curr_pos = go.get_world_position()
if go.exists(enemy_id) then
self.target_pos = go.get_world_position(enemy_id)
self.in_target_zone = true
local direction = vmath.vector3(self.target_pos.x - self.curr_pos.x, self.target_pos.y - self.curr_pos.y, 0)
-- Calculate the angle
local angle = math.atan2(direction.y, direction.x)
-- Adjust the angle to point in the direction of the target
angle = angle + math.pi/2
angle = angle + math.pi
-- Set the rotation of the turret
go.set_rotation(vmath.quat_rotation_z(angle))
end
end
local function explode_missile(self)
self.target_id = nil -- Clear the target
-- Additional logic to handle lost target (e.g., redirect missile)
sprite.play_flipbook("#sprite", "explosion", function()
go.cancel_animations(".")
go.delete() -- deletes the missile game object
end)
end
function init(self)
entmgr.subscribe(msg.url("#"), entmgr.enemies)
self.target_id = nil -- Add this to store the target ID
end
function on_message(self, message_id, message, sender)
-- Handle the set_target message
if message_id == hash("set_target") then
DAMAGE = attributes.get_attribute(BULLET_NAME, "damage")
SPEED = attributes.get_attribute(BULLET_NAME, "speed")
self.target_id = message.target_id
return
elseif message_id == hash("entity destroyed") and message.group == entmgr.enemies then
if message.entity == self.target_id then
explode_missile(self)
end
end
if message_id == TRIGGER_RESPONSE then
if message.group == ENEMY and message.own_group == OWN_GROUP then
explode_missile(self)
msg.post(message.other_id, "take_damage", { damage_amount = DAMAGE } )
end
end
end
function update(self, dt)
if go.exists(self.target_id) and self.target_id ~= nil then --this appears redundant but it is needed to make this work properly
target_enemy(self, self.target_id)
local missile_pos = go.get_world_position(self.missile_id)
local target_pos = go.get_world_position(self.target_id)
local dir = vmath.normalize(self.target_pos - missile_pos)
-- move the missile towards the target
missile_pos = missile_pos + dir * SPEED * dt
go.set_position(missile_pos, self.missile_id)
--rotate missile toward target
local angle = math.atan2(dir.y, dir.x)
angle = angle - math.pi/2
--move missile toward target
go.set_rotation(vmath.quat_rotation_z(angle), self.missile_id)
else
self.target_id = nil -- need to set self.target_id to nil so the If statement can trap the enemy object being deleted
end
end
function final(self)
-- Unsubscribe when the script is deleted
entmgr.unsubscribe(msg.url("#"), entmgr.enemies)
end
This is the tower script. It has a few more variables that need to be changed.
local entmgr = require("modules.entity_manager")
local attributes = require("modules.attributes")
local COLLISION_RESPONSE = hash("collision_response")
local TRIGGER_RESPONSE = hash("trigger_response")
local CONTACT_POINT_RESPONSE = hash("contact_point_response")
local ENEMY = hash("enemy")
local TOWER = hash("tower")
local FACTORY = "/bullet_factories#twin_rocket"
local TOWER_NAME = "twin_rocket_tower"
local RELOAD_DELAY = nil
local AMMO_MAGAZINE = nil
local tower = {}
tower.targets = {} -- Table to store targets
local shots_fired = 0 --number of bullets that can fire at once
local tower_ready = true
-- Call this function when a target enters the zone
local function onTargetEnter(target)
tower.addTarget(target)
end
-- Call this function when a target leaves the zone or is destroyed
local function onTargetExit(target)
tower.removeTarget(target)
end
local function safe_get_world_position(id)
return go.get_world_position(id)
end
local function target_enemy(self, enemy_id)
self.curr_pos = go.get_world_position()
if go.exists(enemy_id) then
self.target_id_pos = go.get_world_position(enemy_id)
self.in_target_zone = true
local direction = vmath.vector3(self.target_id_pos.x - self.curr_pos.x, self.target_id_pos.y - self.curr_pos.y, 0)
-- Calculate the angle
local angle = math.atan2(direction.y, direction.x)
-- Adjust the angle to point in the direction of the target
angle = angle + math.pi/2
angle = angle + math.pi
-- Set the rotation of the turret
go.set_rotation(vmath.quat_rotation_z(angle))
end
end
local function late_init()
RELOAD_DELAY = attributes.get_attribute(TOWER_NAME, "reload_delay")
AMMO_MAGAZINE = attributes.get_attribute(TOWER_NAME, "magazine_size")
end
function init(self)
entmgr.subscribe(msg.url("#"), entmgr.enemies)
self.timer = 0 -- Add this line to initialize the timer
self.reload_delay = RELOAD_DELAY
self.in_target_zone = false
self.target_id = nil
-- Set up a timer to call late_init after 2 seconds
timer.delay(1, false, function()
late_init()
end)
end
function update(self, dt)
if tower_ready and #tower.targets > 0 then
tower.shootAtTarget(self)
end
end
function on_message(self, message_id, message, sender)
if message_id == TRIGGER_RESPONSE then
if message.enter and message.group == ENEMY then
tower.addTarget(message.other_id)
elseif message.enter == false and message.group == ENEMY then
tower.removeTarget(message.other_id)
end
elseif message_id == hash("entity destroyed") and message.group == entmgr.enemies then
tower.removeTarget(message.entity)
end
end
function tower.addTarget(target)
table.insert(tower.targets, target)
-- Additional logic if needed when a target is added
end
function tower.removeTarget(target)
for i, t in ipairs(tower.targets) do
if t == target then
table.remove(tower.targets, i)
break
end
end
-- Additional logic if needed when a target is removed
end
function tower.chooseTarget()
-- Example: Choose the first target in the list
if #tower.targets > 0 then
return tower.targets[1]
else
return nil
end
end
function tower.shootAtTarget(self)
if not tower_ready or #tower.targets == 0 then return end
local target = tower.chooseTarget()
if target then
self.target_id = target -- Set the current target
target_enemy(self, target) -- Aim at the target
self.bullet_id = factory.create(FACTORY, self.curr_pos) -- create the bullet
msg.post(self.bullet_id, "set_target", { target_id = target })
shots_fired = shots_fired + 1
if shots_fired >= AMMO_MAGAZINE then
tower_ready = false
timer.delay(RELOAD_DELAY, false, function()
tower_ready = true
shots_fired = 0
end)
end
end
end
function final(self)
-- Unsubscribe from notifications when the script is destroyed
entmgr.unsubscribe(msg.url("#"), entmgr.enemies)
end