Gemma4-Quest-System.md
· 5.7 KiB · Markdown
原始檔案
To implement an **AI-Driven NPC Quest System**, we need to combine almost every feature of our Universal DSL: **AI generation** for the quest, **Fork-Join** for multiple objectives, **Event Listeners** for player actions, and **Saga/Undo** logic for quest failure.
In this scenario, the NPC doesn't have a hard-coded quest. Instead, the AI looks at the game world, generates a dynamic quest, and monitors the player's progress in real-time.
### The Scenario: "The Dynamic Village Savior"
**Goal:** An NPC (The Elder) identifies a problem in the world, creates a multi-part quest for the player, and updates the world state upon completion.
```python
# ==============================================================================
# PROJECT: AI-Driven Dynamic Quest System
# NPC: Village Elder
# ==============================================================================
# --- GLOBAL STATE ---
$quest_id = null
$quest_status = "idle"
$world_state = { "forest_danger": "high", "village_food": "low" }
$player_progress = {}
# --- MODULE: QUEST GENERATION (The AI Brain) ---
Module QuestGenerator {
# AI analyzes world state and generates a unique quest objective
AI.Generate [model: "gpt-4-game-engine"] {
context: $world_state,
prompt: "Generate a quest to fix the village's main problem.
Provide two distinct objectives: one 'Fetch' and one 'Slay'."
} -> Quest.Validate
# Ensure the AI didn't generate something impossible (Constraint Check)
Quest.Validate /check /verify {objectives} ? [
is_feasible -> Quest.Finalize,
impossible -> AI.Generate # Loop back to regenerate
]
Quest.Finalize /save {quest_data} -> $quest_id = {qid}
return {quest_active: true}
}
# --- MODULE: QUEST EXECUTION (The Parallel Game Loop) ---
Module QuestExecution {
# Fork: The player must complete multiple objectives in any order
Quest.Start F-> [Obj.FetchItem, Obj.SlayMonster, Obj.DialogueNPC]
# Objective 1: Fetch Item (Event-Driven)
# Wait for the player to pick up the specific AI-generated item
Obj.FetchItem /wait ON (Player.InventoryUpdate) {
filter: { item == $quest_data.fetch_item }
} -> Obj.FetchComplete
# Objective 2: Slay Monster (Event-Driven)
# Wait for the combat system to report the death of the target
Obj.SlayMonster /wait ON (Combat.EntityDeath) {
filter: { target == $quest_data.target_monster }
} -> Obj.SlayComplete
# Objective 3: Dialogue (Sequential)
Obj.DialogueNPC /interact {npc: "ForestSpirit"} /save {info} -> Obj.DialogueComplete
# Join: Synchronize all objectives
[Obj.FetchComplete, Obj.SlayComplete, Obj.DialogueComplete] J-> Quest.VerifyCompletion
}
# --- MODULE: VERIFICATION & WORLD UPDATE ---
Module QuestResolution {
# AI evaluates the "Quality" of completion (e.g., did the player kill too many innocents?)
AI.Evaluate {
input: $player_progress,
criteria: "Evaluate if the quest was completed honorably"
} -> Eval.Result
Eval.Result ? [
honorable -> World.UpdateSuccess,
dishonorable -> World.UpdateMixed
]
# Server-side Transaction: Update world state and reward player
World.UpdateSuccess {
Lock ($village_id) {
EXT:Server.UpdateWorldState { "forest_danger": "low" }
EXT:Server.GrantReward { player_id: $player_id, amount: 1000, item: "HeroCape" }
}
}
# If something fails during the server update, rollback the quest state
World.UpdateSuccess /undo {
$quest_status = "error";
Notify.Player("World update failed. Please contact support.");
}
}
# --- MAIN FLOW ---
# 1. Trigger: Player interacts with the Elder
Welcome.Interaction /start /on (Player.Interact, "Elder") -> QuestGenerator
# 2. Transition to Execution
QuestGenerator.Complete -> QuestExecution
# 3. Transition to Resolution
QuestExecution.Complete -> QuestResolution
# 4. The "Failure" Path (Saga/Recovery)
# If the player dies during the quest, the quest doesn't vanish; it pauses.
ON (Player.Death) -> Quest.Pause {
$quest_status = "paused"
Saga.Compensate {
# Move player back to graveyard, but keep quest items in inventory
EXT:Server.RelocatePlayer { location: "Graveyard" }
}
-> WAIT (Player.Respawn) -> QuestExecution
}
# 5. The "Boredom" Timer (Tick)
# If the player ignores the quest for too long, the NPC gets grumpy
Tick [1hour] {
condition: $quest_status == "active",
logic: NPC.Elder.SetMood("Grumpy")
}
```
### Why this is powerful (The "Universal" breakdown):
1. **Non-Deterministic Logic (AI):** Instead of `if (quest_id == 1)`, the system uses `AI.Generate` and `AI.Evaluate`. The quest changes every time based on `$world_state`.
2. **Asynchronous Event-Driven Architecture:** The `Obj.FetchItem` and `Obj.SlayMonster` nodes don't "poll" the database. They use `ON (Event)` listeners, meaning the workflow sleeps until the game engine pushes a specific event.
3. **Strict Synchronization (Fork-Join):** The `J->` (Join) ensures the player cannot turn in the quest by just killing the monster; they *must* also find the item and talk to the spirit.
4. **Infrastructure Safety:** The use of `Lock ($village_id)` prevents a "race condition" where two players completing the same quest at the same time could double-reward themselves or glitch the world state.
5. **Resilience (Saga/Recovery):** The `ON (Player.Death)` handler acts as a recovery mechanism, ensuring the state is preserved across a "fail state" (Death) and resuming the workflow exactly where it left off.
6. **Real-time Ambience (Tick):** The `Tick [1hour]` allows the NPC to react to the passage of time, adding a layer of realism that standard linear workflows cannot handle.
To implement an AI-Driven NPC Quest System, we need to combine almost every feature of our Universal DSL: AI generation for the quest, Fork-Join for multiple objectives, Event Listeners for player actions, and Saga/Undo logic for quest failure.
In this scenario, the NPC doesn't have a hard-coded quest. Instead, the AI looks at the game world, generates a dynamic quest, and monitors the player's progress in real-time.
The Scenario: "The Dynamic Village Savior"
Goal: An NPC (The Elder) identifies a problem in the world, creates a multi-part quest for the player, and updates the world state upon completion.
# ==============================================================================
# PROJECT: AI-Driven Dynamic Quest System
# NPC: Village Elder
# ==============================================================================
# --- GLOBAL STATE ---
$quest_id = null
$quest_status = "idle"
$world_state = { "forest_danger": "high", "village_food": "low" }
$player_progress = {}
# --- MODULE: QUEST GENERATION (The AI Brain) ---
Module QuestGenerator {
# AI analyzes world state and generates a unique quest objective
AI.Generate [model: "gpt-4-game-engine"] {
context: $world_state,
prompt: "Generate a quest to fix the village's main problem.
Provide two distinct objectives: one 'Fetch' and one 'Slay'."
} -> Quest.Validate
# Ensure the AI didn't generate something impossible (Constraint Check)
Quest.Validate /check /verify {objectives} ? [
is_feasible -> Quest.Finalize,
impossible -> AI.Generate # Loop back to regenerate
]
Quest.Finalize /save {quest_data} -> $quest_id = {qid}
return {quest_active: true}
}
# --- MODULE: QUEST EXECUTION (The Parallel Game Loop) ---
Module QuestExecution {
# Fork: The player must complete multiple objectives in any order
Quest.Start F-> [Obj.FetchItem, Obj.SlayMonster, Obj.DialogueNPC]
# Objective 1: Fetch Item (Event-Driven)
# Wait for the player to pick up the specific AI-generated item
Obj.FetchItem /wait ON (Player.InventoryUpdate) {
filter: { item == $quest_data.fetch_item }
} -> Obj.FetchComplete
# Objective 2: Slay Monster (Event-Driven)
# Wait for the combat system to report the death of the target
Obj.SlayMonster /wait ON (Combat.EntityDeath) {
filter: { target == $quest_data.target_monster }
} -> Obj.SlayComplete
# Objective 3: Dialogue (Sequential)
Obj.DialogueNPC /interact {npc: "ForestSpirit"} /save {info} -> Obj.DialogueComplete
# Join: Synchronize all objectives
[Obj.FetchComplete, Obj.SlayComplete, Obj.DialogueComplete] J-> Quest.VerifyCompletion
}
# --- MODULE: VERIFICATION & WORLD UPDATE ---
Module QuestResolution {
# AI evaluates the "Quality" of completion (e.g., did the player kill too many innocents?)
AI.Evaluate {
input: $player_progress,
criteria: "Evaluate if the quest was completed honorably"
} -> Eval.Result
Eval.Result ? [
honorable -> World.UpdateSuccess,
dishonorable -> World.UpdateMixed
]
# Server-side Transaction: Update world state and reward player
World.UpdateSuccess {
Lock ($village_id) {
EXT:Server.UpdateWorldState { "forest_danger": "low" }
EXT:Server.GrantReward { player_id: $player_id, amount: 1000, item: "HeroCape" }
}
}
# If something fails during the server update, rollback the quest state
World.UpdateSuccess /undo {
$quest_status = "error";
Notify.Player("World update failed. Please contact support.");
}
}
# --- MAIN FLOW ---
# 1. Trigger: Player interacts with the Elder
Welcome.Interaction /start /on (Player.Interact, "Elder") -> QuestGenerator
# 2. Transition to Execution
QuestGenerator.Complete -> QuestExecution
# 3. Transition to Resolution
QuestExecution.Complete -> QuestResolution
# 4. The "Failure" Path (Saga/Recovery)
# If the player dies during the quest, the quest doesn't vanish; it pauses.
ON (Player.Death) -> Quest.Pause {
$quest_status = "paused"
Saga.Compensate {
# Move player back to graveyard, but keep quest items in inventory
EXT:Server.RelocatePlayer { location: "Graveyard" }
}
-> WAIT (Player.Respawn) -> QuestExecution
}
# 5. The "Boredom" Timer (Tick)
# If the player ignores the quest for too long, the NPC gets grumpy
Tick [1hour] {
condition: $quest_status == "active",
logic: NPC.Elder.SetMood("Grumpy")
}
Why this is powerful (The "Universal" breakdown):
- Non-Deterministic Logic (AI): Instead of
if (quest_id == 1), the system usesAI.GenerateandAI.Evaluate. The quest changes every time based on$world_state. - Asynchronous Event-Driven Architecture: The
Obj.FetchItemandObj.SlayMonsternodes don't "poll" the database. They useON (Event)listeners, meaning the workflow sleeps until the game engine pushes a specific event. - Strict Synchronization (Fork-Join): The
J->(Join) ensures the player cannot turn in the quest by just killing the monster; they must also find the item and talk to the spirit. - Infrastructure Safety: The use of
Lock ($village_id)prevents a "race condition" where two players completing the same quest at the same time could double-reward themselves or glitch the world state. - Resilience (Saga/Recovery): The
ON (Player.Death)handler acts as a recovery mechanism, ensuring the state is preserved across a "fail state" (Death) and resuming the workflow exactly where it left off. - Real-time Ambience (Tick): The
Tick [1hour]allows the NPC to react to the passage of time, adding a layer of realism that standard linear workflows cannot handle.