// player_config.cpp - Implementació del parser de PlayerConfig // © 2026 JailDesigner #include "game/entities/player_config.hpp" #include #include #include #include #include namespace { // Helper: extreu un SDL_Color d'una seqüència de 3 enters [r, g, b] del YAML. // Retorna true si el format és vàlid. auto parseColor(const fkyaml::node& node, SDL_Color& out) -> bool { if (!node.is_sequence() || node.size() != 3) { return false; } const auto R = node[0].get_value(); const auto G = node[1].get_value(); const auto B = node[2].get_value(); out = SDL_Color{ .r = static_cast(R), .g = static_cast(G), .b = static_cast(B), .a = 255}; return true; } } // namespace auto PlayerConfig::fromYaml(const fkyaml::node& node) -> std::optional { try { PlayerConfig cfg; cfg.name = node.contains("name") ? node["name"].get_value() : "player"; // shape if (!node.contains("shape") || !node["shape"].contains("path")) { std::cerr << "[PlayerConfig] Error: falta 'shape.path'" << '\n'; return std::nullopt; } cfg.shape.path = node["shape"]["path"].get_value(); // physics if (!node.contains("physics")) { std::cerr << "[PlayerConfig] Error: falta 'physics'" << '\n'; return std::nullopt; } const auto& physics = node["physics"]; cfg.physics.mass = physics["mass"].get_value(); cfg.physics.restitution = physics["restitution"].get_value(); cfg.physics.linear_damping = physics["linear_damping"].get_value(); cfg.physics.angular_damping = physics["angular_damping"].get_value(); cfg.physics.collision_radius = physics["collision_radius"].get_value(); cfg.physics.rotation_speed = physics["rotation_speed"].get_value(); cfg.physics.acceleration = physics["acceleration"].get_value(); cfg.physics.max_velocity = physics["max_velocity"].get_value(); cfg.physics.death_impact_factor = physics["death_impact_factor"].get_value(); // invulnerability if (!node.contains("invulnerability")) { std::cerr << "[PlayerConfig] Error: falta 'invulnerability'" << '\n'; return std::nullopt; } const auto& invul = node["invulnerability"]; cfg.invulnerability.duration = invul["duration"].get_value(); cfg.invulnerability.blink_visible = invul["blink_visible"].get_value(); cfg.invulnerability.blink_invisible = invul["blink_invisible"].get_value(); // hurt if (!node.contains("hurt")) { std::cerr << "[PlayerConfig] Error: falta 'hurt'" << '\n'; return std::nullopt; } cfg.hurt.duration = node["hurt"]["duration"].get_value(); cfg.hurt.blink_hz = node["hurt"]["blink_hz"].get_value(); // visual_thrust if (!node.contains("visual_thrust")) { std::cerr << "[PlayerConfig] Error: falta 'visual_thrust'" << '\n'; return std::nullopt; } cfg.visual_thrust.push_divisor = node["visual_thrust"]["push_divisor"].get_value(); cfg.visual_thrust.scale_divisor = node["visual_thrust"]["scale_divisor"].get_value(); // colors if (!node.contains("colors") || !parseColor(node["colors"]["normal"], cfg.colors.normal) || !parseColor(node["colors"]["hurt"], cfg.colors.hurt)) { std::cerr << "[PlayerConfig] Error: 'colors.normal' / 'colors.hurt' no són seqüències [r,g,b]" << '\n'; return std::nullopt; } // weapon if (!node.contains("weapon")) { std::cerr << "[PlayerConfig] Error: falta 'weapon'" << '\n'; return std::nullopt; } cfg.weapon.bullet_speed = node["weapon"]["bullet_speed"].get_value(); return cfg; } catch (const std::exception& e) { std::cerr << "[PlayerConfig] Excepció parsejant: " << e.what() << '\n'; return std::nullopt; } }