use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuestSystem {
pub quests: HashMap<String, Quest>,
pub active_quests: Vec<String>,
pub completed_quests: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Quest {
pub id: String,
pub title: String,
pub description: String,
pub objectives: Vec<QuestObjective>,
pub rewards: QuestRewards,
pub status: QuestStatus,
pub required_level: u32,
pub prerequisites: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuestObjective {
pub id: String,
pub description: String,
pub objective_type: ObjectiveType,
pub current: u32,
pub target: u32,
pub completed: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ObjectiveType {
Kill { enemy_id: String },
Collect { item_id: String },
Talk { npc_id: String },
Reach { location: String },
Escort { npc_id: String },
Defend { duration: f32 },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QuestRewards {
pub exp: u32,
pub gold: u32,
pub items: Vec<(String, u32)>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum QuestStatus {
Available,
Active,
Completed,
Failed,
}
impl QuestSystem {
pub fn new() -> Self {
Self {
quests: HashMap::new(),
active_quests: Vec::new(),
completed_quests: Vec::new(),
}
}
pub fn add_quest(&mut self, quest: Quest) {
self.quests.insert(quest.id.clone(), quest);
}
pub fn start_quest(&mut self, quest_id: &str) -> bool {
if let Some(quest) = self.quests.get_mut(quest_id) {
if quest.status == QuestStatus::Available {
quest.status = QuestStatus::Active;
self.active_quests.push(quest_id.to_string());
return true;
}
}
false
}
pub fn complete_quest(&mut self, quest_id: &str) -> Option<QuestRewards> {
if let Some(quest) = self.quests.get_mut(quest_id) {
if quest.status == QuestStatus::Active && quest.all_objectives_complete() {
quest.status = QuestStatus::Completed;
self.active_quests.retain(|id| id != quest_id);
self.completed_quests.push(quest_id.to_string());
return Some(quest.rewards.clone());
}
}
None
}
pub fn update_objective(&mut self, quest_id: &str, objective_id: &str, progress: u32) {
if let Some(quest) = self.quests.get_mut(quest_id) {
if quest.status == QuestStatus::Active {
for obj in &mut quest.objectives {
if obj.id == objective_id {
obj.current = obj.current.max(progress).min(obj.target);
obj.completed = obj.current >= obj.target;
}
}
}
}
}
pub fn get_active_quests(&self) -> Vec<&Quest> {
self.active_quests
.iter()
.filter_map(|id| self.quests.get(id))
.collect()
}
pub fn get_available_quests(&self, player_level: u32) -> Vec<&Quest> {
self.quests
.values()
.filter(|q| {
q.status == QuestStatus::Available
&& q.required_level <= player_level
&& q.prerequisites.iter().all(|p| self.completed_quests.contains(p))
})
.collect()
}
}
impl Quest {
pub fn new(id: &str, title: &str, description: &str) -> Self {
Self {
id: id.to_string(),
title: title.to_string(),
description: description.to_string(),
objectives: Vec::new(),
rewards: QuestRewards::default(),
status: QuestStatus::Available,
required_level: 1,
prerequisites: Vec::new(),
}
}
pub fn add_objective(&mut self, objective: QuestObjective) {
self.objectives.push(objective);
}
pub fn all_objectives_complete(&self) -> bool {
!self.objectives.is_empty() && self.objectives.iter().all(|obj| obj.completed)
}
pub fn progress_percentage(&self) -> f32 {
if self.objectives.is_empty() {
return 0.0;
}
let completed = self.objectives.iter().filter(|obj| obj.completed).count();
(completed as f32 / self.objectives.len() as f32) * 100.0
}
}
impl QuestObjective {
pub fn kill(id: &str, description: &str, enemy_id: &str, count: u32) -> Self {
Self {
id: id.to_string(),
description: description.to_string(),
objective_type: ObjectiveType::Kill {
enemy_id: enemy_id.to_string(),
},
current: 0,
target: count,
completed: false,
}
}
pub fn collect(id: &str, description: &str, item_id: &str, count: u32) -> Self {
Self {
id: id.to_string(),
description: description.to_string(),
objective_type: ObjectiveType::Collect {
item_id: item_id.to_string(),
},
current: 0,
target: count,
completed: false,
}
}
}
impl Default for QuestRewards {
fn default() -> Self {
Self {
exp: 0,
gold: 0,
items: Vec::new(),
}
}
}
impl Default for QuestSystem {
fn default() -> Self {
Self::new()
}
}