├── .gitignore ├── imgs ├── items.png └── range.png ├── utilities ├── pick color.bas └── replace color.bas ├── external formats ├── import 3x4 sheet.bas ├── import 4x4 sheet.bas ├── import frame.bas ├── import tilesheet.bas ├── export frame.bas ├── export frames.bas └── import sequence.bas ├── LICENSE ├── common ├── frame.bas ├── sheet.bas └── utils.bas ├── working area ├── flip vertically.bas ├── flip horizontally.bas ├── shift up.bas ├── shift down.bas ├── shift left.bas ├── shift right.bas ├── rotate clockwise.bas └── rotate anticlockwise.bas ├── frames └── copy to all.bas └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /ignored 2 | -------------------------------------------------------------------------------- /imgs/items.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paladin-t/b8.plugins/HEAD/imgs/items.png -------------------------------------------------------------------------------- /imgs/range.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paladin-t/b8.plugins/HEAD/imgs/range.png -------------------------------------------------------------------------------- /utilities/pick color.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Loads the plugin. 6 | 7 | def plug() 8 | print "Loading plugin: Pick color..."; 9 | register_plugin 10 | ( 11 | "sprite, tiles, quantized", ' Target assets. 12 | "Pick color", ' Name. 13 | "Active palette with picked color", ' Tooltips. 14 | false, ' Selection only? 15 | false, ' Square only? 16 | 10, ' Category. 17 | 10 ' Priority. 18 | ) 19 | enddef 20 | 21 | REM Runs the plugin. 22 | 23 | def run() 24 | print "Running plugin: Pick color..."; 25 | p = get_cursor_position() 26 | if p = nil then 27 | return 28 | endif 29 | x = 0 30 | y = 0 31 | unpack(p, x, y) 32 | i = get_pixel(x, y) 33 | set_source_index(i) 34 | enddef 35 | 36 | REM Checks the operation. 37 | 38 | ph = get_phase() 39 | if ph = "plug" then 40 | plug() 41 | elseif ph = "run" then 42 | run() 43 | endif 44 | -------------------------------------------------------------------------------- /external formats/import 3x4 sheet.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/sheet" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Import 3x4 sheet..."; 13 | register_plugin 14 | ( 15 | "sprite", ' Target assets. 16 | "Import 3x4 sheet", ' Name. 17 | "Import an image sheet to the current sprite asset", ' Tooltips. 18 | false, ' Selection only? 19 | false, ' Square only? 20 | 40, ' Category. 21 | 30 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | print "Running plugin: Import 3x4 sheet..."; 29 | f = open_file_dialog("png,bmp,tga,jpg") 30 | if not f then 31 | return 32 | endif 33 | import_sheet_file(f, 3, 4, true) ' Imports 3x4 sheet, stretches if necessary. 34 | enddef 35 | 36 | REM Checks the operation. 37 | 38 | ph = get_phase() 39 | if ph = "plug" then 40 | plug() 41 | elseif ph = "run" then 42 | run() 43 | endif 44 | -------------------------------------------------------------------------------- /external formats/import 4x4 sheet.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/sheet" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Import 4x4 sheet..."; 13 | register_plugin 14 | ( 15 | "sprite", ' Target assets. 16 | "Import 4x4 sheet", ' Name. 17 | "Import an image sheet to the current sprite asset", ' Tooltips. 18 | false, ' Selection only? 19 | false, ' Square only? 20 | 40, ' Category. 21 | 20 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | print "Running plugin: Import 4x4 sheet..."; 29 | f = open_file_dialog("png,bmp,tga,jpg") 30 | if not f then 31 | return 32 | endif 33 | import_sheet_file(f, 4, 4, true) ' Imports 4x4 sheet, stretches if necessary. 34 | enddef 35 | 36 | REM Checks the operation. 37 | 38 | ph = get_phase() 39 | if ph = "plug" then 40 | plug() 41 | elseif ph = "run" then 42 | run() 43 | endif 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2018 - 2019 Tony Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /common/frame.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | import "common/utils" 6 | 7 | ' Gets all pixels within an area in the current asset context. 8 | ' @param x0 - X position. 9 | ' @param y0 - Y position. 10 | ' @param w - Width of an area. 11 | ' @param h - Height of an area. 12 | ' @return - Returns a dictionary with hash value as key, pixel data as value. 13 | def get_pixels_rect(x0, y0, w, h) 14 | d = dict() 15 | for j = 0 to h - 1 16 | y = y0 + j 17 | for i = 0 to w - 1 18 | x = x0 + i 19 | k = hash_xy(x, y, w) 20 | c = get_pixel(x, y) 21 | set(d, k, c) 22 | next 23 | next 24 | return d 25 | enddef 26 | 27 | ' Checks whether the current frame is blank in the current asset context. 28 | ' @param t - Transparent palette index. 29 | ' @return - Returns true if it's blank. 30 | def is_frame_blank(t) 31 | w = 0 32 | h = 0 33 | v = get_frame_size() 34 | unpack(v, w, h) 35 | for j = 0 to h - 1 36 | for i = 0 to w - 1 37 | c = get_pixel(i, j) 38 | if c <> t then 39 | return false 40 | endif 41 | next 42 | next 43 | return true 44 | enddef 45 | -------------------------------------------------------------------------------- /utilities/replace color.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Loads the plugin. 6 | 7 | def plug() 8 | print "Loading plugin: Replace color..."; 9 | register_plugin 10 | ( 11 | "sprite, map, tiles, quantized", ' Target assets. 12 | "Replace color", ' Name. 13 | "Replace all pixels in the color at the specified position with the active palette", ' Tooltips. 14 | false, ' Selection only? 15 | false, ' Square only? 16 | 10, ' Category. 17 | 20 ' Priority. 18 | ) 19 | enddef 20 | 21 | REM Runs the plugin. 22 | 23 | def run() 24 | ' Prepares. 25 | print "Running plugin: Replace color..."; 26 | src = 0 27 | tgt = 0 28 | ' Gets the source and target colors. 29 | p = get_cursor_position() 30 | if p = nil then 31 | return 32 | endif 33 | x = 0 34 | y = 0 35 | unpack(p, x, y) 36 | src = get_pixel(x, y) 37 | tgt = get_source_index() 38 | if src = tgt then 39 | return 40 | endif 41 | ' Sets with the new color. 42 | begin_operation("Replace color") 43 | v = get_frame_size() 44 | unpack(v, w, h) 45 | for j = 0 to h - 1 46 | for i = 0 to w - 1 47 | c = get_pixel(i, j) 48 | if c = src then 49 | set_pixel(i, j, tgt) 50 | endif 51 | next 52 | next 53 | end_operation() 54 | ' Marks the asset dirty. 55 | set_asset_unsaved() 56 | enddef 57 | 58 | REM Checks the operation. 59 | 60 | ph = get_phase() 61 | if ph = "plug" then 62 | plug() 63 | elseif ph = "run" then 64 | run() 65 | endif 66 | -------------------------------------------------------------------------------- /external formats/import frame.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | REM Loads the plugin. 8 | 9 | def plug() 10 | print "Loading plugin: Import frame..."; 11 | register_plugin 12 | ( 13 | "sprite, quantized", ' Target assets. 14 | "Import frame", ' Name. 15 | "Import a frame of image to the current asset", ' Tooltips. 16 | false, ' Selection only? 17 | false, ' Square only? 18 | 40, ' Category. 19 | 50 ' Priority. 20 | ) 21 | enddef 22 | 23 | REM Runs the plugin. 24 | 25 | def run() 26 | ' Prepares. 27 | print "Running plugin: Import frame..."; 28 | w = 0 29 | h = 0 30 | ' Gets the asset information. 31 | v = get_frame_size() 32 | unpack(v, w, h) 33 | ' Gets a file. 34 | f = open_file_dialog("png,bmp,tga,jpg") 35 | if not f then 36 | return 37 | endif 38 | ' Gets the source frame. 39 | g = image() 40 | g.load(f) 41 | iw = g.len(0) 42 | ih = g.len(1) 43 | if iw <> w or ih <> h then 44 | g.resize(w, h) 45 | iw = w 46 | ih = h 47 | endif 48 | ' Fills the frame. 49 | begin_operation("Fill frame") 50 | for j = 0 to h - 1 51 | for i = 0 to w - 1 52 | c = g.get(i, j) 53 | c = get_palette_from_color(c) 54 | set_pixel(i, j, c) 55 | next 56 | next 57 | end_operation() 58 | ' Marks the asset dirty. 59 | set_asset_unsaved() 60 | enddef 61 | 62 | REM Checks the operation. 63 | 64 | ph = get_phase() 65 | if ph = "plug" then 66 | plug() 67 | elseif ph = "run" then 68 | run() 69 | endif 70 | -------------------------------------------------------------------------------- /external formats/import tilesheet.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | REM Loads the plugin. 8 | 9 | def plug() 10 | print "Loading plugin: Import tilesheet..."; 11 | register_plugin 12 | ( 13 | "tiles", ' Target assets. 14 | "Import tilesheet", ' Name. 15 | "Import a tilesheet of image to the current tiles asset", ' Tooltips. 16 | false, ' Selection only? 17 | false, ' Square only? 18 | 40, ' Category. 19 | 40 ' Priority. 20 | ) 21 | enddef 22 | 23 | REM Runs the plugin. 24 | 25 | def run() 26 | ' Prepares. 27 | print "Running plugin: Import tilesheet..."; 28 | w = 0 29 | h = 0 30 | ' Gets the asset information. 31 | v = get_frame_size() 32 | unpack(v, w, h) 33 | ' Gets a file. 34 | f = open_file_dialog("png,bmp,tga,jpg") 35 | if not f then 36 | return 37 | endif 38 | ' Gets the source image. 39 | g = image() 40 | g.load(f) 41 | iw = g.len(0) 42 | ih = g.len(1) 43 | if iw <> w or ih <> h then 44 | g.resize(w, h) 45 | iw = w 46 | ih = h 47 | endif 48 | ' Fills the frame. 49 | begin_operation("Fill tiles") 50 | for j = 0 to h - 1 51 | for i = 0 to w - 1 52 | c = g.get(i, j) 53 | c = get_palette_from_color(c) 54 | set_pixel(i, j, c) 55 | next 56 | next 57 | end_operation() 58 | ' Marks the asset dirty. 59 | set_asset_unsaved() 60 | enddef 61 | 62 | REM Checks the operation. 63 | 64 | ph = get_phase() 65 | if ph = "plug" then 66 | plug() 67 | elseif ph = "run" then 68 | run() 69 | endif 70 | -------------------------------------------------------------------------------- /working area/flip vertically.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/frame" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Flip vertically..."; 13 | register_plugin 14 | ( 15 | "sprite, map, tiles, quantized", ' Target assets. 16 | "Flip vertically", ' Name. 17 | nil, ' Tooltips. 18 | false, ' Selection only? 19 | false, ' Square only? 20 | 20, ' Category. 21 | 40 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | ' Prepares. 29 | print "Running plugin: Flip vertically..."; 30 | x0 = 0 31 | y0 = 0 32 | x1 = 0 33 | y1 = 0 34 | w = 0 35 | h = 0 36 | d = nil 37 | ' Gets the operating area. 38 | if has_selection() then 39 | v = get_selection_range() 40 | unpack(v, x0, y0, x1, y1) 41 | else 42 | v = get_frame_size() 43 | unpack(v, x1, y1) 44 | x1 = x1 - 1 45 | y1 = y1 - 1 46 | endif 47 | w = x1 - x0 + 1 48 | h = y1 - y0 + 1 49 | ' Gets the current pixels. 50 | d = get_pixels_rect(x0, y0, w, h) 51 | ' Sets with the new pixels. 52 | begin_operation("Flip vertically") 53 | for j = 0 to h - 1 54 | y = y0 + j 55 | oy = y0 + (h - 1 - j) 56 | for i = 0 to w - 1 57 | x = x0 + i 58 | k = hash_xy(x, oy, w) 59 | c = get(d, k) 60 | set_pixel(x, y, c) 61 | next 62 | next 63 | end_operation() 64 | ' Marks the asset dirty. 65 | set_asset_unsaved() 66 | enddef 67 | 68 | REM Checks the operation. 69 | 70 | ph = get_phase() 71 | if ph = "plug" then 72 | plug() 73 | elseif ph = "run" then 74 | run() 75 | endif 76 | -------------------------------------------------------------------------------- /working area/flip horizontally.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/frame" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Flip horizontally..."; 13 | register_plugin 14 | ( 15 | "sprite, map, tiles, quantized", ' Target assets. 16 | "Flip horizontally", ' Name. 17 | nil, ' Tooltips. 18 | false, ' Selection only? 19 | false, ' Square only? 20 | 20, ' Category. 21 | 30 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | ' Prepares. 29 | print "Running plugin: Flip horizontally..."; 30 | x0 = 0 31 | y0 = 0 32 | x1 = 0 33 | y1 = 0 34 | w = 0 35 | h = 0 36 | d = nil 37 | ' Gets the operating area. 38 | if has_selection() then 39 | v = get_selection_range() 40 | unpack(v, x0, y0, x1, y1) 41 | else 42 | v = get_frame_size() 43 | unpack(v, x1, y1) 44 | x1 = x1 - 1 45 | y1 = y1 - 1 46 | endif 47 | w = x1 - x0 + 1 48 | h = y1 - y0 + 1 49 | ' Gets the current pixels. 50 | d = get_pixels_rect(x0, y0, w, h) 51 | ' Sets with the new pixels. 52 | begin_operation("Flip horizontally") 53 | for j = 0 to h - 1 54 | y = y0 + j 55 | for i = 0 to w - 1 56 | x = x0 + i 57 | ox = x0 + (w - 1 - i) 58 | k = hash_xy(ox, y, w) 59 | c = get(d, k) 60 | set_pixel(x, y, c) 61 | next 62 | next 63 | end_operation() 64 | ' Marks the asset dirty. 65 | set_asset_unsaved() 66 | enddef 67 | 68 | REM Checks the operation. 69 | 70 | ph = get_phase() 71 | if ph = "plug" then 72 | plug() 73 | elseif ph = "run" then 74 | run() 75 | endif 76 | -------------------------------------------------------------------------------- /working area/shift up.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/frame" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Shift up..."; 13 | register_plugin 14 | ( 15 | "sprite, map, tiles, quantized", ' Target assets. 16 | "Shift up", ' Name. 17 | nil, ' Tooltips. 18 | false, ' Selection only? 19 | false, ' Square only? 20 | 20, ' Category. 21 | 70 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | ' Prepares. 29 | print "Running plugin: Shift up..."; 30 | x0 = 0 31 | y0 = 0 32 | x1 = 0 33 | y1 = 0 34 | w = 0 35 | h = 0 36 | d = nil 37 | ' Gets the operating area. 38 | if has_selection() then 39 | v = get_selection_range() 40 | unpack(v, x0, y0, x1, y1) 41 | else 42 | v = get_frame_size() 43 | unpack(v, x1, y1) 44 | x1 = x1 - 1 45 | y1 = y1 - 1 46 | endif 47 | w = x1 - x0 + 1 48 | h = y1 - y0 + 1 49 | ' Gets the current pixels. 50 | d = get_pixels_rect(x0, y0, w, h) 51 | ' Sets with the new pixels. 52 | begin_operation("Shift up") 53 | for i = 0 to w - 1 54 | x = x0 + i 55 | for j = 0 to h - 1 56 | y = y0 + j 57 | oy = y0 + j + 1 58 | if oy > y1 then 59 | oy = y0 60 | endif 61 | k = hash_xy(x, oy, w) 62 | c = get(d, k) 63 | set_pixel(x, y, c) 64 | next 65 | next 66 | end_operation() 67 | ' Marks the asset dirty. 68 | set_asset_unsaved() 69 | enddef 70 | 71 | REM Checks the operation. 72 | 73 | ph = get_phase() 74 | if ph = "plug" then 75 | plug() 76 | elseif ph = "run" then 77 | run() 78 | endif 79 | -------------------------------------------------------------------------------- /working area/shift down.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/frame" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Shift down..."; 13 | register_plugin 14 | ( 15 | "sprite, map, tiles, quantized", ' Target assets. 16 | "Shift down", ' Name. 17 | nil, ' Tooltips. 18 | false, ' Selection only? 19 | false, ' Square only? 20 | 20, ' Category. 21 | 80 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | ' Prepares. 29 | print "Running plugin: Shift down..."; 30 | x0 = 0 31 | y0 = 0 32 | x1 = 0 33 | y1 = 0 34 | w = 0 35 | h = 0 36 | d = nil 37 | ' Gets the operating area. 38 | if has_selection() then 39 | v = get_selection_range() 40 | unpack(v, x0, y0, x1, y1) 41 | else 42 | v = get_frame_size() 43 | unpack(v, x1, y1) 44 | x1 = x1 - 1 45 | y1 = y1 - 1 46 | endif 47 | w = x1 - x0 + 1 48 | h = y1 - y0 + 1 49 | ' Gets the current pixels. 50 | d = get_pixels_rect(x0, y0, w, h) 51 | ' Sets with the new pixels. 52 | begin_operation("Shift down") 53 | for i = 0 to w - 1 54 | x = x0 + i 55 | for j = 0 to h - 1 56 | y = y0 + j 57 | oy = y0 + j - 1 58 | if oy < y0 then 59 | oy = y1 60 | endif 61 | k = hash_xy(x, oy, w) 62 | c = get(d, k) 63 | set_pixel(x, y, c) 64 | next 65 | next 66 | end_operation() 67 | ' Marks the asset dirty. 68 | set_asset_unsaved() 69 | enddef 70 | 71 | REM Checks the operation. 72 | 73 | ph = get_phase() 74 | if ph = "plug" then 75 | plug() 76 | elseif ph = "run" then 77 | run() 78 | endif 79 | -------------------------------------------------------------------------------- /working area/shift left.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/frame" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Shift left..."; 13 | register_plugin 14 | ( 15 | "sprite, map, tiles, quantized", ' Target assets. 16 | "Shift left", ' Name. 17 | nil, ' Tooltips. 18 | false, ' Selection only? 19 | false, ' Square only? 20 | 20, ' Category. 21 | 50 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | ' Prepares. 29 | print "Running plugin: Shift left..."; 30 | x0 = 0 31 | y0 = 0 32 | x1 = 0 33 | y1 = 0 34 | w = 0 35 | h = 0 36 | d = nil 37 | ' Gets the operating area. 38 | if has_selection() then 39 | v = get_selection_range() 40 | unpack(v, x0, y0, x1, y1) 41 | else 42 | v = get_frame_size() 43 | unpack(v, x1, y1) 44 | x1 = x1 - 1 45 | y1 = y1 - 1 46 | endif 47 | w = x1 - x0 + 1 48 | h = y1 - y0 + 1 49 | ' Gets the current pixels. 50 | d = get_pixels_rect(x0, y0, w, h) 51 | ' Sets with the new pixels. 52 | begin_operation("Shift left") 53 | for j = 0 to h - 1 54 | y = y0 + j 55 | for i = 0 to w - 1 56 | x = x0 + i 57 | ox = x0 + i + 1 58 | if ox > x1 then 59 | ox = x0 60 | endif 61 | k = hash_xy(ox, y, w) 62 | c = get(d, k) 63 | set_pixel(x, y, c) 64 | next 65 | next 66 | end_operation() 67 | ' Marks the asset dirty. 68 | set_asset_unsaved() 69 | enddef 70 | 71 | REM Checks the operation. 72 | 73 | ph = get_phase() 74 | if ph = "plug" then 75 | plug() 76 | elseif ph = "run" then 77 | run() 78 | endif 79 | -------------------------------------------------------------------------------- /working area/shift right.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/frame" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Shift right..."; 13 | register_plugin 14 | ( 15 | "sprite, map, tiles, quantized", ' Target assets. 16 | "Shift right", ' Name. 17 | nil, ' Tooltips. 18 | false, ' Selection only? 19 | false, ' Square only? 20 | 20, ' Category. 21 | 60 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | ' Prepares. 29 | print "Running plugin: Shift right..."; 30 | x0 = 0 31 | y0 = 0 32 | x1 = 0 33 | y1 = 0 34 | w = 0 35 | h = 0 36 | d = nil 37 | ' Gets the operating area. 38 | if has_selection() then 39 | v = get_selection_range() 40 | unpack(v, x0, y0, x1, y1) 41 | else 42 | v = get_frame_size() 43 | unpack(v, x1, y1) 44 | x1 = x1 - 1 45 | y1 = y1 - 1 46 | endif 47 | w = x1 - x0 + 1 48 | h = y1 - y0 + 1 49 | ' Gets the current pixels. 50 | d = get_pixels_rect(x0, y0, w, h) 51 | ' Sets with the new pixels. 52 | begin_operation("Shift right") 53 | for j = 0 to h - 1 54 | y = y0 + j 55 | for i = 0 to w - 1 56 | x = x0 + i 57 | ox = x0 + i - 1 58 | if ox < x0 then 59 | ox = x1 60 | endif 61 | k = hash_xy(ox, y, w) 62 | c = get(d, k) 63 | set_pixel(x, y, c) 64 | next 65 | next 66 | end_operation() 67 | ' Marks the asset dirty. 68 | set_asset_unsaved() 69 | enddef 70 | 71 | REM Checks the operation. 72 | 73 | ph = get_phase() 74 | if ph = "plug" then 75 | plug() 76 | elseif ph = "run" then 77 | run() 78 | endif 79 | -------------------------------------------------------------------------------- /external formats/export frame.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/utils" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Export frame..."; 13 | register_plugin 14 | ( 15 | "sprite, quantized", ' Target assets. 16 | "Export frame", ' Name. 17 | "Export the selection or current frame to image", ' Tooltips. 18 | false, ' Selection only? 19 | false, ' Square only? 20 | 40, ' Category. 21 | 60 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | ' Prepares. 29 | print "Running plugin: Export frame..."; 30 | x0 = 0 31 | y0 = 0 32 | x1 = 0 33 | y1 = 0 34 | w = 0 35 | h = 0 36 | ' Gets the operating area. 37 | if has_selection() then 38 | v = get_selection_range() 39 | unpack(v, x0, y0, x1, y1) 40 | else 41 | v = get_frame_size() 42 | unpack(v, x1, y1) 43 | x1 = x1 - 1 44 | y1 = y1 - 1 45 | endif 46 | w = x1 - x0 + 1 47 | h = y1 - y0 + 1 48 | ' Fills in the pixels at target image with the working area. 49 | g = image() 50 | g.resize(w, h) 51 | for j = 0 to h - 1 52 | y = y0 + j 53 | for i = 0 to w - 1 54 | x = x0 + i 55 | p = get_pixel(x, y) 56 | c = get_color_from_palette(p) 57 | g.set(i, j, c) 58 | next 59 | next 60 | ' Saves to file. 61 | f = save_file_dialog("png,bmp,tga,jpg") 62 | if not f then 63 | return 64 | endif 65 | f = validate_image_file(f) 66 | g.save(f, right(f, 3)) 67 | enddef 68 | 69 | REM Checks the operation. 70 | 71 | ph = get_phase() 72 | if ph = "plug" then 73 | plug() 74 | elseif ph = "run" then 75 | run() 76 | endif 77 | -------------------------------------------------------------------------------- /working area/rotate clockwise.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/frame" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Rotate clockwise..."; 13 | register_plugin 14 | ( 15 | "sprite, map, tiles, quantized", ' Target assets. 16 | "Rotate clockwise", ' Name. 17 | nil, ' Tooltips. 18 | false, ' Selection only? 19 | true, ' Square only? 20 | 20, ' Category. 21 | 10 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | ' Prepares. 29 | print "Running plugin: Rotate clockwise..."; 30 | x0 = 0 31 | y0 = 0 32 | x1 = 0 33 | y1 = 0 34 | w = 0 35 | h = 0 36 | d = nil 37 | ' Gets the operating area. 38 | if has_selection() then 39 | v = get_selection_range() 40 | unpack(v, x0, y0, x1, y1) 41 | else 42 | v = get_frame_size() 43 | unpack(v, x1, y1) 44 | x1 = x1 - 1 45 | y1 = y1 - 1 46 | endif 47 | w = x1 - x0 + 1 48 | h = y1 - y0 + 1 49 | assert(w = h) 50 | ' Gets the current pixels. 51 | d = get_pixels_rect(x0, y0, w, h) 52 | ' Sets with the new pixels. 53 | begin_operation("Rotate clockwise") 54 | for j = w - 1 to 0 step -1 55 | x = x0 + j 56 | oy = y0 + (w - 1 - j) 57 | for i = 0 to h - 1 58 | y = y0 + i 59 | ox = x0 + i 60 | k = hash_xy(ox, oy, w) 61 | c = get(d, k) 62 | set_pixel(x, y, c) 63 | next 64 | next 65 | end_operation() 66 | ' Marks the asset dirty. 67 | set_asset_unsaved() 68 | enddef 69 | 70 | REM Checks the operation. 71 | 72 | ph = get_phase() 73 | if ph = "plug" then 74 | plug() 75 | elseif ph = "run" then 76 | run() 77 | endif 78 | -------------------------------------------------------------------------------- /working area/rotate anticlockwise.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/frame" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Rotate anticlockwise..."; 13 | register_plugin 14 | ( 15 | "sprite, map, tiles, quantized", ' Target assets. 16 | "Rotate anticlockwise", ' Name. 17 | nil, ' Tooltips. 18 | false, ' Selection only? 19 | true, ' Square only? 20 | 20, ' Category. 21 | 20 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | ' Prepares. 29 | print "Running plugin: Rotate anticlockwise..."; 30 | x0 = 0 31 | y0 = 0 32 | x1 = 0 33 | y1 = 0 34 | w = 0 35 | h = 0 36 | d = nil 37 | ' Gets the operating area. 38 | if has_selection() then 39 | v = get_selection_range() 40 | unpack(v, x0, y0, x1, y1) 41 | else 42 | v = get_frame_size() 43 | unpack(v, x1, y1) 44 | x1 = x1 - 1 45 | y1 = y1 - 1 46 | endif 47 | w = x1 - x0 + 1 48 | h = y1 - y0 + 1 49 | assert(w = h) 50 | ' Gets the current pixels. 51 | d = get_pixels_rect(x0, y0, w, h) 52 | ' Sets with the new pixels. 53 | begin_operation("Rotate anticlockwise") 54 | for j = 0 to w - 1 55 | x = x0 + j 56 | oy = y0 + j 57 | for i = h - 1 to 0 step -1 58 | y = y0 + i 59 | ox = x0 + (h - 1 - i) 60 | k = hash_xy(ox, oy, w) 61 | c = get(d, k) 62 | set_pixel(x, y, c) 63 | next 64 | next 65 | end_operation() 66 | ' Marks the asset dirty. 67 | set_asset_unsaved() 68 | enddef 69 | 70 | REM Checks the operation. 71 | 72 | ph = get_phase() 73 | if ph = "plug" then 74 | plug() 75 | elseif ph = "run" then 76 | run() 77 | endif 78 | -------------------------------------------------------------------------------- /frames/copy to all.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/frame" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Copy to all..."; 13 | register_plugin 14 | ( 15 | "sprite", ' Target assets. 16 | "Copy to all", ' Name. 17 | "Copy the current frame to all other ones in this asset", ' Tooltips. 18 | false, ' Selection only? 19 | false, ' Square only? 20 | 30, ' Category. 21 | 10 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | ' Prepares. 29 | print "Running plugin: Copy to all..."; 30 | x0 = 0 31 | y0 = 0 32 | x1 = 0 33 | y1 = 0 34 | w = 0 35 | h = 0 36 | d = nil 37 | ' Gets the operating area. 38 | if has_selection() then 39 | v = get_selection_range() 40 | unpack(v, x0, y0, x1, y1) 41 | else 42 | v = get_frame_size() 43 | unpack(v, x1, y1) 44 | x1 = x1 - 1 45 | y1 = y1 - 1 46 | endif 47 | w = x1 - x0 + 1 48 | h = y1 - y0 + 1 49 | ' Gets the current pixels. 50 | d = get_pixels_rect(x0, y0, w, h) 51 | ' Sets with the new pixels. 52 | g = get_frame_index() 53 | n = get_frame_count() 54 | for f = 1 to n 55 | if f = g then next 56 | set_frame_index(f) 57 | begin_operation("Copy frame") 58 | for j = 0 to h - 1 59 | y = y0 + j 60 | for i = 0 to w - 1 61 | x = x0 + i 62 | k = hash_xy(x, y, w) 63 | c = get(d, k) 64 | set_pixel(x, y, c) 65 | next 66 | next 67 | end_operation() 68 | next 69 | set_frame_index(g) 70 | ' Marks the asset dirty. 71 | set_asset_unsaved() 72 | enddef 73 | 74 | REM Checks the operation. 75 | 76 | ph = get_phase() 77 | if ph = "plug" then 78 | plug() 79 | elseif ph = "run" then 80 | run() 81 | endif 82 | -------------------------------------------------------------------------------- /common/sheet.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | import "common/frame" 6 | 7 | ' Imports an image sheet to sprite asset. 8 | ' @param f - File to be imported. 9 | ' @param xc - Slice count at x-axis. 10 | ' @param yc - Slice count at y-axis. 11 | ' @parap st - Whether stretches the image, when the size doesn't match. 12 | def import_sheet_file(f, xc, yc, st) 13 | ' Gets the asset information. 14 | w = 0 15 | h = 0 16 | v = get_frame_size() 17 | unpack(v, w, h) 18 | tw = w * xc 19 | th = h * yc 20 | ' Gets the source image. 21 | g = image() 22 | g.load(f) 23 | iw = g.len(0) 24 | ih = g.len(1) 25 | if (iw <> tw or ih <> th) and st then 26 | g.resize(tw, th) 27 | iw = tw 28 | ih = th 29 | endif 30 | sw = iw / xc 31 | sh = ih / yc 32 | ' Checks whether the last frame is blank. 33 | set_frame_index(0) 34 | b = is_frame_blank(0) 35 | ' Loads the slices. 36 | for j = 0 to yc - 1 37 | offy = j * sh + (sh - h) / 2 38 | for i = 0 to xc - 1 39 | offx = i * sw + (sw - w) / 2 40 | ' Adds a frame. 41 | if i = 0 and j = 0 then 42 | if not b then 43 | push_operation("add") 44 | endif 45 | else 46 | push_operation("add") 47 | endif 48 | n = get_frame_count() 49 | set_frame_index(n) 50 | ' Fills the frame. 51 | begin_operation("Fill frame") 52 | x0 = i * sw 53 | x1 = x0 + sw - 1 54 | y0 = j * sh 55 | y1 = y0 + sh - 1 56 | for y = 0 to w - 1 57 | for x = 0 to h - 1 58 | p = x + offx 59 | q = y + offy 60 | if p >= x0 and p <= x1 and q >= y0 and q <= y1 then 61 | c = g.get(p, q) 62 | c = get_palette_from_color(c) 63 | else 64 | c = 0 65 | endif 66 | set_pixel(x, y, c) 67 | next 68 | next 69 | end_operation() 70 | next 71 | next 72 | ' Marks the asset dirty. 73 | set_asset_unsaved() 74 | enddef 75 | -------------------------------------------------------------------------------- /common/utils.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | ' Gets the hash value of a given point. 6 | ' @param x - X position. 7 | ' @param y - Y position. 8 | ' @param w - Width of an area. 9 | ' @return - Integer denotes for a unique value within a given area. 10 | def hash_xy(x, y, w) 11 | return x + y * w 12 | enddef 13 | 14 | ' Validates an image file path, appends extension name if necessary. 15 | ' @param f - File path to be validated. 16 | ' @return - Valid image path. 17 | def validate_image_file(f) 18 | e = right(f, 4) 19 | if e = ".png" then return f 20 | if e = ".bmp" then return f 21 | if e = ".tga" then return f 22 | if e = ".jpg" then return f 23 | return f + ".png" ' Defaults to PNG. 24 | enddef 25 | 26 | ' Splits a file path into parts. 27 | ' @param f - File path to be splitted. 28 | ' @return - Splitted directory path, file name, number postfix, and extension name. 29 | def split_file_with_number_postfix(f) 30 | ' Separates the extension name. 31 | l = len(f) 32 | for i = l - 1 to 0 step -1 33 | if mid(f, i, 1) = "." then 34 | exit 35 | endif 36 | next 37 | e = "" 38 | if i > 0 then 39 | e = right(f, l - i) 40 | f = left(f, i) 41 | endif 42 | ' Separates the number postfix. 43 | l = len(f) 44 | for i = l - 1 to 0 step -1 45 | c = mid(f, i, 1) 46 | c = asc(c) 47 | if c < asc("0") or c > asc("9") then 48 | exit 49 | endif 50 | next 51 | n = "" 52 | if i < l - 1 then 53 | n = right(f, l - i - 1) 54 | f = left(f, i + 1) 55 | endif 56 | ' Separates the file name. 57 | l = len(f) 58 | for i = l - 1 to 0 step -1 59 | c = mid(f, i, 1) 60 | if c = "/" or c = "\" then 61 | exit 62 | endif 63 | next 64 | if i = l - 1 then 65 | d = left(f, i + 1) 66 | f = "" 67 | else 68 | d = left(f, i + 1) 69 | f = right(f, l - i - 1) 70 | endif 71 | ' Returns the result. 72 | return list(d, f, n, e) 73 | enddef 74 | -------------------------------------------------------------------------------- /external formats/export frames.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/utils" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Export frames..."; 13 | register_plugin 14 | ( 15 | "sprite", ' Target assets. 16 | "Export frames", ' Name. 17 | "Export the selected range to images", ' Tooltips. 18 | false, ' Selection only? 19 | false, ' Square only? 20 | 40, ' Category. 21 | 70 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | ' Prepares. 29 | print "Running plugin: Export frames..."; 30 | b = 0 31 | e = 0 32 | w = 0 33 | h = 0 34 | ' Saves the selected frame index. 35 | s = get_frame_index() 36 | ' Gets the frame size. 37 | v = get_frame_size() 38 | unpack(v, w, h) 39 | ' Gets the selected range. 40 | v = get_frame_range() 41 | unpack(v, b, e) 42 | if b < 1 then b = 1 43 | if b > get_frame_count() then b = get_frame_count() 44 | if e < 1 then e = 1 45 | if e > get_frame_count() then e = get_frame_count() 46 | ' Fills in the pixels at target image with the working area. 47 | l = list() 48 | for i = b to e 49 | set_frame_index(i) 50 | g = image() 51 | g.resize(w, h) 52 | for y = 0 to h - 1 53 | for x = 0 to w - 1 54 | p = get_pixel(x, y) 55 | c = get_color_from_palette(p) 56 | g.set(x, y, c) 57 | next 58 | next 59 | push(l, g) 60 | next 61 | ' Saves to file. 62 | f = save_file_dialog("png,bmp,tga,jpg") 63 | if not f then 64 | return 65 | endif 66 | f = validate_image_file(f) 67 | for i = 0 to len(l) - 1 68 | g = get(l, i) 69 | n = left(f, len(f) - 4) + str(i) + right(f, 4) 70 | g.save(n, right(f, 3)) 71 | next 72 | ' Restores the selected frame index. 73 | set_frame_index(s) 74 | enddef 75 | 76 | REM Checks the operation. 77 | 78 | ph = get_phase() 79 | if ph = "plug" then 80 | plug() 81 | elseif ph = "run" then 82 | run() 83 | endif 84 | -------------------------------------------------------------------------------- /external formats/import sequence.bas: -------------------------------------------------------------------------------- 1 | REM BASIC8 2 | REM Copyright (C) 2018 - 2019 Tony Wang 3 | REM Plugin program of BASIC8. 4 | 5 | REM Imports common modules. 6 | 7 | import "common/frame" 8 | 9 | REM Loads the plugin. 10 | 11 | def plug() 12 | print "Loading plugin: Import sequence..."; 13 | register_plugin 14 | ( 15 | "sprite", ' Target assets. 16 | "Import sequence", ' Name. 17 | "Import a sequence of images to the current sprite asset", ' Tooltips. 18 | false, ' Selection only? 19 | false, ' Square only? 20 | 40, ' Category. 21 | 10 ' Priority. 22 | ) 23 | enddef 24 | 25 | REM Runs the plugin. 26 | 27 | def run() 28 | ' Prepares. 29 | print "Running plugin: Import sequence..."; 30 | d = nil 31 | f = nil 32 | n = nil 33 | e = nil 34 | w = 0 35 | h = 0 36 | ' Gets the asset information. 37 | v = get_frame_size() 38 | unpack(v, w, h) 39 | ' Gets a file seed. 40 | f = open_file_dialog("png,bmp,tga,jpg") 41 | if not f then 42 | return 43 | endif 44 | t = split_file_with_number_postfix(f) 45 | d = get(t, 0) 46 | f = get(t, 1) 47 | n = get(t, 2) 48 | e = get(t, 3) 49 | ' Sorts the file list to be imported. 50 | di = directory_info(d) 51 | p = f + "*" + e 52 | fis = di.get_files(p) 53 | l = list() 54 | for fi in fis 55 | p = get_full_path(fi) 56 | t = split_file_with_number_postfix(p) 57 | n = get(t, 2) 58 | if n = "" then 59 | n = nil 60 | else 61 | n = val(n) 62 | endif 63 | push(l, n) 64 | next 65 | sort(l) 66 | ' Checks whether the last frame is blank. 67 | set_frame_index(0) 68 | b = is_frame_blank(0) 69 | ' Imports the files. 70 | j = 0 71 | for i in l 72 | ' Reads the image. 73 | if i = nil then 74 | p = d + f + e 75 | else 76 | p = d + f + str(i) + e 77 | endif 78 | g = image() 79 | g.load(p) 80 | if g.len(0) <> w or g.len(1) <> h then 81 | g.resize(w, h) 82 | endif 83 | ' Adds a frame. 84 | if j = 0 then 85 | if not b then 86 | push_operation("add") 87 | endif 88 | else 89 | push_operation("add") 90 | endif 91 | n = get_frame_count() 92 | set_frame_index(n) 93 | j = j + 1 94 | ' Fills the frame. 95 | begin_operation("Fill frame") 96 | for y = 0 to w - 1 97 | for x = 0 to h - 1 98 | c = g.get(x, y) 99 | c = get_palette_from_color(c) 100 | set_pixel(x, y, c) 101 | next 102 | next 103 | end_operation() 104 | next 105 | ' Marks the asset dirty. 106 | set_asset_unsaved() 107 | enddef 108 | 109 | REM Checks the operation. 110 | 111 | ph = get_phase() 112 | if ph = "plug" then 113 | plug() 114 | elseif ph = "run" then 115 | run() 116 | endif 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Plugins for [BASIC8](https://paladin-t.github.io/b8/) 2 | 3 | It's designed to contain the most commonly used features as built-in editors in BASIC8. I've also been getting questions like, "hi, can you make an importer/exporter for this or that format?" One of the reasons I created this repository is implementing some mechanism to simply and handily make operations BASIC8 doesn't offer. Obviously the title tells that "plugin" is that mechanism. 4 | 5 | This repository contains plugins for paintable assets that I consider being between "generic" and "specific". It covers frequently asked requirements; and describes necessary information for developing your own plugins. 6 | 7 | ## Contents 8 | 9 | You can pick anything you need here, and ignore others. But some of the plugin scripts require modules in `common` as dependency, so it's necessary to keep that directory, it's also recommended to put your own reusable modules inside it. 10 | 11 | * [`common`](common): common modules, imported by code in other directories 12 | * [`utils.bas`](common/utils.bas) 13 | * [`frame.bas`](common/frame.bas) 14 | * [`sheet.bas`](common/sheet.bas) 15 | * [`utilities`](utilities): just as its name implies 16 | * [`pick color.bas`](utilities/pick%20color.bas) 17 | * [`replace color.bas`](utilities/replace%20color.bas) 18 | * [`working area`](working%20area): manipulates pixels for sprite, map, tiles, quantized, etc. 19 | * [`rotate clockwise.bas`](working%20area/rotate%20clockwise.bas) 20 | * [`rotate anticlockwise.bas`](working%20area/rotate%20anticlockwise.bas) 21 | * [`flip horizontally.bas`](working%20area/flip%20horizontally.bas) 22 | * [`flip vertically.bas`](working%20area/flip%20vertically.bas) 23 | * [`shift left.bas`](working%20area/shift%20left.bas) 24 | * [`shift right.bas`](working%20area/shift%20right.bas) 25 | * [`shift up.bas`](working%20area/shift%20up.bas) 26 | * [`shift down.bas`](working%20area/shift%20down.bas) 27 | * [`frames`](frames): operations across frames 28 | * [`copy to all.bas`](frames/copy%20to%20all.bas) 29 | * [`external formats`](external%20formats): importers and exporters 30 | * [`import sequence.bas`](external%20formats/import%20sequence.bas) 31 | * [`import 4x4 sheet.bas`](external%20formats/import%204x4%20sheet.bas) 32 | * [`import 3x4 sheet.bas`](external%20formats/import%203x4%20sheet.bas) 33 | * [`import tilesheet.bas`](external%20formats/import%20tilesheet.bas) 34 | * [`import frame.bas`](external%20formats/import%20frame.bas) 35 | * [`export frame.bas`](external%20formats/export%20frame.bas) 36 | * [`export frames.bas`](external%20formats/export%20frames.bas) 37 | 38 | ## How to use 39 | 40 | ### Installing 41 | 42 | It's not recommended to manipulate regular disks under the library directory manually, but don't be afraid of screwing off to plug these expansions: 43 | 44 | 1. Clone or [download](https://github.com/paladin-t/b8.plugins/archive/master.zip) and extract the latest content somewhere on your computer 45 | 2. Create a `plugins` directory under the root directory of your disk library, if it's not yet been there, BASIC8 looks for plugins under that directory strictly 46 | 3. Put plugin scripts you just extracted under the `plugins` directory, do not include the `.git` stuff if you were cloning from here 47 | 4. You need to reopen BASIC8 to use any new plugged expansions 48 | 49 | Some plugins require dependency modules by the `IMPORT` statement, plugin uses the following directory, for example, as lookup root to import another source file: 50 | 51 | * "C:/Users/YourName/Documents/BASIC8/plugins/" on Windows 52 | * "/Users/YourName/Documents/BASIC8/plugins/" on MacOS 53 | * "/home/YourName/Documents/BASIC8/plugins/" on Linux 54 | 55 | It's important to have the directory structure remain unchanged according to this repository under your local `plugins` directory. Files should be located at, for example: 56 | 57 | * "C:/Users/YourName/Documents/BASIC8/plugins/[`common`](common)/[`frame.bas`](common/frame.bas)" 58 | * "C:/Users/YourName/Documents/BASIC8/plugins/[`working area`](working%20area)/[`rotate clockwise.bas`](working%20area/rotate%20clockwise.bas)" 59 | * Etc. 60 | 61 | ### Running 62 | 63 | ![](imgs/items.png) 64 | 65 | Right click on the editing area of any paintable asset to show all plugged expansions which are usable for current context. Left click on a menu item to run it. 66 | 67 | ### Interrupting 68 | 69 | Unlike regular disks, plugins run in the same thread with the graphics part. So as a plugin developer, you were in charge of guaranteeing a plugin runs and terminates normally. Press the Pause/Break key to interrupt the execution whenever you consider something is going wrong in plugin; click the close button on the BASIC8 window or Alt+F4 for the same affect, it breaks out even from unexpected infinite loop. 70 | 71 | ## Development 72 | 73 | Only a subset of the BASIC8 programming libraries are exposed for plugin, including `Bytes`, `File`, `Image`, `IO`, `JSON`, `Math`, `System`, `Text` and `Utils`; besides, there are also some dedicated plugin only functions. 74 | 75 | BASIC8 scans and plugs all plugins on startup by running it top down, reopen it for any new plugin; it also executes top down when triggering a plugin, but you don't need to reopen BASIC8 for changes in existing plugin, because each triggering is a new read-evaluate-process loop; and the plugin interpreter doesn't reserve values of variables. These two phases are called "plug" and "run" respectively. 76 | 77 | ### Meta 78 | 79 | * `GET_PHASE()`: gets the current execution phase 80 | * returns either "plug" or "run" 81 | * `REGISTER_PLUGIN(target, name, tips = NIL, sel = FALSE, squ = FALSE, cat = 100, pri = 10)`: registers a plugin 82 | * `target`: asset types to operate on, can be one or more in "sprite", "map", "tiles", "quantized", separated by comma, eg. "sprite, quantized" 83 | * `name`: plugin name 84 | * `tips`: tooltips 85 | * `sel`: whether plugin is available for selection only 86 | * `squ`: whether plugin is available for square area only 87 | * `cat`: category to group plugins in the context menu 88 | * `pri`: priority to sort plugins in the same category 89 | 90 | A plugin script should contains zero (for common module) or one (for functional plugin) entry function called `REGISTER_PLUGIN`, generally you only need to call it during the "plug" phase. The `sel`, `squ` parameters are orthogonal in the `REGISTER_PLUGIN` function. 91 | 92 | ### Disk and assets 93 | 94 | * `GET_DISK_CONTAINER_DIRECTORY()`: gets the path of container directory of the current disk 95 | * returns directory path 96 | * `GET_DISK_CONTENT_DIRECTORY()`: gets the path of content directory of the current disk 97 | * returns directory path 98 | 99 | * `GET_ASSET_FILE()`: gets the file path of the current asset 100 | * returns file path 101 | * `SET_ASSET_UNSAVED()`: sets the current asset as unsaved 102 | 103 | * `GET_ASSETS()`: get the file paths of all the assets 104 | * returns list of paths 105 | * `OPEN_ASSETS([pattern])`: opens the assets which match the specific pattern 106 | * `patterh`: the specific pattern, eg. "*.sprite", "img_??.quantized" 107 | 108 | ### Working area 109 | 110 | Sprite frame, map layer and quantized image are all conceptualized as "frame" for plugins. The index of sprite starts from 1. 111 | 112 | * `GET_FRAME_COUNT()`: gets the count of frames in the current asset 113 | * returns integer 114 | * `GET_FRAME_RANGE()`: gets the appointed range in the current asset, only works with sprite by the indicator below 115 | * returns vec2 of begin, end frame indices 116 | 117 | ![](imgs/range.png) 118 | 119 | * `GET_FRAME_SIZE()`: gets the size in pixels of a pixeled frame, or tile count of a map 120 | * returns vec2 of width, height 121 | * `GET_FRAME_INDEX()`: gets the active frame index of the current asset 122 | * returns integer 123 | * `SET_FRAME_INDEX(i)`: sets the active frame index of the current asset 124 | * `i`: target index 125 | 126 | * `HAS_SELECTION()`: checks whether any area is selected by the lasso tool 127 | * returns true for area selected 128 | * `GET_SELECTION_RANGE()`: gets the selected area 129 | * returns vec4 of left, top, right, bottom, or nil for none selection 130 | 131 | * `GET_CURSOR_POSITION()`: gets the position of the cursor 132 | * returns vec2 of x, y, or nil for not available 133 | 134 | * `GET_PIXEL(x, y)`: gets the data at a specific position 135 | * `x`: x position 136 | * `y`: y position 137 | * returns palette index for pixeled, or tile index for map 138 | * `SET_PIXEL(x, y, p)`: sets the data at a specific position with given data 139 | * `x`: x position 140 | * `y`: y position 141 | * `p`: palette or tile index as integer 142 | 143 | ### Operation 144 | 145 | * `PUSH_OPERATION(op [, i])`: pushes an operation, as if operated manually by a user on editors 146 | * `op`: operation type, only "add" available for now, for adding frame or layer to sprite, map assets 147 | * `i`: destination index to perform adding, or append to tail when not specified 148 | * `BEGIN_OPERATION([msg])`: begins a plugin operation 149 | * `msg`: text message to be displayed on the undo/redo tooltip 150 | * `END_OPERATION()`: ends a plugin operation 151 | 152 | All pixel modifications by the `SET_PIXEL` function are packed between a pair of `BEGIN_OPERATION` and `END_OPERATION`, as a single undo/redo step. 153 | 154 | ### Source 155 | 156 | * `GET_SOURCE_INDEX()`: gets the selected palette color, tile index, etc. 157 | * returns integer 158 | * `SET_SOURCE_INDEX(src)`: sets the selected palette color, tile index, etc. 159 | * `src`: integer 160 | 161 | * `GET_PALETTE_FROM_COLOR(c)`: gets the palette index represents for the nearest color with specific color 162 | * `c`: color to match 163 | * returns palette index 164 | * `GET_COLOR_FROM_PALETTE(p)`: gets the RGBA value from specific palette index 165 | * returns RGBA value 166 | 167 | ### File dialog 168 | 169 | * `OPEN_FILE_DIALOG([y, m = FALSE])`: shows a dialog box to open file 170 | * `y`: file type extensions, separated by comma 171 | * `m`: true for allowing multiple selection 172 | * returns file path for single selection, list of file paths for multiple selection, or nil for canceled 173 | * `SAVE_FILE_DIALOG([y])`: shows a dialog box to save file 174 | * `y`: file type extensions, separated by comma 175 | * returns file path, or nil for canceled 176 | * `PICK_DIRECTORY_DIALOG([d])`: shows a dialog to pick directory 177 | * `d`: default directory 178 | * returns dialog path, or nil for canceled 179 | --------------------------------------------------------------------------------