From 19e95ed7918ff18f31d247c8a9f493cc6347f8c7 Mon Sep 17 00:00:00 2001 From: George Sokianos Date: Sat, 9 Mar 2024 13:11:31 +0000 Subject: [PATCH] Fixed ghmarkdown plugin, added tetris plugin, prepare a new release" --- README_Amiga.md | 12 +- resources/amiga/addons/plugins/ghmarkdown.lua | 29 +- resources/amiga/addons/plugins/tetris.lua | 414 ++++++++++++++++++ src/main.c | 2 +- 4 files changed, 452 insertions(+), 5 deletions(-) create mode 100644 resources/amiga/addons/plugins/tetris.lua diff --git a/README_Amiga.md b/README_Amiga.md index 4cdfc23d..916f3049 100644 --- a/README_Amiga.md +++ b/README_Amiga.md @@ -115,7 +115,10 @@ Marks tabs as non-preview on any change or tab double clicking. **ghmarkdown** Opens a preview of the current markdown file in a browser window. On AmigaOS 4 it uses *urlopen* and on MorphOS it uses *openurl* to load -the generated html in the browser. +the generated html in the browser. It requires a GitHub application token +because it uses its Rest API. Add it at the init.lua file in your config +folder like below: +`config.plugins.ghmarkdown.github_token = ""` **indentguide** Adds indent guides @@ -174,6 +177,9 @@ Highlights regions of code that match the current selection **smallclock** It adds a small clock at the bottom right corner. +**tetris** +Play Tetris inside Lite XL. + ## Tips and tricks ### Transitions @@ -223,9 +229,10 @@ https://git.walkero.gr/walkero/lite-xl/issues # Changelog -## [2.1.3r1] - future +## [2.1.3r1] - 2024-03-09 ### Added - Added AmiUpdate support +- Added the Tetris plugin ### Updated - Updated the code to the upstream 2.1.3 release @@ -248,6 +255,7 @@ https://git.walkero.gr/walkero/lite-xl/issues not be opened - Improved the folder suggestions when opening projects or changing paths. Now even the root folders of a partition are presented +- Fixed ghmarkdown plugin, but now requires a GitHub token to be provided ## [2.1.2r1] - 2023-12-19 ### Added diff --git a/resources/amiga/addons/plugins/ghmarkdown.lua b/resources/amiga/addons/plugins/ghmarkdown.lua index 003acd71..c5a1f14f 100644 --- a/resources/amiga/addons/plugins/ghmarkdown.lua +++ b/resources/amiga/addons/plugins/ghmarkdown.lua @@ -1,8 +1,25 @@ -- mod-version:3 local core = require "core" local command = require "core.command" +local common = require "core.common" +local config = require "core.config" local keymap = require "core.keymap" +config.plugins.ghmarkdown = common.merge({ + -- Find information on how to generate your own token at + -- https://docs.github.com/en/rest/markdown/markdown?apiVersion=2022-11-28#render-a-markdown-document-in-raw-mode + github_token = "", + config_spec = { + name = "GHMarkdown", + { + label = "GitHub token", + description = "Enter your personal GitHub token", + path = "github_token", + type = "string", + default = "" + } + } +}, config.plugins.ghmarkdown) local html = [[ @@ -31,7 +48,9 @@ local html = [[ @@ -42,11 +61,17 @@ local html = [[ command.add("core.docview!", { ["ghmarkdown:show-preview"] = function(dv) + if config.plugins.ghmarkdown.github_token == "" then + core.error "You need to provide your own GitHub token" + return + end + local content = dv.doc:get_text(1, 1, math.huge, math.huge) local esc = { ['"'] = '\\"', ["\n"] = '\\n' } local text = html:gsub("${(.-)}", { title = dv:get_name(), - content = content:gsub(".", esc) + content = content:gsub(".", esc), + token = config.plugins.ghmarkdown.github_token }) local htmlfile = core.temp_filename(".html") diff --git a/resources/amiga/addons/plugins/tetris.lua b/resources/amiga/addons/plugins/tetris.lua new file mode 100644 index 00000000..b7b2258f --- /dev/null +++ b/resources/amiga/addons/plugins/tetris.lua @@ -0,0 +1,414 @@ +-- mod-version:3 + +local core = require "core" +local config = require "core.config" +local style = require "core.style" +local common = require "core.common" +local View = require "core.view" +local command = require "core.command" +local keymap = require "core.keymap" + +local TetrisView = View:extend() + +config.plugins.tetris = common.merge({ + tick = 0.5, -- The amount of time in seconds it takes for a piece to fall one line at 0 score. + height = 30, -- The amount of cells of height. + width = 10, -- The amount of cells of width. + cell_size = 18, -- The size in pixels of each cell. + cell_padding = 2, -- pixels between each cell + drop_shadow = true, -- Should cast a drop shadow. + lock_delay = 3, -- the multiplier for lock delay over a normal tick. set to 0 to disable + down_amount = 1 -- the amount we move a tetronimo down when you hit the down key; change to math.huge for instant. +}, config.plugins.tetris) + +function TetrisView:new(options) + TetrisView.super.new(self) + self.cell_size = options.cell_size + self.cell_padding = options.cell_padding + self.grid = { x = options.width, y = options.height } + self.size.x = self.grid.x * (self.cell_size + self.cell_padding) + style.padding.x * 2 + self.cells = { } + self.score = 0 + self.paused = false + self.initial_tick = options.tick + self.lock_delay = options.lock_delay + self.drop_shadow = options.drop_shadow + self.tick = self:calculate_tick(self.score) + self.finished = false + self.thread = core.add_thread(function() + while not self.finished do + self:step() + core.redraw = true + coroutine.yield(self.tick) + end + end) + -- easier to specify rotations than to start doing matrix multiplication + self.tetronimos = { + { + color = { common.color "#ff0000" }, + shape = { { + 0,1,1,0, + 0,1,1,0, + 0,0,0,0, + 0,0,0,0 + } } + }, + { + color = { common.color "#00ff00" }, + shape = { { + 1,1,0,0, + 1,0,0,0, + 1,0,0,0, + 0,0,0,0 + }, { + 1,1,1,0, + 0,0,1,0, + 0,0,0,0, + 0,0,0,0 + }, { + 0,0,1,0, + 0,0,1,0, + 0,1,1,0, + 0,0,0,0 + }, { + 0,0,0,0, + 1,0,0,0, + 1,1,1,0, + 0,0,0,0 + } } + }, + { + color = { common.color "#0000ff" }, + shape = { { + 1,0,0,0, + 1,0,0,0, + 1,1,0,0, + 0,0,0,0 + }, { + 1,1,1,0, + 1,0,0,0, + 0,0,0,0, + 0,0,0,0 + }, { + 0,1,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,0,0 + }, { + 0,0,0,0, + 0,0,1,0, + 1,1,1,0, + 0,0,0,0 + } } + }, + { + color = { common.color "#00ffff" }, + shape = { { + 1,1,0,0, + 0,1,1,0, + 0,0,0,0, + 0,0,0,0 + }, { + 0,0,1,0, + 0,1,1,0, + 0,1,0,0, + 0,0,0,0 + }, { + 0, 0,0,0, + 1,1,0,0, + 0,1,1,0, + 0,0,0,0 + }, { + 0,1,0,0, + 1,1,0,0, + 1,0,0,0, + 0,0,0,0 + } } + }, + { + color = { common.color "#ffff00" }, + shape = { { + 0,1,1,0, + 1,1,0,0, + 0,0,0,0, + 0,0,0,0 + }, { + 0,1,0,0, + 0,1,1,0, + 0,0,1,0, + 0,0,0,0 + }, { + 0,0,0,0, + 0,1,1,0, + 1,1,0,0, + 0,0,0,0 + }, { + 1,0,0,0, + 1,1,0,0, + 0,1,0,0, + 0,0,0,0 + } } + }, + { + color = { common.color "#ff00ff" }, + shape = { { + 0,1,0,0, + 0,1,0,0, + 0,1,0,0, + 0,1,0,0 + }, { + 0,0,0,0, + 1,1,1,1, + 0,0,0,0, + 0,0,0,0 + }, { + 0,0,1,0, + 0,0,1,0, + 0,0,1,0, + 0,0,1,0 + }, { + 0,0,0,0, + 0,0,0,0, + 1,1,1,1, + 0,0,0,0 + } } + }, + { + color = { common.color "#ffffff" }, + shape = { { + 1,1,1,0, + 0,1,0,0, + 0,0,0,0, + 0,0,0,0 + }, { + 0,0,1,0, + 0,1,1,0, + 0,0,1,0, + 0,0,0,0 + }, { + 0,0,0,0, + 0,1,0,0, + 1,1,1,0, + 0,0,0,0 + }, { + 1,0,0,0, + 1,1,0,0, + 1,0,0,0, + 0,0,0,0 + } } + } + } + self.live_piece = nil + self.hold_piece = nil +end + +function TetrisView:calculate_tick(score) + return self.initial_tick / (math.floor(score / 10) + 1) +end + + +function TetrisView:does_collide(x, y, tetronimo, rot) + local shape = tetronimo.shape[rot] + for i = 0, 3 do + for j = 0, 3 do + local ny = y + i + local nx = x + j + if (nx >= self.grid.x or ny >= self.grid.y or nx < 0 or ny < 0 or self.cells[self.grid.x * ny + nx + 1]) and shape[i * 4 + j + 1] == 1 then + return true + end + end + end + return false +end + +function TetrisView:finalize_live_piece() + assert(self.live_piece) + local shape = self.live_piece.tetronimo.shape[self.live_piece.rot] + for i = 0, 3 do + for j = 0, 3 do + local ny = self.live_piece.y + i + local nx = self.live_piece.x + j + if shape[(i * 4 + j) + 1] == 1 then + self.cells[ny * self.grid.x + nx + 1] = self.live_piece.idx + end + end + end + for y = self.live_piece.y, math.min(self.live_piece.y + 4, self.grid.y - 1) do + local all_present = true + for x = 0, self.grid.x - 1 do + if not self.cells[y * self.grid.x + x + 1] then all_present = false end + end + if all_present then + self.score = self.score + 1 + self.tick = self:calculate_tick(self.score) + for ny = y, 2, -1 do + for nx = 0, self.grid.x - 1 do + self.cells[ny * self.grid.x + nx + 1] = self.cells[(ny - 1) * self.grid.x + nx + 1] + end + end + end + end + self.live_piece = nil +end + + +function TetrisView:step() + if not self.finished and not self.paused then + if not self.live_piece then + local idx = self.next_piece or math.floor(math.random() * #self.tetronimos) + 1 + self.live_piece = { tetronimo = self.tetronimos[idx], idx = idx, x = math.floor(self.grid.x / 2), y = 0, rot = 1 } + self.next_piece = math.floor(math.random() * #self.tetronimos) + 1 + if (self:does_collide(self.live_piece.x, self.live_piece.y + 1, self.live_piece.tetronimo, self.live_piece.rot)) then + self:finalize_live_piece() + self.finished = true + end + else + if (self:does_collide(self.live_piece.x, self.live_piece.y + 1, self.live_piece.tetronimo, self.live_piece.rot)) then + self.live_piece.countup = (self.live_piece.countup or 0) + 1 + if self.live_piece.countup > self.lock_delay then + self:finalize_live_piece() + end + else + self.live_piece.y = self.live_piece.y + 1 + self.live_piece.countup = 0 + end + end + end +end + +function TetrisView:draw_tetronimo(posx, posy, tetronimo, rot, color) + local shape = tetronimo.shape[rot] + for y = 0, 3 do + for x = 0, 3 do + if shape[y * 4 + x + 1] == 1 then + renderer.draw_rect(posx + x * (self.cell_size + self.cell_padding), posy + y * (self.cell_size + self.cell_padding), self.cell_size, self.cell_size, color or tetronimo.color) + end + end + end +end + +function TetrisView:draw() + self:draw_background(style.background3) + local lh = style.font:get_height() + local tx = self.position.x + style.padding.x + local ty = self.position.y + style.padding.y + + renderer.draw_text(style.font, "Score: " .. self.score, tx, self.position.y + style.padding.y, style.normal) + local w = renderer.draw_text(style.font, "Next Piece", tx, self.position.y + style.padding.y + lh, style.normal) + if self.next_piece then + self:draw_tetronimo(tx, self.position.y + style.padding.y + lh * 2, self.tetronimos[self.next_piece], 1) + end + if self.held_piece then + self:draw_tetronimo(w + style.padding.x, self.position.y + style.padding.y + lh * 2, self.tetronimos[self.held_piece], 1) + end + renderer.draw_text(style.font, "Held Piece", w + style.padding.x, self.position.y + style.padding.y + lh, style.normal) + ty = ty + lh * 2 + (self.cell_size + self.cell_padding) * 4 + style.padding.y + + renderer.draw_rect(tx, ty, (self.cell_size + self.cell_padding) * self.grid.x, (self.cell_size + self.cell_padding) * self.grid.y, style.background) + for y = 0, self.grid.y - 1 do + for x = 0, self.grid.x - 1 do + if self.cells[y * self.grid.x + x + 1] then + local color = self.tetronimos[self.cells[y * self.grid.x + x + 1]].color + renderer.draw_rect(tx + x * (self.cell_size + self.cell_padding), ty + y * (self.cell_size + self.cell_padding), self.cell_size, self.cell_size, color) + end + end + end + if self.live_piece then + self:draw_tetronimo(tx + self.live_piece.x * (self.cell_size + self.cell_padding), ty + self.live_piece.y * (self.cell_size + self.cell_padding), self.live_piece.tetronimo, self.live_piece.rot) + if self.drop_shadow then + local y = self:get_max_drop(math.huge) + if y ~= self.live_piece.y then + self:draw_tetronimo(tx + self.live_piece.x * (self.cell_size + self.cell_padding), ty + y * (self.cell_size + self.cell_padding), self.live_piece.tetronimo, self.live_piece.rot, { self.live_piece.tetronimo.color[1], self.live_piece.tetronimo.color[2], self.live_piece.tetronimo.color[3], 50 }) + end + end + end + if self.finished or self.paused then renderer.draw_rect(tx, ty, self.grid.x * (self.cell_size + self.cell_padding), self.grid.y * (self.cell_size + self.cell_padding), { common.color "rgba(255, 255, 255, 0.5)" }) end + if self.finished then common.draw_text(style.font, style.error, "GAME OVER", "center", tx, ty, self.grid.x * (self.cell_size + self.cell_padding), self.grid.y * (self.cell_size + self.cell_padding)) end + if self.paused then common.draw_text(style.font, style.warn, "PAUSED", "center", tx, ty, self.grid.x * (self.cell_size + self.cell_padding), self.grid.y * (self.cell_size + self.cell_padding)) end +end + +function TetrisView:rotate() + if self.live_piece and not self.paused then + local new_rot = (self.live_piece.rot % #self.live_piece.tetronimo.shape) + 1 + if not self:does_collide(self.live_piece.x, self.live_piece.y, self.live_piece.tetronimo, new_rot) then + self.live_piece.rot = new_rot + end + end +end + +function TetrisView:hold() + if self.live_piece and not self.paused then + if self.held_piece then + if not self:does_collide(self.live_piece.x, self.live_piece.y, self.tetronimos[self.held_piece], 1) then + local live_piece = self.live_piece.idx + self.live_piece = { x = self.live_piece.x, y = self.live_piece.y, rot = 1, idx = self.held_piece, tetronimo = self.tetronimos[self.held_piece] } + self.held_piece = live_piece + end + else + self.held_piece = self.live_piece.idx + self.live_piece = nil + end + end +end + +function TetrisView:get_max_drop(amount) + if self.live_piece then + for y = self.live_piece.y, math.min(self.grid.y, self.live_piece.y + amount) do + if self:does_collide(self.live_piece.x, y + 1, self.live_piece.tetronimo, self.live_piece.rot) then + return y, true + end + end + end + return self.live_piece.y + amount, false +end + +function TetrisView:drop(amount) + if self.live_piece and not self.paused then + local y, collides = self:get_max_drop(amount) + self.live_piece.y = y + if collides then + self:finalize_live_piece() + end + end +end + +function TetrisView:shift(delta) + if self.live_piece and not self.paused and not self:does_collide(self.live_piece.x + delta, self.live_piece.y, self.live_piece.tetronimo, self.live_piece.rot) then + self.live_piece.x = self.live_piece.x + delta + end +end + +command.add(TetrisView, { + ["tetris:rotate"] = function() core.active_view:rotate() end, + ["tetris:shift-left"] = function() core.active_view:shift(-1) end, + ["tetris:shift-right"] = function() core.active_view:shift(1) end, + ["tetris:drop"] = function() core.active_view:drop(config.plugins.tetris.down_amount) end, + ["tetris:hard-drop"] = function() core.active_view:drop(math.huge) end, + ["tetris:hold"] = function() core.active_view:hold() end, + ["tetris:toggle-pause"] = function() core.active_view.paused = not core.active_view.paused end, + ["tetris:quit"] = function() + core.active_view.finished = true + core.active_view.node:close_view(core.root_view.root_node, core.active_view) + end +}) +command.add(nil, { + ["tetris:start"] = function() + local view = TetrisView(config.plugins.tetris) + local node = core.root_view:get_active_node() + view.node = node:split("right", view, { x = true }, false) + core.set_active_view(view) + end +}) + +keymap.add { + ["up"] = "tetris:rotate", + ["left"] = "tetris:shift-left", + ["right"] = "tetris:shift-right", + ["down"] = "tetris:drop", + ["space"] = "tetris:hard-drop", + ["tab"] = "tetris:hold", + ["escape"] = "tetris:quit", + ["ctrl+e"] = { "tetris:quit", "tetris:start" }, + ["p"] = "tetris:toggle-pause" +} +return { view = TetrisView } diff --git a/src/main.c b/src/main.c index ef5efd25..dc55323b 100644 --- a/src/main.c +++ b/src/main.c @@ -8,7 +8,7 @@ #include #if defined(__amigaos4__) || defined(__morphos__) -#define VSTRING "Lite XL 2.1.3r1 (17.02.2024)" +#define VSTRING "Lite XL 2.1.3r1 (09.03.2024)" #define VERSTAG "\0$VER: " VSTRING #endif