Air-Trap 1.0.0
A multiplayer R-Type clone game engine built with C++23 and ECS architecture
Loading...
Searching...
No Matches
WeaponConfig.hpp
Go to the documentation of this file.
1/*
2** WeaponConfig.hpp
3** Header-only loader for weapon definitions (custom JSON parser, no external deps)
4*/
5#pragma once
6
9#include "RType/Logger.hpp"
10#include <unordered_map>
11#include <string>
12
13namespace rtp::config {
14
15 inline std::unordered_map<rtp::ecs::components::WeaponKind, rtp::ecs::components::SimpleWeapon> loadWeaponConfigs(const std::string &path = "config/common/weapons.json")
16 {
17 std::unordered_map<rtp::ecs::components::WeaponKind, rtp::ecs::components::SimpleWeapon> map;
18
19 // Try multiple candidate locations to be robust to working-directory differences
20 std::vector<std::string> candidates = {
21 path,
22 "config/common/weapons.json",
23 "config/weapons.json",
24 "./config/common/weapons.json",
25 "../config/common/weapons.json"
26 };
27
28 std::unordered_map<std::string, SimpleJson> weapons;
29 for (const auto &p : candidates) {
30 weapons = parseWeaponsFile(p);
31 if (!weapons.empty()) break;
32 }
33
34 if (weapons.empty()) {
35 return map; // empty => callers should fallback to hardcoded defaults
36 }
37
38 for (const auto& [name, data] : weapons) {
40
41 // Determine kind from data or name
42 int kindVal = data.getInt("kind", -1);
43 if (kindVal >= 0) {
44 kind = static_cast<rtp::ecs::components::WeaponKind>(kindVal);
45 } else {
46 if (name == "Classic") kind = rtp::ecs::components::WeaponKind::Classic;
47 else if (name == "Beam") kind = rtp::ecs::components::WeaponKind::Beam;
48 // Paddle (Reflector) removed from configs
49 else if (name == "Tracker") kind = rtp::ecs::components::WeaponKind::Tracker;
50 else if (name == "Boomerang") kind = rtp::ecs::components::WeaponKind::Boomerang;
51 }
52
54 cfg.kind = kind;
55 cfg.fireRate = data.getFloat("fireRate", 0.0f);
56 cfg.lastShotTime = data.getFloat("lastShotTime", 0.0f);
57 cfg.damage = data.getInt("damage", 0);
58 cfg.ammo = data.getInt("ammo", -1);
59 cfg.maxAmmo = data.getInt("maxAmmo", -1);
60 cfg.beamDuration = data.getFloat("beamDuration", 0.0f);
61 cfg.beamCooldown = data.getFloat("beamCooldown", 0.0f);
62 // Reflect feature removed; no longer loading canReflect
63 cfg.homing = data.getBool("homing", false);
64 cfg.homingSteering = data.getFloat("homingSteering", 3.0f);
65 cfg.homingRange = data.getFloat("homingRange", 600.0f);
66 cfg.isBoomerang = data.getBool("isBoomerang", false);
67 cfg.difficulty = data.getInt("difficulty", 2);
68
69 map[kind] = cfg;
70 }
71
72 return map;
73 }
74
75 // Internal cache helpers (function-statics so header-only usage is safe)
76 inline std::unordered_map<rtp::ecs::components::WeaponKind, rtp::ecs::components::SimpleWeapon> &weapon_cache()
77 {
78 static std::unordered_map<rtp::ecs::components::WeaponKind, rtp::ecs::components::SimpleWeapon> cache;
79 return cache;
80 }
81
82 inline bool &weapon_cache_loaded()
83 {
84 static bool loaded = false;
85 return loaded;
86 }
87
88 inline void reloadWeaponConfigs()
89 {
91 weapon_cache_loaded() = true;
92 log::info("Reloaded {} weapon configs", weapon_cache().size());
93 }
94
96 {
97 if (!weapon_cache_loaded()) {
99 weapon_cache_loaded() = true;
100 }
101
102 auto it = weapon_cache().find(kind);
103 static rtp::ecs::components::SimpleWeapon fallback{};
104 if (it != weapon_cache().end())
105 return it->second;
106
107 return fallback; // empty fallback; callers may set defaults
108 }
109
110 inline bool hasWeaponConfigs()
111 {
112 if (!weapon_cache_loaded()) {
114 weapon_cache_loaded() = true;
115 }
116 return !weapon_cache().empty();
117 }
118
120 {
121 // Try candidate paths like loadWeaponConfigs
122 std::vector<std::string> candidates = {
123 "config/common/weapons.json",
124 "config/weapons.json",
125 "./config/common/weapons.json",
126 "../config/common/weapons.json"
127 };
128
129 for (const auto &p : candidates) {
130 auto weapons = parseWeaponsFile(p);
131 if (weapons.empty()) continue;
132
133 for (const auto& [name, data] : weapons) {
134 int k = data.getInt("kind", -1);
135 if (k < 0) {
136 if (name == "Classic") k = 0;
137 else if (name == "Beam") k = 1;
138 else if (name == "Tracker") k = 3;
139 else if (name == "Boomerang") k = 4;
140 }
141
142 if (static_cast<uint8_t>(kind) == static_cast<uint8_t>(k)) {
143 std::string displayName = data.getString("displayName", "");
144 if (!displayName.empty()) return displayName;
145 return name;
146 }
147 }
148 }
149
150 return std::string();
151 }
152
153} // namespace rtp::config
Logger declaration with support for multiple log levels.
File : SpriteMapConfig.hpp License: MIT Author : GitHub Copilot Date : 18/01/2026.
bool & weapon_cache_loaded()
std::string getWeaponDisplayName(rtp::ecs::components::WeaponKind kind)
bool hasWeaponConfigs()
std::unordered_map< std::string, SimpleJson > parseWeaponsFile(const std::string &filepath="config/common/weapons.json")
std::unordered_map< rtp::ecs::components::WeaponKind, rtp::ecs::components::SimpleWeapon > loadWeaponConfigs(const std::string &path="config/common/weapons.json")
std::unordered_map< rtp::ecs::components::WeaponKind, rtp::ecs::components::SimpleWeapon > & weapon_cache()
const rtp::ecs::components::SimpleWeapon & getWeaponDef(rtp::ecs::components::WeaponKind kind)
void reloadWeaponConfigs()
WeaponKind
Types of player weapons.
@ Tracker
Weak auto-homing shots.
@ Boomerang
Single projectile that returns.
@ Classic
Default spam/charge laser.
@ Beam
Continuous beam 5s active, 5s cooldown.
void info(LogFmt< std::type_identity_t< Args >... > fmt, Args &&...args) noexcept
Log an informational message.
Component representing a weapon configuration.
int difficulty
Difficulty to use this weapon (1-5)
float beamCooldown
Beam cooldown time (Beam)
int maxAmmo
Max ammo (-1 = infinite)
bool isBoomerang
Projectile returns (Boomerang)
int ammo
Current ammo (-1 = infinite)
float homingSteering
Steering factor for homing bullets.
float beamDuration
Beam active time (Beam)
float fireRate
Shots per second (or cooldown)
bool homing
Shots home to enemies (Tracker)
float lastShotTime
Time since last shot.
float homingRange
Detection range for homing bullets.