0,>S}Wup8d%qy)6o;]AsVl3 0 then player.powerup_timer = player.powerup_timer - 1 / 60 -- Decrease timer if player.powerup_timer <= 0 then -- Remove the power-up effects when the timer expires player.powerup_timer = 0 player.active_powerup = nil player.damageMult = 1 -- Reset to default player.bsMult = 0.5 -- Reset to default end end end function end_game() CURRENT_STATE = STATE_GAME_OVER draw_game_over() end function draw_game_over() local winnerName local loserName local winnerColor local loserColor if winner==1 then winnerName="Player2" winnerColor=10 loserName="Player1" loserColor=3 elseif winner==2 then winnerName="Player1" winnerColor=3 loserName="Player2" loserColor=10 end cls() map(MINX, MINY, MAXX + 1, MAXY + 1) draw_floor() draw_limits() print("GAME OVER",66,40,12,false,2) if winner~=3 then print("Winner: "..winnerName, 78,60,winnerColor) print("Loser: "..loserName, 79,70,loserColor) else print("Game is a draw",78,65,15) end draw_press_restart() end function update_maze_timer() if maze_timer > 0 then maze_timer = maze_timer - 1 / 60 else local p1_tile = {player1.x, player1.y} local p2_tile = {player2.x, player2.y} maze_timer = 10 sfx(1,"E-5",-1,2,-1,-1) -- Set player positions as FLOOR and non-buildable mset(p1_tile[1], p1_tile[2], FLOOR) mset(p2_tile[1], p2_tile[2], FLOOR) -- Remove player tiles from TILES for i = #TILES, 1, -1 do local tile = TILES[i] if (tile[1] == p1_tile[1] and tile[2] == p1_tile[2]) or (tile[1] == p2_tile[1] and tile[2] == p2_tile[2]) then table.remove(TILES, i) end end start() -- Players stay in place player1.x, player1.y = p1_tile[1], p1_tile[2] player2.x, player2.y = p2_tile[1], p2_tile[2] end end function BULLET:new(x,y,direction,sprite,owner) local obj={ x=x, y=y, direction=direction, sprite=sprite, speed=1, alive=true, owner=owner, speedMult=owner.bsMult } setmetatable(obj,self) return obj end -- Manage bullets bullets = {} function teleport_player(player) -- Choose a random tile to teleport local index = math.random(#TILES) local new_tile = TILES[index] -- Update player position to the new tile player.x = new_tile[1] player.y = new_tile[2] end function player_damage(player,bullet) -- Increment the score of the player who shot the bullet local owner=bullet.owner if owner==player1 then health2=health2-(1*player1.damageMult) sfx(2,"A#1",-1,3,-1,0) teleport_player(player2) elseif owner==player2 then health1=health1-(1*player2.damageMult) sfx(2,"A#1",-1,3,-1,0) teleport_player(player1) end end function BULLET:update() if self.alive then local new_x=self.x+(self.direction[1]*self.speedMult) local new_y=self.y+(self.direction[2]*self.speedMult) -- Check for collision with walls or map boundaries if solid(new_x,new_y) or new_xMAXX or new_yMAXY then self.alive = false else self.x=new_x self.y=new_y -- Check for collision with players if self.x==player1.x and self.y==player1.y and self.owner~=player1 then player_damage(player1, self) self.alive=false -- destroy the bullet elseif self.x==player2.x and self.y==player2.y and self.owner~=player2 then player_damage(player2,self) self.alive=false -- destroy the bullet end end end end -- Draw bullet function BULLET:draw() if self.alive then spr(self.sprite, self.x * 8, self.y * 8, 0) end end -- Function to shoot a bullet function shoot(player, sprite) local bullet=BULLET:new(player.x,player.y,player.direction,sprite,player) table.insert(bullets,bullet) sfx(0,"D-4",-1,3,-1,0) end -- Update and draw bullets function update_bullets() for i = #bullets, 1, -1 do local bullet = bullets[i] bullet:update() if not bullet.alive then table.remove(bullets, i) end end end function draw_bullets() for _, bullet in ipairs(bullets) do bullet:draw() end end function draw_ui() spr(player1.sprite,0,0,0) print("P1",9,2,12) draw_hearts_p1(); -- Check if player1 has an active power-up and the timer is running if player1.active_powerup and player1.powerup_timer > 0 then local icon_sprite = (player1.active_powerup == "double_damage") and POWERUP_HP or POWERUP_BS spr(icon_sprite, P1_ICON_X, ICON_Y, 0) -- Draw power-up icon for Player 1 end spr(player2.sprite,232,0,0) print("P2",220,2,12) draw_hearts_p2(); -- Check if player2 has an active power-up and the timer is running if player2.active_powerup and player2.powerup_timer > 0 then local icon_sprite = (player2.active_powerup == "double_damage") and POWERUP_HP or POWERUP_BS spr(icon_sprite, P2_ICON_X, ICON_Y, 0) -- Draw power-up icon for Player 2 end end function draw_title() local title_width=8*8*2 local start_x=(240-title_width)/2 local start_y=40 for i=0,7 do spr(384+i,start_x+i*16,start_y,0,2,0,0) end for i=0,7 do spr(392+i,start_x+i*16,start_y+16,0,2,0,0) end end function draw_press_restart() local text="PRESS Z TO RESTART" local text_x=68 local text_y=90 blink_timer=blink_timer+1 if blink_timer>blink_duration*2 then blink_timer=0 end if blink_timerblink_duration*2 then blink_timer=0 end if blink_timer= MAXX or new_y <= MINY+1 or new_y >= MAXY) -- Check if the new position is solid if solid(new_x, new_y) or is_at_border then -- If the player hits a wall on the border, find an alternate direction if is_at_border then self:find_alternate_direction(sprites) end self:update_sprite(sprites) else -- Move to the new position self.x = new_x self.y = new_y end self.timer = self.speed else self.timer = self.timer - 1 end end function PLAYER:update_direction(up, down, left, right, sprites) if keyp(up) and self.direction ~= DIR_DOWN then self.direction = DIR_UP self.sprite = sprites.up -- up sprite elseif keyp(down) and self.direction ~= DIR_UP then self.direction = DIR_DOWN self.sprite = sprites.down -- down sprite elseif keyp(left) and self.direction ~= DIR_RIGHT then self.direction = DIR_LEFT self.sprite = sprites.left -- left sprite elseif keyp(right) and self.direction ~= DIR_LEFT then self.direction = DIR_RIGHT self.sprite = sprites.right -- right sprite end end function PLAYER:find_alternate_direction(sprites) local possible_directions = {DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT} local valid_directions = {} -- Check all directions, exclude the opposite one for _, dir in ipairs(possible_directions) do local new_x = self.x + dir[1] local new_y = self.y + dir[2] local is_opposite = (self.direction == DIR_UP and dir == DIR_DOWN) or (self.direction == DIR_DOWN and dir == DIR_UP) or (self.direction == DIR_LEFT and dir == DIR_RIGHT) or (self.direction == DIR_RIGHT and dir == DIR_LEFT) if not solid(new_x, new_y) and not is_opposite then table.insert(valid_directions, dir) end end -- If there are valid directions, choose one randomly and update the sprite if #valid_directions > 0 then self.direction = valid_directions[math.random(1, #valid_directions)] self:update_sprite(sprites) -- Update the sprite based on new direction end end function draw_players() spr(player1.sprite,player1.x*8,player1.y*8,0) spr(player2.sprite,player2.x*8,player2.y*8,0) end --draw the entire floor --according to the map specs function draw_floor() for x=MINX,MAXX do for y=MINY,MAXY do mset(x,y,FLOOR) end end end --draw the map limits function draw_limits() for x=MINX,MAXX do for y=MINY,MAXY do if CURRENT_STATE==STATE_MAIN_MENU or CURRENT_STATE==STATE_GAME_OVER or CURRENT_STATE == STATE_CONTROLS_MENU then if x==MINX or x==MAXX then mset(x,y,WALL) elseif y==MINY or y==MAXY then mset(x,y,WALL) end elseif CURRENT_STATE==STATE_IN_GAME then if x==MINX and y>MINY+1 or x==MAXX and y>MINY+1 then mset(x,y,WALL) elseif y==MINY+1 or y==MAXY then mset(x,y,WALL) end end end end end --store every empty tile to be used later function store_floor() for x=MINX,MAXX do for y=MINY+1,MAXY do if--check EVERY type of floor tile mget(x,y)==FLOOR or mget(x,y)==SFLOOR_LEFT1 or mget(x,y)==SFLOOR_LEFT2 or mget(x,y)==SFLOOR_TOP1 or mget(x,y)==SFLOOR_TOP2 or mget(x,y)==SFLOOR_CORNER or mget(x,y)==SFLOOR_FULL then table.insert(TILES,#TILES+1,{x,y}) end end end end --check if you can build in a tile function check_can_build(tileID) local x=tileID[1] local y=tileID[2] if --top tiles mget(x-1,y-1)= 2 then -- Ensure there are at least two empty tiles -- Select two random tiles from the TILES list local index1 = math.random(#TILES) local trap_tile1 = table.remove(TILES, index1) -- Remove the first tile from TILES local index2 = math.random(#TILES) local trap_tile2 = table.remove(TILES, index2) -- Remove the second tile from TILES -- Place traps on the selected tiles mset(trap_tile1[1], trap_tile1[2], TRAP) mset(trap_tile2[1], trap_tile2[2], TRAP) end end function check_trap_collision() -- Get the positions of traps local traps = {} for x = MINX, MAXX do for y = MINY, MAXY do if mget(x, y) == TRAP then table.insert(traps, {x = x, y = y}) end end end -- Check for player 1 for _, trap in ipairs(traps) do if player1.x == trap.x and player1.y == trap.y then sfx(4,"C-4",-1,3,-1,-1) --implement trap effect here health1 = health1 - 1 mset(trap.x, trap.y, FLOOR) -- Reset trap to floor after it is triggered break -- Exit the loop after processing end end -- Check for player 2 for _, trap in ipairs(traps) do if player2.x == trap.x and player2.y == trap.y then --implement trap effect here health2 = health2 - 1 sfx(4,"C-4",-1,3,-1,-1) mset(trap.x, trap.y, FLOOR) -- Reset trap to floor after it is triggered break -- Exit the loop after processing end end end --draw the walls on the map --as long as there are available tiles function draw_walls() place_wall() if #TILES>0 then draw_walls() end end function draw() cls()--clear screen map(MINX,MINY,MAXX+1,MAXY+1) draw_ui() check_trap_collision() if powerup then if powerup_type=="player_heal" then spr(POWERUP_HP,powerup[1]*8,powerup[2]*8, 0) elseif powerup_type=="bullet_speed" then spr(POWERUP_BS,powerup[1]*8,powerup[2]*8,0) end end end function draw_menu() cls() map(MINX,MINY,MAXX+1,MAXY+1) draw_limits() draw_title() draw_press_start() end function draw_controls_menu() cls() map(MINX,MINY,MAXX+1,MAXY+1) draw_limits() print("CONTROLS", 77, 13, 12, false, 2) -- Player 1 controls print("Player 1 Controls:", 77, 35, 3) print("Move: W A S D", 77, 45, 12) print("Shoot: Space", 77, 55, 12) -- Player 2 controls print("Player 2 Controls:", 77, 75, 10) print("Move: Arrow Keys", 77, 85, 12) print("Shoot: L", 77, 95, 12) -- Instruction to return to the main menu blink_timer=blink_timer+1 if blink_timer>blink_duration*2 then blink_timer=0 end if blink_timer 0 then -- check if there are empty tiles local INDEX = math.random(#TILES) -- get a random one local SPAWN_TILE = TILES[INDEX] -- set it as spawn player.x = SPAWN_TILE[1] player.y = SPAWN_TILE[2] -- Set the direction to be random on spawn player.direction = random_direction() -- Now it can be any direction -- Update sprite based on the random direction using specific sprites for each player player:update_sprite({ up=sprite_up, right=sprite_right, down=sprite_down, left=sprite_left }) end end --idk if i should explain what this does function start() draw_floor()--start with the floors draw_limits()--then the limits store_floor()--store all empty tiles(floor) draw_walls()--draw the random walls store_floor()--re-store all available tiles place_traps() store_floor() spawn_powerup() store_floor() end -- Before the TIC function, create instances of the players player1=PLAYER:new(0,0,256,damageMult1,bulletSpeedMult1) -- Initial position (0,0) and default sprite player2=PLAYER:new(0,0,272,damageMult2,bulletSpeedMult2) -- Initial position (0,0) and default sprite function TIC() -- Start music initially if it's not playing if not is_playing then music(track_1) -- Start the first track (normal notes) is_playing = true end -- Increment the row counter based on frames current_row = current_row + 1 -- Switch tracks after 64 rows (adjusted by frames_per_row) if current_row >= rows_per_track * frames_per_row then -- Alternate between tracks if track == 1 then music(track_2) -- Play the higher octave track track = 2 else music(track_1) -- Play the normal notes track track = 1 end -- Reset the row counter current_row = 0 end if CURRENT_STATE == STATE_MAIN_MENU then draw_menu() if btnp(4) then sfx(3,"E-5",60,3,-1,0) CURRENT_STATE = STATE_IN_GAME elseif btnp(5) then sfx(3,"E-5",60,3,-1,0) CURRENT_STATE = STATE_CONTROLS_MENU end elseif CURRENT_STATE == STATE_CONTROLS_MENU then draw_controls_menu() if btnp(5) then CURRENT_STATE = STATE_MAIN_MENU sfx(3,"E-5",60,3,-1,0) end elseif CURRENT_STATE == STATE_IN_GAME then if not GAME_INITIALIZED then health1=5 health2=5 maze_timer=10 start() spawn_player(player1, 256, 257, 258, 259) -- Player 1 sprites spawn_player(player2, 272, 273, 274, 275) -- Player 2 sprites GAME_INITIALIZED = true end update_maze_timer() draw() -- Move players player1:move(23, 19, 1, 4, {up=256, right=257, down=258, left=259}) -- wasd player2:move(58, 59, 60, 61, {up=272, right=273, down=274, left=275}) -- arrows -- Shooting logic if keyp(48) then -- space for player1 shoot(player1,288) -- player1 bullet sprite is 288 end if keyp(12) then -- L for player2 shoot(player2,289) -- player2 bullet sprite is 289 end -- Update and draw bullets update_bullets() draw_bullets() --powerups check_powerup_collision() update_powerups(player1) update_powerups(player2) -- Draw players draw_players() -- end game by health if health1<=0 or health2<=0 then if health1<=0 then winner=1 end_game() elseif health2<=0 then winner=2 end_game() end end -- Restart game if player presses Z if btnp(4) then GAME_INITIALIZED = false end elseif CURRENT_STATE == STATE_GAME_OVER then draw_game_over() -- Restart game if player presses Z if btnp(4) then-- Reset scores-- Reset timer sfx(3,"E-5",60,3,-1,0) GAME_INITIALIZED=false CURRENT_STATE = STATE_IN_GAME -- Return to main menu end end end