ff9mapkit 1.0.0b3__tar.gz → 1.0.0b5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/PKG-INFO +1 -1
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/__init__.py +1 -1
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/build.py +158 -51
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/cli.py +86 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/config.py +140 -16
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/conductor.py +137 -51
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/cutscene.py +70 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/text.py +7 -3
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/dialogue.py +1 -1
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/extract.py +15 -5
- ff9mapkit-1.0.0b5/ff9mapkit/memoria.py +91 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/provision.py +29 -1
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit.egg-info/PKG-INFO +1 -1
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit.egg-info/SOURCES.txt +2 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/pyproject.toml +1 -1
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_ate.py +124 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_campaign.py +1 -1
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_content.py +122 -13
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_flags.py +1 -1
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_on_entry.py +2 -2
- ff9mapkit-1.0.0b5/tests/test_setup.py +175 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_text.py +4 -4
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_verbatim.py +122 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/LICENSE +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/README.md +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/__main__.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_animdb.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_animdb_all.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_fieldtable.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_fieldtext.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_held_poses.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_itemdb.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_modeldb.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_narrowmap_data.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_npcparams.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_regen_animdb.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_regen_animdb_all.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_regen_fieldtable.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_regen_fieldtext.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_regen_modeldb.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_regen_npcparams.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_regen_scenedb.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/_scenedb.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/abilities.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/animations.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/archetypes.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/areatitle.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/__init__.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/abilityfeatures.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/actiondelta.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/aiauthor.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/ailint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/aipatch.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/battleai.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/battlecsv.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/battlepatch.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/build.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/camera_codec.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/camera_data.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/characterdelta.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/event_data.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/extract.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/fbx.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/reskin.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/scene_codec.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/scene_data.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/scenelint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/seqasm.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/seqauthor.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/seqcodec.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/seqdis.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle/seqpatch.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/battle_bgm.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/binutils.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/campaign.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/catalog.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/chain.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/__init__.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/areatitle.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/ate.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/camera.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/chest.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/choice.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/encounter.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/entry_settle.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/equipment.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/event.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/gateway.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/inventory.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/itemdata.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/itemtext.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/jump.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/ladder.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/movement.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/music.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/npc.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/object.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/onentry.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/party.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/pathfind.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/platform.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/player.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/prop.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/region.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/reinit.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/savepoint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/shop.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/sps_trigger.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/startup.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/synthesis.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/textcarry.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/verbatim.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/content/walkmesh_hotfix.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/__init__.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/_regen_provenance.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/provenance/blank.es.patch +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/provenance/blank.fr.patch +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/provenance/blank.gr.patch +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/provenance/blank.it.patch +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/provenance/blank.jp.patch +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/provenance/blank.uk.patch +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/provenance/blank.us.patch +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/provenance/manifest.json +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/provenance/region_template.patch +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/reference_arcs.toml +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/data/region_catalog.toml +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/deploystack.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eb/__init__.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eb/_exprtable.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eb/_membertable.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eb/_optables.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eb/_regen_optables.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eb/cmdasm.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eb/disasm.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eb/edit.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eb/exprasm.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eb/model.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eb/opcodes.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eblint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/editor/__init__.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/editor/app.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/editor/battle_forms.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/editor/breadcrumb.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/editor/dialogs.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/editor/feedback.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/editor/forms.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/editor/graphview.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/editor/jobs.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/editor/model.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/editor/picker.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/editor/theme.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/eventscan.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/flags.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/forkreport.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/hub.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/idgated.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/infohub.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/items.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/itemstats.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/journey.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/keyitems.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/logic_add.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/logic_edit.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/logic_map.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/pack.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/playerswap.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/prop_archetypes.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/refarc.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/save.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/save_items.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/scene/__init__.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/scene/arena.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/scene/bgart.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/scene/bgi.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/scene/bgs.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/scene/bgx.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/scene/cam.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/scene/guide.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/scene/paint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/scene/placeholder.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/sjbinary.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/sps/__init__.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/sps/author.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/sps/catalog.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/sps/codec.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/sps/edit.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/sps/lint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/sps/render.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/sps/templates.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/sps/texture.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/walkmesh_hotfixes.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/workspace/__init__.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/workspace/battledoc.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/workspace/builddoc.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/workspace/forms_qt.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/workspace/importdoc.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/workspace/mapview.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/workspace/palette.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/workspace/savedoc.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/workspace/shell.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/workspace/style.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit/workspace/tuningdialog.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit.egg-info/dependency_links.txt +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit.egg-info/entry_points.txt +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit.egg-info/requires.txt +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/ff9mapkit.egg-info/top_level.txt +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/setup.cfg +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_abilities.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_abilityfeatures.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_actiondelta.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_ai_phase_insert.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_ai_phase_insert_adversary.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_aiauthor.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_ailint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_aipatch.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_animations.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_archetypes.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_areatitle.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_arming.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_battle.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_battle_bgm.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_battle_forms.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_battle_scene_codec.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_battle_seq.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_battleai.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_battlecsv.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_battlepatch.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_bgart.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_bgs.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_build.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_cameras.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_capstone.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_carry_text_lint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_catalog.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_chain.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_characterdelta.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_choice.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_cli_entry.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_cmdasm.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_cmdasm_relocate.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_deploy_campaign.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_deploystack.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_dialogue.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_eb.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_eblint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_editor_app.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_editor_breadcrumb.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_editor_feedback.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_editor_forms.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_editor_integration.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_editor_jobs.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_editor_model.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_editor_theme.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_entry_settle.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_eventscan.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_export.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_exprasm.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_extract_area.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_find_field.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_forkreport.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_gateway_advance.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_graphview.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_hub_gen.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_idgated.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_import_borrow.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_infohub.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_itemdata.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_items.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_itemstats.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_itemtext.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_journey.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_journey_merge.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_jump.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_ladder.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_lint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_logic_add.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_logic_edit.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_logic_map.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_movement.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_npc_model.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_npc_verbatim.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_npcparams.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_object_graft.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_occlusion.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_pack.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_paint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_party.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_platform.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_player_graft.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_playerswap.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_prop_archetypes.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_provision.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_refarc.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_repaint_native.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_reskin.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_save.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_save_items.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_savepoint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_scene.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_scenelint.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_scroll.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_shared_text_block.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_shop.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_showcase.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_sjbinary.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_spawn.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_sps.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_startstate.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_startup.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_synthesis.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_textcarry.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_walkmesh_hotfix.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_workspace_style.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_world_hub.py +0 -0
- {ff9mapkit-1.0.0b3 → ff9mapkit-1.0.0b5}/tests/test_yaw_movement.py +0 -0
|
@@ -15,4 +15,4 @@ Public surface is organized as:
|
|
|
15
15
|
ff9mapkit.battle — the battle.toml -> custom battle-background (BBG) builder (fork/edit/build a 3D battle map)
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
__version__ = "1.0.
|
|
18
|
+
__version__ = "1.0.0b5" # keep in lockstep with [project] version in pyproject.toml
|
|
@@ -863,6 +863,22 @@ def validate(project: FieldProject) -> list[str]:
|
|
|
863
863
|
# on-exit story advance: set_scenario / set_flags fire when the player takes this exit
|
|
864
864
|
_validate_story_writes(gw, "[[gateway]]", story_names, problems,
|
|
865
865
|
scenario_key="set_scenario", flags_key="set_flags")
|
|
866
|
+
# forced-ATE warp-in: ate = true flashes the grey banner WARNING before this exit warps (the faithful
|
|
867
|
+
# grey-ATE trigger -- pair with a plain [cutscene] + exit_warp on the destination, which returns you).
|
|
868
|
+
if "ate_mode" in gw and not gw.get("ate"):
|
|
869
|
+
problems.append("[[gateway]] ate_mode is set but ate is not true -- set ate = true to make this "
|
|
870
|
+
"exit a forced-ATE warp (grey banner warning, then warp), or drop ate_mode.")
|
|
871
|
+
if gw.get("ate"):
|
|
872
|
+
m = gw.get("ate_mode", _cutscene.ATE_DEFAULT_MODE)
|
|
873
|
+
if not isinstance(m, int) or isinstance(m, bool) or not (0 <= m <= 255):
|
|
874
|
+
problems.append(f"[[gateway]] ate_mode {m!r} must be an int 0..255 "
|
|
875
|
+
f"(6 = grey unskippable ATE banner; ATE 0xD7 mode).")
|
|
876
|
+
if "ate_title" in gw:
|
|
877
|
+
if not gw.get("ate"):
|
|
878
|
+
problems.append("[[gateway]] ate_title is set but ate is not true -- set ate = true (it's the "
|
|
879
|
+
"title window the forced-ATE warp-in shows), or drop ate_title.")
|
|
880
|
+
elif not isinstance(gw["ate_title"], str) or not gw["ate_title"].strip():
|
|
881
|
+
problems.append("[[gateway]] ate_title must be a non-empty string (the ATE title-window text).")
|
|
866
882
|
for ev in project.raw.get("event", []):
|
|
867
883
|
z = ev.get("zone", [])
|
|
868
884
|
if len(z) not in (4, 5):
|
|
@@ -1823,6 +1839,16 @@ def lint_logic(project: FieldProject) -> list[str]:
|
|
|
1823
1839
|
for ln in _text.overflow_lines(t, wrap):
|
|
1824
1840
|
out.append(f"{who} has a word too wide to fit one line ({ln!r}) -- it will overflow; "
|
|
1825
1841
|
f"shorten it or raise [dialogue] wrap.")
|
|
1842
|
+
# DEPRECATION: `[cutscene] ate = true` is the OLD, unfaithful forced-ATE model -- a grey banner held over
|
|
1843
|
+
# THIS in-place cutscene. A real grey ATE WARPS you to a dedicated scene and back (verified vs 6 real grey
|
|
1844
|
+
# ATEs; see project-ff9-ate-system). Steer to the faithful trigger; the held-banner still builds (not an error).
|
|
1845
|
+
_cs = raw.get("cutscene")
|
|
1846
|
+
if isinstance(_cs, dict) and _cs.get("ate"):
|
|
1847
|
+
out.append("[cutscene] ate = true is the OLD held-banner ATE styling (a grey banner over this in-place "
|
|
1848
|
+
"cutscene) -- NOT how a real grey ATE works (it WARPS you to a scene, plays it, and warps you "
|
|
1849
|
+
"back). For a faithful forced ATE use `[[gateway]] ate = true` (the warp-in trigger: banner "
|
|
1850
|
+
"warning + centered title window, then the warp) + a plain [cutscene] + exit_warp on the "
|
|
1851
|
+
"destination field. This held-banner flavor still builds, but it isn't faithful.")
|
|
1826
1852
|
|
|
1827
1853
|
# reference-data sanity (Info Hub): an [[npc]] model id / animation id the engine won't recognise.
|
|
1828
1854
|
# A model NAME is handled by validate() (fatal); here we WARN on a raw id outside the known tables
|
|
@@ -2475,7 +2501,9 @@ def _gateway_on_exit_body(gw: dict, names: dict) -> bytes:
|
|
|
2475
2501
|
"""The story-state advance a ``[[gateway]]`` applies when the player TAKES this exit: the raw
|
|
2476
2502
|
``set_var`` bytes from ``set_scenario`` (ScenarioCounter) + ``set_flags`` (gEventGlobal bits), built
|
|
2477
2503
|
with the shared :func:`ff9mapkit.content.startup.startup_body`. ``b""`` when the gateway has neither
|
|
2478
|
-
(so the build is byte-identical to a gateway without on-exit writes).
|
|
2504
|
+
(so the build is byte-identical to a gateway without on-exit writes). (NB ``ate = true`` is NOT handled
|
|
2505
|
+
here -- a forced-ATE warp-in routes to :func:`cutscene.inject_forced_ate` instead, because the banner +
|
|
2506
|
+
timed warp must run in a RunScript'd player func, not inline in the region, or the region's Wait freezes.)"""
|
|
2479
2507
|
sc = gw.get("set_scenario")
|
|
2480
2508
|
if isinstance(sc, str):
|
|
2481
2509
|
sc = _flags.resolve_scenario(sc)
|
|
@@ -3165,9 +3193,10 @@ _UID_HOTFIX_DONORS = frozenset((900, 2803))
|
|
|
3165
3193
|
|
|
3166
3194
|
|
|
3167
3195
|
def _verbatim_donor_id(project: FieldProject):
|
|
3168
|
-
"""Best-effort donor field id of
|
|
3169
|
-
records it as ``[verbatim_eb] donor
|
|
3170
|
-
|
|
3196
|
+
"""Best-effort donor field id of ANY fork (verbatim OR native/synth), for engine-hotfix warnings AND the
|
|
3197
|
+
deploy-time ForkDonorPatch `<forkId> <donorId>` mapping. The import records it as ``[verbatim_eb] donor``
|
|
3198
|
+
(verbatim) or ``[field] source_field`` (native/synth); ``borrow_field`` is the BG-borrow form. ``None``
|
|
3199
|
+
when an older fork's toml lacks it (the warn/emit is then skipped -- pre-record forks need a hand-added line)."""
|
|
3171
3200
|
for blk, key in (("verbatim_eb", "donor"), ("field", "source_field"), ("field", "borrow_field")):
|
|
3172
3201
|
v = (project.raw.get(blk) or {}).get(key)
|
|
3173
3202
|
if isinstance(v, bool):
|
|
@@ -3251,6 +3280,61 @@ def _inject_verbatim_npcs(project: FieldProject, eb: bytes, npc_txids: dict, *,
|
|
|
3251
3280
|
return eb, npc_slots
|
|
3252
3281
|
|
|
3253
3282
|
|
|
3283
|
+
def _gen_conductor_walk_tags(project: FieldProject, eb: bytes, steps, npc_slots):
|
|
3284
|
+
"""Shared by the synth + verbatim conductor wiring: add the per-actor choreography tags a conductor's
|
|
3285
|
+
``walk`` beats need. For each ``walk`` step, add a walk tag (20+) to the actor's entry (the conductor
|
|
3286
|
+
``RunScript``s into it -- base ``Walk`` acts on the executing object, so the walk must run in the actor's
|
|
3287
|
+
own context). For each actor that walks inside a PARALLEL group (``with_prev``), also add ONE bare-RETURN
|
|
3288
|
+
join tag -- the conductor async-forks the walk then ``RunScriptSync``s the join tag to block until the
|
|
3289
|
+
walk frees the actor's script level. ``add_function`` grows an entry but keeps slot INDICES stable, so the
|
|
3290
|
+
conductor's by-uid refs (and any later band insert) stay valid. Returns ``(eb, walk_calls, join_tags)``.
|
|
3291
|
+
|
|
3292
|
+
The PLAYER walks too: ``"player"`` -> the tag goes on the player's OWN entry (``DefinePlayerCharacter``,
|
|
3293
|
+
located by :func:`ff9mapkit.content.player.find_player_entry`), but the conductor addresses it by the
|
|
3294
|
+
control-char sentinel uid 250 (``GetObjUID(250)`` -> the control character) -- so its add-target entry and
|
|
3295
|
+
its conductor uid differ (an NPC's are the same, slot == uid). Same recipe the ladder uses for the player."""
|
|
3296
|
+
walk_calls, join_tags = {}, {}
|
|
3297
|
+
if not any("walk" in s for s in steps):
|
|
3298
|
+
return eb, walk_calls, join_tags
|
|
3299
|
+
from .eb import edit as _eb_edit
|
|
3300
|
+
from .content import player as _player
|
|
3301
|
+
move_reg = _position_registry(project)
|
|
3302
|
+
# the player entry index is stable across add_function (slot indices don't move), so resolve it once
|
|
3303
|
+
player_entry = (_player.find_player_entry(EbScript.from_bytes(eb))
|
|
3304
|
+
if any(s.get("actor") == "player" and "walk" in s for s in steps) else None)
|
|
3305
|
+
|
|
3306
|
+
def _walk_target(actor):
|
|
3307
|
+
"""(entry index to add the tag to, uid the conductor addresses). Player: its own entry, uid 250."""
|
|
3308
|
+
if actor == "player":
|
|
3309
|
+
return player_entry, _conductor.PLAYER_UID
|
|
3310
|
+
slot = npc_slots.get(actor) # an NPC: entry index == runtime uid
|
|
3311
|
+
return slot, slot
|
|
3312
|
+
|
|
3313
|
+
next_tag = {} # actor name -> next free walk tag
|
|
3314
|
+
for i, s in enumerate(steps):
|
|
3315
|
+
if "walk" not in s:
|
|
3316
|
+
continue
|
|
3317
|
+
actor = s.get("actor")
|
|
3318
|
+
entry_idx, uid = _walk_target(actor)
|
|
3319
|
+
pt = _resolve_point(s["walk"], move_reg)
|
|
3320
|
+
t = next_tag.get(actor, _conductor.WALK_TAG_BASE)
|
|
3321
|
+
next_tag[actor] = t + 1
|
|
3322
|
+
eb = _eb_edit.add_function(eb, entry_idx, t, _conductor.walk_tag_body(pt[0], pt[1], s.get("speed")))
|
|
3323
|
+
walk_calls[i] = (uid, t)
|
|
3324
|
+
# actors that walk inside a parallel group (>1 member) need a join tag (async-fork + sync-drain)
|
|
3325
|
+
parallel_actors = set()
|
|
3326
|
+
for g in _conductor.group_parallel(steps):
|
|
3327
|
+
if len(g) > 1:
|
|
3328
|
+
parallel_actors |= {s.get("actor") for _i, s in g if "walk" in s and s.get("actor")}
|
|
3329
|
+
for actor in sorted(parallel_actors):
|
|
3330
|
+
entry_idx, uid = _walk_target(actor)
|
|
3331
|
+
if entry_idx is None:
|
|
3332
|
+
continue
|
|
3333
|
+
eb = _eb_edit.add_function(eb, entry_idx, _conductor.PARALLEL_JOIN_TAG, _conductor.join_tag_body())
|
|
3334
|
+
join_tags[uid] = _conductor.PARALLEL_JOIN_TAG
|
|
3335
|
+
return eb, walk_calls, join_tags
|
|
3336
|
+
|
|
3337
|
+
|
|
3254
3338
|
def _inject_verbatim_conductor(project: FieldProject, eb: bytes, npc_slots: dict, cutscene_txids, *, warnings) -> bytes:
|
|
3255
3339
|
"""Inject a MULTI-ACTOR ``[cutscene]`` conductor into a VERBATIM fork: ONE director code entry, seated
|
|
3256
3340
|
BELOW the donor's party band (so the 9 characters stay the top slots), that drives the additive ``[[npc]]``
|
|
@@ -3263,22 +3347,8 @@ def _inject_verbatim_conductor(project: FieldProject, eb: bytes, npc_slots: dict
|
|
|
3263
3347
|
return eb
|
|
3264
3348
|
steps = _resolve_conductor_steps(cs["steps"], project)
|
|
3265
3349
|
# walk beats -> a walk-choreography tag on the actor's below-band entry, RunScript'd by the conductor (the
|
|
3266
|
-
# synth path's mechanism; here the actor entries sit below the band)
|
|
3267
|
-
|
|
3268
|
-
walk_calls = {}
|
|
3269
|
-
if any("walk" in s for s in steps):
|
|
3270
|
-
from .eb import edit as _eb_edit
|
|
3271
|
-
move_reg = _position_registry(project)
|
|
3272
|
-
next_tag = {}
|
|
3273
|
-
for i, s in enumerate(steps):
|
|
3274
|
-
if "walk" not in s:
|
|
3275
|
-
continue
|
|
3276
|
-
slot = npc_slots.get(s.get("actor")) # validated to be a real NPC actor (not player)
|
|
3277
|
-
pt = _resolve_point(s["walk"], move_reg)
|
|
3278
|
-
t = next_tag.get(s["actor"], _conductor.WALK_TAG_BASE)
|
|
3279
|
-
next_tag[s["actor"]] = t + 1
|
|
3280
|
-
eb = _eb_edit.add_function(eb, slot, t, _conductor.walk_tag_body(pt[0], pt[1], s.get("speed")))
|
|
3281
|
-
walk_calls[i] = (slot, t)
|
|
3350
|
+
# synth path's mechanism; here the actor entries sit below the band); a parallel walk also gets a join tag.
|
|
3351
|
+
eb, walk_calls, join_tags = _gen_conductor_walk_tags(project, eb, steps, npc_slots)
|
|
3282
3352
|
auto = _FlagAlloc(getattr(project, "flag_base", None)) # campaign-safe once-flag (matches the other verbatim blocks)
|
|
3283
3353
|
c_fclass, c_fidx = _cutscene.once_flag_for(cs)
|
|
3284
3354
|
if auto.base is not None and "flag" not in cs and cs.get("once", True):
|
|
@@ -3289,7 +3359,7 @@ def _inject_verbatim_conductor(project: FieldProject, eb: bytes, npc_slots: dict
|
|
|
3289
3359
|
warmup=int(cs.get("warmup", _cutscene.DEFAULT_WARMUP)),
|
|
3290
3360
|
owns_control=bool(cs.get("owns_control", True)),
|
|
3291
3361
|
exit_warp=(int(cs["exit_warp"]) if cs.get("exit_warp") else None),
|
|
3292
|
-
walk_calls=walk_calls, reserve_party_band=True)
|
|
3362
|
+
walk_calls=walk_calls, join_tags=join_tags, reserve_party_band=True)
|
|
3293
3363
|
|
|
3294
3364
|
|
|
3295
3365
|
def _inject_verbatim_props(project: FieldProject, eb: bytes, prop_txids=None, *, warnings) -> bytes:
|
|
@@ -3351,6 +3421,11 @@ def _inject_verbatim_gateways(project: FieldProject, eb: bytes, *, warnings) ->
|
|
|
3351
3421
|
zone = gw["zone"]
|
|
3352
3422
|
if len(zone) == 4:
|
|
3353
3423
|
zone = _gw.quad_zone(zone)
|
|
3424
|
+
if gw.get("ate"): # a forced-ATE warp-in, seated below the party band
|
|
3425
|
+
eb = _cutscene.inject_forced_ate(eb, [tuple(p) for p in zone], int(gw["to"]),
|
|
3426
|
+
mode=int(gw.get("ate_mode", _cutscene.ATE_DEFAULT_MODE)),
|
|
3427
|
+
reserve_party_band=True)
|
|
3428
|
+
continue
|
|
3354
3429
|
gf, gs = _gate_of(gw)
|
|
3355
3430
|
eb = _gw.inject_gateway(eb, int(gw["to"]), entrance=int(gw.get("entrance", 0)),
|
|
3356
3431
|
zone=[tuple(p) for p in zone], gate_flag=gf, gate_require_set=gs,
|
|
@@ -3391,12 +3466,14 @@ def build_script(project: FieldProject, lang: str, dialogue_txids: dict,
|
|
|
3391
3466
|
control_value: int = -1, event_txids: dict | None = None,
|
|
3392
3467
|
cutscene_txids: list | None = None, walkmesh=None,
|
|
3393
3468
|
choice_txids: dict | None = None, on_entry_txids: dict | None = None,
|
|
3394
|
-
ate_txids: dict | None = None, chest_txids: dict | None = None
|
|
3469
|
+
ate_txids: dict | None = None, chest_txids: dict | None = None,
|
|
3470
|
+
gateway_txids: dict | None = None) -> bytes:
|
|
3395
3471
|
"""Build one language's .eb by applying the project's content to the blank field."""
|
|
3396
3472
|
_auto = _FlagAlloc(getattr(project, "flag_base", None))
|
|
3397
3473
|
event_txids = event_txids or {}
|
|
3398
3474
|
cutscene_txids = cutscene_txids or []
|
|
3399
3475
|
choice_txids = choice_txids or {}
|
|
3476
|
+
gateway_txids = gateway_txids or {}
|
|
3400
3477
|
on_entry_txids = on_entry_txids or {}
|
|
3401
3478
|
ate_txids = ate_txids or {}
|
|
3402
3479
|
chest_txids = chest_txids or {}
|
|
@@ -3557,10 +3634,15 @@ def build_script(project: FieldProject, lang: str, dialogue_txids: dict,
|
|
|
3557
3634
|
|
|
3558
3635
|
# gateways
|
|
3559
3636
|
gw_names = _story_names(project) # [[flag]] name -> index, for set_flags resolution
|
|
3560
|
-
for gw in project.raw.get("gateway", []):
|
|
3637
|
+
for gi, gw in enumerate(project.raw.get("gateway", [])):
|
|
3561
3638
|
zone = gw["zone"]
|
|
3562
3639
|
if len(zone) == 4:
|
|
3563
3640
|
zone = _gw.quad_zone(zone)
|
|
3641
|
+
if gw.get("ate"): # a forced-ATE warp-in: grey banner WARNING + title, then warp
|
|
3642
|
+
eb = _cutscene.inject_forced_ate(eb, [tuple(p) for p in zone], int(gw["to"]),
|
|
3643
|
+
mode=int(gw.get("ate_mode", _cutscene.ATE_DEFAULT_MODE)),
|
|
3644
|
+
title_txid=gateway_txids.get(gi))
|
|
3645
|
+
continue
|
|
3564
3646
|
gf, gs = _gate_of(gw)
|
|
3565
3647
|
eb = _gw.inject_gateway(eb, int(gw["to"]), entrance=int(gw.get("entrance", 0)),
|
|
3566
3648
|
zone=[tuple(p) for p in zone], gate_flag=gf, gate_require_set=gs,
|
|
@@ -3705,29 +3787,16 @@ def build_script(project: FieldProject, lang: str, dialogue_txids: dict,
|
|
|
3705
3787
|
if _auto.base is not None and "flag" not in cs and cs.get("once", True):
|
|
3706
3788
|
c_fidx = _auto.cutscene() # campaign: pack into this member's flag block
|
|
3707
3789
|
# walk beats can't run inline (base Walk acts on the EXECUTING object; there's no targeted WalkEx),
|
|
3708
|
-
# so generate a per-actor walk-choreography TAG on the actor's own [[npc]] entry and RunScript into
|
|
3709
|
-
#
|
|
3710
|
-
walk_calls =
|
|
3711
|
-
if any("walk" in s for s in c_steps):
|
|
3712
|
-
from .eb import edit as _eb_edit
|
|
3713
|
-
move_reg = _position_registry(project)
|
|
3714
|
-
next_tag = {} # actor name -> next free walk tag
|
|
3715
|
-
for i, s in enumerate(c_steps):
|
|
3716
|
-
if "walk" not in s:
|
|
3717
|
-
continue
|
|
3718
|
-
slot = npc_slots.get(s.get("actor")) # validated to be a real NPC actor (not player)
|
|
3719
|
-
pt = _resolve_point(s["walk"], move_reg)
|
|
3720
|
-
t = next_tag.get(s["actor"], _conductor.WALK_TAG_BASE)
|
|
3721
|
-
next_tag[s["actor"]] = t + 1
|
|
3722
|
-
eb = _eb_edit.add_function(eb, slot, t, _conductor.walk_tag_body(pt[0], pt[1], s.get("speed")))
|
|
3723
|
-
walk_calls[i] = (slot, t)
|
|
3790
|
+
# so generate a per-actor walk-choreography TAG on the actor's own [[npc]] entry and RunScript into it
|
|
3791
|
+
# (animates in the actor's context, blocks until arrival); a parallel walk also gets a join tag.
|
|
3792
|
+
eb, walk_calls, join_tags = _gen_conductor_walk_tags(project, eb, c_steps, npc_slots)
|
|
3724
3793
|
eb = _conductor.inject_conductor(
|
|
3725
3794
|
eb, c_steps, npc_slots, cutscene_txids,
|
|
3726
3795
|
once_flag=(c_fidx if cs.get("once", True) else None), flag_class=c_fclass,
|
|
3727
3796
|
warmup=int(cs.get("warmup", _cutscene.DEFAULT_WARMUP)),
|
|
3728
3797
|
owns_control=bool(cs.get("owns_control", True)),
|
|
3729
3798
|
exit_warp=(int(cs["exit_warp"]) if cs.get("exit_warp") else None),
|
|
3730
|
-
say_flags=cs_say_flags, walk_calls=walk_calls)
|
|
3799
|
+
say_flags=cs_say_flags, walk_calls=walk_calls, join_tags=join_tags)
|
|
3731
3800
|
|
|
3732
3801
|
# cutscene (narration, no actor): an ordered, control-locked sequence on entry (once), run as a
|
|
3733
3802
|
# standalone director code entry. Steps = say / wait / set_flag. An ACTOR cutscene was already
|
|
@@ -4133,17 +4202,40 @@ def _validate_conductor(project, cs, problems):
|
|
|
4133
4202
|
_animations.resolve(token, a)
|
|
4134
4203
|
except ValueError as e:
|
|
4135
4204
|
problems.append(f"[cutscene] step {k}: {e}")
|
|
4136
|
-
if act == "walk":
|
|
4137
|
-
|
|
4138
|
-
problems.append(f"[cutscene] step {k}: walk is not yet supported for \"player\" (only [[npc]] "
|
|
4139
|
-
f"actors can walk in a cutscene for now); use turn/anim/say on the player.")
|
|
4140
|
-
try:
|
|
4205
|
+
if act == "walk": # walk on "player" runs in the player's own
|
|
4206
|
+
try: # entry (uid 250); an [[npc]] in its slot entry
|
|
4141
4207
|
_resolve_point(s["walk"], move_reg) # [x, z] or a known marker/NPC name
|
|
4142
4208
|
except ValueError as e:
|
|
4143
4209
|
problems.append(f"[cutscene] step {k}: {e}")
|
|
4144
4210
|
t = s.get("tail")
|
|
4145
4211
|
if t is not None and t not in _text.TAIL_CODES:
|
|
4146
4212
|
problems.append(f"[cutscene] step {k} tail {t!r} is not a valid TAIL code")
|
|
4213
|
+
# parallel beats (with_prev): a step marked with_prev runs together with the preceding beat. Only
|
|
4214
|
+
# walk/anim/turn can run in parallel (say/wait/set_flag are sequential barriers), the group leader
|
|
4215
|
+
# must be one of those too, and no actor may act twice in a group (it has one execution context).
|
|
4216
|
+
if steps[0].get("with_prev"):
|
|
4217
|
+
problems.append("[cutscene] step 0 can't have with_prev = true (nothing precedes it)")
|
|
4218
|
+
_par_ok = ("walk", "anim", "turn")
|
|
4219
|
+
for g in _conductor.group_parallel(steps):
|
|
4220
|
+
if len(g) < 2:
|
|
4221
|
+
continue
|
|
4222
|
+
lead_k, lead_s = g[0]
|
|
4223
|
+
lead_act = next((key for key in _par_ok if key in lead_s), None)
|
|
4224
|
+
if lead_act is None:
|
|
4225
|
+
problems.append(f"[cutscene] step {lead_k} has a with_prev beat after it but is a "
|
|
4226
|
+
f"say/wait/set_flag (a sequential barrier) -- only walk/anim/turn run in parallel")
|
|
4227
|
+
seen = {lead_s.get("actor")} if lead_act else set()
|
|
4228
|
+
for k, s in g[1:]:
|
|
4229
|
+
act = next((key for key in _par_ok if key in s), None)
|
|
4230
|
+
if act is None:
|
|
4231
|
+
problems.append(f"[cutscene] step {k}: only walk/anim/turn can run with_prev "
|
|
4232
|
+
f"(say/wait/set_flag are sequential barriers)")
|
|
4233
|
+
continue
|
|
4234
|
+
who = s.get("actor")
|
|
4235
|
+
if who in seen:
|
|
4236
|
+
problems.append(f"[cutscene] step {k}: actor {who!r} already acts in this parallel group "
|
|
4237
|
+
f"(an actor can't do two things at once)")
|
|
4238
|
+
seen.add(who)
|
|
4147
4239
|
if "exit_warp" in cs:
|
|
4148
4240
|
ew = cs["exit_warp"]
|
|
4149
4241
|
if not (isinstance(ew, int) and not isinstance(ew, bool) and ew > 0):
|
|
@@ -4432,8 +4524,9 @@ def _wrap_width(project: FieldProject):
|
|
|
4432
4524
|
|
|
4433
4525
|
def collect_text(project: FieldProject):
|
|
4434
4526
|
"""Return (mes_body, npc_txids, event_txids, cutscene_txids, choice_txids, on_entry_txids, ate_txids,
|
|
4435
|
-
chest_txids). All field text (NPC dialogue, event messages, cutscene 'say' lines, choice
|
|
4436
|
-
replies, on-entry messages, the ATE menu, chest "Received X" boxes
|
|
4527
|
+
chest_txids, gateway_txids). All field text (NPC dialogue, event messages, cutscene 'say' lines, choice
|
|
4528
|
+
prompts + replies, on-entry messages, the ATE menu, chest "Received X" boxes, forced-ATE gateway titles)
|
|
4529
|
+
shares one .mes block, in that order
|
|
4437
4530
|
(so a field with no events/cutscene/choices/on_entry/ate/chests is byte-identical to the old layout).
|
|
4438
4531
|
``cutscene_txids`` is a list (one per 'say' step); ``choice_txids[c]`` = ``{"prompt": id, "replies":
|
|
4439
4532
|
{opt_index: id}}``; ``on_entry_txids[k]`` = the txid of hook ``k``'s message (only for hooks that have
|
|
@@ -4521,8 +4614,20 @@ def collect_text(project: FieldProject):
|
|
|
4521
4614
|
for k, ch in enumerate(project.raw.get("chest", [])):
|
|
4522
4615
|
text, strt, tail = _chest_received_box(ch)
|
|
4523
4616
|
ch_pos[k] = _add_raw(text, ch.get("tail") or tail, strt=strt)
|
|
4617
|
+
# forced-ATE gateway titles: the winATE-captioned, CENTERED "title window" a `[[gateway]] ate = true` shows
|
|
4618
|
+
# as it warps. Geometry verified vs real grey ATEs 956/2211: `[STRT=W,1][IMME][CENT=W]title` -- the engine
|
|
4619
|
+
# auto-centers a system window from its [STRT] width (like the chest box) + [CENT] centers the text + [IMME]
|
|
4620
|
+
# pops it fully drawn. W ~ the rendered text width (real STRT ~= text.measure * 7.2). The default dialogue
|
|
4621
|
+
# geometry (10,1)+TAIL=UPR pins it TOP-RIGHT (the reported bug). Added LAST (after chests) -> byte-identical
|
|
4622
|
+
# for a field without one.
|
|
4623
|
+
gw_pos = {}
|
|
4624
|
+
for gi, gw in enumerate(project.raw.get("gateway", [])):
|
|
4625
|
+
if gw.get("ate") and gw.get("ate_title"):
|
|
4626
|
+
_title = str(gw["ate_title"])
|
|
4627
|
+
_w = max(8, round(_text.measure(_title) * 7.2)) # ~ the FF9 STRT width of the rendered title
|
|
4628
|
+
gw_pos[gi] = _add_raw(f"[IMME][CENT={_w}]{_title}", "", strt=(_w, 1)) # NO tail -> the real ATE's true centre
|
|
4524
4629
|
if not lines:
|
|
4525
|
-
return "", {}, {}, [], {}, {}, {}, {}
|
|
4630
|
+
return "", {}, {}, [], {}, {}, {}, {}, {}
|
|
4526
4631
|
body, mapping = _text.build_mes(lines, start_txid=_text.DEFAULT_BASE_TXID, tails=tails, strts=strts)
|
|
4527
4632
|
npc_txids = {i: mapping[p] for i, p in npc_pos.items()}
|
|
4528
4633
|
event_txids = {j: mapping[p] for j, p in ev_pos.items()}
|
|
@@ -4536,7 +4641,9 @@ def collect_text(project: FieldProject):
|
|
|
4536
4641
|
"replies": {oi: mapping[p] for oi, p in ate_reply_pos.items()}}
|
|
4537
4642
|
if ate_prompt_pos is not None else {})
|
|
4538
4643
|
chest_txids = {k: mapping[p] for k, p in ch_pos.items()}
|
|
4539
|
-
|
|
4644
|
+
gateway_txids = {gi: mapping[p] for gi, p in gw_pos.items()} # forced-ATE title-window txids (by gw index)
|
|
4645
|
+
return (body, npc_txids, event_txids, cutscene_txids, choice_txids, on_entry_txids, ate_txids,
|
|
4646
|
+
chest_txids, gateway_txids)
|
|
4540
4647
|
|
|
4541
4648
|
|
|
4542
4649
|
# --------------------------------------------------------------------------- the build
|
|
@@ -4717,7 +4824,7 @@ def build_field(project: FieldProject, layout: ModLayout, *, langs=LANGS) -> Fie
|
|
|
4717
4824
|
|
|
4718
4825
|
_autofill_ladder_landing_y(project, cutscene_wmesh) # elevated dismount floors get their real Y
|
|
4719
4826
|
# --- dialogue + per-language script ---
|
|
4720
|
-
mes_body, txids, event_txids, cutscene_txids, choice_txids, on_entry_txids, ate_txids, chest_txids = collect_text(project)
|
|
4827
|
+
mes_body, txids, event_txids, cutscene_txids, choice_txids, on_entry_txids, ate_txids, chest_txids, gateway_txids = collect_text(project)
|
|
4721
4828
|
control_value = resolve_control_value(project, camera)
|
|
4722
4829
|
# faithful text carry: the donor's referenced dialogue, shipped VERBATIM per language and APPENDED after
|
|
4723
4830
|
# the authored block (its own [TXID=>=1000] re-index keeps it disjoint -- authored text + the hut golden
|
|
@@ -4923,7 +5030,7 @@ def build_field(project: FieldProject, layout: ModLayout, *, langs=LANGS) -> Fie
|
|
|
4923
5030
|
eb = build_script(project, lang, txids, control_value, event_txids=event_txids,
|
|
4924
5031
|
cutscene_txids=cutscene_txids, walkmesh=cutscene_wmesh,
|
|
4925
5032
|
choice_txids=choice_txids, on_entry_txids=on_entry_txids,
|
|
4926
|
-
ate_txids=ate_txids, chest_txids=chest_txids)
|
|
5033
|
+
ate_txids=ate_txids, chest_txids=chest_txids, gateway_txids=gateway_txids)
|
|
4927
5034
|
base = mes_body or ""
|
|
4928
5035
|
inplace = base
|
|
4929
5036
|
suffix = ""
|
|
@@ -30,6 +30,7 @@ Subcommands are wired up incrementally as the library lands:
|
|
|
30
30
|
models/scenes/catalog - the Info Hub: browse models (+ their animations), battle scenes, or
|
|
31
31
|
search every reference catalog by name
|
|
32
32
|
extract-templates - regenerate base assets from the user's own FF9 install (no game data shipped)
|
|
33
|
+
setup - one-shot: find the FF9 install, remember it, extract base assets, report Memoria status
|
|
33
34
|
|
|
34
35
|
Anything not yet implemented prints a clear "coming in Phase N" message rather than failing
|
|
35
36
|
with an import error, so the installed console script is always runnable.
|
|
@@ -104,6 +105,80 @@ def _cmd_extract_templates(args: argparse.Namespace) -> int:
|
|
|
104
105
|
return 0
|
|
105
106
|
|
|
106
107
|
|
|
108
|
+
def _cmd_setup(args: argparse.Namespace) -> int:
|
|
109
|
+
"""One-shot post-install setup: find the FF9 install, remember it in the user config, regenerate the
|
|
110
|
+
base assets, and report the Memoria engine status. ``--install-engine <zip>`` additionally installs the
|
|
111
|
+
Dream World IX engine bundle (backed up first). Safe to re-run; returns non-zero only so a calling
|
|
112
|
+
script (e.g. the installer) can tell -- it never needs to block."""
|
|
113
|
+
from . import memoria, provision
|
|
114
|
+
from .config import save_game_path
|
|
115
|
+
|
|
116
|
+
# 1) resolve the install, and remember it so future commands need no --game/$FF9_GAME_PATH
|
|
117
|
+
try:
|
|
118
|
+
game = find_game_path(args.game)
|
|
119
|
+
except ConfigError as e:
|
|
120
|
+
print(str(e), file=sys.stderr)
|
|
121
|
+
print("\nOnce you know the path, run: ff9mapkit setup --game \"<path>\"", file=sys.stderr)
|
|
122
|
+
return 2
|
|
123
|
+
print(f"Found FINAL FANTASY IX: {game}")
|
|
124
|
+
try:
|
|
125
|
+
cfg = save_game_path(game)
|
|
126
|
+
print(f" remembered in {cfg} (future commands won't need --game)")
|
|
127
|
+
except OSError as e: # noqa: BLE001 - config write is best-effort
|
|
128
|
+
print(f" (couldn't save the config: {e})", file=sys.stderr)
|
|
129
|
+
|
|
130
|
+
rc = 0
|
|
131
|
+
# 2) regenerate the base assets (the one-time bring-your-own-install step; reads YOUR install)
|
|
132
|
+
if not args.no_extract:
|
|
133
|
+
if provision.templates_present() and not args.force:
|
|
134
|
+
print("Base assets: already extracted (use --force to redo).")
|
|
135
|
+
elif not _has_unitypy():
|
|
136
|
+
print("Base assets: SKIPPED -- UnityPy isn't installed (it reads FF9's assetbundles).\n"
|
|
137
|
+
" Install it: pip install UnityPy (or reinstall ff9mapkit with the [assets] extra)",
|
|
138
|
+
file=sys.stderr)
|
|
139
|
+
rc = 1
|
|
140
|
+
else:
|
|
141
|
+
print("Base assets: regenerating from your install (ff9mapkit ships no game data)...")
|
|
142
|
+
try:
|
|
143
|
+
rep = provision.extract_templates(game=str(game), fixtures=not args.no_fixtures, verbose=True)
|
|
144
|
+
print(f" OK -- {len(rep['verified'])} assets regenerated + verified.")
|
|
145
|
+
except Exception as e: # noqa: BLE001
|
|
146
|
+
print(f" extract-templates failed: {e}", file=sys.stderr)
|
|
147
|
+
rc = 1
|
|
148
|
+
|
|
149
|
+
# 3) Memoria engine status (forked fields need it; novel/from-scratch fields run on stock Memoria)
|
|
150
|
+
st = memoria.memoria_status(game)
|
|
151
|
+
if st["installed"]:
|
|
152
|
+
print("Memoria engine: detected -- forked real fields will work.")
|
|
153
|
+
else:
|
|
154
|
+
print("Memoria engine: NOT detected.")
|
|
155
|
+
print(" From-scratch / BG-borrow fields run on stock Memoria. To play FORKED real fields, install\n"
|
|
156
|
+
" Memoria + the Dream World IX engine bundle (dwix-custom-memoria-*.zip) -- see docs/ENGINE.md,\n"
|
|
157
|
+
" or re-run: ff9mapkit setup --install-engine <bundle.zip>")
|
|
158
|
+
|
|
159
|
+
# 4) opt-in: install the engine bundle (backed up; never touches Memoria.ini)
|
|
160
|
+
if args.install_engine:
|
|
161
|
+
zp = Path(args.install_engine)
|
|
162
|
+
if not zp.is_file():
|
|
163
|
+
print(f"--install-engine: file not found: {zp}", file=sys.stderr)
|
|
164
|
+
return 1
|
|
165
|
+
import datetime # noqa: PLC0415
|
|
166
|
+
stamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
167
|
+
print(f"Engine bundle: installing {zp.name} (backing up the originals first)...")
|
|
168
|
+
try:
|
|
169
|
+
rep = memoria.install_engine_bundle(game, zp, stamp=stamp)
|
|
170
|
+
print(f" backed up {len(rep['backed_up'])} DLL(s) -> {rep['backup_root']}")
|
|
171
|
+
print(f" installed {len(rep['installed'])} DLL(s) into x64 + x86 Managed.")
|
|
172
|
+
print(" Relaunch FF9 to load it. (Undo: copy the backup DLLs back over the Managed ones.)")
|
|
173
|
+
except Exception as e: # noqa: BLE001
|
|
174
|
+
print(f" engine install failed: {e}", file=sys.stderr)
|
|
175
|
+
return 1
|
|
176
|
+
|
|
177
|
+
if rc == 0:
|
|
178
|
+
print("\nSetup complete. Try 'ff9mapkit doctor', or open the GUI with 'ff9mapkit-workspace'.")
|
|
179
|
+
return rc
|
|
180
|
+
|
|
181
|
+
|
|
107
182
|
def _cmd_disasm(args: argparse.Namespace) -> int:
|
|
108
183
|
from .eb import EbScript
|
|
109
184
|
|
|
@@ -3101,6 +3176,17 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
3101
3176
|
xt.add_argument("--no-fixtures", action="store_true", help="skip the test fixtures (templates only)")
|
|
3102
3177
|
xt.set_defaults(func=_cmd_extract_templates)
|
|
3103
3178
|
|
|
3179
|
+
su = sub.add_parser("setup",
|
|
3180
|
+
help="one-shot post-install setup: find your FF9 install, remember it, extract base "
|
|
3181
|
+
"assets, report Memoria status (--install-engine ZIP to install the engine bundle)")
|
|
3182
|
+
su.add_argument("--install-engine", metavar="ZIP", default=None,
|
|
3183
|
+
help="also install the Dream World IX engine bundle (dwix-custom-memoria-*.zip) -- backs "
|
|
3184
|
+
"up the originals; needed only to play FORKED real fields")
|
|
3185
|
+
su.add_argument("--force", action="store_true", help="re-extract the base assets even if already present")
|
|
3186
|
+
su.add_argument("--no-extract", action="store_true", help="skip the base-asset extraction step")
|
|
3187
|
+
su.add_argument("--no-fixtures", action="store_true", help="skip test fixtures during extraction")
|
|
3188
|
+
su.set_defaults(func=_cmd_setup)
|
|
3189
|
+
|
|
3104
3190
|
return p
|
|
3105
3191
|
|
|
3106
3192
|
|