-- title:   cylshooter
-- author:  pixelbath
-- desc:    short description
-- site:    website link
-- license: MIT License (change this to your license of choice)
-- version: 0.1
-- script:  lua


-- The idea:
-- like Resogun, a cylindrical shooter
-- 1. render map
-- 2. fix column-by-column, raycast style
-- 3. how to draw background?

-- 1. render bg map offscreen
-- 2. memcpy to onscreen
-- 3. render fg map offscreen
-- 4. memcpy/pix the fg to the screen?

local t=0

local player = {
    x=140, y=70,
    vx=0, vy=0,
    speed=1.5,
}

function player:update()
	self.vx = 0
	self.vy = 0
	if btn(0) then
		self.vy=-self.speed
	end
	if btn(1) then
		self.vy=self.speed
	end
	if btn(2) then
		self.vx=-self.speed
	end
	if btn(3) then
		self.vx=self.speed
	end

	-- slow down diagonal movements
	if self.vx * self.vy ~= 0 then
		self.vx = self.vx * 0.707
		self.vy = self.vy * 0.707
	end

	self.x = self.x + self.vx
	self.y = self.y + self.vy
end

local Random = {
    angle = {
        between = function(min, max)
            return min + math.random() * (max - min)
        end
    }
}


local Shot = {
    x = 20,
    y = 20,
    speed = {},
    direction = {},
	position = {},
	velocity = {},
    bullets = {},
}

function Shot:new()
    local shot = setmetatable({}, { __index = Shot })
    shot.bullets = {}
    return shot
end

function Shot.speed:set(value)
    return function()
        Shot.speed = value
    end
end

function Shot.position:set(distance, bearing)
    return function()
        Shot.x = math.cos(math.rad(bearing)) * distance
        Shot.y = math.sin(math.rad(bearing)) * distance
    end
end

function Shot.velocity:set(speed, direction)
    return function()
        Shot.speed = speed
        Shot.direction = direction
    end
end

function Shot.direction:add(degrees)
    return function()
        Shot.direction = Shot.direction + degrees
    end
end

-- Pattern manager
local PatternManager = {
    activePatterns = {},
    windowWidth = 800,  -- Default window size
    windowHeight = 600  -- Default window size
}

function PatternManager:new(windowWidth, windowHeight)
    local manager = setmetatable({}, { __index = PatternManager })
    manager.activePatterns = {}
    manager.windowWidth = windowWidth or 800
    manager.windowHeight = windowHeight or 600
    return manager
end


-- Initialize the pattern manager with window dimensions
patternManager = PatternManager:new(240,136)



function TIC()
    cls()
	if t==0 then
		-- Start both patterns
		patternManager:startPattern("firstShot")
		patternManager:startPattern("secondShot")
	end

    -- player:update()
    patternManager:update()
    patternManager:draw()

	t = t + 1
end


-- Pattern system functions
local function aim()
    return function()
		local dx = player.x - Shot.x
		local dy = player.y - Shot.y
        Shot.direction = math.deg(math.atan2(dy, dx))
    end
end

local function line(count, options)
    return function()
        local speedChange = options.shotSpeedChange or 0
        local currentSpeed = Shot.speed
        
        for i = 1, count do
			currentSpeed = currentSpeed + (speedChange * (count - 1))
            table.insert(Shot.bullets, {
                x = Shot.x,
                y = Shot.y,
                speed = currentSpeed,
                direction = Shot.direction,
                vanishTime = nil
            })
        end
    end
end

local function wait(frames)
    return function()
        for i = 1, frames do
            coroutine.yield()
        end
    end
end

local function fire(actions)
    return function()
        local bullet = {
            x = Shot.x,
            y = Shot.y,
            speed = Shot.speed,
            direction = Shot.direction,
            vanishTime = nil
        }
        
        -- for _, action in ipairs(actions) do
        --     if type(action) == "function" then
        --         action(bullet)
        --     end
        -- end
        
        table.insert(Shot.bullets, bullet)
    end
end

local function vanish()
    return function(bullet)
        bullet.vanishTime = 0
    end
end

local function loop(actions)
    return function()
        while true do
            for _, action in ipairs(actions) do
                if type(action) == "function" then
                    action()
                end
            end
        end
    end
end

-- Pattern definitions
local patterns = {
    firstShot = {
        Shot.speed:set(2),
        loop({
            -- Shot.position:set(150, Random.angle.between(0, 360)),
            aim(),
            line(8, { shotSpeedChange = 0.5 }),
            wait(100)
        })
    },
    
    secondShot = {
        Shot.velocity:set(1, 90),
        loop({
            fire({
                wait(60),
                vanish()
            }),
            Shot.direction:add(12),
            wait(1)
        })
    },
}

function PatternManager:startPattern(patternName)
    local pattern = patterns[patternName]
    if not pattern then
        error("Pattern " .. patternName .. " not found")
    end
    
    local co = coroutine.create(function()
        for _, action in ipairs(pattern) do
            if type(action) == "function" then
                action()
            end
        end
    end)
    
    table.insert(self.activePatterns, {
        coroutine = co,
        name = patternName
    })
end

function PatternManager:update()
    -- Update all active patterns
    for i = #self.activePatterns, 1, -1 do
        local pattern = self.activePatterns[i]
        if coroutine.status(pattern.coroutine) ~= "dead" then
            local success, err = coroutine.resume(pattern.coroutine)
            if not success then
                error("Error in pattern " .. pattern.name .. ": " .. err)
            end
        else
            table.remove(self.activePatterns, i)
        end
    end
    
    -- Update bullet positions
    for i = #Shot.bullets, 1, -1 do
        local bullet = Shot.bullets[i]
        if bullet.vanishTime then
            bullet.vanishTime = bullet.vanishTime + 1
            if bullet.vanishTime >= 30 then
                table.remove(Shot.bullets, i)
            end
        else
            bullet.x = bullet.x + math.cos(math.rad(bullet.direction)) * bullet.speed
            bullet.y = bullet.y + math.sin(math.rad(bullet.direction)) * bullet.speed
            
            -- Remove bullets that are off screen
            if bullet.x < -50 or bullet.x > self.windowWidth + 50 or
               bullet.y < -50 or bullet.y > self.windowHeight + 50 then
                table.remove(Shot.bullets, i)
            end
        end
    end
end

function PatternManager:draw()
    -- Draw all bullets
    for _, bullet in ipairs(Shot.bullets) do
		circ(bullet.x, bullet.y, 2, 6)
    end
end






-- <PALETTE>
-- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57
-- </PALETTE>

