09-17-2025, 10:36 AM
(09-17-2025, 10:35 AM)Umbry Wrote: Here's some code I wrote for the framework of an ULTRAKILL-type boss
Code:-- Boss AI Framework (Advanced)
local Boss = {}
Boss.__index = Boss
-- A Boss requires an initial state, a health pool, and a set of attack patterns.
function Boss.new(options)
local boss = {
name = options.name or "Cyber-Fiend",
current_health = options.max_health or 1000,
max_health = options.max_health or 1000,
state = "Idle",
attack_cooldown_timer = 0,
current_phase = 1,
active_target = options.player, -- The player instance.
position = options.position,
attack_patterns = {
-- Phase 1 (100% - 51% health)
{
{name = "bullet_hell", cooldown = 5, duration = 3},
{name = "laser_beam", cooldown = 10, duration = 2},
},
-- Phase 2 (50% - 1% health)
{
{name = "bullet_hell", cooldown = 4, duration = 2},
{name = "laser_beam", cooldown = 8, duration = 2.5},
{name = "charge_attack", cooldown = 6, duration = 1.5},
},
-- Death animation, or other final action
{
{name = "self_destruct", cooldown = 0, duration = 5},
}
}
}
setmetatable(boss, Boss)
return boss
end
-- Main update loop. This should be called every frame by your game engine.
function Boss:update(dt)
-- Check if the boss is dead
if self.current_health <= 0 then
self:die()
return
end
-- Update boss phase based on health
self:update_phase()
-- State machine logic
if self.state == "Idle" then
self:idle_state(dt)
elseif self.state == "Attacking" then
self:attacking_state(dt)
elseif self.state == "TakingDamage" then
self:taking_damage_state(dt)
end
end
-- A simple function to simulate taking damage.
function Boss:take_damage(amount)
if self.state ~= "Dying" then
self.current_health = self.current_health - amount
print(self.name .. " took " .. amount .. " damage. Health: " .. self.current_health)
-- Trigger a visual or audio effect for taking damage
self.state = "TakingDamage"
-- Add screen shake or hit-stop effects here
end
end
-- Handle the boss's death.
function Boss:die()
self.state = "Dying"
-- Trigger explosion, fall animation, loot drop, etc.
print(self.name .. " has been defeated!")
-- Clean up enemy from the game world
end
-- Internal function to check and update the boss's current phase.
function Boss:update_phase()
local health_percentage = self.current_health / self.max_health
if health_percentage <= 0.5 and self.current_phase == 1 then
self.current_phase = 2
print("Boss enters Phase 2! Prepare for new attacks.")
-- Trigger special phase change animations or effects.
elseif health_percentage <= 0 and self.current_phase < 3 then
self.current_phase = 3
end
end
-- Defines the Idle state behavior.
function Boss:idle_state(dt)
-- Wait for player to be in range
local distance_to_player = self:distance_to(self.active_target.position)
if distance_to_player < 500 then -- Chase range
self.state = "Attacking"
self.attack_cooldown_timer = 0
end
end
-- Defines the Attacking state behavior.
function Boss:attacking_state(dt)
self.attack_cooldown_timer = self.attack_cooldown_timer + dt
local current_patterns = self.attack_patterns[self.current_phase]
local attack_chosen = false
-- Iterate through available attacks and trigger if cooldown is ready
for _, attack in ipairs(current_patterns) do
if self.attack_cooldown_timer >= attack.cooldown then
self:execute_attack(attack)
self.attack_cooldown_timer = 0 -- Reset cooldown for next attack
attack_chosen = true
break -- Only do one attack at a time
end
end
-- If no attack was chosen, continue to move or wait
if not attack_chosen then
self:chase_player()
end
end
-- Defines the TakingDamage state behavior.
function Boss:taking_damage_state(dt)
-- Can add hit stun logic here
self.state = "Attacking" -- Resume attacking after being hit
end
-- Placeholder for specific attack logic.
function Boss:execute_attack(attack)
if attack.name == "bullet_hell" then
print(self.name .. " initiates a bullet hell attack!")
-- Call a function in your game engine to spawn many projectiles
elseif attack.name == "laser_beam" then
print(self.name .. " fires a devastating laser beam!")
-- Call a function in your game engine for a large, single-line attack
elseif attack.name == "charge_attack" then
print(self.name .. " charges the player!")
-- Move the boss rapidly toward the player's last known position
elseif attack.name == "self_destruct" then
print(self.name .. " self-destructs in a massive explosion!")
-- Trigger a large explosion effect and kill the player if they are nearby
end
end
-- Movement logic to chase the player.
function Boss:chase_player()
local dir_x = self.active_target.position.x - self.position.x
local dir_y = self.active_target.position.y - self.position.y
-- Normalize direction vector and move the boss
local distance = math.sqrt(dir_x*dir_x + dir_y*dir_y)
if distance > 1 then
self.position.x = self.position.x + dir_x / distance
self.position.y = self.position.y + dir_y / distance
end
end
-- Simple distance calculation.
function Boss:distance_to(target_pos)
return math.sqrt( (self.position.x - target_pos.x)^2 + (self.position.y - target_pos.y)^2 )
end
return Boss
Here is the Player framework
Code:
-- Player Framework
local Player = {}
Player.__index = Player
-- Constructor to create a new player instance.
function Player.new(options)
local player = {
name = options.name or "Player",
current_health = options.max_health or 100,
max_health = options.max_health or 100,
position = options.position or {x = 0, y = 0},
speed = options.speed or 300, -- Pixels per second
is_alive = true,
is_invincible = false,
invincibility_timer = 0,
invincibility_duration = 1, -- Seconds
collider = {width = 32, height = 32}, -- For collision detection
}
setmetatable(player, Player)
return player
end
-- Main update loop, called every frame.
function Player:update(dt)
if not self.is_alive then
return
end
-- Process player input for movement.
self:handle_input(dt)
-- Update invincibility timer if active.
if self.is_invincible then
self.invincibility_timer = self.invincibility_timer + dt
if self.invincibility_timer >= self.invincibility_duration then
self.is_invincible = false
self.invincibility_timer = 0
print("Invincibility worn off.")
end
end
end
-- Handles player input for movement. This function needs to be adapted
-- for your specific game engine's input system. This example uses
-- Love2D's keyboard functions.
function Player:handle_input(dt)
local dx, dy = 0, 0
-- These are examples, replace with your engine's input handlers.
if love.keyboard.isDown("w") or love.keyboard.isDown("up") then
dy = dy - 1
end
if love.keyboard.isDown("s") or love.keyboard.isDown("down") then
dy = dy + 1
end
if love.keyboard.isDown("a") or love.keyboard.isDown("left") then
dx = dx - 1
end
if love.keyboard.isDown("d") or love.keyboard.isDown("right") then
dx = dx + 1
end
-- Normalize movement vector to prevent faster diagonal movement.
local length = math.sqrt(dx^2 + dy^2)
if length > 0 then
self.position.x = self.position.x + (dx / length) * self.speed * dt
self.position.y = self.position.y + (dy / length) * self.speed * dt
end
end
-- Function to handle the player taking damage.
function Player:take_damage(amount)
if self.is_invincible or not self.is_alive then
return
end
self.current_health = self.current_health - amount
print(self.name .. " took " .. amount .. " damage. Health: " .. self.current_health)
-- Trigger invincibility to prevent instant re-damage.
self.is_invincible = true
self.invincibility_timer = 0
if self.current_health other_entity.position.x and
self.position.y other_entity.position.y
end
return PlayerO
y g g
d
r
a
s
i
l
What miracle is this? This giant tree.
It stands ten thousand feet high
But doesn't touch the ground. Yet it stands.
Its roots must hold the sky.
0
y g g
d
r
a
s
i
l
What miracle is this? This giant tree.
It stands ten thousand feet high
But doesn't touch the ground. Yet it stands.
Its roots must hold the sky.
0