@oh-story/dsh 0.1.12 → 0.1.13
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.
- package/README.md +17 -11
- package/lib/client.js +85 -35
- package/lib/client.js.map +3 -3
- package/lib/drama/manifest.json +41 -36
- package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +340 -13
- package/lib/drama/skills/short-drama-assets/references/voice-direction.md +4 -2
- package/lib/drama/skills/short-drama-edit/SKILL.md +32 -12
- package/lib/drama/skills/short-drama-edit/references/still-cuts.md +90 -0
- package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +1035 -145
- package/lib/drama/skills/short-drama-edit/scripts/selftest.py +48 -0
- package/lib/drama/skills/short-drama-produce/SKILL.md +6 -1
- package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +12 -0
- package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +138 -9
- package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +6 -0
- package/lib/drama/skills/short-drama-storyboard/SKILL.md +2 -0
- package/lib/drama/skills/short-drama-storyboard/references/production-shot-grammar.md +3 -1
- package/lib/drama/skills/short-drama-video-prompts/SKILL.md +7 -0
- package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +14 -0
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +19 -6
- package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +1 -0
- package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +5 -0
- package/lib/drama/skills/short-drama-video-prompts/references/wan-3.0.md +4 -1
- package/lib/index.js +16 -7
- package/lib/index.js.map +2 -2
- package/lib/novel-to-game/README.md +98 -44
- package/lib/novel-to-game/README_ZH.md +100 -46
- package/lib/novel-to-game/VERSION +1 -1
- package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +27 -202
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +23 -115
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/chamber.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/court_night.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/court_rain.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/garden.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/hall.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/kitchen.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/street.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/street_dawn.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/bg/temple.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/yue/moon.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/angry.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/blush.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/neutral.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/sad.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/smile.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/meng/surprised.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/angry.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/blush.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/neutral.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/sad.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/smile.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pan/surprised.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/angry.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/blush.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/neutral.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/sad.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/smile.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/pinger/surprised.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/angry.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/blush.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/neutral.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/sad.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/smile.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/xuee/surprised.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/angry.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/blush.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/neutral.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/sad.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/smile.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/sprite/yue/surprised.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +326 -2503
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/index.html +71 -4
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/adv.js +221 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/audio.js +113 -257
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +511 -3026
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/menus.js +277 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/petals.js +67 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/stage.js +180 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/store.js +70 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/common.js +198 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/common2.js +204 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/common3.js +198 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/dates.js +186 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/index.js +122 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/meng.js +190 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/pan.js +212 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/pinger.js +198 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/xuee.js +199 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/story/yue.js +214 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +2 -2
- package/lib/novel-to-game/examples/jin-ping-mei/build/art/generated-art.json +182 -45
- package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +7 -17
- package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +9 -9
- package/lib/novel-to-game/manifest.json +303 -78
- package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +7 -2
- package/lib/novel-to-game/skills/game-concept/references/concept-method.md +2 -0
- package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +2 -0
- package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +9 -0
- package/lib/types/client/drama-production-view.d.ts +11 -0
- package/lib/types/client/drama-production-view.d.ts.map +1 -1
- package/lib/types/client/file-activity.d.ts +1 -1
- package/lib/types/client/file-activity.d.ts.map +1 -1
- package/lib/types/client/production-prompts.d.ts +7 -1
- package/lib/types/client/production-prompts.d.ts.map +1 -1
- package/lib/types/client/production-runtime.d.ts +7 -2
- package/lib/types/client/production-runtime.d.ts.map +1 -1
- package/lib/types/drama-adapters.d.ts.map +1 -1
- package/lib/types/skill-provider.d.ts.map +1 -1
- package/lib/types/video-project.d.ts.map +1 -1
- package/lib/video-recap/CHANGELOG.md +108 -1
- package/lib/video-recap/README.en.md +18 -14
- package/lib/video-recap/README.md +18 -14
- package/lib/video-recap/manifest.json +297 -321
- package/lib/video-recap/skills/video-assemble/SKILL.md +18 -16
- package/lib/video-recap/skills/video-assemble/references/explicit-audio-mix.md +3 -1
- package/lib/video-recap/skills/video-assemble/references/jianying/SOURCE.md +5 -5
- package/lib/video-recap/skills/video-assemble/references/packaging.md +4 -4
- package/lib/video-recap/skills/video-assemble/references/subtitle-track.md +10 -10
- package/lib/video-recap/skills/video-assemble/scripts/adoption/audio_mix_binding.py +13 -2
- package/lib/video-recap/skills/video-assemble/scripts/adoption/av_clock.py +109 -0
- package/lib/video-recap/skills/video-assemble/scripts/adoption/strict_publish.py +4 -1
- package/lib/video-recap/skills/video-assemble/scripts/artifacts.py +2 -9
- package/lib/video-recap/skills/video-assemble/scripts/assemble.py +188 -51
- package/lib/video-recap/skills/video-assemble/scripts/assemble_constants.py +0 -1
- package/lib/video-recap/skills/video-assemble/scripts/assembly_contract.py +32 -75
- package/lib/video-recap/skills/video-assemble/scripts/assembly_settings.py +8 -15
- package/lib/video-recap/skills/video-assemble/scripts/audio_mix.py +202 -161
- package/lib/video-recap/skills/video-assemble/scripts/codec_peak.py +112 -0
- package/lib/video-recap/skills/video-assemble/scripts/export_jianying.py +10 -101
- package/lib/video-recap/skills/video-assemble/scripts/jianying/builders.py +44 -475
- package/lib/video-recap/skills/video-assemble/scripts/jianying/model.py +1 -22
- package/lib/video-recap/skills/video-assemble/scripts/jianying/templates.py +0 -3
- package/lib/video-recap/skills/video-assemble/scripts/jianying/timeline_contract.py +36 -103
- package/lib/video-recap/skills/video-assemble/scripts/jianying/tracks.py +0 -8
- package/lib/video-recap/skills/video-assemble/scripts/jianying/writer.py +28 -203
- package/lib/video-recap/skills/video-assemble/scripts/lib.py +20 -34
- package/lib/video-recap/skills/video-assemble/scripts/loudness.py +332 -0
- package/lib/video-recap/skills/video-assemble/scripts/media.py +158 -38
- package/lib/video-recap/skills/video-assemble/scripts/narration_audio.py +30 -16
- package/lib/video-recap/skills/video-assemble/scripts/render_preflight.py +49 -8
- package/lib/video-recap/skills/video-assemble/scripts/source_subtitles.py +17 -6
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/core.py +24 -2
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/track.py +2 -5
- package/lib/video-recap/skills/video-assemble/scripts/subtitles/track_binding.py +2 -9
- package/lib/video-recap/skills/video-assemble/scripts/timeline.py +12 -34
- package/lib/video-recap/skills/video-assemble/scripts/timeline_emit.py +3 -4
- package/lib/video-recap/skills/video-assemble/scripts/visual_render.py +28 -2
- package/lib/video-recap/skills/video-cut/SKILL.md +37 -11
- package/lib/video-recap/skills/video-cut/references/shot-review.md +1 -1
- package/lib/video-recap/skills/video-cut/scripts/cut.py +2 -34
- package/lib/video-recap/skills/video-cut/scripts/cut_cli.py +96 -124
- package/lib/video-recap/skills/video-cut/scripts/cut_contract.py +97 -182
- package/lib/video-recap/skills/video-cut/scripts/cut_render.py +177 -166
- package/lib/video-recap/skills/video-cut/scripts/frame_grid.py +249 -0
- package/lib/video-recap/skills/video-cut/scripts/lib.py +0 -3
- package/lib/video-recap/skills/video-cut/scripts/media_geometry.py +173 -7
- package/lib/video-recap/skills/video-cut/scripts/sentence_boundaries.py +234 -151
- package/lib/video-recap/skills/video-cut/scripts/sentence_gate.py +189 -0
- package/lib/video-recap/skills/video-cut/scripts/shot_review.py +2 -6
- package/lib/video-recap/skills/video-recap/SKILL.md +24 -27
- package/lib/video-recap/skills/video-recap/references/audio-routing.md +8 -6
- package/lib/video-recap/skills/video-recap/references/config-playbook.md +8 -11
- package/lib/video-recap/skills/video-recap/references/data-schema.md +131 -74
- package/lib/video-recap/skills/video-recap/references/resource-library.md +17 -1
- package/lib/video-recap/skills/video-recap/scripts/doctor.py +51 -268
- package/lib/video-recap/skills/video-recap/scripts/final_qc.py +89 -254
- package/lib/video-recap/skills/video-recap/scripts/lib.py +81 -70
- package/lib/video-recap/skills/video-recap/scripts/library.py +40 -4
- package/lib/video-recap/skills/video-recap/scripts/materials.py +13 -80
- package/lib/video-recap/skills/video-recap/scripts/recap_cli.py +49 -17
- package/lib/video-recap/skills/video-recap/scripts/recap_inspect.py +1 -3
- package/lib/video-recap/skills/video-recap/scripts/recap_runner.py +86 -116
- package/lib/video-recap/skills/video-recap/scripts/recap_runtime.py +87 -23
- package/lib/video-recap/skills/video-recap/scripts/recap_source.py +0 -39
- package/lib/video-recap/skills/video-recap/scripts/recap_stage_qc.py +37 -135
- package/lib/video-recap/skills/video-recap/scripts/recap_timeline.py +118 -127
- package/lib/video-recap/skills/video-recap/scripts/resources/__init__.py +4 -0
- package/lib/video-recap/skills/video-recap/scripts/{resource_lock.py → resources/lock.py} +1 -1
- package/lib/video-recap/skills/video-recap/scripts/{project_binding.py → resources/project_binding.py} +78 -17
- package/lib/video-recap/skills/video-reference/SKILL.md +108 -0
- package/lib/video-recap/skills/video-reference/references/reference-schema.md +139 -0
- package/lib/video-recap/skills/video-reference/scripts/lib.py +85 -0
- package/lib/video-recap/skills/video-reference/scripts/reference.py +96 -0
- package/lib/video-recap/skills/video-reference/scripts/reference_check.py +553 -0
- package/lib/video-recap/skills/video-reference/scripts/reference_frames.py +122 -0
- package/lib/video-recap/skills/video-reference/scripts/reference_measure.py +253 -0
- package/lib/video-recap/skills/video-reference/scripts/reference_profile.py +271 -0
- package/lib/video-recap/skills/video-script/SKILL.md +9 -7
- package/lib/video-recap/skills/video-script/scripts/agent_text.py +18 -267
- package/lib/video-recap/skills/video-script/scripts/deslop_qc.py +12 -30
- package/lib/video-recap/skills/video-script/scripts/entry_suggestion.py +125 -0
- package/lib/video-recap/skills/video-script/scripts/evidence_bundle.py +2 -26
- package/lib/video-recap/skills/video-script/scripts/lib.py +5 -15
- package/lib/video-recap/skills/video-script/scripts/lint_summary.py +184 -0
- package/lib/video-recap/skills/video-script/scripts/narration_lint.py +120 -100
- package/lib/video-recap/skills/video-script/scripts/review.py +3 -34
- package/lib/video-recap/skills/video-script/scripts/review_grounding.py +20 -46
- package/lib/video-recap/skills/video-script/scripts/review_response.py +120 -258
- package/lib/video-recap/skills/video-script/scripts/review_runner.py +2 -10
- package/lib/video-recap/skills/video-script/scripts/speech_ownership.py +89 -5
- package/lib/video-recap/skills/video-script/scripts/validate.py +26 -34
- package/lib/video-recap/skills/video-understanding/SKILL.md +29 -4
- package/lib/video-recap/skills/video-understanding/references/data-schema.md +5 -22
- package/lib/video-recap/skills/video-understanding/scripts/agent_text.py +24 -121
- package/lib/video-recap/skills/video-understanding/scripts/asr.py +11 -5
- package/lib/video-recap/skills/video-understanding/scripts/asr_timing_evidence.py +15 -24
- package/lib/video-recap/skills/video-understanding/scripts/briefing/builder.py +59 -25
- package/lib/video-recap/skills/video-understanding/scripts/briefing/context.py +39 -3
- package/lib/video-recap/skills/video-understanding/scripts/briefing/inputs.py +15 -27
- package/lib/video-recap/skills/video-understanding/scripts/briefing/timeline.py +136 -37
- package/lib/video-recap/skills/video-understanding/scripts/consolidate.py +121 -37
- package/lib/video-recap/skills/video-understanding/scripts/detect.py +118 -16
- package/lib/video-recap/skills/video-understanding/scripts/index_normalize.py +359 -0
- package/lib/video-recap/skills/video-understanding/scripts/lib.py +62 -21
- package/lib/video-recap/skills/video-understanding/scripts/storyboard.py +181 -51
- package/lib/video-recap/skills/video-understanding/scripts/timeline_fusion.py +0 -26
- package/lib/video-recap/skills/video-understanding/scripts/understanding_brief.py +37 -14
- package/lib/video-recap/skills/video-understanding/scripts/understanding_cache.py +40 -32
- package/lib/video-recap/skills/video-understanding/scripts/understanding_runner.py +100 -127
- package/lib/video-recap/skills/video-understanding/scripts/understanding_storyboard.py +182 -30
- package/lib/video-recap/skills/video-understanding/scripts/vlm.py +30 -27
- package/lib/video-recap/skills/video-voiceover/SKILL.md +19 -12
- package/lib/video-recap/skills/video-voiceover/references/index-tts.md +3 -3
- package/lib/video-recap/skills/video-voiceover/scripts/approved_text_policy.py +3 -1
- package/lib/video-recap/skills/video-voiceover/scripts/dub.py +60 -135
- package/lib/video-recap/skills/video-voiceover/scripts/lib.py +9 -34
- package/lib/video-recap/skills/video-voiceover/scripts/providers/fish_audio.py +6 -1
- package/lib/video-recap/skills/video-voiceover/scripts/tts_audio.py +64 -1
- package/lib/video-recap/skills/video-voiceover/scripts/tts_cache.py +99 -0
- package/lib/video-recap/skills/video-voiceover/scripts/voiceover.py +177 -200
- package/package.json +45 -45
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_accord_five.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/cg/group/public_day15.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/assets/household/li_jiaoer.webp +0 -0
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +0 -1494
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/assets.js +0 -147
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +0 -7470
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +0 -13066
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +0 -32
- package/lib/novel-to-game/examples/jin-ping-mei/qa/playable-model.json +0 -99
- package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +0 -152
- package/lib/video-recap/skills/video-assemble/references/foreground-compose.md +0 -118
- package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_segment.json +0 -84
- package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_combination_video_material.json +0 -98
- package/lib/video-recap/skills/video-assemble/references/jianying/empty_jy_draft.json +0 -204
- package/lib/video-recap/skills/video-assemble/references/pair-media.md +0 -74
- package/lib/video-recap/skills/video-assemble/scripts/compose_foreground.py +0 -293
- package/lib/video-recap/skills/video-assemble/scripts/pair_media.py +0 -216
- package/lib/video-recap/skills/video-recap/references/shift-left-qc-schema.md +0 -23
- package/lib/video-recap/skills/video-recap/scripts/mimo_qc.py +0 -33
- package/lib/video-recap/skills/video-recap/scripts/qc/__init__.py +0 -1
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_client.py +0 -13
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_contract.py +0 -9
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_evidence.py +0 -287
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_observations.py +0 -175
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_payload.py +0 -206
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_report.py +0 -318
- package/lib/video-recap/skills/video-recap/scripts/qc/mimo_runner.py +0 -130
- package/lib/video-recap/skills/video-recap/scripts/qc_contract.py +0 -320
- package/lib/video-recap/skills/video-script/scripts/timeline_fusion.py +0 -108
- package/lib/video-recap/skills/video-understanding/scripts/brief.py +0 -9
- package/lib/video-recap/skills/video-understanding/scripts/deslop_qc.py +0 -211
- package/lib/video-recap/skills/video-understanding/scripts/narration_lint.py +0 -707
- package/lib/video-recap/skills/video-understanding/scripts/speech_ownership.py +0 -143
- /package/lib/video-recap/skills/video-cut/scripts/{narration_mapping.py → cut_qc.py} +0 -0
|
@@ -4,15 +4,82 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<meta name="color-scheme" content="light" />
|
|
7
|
-
<meta name="description" content="《金瓶梅·风月总账》
|
|
7
|
+
<meta name="description" content="《金瓶梅·风月总账》18+ 古风恋爱 ADV:一条共通线,五位女主各自的路线与三种结局。" />
|
|
8
8
|
<title>金瓶梅·风月总账</title>
|
|
9
9
|
<link rel="icon" href="data:," />
|
|
10
10
|
<link rel="stylesheet" href="css/style.css" />
|
|
11
|
-
<link rel="stylesheet" href="css/new-guofeng.css" />
|
|
12
11
|
</head>
|
|
13
12
|
<body>
|
|
14
|
-
<div id="app"
|
|
15
|
-
|
|
13
|
+
<div id="app">
|
|
14
|
+
<section id="gate" class="screen" hidden>
|
|
15
|
+
<div class="gate-card">
|
|
16
|
+
<div class="gate-seal">18+</div>
|
|
17
|
+
<h1>十八岁以下,止步</h1>
|
|
18
|
+
<p>本作只供成年人游玩,含成人恋爱关系、轻度裸露与亲密场景及成熟主题。所有可进入亲密场景的人物均为成年虚构角色。</p>
|
|
19
|
+
<div class="gate-actions">
|
|
20
|
+
<button id="btn-age-yes" class="btn primary">我已成年,进入</button>
|
|
21
|
+
<button id="btn-age-no" class="btn">离开</button>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</section>
|
|
25
|
+
|
|
26
|
+
<section id="title" class="screen" hidden>
|
|
27
|
+
<div class="title-bg"></div>
|
|
28
|
+
<canvas id="petals"></canvas>
|
|
29
|
+
<div class="title-veil"></div>
|
|
30
|
+
<div class="title-logo">
|
|
31
|
+
<h1><span>金瓶梅</span><small>风月总账</small></h1>
|
|
32
|
+
<p>那年春天,五扇门,都为你留过灯。</p>
|
|
33
|
+
</div>
|
|
34
|
+
<nav class="title-menu" aria-label="标题菜单">
|
|
35
|
+
<button data-act="new">开始游戏</button>
|
|
36
|
+
<button data-act="continue">继续</button>
|
|
37
|
+
<button data-act="load">读取</button>
|
|
38
|
+
<button data-act="extras">鉴赏</button>
|
|
39
|
+
<button data-act="config">设置</button>
|
|
40
|
+
<button data-act="exit">退出</button>
|
|
41
|
+
</nav>
|
|
42
|
+
<p class="title-foot">18+ · 成人恋爱 ADV · 点击推进,选择改变她们的心意</p>
|
|
43
|
+
</section>
|
|
44
|
+
|
|
45
|
+
<section id="game" class="screen" hidden>
|
|
46
|
+
<div id="stage">
|
|
47
|
+
<div class="bg" id="bg-a"></div>
|
|
48
|
+
<div class="bg" id="bg-b"></div>
|
|
49
|
+
<div id="sprites"></div>
|
|
50
|
+
<div id="weather"></div>
|
|
51
|
+
<div id="cg"><img alt="" /></div>
|
|
52
|
+
<div id="flash"></div>
|
|
53
|
+
</div>
|
|
54
|
+
<div id="ui">
|
|
55
|
+
<div id="textbox">
|
|
56
|
+
<div id="nameplate"></div>
|
|
57
|
+
<p id="text"></p>
|
|
58
|
+
<span id="next-ind" aria-hidden="true"></span>
|
|
59
|
+
<span id="hint" hidden>点击画面或按空格继续 · 右键隐藏文字 · 滚轮上翻回看</span>
|
|
60
|
+
</div>
|
|
61
|
+
<nav id="quickmenu" aria-label="快捷菜单">
|
|
62
|
+
<button data-act="backlog">回看</button>
|
|
63
|
+
<button data-act="rollback">上一选项</button>
|
|
64
|
+
<button data-act="qsave">快存</button>
|
|
65
|
+
<button data-act="qload">快读</button>
|
|
66
|
+
<button data-act="save">存档</button>
|
|
67
|
+
<button data-act="load">读档</button>
|
|
68
|
+
<button data-act="auto">自动</button>
|
|
69
|
+
<button data-act="skip">快进</button>
|
|
70
|
+
<button data-act="config">设置</button>
|
|
71
|
+
<button data-act="hide">隐藏</button>
|
|
72
|
+
<button data-act="menu">菜单</button>
|
|
73
|
+
</nav>
|
|
74
|
+
</div>
|
|
75
|
+
<div id="choices" tabindex="-1" hidden></div>
|
|
76
|
+
<div id="card" hidden></div>
|
|
77
|
+
<div id="mode-badge" hidden></div>
|
|
78
|
+
</section>
|
|
79
|
+
|
|
80
|
+
<section id="overlay" hidden></section>
|
|
81
|
+
<div id="toast" hidden></div>
|
|
82
|
+
</div>
|
|
16
83
|
<script type="module" src="js/main.js"></script>
|
|
17
84
|
</body>
|
|
18
85
|
</html>
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
// ADV 解释器:把 story/*.js 里的行式剧本解析成指令,再一步步执行到下一处需要玩家的地方。
|
|
2
|
+
// 纯逻辑、无 DOM,浏览器与 node(test/lint_script.mjs)共用。
|
|
3
|
+
//
|
|
4
|
+
// 剧本语法(每行一条):
|
|
5
|
+
// # label 标签
|
|
6
|
+
// 名(表情):台词 说话;名可为 CAST 里的中文名,表情为 平/笑/怒/哀/羞/惊
|
|
7
|
+
// 其他文字 旁白
|
|
8
|
+
// @bg id [fade|cut|flash] 背景 @bgm id|stop @se id @weather rain|petals|none
|
|
9
|
+
// @show id 左|中|右 [表情] 立绘 @hide id|all @fx shake|flash
|
|
10
|
+
// @chapter 标题|副题 章节标题卡(「标题 · 副题」也写入存档名)
|
|
11
|
+
// @cg id | @cg off CG @scene id 题名 / @endscene 回想区段
|
|
12
|
+
// @choice + 若干「- 文本 -> label {yue+1 f:flag} ?yue>=2」
|
|
13
|
+
// @jump label | @if 条件 -> label | @do 效果
|
|
14
|
+
// @branch 共通线终点:按好感开放各院门
|
|
15
|
+
// @ending id 记录并展示结局 @fate 人物 原著命数页 @end 回标题
|
|
16
|
+
|
|
17
|
+
export const EXPR = Object.freeze({ 平: 'neutral', 笑: 'smile', 怒: 'angry', 哀: 'sad', 羞: 'blush', 惊: 'surprised' });
|
|
18
|
+
export const POS = Object.freeze({ 左: 'left', 中: 'center', 右: 'right' });
|
|
19
|
+
export const HEROINES = Object.freeze(['yue', 'pan', 'pinger', 'meng', 'xuee']);
|
|
20
|
+
export const ROUTE_MIN = 3;
|
|
21
|
+
|
|
22
|
+
const SAY = /^([^\s::@#\-][^:]{0,5}?)(?:[((]([平笑怒哀羞惊])[))])?:(.+)$/;
|
|
23
|
+
|
|
24
|
+
function parseEffects(text = '') {
|
|
25
|
+
return text.trim().split(/\s+/).filter(Boolean).map((token) => {
|
|
26
|
+
if (token.startsWith('f:')) return { flag: token.slice(2) };
|
|
27
|
+
const m = token.match(/^(\w+)([+-]\d+)$/);
|
|
28
|
+
if (!m) throw new Error(`效果写法不对:${token}`);
|
|
29
|
+
return { aff: m[1], delta: Number(m[2]) };
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function parseCond(text) {
|
|
34
|
+
if (!text) return null;
|
|
35
|
+
return text.split('&').map((part) => {
|
|
36
|
+
const s = part.trim();
|
|
37
|
+
let m = s.match(/^(!?)f:(\w+)$/);
|
|
38
|
+
if (m) return { flag: m[2], not: m[1] === '!' };
|
|
39
|
+
m = s.match(/^(\w+)(>=|<=|>|<|==)(-?\d+)$/);
|
|
40
|
+
if (m) return { aff: m[1], op: m[2], value: Number(m[3]) };
|
|
41
|
+
throw new Error(`条件写法不对:${s}`);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function testCond(cond, vars) {
|
|
46
|
+
if (!cond) return true;
|
|
47
|
+
return cond.every((c) => {
|
|
48
|
+
if (c.flag) return Boolean(vars.flags[c.flag]) !== c.not;
|
|
49
|
+
const v = vars.aff[c.aff] ?? 0;
|
|
50
|
+
return { '>=': v >= c.value, '<=': v <= c.value, '>': v > c.value, '<': v < c.value, '==': v === c.value }[c.op];
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function parseScript(sources, cast, speakers = []) {
|
|
55
|
+
const byName = Object.fromEntries(Object.entries(cast).map(([id, c]) => [c.name, id]));
|
|
56
|
+
const labels = {};
|
|
57
|
+
let current = null;
|
|
58
|
+
let pendingChoice = null;
|
|
59
|
+
for (const source of sources) {
|
|
60
|
+
for (const raw of source.split('\n')) {
|
|
61
|
+
const line = raw.trim();
|
|
62
|
+
if (!line || line.startsWith('//')) continue;
|
|
63
|
+
if (line.startsWith('# ')) {
|
|
64
|
+
current = line.slice(2).trim();
|
|
65
|
+
if (labels[current]) throw new Error(`重复标签:${current}`);
|
|
66
|
+
labels[current] = [];
|
|
67
|
+
pendingChoice = null;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (!current) throw new Error(`标签之外的行:${line}`);
|
|
71
|
+
const list = labels[current];
|
|
72
|
+
if (line.startsWith('- ') && pendingChoice) {
|
|
73
|
+
const m = line.slice(2).match(/^(.+?)\s*->\s*(\w+)\s*(?:\{([^}]*)\})?\s*(?:\?(\S+))?$/);
|
|
74
|
+
if (!m) throw new Error(`选项写法不对:${line}`);
|
|
75
|
+
pendingChoice.options.push({ text: m[1].trim(), target: m[2], effects: parseEffects(m[3]), cond: parseCond(m[4]) });
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
pendingChoice = null;
|
|
79
|
+
if (line.startsWith('@')) {
|
|
80
|
+
const [op, ...rest] = line.slice(1).split(/\s+/);
|
|
81
|
+
const arg = line.slice(op.length + 2).trim();
|
|
82
|
+
let cmd;
|
|
83
|
+
if (op === 'bg') cmd = { t: 'bg', id: rest[0], fx: rest[1] || 'fade' };
|
|
84
|
+
else if (op === 'bgm') cmd = { t: 'bgm', id: rest[0] === 'stop' ? null : rest[0] };
|
|
85
|
+
else if (op === 'se') cmd = { t: 'se', id: rest[0] };
|
|
86
|
+
else if (op === 'show') cmd = { t: 'show', who: rest[0], pos: POS[rest[1]] || 'center', expr: EXPR[rest[2]] || 'neutral' };
|
|
87
|
+
else if (op === 'hide') cmd = { t: 'hide', who: rest[0] };
|
|
88
|
+
else if (op === 'fx') cmd = { t: 'fx', id: rest[0] };
|
|
89
|
+
else if (op === 'weather') cmd = { t: 'weather', id: rest[0] === 'none' ? null : rest[0] };
|
|
90
|
+
else if (op === 'chapter') { const [title, sub = ''] = arg.split('|'); cmd = { t: 'chapter', title: title.trim(), sub: sub.trim() }; }
|
|
91
|
+
else if (op === 'cg') cmd = { t: 'cg', id: rest[0] === 'off' ? null : rest[0] };
|
|
92
|
+
else if (op === 'scene') cmd = { t: 'scene', id: rest[0], title: rest.slice(1).join(' ') };
|
|
93
|
+
else if (op === 'endscene') cmd = { t: 'endscene' };
|
|
94
|
+
else if (op === 'choice') { cmd = { t: 'choice', options: [] }; pendingChoice = cmd; }
|
|
95
|
+
else if (op === 'jump') cmd = { t: 'jump', target: rest[0] };
|
|
96
|
+
else if (op === 'if') { const m = arg.match(/^(\S+)\s*->\s*(\w+)$/); cmd = { t: 'if', cond: parseCond(m[1]), target: m[2] }; }
|
|
97
|
+
else if (op === 'do') cmd = { t: 'do', effects: parseEffects(arg) };
|
|
98
|
+
else if (op === 'branch') cmd = { t: 'branch' };
|
|
99
|
+
else if (op === 'ending') cmd = { t: 'ending', id: rest[0] };
|
|
100
|
+
else if (op === 'fate') cmd = { t: 'fate', who: rest[0] };
|
|
101
|
+
else if (op === 'end') cmd = { t: 'end' };
|
|
102
|
+
else throw new Error(`未知指令:${line}`);
|
|
103
|
+
list.push(cmd);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const m = line.match(SAY);
|
|
107
|
+
if (m && (byName[m[1]] || speakers.includes(m[1]))) {
|
|
108
|
+
list.push({ t: 'say', who: byName[m[1]] ?? null, name: m[1], expr: m[2] ? EXPR[m[2]] : null, text: m[3].trim() });
|
|
109
|
+
} else {
|
|
110
|
+
list.push({ t: 'say', who: null, name: '', expr: null, text: line });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const [label, list] of Object.entries(labels)) list.forEach((cmd, ip) => { if (cmd.t === 'say') cmd.id = `${label}:${ip}`; });
|
|
115
|
+
return labels;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const blankStage = () => ({ bg: null, bgFx: 'fade', sprites: {}, cg: null, bgm: null, weather: null, chapter: '' });
|
|
119
|
+
|
|
120
|
+
export class Story {
|
|
121
|
+
constructor(labels, hooks = {}) {
|
|
122
|
+
this.labels = labels;
|
|
123
|
+
this.hooks = hooks; // onUnlock(kind, id)
|
|
124
|
+
this.reset();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
reset() {
|
|
128
|
+
this.label = null;
|
|
129
|
+
this.ip = 0;
|
|
130
|
+
this.vars = { aff: Object.fromEntries(HEROINES.map((h) => [h, 0])), flags: {} };
|
|
131
|
+
this.stage = blankStage();
|
|
132
|
+
this.replay = false;
|
|
133
|
+
this.pending = null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
start(label = 'start', { replay = false, ip = 0 } = {}) {
|
|
137
|
+
this.reset();
|
|
138
|
+
this.label = label;
|
|
139
|
+
this.ip = ip;
|
|
140
|
+
this.replay = replay;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
jump(target) {
|
|
144
|
+
if (!this.labels[target]) throw new Error(`跳到不存在的标签:${target}`);
|
|
145
|
+
this.label = target;
|
|
146
|
+
this.ip = 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
apply(effects) {
|
|
150
|
+
for (const e of effects) {
|
|
151
|
+
if (e.flag) this.vars.flags[e.flag] = true;
|
|
152
|
+
else this.vars.aff[e.aff] = (this.vars.aff[e.aff] ?? 0) + e.delta;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 执行到下一个阻塞点并返回它:say / choice / chapter / ending / fate / end
|
|
157
|
+
next() {
|
|
158
|
+
for (let guard = 0; guard < 10000; guard += 1) {
|
|
159
|
+
const list = this.labels[this.label];
|
|
160
|
+
if (!list) throw new Error(`标签缺失:${this.label}`);
|
|
161
|
+
if (this.ip >= list.length) throw new Error(`标签 ${this.label} 没有用 @jump/@end 收尾`);
|
|
162
|
+
const cmd = list[this.ip];
|
|
163
|
+
this.ip += 1;
|
|
164
|
+
const s = this.stage;
|
|
165
|
+
switch (cmd.t) {
|
|
166
|
+
case 'bg': s.bg = cmd.id; s.bgFx = cmd.fx; s.cg = null; break;
|
|
167
|
+
case 'bgm': s.bgm = cmd.id; if (cmd.id) this.hooks.onUnlock?.('music', cmd.id); break;
|
|
168
|
+
case 'se': this.hooks.onSe?.(cmd.id); break;
|
|
169
|
+
case 'fx': this.hooks.onFx?.(cmd.id); break;
|
|
170
|
+
case 'weather': s.weather = cmd.id; break;
|
|
171
|
+
case 'show': s.sprites[cmd.who] = { pos: cmd.pos, expr: cmd.expr }; break;
|
|
172
|
+
case 'hide': if (cmd.who === 'all') s.sprites = {}; else delete s.sprites[cmd.who]; break;
|
|
173
|
+
case 'cg': s.cg = cmd.id; if (cmd.id) this.hooks.onUnlock?.('cg', cmd.id); break;
|
|
174
|
+
case 'scene': if (!this.replay) this.hooks.onUnlock?.('scene', cmd.id); break;
|
|
175
|
+
case 'endscene': if (this.replay) return { type: 'end', replay: true }; break;
|
|
176
|
+
case 'jump': this.jump(cmd.target); break;
|
|
177
|
+
case 'if': if (testCond(cmd.cond, this.vars)) this.jump(cmd.target); break;
|
|
178
|
+
case 'do': this.apply(cmd.effects); break;
|
|
179
|
+
case 'say':
|
|
180
|
+
if (cmd.who && cmd.expr && s.sprites[cmd.who]) s.sprites[cmd.who].expr = cmd.expr;
|
|
181
|
+
return { type: 'say', ...cmd };
|
|
182
|
+
case 'chapter': s.chapter = cmd.sub ? `${cmd.title} · ${cmd.sub}` : cmd.title; return { type: 'chapter', title: cmd.title, sub: cmd.sub };
|
|
183
|
+
case 'choice': {
|
|
184
|
+
const options = cmd.options.map((o, i) => ({ i, text: o.text, enabled: testCond(o.cond, this.vars) }));
|
|
185
|
+
this.pending = cmd.options;
|
|
186
|
+
return { type: 'choice', options };
|
|
187
|
+
}
|
|
188
|
+
case 'branch': {
|
|
189
|
+
const open = HEROINES.filter((h) => this.vars.aff[h] >= ROUTE_MIN);
|
|
190
|
+
if (!open.length) { this.jump('lonely'); break; }
|
|
191
|
+
this.pending = HEROINES.map((h) => ({ target: `${h}_route`, effects: [{ flag: `route_${h}` }] }));
|
|
192
|
+
return { type: 'choice', branch: true, options: HEROINES.map((h, i) => ({ i, who: h, enabled: open.includes(h) })) };
|
|
193
|
+
}
|
|
194
|
+
case 'ending': this.hooks.onUnlock?.('ending', cmd.id); return { type: 'ending', id: cmd.id };
|
|
195
|
+
case 'fate': return { type: 'fate', who: cmd.who };
|
|
196
|
+
case 'end': return { type: 'end' };
|
|
197
|
+
default: throw new Error(`未知指令类型:${cmd.t}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
throw new Error('剧本陷入死循环');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
choose(index) {
|
|
204
|
+
const option = this.pending?.[index];
|
|
205
|
+
if (!option) throw new Error(`没有这个选项:${index}`);
|
|
206
|
+
this.pending = null;
|
|
207
|
+
this.apply(option.effects);
|
|
208
|
+
this.jump(option.target);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 存档:停在阻塞指令上,读档后回退一格重新执行即可还原同一画面
|
|
212
|
+
snapshot() {
|
|
213
|
+
return JSON.parse(JSON.stringify({ label: this.label, ip: this.ip - 1, vars: this.vars, stage: this.stage }));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
restore(snap) {
|
|
217
|
+
this.reset();
|
|
218
|
+
Object.assign(this, JSON.parse(JSON.stringify({ label: snap.label, ip: snap.ip, vars: snap.vars, stage: snap.stage })));
|
|
219
|
+
if (!this.labels[this.label]) throw new Error('存档指向的剧本已不存在');
|
|
220
|
+
}
|
|
221
|
+
}
|