Compare commits

..

No commits in common. "e924e8eb75da1f7e55c3cb717d8f9ddda6737df5" and "9b7e7becc526c53e7de946e99f0095f1e12bc4e2" have entirely different histories.

89 changed files with 289 additions and 329 deletions

View file

@ -7,7 +7,7 @@ we maintain our own git repo.
running
-------
just run launch the app (uv run python -m playscii)
just run launch the app (uv run python playscii.py)
just check lint gate (ruff check + format)
just lint ruff check --fix + ruff format
just typecheck ty check (601 errors, legacy code, not in check gate)
@ -16,19 +16,14 @@ running
project structure
-----------------
python source organized as playscii/ package (~50 .py files)
data directories (charsets/, palettes/, etc.) live at repo root
formats/ and games/ also at root (loaded dynamically via importlib)
playscii/
__init__.py package marker
__main__.py entry point for python -m playscii
app.py main application, SDL2/OpenGL setup (was playscii.py)
art.py art/canvas management, the core data model
ui*.py ~20 UI modules (dialogs, panels, menus, toolbar, etc.)
game_*.py game engine (objects, rooms, world, HUD)
renderable*.py rendering pipeline (sprites, lines, framebuffer)
all python source lives at the repo root (no src/ layout)
~50 .py files, flat structure, no packages
playscii.py main entry point, app init, SDL2/OpenGL setup
art.py art/canvas management, the core data model
ui*.py ~20 UI modules (dialogs, panels, menus, toolbar, etc.)
game_*.py game engine (objects, rooms, world, HUD)
renderable*.py rendering pipeline (sprites, lines, framebuffer)
formats/ import/export handlers (ANS, ATA, BMP, EDSCII, PNG, GIF, TXT)
games/ bundled example games (crawler, fireplace, flood, maze, shmup, etc.)
charsets/ 30+ classic computer character sets (.char + .png)
@ -49,12 +44,6 @@ dependencies
gotchas
-------
package structure:
all python source now lives in playscii/ package. files use relative
imports (from .art import ...). formats/ and games/ use absolute imports
(from playscii.art import ...). data directories stay at root since the
app uses CWD-relative paths to load charsets, shaders, etc.
art scripts and exec():
art.py imports random at module level with a noqa comment. art scripts
(.arsc files) are loaded via exec() in art.py's scope. they use random
@ -63,14 +52,14 @@ gotchas
in art.py — check before removing anything.
pdoc detection:
app.py has a contextlib.suppress block that tries to import pdoc.
playscii.py has a contextlib.suppress block that tries to import pdoc.
the import MUST stay inside the suppress block. if it gets moved or
removed, pdoc_available becomes unconditionally True and the help menu
breaks when pdoc isn't installed.
SDL2 init ordering:
many modules import from app.py's namespace or expect SDL2 to be
initialized before they run. import order in app.py matters — E402
many modules import from playscii.py's namespace or expect SDL2 to be
initialized before they run. import order in playscii.py matters — E402
is disabled for this reason.
mutable default arguments:

1
.gitignore vendored
View file

@ -1,7 +1,6 @@
venv/*.*
dist/*.*
build/*.*
playscii/__pycache__/*.*
__pycache__/*.*
.idea/*.*
playscii.profile

View file

@ -6,8 +6,8 @@ import traceback
import numpy as np
from .edit_command import CommandStack, EntireArtCommand
from .image_export import write_thumbnail
from edit_command import CommandStack, EntireArtCommand
from image_export import write_thumbnail
# X, Y, Z
VERT_LENGTH = 3
@ -141,7 +141,6 @@ class Art:
self.instances = []
"List of ArtInstances using us as their source"
# init frames and layers - ArtFromDisk has its own logic for this
self._sorted_layers = None
self.init_layers()
self.init_frames()
# support non-square characters:
@ -178,14 +177,6 @@ class Art:
self.layers_visibility = [True]
self.layer_names = ["Layer 1"]
def get_sorted_layers(self):
"Return layer indices sorted by Z depth, cached until invalidated."
if self._sorted_layers is None:
self._sorted_layers = tuple(
sorted(range(self.layers), key=lambda i: self.layers_z[i])
)
return self._sorted_layers
def init_frames(self):
self.frames = 0
# current frame being edited
@ -318,7 +309,6 @@ class Art:
self.layer_names.append(new_name)
# rebuild geo with added verts for new layer
self.geo_changed = True
self._sorted_layers = None
# set new layer as active
if self is self.app.ui.active_art:
self.app.ui.set_active_layer(self.layers - 1)
@ -356,7 +346,6 @@ class Art:
self.layer_names.pop(index)
self.layers -= 1
self.geo_changed = True
self._sorted_layers = None
self.mark_all_frames_changed()
if self.active_layer > self.layers - 1:
self.app.ui.set_active_layer(self.layers - 1)
@ -528,17 +517,14 @@ class Art:
x2, y2 = left_x, bottom_y
x3, y3 = right_x, bottom_y
# Z of all layers is 0, layer Z set in shader
verts = [x0, y0, 0, x1, y1, 0, x2, y2, 0, x3, y3, 0]
verts = [x0, y0, 0]
verts += [x1, y1, 0]
verts += [x2, y2, 0]
verts += [x3, y3, 0]
self.vert_array[layer][tile_y][tile_x] = verts
# vertex elements
elements = [
vert_index,
vert_index + 1,
vert_index + 2,
vert_index + 1,
vert_index + 2,
vert_index + 3,
]
elements = [vert_index, vert_index + 1, vert_index + 2]
elements += [vert_index + 1, vert_index + 2, vert_index + 3]
self.elem_array[elem_index : elem_index + ELEM_STRIDE] = elements
elem_index += ELEM_STRIDE
# 4 verts in a quad
@ -549,7 +535,11 @@ class Art:
shape = (layers, self.height, self.width, UV_STRIDE)
array = np.zeros(shape, dtype=np.float32)
# default new layer of UVs to "normal" transform
array[:] = uv_types[UV_NORMAL]
uvs = uv_types[UV_NORMAL]
for layer in range(layers):
for y in range(self.height):
for x in range(self.width):
array[layer][y][x] = uvs
return array
def is_tile_inside(self, x, y):
@ -1330,7 +1320,6 @@ class ArtInstance(Art):
self.renderables = []
# instances shouldn't have instances; cause user problems if attempted
self.instances = None
self._sorted_layers = None
self.restore_from_source()
self.source.instances.append(self)
@ -1354,7 +1343,6 @@ class ArtInstance(Art):
setattr(self, prop, getattr(self.source, prop))
# copy lists
self.layers_z = self.source.layers_z[:]
self._sorted_layers = None
self.layers_visibility = self.source.layers_visibility[:]
self.layer_names = self.source.layer_names[:]
self.frame_delays = self.source.frame_delays[:]

View file

@ -1,6 +1,6 @@
import traceback
from .art import ART_DIR
from art import ART_DIR
class ArtExporter:

View file

@ -1,8 +1,8 @@
import os
import traceback
from .art import ART_FILE_EXTENSION, DEFAULT_CHARSET, DEFAULT_PALETTE
from .ui_file_chooser_dialog import GenericImportChooserDialog
from art import ART_FILE_EXTENSION, DEFAULT_CHARSET, DEFAULT_PALETTE
from ui_file_chooser_dialog import GenericImportChooserDialog
class ArtImporter:

View file

@ -2,7 +2,7 @@ import math
import numpy as np
from . import vector
import vector
def clamp(val, lowest, highest):

View file

@ -4,7 +4,7 @@ import time
from PIL import Image
from .texture import Texture
from texture import Texture
CHARSET_DIR = "charsets/"
CHARSET_FILE_EXTENSION = "char"

View file

@ -1,7 +1,7 @@
import math
from collections import namedtuple
from .renderable_line import (
from renderable_line import (
BoxCollisionRenderable,
CircleCollisionRenderable,
TileBoxCollisionRenderable,

View file

@ -4,9 +4,9 @@ import math
import numpy as np
from OpenGL import GL
from . import vector
from .edit_command import EditCommand
from .renderable_sprite import UISpriteRenderable
import vector
from edit_command import EditCommand
from renderable_sprite import UISpriteRenderable
"""
reference diagram:

View file

@ -1,4 +1,4 @@
from playscii.art_import import ArtImporter
from art_import import ArtImporter
DEFAULT_FG, DEFAULT_BG = 7, 0
WIDTH = 80

View file

@ -1,4 +1,4 @@
from playscii.art_import import ArtImporter
from art_import import ArtImporter
# import as white on black for ease of edit + export
DEFAULT_FG, DEFAULT_BG = 113, 1

View file

@ -4,13 +4,14 @@
import os
from PIL import Image
from playscii.art import DEFAULT_CHARSET, DEFAULT_HEIGHT, DEFAULT_PALETTE, DEFAULT_WIDTH
from playscii.art_import import ArtImporter
from playscii.image_convert import ImageConverter
from playscii.palette import PaletteFromFile
from playscii.ui_art_dialog import ImportOptionsDialog
from playscii.ui_dialog import Field, UIDialog
from playscii.ui_file_chooser_dialog import ImageFileChooserDialog
from art import DEFAULT_CHARSET, DEFAULT_HEIGHT, DEFAULT_PALETTE, DEFAULT_WIDTH
from art_import import ArtImporter
from image_convert import ImageConverter
from palette import PaletteFromFile
from ui_art_dialog import ImportOptionsDialog
from ui_dialog import Field, UIDialog
from ui_file_chooser_dialog import ImageFileChooserDialog
# custom chooser showing image previews, shares parent w/ "palette from image"

View file

@ -1,11 +1,12 @@
import numpy as np
from formats.in_bitmap import (
BitmapImageImporter,
ConvertImageChooserDialog,
ConvertImageOptionsDialog,
)
from playscii.image_convert import ImageConverter
from playscii.ui_dialog import Field, SkipFieldType, UIDialog
from image_convert import ImageConverter
from ui_dialog import Field, SkipFieldType, UIDialog
class TwoColorConvertImageOptionsDialog(ConvertImageOptionsDialog):

View file

@ -6,7 +6,7 @@ import os
import time
import formats.in_bitmap as bm
from playscii import image_convert
import image_convert
class ImageSequenceConverter:

View file

@ -1,6 +1,6 @@
from playscii.art_import import ArtImporter
from playscii.ui_art_dialog import ImportOptionsDialog
from playscii.ui_dialog import Field, UIDialog
from art_import import ArtImporter
from ui_art_dialog import ImportOptionsDialog
from ui_dialog import Field, UIDialog
class EDSCIIImportOptionsDialog(ImportOptionsDialog):

View file

@ -1,4 +1,4 @@
from playscii.art_import import ArtImporter
from art_import import ArtImporter
class EndDoomImporter(ArtImporter):

View file

@ -1,4 +1,4 @@
from playscii.art_import import ArtImporter
from art_import import ArtImporter
class TextImporter(ArtImporter):

View file

@ -1,4 +1,4 @@
from playscii.art_export import ArtExporter
from art_export import ArtExporter
WIDTH = 80
ENCODING = "cp1252" # old default

View file

@ -1,5 +1,5 @@
from playscii.art import TileIter
from playscii.art_export import ArtExporter
from art import TileIter
from art_export import ArtExporter
class ANSExporter(ArtExporter):

View file

@ -1,4 +1,4 @@
from playscii.art_export import ArtExporter
from art_export import ArtExporter
WIDTH, HEIGHT = 80, 25

View file

@ -1,5 +1,5 @@
from playscii.art_export import ArtExporter
from playscii.image_export import export_animation
from art_export import ArtExporter
from image_export import export_animation
class GIFExporter(ArtExporter):

View file

@ -1,7 +1,7 @@
from playscii.art_export import ArtExporter
from playscii.image_export import export_still_image
from playscii.ui_art_dialog import ExportOptionsDialog
from playscii.ui_dialog import Field, UIDialog
from art_export import ArtExporter
from image_export import export_still_image
from ui_art_dialog import ExportOptionsDialog
from ui_dialog import Field, UIDialog
DEFAULT_SCALE = 4
DEFAULT_CRT = True

View file

@ -1,10 +1,10 @@
import os
from playscii.art_export import ArtExporter
from playscii.image_export import export_still_image
from playscii.renderable import LAYER_VIS_FULL, LAYER_VIS_NONE
from playscii.ui_art_dialog import ExportOptionsDialog
from playscii.ui_dialog import Field, UIDialog
from art_export import ArtExporter
from image_export import export_still_image
from renderable import LAYER_VIS_FULL, LAYER_VIS_NONE
from ui_art_dialog import ExportOptionsDialog
from ui_dialog import Field, UIDialog
FILE_EXTENSION = "png"

View file

@ -1,4 +1,4 @@
from playscii.art_export import ArtExporter
from art_export import ArtExporter
class TextExporter(ArtExporter):

View file

@ -1,5 +1,5 @@
from .art import Art
from .renderable import TileRenderable
from art import Art
from renderable import TileRenderable
class GameHUDArt(Art):

View file

@ -2,9 +2,9 @@ import math
import os
import random
from . import vector
from .art import ArtInstance
from .collision import (
import vector
from art import ArtInstance
from collision import (
CST_AABB,
CST_CIRCLE,
CST_NONE,
@ -15,8 +15,8 @@ from .collision import (
Contact,
point_in_box,
)
from .renderable import GameObjectRenderable
from .renderable_line import BoundsIndicatorRenderable, OriginIndicatorRenderable
from renderable import GameObjectRenderable
from renderable_line import BoundsIndicatorRenderable, OriginIndicatorRenderable
# facings
GOF_LEFT = 0

View file

@ -1,4 +1,4 @@
from .game_object import GameObject
from game_object import GameObject
class GameRoom:

View file

@ -1,7 +1,7 @@
import os.path
import random
from .collision import (
from collision import (
CST_AABB,
CST_CIRCLE,
CST_TILE,
@ -10,7 +10,7 @@ from .collision import (
CT_NONE,
CT_PLAYER,
)
from .game_object import FACING_DIRS, GameObject
from game_object import FACING_DIRS, GameObject
class GameObjectAttachment(GameObject):

View file

@ -9,12 +9,17 @@ from collections import namedtuple
import sdl2
from . import collision, game_hud, game_object, game_room, game_util_objects, vector
from .art import ART_DIR
from .camera import Camera
from .charset import CHARSET_DIR
from .grid import GameGrid
from .palette import PALETTE_DIR
import collision
import game_hud
import game_object
import game_room
import game_util_objects
import vector
from art import ART_DIR
from camera import Camera
from charset import CHARSET_DIR
from grid import GameGrid
from palette import PALETTE_DIR
TOP_GAME_DIR = "games/"
DEFAULT_STATE_FILENAME = "start"
@ -24,8 +29,8 @@ SOUNDS_DIR = "sounds/"
# generic starter script with a GO and Player subclass
STARTER_SCRIPT = """
from playscii.game_object import GameObject
from playscii.game_util_objects import Player
from game_object import GameObject
from game_util_objects import Player
class MyGamePlayer(Player):

View file

@ -1,4 +1,4 @@
from playscii.game_object import GameObject
from game_object import GameObject
# initial work: 2019-02-17 and 18
# FP view research: 2021-11-22

View file

@ -1,6 +1,6 @@
from playscii import vector
from playscii.game_object import GameObject
from playscii.renderable_line import DebugLineRenderable
import vector
from game_object import GameObject
from renderable_line import DebugLineRenderable
# stuff for troubleshooting "get tiles intersecting line" etc

View file

@ -1,3 +1,4 @@
from game_util_objects import Player
from games.crawler.scripts.crawler import (
DIR_EAST,
DIR_NAMES,
@ -8,7 +9,6 @@ from games.crawler.scripts.crawler import (
OPPOSITE_DIRS,
RIGHT_TURN_DIRS,
)
from playscii.game_util_objects import Player
class CrawlPlayer(Player):

View file

@ -1,12 +1,12 @@
from art import TileIter
from game_object import GameObject
from games.crawler.scripts.crawler import (
DIR_EAST,
DIR_NORTH,
DIR_SOUTH,
DIR_WEST,
)
from playscii.art import TileIter
from playscii.game_object import GameObject
from playscii.vector import get_tiles_along_integer_line
from vector import get_tiles_along_integer_line
class CrawlTopDownView(GameObject):

View file

@ -1,9 +1,4 @@
from playscii.game_util_objects import (
DynamicBoxObject,
Pickup,
StaticTileObject,
TopDownPlayer,
)
from game_util_objects import DynamicBoxObject, Pickup, StaticTileObject, TopDownPlayer
class CronoPlayer(TopDownPlayer):

View file

@ -13,8 +13,8 @@ import os
import webbrowser
from random import choice, randint
from playscii.art import TileIter
from playscii.game_object import GameObject
from art import TileIter
from game_object import GameObject
#
# some tuning knobs

View file

@ -1,7 +1,7 @@
from random import choice
from playscii.art import TileIter
from playscii.game_object import GameObject
from art import TileIter
from game_object import GameObject
# TODO:
# solver? https://stackoverflow.com/questions/1430962/how-to-optimally-solve-the-flood-fill-puzzle

View file

@ -1,4 +1,4 @@
from playscii.game_hud import GameHUD, GameHUDRenderable
from game_hud import GameHUD, GameHUDRenderable
class MazeHUD(GameHUD):

View file

@ -1,16 +1,16 @@
import math
import random
from playscii.art import TileIter
from playscii.collision import (
from art import TileIter
from collision import (
CST_CIRCLE,
CST_TILE,
CT_GENERIC_DYNAMIC,
CT_GENERIC_STATIC,
CT_NONE,
)
from playscii.game_object import GameObject
from playscii.game_util_objects import Player, StaticTileBG
from game_object import GameObject
from game_util_objects import Player, StaticTileBG
class MazeBG(StaticTileBG):

View file

@ -1,7 +1,7 @@
import math
from game_util_objects import BlobShadow, Player
from games.maze.scripts.rooms import OutsideRoom
from playscii.game_util_objects import BlobShadow, Player
class PlayerBlobShadow(BlobShadow):

View file

@ -1,4 +1,4 @@
from playscii.game_room import GameRoom
from game_room import GameRoom
class MazeRoom(GameRoom):

View file

@ -1,7 +1,7 @@
import math
import random
from playscii.game_util_objects import Character, Player, StaticTileBG, WarpTrigger
from game_util_objects import Character, Player, StaticTileBG, WarpTrigger
class PlatformWorld(StaticTileBG):

View file

@ -1,14 +1,8 @@
import math
import random
from playscii.game_object import GameObject
from playscii.game_util_objects import (
Character,
ObjectSpawner,
Player,
Projectile,
StaticTileBG,
)
from game_object import GameObject
from game_util_objects import Character, ObjectSpawner, Player, Projectile, StaticTileBG
class ShmupPlayer(Player):

View file

@ -1,12 +1,12 @@
import random
import time
from art import ART_DIR, UV_FLIPX, UV_FLIPY, UV_ROTATE180
from game_object import GameObject
from games.wildflowers.scripts.frond import Frond
from games.wildflowers.scripts.petal import Petal
from games.wildflowers.scripts.ramps import PALETTE_RAMPS
from playscii.art import ART_DIR, UV_FLIPX, UV_FLIPY, UV_ROTATE180
from playscii.game_object import GameObject
from playscii.renderable import TileRenderable
from renderable import TileRenderable
# TODO: random size range?
# (should also change camera zoom, probably frond/petal counts)

View file

@ -1,5 +1,5 @@
from playscii.game_util_objects import GameObject, WorldGlobalsObject
from playscii.image_export import export_still_image
from game_util_objects import GameObject, WorldGlobalsObject
from image_export import export_still_image
"""
overall approach:

View file

@ -1,6 +1,6 @@
import numpy as np
from .renderable_line import LineRenderable
from renderable_line import LineRenderable
# grid that displays as guide for Cursor

View file

@ -5,8 +5,8 @@ import time
import numpy as np
from PIL import Image
from .lab_color import lab_color_diff, rgb_to_lab
from .renderable_sprite import SpriteRenderable
from lab_color import lab_color_diff, rgb_to_lab
from renderable_sprite import SpriteRenderable
"""
notes / future research

View file

@ -1,7 +1,7 @@
from OpenGL import GL
from PIL import GifImagePlugin, Image, ImageChops
from .framebuffer import ExportFramebuffer, ExportFramebufferNoCRT
from framebuffer import ExportFramebuffer, ExportFramebufferNoCRT
def get_frame_image(app, art, frame, allow_crt=True, scale=1, bg_color=(0, 0, 0, 0)):

View file

@ -5,12 +5,12 @@ from sys import exit
import sdl2
from .art import ART_DIR, ART_FILE_EXTENSION
from .collision import CT_NONE
from .key_shifts import NUMLOCK_OFF_MAP, NUMLOCK_ON_MAP
from .renderable import LAYER_VIS_DIM, LAYER_VIS_FULL, LAYER_VIS_NONE
from .ui import OIS_FILL, OIS_HEIGHT, OIS_WIDTH, SCALE_INCREMENT
from .ui_art_dialog import (
from art import ART_DIR, ART_FILE_EXTENSION
from collision import CT_NONE
from key_shifts import NUMLOCK_OFF_MAP, NUMLOCK_ON_MAP
from renderable import LAYER_VIS_DIM, LAYER_VIS_FULL, LAYER_VIS_NONE
from ui import OIS_FILL, OIS_HEIGHT, OIS_WIDTH, SCALE_INCREMENT
from ui_art_dialog import (
AddFrameDialog,
AddLayerDialog,
CloseUnsavedChangesDialog,
@ -32,7 +32,7 @@ from .ui_art_dialog import (
SetLayerNameDialog,
SetLayerZDialog,
)
from .ui_file_chooser_dialog import (
from ui_file_chooser_dialog import (
ArtChooserDialog,
CharSetChooserDialog,
OverlayImageFileChooserDialog,
@ -40,7 +40,7 @@ from .ui_file_chooser_dialog import (
PaletteFromImageChooserDialog,
RunArtScriptDialog,
)
from .ui_game_dialog import (
from ui_game_dialog import (
AddRoomDialog,
NewGameDirDialog,
RenameRoomDialog,
@ -49,7 +49,7 @@ from .ui_game_dialog import (
SetRoomCamDialog,
SetRoomEdgeWarpsDialog,
)
from .ui_list_operations import (
from ui_list_operations import (
LO_LOAD_STATE,
LO_OPEN_GAME_DIR,
LO_SELECT_OBJECTS,

View file

@ -11,4 +11,4 @@ test:
check: lint
run:
uv run python -m playscii
uv run python playscii.py

View file

@ -5,8 +5,8 @@ from random import randint
from PIL import Image
from .lab_color import lab_color_diff, rgb_to_lab
from .texture import Texture
from lab_color import lab_color_diff, rgb_to_lab
from texture import Texture
PALETTE_DIR = "palettes/"
PALETTE_EXTENSIONS = ["png", "gif", "bmp"]
@ -72,7 +72,6 @@ class Palette:
# scan image L->R T->B for unique colors, store em as tuples
# color 0 is always fully transparent
self.colors = [(0, 0, 0, 0)]
seen = {(0, 0, 0, 0)}
# determine lightest and darkest colors in palette for defaults
lightest = 0
darkest = 255 * 3 + 1
@ -83,8 +82,7 @@ class Palette:
if len(self.colors) >= MAX_COLORS:
break
color = src_img.getpixel((x, y))
if color not in seen:
seen.add(color)
if color not in self.colors:
self.colors.append(color)
# is this lightest/darkest unique color so far? save index
luminosity = color[0] * 0.21 + color[1] * 0.72 + color[2] * 0.07

View file

@ -49,7 +49,7 @@ with contextlib.suppress(Exception):
pdoc_available = True
# submodules - set here so cfg file can modify them all easily
from .art import (
from art import (
ART_DIR,
ART_FILE_EXTENSION,
ART_SCRIPT_DIR,
@ -61,25 +61,25 @@ from .art import (
Art,
ArtFromDisk,
)
from .art_export import ArtExporter
from .art_import import ArtImporter
from .audio import AudioLord
from .camera import Camera
from .charset import CHARSET_DIR, CharacterSet, CharacterSetLord
from .cursor import Cursor
from .framebuffer import Framebuffer
from .game_world import TOP_GAME_DIR, GameWorld
from .grid import ArtGrid
from .input_handler import InputLord
from .palette import PALETTE_DIR, Palette, PaletteLord
from .renderable import OnionTileRenderable, TileRenderable
from art_export import ArtExporter
from art_import import ArtImporter
from audio import AudioLord
from camera import Camera
from charset import CHARSET_DIR, CharacterSet, CharacterSetLord
from cursor import Cursor
from framebuffer import Framebuffer
from game_world import TOP_GAME_DIR, GameWorld
from grid import ArtGrid
from input_handler import InputLord
from palette import PALETTE_DIR, Palette, PaletteLord
from renderable import OnionTileRenderable, TileRenderable
# some classes are imported only so the cfg file can modify their defaults
from .renderable_line import DebugLineRenderable
from .renderable_sprite import SpriteRenderable, UIBGTextureRenderable
from .shader import ShaderLord
from .ui import OIS_WIDTH, UI
from .ui_file_chooser_dialog import THUMBNAIL_CACHE_DIR
from renderable_line import DebugLineRenderable
from renderable_sprite import SpriteRenderable, UIBGTextureRenderable
from shader import ShaderLord
from ui import OIS_WIDTH, UI
from ui_file_chooser_dialog import THUMBNAIL_CACHE_DIR
APP_NAME = "Playscii"
VERSION_FILENAME = "version"
@ -409,8 +409,6 @@ class Application:
self.onion_renderables_prev, self.onion_renderables_next = [], []
# lists of currently loaded character sets and palettes
self.charsets, self.palettes = [], []
self._charset_map = {}
self._palette_map = {}
self.csl = CharacterSetLord(self)
self.pl = PaletteLord(self)
# set/create an active art
@ -741,13 +739,12 @@ class Application:
# already loaded?
base_charset_to_load = os.path.basename(charset_to_load)
base_charset_to_load = os.path.splitext(base_charset_to_load)[0]
cached = self._charset_map.get(base_charset_to_load)
if cached is not None:
return cached
for charset in self.charsets:
if charset.base_filename == base_charset_to_load:
return charset
new_charset = CharacterSet(self, charset_to_load, log)
if new_charset.init_success:
self.charsets.append(new_charset)
self._charset_map[base_charset_to_load] = new_charset
return new_charset
elif self.ui and self.ui.active_art:
# if init failed (eg bad filename) return something safe
@ -756,13 +753,12 @@ class Application:
def load_palette(self, palette_to_load, log=False):
base_palette_to_load = os.path.basename(palette_to_load)
base_palette_to_load = os.path.splitext(base_palette_to_load)[0]
cached = self._palette_map.get(base_palette_to_load)
if cached is not None:
return cached
for palette in self.palettes:
if palette.base_filename == base_palette_to_load:
return palette
new_palette = Palette(self, palette_to_load, log)
if new_palette.init_success:
self.palettes.append(new_palette)
self._palette_map[base_palette_to_load] = new_palette
return new_palette
elif self.ui and self.ui.active_art:
# if init failed (eg bad filename) return something safe
@ -1149,8 +1145,7 @@ class Application:
return
for module_name in AUTOGEN_DOC_MODULES:
# pdoc.pdoc takes module name as string, returns HTML doc string
qualified_name = f"playscii.{module_name}"
html = pdoc.pdoc(qualified_name)
html = pdoc.pdoc(module_name)
docfile = open(AUTOGEN_DOCS_PATH + module_name + ".html", "w")
docfile.write(html)
docfile.close()
@ -1322,3 +1317,11 @@ def get_app():
autoplay_game,
)
return app
if __name__ == "__main__":
app = get_app()
error = app.main_loop()
app.quit()
app.logger.close()
sys.exit(error)

View file

@ -1 +0,0 @@
# playscii package

View file

@ -1,9 +0,0 @@
import sys
from .app import get_app
app = get_app()
error = app.main_loop()
app.quit()
app.logger.close()
sys.exit(error)

View file

@ -23,7 +23,7 @@ requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.ruff]
src = ["playscii"]
src = ["."]
line-length = 88
[tool.ruff.lint]

View file

@ -4,8 +4,8 @@ import math
import numpy as np
from OpenGL import GL
from .art import VERT_LENGTH
from .palette import MAX_COLORS
from art import VERT_LENGTH
from palette import MAX_COLORS
# inactive layer alphas
LAYER_VIS_FULL = 1
@ -92,11 +92,6 @@ class TileRenderable:
self.alpha_uniform = self.shader.get_uniform_location("alpha")
self.brightness_uniform = self.shader.get_uniform_location("brightness")
self.bg_alpha_uniform = self.shader.get_uniform_location("bgColorAlpha")
self.attrib_vert_position = self.shader.get_attrib_location("vertPosition")
self.attrib_char_index = self.shader.get_attrib_location("charIndex")
self.attrib_uv_mod = self.shader.get_attrib_location("uvMod")
self.attrib_fg_color_index = self.shader.get_attrib_location("fgColorIndex")
self.attrib_bg_color_index = self.shader.get_attrib_location("bgColorIndex")
self.create_buffers()
# finish
if self.app.use_vao:
@ -490,45 +485,48 @@ class TileRenderable:
if self.app.use_vao:
GL.glBindVertexArray(self.vao)
else:
attrib = self.shader.get_attrib_location # for brevity
vp = ctypes.c_void_p(0)
# bind each buffer and set its attrib:
# verts
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vert_buffer)
GL.glVertexAttribPointer(
self.attrib_vert_position, VERT_LENGTH, GL.GL_FLOAT, GL.GL_FALSE, 0, vp
attrib("vertPosition"), VERT_LENGTH, GL.GL_FLOAT, GL.GL_FALSE, 0, vp
)
GL.glEnableVertexAttribArray(self.attrib_vert_position)
GL.glEnableVertexAttribArray(attrib("vertPosition"))
# chars
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.char_buffer)
GL.glVertexAttribPointer(
self.attrib_char_index, 1, GL.GL_FLOAT, GL.GL_FALSE, 0, vp
attrib("charIndex"), 1, GL.GL_FLOAT, GL.GL_FALSE, 0, vp
)
GL.glEnableVertexAttribArray(self.attrib_char_index)
GL.glEnableVertexAttribArray(attrib("charIndex"))
# uvs
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.uv_buffer)
GL.glVertexAttribPointer(
self.attrib_uv_mod, 2, GL.GL_FLOAT, GL.GL_FALSE, 0, vp
attrib("uvMod"), 2, GL.GL_FLOAT, GL.GL_FALSE, 0, vp
)
GL.glEnableVertexAttribArray(self.attrib_uv_mod)
GL.glEnableVertexAttribArray(attrib("uvMod"))
# fg colors
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.fg_buffer)
GL.glVertexAttribPointer(
self.attrib_fg_color_index, 1, GL.GL_FLOAT, GL.GL_FALSE, 0, vp
attrib("fgColorIndex"), 1, GL.GL_FLOAT, GL.GL_FALSE, 0, vp
)
GL.glEnableVertexAttribArray(self.attrib_fg_color_index)
GL.glEnableVertexAttribArray(attrib("fgColorIndex"))
# bg colors
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.bg_buffer)
GL.glVertexAttribPointer(
self.attrib_bg_color_index, 1, GL.GL_FLOAT, GL.GL_FALSE, 0, vp
attrib("bgColorIndex"), 1, GL.GL_FLOAT, GL.GL_FALSE, 0, vp
)
GL.glEnableVertexAttribArray(self.attrib_bg_color_index)
GL.glEnableVertexAttribArray(attrib("bgColorIndex"))
# finally, bind element buffer
GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, self.elem_buffer)
GL.glEnable(GL.GL_BLEND)
GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)
# draw all specified layers if no list given
if layers is None:
layers = self.art.get_sorted_layers()
# sort layers in Z depth
layers = list(range(self.art.layers))
layers.sort(key=lambda i: self.art.layers_z[i], reverse=False)
# handle a single int param
elif type(layers) is int:
layers = [layers]

View file

@ -6,7 +6,7 @@ import time
import numpy as np
from OpenGL import GL
from .renderable import TileRenderable
from renderable import TileRenderable
class LineRenderable:

View file

@ -5,7 +5,7 @@ import numpy as np
from OpenGL import GL
from PIL import Image
from .texture import Texture
from texture import Texture
class SpriteRenderable:

View file

@ -2,7 +2,7 @@ import math
import numpy as np
from .renderable_line import LineRenderable
from renderable_line import LineRenderable
class SelectionRenderable(LineRenderable):

View file

@ -3,17 +3,17 @@ import sdl2
from OpenGL import GL
from PIL import Image
from .art import (
from art import (
UV_FLIP270,
UV_NORMAL,
uv_names,
)
from .edit_command import EditCommand, EditCommandTile, EntireArtCommand
from .texture import Texture
from .ui_colors import UIColors
from .ui_console import ConsoleUI
from .ui_edit_panel import EditListPanel
from .ui_element import (
from edit_command import EditCommand, EditCommandTile, EntireArtCommand
from texture import Texture
from ui_colors import UIColors
from ui_console import ConsoleUI
from ui_edit_panel import EditListPanel
from ui_element import (
DebugTextUI,
FPSCounterUI,
GameHoverLabel,
@ -22,12 +22,12 @@ from .ui_element import (
ToolTip,
UIArt,
)
from .ui_menu_bar import ArtMenuBar, GameMenuBar
from .ui_menu_pulldown import PulldownMenu
from .ui_object_panel import EditObjectPanel
from .ui_popup import ToolPopup
from .ui_status_bar import StatusBarUI
from .ui_tool import (
from ui_menu_bar import ArtMenuBar, GameMenuBar
from ui_menu_pulldown import PulldownMenu
from ui_object_panel import EditObjectPanel
from ui_popup import ToolPopup
from ui_status_bar import StatusBarUI
from ui_tool import (
EraseTool,
FillTool,
GrabTool,
@ -37,7 +37,7 @@ from .ui_tool import (
SelectTool,
TextTool,
)
from .ui_toolbar import ArtToolBar
from ui_toolbar import ArtToolBar
UI_ASSET_DIR = "ui/"
SCALE_INCREMENT = 0.25

View file

@ -1,6 +1,6 @@
import os.path
from .art import (
from art import (
ART_DIR,
ART_FILE_EXTENSION,
DEFAULT_FRAME_DELAY,
@ -8,10 +8,10 @@ from .art import (
DEFAULT_LAYER_Z_OFFSET,
DEFAULT_WIDTH,
)
from .palette import PaletteFromFile
from .ui_chooser_dialog import ChooserDialog, ChooserItem, ChooserItemButton
from .ui_console import SaveCommand
from .ui_dialog import Field, UIDialog
from palette import PaletteFromFile
from ui_chooser_dialog import ChooserDialog, ChooserItem, ChooserItemButton
from ui_console import SaveCommand
from ui_dialog import Field, UIDialog
class BaseFileDialog(UIDialog):
@ -693,7 +693,6 @@ class SetLayerZDialog(UIDialog):
return
new_z = float(self.field_texts[0])
self.ui.active_art.layers_z[self.ui.active_art.active_layer] = new_z
self.ui.active_art._sorted_layers = None
self.ui.active_art.set_unsaved_changes(True)
self.ui.app.grid.reset()
self.dismiss()

View file

@ -1,4 +1,4 @@
from .ui_colors import UIColors
from ui_colors import UIColors
TEXT_LEFT = 0
TEXT_CENTER = 1

View file

@ -2,11 +2,11 @@ import os
import sdl2
from .art import UV_FLIPY, UV_NORMAL
from .renderable_sprite import UISpriteRenderable
from .ui_button import UIButton
from .ui_colors import UIColors
from .ui_dialog import Field, UIDialog
from art import UV_FLIPY, UV_NORMAL
from renderable_sprite import UISpriteRenderable
from ui_button import UIButton
from ui_colors import UIColors
from ui_dialog import Field, UIDialog
class ChooserItemButton(UIButton):

View file

@ -4,12 +4,12 @@ from math import ceil
import sdl2
# imports for console execution namespace - be careful!
from .art import UV_FLIPY
from .image_convert import ImageConverter
from .image_export import export_animation, export_still_image
from .key_shifts import SHIFT_MAP
from .palette import PaletteFromFile
from .ui_element import UIElement
from art import UV_FLIPY
from image_convert import ImageConverter
from image_export import export_animation, export_still_image
from key_shifts import SHIFT_MAP
from palette import PaletteFromFile
from ui_element import UIElement
CONSOLE_HISTORY_FILENAME = "console_history"

View file

@ -3,10 +3,10 @@ from collections import namedtuple
import sdl2
from .key_shifts import SHIFT_MAP
from .ui_button import TEXT_CENTER, UIButton
from .ui_colors import UIColors
from .ui_element import UIElement
from key_shifts import SHIFT_MAP
from ui_button import TEXT_CENTER, UIButton
from ui_colors import UIColors
from ui_element import UIElement
Field = namedtuple(
"Field",

View file

@ -1,11 +1,11 @@
import os
from .game_world import STATE_FILE_EXTENSION, TOP_GAME_DIR
from .ui_button import UIButton
from .ui_chooser_dialog import ScrollArrowButton
from .ui_colors import UIColors
from .ui_element import UIElement
from .ui_list_operations import (
from game_world import STATE_FILE_EXTENSION, TOP_GAME_DIR
from ui_button import UIButton
from ui_chooser_dialog import ScrollArrowButton
from ui_colors import UIColors
from ui_element import UIElement
from ui_list_operations import (
LO_LOAD_STATE,
LO_NONE,
LO_OPEN_GAME_DIR,

View file

@ -1,9 +1,9 @@
import time
from math import ceil
from . import vector
from .art import Art
from .renderable import TileRenderable
import vector
from art import Art
from renderable import TileRenderable
class UIElement:

View file

@ -4,20 +4,20 @@ import time
from PIL import Image
from .art import (
from art import (
ART_DIR,
ART_FILE_EXTENSION,
ART_SCRIPT_DIR,
SCRIPT_FILE_EXTENSION,
THUMBNAIL_CACHE_DIR,
)
from .charset import CHARSET_DIR, CHARSET_FILE_EXTENSION
from .image_export import write_thumbnail
from .palette import PALETTE_DIR, PALETTE_EXTENSIONS
from .texture import Texture
from .ui_art_dialog import ImportOptionsDialog, PaletteFromFileDialog
from .ui_chooser_dialog import ChooserDialog, ChooserItem
from .ui_console import OpenCommand
from charset import CHARSET_DIR, CHARSET_FILE_EXTENSION
from image_export import write_thumbnail
from palette import PALETTE_DIR, PALETTE_EXTENSIONS
from texture import Texture
from ui_art_dialog import ImportOptionsDialog, PaletteFromFileDialog
from ui_chooser_dialog import ChooserDialog, ChooserItem
from ui_console import OpenCommand
class BaseFileChooserItem(ChooserItem):

View file

@ -1,6 +1,6 @@
from .ui_console import LoadGameStateCommand, SaveGameStateCommand
from .ui_dialog import Field, UIDialog
from .ui_list_operations import (
from ui_console import LoadGameStateCommand, SaveGameStateCommand
from ui_dialog import Field, UIDialog
from ui_list_operations import (
LO_NONE,
)

View file

@ -1,4 +1,4 @@
from .ui_menu_pulldown_item import (
from ui_menu_pulldown_item import (
FileQuitItem,
PulldownMenuData,
PulldownMenuItem,

View file

@ -1,7 +1,7 @@
import sdl2
from .ui_dialog import UIDialog
from .ui_element import UIElement
from ui_dialog import UIDialog
from ui_element import UIElement
class PagedInfoDialog(UIDialog):

View file

@ -1,10 +1,10 @@
from math import ceil
from .renderable_sprite import UISpriteRenderable
from .ui_button import TEXT_CENTER, UIButton
from .ui_colors import UIColors
from .ui_element import UIElement
from .ui_game_menu_pulldown_item import (
from renderable_sprite import UISpriteRenderable
from ui_button import TEXT_CENTER, UIButton
from ui_colors import UIColors
from ui_element import UIElement
from ui_game_menu_pulldown_item import (
GameMenuData,
GameObjectMenuData,
GameRoomMenuData,
@ -12,8 +12,8 @@ from .ui_game_menu_pulldown_item import (
GameViewMenuData,
GameWorldMenuData,
)
from .ui_info_dialog import AboutDialog
from .ui_menu_pulldown_item import (
from ui_info_dialog import AboutDialog
from ui_menu_pulldown_item import (
ArtMenuData,
CharColorMenuData,
EditMenuData,

View file

@ -1,8 +1,8 @@
from .art import UV_FLIPX, UV_FLIPY, UV_ROTATE180
from .ui_button import UIButton
from .ui_colors import UIColors
from .ui_element import UIElement
from .ui_menu_pulldown_item import PulldownMenuData, PulldownMenuItem, SeparatorItem
from art import UV_FLIPX, UV_FLIPY, UV_ROTATE180
from ui_button import UIButton
from ui_colors import UIColors
from ui_element import UIElement
from ui_menu_pulldown_item import PulldownMenuData, PulldownMenuItem, SeparatorItem
class MenuItemButton(UIButton):

View file

@ -1,5 +1,5 @@
from .renderable import LAYER_VIS_DIM, LAYER_VIS_FULL, LAYER_VIS_NONE
from .ui_tool import (
from renderable import LAYER_VIS_DIM, LAYER_VIS_FULL, LAYER_VIS_NONE
from ui_tool import (
EraseTool,
FillTool,
GrabTool,

View file

@ -1,9 +1,9 @@
import os
from .ui_button import TEXT_RIGHT, UIButton
from .ui_colors import UIColors
from .ui_dialog import Field, UIDialog
from .ui_edit_panel import GamePanel
from ui_button import TEXT_RIGHT, UIButton
from ui_colors import UIColors
from ui_dialog import Field, UIDialog
from ui_edit_panel import GamePanel
class ResetObjectButton(UIButton):

View file

@ -1,11 +1,11 @@
from .art import UV_FLIPX, UV_FLIPY, UV_NORMAL, UV_ROTATE90, UV_ROTATE180, UV_ROTATE270
from .renderable_line import SwatchSelectionBoxRenderable
from .ui_button import TEXT_CENTER, UIButton
from .ui_colors import UIColors
from .ui_element import UIArt, UIElement
from .ui_file_chooser_dialog import CharSetChooserDialog, PaletteChooserDialog
from .ui_swatch import MIN_CHARSET_WIDTH, CharacterSetSwatch, PaletteSwatch
from .ui_tool import FILL_BOUND_BG_COLOR, FILL_BOUND_CHAR, FILL_BOUND_FG_COLOR, FillTool
from art import UV_FLIPX, UV_FLIPY, UV_NORMAL, UV_ROTATE90, UV_ROTATE180, UV_ROTATE270
from renderable_line import SwatchSelectionBoxRenderable
from ui_button import TEXT_CENTER, UIButton
from ui_colors import UIColors
from ui_element import UIArt, UIElement
from ui_file_chooser_dialog import CharSetChooserDialog, PaletteChooserDialog
from ui_swatch import MIN_CHARSET_WIDTH, CharacterSetSwatch, PaletteSwatch
from ui_tool import FILL_BOUND_BG_COLOR, FILL_BOUND_CHAR, FILL_BOUND_FG_COLOR, FillTool
TOOL_PANE_WIDTH = 10

View file

@ -2,11 +2,11 @@ import os.path
import time
from math import ceil
from .art import uv_names
from .renderable_line import UIRenderableX
from .ui_button import TEXT_CENTER, TEXT_RIGHT, UIButton
from .ui_colors import UIColors
from .ui_element import UIArt, UIElement, UIRenderable
from art import uv_names
from renderable_line import UIRenderableX
from ui_button import TEXT_CENTER, TEXT_RIGHT, UIButton
from ui_colors import UIColors
from ui_element import UIArt, UIElement, UIRenderable
# buttons to toggle "affects" status / cycle through choices, respectively

View file

@ -3,8 +3,8 @@ import time
import numpy as np
from .renderable_line import LineRenderable, SwatchSelectionBoxRenderable, UIRenderableX
from .ui_element import UIArt, UIElement, UIRenderable
from renderable_line import LineRenderable, SwatchSelectionBoxRenderable, UIRenderableX
from ui_element import UIArt, UIElement, UIRenderable
# min width for charset; if charset is tiny adjust to this
MIN_CHARSET_WIDTH = 16

View file

@ -1,7 +1,7 @@
import sdl2
from PIL import Image
from .art import (
from art import (
UV_FLIP90,
UV_FLIP270,
UV_FLIPX,
@ -11,10 +11,10 @@ from .art import (
UV_ROTATE180,
UV_ROTATE270,
)
from .edit_command import EditCommandTile
from .key_shifts import SHIFT_MAP
from .selection import SelectionRenderable
from .texture import Texture
from edit_command import EditCommandTile
from key_shifts import SHIFT_MAP
from selection import SelectionRenderable
from texture import Texture
class UITool:

View file

@ -1,7 +1,7 @@
from .renderable_line import ToolSelectionBoxRenderable
from .renderable_sprite import UISpriteRenderable
from .ui_button import UIButton
from .ui_element import UIElement
from renderable_line import ToolSelectionBoxRenderable
from renderable_sprite import UISpriteRenderable
from ui_button import UIButton
from ui_element import UIElement
class ToolBar(UIElement):