From 88331f76867a4715525d476aae5f25cf31631677 Mon Sep 17 00:00:00 2001 From: Dangrain Date: Sun, 16 Mar 2025 23:00:56 +0100 Subject: [PATCH] Add files via upload --- libs/anim8.lua | 302 ++++++++++++++++++++++++++++++++++++++++ main.lua | 110 +++++++++++++++ sprites/computer.png | Bin 0 -> 1517 bytes sprites/spritesheet.png | Bin 0 -> 8235 bytes 4 files changed, 412 insertions(+) create mode 100644 libs/anim8.lua create mode 100644 main.lua create mode 100644 sprites/computer.png create mode 100644 sprites/spritesheet.png diff --git a/libs/anim8.lua b/libs/anim8.lua new file mode 100644 index 0000000..b7a6819 --- /dev/null +++ b/libs/anim8.lua @@ -0,0 +1,302 @@ +local anim8 = { + _VERSION = 'anim8 v2.3.1', + _DESCRIPTION = 'An animation library for LÖVE', + _URL = 'https://github.com/kikito/anim8', + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2011 Enrique García Cota + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ]] +} + +local Grid = {} + +local _frames = {} + +local function assertPositiveInteger(value, name) + if type(value) ~= 'number' then error(("%s should be a number, was %q"):format(name, tostring(value))) end + if value < 1 then error(("%s should be a positive number, was %d"):format(name, value)) end + if value ~= math.floor(value) then error(("%s should be an integer, was %f"):format(name, value)) end +end + +local function createFrame(self, x, y) + local fw, fh = self.frameWidth, self.frameHeight + return love.graphics.newQuad( + self.left + (x-1) * fw + x * self.border, + self.top + (y-1) * fh + y * self.border, + fw, + fh, + self.imageWidth, + self.imageHeight + ) +end + +local function getGridKey(...) + return table.concat( {...} ,'-' ) +end + +local function getOrCreateFrame(self, x, y) + if x < 1 or x > self.width or y < 1 or y > self.height then + error(("There is no frame for x=%d, y=%d"):format(x, y)) + end + local key = self._key + _frames[key] = _frames[key] or {} + _frames[key][x] = _frames[key][x] or {} + _frames[key][x][y] = _frames[key][x][y] or createFrame(self, x, y) + return _frames[key][x][y] +end + +local function parseInterval(str) + if type(str) == "number" then return str,str,1 end + str = str:gsub('%s', '') -- remove spaces + local min, max = str:match("^(%d+)-(%d+)$") + assert(min and max, ("Could not parse interval from %q"):format(str)) + min, max = tonumber(min), tonumber(max) + local step = min <= max and 1 or -1 + return min, max, step +end + +function Grid:getFrames(...) + local result, args = {}, {...} + local minx, maxx, stepx, miny, maxy, stepy + + for i=1, #args, 2 do + minx, maxx, stepx = parseInterval(args[i]) + miny, maxy, stepy = parseInterval(args[i+1]) + for y = miny, maxy, stepy do + for x = minx, maxx, stepx do + result[#result+1] = getOrCreateFrame(self,x,y) + end + end + end + + return result +end + +local Gridmt = { + __index = Grid, + __call = Grid.getFrames +} + +local function newGrid(frameWidth, frameHeight, imageWidth, imageHeight, left, top, border) + assertPositiveInteger(frameWidth, "frameWidth") + assertPositiveInteger(frameHeight, "frameHeight") + assertPositiveInteger(imageWidth, "imageWidth") + assertPositiveInteger(imageHeight, "imageHeight") + + left = left or 0 + top = top or 0 + border = border or 0 + + local key = getGridKey(frameWidth, frameHeight, imageWidth, imageHeight, left, top, border) + + local grid = setmetatable( + { frameWidth = frameWidth, + frameHeight = frameHeight, + imageWidth = imageWidth, + imageHeight = imageHeight, + left = left, + top = top, + border = border, + width = math.floor(imageWidth/frameWidth), + height = math.floor(imageHeight/frameHeight), + _key = key + }, + Gridmt + ) + return grid +end + +----------------------------------------------------------- + +local Animation = {} + +local function cloneArray(arr) + local result = {} + for i=1,#arr do result[i] = arr[i] end + return result +end + +local function parseDurations(durations, frameCount) + local result = {} + if type(durations) == 'number' then + for i=1,frameCount do result[i] = durations end + else + local min, max, step + for key,duration in pairs(durations) do + assert(type(duration) == 'number', "The value [" .. tostring(duration) .. "] should be a number") + min, max, step = parseInterval(key) + for i = min,max,step do result[i] = duration end + end + end + + if #result < frameCount then + error("The durations table has length of " .. tostring(#result) .. ", but it should be >= " .. tostring(frameCount)) + end + + return result +end + +local function parseIntervals(durations) + local result, time = {0},0 + for i=1,#durations do + time = time + durations[i] + result[i+1] = time + end + return result, time +end + +local Animationmt = { __index = Animation } +local nop = function() end + +local function newAnimation(frames, durations, onLoop) + local td = type(durations); + if (td ~= 'number' or durations <= 0) and td ~= 'table' then + error("durations must be a positive number. Was " .. tostring(durations) ) + end + onLoop = onLoop or nop + durations = parseDurations(durations, #frames) + local intervals, totalDuration = parseIntervals(durations) + return setmetatable({ + frames = cloneArray(frames), + durations = durations, + intervals = intervals, + totalDuration = totalDuration, + onLoop = onLoop, + timer = 0, + position = 1, + status = "playing", + flippedH = false, + flippedV = false + }, + Animationmt + ) +end + +function Animation:clone() + local newAnim = newAnimation(self.frames, self.durations, self.onLoop) + newAnim.flippedH, newAnim.flippedV = self.flippedH, self.flippedV + return newAnim +end + +function Animation:flipH() + self.flippedH = not self.flippedH + return self +end + +function Animation:flipV() + self.flippedV = not self.flippedV + return self +end + +local function seekFrameIndex(intervals, timer) + local high, low, i = #intervals-1, 1, 1 + + while(low <= high) do + i = math.floor((low + high) / 2) + if timer >= intervals[i+1] then low = i + 1 + elseif timer < intervals[i] then high = i - 1 + else + return i + end + end + + return i +end + +function Animation:update(dt) + if self.status ~= "playing" then return end + + self.timer = self.timer + dt + local loops = math.floor(self.timer / self.totalDuration) + if loops ~= 0 then + self.timer = self.timer - self.totalDuration * loops + local f = type(self.onLoop) == 'function' and self.onLoop or self[self.onLoop] + f(self, loops) + end + + self.position = seekFrameIndex(self.intervals, self.timer) +end + +function Animation:pause() + self.status = "paused" +end + +function Animation:gotoFrame(position) + self.position = position + self.timer = self.intervals[self.position] +end + +function Animation:pauseAtEnd() + self.position = #self.frames + self.timer = self.totalDuration + self:pause() +end + +function Animation:pauseAtStart() + self.position = 1 + self.timer = 0 + self:pause() +end + +function Animation:resume() + self.status = "playing" +end + +function Animation:draw(image, x, y, r, sx, sy, ox, oy, kx, ky) + love.graphics.draw(image, self:getFrameInfo(x, y, r, sx, sy, ox, oy, kx, ky)) +end + +function Animation:getFrameInfo(x, y, r, sx, sy, ox, oy, kx, ky) + local frame = self.frames[self.position] + if self.flippedH or self.flippedV then + r,sx,sy,ox,oy,kx,ky = r or 0, sx or 1, sy or 1, ox or 0, oy or 0, kx or 0, ky or 0 + local _,_,w,h = frame:getViewport() + + if self.flippedH then + sx = sx * -1 + ox = w - ox + kx = kx * -1 + ky = ky * -1 + end + + if self.flippedV then + sy = sy * -1 + oy = h - oy + kx = kx * -1 + ky = ky * -1 + end + end + return frame, x, y, r, sx, sy, ox, oy, kx, ky +end + +function Animation:getDimensions() + local _,_,w,h = self.frames[self.position]:getViewport() + return w,h +end + +----------------------------------------------------------- + +anim8.newGrid = newGrid +anim8.newAnimation = newAnimation + +return anim8 diff --git a/main.lua b/main.lua new file mode 100644 index 0000000..f6e930d --- /dev/null +++ b/main.lua @@ -0,0 +1,110 @@ + +local r, g, b = love.math.colorFromBytes(132, 193, 238) +love.graphics.setBackgroundColor(r, g, b) + + +function love.load() + --Other random stuff + love.window.setTitle("Hacking game?") + love.keyboard.setKeyRepeat(true) + + --lib loading + anim8 = require 'libs/anim8' + + + --player stuff + player = {x = 0, y = 0, width = 62, height = 62, animations = {}, spritesheet = love.graphics.newImage('sprites/spritesheet.png')} + player.grid = anim8.newGrid(66, 66, player.spritesheet:getWidth(), player.spritesheet:getHeight()) + player.animations.right = anim8.newAnimation( player.grid('1-3', 1),0.15) + player.animations.left = anim8.newAnimation( player.grid('1-3', 2),0.15) + player.anim = player.animations.right + -- other game objects + computer = { x = 200, y = 0, width = 32, height = 32} + computer.sprite = love.graphics.newImage('sprites/computer.png') + + -- Variable, bools and other data types MK II + + bwa = false +end + +-- update function +function love.update(dt) + -- Variables, bools and other data types + + -- movement + local isMoving = false + + + if love.keyboard.isDown("right") then + player.anim = player.animations.right + player.x = player.x + 2 + isMoving = true + end + + if love.keyboard.isDown("left") then + isMoving = true + player.x = player.x - 2 + player.anim = player.animations.left + end + + if isMoving == false then + player.anim:gotoFrame(1) + end + + -- Things that need regular updating + player.anim:update(dt) + + -- Collisions and touches +end + +-- drawing +function love.draw() + love.graphics.draw(computer.sprite, computer.x, computer.y, nil, 0.5) + player.anim:draw(player.spritesheet, player.x, player.y, nil, 1) + if bwa == true then + love.graphics.print("Hacking!!!") + end +end + +-- Other functions + + +function checkCollision(obj1, obj2) + return obj1.x < obj2.x + obj2.width and + obj1.x + obj1.width > obj2.x and + obj1.y < obj2.y + obj2.height and + obj1.y + obj1.height > obj2.y +end + + +function touch(obj1, obj2) + local overlapX = math.min(obj1.x + obj1.width, obj2.x + obj2.width) - math.max(obj1.x, obj2.x) + local overlapY = math.min(obj1.y + obj1.height, obj2.y + obj2.height) - math.max(obj1.y, obj2.y) + + if overlapX < overlapY then + if obj1.x < obj2.x then + obj1.x = obj2.x - obj1.width + else + obj1.x = obj2.x + obj2.width + end + obj1.vx = 0 + else + if obj1.y < obj2.y then + obj1.y = obj2.y - obj1.height + else + obj1.y = obj2.y + obj2.height + end + obj1.vy = 0 + end +end + + +-- hacking + +function love.keypressed(key, scancode, isrepeat) + if key == "up" and checkCollision(player, computer) then + bwa = true + else + bwa = false + end +end diff --git a/sprites/computer.png b/sprites/computer.png new file mode 100644 index 0000000000000000000000000000000000000000..5ee38d35e8eaf5b5587a4076f753ff5936157f06 GIT binary patch literal 1517 zcmVPx)r%6OXRCr$1TS0EyFc74W7Ra^f$qvvyqvP_)Q$GMUzc{MJ zbD@8C0Km;BMO_GLie$GXrAS=s<7gLE*!2V*2ahd~lp7Rr~fgu-l@6>bBd z3?Kjy6Mzb(05EwY0PM0_mLjD{MgRe@w4sy8a!dhWL{|ksG4U8E1%Mj>j!gcTLo#&L0{~(azyOrsn*cyd^7Q~n4Kn{)*=9gJ@DHlcIsssRTmuduH3XYKGeubHqWRXMh`{^0Gz280Av@j>Z7{|s1$VoT&Jjkc$sJZd#`)^WC$_f>z!Kd zl>qqJheB!#0K~LVFQ77D-KiTGz~y>v1|W^nCxLLBrR)MDCBOoZc!1np<6e@lnvev# z7_bVcmjO?AYQZP(zz&iT4A2VBE)%qYPpb#WmBOf9KKpI9W zx9{lmd_x$)wWm&pG+46Y|uylnjE&cZC8vl!R)cT_ig)lT>DIB(hv6UzkSN?PuGU4 zr0Es5P|-?NR11Uj;N$g|tQpF4aR4ZMD*?#s4&4Q`W@SwVn8;`VayD#Wzz6`6h8|E< zf0Y~^iM?0}D797!0KEps&hlqfNaL;{RNh67L{SPuYO zQq$X+MIIpmwnfRhMukRty9#?5u+PEY{J@{R>CH4M)zuosg>D5P<6$v@bjU*hASvzu zKwdZki69DfF#x}_sspfo+sc3=0FbA_!B4N=l>sZ$26=##XiA{4SaI41ct9fq!jBQk zddLOpk^nl9tb2(?uhm(F&|2TEfmkZ<>H#AG2q!4)ufvsga*zb1Re<>EyQKLCH+JA{ T+1EB}00000NkvXXu0mjfb7+)F literal 0 HcmV?d00001 diff --git a/sprites/spritesheet.png b/sprites/spritesheet.png new file mode 100644 index 0000000000000000000000000000000000000000..8d3c38a52d8ddbca4cf247cebc2d2b928b9688fc GIT binary patch literal 8235 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