Initial import of HEROLL source and design documents
@@ -0,0 +1,23 @@
|
||||
# Heroll 玩法与商业化开发包
|
||||
|
||||
本目录把样本实装、运行时观察与新项目建议拆开管理。开发时以 specs 下的规则表为配置与验收基线;原作商品目录只用于理解系统边界,不应直接照搬价格、商品 ID、文案或资源。
|
||||
|
||||
## 文件导航
|
||||
|
||||
- specs/economy-and-monetization-rules.csv:商业化总规则、配置键、埋点和验收条件。
|
||||
- specs/feature-paywall-audit.csv:功能付费墙与玩家优势审计。
|
||||
- specs/economy-resource-map.csv:资源来源、消耗、付费暴露与平衡目标。
|
||||
- specs/analytics-events.csv:玩法、广告、购买与经济流水的最小埋点集合。
|
||||
- ../../analysis/gameplay-monetization/tables/monetization-products.csv:样本声明的 397 条商品配置,供审计使用。
|
||||
- ../../analysis/gameplay-monetization/tables/gacha-actions.csv:样本 23 种抽取动作与成本。
|
||||
- ../../analysis/gameplay-monetization/tables/gameplay-commerce-callflow.csv:由 IL2CPP 元数据恢复的玩法/商业化类与方法入口。
|
||||
- ../../docs/2026-08-13_gameplay-monetization-heroll-report.md:证据、结论、设计取舍及开发落地说明。
|
||||
|
||||
## 采用规则
|
||||
|
||||
1. 字段 actual_evidence 或 sample_implementation 描述 Heroll 样本,不等于推荐方案。
|
||||
2. 字段 product_recommendation 或 launch_rule 才是新项目的默认实现。
|
||||
3. 所有付费结算必须由服务端或平台回执确认,并使用幂等 transaction_id。
|
||||
4. 付费、广告、随机抽取不得改变已生成但尚未向玩家揭示的骰子结果。
|
||||
5. 若团队决定采用“高还原商业模型”,仍必须保留表格中的展示时机、频控、保底和可免费获取约束。
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
"event_name","trigger","required_fields","privacy_rule","validation"
|
||||
"run_start","服务端确认体力扣除并生成 run_id 后","run_id,mode,stage,loop,energy_before,energy_after,seed_version,boosters","不记录广告 ID 或账户明文","run_id 全局唯一;体力流水恰好一条"
|
||||
"dice_roll_committed","骰子结果写入局内状态时","run_id,turn,dice_a,dice_b,is_double,position_before,position_after,rng_counter","seed 仅记录版本和审计散列,不上传可反推账户的信息","每次移动前恰好一个 committed 事件;与存档可重放一致"
|
||||
"tile_landed","棋子完成落点处理时","run_id,turn,tile_id,tile_type,reward_ids,combat_id","不记录自由文本","位置与 dice_roll_committed 对得上"
|
||||
"battle_end","战斗状态进入 PostBattle 时","run_id,battle_id,result,duration_sec,hp_ratio,rewards,revive_count","不上传设备稳定标识","奖励流水总和与 reward_grant 一致"
|
||||
"run_end","结算或合法退出时","run_id,result,stage,loop,duration_sec,reason,rewards","reason 使用枚举","每个 run_id 最终恰好一个终态"
|
||||
"economy_flow","任何资源变化时","transaction_id,resource_id,delta,balance_after,source_or_sink,reason,run_id,sku_id","sku_id 可为空;不得含平台回执全文","同 transaction_id 幂等;balance_after 不为非法负数"
|
||||
"ad_offer","广告入口展示时","placement,run_id,eligible_reason,daily_count","不写广告网络用户标识","非 eligible 状态不得出现 offer"
|
||||
"ad_start","播放器确认开始时","placement,request_id,network,daily_count","network 为枚举,不记录 ad_unit_id","必须先有同 request_id 的成功加载状态"
|
||||
"ad_complete","服务端接受完成证明时","placement,request_id,reward_transaction_id,duration_sec","不记录广告收入明细到客户端分析流","一个 request_id 最多发放一次奖励"
|
||||
"ad_fail","加载或播放失败时","placement,request_id,error_category,retryable","错误详情脱敏","失败不增加已完成次数且不扣奖励资格"
|
||||
"store_view","商店或礼包页面曝光时","surface,catalog_version,visible_skus,session_impression_count","不记录平台支付账户信息","visible_skus 必须均为当前平台 available"
|
||||
"purchase_start","玩家在确认页主动提交时","sku_id,localized_price,currency,catalog_version,offer_context","价格来自平台;不上传支付凭证","必须存在先前 purchase_confirm_view"
|
||||
"platform_purchase_result","平台返回购买状态时","sku_id,transaction_id,status,error_category","回执只发安全校验服务,不进普通分析仓","重复平台回调不重复记 success"
|
||||
"receipt_verify","服务端校验回执时","sku_id,transaction_id,result,platform,latency_ms","严禁记录原始回执、签名和账户标识","成功后才能产生 entitlement_grant 或 reward_grant"
|
||||
"entitlement_grant","永久/订阅权益发放时","transaction_id,entitlement_id,start_at,end_at,restore","时间为 UTC","恢复购买不会创建重复权益"
|
||||
"gacha_pull_start","抽取消耗确认时","pull_id,pool_id,cost_resource,cost,pity_before,probability_version","不上传可预测随机种子","先成功扣费再请求服务端结果,失败可原子回滚"
|
||||
"gacha_pull_result","服务端结果落库时","pull_id,pool_id,item_id,rarity,is_duplicate,pity_after,conversion","不含 RNG 内部状态","与概率版本、保底规则一致;pull_id 幂等"
|
||||
"offer_impression","礼包卡片达到可见阈值时","offer_id,trigger,stage,time_since_failure_sec,impression_count","不使用敏感画像字段","post_failure_disabled 时 time_since_failure_sec 小于 1800 的事件数为 0"
|
||||
"pass_xp_gain","通行证经验变化时","season_id,source,delta,xp_after,free_cap,premium_cap","不记录任务自由文本","经验上限与配置一致;购买高级轨不重置既有经验"
|
||||
"afk_claim","领取离线收益时","offline_sec,capped_sec,multiplier,rewards,quick,ad_used","不记录前后台应用列表","服务器时间决定 capped_sec;相同领取窗口只发一次"
|
||||
|
||||
|
@@ -0,0 +1,18 @@
|
||||
"rule_id","system","sample_implementation","launch_rule","dev_config","required_telemetry","acceptance_criteria"
|
||||
"MON-001","骰子与局内随机","样本存在好地块、开局装备、回合点数、首领防御和监狱脱出等付费或广告道具","已经生成的骰子结果不可付费重掷或暗改;辅助道具只在开局前选用,单局每类最多 1 个,均有免费获取路径","commerce.allow_midroll_offer=false; run_booster.max_per_type=1","run_booster_offer; run_booster_use; dice_roll_committed","同一 seed 和输入在付费与非付费账户上得到相同骰子序列;掷骰动画开始后不出现购买弹窗"
|
||||
"MON-002","体力","样本每局消耗 5,10 分钟恢复 1,上限 40,VIP 增加 20 上限","失败重试不重复扣体力;首日给予至少 12 局体验量;付费只加速,不封锁主线结局","energy.cost_per_run=5; energy.regen_sec=600; energy.base_cap=40; energy.retry_grace_sec=120","energy_flow; run_start; run_abort; energy_cap_reached","断线或 120 秒内技术性退出可恢复扣除;自然恢复时间由服务端时间计算"
|
||||
"MON-003","奖励广告","样本商品表含 22 条 AD_WATCH 行,运行时奖励广告已加载","仅设置结算加倍、每日免费体力、AFK 加速、商店免费刷新四类自愿入口;启动版本每日完整奖励最多 6 次","ad.rewarded.daily_cap=6; ad.rewarded.cooldown_sec=30; ad.rewarded.placements=result_double,energy,afk,shop_refresh","ad_offer; ad_start; ad_complete; ad_fail; reward_grant","关闭广告不损失原基础奖励;失败时不扣次数;奖励发放幂等且可补发"
|
||||
"MON-004","插屏广告","样本含插屏管理与去广告商品,运行时插屏库存已加载","只允许在返回大厅后的自然断点展示;至少完成 3 局且间隔 10 分钟;失败结算、首局、购买前后和教学期禁用","ad.interstitial.min_runs=3; ad.interstitial.cooldown_sec=600; ad.interstitial.tutorial_disabled=true","interstitial_eligible; interstitial_show; interstitial_close; session_run_count","任何局内、战斗、骰子结果和失败页面均不弹插屏;频控跨重启保存"
|
||||
"MON-005","去广告","样本存在 1.99 美元配置的 remove_interstitial_package 与 forceAdRemove 道具","一次性去除强制插屏;奖励广告入口保留且完全自愿;商品描述必须明确不移除奖励广告","entitlement.remove_forced_ads=lifetime","purchase_start; purchase_success; entitlement_grant; ad_suppressed","跨设备恢复购买后立即生效;拥有权益的账户 100% 不触发插屏请求"
|
||||
"MON-006","抽卡与保底","样本 23 个抽取动作,其中 7 个广告、1 个免费;装备保底 20/50,坐骑保底 400","公开每档概率、重复品转换和保底规则;保底进度服务端保存且跨卡池规则明确;首次上线不设置超过 80 抽的大保底","gacha.probability_version=v1; gacha.pity_hard_cap<=80; gacha.server_authoritative=true","gacha_view; gacha_pull_start; gacha_pull_result; pity_change; duplicate_convert","概率表和客户端展示一致;模拟至少 1000 万抽后误差在统计置信区间内;断线不会重复扣款"
|
||||
"MON-007","通行证","样本 normal pass 含日/周/挑战任务、免费与高级经验上限,并声明多个 pass 商品","首发只保留免费轨和一个高级轨;赛季 28 天;高级奖励以外观、便利和可追赶资源为主,不售 premium-plus","pass.duration_days=28; pass.premium_tiers=1; pass.catchup_week=3","pass_xp_gain; pass_tier_unlock; pass_purchase; pass_reward_claim","赛季最后 7 天提供任务追赶;未领取奖励在赛季结束后自动邮件补发"
|
||||
"MON-008","每日购买奖励","样本表按购买点数 0/1/2/4/7/10/13 累进发奖","首发移除按当日消费额累进的额外奖励,改为非付费日常活跃里程碑","daily_spend_ladder.enabled=false; daily_activity_track.enabled=true","daily_activity_progress; daily_activity_claim","不出现要求继续消费才能保住进度的 UI;所有日常奖励可由玩法完成"
|
||||
"MON-009","阶段/失败触发礼包","样本声明大量 stage-clear 与 trigger 商品族","礼包只在成功通关后的大厅庆祝区展示,每个里程碑最多一次;失败后 30 分钟内不展示付费挽回弹窗","offer.post_failure_disabled=true; offer.stage_clear.max_impressions=1; offer.cooldown_sec=1800","offer_eligible; offer_impression; offer_dismiss; offer_purchase","连续失败不会提高价格、缩短倒计时或改变概率;关闭一次后本里程碑不再弹"
|
||||
"MON-010","角色/装备/宠物/遗物/坐骑","样本现金商品可直接投向装备、宠物、遗物、坐骑等战力系统,部分价格配置最高 119.99 美元","所有战斗属性均可稳定免费获得;付费只缩短时间;同赛季付费加速形成的有效战力差控制在 15% 内","power.paid_advantage_cap=0.15; power.exclusive_paid_stats=false","power_snapshot; item_source; progression_velocity; payer_cohort","无现金专属战斗词条;免费玩家在一个赛季内可达成全部关键玩法门槛"
|
||||
"MON-011","价格梯度","样本现金配置 343 条,0.99–119.99 美元,中位数 8.99 美元","首发 SKU 不超过 12 个;建议档位 0.99/2.99/4.99/9.99/19.99/49.99,暂不启用 99.99 或 119.99","store.launch_sku_cap=12; store.price_ceiling_usd=49.99","store_view; sku_impression; purchase_start; purchase_success; purchase_fail","商店首屏不超过 6 个付费 CTA;价格从平台本地化接口读取,不硬编码"
|
||||
"MON-012","订阅/VIP","样本声明 subscription/VIP 商品,并可增加体力上限","首发最多一个月卡;权益为每日货币、去插屏与便利,不提供现金专属关卡、伤害或掉率","subscription.max_plans=1; subscription.combat_bonus=0","subscription_view; subscribe; renew; cancel; entitlement_expire","过期后只停止未来权益,不回收已领取资产;取消路径在两次点击内可达"
|
||||
"MON-013","猪猪存钱罐与累积价值","元数据存在 PigBankView 和购买入口","首发关闭;若后续开启,只累积展示价值,不截留玩家本应获得的基础掉落,且无损失倒计时","piggy_bank.enabled=false","piggy_accumulate; piggy_view; piggy_purchase","关闭功能不改变基础掉落;不得用即将清空的威胁推动购买"
|
||||
"MON-014","AFK 收益","样本离线收益最长 1440 分钟,快速 AFK 360 分钟,每日 3 次,消耗 15 体力,广告快速 AFK 每日 1 次","保留 24 小时上限;广告加速每日 1 次;快速收益不可同时收取体力和现金","afk.max_min=1440; afk.quick_min=360; afk.ad_daily_cap=1; afk.cash_cost_enabled=false","afk_open; afk_claim; afk_quick_claim; afk_multiplier","服务器重算离线时长;修改本机时间不能增加收益;显示领取前的确定数值"
|
||||
"MON-015","交易与回执","样本使用 Unity IAP/Google Play Billing,当前 Google Play Games 会话有多项商品 unavailable","商品可用性与目录版本分离;服务端校验回执、发货幂等、支持恢复购买与延迟订单","purchase.receipt_server_verify=true; purchase.idempotency_key=platform_transaction_id","purchase_start; platform_result; receipt_verify; grant_result; restore_purchase","重复回调只发一次货;网络恢复后可重试;不可用商品隐藏而不是显示可点击空壳"
|
||||
"MON-016","随机与价格披露","样本资源表可恢复保底,但客户端是否完整展示无法由本轮确认","所有概率、保底剩余、价格、有效期、内容物和重复品规则在确认付款前可见","compliance.prepurchase_disclosure=true","disclosure_open; purchase_confirm_view; gacha_probability_view","QA 截图可证明每个随机付费入口在付款前两步内可达概率页"
|
||||
|
||||
|
@@ -0,0 +1,14 @@
|
||||
"resource_id","resource","sample_source","sample_sink","paid_exposure","launch_balance_target","hard_guardrail"
|
||||
"RES-001","金币 gold","战斗、棋盘、AFK、日常与商品包","日常商店、成长升级与刷新","现金包和礼包可间接提供","保持 3–5 个持续消耗口;中期日净产出约为关键升级需求的 20–30%","不允许因付费跳过全部核心成长;大额流水必须带 source/sink reason"
|
||||
"RES-002","钻石 dia","成就、日常、活动、少量局内上限与现金购买","抽卡、体力、局内辅助和商店","核心付费货币;样本存在每日局内获得上限 100","免费周收入至少覆盖 10 抽或等价目标;局内可得上限在 UI 明示","付费钻与赠送钻分账;退款不产生负库存死锁"
|
||||
"RES-003","体力 energy","自然恢复、广告、活动、钻石与包","开始主线/快速 AFK;样本每局 5","付费可补充但不作为唯一玩法入口","基础上限 40;10 分钟/1;技术退出宽限;睡眠 8 小时后至少可玩 8 局"
|
||||
"RES-004","遗物灵魂 relic_soul","重复品转换、玩法奖励与包","遗物/高级遗物抽取或定向兑换","可被礼包加速","设置确定性商店,连续 14 天活跃可定向一个关键遗物","重复转换价值固定;保底和货币不可赛季清零"
|
||||
"RES-005","药水战斗票 potionBattleTicket","自然/活动/商店;样本上限 10、每小时恢复 1","药水战斗玩法","可由包补充","每日自然产出覆盖基础任务,活动奖励提供额外次数","到上限提醒;恢复计时由服务端维护"
|
||||
"RES-006","突袭票 raid_ticket","日常、活动、广告与包","Raid 入场","广告和现金包可增加","免费玩家每日可完成全部基础 Raid 奖励门槛","不同时收取体力和票券;赛季任务不要求付费次数"
|
||||
"RES-007","突袭币 raid_coin","Raid 结算、任务","Raid 商店定向兑换","付费只通过额外次数间接加速","每周可定向至少一个成长目标","商店轮换前展示剩余时间;核心目标不只随机出现"
|
||||
"RES-008","命运币 destiny_coin","命运玩法、活动","命运成长/商店","样本商品族可提供","与赛季任务绑定但保留稳定免费来源","不售现金专属节点;赛季结束提供兑换缓冲期"
|
||||
"RES-009","坐骑活动票 mount_event_ticket","活动、任务、包","坐骑抽取;样本坐骑保底 400","现金礼包可加速","若保留随机抽取,硬保底下调至不超过 80;提供定向碎片","概率、剩余保底和过期规则始终可见"
|
||||
"RES-010","宠物抽卡券 petGachaTicket","活动、任务、通行证与包","宠物抽取","现金商品密集","免费月收入覆盖一个明确保底周期的至少 50%,其余可由定向兑换补齐","无现金专属宠物战斗技能;重复品有保底价值"
|
||||
"RES-011","局内辅助道具 600–604","任务、成就、免费商店;样本也支持钻石/广告/商品","开局前消耗以提供防御、脱出、奖励或成长便利","样本有直接付费与广告入口","作为可选构筑资源,不成为默认难度补丁","不在已知坏结果后售卖;排行榜模式禁用;每类单局最多 1"
|
||||
"RES-012","通行证经验 pass_xp","日/周/挑战任务、循环通关;样本普通玩法经验上限 200、高级 400","解锁免费/高级轨奖励","购买高级轨提升可领范围","活跃玩家 70% 赛季时长可完成;最后一周可追赶","不得通过购买经验成为唯一完成方式;赛季结束自动补发已解锁项"
|
||||
|
||||
|
@@ -0,0 +1,16 @@
|
||||
"audit_id","feature","sample_evidence","payer_advantage","design_risk","launch_decision","guardrail"
|
||||
"PAY-001","首领防御/闪避","商品与道具表存在首领防御类消耗品","直接降低局内失败概率","高:将挑战结果与付费绑定","仅作为可游玩获得的开局道具,首发不直接现金售卖","单局最多 1 个;使用前显示效果;不在失败瞬间推销"
|
||||
"PAY-002","监狱脱出","商品表存在监狱脱出道具","跳过负面棋盘事件","高:付费消除随机惩罚","改为任务、成就和免费商店货币来源","触发监狱后不得弹现金购买;可使用既有库存"
|
||||
"PAY-003","好地块保证","钻石、广告与商品配置存在 good tile 相关条目","改变落点收益分布","高:可能破坏骰子公平感","只允许开局前选择一次性护符,效果改为提升下一次奖励而非改写落点","不得重算已提交骰子;效果记录进 run seed 日志"
|
||||
"PAY-004","开局装备","商品配置存在 start gear 相关条目","缩短成型时间并影响战力","中高","作为赛前构筑槽位;可通过局外升级解锁,不现金独占","付费获取速度优势不超过一个赛季 15% 有效战力差"
|
||||
"PAY-005","回合/循环点数","商品配置存在 loop point 相关条目","加快进度、提前触发成长","中高","转为周常可得的便利消耗品,不在排行榜模式生效","排行榜、首通与竞速模式自动禁用"
|
||||
"PAY-006","体力购买","样本可用钻石购买 15/50 体力并有广告补充","增加可玩次数","中","保留自然恢复、活动补给和每日广告;失败重试宽限","不销售永久体力伤害;活动门票与主线体力不双重收费"
|
||||
"PAY-007","装备/宠物/遗物/坐骑抽取","样本含多条抽取及现金加速商品","长期战力与收集进度提升","高","可保留但所有战力有免费确定性路径;现金主要售外观与时间","公布概率/保底;重复品可兑换目标;无付费专属属性"
|
||||
"PAY-008","通行证","样本含 normal、raid、mount 等 pass 商品族","资源密度与收集速度提升","中高:多 pass 叠加造成订阅疲劳","首发合并为一个 28 天通行证","一个高级轨;奖励可追赶;不要求购买第二张玩法通行证"
|
||||
"PAY-009","订阅/VIP","样本含 subscription/VIP,VIP 增加体力上限","持续便利与资源收益","中","首发仅一个月卡","无伤害/暴击/掉率加成;可随时取消;权益写清"
|
||||
"PAY-010","阶段通关礼包","商品目录存在大量 stage clear/trigger 家族","里程碑时提供高价值加速","中高:高频弹窗与锚定定价","成功后大厅卡片展示,不自动遮挡结算","每里程碑一次曝光;无损失倒计时;不在失败后出现"
|
||||
"PAY-011","每日购买奖励","配置以 0/1/2/4/7/10/13 购买点累进","刺激当日追加消费","高:诱发沉没成本与冲动消费","不采用,改为每日活跃奖励","进度只由玩法行为积累;不显示消费缺口"
|
||||
"PAY-012","插屏与去广告","样本含插屏管理、频控入口和去广告商品","付费改善体验但不增加战力","中","允许自然断点插屏并提供一次性去插屏","教学期禁用;3 局/10 分钟频控;奖励广告不受影响"
|
||||
"PAY-013","AFK 快速领取","样本可消耗体力并可看广告快速领取","缩短等待时间","低中","保留每日一次广告加速,不现金收费","预览确定收益;不同时消耗体力和现金;本机改时无效"
|
||||
"PAY-014","复活","BoardController 元数据存在 UseRevive,实际价格与触发规则未由方法体确认","可能直接延长单局并保留构筑","高","每日首次可用免费资源;付费币复活只限非排行榜 PvE 且每局一次","失败后只显示已有资源复活,不弹现金包;复活不改变已结算掉落"
|
||||
|
||||
|
@@ -0,0 +1,843 @@
|
||||
# HEROLL 本地 H5 玩法验证 GDD
|
||||
|
||||
> **仅限本机内部验证、未取得发行授权、不得公开或分发。**
|
||||
>
|
||||
> 本文档只描述 **HEROLL**。`art-bible/`、`design/gameplay-monetization/`、`outputs/iaa/` 以及任何 Dice Lords 资料均为非规范历史资料,不得作为本验证版的设计、数值或美术依据。
|
||||
>
|
||||
> 复用提取的 HEROLL 图像、动画、音效与文本并不消除版权风险。数字化复制仍受著作权法保护,合理使用例外具有条件限制;本包不得用于发行、公开演示或向第三方分发。[《中华人民共和国著作权法》](https://www.npc.gov.cn/c2/c30834/202011/t20201119_308796.html)
|
||||
|
||||
文档版本:1.1-RC(关卡刷新逆向纠偏)
|
||||
目标引擎:Cocos Creator 3.x / TypeScript 5.x
|
||||
目标平台:本地浏览器、竖屏 H5、简体中文
|
||||
权威目录:`design/gameplay-validation/`
|
||||
配置索引:[specs/README.md](./specs/README.md)
|
||||
|
||||
关卡楼层、增益地块与 Boss 压力的权威逆向覆盖层:[HEROLL 2.0.1 关卡楼层分布与刷新规则逆向报告](../../analysis/stage-floor-rules/2026-08-31_逆向-HEROLL关卡楼层分布与刷新规则-report.md)。该报告优先于本文件旧版本中的“每层固定 2 个有益地块”“第 1 关运行 Boss Gauge”“六类事件保证袋”等验证版假设。
|
||||
|
||||
## 1. 文档状态与签收边界
|
||||
|
||||
本 GDD 可直接用于搭建完整的 10 层玩法验证包。产品循环、界面、数据结构、装备、祝福、药水、商店、事件、存档和 QA 契约均已锁定;当前构建不开放广告或即时增强入口。
|
||||
|
||||
战斗公式采用两层契约:
|
||||
|
||||
1. **验证实现公式**:本文 §5.4 给出的确定性公式,来源标记为“验证版新增”,开发可立即实现。
|
||||
2. **HEROLL 拟合公式**:必须用无局外加成的实机样本对验证公式校正;伤害中位误差 ≤5%,战斗时长误差 ≤10% 后,才可把 `formula-fit.csv` 的门禁改为 `PASS`。在此之前不得宣称“原作公式已还原”。
|
||||
|
||||
当前桌面实机已确认棋盘、宝箱、铁匠铺、自动战斗与局内点数表现,但测试账号带有局外属性,不能作为裸装公式样本。最小补录矩阵已经写入 [formula-fit.csv](./specs/formula-fit.csv),这是拟合签收所需的全部输入,不是隐藏的设计项。
|
||||
|
||||
### 1.1 规范优先级
|
||||
|
||||
发生冲突时按以下顺序执行:
|
||||
|
||||
1. 本 GDD、上方链接的专项逆向覆盖层与同目录 `specs/*.csv`;专项报告在其命名领域内优先。
|
||||
2. HEROLL 提取源码表与 IL2CPP 元数据。
|
||||
3. 无局外加成的 HEROLL 实机黑盒结果。
|
||||
4. 用户提供的视频、截图说明和当前桌面试玩观察。
|
||||
5. 其他历史文档;仅作检索线索,不具规范效力。
|
||||
|
||||
同一优先级内,CSV 数值优先于正文示例。所有新增规则必须登记来源标签。
|
||||
|
||||
### 1.2 来源标签
|
||||
|
||||
| 标签 | 含义 | 可否直接实现 |
|
||||
|---|---|---|
|
||||
| `[源码确认]` | 来自提取 CSV、元数据字段或可复现调用边界 | 是 |
|
||||
| `[实机观察]` | 从 HEROLL 视频、截图或桌面运行直接观察 | 是;仅限观察到的表现 |
|
||||
| `[黑盒拟合]` | 由受控样本拟合且通过误差门槛 | 是 |
|
||||
| `[验证版新增]` | 为本地玩法验证明确新增或裁剪 | 是;不得冒充原作 |
|
||||
|
||||
完整 Evidence → Finding → Design 映射见 [evidence-map.csv](./specs/evidence-map.csv)。
|
||||
|
||||
## 2. 产品目标与范围
|
||||
|
||||
### 2.1 一句话定义
|
||||
|
||||
玩家手动投掷两枚公平 D6,在不可控移动中利用装备、祝福、药水、商店和事件构筑解决方案,连续完成 10 层并击败 `boss_0`;单局目标 10–12 分钟。
|
||||
|
||||
### 2.2 验证假设
|
||||
|
||||
| 假设 | 成功信号 | 失败信号 |
|
||||
|---|---|---|
|
||||
| 公平双骰能制造可读的局势变化 | 玩家接受坏落点并转向构筑补救 | 玩家认为结果被暗改或无法理解 |
|
||||
| 6–8 次装备查看足以形成构筑方向 | 第 6 层前出现攻击、攻速/暴击或生存路线 | 装备选择只看品质,不看词条 |
|
||||
| 10 层能形成完整起承转合 | 前 3 层学习、中 4 层成型、后 3 层压力、Boss 验收 | 过程重复或 Boss 与构筑无关 |
|
||||
| 固定棋盘和原作地块图标能让移动与落点一眼可读 | 玩家能预判顺时针路径并辨认战斗、商店和事件格 | 玩家误以为棋盘旋转,或只能靠颜色猜地块 |
|
||||
|
||||
### 2.3 包含内容
|
||||
|
||||
- 一个 Grass 主题、Normal、`stage=0` 的 10 层关卡。
|
||||
- 两枚公平D6、40个独立实际地块、逐层推进和Boss。
|
||||
- 自动战斗、手动药水、四类普通敌人与 `boss_0`。
|
||||
- 40 件局内装备、13 类装备属性、五档品质。
|
||||
- 31 个启用源码祝福和 1 个“第三骰替换”祝福。
|
||||
- 药水容量、治疗等级与 9 个启用药水强化。
|
||||
- 商人、97 条商品、12 种可安装事件地块。
|
||||
- 固定棋盘、顺时针玩家移动,以及战斗、商店和 12 类安装事件的原作图标。
|
||||
- 本地安全点存档、确定性随机、隐藏调试台。
|
||||
- HUD、棋盘、战斗、构筑、事件、结算与功能音效。
|
||||
|
||||
### 2.4 明确排除
|
||||
|
||||
- 账号、登录、服务器、联网依赖和云存档。
|
||||
- 局外成长、英雄养成、宠物、坐骑、遗物、任务、成就、AFK。
|
||||
- 体力、抽卡、钻石、IAP、订阅、通行证、真实广告 SDK、插屏、横幅,以及任何广告/即时增强/广告复活按钮。
|
||||
- 剧情、对白、开场、结局、背景音乐。
|
||||
- 完整 IAA GDD 与 MVP GDD;待本包完成 10 局现场签收后另行抽象。
|
||||
|
||||
## 3. 单局结构
|
||||
|
||||
### 3.1 开局配置
|
||||
|
||||
| 配置 | 值 | 来源 |
|
||||
|---|---:|---|
|
||||
| `stage` / `mode` | `0` / `Normal` | `[源码确认]` |
|
||||
| 场景主题 | `Grass` | `[源码确认]` |
|
||||
| 层数 | 10 | `[源码确认] stageinfo.bossSpawnRuleValue` |
|
||||
| Boss | `boss_0` / `Board_Boss001` | `[源码确认]` |
|
||||
| Boss 技能 | `skill_0` | `[源码确认]` |
|
||||
| Boss 攻击冷却 | 7 秒 | `[源码确认]` |
|
||||
| 玩家装备 | 四槽全空 | `[验证版新增]` |
|
||||
| 玩家丹药 | 容量 1、基础恢复 50 HP | `[native 公式确认] PotionController.GetPotionFinalHeal` |
|
||||
| 初始局内点数 | 0 | `[验证版新增]` |
|
||||
| 普通入口速度 | 固定 1× | `[验证版新增]` |
|
||||
|
||||
### 3.2 核心循环
|
||||
|
||||
1. 创建随机 `runSeed`,初始化四条 RNG 流与空构筑。
|
||||
2. 运行原作初始化链:初始地块、全部角落地块、玩家有益地块牌组 8 次抽取,进入 `AwaitRoll`。
|
||||
3. 玩家手动掷两枚 D6;若持有第三骰祝福,再选择替换 A、替换 B或放弃。
|
||||
4. 棋盘保持固定;玩家按最终点数从底部起点沿左侧、顶部、右侧顺时针逐格移动。每1点对应1个实际地块,只在最终落点结算。
|
||||
5. 仅当 `bossSpawnRule=DiceGauge` 时推进 Boss 压力循环;本验证关 `stage=0` 为 `Default`,不运行压力 Gauge。
|
||||
6. 原子结算落点:战斗、装备、祝福、商店、事件或空地。
|
||||
7. 完整结算后写安全点,返回掷骰。
|
||||
8. 越过起点完成一圈:层数 +1、恢复 30% 最大生命,并按 `AddRandomGoodTiles(loopIndex+2)` 从玩家牌组执行安装尝试,再按 `enemyTilesPerLoop` 增加敌人。
|
||||
9. 第 10 层结束后进入 `boss_0`;胜利进入结算,死亡且无可用复活则失败。
|
||||
|
||||
### 3.3 局状态机
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> RunInit
|
||||
RunInit --> FloorSetup
|
||||
FloorSetup --> AwaitRoll
|
||||
AwaitRoll --> DiceResolve: 手动掷骰
|
||||
DiceResolve --> ThirdDieChoice: 持有第三骰祝福
|
||||
DiceResolve --> Moving: 无第三骰
|
||||
ThirdDieChoice --> Moving: 替换A/替换B/放弃
|
||||
Moving --> BossGaugeResolve: DiceGauge关卡行动完成
|
||||
Moving --> TileResolve: Default关卡行动完成
|
||||
BossGaugeResolve --> BossBoardAction: Gauge 满值
|
||||
BossGaugeResolve --> TileResolve: Gauge 未满
|
||||
BossBoardAction --> TileResolve: Boss 行动演出完成
|
||||
TileResolve --> Battle
|
||||
TileResolve --> Event
|
||||
TileResolve --> Shop
|
||||
TileResolve --> RewardChoice
|
||||
Battle --> ReviveResolve: 玩家生命归零
|
||||
ReviveResolve --> Battle: 源码复生祝福成功
|
||||
ReviveResolve --> RunFailed: 祝福不可用或结算后仍死亡
|
||||
Battle --> Checkpoint: 胜利
|
||||
Event --> Checkpoint
|
||||
Shop --> Checkpoint
|
||||
RewardChoice --> Checkpoint
|
||||
Checkpoint --> FloorTransition: 越过起点
|
||||
Checkpoint --> AwaitRoll: 未过层
|
||||
FloorTransition --> BossBattle: 完成第10层
|
||||
FloorTransition --> FloorSetup: 继续
|
||||
BossBattle --> RunClear: 胜利
|
||||
BossBattle --> ReviveResolve: 死亡
|
||||
RunClear --> [*]
|
||||
RunFailed --> [*]
|
||||
```
|
||||
|
||||
## 4. 骰子、棋盘与层推进
|
||||
|
||||
### 4.1 公平骰子
|
||||
|
||||
- `dieA` 与 `dieB` 分别调用 `diceRng.nextInt(1, 6)`,互相独立。
|
||||
- 36 个有序结果严格等权;移动值为 `dieA + dieB`。
|
||||
- 对子触发“机缘”,但不得改变基础骰面分布:随机令本局伤害、最大生命或防御之一永久 +10%,可叠加;并令下一次落入战斗格时开场造成 100% 当前攻击的先攻伤害。下一落点不是战斗格则只清除先攻,永久属性仍保留。
|
||||
- 禁止暗改、保底、重掷、锁骰、提高期望值或根据 DDA 调整骰面。
|
||||
- 每次掷骰记录 `rollIndex`、两枚原始结果、第三骰结果、最终结果与 RNG 索引。
|
||||
- 同一 `runSeed + RngStreamState + 输入序列` 必须得到相同结果。
|
||||
|
||||
第三骰祝福的顺序固定为:先生成 A、B → 再独立生成 C → 弹出三按钮 → 选择替换 → 计算移动值。放弃 C 不回滚 RNG。
|
||||
|
||||
### 4.2 40格实际棋盘
|
||||
|
||||
- 棋盘固定40个独立实际落点:`Start=0`、`Tile01–Tile39`,不存在只用于表现但不参与移动的装饰格。
|
||||
- 棋盘本体和镜头不因玩家位置旋转。底部为`Start`,索引递增方向固定为底部→左侧→顶部→右侧,即屏幕顺时针。
|
||||
- 玩家位置用`tileIndex: 0..39`表示;每1点执行一次`(index + 1) % 40`。例如从起点ROLL 10,最终停在`Tile10`,移动动画必须恰好播放10次逐格位移。
|
||||
- 从39移动到0即完成一圈;每层只完成一圈,因此一次骰子最多触发一次过层。
|
||||
- 每个功能格必须使用HEROLL原作像素图标:普通战斗格使用`tile_battle`和`icon_NormalEnemy_1..4`;图标骷髅数量严格等于实际敌人数;商店使用`icon_start_shop`;12类事件使用对应`Installtile_*`。
|
||||
- 事件、商店、普通战斗或小Boss完整结算后,该格立即变为`EMPTY/荒地`。荒地跨层原位保留,只有Boss污染或玩家新增地块可以覆盖;禁止下一层整盘重新生成。
|
||||
- 过层有益地块按 `loopIndex+2` 执行元数据抽取/安装尝试;尝试数不等于保证可见新增数。当前静态证据不能证明“只能覆盖荒地”这一完整优先级,原作实现必须记录候选与失败原因,禁止把旧验证版保护表冒充源码规则。
|
||||
|
||||
| 地块 | 数据 | 停留效果 | 结算后 |
|
||||
|---|---|---|---|
|
||||
| 起点 | `START` | 越过时升层并恢复30%最大生命 | 永久保留 |
|
||||
| 荒地 | `EMPTY` | 无效果 | 跨层保留 |
|
||||
| 1–4怪物 | `BATTLE.enemyCount=1..4` | 进入对应数量的自动战斗 | 胜利后变荒地 |
|
||||
| 小Boss | `MINIBOSS.enemyCount=1..4` | 怪物数按60%/25%/10%/5%生成,敌方攻击×1.12、生命×1.18 | 胜利后变荒地 |
|
||||
| 污染Boss | `INFECTED_BOSS` | 同小Boss战;若过层时仍未消灭,则普通怪物格污染+1 | 胜利后变荒地;未消灭最多生效3次 |
|
||||
| 商店 | `MERCHANT` | 3固定丹药商品+3随机商品 | 离开后变荒地 |
|
||||
| 12类事件 | `EVENT` | 按§9和`events.csv`结算 | 完整结算或主动离开后变荒地 |
|
||||
|
||||
### 4.3 Boss Gauge / 倒计时循环(仅 `DiceGauge` 关卡)
|
||||
|
||||
`stage=0` 的规则为 `Default=10`,本节在第 1 关普通验证流程中完全禁用;它仅用于复现后续 `DiceGauge` 关卡。
|
||||
|
||||
- `BUILDUP`:新局从 `0/2` 开始。玩家每完成一次 ROLL 计 1 轮;第一次显示 `1/2`,第二次结束后不立即执行 Boss 行动,而是进入倒计时。
|
||||
- `COUNTDOWN`:从 `4` 开始;玩家之后每完成一次 ROLL 依次显示 `3、2、1、0`。第 4 次 ROLL 的移动演出结束后,Boss 才执行行动。
|
||||
- `BOSS ACTION`:从符合当前候选谓词的非祝福地块中无放回选择 4 个,逐个转换为怪物战斗地块;转换完成后回到 `BUILDUP 0/2`,持续循环。
|
||||
- 祝福地块是实机确认的排除项。起点及其他模式专属保护项、合法候选不足 4 个时的降级策略尚未由静态代码闭环;实现时必须配置化并记录日志,不得标为源码确认。
|
||||
- 该机制只负责棋盘回合节拍,**不是**“玩家攻击 Boss 若干次后 Boss 才攻击一次”。进入战斗后,双方仍按 §5 的攻击间隔与技能冷却独立行动。
|
||||
- 表现顺序固定为:骰子演出 → 逐格移动/落地 → Boss 计数变化 → 满倒计时警示/棋盘震动 → 4 格逐个感染 → 才允许落点事件或战斗界面出现。所有环节均无跳过按钮,禁止直接硬切。
|
||||
- 逆向层仅确认 `DiceGauge=50` 是原作增量、实时值范围 0..100 且 `MoveTile` 调用 `TrySpawnBossTilesByGauge`;本节的 `2 + 4 + 转换 4 格` 时序采用负责人实机确认,优先于旧验证版的“+50 后立即行动”解释。
|
||||
|
||||
污染Boss跨层规则:若棋盘上仍有`INFECTED_BOSS`且剩余次数大于0,则进入新层时所有普通`BATTLE`地块的`enemyCount +1`(上限4)、`pollutionLevel +1`(上限3),然后该Boss剩余次数减1。污染只改变普通怪物地块,不改变骰面、事件、小Boss或最终Boss。
|
||||
|
||||
### 4.4 十层节奏
|
||||
|
||||
逐层敌方基础数值见 [floor-balance.csv](./specs/floor-balance.csv)。层号 UI 使用 1–10,源码 `loop` 使用 0–9。
|
||||
|
||||
| 层段 | 设计目的 | 强制内容 |
|
||||
|---|---|---|
|
||||
| 1–2 | 学会掷骰、战斗、装备比较 | 首次掷骰与首次换装轻引导 |
|
||||
| 3 | 第一次经济决策 | 商人出现 |
|
||||
| 4–5 | 形成第一条构筑方向 | 至少已安装 3 种核心事件 |
|
||||
| 6 | 中盘修正 | 商人出现、敌人明显增强 |
|
||||
| 7–8 | 检查生存与伤害平衡 | 保证六类核心事件已全部生成 |
|
||||
| 9 | Boss 前最后调整 | 商人出现 |
|
||||
| 10 | 构筑压力测试 | 完成后立即进入 Boss |
|
||||
|
||||
每次过层按以下原子顺序执行:
|
||||
|
||||
1. 结算当前地块并写 `beforeFloorTransition` 安全点。
|
||||
2. `floor += 1`。
|
||||
3. `hp = min(maxHp, hp + round(maxHp * 0.30))`。
|
||||
4. 从玩家当前有益地块洗牌牌组抽取 `loopIndex+2` 次并逐次尝试安装;同一牌组轮内无放回,耗尽后重洗。
|
||||
5. 应用 DDA 层状态并写 `afterFloorTransition` 安全点。
|
||||
6. 第 10 层已结束则进入 Boss,否则恢复掷骰输入。
|
||||
|
||||
## 5. 角色、敌人与战斗
|
||||
|
||||
### 5.1 玩家裸装属性
|
||||
|
||||
| 属性 | 初始值 | 语义 |
|
||||
|---|---:|---|
|
||||
| `range` | 5 | 目标搜索距离 |
|
||||
| `moveSpeed` | 40 | 角色移动表现值 |
|
||||
| `attackPower` | 10 | 基础攻击 |
|
||||
| `attackSpeed` | 100 | 100 表示每秒 1 次基础攻击 |
|
||||
| `health/maxHealth` | 150 | 当前/最大生命 |
|
||||
| `defense` | 0 | 减伤参数 |
|
||||
| `lifeSteal` | 0 | 伤害吸血百分比 |
|
||||
| `evasion` | 0 | 闪避参数 |
|
||||
| `aim` | 5 | 命中参数 |
|
||||
|
||||
以上全部为 `[源码确认] playerdefaultstat.csv`。验证版不得读取账号、装备、宠物、遗物或其他局外属性。
|
||||
|
||||
### 5.2 敌人角色
|
||||
|
||||
| 角色 | `EAttackType` | 行为规则 | 可读性要求 |
|
||||
|---|---|---|---|
|
||||
| 剑士 | `SWORD` | 接近后近战攻击;作为基础压力 | 近战挥砍与命中帧明确 |
|
||||
| 直射 | `STRAIGHT` | 保持射程,直线弹道命中 | 弹道方向与命中点一致 |
|
||||
| 抛射 | `CURVED` | 高伤低频弧线弹道 | 地面落点预告不少于 300ms |
|
||||
| 冲锋 | `RUSH` | 短前摇后直线突进 | 前摇、冲锋线、碰撞结束分离 |
|
||||
|
||||
`group_10` 源码角色配额为 `SWORD,SWORD,STRAIGHT,STRAIGHT`;为了完整验证四种敌人,本验证版在每层按 `enemyRng` 从四种角色中生成 2 个,保证十层内每种至少出现两次。这个角色覆盖规则为 `[验证版新增]`,敌人属性仍逐层读取源码 `board.csv`。
|
||||
|
||||
同角色的 prefab 变体及倍率来自 `monsterinfo.csv`。普通敌人生成后属性为:
|
||||
|
||||
```text
|
||||
attack = floor.attackPower × prefab.attackMultiplier × dda.attackMultiplier
|
||||
maxHp = floor.health × prefab.healthMultiplier × dda.healthMultiplier
|
||||
defense = floor.defense × prefab.defenseMultiplier
|
||||
attackInterval = floor.attackSpeed × prefab.attackSpeedMultiplier
|
||||
evasion = floor.evasion × prefab.evasionMultiplier
|
||||
aim = floor.aim × prefab.aimMultiplier
|
||||
lifeSteal = floor.lifeSteal × prefab.lifeStealMultiplier
|
||||
```
|
||||
|
||||
`boss_0` 使用第 10 层基础值和源码倍率:攻击 ×2、生命 ×4、防御 ×1、攻速间隔 ×1、闪避 ×1、命中 ×1、吸血 ×0;技能为 `skill_0`,主动技能冷却 7 秒。
|
||||
|
||||
### 5.3 战斗控制与阶段
|
||||
|
||||
战斗固定为 `Prepare → Spawn → PreBattle → Fight → PostBattle`:
|
||||
|
||||
- `Prepare`:冻结棋盘输入,创建 `BattleSnapshot`,加载敌人与背景。
|
||||
- `Spawn`:按角色槽位生成实体;逻辑已生成,表现可跳过。
|
||||
- `PreBattle`:结算开战祝福、护盾与先手效果。
|
||||
- `Fight`:双方自动索敌、移动、普攻和技能。玩家唯一主动输入是药水。
|
||||
- `PostBattle`:停止计时器,按一次事务生成掉落、祝福触发和点数,再写安全点。
|
||||
|
||||
普通入口只显示 1×速度;隐藏调试台可切换 1×/4×。暂停只冻结逻辑时钟,不改变 RNG。
|
||||
|
||||
### 5.4 验证实现公式与拟合门禁
|
||||
|
||||
以下公式是可立即编码的 `[验证版新增]` 基线,绝不标记为 HEROLL 原作还原:
|
||||
|
||||
```text
|
||||
hitChance = clamp(0.05, 0.95, (100 + attacker.aim - defender.evasion) / 100)
|
||||
isHit = combatRng.nextFloat01() < hitChance
|
||||
variance = combatRng.uniform(0.85, 1.15) // EnemyDamgeRange = 15
|
||||
mitigation = 100 / (100 + max(0, defender.defense))
|
||||
normalDamage = max(1, round(attacker.attackPower × mitigation × variance))
|
||||
criticalDamage = round(normalDamage × 1.50) // BaseCritical = 150
|
||||
criticalRoll = combatRng.nextInt(0, 100) // 含 0 与 100,共 101 个整数
|
||||
critical = criticalRate > criticalRoll
|
||||
totalDamage = round((critical ? criticalDamage : normalDamage) × (1 + totalDamagePct/100))
|
||||
finalDamage = max(1, totalDamage + trueDamage)
|
||||
lifeStealHeal = floor(finalDamage × lifeStealPct / 100)
|
||||
playerAttackInterval = max(0.15, 1 / (attackSpeed / 100))
|
||||
enemyAttackInterval = max(0.15, boardAttackSpeed × prefabAttackSpeedMultiplier)
|
||||
```
|
||||
|
||||
- 暴击判定恢复为 `criticalRate > RandomInt[0,100]`;不再额外钳制为 5%–95%,裸装为 0。
|
||||
- 闪避发生在暴击前;未命中不触发吸血、反击和命中祝福。
|
||||
- 玩家面板保留“反击”属性;玩家受击后按反击率独立判定,成功则立即对该攻击者造成一次当前攻击力伤害。
|
||||
- 护盾先扣,生命后扣;真伤仍受命中但无视防御。
|
||||
- 同一逻辑 tick 内的伤害按 `sourceEntityId` 排序,避免帧率改变结果。
|
||||
- 生命、护盾和点数均为整数;防御、攻速和百分比保留 2 位小数。
|
||||
|
||||
拟合时只允许修改 [formula-fit.csv](./specs/formula-fit.csv) 中声明的函数和常量,不得改 `board.csv` 行值。门禁统计使用至少 30 次普攻命中样本和 10 场完整战斗:
|
||||
|
||||
- 每类伤害样本的预测/实测中位绝对百分比误差 ≤5%。
|
||||
- 每个层段战斗时长中位绝对百分比误差 ≤10%。
|
||||
- 命中、闪避、暴击和吸血的 Wilson 95% 区间必须覆盖预测值。
|
||||
- 若不通过,`build.releaseCandidate=false`,但可继续内部原型调试。
|
||||
|
||||
### 5.5 动态难度
|
||||
|
||||
源码确认参数为:`dynamicDifficultyAbleStage=0..6`、条件 `5,8,12`、值 `10,15,20,5,5,5`,另有 `normalEnemyBattleTimeStatUp=13,3,100,5,1`。方法体未恢复,当前不得擅自解释各数组下标。
|
||||
|
||||
为了让裸装初始配置稳定学会循环,当前 DEMO 对普通敌人增加 `[验证版新增]` 的前期容错:1/2/3 层攻击倍率为 `0.72/0.76/0.80`、生命倍率为 `0.88/0.90/0.92`;4–5 层为 `0.90/0.96`;第 6 层起恢复 `1.0/1.0`,Boss 始终为 `1.0/1.0`。该倍率不修改配置表原值,且在战斗快照中可复现。
|
||||
|
||||
实现必须:
|
||||
|
||||
- 保留 `DdaState`、配置原值、每次状态迁移原因和应用前后倍率。
|
||||
- DDA 只可改变敌人战斗属性,禁止改变骰面、商店、掉落品质和事件生成。
|
||||
- 威胁倍率限制在 `0.78..1.05`,前三层上限固定为 `1.0`;最近生命均值低于 `0.45` 时下降 `0.12`,低于 `0.70` 或战斗超过 11 秒时下降 `0.06`,高熟练表现最多上升 `0.02`。
|
||||
- 黑盒签收后只替换 `DdaEvaluator`,不得改变存档结构和日志字段。
|
||||
|
||||
这条门禁确保没有用猜测公式冒充源码结果。
|
||||
|
||||
### 5.6 死亡与复活
|
||||
|
||||
1. 玩家生命归零时冻结战斗逻辑并创建死亡快照。
|
||||
2. 若持有可用的源码 `Revive` 祝福,先按祝福规则结算并消耗对应次数。
|
||||
3. 若祝福不可用或结算后生命仍为 0,播放完整 `DEFEAT` 演出后直接进入失败结算。
|
||||
4. 当前 DEMO 不渲染广告复活、放弃广告或任何伪广告按钮;历史 `instantRevive`/事务字段只用于旧存档兼容,不可从普通 UI 触发。
|
||||
|
||||
## 6. 局内装备
|
||||
|
||||
### 6.1 目录、槽位与品质
|
||||
|
||||
四个槽位为武器、戒指、护手、鞋子;开局为空。完整 40 件目录见 [gear-catalog.csv](./specs/gear-catalog.csv),装备属性定义见 [gear-status.csv](./specs/gear-status.csv)。
|
||||
|
||||
| 品质 | rank | 权重 | 颜色 | 分解点数 |
|
||||
|---|---:|---:|---|---:|
|
||||
| 普通 | 0 | 210 | 灰白 | 1 |
|
||||
| 稀有 | 1 | 60 | 蓝 | 2 |
|
||||
| 英雄 | 2 | 28 | 紫 | 4 |
|
||||
| 传奇 | 3 | 4 | 金 | 8 |
|
||||
| 神话 | 4 | 1 | 红 | 16 |
|
||||
|
||||
权重取源码 `210:60:28:4:1`,只使用前五档;源码数组中的第六个 `4` 不进入本验证版品质池。
|
||||
|
||||
### 6.2 生成与属性
|
||||
|
||||
每件掉落按固定顺序消费 `lootRng`:
|
||||
|
||||
1. 选择槽位;若某槽为空,其权重为 2,其余为 1。
|
||||
2. 在该槽的 10 个外观 ID 中等权选 1 个。
|
||||
3. 按五档权重选择品质。
|
||||
4. 从 13 个 `weaponstatus` 中等权选 1 个主属性。
|
||||
5. `baseRoll = uniform(baseMin, baseMax)`。
|
||||
6. `value = baseRoll + plusValue × qualityRank`;按属性显示精度取整。
|
||||
7. 创建不可变 `GearInstance.instanceId`。
|
||||
|
||||
第 4–6 步为 `[验证版新增]` 的明确生成规则,因为 IL2CPP 元数据没有恢复原作 `GetStatus` 方法体。CSV 保留原始 `baseValue`、`plusValue`、百分比和浮点标记,后续可替换生成器而不改目录。
|
||||
|
||||
### 6.3 查看、换装与分解
|
||||
|
||||
- 大量随机局的装备“查看机会”P50 必须为 6–8;机会包括宝箱、战斗掉落、铁匠铺、传奇装备地块和小游戏奖励。
|
||||
- 掉落弹窗同时显示新旧装备、品质、主属性和总面板变化;增加为绿色、降低为红色、无变化为灰色。
|
||||
- 选择装备:新装备飞入固定槽,旧装备从槽中掉出,随后自动分解并增加点数。
|
||||
- 选择分解:新装备直接转为品质对应点数。
|
||||
- 关闭掉落弹窗:所有未处理装备按出现顺序自动分解。
|
||||
- 同一掉落批次只在 `PostBattle/RewardChoice` 提交一次;动画重播不得重复加点。
|
||||
|
||||
### 6.4 铁匠铺
|
||||
|
||||
选择当前已装备的一件装备后,可选择“直接领取”或“挑战强化”:
|
||||
|
||||
- 目标从当前品质升至下一品质;神话不可强化。
|
||||
- 基础成功率依次为 80% / 50% / 30% / 10%。
|
||||
- 成功:品质 +1,按新 rank 重算主属性,保留 instanceId 并增加 `upgradeCount`。
|
||||
- 失败:装备销毁,从槽位移除,并按失败前品质自动分解。
|
||||
- 确认后先写 intent,随机完成后整体提交;刷新只能恢复最终结果,不能重抽。
|
||||
|
||||
## 7. 祝福与药水
|
||||
|
||||
### 7.1 祝福池
|
||||
|
||||
祝福池为源码 31 个 `isEnabled=True` 条目加 `ThirdDieReplace`,共 32 个,详见 [blessings.csv](./specs/blessings.csv)。源码四档权重为 `50:30:20:5`;第三骰固定为传奇档。
|
||||
|
||||
祝福获得规则:
|
||||
|
||||
- 每次只揭示 1 张卡;卡片生成后立即把 `blessingId` 写入待选择状态。
|
||||
- 玩家直接接受已揭示卡;当前构建不提供广告重抽或强制换卡。
|
||||
- 可叠加、最大层数、依赖项和参数严格读取 CSV。
|
||||
- 同一祝福的星级费用读取 `starsPerAugment=1,2,2,3,3,3,4,4,4,4,5…`;持有层数从 0 计数,用 `min(level, 29)` 取值。
|
||||
|
||||
`ThirdDieReplace`:每次正常投出 A/B 后投 C;玩家必须选择“替换 A”“替换 B”“放弃”。它只改变当前移动值,不修改 A/B 的基础随机结果与统计日志。
|
||||
|
||||
### 7.2 丹药
|
||||
|
||||
全局有且只有一种丹药。开局容量 1、基础恢复 `50 HP`。固定商店升级可把容量提升到 10;基础恢复曲线为 `50、80、110……650 HP`,每次升级增加 30 HP,完整曲线见 [potions.csv](./specs/potions.csv)。
|
||||
|
||||
- 解密后的 `PotionController.GetPotionFinalHeal` native 方法体确认最终基线为 `(healLevelValue + percentHealBonus) × adaptiveMultiplier / 100`:`healLevelValue` 是 `potion.csv` 的固定 HP;独立 `Potion_PercentHeal(valueA=10)` 才计算 `maxHp × 10%` 后与基线相加。网络检索未发现更高置信度的公开精确公式。
|
||||
- 因此本 MVP 不采用“基础恢复 10%、每级 ×1.25”的无证据回退公式;该公式只在 native 证据缺失时才启用。最终生命仍不得超过最大生命。
|
||||
- 丹药只允许在 `Fight` 且玩家存活、剩余次数 >0 时手动使用。
|
||||
- 点击后先锁按钮,结算恢复和丹药强化,再扣 1 次,最后解锁。
|
||||
- 同一输入 tick 只接受一次;暂停、连点或恢复存档不得重复生效。
|
||||
- 治疗不超过最大生命;最大生命增加类效果先改上限再治疗。
|
||||
- 丹药只能通过三个固定商店商品和丹药相关祝福强化,不再存在第二种丹药或独立的随机“丹药强化商品”。原 9 条 `potionaugment` 数据仅作为丹药相关祝福的设计参数参考;禁用的 `GuaranteedDouble` 不进入任何池。
|
||||
|
||||
## 8. 商店与局内点数
|
||||
|
||||
### 8.1 商人出现与界面
|
||||
|
||||
- 商人在第 3、6、9 层的一个合法普通格上出现;只保证生成,不强制玩家落点。
|
||||
- 商店显示 3 个固定升级槽和 3 个随机商品槽。
|
||||
- 固定槽为:补充 1 瓶且不超过上限的 `potionAdd`、只令持有上限 +1 的 `potionCount`、令基础恢复量 +30 HP 的 `potionHeal`。
|
||||
- 随机槽从地块、祝福、直接属性、彩票和新增第三骰商品中无放回抽取;不再抽取独立 `POTION` 商品。
|
||||
- 已达购买上限、依赖不满足或本局已售罄的商品不进入候选池。
|
||||
- 刷新费用为 `3 + 2 × rerollCount` 点;支付和换货同一事务提交。
|
||||
|
||||
### 8.2 97 条商品
|
||||
|
||||
[shop-offers.csv](./specs/shop-offers.csv) 完整保留源码 96 条商品,并新增:
|
||||
|
||||
| ID | 类型 | 内容 | 价格 | 上限 | 来源 |
|
||||
|---|---|---|---:|---:|---|
|
||||
| 196 | `AUGMENT` | `ThirdDieReplace` | 27 点 | 1 | `[验证版新增]` |
|
||||
|
||||
原 96 条数据仍完整保存用于溯源;其中 9 条药水强化不进入 DEMO 商店随机池,其效果只允许经丹药相关祝福设计接入。其余价格、购买上限、固定槽和排序值按源码表。
|
||||
|
||||
### 8.3 点数源汇
|
||||
|
||||
点数是本验证版唯一局内货币,不跨局保存:
|
||||
|
||||
- 来源:移动结算、战斗/事件奖励、装备分解、祝福 `PointSupply`、即时商人 +20。
|
||||
- 消耗:商店购买、商店刷新和需要点数的小游戏。
|
||||
- 所有变化写 `transactionId、reason、delta、balanceAfter`。
|
||||
- 余额不足时按钮禁用并显示缺口;任何界面不得允许负余额。
|
||||
- 结算页只展示本局获得/消耗,不结转到下一局。
|
||||
|
||||
## 9. 事件与广告入口边界
|
||||
|
||||
### 9.1 自然生成与玩家有益地块注入
|
||||
|
||||
原作不存在已证实的“十层内六类各生成一次”保证袋。严格还原采用两条独立入口:
|
||||
|
||||
1. 自然生成候选受 `newtile.isEnabled`、`minStage`、`minLoop` 与 `maxCornerPerGame` 过滤,完整资格表见 [enabled-tile-eligibility.csv](../../analysis/stage-floor-rules/tables/enabled-tile-eligibility.csv)。
|
||||
2. 玩家有益地块从当前可用/装备的 `OutGameTile` 元数据构成洗牌牌组;牌组内无放回,耗尽后重洗。
|
||||
3. 新账号 8 个默认装备地块见 [default-good-tile-deck.csv](../../analysis/stage-floor-rules/tables/default-good-tile-deck.csv);初始化调用固定抽取 8 次。
|
||||
4. 换层调用固定为 `AddRandomGoodTiles(loopIndex+2)`;这是抽取/安装尝试数,不保证成功落盘数。
|
||||
5. 商店依附起点,每 3 个显示楼层出现,不进入普通 GoodEvent 随机池。
|
||||
|
||||
### 9.2 十二种可安装地块
|
||||
|
||||
商店购买后可安装全部 12 种源码地块:137、138、139、140、141、142、143、144、151、152、164、166。基础规则、源码参数、界面和调试验收见 [events.csv](./specs/events.csv)。
|
||||
|
||||
- 安装时玩家选择一个高亮合法格;取消则商品保持 `PurchasedPendingInstall`,不退款、不丢失。
|
||||
- 同一格只能有一个事件;事件完整结算或主动离开后变为荒地,不能在后续楼层重复触发。玩家购买或过层新增地块可覆盖荒地。
|
||||
- 额外事件不要求十局自然覆盖,必须通过调试台逐项强制生成验收。
|
||||
- 挖掘固定 5×5、基础 5 次;覆盖形状读取 `herobodyminigame.csv`。
|
||||
- 抓娃娃每局面生成 20 个胶囊;权重读取 `clawcraneminigame.csv`。
|
||||
- 泡泡龙从 6 个源码模板中按 seed 选 1 个;基础 3 发、18 个目标配置。
|
||||
|
||||
### 9.3 广告与即时增强入口
|
||||
|
||||
当前 MVP 不渲染任何广告、`▶` 即时增强、事件重抽、补次数、赠送点数或广告复活按钮。事件全部按基础次数和基础收益结算;祝福卡揭示后直接接受。旧 [reward-boosts.csv](./specs/reward-boosts.csv) 与 `RewardBoostTransaction` 类型仅作为未来 IAA 讨论和旧存档迁移的休眠接口,当前普通 UI 和调试台都不可触发。后续增加 IAA 必须在 DEMO 签收后另行设计、授权和验收。
|
||||
|
||||
## 10. UI/UX 规范
|
||||
|
||||
### 10.1 全局布局
|
||||
|
||||
- 基准画布 750×1334,`Fit Height`,安全区内边距左右 24、顶部 44、底部 34。
|
||||
- 世界和像素资源只允许整数倍缩放;相机与节点落点取整,禁止静止时半像素。
|
||||
- 顶部:点数、层数、暂停;仅 `DiceGauge` 调试关额外显示 Boss Gauge。
|
||||
- 中部:棋盘或战斗舞台;弹窗不得遮住当前关键目标或 Boss 预警。
|
||||
- 下部:HP、四装备槽、主操作按钮、药水。
|
||||
- 普通入口隐藏自动掷骰、战斗加速和所有调试功能。
|
||||
|
||||
### 10.2 页面清单
|
||||
|
||||
完整控件和状态见 [ui-screens.csv](./specs/ui-screens.csv)。必须实现:
|
||||
|
||||
1. 本地开始页:开始新局、继续安全点、清除本局。
|
||||
2. 棋盘 HUD:点数、层数、HP、四槽、Roll、丹药、暂停;`Default` 第 1 关不显示 Boss 蓄势,只有 `DiceGauge` 调试关显示蓄势(0/2)或倒计时(4..0)。
|
||||
3. 骰子结果/第三骰选择。
|
||||
4. 自动战斗 HUD:双方血条、伤害数字、状态、药水。
|
||||
5. 掉落比较与批量未处理提示。
|
||||
6. 商店与地块安装。
|
||||
7. 宝箱、营火、铁匠铺、祝福、挖掘、彩票、21点、抓娃娃、黄金马车、骰子机、泡泡龙。
|
||||
8. 源码复生祝福、失败、Boss 胜利和局结算;不含广告复活页。
|
||||
9. 暂停、玩家面板与退出确认;玩家面板必须显示反击。
|
||||
10. 隐藏调试台。
|
||||
|
||||
### 10.3 轻引导
|
||||
|
||||
仅提供三个一次性上下文提示,状态保存在本机设置而非 run:
|
||||
|
||||
- 首次掷骰:高亮 Roll,说明“两枚骰子点数相加”。
|
||||
- 首次战斗:说明自动攻击,药水可手动使用。
|
||||
- 首次装备:指向属性对比与替换/分解。
|
||||
|
||||
提示不暂停计时战斗;如果触发于战斗前,必须在 `PreBattle` 完成后再开始逻辑时钟。
|
||||
|
||||
## 11. 美术、动作、特效与音频
|
||||
|
||||
### 11.1 美术边界
|
||||
|
||||
- 只使用可提取的 HEROLL 内部原型资产;不得引用 Dice Lords 形象、配色、构图或旧 Art Bible 规则。
|
||||
- 风格为传统低清像素:深色森林/地牢底、紫灰轨道、青绿有益地块、红黑 Boss 压力、厚描边 UI。
|
||||
- Grass 场景使用分层远景、暗绿色地表、灰蓝岩石与暖色火把;不引入其他章节主题。
|
||||
- 角色、怪物、场景、UI、动作和特效映射见 [assets.csv](./specs/assets.csv) 与 [animation-vfx.csv](./specs/animation-vfx.csv)。
|
||||
|
||||
### 11.2 角色与怪物规则
|
||||
|
||||
- 玩家剑士保持短身像素比例、红兜帽/深甲/亮剑盾轮廓;武器和盾牌必须在战斗缩放下可区分。
|
||||
- 剑士敌人用宽肩和近战武器;直射敌人用细长水平武器;抛射敌人用高举/弧线姿态;冲锋敌人用前倾轮廓。
|
||||
- Boss 至少是玩家高度 2.5 倍,黑灰主体、红色外沿、紫色能量点;不依赖文字传达危险。
|
||||
- 同屏角色描边至少 1 个原始像素;状态着色不能覆盖面部、武器和攻击预警。
|
||||
|
||||
### 11.3 动作和特效规则
|
||||
|
||||
- 全系统最低反馈契约:按钮按压、骰子、逐格移动、落地、楼层切换、Boss 计数/转换、战斗进出、双方攻击/受击、伤害/治疗数字、装备揭示/换装/分解、祝福、商店购买、每类事件、复活和最终结算都必须保留至少一段可读动画或数值过渡;普通入口禁止跳过、瞬移式结算或无反馈硬切。
|
||||
- 逻辑命中由时间轴事件驱动,不能绑定动画结束回调。
|
||||
- 普攻:180–300ms,命中前摇 40–80ms,命中停顿 60–90ms。
|
||||
- Boss 攻击:预警 ≥500ms,预警形状与伤害区域误差 ≤4 个逻辑像素。
|
||||
- 暴击:允许一次 ≤10px 相机脉冲;全屏白闪不得超过 33ms。
|
||||
- 伤害数字同来源 150ms 内合并,同屏最多 24 个。
|
||||
- 治疗用绿/青白核心,护盾用青色轮廓,受伤用红色但不与有益效果混色。
|
||||
- 低特效模式仍保留地面预警、命中点和状态图标。
|
||||
|
||||
### 11.4 音频
|
||||
|
||||
不使用背景音乐。只加载必要功能音效:按钮、骰子滚动/落定、移动步、近战命中、直射、抛射落点、冲锋、暴击、受伤、治疗、护盾、装备获取/分解、祝福揭示、购买、事件成功/失败、Boss 预警与胜负。
|
||||
|
||||
同类短音效 80ms 内最多播放一次;战斗退出时停止循环或延迟音;页面切换不得残留音频实例。
|
||||
|
||||
## 12. 技术架构与数据契约
|
||||
|
||||
### 12.1 模块边界
|
||||
|
||||
```text
|
||||
Bootstrap
|
||||
├─ RunDomain // 状态机、楼层、输入、结算
|
||||
├─ RngDomain // 多流确定性随机
|
||||
├─ BoardDomain // 棋盘、移动、地块安装
|
||||
├─ BattleDomain // 实体、公式、祝福、药水、DDA
|
||||
├─ BuildDomain // 装备、分解、祝福
|
||||
├─ ShopDomain // 报价、购买、刷新
|
||||
├─ EventDomain // 12事件;历史增强结构保持休眠
|
||||
├─ SaveDomain // 安全点、迁移、幂等事务
|
||||
├─ Presentation // Cocos 场景、UI、动画、音频
|
||||
└─ DebugDomain // 隐藏调试台与技术日志
|
||||
```
|
||||
|
||||
表现层只能派发 command,不得直接修改 HP、点数、库存或 RNG。所有 Domain command 返回不可变 result,再由表现层播放。
|
||||
|
||||
### 12.2 TypeScript 接口
|
||||
|
||||
```typescript
|
||||
type RunPhase =
|
||||
| 'RUN_INIT' | 'FLOOR_SETUP' | 'AWAIT_ROLL' | 'DICE_RESOLVE'
|
||||
| 'THIRD_DIE_CHOICE' | 'MOVING' | 'TILE_RESOLVE' | 'BATTLE'
|
||||
| 'EVENT' | 'SHOP' | 'REWARD_CHOICE' | 'FLOOR_TRANSITION'
|
||||
| 'BOSS_BATTLE' | 'RUN_CLEAR' | 'RUN_FAILED';
|
||||
|
||||
type RngStreamName = 'dice' | 'board' | 'loot' | 'combat' | 'shop' | 'event';
|
||||
|
||||
interface RngStreamState {
|
||||
algorithm: 'xoshiro128ss';
|
||||
runSeed: string;
|
||||
stream: RngStreamName;
|
||||
state: [number, number, number, number];
|
||||
index: number;
|
||||
}
|
||||
|
||||
interface DdaState {
|
||||
enabled: boolean;
|
||||
tier: number;
|
||||
sourceConditions: [5, 8, 12];
|
||||
sourceValues: [10, 15, 20, 5, 5, 5];
|
||||
recentBattleMs: number[];
|
||||
attackMultiplier: number;
|
||||
healthMultiplier: number;
|
||||
lastTransitionReason: string;
|
||||
}
|
||||
|
||||
interface DiceRoll {
|
||||
rollId: string;
|
||||
rollIndex: number;
|
||||
dieA: number;
|
||||
dieB: number;
|
||||
thirdDie?: number;
|
||||
thirdDieAction?: 'REPLACE_A' | 'REPLACE_B' | 'DISCARD';
|
||||
finalA: number;
|
||||
finalB: number;
|
||||
moveValue: number;
|
||||
rngIndexBefore: number;
|
||||
rngIndexAfter: number;
|
||||
}
|
||||
|
||||
interface BattleSnapshot {
|
||||
battleId: string;
|
||||
floor: number;
|
||||
isBoss: boolean;
|
||||
logicTimeMs: number;
|
||||
player: CombatantSnapshot;
|
||||
enemies: CombatantSnapshot[];
|
||||
statuses: StatusSnapshot[];
|
||||
cooldowns: Record<string, number>;
|
||||
combatRng: RngStreamState;
|
||||
reviveBlessingResolved: boolean;
|
||||
instantReviveUsed: boolean; // 旧存档兼容字段,当前 UI 不可达
|
||||
}
|
||||
|
||||
interface GearInstance {
|
||||
instanceId: string;
|
||||
catalogId: number;
|
||||
slot: 'WEAPON' | 'RING' | 'GAUNTLET' | 'BOOTS';
|
||||
qualityRank: 0 | 1 | 2 | 3 | 4;
|
||||
statusId: number;
|
||||
value: number;
|
||||
upgradeCount: number;
|
||||
}
|
||||
|
||||
interface BlessingInstance {
|
||||
instanceId: string;
|
||||
blessingId: string;
|
||||
level: number;
|
||||
stacks: number;
|
||||
remainingTriggers?: number;
|
||||
}
|
||||
|
||||
interface PotionState {
|
||||
countLevel: number;
|
||||
healLevel: number;
|
||||
charges: number;
|
||||
maxCharges: number;
|
||||
healAmount: number; // fixed HP from HEAL_LEVEL.value
|
||||
augments: number[];
|
||||
inputLocked: boolean;
|
||||
}
|
||||
|
||||
interface ShopState {
|
||||
merchantInstanceId: string;
|
||||
floor: number;
|
||||
fixedOfferIds: number[];
|
||||
randomOfferIds: number[];
|
||||
purchasedCountByOffer: Record<string, number>;
|
||||
rerollCount: number;
|
||||
pendingInstallOfferId?: number;
|
||||
}
|
||||
|
||||
interface EventInstance {
|
||||
instanceId: string;
|
||||
tileId: number | 'MERCHANT';
|
||||
floor: number;
|
||||
tileIndex: number;
|
||||
visitIndex: number;
|
||||
phase: 'READY' | 'RESOLVING' | 'RESOLVED';
|
||||
boostUsed: boolean; // 旧存档兼容字段,当前 UI 不可达
|
||||
payload: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface RewardBoostTransaction { // 仅保留旧存档迁移,不创建新事务
|
||||
transactionId: string;
|
||||
placementId: string;
|
||||
eventInstanceId: string;
|
||||
status: 'PENDING' | 'COMMITTED';
|
||||
rewardPayload: Record<string, number | string | boolean>;
|
||||
createdAtLogicTick: number;
|
||||
committedAtLogicTick?: number;
|
||||
}
|
||||
|
||||
interface Checkpoint {
|
||||
schemaVersion: 1;
|
||||
checkpointId: string;
|
||||
createdAtLogicTick: number;
|
||||
reason: string;
|
||||
run: RunState;
|
||||
rng: Record<RngStreamName, RngStreamState>;
|
||||
committedTransactionIds: string[];
|
||||
checksum: string;
|
||||
}
|
||||
|
||||
interface RunState {
|
||||
runId: string;
|
||||
runSeed: string;
|
||||
phase: RunPhase;
|
||||
floor: number;
|
||||
tileIndex: number;
|
||||
lapSteps: number;
|
||||
points: number;
|
||||
playerStats: CombatStats;
|
||||
gear: Partial<Record<GearInstance['slot'], GearInstance>>;
|
||||
blessings: BlessingInstance[];
|
||||
potion: PotionState;
|
||||
shop?: ShopState;
|
||||
activeEvent?: EventInstance;
|
||||
activeBattle?: BattleSnapshot;
|
||||
dda: DdaState;
|
||||
reviveUsedThisFloor: boolean; // 旧存档兼容字段
|
||||
generatedCoreEvents: string[];
|
||||
pendingTileInstalls: number[];
|
||||
saveVersion: 1;
|
||||
}
|
||||
```
|
||||
|
||||
`CombatantSnapshot`、`StatusSnapshot` 和 `CombatStats` 是纯数据对象,禁止包含 Cocos Node、组件引用或函数。CSV 到运行时字段的映射见 [data-contracts.csv](./specs/data-contracts.csv)。
|
||||
|
||||
### 12.3 随机流
|
||||
|
||||
所有流从 `hash(runSeed + ':' + streamName)` 派生,禁止共享索引:
|
||||
|
||||
- `dice`:基础骰与第三骰。
|
||||
- `board`:地块位置和普通战斗格。
|
||||
- `loot`:装备、品质、词条、祝福。
|
||||
- `combat`:命中、暴击、伤害波动、战斗触发。
|
||||
- `shop`:随机商品和刷新。
|
||||
- `event`:保证袋、小游戏与事件奖励。
|
||||
|
||||
UI 动画、粒子和音效不得消费任何 Domain RNG;表现随机使用不保存的 `visualRng`。
|
||||
|
||||
## 13. 安全点、恢复与日志
|
||||
|
||||
### 13.1 安全点规则
|
||||
|
||||
每次地块、战斗、购买、换装、祝福选择或事件**完整结算后**写安全点。禁止在扣点后、发奖前或随机已消费但结果未提交时保存半状态。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
command["接收玩家命令"] --> clone["复制当前 RunState"]
|
||||
clone --> resolve["在副本消费 RNG 并完整结算"]
|
||||
resolve --> validate{"不变量通过?"}
|
||||
validate -- 否 --> rollback["丢弃副本并记录异常"]
|
||||
validate -- 是 --> tx["提交事务与 RunState"]
|
||||
tx --> save["序列化 Checkpoint + checksum"]
|
||||
save --> atomic["写临时槽并原子替换主槽"]
|
||||
atomic --> render["通知表现层"]
|
||||
crash["刷新/崩溃/重新打开"] --> load["读取主槽;失败则读备份槽"]
|
||||
load --> verify{"版本和 checksum 有效?"}
|
||||
verify -- 否 --> newRun["提示存档损坏并开始新局"]
|
||||
verify -- 是 --> dedupe["恢复已提交 transactionId 集合"]
|
||||
dedupe --> resume["从最近安全 phase 恢复"]
|
||||
```
|
||||
|
||||
### 13.2 本地存储
|
||||
|
||||
- 主槽 `heroll.validation.run.v1`,备份槽 `heroll.validation.run.v1.bak`。
|
||||
- 设置槽 `heroll.validation.settings.v1` 只保存音量、轻引导和低特效选项。
|
||||
- 写入顺序:序列化 → checksum → 写备份 → 写主槽 → 删除临时键。
|
||||
- `schemaVersion` 不匹配时调用显式迁移;迁移失败不得猜字段。
|
||||
- 开始新局、胜利或失败确认后清除 run 主/备份槽,不清除设置。
|
||||
|
||||
### 13.3 技术日志
|
||||
|
||||
只记录:`runId`、seed、各 RNG 索引、DDA 状态、状态迁移、事务、异常、checkpointId、checksum 和存档版本。禁止制作人工试玩表、用户画像或跨局统计。
|
||||
|
||||
日志为内存环形缓冲 2,000 条,可从调试台导出 JSON;普通界面不显示。单局时长只由现场负责人观察确认,不写聚合分析。
|
||||
|
||||
## 14. 隐藏调试台
|
||||
|
||||
入口:开始页标题连续点击 7 次,或开发构建按 `Ctrl+Shift+D`。发布型本地包默认隐藏但保留代码。
|
||||
|
||||
调试能力:
|
||||
|
||||
- 指定/复制 seed、重新开局、查看各流索引。
|
||||
- 调整层数、位置、HP、点数、药水和战斗速度 1×/4×。
|
||||
- 重置/注入 DDA tier。
|
||||
- 强制 12 种事件、指定装备/品质/词条、指定祝福、强制死亡。
|
||||
- 跳到 Boss、重放最近安全点、模拟刷新、重复提交 transactionId。
|
||||
- 导出技术日志和当前 `Checkpoint`。
|
||||
|
||||
调试命令必须经过同一 Domain API,不得直接改 Cocos 节点;每条命令日志标记 `debug=true`,避免与普通 seed 复现混淆。
|
||||
|
||||
## 15. QA 与发布验收
|
||||
|
||||
详细用例见 [qa-cases.csv](./specs/qa-cases.csv)。以下为发布门禁:
|
||||
|
||||
1. 两枚骰子 36 个有序结果理论等权;100 万次模拟通过卡方检验,`p >= 0.01`。
|
||||
2. 同一 seed、DDA 和输入序列生成完全相同的棋盘、掉落、战斗和商店。
|
||||
3. 十层推进、30% 恢复、初始化 8 次玩家有益牌组抽取、换层 `loopIndex+2` 次尝试、商人 3/6/9 与最终 Boss 全部通过。
|
||||
4. 第 1 关不得创建 Boss Gauge/倒计时状态;专项 DiceGauge 调试关才验证 2 次 ROLL 蓄势 + 4 次 ROLL 倒计时、4 个唯一非祝福转换与 Gauge 清零。
|
||||
5. 战斗拟合伤害误差 ≤5%、时长误差 ≤10%;未通过则不得标记原作还原。
|
||||
6. 40 件装备均可生成;品质权重正确;10 万局模拟中查看机会 P50 为 6–8。
|
||||
7. 32 个祝福、第三骰三种处理、丹药 10/21 级曲线和 9 个强化全部可达。
|
||||
8. 商店恰好 97 条;固定链、价格、上限、刷新和地块安装通过。
|
||||
9. 12 种事件逐项检查输入、表现、结算、存档、退出和调试强制路径。
|
||||
10. 普通 UI 中广告、即时增强、广告复活、重抽和补次数按钮数量均为 0;历史事务接口不可达。
|
||||
11. 每种弹窗、战斗结束、购买、换装和事件边界恢复时不存在半结算。
|
||||
12. 750×1334 以及 808×1440 视口无裁切;像素资源静止时无半像素模糊。
|
||||
13. 构建中不存在账号、局外成长、体力、抽卡、IAP、广告按钮、真实广告 SDK、剧情或 BGM 入口。
|
||||
14. 固定棋盘在移动前后计算样式均为 `transform:none`,角色下一格位于起点左侧;每类功能格加载对应原作图标。
|
||||
15. 初始配置不使用丹药、广告或调试加成时,自动战斗可连续击败前三组普通怪物。
|
||||
|
||||
### 15.1 十局现场签收
|
||||
|
||||
项目组现场完成 10 局,不制作用户统计表:
|
||||
|
||||
- 至少 3 局攻击构筑。
|
||||
- 至少 3 局攻速/暴击构筑。
|
||||
- 至少 3 局生存构筑。
|
||||
- 至少 1 局自由构筑。
|
||||
- 覆盖无复生祝福的直接失败路径,以及源码 `Revive` 祝福路径。
|
||||
- 负责人现场确认大体处于 10–12 分钟,并记录“通过/不通过”会议结论;不保存个人行为数据。
|
||||
|
||||
十局签收通过后,才进入“完整 IAA GDD”讨论;当前 DEMO 不预埋可见广告入口。
|
||||
|
||||
## 16. Evidence → Finding → Design
|
||||
|
||||
### 16.1 关键 Findings
|
||||
|
||||
| Finding | 证据 | 设计落点 | 置信度 |
|
||||
|---|---|---|---|
|
||||
| 双骰、棋盘、自动战斗、构筑和 Boss 是核心循环 | E-001/E-002/E-019/E-020 | §3–5 | 高 |
|
||||
| `stage=0` 为 Grass、10 层、`boss_0/skill_0`、冷却 7 | E-001 | §3.1 | 高 |
|
||||
| 第 1 关 10 行敌方基础属性可逐层复原 | E-002 | `floor-balance.csv` | 高 |
|
||||
| 玩家裸装初始属性已完整提取 | E-003 | §5.1 | 高 |
|
||||
| 31 个启用祝福、40 件局内装备和 96 条商店商品可完整列举 | E-006/E-007/E-009 | `blessings/gear/shop` CSV | 高 |
|
||||
| 原作方法体不足以证明最终战斗与 DDA 公式 | E-012/E-016 | §5.4–5.5、拟合门禁 | 高 |
|
||||
| 当前实机账号带局外属性,不适合作为裸装拟合样本 | E-019 | `formula-fit.csv` 补录矩阵 | 高 |
|
||||
| 提取资产支持低清像素 Grass、Boss、UI 和 VFX 复用 | E-018 | §11、资产表 | 高 |
|
||||
|
||||
### 16.2 实现 Path
|
||||
|
||||
`P-001`:`stageinfo/board/playerdefaultstat` → 关卡与角色配置 → `RunDomain/BattleDomain` → 十层 Boss 验收。
|
||||
`P-002`:`weapondrop/weaponstatus/augment/potion` → 构筑实例 → 战斗属性快照 → 掉落、换装与祝福验收。
|
||||
`P-003`:`ingameshoplist/newtile/小游戏表` → 商店报价 → 事件安装与基础结算;历史增强事务保持休眠不可达。
|
||||
`P-004`:run command → 完整副本结算 → transaction commit → checkpoint → 崩溃恢复。
|
||||
|
||||
逐条可复现路径、文件哈希和 Design ID 见 [evidence-map.csv](./specs/evidence-map.csv)。
|
||||
|
||||
## 17. 开发交付清单
|
||||
|
||||
- 主 GDD:本文件。
|
||||
- 配置索引:[specs/README.md](./specs/README.md)。
|
||||
- 证据:[evidence-map.csv](./specs/evidence-map.csv)。
|
||||
- 关卡/公式:[floor-balance.csv](./specs/floor-balance.csv)、[formula-fit.csv](./specs/formula-fit.csv)。
|
||||
- 构筑:[gear-catalog.csv](./specs/gear-catalog.csv)、[gear-status.csv](./specs/gear-status.csv)、[blessings.csv](./specs/blessings.csv)、[potions.csv](./specs/potions.csv)。
|
||||
- 商店/事件:[shop-offers.csv](./specs/shop-offers.csv)、[events.csv](./specs/events.csv)、[reward-boosts.csv](./specs/reward-boosts.csv)。
|
||||
- 技术/UI/美术:[data-contracts.csv](./specs/data-contracts.csv)、[ui-screens.csv](./specs/ui-screens.csv)、[assets.csv](./specs/assets.csv)、[animation-vfx.csv](./specs/animation-vfx.csv)。
|
||||
- 验收:[qa-cases.csv](./specs/qa-cases.csv)。
|
||||
|
||||
完成定义:所有 CSV 通过结构校验,Mermaid 可解析,97/40/32 数量正确,禁用系统无残留入口,QA `P0` 全部通过,战斗拟合门禁通过,并完成 10 局现场签收。
|
||||
@@ -0,0 +1,35 @@
|
||||
# HEROLL 玩法验证 GDD 结构验收
|
||||
|
||||
验收日期:2026-08-24
|
||||
验收对象:`design/gameplay-validation/`
|
||||
|
||||
## 已通过
|
||||
|
||||
- 主 GDD 存在且包含范围、循环、战斗、构筑、商店、事件、UI、美术、技术契约、存档、调试和 QA。
|
||||
- GDD 内嵌 2 个 Mermaid 代码块;独立源码保留在 `diagrams/`。
|
||||
- 34 个相对 Markdown 链接全部指向存在的本地文件。
|
||||
- 15 个 CSV 均可由 RFC 4180 解析;每个文件所有数据行列数与表头一致。
|
||||
- 核心数量:10 层、40 个独立实际地块、40 件装备、13 个装备属性、32 个祝福、40 条丹药记录、97 条商店商品、13 个事件、9 个休眠且不暴露的历史事务 placement、26 个 UI 页面(其中广告复活页禁用)、81 条 QA 用例。
|
||||
- 当前 UI 验收覆盖固定棋盘、1 点严格移动 1 个地块、1–4 骷髅战斗格、两类小 Boss 地块、荒地跨层保留、污染 Boss 三次污染、原作功能格图标、零广告按钮和初始配置连续击败前三组怪物。
|
||||
- `shop-offers.csv` 的 96 条 `SOURCE_CONFIRMED` 行与原 `ingameshoplist.csv` 在 ID、类型、目标、固定槽、排序、价格、购买上限和副文本标记上逐行一致;另有 1 条 `ThirdDieReplace` 新增商品。
|
||||
- 所有主键无重复;所有 GDD 内部配置链接无缺失。
|
||||
- 旧 `art-bible/`、商业化与 IAA 资料未删除、未改写;新 GDD 顶部已声明其非规范状态。
|
||||
|
||||
## 显式发布门禁
|
||||
|
||||
战斗与 DDA 的 IL2CPP 方法体未恢复,当前桌面账号又带有局外属性,因此 14 项拟合检查保持 `BLOCKED_CLEAN_CAPTURE`。验证包可以按 GDD 的“验证实现公式”开发,但在 [formula-fit.csv](./specs/formula-fit.csv) 达到伤害 ≤5%、时长 ≤10% 之前,不得宣称原作公式已还原,也不得把构建标记为最终签收版。
|
||||
|
||||
Mermaid 源码已经完成指令、节点、边、引号和代码围栏的结构检查。官方 Mermaid CLI `mmdc` 当前未安装,因此本轮未生成 SVG,也未把“官方渲染成功”列为通过项;本计划只要求在 GDD 中内嵌可编辑流程图。
|
||||
|
||||
## 可复现检查摘要
|
||||
|
||||
```text
|
||||
CSV strict parse: PASS
|
||||
Relative links: 34 / 34 PASS
|
||||
Mermaid blocks: 2
|
||||
Shop source rows: 96 / 96 exact
|
||||
Validation-added shop rows: 1
|
||||
Duplicate primary keys: 0
|
||||
Combat fit: BLOCKED_CLEAN_CAPTURE
|
||||
Official Mermaid render: NOT_RUN_MISSING_MMDC
|
||||
```
|
||||
@@ -0,0 +1,14 @@
|
||||
flowchart TD
|
||||
command["接收玩家命令"] --> clone["复制当前 RunState"]
|
||||
clone --> resolve["在副本消费 RNG 并完整结算"]
|
||||
resolve --> validate{"不变量通过?"}
|
||||
validate -- 否 --> rollback["丢弃副本并记录异常"]
|
||||
validate -- 是 --> tx["提交事务与 RunState"]
|
||||
tx --> save["序列化 Checkpoint + checksum"]
|
||||
save --> atomic["写临时槽并原子替换主槽"]
|
||||
atomic --> render["通知表现层"]
|
||||
crash["刷新/崩溃/重新打开"] --> load["读取主槽;失败则读备份槽"]
|
||||
load --> verify{"版本和 checksum 有效?"}
|
||||
verify -- 否 --> newRun["提示存档损坏并开始新局"]
|
||||
verify -- 是 --> dedupe["恢复已提交 transactionId 集合"]
|
||||
dedupe --> resume["从最近安全 phase 恢复"]
|
||||
@@ -0,0 +1,13 @@
|
||||
flowchart TD
|
||||
click["点击即时增强"] --> eligible{"实例可用且未领取?"}
|
||||
eligible -- 否 --> reject["保持原状态并提示"]
|
||||
eligible -- 是 --> create["创建 transactionId 与 PENDING 事务"]
|
||||
create --> persist["写入安全存档"]
|
||||
persist --> apply["应用确定收益"]
|
||||
apply --> commit["事务标记 COMMITTED"]
|
||||
commit --> checkpoint["写完整结算安全点"]
|
||||
checkpoint --> render["播放图标反馈与收益动画"]
|
||||
recover["刷新/崩溃恢复"] --> lookup{"transactionId 状态"}
|
||||
lookup -- COMMITTED --> renderOnly["只恢复表现,不再发奖"]
|
||||
lookup -- PENDING --> apply
|
||||
lookup -- 不存在 --> click
|
||||
@@ -0,0 +1,28 @@
|
||||
stateDiagram-v2
|
||||
[*] --> RunInit
|
||||
RunInit --> FloorSetup
|
||||
FloorSetup --> AwaitRoll
|
||||
AwaitRoll --> DiceResolve: 手动掷骰
|
||||
DiceResolve --> ThirdDieChoice: 持有第三骰祝福
|
||||
DiceResolve --> Moving: 无第三骰
|
||||
ThirdDieChoice --> Moving: 替换A/替换B/放弃
|
||||
Moving --> TileResolve
|
||||
TileResolve --> Battle
|
||||
TileResolve --> Event
|
||||
TileResolve --> Shop
|
||||
TileResolve --> RewardChoice
|
||||
Battle --> ReviveResolve: 玩家生命归零
|
||||
ReviveResolve --> Battle: 祝福或即时复活成功
|
||||
ReviveResolve --> RunFailed: 无复活
|
||||
Battle --> Checkpoint: 胜利
|
||||
Event --> Checkpoint
|
||||
Shop --> Checkpoint
|
||||
RewardChoice --> Checkpoint
|
||||
Checkpoint --> FloorTransition: 越过起点
|
||||
Checkpoint --> AwaitRoll: 未过层
|
||||
FloorTransition --> BossBattle: 完成第10层
|
||||
FloorTransition --> FloorSetup: 继续
|
||||
BossBattle --> RunClear: 胜利
|
||||
BossBattle --> ReviveResolve: 死亡
|
||||
RunClear --> [*]
|
||||
RunFailed --> [*]
|
||||
@@ -0,0 +1,29 @@
|
||||
# HEROLL 玩法验证配置索引
|
||||
|
||||
本目录与 `../Heroll_Gameplay_Validation_GDD.md` 共同构成玩法验证版唯一规范。编码 UTF-8;数组在 CSV 中使用竖线 `|` 分隔;布尔值使用 `true/false`;空值表示字段不适用,不表示待定。`potions.csv` 中 `HEAL_LEVEL.value` 是固定 HP,商店每级增加 30 HP;最大生命百分比恢复属于独立丹药强化。
|
||||
|
||||
关卡楼层、刷新与 Boss 压力以 [专项逆向报告](../../../analysis/stage-floor-rules/2026-08-31_逆向-HEROLL关卡楼层分布与刷新规则-report.md) 及其 `tables/` 为覆盖规范;旧版“每层固定两个有益地块”“六类事件保证袋”和“stage 0 启用 DiceGauge”均已失效。
|
||||
|
||||
| 文件 | 主键 | 用途 | 目标数量 |
|
||||
|---|---|---|---:|
|
||||
| `evidence-map.csv` | `evidence_id` | Evidence → Finding → Design 追溯 | ≥16 |
|
||||
| `floor-balance.csv` | `floor` | 10 层敌方基础值与 Boss 基准 | 10 |
|
||||
| `formula-fit.csv` | `gate_id` | 战斗/DDA 拟合门禁与补录矩阵 | ≥12 |
|
||||
| `gear-catalog.csv` | `catalog_id` | 40 件局内装备外观目录 | 40 |
|
||||
| `gear-status.csv` | `status_id` | 13 个装备属性生成规则 | 13 |
|
||||
| `blessings.csv` | `blessing_id` | 31 源码祝福 + 第三骰 | 32 |
|
||||
| `potions.csv` | `record_id` | 容量、治疗曲线与强化 | 40 |
|
||||
| `shop-offers.csv` | `offer_id` | 96 源码商品 + 第三骰商品 | 97 |
|
||||
| `events.csv` | `event_id` | 商人、核心六类和 12 地块 | 13 |
|
||||
| `reward-boosts.csv` | `placement_id` | 8 事件增强 + 复活 | 9 |
|
||||
| `data-contracts.csv` | `contract` | TypeScript/CSV 映射 | ≥12 |
|
||||
| `ui-screens.csv` | `screen_id` | 页面、控件与状态 | ≥20 |
|
||||
| `assets.csv` | `asset_id` | HEROLL 原型资产映射 | ≥20 |
|
||||
| `animation-vfx.csv` | `event` | 动作、特效、音频时序 | ≥20 |
|
||||
| `qa-cases.csv` | `case_id` | 自动化、调试台与现场验收 | ≥45 |
|
||||
|
||||
专项外部表:`analysis/stage-floor-rules/tables/stage-boss-rules.csv`、`stage0-loop-enemy-stats.csv`、`enabled-tile-eligibility.csv`、`default-good-tile-deck.csv`。
|
||||
|
||||
`../diagrams/` 保存与主文档三个 Mermaid 代码块一致的独立可编辑源码:`run-state.mmd`、`reward-transaction.mmd`、`checkpoint-recovery.mmd`。结构验收结果见 `../VALIDATION_REPORT.md`。
|
||||
|
||||
来源标签:`SOURCE_CONFIRMED`、`RUNTIME_OBSERVED`、`BLACKBOX_FITTED`、`VALIDATION_ADDED`。`formula-fit.csv` 中的 `BLOCKED_CLEAN_CAPTURE` 是显式发布门禁,不得改写为 `PASS`,除非保存了对应的无局外加成样本和误差报告。
|
||||
@@ -0,0 +1,29 @@
|
||||
event,total_duration_ms,anticipation_ms,active_ms,recovery_ms,hit_stop_ms,logic_marker,vfx_layer,audio_cue,performance_budget,acceptance,source_tag
|
||||
UIPrimaryPress,180,0,70,110,0,onPointerDown,HUD,sfx_ui_confirm,0 particles,feedback begins within 50ms,VALIDATION_ADDED
|
||||
DiceRoll,1267,120,650,247,0,diceResultCommitted,WorldUI,sfx_dice_roll|sfx_dice_settle,1 trail + 1 result halo,result still readable >=250ms,RUNTIME_OBSERVED
|
||||
DiceThirdChoice,300,60,100,140,0,thirdChoiceCommitted,ModalFront,sfx_ui_select,1 replacement streak,A B C values remain visible through choice,VALIDATION_ADDED
|
||||
MoveOneTile,160,20,100,40,0,tileStepAdvanced,World,sfx_step,1 dust burst,actor settles on integer pixels,VALIDATION_ADDED
|
||||
FloorTransition,900,150,300,450,0,floorStateCommitted,WorldUI,sfx_floor_up,2 tile glows + heal burst,HP and layer update only once,VALIDATION_ADDED
|
||||
BossCountdownTick,300,40,160,100,0,bossCounterCommitted,HUD,sfx_boss_warn,1 meter pulse,number and rail color visibly update before tile resolution,VALIDATION_ADDED
|
||||
BossConvertFourTiles,2240,520,1360,360,0,bossTilesCommitted,WorldUI,sfx_boss_warn|sfx_boss_hit,4 sequential tile flashes,each selected tile visibly changes once and blessing tile never changes,VALIDATION_ADDED
|
||||
BattleEnter,700,0,420,280,0,battleSnapshotCreated,Camera|HUD,sfx_battle_enter,no heavy particles,repeat entrance completes <=700ms,RUNTIME_OBSERVED
|
||||
EnemySpawn,350,100,100,150,0,entitySpawned,CharacterUnderlay,sfx_spawn,<=24 particles per entity,collider active only after spawn marker,VALIDATION_ADDED
|
||||
NormalAttack,240,60,90,90,70,damageResolved,CharacterFront,sfx_sword_hit,<=80 live particles,weapon crosses target during active window,RUNTIME_OBSERVED
|
||||
StraightShot,420,80,220,120,40,projectileImpact,ProjectileFront,sfx_arrow,1 projectile + <=16 impact particles,origin and target line unambiguous,VALIDATION_ADDED
|
||||
CurvedShot,800,350,250,200,50,projectileImpact,GroundTell|ProjectileFront,sfx_arc_launch|sfx_arc_hit,1 decal + 1 projectile,landing tell visible >=300ms,VALIDATION_ADDED
|
||||
RushAttack,850,500,180,170,90,rushCollision,GroundTell|CharacterFront,sfx_rush_prepare|sfx_rush_hit,1 strip + <=32 particles,warning strip equals collision path,VALIDATION_ADDED
|
||||
CriticalAttack,1000,250,120,630,120,criticalDamageResolved,VFXFront,sfx_critical,<=120 particles + 1 flash,no white frame >33ms,VALIDATION_ADDED
|
||||
BossSkill0,1100,550,200,350,150,bossSkillDamage,GroundTell|VFXFront,sfx_boss_warn|sfx_boss_hit,<=200 live particles,tell and damage area mismatch <=4 logical px,SOURCE_CONFIRMED
|
||||
DamageNumber,667,0,80,587,0,damageResolved,HUDFront,sfx_none,<=24 visible numbers,merge same source hits within150ms,RUNTIME_OBSERVED
|
||||
HealEffect,650,80,250,320,0,healCommitted,CharacterFront,sfx_heal,<=60 particles,green cyan-white core visible on Grass,VALIDATION_ADDED
|
||||
ShieldEffect,300,80,120,100,0,shieldCommitted,CharacterUnderlay,sfx_shield,<=32 persistent particles,persistent opacity <=45%,VALIDATION_ADDED
|
||||
BattleVictory,750,0,250,500,100,postBattleCommitted,HUDFront,sfx_victory,1 banner burst,reward values appear after commit,RUNTIME_OBSERVED
|
||||
GearRevealCommon,250,40,120,90,0,gearOfferCreated,ModalFront,sfx_gear_reveal,1 rim light,item icon readable by180ms,RUNTIME_OBSERVED
|
||||
GearRevealLegend,633,120,250,263,0,gearOfferCreated,ModalFront,sfx_gear_legend,<=80 particles,legend reveal longer but skippable after marker,RUNTIME_OBSERVED
|
||||
GearEquip,500,80,180,240,0,equipCommitted,HUDFront,sfx_equip,1 fly trail + slot flash,old gear exits before salvage number,VALIDATION_ADDED
|
||||
GearSalvage,400,60,120,220,0,salvageCommitted,HUDFront,sfx_salvage,<=32 shard particles,point delta matches transaction once,VALIDATION_ADDED
|
||||
BlessingReveal,450,80,180,190,0,blessingOfferCreated,ModalFront,sfx_blessing,<=48 particles,title readable by250ms,VALIDATION_ADDED
|
||||
BoostCommit,360,40,100,220,0,rewardBoostCommitted,ModalFront,sfx_boost,play icon pulse + one reward burst,legacy animation only; no current UI trigger,DEFERRED_NOT_EXPOSED
|
||||
Revive,900,250,250,400,0,sourceReviveBlessingResolved,CharacterFront,sfx_revive,<=100 particles,source blessing only; no ad revive UI,SOURCE_CONFIRMED
|
||||
BossDeath,1183,150,350,683,180,bossVictoryCommitted,VFXFront,sfx_boss_death,<=240 particles,reward modal waits for silhouette settle,RUNTIME_OBSERVED
|
||||
Toast,1740,180,1400,160,0,toastQueued,HUD,sfx_toast,0 particles,does not cover Roll potion or boss tell,VALIDATION_ADDED
|
||||
|
@@ -0,0 +1,31 @@
|
||||
asset_id,category,logical_use,source_asset_or_key,prototype_path,import_rule,rights_boundary,source_tag
|
||||
AS-001,scene,Grass back layer,chapter_select_back_grass,analysis/visual-study/unity-samples/scene/sharedassets4__chapter_select_back_grass.png,"Point filter|no mipmap|pixelsPerUnit source-native|integer scale",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-002,scene,Grass front layer,chapter_select_front_grass,analysis/visual-study/unity-samples/scene/sharedassets4__chapter_select_front_grass.png,"Point filter|no mipmap|separate parallax layer",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-003,ui,common 9-slice panel,ingame_common_bg,analysis/visual-study/unity-samples/ui/sharedassets3__ingame_common_bg.png,"Point filter|9-slice borders from source|integer scale",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-004,ui,normal board tile,tile_normal,analysis/visual-study/unity-samples/ui/sharedassets3__tile_normal.png,"Point filter|pivot center|no rotation interpolation",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-005,ui,good event tile,tile_goodEvent,analysis/visual-study/unity-samples/ui/sharedassets3__tile_goodEvent.png,"Point filter|green semantic frame retained",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-006,ui,dice primary button,dice_btn_2,analysis/visual-study/unity-samples/ui/sharedassets3__dice_btn_2.png,"Point filter|pressed frame separate|minimum hit 180x96 at 750 canvas",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-007,ui,equipment status frame,weapon_status_bg,analysis/visual-study/unity-samples/ui/sharedassets3__weapon_status_bg.png,"Point filter|quality border overlays separate",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-008,ui,tutorial bubble,tutorial_bubble_bg,analysis/visual-study/unity-samples/ui/sharedassets3__tutorial_bubble_bg.png,"Point filter|9-slice|pointer separate",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-009,ui,boss icon,ingame_boss_icon,analysis/visual-study/unity-samples/ui/sharedassets3__ingame_boss_icon.png,"Point filter|render >=2x integer",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-010,ui,blessing card motif,joker_card_hero,analysis/visual-study/unity-samples/ui/sharedassets3__joker_card_hero.png,"Point filter|preserve HEROLL source colors",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-011,vfx,tile glow,Fx_T_TileGlow02,analysis/visual-study/unity-samples/vfx/sharedassets3__Fx_T_TileGlow02.png,"Point filter|additive only for core|<=48 live particles",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-012,vfx,sword slash,Fx_T_Sword01,analysis/visual-study/unity-samples/vfx/sharedassets3__Fx_T_Sword01.png,"Point filter|sprite sheet|direction mirrors with actor",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-013,vfx,slash sequence,fx_T_slashSeq02,analysis/visual-study/unity-samples/vfx/sharedassets3__fx_T_slashSeq02.png,"Point filter|alpha then additive|one instance per hit",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-014,vfx,dice machine burst,game_machine_dice_VFX_3,analysis/visual-study/unity-samples/vfx/sharedassets4__game_machine_dice_VFX_3.png,"Point filter|reuse only for dice-machine result",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-015,player,player sword battle set,HEROLL current sword hero prefab,SliceDice/analysis/heroll/assets-inventory/objects.csv,"extract sprite/anim clips from owning Unity bundle|Point filter|preserve pivots",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-016,enemy,melee family,enemy_sword_0|enemy_sword_1|enemy_sword_2|enemy_sword_3|enemy_sword_4,SliceDice/analysis/heroll/assets-inventory/objects.csv,"map idle move attack hit death by prefab key",internal-only HEROLL asset,SOURCE_CONFIRMED
|
||||
AS-017,enemy,straight family,enemy_straight_0|enemy_straight_1,SliceDice/analysis/heroll/assets-inventory/objects.csv,"map projectile origin anchor explicitly",internal-only HEROLL asset,SOURCE_CONFIRMED
|
||||
AS-018,enemy,curved family,enemy_curved_0|enemy_curved_1,SliceDice/analysis/heroll/assets-inventory/objects.csv,"separate projectile and ground-target sprites",internal-only HEROLL asset,SOURCE_CONFIRMED
|
||||
AS-019,enemy,rush family,enemy_rush_0|enemy_rush_1,SliceDice/analysis/heroll/assets-inventory/objects.csv,"separate prepare rush collision recovery clips",internal-only HEROLL asset,SOURCE_CONFIRMED
|
||||
AS-020,boss,boss prefab,boss_0|Board_Boss001,SliceDice/analysis/heroll/assets-inventory/objects.csv,"preserve source scale ratios|separate skill_0 timeline",internal-only HEROLL asset,SOURCE_CONFIRMED
|
||||
AS-021,boss,boss start sprite,boss_start,analysis/visual-study/unity-samples/character_boss/sharedassets3__boss_start.png,"Point filter|one-shot entrance",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-022,boss,boss idle reference,014_boss_idle_1|001_boss_idle_2,analysis/visual-study/unity-samples/character_boss,"Point filter|preserve frame duration",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-023,boss,boss attack reference,019_boss_attack_8|001_boss_attack_2,analysis/visual-study/unity-samples/character_boss,"Point filter|damage tick authored separately",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
AS-024,event,smithy installation icon,Installtile_smithy,SliceDice/analysis/heroll/assets-inventory/objects.csv,"Point filter|green good-event frame",internal-only HEROLL asset,SOURCE_CONFIRMED
|
||||
AS-025,event,recovery installation icon,Installtile_healing,SliceDice/analysis/heroll/assets-inventory/objects.csv,"Point filter|green good-event frame",internal-only HEROLL asset,SOURCE_CONFIRMED
|
||||
AS-026,event,legend installation icon,Installtile_legend,SliceDice/analysis/heroll/assets-inventory/objects.csv,"Point filter|gold inner cue plus shape",internal-only HEROLL asset,SOURCE_CONFIRMED
|
||||
AS-027,event,dig installation icon,Installtile_corpse,SliceDice/analysis/heroll/assets-inventory/objects.csv,"Point filter|do not gore-enhance",internal-only HEROLL asset,SOURCE_CONFIRMED
|
||||
AS-028,event,chest installation icon,Installtile_treasure_chest,SliceDice/analysis/heroll/assets-inventory/objects.csv,"Point filter|closed/open frames",internal-only HEROLL asset,SOURCE_CONFIRMED
|
||||
AS-029,event,minigame icons,Installtile_lotto|Installtile_blackjack|Installtile_clwa|Installtile_goldCarriage|Installtile_dice_machine|Installtile_bubble_shooter,SliceDice/analysis/heroll/assets-inventory/objects.csv,"Point filter|each retains unique silhouette",internal-only HEROLL asset,SOURCE_CONFIRMED
|
||||
AS-030,audio,functional SFX set,HEROLL extracted FMOD/audio entries,SliceDice/analysis/heroll/assets-inventory,"decode for local prototype only|no BGM banks|mono short clips preferred",internal-only HEROLL asset,RUNTIME_OBSERVED
|
||||
|
@@ -0,0 +1,33 @@
|
||||
blessing_id,source_id,string_id,enabled,grade_rule,prob_weight,stackable,linear_value,values,after_plus_value,time_value,max_count,stat_value,need_ids,icon,effect_contract,source_tag
|
||||
AUG-100,100,AttackShield,true,SCALABLE,10,false,6,6,,-1,1,,,a_0,on each successful basic attack grant shield 6,SOURCE_CONFIRMED
|
||||
AUG-102,102,CritHeal,true,SCALABLE,10,true,15,15|20|25|30,1,-1,999,,,a_2,on critical hit heal selected value percent of dealt damage,VALIDATION_ADDED
|
||||
AUG-103,103,CritDamageUp,true,SCALABLE,10,true,30,30|35|40,2,-1,999,,,a_3,add selected value percentage points to critical damage multiplier,VALIDATION_ADDED
|
||||
AUG-106,106,Revive,true,SCALABLE,10,false,4,4,,-1,100,,,a_6,on lethal damage revive once and restore 4 percent max health,VALIDATION_ADDED
|
||||
AUG-108,108,FirstAttackDMGUp,true,SCALABLE,10,true,70,70|80|90,1,-1,999,,,a_8,first player hit in battle deals selected percent bonus damage,VALIDATION_ADDED
|
||||
AUG-109,109,DMGUpAfterSecond,true,SCALABLE,10,true,30,30,2,3,999,,,a_9,after 2 seconds add 30 percent total damage for 3 seconds,VALIDATION_ADDED
|
||||
AUG-110,110,ASPUpAfterSecond,true,SCALABLE,10,true,30,30,2,3,999,,,a_10,after 2 seconds add 30 percent attack speed for 3 seconds,VALIDATION_ADDED
|
||||
AUG-111,111,DoubleHeal,true,SCALABLE,10,true,8,8,2,-1,999,,,a_11,healing events have 8 percent chance to resolve twice,VALIDATION_ADDED
|
||||
AUG-112,112,LowHPASPUp,true,SCALABLE,10,true,3,3,1,-1,999,,,a_12,while hp at or below 30 percent gain 30 percent attack speed,VALIDATION_ADDED
|
||||
AUG-113,113,BattleStartHeal,true,SCALABLE,10,false,10,10,1,-1,6,,,a_13,at PreBattle heal 10 percent max health,SOURCE_CONFIRMED
|
||||
AUG-114,114,LuckyHit,true,SCALABLE,10,false,4,4,1,-1,5,,,a_14,every fourth successful hit is guaranteed critical,VALIDATION_ADDED
|
||||
AUG-115,115,DiceSumDMGUp,true,SCALABLE,10,true,30,30|40|50,1,-1,999,,,a_15,if latest dice sum at least 9 add selected total damage percent for next battle,VALIDATION_ADDED
|
||||
AUG-116,116,DiceSumShield,true,SCALABLE,10,false,1,1,,-1,1,,,a_16,at PreBattle gain shield equal to latest dice sum,SOURCE_CONFIRMED
|
||||
AUG-117,117,KillHPUp,true,SCALABLE,10,true,10,5,1,-1,1,,,a_17,on enemy kill increase max health by 5 and heal same amount,VALIDATION_ADDED
|
||||
AUG-118,118,ReduceEnemyDEF,true,SCALABLE,10,true,30,10|20|30,1,-1,999,,,a_18,reduce all spawned enemy defense by selected percent,SOURCE_CONFIRMED
|
||||
AUG-121,121,EnemyCountStatUp,true,SCALABLE,10,true,8,8,1,-1,5,ALL,,a_20,for each enemy above one add 8 percent to attack health defense aim and evasion,VALIDATION_ADDED
|
||||
AUG-122,122,OddDiceCritUp,true,SCALABLE,10,true,20,20,2,-1,11,,,a_21,if latest move sum is odd add 20 critical rate for next battle,SOURCE_CONFIRMED
|
||||
AUG-124,124,KillInTimeGetGear,true,SCALABLE,10,false,0,0,,2.5,999,,,a_23,if battle ends within 2.5 seconds add one gear view opportunity,SOURCE_CONFIRMED
|
||||
AUG-125,125,HealAfterSecond,true,SCALABLE,10,true,20,20,2,5,6,,,a_24,every 5 seconds heal 20 flat health,SOURCE_CONFIRMED
|
||||
AUG-126,126,LightingEverySec,true,SCALABLE,10,true,10,10,1,1,21,,,a_25,every second deal 10 percent attack as true damage to all enemies,VALIDATION_ADDED
|
||||
AUG-132,132,KillForHeroGear,true,SCALABLE,10,false,10,10,1,-1,999,,,a_29,each kill adds 10 percent chance for one EPIC gear after battle,SOURCE_CONFIRMED
|
||||
AUG-147,147,KillAllEmeny,true,SCALABLE,10,false,,0,,-1,999,,,a_44,on acquire defeat all non-boss enemies in current battle; otherwise arm for next normal battle,VALIDATION_ADDED
|
||||
AUG-148,148,PointSupply,true,SCALABLE,10,false,20,20,,-1,999,,,a_45,on acquire grant 20 points,SOURCE_CONFIRMED
|
||||
AUG-149,149,DicePotionGet,true,SCALABLE,10,false,,0,,-1,5,,,a_46,on rolling doubles restore one potion charge up to max,VALIDATION_ADDED
|
||||
AUG-150,150,KillPotionGet,true,SCALABLE,10,true,10,10,1,-1,1,,,a_47,on enemy kill 10 percent chance restore one potion charge,SOURCE_CONFIRMED
|
||||
AUG-151,151,ShopFreeReroll,true,SCALABLE,10,true,1,1,1,-1,10,,,a_48,grant one free merchant reroll token,SOURCE_CONFIRMED
|
||||
AUG-152,152,EmptyTripleBuild,true,SCALABLE,10,false,2,2,,-1,999,,,a_49,when three empty tiles are passed consecutively install one random core good tile,VALIDATION_ADDED
|
||||
AUG-153,153,EvaHeal,true,SCALABLE,10,true,3,1|2|3,1,-1,10,,,a_50,on successful evade heal selected flat value,SOURCE_CONFIRMED
|
||||
AUG-154,154,BattleEndHeal,true,SCALABLE,10,true,10,10,1,-1,11,,,a_51,on normal battle victory heal 10 percent max health,SOURCE_CONFIRMED
|
||||
AUG-156,156,BurnEnemyDead,true,SCALABLE,10,true,150,150,5,-1,15,,FirstEnemyAllBurn,a_53,when a burning enemy dies deal 150 percent attack burn damage to all remaining enemies,VALIDATION_ADDED
|
||||
AUG-157,157,FirstEnemyAllBurn,true,SCALABLE,10,false,10,10,1,-1,10,,,a_54,first enemy hit applies burn 10 percent attack per second for 3 seconds,VALIDATION_ADDED
|
||||
AUG-THIRD-DIE,,ThirdDieReplace,true,LEGENDARY,1,false,1,1,,-1,1,,,a_third_die,after A and B roll generate C then replace A replace B or discard,VALIDATION_ADDED
|
||||
|
@@ -0,0 +1,18 @@
|
||||
contract,typescript_field,csv_file,csv_column,parse_rule,validation,error_policy,source_tag
|
||||
RunState,floor,floor-balance.csv,floor,"int 1..10","1<=floor<=10",reject_checkpoint,VALIDATION_ADDED
|
||||
RunState,points,shop-offers.csv,price,"signed int transaction result","points>=0",rollback_command,VALIDATION_ADDED
|
||||
RngStreamState,algorithm,none,none,"literal xoshiro128ss","state has 4 uint32",reject_checkpoint,VALIDATION_ADDED
|
||||
RngStreamState,index,technical_log,rngIndex,"uint64 serialized as decimal string","monotonic per stream",reject_checkpoint,VALIDATION_ADDED
|
||||
DdaState,sourceConditions,gameconfig.csv,dynamicDifficultyCondition,"int[3] 5|8|12","exact source array",force_tier0,SOURCE_CONFIRMED
|
||||
DdaState,sourceValues,gameconfig.csv,dynamicDifficultyValue,"int[6] 10|15|20|5|5|5","exact source array",force_tier0,SOURCE_CONFIRMED
|
||||
DiceRoll,dieA|dieB,none,none,"diceRng int inclusive 1..6","36 ordered outcomes equal",abort_roll,VALIDATION_ADDED
|
||||
BattleSnapshot,enemies,floor-balance.csv|monsterinfo.csv,combat columns,"base × prefab × dda","finite nonnegative stats",abort_battle,SOURCE_CONFIRMED
|
||||
GearInstance,catalogId,gear-catalog.csv,catalog_id,int foreign key,"exactly 40 valid ids",reject_reward,SOURCE_CONFIRMED
|
||||
GearInstance,statusId|value,gear-status.csv,status_id|base_min|base_max|plus_value,"uniform base + plus×rank","finite and display precision",reject_reward,VALIDATION_ADDED
|
||||
BlessingInstance,blessingId,blessings.csv,blessing_id,string foreign key,"exactly 32 ids",reject_reward,SOURCE_CONFIRMED
|
||||
PotionState,countLevel|healLevel|healAmount,potions.csv,level|value,"lookup HEAL_LEVEL value as flat HP","count 1..10 healAmount 50..650",reject_purchase,SOURCE_CONFIRMED
|
||||
BossGaugeState,enabled|runtimeGauge|mode|playerRolls|countdownRemaining,stage-boss-rules.csv,bossSpawnRule|bossSpawnRuleValue,"enabled only for DiceGauge; gauge clamp 0..100; BUILDUP 0..2 then COUNTDOWN 4..0","stage0 enabled=false; DiceGauge increment=50 and 2+4 observed cycle",reject_checkpoint,RUNTIME_OBSERVED
|
||||
ShopState,fixedOfferIds|randomOfferIds,shop-offers.csv,offer_id,"eligible filtered sample","3 fixed + <=3 random unique",rollback_reroll,SOURCE_CONFIRMED
|
||||
EventInstance,tileId,events.csv,tile_id,"int or MERCHANT","known event and legal tile",rollback_install,SOURCE_CONFIRMED
|
||||
RewardBoostTransaction,placementId,reward-boosts.csv,placement_id,string foreign key,"event placement matches",reject_transaction,VALIDATION_ADDED
|
||||
Checkpoint,checksum,none,none,"SHA-256 canonical JSON without checksum","64 lowercase hex",load_backup,VALIDATION_ADDED
|
||||
|
@@ -0,0 +1,15 @@
|
||||
event_id,tile_id,string_id,natural_core,purchasable,min_floor,base_rule,source_values,source_tables,boost_placement,instance_reset,source_tag
|
||||
EV-MERCHANT,MERCHANT,Merchant,true,false,3,"show 3 fixed upgrade offers and 3 random offers; floors 3|6|9; reroll cost 3+2*n",3|6|9,ingameshoplist.csv|gameconfig.csv,BOOST_MERCHANT_POINTS,new merchant per generated tile,VALIDATION_ADDED
|
||||
EV-137,137,GoodEventSmithy,true,true,1,"choose equipped gear; direct claim or upgrade; success by quality 80|50|30|10; failure destroys and salvages",80|50|30|10,newtile.csv,BOOST_SMITHY_RATE,new visit creates new event instance,SOURCE_CONFIRMED
|
||||
EV-138,138,GoodEventRecovery,true,true,1,"heal 30 percent max health capped at max",30,newtile.csv,BOOST_CAMPFIRE_HEAL_TILE,new visit creates new event instance,VALIDATION_ADDED
|
||||
EV-139,139,GoodEventLegendGear,false,true,1,"create one LEGENDARY rank3 gear view opportunity",3,newtile.csv,,new visit creates new event instance,SOURCE_CONFIRMED
|
||||
EV-140,140,GoodEventHeroBody,true,true,1,"5x5 digging board; seed-select source shapes; base 5 digs; reveal all cells in selected stamp and grant revealed rewards",5|5|5,newtile.csv|herobodyminigame.csv,BOOST_DIG_EXTRA,new visit creates new board,VALIDATION_ADDED
|
||||
EV-141,141,GoodEventChest,true,true,1,"open chest and atomically generate three gear view opportunities using normal loot generator",3,newtile.csv|GoodEventChest native|owner screenshot,BOOST_CHEST_REROLL,new visit creates new chest,SOURCE_CONFIRMED
|
||||
EV-142,142,GoodEventLotto,false,true,4,"pay 5 points for one draw; outcomes NONE|POINT5|POINT10|RANDOM_GEAR with weights 50|30|15|5",5|50|30|15|5,newtile.csv|ingameshoplist.csv,,one draw per visit,VALIDATION_ADDED
|
||||
EV-143,143,GoodEventBlackJack,false,true,4,"start with 3 D6; player may hit or stand to approach 21; first win is 10 points; collect or rematch with streak multipliers 2.5|5|10; loss forfeits unclaimed streak reward",3|10|2.5|5|10,newtile.csv|BlackJackView native|Tiles wiki,,new dice stream per hand; streak persists only through rematch,SOURCE_CONFIRMED_PARTIAL_TIE
|
||||
EV-144,144,GoodEventPowerStar,true,true,4,"draw one blessing card from 31 source plus ThirdDieReplace pool; accept or one forced replacement",0,newtile.csv|augment.csv,BOOST_BLESSING_REROLL,new visit creates new blessing offer,SOURCE_CONFIRMED
|
||||
EV-151,151,GoodEventClawGrab,false,true,1,"spawn 20 capsules with one guaranteed Legendary capsule at bottom and weighted normal pool; base 1 attempt; 15 second input window; reward only when capsule reaches collection trigger",1|15,newtile.csv|clawcraneminigame.csv|PhysicsClawGame native,BOOST_CLAW_ATTEMPT,new cabinet per visit,SOURCE_CONFIRMED
|
||||
EV-152,152,GoodEventGoldCarriage,false,true,1,"show 4 closed carts; choose one; reward tier selected by hp ratio thresholds 75|30|0 and source point potion gear caps",4,newtile.csv|goldcarriage.csv,,new four-cart layout per visit,VALIDATION_ADDED
|
||||
EV-164,164,GoodEventDiceMachine,false,true,4,"roll three fair D6 displayed as zero-based match keys; exact pair/triple patterns grant point gear free-roll blessing tile or potion per source table; unmatched grants none",,newtile.csv|dicemachine.csv,,one roll per visit,SOURCE_CONFIRMED
|
||||
EV-166,166,GoodEventBubbleShooter,false,true,4,"seed-select one of 6 source maps; base 3 projectiles; 18 second round; match 3 colors; clear thresholds grant 3|5|10 points",3|18|0|3|5|10,newtile.csv|bubbleshooterminigame.csv,BOOST_BUBBLE_SHOT,new board per visit,VALIDATION_ADDED
|
||||
EV-169,169,GoodEventSpito,false,true,4,"preselect point prize with weights 10|50|40; scratch cover until reveal threshold; grant exactly one prize 30|10|1",30|10|1|10|50|40,newtile.csv|SpitoEventView|ScratchPainter|owner screenshot,,new card per visit,SOURCE_CONFIRMED
|
||||
|
@@ -0,0 +1,21 @@
|
||||
evidence_id,title,source_type,source_ref,sha256,repro,raw_excerpt,finding_id,finding,design_id,design_ref,confidence
|
||||
E-001,第1关阶段配置,file,SliceDice/analysis/heroll/resource-tables/extracted/csv/stageinfo.csv,C71617B18829B5F07838BD725C946317932811E6C28A57BA86B159BCFEDC41DA,"读取stage=0且EPlayMode=Normal行","Grass|Default|10|boss_0|skill_0|7",F-001,第1关为Grass主题10层并使用boss_0,D-001,GDD §3.1,high
|
||||
E-002,第1关十层敌方基础值,file,SliceDice/analysis/heroll/resource-tables/extracted/csv/board.csv,5AFB1F02C1508B6EE5B83EC03B40785EA817563323265EB606DC0105C4CDB026,"筛选Stage=0且loop=0..9","attack 3..60|health 30..159|defense 0..13.5",F-002,十层基础属性可逐行还原,D-002,floor-balance.csv,high
|
||||
E-003,玩家裸装属性,file,SliceDice/analysis/heroll/resource-tables/extracted/csv/playerdefaultstat.csv,0172D9DE8C26657507C000667C1CEC2A951489D905D9CA5C6476A192F911ABEA,"读取全部9行","range5|move40|attack10|attackSpeed100|health150",F-003,验证版可以从裸装固定属性开始,D-003,GDD §5.1,high
|
||||
E-004,怪物组和攻击角色,file,SliceDice/analysis/heroll/resource-tables/extracted/csv/monstergroup.csv,C6259715E9EC02A19C99E00950DFA7D036F5CA460BAC752965630FA76B572904,"读取group_10与boss_0","SWORD,SWORD,STRAIGHT,STRAIGHT|boss_0",F-004,源码确认普通组和Boss入口,D-004,GDD §5.2,high
|
||||
E-005,敌人与Boss倍率,file,SliceDice/analysis/heroll/resource-tables/extracted/csv/monsterinfo.csv,EBC3D2F4CFE57E76277DAD786E150436CE9203E1AD1A4C92EC16622487CC9E40,"筛选enemy_sword/straight/curved/rush与boss_0","boss attack×2|health×4|attackSpeed×1",F-005,四种攻击角色有独立prefab倍率,D-005,GDD §5.2,high
|
||||
E-006,启用祝福,file,SliceDice/analysis/heroll/resource-tables/extracted/csv/augment.csv,E0B14AF4709E3F0D548973E124F679EF091589D7C99B0E35DE78C628A73D032A,"筛选isEnabled=True","31 rows",F-006,源码祝福池为31个启用项,D-006,blessings.csv,high
|
||||
E-007,局内装备目录,file,SliceDice/analysis/heroll/resource-tables/extracted/csv/weapondrop.csv,AF8C97D022A83C1FF898D33FC541F157CAD1DC6C5732FF57D61A502E82914809,"按EMonoWeapon分组","4 slots × 10",F-007,完整局内装备目录为40件,D-007,gear-catalog.csv,high
|
||||
E-008,装备属性表,file,SliceDice/analysis/heroll/resource-tables/extracted/csv/weaponstatus.csv,1F40ED34DD80B1E1674C7E03266E488141491B22527215C3D5C2671CC119A0B6,"读取全部13行","13 status definitions",F-008,装备词条有13类基础区间,D-008,gear-status.csv,high
|
||||
E-009,局内商店表,file,SliceDice/analysis/heroll/resource-tables/extracted/csv/ingameshoplist.csv,7D32A03BC4D82177999027A5B2A7171888C4C2B3831B8FC1B2F141F02D74ABC8,"过滤数值Id并按itemType计数","96 rows:12 tile|31 augment|9 potion|13 stat|30 fixed|1 lotto",F-009,源码局内商品恰好96条,D-009,shop-offers.csv,high
|
||||
E-010,药水等级曲线,file,SliceDice/analysis/heroll/resource-tables/extracted/csv/potion.csv,8049B5B835636F6A6FFE73B14D7CDB75FB8B48C18F4DF4D53DB0C8B3D18EFA99,"按type读取","count 1..10|heal 50..650",F-010,药水容量和治疗可直接配置,D-010,potions.csv,high
|
||||
E-011,药水强化,file,SliceDice/analysis/heroll/resource-tables/extracted/csv/potionaugment.csv,A8585434956E68A5C2E273809BD010DFC57531C642A83B75B2B5A1FA79987095,"筛选isEnabled=True","9 enabled|GuaranteedDouble disabled",F-011,验证版药水强化池为9,D-011,potions.csv,high
|
||||
E-012,全局玩法参数,file,SliceDice/analysis/heroll/resource-tables/extracted/csv/gameconfig.csv,A57510DEDE13DAF1A597DE1EE73E47B0ABD830D96719DAD24B2CA7370E8DFE3D,"读取BaseCritical EnemyDamgeRange starsPerAugment augmentRatio shopReroll DDA","150|15|stars|50,30,20,5|3,2|DDA arrays",F-012,关键权重和DDA原始数组已确认但语义方法体不足,D-012,GDD §5.4-5.5,high
|
||||
E-013,用户流程录像,file,参考资料/功能浏览与关卡流程.mp4,382875D92932E77091DE698476310F14B017575BFE3C7F0CAB95889E0F3F1D97,"本地播放并按时间轴观察","UI and run flow reference",F-013,视频可辅助界面与流程但不证明隐藏公式,D-013,ui-screens.csv,medium
|
||||
E-014,用户抽卡录像,file,参考资料/抽卡.mp4,F7789EB239D1B4D19CCED8D48B462B8377BB350248CB57AA58356726F0096507,"本地播放并按时间轴观察","out-of-scope gacha reference",F-014,抽卡属于本验证版排除项,D-014,GDD §2.4,high
|
||||
E-015,截图说明,file,参考资料/截图说明.jpg,F3C468A2D91E5D4145EC91BDA07855827635371F36BA8E7021C790F4ED0CBA08,"打开原图读取标注","user annotations",F-015,标注用于解释HEROLL界面且不覆盖源码数值,D-015,ui-screens.csv,medium
|
||||
E-016,IL2CPP元数据,file,SliceDice/analysis/heroll/metadata/assembly-csharp.json,BA6D3F4BD91B0D3977EB1229AB46FE563118CC55793346796F93816BA7FEF526,"搜索BoardController BattleController DDA和formula相关字段","types and method names without recovered bodies",F-016,调用边界可确认但最终战斗和DDA方法体不可据此恢复,D-016,formula-fit.csv,high
|
||||
E-017,系统摘要,file,SliceDice/analysis/heroll/metadata-analysis/system-summary.json,C29F1ED5039C1CCC88F5FA20B98CCE840FA78957ED8A7D7F60AFB03161269236,"读取玩法系统类型与入口摘要","Board/Battle/Shop/Event boundaries",F-017,可按领域拆分Cocos模块,D-017,GDD §12.1,high
|
||||
E-018,提取资产清单,file,analysis/visual-study/unity-samples/manifest.csv,2CECF139A96DE0389B9D9772F9DFD365145B18E3450A439CCB87464365107C6A,"读取exported条目","Grass scene|boss|UI|VFX samples",F-018,HEROLL内部原型资产可支撑玩法验证表现,D-018,assets.csv,high
|
||||
E-019,桌面试玩棋盘与事件,runtime,Heroll : Dice Roguelike desktop session,n/a,"启动桌面快捷方式并进入关卡","point0→chest→smithy80%→board roll→auto battle victory",F-019,实机确认宝箱铁匠铺点数与自动战斗表现但账号HP329含局外加成,D-019,formula-fit.csv,high
|
||||
E-020,运行时启动日志,log,analysis/gameplay-monetization/evidence/runtime-session-2026-08-13.md,n/a,"按文档复现启动并检索Board与boss","Normal|boss_0|Board init|Ingame Save",F-020,棋盘场景与boss_0运行链存在,D-020,GDD §3,high
|
||||
|
@@ -0,0 +1,11 @@
|
||||
floor,source_loop,monster_group,attack_power,health,defense,attack_speed,evasion,aim,life_steal,heal_on_entry_pct,good_tiles_injected,merchant_present,boss_after_floor,source_tag
|
||||
1,0,10,3,30,0,1,12,0,0,0,0,false,false,SOURCE_CONFIRMED
|
||||
2,1,10,6,38,1.5,1,23,7,0,30,2,false,false,SOURCE_CONFIRMED
|
||||
3,2,10,8,43,3,1,34,14,0,30,2,true,false,SOURCE_CONFIRMED
|
||||
4,3,10,12,58,4.5,1,45,21,0,30,2,false,false,SOURCE_CONFIRMED
|
||||
5,4,10,17,67,6,1,56,28,0,30,2,false,false,SOURCE_CONFIRMED
|
||||
6,5,10,22,80,7.5,1,67,35,0,30,2,true,false,SOURCE_CONFIRMED
|
||||
7,6,10,30,109,9,1,78,42,0,30,2,false,false,SOURCE_CONFIRMED
|
||||
8,7,10,39,125,10.5,1,89,49,0,30,2,false,false,SOURCE_CONFIRMED
|
||||
9,8,10,46,141,12,1,100,56,0,30,2,true,false,SOURCE_CONFIRMED
|
||||
10,9,10,60,159,13.5,1,111,63,0,30,2,false,true,SOURCE_CONFIRMED
|
||||
|
@@ -0,0 +1,15 @@
|
||||
gate_id,system,parameter_or_function,validation_baseline,source_evidence,capture_protocol,min_samples,acceptance,current_status,release_effect
|
||||
FIT-001,damage,player_normal_damage,"max(1,round(atk*100/(100+def)*U[0.85,1.15]))",E-003|E-008|E-012,"裸装stage0;录属性页与至少30次非暴击命中;画面需同时包含敌人层号和伤害数字",30,median_APE<=5%,BLOCKED_CLEAN_CAPTURE,prototype_allowed_release_claim_blocked
|
||||
FIT-002,damage,enemy_normal_damage,"same formula using floor/prefab attack",E-002|E-005|E-012,"无局外减伤;floor1/floor5/floor10各录30次敌人非暴击命中与玩家掉血前后",90,median_APE<=5%,BLOCKED_CLEAN_CAPTURE,prototype_allowed_release_claim_blocked
|
||||
FIT-003,defense,mitigation,"100/(100+defense)",E-008|E-016,"同层同敌人;分别使用0防御和一件已知防御装备;各30次命中",60,paired_median_APE<=5%,BLOCKED_CLEAN_CAPTURE,prototype_allowed_release_claim_blocked
|
||||
FIT-004,hit,hit_chance,"clamp(0.05,0.95,(100+aim-evasion)/100)",E-002|E-003|E-008,"固定敌人;记录裸装和已知Aim装备各200次攻击的命中/闪避",400,prediction_inside_Wilson95,BLOCKED_CLEAN_CAPTURE,prototype_allowed_release_claim_blocked
|
||||
FIT-005,evasion,evasion_interaction,"same hit function with defender evasion",E-002|E-008,"固定玩家命中;0闪避和已知闪避值各200次敌人攻击",400,prediction_inside_Wilson95,BLOCKED_CLEAN_CAPTURE,prototype_allowed_release_claim_blocked
|
||||
FIT-006,critical,critical_multiplier,1.50,E-012,"装备已知暴击率;记录至少30组相同攻击/防御下普通与暴击伤害",30,median_ratio_APE<=5%,BLOCKED_CLEAN_CAPTURE,prototype_allowed_release_claim_blocked
|
||||
FIT-007,life_steal,heal_from_damage,"floor(finalDamage*lifeStealPct/100)",E-003|E-008,"降低玩家生命并装备已知吸血;记录30次伤害与同步回血",30,median_APE<=5%,BLOCKED_CLEAN_CAPTURE,prototype_allowed_release_claim_blocked
|
||||
FIT-008,attack_speed,player_attack_interval,"max(0.15,1/(attackSpeed/100))",E-003|E-008,"1x速度;裸装与已知攻速装备各录连续20次攻击时间戳",40,median_interval_APE<=5%,BLOCKED_CLEAN_CAPTURE,prototype_allowed_release_claim_blocked
|
||||
FIT-009,attack_speed,enemy_attack_interval,"board.attackSpeed*prefab.multiplier",E-002|E-005,"同一敌人连续20次攻击;包含完整前摇和命中帧",20,median_interval_APE<=5%,BLOCKED_CLEAN_CAPTURE,prototype_allowed_release_claim_blocked
|
||||
FIT-010,battle_duration,normal_battle,"deterministic simulation",E-002|E-003|E-005,"裸装或完整记录装备/祝福;floor1/floor5/floor10各10场;从Fight首帧到PostBattle",30,median_APE<=10%,BLOCKED_CLEAN_CAPTURE,release_candidate_false
|
||||
FIT-011,battle_duration,boss_0,"stage0 floor10 + boss multipliers",E-001|E-002|E-005,"完整记录进Boss前属性/装备/祝福与10场Boss战;不手动用药或标记用药tick",10,median_APE<=10%,BLOCKED_CLEAN_CAPTURE,release_candidate_false
|
||||
FIT-012,DDA,condition_and_value_mapping,"tier0 multipliers=1 until fitted",E-012|E-016,"用同一seed连续制造<=5s 5-8s 8-12s >12s战斗;每次录下一战敌方属性和技术日志",24,exact_transition_match,BLOCKED_CLEAN_CAPTURE,dda_forced_tier0
|
||||
FIT-013,potion,potion_heal_and_augments,"potions.csv validation semantics",E-010|E-011,"每个强化单独启用;录使用前后HP/护盾/次数和后续一次攻击或战斗结束",9,exact_state_delta_match,BLOCKED_CLEAN_CAPTURE,prototype_uses_validation_semantics
|
||||
FIT-014,points,point_sources_and_salvage,"grade salvage=1|2|4|8|16",E-007|E-009|E-019,"分别分解五品质装备并录余额前后;再录移动 战斗 宝箱各一次",8,exact_delta_match,BLOCKED_CLEAN_CAPTURE,prototype_uses_validation_values
|
||||
|
@@ -0,0 +1,41 @@
|
||||
catalog_id,string_id,slot,icon_name,title_key,index_in_slot,source_tag
|
||||
100,ingame_weapon_01,WEAPON,ingame_weapon_icon_01,ingame_weapon_01_title,1,SOURCE_CONFIRMED
|
||||
101,ingame_weapon_02,WEAPON,ingame_weapon_icon_02,ingame_weapon_02_title,2,SOURCE_CONFIRMED
|
||||
102,ingame_weapon_03,WEAPON,ingame_weapon_icon_03,ingame_weapon_03_title,3,SOURCE_CONFIRMED
|
||||
103,ingame_weapon_04,WEAPON,ingame_weapon_icon_04,ingame_weapon_04_title,4,SOURCE_CONFIRMED
|
||||
104,ingame_weapon_05,WEAPON,ingame_weapon_icon_05,ingame_weapon_05_title,5,SOURCE_CONFIRMED
|
||||
105,ingame_weapon_06,WEAPON,ingame_weapon_icon_06,ingame_weapon_06_title,6,SOURCE_CONFIRMED
|
||||
106,ingame_weapon_07,WEAPON,ingame_weapon_icon_07,ingame_weapon_07_title,7,SOURCE_CONFIRMED
|
||||
107,ingame_weapon_08,WEAPON,ingame_weapon_icon_08,ingame_weapon_08_title,8,SOURCE_CONFIRMED
|
||||
108,ingame_weapon_09,WEAPON,ingame_weapon_icon_09,ingame_weapon_09_title,9,SOURCE_CONFIRMED
|
||||
109,ingame_weapon_10,WEAPON,ingame_weapon_icon_10,ingame_weapon_10_title,10,SOURCE_CONFIRMED
|
||||
113,ingame_ring_01,RING,ingame_ring_icon_01,ingame_ring_01_title,1,SOURCE_CONFIRMED
|
||||
114,ingame_ring_02,RING,ingame_ring_icon_02,ingame_ring_02_title,2,SOURCE_CONFIRMED
|
||||
115,ingame_ring_03,RING,ingame_ring_icon_03,ingame_ring_03_title,3,SOURCE_CONFIRMED
|
||||
116,ingame_ring_04,RING,ingame_ring_icon_04,ingame_ring_04_title,4,SOURCE_CONFIRMED
|
||||
117,ingame_ring_05,RING,ingame_ring_icon_05,ingame_ring_05_title,5,SOURCE_CONFIRMED
|
||||
118,ingame_ring_06,RING,ingame_ring_icon_06,ingame_ring_06_title,6,SOURCE_CONFIRMED
|
||||
119,ingame_ring_07,RING,ingame_ring_icon_07,ingame_ring_07_title,7,SOURCE_CONFIRMED
|
||||
120,ingame_ring_08,RING,ingame_ring_icon_08,ingame_ring_08_title,8,SOURCE_CONFIRMED
|
||||
121,ingame_ring_09,RING,ingame_ring_icon_09,ingame_ring_09_title,9,SOURCE_CONFIRMED
|
||||
122,ingame_ring_10,RING,ingame_ring_icon_10,ingame_ring_10_title,10,SOURCE_CONFIRMED
|
||||
129,ingame_gauntlet_01,GAUNTLET,ingame_gauntlet_icon_01,ingame_gauntlet_01_title,1,SOURCE_CONFIRMED
|
||||
130,ingame_gauntlet_02,GAUNTLET,ingame_gauntlet_icon_02,ingame_gauntlet_02_title,2,SOURCE_CONFIRMED
|
||||
131,ingame_gauntlet_03,GAUNTLET,ingame_gauntlet_icon_03,ingame_gauntlet_03_title,3,SOURCE_CONFIRMED
|
||||
132,ingame_gauntlet_04,GAUNTLET,ingame_gauntlet_icon_04,ingame_gauntlet_04_title,4,SOURCE_CONFIRMED
|
||||
133,ingame_gauntlet_05,GAUNTLET,ingame_gauntlet_icon_05,ingame_gauntlet_05_title,5,SOURCE_CONFIRMED
|
||||
134,ingame_gauntlet_06,GAUNTLET,ingame_gauntlet_icon_06,ingame_gauntlet_06_title,6,SOURCE_CONFIRMED
|
||||
135,ingame_gauntlet_07,GAUNTLET,ingame_gauntlet_icon_07,ingame_gauntlet_07_title,7,SOURCE_CONFIRMED
|
||||
136,ingame_gauntlet_08,GAUNTLET,ingame_gauntlet_icon_08,ingame_gauntlet_08_title,8,SOURCE_CONFIRMED
|
||||
137,ingame_gauntlet_09,GAUNTLET,ingame_gauntlet_icon_09,ingame_gauntlet_09_title,9,SOURCE_CONFIRMED
|
||||
138,ingame_gauntlet_10,GAUNTLET,ingame_gauntlet_icon_10,ingame_gauntlet_10_title,10,SOURCE_CONFIRMED
|
||||
145,ingame_boots_01,BOOTS,ingame_boots_icon_01,ingame_boots_01_title,1,SOURCE_CONFIRMED
|
||||
146,ingame_boots_02,BOOTS,ingame_boots_icon_02,ingame_boots_02_title,2,SOURCE_CONFIRMED
|
||||
147,ingame_boots_03,BOOTS,ingame_boots_icon_03,ingame_boots_03_title,3,SOURCE_CONFIRMED
|
||||
148,ingame_boots_04,BOOTS,ingame_boots_icon_04,ingame_boots_04_title,4,SOURCE_CONFIRMED
|
||||
149,ingame_boots_05,BOOTS,ingame_boots_icon_05,ingame_boots_05_title,5,SOURCE_CONFIRMED
|
||||
150,ingame_boots_06,BOOTS,ingame_boots_icon_06,ingame_boots_06_title,6,SOURCE_CONFIRMED
|
||||
151,ingame_boots_07,BOOTS,ingame_boots_icon_07,ingame_boots_07_title,7,SOURCE_CONFIRMED
|
||||
152,ingame_boots_08,BOOTS,ingame_boots_icon_08,ingame_boots_08_title,8,SOURCE_CONFIRMED
|
||||
153,ingame_boots_09,BOOTS,ingame_boots_icon_09,ingame_boots_09_title,9,SOURCE_CONFIRMED
|
||||
154,ingame_boots_10,BOOTS,ingame_boots_icon_10,ingame_boots_10_title,10,SOURCE_CONFIRMED
|
||||
|
@@ -0,0 +1,14 @@
|
||||
status_id,string_id,stat_type,ref_status,base_min,base_max,plus_value,is_percent,decimals,combat_power_multiplier,effect_contract,source_tag
|
||||
100,ATTACK_POWER,NORMAL,,3,7,5,false,0,4,add attackPower,SOURCE_CONFIRMED
|
||||
101,HEALTH,NORMAL,,20,40,50,false,0,1,add maxHealth and currentHealth by same delta,SOURCE_CONFIRMED
|
||||
102,AIM,NORMAL,,5,15,10,false,0,1.5,add aim,SOURCE_CONFIRMED
|
||||
103,DEFENSE,NORMAL,,1.5,2.5,1.5,false,1,2,add defense,SOURCE_CONFIRMED
|
||||
104,EVASION,UTIL,,14,20,7,false,0,1,add evasion,SOURCE_CONFIRMED
|
||||
105,REGEN_PER_SECOND,HEAL,HEALTH,1,2,2.4,false,1,3,heal value each logic second during Fight,SOURCE_CONFIRMED
|
||||
106,TOTAL_DAMAGE,ATTACK,ATTACK_POWER,1,2,1,false,1,5,add totalDamagePct despite source IsPercent false; validation semantic,VALIDATION_ADDED
|
||||
107,TRUE_DAMAGE,ATTACK,ATTACK_POWER,1,2.5,1.7,false,1,2,add flat trueDamage,SOURCE_CONFIRMED
|
||||
108,KILL_HEAL,HEAL,HEALTH,6,9,2.5,false,1,3,heal flat value on enemy death,SOURCE_CONFIRMED
|
||||
109,COUNTER_ATTACK,UTIL,,7,9,1.5,true,1,1,on hit received roll percent then immediate basic counter,SOURCE_CONFIRMED
|
||||
110,ATTACK_SPEED,UTIL,,7,9,1.4,true,1,2,add attackSpeed percent,SOURCE_CONFIRMED
|
||||
111,CRITICAL_RATE,ATTACK,,10,12,1.5,true,1,2,add criticalRate percent,SOURCE_CONFIRMED
|
||||
112,LIFE_STEAL,HEAL,,4.5,7.5,1.5,true,1,2,add lifeSteal percent,SOURCE_CONFIRMED
|
||||
|
@@ -0,0 +1,41 @@
|
||||
record_id,record_type,source_id,string_id,level,value,value_a,value_b,enabled,effect_contract,source_tag
|
||||
COUNT-0,COUNT_LEVEL,100,Potion_CountLevel_0,0,1,,,true,max charges 1,SOURCE_CONFIRMED
|
||||
COUNT-1,COUNT_LEVEL,101,Potion_CountLevel_1,1,2,,,true,max charges 2,SOURCE_CONFIRMED
|
||||
COUNT-2,COUNT_LEVEL,102,Potion_CountLevel_2,2,3,,,true,max charges 3,SOURCE_CONFIRMED
|
||||
COUNT-3,COUNT_LEVEL,103,Potion_CountLevel_3,3,4,,,true,max charges 4,SOURCE_CONFIRMED
|
||||
COUNT-4,COUNT_LEVEL,104,Potion_CountLevel_4,4,5,,,true,max charges 5,SOURCE_CONFIRMED
|
||||
COUNT-5,COUNT_LEVEL,105,Potion_CountLevel_5,5,6,,,true,max charges 6,SOURCE_CONFIRMED
|
||||
COUNT-6,COUNT_LEVEL,106,Potion_CountLevel_6,6,7,,,true,max charges 7,SOURCE_CONFIRMED
|
||||
COUNT-7,COUNT_LEVEL,107,Potion_CountLevel_7,7,8,,,true,max charges 8,SOURCE_CONFIRMED
|
||||
COUNT-8,COUNT_LEVEL,108,Potion_CountLevel_8,8,9,,,true,max charges 9,SOURCE_CONFIRMED
|
||||
COUNT-9,COUNT_LEVEL,109,Potion_CountLevel_9,9,10,,,true,max charges 10,SOURCE_CONFIRMED
|
||||
HEAL-0,HEAL_LEVEL,110,Potion_HealLevel_0,0,50,,,true,restore 50 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-1,HEAL_LEVEL,111,Potion_HealLevel_1,1,80,,,true,restore 80 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-2,HEAL_LEVEL,112,Potion_HealLevel_2,2,110,,,true,restore 110 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-3,HEAL_LEVEL,113,Potion_HealLevel_3,3,140,,,true,restore 140 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-4,HEAL_LEVEL,114,Potion_HealLevel_4,4,170,,,true,restore 170 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-5,HEAL_LEVEL,115,Potion_HealLevel_5,5,200,,,true,restore 200 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-6,HEAL_LEVEL,116,Potion_HealLevel_6,6,230,,,true,restore 230 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-7,HEAL_LEVEL,117,Potion_HealLevel_7,7,260,,,true,restore 260 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-8,HEAL_LEVEL,118,Potion_HealLevel_8,8,290,,,true,restore 290 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-9,HEAL_LEVEL,119,Potion_HealLevel_9,9,320,,,true,restore 320 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-10,HEAL_LEVEL,120,Potion_HealLevel_10,10,350,,,true,restore 350 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-11,HEAL_LEVEL,121,Potion_HealLevel_11,11,380,,,true,restore 380 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-12,HEAL_LEVEL,122,Potion_HealLevel_12,12,410,,,true,restore 410 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-13,HEAL_LEVEL,123,Potion_HealLevel_13,13,440,,,true,restore 440 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-14,HEAL_LEVEL,124,Potion_HealLevel_14,14,470,,,true,restore 470 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-15,HEAL_LEVEL,125,Potion_HealLevel_15,15,500,,,true,restore 500 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-16,HEAL_LEVEL,126,Potion_HealLevel_16,16,530,,,true,restore 530 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-17,HEAL_LEVEL,127,Potion_HealLevel_17,17,560,,,true,restore 560 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-18,HEAL_LEVEL,128,Potion_HealLevel_18,18,590,,,true,restore 590 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-19,HEAL_LEVEL,129,Potion_HealLevel_19,19,620,,,true,restore 620 flat HP,SOURCE_CONFIRMED
|
||||
HEAL-20,HEAL_LEVEL,130,Potion_HealLevel_20,20,650,,,true,restore 650 flat HP,SOURCE_CONFIRMED
|
||||
AUG-100,AUGMENT,100,Potion_AttackBoost,,,300,1,true,next successful player attack deals plus 300 percent damage; one hit,VALIDATION_ADDED
|
||||
AUG-101,AUGMENT,101,Potion_AutoCharge,,,1,-1,true,after winning a battle restore 1 potion charge up to max,VALIDATION_ADDED
|
||||
AUG-103,AUGMENT,103,Potion_LightningAttack,,,-1,-1,true,on potion use deal 100 percent attack true damage to all enemies,VALIDATION_ADDED
|
||||
AUG-104,AUGMENT,104,Potion_ExtraShield,,,1,-1,true,on potion use grant shield equal to actual heal amount,VALIDATION_ADDED
|
||||
AUG-105,AUGMENT,105,Potion_LowStockBoost,,,20,-1,true,if charge before use is 1 increase all potion healing by 20 percent,VALIDATION_ADDED
|
||||
AUG-106,AUGMENT,106,Potion_AdaptiveHealing,,,50,-1,true,if hp before use is at or below 50 percent increase healing by 50 percent,VALIDATION_ADDED
|
||||
AUG-107,AUGMENT,107,Potion_MaxHealthGain,,,5,-1,true,on use increase max health by 5 percent then heal,VALIDATION_ADDED
|
||||
AUG-108,AUGMENT,108,Potion_PercentHeal,,,10,-1,true,on use additionally heal 10 percent max health,VALIDATION_ADDED
|
||||
AUG-109,AUGMENT,109,Potion_GachaHeal,,,20,200,true,on use 20 percent chance add 200 flat healing,VALIDATION_ADDED
|
||||
|
@@ -0,0 +1,83 @@
|
||||
case_id,priority,area,setup,action,expected,automation,gate,source_tag
|
||||
QA-001,P0,scope,production-like local build,scan scenes scripts configs and packages,no account meta energy gacha IAP network real ad SDK story or BGM entry,static scan,release,VALIDATION_ADDED
|
||||
QA-002,P0,dice,fixed seed and fresh streams,simulate 1000000 rolls,36 ordered outcomes pass chi-square p>=0.01,node test,release,VALIDATION_ADDED
|
||||
QA-003,P0,dice,fixed seed,run 100 rolls twice,dieA dieB and RNG indices byte-identical,node test,release,VALIDATION_ADDED
|
||||
QA-004,P0,dice,all blessings and DDA tiers,compare base dice sequence to clean run,base A/B sequence unchanged,node test,release,VALIDATION_ADDED
|
||||
QA-005,P0,dice,ThirdDieReplace active,exercise replaceA replaceB discard,move sums correct and A/B log preserved,node test,release,VALIDATION_ADDED
|
||||
QA-006,P1,dice,normal roll,force doubles,no universal reward or reroll appears,node test,release,VALIDATION_ADDED
|
||||
QA-007,P0,board,position Tile39 and roll crosses Start,resolve move,exactly one floor transition and no second landing resolve,node test,release,VALIDATION_ADDED
|
||||
QA-008,P0,board,each floor 1..10,finish lap,floor increments and heal round(maxHp*0.30) capped,node test,release,VALIDATION_ADDED
|
||||
QA-009,P0,board,each floor transition with recorded zero-based loopIndex,inspect good-tile draw and install attempts,request count equals loopIndex plus2 and success count never exceeds request count,node test,release,SOURCE_CONFIRMED
|
||||
QA-010,P0,board,fresh account default eight-meta deck and fixed seed,initialize run and inspect first eight draws,all eight default metas appear once before reshuffle,node simulation,release,SOURCE_CONFIRMED
|
||||
QA-011,P0,board,seed suite 10000 runs,inspect merchant generation,merchant only and always on floors3 6 9,node simulation,release,VALIDATION_ADDED
|
||||
QA-012,P0,boss,complete floor10,resolve transition,boss_0 with skill_0 and cooldown7 starts,node test,release,SOURCE_CONFIRMED
|
||||
QA-013,P0,combat,naked run,create player snapshot,stats equal 5|40|10|100|150|0|0|0|5,node test,release,SOURCE_CONFIRMED
|
||||
QA-014,P0,combat,floor1..10,spawn base enemies,base stats match floor-balance.csv exactly,node test,release,SOURCE_CONFIRMED
|
||||
QA-015,P0,combat,floor10 boss spawn,inspect snapshot,multipliers attack2 hp4 defense1 speed1 evasion1 aim1 lifeSteal0,node test,release,SOURCE_CONFIRMED
|
||||
QA-016,P0,combat,controlled attack and defense,run deterministic hit,validation baseline formula matches exact integer result,node test,prototype,VALIDATION_ADDED
|
||||
QA-017,P0,combat,clean capture matrix,run fit report,player and enemy damage median APE<=5%,analysis script,release,VALIDATION_ADDED
|
||||
QA-018,P0,combat,clean capture matrix,run duration fit,floor bands and boss median APE<=10%,analysis script,release,VALIDATION_ADDED
|
||||
QA-019,P0,combat,hit and evasion capture,run Wilson check,predictions inside 95 percent intervals,analysis script,release,VALIDATION_ADDED
|
||||
QA-020,P0,combat,critical and lifeSteal capture,run fit,crit ratio and healing median APE<=5%,analysis script,release,VALIDATION_ADDED
|
||||
QA-021,P0,combat,attack speed capture,run interval fit,player and enemy median interval APE<=5%,analysis script,release,VALIDATION_ADDED
|
||||
QA-022,P0,DDA,tier0 normal run,roll dice and loot sequences,DDA does not alter dice shop loot or events,node test,release,VALIDATION_ADDED
|
||||
QA-023,P0,DDA,known state transitions,save reload,state arrays multipliers recentBattleMs and reason preserved,node test,release,VALIDATION_ADDED
|
||||
QA-024,P0,DDA,clean DDA captures,run transition matcher,exact transition and multiplier match,analysis script,release,VALIDATION_ADDED
|
||||
QA-025,P0,potion,Fight and one charge,double click same tick,only one use and one heal,node test,release,VALIDATION_ADDED
|
||||
QA-026,P0,potion,each count and heal level,load lookup,10 capacity and 21 heal records exact,node test,release,SOURCE_CONFIRMED
|
||||
QA-027,P0,potion,each of 9 augments,run isolated scripted scenario,state delta equals potions.csv contract,node test,release,VALIDATION_ADDED
|
||||
QA-028,P1,potion,GuaranteedDouble source row,scan pools and shop,disabled augment never appears,static scan,release,SOURCE_CONFIRMED
|
||||
QA-029,P0,gear,catalog load,count and group,exactly 40 with 10 per slot,node test,release,SOURCE_CONFIRMED
|
||||
QA-030,P0,gear,loot simulation 1000000,inspect quality counts,weights fit 210|60|28|4|1 within statistical tolerance,node simulation,release,SOURCE_CONFIRMED
|
||||
QA-031,P0,gear,100000 simulated runs,count view opportunities,P50 between6 and8,node simulation,release,VALIDATION_ADDED
|
||||
QA-032,P0,gear,slot empty and occupied,equip new gear,new flies in old leaves and old salvage points once,integration,release,VALIDATION_ADDED
|
||||
QA-033,P0,gear,batch with 3 unhandled,close modal,all 3 salvage in source order with no duplicate,integration,release,VALIDATION_ADDED
|
||||
QA-034,P0,gear,all 13 statuses,generate each,min max plus rank and display precision match,node test,release,SOURCE_CONFIRMED
|
||||
QA-035,P0,smithy,quality ranks0..3,run 100000 trials each,base success rates fit80|50|30|10,node simulation,release,SOURCE_CONFIRMED
|
||||
QA-036,P0,smithy,boost used ranks0..3,run 100000 trials each,boost rates fit100|80|50|30,node simulation,release,VALIDATION_ADDED
|
||||
QA-037,P0,smithy,force failure,resolve,equipment removed and pre-failure quality salvage once,node test,release,VALIDATION_ADDED
|
||||
QA-038,P0,blessing,load pool,count enabled,31 source plus ThirdDieReplace equals32,node test,release,SOURCE_CONFIRMED
|
||||
QA-039,P0,blessing,reveal card,use boost,replacement differs and only Accept remains,node test,release,VALIDATION_ADDED
|
||||
QA-040,P0,blessing,pool edge only same eligible,use boost,original accepted and transaction commits without loop,node test,release,VALIDATION_ADDED
|
||||
QA-041,P0,blessing,levels0..40,lookup star cost,index clamps to29 and sequence matches source,node test,release,SOURCE_CONFIRMED
|
||||
QA-042,P0,shop,load offers,count and group,97 total with source distribution plus one augment,node test,release,VALIDATION_ADDED
|
||||
QA-043,P0,shop,open merchant,inspect slots,3 fixed and 3 unique random eligible offers,integration,release,VALIDATION_ADDED
|
||||
QA-044,P0,shop,reroll count0..5,inspect quote,cost equals3+2*n,node test,release,SOURCE_CONFIRMED
|
||||
QA-045,P0,shop,insufficient points,attempt purchase,no negative balance no RNG consumption no offer mutation,node test,release,VALIDATION_ADDED
|
||||
QA-046,P0,shop,purchase limit reached,reroll and reopen,offer remains ineligible,node test,release,SOURCE_CONFIRMED
|
||||
QA-047,P0,shop,purchase tile and cancel install,reopen,pending install preserved and no refund or loss,integration,release,VALIDATION_ADDED
|
||||
QA-048,P0,event,EV-140 forced,perform base digs,5x5 board and exactly5 actions before boost,integration,release,VALIDATION_ADDED
|
||||
QA-049,P0,event,EV-151 forced,inspect cabinet,20 capsules and1 base attempt,integration,release,SOURCE_CONFIRMED
|
||||
QA-050,P0,event,EV-166 forced,inspect all seeds,one of6 source maps and3 base shots,integration,release,SOURCE_CONFIRMED
|
||||
QA-051,P0,event,each tile137 138 139 140 141 142 143 144 151 152 164 166,force via debug,input presentation settlement save and exit all pass,manual debug checklist,release,SOURCE_CONFIRMED
|
||||
QA-052,P2,legacy boost,load old transaction fixture,restore,no UI control rendered and no new transaction created,node migration,archive,DEFERRED_NOT_EXPOSED
|
||||
QA-053,P2,legacy boost,old PENDING fixture,restore,compatibility recovery does not expose button,node migration,archive,DEFERRED_NOT_EXPOSED
|
||||
QA-054,P2,legacy boost,old COMMITTED fixture,restore,render does not apply balance or reward delta again,node migration,archive,DEFERRED_NOT_EXPOSED
|
||||
QA-055,P2,legacy boost,normal event instance,inspect controls,no boost transaction can be initiated from UI,node test,archive,DEFERRED_NOT_EXPOSED
|
||||
QA-056,P0,revive,lethal with source Revive available,resolve,source blessing resolves once before failure decision,node test,release,SOURCE_CONFIRMED
|
||||
QA-057,P0,revive,lethal with no source Revive,resolve,full DEFEAT animation then direct failed state with no ad controls,node integration,release,VALIDATION_ADDED
|
||||
QA-058,P0,revive,source Revive resolves but hp remains zero,resolve,run fails with no ad or second revive choice,node test,release,VALIDATION_ADDED
|
||||
QA-059,P0,revive,transition to next floor then lethal without blessing,resolve,run fails directly and no floor-based ad eligibility exists,node test,release,VALIDATION_ADDED
|
||||
QA-060,P0,checkpoint,each modal battle purchase equip blessing event boundary,refresh before and after commit,no half settlement duplicate RNG or duplicate reward,node integration,release,VALIDATION_ADDED
|
||||
QA-061,P0,checkpoint,corrupt main checksum,load,backup slot loads and committed transaction set preserved,node test,release,VALIDATION_ADDED
|
||||
QA-062,P0,checkpoint,corrupt both slots,load,user sees corrupt save prompt and can start clean run,node integration,release,VALIDATION_ADDED
|
||||
QA-063,P0,checkpoint,same seed DDA and command transcript,replay,board loot combat shop and checkpoints byte-identical,node replay,release,VALIDATION_ADDED
|
||||
QA-064,P0,UI,750x1334 and808x1440,visit all screens,no crop overlap or inaccessible control,visual regression,release,VALIDATION_ADDED
|
||||
QA-065,P1,UI,first-time flags clear,trigger five guides,each appears once points at correct control and never stacks,integration,release,VALIDATION_ADDED
|
||||
QA-066,P0,UI,normal build,inspect board and combat,no auto-roll combat speed debug or real ad playback screen,static and manual,release,VALIDATION_ADDED
|
||||
QA-067,P0,art,all sprite nodes at rest,capture transform,world pixel coordinates and scales are integers,node test,release,VALIDATION_ADDED
|
||||
QA-068,P0,VFX,all boss and curved/rush attacks,compare tell and hit mask,mismatch<=4 logical pixels,integration capture,release,VALIDATION_ADDED
|
||||
QA-069,P1,VFX,stress battle and boss death,profile particles,live particles stay within animation-vfx budgets,profiler,release,VALIDATION_ADDED
|
||||
QA-070,P0,audio,production build,scan loaded banks and clips,no background music and all looping SFX stop on exit,static and integration,release,VALIDATION_ADDED
|
||||
QA-071,P0,debug,force all commands,inspect logs,commands use Domain API and every record has debug=true,integration,release,VALIDATION_ADDED
|
||||
QA-072,P0,logs,normal 10-floor run,export JSON,only technical allowlist fields and no user profile or cross-run stats,static schema test,release,VALIDATION_ADDED
|
||||
QA-073,P0,onsite,10 live runs,cover attack attackSpeedCrit survival zeroRevive oneRevive,lead signs overall gameplay and roughly10-12min,onsite signoff,release,VALIDATION_ADDED
|
||||
QA-074,P0,boss cycle,DiceGauge debug stage and six completed ROLL actions,observe runtimeGauge counter and board,first2 reach gauge100 and enter countdown4; next4 reach0 then exactly4 unique nonblessing tiles convert and runtimeGauge clears,node integration and visual regression,release,RUNTIME_OBSERVED
|
||||
QA-075,P0,potion,150 maxHP 1 currentHP and heal level80,use one potion,currentHP becomes81 and charge decreases once; healing caps at maxHP,node test,release,SOURCE_CONFIRMED
|
||||
QA-076,P0,board,ROLL any legal 2D6 result from Start,compare visible sum animation steps and final tile index,visible total equals exact number of clockwise one-tile moves and final index equals sum,browser visual regression,release,RUNTIME_OBSERVED
|
||||
QA-077,P0,board,render battle merchant mini-boss infected-boss and all installable event tiles,inspect labels image URLs and load status,1-4 skull battle tiles and both boss tiles use distinct original assets and every event remains identifiable,browser integration,release,SOURCE_CONFIRMED
|
||||
QA-078,P0,ads,open every normal screen event and lethal flow,scan controls and DOM,zero ad boost reroll extra-attempt extra-shot or ad-revive buttons; lethal flow reaches DEFEAT,browser integration,release,VALIDATION_ADDED
|
||||
QA-079,P0,balance,new run with initial stats and no potion ads or debug buff,resolve first three normal battle groups,player survives and defeats all three groups,node test,release,VALIDATION_ADDED
|
||||
QA-080,P0,tile lifecycle,resolve battle mini-boss and event then advance a floor,inspect source cells,each resolved source becomes EMPTY wasteland and stays wasteland unless a legal new tile covers it,node integration,release,VALIDATION_ADDED
|
||||
QA-081,P0,infected boss,leave infected boss alive and cross Start three times,inspect all existing normal battle cells,each crossing adds one enemy per normal battle capped at4 and source stops after three pollution triggers,node integration,release,VALIDATION_ADDED
|
||||
QA-082,P0,boss rule,stage0 Default run from initialization through floor10,inspect state HUD and checkpoints,no runtimeBossGauge or BossDiceCounter pressure state is created; boss_0 begins only after the tenth floor,node integration and visual regression,release,SOURCE_CONFIRMED
|
||||
|
@@ -0,0 +1,10 @@
|
||||
placement_id,event_id,button_label,reward,scope_limit,eligibility,transaction_key,idempotency_rule,failure_rule,source_tag
|
||||
BOOST_DIG_EXTRA,EV-140,立即增加5次,add 5 dig actions,once per event instance,event phase READY and boostUsed false,eventInstanceId+placementId,"COMMITTED returns prior result without applying again",no state change and no usage consumed,DEFERRED_NOT_EXPOSED
|
||||
BOOST_CHEST_REROLL,EV-141,重抽1件装备,discard current unaccepted gear and generate one non-identical catalog-quality-status tuple,once per event instance,gear offer exists and boostUsed false,eventInstanceId+placementId,"COMMITTED restores rerolled gear instance",no state change and no usage consumed,DEFERRED_NOT_EXPOSED
|
||||
BOOST_MERCHANT_POINTS,EV-MERCHANT,立即获得20点,add 20 points,once per merchant instance,shop open and boostUsed false,eventInstanceId+placementId,"COMMITTED restores balance delta once",no state change and no usage consumed,DEFERRED_NOT_EXPOSED
|
||||
BOOST_CAMPFIRE_HEAL_TILE,EV-138,额外恢复10%并生成1个有益地块,heal 10 percent max hp then enqueue one core good tile install,once per event instance,recovery event unresolved and boostUsed false,eventInstanceId+placementId,"COMMITTED restores hp and exact installed tile once",if no legal tile enqueue next-floor install,DEFERRED_NOT_EXPOSED
|
||||
BOOST_SMITHY_RATE,EV-137,提升本次强化概率,set current attempt rates to 100|80|50|30,once per event instance,gear selected before random resolution and boostUsed false,eventInstanceId+placementId,"COMMITTED restores boosted rate flag; random result uses stored roll",after forge resolved placement becomes ineligible,DEFERRED_NOT_EXPOSED
|
||||
BOOST_BLESSING_REROLL,EV-144,强制换成新祝福,replace current card with non-identical card and force accept,once per event instance,original card revealed and boostUsed false,eventInstanceId+placementId,"COMMITTED restores replacement card and cannot redraw",if pool has only same card grant original and mark committed,DEFERRED_NOT_EXPOSED
|
||||
BOOST_CLAW_ATTEMPT,EV-151,增加1次抓取,add 1 claw attempt,once per event instance,claw round not ended and boostUsed false,eventInstanceId+placementId,"COMMITTED restores attempt count once",no state change and no usage consumed,DEFERRED_NOT_EXPOSED
|
||||
BOOST_BUBBLE_SHOT,EV-166,增加1发,add 1 bubble projectile,once per event instance,bubble round not ended and boostUsed false,eventInstanceId+placementId,"COMMITTED restores projectile count once",no state change and no usage consumed,DEFERRED_NOT_EXPOSED
|
||||
BOOST_REVIVE,BATTLE,恢复50%最大生命并继续,ceil maxHp times 0.50 and resume exact BattleSnapshot,once per floor,source Revive blessing resolved and player still dead and reviveUsedThisFloor false,runId+floor+placementId,"COMMITTED restores exact post-revive snapshot without healing twice",second death same floor fails run,DEFERRED_NOT_EXPOSED
|
||||
|
@@ -0,0 +1,98 @@
|
||||
offer_id,string_id,item_type,item_id,fixed_key,fixed_sort,fixed_slot,price,buy_count,is_sub_text,source_tag
|
||||
100,TILE_137,TILE,137,,,-1,6,1,false,SOURCE_CONFIRMED
|
||||
101,TILE_138,TILE,138,,,-1,9,1,false,SOURCE_CONFIRMED
|
||||
102,TILE_139,TILE,139,,,-1,7,1,false,SOURCE_CONFIRMED
|
||||
103,TILE_140,TILE,140,,,-1,4,1,false,SOURCE_CONFIRMED
|
||||
104,TILE_141,TILE,141,,,-1,5,1,false,SOURCE_CONFIRMED
|
||||
105,TILE_142,TILE,142,,,-1,5,1,false,SOURCE_CONFIRMED
|
||||
106,TILE_143,TILE,143,,,-1,4,1,false,SOURCE_CONFIRMED
|
||||
107,TILE_144,TILE,144,,,-1,7,1,false,SOURCE_CONFIRMED
|
||||
108,TILE_151,TILE,151,,,-1,7,1,false,SOURCE_CONFIRMED
|
||||
109,TILE_152,TILE,152,,,-1,7,1,false,SOURCE_CONFIRMED
|
||||
110,TILE_164,TILE,164,,,-1,5,1,false,SOURCE_CONFIRMED
|
||||
111,TILE_166,TILE,166,,,-1,8,1,false,SOURCE_CONFIRMED
|
||||
112,AUGMENT_100,AUGMENT,100,,,-1,17,1,false,SOURCE_CONFIRMED
|
||||
113,AUGMENT_102,AUGMENT,102,,,-1,14,1,false,SOURCE_CONFIRMED
|
||||
114,AUGMENT_103,AUGMENT,103,,,-1,10,1,false,SOURCE_CONFIRMED
|
||||
115,AUGMENT_106,AUGMENT,106,,,-1,27,1,false,SOURCE_CONFIRMED
|
||||
116,AUGMENT_108,AUGMENT,108,,,-1,12,1,false,SOURCE_CONFIRMED
|
||||
117,AUGMENT_109,AUGMENT,109,,,-1,15,1,false,SOURCE_CONFIRMED
|
||||
118,AUGMENT_110,AUGMENT,110,,,-1,15,1,false,SOURCE_CONFIRMED
|
||||
119,AUGMENT_111,AUGMENT,111,,,-1,23,1,false,SOURCE_CONFIRMED
|
||||
120,AUGMENT_112,AUGMENT,112,,,-1,16,1,false,SOURCE_CONFIRMED
|
||||
121,AUGMENT_113,AUGMENT,113,,,-1,14,1,false,SOURCE_CONFIRMED
|
||||
122,AUGMENT_114,AUGMENT,114,,,-1,15,1,false,SOURCE_CONFIRMED
|
||||
123,AUGMENT_115,AUGMENT,115,,,-1,14,1,false,SOURCE_CONFIRMED
|
||||
124,AUGMENT_116,AUGMENT,116,,,-1,20,1,false,SOURCE_CONFIRMED
|
||||
125,AUGMENT_117,AUGMENT,117,,,-1,17,1,false,SOURCE_CONFIRMED
|
||||
126,AUGMENT_118,AUGMENT,118,,,-1,16,1,false,SOURCE_CONFIRMED
|
||||
127,AUGMENT_121,AUGMENT,121,,,-1,10,1,false,SOURCE_CONFIRMED
|
||||
128,AUGMENT_122,AUGMENT,122,,,-1,13,1,false,SOURCE_CONFIRMED
|
||||
129,AUGMENT_124,AUGMENT,124,,,-1,19,1,false,SOURCE_CONFIRMED
|
||||
130,AUGMENT_125,AUGMENT,125,,,-1,18,1,false,SOURCE_CONFIRMED
|
||||
131,AUGMENT_126,AUGMENT,126,,,-1,16,1,false,SOURCE_CONFIRMED
|
||||
132,AUGMENT_132,AUGMENT,132,,,-1,17,1,false,SOURCE_CONFIRMED
|
||||
133,AUGMENT_147,AUGMENT,147,,,-1,15,1,false,SOURCE_CONFIRMED
|
||||
134,AUGMENT_148,AUGMENT,148,,,-1,10,1,false,SOURCE_CONFIRMED
|
||||
135,AUGMENT_149,AUGMENT,149,,,-1,17,1,false,SOURCE_CONFIRMED
|
||||
136,AUGMENT_150,AUGMENT,150,,,-1,19,1,false,SOURCE_CONFIRMED
|
||||
137,AUGMENT_151,AUGMENT,151,,,-1,8,1,false,SOURCE_CONFIRMED
|
||||
138,AUGMENT_152,AUGMENT,152,,,-1,11,1,false,SOURCE_CONFIRMED
|
||||
139,AUGMENT_153,AUGMENT,153,,,-1,15,1,false,SOURCE_CONFIRMED
|
||||
140,AUGMENT_154,AUGMENT,154,,,-1,24,1,false,SOURCE_CONFIRMED
|
||||
141,AUGMENT_156,AUGMENT,156,,,-1,16,1,false,SOURCE_CONFIRMED
|
||||
142,AUGMENT_157,AUGMENT,157,,,-1,18,1,false,SOURCE_CONFIRMED
|
||||
143,POTION_100,POTION,100,,,-1,16,1,false,SOURCE_CONFIRMED
|
||||
144,POTION_101,POTION,101,,,-1,15,1,false,SOURCE_CONFIRMED
|
||||
145,POTION_103,POTION,103,,,-1,15,1,false,SOURCE_CONFIRMED
|
||||
146,POTION_104,POTION,104,,,-1,13,1,false,SOURCE_CONFIRMED
|
||||
147,POTION_105,POTION,105,,,-1,14,1,false,SOURCE_CONFIRMED
|
||||
148,POTION_106,POTION,106,,,-1,16,1,false,SOURCE_CONFIRMED
|
||||
149,POTION_107,POTION,107,,,-1,20,1,false,SOURCE_CONFIRMED
|
||||
150,POTION_108,POTION,108,,,-1,19,1,false,SOURCE_CONFIRMED
|
||||
151,POTION_109,POTION,109,,,-1,13,1,false,SOURCE_CONFIRMED
|
||||
152,STAT_100,STAT,100,,,-1,3,10,false,SOURCE_CONFIRMED
|
||||
153,STAT_101,STAT,101,,,-1,3,10,false,SOURCE_CONFIRMED
|
||||
154,STAT_102,STAT,102,,,-1,5,5,false,SOURCE_CONFIRMED
|
||||
155,STAT_103,STAT,103,,,-1,3,10,false,SOURCE_CONFIRMED
|
||||
156,STAT_104,STAT,104,,,-1,5,5,false,SOURCE_CONFIRMED
|
||||
157,STAT_105,STAT,105,,,-1,8,3,false,SOURCE_CONFIRMED
|
||||
158,STAT_106,STAT,106,,,-1,9,3,false,SOURCE_CONFIRMED
|
||||
159,STAT_107,STAT,107,,,-1,8,3,false,SOURCE_CONFIRMED
|
||||
160,STAT_108,STAT,108,,,-1,7,3,false,SOURCE_CONFIRMED
|
||||
161,STAT_109,STAT,109,,,-1,5,5,false,SOURCE_CONFIRMED
|
||||
162,STAT_110,STAT,110,,,-1,8,3,false,SOURCE_CONFIRMED
|
||||
163,STAT_111,STAT,111,,,-1,7,3,false,SOURCE_CONFIRMED
|
||||
164,STAT_112,STAT,112,,,-1,8,3,false,SOURCE_CONFIRMED
|
||||
165,FIXED_potionCount_0,FIXED,,potionCount,0,1,10,1,true,SOURCE_CONFIRMED
|
||||
166,FIXED_potionCount_1,FIXED,,potionCount,1,1,15,1,true,SOURCE_CONFIRMED
|
||||
167,FIXED_potionCount_2,FIXED,,potionCount,2,1,21,1,true,SOURCE_CONFIRMED
|
||||
168,FIXED_potionCount_3,FIXED,,potionCount,3,1,28,1,true,SOURCE_CONFIRMED
|
||||
169,FIXED_potionCount_4,FIXED,,potionCount,4,1,36,1,true,SOURCE_CONFIRMED
|
||||
170,FIXED_potionCount_5,FIXED,,potionCount,5,1,45,1,true,SOURCE_CONFIRMED
|
||||
171,FIXED_potionCount_6,FIXED,,potionCount,6,1,55,1,true,SOURCE_CONFIRMED
|
||||
172,FIXED_potionCount_7,FIXED,,potionCount,7,1,66,1,true,SOURCE_CONFIRMED
|
||||
173,FIXED_potionCount_8,FIXED,,potionCount,8,1,78,1,true,SOURCE_CONFIRMED
|
||||
174,FIXED_potionHeal_0,FIXED,,potionHeal,0,2,7,1,true,SOURCE_CONFIRMED
|
||||
175,FIXED_potionHeal_1,FIXED,,potionHeal,1,2,11,1,true,SOURCE_CONFIRMED
|
||||
176,FIXED_potionHeal_2,FIXED,,potionHeal,2,2,15,1,true,SOURCE_CONFIRMED
|
||||
177,FIXED_potionHeal_3,FIXED,,potionHeal,3,2,20,1,true,SOURCE_CONFIRMED
|
||||
178,FIXED_potionHeal_4,FIXED,,potionHeal,4,2,25,1,true,SOURCE_CONFIRMED
|
||||
179,FIXED_potionHeal_5,FIXED,,potionHeal,5,2,30,1,true,SOURCE_CONFIRMED
|
||||
180,FIXED_potionHeal_6,FIXED,,potionHeal,6,2,35,1,true,SOURCE_CONFIRMED
|
||||
181,FIXED_potionHeal_7,FIXED,,potionHeal,7,2,40,1,true,SOURCE_CONFIRMED
|
||||
182,FIXED_potionHeal_8,FIXED,,potionHeal,8,2,45,1,true,SOURCE_CONFIRMED
|
||||
183,FIXED_potionHeal_9,FIXED,,potionHeal,9,2,50,1,true,SOURCE_CONFIRMED
|
||||
184,FIXED_potionHeal_10,FIXED,,potionHeal,10,2,55,1,true,SOURCE_CONFIRMED
|
||||
185,FIXED_potionHeal_11,FIXED,,potionHeal,11,2,60,1,true,SOURCE_CONFIRMED
|
||||
186,FIXED_potionHeal_12,FIXED,,potionHeal,12,2,65,1,true,SOURCE_CONFIRMED
|
||||
187,FIXED_potionHeal_13,FIXED,,potionHeal,13,2,70,1,true,SOURCE_CONFIRMED
|
||||
188,FIXED_potionHeal_14,FIXED,,potionHeal,14,2,75,1,true,SOURCE_CONFIRMED
|
||||
189,FIXED_potionHeal_15,FIXED,,potionHeal,15,2,80,1,true,SOURCE_CONFIRMED
|
||||
190,FIXED_potionHeal_16,FIXED,,potionHeal,16,2,85,1,true,SOURCE_CONFIRMED
|
||||
191,FIXED_potionHeal_17,FIXED,,potionHeal,17,2,90,1,true,SOURCE_CONFIRMED
|
||||
192,FIXED_potionHeal_18,FIXED,,potionHeal,18,2,95,1,true,SOURCE_CONFIRMED
|
||||
193,FIXED_potionHeal_19,FIXED,,potionHeal,19,2,100,1,true,SOURCE_CONFIRMED
|
||||
194,FIXED_potionAdd_0,FIXED,,potionAdd,0,0,5,-1,true,SOURCE_CONFIRMED
|
||||
195,Lotto,LOTTO,,,,-1,5,1,false,SOURCE_CONFIRMED
|
||||
196,AUGMENT_THIRD_DIE,AUGMENT,ThirdDieReplace,,,-1,27,1,false,VALIDATION_ADDED
|
||||
|
@@ -0,0 +1,27 @@
|
||||
screen_id,screen_name,entry,primary_controls,required_states,exit,checkpoint_boundary,acceptance,source_tag
|
||||
UI-001,本地开始页,app boot,"开始新局|继续|清除本局|设置","noSave|hasSave|corruptSave","进入棋盘|设置",before new run,无账号 体力 商店 抽卡或联网入口,VALIDATION_ADDED
|
||||
UI-002,棋盘HUD,FloorSetup,"40个实际地块|点数|层数|条件式Boss压力|HP|四装备槽|Roll|丹药|暂停","awaitRoll|moving|bossCountdown|bossActing|inputLocked|tileResolving",tile event or pause,after prior atomic resolution,1点严格移动1个实际地块;stage0不显示Boss压力;DiceGauge调试关显示0/2或4..0且演出不可跳过,RUNTIME_OBSERVED
|
||||
UI-003,骰子结果,DiceResolve,"dieA|dieB|点数和","rolling|settled",Moving,roll result committed,结果静止可读至少250ms且无重掷入口,RUNTIME_OBSERVED
|
||||
UI-004,第三骰选择,ThirdDieChoice,"A|B|C|替换A|替换B|放弃","choiceReady|committing",Moving,choice committed,三种选择明确且放弃不回滚RNG,VALIDATION_ADDED
|
||||
UI-005,自动战斗HUD,Battle Fight,"玩家/敌人血条|1-4敌人数|小Boss标识|状态|伤害数字|药水","prepare|spawn|fight|paused|victory|death",PostBattle or revive,battle result committed,1-4骷髅严格生成1-4只怪物;小Boss入口可辨;普通入口无加速按钮且唯一主动输入为药水,RUNTIME_OBSERVED
|
||||
UI-006,装备掉落比较,RewardChoice,"新旧装备|属性差|装备|分解|关闭","slotEmpty|upgrade|downgrade|batch",Checkpoint,reward choice committed,关闭自动分解全部未处理装备,RUNTIME_OBSERVED
|
||||
UI-007,商店,Merchant event,"3固定槽|3随机槽|余额|刷新|退出","available|soldOut|insufficient|rerolled",Board,each purchase or reroll,恰好6槽且无广告赠点按钮;商品均使用局内点数,SOURCE_CONFIRMED
|
||||
UI-008,地块安装,purchased TILE,"棋盘合法格|确认|取消","selecting|invalid|pending",Shop or Board,install committed,只能选择合法普通格且取消保留待安装商品,VALIDATION_ADDED
|
||||
UI-009,宝箱,EV-141,"宝箱|装备卡|装备|分解|退出","closed|opened|resolved",Board,event committed,无广告重抽按钮;装备结果只提交一次,RUNTIME_OBSERVED
|
||||
UI-010,营火,EV-138,"恢复预览|领取","ready|resolved",Board,event committed,只显示基础治疗且不溢出最大生命,VALIDATION_ADDED
|
||||
UI-011,铁匠铺,EV-137,"装备|成功率|直接领取|挑战","noGear|ready|success|destroyed",Board,forge result committed,失败销毁和分解点数同屏可见且无概率增强按钮,RUNTIME_OBSERVED
|
||||
UI-012,祝福点,EV-144,"单卡|接受","revealed|accepted",Board,blessing committed,无广告换卡或重抽按钮,SOURCE_CONFIRMED
|
||||
UI-013,挖掘,EV-140,"5x5格|剩余次数|形状预览|退出","ready|digging|complete",Board,dig result committed,基础5次;每次按完整形状原子揭示并发奖,VALIDATION_ADDED
|
||||
UI-014,传奇装备,EV-139,"传奇装备卡|装备|分解","revealed|resolved",Board,reward committed,品质固定传奇且走标准装备比较,VALIDATION_ADDED
|
||||
UI-015,彩票,EV-142,"费用5|抽取|结果|退出","insufficient|ready|rolling|resolved",Board,event committed,概率结果由eventRng确定且刷新不重抽,VALIDATION_ADDED
|
||||
UI-016,21点,EV-143,"下注3|手牌|庄家牌|要牌|停牌|结果","insufficient|playerTurn|dealerTurn|win|lose|push",Board,event committed,Ace固定1避免额外规则且庄家17停牌,VALIDATION_ADDED
|
||||
UI-017,抓娃娃,EV-151,"20胶囊|摇杆/抓取|次数|结果","ready|aiming|grabbing|complete",Board,event committed,基础次数且无广告补次数;胶囊碰撞和奖励在确定逻辑tick结算,SOURCE_CONFIRMED
|
||||
UI-018,黄金马车,EV-152,"4辆遮盖车|选择|奖励","ready|revealed|resolved",Board,event committed,只能选一次且HP阈值快照在进入时锁定,VALIDATION_ADDED
|
||||
UI-019,骰子机,EV-164,"三骰|启动|匹配表|奖励","ready|rolling|matched|none",Board,event committed,显示原始三骰并按源码匹配表发奖,SOURCE_CONFIRMED
|
||||
UI-020,泡泡龙,EV-166,"棋盘|瞄准线|剩余发数|计时|奖励","ready|aiming|flying|complete",Board,event committed,基础3发且无广告补发;碰撞不依赖渲染帧,SOURCE_CONFIRMED
|
||||
UI-021,源码祝福复活,lethal damage with Revive,"祝福图标|恢复结果","resolving|success|stillDead",Battle,revive blessing result committed,只结算一次;失败则直接进入DEFEAT,SOURCE_CONFIRMED
|
||||
UI-022,广告复活(不暴露),disabled,none,notExposed,Fail,none,普通UI与调试台均不可达;无恢复和放弃广告按钮,DEFERRED_NOT_EXPOSED
|
||||
UI-023,Boss胜利,boss PostBattle,"胜利|构筑摘要|结束本局","enter|summary|confirmed",Start,result acknowledged,不含倍增广告 付费或剧情,VALIDATION_ADDED
|
||||
UI-024,失败结算,no revive,"失败|层数|构筑摘要|结束本局","enter|summary|confirmed",Start,result acknowledged,无促销弹窗且不可在失败页增强,VALIDATION_ADDED
|
||||
UI-025,暂停,board or battle,"继续|设置|退出本局","boardPause|battlePause|confirmExit",prior screen or Start,no new checkpoint,暂停冻结逻辑时钟且退出需二次确认,VALIDATION_ADDED
|
||||
UI-026,隐藏调试台,7 taps or CtrlShiftD,"seed|floor|DDA|强制事件/掉落/死亡|4x|恢复|导出","closed|open|commandResult|error",prior screen,debug command committed,调试命令走Domain API并标记debug true,VALIDATION_ADDED
|
||||
|
@@ -0,0 +1,44 @@
|
||||
# HEROLL 关卡内 UE/UI 视觉验收报告
|
||||
|
||||
验收日期:2026-08-18
|
||||
目标:750 × 1334、9:16 竖屏、仅关卡内界面、仅使用逆向出的 HEROLL 原作位图。
|
||||
|
||||
## 自动化结果
|
||||
|
||||
| 项目 | 结果 |
|
||||
|---|---:|
|
||||
| 规格内界面 | 23 / 23 已渲染 |
|
||||
| 关键预览 | 8 / 8 已生成 |
|
||||
| 预览尺寸 | 8 / 8 为 750 × 1334 |
|
||||
| 位图加载错误 | 0 |
|
||||
| 网络请求失败 | 0 |
|
||||
| 页面脚本错误 | 0 |
|
||||
| 总结 | PASS |
|
||||
|
||||
机器可读明细见 `qa-results.json`。测试由 `tools/capture-and-validate.mjs` 启动本地 HTTP 服务并调用本机 Chromium 内核完成,可重复运行。
|
||||
|
||||
## 人工视觉复核
|
||||
|
||||
- 棋盘:火山后景、菱形环路、中心首领、左上积分、右侧层数和底部 ROLL 的视觉顺序与参考视频一致。
|
||||
- 战斗:保留原作上下 HUD,自动战斗进度置顶,角色左右对峙,药水仍可触达,掉落装备在右下高亮。
|
||||
- 装备:比较层不销毁战斗背景;新/旧装备上下排列,装备槽保持可见。
|
||||
- 商店:精灵摊位占上半区,六件商品为 3×2,购买后原位 `SOLD`,EXIT 回到底部固定动词位。
|
||||
- 挖掘:5×5 网格、剩余宝物与铲数同屏,打开格和奖励格具有不同状态。
|
||||
- 泡泡射手:原作森林背景和炮台作为强识别物,剩余射击次数靠近炮台但不遮挡瞄准区域。
|
||||
- 复活:复活后 50% HP、保留敌方战斗状态、本层一次的规则在选择前完整显示。
|
||||
- 结算:胜利与失败使用原作结果背景,移除局内 HUD,唯一主操作引导离开。
|
||||
|
||||
## 复刻取舍
|
||||
|
||||
- 参考视频使用 13 层演示;当前验证 GDD 明确要求 10 层,因此右侧层数规范统一为 `/10`,不照抄演示数字。
|
||||
- 斩击序列 Texture2D 的单帧包含不适合静态 UI 的底色;静态样稿不直接贴该帧,正式实现应由原序列材质按帧播放。
|
||||
- 字体未从原包强行提取,样稿使用系统可用的粗体回退;开发接入时应替换为项目已授权的 CJK 像素字体,并保持现有字号、描边与断行。
|
||||
- 样稿使用逆向位图重建布局,不把视频帧或参考截图当作界面背景,因此可继续接入真实游戏状态。
|
||||
|
||||
## 发布前门槛
|
||||
|
||||
1. 确认原作资源的使用授权;无授权时,本包只能停留在内部研究和验证环境。
|
||||
2. 在目标设备覆盖刘海、挖孔、圆角和手势条,复测主操作与药水热区。
|
||||
3. 为骰子、棋子逐格移动、战斗帧动画、购买、强化、奖励和结算补齐原作序列帧与音效时序。
|
||||
4. 以真实数据分别验证 320–750 逻辑宽、两行简中文案、四种装备品质和极端数值。
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# HEROLL 关卡内 UE/UI 复刻设计包
|
||||
|
||||
> 版本:1.0 · 目标画布:750 × 1334(9:16 竖屏) · 范围:进入关卡后至结算离开
|
||||
> 版权边界:本包仅用于本地研究、验证与开发。所有位图均从用户提供的 HEROLL 原作包中逆向导出,不得公开发布、转售或并入无授权产品。
|
||||
|
||||
## 交付概览
|
||||
|
||||
- `prototype/`:覆盖 23 个关卡内状态的可交互高保真 HTML 样稿;桌面端左侧切屏,移动端全屏呈现,支持键盘左右键切换。
|
||||
- `assets/original/`:196 张从原作 Unity 资源导出的 Texture2D,按棋盘、HUD、战斗、装备、商店、事件、结算分类;新增 1–4 骷髅战斗、小 Boss 与污染 Boss 专用资源,`manifest.json` 保留源包、PathID、尺寸与导出状态。
|
||||
- `screen-specs.csv`:逐屏用途、入口、出口、固定锚点、主要操作与验收条件。
|
||||
- `preview/`:关键界面的 750 × 1334 实际浏览器渲染截图。
|
||||
- `qa-results.json`:自动化渲染、资源加载、画布尺寸和界面覆盖校验结果。
|
||||
|
||||
## 1. 复刻依据
|
||||
|
||||
设计依据来自三组相互校验的资料:
|
||||
|
||||
1. 「逆向分析游戏并输出完整 GDD」会话与 `design/gameplay-validation/Heroll_Gameplay_Validation_GDD.md`:确定 10 层验证局、双 D6、固定棋盘顺时针移动、全自动战斗、四装备槽、商店层 3/6/9、事件与结算规则。
|
||||
2. `参考资料/功能浏览与关卡流程.mp4`:确认真实关卡内构图、交互锚点、界面覆盖关系和事件节奏。
|
||||
3. `参考资料/截图说明.jpg` 与逆向 Texture2D:确认像素厚描边、暗紫灰面板、暖黄主操作、火山棋盘、精灵商店与事件小游戏的原作视觉。
|
||||
|
||||
关键视频样本:约 02:12 棋盘;04:24 自动战斗;06:37 商店;07:21/09:14 装备比较;11:02 挖掘;14:42 泡泡射手。样稿不是依靠截图贴图,而是用逆向出的原作分层资源重新搭建,因此各状态可交互、可适配。
|
||||
|
||||
## 2. 关卡内体验架构
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["关卡棋盘:ROLL"] --> B["骰子结算:移动预告"]
|
||||
B --> C{"落点类型"}
|
||||
C -->|战斗| D["自动战斗"]
|
||||
D --> E{"装备掉落"}
|
||||
E -->|有| F["装备比较 / 装备或分解"]
|
||||
E -->|无| G["返回棋盘"]
|
||||
F --> G
|
||||
C -->|事件| H["商店 / 宝箱 / 篝火 / 铁匠 / 祝福 / 小游戏"]
|
||||
H --> G
|
||||
C -->|完成一圈| I["回复 30% + 安装 2 个增益地块"]
|
||||
I --> G
|
||||
D -->|生命归零| J{"源码复生祝福可用"}
|
||||
J -->|是| K["按祝福规则复生"]
|
||||
K --> D
|
||||
J -->|否| L["失败结算"]
|
||||
G -->|第 10 层首领击败| M["胜利结算"]
|
||||
```
|
||||
|
||||
体验节奏采用「教—测—奖」:前两层让玩家理解掷骰、自动战斗和装备;中段用商店与事件测试资源分配;每圈回复和增益地块构成阶段奖励;最终层以首领、源码复生祝福和结算收束。
|
||||
|
||||
## 3. 竖屏布局规范
|
||||
|
||||
### 3.1 基准与安全区
|
||||
|
||||
- 逻辑画布:750 × 1334;浏览器等比缩放,不拉伸。
|
||||
- 推荐安全区:左右 24 px、顶部 22 px、底部 22 px;接入原生端时外加 `safe-area-inset-*`。
|
||||
- 核心交互全部位于下方 42% 的单手可达区;顶部仅放状态与暂停。
|
||||
- 最小视觉点击尺寸:92 × 92 px;主操作 710 × 66 px;任何关键操作不依赖悬停。
|
||||
|
||||
### 3.2 五个固定锚点
|
||||
|
||||
| 区域 | 位置 | 内容 | 状态规则 |
|
||||
|---|---|---|---|
|
||||
| 积分 | 左上 | 菱形积分图标+数值 | 棋盘、战斗、事件均保留 |
|
||||
| 系统 | 右上 | AUTO+暂停 | AUTO 只改变战斗/事件演算;暂停永远可见 |
|
||||
| 层数 | 右侧 | 当前层/10、进度柱、首领标记 | 大型模态可遮暗但不重排 |
|
||||
| 局内资产 | 底部上排 | 等级、生命、四装备槽 | 战斗掉落比较时仍能直接对照 |
|
||||
| 当前动词 | 底部 | ROLL / FIGHTING / EXIT / 事件动作 | 一屏只保留一个主操作 |
|
||||
|
||||
### 3.3 覆盖关系
|
||||
|
||||
- 棋盘、战斗是两个基础场景层;顶部与底部 HUD 使用同一锚点,切换时不跳动。
|
||||
- 商店、挖掘、泡泡射手等是中部场景替换:保留积分、AUTO/暂停、层数和底部局内资产。
|
||||
- 骰子结果、第三骰、装备比较、复活、暂停是叠层:原场景继续提供上下文,使用半透明暗幕。
|
||||
- 胜利/失败为全屏终态,才允许移除局内 HUD。
|
||||
|
||||
## 4. 视觉语言
|
||||
|
||||
- 轮廓:重要容器 6–8 px 深蓝黑描边;按钮和卡片保留 6–12 px 底部像素压边。
|
||||
- 色彩:深蓝黑 `#11162C`;暗紫灰面板 `#343754`;暖黄主操作 `#FFD34A`;危险红 `#D9483F`;确认绿 `#58C561`。
|
||||
- 层次:交互按钮 > 事件容器 > 角色与棋盘 > 场景前后景;不用现代玻璃拟态或无描边扁平控件。
|
||||
- 图像:启用 nearest/pixelated 采样;禁止 AI 重绘、第三方占位图或与原作风格不一致的矢量图标。
|
||||
- 动效:位移使用阶梯式缓动;按钮按下压缩 4–6 px;奖励只在焦点周围使用 2–4 帧闪光,不做全屏粒子污染。
|
||||
|
||||
## 5. 关键交互说明
|
||||
|
||||
### 棋盘与投掷
|
||||
|
||||
点击 `ROLL` 后锁定主按钮,双骰在中部展示 0.7–1.0 秒;同时给出总点数和落点类型。棋盘固定不旋转,40 个画面格全部是独立实际地块;棋子从底部起点沿左侧、顶部、右侧顺时针移动,1 点严格对应 1 个地块,最终落点才进入战斗/事件。1–4 骷髅、两类小 Boss、商店及 12 类事件格必须显示各自原作图标。第三骰祝福生效时,先选骰再投掷,不在结果后追加选择。
|
||||
|
||||
### 自动战斗与装备
|
||||
|
||||
战斗默认 1× 全自动。玩家唯一即时干预是右下药水;AUTO 关闭只暂停自动施放,不将系统改为手操战斗。装备掉落以右下发光卡提示,打开比较后上方为新装备、下方为当前装备;主按钮执行“装备并自动分解旧物品”,退出则保留当前装备并分解新掉落。
|
||||
|
||||
### 事件
|
||||
|
||||
商店固定为三普通+三特殊商品,购买后原位显示 `SOLD`;商店层为 3/6/9。宝箱、篝火、铁匠、祝福、挖掘和小游戏按各自基础收益与基础次数结算;不显示广告、即时增强、广告重抽或补次数按钮。事件结束统一通过底部 `EXIT` 或结果领取按钮回到棋盘。
|
||||
|
||||
### 复活与结算
|
||||
|
||||
仅保留源码 `Revive` 祝福的复生规则。祝福不可用或结算后仍死亡时,完整播放 `DEFEAT` 动画并进入失败页;不显示广告复活或放弃广告按钮。第 10 层首领击败进入全屏胜利;胜利与失败终态都不再保留局内 HUD。
|
||||
|
||||
## 6. 实现建议
|
||||
|
||||
- Unity/UE 实现都应以 750 × 1334 逻辑坐标搭建锚点。若这里的 “UE” 指 User Experience,本样稿已给出完整交互流;若落地 Unreal Engine,可将五个固定锚点拆成 CommonUI Layer。
|
||||
- 位图按原生尺寸整数倍放大;Texture Filter 使用 Point/Nearest,关闭 Mipmap,UI Atlas 禁止有损压缩。
|
||||
- 棋盘格、装备槽、商品卡、祝福卡应做成数据驱动复用组件,不为单个事件复制 HUD。
|
||||
- 模态出现时不销毁战斗/棋盘状态;源码祝福复生和装备比较必须返回同一帧上下文。
|
||||
- 文案长度以简中两行上限验收;原作英文动词 `ROLL / FIGHTING / EXIT` 保留作为高识别动作标签。
|
||||
|
||||
## 7. 验收基线
|
||||
|
||||
- 23 个 `UI-002`–`UI-025` 关卡内状态均可直接打开;不含登录、大厅、抽卡、局外养成。
|
||||
- 所有 `<img>` 均来自 `assets/original/manifest.json`,加载失败数必须为 0。
|
||||
- 每张关键预览严格为 750 × 1334;顶部、右侧与底部锚点在所有非终态界面不漂移。
|
||||
- 主按钮可见且唯一;战斗中药水不被主操作遮挡;装备比较仍能看到四装备槽。
|
||||
- 商店、挖掘、泡泡射手分别保持原作的精灵摊位、5×5 网格、森林泡泡场景识别物。
|
||||
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 176 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,221 @@
|
||||
const A = '../assets/original';
|
||||
const screens = [
|
||||
['board','UI-002','关卡棋盘','掷骰—移动—触发的主循环'],
|
||||
['dice','UI-003','骰子结算','双骰结果与落点预告'],
|
||||
['third-die','UI-004','第三骰选择','特殊祝福的局内决策'],
|
||||
['battle','UI-005','自动战斗','自动演算,仅保留药水干预'],
|
||||
['gear','UI-006','装备比较','战斗中即时换装或分解'],
|
||||
['shop','UI-007','商店事件','三普通+三特殊商品'],
|
||||
['tile-install','UI-008','地块安装','完成一圈后的正向成长'],
|
||||
['chest','UI-009','宝箱事件','开启、领取'],
|
||||
['campfire','UI-010','篝火事件','回复与增益地块'],
|
||||
['smithy','UI-011','铁匠事件','强化当前装备'],
|
||||
['blessing','UI-012','祝福揭示','单卡接受'],
|
||||
['dig','UI-013','挖掘事件','5×5 网格与有限挖掘次数'],
|
||||
['legendary','UI-014','传奇装备','高价值装备确认'],
|
||||
['lotto','UI-015','乐透事件','抽取式奖励'],
|
||||
['blackjack','UI-016','二十一点事件','风险收益抉择'],
|
||||
['claw','UI-017','抓娃娃事件','时机型小游戏'],
|
||||
['carriage','UI-018','黄金马车','一次性高价值事件'],
|
||||
['dice-machine','UI-019','骰子机事件','组合结果奖励'],
|
||||
['bubble','UI-020','泡泡射手','局内轻量技能事件'],
|
||||
['revive','UI-021','源码祝福复生','自动结算且无广告按钮'],
|
||||
['victory','UI-023','关卡胜利','奖励收束与离开'],
|
||||
['defeat','UI-024','关卡失败','失败归因与离开'],
|
||||
['pause','UI-025','暂停界面','继续、设置、退出']
|
||||
];
|
||||
|
||||
const path = (cat,name) => `${A}/${cat}/${name}`;
|
||||
const image = (cat,name,cls='',alt='') => `<img class="${cls}" src="${path(cat,name)}" alt="${alt}">`;
|
||||
|
||||
function sceneLayers(theme='volcano'){
|
||||
return `<div class="scene">
|
||||
${image('scene',`sharedassets4__chapter_select_back_${theme}.png`,'scene-back','原作场景后景')}
|
||||
${image('scene',`sharedassets4__chapter_select_front_${theme}.png`,'scene-front','原作场景前景')}
|
||||
<div class="vignette"></div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function topHud(points=16, auto=true){
|
||||
return `<div class="safe-top">
|
||||
<div class="currency">${points}</div>
|
||||
<div class="top-actions"><button class="auto ${auto?'on':''}">AUTO</button><button class="pause" aria-label="暂停"></button></div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function floorTrack(floor=4){
|
||||
const pct = Math.min(90, Math.max(7, floor/10*100));
|
||||
return `<div class="floor-track"><div class="floor-label">${floor} / 10</div><div class="track-line"><span class="track-fill" style="height:${pct}%"></span><i class="track-dot" style="top:${100-pct}%"></i></div><div class="boss-medal"></div></div>`;
|
||||
}
|
||||
|
||||
function slots(){
|
||||
return `<div class="slots">
|
||||
<div class="slot"><b>12</b>${image('gear','sharedassets3__legend_gear_sword.png','','武器')}</div>
|
||||
<div class="slot ring"><b>8</b></div>
|
||||
<div class="slot"><b>9</b>${image('gear','sharedassets3__legend_gear_arm.png','','手套')}</div>
|
||||
<div class="slot boot"><b>7</b></div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function bottomHud(action='ROLL', cls='', hp='742 / 1000'){
|
||||
return `<div class="bottom-hud">
|
||||
<div class="hp-wrap"><div class="level-badge">18</div><div class="hp-bar"><div class="hp-fill"></div><div class="hp-text">${hp}</div></div></div>
|
||||
<button class="side-btn map-btn" aria-label="地图"></button>
|
||||
${slots()}
|
||||
<button class="side-btn potion" aria-label="使用药水">${image('hud','sharedassets3__potion_image.png','','药水')}<em>2</em></button>
|
||||
<button class="primary ${cls}">${action}</button>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function tiles(){
|
||||
return Array.from({length:16},(_,i)=>{
|
||||
const good = [3,8,12].includes(i+1);
|
||||
const file = good ? 'sharedassets3__tile_goodEvent.png' : (i%4===0?'sharedassets3__tile_corner.png':'sharedassets3__tile_normal.png');
|
||||
return `<div class="tile t${i+1} ${good?'good':''}">${image('board',file,'',good?'正向事件格':'普通格')}</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function boardWorld(){
|
||||
return `<div class="board-loop">
|
||||
<div class="board-center"><div class="center-lava"></div>${image('battle','sharedassets3__001_boss_idle_2.png','boss','关底首领')}</div>
|
||||
${tiles()}
|
||||
</div>${image('tutorial_vfx','sharedassets3__Tutorial_knight.png','hero-token','玩家棋子')}`;
|
||||
}
|
||||
|
||||
function base({action='ROLL',actionClass='',floor=4,points=16,hp='742 / 1000',theme='volcano',auto=true,content='',toast=''}){
|
||||
return `<section class="game-screen">${sceneLayers(theme)}${content}${topHud(points,auto)}${floorTrack(floor)}${bottomHud(action,actionClass,hp)}${toast}</section>`;
|
||||
}
|
||||
|
||||
function boardScreen(){ return base({content:boardWorld(),toast:'<div class="toast">前进 7 格<small>落点:普通战斗</small></div>'}); }
|
||||
|
||||
function diceScreen(){
|
||||
return base({content:`${boardWorld()}<div class="modal-dim"></div><div class="dice-result">${image('hud','sharedassets3__dice_red_3.png','','红骰结果 3')}${image('hud','sharedassets3__dice_blue_4.png','','蓝骰结果 4')}</div><div class="dice-sum">7</div>`,action:'前进 7 格'});
|
||||
}
|
||||
|
||||
function thirdDieScreen(){
|
||||
const choices = [
|
||||
['hud','sharedassets3__dice_red_3.png','攻击骰','下次战斗伤害 +20%'],
|
||||
['hud','sharedassets3__dice_blue_4.png','护盾骰','本层获得 120 护盾'],
|
||||
['event_blessing','sharedassets3__power_star_slot1_1.png','命运骰','移动点数额外 +3']
|
||||
];
|
||||
return base({content:`${boardWorld()}<div class="modal-dim"></div><div class="modal"><h2 class="modal-title">选择第三颗骰子</h2><p class="modal-copy">命运祝福已生效,本次投掷额外选择一枚骰子。</p><div class="choice-row">${choices.map((c,i)=>`<button class="choice ${i===2?'selected':''}">${image(c[0],c[1])}<b>${c[2]}</b><span>${c[3]}</span></button>`).join('')}</div></div>`,action:'确认选择'});
|
||||
}
|
||||
|
||||
function battleContent(withDrop=true){
|
||||
return `<div class="battle-stage"><div class="battle-progress"><div class="team-fill"></div><div class="vs">VS</div></div>
|
||||
${image('tutorial_vfx','sharedassets3__Tutorial_knight.png','fighter hero','玩家队伍')}
|
||||
${image('battle','sharedassets3__001_boss_attack_2.png','fighter enemy','敌方首领')}
|
||||
<div class="damage">-248</div>
|
||||
${withDrop?`<div class="drop">${image('gear','sharedassets3__legend_gear_sword.png','','掉落武器')}</div>`:''}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function battleScreen(){ return base({content:battleContent(),action:'FIGHTING…',actionClass:'disabled',floor:7,hp:'618 / 1000',auto:true}); }
|
||||
|
||||
function gearScreen(){
|
||||
const panel=(label,level,atk,spd,isNew)=>`<div class="gear-panel ${isNew?'new':''}"><div class="gear-icon"><b>${level}</b>${image('gear','sharedassets3__legend_gear_sword.png')}</div><div class="gear-stats"><h3>${label}</h3><div class="stat"><span>攻击</span><b class="${isNew?'up':''}">${atk}</b></div><div class="stat"><span>攻速</span><b class="${isNew?'down':''}">${spd}</b></div><div class="stat"><span>品质</span><b>${isNew?'史诗':'稀有'}</b></div></div></div>`;
|
||||
return base({content:`${battleContent(false)}<div class="modal-dim"></div><div class="compare">${panel('新装备',14,'+82','0.94',true)}${panel('当前装备',12,'+61','1.08',false)}</div>`,action:'装备并分解旧武器',actionClass:'green',floor:7});
|
||||
}
|
||||
|
||||
function shopScreen(){
|
||||
const goods=[
|
||||
['gear','sharedassets3__legend_gear_sword.png','精钢长剑','20'],
|
||||
['hud','sharedassets3__potion_image.png','回复药水','12'],
|
||||
['event_blessing','sharedassets3__power_star_slot1_1.png','祝福碎片','16'],
|
||||
['gear','sharedassets3__legend_gear_arm.png','战斗手套','24'],
|
||||
['hud','sharedassets3__dice_blue_4.png','护盾骰','20'],
|
||||
['event_chest','sharedassets3__treasure_box_special_1.png','神秘宝箱','30']
|
||||
];
|
||||
return base({content:`<div class="shop-stage">${image('shop','sharedassets3__ingame_shop_bg.png','shop-bg','原作商店背景')}${image('shop','sharedassets3__ingame_shop_master.png','shop-master','原作商人')}<div class="shop-title">旅行商店</div><div class="goods">${goods.map((g,i)=>`<button class="good-card ${i===1?'sold':''}"><span class="good-icon">${image(g[0],g[1])}</span><b>${g[2]}</b><span class="price">◆ ${g[3]}</span></button>`).join('')}</div></div>`,action:'EXIT',floor:6,points:46});
|
||||
}
|
||||
|
||||
function tileInstallScreen(){
|
||||
return base({content:`${boardWorld()}<div class="modal-dim"></div><div class="modal"><h2 class="modal-title">安装增益地块</h2><p class="modal-copy">完成一圈:回复 30% 最大生命,并安装 2 个增益地块。</p><div class="choice-row"><button class="choice selected">${image('board','sharedassets3__tile_goodEvent.png')}<b>治疗之泉</b><span>经过时回复 10%</span></button><button class="choice">${image('board','sharedassets3__tile_goodEvent.png')}<b>幸运宝箱</b><span>获得随机装备</span></button><button class="choice">${image('board','sharedassets3__tile_goodEvent.png')}<b>祝福神殿</b><span>三选一祝福</span></button></div></div>`,action:'安装到高亮地块',floor:5});
|
||||
}
|
||||
|
||||
function eventFrame({title,sub,art,category,action='领取奖励',extra='',floor=5,points=33,actionClass=''}){
|
||||
return base({content:`<div class="event-stage"><div class="event-title">${title}</div><div class="event-sub">${sub}</div>${art?image(category,art,'event-art',title):''}${extra}</div>`,action,actionClass,floor,points});
|
||||
}
|
||||
|
||||
function chestScreen(){ return eventFrame({title:'神秘宝箱',sub:'开启宝箱并领取本次装备奖励。',art:'sharedassets3__treasure_box_special_1.png',category:'event_chest',action:'打开宝箱'}); }
|
||||
function campfireScreen(){ return eventFrame({title:'温暖篝火',sub:'恢复 10% 最大生命,并在棋盘安装 1 个增益地块。',art:'sharedassets3__bonfire.png',category:'event_camp_smithy',action:'休息',actionClass:'green'}); }
|
||||
function smithyScreen(){ return eventFrame({title:'矮人铁匠铺',sub:'选择一件已装备物品进行强化;强化存在品质跃迁概率。',art:'sharedassets3__anvil.png',category:'event_camp_smithy',action:'强化武器'}); }
|
||||
|
||||
function blessingScreen(){
|
||||
return base({content:`<div class="event-stage"><div class="event-title">获得祝福</div><div class="event-sub">本次揭示的祝福将在本局持续生效。</div><div class="bless-cards"><button class="bless-card pick">${image('event_blessing','sharedassets3__power_star_slot1_1.png')}<b>命运之骰</b><p>每次投掷可追加第三颗骰子</p></button></div></div>`,action:'接受祝福',floor:6,points:41});
|
||||
}
|
||||
|
||||
function digScreen(){
|
||||
const cells=Array.from({length:25},(_,i)=>`<button class="dig-cell ${[7,8,13].includes(i)?'open':''} ${i===8?'dig-reward':''}">${[7,13].includes(i)?image('event_dig',i===7?'sharedassets3__debris_stone_open_2.png':'sharedassets3__debris_bone_open_3.png'):image('event_dig','sharedassets3__hero_body_tile_2.png')}</button>`).join('');
|
||||
return base({content:`<div class="event-stage"><div class="event-title">遗迹挖掘</div><div class="event-sub">选择土地挖掘,找到全部宝物前不要浪费铲数。</div><div class="dig-grid">${cells}</div><div class="event-count">剩余宝物 3 · 铲数 7</div></div>`,action:'DIGGING…',actionClass:'disabled',floor:7});
|
||||
}
|
||||
|
||||
function legendaryScreen(){
|
||||
return eventFrame({title:'传奇装备!',sub:'本局唯一高阶掉落。装备后旧物品会自动分解。',art:'sharedassets3__legend_gear_bg.png',category:'gear',action:'装备「炎刃」',actionClass:'green',extra:`${image('gear','sharedassets3__legend_gear_sword.png','event-art','传奇武器')}<div class="event-count">攻击 +128 · 暴击 +12%</div>`});
|
||||
}
|
||||
|
||||
function lottoScreen(){
|
||||
return eventFrame({title:'幸运乐透',sub:'从奖池中抽取一份奖励,本事件每层仅可触发一次。',art:'sharedassets3__lottery_bg.png',category:'event_lotto',action:'抽取奖励',extra:`${image('event_lotto','sharedassets3__lottery_front.png','lotto-layer','乐透机前景')}`});
|
||||
}
|
||||
|
||||
function blackjackScreen(){
|
||||
const cards='<div class="choice-row"><button class="choice selected"><b style="font-size:54px">K</b><span>当前点数 10</span></button><button class="choice"><b style="font-size:54px">7</b><span>当前点数 17</span></button><button class="choice"><b style="font-size:54px">?</b><span>下一张牌</span></button></div>';
|
||||
return base({content:`<div class="event-stage"><div class="event-title">二十一点</div><div class="event-sub">当前 17 点。继续要牌,或停牌领取 ◆ 12。</div>${cards}<div class="event-count">庄家明牌:9</div></div>`,action:'停牌并领取',floor:8,points:52});
|
||||
}
|
||||
|
||||
function clawScreen(){
|
||||
return eventFrame({title:'魔法抓取机',sub:'左右调整位置,在光标对准奖励时落爪。',art:'sharedassets3__claw_machine.png',category:'event_claw',action:'落爪',extra:`${image('event_claw','sharedassets3__claw_arm.png','event-art','机械爪')}<div class="event-count">剩余次数 2</div>`,floor:8});
|
||||
}
|
||||
|
||||
function carriageScreen(){
|
||||
return eventFrame({title:'黄金马车',sub:'路过的王室马车留下了宝物。选择金币或装备。',art:'sharedassets3__treasure_box_3.png',category:'event_chest',action:'选择传奇装备',extra:'<div class="event-count">◆ 30 或 随机史诗装备</div>',floor:8,points:61});
|
||||
}
|
||||
|
||||
function diceMachineScreen(){
|
||||
return base({content:`<div class="event-stage"><div class="event-title">命运骰子机</div><div class="event-sub">投出三个相同数字可获得大奖。</div>${image('event_dice_machine','sharedassets4__game_machine_frame.png','machine','原作骰子机')}<div class="machine-screen">${[2,2,6].map(n=>image('event_dice_machine',`sharedassets4__game_machine_dice_${n}.png`)).join('')}</div><div class="event-count">剩余次数 1</div></div>`,action:'ROLL',floor:9,points:70});
|
||||
}
|
||||
|
||||
function bubbleScreen(){
|
||||
const bubbles=Array.from({length:36},()=>'<i class="bubble-ball"></i>').join('');
|
||||
return base({content:`<div class="event-stage"><div class="event-title">泡泡射手</div>${image('event_bubble','sharedassets3__bubble_shooter_bg.png','bubble-field','原作泡泡射手背景')}<div class="bubble-pack">${bubbles}</div>${image('event_bubble','sharedassets3__bubble_shooter_cannon.png','cannon','原作发射器')}<div class="event-count">剩余射击 4</div></div>`,action:'SHOOT',floor:9,points:75});
|
||||
}
|
||||
|
||||
function reviveScreen(){
|
||||
return base({content:`${battleContent(false)}<div class="modal-dim"></div><div class="revive-card"><h2>复生祝福发动</h2><p>按源码祝福规则自动恢复生命,完整播放复生反馈后继续战斗。</p><div class="countdown">无广告复活或放弃广告按钮</div></div>`,action:'FIGHTING...',floor:10,hp:'40 / 1000'});
|
||||
}
|
||||
|
||||
function resultScreen(win=true){
|
||||
return `<section class="game-screen result-screen">${image('result',win?'sharedassets7__result_bg_clear.png':'sharedassets7__result_bg_youdied.png','result-bg',win?'胜利背景':'失败背景')}<div class="result-title">${win?'STAGE CLEAR':'YOU DIED'}</div>${win?image('battle','sharedassets3__victory_knight.png','result-knight','胜利骑士'):image('battle','sharedassets3__fail_shield.png','result-knight','失败盾牌')}<div class="reward-row"><div class="reward">◆ 120</div><div class="reward">EXP<br>+48</div><div class="reward">× 1</div></div><button class="result-button">${win?'领取并离开':'返回关卡选择'}</button></section>`;
|
||||
}
|
||||
|
||||
function pauseScreen(){
|
||||
return base({content:`${boardWorld()}<div class="modal-dim"></div><div class="pause-panel"><h2>PAUSE</h2><button>继续游戏</button><button>音效与震动</button><button>战斗说明</button><button>退出本局</button></div>`,action:'ROLL',floor:4});
|
||||
}
|
||||
|
||||
const renderers = {
|
||||
board:boardScreen,dice:diceScreen,'third-die':thirdDieScreen,battle:battleScreen,gear:gearScreen,shop:shopScreen,'tile-install':tileInstallScreen,chest:chestScreen,campfire:campfireScreen,smithy:smithyScreen,blessing:blessingScreen,dig:digScreen,legendary:legendaryScreen,lotto:lottoScreen,blackjack:blackjackScreen,claw:clawScreen,carriage:carriageScreen,'dice-machine':diceMachineScreen,bubble:bubbleScreen,revive:reviveScreen,victory:()=>resultScreen(true),defeat:()=>resultScreen(false),pause:pauseScreen
|
||||
};
|
||||
|
||||
const nav=document.querySelector('#screenNav');
|
||||
const phone=document.querySelector('#phone');
|
||||
let current=0;
|
||||
|
||||
function selectScreen(id,push=true){
|
||||
const index=screens.findIndex(s=>s[0]===id);
|
||||
current=index<0?0:index;
|
||||
const [key,code,title,note]=screens[current];
|
||||
phone.innerHTML=renderers[key]();
|
||||
document.querySelector('#screenTitle').textContent=title;
|
||||
document.querySelector('#screenCode').textContent=code;
|
||||
document.querySelector('#screenNote').textContent=note;
|
||||
document.querySelectorAll('.nav-btn').forEach((b,i)=>b.classList.toggle('active',i===current));
|
||||
if(push && !document.body.classList.contains('capture')) history.replaceState(null,'',`?screen=${key}`);
|
||||
}
|
||||
|
||||
nav.innerHTML=screens.map((s,i)=>`<button class="nav-btn" data-screen="${s[0]}"><b>${s[1]}</b>${s[2]}</button>`).join('');
|
||||
nav.addEventListener('click',e=>{const btn=e.target.closest('[data-screen]');if(btn)selectScreen(btn.dataset.screen)});
|
||||
document.addEventListener('keydown',e=>{if(e.key==='ArrowRight')selectScreen(screens[(current+1)%screens.length][0]);if(e.key==='ArrowLeft')selectScreen(screens[(current-1+screens.length)%screens.length][0]);});
|
||||
const query=new URLSearchParams(location.search);
|
||||
if(query.get('capture')==='1') document.body.classList.add('capture');
|
||||
selectScreen(query.get('screen')||'board',false);
|
||||
window.HEROLL_UI={screens:screens.map(s=>s[0]),selectScreen};
|
||||
@@ -0,0 +1,29 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
||||
<title>HEROLL|关卡内 UE/UI 复刻设计</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="workbench">
|
||||
<aside class="navigator" aria-label="界面导航">
|
||||
<div class="brand">HEROLL <small>IN-RUN UE/UI</small></div>
|
||||
<p>750 × 1334 · 9:16 竖屏<br>原作逆向资源复用</p>
|
||||
<nav id="screenNav"></nav>
|
||||
<div class="legend"><i></i> 可交互设计样稿<br><span>键盘 ← → 切换界面</span></div>
|
||||
</aside>
|
||||
<section class="device-stage">
|
||||
<header class="stage-meta">
|
||||
<div><strong id="screenTitle">关卡棋盘</strong><span id="screenCode">UI-002</span></div>
|
||||
<p id="screenNote">主循环决策界面</p>
|
||||
</header>
|
||||
<div class="phone-frame">
|
||||
<div id="phone" class="phone" role="application" aria-label="HEROLL 竖屏界面样稿"></div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,67 @@
|
||||
:root {
|
||||
--ink:#11162c;
|
||||
--deep:#20223d;
|
||||
--panel:#343754;
|
||||
--paper:#f5e7bd;
|
||||
--gold:#ffd34a;
|
||||
--orange:#f58d2b;
|
||||
--red:#d9483f;
|
||||
--green:#58c561;
|
||||
--cyan:#66d9e8;
|
||||
--outline:6px;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
html,body{margin:0;min-height:100%;background:#121525;color:#f4f1df;font-family:"Trebuchet MS","Microsoft YaHei",sans-serif}
|
||||
body{overflow:hidden}
|
||||
button{font:inherit}
|
||||
.workbench{height:100vh;display:grid;grid-template-columns:270px 1fr;background:radial-gradient(circle at 70% 15%,#303659 0,#171a2c 46%,#0d0f1c 100%)}
|
||||
.navigator{padding:30px 22px 24px;background:rgba(10,12,24,.9);border-right:1px solid #383d62;overflow:auto}
|
||||
.brand{font-size:28px;font-weight:1000;letter-spacing:1px;color:var(--gold);text-shadow:3px 3px #8c392f}
|
||||
.brand small{display:block;color:#f4f1df;font-size:11px;letter-spacing:2px;text-shadow:none;margin-top:2px}
|
||||
.navigator>p{font-size:12px;line-height:1.65;color:#8f96b8;margin:17px 0 20px}
|
||||
#screenNav{display:grid;gap:5px}
|
||||
.nav-btn{width:100%;border:0;border-left:3px solid transparent;background:transparent;color:#aeb4d1;text-align:left;padding:8px 10px;cursor:pointer;border-radius:4px;font-size:12px}
|
||||
.nav-btn:hover{background:#252943;color:white}.nav-btn.active{background:#333a60;color:#fff8cd;border-left-color:var(--gold)}
|
||||
.nav-btn b{display:inline-block;width:47px;color:#707898;font-size:10px}
|
||||
.legend{font-size:11px;line-height:1.8;color:#737b9d;margin-top:24px}.legend i{display:inline-block;width:7px;height:7px;background:var(--green);border-radius:50%;margin-right:6px}.legend span{margin-left:13px}
|
||||
.device-stage{display:flex;flex-direction:column;align-items:center;justify-content:center;min-width:0;min-height:0;padding:18px}
|
||||
.stage-meta{width:min(calc((100vh - 105px)*750/1334),750px);display:flex;justify-content:space-between;align-items:end;margin-bottom:10px;color:#aeb4d1}
|
||||
.stage-meta div{display:flex;align-items:baseline;gap:12px}.stage-meta strong{color:#fff;font-size:15px}.stage-meta span{font-size:10px;color:var(--gold);letter-spacing:1px}.stage-meta p{font-size:11px;margin:0}
|
||||
.phone-frame{height:calc(100vh - 105px);aspect-ratio:750/1334;padding:6px;background:#080a13;border:1px solid #4f567a;border-radius:19px;box-shadow:0 25px 70px #050611}
|
||||
.phone{position:relative;width:100%;height:100%;overflow:hidden;background:#141b2d;border-radius:13px;container-type:size;image-rendering:pixelated;isolation:isolate}
|
||||
.game-screen{position:absolute;inset:0;overflow:hidden;background:#18223a;color:white;--s:calc(100cqw / 750)}
|
||||
.game-screen img{image-rendering:pixelated;user-select:none;pointer-events:none}
|
||||
.safe-top{position:absolute;left:24px;right:24px;top:22px;height:72px;z-index:50;display:flex;align-items:flex-start;justify-content:space-between}
|
||||
.currency{display:flex;align-items:center;min-width:118px;height:52px;padding:0 18px 0 48px;border:5px solid #11162c;border-radius:13px;background:#403e51;box-shadow:inset 0 -6px #26283d,0 4px 0 #11162c;font-size:25px;font-weight:1000;color:#fff3b2;position:relative}
|
||||
.currency:before{content:"";position:absolute;left:9px;width:28px;height:28px;background:var(--gold);border:4px solid #11162c;transform:rotate(45deg);box-shadow:inset -4px -4px #e4912f}
|
||||
.top-actions{display:flex;gap:11px;align-items:center}.auto{height:50px;padding:0 15px;border:5px solid #11162c;border-radius:11px;color:white;background:#4f5369;font-size:19px;font-weight:1000;box-shadow:inset 0 -6px #34364d}.auto.on{background:#4e8a51;box-shadow:inset 0 -6px #2d6034}.pause{width:53px;height:53px;border:5px solid #11162c;border-radius:12px;background:#45495e url('../assets/original/hud/sharedassets3__pause_icon.png') center/24px no-repeat;box-shadow:inset 0 -6px #292c43}
|
||||
.scene{position:absolute;inset:0;overflow:hidden}.scene-back{position:absolute;width:100%;height:100%;object-fit:cover;object-position:center top;filter:saturate(.82) brightness(.73)}.scene-front{position:absolute;left:0;bottom:160px;width:100%;height:56%;object-fit:cover;object-position:center bottom;opacity:.92}
|
||||
.vignette{position:absolute;inset:0;background:linear-gradient(#090b18aa 0,transparent 19%,transparent 64%,#0b0e1ee8 88%,#0b0e1e 100%);pointer-events:none}
|
||||
.floor-track{position:absolute;z-index:30;right:18px;top:126px;width:62px;height:580px;display:flex;flex-direction:column;align-items:center}.floor-label{font-size:15px;font-weight:1000;text-shadow:3px 3px #11162c;white-space:nowrap}.track-line{position:relative;width:18px;flex:1;margin:9px 0;background:#22263d;border:4px solid #0d1122;border-radius:12px;box-shadow:inset 0 0 0 3px #5b6079}.track-fill{position:absolute;left:3px;right:3px;bottom:3px;height:42%;background:linear-gradient(#ffca42,#e85c32);border-radius:8px}.track-dot{position:absolute;left:50%;top:52%;width:34px;height:34px;transform:translate(-50%,-50%);border:5px solid #11162c;border-radius:50%;background:#f0e0b2}.boss-medal{width:58px;height:58px;border:5px solid #11162c;border-radius:50%;background:#822b31 url('../assets/original/board/sharedassets3__ingame_boss_icon.png') center/37px no-repeat;box-shadow:0 4px #11162c}
|
||||
.board-loop{position:absolute;z-index:12;left:106px;top:207px;width:516px;height:516px;transform:rotate(45deg);border:43px solid rgba(18,21,38,.86);border-radius:34px;box-shadow:0 0 0 7px #11162c,0 18px 30px #060811aa,inset 0 0 0 7px #6a5c53}
|
||||
.board-center{position:absolute;inset:59px;background:#55272e;border:6px solid #11162c;border-radius:24px;box-shadow:inset 0 0 0 8px #8d4837;overflow:hidden}.center-lava{position:absolute;inset:-20%;background:radial-gradient(circle,#ffad36 0 6%,#c84d34 7% 25%,#472534 26% 63%,#1c2038 64%)}
|
||||
.boss{position:absolute;z-index:3;left:50%;top:49%;width:210px;transform:translate(-50%,-50%) rotate(-45deg);filter:drop-shadow(0 10px 0 #11162c)}
|
||||
.tile{position:absolute;width:78px;height:78px;transform:rotate(-45deg);filter:drop-shadow(0 5px 0 #11162c)}.tile img{width:100%;height:100%;object-fit:contain}.tile.good{filter:drop-shadow(0 0 12px #ffe96b) drop-shadow(0 5px 0 #11162c)}
|
||||
.t1{left:-18px;top:-18px}.t2{left:95px;top:-39px}.t3{left:207px;top:-39px}.t4{left:319px;top:-39px}.t5{right:-18px;top:-18px}.t6{right:-39px;top:95px}.t7{right:-39px;top:207px}.t8{right:-39px;top:319px}.t9{right:-18px;bottom:-18px}.t10{left:319px;bottom:-39px}.t11{left:207px;bottom:-39px}.t12{left:95px;bottom:-39px}.t13{left:-18px;bottom:-18px}.t14{left:-39px;top:319px}.t15{left:-39px;top:207px}.t16{left:-39px;top:95px}
|
||||
.hero-token{position:absolute;z-index:18;left:133px;top:542px;width:118px;filter:drop-shadow(0 8px 0 #11162c)}
|
||||
.bottom-hud{position:absolute;z-index:45;left:20px;right:20px;bottom:22px;height:235px;display:grid;grid-template-columns:100px 1fr 100px;grid-template-rows:62px 100px 67px;gap:3px 13px;align-items:center}.hp-wrap{grid-column:1/4;display:flex;align-items:center;gap:12px}.level-badge{width:56px;height:56px;display:grid;place-items:center;border:5px solid #11162c;border-radius:50%;background:#e6d8a9;color:#20223d;font-weight:1000;font-size:22px}.hp-bar{height:39px;flex:1;border:6px solid #11162c;border-radius:12px;background:#351f32;position:relative;overflow:hidden}.hp-fill{height:100%;width:74%;background:linear-gradient(#e35b54,#b9343d);box-shadow:inset 0 -7px #8d2636}.hp-text{position:absolute;inset:0;display:grid;place-items:center;font-size:18px;font-weight:1000;text-shadow:2px 2px #11162c}.side-btn{width:92px;height:92px;border:6px solid #11162c;border-radius:19px;background:#41455e;color:#fff;box-shadow:inset 0 -10px #292c44,0 5px #11162c;font-size:13px;font-weight:1000}.map-btn{grid-row:2}.map-btn:before{content:"MAP"}.potion{grid-column:3;grid-row:2;position:relative}.potion img{width:65px;height:65px;object-fit:contain}.potion em{position:absolute;right:-3px;bottom:-5px;width:31px;height:31px;display:grid;place-items:center;background:#f4e6bd;border:4px solid #11162c;border-radius:50%;color:#1f2338;font-style:normal;font-weight:1000}
|
||||
.slots{grid-column:2;grid-row:2;display:grid;grid-template-columns:repeat(4,1fr);gap:7px}.slot{height:92px;border:6px solid #11162c;border-radius:13px;background:linear-gradient(#4b4e69,#2e314b);box-shadow:inset 0 -8px #23263e;position:relative;display:grid;place-items:center}.slot b{position:absolute;left:5px;top:2px;color:#f2e3b2;font-size:15px;text-shadow:2px 2px #11162c}.slot img{max-width:62px;max-height:72px;object-fit:contain}.slot.ring:after{content:"◆";font-size:44px;color:#8fd5dd;text-shadow:4px 4px #11162c}.slot.boot:after{content:"L";font-size:49px;font-weight:1000;color:#d59963;text-shadow:4px 4px #11162c;transform:skew(-8deg)}
|
||||
.primary{grid-column:1/4;grid-row:3;height:66px;border:6px solid #11162c;border-radius:18px;background:linear-gradient(#ffd75b,#f28b2d);box-shadow:inset 0 -10px #ca5e2a,0 6px #11162c;color:#fff8d6;text-shadow:4px 4px #812e30;font-size:31px;font-weight:1000;letter-spacing:1px}.primary.disabled{filter:grayscale(.65);opacity:.8}.primary.danger{background:linear-gradient(#f67a5c,#b73342);box-shadow:inset 0 -10px #7f2333,0 6px #11162c}.primary.green{background:linear-gradient(#8ce26e,#45a853);box-shadow:inset 0 -10px #33753b,0 6px #11162c}
|
||||
.toast{position:absolute;z-index:80;left:50%;top:112px;transform:translateX(-50%);min-width:470px;padding:16px 26px;background:#282b42e8;border:5px solid #11162c;border-radius:16px;text-align:center;font-weight:1000;font-size:23px;box-shadow:0 8px #11162c}.toast small{display:block;color:#ffd85a;margin-top:5px;font-size:15px}
|
||||
.dice-result{position:absolute;z-index:70;left:50%;top:270px;transform:translateX(-50%);display:flex;gap:35px}.dice-result img{width:125px;filter:drop-shadow(0 9px 0 #11162c)}.dice-sum{position:absolute;left:50%;top:420px;transform:translateX(-50%);font-size:65px;font-weight:1000;color:#fff5c0;text-shadow:6px 6px #4f2430}
|
||||
.modal-dim{position:absolute;z-index:55;inset:0;background:#080b18b8;backdrop-filter:blur(1px)}.modal{position:absolute;z-index:60;left:54px;right:54px;top:155px;bottom:254px;border:7px solid #11162c;border-radius:26px;background:linear-gradient(#3d405c,#272a43);box-shadow:inset 0 0 0 6px #77728a,0 14px #080b18;overflow:hidden;padding:30px}.modal-title{font-size:35px;font-weight:1000;text-align:center;color:#fff4c2;text-shadow:5px 5px #7b3340;margin:0 0 10px}.modal-copy{text-align:center;color:#d7d4c4;font-size:18px;line-height:1.45;margin:0}.choice-row{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:39px}.choice{height:245px;border:6px solid #11162c;border-radius:17px;background:linear-gradient(#68567a,#34364e);color:white;position:relative;padding:125px 10px 10px;text-align:center;box-shadow:inset 0 -10px #272a42}.choice img{position:absolute;left:50%;top:27px;transform:translateX(-50%);width:91px;height:91px;object-fit:contain}.choice b{display:block;color:#ffe271;font-size:21px}.choice span{font-size:13px;color:#cfd2e1}.choice.selected{outline:7px solid #ffd85c;transform:translateY(-6px)}
|
||||
.event-stage{position:absolute;z-index:20;left:24px;right:88px;top:102px;bottom:262px;border:7px solid #11162c;border-radius:23px;background:linear-gradient(#303550,#1e2239);box-shadow:inset 0 0 0 5px #585b70;overflow:hidden}.event-title{position:absolute;z-index:8;left:50%;top:20px;transform:translateX(-50%);min-width:390px;padding:13px 25px;border:6px solid #11162c;border-radius:14px;background:#4e3e50;text-align:center;font-size:31px;font-weight:1000;color:#fff1b7;text-shadow:4px 4px #722f3c}.event-sub{position:absolute;z-index:8;left:60px;right:60px;top:88px;text-align:center;font-size:16px;color:#ddd8c2}.event-art{position:absolute;left:50%;top:52%;max-width:94%;max-height:66%;transform:translate(-50%,-50%);object-fit:contain;filter:drop-shadow(0 10px 0 #11162c88)}.event-count{position:absolute;z-index:9;left:50%;bottom:22px;transform:translateX(-50%);padding:7px 18px;border:5px solid #11162c;border-radius:14px;background:#f2e2b6;color:#282a43;font-size:19px;font-weight:1000}
|
||||
.battle-stage{position:absolute;z-index:15;left:24px;right:84px;top:102px;bottom:262px;overflow:hidden}.battle-progress{position:absolute;left:45px;right:32px;top:22px;height:52px;border:6px solid #11162c;border-radius:19px;background:#2d3047;overflow:hidden}.team-fill{height:100%;width:58%;background:linear-gradient(90deg,#4ab4b9,#77d6d5);border-right:6px solid #11162c}.vs{position:absolute;left:54%;top:-4px;width:58px;height:58px;display:grid;place-items:center;transform:translateX(-50%);border:6px solid #11162c;border-radius:50%;background:#f1dca7;color:#34354d;font-weight:1000}.fighter{position:absolute;filter:drop-shadow(0 12px 0 #11162c)}.fighter.hero{left:31px;bottom:128px;width:225px}.fighter.enemy{right:34px;bottom:117px;width:315px;transform:scaleX(-1)}.damage{position:absolute;right:83px;top:285px;font-size:49px;font-weight:1000;color:#fff1a5;text-shadow:5px 5px #8b2d39;transform:rotate(-7deg)}.drop{position:absolute;right:22px;bottom:70px;width:90px;height:90px;border:6px solid #11162c;border-radius:15px;background:#72549b;display:grid;place-items:center;box-shadow:0 0 22px #d8adff}.drop img{max-width:62px;max-height:70px}
|
||||
.compare{position:absolute;z-index:63;left:38px;right:88px;top:112px;display:grid;gap:14px}.gear-panel{height:210px;border:7px solid #11162c;border-radius:20px;background:#343650;box-shadow:inset 0 -12px #24273e;display:grid;grid-template-columns:164px 1fr;padding:18px}.gear-panel.new{outline:5px solid #ffd95d}.gear-icon{height:164px;border:6px solid #11162c;border-radius:15px;background:#6e4f92;display:grid;place-items:center;position:relative}.gear-icon img{max-width:105px;max-height:140px}.gear-icon b{position:absolute;left:8px;top:5px;font-size:25px}.gear-stats{padding:4px 20px}.gear-stats h3{margin:0 0 10px;color:#fff0ad;font-size:24px}.stat{height:40px;display:flex;justify-content:space-between;border-bottom:2px solid #555a70;align-items:center}.up{color:#69dd79}.down{color:#ff6d68}
|
||||
.shop-stage{position:absolute;z-index:20;left:24px;right:88px;top:105px;bottom:262px;border:7px solid #11162c;border-radius:23px;background:#262a43;overflow:hidden}.shop-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}.shop-master{position:absolute;right:4px;top:43px;height:288px}.shop-title{position:absolute;left:50%;top:17px;transform:translateX(-50%);font-size:35px;font-weight:1000;color:#fff1b5;text-shadow:4px 4px #772f3b}.goods{position:absolute;left:27px;right:27px;bottom:42px;display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.good-card{height:184px;border:6px solid #11162c;border-radius:15px;background:#f0dfb3;color:#2a2c43;padding:9px;text-align:center;box-shadow:inset 0 -9px #c29f72;position:relative}.good-card .good-icon{height:92px;display:grid;place-items:center}.good-card img{max-height:86px;max-width:82px}.good-card b{display:block;font-size:16px}.price{position:absolute;left:10px;right:10px;bottom:8px;border:4px solid #11162c;border-radius:8px;background:#4a4960;color:#ffdf68;font-weight:1000;padding:4px}.sold{filter:grayscale(1);opacity:.55}.sold:after{content:"SOLD";position:absolute;inset:58px 8px auto;transform:rotate(-13deg);font-size:31px;color:#b12d3c;font-weight:1000;text-shadow:2px 2px #fff0c0}
|
||||
.dig-grid{position:absolute;z-index:5;left:93px;right:93px;top:145px;display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:5px}.dig-cell{min-width:0;width:100%;padding:0;overflow:hidden;aspect-ratio:1;border:4px solid #11162c;background:#8f603e;position:relative}.dig-cell img{width:100%;height:100%;object-fit:cover}.dig-cell.open{background:#3d2e31;box-shadow:inset 0 0 0 6px #261f2b}.dig-cell.dig-reward:after{content:"◆";position:absolute;inset:0;display:grid;place-items:center;color:#ffe05c;font-size:38px;text-shadow:3px 3px #7b313e}
|
||||
.bubble-field{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}.bubble-pack{position:absolute;left:75px;right:75px;top:135px;display:flex;flex-wrap:wrap;justify-content:center}.bubble-ball{width:58px;height:58px;margin:-2px 2px;border:5px solid #11162c;border-radius:50%;background:#e85d57;box-shadow:inset -8px -8px #9d303d}.bubble-ball:nth-child(3n){background:#52b7d0;box-shadow:inset -8px -8px #2e758d}.bubble-ball:nth-child(3n+1){background:#f0cc50;box-shadow:inset -8px -8px #a67a2f}.cannon{position:absolute;left:50%;bottom:38px;width:116px;transform:translateX(-50%);filter:drop-shadow(0 7px #11162c)}
|
||||
.machine{position:absolute;left:50%;top:155px;transform:translateX(-50%);width:430px;filter:drop-shadow(0 12px 0 #11162c)}.machine-screen{position:absolute;z-index:2;left:50%;top:267px;transform:translateX(-50%);width:260px;display:flex;justify-content:center;gap:10px}.machine-screen img{width:70px}.lotto-layer{position:absolute;left:50%;top:58%;transform:translate(-50%,-50%);max-width:87%;max-height:62%;filter:drop-shadow(0 10px #11162c)}
|
||||
.bless-cards{position:absolute;left:36px;right:36px;top:145px;display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.bless-card{height:376px;border:6px solid #11162c;border-radius:22px;background:#574a68;padding:18px 10px;text-align:center;box-shadow:inset 0 -12px #302d46}.bless-card img{width:118px;height:175px;object-fit:contain}.bless-card b{display:block;color:#ffe36e;font-size:19px}.bless-card p{font-size:13px;color:#ddd9c9}.bless-card.pick{outline:6px solid #ffdb5d;transform:translateY(-8px)}
|
||||
.revive-card{position:absolute;z-index:63;left:70px;right:70px;top:217px;padding:36px 30px 31px;border:8px solid #11162c;border-radius:29px;background:linear-gradient(#55536d,#292c45);box-shadow:inset 0 0 0 7px #8d8192,0 16px #080b18;text-align:center}.revive-card h2{font-size:43px;color:#fff0b3;text-shadow:5px 5px #792f3b;margin:0 0 13px}.revive-card p{font-size:19px;line-height:1.55;color:#dedac9}.revive-card .countdown{font-size:25px;color:#ffcc55;font-weight:1000}.revive-actions{display:grid;grid-template-columns:1fr 1fr;gap:15px;margin-top:22px}.small-action{height:75px;border:6px solid #11162c;border-radius:16px;background:#4d526c;color:#fff;font-size:20px;font-weight:1000;box-shadow:inset 0 -9px #30344d}.small-action.ad{background:#d58033;box-shadow:inset 0 -9px #95472d}
|
||||
.result-screen{background:#171a2c}.result-bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}.result-knight{position:absolute;left:50%;top:265px;width:380px;transform:translateX(-50%);filter:drop-shadow(0 14px #11162c)}.result-title{position:absolute;left:0;right:0;top:125px;text-align:center;font-size:57px;color:#fff0a8;text-shadow:7px 7px #7d2e3c;font-weight:1000}.reward-row{position:absolute;left:75px;right:75px;bottom:290px;display:flex;justify-content:center;gap:19px}.reward{width:120px;height:120px;border:7px solid #11162c;border-radius:18px;background:#423f57;display:grid;place-items:center;color:#ffe372;font-size:29px;font-weight:1000;box-shadow:inset 0 -12px #282b42}.result-button{position:absolute;left:55px;right:55px;bottom:93px;height:78px;border:7px solid #11162c;border-radius:19px;background:linear-gradient(#ffd45a,#e9822f);color:#fff8cf;text-shadow:4px 4px #822f34;font-size:29px;font-weight:1000;box-shadow:inset 0 -10px #b64f29,0 7px #11162c}
|
||||
.pause-panel{position:absolute;z-index:65;left:70px;right:70px;top:200px;padding:35px;border:8px solid #11162c;border-radius:28px;background:#3e415a;box-shadow:inset 0 0 0 6px #737184,0 15px #080b18}.pause-panel h2{text-align:center;color:#fff0b2;font-size:40px;margin:0 0 24px;text-shadow:5px 5px #782e3c}.pause-panel button{width:100%;height:78px;margin:8px 0;border:6px solid #11162c;border-radius:16px;background:#5b6077;color:white;font-size:23px;font-weight:1000;box-shadow:inset 0 -9px #34374f}.pause-panel button:first-of-type{background:#56ad60;box-shadow:inset 0 -9px #34703c}.pause-panel button:last-of-type{color:#ff9b8c}
|
||||
.floor-up{position:absolute;z-index:70;left:60px;right:60px;top:212px;padding:30px;border:7px solid #11162c;border-radius:25px;background:#343751ee;box-shadow:0 14px #080b18;text-align:center}.floor-up h2{font-size:39px;color:#fff0ad;margin:0 0 18px;text-shadow:5px 5px #7a2f3c}.floor-up ul{text-align:left;margin:0 auto;width:82%;font-size:20px;line-height:1.7;color:#e1dcc6}.floor-up li::marker{color:#ffd559}
|
||||
.tap-hint{position:absolute;z-index:90;left:50%;bottom:260px;transform:translateX(-50%);font-size:14px;color:#d4d7e8;animation:pulse 1.5s infinite}@keyframes pulse{50%{opacity:.45}}
|
||||
@media(max-width:920px){.workbench{grid-template-columns:1fr}.navigator,.stage-meta{display:none}.device-stage{padding:0}.phone-frame{height:100vh;max-width:100vw;border:0;border-radius:0;padding:0}.phone{border-radius:0}}
|
||||
body.capture .workbench{display:block;background:#000}body.capture .navigator,body.capture .stage-meta{display:none}body.capture .device-stage{padding:0;display:block}body.capture .phone-frame{width:750px;height:1334px;padding:0;border:0;border-radius:0}body.capture .phone{border-radius:0}
|
||||
@@ -0,0 +1,252 @@
|
||||
{
|
||||
"generatedAt": "2026-08-24T04:58:06.711Z",
|
||||
"target": {
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"aspect": "9:16 portrait"
|
||||
},
|
||||
"browser": {
|
||||
"executablePath": "C:/Program Files/Google/Chrome/Application/chrome.exe"
|
||||
},
|
||||
"summary": {
|
||||
"expectedScreenCount": 23,
|
||||
"renderedScreenCount": 23,
|
||||
"screenshotCount": 8,
|
||||
"brokenImageCount": 0,
|
||||
"requestFailureCount": 0,
|
||||
"pageErrorCount": 0,
|
||||
"dimensionsPass": true,
|
||||
"pass": true
|
||||
},
|
||||
"screenshots": [
|
||||
{
|
||||
"screen": "board",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"imageCount": 23,
|
||||
"broken": [],
|
||||
"screenshot": "preview/board.png"
|
||||
},
|
||||
{
|
||||
"screen": "battle",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"imageCount": 8,
|
||||
"broken": [],
|
||||
"screenshot": "preview/battle.png"
|
||||
},
|
||||
{
|
||||
"screen": "gear",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"imageCount": 9,
|
||||
"broken": [],
|
||||
"screenshot": "preview/gear.png"
|
||||
},
|
||||
{
|
||||
"screen": "shop",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"imageCount": 13,
|
||||
"broken": [],
|
||||
"screenshot": "preview/shop.png"
|
||||
},
|
||||
{
|
||||
"screen": "dig",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"imageCount": 30,
|
||||
"broken": [],
|
||||
"screenshot": "preview/dig.png"
|
||||
},
|
||||
{
|
||||
"screen": "bubble",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"imageCount": 7,
|
||||
"broken": [],
|
||||
"screenshot": "preview/bubble.png"
|
||||
},
|
||||
{
|
||||
"screen": "revive",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"imageCount": 7,
|
||||
"broken": [],
|
||||
"screenshot": "preview/revive.png"
|
||||
},
|
||||
{
|
||||
"screen": "victory",
|
||||
"width": 750,
|
||||
"height": 1334,
|
||||
"imageCount": 2,
|
||||
"broken": [],
|
||||
"screenshot": "preview/victory.png"
|
||||
}
|
||||
],
|
||||
"coverage": [
|
||||
{
|
||||
"id": "board",
|
||||
"rendered": true,
|
||||
"images": 23,
|
||||
"broken": [],
|
||||
"primary": "ROLL"
|
||||
},
|
||||
{
|
||||
"id": "dice",
|
||||
"rendered": true,
|
||||
"images": 25,
|
||||
"broken": [],
|
||||
"primary": "前进 7 格"
|
||||
},
|
||||
{
|
||||
"id": "third-die",
|
||||
"rendered": true,
|
||||
"images": 26,
|
||||
"broken": [],
|
||||
"primary": "确认选择"
|
||||
},
|
||||
{
|
||||
"id": "battle",
|
||||
"rendered": true,
|
||||
"images": 8,
|
||||
"broken": [],
|
||||
"primary": "FIGHTING…"
|
||||
},
|
||||
{
|
||||
"id": "gear",
|
||||
"rendered": true,
|
||||
"images": 9,
|
||||
"broken": [],
|
||||
"primary": "装备并分解旧武器"
|
||||
},
|
||||
{
|
||||
"id": "shop",
|
||||
"rendered": true,
|
||||
"images": 13,
|
||||
"broken": [],
|
||||
"primary": "EXIT"
|
||||
},
|
||||
{
|
||||
"id": "tile-install",
|
||||
"rendered": true,
|
||||
"images": 26,
|
||||
"broken": [],
|
||||
"primary": "安装到高亮地块"
|
||||
},
|
||||
{
|
||||
"id": "chest",
|
||||
"rendered": true,
|
||||
"images": 6,
|
||||
"broken": [],
|
||||
"primary": "打开宝箱"
|
||||
},
|
||||
{
|
||||
"id": "campfire",
|
||||
"rendered": true,
|
||||
"images": 6,
|
||||
"broken": [],
|
||||
"primary": "休息"
|
||||
},
|
||||
{
|
||||
"id": "smithy",
|
||||
"rendered": true,
|
||||
"images": 6,
|
||||
"broken": [],
|
||||
"primary": "强化武器"
|
||||
},
|
||||
{
|
||||
"id": "blessing",
|
||||
"rendered": true,
|
||||
"images": 6,
|
||||
"broken": [],
|
||||
"primary": "接受祝福"
|
||||
},
|
||||
{
|
||||
"id": "dig",
|
||||
"rendered": true,
|
||||
"images": 30,
|
||||
"broken": [],
|
||||
"primary": "DIGGING…"
|
||||
},
|
||||
{
|
||||
"id": "legendary",
|
||||
"rendered": true,
|
||||
"images": 7,
|
||||
"broken": [],
|
||||
"primary": "装备「炎刃」"
|
||||
},
|
||||
{
|
||||
"id": "lotto",
|
||||
"rendered": true,
|
||||
"images": 7,
|
||||
"broken": [],
|
||||
"primary": "抽取奖励"
|
||||
},
|
||||
{
|
||||
"id": "blackjack",
|
||||
"rendered": true,
|
||||
"images": 5,
|
||||
"broken": [],
|
||||
"primary": "停牌并领取"
|
||||
},
|
||||
{
|
||||
"id": "claw",
|
||||
"rendered": true,
|
||||
"images": 7,
|
||||
"broken": [],
|
||||
"primary": "落爪"
|
||||
},
|
||||
{
|
||||
"id": "carriage",
|
||||
"rendered": true,
|
||||
"images": 6,
|
||||
"broken": [],
|
||||
"primary": "选择传奇装备"
|
||||
},
|
||||
{
|
||||
"id": "dice-machine",
|
||||
"rendered": true,
|
||||
"images": 9,
|
||||
"broken": [],
|
||||
"primary": "ROLL"
|
||||
},
|
||||
{
|
||||
"id": "bubble",
|
||||
"rendered": true,
|
||||
"images": 7,
|
||||
"broken": [],
|
||||
"primary": "SHOOT"
|
||||
},
|
||||
{
|
||||
"id": "revive",
|
||||
"rendered": true,
|
||||
"images": 7,
|
||||
"broken": [],
|
||||
"primary": "FIGHTING..."
|
||||
},
|
||||
{
|
||||
"id": "victory",
|
||||
"rendered": true,
|
||||
"images": 2,
|
||||
"broken": [],
|
||||
"primary": "领取并离开"
|
||||
},
|
||||
{
|
||||
"id": "defeat",
|
||||
"rendered": true,
|
||||
"images": 2,
|
||||
"broken": [],
|
||||
"primary": "返回关卡选择"
|
||||
},
|
||||
{
|
||||
"id": "pause",
|
||||
"rendered": true,
|
||||
"images": 23,
|
||||
"broken": [],
|
||||
"primary": "ROLL"
|
||||
}
|
||||
],
|
||||
"requestFailures": [],
|
||||
"pageErrors": []
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
screen_id,screen_name,layer_type,entry,primary_action,exit,fixed_anchors,acceptance
|
||||
UI-002,关卡棋盘,基础场景,进入关卡或事件结束,ROLL,骰子结算,"积分|AUTO/暂停|层数|HP/装备|药水",40个实际地块均可独立落点且菱形环路与中心首领清晰
|
||||
UI-003,骰子结算,叠层,点击ROLL,前进N格,棋子移动,"积分|AUTO/暂停|层数|HP/装备|药水",红蓝双骰与总点数同屏且1点严格对应1个顺时针地块
|
||||
UI-004,第三骰选择,叠层,特殊祝福触发,确认选择,返回投掷,"积分|AUTO/暂停|层数|HP/装备|药水",三项差异清晰且只允许一个选中态
|
||||
UI-005,自动战斗,基础场景,战斗格触发,FIGHTING,战斗胜利或失败,"积分|AUTO/暂停|层数|HP/装备|药水",双方进度与伤害可读且仅药水为即时干预
|
||||
UI-006,装备比较,叠层,战斗掉落点击,装备并分解旧装备,返回战斗,"积分|AUTO/暂停|层数|HP/装备|药水",新装备在上且属性升降色形双重表达
|
||||
UI-007,商店事件,中部替换,商店格或3/6/9层,购买或EXIT,返回棋盘,"积分|AUTO/暂停|层数|HP/装备|药水",三普通三特殊商品且已购原位SOLD
|
||||
UI-008,地块安装,叠层,完成一圈,安装到高亮地块,返回棋盘,"积分|AUTO/暂停|层数|HP/装备|药水",明确回复30%和安装2格并防止重复落点
|
||||
UI-009,宝箱事件,中部替换,宝箱格,打开宝箱,领取后返回棋盘,"积分|AUTO/暂停|层数|HP/装备|药水",箱体是主焦点且重掷成本可见
|
||||
UI-010,篝火事件,中部替换,篝火格,休息,返回棋盘,"积分|AUTO/暂停|层数|HP/装备|药水",回复10%与新增地块同时反馈
|
||||
UI-011,铁匠事件,中部替换,铁匠格,强化装备,返回棋盘,"积分|AUTO/暂停|层数|HP/装备|药水",强化对象和结果品质变化可追溯
|
||||
UI-012,祝福三选一,中部替换,祝福格,确认祝福,返回棋盘,"积分|AUTO/暂停|层数|HP/装备|药水",三卡同屏且重掷成本不会误触
|
||||
UI-013,挖掘事件,中部替换,挖掘格,DIGGING,次数耗尽或宝物找全,"积分|AUTO/暂停|层数|HP/装备|药水",5×5网格与剩余宝物铲数始终可见
|
||||
UI-014,传奇装备,中部替换,稀有事件触发,装备,返回棋盘,"积分|AUTO/暂停|层数|HP/装备|药水",品质和核心属性在确认前完整可见
|
||||
UI-015,乐透事件,中部替换,乐透格,抽取奖励,返回棋盘,"积分|AUTO/暂停|层数|HP/装备|药水",抽取次数和奖池收益明确
|
||||
UI-016,二十一点事件,中部替换,纸牌事件格,停牌或要牌,结算后返回棋盘,"积分|AUTO/暂停|层数|HP/装备|药水",当前点数与爆牌风险无需计算外信息
|
||||
UI-017,抓娃娃事件,中部替换,抓取机格,落爪,次数耗尽或领取,"积分|AUTO/暂停|层数|HP/装备|药水",准星位置和剩余次数可见
|
||||
UI-018,黄金马车,中部替换,稀有事件格,选择奖励,返回棋盘,"积分|AUTO/暂停|层数|HP/装备|药水",金币与装备价值并列比较
|
||||
UI-019,骰子机事件,中部替换,骰子机格,ROLL,结算后返回棋盘,"积分|AUTO/暂停|层数|HP/装备|药水",三个结果和大奖规则同屏
|
||||
UI-020,泡泡射手,中部替换,泡泡事件格,SHOOT,次数耗尽或领取,"积分|AUTO/暂停|层数|HP/装备|药水",炮台方向泡泡区与剩余射击不互相遮挡
|
||||
UI-021,源码祝福复生,叠层,战斗生命归零且Revive祝福可用,按祝福规则自动结算,恢复战斗或失败,"积分|AUTO/暂停|层数|HP/装备|药水",无广告复活和放弃广告按钮;祝福不可用时完整播放DEFEAT后失败
|
||||
UI-023,关卡胜利,全屏终态,第10层首领击败,领取并离开,关卡外,无,奖励完整且不可返回战斗
|
||||
UI-024,关卡失败,全屏终态,无复活且生命归零,返回关卡选择,关卡外,无,失败信息与离开动作单一
|
||||
UI-025,暂停界面,叠层,点击暂停,继续游戏,返回原状态,"积分|层数|HP/装备",继续为首项且退出本局使用危险色
|
||||
|
@@ -0,0 +1,110 @@
|
||||
import http from 'node:http';
|
||||
import fs from 'node:fs/promises';
|
||||
import fsSync from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { chromium } from 'file:///C:/Users/toshio/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright/index.mjs';
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const workspace = path.resolve(here, '../../..');
|
||||
const previewDir = path.resolve(here, '../preview');
|
||||
const qaPath = path.resolve(here, '../qa-results.json');
|
||||
const screenshotScreens = ['board','battle','gear','shop','dig','bubble','revive','victory'];
|
||||
const expectedScreens = ['board','dice','third-die','battle','gear','shop','tile-install','chest','campfire','smithy','blessing','dig','legendary','lotto','blackjack','claw','carriage','dice-machine','bubble','revive','victory','defeat','pause'];
|
||||
|
||||
const mime = new Map([
|
||||
['.html','text/html; charset=utf-8'],['.css','text/css; charset=utf-8'],['.js','text/javascript; charset=utf-8'],
|
||||
['.json','application/json; charset=utf-8'],['.png','image/png'],['.jpg','image/jpeg'],['.jpeg','image/jpeg']
|
||||
]);
|
||||
|
||||
const server = http.createServer(async (req,res) => {
|
||||
try {
|
||||
const url = new URL(req.url, 'http://127.0.0.1');
|
||||
const decoded = decodeURIComponent(url.pathname);
|
||||
const relative = decoded === '/' ? 'design/ue-ui/prototype/index.html' : decoded.replace(/^\//,'');
|
||||
let target = path.resolve(workspace, relative);
|
||||
if (!target.startsWith(workspace)) throw new Error('Path outside workspace');
|
||||
const stat = await fs.stat(target);
|
||||
if (stat.isDirectory()) target = path.join(target, 'index.html');
|
||||
const data = await fs.readFile(target);
|
||||
res.writeHead(200, {'Content-Type': mime.get(path.extname(target).toLowerCase()) || 'application/octet-stream','Cache-Control':'no-store'});
|
||||
res.end(data);
|
||||
} catch {
|
||||
res.writeHead(404, {'Content-Type':'text/plain; charset=utf-8'});
|
||||
res.end('Not found');
|
||||
}
|
||||
});
|
||||
|
||||
await new Promise(resolve => server.listen(0,'127.0.0.1',resolve));
|
||||
const port = server.address().port;
|
||||
const candidates = [
|
||||
'C:/Program Files/Google/Chrome/Application/chrome.exe',
|
||||
'C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe',
|
||||
'C:/Program Files/Microsoft/Edge/Application/msedge.exe'
|
||||
];
|
||||
const executablePath = candidates.find(p => fsSync.existsSync(p));
|
||||
if (!executablePath) throw new Error('No local Chrome or Edge executable found.');
|
||||
|
||||
await fs.mkdir(previewDir,{recursive:true});
|
||||
const browser = await chromium.launch({headless:true,executablePath});
|
||||
const context = await browser.newContext({viewport:{width:750,height:1334},deviceScaleFactor:1});
|
||||
const page = await context.newPage();
|
||||
const requestFailures = [];
|
||||
const pageErrors = [];
|
||||
page.on('requestfailed',r => requestFailures.push({url:r.url(),error:r.failure()?.errorText || 'unknown'}));
|
||||
page.on('pageerror',e => pageErrors.push(String(e)));
|
||||
|
||||
const results = [];
|
||||
try {
|
||||
for (const id of screenshotScreens) {
|
||||
await page.goto(`http://127.0.0.1:${port}/design/ue-ui/prototype/?screen=${id}&capture=1`,{waitUntil:'networkidle'});
|
||||
await page.waitForFunction(() => [...document.images].every(img => img.complete));
|
||||
const metrics = await page.evaluate(() => {
|
||||
const phone=document.querySelector('#phone');
|
||||
const rect=phone.getBoundingClientRect();
|
||||
const broken=[...document.images].filter(img=>!img.naturalWidth).map(img=>img.getAttribute('src'));
|
||||
return {width:rect.width,height:rect.height,imageCount:document.images.length,broken};
|
||||
});
|
||||
await page.screenshot({path:path.join(previewDir,`${id}.png`),fullPage:false});
|
||||
results.push({screen:id,...metrics,screenshot:`preview/${id}.png`});
|
||||
}
|
||||
|
||||
await page.goto(`http://127.0.0.1:${port}/design/ue-ui/prototype/?screen=board&capture=1`,{waitUntil:'networkidle'});
|
||||
const coverage = await page.evaluate(async ids => {
|
||||
const rows=[];
|
||||
for (const id of ids) {
|
||||
window.HEROLL_UI.selectScreen(id);
|
||||
await new Promise(r=>requestAnimationFrame(()=>requestAnimationFrame(r)));
|
||||
const root=document.querySelector('.game-screen');
|
||||
const imgs=[...document.images];
|
||||
rows.push({id,rendered:Boolean(root),images:imgs.length,broken:imgs.filter(i=>!i.naturalWidth).map(i=>i.getAttribute('src')),primary:document.querySelector('.primary,.result-button')?.textContent?.trim() || ''});
|
||||
}
|
||||
return rows;
|
||||
}, expectedScreens);
|
||||
|
||||
const qa = {
|
||||
generatedAt:new Date().toISOString(),
|
||||
target:{width:750,height:1334,aspect:'9:16 portrait'},
|
||||
browser:{executablePath},
|
||||
summary:{
|
||||
expectedScreenCount:expectedScreens.length,
|
||||
renderedScreenCount:coverage.filter(r=>r.rendered).length,
|
||||
screenshotCount:results.length,
|
||||
brokenImageCount:[...results.flatMap(r=>r.broken),...coverage.flatMap(r=>r.broken)].length,
|
||||
requestFailureCount:requestFailures.length,
|
||||
pageErrorCount:pageErrors.length,
|
||||
dimensionsPass:results.every(r=>r.width===750 && r.height===1334)
|
||||
},
|
||||
screenshots:results,
|
||||
coverage,
|
||||
requestFailures,
|
||||
pageErrors
|
||||
};
|
||||
qa.summary.pass = qa.summary.renderedScreenCount===qa.summary.expectedScreenCount && qa.summary.brokenImageCount===0 && qa.summary.requestFailureCount===0 && qa.summary.pageErrorCount===0 && qa.summary.dimensionsPass;
|
||||
await fs.writeFile(qaPath,JSON.stringify(qa,null,2),'utf8');
|
||||
console.log(JSON.stringify(qa.summary,null,2));
|
||||
if (!qa.summary.pass) process.exitCode=1;
|
||||
} finally {
|
||||
await browser.close();
|
||||
await new Promise(resolve=>server.close(resolve));
|
||||
}
|
||||