Commit Graph

57 Commits

Author SHA1 Message Date
bryanthaboi 2f9a920f4d Merge pull request #1825 from BountyHunterKanden/encounter-table-preview
Add API seam for mods to read/give information about altered encounter tables
2026-09-01 13:50:11 -04:00
BountyHunterKanden 36431f4c24 rfc: tighten 0019, merge migration/compat sections
Migration and Backward compatibility were two separate sections
saying the same thing about encounter.roll/encounter.species being
untouched, on top of a third mention in "The decision it extends" --
RFC 0014 and 0015 both fold this into one "Migration and
compatibility" section, so this does the same and drops the
redundant third restatement.

Also fixes the chance bullet's "the same reasoning that scoped
fishing out of this RFC" -- that reasoning was never actually stated
anywhere in the doc, just referenced as if it had been. Reworded to
stand on its own: encounter.fishing is a real, separate, existing
hook this RFC doesn't touch, which is reason enough on its own.
2026-08-26 20:31:31 -05:00
DESKTOP-8SRFDDM\cam95 49a5408c2d engine: pokemon.level_visible, a level a mode can take off the screen (RFC 0019)
A Pokémon's level is printed on four Gen 1 surfaces -- both battle
healthboxes, the party rows, and both status pages -- and every one of them
prints it unconditionally. There is no seam, so a mode that wants the number
gone has two options today and both are bad: paint over the engine's own
pixels from render.hud (four rectangles, a background shade to match, and
the palette flashes and healthbox slide to survive), or monkey-patch the
render modules from inside the sandbox, which works and is exactly what
CONTRIBUTING-mods.md tells mods not to do.

The motivating case is a battle royale that scales every party to a shared
rung rising with its fog: the number is the same for everyone, it changes on
a clock, and it reads as a threat it is not -- a Lv37 opponent looks
dangerous to a player who has not worked out that their own team is Lv37
too. A randomizer keeping an encounter unreadable, a challenge run that
forbids level-checking and a blind Nuzlocke want the same switch.

New hook `pokemon.level_visible`, taking the shape the presentation
predicates on the battle screen already use -- battle.status_hud_visible,
battle.bottom_ui_visible, battle.caught_marker_visible: consulted behind
Runtime.wantsHook, default visible, only an explicit false suppresses. It is
not named battle.* because a level is not a battle-only readout, and it
carries the surface that asked (battle.enemy / battle.player / party /
summary) so a mode can hide an opponent's level and keep its own.

src/ui/LevelDisplay.lua holds the one definition of "visible", so the four
call sites are a one-line guard each rather than four copies of the same
five lines that can drift apart.

No layout moves. Each site keeps its own hand-rolled PrintLevel rule
(home/pokemon.asm:335-345), it just asks first. Two details are deliberate:
a status condition still replaces the level on a healthbox exactly as in the
cart, so hiding a level never hides PSN or BRN (the guard is an elseif on
the existing status branch); and on status page 2 the <to> arrow is hidden
with the level it points at, because an arrow with nothing after it is half
a sentence.

Gen 1 only. The Gen 2 screens and the Gen 1 PC box list -- where the level
is part of a row label rather than a drawn field -- keep their own readouts
and do not consult the hook. Both are stated as follow-ups in the RFC and
beside the hook in docs/modding.md, so a mod author reads the limit before
depending on it.

Verification: tests/modkit/cases/pokemon_level_visible.lua covers the
contract through the public mod API; gate_hooks picks the hook up on its own
because it walks the live catalog; gate_meta_coverage is satisfied by the
change that introduces the seam, so it never enters the DEBT ledger.
tests/run_modkit.lua 33/33, tests/run_engine.lua 327/331 -- the same four
audio/hostshell suites fail unchanged on dev without this branch.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-26 10:53:09 -05:00
BountyHunterKanden f38b99caa6 engine: encounter.table -- preview the effective wild-encounter distribution (RFC 0019)
Lets a mod ask what a map's wild-encounter odds look like right now,
composed with any live encounter.table wrapper, without rolling the RNG.
encounter.roll/encounter.species can only transform one draw; this is the
missing read side. Guarded query method (mod.world:effectiveEncounters),
no changes to the real roll path, docs and a public-API modkit case.
2026-08-25 21:41:30 -05:00
DESKTOP-8SRFDDM\cam95 2ca07cfd09 engine: battle.style and catch.nickname rule hooks (RFC 0015)
Two decisions the OPTION screen and the cart make for the player, made
hookable so a game mode can make them instead: whether a faint offers the
SHIFT free switch, and whether a catch asks for a nickname.  Guarded call
sites, file-local vanilla links, docs and a public-API modkit case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1DVpYXGabigHqMwviDoKV
2026-08-25 09:20:35 -05:00
bryanthaboi 9dd38e06a5 Merge pull request #1767 from MaxTomahawk/adaptive-trainers/dataset-view-api
feat(mod-api): expose imported dataset views
2026-08-25 08:26:51 -04:00
MaxTomahawk ef62962010 Merge upstream dev into dataset view API 2026-08-25 11:54:22 +00:00
DESKTOP-8SRFDDM\cam95 7d6566fa51 engine: catch.party_full custody for a full-party catch (RFC 0018)
A capture the party cannot hold silently falls through to the box; the
new partyFullDestination seam lets a mode claim custody at that moment
instead, and pokemon.caught reports destination "mod" so the mode can
find the mon again.  Guarded call site, file-local vanilla, docs and a
public-API modkit case.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1DVpYXGabigHqMwviDoKV
2026-08-24 21:06:07 -05:00
bryanthaboi d575a64287 Merge pull request #1734 from BoLayerDev/feature/mew-dock-occupancy-seam
Add composable map occupancy and active-block APIs
2026-08-24 08:37:27 -04:00
bryanthaboi ff826ce01e Merge pull request #1769 from MaxTomahawk/adaptive-trainers/mod-developer-mode
feat(mod-api): expose loader developer mode
2026-08-24 08:35:49 -04:00
bryanthaboi 0ab4ef2755 Merge pull request #1766 from MaxTomahawk/adaptive-trainers/field-residual-hook
feat(mod-api): expose field residual descriptors
2026-08-24 08:35:10 -04:00
MaxTomahawk 96ed862b50 docs(mod-api): clarify developer mode sources 2026-08-24 10:51:09 +02:00
MaxTomahawk 58681fe62e feat(mod-api): expose developer mode 2026-08-24 10:38:31 +02:00
MaxTomahawk 38d515f547 fix(mod-api): reserve generated metadata ids 2026-08-24 10:10:23 +02:00
MaxTomahawk 7e069df740 fix(mod-api): validate dataset roots lazily 2026-08-24 09:57:57 +02:00
MaxTomahawk ca700c44d7 fix(mod-api): harden imported dataset views 2026-08-24 09:33:51 +02:00
MaxTomahawk 2ad2e028d1 feat(mod-api): expose field residual descriptors 2026-08-24 09:32:34 +02:00
MaxTomahawk b286e6584f feat(mod-api): add imported dataset views 2026-08-24 09:10:35 +02:00
DESKTOP-8SRFDDM\cam95 b550db3abb rfc: guard the new seams and write RFC 0014
Route B in CONTRIBUTING-mods.md asks an event/hook change for five things.
This adds the two that were missing and fixes what the third turned up.

link.battle_ended built its payload unconditionally. Route B is explicit
that a new event must not allocate when nothing wants it, and every other
emit in the engine already guards -- Runtime.wants now gates this one too,
so an unsubscribed build runs the branch exactly as it did.

world.talk was handing Runtime.call a closure built fresh on every A press,
purely to have a fallthrough to pass. It is a file-local now, so an unhooked
press allocates nothing it did not allocate before.

The RFC covers motivation, the API delta with call sites, migration (nothing
changes for existing mods), and verification. The backward-compatibility
statement is in it: every item is a new name or a new optional argument, and
example_mew_starter -- api 1, category = "GAMEPLAY", whole-species copy --
still loads, which run_modkit proves on every run.

No registry or schema field is added, so gen_registry_docs has nothing to
emit for this change. Running it does show pre-existing drift in
docs/modding/reference/registries.md (timeFishGroups, an objects refinement)
from earlier Schemas.lua edits that were never regenerated; that is not this
branch's to carry, so it is left alone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 10:35:12 -05:00
Bo Layer d17f0725d8 fix(mod-api): validate active block storage 2026-08-23 03:20:43 -06:00
Bo Layer c9221daa90 fix(mod-api): fail closed on malformed map blocks 2026-08-23 03:19:19 -06:00
Bo Layer 0147e3d87b feat(mod-api): add composable map occupancy seam 2026-08-23 03:17:27 -06:00
jramiresbrito 5ec9ce5f88 feat(gen2): read applyShare's announce argument in battle.exp_award
battle.exp_award hands a mod ctx.applyShare(mon, split, announce) on both
generations, and on Gen 1 the third argument decides whether the mon's
GainedText box is printed -- which is how a mod paying the whole party
prints ONE summary line instead of a box per recipient.  Gold accepted
the argument and ignored it, so the same mod source printed one line on
Red and one per party member on Gold.

The Exp Share mod is the live case: it declares games gen1+gen2 and its
description promises "a single shared-exp line instead of one message per
Pokemon", passing true for the fighters and nil for the bench exactly as
the Gen 1 seam asks.  On Gold every nil call announced anyway, so a
five-mon party turned every KO into six boxes.  There was no mod-side
fix: the emit sits behind no hook, and the argument meaning "quietly" was
discarded.

Gold now reads it, and ONLY when it is actually passed -- by argument
count, not by value.  select("#", ...) counts an explicit nil, so
applyShare(mon, split) is distinguishable from applyShare(mon, split,
nil); the first is a Gen 2-era call written against a seam that always
announced and keeps announcing, the second is a deliberate "pay this one
quietly" and is now silent on both games.  No mod that exists today
changes behaviour, and a mod that passes the argument gets parity.

Only the { kind = "experience" } event is affected.  A silent award is
still a whole award: exp, stat exp, battle.exp_gained, "grew to level",
learned moves and the forget prompt are untouched, in the same order.
giveExperiencePass takes a sixth `silent` parameter that defaults to
announcing, so both of the cart's own passes are unchanged.

RFC: docs/rfcs/0012-gen2-exp-award-announce.md
Docs: docs/mod-api-gen2-compat.md gains the reading and the residual
      omitted-argument difference beside the existing payload note.
Tests: tests/gen2_exp_share_test.lua grows the two Route B tests -- the
       no-mod parity case and the seam driven through hooks:wrap -- and
       its 23 existing checks are unchanged.
2026-08-21 18:07:36 -03:00
bryanthaboi d191aaa34d Merge pull request #1616 from HighDrexler/fix/large-required-imports-v2
Fix/large required imports v2
2026-08-21 09:59:17 -04:00
MaxTomahawk 4b0496bad1 feat(mod-api): expose charge decision hook 2026-08-21 14:40:51 +02:00
HighDrexler 911e11a372 test(mods): document and cover streamed import access 2026-08-20 17:14:32 -04:00
bryanthaboi 5f2b2c616e CLOSES #1181, CLOSES #1212, CLOSES #1214, CLOSES #1224, CLOSES #1230, CLOSES #1249, CLOSES #1271, CLOSES #1272, CLOSES #1273, CLOSES #1298, CLOSES #1305, CLOSES #1307, CLOSES #1318, CLOSES #1328, CLOSES #1330, CLOSES #1331, CLOSES #1333, CLOSES #1334, CLOSES #1335, CLOSES #1340, CLOSES #1345, CLOSES #1346, CLOSES #1360, CLOSES #1362 (#1395)
* CLOSES #1181, CLOSES #1212, CLOSES #1214, CLOSES #1224, CLOSES #1230, CLOSES #1249, CLOSES #1271, CLOSES #1272, CLOSES #1273, CLOSES #1298, CLOSES #1305, CLOSES #1307, CLOSES #1318, CLOSES #1328, CLOSES #1330, CLOSES #1331, CLOSES #1333, CLOSES #1334, CLOSES #1335, CLOSES #1340, CLOSES #1345, CLOSES #1346, CLOSES #1360, CLOSES #1362

* conv
2026-08-16 06:55:09 -04:00
Shane McGovern 9fab992d42 Add opaque byte storage to mod API 2026-08-14 21:42:45 +01:00
MaxTomahawk 407f649e9d fix(mod-api): harden deferred trainer preparation 2026-08-14 17:57:59 +02:00
MaxTomahawk a77210799f feat(mod-api): add trainer battle party scope 2026-08-14 17:33:17 +02:00
bryanthaboi e90dff6764 Merge branch 'grandmas-kitchen' into dev 2026-08-14 07:13:00 -04:00
Myles Resnick bde606f966 Permission-gated step bridge for sandboxed mods
The sandbox blocks love.system and love.filesystem, which orphans the
native step bridge (#452, #489): its one consumer can no longer call
syncHealthSteps or read steps_pending.json (#1186).

Adds a "steps" manifest permission (shown to the player like the
others) gating a mod.steps facade: available() probes the bridge
quietly, sync() forwards the async refresh, poll() hands the mod its
copy of a delivery. The engine owns the pending file -- mods never name
a path and receive only { steps, from, to }. Without the permission the
acting calls name it, following the network gate. No new events, hooks
or registries; nothing removed.

RFC 0009. Tests: tests/modkit/cases/steps_bridge.lua (no-mod cold
bridge, permissioned sync/poll, per-mod copies, contract-field
filtering, malformed-delivery drop, unpermissioned refusal, bridgeless
build).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 11:19:06 -04:00
AverageConsumer e44769a48a Expose read-only device power info to mods 2026-08-12 22:13:32 +02:00
MaxTomahawk 238af263d6 Merge remote-tracking branch 'origin/dev' into feat/battle-menu-auxiliary
# Conflicts:
#	src/core/Checkpoint.lua
2026-08-12 09:46:00 +02:00
bryanthaboi 3aaaf9936e Merge pull request #1076 from MaxTomahawk/feat/mod-title-checkpoint-resume
feat(mods): resume selected checkpoints from title
2026-08-11 21:23:08 -04:00
bryanthaboi af33c6e810 Merge pull request #1078 from MaxTomahawk/feat/scripted-battle-checkpoints
feat(mods): checkpoint scripted battle decisions
2026-08-11 21:22:40 -04:00
bryanthaboi ae6cac89e1 G2 support 2026-08-11 11:53:30 -04:00
MaxTomahawk 4db97164bb feat: anchor first checkpoint for cold restart 2026-08-11 08:59:47 +02:00
MaxTomahawk 882763cfe1 feat: checkpoint scripted battle decisions 2026-08-11 08:58:33 +02:00
MaxTomahawk 59725c0ead feat(mods): add battle menu auxiliary action 2026-08-10 23:32:38 +02:00
MaxTomahawk 67491e2dac feat(mods): expose selected save chronology 2026-08-10 23:32:35 +02:00
MaxTomahawk b8138ef850 feat(mods): resume selected checkpoints from title 2026-08-10 23:32:35 +02:00
bryanthaboi ee891fb8fa Merge pull request #993 from MaxTomahawk/feat/checkpoint-restore-event
feat(mods): signal verified checkpoint restores
2026-08-10 14:31:45 -04:00
bryanthaboi 9302bbcbf3 Merge branch 'fix1037' into fix1038
# Conflicts:
#	docs/modding.md
2026-08-10 14:12:21 -04:00
david 3c71afb9fa Per-category GAME SPEED: overworld/battle/menu + core.logic_speed hook (RFC 0007)
GameSpeed is a single fast-forward multiplier applied uniformly to the
whole logic clock -- overworld walking, menu navigation and battle turns
all scale together. A player who wants 4X battles but 1X overworld (so a
cutscene or NPC dialogue doesn't blur past) has no way to get both.

Splits save.options.speed into speedOverworld/speedBattle/speedMenu, each
cycling independently, with an automatic migration so an existing save's
speed choice carries over. Game.speedCategoryInStack resolves which
category is active by walking the state stack (the same idiom
wideBattleInStack/fillScaleInStack already use), so a menu opened mid-
battle inherits battle speed rather than resetting to whatever "menu"
defaults to. Adds a new core.logic_speed hook so a mod can read or
override the resolved multiplier for the current frame regardless of
which category produced it, sitting after the link-play and run-argument
overrides so neither is a seam a mod can defeat.

RFC 0007 status: Proposed.
2026-08-09 20:13:21 -07:00
david e79107c644 Add core.update/core.quit_to_launcher platform lifecycle hooks (RFC 0006)
A platform-specific launcher wrapper (a native shell embedding this engine,
owning its own UI around the game window) needs to pause the simulation
while its own UI is on top, live-reload options it wrote outside any Lua
UI, and veto main.lua's "closing the window returns to the Lua launcher"
behavior when it owns that job itself. Implementing this by hand-patching
main.lua's love.update/love.quit directly ties every such integration to
editing the one file every other engine change also touches, guaranteeing
merge conflicts. No existing hook covers "should the per-frame simulation
step run" or "should closing the window return to the Lua launcher."

Adds two generic, additive hooks (src/core/PlatformHooks.lua): core.update
and core.quit_to_launcher, replacing what would otherwise be inline
main.lua special-casing. Also adds Manifest.force_enable_env, letting a
mod that cannot function disabled on the one build where its env var is
set (a platform-bridge mod bundled only with that build) re-enable itself
regardless of a saved disable.

RFC 0006 status: Proposed.
2026-08-09 20:05:06 -07:00
MaxTomahawk aa3b2a18ec feat(mods): signal verified checkpoint restores 2026-08-08 13:28:56 +02:00
bryanthaboi 983bea61aa Merge pull request #986 from MaxTomahawk/feat/mod-battle-checkpoints
feat: add persistent battle safe-point checkpoints
2026-08-08 05:46:34 -04:00
bryanthaboi 653b72d86a Merge pull request #925 from Bortlesboat/multiplayer-session-foundation
Add a reusable multiplayer session layer
2026-08-08 05:42:23 -04:00
MaxTomahawk 5bffc89a2f docs: specify battle checkpoint extension 2026-08-08 09:53:41 +02:00