diff --git a/.github/workflows/build-and-archive.yml b/.github/workflows/build-and-archive.yml index a349d33..a22b456 100644 --- a/.github/workflows/build-and-archive.yml +++ b/.github/workflows/build-and-archive.yml @@ -10,7 +10,7 @@ jobs: fetch-depth: 15 - uses: actions/setup-node@v4 - name: Setup Lua/Teal - run: sudo apt install luarocks make zip && sudo luarocks install tl + run: sudo apt install luarocks make zip && sudo luarocks install tl && sudo luarocks install cerulean - name: Setup release bot run: npm install -g semantic-release @semantic-release/release-notes-generator @semantic-release/exec @commitlint/cli @commitlint/config-conventional - name: Lint diff --git a/Makefile b/Makefile index 2a17827..73bce31 100644 --- a/Makefile +++ b/Makefile @@ -28,3 +28,7 @@ package: ${LUTRO_DIST_PATH} lint: tl check ${SRCS_LINT} + ceru --check src + +format: + ceru src diff --git a/README.MD b/README.MD index 66b21af..ae977cf 100644 --- a/README.MD +++ b/README.MD @@ -2,9 +2,9 @@ A minimal Saturn Bomberman clone that is easy to play on RecalBox with friends. Will also work as a way to add new interesting game mechanics. ### Development setup -For developing and building, the dependencies [Lua](https://www.lua.org/), [LuaRocks](https://luarocks.org/), [Make](https://www.gnu.org/software/make/), Zip, and [Teal](https://teal-language.org/) are needed. +For developing and building, the dependencies [Lua](https://www.lua.org/), [LuaRocks](https://luarocks.org/), [Make](https://www.gnu.org/software/make/), Zip, [Cerulean](https://github.com/efredriksson/cerulean), and [Teal](https://teal-language.org/) are needed. -So, something like ```sudo apt install luarocks make zip``` and then ```sudo luarocks install tl``` or similar for your system should do the trick. +So, something like ```sudo apt install luarocks make zip``` and then ```sudo luarocks install tl && luarocks install cerulean``` or similar for your system should do the trick. You most likely want to setup so that you have a Teal LSP, see [here](https://github.com/teal-language/tl?tab=readme-ov-file#text-editor-support). diff --git a/src/bombs/bombs.tl b/src/bombs/bombs.tl index 248ccbd..bf1f6a5 100644 --- a/src/bombs/bombs.tl +++ b/src/bombs/bombs.tl @@ -116,8 +116,10 @@ function Bomb:_trigger_belt(direction?: Vector): ConveyorBelt | nil for _, belt in ipairs(belts) do if self.position:distance_to(grid.snap_to(self.position)) <= 3 then local snap_pos = grid.snap_to(self.position) - if snap_pos:equal(belt.position) - and not belt.direction:same_direction(bomb_move_dir) then + if + snap_pos:equal(belt.position) + and not belt.direction:same_direction(bomb_move_dir) + then return belt end end @@ -148,8 +150,10 @@ local function moving_into_new_block( snaps_to.x + i * grid.TILE_SIZE, snaps_to.y + j * grid.TILE_SIZE ) ) - if new_movementbox:overlap(close_block) - and not movementbox:overlap(close_block) then + if + new_movementbox:overlap(close_block) + and not movementbox:overlap(close_block) + then return close_block end end @@ -538,8 +542,9 @@ function Bombs:update( bomb_bouncing = true end - if not grid.area():contains(bomb:hitbox()) - and not bomb.delegate:get_goal() then + if + not grid.area():contains(bomb:hitbox()) and not bomb.delegate:get_goal() + then bomb_bouncing = true end diff --git a/src/dino_inputs.tl b/src/dino_inputs.tl index 73083d8..797fb43 100644 --- a/src/dino_inputs.tl +++ b/src/dino_inputs.tl @@ -92,8 +92,10 @@ local function handle_jump_input( dino.jump_powerup_for = dino.jump_powerup_for + dt end - if (joysticks.is_released(player.id, joysticks.BUTTONS.B) and player:can_jump()) - or player:must_jump() then + if + (joysticks.is_released(player.id, joysticks.BUTTONS.B) and player:can_jump()) + or player:must_jump() + then local jump_to = get_jump_target() player:jump_with_dino(jump_to) end diff --git a/src/engine/collision.tl b/src/engine/collision.tl index 0dc5a82..6d8898d 100644 --- a/src/engine/collision.tl +++ b/src/engine/collision.tl @@ -162,8 +162,10 @@ function Lookup:collisions_between( for j = y_min, y_max do local index = index_2d_to_1d(i, j) for _, check_against in ipairs(self.map[index] or {}) do - if entity ~= check_against - and entity:hitbox():overlap(check_against:hitbox()) then + if + entity ~= check_against + and entity:hitbox():overlap(check_against:hitbox()) + then local in_group = group:get(check_against) if not (in_group is nil) then collides_with[entity] = in_group diff --git a/src/engine/geometry/rectangles.tl b/src/engine/geometry/rectangles.tl index d0a31ce..f5cfb6f 100644 --- a/src/engine/geometry/rectangles.tl +++ b/src/engine/geometry/rectangles.tl @@ -31,8 +31,8 @@ end function Rectangle:overlap(other: Rectangle): boolean return interval_overlap(self.x, self.x + self.width, other.x, other.x + other.width) and interval_overlap( - self.y, self.y + self.height, other.y, other.y + other.height - ) + self.y, self.y + self.height, other.y, other.y + other.height + ) end local function interval_contained( diff --git a/src/engine/scenes.tl b/src/engine/scenes.tl index eb187bc..3e38ed0 100644 --- a/src/engine/scenes.tl +++ b/src/engine/scenes.tl @@ -133,7 +133,9 @@ function PerformanceSample:start(): number end function PerformanceSample:record(before_seconds: number, label: string) - self.accumulated_seconds = self.accumulated_seconds + (lutro.timer.getTime() - before_seconds) + self.accumulated_seconds = self.accumulated_seconds + ( + lutro.timer.getTime() - before_seconds + ) self.frame_count = self.frame_count + 1 if self.frame_count >= 300 then local avg_ms = (self.accumulated_seconds / self.frame_count) * 1000 diff --git a/src/engine/tracing.tl b/src/engine/tracing.tl index d276e8d..d0a16d7 100644 --- a/src/engine/tracing.tl +++ b/src/engine/tracing.tl @@ -77,9 +77,11 @@ function SpanTiming:_reqursive_report(indentation: string): string local parts: {string} = {} table.insert( parts, - indentation .. self.name .. ": " .. string.format( - "%.1f", self.elapsed * 1000 - ) .. "ms" + indentation + .. self.name + .. ": " + .. string.format("%.1f", self.elapsed * 1000) + .. "ms" ) for _, next_level in ipairs(self.next_levels) do table.insert(parts, next_level:_reqursive_report(indentation .. " ")) diff --git a/src/game/timer.tl b/src/game/timer.tl index f03251d..927f6d1 100644 --- a/src/game/timer.tl +++ b/src/game/timer.tl @@ -79,8 +79,10 @@ function GameTimer:update(dt: number) hurry_up_sound:play() end if self:warn_for_sudden_death() then - if self.hurry_text_position.x < hurry_letters_mid - and self.hurry_text_mid_countdown > 0 then + if + self.hurry_text_position.x < hurry_letters_mid + and self.hurry_text_mid_countdown > 0 + then self.hurry_text_mid_countdown = self.hurry_text_mid_countdown - dt else self.hurry_text_position = self.hurry_text_position:move( diff --git a/src/game/win_condition.tl b/src/game/win_condition.tl index 64dc7ea..a3863cb 100644 --- a/src/game/win_condition.tl +++ b/src/game/win_condition.tl @@ -46,8 +46,9 @@ function SinglesWinCondition:update(dt: number, players_: {Player}) self.is_standing_count = true end - if not self.have_winner - and self.time_of_1_alive_player > time_to_wait_to_end_game then + if + not self.have_winner and self.time_of_1_alive_player > time_to_wait_to_end_game + then local winner = alive_players[1] self.score_board:add_point(winner.id) self.have_winner = true @@ -91,8 +92,9 @@ function TeamsWinCondition:update(dt: number, players_: {Player}) self.is_standing_count = true end - if not self.have_winner - and self.time_of_1_alive_team > time_to_wait_to_end_game then + if + not self.have_winner and self.time_of_1_alive_team > time_to_wait_to_end_game + then for winning_team, _ in pairs(alive_teams) do self.score_board:add_point(winning_team) end diff --git a/src/itertools.tl b/src/itertools.tl index e4fa922..d0032be 100644 --- a/src/itertools.tl +++ b/src/itertools.tl @@ -22,8 +22,9 @@ function LoopIndex:values(): function(): integer local val = self.start - self.step local function value_iterator(): integer val = val + self.step - if (val <= self.end_ and self.step > 0) - or (val >= self.end_ and self.step < 0) then + if + (val <= self.end_ and self.step > 0) or (val >= self.end_ and self.step < 0) + then return val end end diff --git a/src/level_layout/block_layouts.tl b/src/level_layout/block_layouts.tl index 6189c19..267ce42 100644 --- a/src/level_layout/block_layouts.tl +++ b/src/level_layout/block_layouts.tl @@ -64,8 +64,10 @@ local function place_soft_blocks( local i = lutro.math.random(dim.x_max + 1) - 1 local j = lutro.math.random(dim.y_max + 1) - 1 local cell: BlockType = layout[i][j] - if cell == "none" - and not contains_point(exclusion_zone, grid.get_tile_position(i, j)) then + if + cell == "none" + and not contains_point(exclusion_zone, grid.get_tile_position(i, j)) + then layout[i][j] = "soft" count = count + 1 end diff --git a/src/mad_bombers.tl b/src/mad_bombers.tl index 25ddd86..2d8702b 100644 --- a/src/mad_bombers.tl +++ b/src/mad_bombers.tl @@ -124,7 +124,8 @@ function PlayerCart.new(id: integer, position: number, head: CharacterName): Pla self.cannon_powerup_for = 0.0 self.turbo = false self.reload_countdown = 0.0 - self.animations, self.loading_animations, self.firing_animations = get_cart_animations() + self.animations, self.loading_animations, self.firing_animations = get_cart_animations( + ) return self end @@ -360,13 +361,16 @@ function MadBombers:handle_cart_move_input(dt: number, cart: PlayerCart) cart:set_state("backward") end - if lutro.joystick.isDown(cart.id, joysticks.BUTTONS.R) - and cart.cannon == "ready" then + if + lutro.joystick.isDown(cart.id, joysticks.BUTTONS.R) and cart.cannon == "ready" + then cart.cannon_powerup_for = cart.cannon_powerup_for + dt end - if joysticks.is_released(cart.id, joysticks.BUTTONS.R) - or cart.cannon_powerup_for > 0.8 then + if + joysticks.is_released(cart.id, joysticks.BUTTONS.R) + or cart.cannon_powerup_for > 0.8 + then local fire_from, fire_len = cart:fire_cannon() if fire_from then local fire_dir = THROW_BOMB_DIRECTION_BY_SIDE[side] diff --git a/src/menus/update_screen.tl b/src/menus/update_screen.tl index fec7052..6d38802 100644 --- a/src/menus/update_screen.tl +++ b/src/menus/update_screen.tl @@ -87,8 +87,10 @@ function UpdateCheckScene:download_finished(): boolean end function UpdateCheckScene:update(dt: number) - if joysticks.is_pressed_any(joysticks.BUTTONS.L) - or joysticks.is_pressed_any(joysticks.BUTTONS.A) then + if + joysticks.is_pressed_any(joysticks.BUTTONS.L) + or joysticks.is_pressed_any(joysticks.BUTTONS.A) + then scenes.set(self.ctx) return end @@ -105,7 +107,10 @@ function UpdateCheckScene:update(dt: number) end if self.have_connection and not (self.started_download or self.is_up_to_date) then self.started_download = true - local download_cmd = "wget -O " .. self:download_dst() .. " " .. self.download_url + local download_cmd = "wget -O " + .. self:download_dst() + .. " " + .. self.download_url local finish_touch = " && touch " .. self:download_finish_marker() os.execute(download_cmd .. finish_touch .. " &") end diff --git a/src/movement.tl b/src/movement.tl index d3ad7a3..67eba35 100644 --- a/src/movement.tl +++ b/src/movement.tl @@ -222,8 +222,10 @@ local function restrict_player_movement( local bombs_player_is_inside: {Kinematic: Kinematic} = {} for _, bomb in ipairs(close_bombs) do - if bomb.position:distance_to(player.position) <= grid.TILE_SIZE * (3 / 4) - and not bomb.has_been_kicked then + if + bomb.position:distance_to(player.position) <= grid.TILE_SIZE * (3 / 4) + and not bomb.has_been_kicked + then bombs_player_is_inside[bomb] = bomb end end @@ -263,8 +265,9 @@ local function restrict_player_movement( for _, bomb in ipairs(close_bombs) do local bomb_hitbox = bomb:movementbox() - if walking_further_into_block(bomb_hitbox) - and not bombs_player_is_inside[bomb] then + if + walking_further_into_block(bomb_hitbox) and not bombs_player_is_inside[bomb] + then -- If a bomb has been kicked then it is no longer to move -- freely in far inside it. This is to stop moving through -- bombs that are kicked against a player. @@ -294,8 +297,9 @@ local function restrict_player_movement( for _, bomb in ipairs(close_lightning_bombs) do local bomb_hitbox = bomb:movementbox() - if walking_further_into_block(bomb_hitbox) - and not bombs_player_is_inside[bomb] then + if + walking_further_into_block(bomb_hitbox) and not bombs_player_is_inside[bomb] + then local corner_cut = try_corner_cut(bomb_hitbox, bomb) if corner_cut then return corner_cut diff --git a/src/player_animations.tl b/src/player_animations.tl index fa9deed..2857dbd 100644 --- a/src/player_animations.tl +++ b/src/player_animations.tl @@ -272,9 +272,11 @@ end function PlayerEntity:_get_animation(): Animation local function get_direction(): Direction - if self.player.dino + if + self.player.dino and self.player.dino.action == "slide" - and self.player.dino.level == 2 then + and self.player.dino.level == 2 + then -- For level 2 dino sliding the character riding it should turn depending -- on the direction sliding for it to look like they bracing sliding to a stop: return SLIDE_WIHT_DINO_DIR_MAP[self.player.direction] @@ -338,9 +340,11 @@ function PlayerEntity:_draw_mounted() draw_sonic_blasts(self.player) end - if self.player.action == "dash" + if + self.player.action == "dash" and self.player.dino - and self.player.dino.level == 3 then + and self.player.dino.level == 3 + then local shift_vector = DINO_AFTERIMGE_SHIFT[self.player.direction]:scale( self.player.after_image_shift ) @@ -348,9 +352,11 @@ function PlayerEntity:_draw_mounted() draw(dino_animation, dino_position():move(shift_vector)) end - if self.player.action == "slide" + if + self.player.action == "slide" and self.player.dino - and self.player.dino.level == 2 then + and self.player.dino.level == 2 + then local character_pos = draw_position:move( DINO_SLIDE_CHARACTER_ADJUST[self.player.direction] ) @@ -418,7 +424,12 @@ local function character_sprite_for_action( end end - local base_path = "assets/" .. character_asset .. "/" .. state .. "/" .. action_resolved() + local base_path = "assets/" + .. character_asset + .. "/" + .. state + .. "/" + .. action_resolved() return { up = lutro.graphics.newImage(base_path .. "_up.png"), down = lutro.graphics.newImage(base_path .. "_down.png"), @@ -507,7 +518,11 @@ function player_animations.load_assets() local function get_stun_cry_text_for_dir(dir: string): {integer: Image} local result: {integer: Image} = {} for level = 1, 3 do - local asset = "assets/tirras/level_" .. level .. "/stun_cry_text_" .. dir .. ".png" + local asset = "assets/tirras/level_" + .. level + .. "/stun_cry_text_" + .. dir + .. ".png" result[level] = lutro.graphics.newImage(asset) end diff --git a/src/player_selection.tl b/src/player_selection.tl index 64bf8bf..56d3635 100644 --- a/src/player_selection.tl +++ b/src/player_selection.tl @@ -200,8 +200,10 @@ function PlayerSelection:handle_player_input() ok_action = self.selector:increment() elseif joysticks.is_pressed(self.player_selecting, joysticks.BUTTONS.LEFT) then ok_action = self.selector:decrement() - elseif (joysticks.is_pressed(self.player_selecting, joysticks.BUTTONS.B) - or joysticks.is_pressed(self.player_selecting, joysticks.BUTTONS.R)) then + elseif ( + joysticks.is_pressed(self.player_selecting, joysticks.BUTTONS.B) + or joysticks.is_pressed(self.player_selecting, joysticks.BUTTONS.R) + ) then local selected = table.remove( self.selectable_characters, self.selector:get_value() ) diff --git a/src/players.tl b/src/players.tl index 05f5a9c..f7124c8 100644 --- a/src/players.tl +++ b/src/players.tl @@ -216,8 +216,10 @@ function Player:set_action(action: Action) return end - if (self.action == "dash" or action == "roar") - and (action == "walk" or action == "idle") then + if + (self.action == "dash" or action == "roar") + and (action == "walk" or action == "idle") + then -- Not possible to exit dash action into walk or idle return end @@ -385,9 +387,9 @@ function Player:can_jump(): boolean end function Player:get_max_jump_dist(): integer - local jump_dist = 2 * (math.floor( - self.dino.jump_powerup_for / DINO_POWERUP_TIME_INTERVAL - ) + 1) + local jump_dist = 2 * ( + math.floor(self.dino.jump_powerup_for / DINO_POWERUP_TIME_INTERVAL) + 1 + ) -- Clamp max jump dist return math.min(jump_dist, self.dino.level * 2) end @@ -541,9 +543,8 @@ function Player:update(dt: number) self.roar_for = self.roar_for - dt self.last_time_adding_sonic_wave = self.last_time_adding_sonic_wave + dt - self.after_image_shift = self.after_image_shift + self:get_speed() * dt + lutro.math.random( - 4 - ) + self.after_image_shift = self.after_image_shift + self:get_speed( + ) * dt + lutro.math.random(4) self.after_image_shift = math.fmod(self.after_image_shift, 37) if self:is_sonic_blasting() then diff --git a/src/team_selection.tl b/src/team_selection.tl index a47a366..029ee55 100644 --- a/src/team_selection.tl +++ b/src/team_selection.tl @@ -280,7 +280,13 @@ local function get_image( is_walking: boolean, sprite: CharacterName, direction: Direction ): Image local walk_name = {[true] = "walk", [false] = "idle"} - local path = "assets/" .. sprite .. "/foot/" .. walk_name[is_walking] .. "_" .. direction .. ".png" + local path = "assets/" + .. sprite + .. "/foot/" + .. walk_name[is_walking] + .. "_" + .. direction + .. ".png" return lutro.graphics.newImage(path) end