Commit 322f7929 authored by Nemo Ma's avatar Nemo Ma

Add Quests Implementation Phase 1

parent 87386b40
QUEST系统实装计划 / QUEST System Implementation Plan
目标 / Goal
在不指定Ruleset的主房间/默认规则下,正式实装QUEST系统:
玩家在游荡中满足条件即可接到任务;任务状态保存在clbpara;
slidingpanel的任务窗体展示任务进度与追踪信息。
一、数据结构 / Data Model
1) clbpara结构 / clbpara schema
- clbpara['quest'] 作为任务主容器
- active: 当前进行任务(键为QuestID,值为任务状态)
- completed: 已完成记录(可存时间戳与次数)
- failed: 失败记录(可存原因与时间戳)
- cooldown: 任务分配冷却/限频信息
2) 任务状态字段 / Per-quest state fields
- step: 当前步骤
- progress: 进度计数/数值
- flags: 阶段标记(如已召唤、已交付、已触发)
- linked_npc_id / linked_player_id
- buff_level / threshold / ban_count等
- last_update_time
3) 任务物品 itmpara / Quest item itmpara
- IsQuestItem=1
- QuestID / QuestStep
- LinkedNPCID / LinkedPlayerID
- BuffLevel / Threshold / RequiredValue
- QuestType / QuestPhase
二、配置文件 / Configuration Files
1) 新增Quest配置文件(默认规则)
- gamedata/questcfg_1.php
- 任务元数据:标题、描述、颜色、接取条件、步骤、奖励、判定阈值
- NPC模板引用与道具模板引用(使用ID而非名称)
2) 新增NPC定义文件(默认规则)
- gamedata/addnpc_quest_1.php
- 各任务NPC模板(敌对/非敌对、掉落、对话等)
- 预留占位名称,后续可替换
3) 新增物品定义文件(默认规则)
- gamedata/questitem_1.php
- 任务道具模板(召唤道具、交付道具、判定道具、消耗道具)
- 统一写入itmpara关键字段
4) 配置加载机制
- 增加资源加载入口:当ruleset为空时读取gamedata下的questcfg/addnpc_quest/questitem
- 统一通过config()或新helper读取,避免硬编码
三、核心逻辑模块 / Core Logic Modules
1) include/game/quest.func.php
- quest_try_assign(): 游荡时尝试接任务
- quest_start(): 初始化任务状态,发放道具
- quest_update_progress(): 进度更新
- quest_complete()/quest_fail(): 结算/失败
- quest_spawn_npc(): 封装addnpc + clbpara初始化
- quest_spawn_item(): 发放任务道具
- quest_build_ui_payload(): 给slidingpanel使用的数据格式
2) include/game/item.quest.php
- quest_item_use(): 解析itmpara并执行任务逻辑
- QUEST1/2/3/4/5/6/7对应的道具使用分支
- 统一处理道具消耗/替换
3) 加载入口
- include/game/item.main.php: 在item_npc之前或之后加入item_quest
- include/game/search.func.php: 在探索/移动完成后调用quest_try_assign
- include/state.func.php: 在击杀/死亡事件中检测QUEST相关NPC
- include/game/revcombat_extra.func.php: 战斗前事件层用于非战斗交互
四、UI显示 / Slidingpanel UI
1) templates/default/slidingpanel.htm
- 替换占位内容,改为读取clbpara['quest']并渲染
- 如果无任务显示“暂无任务”
- 展示:任务名、步骤、进度、提示文本
2) templates/nouveau/slidingpanel.htm
- 增加QUEST标签页或STATUS子模块
- 同样读取clbpara['quest']
五、任务流程落地 / Quest Flow Implementation
QUEST1 猎杀乱入NPC
- 接任务 -> 获得召唤道具 -> 使用道具召唤NPC -> 击杀判定成功
- 通过NPC clbpara标记QuestID/LinkedPlayerID
- 死亡事件中判定并记录clbpara
QUEST2 保护乱入NPC
- 接任务 -> 获得召唤道具+守护道具
- NPC clbpara: LinkedPlayerID, BuffLevel, QuestID
- 道具使用:玩家损HP,NPC回复LP并提升BuffLevel
- NPC死亡:失败并记录击杀者ID(gamevars或NPC clbpara)
- 超过X禁且NPC存活:成功,守护道具变为领赏道具
QUEST3 数值怪
- 接任务 -> 获得判定道具
- 使用时检查指定数值(att/def/ss等)达到阈值即完成
QUEST4 昔日重现
- 接任务 -> 获得照片道具 -> 指定地图使用召唤NPC
- 战斗前事件层检测到NPC+特定道具:触发对话并移除NPC
- 完成任务标记,不计击杀
QUEST5 救赎之手
- 使用药剂召唤敌对NPC
- 战斗中将HP压到阈值以下
- 再次使用药剂触发转化事件(非击杀)
- NPC撤退或掉落“清醒的证明”
QUEST6 躲猫猫
- 使用侦探眼镜提示地点
- 遇到NPC时跳过战斗,直接获得糖果
- 收集3个糖果合成任务物品交付
QUEST7 偶像握手会
- 歌魂达到标准后才能开启
- 使用应援棒召唤NPC
- 战斗界面改为“打Call”模式(限制指令/技能)
- 坚持X回合或应援值达标即成功
六、兼容性与安全 / Compatibility & Safety
- 不修改现有规则集配置文件(只新增默认资源)
- 任务名/NPC名/物品名均为占位,可随时替换
- 使用clbpara存储,避免DB结构变更
- 所有任务道具标记IsQuestItem防止被误用
七、文件清单 / File List
- gamedata/questcfg_1.php
- gamedata/addnpc_quest_1.php
- gamedata/questitem_1.php
- include/game/quest.func.php
- include/game/item.quest.php
- include/game/search.func.php(接取钩子)
- include/game/item.main.php(道具分发)
- include/state.func.php(NPC死亡判定)
- include/game/revcombat_extra.func.php(非战斗交互)
- templates/default/slidingpanel.htm
- templates/nouveau/slidingpanel.htm
八、验证 / Verification
- 进入游戏后游荡触发任务
- slidingpanel显示任务
- 逐个任务完成/失败逻辑可被clbpara正确记录
------------------------------
English Summary
+This plan implements a full QUEST system in default (non-ruleset) rooms by:
+- Adding a quest data model in clbpara
+- Creating default quest/NPC/item config files
+- Implementing quest core logic + quest item handlers
+- Hooking assignment into exploration and completion into NPC death events
+- Updating slidingpanel UI to show quest status
+- Implementing quest-specific flow logic for QUEST1-QUEST7
Awaiting review and instructions.
QUEST系统实装 / QUEST System Implementation
变更内容 / Changes
1) 新增默认任务配置、任务NPC与任务物品定义文件。
Added default quest config, quest NPC, and quest item definition files.
2) 实装QUEST核心流程:接取、进度、完成/失败、召唤NPC与发放道具。
Implemented core quest flow: assign, progress, complete/fail, summon NPCs, and spawn items.
3) 接入探索与战斗流程:探索时尝试分配任务,战斗前/结算事件处理特殊互动,NPC死亡触发判定。
Wired into exploration and combat: assign during search, combat prepare/attack result events, NPC death handling.
4) 更新副面板任务窗体(默认与nouveau)为动态显示。
Updated sliding panel quest UI (default + nouveau) to render dynamic quest state.
设计考量 / Design Notes
- 任务名、NPC名、物品名均为占位符,便于复用与替换。
Names are placeholders and can be replaced without logic changes.
- 任务状态统一存储于clbpara,避免DB结构变更。
Quest state is stored in clbpara to avoid DB schema changes.
- 任务物品使用itmpara标记,便于识别与扩展。
Quest items use itmpara tags for identification and extension.
影响范围 / Affected Files
- gamedata/questcfg_1.php
- gamedata/addnpc_quest_1.php
- gamedata/questitem_1.php
- include/resources.func.php
- include/game/quest.func.php
- include/game/item.quest.php
- include/game/item.main.php
- include/game/search.func.php
- include/game/revcombat_extra.func.php
- include/game/revcombat.func.php
- include/state.func.php
- templates/default/slidingpanel.htm
- templates/nouveau/slidingpanel.htm
https://opncd.ai/share/nLHgfNma
\ No newline at end of file
<?php
if (!defined('IN_GAME')) {
exit('Access Denied');
}
// QUEST NPC definitions (default ruleset)
// 任务NPC定义(默认规则)
// 仅追加sub,不覆盖原有类型定义 / Append subs only, keep base type settings
$quest_anpcinfo = array(
93 => array(
'sub' => array(
// Q1: intruder hunter target
'quest1_intruder' => array(
'name' => '乱入的影子',
'description' => '占位NPC:猎杀目标。/ Placeholder NPC: hunt target.',
'icon' => 7,
'gd' => 'm',
'wep' => '未知武装',
'wepk' => 'WK',
'wepe' => 180,
'weps' => 80,
'mhp' => 800,
'msp' => 300,
'att' => 220,
'def' => 160,
'lvl' => 18,
'skill' => 120,
'money' => 200,
'pls' => 99,
),
// Q2: protected NPC
'quest2_protect' => array(
'name' => '脆弱的来访者',
'description' => '占位NPC:需要保护。/ Placeholder NPC: protect target.',
'icon' => 8,
'gd' => 'f',
'wep' => '临时防身品',
'wepk' => 'WP',
'wepe' => 60,
'weps' => 50,
'mhp' => 500,
'msp' => 200,
'att' => 120,
'def' => 100,
'lvl' => 10,
'skill' => 80,
'money' => 50,
'pls' => 99,
),
// Q4: phantom NPC (non-hostile interaction)
'quest4_phantom' => array(
'name' => '逝去的幻影',
'description' => '占位NPC:非敌对幻影。/ Placeholder NPC: friendly phantom.',
'icon' => 9,
'gd' => 'f',
'wep' => '无形回忆',
'wepk' => 'WP',
'wepe' => 1,
'weps' => 1,
'mhp' => 300,
'msp' => 200,
'att' => 1,
'def' => 1,
'lvl' => 1,
'skill' => 1,
'money' => 0,
'pls' => 99,
),
// Q5: experimental subject (purify)
'quest5_subject' => array(
'name' => '暴走的实验体',
'description' => '占位NPC:可净化目标。/ Placeholder NPC: purifiable target.',
'icon' => 10,
'gd' => 'm',
'wep' => '破坏冲动',
'wepk' => 'WG',
'wepe' => 140,
'weps' => 70,
'mhp' => 1200,
'msp' => 400,
'att' => 260,
'def' => 180,
'lvl' => 20,
'skill' => 140,
'money' => 150,
'pls' => 99,
),
// Q6: hide-and-seek
'quest6_hide' => array(
'name' => '捣蛋鬼',
'description' => '占位NPC:找到即可获得奖励。/ Placeholder NPC: found -> reward.',
'icon' => 11,
'gd' => 'r',
'wep' => '无害恶作剧',
'wepk' => 'WP',
'wepe' => 1,
'weps' => 1,
'mhp' => 200,
'msp' => 200,
'att' => 1,
'def' => 1,
'lvl' => 1,
'skill' => 1,
'money' => 0,
'pls' => 99,
),
// Q7: idol encounter
'quest7_idol' => array(
'name' => '流浪偶像',
'description' => '占位NPC:应援互动。/ Placeholder NPC: cheer interaction.',
'icon' => 12,
'gd' => 'f',
'wep' => '舞台光芒',
'wepk' => 'WC',
'wepe' => 80,
'weps' => 60,
'mhp' => 700,
'msp' => 300,
'att' => 160,
'def' => 120,
'lvl' => 16,
'skill' => 120,
'money' => 80,
'pls' => 99,
),
),
),
);
<?php
if (!defined('IN_GAME')) {
exit('Access Denied');
}
// QUEST系统配置 / QUEST system config
// 注意:此处名称为占位符,可在不改逻辑的情况下替换 / Placeholder names, safe to rename later
$questcfg_global = array(
// 同时进行任务数上限 / Max active quests
'max_active' => 1,
// 探索时触发任务的基础概率(百分比)/ Base assign chance per search (percent)
'assign_obbs' => 15,
// 任务触发冷却(秒)/ Assign cooldown in seconds
'assign_cooldown' => 120,
);
$questcfg = array(
// 橙色任务:强大 / Orange quests: power
'Q1' => array(
'title' => '橙色任务:猎杀乱入者',
'tier' => 'orange',
'repeatable' => 1,
'assign' => array(
'obbs' => 15,
'min_lvl' => 1,
),
'items' => array(
'start' => array('q1_summon'),
'reward' => array('q1_reward'),
),
'reward' => array('money' => 200, 'exp' => 80),
'npc' => array(
'summon' => array('type' => 93, 'sub' => 'quest1_intruder'),
),
'steps' => array(
1 => array('desc' => '使用召唤道具引来目标'),
2 => array('desc' => '击杀目标并完成任务'),
),
),
'Q2' => array(
'title' => '橙色任务:护送乱入者',
'tier' => 'orange',
'repeatable' => 1,
'assign' => array(
'obbs' => 12,
'min_lvl' => 3,
),
'items' => array(
'start' => array('q2_summon'),
'guard' => array('q2_guard'),
'reward' => array('q2_reward'),
),
'reward' => array('money' => 180, 'exp' => 70),
'npc' => array(
'summon' => array('type' => 93, 'sub' => 'quest2_protect'),
),
// 达到指定禁区数即成功 / Success after reaching this ban count
'ban_success' => 1,
'steps' => array(
1 => array('desc' => '召唤并保护目标'),
2 => array('desc' => '目标存活到禁区推进后'),
),
),
'Q3' => array(
'title' => '橙色任务:数值考验',
'tier' => 'orange',
'repeatable' => 1,
'assign' => array(
'obbs' => 18,
'min_lvl' => 1,
),
'items' => array(
'start' => array('q3_check'),
'reward' => array('q3_reward'),
),
'reward' => array('money' => 150, 'exp' => 60),
'requirements' => array(
// 可替换为别的字段 / Replace with other stats if needed
'min_att' => 300,
),
'steps' => array(
1 => array('desc' => '使用道具检查数值'),
),
),
// 蓝色任务:温柔 / Blue quests: gentle
'Q4' => array(
'title' => '蓝色任务:昔日重现',
'tier' => 'blue',
'repeatable' => 1,
'assign' => array(
'obbs' => 10,
'min_lvl' => 1,
),
'items' => array(
'start' => array('q4_photo'),
'flower' => array('q4_flower'),
'reward' => array('q4_reward'),
),
'reward' => array('money' => 160, 'exp' => 70),
'npc' => array(
'summon' => array('type' => 93, 'sub' => 'quest4_phantom'),
),
// 指定地图使用 / Use in specific map (0 means any)
'target_pls' => 0,
'steps' => array(
1 => array('desc' => '前往指定地点使用照片'),
2 => array('desc' => '以花束/对话安抚幻影'),
),
),
'Q5' => array(
'title' => '蓝色任务:救赎之手',
'tier' => 'blue',
'repeatable' => 1,
'assign' => array(
'obbs' => 10,
'min_lvl' => 5,
),
'items' => array(
'start' => array('q5_summon'),
'purify' => array('q5_purifier'),
'reward' => array('q5_reward'),
),
'reward' => array('money' => 220, 'exp' => 90),
'npc' => array(
'summon' => array('type' => 93, 'sub' => 'quest5_subject'),
),
// 触发净化的血量阈值(百分比)/ HP threshold percent to purify
'hp_threshold' => 20,
'steps' => array(
1 => array('desc' => '召唤实验体并削弱'),
2 => array('desc' => '使用净化药剂完成救赎'),
),
),
// 绿色任务:可爱 / Green quests: cute
'Q6' => array(
'title' => '绿色任务:躲猫猫',
'tier' => 'green',
'repeatable' => 1,
'assign' => array(
'obbs' => 20,
'min_lvl' => 1,
),
'items' => array(
'start' => array('q6_glasses'),
'candy' => array('q6_candy'),
'reward' => array('q6_reward'),
),
'reward' => array('money' => 120, 'exp' => 50),
'npc' => array(
'summon' => array('type' => 93, 'sub' => 'quest6_hide'),
),
// 需要收集的数量 / Candy count needed
'candy_need' => 3,
'steps' => array(
1 => array('desc' => '用侦探眼镜定位捣蛋鬼'),
2 => array('desc' => '收集糖果并交付'),
),
),
'Q7' => array(
'title' => '绿色任务:偶像握手会',
'tier' => 'green',
'repeatable' => 1,
'assign' => array(
'obbs' => 12,
'min_ss' => 50,
),
'items' => array(
'start' => array('q7_cheerstick'),
'reward' => array('q7_reward'),
),
'reward' => array('money' => 200, 'exp' => 80),
'npc' => array(
'summon' => array('type' => 93, 'sub' => 'quest7_idol'),
),
// 需要坚持回合 / Required turns
'turn_need' => 3,
// 需要的应援值 / Required cheer points
'cheer_need' => 600,
'steps' => array(
1 => array('desc' => '召唤偶像并完成应援'),
),
),
);
<?php
if (!defined('IN_GAME')) {
exit('Access Denied');
}
// QUEST items definitions (default ruleset)
// 任务物品定义(默认规则)
$questiteminfo = array(
// Q1
'q1_summon' => array(
'itm' => '猎杀信标',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q1', 'QuestAction' => 'summon'),
),
'q1_reward' => array(
'itm' => '猎杀徽记',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q1', 'QuestAction' => 'reward'),
),
// Q2
'q2_summon' => array(
'itm' => '护送信标',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q2', 'QuestAction' => 'summon'),
),
'q2_guard' => array(
'itm' => '护卫终端',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q2', 'QuestAction' => 'guard'),
),
'q2_reward' => array(
'itm' => '护送证明',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q2', 'QuestAction' => 'reward'),
),
// Q3
'q3_check' => array(
'itm' => '数值检验器',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q3', 'QuestAction' => 'check'),
),
'q3_reward' => array(
'itm' => '检验合格章',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q3', 'QuestAction' => 'reward'),
),
// Q4
'q4_photo' => array(
'itm' => '褪色的旧照片',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q4', 'QuestAction' => 'summon'),
),
'q4_flower' => array(
'itm' => '献上的花束',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q4', 'QuestAction' => 'comfort'),
),
'q4_reward' => array(
'itm' => '遗言碎片',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q4', 'QuestAction' => 'reward'),
),
// Q5
'q5_summon' => array(
'itm' => '净化召唤器',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q5', 'QuestAction' => 'summon'),
),
'q5_purifier' => array(
'itm' => '净化药剂',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q5', 'QuestAction' => 'purify'),
),
'q5_reward' => array(
'itm' => '清醒的证明',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q5', 'QuestAction' => 'reward'),
),
// Q6
'q6_glasses' => array(
'itm' => '侦探眼镜',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q6', 'QuestAction' => 'clue'),
),
'q6_candy' => array(
'itm' => '捣蛋鬼的糖果',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q6', 'QuestAction' => 'candy'),
),
'q6_reward' => array(
'itm' => '捉迷藏证明',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q6', 'QuestAction' => 'reward'),
),
// Q7
'q7_cheerstick' => array(
'itm' => '应援棒',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q7', 'QuestAction' => 'summon'),
),
'q7_reward' => array(
'itm' => '握手会凭证',
'itmk' => 'YQ',
'itme' => 1,
'itms' => 1,
'itmsk' => '',
'itmpara' => array('IsQuestItem' => 1, 'QuestID' => 'Q7', 'QuestAction' => 'reward'),
),
);
......@@ -77,6 +77,7 @@ function itemuse($itmn,&$data=NULL) {
include_once GAME_ROOT.'./include/game/item.ending.php';
include_once GAME_ROOT.'./include/game/item.special_effect.php';
include_once GAME_ROOT.'./include/game/item.npc.php';
include_once GAME_ROOT.'./include/game/item.quest.php';
include_once GAME_ROOT.'./include/game/item.synthesis.php';
include_once GAME_ROOT.'./include/game/item.club_card.php';
include_once GAME_ROOT.'./include/game/item.nachster_booster.php';
......@@ -180,6 +181,8 @@ function itemuse($itmn,&$data=NULL) {
item_special_effect($itmn, $data);
// Check if it's an NPC-related item
item_npc($itmn, $data);
// Check if it's a quest item
item_quest($itmn, $data);
// Check if it's a synthesis item
item_synthesis($itmn, $data);
// Check if it's a club card
......
This diff is collapsed.
This diff is collapsed.
......@@ -676,7 +676,8 @@ namespace revcombat
else
{
# 执行扣血后的战斗结算阶段事件
attack_result_events($pa,$pd,$active);
$event_flag = attack_result_events($pa,$pd,$active);
if($event_flag < 0) return 0;
}
return 1;
}
......@@ -724,4 +725,4 @@ namespace revcombat
return;
}
}
?>
\ No newline at end of file
?>
......@@ -11,6 +11,7 @@ namespace revcombat
include_once GAME_ROOT.'./include/game/revattr.func.php';
include_once GAME_ROOT.'./include/game/revattr.calc.php';
include_once GAME_ROOT.'./include/game/revattr_extra.func.php';
include_once GAME_ROOT.'./include/game/quest.func.php';
# 初始化双方的攻击相关参数:
# 依以下次序判定:
......@@ -67,13 +68,17 @@ namespace revcombat
if(!empty($pa['arbs']) && $pa['arb'] == '【智代专用熊装】') \revattr\attr_ach53_check($pa,$pd,$active);
if(!empty($pd['arbs']) && $pd['arb'] == '【智代专用熊装】') \revattr\attr_ach53_check($pd,$pa,$active);
# QUEST战斗前事件 / QUEST combat prepare events
$quest_flag = \quest_combat_prepare_events($pa,$pd,$active);
if($quest_flag < 0) return $quest_flag;
return 1;
}
# 进入rev_combat战斗状态后,在判定伤害、反击流程前的喊话事件
function combat_prepare_logs(&$pa,&$pd,$active)
{
global $log;
global $log,$now;
if(!empty($pa['message']))
{
$log.="<span class=\"lime\">{$pa['nm']}{$pd['nm']}喊道:「{$pa['message']}」!</span><br>";
......@@ -363,6 +368,10 @@ namespace revcombat
}
}
}
# QUEST战斗结算事件 / QUEST attack result events
$quest_flag = \quest_attack_result_events($pa,$pd,$active);
if($quest_flag < 0) return $quest_flag;
return;
}
......@@ -502,4 +511,4 @@ namespace revcombat
}
?>
\ No newline at end of file
?>
......@@ -11,6 +11,7 @@ include_once GAME_ROOT.'./include/game/revbattle.func.php';
include_once GAME_ROOT.'./include/game/revbattle.calc.php';
include_once GAME_ROOT.'./include/game/revcombat.func.php';
include_once GAME_ROOT.'./include/game/revevent.func.php';
include_once GAME_ROOT.'./include/game/quest.func.php';
function check_can_move($pls,$pgroup,$moveto)
{
......@@ -119,6 +120,10 @@ function move($moveto = 99,&$data=NULL)
# 更新charge值
process_charge_events($data);
// QUEST周期与分配 / QUEST tick and assignment
quest_tick($data);
quest_try_assign($data);
# 如果是种火歌者,处理种火相关逻辑
if($club == 22) {
include_once GAME_ROOT.'./include/game/club22.func.php';
......
......@@ -71,6 +71,27 @@ function get_addnpcinfo()
{
global $gamecfg;
include config("addnpc",$gamecfg);
// 追加QUEST NPC定义 / Append QUEST NPC definitions
$quest_npc_file = GAME_ROOT.'./gamedata/addnpc_quest_1.php';
if (file_exists($quest_npc_file)) {
include $quest_npc_file;
if (!empty($quest_anpcinfo) && is_array($quest_anpcinfo)) {
foreach ($quest_anpcinfo as $qtype => $qinfo) {
if (!isset($anpcinfo[$qtype])) {
$anpcinfo[$qtype] = $qinfo;
continue;
}
// 仅合并sub,避免覆盖原有基础设定 / Merge subs only, keep base settings
if (isset($qinfo['sub']) && is_array($qinfo['sub'])) {
if (empty($anpcinfo[$qtype]['sub'])) {
$anpcinfo[$qtype]['sub'] = array();
}
$anpcinfo[$qtype]['sub'] = array_merge($anpcinfo[$qtype]['sub'], $qinfo['sub']);
}
}
}
}
return $anpcinfo;
}
......@@ -112,5 +133,28 @@ function get_set_items_info()
return $set_items_info;
}
// QUEST config / 任务配置
function get_questcfg()
{
$questcfg = array();
$questcfg_global = array();
$quest_file = GAME_ROOT.'./gamedata/questcfg_1.php';
if (file_exists($quest_file)) {
include $quest_file;
}
return array($questcfg, $questcfg_global);
}
// QUEST item templates / 任务物品模板
function get_questiteminfo()
{
$questiteminfo = array();
$quest_item_file = GAME_ROOT.'./gamedata/questitem_1.php';
if (file_exists($quest_item_file)) {
include $quest_item_file;
}
return $questiteminfo;
}
?>
......@@ -313,6 +313,7 @@
function check_death_events(&$pa,&$pd,$active)
{
global $db,$tablepre,$log,$now,$nosta;
include_once GAME_ROOT.'./include/game/quest.func.php';
# 静流下线事件:
if($pd['type'] == 15)
......@@ -358,6 +359,9 @@
# 保存击杀种火或小兵的记录
if(empty($pa['clbpara']['achvars']['kill_minion']) && ($pd['type'] == 90 || $pd['type'] == 91 || $pd['type'] == 92)) $pa['clbpara']['achvars']['kill_minion'] = 1;
// QUEST NPC死亡事件 / QUEST NPC death handling
quest_handle_npc_death($pa,$pd);
# 成就504,保存在RF高校用过的武器记录
if($pa['pls'] == 2) $pa['clbpara']['achvars']['ach504'][$pa['wep_kind']] = 1;
......
......@@ -27,60 +27,8 @@
<div id="misc-tab" class="tab-pane active">
<div class="section-container">
<h4 class="section-title">当前任务</h4>
<div class="section-content">
<!-- 任务项目1 -->
<div class="progress-item">
<div class="progress-label">
<span>任务信息</span>
</div>
<div class="quest-item">
这里是任务窗体。该窗体的内容通过读取clbpara中的键值生成。
</div>
</div>
<!-- 任务项目2 -->
<div class="progress-item">
<div class="progress-label">
<span>收集材料</span>
<span class="progress-value">3/10</span>
</div>
<div class="quest-item">
前往森林收集木材和药草,并将它们带回营地。
</div>
</div>
<!-- 任务项目3 -->
<div class="progress-item">
<div class="progress-label">
<span>探索遗迹</span>
<span class="progress-value">已发现入口</span>
</div>
<div class="quest-item">
探索古老的遗迹,寻找可能的线索和宝藏。
</div>
</div>
<!-- 任务项目4 -->
<div class="progress-item">
<div class="progress-label">
<span>消灭威胁</span>
<span class="progress-value">5/15</span>
</div>
<div class="quest-item">
消灭在附近地区活动的怪物,保护居民的安全。
</div>
</div>
<!-- 任务项目5 -->
<div class="progress-item">
<div class="progress-label">
<span>长期任务</span>
<span class="progress-value">初步阶段</span>
</div>
<div class="quest-item">
这是一个非常长的任务描述,用于测试文本换行和显示效果。这个任务需要玩家前往多个地点,收集各种物品,并且与多个NPC交流。
</div>
</div>
<div id="quest-list" class="section-content">
<div class="quest-item">暂无任务。</div>
</div>
</div>
</div>
......@@ -740,6 +688,9 @@
// 初始更新进度条(仅一次)
updateChargeProgressBars();
// 初始更新任务窗体
updateQuestPanel();
});
// 初始化面板
......@@ -1171,6 +1122,45 @@
}
}
// 更新任务窗体
function updateQuestPanel() {
const questList = document.getElementById('quest-list');
if (!questList) return;
if (!clbpara || !clbpara.quest || !clbpara.quest.active) {
questList.innerHTML = '<div class="quest-item">暂无任务。</div>';
return;
}
const active = clbpara.quest.active;
const questIds = Object.keys(active);
if (questIds.length === 0) {
questList.innerHTML = '<div class="quest-item">暂无任务。</div>';
return;
}
let html = '';
questIds.forEach(function(qid) {
const q = active[qid] || {};
const title = q.title || qid;
const stepDesc = q.step_desc || '';
const progress = (typeof q.progress !== 'undefined') ? q.progress : '';
const progressMax = (typeof q.progress_max !== 'undefined') ? q.progress_max : 0;
const progressText = (progressMax && progress !== '') ? (progress + '/' + progressMax) : '';
const statusText = q.ready_to_claim ? '可交付' : '';
html += '<div class="progress-item">';
html += ' <div class="progress-label">';
html += ' <span>' + title + '</span>';
html += ' <span class="progress-value">' + (progressText || statusText) + '</span>';
html += ' </div>';
html += ' <div class="quest-item">' + stepDesc + '</div>';
html += '</div>';
});
questList.innerHTML = html;
}
// 设置演示值
function setDemoValues() {
// Charge1 (0-101)
......@@ -1226,6 +1216,9 @@
// 更新种火标签页
updateFireseedTab();
// 更新任务窗体
updateQuestPanel();
console.log('Panel opened');
// 隐藏副面板按钮
......@@ -1296,6 +1289,12 @@
// 更新种火标签页
updateFireseedTab();
// 更新任务窗体
updateQuestPanel();
// 更新任务窗体
updateQuestPanel();
} catch (e) {
console.error('Failed to parse clbpara data:', e);
}
......@@ -1405,6 +1404,7 @@
// 使用当前数据更新显示
updateChargeProgressBars();
updateQuestPanel();
}
// 显示更新消息
......
......@@ -118,6 +118,34 @@
</div>
</div>
</div>
<!-- Quest Status -->
<div>
<h4 class="cyber-text-primary text-sm font-bold mb-2">QUESTS</h4>
<div class="space-y-2 text-xs">
<!--{if !empty($clbpara['quest']['active'])}-->
<!--{loop $clbpara['quest']['active'] $qid $qstate}-->
<div class="cyber-card p-2">
<div class="flex justify-between">
<span class="cyber-text-secondary">$qstate['title']</span>
<span class="cyber-text-primary">
<!--{if !empty($qstate['progress_max'])}-->
$qstate['progress']/$qstate['progress_max']
<!--{elseif !empty($qstate['ready_to_claim'])}-->
可交付
<!--{/if}-->
</span>
</div>
<div class="cyber-text-secondary text-xs">$qstate['step_desc']</div>
</div>
<!--{/loop}-->
<!--{else}-->
<div class="cyber-card p-2">
<span class="cyber-text-secondary">暂无任务</span>
</div>
<!--{/if}-->
</div>
</div>
</div>
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment