任务
This commit is contained in:
parent
2a8b44765c
commit
daef6be5c7
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"POTION_01": {
|
||||
"id": 1,
|
||||
"id": "POTION_01",
|
||||
"name": "疗伤药",
|
||||
"type": "potion",
|
||||
"description": "普通的疗伤药,可以恢复少量生命值。",
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
}
|
||||
},
|
||||
"POTION_02": {
|
||||
"id": 2,
|
||||
"id": "POTION_02",
|
||||
"name": "回气丹",
|
||||
"type": "potion",
|
||||
"description": "可以恢复一定量的真气。",
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
}
|
||||
},
|
||||
"POTION_03": {
|
||||
"id": 3,
|
||||
"id": "POTION_03",
|
||||
"name": "长春丹",
|
||||
"type": "potion",
|
||||
"description": "传说中的长春丹,据说有延年益寿之效。",
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
}
|
||||
},
|
||||
"WEAPON_01": {
|
||||
"id": 4,
|
||||
"id": "WEAPON_01",
|
||||
"name": "精钢剑",
|
||||
"type": "weapon",
|
||||
"description": "一柄精钢打造的长剑,锋利坚韧。",
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
}
|
||||
},
|
||||
"ARMOR_01": {
|
||||
"id": 5,
|
||||
"id": "ARMOR_01",
|
||||
"name": "铁布衫",
|
||||
"type": "armor",
|
||||
"description": "一件铁布衫,可以有效防护。",
|
||||
|
|
@ -53,21 +53,21 @@
|
|||
}
|
||||
},
|
||||
"CAOYAO": {
|
||||
"id": 6,
|
||||
"id": "CAOYAO",
|
||||
"name": "灵草药",
|
||||
"type": "material",
|
||||
"description": "一株灵草药,可用于炼制丹药。",
|
||||
"value": 50
|
||||
},
|
||||
"ZHENGBI": {
|
||||
"id": 7,
|
||||
"id": "CAOYAO",
|
||||
"name": "银两",
|
||||
"type": "currency",
|
||||
"description": "七玄门通行的货币,可用于交易。",
|
||||
"value": 1
|
||||
},
|
||||
"ZHUTIANPING": {
|
||||
"id": 8,
|
||||
"id": "ZHUTIANPING",
|
||||
"name": "掌天瓶",
|
||||
"type": "treasure",
|
||||
"description": "传说中的至宝掌天瓶,具有神秘力量。",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,13 @@
|
|||
"E": "VILLAGE_01",
|
||||
"W": "FOREST_01"
|
||||
},
|
||||
"encounterPool": ["GOBLIN", "WOLF"],
|
||||
"encounterPool": [{
|
||||
"id": "GOBLIN",
|
||||
"weight": 60
|
||||
}, {
|
||||
"id": "WOLF",
|
||||
"weight": 40
|
||||
}],
|
||||
"encounterChance": 0.5,
|
||||
"npcIds": ["VILLAGER_1"],
|
||||
"lootIds": ["POTION_01"]
|
||||
|
|
@ -20,7 +26,15 @@
|
|||
"W": "TOWN_01",
|
||||
"N": "CAVE_01"
|
||||
},
|
||||
"encounterPool": ["WOLF", "BEAR"],
|
||||
"encounterPool": [
|
||||
{
|
||||
"id": "WOLF",
|
||||
"weight": 60
|
||||
},
|
||||
{
|
||||
"id": "BEAR",
|
||||
"weight": 40
|
||||
}],
|
||||
"encounterChance": 0.5,
|
||||
"npcIds": [],
|
||||
"lootIds": ["HERB_BASIC"]
|
||||
|
|
@ -32,7 +46,12 @@
|
|||
"connections": {
|
||||
"E": "TOWN_01"
|
||||
},
|
||||
"encounterPool": ["STRANGE_CULTIVATOR"],
|
||||
"encounterPool": [
|
||||
{
|
||||
"id": "STRANGE_CULTIVATOR",
|
||||
"weight": 60
|
||||
}
|
||||
],
|
||||
"encounterChance": 0.5,
|
||||
"npcIds": ["BLACKSMITH"],
|
||||
"lootIds": ["CAOYAO"]
|
||||
|
|
@ -44,7 +63,12 @@
|
|||
"connections": {
|
||||
"S": "VILLAGE_01"
|
||||
},
|
||||
"encounterPool": ["GUARD"],
|
||||
"encounterPool": [
|
||||
{
|
||||
"id": "GUARD",
|
||||
"weight": 60
|
||||
}
|
||||
],
|
||||
"encounterChance": 0.5,
|
||||
"npcIds": ["MODOCTOR"],
|
||||
"lootIds": ["ZHENGBI", "ZHUTIANPING"]
|
||||
|
|
|
|||
|
|
@ -3,88 +3,171 @@
|
|||
"id": "QUEST_001",
|
||||
"name": "七玄门初入江湖",
|
||||
"description": "作为新入门的弟子,需要熟悉门派环境,完成基础任务。",
|
||||
"type": "collect",
|
||||
"target": {
|
||||
"entityId": "ZHENGBI",
|
||||
"count": 10
|
||||
},
|
||||
"rewards": {
|
||||
"xp": 50,
|
||||
"gold": 20,
|
||||
"itemId": "POTION_01"
|
||||
},
|
||||
"triggerType": "NPC",
|
||||
"triggerValue": "VILLAGER_1",
|
||||
"dialogue": {
|
||||
"start": "师弟,门中最近需要一些银两,你能帮我去后山收集10两银子吗?",
|
||||
"progress": "还差一些银两,继续努力。",
|
||||
"complete": "很好,师弟表现不错,这是你的奖励。"
|
||||
"npcId": "VILLAGER_1",
|
||||
"nodes": {
|
||||
"START": {
|
||||
"type": "dialogue",
|
||||
"text": "师弟,门中最近需要一些银两,你能帮我去后山收集10两银子吗?",
|
||||
"options": [
|
||||
{ "text": "好,我去收集银两。", "next_node": "ACCEPT" },
|
||||
{ "text": "现在没时间,下次再说。", "next_node": "END" }
|
||||
],
|
||||
"preConditions": [
|
||||
{ "type": "level", "value": 1 }
|
||||
]
|
||||
},
|
||||
"ACCEPT": {
|
||||
"type": "objective",
|
||||
"goal": { "type": "collect", "entityId": "ZHENGBI", "count": 10 },
|
||||
"on_complete": "REWARD"
|
||||
},
|
||||
"REWARD": {
|
||||
"type": "dialogue",
|
||||
"text": "很好,师弟表现不错,这是你的奖励。",
|
||||
"options": [
|
||||
{ "text": "感谢奖励", "next_node": null }
|
||||
],
|
||||
"actions": [
|
||||
{ "type": "give_xp", "amount": 50 },
|
||||
{ "type": "give_gold", "amount": 20 },
|
||||
{ "type": "give_item", "itemId": "POTION_01", "quantity": 1 },
|
||||
{ "type": "complete_quest", "questId": "QUEST_001" }
|
||||
]
|
||||
},
|
||||
"END": {
|
||||
"type": "dialogue",
|
||||
"text": "好吧,有时间再帮忙。",
|
||||
"options": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"QUEST_002": {
|
||||
"id": "QUEST_002",
|
||||
"name": "后山除害",
|
||||
"description": "后山出现了一些野兽,威胁门派安全,需要清除。",
|
||||
"type": "kill",
|
||||
"target": {
|
||||
"entityId": "WOLF",
|
||||
"count": 3
|
||||
},
|
||||
"rewards": {
|
||||
"xp": 100,
|
||||
"gold": 30,
|
||||
"itemId": "WEAPON_01"
|
||||
},
|
||||
"triggerType": "NPC",
|
||||
"triggerValue": "VILLAGER_1",
|
||||
"dialogue": {
|
||||
"start": "师弟,后山有三只野狼威胁弟子安全,你能帮我们解决吗?",
|
||||
"progress": "野狼还未全部清除,小心应对。",
|
||||
"complete": "太好了,后山安全了,这是给你的奖励。"
|
||||
"description": "后山出现了一些野狼,威胁门派安全,需要清除。",
|
||||
"npcId": "VILLAGER_1",
|
||||
"nodes": {
|
||||
"START": {
|
||||
"type": "dialogue",
|
||||
"text": "师弟,后山有三只野狼威胁弟子安全,你能帮我们解决吗?",
|
||||
"options": [
|
||||
{ "text": "野狼?我可以帮忙处理。", "next_node": "ACCEPT" },
|
||||
{ "text": "太危险了,我不去。", "next_node": "END" }
|
||||
],
|
||||
"preConditions": [
|
||||
{ "type": "level", "value": 2 }
|
||||
]
|
||||
},
|
||||
"ACCEPT": {
|
||||
"type": "objective",
|
||||
"goal": { "type": "kill", "entityId": "WOLF", "count": 3 },
|
||||
"on_complete": "REWARD"
|
||||
},
|
||||
"REWARD": {
|
||||
"type": "dialogue",
|
||||
"text": "太好了,后山安全了,这是给你的奖励。",
|
||||
"options": [
|
||||
{ "text": "收下奖励", "next_node": null }
|
||||
],
|
||||
"actions": [
|
||||
{ "type": "give_xp", "amount": 100 },
|
||||
{ "type": "give_gold", "amount": 30 },
|
||||
{ "type": "give_item", "itemId": "WEAPON_01", "quantity": 1 },
|
||||
{ "type": "complete_quest", "questId": "QUEST_002" }
|
||||
]
|
||||
},
|
||||
"END": {
|
||||
"type": "dialogue",
|
||||
"text": "好吧,注意安全。",
|
||||
"options": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"QUEST_003": {
|
||||
"id": "QUEST_003",
|
||||
"name": "意外救墨大夫",
|
||||
"description": "在地牢中发现被困的神秘人物,需要帮助他脱困。",
|
||||
"type": "explore",
|
||||
"target": {
|
||||
"entityId": "CAVE_01",
|
||||
"count": 1
|
||||
},
|
||||
"rewards": {
|
||||
"xp": 200,
|
||||
"gold": 100,
|
||||
"itemId": "POTION_03"
|
||||
},
|
||||
"triggerType": "SYSTEM",
|
||||
"triggerValue": "EXPLORE_CAVE",
|
||||
"dialogue": {
|
||||
"start": "小友,老夫被困在此地多日,若能助我脱困,必有重谢。",
|
||||
"progress": "需要找到方法帮助墨大夫。",
|
||||
"complete": "多谢小友相助,这是老夫的一点心意。"
|
||||
"npcId": "MODOCTOR",
|
||||
"nodes": {
|
||||
"START": {
|
||||
"type": "dialogue",
|
||||
"text": "小友,老夫被困在此地多日,若能助我脱困,必有重谢。",
|
||||
"options": [
|
||||
{ "text": "墨大夫,我来救你。", "next_node": "ACCEPT" },
|
||||
{ "text": "我还有事,改日再来。", "next_node": "END" }
|
||||
],
|
||||
"preConditions": [
|
||||
{ "type": "level", "value": 3 }
|
||||
]
|
||||
},
|
||||
"ACCEPT": {
|
||||
"type": "objective",
|
||||
"goal": { "type": "explore", "entityId": "CAVE_01", "count": 1 },
|
||||
"on_complete": "REWARD"
|
||||
},
|
||||
"REWARD": {
|
||||
"type": "dialogue",
|
||||
"text": "多谢小友相助!这是老夫的一点心意。",
|
||||
"options": [
|
||||
{ "text": "收下奖励", "next_node": null }
|
||||
],
|
||||
"actions": [
|
||||
{ "type": "give_xp", "amount": 200 },
|
||||
{ "type": "give_gold", "amount": 100 },
|
||||
{ "type": "give_item", "itemId": "POTION_03", "quantity": 1 },
|
||||
{ "type": "complete_quest", "questId": "QUEST_003" }
|
||||
]
|
||||
},
|
||||
"END": {
|
||||
"type": "dialogue",
|
||||
"text": "希望下次能帮上忙。",
|
||||
"options": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"QUEST_004": {
|
||||
"id": "QUEST_004",
|
||||
"name": "发现长春功",
|
||||
"description": "从墨大夫处学习到神秘功法,需要修炼至小成。",
|
||||
"type": "level",
|
||||
"target": {
|
||||
"entityId": "PLAYER",
|
||||
"count": 2
|
||||
},
|
||||
"rewards": {
|
||||
"xp": 300,
|
||||
"gold": 200,
|
||||
"itemId": "ZHUTIANPING"
|
||||
},
|
||||
"triggerType": "NPC",
|
||||
"triggerValue": "MODOCTOR",
|
||||
"dialogue": {
|
||||
"start": "小友,老夫观你骨骼精奇,可传授你长春功,但需谨慎修炼。",
|
||||
"progress": "修炼需要时间,循序渐进。",
|
||||
"complete": "恭喜小友,长春功已初见成效,这是掌天瓶,望善用之。"
|
||||
"npcId": "MODOCTOR",
|
||||
"nodes": {
|
||||
"START": {
|
||||
"type": "dialogue",
|
||||
"text": "小友,老夫观你骨骼精奇,可传授你长春功,但需谨慎修炼。",
|
||||
"options": [
|
||||
{ "text": "请墨大夫传授功法。", "next_node": "PATH_ACCEPT" },
|
||||
{ "text": "我现在还不想学。", "next_node": "PATH_DECLINE" }
|
||||
],
|
||||
"preConditions": [
|
||||
{ "type": "completed_quest", "questId": "QUEST_003" }
|
||||
]
|
||||
},
|
||||
"PATH_ACCEPT": {
|
||||
"type": "objective",
|
||||
"goal": { "type": "level", "value": 5 },
|
||||
"on_complete": "REWARD"
|
||||
},
|
||||
"PATH_DECLINE": {
|
||||
"type": "dialogue",
|
||||
"text": "功法修炼需要机缘,日后再谈。",
|
||||
"options": [],
|
||||
"actions": [
|
||||
{ "type": "complete_quest", "questId": "QUEST_004" }
|
||||
]
|
||||
},
|
||||
"REWARD": {
|
||||
"type": "dialogue",
|
||||
"text": "恭喜小友,长春功已初见成效,这是掌天瓶,望善用之。",
|
||||
"options": [
|
||||
{ "text": "收下奖励", "next_node": null }
|
||||
],
|
||||
"actions": [
|
||||
{ "type": "give_xp", "amount": 300 },
|
||||
{ "type": "give_gold", "amount": 200 },
|
||||
{ "type": "give_item", "itemId": "ZHUTIANPING", "quantity": 1 },
|
||||
{ "type": "complete_quest", "questId": "QUEST_004" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
177
save/player.json
177
save/player.json
|
|
@ -1,174 +1,23 @@
|
|||
{
|
||||
"player": {
|
||||
"name": "AutoSaveTest",
|
||||
"health": 160,
|
||||
"maxHealth": 160,
|
||||
"base_attack": 22,
|
||||
"base_defense": 13,
|
||||
"level": 5,
|
||||
"currentXp": 175,
|
||||
"gold": 241,
|
||||
"inventory": [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "\u5c0f\u578b\u6cbb\u7597\u836f\u6c34",
|
||||
"type": "potion",
|
||||
"description": "\u6062\u590d\u5c11\u91cf\u751f\u547d\u3002",
|
||||
"value": 10,
|
||||
"effects": {
|
||||
"heal": 20
|
||||
},
|
||||
"stats": [],
|
||||
"slot": null
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "\u5c0f\u578b\u6cbb\u7597\u836f\u6c34",
|
||||
"type": "potion",
|
||||
"description": "\u6062\u590d\u5c11\u91cf\u751f\u547d\u3002",
|
||||
"value": 10,
|
||||
"effects": {
|
||||
"heal": 20
|
||||
},
|
||||
"stats": [],
|
||||
"slot": null
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "\u5c0f\u578b\u6cbb\u7597\u836f\u6c34",
|
||||
"type": "potion",
|
||||
"description": "\u6062\u590d\u5c11\u91cf\u751f\u547d\u3002",
|
||||
"value": 10,
|
||||
"effects": {
|
||||
"heal": 20
|
||||
},
|
||||
"stats": [],
|
||||
"slot": null
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "\u5c0f\u578b\u6cbb\u7597\u836f\u6c34",
|
||||
"type": "potion",
|
||||
"description": "\u6062\u590d\u5c11\u91cf\u751f\u547d\u3002",
|
||||
"value": 10,
|
||||
"effects": {
|
||||
"heal": 20
|
||||
},
|
||||
"stats": [],
|
||||
"slot": null
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "\u5c0f\u578b\u6cbb\u7597\u836f\u6c34",
|
||||
"type": "potion",
|
||||
"description": "\u6062\u590d\u5c11\u91cf\u751f\u547d\u3002",
|
||||
"value": 10,
|
||||
"effects": {
|
||||
"heal": 20
|
||||
},
|
||||
"stats": [],
|
||||
"slot": null
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "\u5c0f\u578b\u6cbb\u7597\u836f\u6c34",
|
||||
"type": "potion",
|
||||
"description": "\u6062\u590d\u5c11\u91cf\u751f\u547d\u3002",
|
||||
"value": 10,
|
||||
"effects": {
|
||||
"heal": 20
|
||||
},
|
||||
"stats": [],
|
||||
"slot": null
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "\u5c0f\u578b\u6cbb\u7597\u836f\u6c34",
|
||||
"type": "potion",
|
||||
"description": "\u6062\u590d\u5c11\u91cf\u751f\u547d\u3002",
|
||||
"value": 10,
|
||||
"effects": {
|
||||
"heal": 20
|
||||
},
|
||||
"stats": [],
|
||||
"slot": null
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "\u5c0f\u578b\u6cbb\u7597\u836f\u6c34",
|
||||
"type": "potion",
|
||||
"description": "\u6062\u590d\u5c11\u91cf\u751f\u547d\u3002",
|
||||
"value": 10,
|
||||
"effects": {
|
||||
"heal": 20
|
||||
},
|
||||
"stats": [],
|
||||
"slot": null
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "\u5c0f\u578b\u6cbb\u7597\u836f\u6c34",
|
||||
"type": "potion",
|
||||
"description": "\u6062\u590d\u5c11\u91cf\u751f\u547d\u3002",
|
||||
"value": 10,
|
||||
"effects": {
|
||||
"heal": 20
|
||||
},
|
||||
"stats": [],
|
||||
"slot": null
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "\u5c0f\u578b\u6cbb\u7597\u836f\u6c34",
|
||||
"type": "potion",
|
||||
"description": "\u6062\u590d\u5c11\u91cf\u751f\u547d\u3002",
|
||||
"value": 10,
|
||||
"effects": {
|
||||
"heal": 20
|
||||
},
|
||||
"stats": [],
|
||||
"slot": null
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "\u7834\u65e7\u7684\u77ed\u5251",
|
||||
"type": "weapon",
|
||||
"description": "\u653b\u51fb\u529b\u5fae\u5f31\u3002",
|
||||
"value": 50,
|
||||
"effects": [],
|
||||
"stats": [],
|
||||
"slot": "weapon"
|
||||
}
|
||||
],
|
||||
"name": "hant",
|
||||
"health": 58,
|
||||
"maxHealth": 100,
|
||||
"base_attack": 15,
|
||||
"base_defense": 5,
|
||||
"level": 1,
|
||||
"currentXp": 40,
|
||||
"gold": 21,
|
||||
"inventory": [],
|
||||
"equipment": {
|
||||
"weapon": {
|
||||
"id": 2,
|
||||
"name": "\u7834\u65e7\u7684\u77ed\u5251",
|
||||
"type": "weapon",
|
||||
"description": "\u653b\u51fb\u529b\u5fae\u5f31\u3002",
|
||||
"value": 50,
|
||||
"effects": [],
|
||||
"stats": [],
|
||||
"slot": "weapon"
|
||||
},
|
||||
"weapon": null,
|
||||
"armor": null,
|
||||
"helmet": {
|
||||
"id": 4,
|
||||
"name": "\u5e03\u7532\u5934\u76d4",
|
||||
"type": "armor",
|
||||
"description": "\u63d0\u4f9b\u5c11\u91cf\u9632\u5fa1\u3002",
|
||||
"value": 30,
|
||||
"effects": [],
|
||||
"stats": [],
|
||||
"slot": "helmet"
|
||||
}
|
||||
"helmet": null
|
||||
},
|
||||
"activeQuests": [],
|
||||
"completedQuests": [
|
||||
"KILL_GOBLIN"
|
||||
]
|
||||
"completedQuests": []
|
||||
},
|
||||
"world": {
|
||||
"currentTileId": "FOREST_01"
|
||||
"currentTileId": "CAVE_01"
|
||||
}
|
||||
}
|
||||
|
|
@ -18,11 +18,11 @@ class MapRepository
|
|||
'id' => $id,
|
||||
'name' => $tileData['name'],
|
||||
'description' => $tileData['description'],
|
||||
'encounter_chance' => $tileData['encounter_chance'] ?? 0,
|
||||
'encounter_chance' => $tileData['encounterChance'] ?? 0,
|
||||
'connections' => $tileData['connections'] ?? [],
|
||||
'encounter_pool' => $tileData['encounter_pool'] ?? [],
|
||||
'npc_ids' => $tileData['npc_ids'] ?? [],
|
||||
'loot_ids' => $tileData['loot_ids'] ?? []
|
||||
'encounter_pool' => $tileData['encounterPool'] ?? [],
|
||||
'npc_ids' => $tileData['npcIds'] ?? [],
|
||||
'loot_ids' => $tileData['lootIds'] ?? []
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,14 +21,17 @@ class QuestRepository implements RepositoryInterface {
|
|||
*/
|
||||
private function buildNpcQuestIndex(): void {
|
||||
foreach ($this->data as $questId => $questData) {
|
||||
// 优先使用 triggerType = NPC 的 triggerValue
|
||||
if (isset($questData['triggerType']) && $questData['triggerType'] === 'NPC' && isset($questData['triggerValue'])) {
|
||||
// 优先使用新的 npcId 字段
|
||||
if (isset($questData['npcId'])) {
|
||||
$npcId = $questData['npcId'];
|
||||
$this->questsByNpc[$npcId][] = $questId;
|
||||
}
|
||||
// 兼容旧数据:使用 triggerType = NPC 的 triggerValue
|
||||
elseif (isset($questData['triggerType']) && $questData['triggerType'] === 'NPC' && isset($questData['triggerValue'])) {
|
||||
$npcId = $questData['triggerValue'];
|
||||
$this->questsByNpc[$npcId][] = $questId;
|
||||
}
|
||||
// 兼容旧数据:检查 target_npc_id (如果是 talk 类型或者仅仅是关联)
|
||||
// 但如果不作为触发条件,可能不应该在这里索引?
|
||||
// 保持兼容性:如果还没索引过,且有 target_npc_id
|
||||
// 兼容旧数据:检查 target_npc_id
|
||||
elseif (isset($questData['target_npc_id'])) {
|
||||
$targetId = $questData['target_npc_id'];
|
||||
// 避免重复?
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ class Player extends Character {
|
|||
'helmet' => null,
|
||||
// 可根据需要添加 'ring', 'amulet' 等
|
||||
];
|
||||
// ⭐ 新增:任务节点状态
|
||||
protected array $activeQuestNodes = [];
|
||||
|
||||
public function __construct(string $name, int $maxHealth, int $attack, int $defense,int $maxMana = 50) {
|
||||
// 调用父类 (Character) 的构造函数来初始化核心属性
|
||||
parent::__construct($name, $maxHealth, $attack, $defense,$maxMana);
|
||||
|
|
@ -127,6 +130,31 @@ class Player extends Character {
|
|||
return in_array($questId, $this->completedQuests);
|
||||
}
|
||||
|
||||
// ⭐ 新增方法:添加任务节点
|
||||
public function addActiveQuestNode(string $questId, string $nodeId): void {
|
||||
$this->activeQuestNodes[$questId] = $nodeId;
|
||||
}
|
||||
|
||||
// ⭐ 新增方法:获取任务节点
|
||||
public function getQuestNode(string $questId): ?string {
|
||||
return $this->activeQuestNodes[$questId] ?? null;
|
||||
}
|
||||
|
||||
// ⭐ 新增方法:更新任务节点
|
||||
public function updateQuestNode(string $questId, string $nodeId): void {
|
||||
$this->activeQuestNodes[$questId] = $nodeId;
|
||||
}
|
||||
|
||||
// ⭐ 新增方法:获取所有任务节点
|
||||
public function getActiveQuestNodes(): array {
|
||||
return $this->activeQuestNodes;
|
||||
}
|
||||
|
||||
// ⭐ 新增方法:设置任务节点(用于存档加载)
|
||||
public function setActiveQuestNodes(array $nodes): void {
|
||||
$this->activeQuestNodes = $nodes;
|
||||
}
|
||||
|
||||
// ⭐ 新增:玩家背包 (存储 Item 实例)
|
||||
protected array $inventory = [];
|
||||
|
||||
|
|
|
|||
39
src/Model/QuestNode.php
Normal file
39
src/Model/QuestNode.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
namespace Game\Model;
|
||||
|
||||
/**
|
||||
* 任务节点模型
|
||||
*/
|
||||
class QuestNode {
|
||||
public string $id;
|
||||
public string $type; // dialogue, objective, branch
|
||||
public string $text; // 用于dialogue类型
|
||||
public array $options; // 用于dialogue类型
|
||||
public array $goal; // 用于objective类型
|
||||
public ?string $nextNode; // 默认跳转节点
|
||||
public ?string $onComplete; // 完成后跳转节点
|
||||
public array $preConditions; // 前置条件
|
||||
public array $actions; // 完成后执行的动作
|
||||
|
||||
public function __construct(
|
||||
string $id,
|
||||
string $type,
|
||||
string $text = '',
|
||||
array $options = [],
|
||||
array $goal = [],
|
||||
?string $nextNode = null,
|
||||
?string $onComplete = null,
|
||||
array $preConditions = [],
|
||||
array $actions = []
|
||||
) {
|
||||
$this->id = $id;
|
||||
$this->type = $type;
|
||||
$this->text = $text;
|
||||
$this->options = $options;
|
||||
$this->goal = $goal;
|
||||
$this->nextNode = $nextNode;
|
||||
$this->onComplete = $onComplete;
|
||||
$this->preConditions = $preConditions;
|
||||
$this->actions = $actions;
|
||||
}
|
||||
}
|
||||
75
src/System/ActionExecutor.php
Normal file
75
src/System/ActionExecutor.php
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
namespace Game\System;
|
||||
|
||||
use Game\Event\EventDispatcher;
|
||||
use Game\Model\Player;
|
||||
|
||||
/**
|
||||
* 动作执行器 - 统一处理 JSON 中的指令
|
||||
*/
|
||||
class ActionExecutor {
|
||||
private EventDispatcher $dispatcher;
|
||||
|
||||
public function __construct(EventDispatcher $dispatcher) {
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
public function executeActions(Player $player, array $actions): void {
|
||||
foreach ($actions as $action) {
|
||||
$this->executeAction($player, $action);
|
||||
}
|
||||
}
|
||||
|
||||
private function executeAction(Player $player, array $action): void {
|
||||
$type = $action['type'] ?? '';
|
||||
|
||||
switch ($type) {
|
||||
case 'give_item':
|
||||
$this->giveItem($player, $action);
|
||||
break;
|
||||
case 'give_xp':
|
||||
$this->giveXp($player, $action);
|
||||
break;
|
||||
case 'give_gold':
|
||||
$this->giveGold($player, $action);
|
||||
break;
|
||||
case 'set_variable':
|
||||
$this->setVariable($player, $action);
|
||||
break;
|
||||
case 'complete_quest':
|
||||
$this->completeQuest($player, $action);
|
||||
break;
|
||||
default:
|
||||
// 未知动作类型,跳过
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function giveItem(Player $player, array $action): void {
|
||||
$itemId = $action['itemId'] ?? '';
|
||||
$quantity = $action['quantity'] ?? 1;
|
||||
// 这里可以调用LootService给予物品
|
||||
}
|
||||
|
||||
private function giveXp(Player $player, array $action): void {
|
||||
$amount = $action['amount'] ?? 0;
|
||||
$player->gainXp($amount);
|
||||
}
|
||||
|
||||
private function giveGold(Player $player, array $action): void {
|
||||
$amount = $action['amount'] ?? 0;
|
||||
$player->gainGold($amount);
|
||||
}
|
||||
|
||||
private function setVariable(Player $player, array $action): void {
|
||||
// 设置玩家变量
|
||||
$name = $action['name'] ?? '';
|
||||
$value = $action['value'] ?? null;
|
||||
// 这里可以扩展玩家变量系统
|
||||
}
|
||||
|
||||
private function completeQuest(Player $player, array $action): void {
|
||||
$questId = $action['questId'] ?? '';
|
||||
$player->markQuestCompleted($questId);
|
||||
}
|
||||
}
|
||||
44
src/System/ConditionEngine.php
Normal file
44
src/System/ConditionEngine.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
namespace Game\System;
|
||||
|
||||
use Game\Model\Player;
|
||||
|
||||
/**
|
||||
* 条件引擎 - 统一判定玩家是否满足某个配置条件
|
||||
*/
|
||||
class ConditionEngine {
|
||||
|
||||
public function checkConditions(Player $player, array $conditions): bool {
|
||||
foreach ($conditions as $condition) {
|
||||
if (!$this->checkSingleCondition($player, $condition)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkSingleCondition(Player $player, array $condition): bool {
|
||||
$type = $condition['type'] ?? '';
|
||||
|
||||
switch ($type) {
|
||||
case 'level':
|
||||
return $player->getLevel() >= ($condition['value'] ?? 0);
|
||||
case 'completed_quest':
|
||||
return $player->isQuestCompleted($condition['questId'] ?? '');
|
||||
case 'variable':
|
||||
// 检查玩家变量
|
||||
$varName = $condition['name'] ?? '';
|
||||
$expectedValue = $condition['value'] ?? null;
|
||||
// 这里可以扩展玩家变量检查逻辑
|
||||
return true; // 简化实现
|
||||
case 'item':
|
||||
// 检查玩家是否拥有特定物品
|
||||
$itemId = $condition['itemId'] ?? '';
|
||||
$minCount = $condition['count'] ?? 1;
|
||||
// 这里可以扩展物品检查逻辑
|
||||
return true; // 简化实现
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ class DialogueService implements EventListenerInterface {
|
|||
|
||||
private array $currentDialogueTree = [];
|
||||
private string $currentNodeId = '';
|
||||
private ?string $currentQuestId = null; // ⭐ 添加任务ID
|
||||
|
||||
public function __construct(EventDispatcher $dispatcher, StateManager $stateManager, InputInterface $input, OutputInterface $output, QuestionHelper $helper) {
|
||||
$this->dispatcher = $dispatcher;
|
||||
|
|
@ -37,11 +38,13 @@ class DialogueService implements EventListenerInterface {
|
|||
switch ($event->getType()) {
|
||||
case 'StartDialogueEvent':
|
||||
$dialogue = $event->getPayload()['dialogue'];
|
||||
$this->startDialogue($dialogue);
|
||||
$questId = $event->getPayload()['questId'] ?? null; // ⭐ 获取任务ID
|
||||
$this->startDialogue($dialogue, $questId); // ⭐ 传递任务ID
|
||||
break;
|
||||
case 'DialogueChoice': // ⭐ Listen for choice event
|
||||
$choice = $event->getPayload()['choice'];
|
||||
$this->handleChoice($choice);
|
||||
$choiceIndex = $event->getPayload()['choiceIndex']; // ⭐ 修正键名
|
||||
$questId = $event->getPayload()['questId'] ?? null; // ⭐ 获取任务ID
|
||||
$this->handleChoice($choiceIndex, $questId); // ⭐ 传递任务ID
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -49,9 +52,10 @@ class DialogueService implements EventListenerInterface {
|
|||
/**
|
||||
* 开始一段对话 (非阻塞,初始化状态)
|
||||
*/
|
||||
public function startDialogue(array $dialogueTree): void {
|
||||
public function startDialogue(array $dialogueTree, ?string $questId = null): void {
|
||||
$this->currentDialogueTree = $dialogueTree;
|
||||
$this->currentNodeId = 'root';
|
||||
$this->currentNodeId = 'root'; // ⭐ 使用'root'作为根节点
|
||||
$this->currentQuestId = $questId; // ⭐ 保存任务ID
|
||||
|
||||
// 切换游戏模式
|
||||
$this->stateManager->setMode(StateManager::MODE_DIALOGUE);
|
||||
|
|
@ -89,7 +93,7 @@ class DialogueService implements EventListenerInterface {
|
|||
/**
|
||||
* 处理玩家选择
|
||||
*/
|
||||
private function handleChoice(string $input): void {
|
||||
private function handleChoice(string $input, ?string $questId = null): void { // ⭐ 添加任务ID参数
|
||||
if (!isset($this->currentDialogueTree[$this->currentNodeId])) {
|
||||
$this->endDialogue();
|
||||
return;
|
||||
|
|
@ -131,11 +135,20 @@ class DialogueService implements EventListenerInterface {
|
|||
$this->currentNodeId = $selectedOption['next'];
|
||||
$this->displayCurrentNode();
|
||||
}
|
||||
|
||||
// 3. 如果是任务对话,分发选择事件到InteractionSystem
|
||||
if ($questId) {
|
||||
$this->dispatcher->dispatch(new Event('DialogueChoice', [
|
||||
'choiceIndex' => (int)$input,
|
||||
'questId' => $questId
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
private function endDialogue(): void {
|
||||
$this->currentDialogueTree = [];
|
||||
$this->currentNodeId = '';
|
||||
$this->currentQuestId = null; // ⭐ 重置任务ID
|
||||
$this->output->writeln("\n[对话结束]");
|
||||
|
||||
// 恢复地图模式 (或者之前的模式,稍微简化处理)
|
||||
|
|
|
|||
71
src/System/Evaluator.php
Normal file
71
src/System/Evaluator.php
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
namespace Game\System;
|
||||
|
||||
use Game\Model\Player;
|
||||
use Game\Model\QuestNode;
|
||||
|
||||
/**
|
||||
* 目标评估器 - 使用策略模式处理不同类型的任务目标
|
||||
*/
|
||||
class Evaluator {
|
||||
|
||||
public function evaluateObjective(Player $player, array $goal): bool {
|
||||
$type = $goal['type'] ?? '';
|
||||
|
||||
switch ($type) {
|
||||
case 'kill':
|
||||
return $this->evaluateKillGoal($player, $goal);
|
||||
case 'collect':
|
||||
return $this->evaluateCollectGoal($player, $goal);
|
||||
case 'level':
|
||||
return $this->evaluateLevelGoal($player, $goal);
|
||||
case 'explore':
|
||||
return $this->evaluateExploreGoal($player, $goal);
|
||||
case 'talk':
|
||||
return $this->evaluateTalkGoal($player, $goal);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function evaluateKillGoal(Player $player, array $goal): bool {
|
||||
// 这里需要跟踪玩家击杀记录,简化实现
|
||||
// 在实际实现中,需要在BattleService中记录击杀
|
||||
$requiredEntityId = $goal['entityId'] ?? '';
|
||||
$requiredCount = $goal['count'] ?? 1;
|
||||
|
||||
// 这里需要检查玩家的击杀记录,暂时返回false
|
||||
return false;
|
||||
}
|
||||
|
||||
private function evaluateCollectGoal(Player $player, array $goal): bool {
|
||||
// 检查玩家背包中是否拥有足够数量的物品
|
||||
$requiredItemId = $goal['entityId'] ?? '';
|
||||
$requiredCount = $goal['count'] ?? 1;
|
||||
|
||||
// 计算玩家背包中指定物品的数量
|
||||
$itemCount = 0;
|
||||
foreach ($player->getInventory() as $item) {
|
||||
if ($item->id === $requiredItemId) {
|
||||
$itemCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return $itemCount >= $requiredCount;
|
||||
}
|
||||
|
||||
private function evaluateLevelGoal(Player $player, array $goal): bool {
|
||||
$requiredLevel = $goal['value'] ?? 1;
|
||||
return $player->getLevel() >= $requiredLevel;
|
||||
}
|
||||
|
||||
private function evaluateExploreGoal(Player $player, array $goal): bool {
|
||||
// 这里需要跟踪玩家探索记录,简化实现
|
||||
return false;
|
||||
}
|
||||
|
||||
private function evaluateTalkGoal(Player $player, array $goal): bool {
|
||||
// 这里需要跟踪玩家对话记录,简化实现
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -59,6 +59,12 @@ class InteractionSystem implements EventListenerInterface {
|
|||
$npcId = $event->getPayload()['npcId'];
|
||||
$this->startInteraction($npcId);
|
||||
break;
|
||||
// ⭐ 处理StartDialogueEvent
|
||||
case 'StartDialogueEvent':
|
||||
$dialogue = $event->getPayload()['dialogue'];
|
||||
$questId = $event->getPayload()['questId'] ?? null;
|
||||
$this->dialogueService->startDialogue($dialogue, $questId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +83,7 @@ class InteractionSystem implements EventListenerInterface {
|
|||
}
|
||||
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||||
'message' => "👤 你走近了 <fg=cyan;options=bold>{$npc->getName()}</>。"
|
||||
'message' => "👤 你走近了 <fg=cyan;options=bold>{$npc->getName()}</>"
|
||||
]));
|
||||
|
||||
// ⭐ 直接尝试触发任务对话
|
||||
|
|
@ -108,7 +114,7 @@ class InteractionSystem implements EventListenerInterface {
|
|||
$questData = $this->questRepository->find($questId);
|
||||
if ($questData && !empty($questData['dialogue'])) {
|
||||
// ⭐ 使用新版对话系统
|
||||
$this->dialogueService->startDialogue($questData['dialogue']);
|
||||
$this->dialogueService->startDialogue($questData['dialogue'], $questId);
|
||||
$foundQuest = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -120,7 +126,7 @@ class InteractionSystem implements EventListenerInterface {
|
|||
// 如果没有新任务,显示 NPC 默认闲聊
|
||||
$defaultMsg = is_array($npc->dialogue) ? ($npc->dialogue['greeting'] ?? '...') : '...';
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||||
'message' => "<fg=cyan>{$npc->getName()}</><fg=white>{$defaultMsg}</>"
|
||||
'message' => "<fg=cyan>{$npc->getName()}</>:<fg=white>{$defaultMsg}</>"
|
||||
]));
|
||||
|
||||
// ⭐ 检查NPC是否有商店
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ class LootService implements EventListenerInterface {
|
|||
$data['health'],
|
||||
$data['attack'],
|
||||
$data['defense'],
|
||||
$data['xp_reward'],
|
||||
$data['loot_table'] ?? [] // 传递 loot_table
|
||||
$data['xp'],
|
||||
$data['loot'] ?? [] // 传递 loot_table
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -150,20 +150,14 @@ class LootService implements EventListenerInterface {
|
|||
/**
|
||||
* 处理探索时发现的宝箱/固定物品 (保持不变)
|
||||
*/
|
||||
private function handleLootFound(int $lootId): void {
|
||||
// ...
|
||||
if ($lootId === 5) {
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||||
'message' => "🗝️ 你打开了宝箱!"
|
||||
]));
|
||||
$this->giveItemToPlayer(2);
|
||||
}
|
||||
private function handleLootFound($lootId): void {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 核心逻辑:创建物品实例并添加到玩家背包 (保持不变)
|
||||
*/
|
||||
private function giveItemToPlayer(int $itemId): void {
|
||||
private function giveItemToPlayer($itemId): void {
|
||||
// ...
|
||||
$item = $this->itemRepository->createItem($itemId);
|
||||
|
||||
|
|
|
|||
|
|
@ -120,7 +120,6 @@ class MapSystem implements EventListenerInterface {
|
|||
*/
|
||||
private function checkRandomEncounter(float $chance): bool {
|
||||
$currentTile = $this->stateManager->getCurrentTile();
|
||||
dd($currentTile);
|
||||
if (!$currentTile->encounterPool || rand(1, 100) / 100 > $chance) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -142,7 +141,7 @@ class MapSystem implements EventListenerInterface {
|
|||
$currentWeight = 0;
|
||||
foreach ($pool as $item) {
|
||||
$currentWeight += $item['weight'];
|
||||
if ($randValue <= $currentWeight) return $item['enemyId'];
|
||||
if ($randValue <= $currentWeight) return $item['id'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,247 +5,290 @@ use Game\Database\QuestRepository;
|
|||
use Game\Event\Event;
|
||||
use Game\Event\EventListenerInterface;
|
||||
use Game\Event\EventDispatcher;
|
||||
use Game\Model\Quest;
|
||||
use Game\Model\QuestNode;
|
||||
use Game\Model\Player;
|
||||
|
||||
/**
|
||||
* QuestService: 管理任务的接受、追踪和奖励发放。
|
||||
* 重构后的任务服务 - 基于事件驱动和有向图节点
|
||||
*/
|
||||
class QuestService implements EventListenerInterface {
|
||||
|
||||
private EventDispatcher $dispatcher;
|
||||
private StateManager $stateManager;
|
||||
private QuestRepository $questRepository; // ⭐ 新增属性
|
||||
private QuestRepository $questRepository;
|
||||
private ConditionEngine $conditionEngine;
|
||||
private Evaluator $evaluator;
|
||||
private ActionExecutor $actionExecutor;
|
||||
|
||||
// ⭐ 修正构造函数:注入 QuestRepository
|
||||
private $questData = [];
|
||||
|
||||
public function __construct(EventDispatcher $dispatcher, StateManager $stateManager, QuestRepository $questRepository) {
|
||||
public function __construct(
|
||||
EventDispatcher $dispatcher,
|
||||
StateManager $stateManager,
|
||||
QuestRepository $questRepository
|
||||
) {
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->stateManager = $stateManager;
|
||||
$this->questRepository = $questRepository; // ⭐ 赋值
|
||||
$this->questRepository = $questRepository;
|
||||
$this->conditionEngine = new ConditionEngine();
|
||||
$this->evaluator = new Evaluator();
|
||||
$this->actionExecutor = new ActionExecutor($dispatcher);
|
||||
}
|
||||
|
||||
|
||||
public function handleEvent(Event $event): void {
|
||||
switch ($event->getType()) {
|
||||
case 'GameStartEvent':
|
||||
$this->initializeQuests(); // 游戏开始时检查是否有初始任务
|
||||
$this->initializeQuests();
|
||||
break;
|
||||
case 'BattleEndEvent': // 响应战斗结束,检查击杀目标
|
||||
$payload = $event->getPayload();
|
||||
if (isset($payload['enemyId'])) {
|
||||
$this->checkKillQuests($payload['enemyId']);
|
||||
}
|
||||
case 'QuestAcceptRequest':
|
||||
$this->acceptQuest($event->getPayload()['questId']);
|
||||
break;
|
||||
case 'MapMoveEvent': // ⭐ 响应移动,检查地点触发任务
|
||||
$this->checkSystemTriggers('MAP_MOVE', $event->getPayload()['targetId'] ?? ''); // 假设 MapMoveEvent 携带 targetId (MapTile ID)
|
||||
case 'QuestProgressEvent':
|
||||
$this->updateQuestProgress($event->getPayload());
|
||||
break;
|
||||
case 'LevelUpEvent': // ⭐ 响应升级
|
||||
$this->checkSystemTriggers('LEVEL_UP', (string)$event->getPayload()['newLevel']);
|
||||
case 'DialogueChoice':
|
||||
$this->handleDialogueChoice($event->getPayload());
|
||||
break;
|
||||
case 'QuestAcceptRequest': // ⭐ 响应对话中的接受任务请求
|
||||
$this->startQuest($event->getPayload()['questId']);
|
||||
case 'BattleEndEvent':
|
||||
$this->checkKillQuests($event->getPayload());
|
||||
break;
|
||||
case 'QuestTurnInConfirm': // ⭐ 交付任务确认
|
||||
$this->turnInQuest($event->getPayload()['questId']);
|
||||
case 'LevelUpEvent':
|
||||
$this->checkLevelQuests($event->getPayload());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 3. 检查击杀类任务进度
|
||||
* 接受任务
|
||||
*/
|
||||
private function checkKillQuests(string $killedEnemyId): void {
|
||||
private function acceptQuest(string $questId): void {
|
||||
$player = $this->stateManager->getPlayer();
|
||||
$activeQuests = $player->getActiveQuests();
|
||||
|
||||
foreach ($activeQuests as $questId => $progress) {
|
||||
$questConfig = $this->questRepository->find($questId);
|
||||
if ($questConfig['type'] === 'kill' && $questConfig['target']['entityId'] == $killedEnemyId) {
|
||||
// 更新玩家任务进度
|
||||
$player->updateQuestProgress($questId, 1);
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||||
'message' => "任务进度更新:[{$questConfig['name']}] {$player->getActiveQuests()[$questId]->getCurrentCount()}/{$questConfig['target']['count']}"
|
||||
]));
|
||||
|
||||
// 如果任务完成,触发 QuestCompletedEventRequest
|
||||
if ($player->getActiveQuests()[$questId]->isCompleted()) {
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||||
'message' => "任务 [{$questConfig['name']}] <fg=green;options=bold>已完成!</>请回去找NPC提交。"
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 4. 检查任务是否可以提交并给予奖励
|
||||
*/
|
||||
private function checkQuestCompletion(string $questId): void {
|
||||
$player = $this->stateManager->getPlayer();
|
||||
$progress = $player->getActiveQuests()[$questId];
|
||||
$questConfig = $this->questData[$questId];
|
||||
|
||||
if ($progress['isCompleted']) {
|
||||
// 发放奖励
|
||||
$rewards = $questConfig['rewards'];
|
||||
|
||||
// 经验值奖励 (交给 CharacterService)
|
||||
if (isset($rewards['xp'])) {
|
||||
$this->dispatcher->dispatch(new Event('XpGainedEvent', ['xp' => $rewards['xp']]));
|
||||
}
|
||||
|
||||
// 物品奖励 (交给 LootService)
|
||||
if (isset($rewards['itemId'])) {
|
||||
$this->dispatcher->dispatch(new Event('LootFoundEvent', ['lootId' => $rewards['itemId']]));
|
||||
}
|
||||
|
||||
// 标记玩家任务完成
|
||||
$player->markQuestCompleted($questId);
|
||||
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||||
'message' => "🎉 <fg=yellow>任务提交成功!</> 获得了奖励。"
|
||||
]));
|
||||
} else {
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||||
'message' => "(村长)任务 [{$questConfig['name']}] 还没有完成。目标:{$progress['currentCount']}/{$progress['targetCount']}"
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟:根据 NPC ID 获取对应的任务 ID
|
||||
*/
|
||||
private function getQuestIdForNpc(string $npcId): ?string {
|
||||
return $this->questRepository->find($npcId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始一个任务
|
||||
*/
|
||||
public function startQuest(string $questId): void {
|
||||
$player = $this->stateManager->getPlayer();
|
||||
|
||||
// ⭐ 使用 Repository 获取数据
|
||||
$questData = $this->questRepository->find($questId);
|
||||
|
||||
if (!$questData) {
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => "❌ 错误:任务ID '{$questId}' 不存在。"]));
|
||||
return;
|
||||
}
|
||||
// 实例化 Quest 模型 (假设 Quest 模型已适配 JSON 键名)
|
||||
$quest = new Quest(
|
||||
$questData['id'],
|
||||
$questData['name'],
|
||||
$questData['description'],
|
||||
$questData['type'],
|
||||
$questData['target'],
|
||||
$questData['rewards'],
|
||||
$questData['next_quest_id'] ?? null
|
||||
// ... 其它必要的 Quest 构造参数
|
||||
);
|
||||
|
||||
$player->addActiveQuest($quest);
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||||
'message' => "📝 接受任务: <fg=yellow>{$quest->getName()}</> - {$quest->getDescription()}"
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 游戏开始时尝试加载初始任务 (或通过 NPC 交互触发)
|
||||
*/
|
||||
public function initializeQuests(): void {
|
||||
$player = $this->stateManager->getPlayer();
|
||||
|
||||
// if (empty($player->getActiveQuests()) && empty($player->getCompletedQuests())) {
|
||||
// if ($startingQuestId) {
|
||||
// $this->startQuest($startingQuestId);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* ⭐ 交付任务
|
||||
*/
|
||||
private function turnInQuest(string $questId): void {
|
||||
$player = $this->stateManager->getPlayer();
|
||||
$activeQuests = $player->getActiveQuests();
|
||||
|
||||
if (!isset($activeQuests[$questId])) {
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => '任务不存在或已完成。']));
|
||||
return;
|
||||
// 检查前置条件
|
||||
$startNode = $questData['nodes']['START'] ?? null;
|
||||
if ($startNode && isset($startNode['preConditions'])) {
|
||||
if (!$this->conditionEngine->checkConditions($player, $startNode['preConditions'])) {
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => "前置条件不满足,无法接受此任务。"]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$quest = $activeQuests[$questId];
|
||||
if (!$quest->isCompleted()) {
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => '任务还未完成,无法交付。']));
|
||||
// 初始化任务状态
|
||||
$player->addActiveQuestNode($questId, 'START');
|
||||
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||||
'message' => "📝 接受任务: <fg=yellow>{$questData['name']}</> - {$questData['description']}"
|
||||
]));
|
||||
|
||||
// 显示起始对话
|
||||
if (isset($startNode['type']) && $startNode['type'] === 'dialogue') {
|
||||
$this->dispatcher->dispatch(new Event('StartDialogueEvent', [
|
||||
'dialogue' => [
|
||||
'root' => [
|
||||
'text' => $startNode['text'],
|
||||
'options' => $startNode['options']
|
||||
]
|
||||
],
|
||||
'questId' => $questId // 添加任务ID以便对话系统处理选择
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务进度
|
||||
*/
|
||||
private function updateQuestProgress(array $payload): void {
|
||||
$questId = $payload['questId'];
|
||||
$player = $this->stateManager->getPlayer();
|
||||
$currentNode = $player->getQuestNode($questId);
|
||||
|
||||
if (!$currentNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
$questData = $this->questRepository->find($questId);
|
||||
if (!$questData) {
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => '任务数据出错。']));
|
||||
return;
|
||||
}
|
||||
|
||||
// 发放奖励
|
||||
$rewards = $questData['rewards'] ?? [];
|
||||
|
||||
// 经验值奖励
|
||||
if (isset($rewards['xp'])) {
|
||||
$player->gainXp($rewards['xp']);
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||||
'message' => " ✅ 获得 <fg=yellow>{$rewards['xp']}</> 经验值"
|
||||
]));
|
||||
$nodeData = $questData['nodes'][$currentNode] ?? null;
|
||||
if (!$nodeData || $nodeData['type'] !== 'objective') {
|
||||
return;
|
||||
}
|
||||
|
||||
// 金币奖励
|
||||
if (isset($rewards['gold'])) {
|
||||
$player->gainGold($rewards['gold']);
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||||
'message' => " ✅ 获得 <fg=yellow>{$rewards['gold']}</> 金币"
|
||||
]));
|
||||
// 检查目标是否完成
|
||||
if ($this->evaluator->evaluateObjective($player, $nodeData['goal'])) {
|
||||
$this->completeObjective($questId, $nodeData);
|
||||
}
|
||||
|
||||
// 物品奖励
|
||||
if (isset($rewards['itemId'])) {
|
||||
$this->dispatcher->dispatch(new Event('LootFoundEvent', ['lootId' => $rewards['itemId']]));
|
||||
}
|
||||
|
||||
// 标记任务完成
|
||||
$player->markQuestCompleted($questId);
|
||||
|
||||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||||
'message' => "\n🎉 <fg=green;options=bold>任务已交付!</> 感谢你的帮助!"
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 7. 检查系统触发的任务
|
||||
* 完成目标节点
|
||||
*/
|
||||
private function checkSystemTriggers(string $triggerType, string $triggerValue): void {
|
||||
private function completeObjective(string $questId, array $nodeData): void {
|
||||
$player = $this->stateManager->getPlayer();
|
||||
$allQuests = $this->questRepository->findAll(); // 假设 we can get all quests
|
||||
|
||||
foreach ($allQuests as $questData) {
|
||||
$questId = $questData['id'];
|
||||
// 执行完成动作
|
||||
if (isset($nodeData['actions'])) {
|
||||
$this->actionExecutor->executeActions($player, $nodeData['actions']);
|
||||
}
|
||||
|
||||
// 检查触发条件
|
||||
if (isset($questData['triggerType']) && $questData['triggerType'] === 'SYSTEM' &&
|
||||
isset($questData['triggerValue']) && $questData['triggerValue'] === $triggerValue) {
|
||||
// 跳转到下一个节点
|
||||
$nextNode = $nodeData['onComplete'] ?? $nodeData['nextNode'] ?? null;
|
||||
if ($nextNode) {
|
||||
$player->updateQuestNode($questId, $nextNode);
|
||||
|
||||
// 检查是否已完成或已接取
|
||||
if (!$player->isQuestCompleted($questId) && !isset($player->getActiveQuests()[$questId])) {
|
||||
// 触发对话
|
||||
if (!empty($questData['dialogue'])) {
|
||||
$this->dispatcher->dispatch(new Event('StartDialogueEvent', ['dialogue' => $questData['dialogue']]));
|
||||
} else {
|
||||
// 无对话直接接取? 或者弹窗提示
|
||||
// 简单起见,无对话也强制开始(可能会有默认提示)
|
||||
$this->startQuest($questId);
|
||||
}
|
||||
// 处理下一个节点
|
||||
$questData = $this->questRepository->find($questId);
|
||||
$nextNodeData = $questData['nodes'][$nextNode] ?? null;
|
||||
|
||||
if ($nextNodeData) {
|
||||
$this->processNode($questId, $nextNode, $nextNodeData);
|
||||
}
|
||||
} else {
|
||||
// 没有下一个节点,任务完成
|
||||
$player->markQuestCompleted($questId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理对话选择
|
||||
*/
|
||||
private function handleDialogueChoice(array $payload): void {
|
||||
$questId = $payload['questId'] ?? null;
|
||||
$choiceIndex = $payload['choiceIndex'];
|
||||
$player = $this->stateManager->getPlayer();
|
||||
|
||||
if (!$questId) {
|
||||
return;
|
||||
}
|
||||
|
||||
$currentNode = $player->getQuestNode($questId);
|
||||
if (!$currentNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
$questData = $this->questRepository->find($questId);
|
||||
if (!$questData) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nodeData = $questData['nodes'][$currentNode] ?? null;
|
||||
if (!$nodeData || $nodeData['type'] !== 'dialogue') {
|
||||
return;
|
||||
}
|
||||
|
||||
$selectedOption = $nodeData['options'][$choiceIndex] ?? null;
|
||||
if (!$selectedOption) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 跳转到下一个节点
|
||||
$nextNode = $selectedOption['next_node'] ?? null;
|
||||
if ($nextNode) {
|
||||
$player->updateQuestNode($questId, $nextNode);
|
||||
|
||||
// 处理下一个节点
|
||||
$nextNodeData = $questData['nodes'][$nextNode] ?? null;
|
||||
if ($nextNodeData) {
|
||||
$this->processNode($questId, $nextNode, $nextNodeData);
|
||||
}
|
||||
} else {
|
||||
// 没有下一个节点,任务完成
|
||||
$player->markQuestCompleted($questId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理节点
|
||||
*/
|
||||
private function processNode(string $questId, string $nodeId, array $nodeData): void {
|
||||
switch ($nodeData['type']) {
|
||||
case 'dialogue':
|
||||
$this->dispatcher->dispatch(new Event('StartDialogueEvent', [
|
||||
'dialogue' => [
|
||||
'root' => [
|
||||
'text' => $nodeData['text'],
|
||||
'options' => $nodeData['options']
|
||||
]
|
||||
],
|
||||
'questId' => $questId // 添加任务ID以便对话系统处理选择
|
||||
]));
|
||||
break;
|
||||
case 'objective':
|
||||
// 目标节点,等待相应事件触发
|
||||
break;
|
||||
case 'branch':
|
||||
// 分支节点,可能需要特殊处理
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查击杀类任务
|
||||
*/
|
||||
private function checkKillQuests(array $payload): void {
|
||||
$killedEnemyId = $payload['enemyId'] ?? '';
|
||||
$player = $this->stateManager->getPlayer();
|
||||
|
||||
// 遍历所有进行中的任务
|
||||
foreach ($player->getActiveQuestNodes() as $questId => $currentNode) {
|
||||
$questData = $this->questRepository->find($questId);
|
||||
if (!$questData) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$nodeData = $questData['nodes'][$currentNode] ?? null;
|
||||
if (!$nodeData || $nodeData['type'] !== 'objective') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否为击杀目标
|
||||
if (isset($nodeData['goal']['type']) && $nodeData['goal']['type'] === 'kill' &&
|
||||
isset($nodeData['goal']['entityId']) && $nodeData['goal']['entityId'] === $killedEnemyId) {
|
||||
|
||||
// 更新任务进度
|
||||
$this->dispatcher->dispatch(new Event('QuestProgressEvent', [
|
||||
'questId' => $questId
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查等级类任务
|
||||
*/
|
||||
private function checkLevelQuests(array $payload): void {
|
||||
$player = $this->stateManager->getPlayer();
|
||||
|
||||
// 遍历所有进行中的任务
|
||||
foreach ($player->getActiveQuestNodes() as $questId => $currentNode) {
|
||||
$questData = $this->questRepository->find($questId);
|
||||
if (!$questData) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$nodeData = $questData['nodes'][$currentNode] ?? null;
|
||||
if (!$nodeData || $nodeData['type'] !== 'objective') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否为等级目标
|
||||
if (isset($nodeData['goal']['type']) && $nodeData['goal']['type'] === 'level') {
|
||||
if ($this->evaluator->evaluateObjective($player, $nodeData['goal'])) {
|
||||
$this->completeObjective($questId, $nodeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化任务
|
||||
*/
|
||||
private function initializeQuests(): void {
|
||||
// 初始化逻辑
|
||||
}
|
||||
}
|
||||
|
|
@ -107,7 +107,8 @@ class StateManager {
|
|||
|
||||
public function getCurrentTile(): MapTile {
|
||||
if (!isset($this->currentTile)) {
|
||||
throw new \RuntimeException("Current MapTile not set in StateManager.");
|
||||
$this->currentTile = $this->mapRepository->createTile('TOWN_01');
|
||||
// throw new \RuntimeException("Current MapTile not set in StateManager.");
|
||||
}
|
||||
return $this->currentTile;
|
||||
}
|
||||
|
|
|
|||
77
tests/test_dialogue_tree.php
Normal file
77
tests/test_dialogue_tree.php
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Game\Core\ServiceContainer;
|
||||
use Game\Event\Event;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
|
||||
$input = new ArrayInput([]);
|
||||
$output = new BufferedOutput();
|
||||
$helperSet = new HelperSet(['question' => new QuestionHelper()]);
|
||||
|
||||
$container = new ServiceContainer($input, $output, $helperSet);
|
||||
$dispatcher = $container->registerServices();
|
||||
$stateManager = $container->getStateManager();
|
||||
|
||||
// 创建测试玩家
|
||||
$player = new \Game\Model\Player("HanLi", 100, 10, 5);
|
||||
$player->gainGold(50); // 给玩家一些初始金币
|
||||
$stateManager->setPlayer($player);
|
||||
$stateManager->setCurrentTileId('TOWN_01');
|
||||
|
||||
echo "=== 《凡人修仙传》任务对话树测试 ===\n\n";
|
||||
|
||||
// 获取任务对话数据
|
||||
$questRepo = $container->getQuestRepository();
|
||||
$questData = $questRepo->find('QUEST_001');
|
||||
|
||||
echo "1. 任务对话数据测试:\n";
|
||||
echo " - 任务: {$questData['name']}\n";
|
||||
echo " - 对话结构类型: " . (isset($questData['dialogue']['root']) ? '对话树格式' : '简单格式') . "\n";
|
||||
|
||||
if (isset($questData['dialogue']['root'])) {
|
||||
echo " - 根节点文本: " . substr($questData['dialogue']['root']['text'], 0, 30) . "...\n";
|
||||
echo " - 选项数量: " . count($questData['dialogue']['root']['options']) . "\n";
|
||||
|
||||
foreach ($questData['dialogue']['root']['options'] as $index => $option) {
|
||||
echo " - 选项 {$index}: {$option['text']} -> {$option['next']}\n";
|
||||
}
|
||||
}
|
||||
|
||||
// 检查DialogueService
|
||||
echo "\n2. 对话服务测试:\n";
|
||||
$dialogueService = new \Game\System\DialogueService($dispatcher, $stateManager, $input, $output, $helperSet);
|
||||
echo " - DialogueService 创建成功\n";
|
||||
|
||||
// 检查对话树是否能正确启动
|
||||
echo "\n3. 对话树启动测试:\n";
|
||||
try {
|
||||
$dialogueService->startDialogue($questData['dialogue']);
|
||||
echo " - 对话树启动成功\n";
|
||||
echo " - 当前节点: {$dialogueService->currentNodeId}\n";
|
||||
} catch (Exception $e) {
|
||||
echo " - 对话树启动失败: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
// 测试对话处理
|
||||
echo "\n4. 对话处理测试:\n";
|
||||
$initialActiveQuests = count($player->getActiveQuests());
|
||||
echo " - 初始进行中任务数: {$initialActiveQuests}\n";
|
||||
|
||||
// 模拟选择接受任务
|
||||
$dispatcher->dispatch(new Event('StartDialogueEvent', ['dialogue' => $questData['dialogue']]));
|
||||
|
||||
// 这里我们直接测试任务接受事件
|
||||
$dispatcher->dispatch(new Event('QuestAcceptRequest', ['questId' => 'QUEST_001']));
|
||||
|
||||
$finalActiveQuests = count($player->getActiveQuests());
|
||||
echo " - 最终进行中任务数: {$finalActiveQuests}\n";
|
||||
echo " - 任务接受成功: " . ($finalActiveQuests > $initialActiveQuests ? '是' : '否') . "\n";
|
||||
|
||||
echo "\n输出消息:\n";
|
||||
echo $output->fetch() . "\n";
|
||||
|
||||
echo "=== 对话树测试完成 ===\n";
|
||||
|
|
@ -26,8 +26,8 @@ echo "=== 《凡人修仙传》第一阶段地图显示测试 ===\n\n";
|
|||
|
||||
// 测试地图显示
|
||||
$mapRepo = $container->getMapRepository();
|
||||
$stateManager->setCurrentTileId('VILLAGE_01');
|
||||
$dispatcher->dispatch(new Event('MapExploreRequest'));
|
||||
$stateManager->setCurrentTileId('CAVE_01');
|
||||
$dispatcher->dispatch(new Event('StartInteractionEvent', ['npcId' => 'MODOCTOR']));
|
||||
dd(2);
|
||||
// 手动调用UIService显示地图信息
|
||||
$uiService = new \Game\System\UIService($output, $stateManager, $container->getQuestRepository());
|
||||
|
|
|
|||
129
tests/test_new_quest_core.php
Normal file
129
tests/test_new_quest_core.php
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Game\Core\ServiceContainer;
|
||||
use Game\Event\Event;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
|
||||
$input = new ArrayInput([]);
|
||||
$output = new BufferedOutput();
|
||||
$helperSet = new HelperSet(['question' => new QuestionHelper()]);
|
||||
|
||||
$container = new ServiceContainer($input, $output, $helperSet);
|
||||
$dispatcher = $container->registerServices();
|
||||
$stateManager = $container->getStateManager();
|
||||
|
||||
// 创建测试玩家
|
||||
$player = new \Game\Model\Player("HanLi", 100, 10, 5);
|
||||
$player->setLevel(5); // 设置等级为5,满足所有任务条件
|
||||
$player->gainGold(50); // 给玩家一些初始金币
|
||||
$stateManager->setPlayer($player);
|
||||
$stateManager->setCurrentTileId('TOWN_01');
|
||||
|
||||
echo "=== 《凡人修仙传》新任务系统核心功能测试 ===\n\n";
|
||||
|
||||
// 测试1: 检查任务数据结构
|
||||
echo "1. 任务数据结构测试:\n";
|
||||
$questRepo = $container->getQuestRepository();
|
||||
$questData = $questRepo->find('QUEST_001');
|
||||
|
||||
if (isset($questData['nodes'])) {
|
||||
echo " - 任务节点结构: ✅\n";
|
||||
echo " - 节点数量: " . count($questData['nodes']) . "\n";
|
||||
foreach ($questData['nodes'] as $nodeId => $node) {
|
||||
echo " - 节点 {$nodeId}: 类型={$node['type']}\n";
|
||||
}
|
||||
} else {
|
||||
echo " - 任务节点结构: ❌\n";
|
||||
}
|
||||
|
||||
// 测试2: 检查ConditionEngine
|
||||
echo "\n2. 条件引擎测试:\n";
|
||||
$conditionEngine = new \Game\System\ConditionEngine();
|
||||
$conditions = $questData['nodes']['START']['preConditions'] ?? [];
|
||||
$playerSatisfies = $conditionEngine->checkConditions($player, $conditions);
|
||||
echo " - 玩家满足前置条件: " . ($playerSatisfies ? '✅' : '❌') . "\n";
|
||||
|
||||
// 测试3: 检查Evaluator
|
||||
echo "\n3. 目标评估器测试:\n";
|
||||
$evaluator = new \Game\System\Evaluator();
|
||||
$objectiveNode = $questData['nodes']['ACCEPT'];
|
||||
$objectiveResult = $evaluator->evaluateObjective($player, $objectiveNode['goal']);
|
||||
echo " - 目标评估结果: " . ($objectiveResult ? '完成' : '未完成') . "\n";
|
||||
|
||||
// 测试4: 检查ActionExecutor
|
||||
echo "\n4. 动作执行器测试:\n";
|
||||
$actionExecutor = new \Game\System\ActionExecutor($dispatcher);
|
||||
$initialGold = $player->getGold();
|
||||
$initialXp = $player->getCurrentXp();
|
||||
|
||||
$rewardNode = $questData['nodes']['REWARD'];
|
||||
if (isset($rewardNode['actions'])) {
|
||||
$actionExecutor->executeActions($player, $rewardNode['actions']);
|
||||
$finalGold = $player->getGold();
|
||||
$finalXp = $player->getCurrentXp();
|
||||
|
||||
echo " - 金币变化: {$initialGold} -> {$finalGold}\n";
|
||||
echo " - 经验变化: {$initialXp} -> {$finalXp}\n";
|
||||
echo " - 动作执行: ✅\n";
|
||||
}
|
||||
|
||||
// 测试5: 任务接受流程
|
||||
echo "\n5. 任务接受流程测试:\n";
|
||||
$initialActiveNodes = count($player->getActiveQuestNodes());
|
||||
echo " - 初始活跃节点: {$initialActiveNodes}\n";
|
||||
|
||||
$dispatcher->dispatch(new Event('QuestAcceptRequest', ['questId' => 'QUEST_001']));
|
||||
$currentNodes = $player->getActiveQuestNodes();
|
||||
echo " - 接受后活跃节点: " . count($currentNodes) . "\n";
|
||||
if (isset($currentNodes['QUEST_001'])) {
|
||||
echo " - QUEST_001当前节点: {$currentNodes['QUEST_001']}\n";
|
||||
}
|
||||
|
||||
// 测试6: 任务完成流程
|
||||
echo "\n6. 任务完成流程测试:\n";
|
||||
// 将任务节点更新到目标节点
|
||||
$player->updateQuestNode('QUEST_001', 'ACCEPT');
|
||||
|
||||
// 模拟目标完成(这里我们直接跳过实际的收集过程)
|
||||
$dispatcher->dispatch(new Event('QuestProgressEvent', [
|
||||
'questId' => 'QUEST_001'
|
||||
]));
|
||||
|
||||
$afterProgressNodes = $player->getActiveQuestNodes();
|
||||
echo " - 完成目标后节点: " . ($afterProgressNodes['QUEST_001'] ?? '未找到') . "\n";
|
||||
|
||||
// 检查是否已完成
|
||||
$completedQuests = $player->getCompletedQuests();
|
||||
echo " - 已完成任务数: " . count($completedQuests) . "\n";
|
||||
if (in_array('QUEST_001', $completedQuests)) {
|
||||
echo " - QUEST_001已完成: ✅\n";
|
||||
} else {
|
||||
echo " - QUEST_001已完成: ❌\n";
|
||||
}
|
||||
|
||||
// 测试7: 对话选择功能
|
||||
echo "\n7. 对话选择功能测试:\n";
|
||||
// 重新接受一个任务
|
||||
$dispatcher->dispatch(new Event('QuestAcceptRequest', ['questId' => 'QUEST_002']));
|
||||
$currentNodes = $player->getActiveQuestNodes();
|
||||
if (isset($currentNodes['QUEST_002'])) {
|
||||
echo " - QUEST_002接受成功,当前节点: {$currentNodes['QUEST_002']}\n";
|
||||
|
||||
// 模拟对话选择
|
||||
$dispatcher->dispatch(new Event('DialogueChoice', [
|
||||
'choiceIndex' => 1, // 选择"太危险了,我不去。"
|
||||
'questId' => 'QUEST_002'
|
||||
]));
|
||||
|
||||
$afterChoiceNodes = $player->getActiveQuestNodes();
|
||||
echo " - 选择后节点: " . ($afterChoiceNodes['QUEST_002'] ?? '未找到') . "\n";
|
||||
}
|
||||
|
||||
echo "\n输出消息:\n";
|
||||
echo $output->fetch() . "\n";
|
||||
|
||||
echo "=== 核心功能测试完成 ===\n";
|
||||
106
tests/test_new_quest_system.php
Normal file
106
tests/test_new_quest_system.php
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Game\Core\ServiceContainer;
|
||||
use Game\Event\Event;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
|
||||
$input = new ArrayInput([]);
|
||||
$output = new BufferedOutput();
|
||||
$helperSet = new HelperSet(['question' => new QuestionHelper()]);
|
||||
|
||||
$container = new ServiceContainer($input, $output, $helperSet);
|
||||
$dispatcher = $container->registerServices();
|
||||
$stateManager = $container->getStateManager();
|
||||
|
||||
// 创建测试玩家
|
||||
$player = new \Game\Model\Player("HanLi", 100, 10, 5);
|
||||
$player->setLevel(5); // 设置等级为5,满足所有任务条件
|
||||
$player->gainGold(50); // 给玩家一些初始金币
|
||||
$stateManager->setPlayer($player);
|
||||
$stateManager->setCurrentTileId('TOWN_01');
|
||||
|
||||
echo "=== 《凡人修仙传》新任务系统测试 ===\n\n";
|
||||
|
||||
// 测试1: 检查任务数据结构
|
||||
echo "1. 任务数据结构测试:\n";
|
||||
$questRepo = $container->getQuestRepository();
|
||||
$questData = $questRepo->find('QUEST_001');
|
||||
|
||||
if (isset($questData['nodes'])) {
|
||||
echo " - 任务节点结构: ✅\n";
|
||||
echo " - 节点数量: " . count($questData['nodes']) . "\n";
|
||||
foreach ($questData['nodes'] as $nodeId => $node) {
|
||||
echo " - 节点 {$nodeId}: 类型={$node['type']}\n";
|
||||
}
|
||||
} else {
|
||||
echo " - 任务节点结构: ❌\n";
|
||||
}
|
||||
|
||||
// 测试2: 检查ConditionEngine
|
||||
echo "\n2. 条件引擎测试:\n";
|
||||
$conditionEngine = new \Game\System\ConditionEngine();
|
||||
$conditions = $questData['nodes']['START']['preConditions'] ?? [];
|
||||
$playerSatisfies = $conditionEngine->checkConditions($player, $conditions);
|
||||
echo " - 玩家满足前置条件: " . ($playerSatisfies ? '✅' : '❌') . "\n";
|
||||
|
||||
// 测试3: 检查Evaluator
|
||||
echo "\n3. 目标评估器测试:\n";
|
||||
$evaluator = new \Game\System\Evaluator();
|
||||
$objectiveNode = $questData['nodes']['ACCEPT'];
|
||||
$objectiveResult = $evaluator->evaluateObjective($player, $objectiveNode['goal']);
|
||||
echo " - 目标评估结果: " . ($objectiveResult ? '完成' : '未完成') . "\n";
|
||||
|
||||
// 测试4: 检查ActionExecutor
|
||||
echo "\n4. 动作执行器测试:\n";
|
||||
$actionExecutor = new \Game\System\ActionExecutor($dispatcher);
|
||||
$initialGold = $player->getGold();
|
||||
$initialXp = $player->getCurrentXp();
|
||||
|
||||
$rewardNode = $questData['nodes']['REWARD'];
|
||||
if (isset($rewardNode['actions'])) {
|
||||
$actionExecutor->executeActions($player, $rewardNode['actions']);
|
||||
$finalGold = $player->getGold();
|
||||
$finalXp = $player->getCurrentXp();
|
||||
|
||||
echo " - 金币变化: {$initialGold} -> {$finalGold}\n";
|
||||
echo " - 经验变化: {$initialXp} -> {$finalXp}\n";
|
||||
echo " - 动作执行: ✅\n";
|
||||
}
|
||||
|
||||
// 测试5: 任务服务交互
|
||||
echo "\n5. 任务服务交互测试:\n";
|
||||
|
||||
// 测试接受任务
|
||||
$initialActiveNodes = count($player->getActiveQuestNodes());
|
||||
$dispatcher->dispatch(new Event('QuestAcceptRequest', ['questId' => 'QUEST_001']));
|
||||
$finalActiveNodes = count($player->getActiveQuestNodes());
|
||||
|
||||
echo " - 任务节点状态: {$initialActiveNodes} -> {$finalActiveNodes}\n";
|
||||
if ($finalActiveNodes > $initialActiveNodes) {
|
||||
echo " - 任务接受: ✅\n";
|
||||
} else {
|
||||
echo " - 任务接受: ❌\n";
|
||||
}
|
||||
|
||||
// 测试6: 检查完成状态
|
||||
echo "\n6. 任务完成状态测试:\n";
|
||||
$currentNode = $player->getQuestNode('QUEST_001');
|
||||
echo " - 当前节点: {$currentNode}\n";
|
||||
|
||||
// 模拟完成目标
|
||||
$dispatcher->dispatch(new Event('QuestProgressEvent', [
|
||||
'questId' => 'QUEST_001'
|
||||
]));
|
||||
|
||||
// 检查是否跳转到奖励节点
|
||||
$updatedNode = $player->getQuestNode('QUEST_001');
|
||||
echo " - 更新后节点: {$updatedNode}\n";
|
||||
|
||||
echo "\n输出消息:\n";
|
||||
echo $output->fetch() . "\n";
|
||||
|
||||
echo "=== 新任务系统测试完成 ===\n";
|
||||
93
tests/test_new_quest_system_full.php
Normal file
93
tests/test_new_quest_system_full.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Game\Core\ServiceContainer;
|
||||
use Game\Event\Event;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
|
||||
$input = new ArrayInput([]);
|
||||
$output = new BufferedOutput();
|
||||
$helperSet = new HelperSet(['question' => new QuestionHelper()]);
|
||||
|
||||
$container = new ServiceContainer($input, $output, $helperSet);
|
||||
$dispatcher = $container->registerServices();
|
||||
$stateManager = $container->getStateManager();
|
||||
|
||||
// 创建测试玩家
|
||||
$player = new \Game\Model\Player("HanLi", 100, 10, 5);
|
||||
$player->setLevel(5); // 设置等级为5,满足所有任务条件
|
||||
$player->gainGold(50); // 给玩家一些初始金币
|
||||
$stateManager->setPlayer($player);
|
||||
$stateManager->setCurrentTileId('TOWN_01');
|
||||
|
||||
echo "=== 《凡人修仙传》新任务系统完整流程测试 ===\n\n";
|
||||
|
||||
// 检查初始状态
|
||||
echo "1. 初始状态:\n";
|
||||
echo " - 活跃任务节点数: " . count($player->getActiveQuestNodes()) . "\n";
|
||||
echo " - 金币: " . $player->getGold() . "\n";
|
||||
echo " - 经验: " . $player->getCurrentXp() . "\n";
|
||||
|
||||
// 接受任务
|
||||
echo "\n2. 接受任务:\n";
|
||||
$dispatcher->dispatch(new Event('QuestAcceptRequest', ['questId' => 'QUEST_001']));
|
||||
|
||||
$initialNodes = $player->getActiveQuestNodes();
|
||||
echo " - 接受后活跃节点: " . count($initialNodes) . "\n";
|
||||
if (isset($initialNodes['QUEST_001'])) {
|
||||
echo " - 任务QUEST_001节点: " . $initialNodes['QUEST_001'] . "\n";
|
||||
}
|
||||
|
||||
// 模拟完成目标
|
||||
echo "\n3. 完成任务目标:\n";
|
||||
// 更新任务节点到目标节点
|
||||
$player->updateQuestNode('QUEST_001', 'ACCEPT');
|
||||
|
||||
// 模拟目标完成事件
|
||||
$dispatcher->dispatch(new Event('QuestProgressEvent', [
|
||||
'questId' => 'QUEST_001'
|
||||
]));
|
||||
|
||||
$afterProgressNodes = $player->getActiveQuestNodes();
|
||||
echo " - 完成目标后节点: " . ($afterProgressNodes['QUEST_001'] ?? '未找到') . "\n";
|
||||
|
||||
// 检查奖励是否发放
|
||||
echo "\n4. 奖励发放检查:\n";
|
||||
echo " - 金币: " . $player->getGold() . "\n";
|
||||
echo " - 经验: " . $player->getCurrentXp() . "\n";
|
||||
|
||||
// 检查任务是否完成
|
||||
echo "\n5. 任务完成检查:\n";
|
||||
$completedQuests = $player->getCompletedQuests();
|
||||
echo " - 已完成任务数: " . count($completedQuests) . "\n";
|
||||
if (in_array('QUEST_001', $completedQuests)) {
|
||||
echo " - QUEST_001 已完成: ✅\n";
|
||||
} else {
|
||||
echo " - QUEST_001 已完成: ❌\n";
|
||||
}
|
||||
|
||||
// 测试分支任务
|
||||
echo "\n6. 分支任务测试 (QUEST_004):\n";
|
||||
$dispatcher->dispatch(new Event('QuestAcceptRequest', ['questId' => 'QUEST_004']));
|
||||
|
||||
$quest4Nodes = $player->getActiveQuestNodes();
|
||||
if (isset($quest4Nodes['QUEST_004'])) {
|
||||
echo " - 任务QUEST_004节点: " . $quest4Nodes['QUEST_004'] . "\n";
|
||||
|
||||
// 模拟选择接受路径
|
||||
$dispatcher->dispatch(new Event('DialogueChoice', [
|
||||
'choiceIndex' => 0, // 选择"请墨大夫传授功法"
|
||||
'questId' => 'QUEST_004'
|
||||
]));
|
||||
|
||||
$afterChoiceNodes = $player->getActiveQuestNodes();
|
||||
echo " - 选择后节点: " . ($afterChoiceNodes['QUEST_004'] ?? '未找到') . "\n";
|
||||
}
|
||||
|
||||
echo "\n输出消息:\n";
|
||||
echo $output->fetch() . "\n";
|
||||
|
||||
echo "=== 完整流程测试完成 ===\n";
|
||||
93
tests/test_npc_quest_assoc.php
Normal file
93
tests/test_npc_quest_assoc.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Game\Core\ServiceContainer;
|
||||
use Game\Event\Event;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
|
||||
$input = new ArrayInput([]);
|
||||
$output = new BufferedOutput();
|
||||
$helperSet = new HelperSet(['question' => new QuestionHelper()]);
|
||||
|
||||
$container = new ServiceContainer($input, $output, $helperSet);
|
||||
$dispatcher = $container->registerServices();
|
||||
$stateManager = $container->getStateManager();
|
||||
|
||||
// 创建测试玩家
|
||||
$player = new \Game\Model\Player("HanLi", 100, 10, 5);
|
||||
$player->setLevel(5); // 设置等级为5,满足所有任务条件
|
||||
$player->gainGold(50); // 给玩家一些初始金币
|
||||
$stateManager->setPlayer($player);
|
||||
$stateManager->setCurrentTileId('TOWN_01');
|
||||
|
||||
echo "=== NPC与任务关联测试 ===\n\n";
|
||||
|
||||
// 测试1: 检查任务与NPC的关联
|
||||
echo "1. 检查任务与NPC关联:\n";
|
||||
$questRepo = $container->getQuestRepository();
|
||||
$npcRepo = $container->getNpcRepository();
|
||||
|
||||
// 获取VILLAGER_1相关的任务
|
||||
$villagerQuests = $questRepo->getQuestsByNpc('VILLAGER_1');
|
||||
echo " - VILLAGER_1关联的任务: " . implode(', ', $villagerQuests) . "\n";
|
||||
|
||||
// 获取MODOCTOR相关的任务
|
||||
$doctorQuests = $questRepo->getQuestsByNpc('MODOCTOR');
|
||||
echo " - MODOCTOR关联的任务: " . implode(', ', $doctorQuests) . "\n";
|
||||
|
||||
// 检查任务数据中的NPC ID
|
||||
$quest1 = $questRepo->find('QUEST_001');
|
||||
if (isset($quest1['npcId'])) {
|
||||
echo " - QUEST_001的NPC: {$quest1['npcId']}\n";
|
||||
} else {
|
||||
echo " - QUEST_001没有NPC关联\n";
|
||||
}
|
||||
|
||||
$quest3 = $questRepo->find('QUEST_003');
|
||||
if (isset($quest3['npcId'])) {
|
||||
echo " - QUEST_003的NPC: {$quest3['npcId']}\n";
|
||||
} else {
|
||||
echo " - QUEST_003没有NPC关联\n";
|
||||
}
|
||||
|
||||
// 测试2: 检查NPC是否存在
|
||||
echo "\n2. 检查NPC数据:\n";
|
||||
$villager = $npcRepo->createNPC('VILLAGER_1');
|
||||
if ($villager) {
|
||||
echo " - VILLAGER_1存在: {$villager->getName()}\n";
|
||||
} else {
|
||||
echo " - VILLAGER_1不存在\n";
|
||||
}
|
||||
|
||||
$doctor = $npcRepo->createNPC('MODOCTOR');
|
||||
if ($doctor) {
|
||||
echo " - MODOCTOR存在: {$doctor->getName()}\n";
|
||||
} else {
|
||||
echo " - MODOCTOR不存在\n";
|
||||
}
|
||||
|
||||
// 测试3: 模拟NPC交互
|
||||
echo "\n3. 模拟NPC交互测试:\n";
|
||||
$initialActiveQuests = count($player->getActiveQuests());
|
||||
echo " - 初始活跃任务数: {$initialActiveQuests}\n";
|
||||
|
||||
// 模拟与VILLAGER_1的交互
|
||||
$dispatcher->dispatch(new Event('StartInteractionEvent', ['npcId' => 'VILLAGER_1']));
|
||||
|
||||
$afterInteractionQuests = count($player->getActiveQuests());
|
||||
echo " - 与VILLAGER_1交互后任务数: {$afterInteractionQuests}\n";
|
||||
|
||||
// 检查是否接受了任务
|
||||
$activeQuestNodes = $player->getActiveQuestNodes();
|
||||
echo " - 活跃任务节点: " . count($activeQuestNodes) . "\n";
|
||||
foreach ($activeQuestNodes as $questId => $nodeId) {
|
||||
echo " - 任务{$questId}节点: {$nodeId}\n";
|
||||
}
|
||||
|
||||
echo "\n输出消息:\n";
|
||||
echo $output->fetch() . "\n";
|
||||
|
||||
echo "=== 关联测试完成 ===\n";
|
||||
87
tests/test_quest_interaction.php
Normal file
87
tests/test_quest_interaction.php
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Game\Core\ServiceContainer;
|
||||
use Game\Event\Event;
|
||||
use Game\Model\Quest;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
|
||||
$input = new ArrayInput([]);
|
||||
$output = new BufferedOutput();
|
||||
$helperSet = new HelperSet(['question' => new QuestionHelper()]);
|
||||
|
||||
$container = new ServiceContainer($input, $output, $helperSet);
|
||||
$dispatcher = $container->registerServices();
|
||||
$stateManager = $container->getStateManager();
|
||||
|
||||
// 创建测试玩家
|
||||
$player = new \Game\Model\Player("HanLi", 100, 10, 5);
|
||||
$player->gainGold(50); // 给玩家一些初始金币
|
||||
$stateManager->setPlayer($player);
|
||||
$stateManager->setCurrentTileId('TOWN_01');
|
||||
|
||||
echo "=== 《凡人修仙传》任务系统交互测试 ===\n\n";
|
||||
|
||||
// 测试任务接受
|
||||
echo "1. 任务接受测试:\n";
|
||||
$questService = new \Game\System\QuestService($dispatcher, $stateManager, $container->getQuestRepository());
|
||||
|
||||
// 手动添加一个任务到玩家
|
||||
$questData = $container->getQuestRepository()->find('QUEST_001');
|
||||
$quest = new Quest(
|
||||
$questData['id'],
|
||||
$questData['name'],
|
||||
$questData['description'],
|
||||
$questData['type'],
|
||||
$questData['target'],
|
||||
$questData['rewards'],
|
||||
$questData['triggerType'] ?? null,
|
||||
$questData['triggerValue'] ?? null,
|
||||
$questData['dialogue'] ?? []
|
||||
);
|
||||
|
||||
$player->addActiveQuest($quest);
|
||||
echo " - 添加任务: {$quest->getTitle()}\n";
|
||||
echo " - 任务类型: {$quest->getType()}\n";
|
||||
echo " - 目标: " . ($quest->getTarget()['count'] ?? '未知') . "\n\n";
|
||||
|
||||
// 测试任务进度更新
|
||||
echo "2. 任务进度更新测试:\n";
|
||||
$player->updateQuestProgress('QUEST_001', 5);
|
||||
$activeQuests = $player->getActiveQuests();
|
||||
$currentQuest = $activeQuests['QUEST_001'];
|
||||
echo " - 当前进度: {$currentQuest->getCurrentCount()}/{$quest->getTarget()['count']}\n\n";
|
||||
|
||||
// 测试任务完成
|
||||
echo "3. 任务完成测试:\n";
|
||||
// 完成任务
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$player->updateQuestProgress('QUEST_001', 1);
|
||||
}
|
||||
$currentQuest = $player->getActiveQuests()['QUEST_001'];
|
||||
echo " - 任务是否完成: " . ($currentQuest->isCompleted() ? '是' : '否') . "\n";
|
||||
echo " - 最终进度: {$currentQuest->getCurrentCount()}/{$quest->getTarget()['count']}\n\n";
|
||||
|
||||
// 测试任务交付
|
||||
echo "4. 任务交付测试:\n";
|
||||
$initialGold = $player->getGold();
|
||||
$initialXp = $player->getCurrentXp();
|
||||
echo " - 交付前金币: {$initialGold}, 经验: {$initialXp}\n";
|
||||
|
||||
// 触发任务交付
|
||||
$dispatcher->dispatch(new Event('QuestTurnInConfirm', ['questId' => 'QUEST_001']));
|
||||
|
||||
$finalGold = $player->getGold();
|
||||
$finalXp = $player->getCurrentXp();
|
||||
echo " - 交付后金币: {$finalGold}, 经验: {$finalXp}\n";
|
||||
echo " - 金币增加: " . ($finalGold - $initialGold) . "\n";
|
||||
echo " - 经验增加: " . ($finalXp - $initialXp) . "\n";
|
||||
echo " - 是否在完成列表: " . (in_array('QUEST_001', $player->getCompletedQuests()) ? '是' : '否') . "\n\n";
|
||||
|
||||
echo "输出消息:\n";
|
||||
echo $output->fetch() . "\n";
|
||||
|
||||
echo "=== 任务系统交互测试完成 ===\n";
|
||||
Loading…
Reference in New Issue
Block a user