Repository navigation
Conversation
eb5e19e removed the automatic window_activation subscription for title animation and rolling credits while decoupling graphical presentation from startup requirements. These surfaces now continue playback on focus loss in normal launches. Scene/modal/suspension pause and explicit subscriptions remain. Correct the roadmap and changelog, and require named removal reporting. Optional advertised-then-requested restoration is proposed only; no runtime behaviour is changed here. The earlier typo-bearing subject is clarified by this forward record, without rewriting history.
Remove CanvasNineSlice::fromPng and migrate Engine callers, tests and documentation to getFromPng, matching BattlerArtwork. Image loading behaviour is unchanged. Replace the G2 roadmap en dash with an ASCII hyphen. BREAKING CHANGE: CanvasNineSlice::fromPng has been removed; use CanvasNineSlice::getFromPng with the same arguments.
Share static grid parsing with the Editor and validate both grid files before loading map data. Remove executable, interpolated and string-array grid support; preserve literal content without evaluating it.
Add optional animation IDs while retaining legacy defaults and old save compatibility. Rehydrate saved item bindings from current definitions.
Cache definitions and compiled summons until battle exit, retain live loading for authoring, and diagnose malformed optional assets without hiding valid neighbours.
Resolve summons once at authored cue, frame or end timing. Preserve cues and gameplay through reduced motion and presentation failures. Add terminal flash pulses, compiled draw ordering and clearing, battle-cached playback, and explicit animation selection. Remove unconditional end-only resolution for cue/frame summons. Explicit missing IDs omit presentation rather than selecting name fallback; unset legacy skill references retain fallback. Reduced motion removes transient flash, shake and transition motion but retains every cue and the final frame.
Keep speech separate from sound effects, honour live mute, expose completion, and optionally duck music on seek-safe backends without restarting unsupported tracks.
Use semantic remappable input and per-page read timing while retaining manual confirmation and authored help. Wait for voice completion on the final page and stop owned speech on modal cleanup.
Resolve emotion and MP3 voice references softly and adopt shared dialogue Auto playback. Keep text progression when voice assets or playback fail, interrupt owned speech on advance, and cover speech, modal and skit lifecycles. Remove marking unfinished skits seen when the game stops or playback is interrupted. Completed text-only skits still record seen. Graphical staging and Editor beat authoring remain later phases.
…fecycle Scope animation and summon caches to the active battle so a later battle reads current assets. Hold reduced-motion final frames during the effect display phase. Share the end timing default and legacy animation fallback with authoring, and warn on unknown timing modes. Move terminal flash color encoding into the color model and document summon cue payloads.
… with contextual Space
Keep invalid destinations from mutating active field state and return failed starts to the title. Report offending grid tokens and lines, and generate collision-safe nowdoc delimiters.
Set destination identity before resolving map music during commit. Prepare NPCs before changing field state so invalid entries preserve the current map and NPC list.
Persist player-facing choices in private .data settings while keeping authored config.php as defaults. Remove the hard-coded space:continue hint so dialogue always shows live bindings.
Store only changed keyboard keys beside player preferences, overlay them over authored defaults at boot, and retain action/controller metadata. Convert local preference filesystem warnings before the Game fatal handler sees them, keeping failed choices live with diagnostics. Add read-only audio mute preflight for automated launches.
…gration Limit provisional current-name identities to missing-id legacy files at load time with diagnostics and no project writes. Explicit ids alone resolve definitions, including duplicate names and name/id collisions. Remove inferred display-name lookup from legacy saves unless an authored compatibility alias exists. Preserve frozen-name saves and test rename identity through the save pipeline. Use lifecycle-free Game doubles in voice tests so delayed destruction cannot reset Console after test teardown.
…hat is missing A project without a recorded format may already have its metadata, so the message no longer claims it predates it. It names both formats and points to ichiloto upgrade, which lists the changes before making them.
…in a cell An upper layer's cell replaced the cell below it whole, so a space in it hid what lay beneath: Home's chest " m" erased the wall "| " beside it. Layers compose per column again, as terminal layers always did; a two-column glyph still replaces its whole cell. Collision resolves each column through the layers as before and the cell is solid when either column is, and the single-grid collision map now uses the same resolver.
A project tileset (Data/Tilesets/<id>.php) names RPG Maker sheets A1 to E and the graphical flags RPG Maker keeps per tile (above characters, A2 tables); each sheet's tile size is read from its image and checked against its layout. A map names its tileset and keeps literal tile layers in graphics/, one RPG Maker tile identity per cell. The engine composes each identity used, including autotile shapes from quarter tiles and animated A1 water, into generic pieces for the retained world, with tile layers in the below and above character bands; the viewport carries RPG Maker's animation counter. AutotileShape resolves shapes from neighbours for authoring tools. Unusable graphics or sheets leave a map on its terminal glyphs; none of this touches geometry, collision, events or saves.
… the graphical field Two-column map cells changed how the terminal plays: a horizontal step moved two columns. The terminal is the game, so they are withdrawn and the graphical field corrects for its own pixels instead. A map cell is one terminal character again in every renderer, and the graphical field draws each one as a 24 x 48 box, the terminal's tall cell enlarged, so a step is one cell in both and a map that looks right in the terminal looks right graphically. Removed (reverting 8535e06, f3fbff7, cc06f98, ab8d472 and their tests and docs): MapCell and two-column parsing, composition and collision; the camera's cell and console column split (getConsolePosition, getConsoleColumns, resizeToConsole); NormalizedRow::fromCells; TwoColumnCellsMigration; project format 2. ProjectFormat::CURRENT is 1 again, so a project recording format 2 is refused as newer. Kept: tilesets and tile layers, the retired glyph-keyed crops (tiles2d still loads with a warning), the project format chain and its refusal of older projects, and the application icon. MapGridMetrics is not restored: its widths only served the retired crops. Graphical field geometry: FieldViewport::CELL_WIDTH and CELL_HEIGHT (24 x 48) size the camera and centring, TILE_SIZE (48) sizes character frames and field images (now counted in character frames), and TILE_COLUMNS (2) is how many cells a tile covers across. The retained world sends cellWidth and cellHeight instead of cellSize and cellColumns. Tile layers hold one identity per terminal cell; a tile covers its cell and the next, and AutotileShape resolves neighbours one tile width apart. BREAKING CHANGE: map coordinates count terminal columns again, the retained world definition carries cellWidth and cellHeight instead of cellSize and cellColumns, tile layer rows are as wide as the map in columns, and a 1 x 1 field image is 48 x 48.
…es centred or halved A field cell is 24 x 48, one column of RPG Maker's quarter tiles. Tiles anchored to a cell's left edge and covering two cells made a wall on one terminal column twice as thick as the column and put furniture half a column off its footprint. Autotiles are now composed per cell: every cell of an area holds its autotile, shapes are resolved from neighbouring cells (AutotileShape no longer steps two cells), and CellTiles chooses the quarter column the cell shows: the left half at a west edge or inner corner, the right half at an east one, the outer half of each when it borders both, and alternating halves inside a run. A plain tile (A5, B to E) is drawn whole and centred on its cell, or, with an L or R suffix in the tile layer (42L), only that half fills the cell. TileSlice cuts composed pieces into those slices, and the retained catalog sends each slice with its own width and left offset. MapTileLayer reads the suffixes into halves and reports each cell's authored entry. Removed: FieldViewport::TILE_COLUMNS and AutotileShape::resolveLayer's column step. BREAKING CHANGE: tile layers paint an autotile area in every cell and a plain tile is centred on its cell rather than anchored at its left edge; catalog tiles may carry width and left.
…exture in phase A map's data may shift a whole tile layer by half a field cell across or down, like a Tiled layer offset: 'tileLayers' => ['lounge' => ['offset' => [0, -0.5]]]. Art can then sit between the cells its terminal footprint allows, such as a coffee table centred between a sofa and a television. Offsets are -0.5, 0 or 0.5 on each axis and never move collision or events. MapGraphics::readLayerOffsets validates them for the Engine and tools, and the retained catalog carries them as tile left and top. An autotile cell away from an edge now shows the half matching its column's parity instead of alternating from the start of its row's run. Runs start in different columns on different rows, which put checkered and planked floors out of phase between rows.
A tileset may list pieces: whole items such as a bed, keyed by id, each with a name, the gameplay layer its terminal glyphs go on, rows of one-cell glyphs, and optional tiles keyed by tile layer name over the same footprint, using the entries a tile layer holds (42, 42L, 0). A space glyph or a 0 tile leaves that cell alone. TilesetPiece validates a piece with the tile layer's own rules, so editors can stamp a piece whole, writing glyphs and tiles together, while collision still comes from the glyphs alone.
… pieces The Terminal and editors bullet still said the TUI editor never edits a map's graphics, contradicting the Pieces bullet: stamping a piece writes its tiles.
A piece with 'connects' => 'lines' is drawn one cell at a time and joins the cells of the same piece beside it. Its glyphs name one glyph for each shape (horizontal, vertical, corner) and its tiles one entry per tile layer or one per shape. TilesetPiece::getLineShape gives a cell's shape from its neighbours (only across: horizontal; only down: vertical; otherwise a corner, which covers junctions and lone posts), and isMember recognises the piece's cells by their glyphs, so editors can reshape neighbours when a cell is drawn or erased.
A map's tileLayers settings may give a tile layer 'movesWith', naming the gameplay layer whose glyphs its tiles travel with when an editor moves them. The runtime only checks it names a gameplay layer. A layer's settings no longer need an offset.
A save compatibility step may now declare `mapShifts` instead of `class` (exactly one of the two): a list of rows or columns inserted into a map, each with map, axis (x or y), the 0-based insertion line and the count. The engine applies them in order to the saved player position, following map aliases, so projects need no generated PHP class for an insertion. Entries are validated strictly when the manifest loads. SaveCompatibilityManifest::migrationFrom(), which returned a class name, is replaced by createMigrationFrom(), which returns the migration for either kind. Its only caller, the save pipeline, is migrated; the migration is now created inside the pipeline's failure handling.
…ansitions Negotiate the key_transitions renderer event subscription. A subscribed session's key events carry a stable control identity and a repeat flag; key_release and input_reset report releases and focus loss. RendererClient queues presses, releases and resets in a bounded FIFO beside the unchanged key FIFO, and rejects transitions a session did not subscribe to. RendererInputSource implements the new HeldInputSourceInterface. Each InputManager::handleInput() applies that update's transitions to a bounded KeyHoldState before gameplay: isButtonHeld(), wasButtonPressed() and getButtonPressOrder() cover every binding of an action, keep quick taps as press edges, and match releases by control so modifier changes never strand a key. Held state clears on reset, source change, renderer restart and any input failure. isButtonDown(), isKeyDown() and axes keep their event-only meaning, so menus and navigation repeat are unchanged, and the terminal source stays event-only. The Game's GPUI session now requires key_transitions, so it needs the updated renderer.
…ween cells With input that reports held keys, the player walks as in RPG Maker MZ: the most recently pressed held direction wins, releasing it falls back to the next one still held, and each step is the ordinary validated tryMove(), so collision, gates, triggers, encounters and events behave exactly as a single step does. PlayerWalk paces steps with the one FieldMetric: RPG Maker's 180 field pixels per second over 24 x 48 cells, 8/60 s sideways and 16/60 s vertically, independent of zoom, scaling and window size. A press from standing steps at once; a direction change waits for the step in progress; taps are kept but cannot outrun walking; blocked movement banks no time; at most one step catches up after a stall; and any update the field did not control (menu, dialogue, cinematic, battle, transfer) cancels walking and makes earlier presses stale. Event-only input keeps stepping once per key event, unchanged. Every committed step now records a CharacterStep. With the field_motion drawing feature, sprites carry the step's duration and the viewport names the followed player, so the renderer slides characters and the camera between cells. Routes present steps at their own secondsPerStep through GameScene::moveAtPace(); wander steps walk at field speed. Renderers that do not advertise field_motion receive neither field. CharacterWalkAnimation now advances by distance travelled, one pattern per 30 field pixels, so both axes animate at one pace and continuous walking no longer stands between steps. BREAKING CHANGE: CharacterWalkAnimation::step() takes the CharacterStep it presents and STRIDE_SECONDS is removed (STOP_SECONDS replaces it); GraphicalSpriteProviderInterface gains getGraphicalSpriteMotion(); CinematicStageManager::subjectMoved() and StagedActor::beginGraphicalStep() take the step. Npc::beginGraphicalStep() without a step still advances one stride. Removed behaviour: under reduced motion the player and staged actors no longer animate their walking frames; they show the standing frame, as NPCs already did.
…an and field docs Update the controller-ready input plan in place: key transitions, held state and four-directional field walking are implemented; diagonal movement, physical controllers, analog input and enhanced terminal reporting stay planned. Document distance-based walking frames, route and wander pacing, slides, reduced motion and the renderer contract, and record the changes in the changelog.
A field cell was a 24 x 48 box, the terminal's own shape, so every RPG Maker tile and character had half a tile of width: a character covered one 24-pixel floor square across, a one-column bed overlapped the wall beside it and neighbouring chairs overlapped each other. RPG Maker's style fills 48 x 48 tiles, so the graphical field now draws each terminal cell as one 48 x 48 tile. The terminal is unchanged: one column is still one map cell and one step. - FieldViewport sizes the camera, centring and zoom in TILE_SIZE squares, so the field shows fewer columns than before; the retained world sends cellWidth and cellHeight of 48. - Each cell shows one whole tile: an autotile is composed for its cell from all four quarters by the shape its neighbours give it, and a plain tile (A5, B to E) is drawn whole in its cell. Layer offsets are in tiles (half a cell is 24 pixels on either axis); movesWith is unchanged. - A 48 x 48 character frame, bottom-centred as before, fills its cell. - FieldMetric takes one cell size, so every step, across or down, walks in 16/60 s at RPG Maker's 180 pixels per second, and each step carries the walk cycle 1.6 patterns on. Removed behaviour: - Per-cell autotile slicing: CellTiles, TileSlice (LEFT, RIGHT, NARROW, WHOLE), column parity and the centring offset (a whole tile at left -12). The retained catalog no longer carries a tile width. - The L and R half-tile suffix (42L). MapTileLayer::$halves is gone, and a tile layer or tileset piece entry naming half a tile is now refused with a message asking for whole tiles, not reinterpreted. - FieldViewport::CELL_WIDTH and CELL_HEIGHT, and FieldMetric's separate cell width and height. - Sideways steps no longer take 8/60 s; they take 16/60 s like vertical ones. BREAKING CHANGE: tile layers and pieces accept only whole tile identities; FieldViewport::CELL_WIDTH and CELL_HEIGHT are removed (use TILE_SIZE); FieldMetric's constructor takes one cellSize; the retained world's cells are 48 x 48 and maps look twice as wide graphically as in the terminal.
The graphical field plan, maps, tile batches, runtime, sprite sheets, input sources and the layered tilemap history now say that a terminal cell is drawn as one 48 x 48 RPG Maker tile: each cell shows one whole tile, a character frame fills its cell and every step walks in 16/60 s. The plan records that maps are laid out in tiles, so a map laid out only for the terminal's proportions looks twice as wide graphically. Removed: the descriptions of 24 x 48 cells, quarter-column autotile slices, column parity, centred plain tiles and L/R half-tile entries, which the engine no longer supports.
…t belongs to A tile layer belongs to one gameplay layer: the one its map data names (tileLayers.<name>.movesWith), and otherwise the gameplay layer whose tileset pieces write it, when exactly one does. That resolution lived only in the editor; MapGraphics::resolveLayerOwners() now owns it, returning each tile layer's gameplay layer or null, and loadFromDirectory() keeps the result in MapGraphics::$owners. With the new tile_covers drawing feature, PresentationWorld names that gameplay layer as coversLayerId on both draw bands of a tile layer, using the same ids as ownerLayerId, so a renderer hides a world cell's glyph only under tiles of the layer that owns the cell. A floor that belongs to buildings no longer hides the glyph of a fixture with no tile yet, such as Home's chest, sofa, coffee table and television. A tile layer that belongs to no gameplay layer keeps hiding every glyph it paints. RetainedPresentation sends the field only to a renderer that advertised tile_covers, choosing it per session on every world upload and reset; PresentationWorld::$operations stays the upload without it, so other renderers receive exactly the previous world.
…and cover only its glyphs
Console writes clamped their column into the screen, so text that started left of it was drawn at column 0 and text past its right edge at the last column. A field sprite that had scrolled off the side of the view, such as an NPC shown by its glyph in GPUI, stayed pinned to the view's edge. Writes are now clipped at the side edges as they already were at the top and bottom: the visible part lands where it belongs and a wide glyph the edge cuts is dropped whole. Changed behaviour: text written at a negative column, or at or past the screen's width, is no longer moved onto the screen.
A tileset belongs to a kind of setting, such as interiors, and every map of that kind names it, as RPG Maker groups its own. Its sheets are sorted by kind, and new art goes into free cells because a tile's identity is its place on its sheet. The examples now name an interior tileset.
RPG Maker MZ draws every character's frame 6 pixels above its tile (Sprite_Character shiftY), except object sheets whose file name starts with "!", so artists draw feet on the frame's bottom edge. Ichiloto drew such art on the cell's bottom edge, 6 pixels low. CharacterSheet now owns the rule. It reads the file name's leading run of "$" and "!" marks as RPG Maker does and gives every frame a lift of CharacterSheet::LIFT (6 field pixels, the sprite's own pixel space) or 0 for an object sheet. The lift is derived from the name alone, never from the image. GraphicalSpriteDefinition and PresentationSprite carry it, validated between 0 and the sprite's height, and GraphicalSpriteProjector passes it through, so the player, NPCs and staged cinematic actors drawn from a sheet are lifted. Single-image field art such as cinematic poses, battle and interface sprites keep no lift. Cells, collision, events, saves, row draw order and the terminal are unchanged. With the new sprite_lift drawing feature, RetainedPresentation names a nonzero lift on a sprite's value only for a renderer that negotiated it. Other renderers receive byte for byte the previous upload. Changed behaviour: a sheet named "!$Name" or "$!Name" is now read as a single-character sheet, as in RPG Maker; before, only a name starting with "$" was, so such a sheet was read as eight characters and an index of 1 to 7 on it is now rejected.
…d a minimap Records Andrew's direction: every glyph has graphical art, cues draw as bordered slanted exclamation marks in blue or yellow, story cues outside the view show at the screen edge in their direction, and a minimap overlay replaces the coordinate HUD. Planned only; nothing is implemented.
When the player talks to a field NPC, NpcManager::turnNpcToward() turns it toward the player through the existing face() path before it speaks, so its heading becomes the opposite of the player's, its terminal glyph swaps to an authored directional sprite and its character sheet shows the matching row. The NPC is not turned back afterwards; a wanderer stays turned until its next step. Map NPC entries accept an optional directionFix bool (RPG Maker's name, default false). A direction-fixed NPC keeps its heading when talked to; wander steps and move_route still turn it. NPCs a cinematic has staged are never turned by talking. Behaviour change: NPCs no longer keep their authored heading when the player talks to them unless directionFix is true.
As RPG Maker's Game_Event.lock() and unlock() do, the talk turn now remembers the NPC's heading and NpcManager::restoreNpcHeadingAfterTalk() turns it back through the same turnNpc() and face() path when the conversation ends: after the last plain dialogue page, when its script or a variant script completes or fails, or when the script cannot start. A restored wanderer keeps its wander schedule. Restore rule: the heading is restored only if nothing else set the NPC's heading or transform after the talk turn. Npc::face() (every route turn or step, even one toward the player) and restoreFieldTransform() (restored cinematic staging) forget the remembered heading. It is also not restored while a cinematic still stages the NPC or once the NPC has left the current map. RPG Maker restores unconditionally; this keeps script-authored results. A second talk while the NPC's conversation is active no longer turns it. MovementHeading::getDirection() replaces the player's local heading to tile match. Behaviour change: a talked-to NPC no longer keeps facing the player after the conversation; it turns back to its previous heading.
A map's kind is the setting it draws and names the tileset every map of that kind shares. It is chosen when the map is created, and the tiles a map can use change only when its kind does. The Editor asks it at creation, shows it as the Inspector's Kind and warns about a map without one; generate:map takes it as --kind.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2e32b6f68b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| $definitions = isset($data['tiles2d']) ? GraphicalTileDefinition::getForLayers($data['tiles2d'], $layers, $map) : []; | ||
| $loadMs = (hrtime(true) - $started) / 1e6; | ||
| $started = hrtime(true); | ||
| $world = $retained ? PresentationWorld::getFromLayers($layers, $definitions) : null; |
There was a problem hiding this comment.
Update the benchmark for the retained-world API
For every valid benchmark run, $retained is true and $definitions is an array, but PresentationWorld::getFromLayers() now expects a string world ID as its second argument, so this call throws a TypeError before any samples are collected. The same tool also references the deleted GraphicalTileDefinition and GraphicalTileCollector classes; it needs to construct the current MapGraphics/retained-world inputs instead of using the removed stateless tile API.
Useful? React with 👍 / 👎.
On a graphical field, stepping onto a save point opened its notice while the step was still sliding; the modal froze the slide, and when it closed the player jumped to the cell. Touch events (dialogues, transfers, battles) and encounters had the same cut. As RPG Maker does, what reaching a cell does now waits until the player is seen to arrive: the step is committed at once (position, collision, facing), and its triggers, encounter step, save point notice and movement observers run on the field frame its slide ends, never under a menu opened while it slid. A new step completes any arrival still pending first, so none is skipped or reordered. A step without a slide (the terminal, reduced motion) arrives immediately, as before.
The effect animation plan covered battle, skill, item and summon effects but not effects that live with a map. It now records the field as a consumer of the same timelines: cell and object anchors, once and loop playback, map-owned ambient effects declared at cells or by tileset pieces (a save point's energy, torches, magic circles, the planned graphical cues) that start and end with the map, object-attached effects that follow their owner, and image tracks drawn behind or in front of characters. The terminal glyph an effect decorates stays its terminal truth, reduced motion shows a rest frame, and the single field animation slot gives way to many sessions. Per-tile terrain animation stays on the tile counter and character sheets on the walk animation. Phases 1 and 2 gain the field items, and the graphical cues entry in the roadmap points at it.
Brings main up to develop.
Graphical field in RPG Maker proportions
!sheets are not lifted).Field behaviour
Plans
Earlier work on develop
Breaking changes are marked
!in the commits: the field cell geometry, composed autotiles, square tiles, retained presentation, literal map nowdocs andgetFromPngfactories.