|
| 1 | +#include "../../include/SkillParser.hpp" |
| 2 | +#include <fstream> |
| 3 | +#include <iostream> |
| 4 | + |
| 5 | +SkillParser::SkillParser(const std::string& json_file_path) |
| 6 | + : file_path(json_file_path) |
| 7 | +{} |
| 8 | + |
| 9 | +bool SkillParser::loadJson() { |
| 10 | + try { |
| 11 | + std::ifstream file(file_path); |
| 12 | + if (!file.is_open()) { |
| 13 | + std::cerr << "Failed to open file: " << file_path << std::endl; |
| 14 | + return false; |
| 15 | + } |
| 16 | + |
| 17 | + file >> skill_data; |
| 18 | + return true; |
| 19 | + } catch (const json::exception& e) { |
| 20 | + std::cerr << "JSON parsing error: " << e.what() << std::endl; |
| 21 | + return false; |
| 22 | + } |
| 23 | +} |
| 24 | + |
| 25 | +std::vector<std::unique_ptr<Skill>> SkillParser::getCommanderSkills(const std::string& commander_name) const { |
| 26 | + std::vector<std::unique_ptr<Skill>> skills; |
| 27 | + |
| 28 | + if (!skill_data.contains("Commanders") || !skill_data["Commanders"].contains(commander_name)) { |
| 29 | + return skills; |
| 30 | + } |
| 31 | + |
| 32 | + const json& commander = skill_data["Commanders"][commander_name]; |
| 33 | + |
| 34 | + // Parse all skill categories (AwakenedActive, Secondary, etc.) |
| 35 | + for (auto& [category, skill_array] : commander.items()) { |
| 36 | + if (skill_array.is_array()) { |
| 37 | + for (const auto& skill_json : skill_array) { |
| 38 | + auto skill = jsonToSkill(skill_json); |
| 39 | + if (skill) { |
| 40 | + skills.push_back(std::move(skill)); |
| 41 | + } |
| 42 | + } |
| 43 | + } |
| 44 | + } |
| 45 | + |
| 46 | + return skills; |
| 47 | +} |
| 48 | + |
| 49 | +std::unique_ptr<Skill> SkillParser::getSkillById(const std::string& skill_id) const { |
| 50 | + // Search in Commanders |
| 51 | + if (skill_data.contains("Commanders")) { |
| 52 | + for (auto& [commander_name, commander_data] : skill_data["Commanders"].items()) { |
| 53 | + for (auto& [category, skill_array] : commander_data.items()) { |
| 54 | + if (skill_array.is_array()) { |
| 55 | + for (const auto& skill_json : skill_array) { |
| 56 | + if (skill_json.contains("id") && skill_json["id"] == skill_id) { |
| 57 | + return jsonToSkill(skill_json); |
| 58 | + } |
| 59 | + } |
| 60 | + } |
| 61 | + } |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + // Search in Skills section |
| 66 | + if (skill_data.contains("Skills")) { |
| 67 | + for (auto& [skill_name, skill_json] : skill_data["Skills"].items()) { |
| 68 | + if (skill_json.contains("id") && skill_json["id"] == skill_id) { |
| 69 | + return jsonToSkill(skill_json); |
| 70 | + } |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + return nullptr; |
| 75 | +} |
| 76 | + |
| 77 | +std::vector<std::unique_ptr<Skill>> SkillParser::getGenericSkills() const { |
| 78 | + std::vector<std::unique_ptr<Skill>> skills; |
| 79 | + |
| 80 | + if (!skill_data.contains("Skills")) { |
| 81 | + return skills; |
| 82 | + } |
| 83 | + |
| 84 | + for (auto& [skill_name, skill_json] : skill_data["Skills"].items()) { |
| 85 | + auto skill = jsonToSkill(skill_json); |
| 86 | + if (skill) { |
| 87 | + skills.push_back(std::move(skill)); |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + return skills; |
| 92 | +} |
| 93 | + |
| 94 | +std::unique_ptr<Skill> SkillParser::jsonToSkill(const json& skill_json) const { |
| 95 | + if (!skill_json.contains("effects") || !skill_json["effects"].is_array()) { |
| 96 | + std::cerr << "Skill missing effects array" << std::endl; |
| 97 | + return nullptr; |
| 98 | + } |
| 99 | + |
| 100 | + // Get the skill type from skillType or metaType |
| 101 | + SkillType skill_type = SkillType::COMMAND; // Default |
| 102 | + if (skill_json.contains("skillType") && skill_json["skillType"].contains("category")) { |
| 103 | + skill_type = stringToSkillType(skill_json["skillType"]["category"].get<std::string>()); |
| 104 | + } else if (skill_json.contains("metaType") && skill_json["metaType"].contains("category")) { |
| 105 | + skill_type = stringToSkillType(skill_json["metaType"]["category"].get<std::string>()); |
| 106 | + } |
| 107 | + |
| 108 | + // Build skill condition from trigger requirements |
| 109 | + SkillCondition condition = buildSkillCondition(skill_json); |
| 110 | + |
| 111 | + // Determine skill target |
| 112 | + SkillTarget target = determineSkillTarget(skill_json); |
| 113 | + |
| 114 | + // Process effects - create appropriate skill type based on first effect |
| 115 | + const json& effects = skill_json["effects"]; |
| 116 | + if (effects.empty()) { |
| 117 | + std::cerr << "Skill has no effects" << std::endl; |
| 118 | + return nullptr; |
| 119 | + } |
| 120 | + |
| 121 | + const json& first_effect = effects[0]; |
| 122 | + if (!first_effect.contains("type") || !first_effect.contains("magnitude")) { |
| 123 | + std::cerr << "Effect missing type or magnitude" << std::endl; |
| 124 | + return nullptr; |
| 125 | + } |
| 126 | + |
| 127 | + std::string effect_type_str = first_effect["type"].get<std::string>(); |
| 128 | + EffectType effect_type = stringToEffectType(effect_type_str); |
| 129 | + double magnitude = first_effect["magnitude"].get<double>(); |
| 130 | + |
| 131 | + // Determine if this is a status effect (heal, poison, burn, shield, etc.) or direct damage |
| 132 | + if (effect_type_str == "directDamage" || effect_type_str == "damage") { |
| 133 | + // Create DamageSkill |
| 134 | + return std::make_unique<DamageSkill>( |
| 135 | + magnitude, |
| 136 | + skill_type, |
| 137 | + effect_type, |
| 138 | + condition, |
| 139 | + target |
| 140 | + ); |
| 141 | + } else { |
| 142 | + // Create StatusSkill (heal, poison, burn, shield, etc.) |
| 143 | + // Need duration for TimedEffect |
| 144 | + int duration = 1; // Default duration |
| 145 | + if (first_effect.contains("skillDuration")) { |
| 146 | + duration = first_effect["skillDuration"].get<int>(); |
| 147 | + } |
| 148 | + |
| 149 | + TimedEffect timed_effect(duration, magnitude); |
| 150 | + |
| 151 | + return std::make_unique<StatusSkill>( |
| 152 | + timed_effect, |
| 153 | + skill_type, |
| 154 | + effect_type, |
| 155 | + condition, |
| 156 | + target |
| 157 | + ); |
| 158 | + } |
| 159 | +} |
| 160 | + |
| 161 | +SkillCondition SkillParser::buildSkillCondition(const json& skill_json) const { |
| 162 | + // TODO: Parse trigger requirements and build proper SkillCondition |
| 163 | + // For now, return a default condition |
| 164 | + // Need to map triggerRequirement array to ConditionType and EffectType |
| 165 | + |
| 166 | + ConditionType condition_type = ConditionType::HAS_EFFECT_SELF; // Default |
| 167 | + EffectType effect_type = EffectType::POISON; // Default |
| 168 | + |
| 169 | + if (skill_json.contains("trigger") && skill_json["trigger"].contains("triggerRequirement")) { |
| 170 | + const json& requirements = skill_json["trigger"]["triggerRequirement"]; |
| 171 | + if (requirements.is_array() && !requirements.empty()) { |
| 172 | + // TODO: Map string requirements to ConditionType and EffectType |
| 173 | + // Example: "poison" -> ConditionType::HAS_EFFECT_SELF, EffectType::POISON |
| 174 | + // Example: "basicAttack" -> ConditionType related to attack |
| 175 | + std::string first_req = requirements[0].get<std::string>(); |
| 176 | + |
| 177 | + // Basic mapping (needs expansion based on your requirements) |
| 178 | + if (first_req == "poison") { |
| 179 | + condition_type = ConditionType::HAS_EFFECT_SELF; |
| 180 | + effect_type = EffectType::POISON; |
| 181 | + } |
| 182 | + // TODO: Add more condition mappings |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + return SkillCondition(condition_type, effect_type); |
| 187 | +} |
| 188 | + |
| 189 | +SkillTarget SkillParser::determineSkillTarget(const json& skill_json) const { |
| 190 | + // TODO: Determine from JSON if skill targets FRIENDLY or ENEMY |
| 191 | + // This might be in a "target" field or inferred from effect type |
| 192 | + // For now, default to FRIENDLY for heals, ENEMY for damage |
| 193 | + |
| 194 | + if (skill_json.contains("effects") && skill_json["effects"].is_array() && !skill_json["effects"].empty()) { |
| 195 | + std::string effect_type = skill_json["effects"][0]["type"].get<std::string>(); |
| 196 | + if (effect_type == "heal" || effect_type == "shield" || effect_type == "buff") { |
| 197 | + return SkillTarget::FRIENDLY; |
| 198 | + } else if (effect_type == "damage" || effect_type == "directDamage" || effect_type == "poison" || effect_type == "burn") { |
| 199 | + return SkillTarget::ENEMY; |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | + // TODO: Add explicit target field parsing if it exists in JSON |
| 204 | + return SkillTarget::ENEMY; // Default |
| 205 | +} |
| 206 | + |
| 207 | +SkillType SkillParser::stringToSkillType(const std::string& type_str) const { |
| 208 | + if (type_str == "command") return SkillType::COMMAND; |
| 209 | + if (type_str == "passive") return SkillType::PASSIVE; |
| 210 | + if (type_str == "cooperation") return SkillType::COOPERATION; |
| 211 | + if (type_str == "counterattack") return SkillType::COUNTERATTACK; |
| 212 | + // TODO: Add more SkillType mappings as needed |
| 213 | + return SkillType::COMMAND; // Default |
| 214 | +} |
| 215 | + |
| 216 | +EffectType SkillParser::stringToEffectType(const std::string& effect_str) const { |
| 217 | + if (effect_str == "poison") return EffectType::POISON; |
| 218 | + if (effect_str == "heal" || effect_str == "healing") return EffectType::HEALING; |
| 219 | + if (effect_str == "burn") return EffectType::BURN; |
| 220 | + if (effect_str == "bleed") return EffectType::BLEED; |
| 221 | + if (effect_str == "absorption") return EffectType::ABSORPTION; |
| 222 | + if (effect_str == "retribution") return EffectType::RETRIBUTION; |
| 223 | + // TODO: Add specific damage effect type or use appropriate existing type |
| 224 | + if (effect_str == "damage" || effect_str == "directDamage") return EffectType::BURN; // Using BURN as placeholder for damage |
| 225 | + // TODO: Add more EffectType mappings based on your EffectType enum |
| 226 | + return EffectType::POISON; // Default |
| 227 | +} |
| 228 | + |
| 229 | +SkillTarget SkillParser::stringToSkillTarget(const std::string& target_str) const { |
| 230 | + if (target_str == "friendly" || target_str == "FRIENDLY") return SkillTarget::FRIENDLY; |
| 231 | + if (target_str == "enemy" || target_str == "ENEMY") return SkillTarget::ENEMY; |
| 232 | + // TODO: Add more SkillTarget mappings if needed |
| 233 | + return SkillTarget::ENEMY; // Default |
| 234 | +} |
| 235 | + |
| 236 | +ConditionType SkillParser::stringToConditionType(const std::string& condition_str) const { |
| 237 | + if (condition_str == "HAS_EFFECT_SELF") return ConditionType::HAS_EFFECT_SELF; |
| 238 | + if (condition_str == "HAS_EFFECT_TARGET") return ConditionType::HAS_EFFECT_TARGET; |
| 239 | + if (condition_str == "TROOP_COUNT_GREATER_THAN_TARGET") return ConditionType::TROOP_COUNT_GREATER_THAN_TARGET; |
| 240 | + // TODO: Add more ConditionType mappings as needed |
| 241 | + return ConditionType::HAS_EFFECT_SELF; // Default |
| 242 | +} |
0 commit comments