]> git.mcshandy.xyz Git - sumeriangame/commitdiff
Looks like NES-style map movement works
authorRandy McShandy <randy@mcshandy.xyz>
Sun, 22 Feb 2026 14:56:00 +0000 (08:56 -0600)
committerRandy McShandy <randy@mcshandy.xyz>
Sun, 22 Feb 2026 14:56:00 +0000 (08:56 -0600)
main/assets.lua
main/control.lua
main/main.lua
main/player.lua
main/render.lua
main/tiled/test_1.lua
main/tiled/test_1.tmx
main/utils.lua

index 4351fdd26f1911cda8fd383eca1530003a9f6d34..1a05094c1948e8d6e230d7825ca75abde88a85c5 100644 (file)
@@ -24,7 +24,9 @@ PropertyFlag =
   Active = 1,       -- Will be moving around or need other periodic updates
                     -- and should provide its own update function
   Controllable = 2, -- Can be controlled in some way
+  HasSprite = 3,
 }
+assets.PropertyFlag = PropertyFlag
 
 function assets.integrate_object_layers(pmap)
   for k,layer in pairs(pmap.layers) do
@@ -42,13 +44,13 @@ function assets.integrate_object_layers(pmap)
       -- of a map will override the first position an object is drawn in (and worse).
       for ok,ov in pairs(new_layer.objects) do
         local object = assets.get_object(ov.name)
-        if object then
+        if object and utils.flag_set(object.properties, PropertyFlag.HasSprite) then
           object.image = love.graphics.newImage(object.path)
-          pmap[ov.name] = {}
-          -- Tiled does y values from the bottom of the sprite so pull up 1
-          pmap[ov.name].vec = vector.new_xy(ov.x, ov.y - pmap.tileheight)
-          pmap[ov.name].name = ov.name
         end
+        pmap[ov.name] = {}
+        -- Tiled does y values from the bottom of the sprite so pull up 1
+        pmap[ov.name].vec = vector.new_xy(ov.x, ov.y - pmap.tileheight)
+        pmap[ov.name].name = ov.name
       end
 
       new_layer.draw = function(self)
@@ -138,9 +140,10 @@ assets.walk_sfx.size = 3
 -- If relevant, an Object asset's name should match an object as placed
 -- in an Object layer of a Tiled map
 assets.store_object('Player', {
-  properties = (PropertyFlag.Active),
+  properties = bit.bor(PropertyFlag.Active, PropertyFlag.Controllable, PropertyFlag.HasSprite),
   path = 'assets/sprites/gilgamesh.png',
   vec = vector.new_xy(),
+  viewspace = vector.new_xy(),
   update = function(self, dt)
   end
 })
index f4007f401a19c706dff795792438e76107416e04..cddcb138feae6fa7860af7133f2bc05e5f92204c 100644 (file)
@@ -17,19 +17,19 @@ control.keymap.active_keymap = nil
 control.keymap[render.ViewClass.Topdown] = {}
 control.keymap[render.ViewClass.Sidescroll] = {}
 
-control.keymap[render.ViewClass.Topdown]['l'] = { heading = (math.pi / 2) * -0, control = control.ControlType.Movement, }
-control.keymap[render.ViewClass.Topdown]['k'] = { heading = (math.pi / 2) * -1, control = control.ControlType.Movement, }
-control.keymap[render.ViewClass.Topdown]['h'] = { heading = (math.pi / 2) * -2, control = control.ControlType.Movement, }
-control.keymap[render.ViewClass.Topdown]['j'] = { heading = (math.pi / 2) * -3, control = control.ControlType.Movement, }
-control.keymap[render.ViewClass.Topdown]['d'] = { heading = (math.pi / 2) * -0, control = control.ControlType.Movement, }
-control.keymap[render.ViewClass.Topdown]['w'] = { heading = (math.pi / 2) * -1, control = control.ControlType.Movement, }
-control.keymap[render.ViewClass.Topdown]['a'] = { heading = (math.pi / 2) * -2, control = control.ControlType.Movement, }
-control.keymap[render.ViewClass.Topdown]['s'] = { heading = (math.pi / 2) * -3, control = control.ControlType.Movement, }
+control.keymap[render.ViewClass.Topdown]['l'] = { heading = utils.Headings.East,  control = control.ControlType.Movement, }
+control.keymap[render.ViewClass.Topdown]['k'] = { heading = utils.Headings.North, control = control.ControlType.Movement, }
+control.keymap[render.ViewClass.Topdown]['h'] = { heading = utils.Headings.West,  control = control.ControlType.Movement, }
+control.keymap[render.ViewClass.Topdown]['j'] = { heading = utils.Headings.South, control = control.ControlType.Movement, }
+control.keymap[render.ViewClass.Topdown]['d'] = { heading = utils.Headings.East,  control = control.ControlType.Movement, }
+control.keymap[render.ViewClass.Topdown]['w'] = { heading = utils.Headings.North, control = control.ControlType.Movement, }
+control.keymap[render.ViewClass.Topdown]['a'] = { heading = utils.Headings.West,  control = control.ControlType.Movement, }
+control.keymap[render.ViewClass.Topdown]['s'] = { heading = utils.Headings.South, control = control.ControlType.Movement, }
 
-control.keymap[render.ViewClass.Sidescroll]['l'] = { heading = (math.pi / 2) * -0, control = control.ControlType.Movement, }
-control.keymap[render.ViewClass.Sidescroll]['h'] = { heading = (math.pi / 2) * -2, control = control.ControlType.Movement, }
-control.keymap[render.ViewClass.Sidescroll]['a'] = { heading = (math.pi / 2) * -2, control = control.ControlType.Movement, }
-control.keymap[render.ViewClass.Sidescroll]['d'] = { heading = (math.pi / 2) * -0, control = control.ControlType.Movement, }
+control.keymap[render.ViewClass.Sidescroll]['l'] = { heading = utils.Headings.East,  control = control.ControlType.Movement, }
+control.keymap[render.ViewClass.Sidescroll]['h'] = { heading = utils.Headings.West,  control = control.ControlType.Movement, }
+control.keymap[render.ViewClass.Sidescroll]['a'] = { heading = utils.Headings.West,  control = control.ControlType.Movement, }
+control.keymap[render.ViewClass.Sidescroll]['d'] = { heading = utils.Headings.East,  control = control.ControlType.Movement, }
 
 control.keymap['global'] = {}
 control.keymap['global']['q'] = {
index 53edad8ac8a38b82870266c4f1d3a47f1252ef11..d32100edcb6604dcf33f7f47abcaea1f4eef180e 100644 (file)
@@ -52,6 +52,8 @@ function love.draw()
 
   last_color = { love.graphics.getColor() }
   love.graphics.setColor(render.color.dark)
-  love.graphics.print(IntroMessage, math.floor((conf.window.width/16) * 1), math.floor((conf.window.height/16) * 1))
+  local adjust = render.worldspace_to_viewspace(player.vec)
+  love.graphics.rectangle('line', adjust.x, adjust.y, 32, 32)
+  --love.graphics.print(IntroMessage, math.floor((conf.window.width/16) * 1), math.floor((conf.window.height/16) * 1))
   love.graphics.setColor(last_color)
 end
index d0c0370db7c1d719b30449d409a0625fba547b45..c1d2b7a7c7947267243ff3e451415ec2db7df039 100644 (file)
@@ -20,6 +20,8 @@ function player_module.init_controls(player_obj, active_map)
     -- Direction vectors for each heading
     local dx = math.floor(math.cos(value.heading) + 0.5)
     local dy = math.floor(math.sin(value.heading) + 0.5)
+    -- Update the player's heading for use elsewhere
+    player_obj.vec.heading = value.heading
 
     local target_tile_x = current_tile_x + dx
     local target_tile_y = current_tile_y + dy
index fd03337ca0e566e5750b93e7a123cdf7b6d4a421..db59089e01a98232fb3c5d847c289bce4e26c9dc 100644 (file)
@@ -34,9 +34,7 @@ render.map.scale = 2.0
 render.map.active_map = nil
 
 -- A bounding box to mask rendering for the main scene.
--- e.g., anything outside this (or in?) should not be rendered.
--- I don't know if Love supports this but I can always just draw black boxes lol.
--- Look into love.graphics.stencil
+-- e.g., anything outside this should not be rendered.
 render.viewport =
 {
   ox = 0,
@@ -45,14 +43,14 @@ render.viewport =
   ey = conf.window.height,
 }
 
-render.activate_map = function(new_map_name)
+function render.activate_map(new_map_name)
   local success = false
-
   local new_map = assets.get_map(new_map_name)
   if new_map then
     success = true
     render.map.active_map = new_map
     render.active_viewclass = render.ViewClassData[new_map.properties.viewclass]
+    render.map.active_map.first_loop = true
 
     -- Find all objects in the map we're switching to, and update their positional
     -- info with the unique copy stored in each map.
@@ -62,6 +60,8 @@ render.activate_map = function(new_map_name)
         object.vec = object_in_map.vec
         if key == 'Player' then
           render.map.topleft = render.map.active_camera(object)
+        elseif key == 'CameraFocus' then
+          render.map.topleft = render.map.active_camera(object)
         end
       end
     end
@@ -98,6 +98,26 @@ function render.update_viewport()
   return new_viewport
 end
 
+-- Convert an absolute position in the map/gameworld
+-- to where it would show up in the viewport.
+function render.worldspace_to_viewspace(world_position)
+  local view_position = utils.shallow_copy(world_position)
+  local tile = {
+    w = render.map.active_map.tilewidth,
+    h = render.map.active_map.tileheight,
+  }
+
+  local offsets =
+  {
+    x = (render.viewport.ex * math.floor((render.map.scale * world_position.x) / render.viewport.ex)),
+    y = (render.viewport.ey * math.floor((render.map.scale * world_position.y) / render.viewport.ey)),
+  }
+  view_position.x = (render.map.scale * (world_position.x )) + render.viewport.ox - offsets.x
+  view_position.y = (render.map.scale * (world_position.y )) + render.viewport.oy - offsets.y
+
+  return view_position
+end
+
 render.CameraClass = {
   -- Constantly place the player in the dead center of the window
   PlayerInMiddle = 1,
@@ -109,25 +129,57 @@ render.CameraClass = {
 render.map.cameras = {}
 render.map.cameras[render.CameraClass.PlayerInMiddle] =
 function(focus, bounds)
+  if render.map.active_map.first_loop then
+    render.map.active_map.first_loop = false
+  end
+
   local offsets = {}
-  local new_txty =
+  local new_topleft =
   {
     x = (((conf.window.width  / 2) / render.map.scale) - (focus.vec.x)),
     y = (((conf.window.height / 2) / render.map.scale) - (focus.vec.y)),
   }
-  offsets.x = math.fmod(new_txty.x, (render.map.active_map.tilewidth))
-  offsets.y = math.fmod(new_txty.y, (render.map.active_map.tileheight))
-  new_txty.x = new_txty.x - offsets.x
-  new_txty.y = new_txty.y - offsets.y
-  return new_txty
+  offsets.x = math.fmod(new_topleft.x, (render.map.active_map.tilewidth))
+  offsets.y = math.fmod(new_topleft.y, (render.map.active_map.tileheight))
+  new_topleft.x = new_topleft.x - offsets.x
+  new_topleft.y = new_topleft.y - offsets.y
+
+  return new_topleft
 end
 
 render.map.cameras[render.CameraClass.NESFreeRoam] =
 function(focus)
-  -- temp code
-  return focus.vec or { x = 0, y = 0 }
+  local tile = {
+    w = render.map.active_map.tilewidth  * render.map.scale,
+    h = render.map.active_map.tileheight * render.map.scale,
+  }
+
+  -- On the first focusing of a map in this mode, find the map's CameraFocus object
+  -- and use that for our topleft
+  if render.map.active_map.first_loop then
+    render.map.active_map.first_loop = false
+    return
+    {
+      x = render.map.active_map['CameraFocus'].vec.x + (tile.w/2),
+      y = render.map.active_map['CameraFocus'].vec.y + (tile.h),
+    }
+  end
+
+  local new_topleft = render.map.topleft
+  local viewspace_focus = render.worldspace_to_viewspace(focus.vec)
+
+  if ((focus.vec.heading == utils.Headings.East) and viewspace_focus.x == render.viewport.ox) then
+    new_topleft.x = new_topleft.x - (render.viewport.ex / render.map.scale)
+  elseif ((focus.vec.heading == utils.Headings.West) and viewspace_focus.x == render.viewport.ex) then
+    new_topleft.x = new_topleft.x + (render.viewport.ex / render.map.scale)
+  elseif ((focus.vec.heading == utils.Headings.South) and viewspace_focus.y == render.viewport.oy) then
+    new_topleft.y = new_topleft.y - (render.viewport.ey / render.map.scale)
+  elseif ((focus.vec.heading == utils.Headings.North) and viewspace_focus.y == render.viewport.ey) then
+    new_topleft.y = new_topleft.y + (render.viewport.ey / render.map.scale)
+  end
+  return new_topleft
 end
 
-render.map.active_camera = render.map.cameras[render.CameraClass.PlayerInMiddle]
+render.map.active_camera = render.map.cameras[render.CameraClass.NESFreeRoam]
 
 return render
index 3e6823cca3fe23e0eaac9f01a084a614ebfa3f8a..ff8c73f89a368e44316f4e83137256bf726370e0 100644 (file)
@@ -10,7 +10,7 @@ return {
   tilewidth = 16,
   tileheight = 16,
   nextlayerid = 7,
-  nextobjectid = 13,
+  nextobjectid = 14,
   properties = {
     ["viewclass"] = "topdown"
   },
@@ -198,9 +198,9 @@ return {
         51, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
         918, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
         919, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
-        920, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
-        921, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
-        922, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 746, 747, 748, 0, 0, 0, 0, 0, 0, 0, 0, 51,
+        920, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+        921, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+        922, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 746, 747, 748, 0, 0, 0, 0, 0, 0, 0, 0, 1,
         923, 0, 0, 0, 0, 0, 0, 206, 2684354765, 1610612941, 2684354765, 1610612941, 2684354765, 2684354766, 0, 0, 0, 0, 795, 796, 797, 0, 0, 0, 0, 0, 0, 0, 0, 51,
         924, 0, 0, 0, 0, 0, 0, 3221225677, 0, 0, 0, 0, 0, 205, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
         925, 0, 0, 0, 0, 0, 0, 205, 206, 1610612941, 2684354765, 1610612941, 2684354766, 3221225677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
@@ -244,6 +244,19 @@ return {
           gid = 515,
           visible = true,
           properties = {}
+        },
+        {
+          id = 13,
+          name = "CameraFocus",
+          type = "Meta",
+          shape = "point",
+          x = 0,
+          y = 0,
+          width = 0,
+          height = 0,
+          rotation = 0,
+          visible = true,
+          properties = {}
         }
       }
     }
index 3c96aa4e50aa2ef3be10592dd82daac462d680f0..28c2e299c455cb7fcaefe6b6ab09ecd27a0f4f3c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="left-up" width="30" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="7" nextobjectid="13">
+<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="left-up" width="30" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="7" nextobjectid="14">
  <editorsettings>
   <export target="test_1.lua" format="lua"/>
  </editorsettings>
 51,869,870,871,872,873,874,875,876,877,878,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,51,
 918,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,
 919,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,
-920,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,
-921,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,
-922,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,746,747,748,0,0,0,0,0,0,0,0,51,
+920,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+921,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
+922,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,746,747,748,0,0,0,0,0,0,0,0,1,
 923,0,0,0,0,0,0,206,2684354765,1610612941,2684354765,1610612941,2684354765,2684354766,0,0,0,0,795,796,797,0,0,0,0,0,0,0,0,51,
 924,0,0,0,0,0,0,3221225677,0,0,0,0,0,205,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,
 925,0,0,0,0,0,0,205,206,1610612941,2684354765,1610612941,2684354766,3221225677,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,
  </layer>
  <objectgroup id="2" name="Object Layer 1">
   <object id="11" name="Player" gid="515" x="16" y="32" width="16" height="16"/>
+  <object id="13" name="CameraFocus" type="Meta" x="0" y="0">
+   <point/>
+  </object>
  </objectgroup>
 </map>
index 1137b2042403600c1fff9a9d2c3674cc707d0ebc..7b18e40fa3b5a989112395bfcace827a7762aaa7 100644 (file)
@@ -3,10 +3,22 @@ local bit = require('bit')
 
 local utils = {}
 
+utils.Headings =
+{
+  East  = math.pi / 2 * -0,
+  North = math.pi / 2 * -1,
+  West  = math.pi / 2 * -2,
+  South = math.pi / 2 * -3,
+}
+
 function utils.flag_set(value, flag)
   return (bit.band(value, flag) ~= 0)
 end
 
+function utils.set_flag(value, flag)
+  return (bit.bor(value, flag))
+end
+
 function utils.shallow_copy(t)
   local t2 = {}
   for k,v in pairs(t) do
@@ -15,12 +27,22 @@ function utils.shallow_copy(t)
   return t2
 end
 
-function utils.shallow_dump(t)
-  if t then
-    for k,v in pairs(t) do
-      print(k..':', v)
+function utils.shallow_dump(delim, ...)
+  for vark,varv in pairs({...}) do
+    print(delim..vark..':')
+    for k,v in pairs(varv) do
+      print('\t'..k..':', v)
     end
   end
 end
 
+function utils.outside_box(box, pos)
+  -- return a vector indicating which way pos went out of the box
+  return
+  {
+    x = (pos.x >= (box.ex) and 1 or (pos.x < (box.ox - 32) and -1 or 0)),
+    y = (pos.y >= (box.ey) and 1 or (pos.y < box.oy and -1 or 0)),
+  }
+end
+
 return utils