56 bool musicAlreadyPlaying =
false;
59 auto& sources = audioSources.value().get();
60 for (
const auto& entity : sources.entities()) {
61 auto& source = sources[entity];
62 if (source.audioPath ==
"assets/musics/menu.mp3") {
63 musicAlreadyPlaying =
true;
65 log::info(
"Menu music already playing, reusing it");
71 if (!musicAlreadyPlaying) {
76 menuMusic.
audioPath =
"assets/musics/menu.mp3";
78 menuMusic.
loop =
true;
80 menuMusic.
dirty =
true;
93 auto &buttons = buttonsOpt.value().get();
94 if (!buttons.has(entity)) {
97 auto &button = buttons[entity];
98 std::copy(std::begin(style.idleColor), std::end(style.idleColor),
99 std::begin(button.idleColor));
100 std::copy(std::begin(style.hoverColor), std::end(style.hoverColor),
101 std::begin(button.hoverColor));
102 std::copy(std::begin(style.pressedColor), std::end(style.pressedColor),
103 std::begin(button.pressedColor));
166 const EnemySpec enemySpecs[] = {
173 for (
const auto& spec : enemySpecs) {
175 t.
scale = {spec.scale, spec.scale};
195 "assets/fonts/title.ttf",
205 "assets/fonts/title.ttf",
215 "assets/fonts/main.ttf",
221 const float weaponPanelX = 80.0f;
222 const float weaponPanelY = 200.0f;
223 const float weaponPanelW = 300.0f;
224 const float weaponPanelH = 320.0f;
227 const std::vector<ecs::components::WeaponKind> availableKinds = {
237 {weaponPanelX, weaponPanelY},
238 {weaponPanelW, weaponPanelH},
242 styleButton(weaponPanel, panelStyle);
246 {weaponPanelX + 20.0f, weaponPanelY + 18.0f},
248 "assets/fonts/main.ttf",
256 {weaponPanelX + 20.0f, weaponPanelY + 50.0f},
258 "assets/fonts/main.ttf",
266 {weaponPanelX + 20.0f, weaponPanelY + 85.0f},
269 [
this, availableKinds]() {
272 for (
size_t i = 0; i < availableKinds.size(); ++i) {
273 if (availableKinds[i] == currentKind) { idx = i;
break; }
275 if (idx == 0) idx = availableKinds.size() - 1;
283 styleButton(leftArrow, secondaryStyle);
288 if (!dn.empty()) initialWeaponName = dn;
293 {weaponPanelX + 80.0f, weaponPanelY + 93.0f},
295 "assets/fonts/main.ttf",
303 {weaponPanelX + weaponPanelW - 64.0f, weaponPanelY + 85.0f},
306 [
this, availableKinds]() {
309 for (
size_t i = 0; i < availableKinds.size(); ++i) {
310 if (availableKinds[i] == currentKind) { idx = i;
break; }
312 idx = (idx + 1) % availableKinds.size();
319 styleButton(rightArrow, secondaryStyle);
323 {weaponPanelX + 20.0f, weaponPanelY + 150.0f},
325 "assets/fonts/main.ttf",
332 struct BtnDef { std::string key; std::function<void()> cb; };
333 std::vector<BtnDef> buttons{
338 {
"menu.exit", [
this]() { std::exit(0); }}
341 const float panelW = 420.0f;
342 const float panelH = 380.0f;
343 const float panelX = (1280.0f - panelW) / 2.0f;
344 const float panelY = 210.0f;
353 styleButton(menuPanel, panelStyle);
357 {panelX + 40.0f, panelY + 20.0f},
359 "assets/fonts/main.ttf",
365 const float startX = panelX + 40.0f;
366 const float startY = panelY + 70.0f;
367 const float spacingY = 68.0f;
370 for (
size_t i = 0; i < buttons.size(); ++i) {
371 float y = startY +
static_cast<float>(i) * spacingY;
380 styleButton(button, primaryStyle);
382 styleButton(button, secondaryStyle);
389 "Tip: Customize sprites in Mods",
390 "assets/fonts/main.ttf",
452 if (!dn.empty()) text.content = dn;
462 stats +=
"Damage: " + std::to_string(def.damage) +
"\n";
463 if (def.maxAmmo < 0 || def.ammo < 0) {
464 stats +=
"Ammo: Infinite\n";
466 stats +=
"Ammo: " + std::to_string(def.maxAmmo) +
"\n";
468 if (def.fireRate == 0.0f && def.beamDuration > 0.0f) {
469 stats +=
"Fire Rate: Continuous\n";
470 }
else if (def.fireRate >= 5.0f) {
471 stats +=
"Fire Rate: High\n";
472 }
else if (def.fireRate >= 2.0f) {
473 stats +=
"Fire Rate: Medium\n";
475 stats +=
"Fire Rate: Slow\n";
478 if (def.beamDuration > 0.0f) {
479 special +=
"Beam: " + std::to_string(def.beamDuration) +
"s active / " + std::to_string(def.beamCooldown) +
"s cooldown";
483 if (!special.empty()) special +=
" / ";
484 special +=
"Auto-homing shots";
486 if (def.isBoomerang) {
487 if (!special.empty()) special +=
" / ";
488 special +=
"Returns to player";
490 if (special.empty()) special =
"Standard shots";
491 stats +=
"Special: " + special +
"\n";
492 stats +=
"Difficulty: " + std::to_string(def.difficulty) +
"/5";
494 log::warning(
"No weapon configurations found; using default stats display");
499 text.content = stats;
Component representing text to render.