commit bab3bda5642121f85cf1c3be9f736ee9f4ee16d3 Author: makiki Date: Sat Aug 17 18:37:43 2024 +0200 basic engine done diff --git a/conf.lua b/conf.lua new file mode 100644 index 0000000..cbb77f1 --- /dev/null +++ b/conf.lua @@ -0,0 +1,51 @@ +function love.conf(t) + t.identity = nil -- The name of the save directory (string) + t.appendidentity = false -- Search files in source directory before save directory (boolean) + t.version = "11.5" -- The LÖVE version this game was made for (string) + t.console = false -- Attach a console (boolean, Windows only) + t.accelerometerjoystick = true -- Enable the accelerometer on iOS and Android by exposing it as a Joystick (boolean) + t.externalstorage = false -- True to save files (and read from the save directory) in external storage on Android (boolean) + t.gammacorrect = false -- Enable gamma-correct rendering, when supported by the system (boolean) + + t.audio.mic = false -- Request and use microphone capabilities in Android (boolean) + t.audio.mixwithsystem = true -- Keep background music playing when opening LOVE (boolean, iOS and Android only) + + t.window.title = "GMTK2024" -- The window title (string) + t.window.icon = nil -- Filepath to an image to use as the window's icon (string) + t.window.width = 496 -- The window width (number) + t.window.height = 518 -- The window height (number) + t.window.borderless = false -- Remove all border visuals from the window (boolean) + t.window.resizable = true -- Let the window be user-resizable (boolean) + t.window.minwidth = 248 -- Minimum window width if the window is resizable (number) + t.window.minheight = 257 -- Minimum window height if the window is resizable (number) + t.window.fullscreen = false -- Enable fullscreen (boolean) + t.window.fullscreentype = "desktop" -- Choose between "desktop" fullscreen or "exclusive" fullscreen mode (string) + t.window.vsync = 1 -- Vertical sync mode (number) + t.window.msaa = 0 -- The number of samples to use with multi-sampled antialiasing (number) + t.window.depth = nil -- The number of bits per sample in the depth buffer + t.window.stencil = nil -- The number of bits per sample in the stencil buffer + t.window.display = 1 -- Index of the monitor to show the window in (number) + t.window.highdpi = false -- Enable high-dpi mode for the window on a Retina display (boolean) + t.window.usedpiscale = true -- Enable automatic DPI scaling when highdpi is set to true as well (boolean) + t.window.x = nil -- The x-coordinate of the window's position in the specified display (number) + t.window.y = nil -- The y-coordinate of the window's position in the specified display (number) + + t.modules.audio = true -- Enable the audio module (boolean) + t.modules.data = true -- Enable the data module (boolean) + t.modules.event = true -- Enable the event module (boolean) + t.modules.font = true -- Enable the font module (boolean) + t.modules.graphics = true -- Enable the graphics module (boolean) + t.modules.image = true -- Enable the image module (boolean) + t.modules.joystick = false -- Enable the joystick module (boolean) + t.modules.keyboard = true -- Enable the keyboard module (boolean) + t.modules.math = true -- Enable the math module (boolean) + t.modules.mouse = true -- Enable the mouse module (boolean) + t.modules.physics = false -- Enable the physics module (boolean) + t.modules.sound = true -- Enable the sound module (boolean) + t.modules.system = true -- Enable the system module (boolean) + t.modules.thread = false -- Enable the thread module (boolean) + t.modules.timer = true -- Enable the timer module (boolean), Disabling it will result 0 delta time in love.update + t.modules.touch = true -- Enable the touch module (boolean) + t.modules.video = true -- Enable the video module (boolean) + t.modules.window = true -- Enable the window module (boolean) +end \ No newline at end of file diff --git a/game.lua b/game.lua new file mode 100644 index 0000000..0b3c0ff --- /dev/null +++ b/game.lua @@ -0,0 +1,262 @@ +local LevelData = require "leveldata" + +local Game = {} + +Game.__index = Game + +MAX_POWER = 999999999999 + +local player_spr = love.graphics.newImage("res/sprite/player.png") +local ent_spr = { + enemy = love.graphics.newImage("res/sprite/enemy.png"), + enemy_neg = love.graphics.newImage("res/sprite/enemy_neg.png"), + elixir = love.graphics.newImage("res/sprite/elixir.png"), + gate = love.graphics.newImage("res/sprite/gate.png"), + vorpal = love.graphics.newImage("res/sprite/vorpal_sword.png"), + pickaxe = love.graphics.newImage("res/sprite/pickaxe.png"), + stairs_up = love.graphics.newImage("res/sprite/stairs_up.png"), + stairs_down = love.graphics.newImage("res/sprite/stairs_down.png"), +} + +local held_spr = { + vorpal = love.graphics.newImage("res/sprite/vorpal_held.png"), + pickaxe = love.graphics.newImage("res/sprite/pickaxe_held.png"), +} + +function Game.new() + local self = { + player_power = 5, + player_x = 8, + player_y = 15, + player_item = nil, + floor = 1, + level_data = LevelData:new(), + undo_history = {}, + } + setmetatable(self, Game) + return self +end + +function Game:keypressed(key,scancode,isrepeat) + if true then + if scancode=="up" or scancode=="k" or scancode=="w" or scancode=="kp8" then + self:step(0,-1) + elseif scancode=="down" or scancode=="j" or scancode=="s" or scancode=="kp2" then + self:step(0,1) + elseif scancode=="left" or scancode=="h" or scancode=="a" or scancode=="kp4" then + self:step(-1,0) + elseif scancode=="right" or scancode=="l" or scancode=="d" or scancode=="kp6" then + self:step(1,0) + elseif scancode=="z" then + self:undo() + end + end +end + +function Game:step(ox,oy) + tx = self.player_x + ox + ty = self.player_y + oy + --check if border + if tx < 1 or ty < 1 or tx > 15 or ty > 15 then + return + end + -- check if reinforced wall + if self.level_data[self.floor].walls[ty][tx]==2 then + return + end + -- check if normal wall + if self.level_data[self.floor].walls[ty][tx]==1 then + if self.player_item == "pickaxe" then + --push to undo memory + --remove wall + self.level_data[self.floor].walls[ty][tx]=0 + --remove item + self.player_item = nil + end + return + end + --check if entity + local move_legal = true + local entities = self.level_data[self.floor].entities + for i=1,#entities do + if entities[i].x == tx and entities[i].y == ty then + if entities[i].type == nil then --removed entity + break + elseif entities[i].type == "enemy" then + if self.player_item == "vorpal" then + --push to undo memory + --remove entity + entities[i].type = nil + --remove item + self.player_item = nil + break + elseif entities[i].value >= self.player_power then + return + else + --push to undo memory + --remove entity + entities[i].type = nil + --absorb power + self.player_power = self.player_power + entities[i].value + break + end + elseif entities[i].type == "enemy_neg" then + if self.player_item == "vorpal" then + --push to undo memory + --remove entity + entities[i].type = nil + --remove item + self.player_item = nil + break + elseif entities[i].value >= self.player_power then + return + else + --push to undo memory + --remove entity + entities[i].type = nil + --absorb power + self.player_power = self.player_power - entities[i].value + break + end + elseif entities[i].type == "elixir" then + --push to undo memory + --remove entity + entities[i].type = nil + --double power + self.player_power = self.player_power * 2 + break + elseif entities[i].type == "gate" then + --push to undo memory + --remove entity + entities[i].type = nil + --divide power by two + self.player_power = math.ceil(self.player_power / 2) + break + elseif entities[i].type == "pickaxe" then + --can't pick up if you have an item + if self.player_item then + return + end + --push to undo memory + --remove entity + entities[i].type = nil + --equip item + self.player_item = "pickaxe" + break + elseif entities[i].type == "vorpal" then + --can't pick up if you have an item + if self.player_item then + return + end + --push to undo memory + --remove entity + entities[i].type = nil + --equip item + self.player_item = "vorpal" + break + elseif entities[i].type == "stairs_up" then + self.floor = self.floor + 1 + break + elseif entities[i].type == "stairs_down" then + self.floor = self.floor - 1 + break + else + return + end + end + end + --ensure the power cap is upheld to + self.player_power = math.min(self.player_power,MAX_POWER) + --move if possible + if move_legal then + self.player_x = tx + self.player_y = ty + end +end + +function Game:undo() + if #self.undo_history == 0 then + return + end + +end + +function Game:update(dt) +end + +function Game:draw() + love.graphics.setLineWidth(2) + love.graphics.push() + + --floor and walls + love.graphics.setColor(1,1,1,1) + love.graphics.rectangle("fill",0,0,248,248) + local walls = self.level_data[self.floor].walls + for x=1,15 do + for y=1,15 do + if walls[y][x]==2 then + love.graphics.setColor(1,1,1,1) + elseif walls[y][x]==1 and (x+y+self.floor)%2==0 then + love.graphics.setColor(0.85,0.85,0.85,1) + elseif walls[y][x]==1 then + love.graphics.setColor(0.95,0.95,0.95,1) + elseif (x+y+self.floor)%2==0 then + love.graphics.setColor(0.05,0.05,0.05,1) + else + love.graphics.setColor(0.15,0.15,0.15,1) + end + love.graphics.rectangle("fill",x*16-12,y*16-12,16,16) + if walls[y][x]==2 then + love.graphics.setColor(0,0,0,1) + love.graphics.rectangle("fill",x*16-10,y*16-10,2,2) + love.graphics.rectangle("fill",x*16-10,y*16,2,2) + love.graphics.rectangle("fill",x*16,y*16-10,2,2) + love.graphics.rectangle("fill",x*16,y*16,2,2) + end + end + end + + --entities + love.graphics.setFont(FONT_DIGITS) + local entities = self.level_data[self.floor].entities + for i=1,#entities do + if entities[i].type then + --sprite + love.graphics.setColor(1,1,1,1) + love.graphics.draw(ent_spr[entities[i].type],entities[i].x*16-12,entities[i].y*16-12) + --value + if entities[i].value_str then + love.graphics.setColor(0,0,0,1) + love.graphics.printf(entities[i].value_str,entities[i].x*16-10,entities[i].y*16-1,16,"right") + love.graphics.printf(entities[i].value_str,entities[i].x*16-12,entities[i].y*16-1,16,"right") + love.graphics.printf(entities[i].value_str,entities[i].x*16-11,entities[i].y*16,16,"right") + love.graphics.printf(entities[i].value_str,entities[i].x*16-11,entities[i].y*16-2,16,"right") + love.graphics.printf(entities[i].value_str,entities[i].x*16-10,entities[i].y*16,16,"right") + love.graphics.printf(entities[i].value_str,entities[i].x*16-10,entities[i].y*16-2,16,"right") + love.graphics.printf(entities[i].value_str,entities[i].x*16-12,entities[i].y*16,16,"right") + love.graphics.printf(entities[i].value_str,entities[i].x*16-12,entities[i].y*16-2,16,"right") + love.graphics.setColor(1,1,1,1) + love.graphics.printf(entities[i].value_str,entities[i].x*16-11,entities[i].y*16-1,16,"right") + end + end + end + + --player + love.graphics.setColor(1,1,1,1) + love.graphics.draw(player_spr,self.player_x*16-12,self.player_y*16-12) + + --player info + love.graphics.setColor(1,1,1,1) + love.graphics.setFont(FONT_STANDARD) + love.graphics.print("FLOOR "..(self.floor>9 and self.floor or " "..self.floor).." POWER:"..player_power_to_string(self.player_power),1,249) + + --held item + if self.player_item then + love.graphics.draw(held_spr[self.player_item],72,250) + end + + love.graphics.pop() + love.graphics.setColor(1,1,1,1) +end + +return Game diff --git a/leveldata.lua b/leveldata.lua new file mode 100644 index 0000000..dac8f8c --- /dev/null +++ b/leveldata.lua @@ -0,0 +1,105 @@ +LevelData = {} + +LevelData.__index = LevelData + +function LevelData.new() + local self = { + { --floor 1 + walls = { + {0,0,0,0,0,1,2,2,2,1,0,0,0,0,0}, + {0,0,0,0,0,0,2,0,2,0,0,0,0,0,0}, + {1,1,1,1,1,0,2,0,2,0,1,1,1,1,1}, + {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,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,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,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,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,0,0,0,0,0,0,0,2,2,2}, + {0,0,0,0,0,0,0,0,0,0,0,0,1,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,2,0,0}, + }, + entities = { + {type = "enemy", x = 1, y = 1, value_str = "1"}, + {type = "enemy", x = 2, y = 1, value_str = "2"}, + {type = "enemy", x = 3, y = 1, value_str = "3"}, + {type = "enemy", x = 4, y = 1, value_str = "4"}, + {type = "enemy", x = 5, y = 1, value_str = "5"}, + {type = "enemy_neg", x = 11, y = 1, value_str = "1"}, + {type = "enemy_neg", x = 12, y = 1, value_str = "2"}, + {type = "enemy_neg", x = 13, y = 1, value_str = "3"}, + {type = "enemy_neg", x = 14, y = 1, value_str = "4"}, + {type = "enemy_neg", x = 15, y = 1, value_str = "5"}, + + {type = "elixir", x = 14, y = 14}, + {type = "elixir", x = 14, y = 15}, + {type = "elixir", x = 15, y = 14}, + {type = "elixir", x = 15, y = 15}, + + {type = "stairs_up", x = 8, y = 2}, + {type = "gate", x = 8, y = 3}, + }, + textboxes = {} + }, + { --floor 2 + walls = { + {0,0,0,0,0,0,2,2,2,0,0,0,0,0,0}, + {0,0,0,0,0,0,2,0,2,0,0,0,0,0,0}, + {0,0,0,0,0,0,2,0,2,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,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,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,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,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,0}, + {2,2,2,0,0,0,0,0,0,0,0,0,2,2,2}, + {2,1,2,0,0,0,0,0,0,0,0,0,2,1,2}, + {2,2,2,0,0,0,0,0,0,0,0,0,2,2,2}, + }, + entities = { + {type = "vorpal", x = 4, y = 8}, + {type = "pickaxe", x = 12, y = 8}, + + {type = "stairs_down", x = 8, y = 2}, + }, + textboxes = {} + }, + } + + --convert value_str into value + for f=1,#self do + local entities = self[f].entities + for e=1,#entities do + if entities[e].value_str then + arr = {} + num = 0 + for i=1,#entities[e].value_str do + arr[i] = entities[e].value_str:sub(i,i) + end + for i=1,#arr do + if arr[i]=="G" then + num = num * 1000 * 1000 * 1000 + elseif arr[i]=="M" then + num = num * 1000 * 1000 + elseif arr[i]=="k" then + num = num * 1000 + else + num = num*10+arr[i] + end + end + -- print("converted value_str "..entities[e].value_str.." into value "..num) + entities[e].value = num + end + end + end + setmetatable(self, LevelData) + return self +end + +return LevelData \ No newline at end of file diff --git a/main.lua b/main.lua new file mode 100644 index 0000000..215f09a --- /dev/null +++ b/main.lua @@ -0,0 +1,47 @@ +local bit = require "bit" +local Game = require "game" + +require "util" + +BASE_RES_W = 248 +BASE_RES_H = 257 + +FONT_CHARACTERS = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" +FONT_STANDARD = love.graphics.newImageFont("res/font/VictoriaBold.png", FONT_CHARACTERS, -1) + +DIGITFONT_CHARACTERS = "0123456789x[].kMG" +FONT_DIGITS = love.graphics.newImageFont("res/font/digits.png", DIGITFONT_CHARACTERS, 1) + +love.keyboard.setKeyRepeat(true) +love.graphics.setDefaultFilter("nearest","nearest",0) + +main_canvas = love.graphics.newCanvas() + +local game = Game.new() + +function love.keypressed(key,scancode,isrepeat) + game:keypressed(key,scancode,isrepeat) +end + +function love.update(dt) + game:update(dt) +end + +function love.draw() + love.graphics.setCanvas(main_canvas) + love.graphics.clear() + love.graphics.setLineWidth(2) + + game:draw() + + --draw canvas on the main window + love.graphics.setScissor() + love.graphics.setCanvas() + local w, h = love.graphics.getWidth(), love.graphics.getHeight() + local s = math.min(w/BASE_RES_W,h/BASE_RES_H) + local s = math.floor(s) + love.graphics.translate((w-BASE_RES_W*s)/2,(h-BASE_RES_H*s)/2) + love.graphics.scale(s, s) + love.graphics.clear() + love.graphics.draw(main_canvas) +end \ No newline at end of file diff --git a/res/font/CREDITS.md b/res/font/CREDITS.md new file mode 100644 index 0000000..8755ef8 --- /dev/null +++ b/res/font/CREDITS.md @@ -0,0 +1 @@ +The Victoria Bold bitmap font has been taken from https://opengameart.org/content/a-package-of-8-bit-fonts-for-grafx2-and-linux and it available under CC-0 (Public Domain) license. diff --git a/res/font/VictoriaBold.png b/res/font/VictoriaBold.png new file mode 100644 index 0000000..3eef0c5 Binary files /dev/null and b/res/font/VictoriaBold.png differ diff --git a/res/font/digits.png b/res/font/digits.png new file mode 100644 index 0000000..5e10995 Binary files /dev/null and b/res/font/digits.png differ diff --git a/res/sprite/crown.png b/res/sprite/crown.png new file mode 100644 index 0000000..fb72659 Binary files /dev/null and b/res/sprite/crown.png differ diff --git a/res/sprite/elixir.png b/res/sprite/elixir.png new file mode 100644 index 0000000..c9bc18d Binary files /dev/null and b/res/sprite/elixir.png differ diff --git a/res/sprite/enemy.png b/res/sprite/enemy.png new file mode 100644 index 0000000..96ed137 Binary files /dev/null and b/res/sprite/enemy.png differ diff --git a/res/sprite/enemy_neg.png b/res/sprite/enemy_neg.png new file mode 100644 index 0000000..7dc29db Binary files /dev/null and b/res/sprite/enemy_neg.png differ diff --git a/res/sprite/gate.png b/res/sprite/gate.png new file mode 100644 index 0000000..5319592 Binary files /dev/null and b/res/sprite/gate.png differ diff --git a/res/sprite/pickaxe.png b/res/sprite/pickaxe.png new file mode 100644 index 0000000..fe9e443 Binary files /dev/null and b/res/sprite/pickaxe.png differ diff --git a/res/sprite/pickaxe_held.png b/res/sprite/pickaxe_held.png new file mode 100644 index 0000000..7b1c636 Binary files /dev/null and b/res/sprite/pickaxe_held.png differ diff --git a/res/sprite/player.png b/res/sprite/player.png new file mode 100644 index 0000000..ed9cc8a Binary files /dev/null and b/res/sprite/player.png differ diff --git a/res/sprite/stairs_down.png b/res/sprite/stairs_down.png new file mode 100644 index 0000000..18fc3cf Binary files /dev/null and b/res/sprite/stairs_down.png differ diff --git a/res/sprite/stairs_up.png b/res/sprite/stairs_up.png new file mode 100644 index 0000000..c681155 Binary files /dev/null and b/res/sprite/stairs_up.png differ diff --git a/res/sprite/vorpal_held.png b/res/sprite/vorpal_held.png new file mode 100644 index 0000000..5390b89 Binary files /dev/null and b/res/sprite/vorpal_held.png differ diff --git a/res/sprite/vorpal_sword.png b/res/sprite/vorpal_sword.png new file mode 100644 index 0000000..d6328e7 Binary files /dev/null and b/res/sprite/vorpal_sword.png differ diff --git a/util.lua b/util.lua new file mode 100644 index 0000000..9395b54 --- /dev/null +++ b/util.lua @@ -0,0 +1,20 @@ +function player_power_to_string(n) + str = "" + while n > 999 do + last_segment = n%1000 + n = n - last_segment + if last_segment > 99 then + str = "." .. last_segment ..str + elseif last_segment > 9 then + str = ".0" .. last_segment ..str + else + str = ".00" .. last_segment ..str + end + n = n /1000 + end + str = n..str + while #str < 16 do + str = " "..str + end + return str +end \ No newline at end of file