pixelbath 2 weeks ago
parent
commit
05bb929a69
8 changed files with 360 additions and 46 deletions
  1. 240 18
      cylshooter/main.lua
  2. BIN
      megamang/sprites.aseprite
  3. 31 0
      rpg/README.md
  4. BIN
      rpg/rpg.aseprite
  5. 58 18
      rpg/rpg.lua
  6. 30 8
      rpg/tic-rpg.tiled-session
  7. BIN
      shmup/mockup3.aseprite
  8. 1 2
      spiritbreakers/README.md

+ 240 - 18
cylshooter/main.lua

@@ -18,13 +18,10 @@
 -- 3. render fg map offscreen
 -- 3. render fg map offscreen
 -- 4. memcpy/pix the fg to the screen?
 -- 4. memcpy/pix the fg to the screen?
 
 
-
-
-local world = {}
-local entities = {}
+local t=0
 
 
 local player = {
 local player = {
-    x=0, y=0,
+    x=140, y=70,
     vx=0, vy=0,
     vx=0, vy=0,
     speed=1.5,
     speed=1.5,
 }
 }
@@ -55,34 +52,259 @@ function player:update()
 	self.y = self.y + self.vy
 	self.y = self.y + self.vy
 end
 end
 
 
-function player:draw()
-    rect(self.x, self.y, 8, 8, 4)
+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
 end
 
 
-function world:add_entity(en)
-    table.insert(entities, en)
+function Shot.speed:set(value)
+    return function()
+        Shot.speed = value
+    end
 end
 end
 
 
-function world:update()
-	for key,val in ipairs(entities) do
-        entities[key]:update()
+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
 end
 end
 
 
-function world:draw()
-	for key,val in ipairs(entities) do
-        entities[key]:draw()
+function Shot.velocity:set(speed, direction)
+    return function()
+        Shot.speed = speed
+        Shot.direction = direction
     end
     end
 end
 end
 
 
-world:add_entity(player)
+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()
 function TIC()
     cls()
     cls()
-    world:update()
-    world:draw()
+	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
 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>
 -- <PALETTE>
 -- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57
 -- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57
 -- </PALETTE>
 -- </PALETTE>

BIN
megamang/sprites.aseprite


+ 31 - 0
rpg/README.md

@@ -1,4 +1,35 @@
 # RPG
 # RPG
+
+## Mechanics
+### Equipment
+- Weapon
+- Armor
+- Accessory
+- Element
+
+### Items
+- Healing, light/strong
+- MP recover?
+
+### Activities
+- Gambling?
+
+## Todo List
+- [ ] Player movement
+- [ ] Walkable tiles
+- [ ] Map: Riverhaven castle
+- [ ] Map: World map
+- [ ] Tiled converter
+- [ ] Sprites: Font serifs
+- [ ] Sprites: World map
+- [ ] Dialogue system
+    - [ ] Dialogue: Letter spacing
+    - [ ] Dialogue: Word wrapping
+    - [ ] Dialogue: Button to scroll/continue
+    - [ ] Dialogue: Branching / variables
+- [ ] Tiles (castle): empty throne, throne room rug
+- [ ] Sprites: guard, townsfolk, king, archmage, malarch
+
 All `backticked-items` are considered variables that should be interpreted and replaced in the following outline. A list of item types are provided:
 All `backticked-items` are considered variables that should be interpreted and replaced in the following outline. A list of item types are provided:
 - `nature-orb`: The nature orb, known as the Bloom of Eternal Spring.
 - `nature-orb`: The nature orb, known as the Bloom of Eternal Spring.
 - `nature-orb-level`: Greenhollow Forest contains a dungeon which contains `nature-orb`, and is guarded by `nature-orb-boss`.
 - `nature-orb-level`: Greenhollow Forest contains a dungeon which contains `nature-orb`, and is guarded by `nature-orb-boss`.

BIN
rpg/rpg.aseprite


+ 58 - 18
rpg/rpg.lua

@@ -2,15 +2,57 @@
 -- author:  pixelbath
 -- author:  pixelbath
 -- desc:    Fantasy RPG
 -- desc:    Fantasy RPG
 -- site:    https://www.pixelbath.com
 -- site:    https://www.pixelbath.com
--- license: MIT License (change this to your license of choice)
+-- license: MIT License
 -- version: 0.1
 -- version: 0.1
 -- script:  lua
 -- script:  lua
 -- saveid: eldora_v1
 -- saveid: eldora_v1
 
 
-local p_opt = {
+-- buttons
+local UP,DOWN,LEFT,RIGHT = 0,1,2,3
+local A,B,X,Y = 4,5,6,7 -- z,x,a,s
+
+world={
+	ents={},
+	add_entity=function(ent)
+        table.insert(world.ents, ent)
+        return
+    end,
+}
+player={
+	x=0,y=0,dx=0,dy=0,spr=258,
+    alt=0,alt_t=0,
+    hp=0, mp=0, lvl=0, exp=0,
+	upd=function(self)
+		self.dx,self.dy=0,0
+        -- TODO: center player and move world
+		if btn(LEFT) then self.dx=-1 end
+		if btn(RIGHT) then self.dx=1 end
+		if btn(UP) then self.dy=-1 end
+		if btn(DOWN) then self.dy=1 end
+        self.x=self.x+self.dx
+        self.y=self.y+self.dy
+
+		-- collision(self)
+	end,
+	drw=function(self)
+        if alt==1 then
+		    spr(self.spr,self.x,self.y,11,1,0,0,2,2)
+        else
+		    spr(self.spr+2,self.x,self.y,11,1,0,0,2,2)
+        end
+	end,
+}
+
+world.add_entity(player)
+
+
+local g_opt = {
     ["music_vol"] = 16,
     ["music_vol"] = 16,
     ["sfx_vol"] = 16,
     ["sfx_vol"] = 16,
     ["difficulty"] = 1,
     ["difficulty"] = 1,
+}
+local growth = {
+
 }
 }
 -- write all p_opt values to pmem
 -- write all p_opt values to pmem
 function write_p_opt()
 function write_p_opt()
@@ -23,6 +65,12 @@ end
 
 
 local talked_b = false
 local talked_b = false
 
 
+local dlg_by_char = {
+    ["SMITH"] = {
+
+    },
+}
+
 local dlg = {
 local dlg = {
     ["intro_01"] = {
     ["intro_01"] = {
         { "SMITH", "You're being summoned to the Royal Court." },
         { "SMITH", "You're being summoned to the Royal Court." },
@@ -121,9 +169,10 @@ end
 function wrt(text, x, y, color)
 function wrt(text, x, y, color)
     set1bpp()
     set1bpp()
     poke4(pal_map_addr + 1, color)
     poke4(pal_map_addr + 1, color)
-    font(text, x, y, 0, 8, 8)
+    local textw = font(text, x, y, 0, 8, 8)
     poke4(pal_map_addr + 1, 1)
     poke4(pal_map_addr + 1, 1)
     set4bpp()
     set4bpp()
+    return textw
 end
 end
 
 
 testlines = {
 testlines = {
@@ -141,23 +190,14 @@ function TIC()
     -- wrt("`abcdefghijklmnopqrstuvwxyz{|}~", 10, 30, 10)
     -- wrt("`abcdefghijklmnopqrstuvwxyz{|}~", 10, 30, 10)
     -- wrt("We will learn to make this look less childish over time.", 10, 40, 15)
     -- wrt("We will learn to make this look less childish over time.", 10, 40, 15)
 
 
-    draw_dialog_box()
-
-    if talked_b then
-        dlg_key = "intro_blacksmith"
-        dlg_idx = 1
-        talked_b = false
-    end
-
-    cur_dialog = display_dialog(dlg_key)
-    -- TODO: a counter/flag to indicate when text is ready
-
-    if btnp(5) then
-        callback_dialog(dlg_key)
-    end
-    wrt(cur_dialog, 10, 20, 9)
+    -- draw_dialog_box()
     -- draw_dialog_text(testlines)
     -- draw_dialog_text(testlines)
 
 
+	for i,v in ipairs(world.ents) do --draw all entities
+		if v.upd then v:upd() end
+		if v.drw then v:drw() end
+	end
+
     -- read_p_opt()
     -- read_p_opt()
 end
 end
 
 

+ 30 - 8
rpg/tic-rpg.tiled-session

@@ -3,30 +3,52 @@
         "height": 4300,
         "height": 4300,
         "width": 2
         "width": 2
     },
     },
-    "activeFile": "rpg-tiles.tsx",
+    "activeFile": "Riverhaven-castle.tmx",
     "expandedProjectPaths": [
     "expandedProjectPaths": [
+        "."
     ],
     ],
     "fileStates": {
     "fileStates": {
+        "Riverhaven-castle.tmx": {
+            "scale": 2.1457,
+            "selectedLayer": 1,
+            "viewCenter": {
+                "x": 118.60931164654889,
+                "y": 150.3006012024048
+            }
+        },
         "rpg-tiles.tsx": {
         "rpg-tiles.tsx": {
             "scaleInDock": 2,
             "scaleInDock": 2,
             "scaleInEditor": 1
             "scaleInEditor": 1
+        },
+        "rpg-town-tiles.tsx": {
+            "scaleInDock": 2,
+            "scaleInEditor": 1
         }
         }
     },
     },
+    "last.exportedFilePath": "/Users/michael/repos/tic80-stuff/rpg",
+    "last.externalTilesetPath": "/Users/michael/repos/tic80-stuff/rpg",
     "last.imagePath": "/Users/michael/repos/tic80-stuff/rpg",
     "last.imagePath": "/Users/michael/repos/tic80-stuff/rpg",
-    "map.height": 90,
-    "map.tileHeight": 8,
-    "map.tileWidth": 8,
-    "map.width": 120,
+    "map.height": 50,
+    "map.lastUsedExportFilter": "Lua files (*.lua)",
+    "map.lastUsedFormat": "tmx",
+    "map.tileHeight": 16,
+    "map.tileWidth": 16,
+    "map.width": 30,
     "openFiles": [
     "openFiles": [
+        "rpg-town-tiles.tsx",
+        "Riverhaven-castle.tmx",
         "rpg-tiles.tsx"
         "rpg-tiles.tsx"
     ],
     ],
     "project": "tic-rpg.tiled-project",
     "project": "tic-rpg.tiled-project",
     "recentFiles": [
     "recentFiles": [
-        "rpg-tiles.tsx"
+        "rpg-town-tiles.tsx",
+        "rpg-tiles.tsx",
+        "Riverhaven-castle.tmx"
     ],
     ],
+    "tileset.lastUsedFilter": "Tiled tileset files (*.tsx *.xml)",
     "tileset.lastUsedFormat": "tsx",
     "tileset.lastUsedFormat": "tsx",
     "tileset.tileSize": {
     "tileset.tileSize": {
-        "height": 8,
-        "width": 8
+        "height": 16,
+        "width": 16
     }
     }
 }
 }

BIN
shmup/mockup3.aseprite


+ 1 - 2
spiritbreakers/README.md

@@ -38,8 +38,7 @@ Each car is ranked in three categories.
 - Economy - $2000
 - Economy - $2000
     - Holds up to six tools. Very light. Slips a long way when hit.
     - Holds up to six tools. Very light. Slips a long way when hit.
 - Hearse (canonical) - $4800
 - Hearse (canonical) - $4800
-    - Holds up to nine tools. Medium weight. Slips some when 
- hit.
+    - Holds up to nine tools. Medium weight. Slips some when hit.
 - Common - $6000
 - Common - $6000
     - Holds up to eleven tools. Heavy. Slips some when hit.
     - Holds up to eleven tools. Heavy. Slips some when hit.
 - Sportscar - $12000
 - Sportscar - $12000