4444044 ""  ! ""!"! ! " ! ""!ffff` ""      ""  ```nfffwgwfffnwwwffaff,,""!"""""" 2# C4 C42#   w`ffffbbffbb`ff`ffpww""""DD@DD@@wwgfgpf pfpffwwC@@00044 , ,","  "2# 23""23!"""!@D3333CDD3CDD4CDD43333000@$B@2#@2#$B@0C40$B0$BC40 "" ""   ""          "" ""   ""    ""  "   "" ""   "" " "  v/abcdeQRSSTURUVWUXVSRZV&($&.!/&//"" $""""""""$'''-&&%$%%,"&&$"&"""/"(/!()//"!""&"!""""""+&*"""""%"%$"'''''' >fd?""   "" "" ""        ""   "" " ""      "      " """"  ""    33333333 3333CDD3CDD3CDD3CDD4"CDD4CDD4""""CDD4"CDD4CDD4!!3333"33333333""""0"0 0""""000""""000ffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff `` ````````````````````````     @D @ @D @ D @ D>-- title: Super Bunny Dude Adventure -- author: Javariced -- desc: A short game about a bunny dude -- site: -- license: MIT License -- version: 1.0 -- script: lua -- input: gamepad -------GLOBALS-------- local gravity = 0.5 local maxgrav = gravity*5 local jumpheight = maxgrav*2.2 local object_limit = 64 local objects = {} local last_indx = 0 local world_size = 8 local screen = 0 local t = 0 local respawn_anim = 0 local message = "" local message_timer = 0 local ended = false local Object = {} local Coin = {} local Whip = {whip_dist=0} local Switch = {cooldown=10} local player = {} local OtherBunny = {} local Sign = {} local GobEnemy = {} local RabEnemy = {} local sign_text = { [0]={"Drop down to start", "Whip switches to\ntoggle them", "Some items\nare breakable"}, [8]={"Score=Health", "You need to crouch", "Crouching Jump?" }, [10]={"Exploring is\nencouraged"} } -------AUXILIARY FUNCTIONS-------- function getCoord_screen(screen_no) local x = screen_no or screen return (x % world_size)*30, (x // world_size)*17 end function check_tile(x,y,f) local mx, my = getCoord_screen() return fget(mget((x//8)+mx, (y//8)+my),f or 0) end function append_object(obj) local i = last_indx while objects[i] do i = i + 1 if i == object_limit then i = 0 end end objects[i] = obj last_indx = i+1 end function update_objects() for i, obj in pairs(objects) do obj:update() if obj.lifetime > 0 then obj.lifetime = obj.lifetime - 1 elseif obj.lifetime == 0 then objects[i] = nil end end end function shallow_copy(x) local ret = {} for k, v in pairs(x) do ret[k] = v end return ret end function draw_objects() for _, obj in pairs(objects) do obj:draw() end end -------OBJECT CLASS-------- Object.__index = Object function Object.new(x, y, w, h, id, init) local self = setmetatable(init or {}, Object) self.x = x self.y = y self.w = w self.h = h self.vx = 0 self.vy = 0 self.id = id or -1 self.color = id and -1 or 12 self.lifetime = -1 self.facing = 1 return self end function Object:draw(color) if self.id == -1 then local drawcol = color or self.color rect(self.x, self.y, self.w, self.h, drawcol) elseif self.id ~= -1 and self.w == 8 and self.h == 8 then spr(self.id, self.x, self.y, self.color, 1, (self.facing == 1) and 0 or 1) end end function Object:checkCollision(other) return ( self.x <= other.x + other.w and self.x + self.w >= other.x and self.y <= other.y + other.h and self.y + self.h >= other.y ) end function Object:checkCellCollision(ox, oy, f) local offsx = ox or 0 local offsy = oy or 0 return ( check_tile(self.x+offsx, self.y+offsy, f) or check_tile(self.x+self.w+offsx-1, self.y+offsy, f) or check_tile(self.x+offsx, self.y+self.h+offsy-1, f) or check_tile(self.x+self.w+offsx-1, self.y+self.h+offsy-1, f) ) end function Object:move(dx, dy) self.x = self.x + dx self.y = self.y + dy end function Object:update() self.x = self.x + self.vx self.y = self.y + self.vy end function Object:damage(v) self.lifetime = self.lifetime + v if self.lifetime + v > 0 then self.lifetime = 0 end end -------COIN CLASS-------- function Coin:draw() local anim_offs = 16*(((t % 30)//8)%3) spr(self.id+anim_offs, self.x, self.y, 0) end function Coin:update() self.vy = self.vy + gravity if self:checkCellCollision(self.vx, 0) then self.vx = 0 end if self:checkCellCollision(0,self.vy) then self.vy = 0 self.vx = 0 end self:move(self.vx, self.vy) if self:checkCollision(player) then self.lifetime = 0 player.score = player.score + 10 end end -------WHIP CLASS-------- function Whip:draw() local orientation = (self.parent.facing == 1) and 0 or 1 for i=0,self.w-8,8 do local id = 7 if (orientation == 0 and i == self.w-8) or (orientation == 1 and i==0) then id = 8 end spr(id, self.x+i, self.y, 0, 1, orientation) end end function Whip:update() if self.parent then self.y = self.parent.y self.whip_dist = self.parent.facing == 1 and self.parent.w or (-self.w) self.x = self.parent.x + self.whip_dist end for i, obj in pairs(objects) do if self:checkCollision(obj) then if obj.id == 33 then objects[i] = nil elseif obj.id == 34 then if math.random() ~= 0 then local vx = math.random(-2, 2) local coin = Object.new(obj.x, obj.y, 8, 8, 35, shallow_copy(Coin)) coin.vx = vx coin.vy = -maxgrav append_object(coin) end objects[i] = nil elseif obj.id == 36 or obj.id == 37 then objects[i] = nil player.score = player.score + 50 elseif obj.id == 38 then obj:toggle(true) elseif obj.id == 46 then reset() end end end end ------PLAYER CLASS-------- function init_player() player.whip_len = 2 player.score = 50 player.keys = 0 player.crouching = false player.jumps = 1 player.max_jumps = 1 player.spawnx = 0 player.spawny = 0 player.dead = true player.whip_cd = 0 player.inventory = {} Whip.parent = player end function player:jump() if self.jumps <= 0 or self:checkCellCollision() then return end if self.vy ~= 0 and self.jumps == self.max_jumps then self.jumps = self.jumps - 1 end self.vy = -jumpheight self.jumps = self.jumps - 1 end function player:crouch() if self.crouching then if not self:checkCellCollision(0,-4) then self:move(0,-4) self.crouching = false end else self.crouching = true self:move(0,4) end if self.crouching then self.h = 4 else self.h = 8 end end function player:whip() if self.whip_cd > 0 then return end local whip = Object.new(self.x,self.y+2,8*self.whip_len,4,-1,Whip) whip.color = 2 whip.lifetime = 7 append_object(whip) self.whip_cd = 20 end function player:respawn() player.score = player.score - 50 player.dead = false if player.score < 0 then deload_level(screen) screen = 1 load_level(false) else respawn_anim = 20 end player.x = player.spawnx player.y = player.spawny end function player:update() if self.whip_cd > 0 then self.whip_cd = self.whip_cd - 1 end local flag = 0 if self.crouching then flag = 1 end if self.vy < maxgrav then self.vy = self.vy + gravity end if self:checkCellCollision(self.vx, 0, flag) then self.vx = 0 end if self:checkCellCollision(0, self.vy, flag) then if self.vy > 0 then self.jumps = self.max_jumps end self.vy = 0 end if self.vx ~= 0 and self.vy ~= 0 and self:checkCellCollision(self.vx,self.vy,flag) then self.vx = 0 self.vy = 0 self.jumps = self.max_jumps end self.x = self.x + self.vx self.y = self.y + self.vy for _, obj in pairs(objects) do if obj.id == 39 and self:checkCollision(obj) then self.dead = true elseif (obj.id == 36 or obj.id == 37) and self:checkCollision(obj) then self.dead = true obj.lifetime = 0 elseif obj.id==40 and self:checkCollision(obj) then self.keys = self.keys + 1 obj.lifetime = 0 elseif obj.id==41 and self:checkCollision(obj) then message = "+1 Jump!" message_timer = 60 self.max_jumps = self.max_jumps + 1 obj.lifetime = 0 elseif obj.id==42 and self:checkCollision(obj) then self.whip_len = self.whip_len + 1 message = "+1 Whip!" message_timer = 60 obj.lifetime = 0 elseif (obj.id>=43 and obj.id<=45) and self:checkCollision(obj) then table.insert(self.inventory, obj.id) self.score = self.score + 60 obj.lifetime = 0 end end if self:checkCellCollision(self.facing,0,3) and self.keys > 0 then local mx, my = getCoord_screen() mset((mx+self.x//8)+self.facing,my+self.y//8, 0) self.keys = self.keys - 1 end end function player:draw() local orientation = (self.facing == -1) and 1 or 0 local id = self.crouching and self.id + 3 or self.id local anim_offs = 1+((t % 30)//8)%2 if self.vx ~= 0 then id = id + anim_offs end spr(id, self.x, self.y, 0, 1, orientation) local offs = print("Score: "..self.score, 0, 130, 12)+8 spr(40, offs, 128) offs = offs + print(self.keys, offs+8, 130, 12)+16 for i, d in pairs(self.inventory) do spr(d, i*8+offs, 128) end end ------ENTITY CODE-------- function Sign:update() if self:checkCollision(player) then message = self.text message_timer = 10 end end function OtherBunny:update() self.facing = (self.x > player.x) and -1 or 1 if self:checkCollision(player) then ended = true player.vx = 0 player.vy = 0 end end function RabEnemy:update() self.vy = self.vy + gravity if t % 2 == 0 then if self:checkCellCollision(self.facing, 0, 2) then self.facing = -self.facing end self:move(self.facing,0) end if self:checkCellCollision(0, self.vy, 2) then self.vy = 0 end if t % 6 == 0 and self.vy == 0 and math.random()>=0.6 then self.vy = -maxgrav*2 end self:move(0, self.vy) end function GobEnemy:update() self.vy = self.vy + gravity if self:checkCellCollision(0, self.vy, 0, 2) then self.vy = 0 end if self:checkCellCollision(self.facing, 0, 2) then self.facing = -self.facing end self:move(self.facing, self.vy) end -------SWITCH CLASS---- function toggle_level() local mx, my = getCoord_screen() for y=my,my+16,1 do for x=mx,mx+29,1 do local cell = mget(x,y) local ncell = cell if cell == 21 or cell == 23 then ncell = ncell + 1 elseif cell == 22 or cell == 24 then ncell = ncell - 1 end mset(x,y,ncell) end end end function Switch:draw() local id = self.id if self.cooldown > 0 then return end spr(id, self.x, self.y, 0) end function Switch:update() if self.cooldown > 0 then self.cooldown = self.cooldown - 1 end end function Switch:toggle(lv) if self.cooldown > 0 then return end toggle_level() self.cooldown = 16 end ------MAIN CODE-------- function draw_level() local mx, my = getCoord_screen() map(mx, my, 30, 16) return mx, my end function deload_level(scr) -- Long-lived objects such as collectibles need to be preserved upon reload local mx, my = getCoord_screen(scr) for i, v in pairs(objects) do if v.lifetime == -1 and v.id ~= -1 then mset(mx+(v.x//8), my+(v.y//8), v.id) objects[i] = nil end end mset(mx+(player.spawnx//8), my+(player.spawny//8), 1) last_indx = 0 end function spawn_gob(cx, cy) local obj = shallow_copy(GobEnemy) obj = Object.new(cx, cy, 8, 8, 37, obj) obj.color = 0 append_object(obj) end function spawn_rab(cx,cy) local obj = shallow_copy(RabEnemy) obj = Object.new(cx, cy, 8, 8, 36, obj) obj.color = 0 append_object(obj) end local spawn_funcs = {spawn_gob, spawn_rab} function load_level(spawn) local mx, my = draw_level() local text_indx = 1 local spawned = 0 for y=my,my+17,1 do for x=mx,mx+30,1 do local cell = mget(x,y) local cx = (x-mx)*8 local cy = (y-my)*8 if cell == 15 and spawn then spawn_funcs[math.random(#spawn_funcs)](cx, cy) spawned = spawned + 1 elseif cell == 9 then append_object(Object.new(cx, cy, 8, 8, 9, OtherBunny)) elseif cell == 1 then player.spawnx = cx player.spawny = cy mset(x, y, 0) elseif cell >= 32 then mset(x,y,0) if (cell >= 33 and cell <= 34) or (cell >= 39 and cell <= 46) then append_object(Object.new(cx, cy, 8, 8, cell)) objects[last_indx-1].color = 0 elseif cell == 35 then local coin = Object.new(cx, cy, 8, 8, 35, shallow_copy(Coin)) append_object(coin) elseif cell == 37 and spawn then spawn_gob(cx, cy) spawned = spawned + 1 elseif cell == 36 and spawn then spawn_rab(cx, cy) spawned = spawned + 1 elseif cell == 38 or cell == 54 then local switch = Object.new(cx, cy, 8, 8, 38, shallow_copy(Switch)) switch.on = (cell == 54) append_object(switch) elseif cell == 47 then local sign = Object.new(cx, cy, 8,8, cell, shallow_copy(Sign)) sign.text = sign_text[screen][text_indx] text_indx = text_indx + 1 append_object(sign) else mset(x, y, cell) end end end end if player.dead then player:respawn() end return spawned end function checkTransition() local old_screen = screen local ox = player.x local oy = player.y if player.x < 0 then screen = screen - 1 player.x = 232 elseif player.x+player.w > 240 then screen = screen + 1 player.x = 0 end if player.y < 0 then screen = screen - world_size player.y = 128 elseif player.y+player.h > 136 then screen = screen + world_size player.y = 8 end if old_screen ~= screen then deload_level(old_screen) load_level(true) end end function scene_ended() print("The End...", 80, 32, 12) print("Jump to reset...", 60, 50, 12) player:draw() OtherBunny:draw() local id = 10 if #player.inventory ~= 0 then id = 11+(t%60)/(60/#player.inventory) end spr(id,OtherBunny.x+6*OtherBunny.facing,OtherBunny.y-10,0) if btnp(4) then reset() end end function check_input() player.vx = 0 if btn(2) then player.vx = -1 elseif btn(3) then player.vx = 1 end if player.vx ~= 0 then player.facing = (player.vx < 0) and -1 or 1 end if btnp(4) then player:jump() elseif btnp(1) and player.vy == 0 then player:crouch() end if btnp(5) and not player.crouching then player:whip() end end function BOOT() init_player() player = Object.new(8,8,8,8,1,player) load_level(true) end function TIC() t = t + 1 cls(0) draw_level() if ended then scene_ended() return end if respawn_anim > 0 then respawn_anim = respawn_anim - 1 draw_level() draw_objects() player:draw() rect(0,138-respawn_anim*16,240,138,0) return end check_input() player:update() checkTransition() update_objects() if player.dead then load_level(false) end draw_objects() player:draw() if message_timer > 0 then local msgx = player.x-(#message*2) local msgy = player.y-12 print(message, msgx, msgy, 12) message_timer = message_timer - 1 end if screen == 1 then print("You died...", 80, 32, 12) print("Whip grave to try again...", 80, 38, 12) end end