Skip to content

Commit 4a7d6e9

Browse files
Tdotclaude
authored andcommitted
refactor(combat): consolidate weapon colors into single source of truth
Eliminated 32-entry duplicate weaponColors map from updateHUD() — all weapon colors now derived from weaponTypes and evolutionRecipes definitions. Extracted hexToColorStr() utility replacing 6 repeated inline toString(16).padStart(6,'0') conversions. Also fixes a silent kunai color mismatch (0x2f2f2f vs #4a4a4a) that the duplication caused. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5f85996 commit 4a7d6e9

4 files changed

Lines changed: 45 additions & 42 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22

33
All notable changes to Vibe Coder will be documented in this file.
44

5+
## [0.6.9] - 2026-02-11
6+
7+
### Changed
8+
- **Weapon color single source of truth** — Eliminated 32-entry duplicate `weaponColors` map in `updateHUD()`. All weapon colors now derived from `weaponTypes` and `evolutionRecipes` definitions, making color mismatches impossible when adding weapons
9+
- **`hexToColorStr()` utility** — Extracted 6 repeated inline `toString(16).padStart(6, '0')` conversions into a single reusable method used by stage nodes, boss names, modifiers, boss announcements, mini-boss announcements, and evolution effects
10+
- **`getWeaponColorStr()` lookup** — New method resolves any weapon type (base, evolved, or legendary) to its CSS color string by walking `weaponTypes` first, then `evolutionRecipes`
11+
12+
### Fixed
13+
- **Kunai HUD color mismatch** — Kunai displayed as `#4a4a4a` in the HUD but was defined as `0x2f2f2f` in `weaponTypes`. The duplicate color map had drifted. Now impossible since colors derive from the single definition
14+
15+
---
16+
517
## [0.6.8] - 2026-02-11
618

719
### Fixed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ vibe-coder/
273273
| **Spatial hashing** | O(n) collision detection via `SpatialHash` grid instead of O(n²) pairwise checks — handles 200+ entities at 60fps |
274274
| **Real-time XP pipeline** | WebSocket bridge turns any dev tool (Claude Code, IDE, CLI) into a game controller via simple HTTP POST |
275275
| **Procedural maps** | Each biome generates walls, hazards, destructibles, and teleporters at runtime — no static level data |
276+
| **Single-source color system** | All 30 weapon colors derived from `weaponTypes`/`evolutionRecipes` — zero duplication, impossible to mismatch |
276277
| **102 unit tests** | Core systems (SpatialHash, RunModifiers, SaveManager, EventManager, RebirthManager) tested with Vitest |
277278

278279
## 🔧 Tech Stack

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "vibe-coder",
3-
"version": "0.6.8",
3+
"version": "0.6.9",
44
"description": "Vampire Survivors-style idle game powered by your Claude Code activity",
55
"type": "module",
66
"main": "electron/main.js",

src/scenes/ArenaScene.js

Lines changed: 31 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ export default class ArenaScene extends Phaser.Scene {
578578
const char = this.add.text(x, startY - (i * 20), chars[Phaser.Math.Between(0, chars.length - 1)], {
579579
fontFamily: 'monospace',
580580
fontSize: '14px',
581-
color: `#${stage.nodeColor.toString(16).padStart(6, '0')}`
581+
color: this.hexToColorStr(stage.nodeColor)
582582
}).setAlpha(0.1 + (i / charCount) * 0.4);
583583
char.setDepth(-5);
584584
streamGroup.add(char);
@@ -1108,43 +1108,10 @@ export default class ArenaScene extends Phaser.Scene {
11081108
this.killsText.setText(`KILLS: ${state.kills}`);
11091109
this.waveText.setText(`WAVE ${this.waveNumber}`);
11101110

1111-
// Update weapon text
1112-
const weaponColors = {
1113-
basic: '#00ffff',
1114-
spread: '#ff9900',
1115-
pierce: '#0099ff',
1116-
orbital: '#aa44ff',
1117-
rapid: '#ffcc00',
1118-
// New weapons
1119-
homing: '#00ff88',
1120-
bounce: '#88ff00',
1121-
aoe: '#ff4488',
1122-
freeze: '#88ffff',
1123-
// Rare weapons
1124-
rmrf: '#ff0000',
1125-
sudo: '#ffd700',
1126-
forkbomb: '#ff00ff',
1127-
// Evolved weapons
1128-
laserbeam: '#ff0088',
1129-
plasmaorb: '#00ffaa',
1130-
chainlightning: '#00aaff',
1131-
bullethell: '#ff6600',
1132-
ringoffire: '#ff4400',
1133-
seekingmissile: '#00ffcc',
1134-
chaosbounce: '#aaff00',
1135-
deathaura: '#ff00aa',
1136-
icelance: '#00ffff',
1137-
swarm: '#88ff88',
1138-
blizzard: '#aaffff',
1139-
// Melee weapons
1140-
sword: '#cccccc',
1141-
spear: '#8b4513',
1142-
boomerang: '#daa520',
1143-
kunai: '#4a4a4a'
1144-
};
1111+
// Update weapon text — colors derived from weaponTypes/evolutionRecipes (single source of truth)
11451112
const weaponLabel = this.currentWeapon.isEvolved ? `★${this.currentWeapon.type.toUpperCase()}★` : this.currentWeapon.type.toUpperCase();
11461113
this.weaponText.setText(`WEAPON: ${weaponLabel}`);
1147-
this.weaponText.setColor(weaponColors[this.currentWeapon.type] || '#00ffff');
1114+
this.weaponText.setColor(this.getWeaponColorStr(this.currentWeapon.type));
11481115

11491116
// Update stage text
11501117
const stage = this.stages[this.currentStage];
@@ -1173,7 +1140,7 @@ export default class ArenaScene extends Phaser.Scene {
11731140
this.bossHealthBar.fillRect(200, 560, 400 * bossHealthPercent, 25);
11741141

11751142
this.bossNameText.setText(`⚠ ${this.currentBoss.bossName} ⚠`);
1176-
this.bossNameText.setColor(`#${this.currentBoss.bossColor.toString(16).padStart(6, '0')}`);
1143+
this.bossNameText.setColor(this.hexToColorStr(this.currentBoss.bossColor));
11771144
} else {
11781145
this.bossHealthBarBg.setVisible(false);
11791146
this.bossHealthBar.setVisible(false);
@@ -1294,7 +1261,7 @@ export default class ArenaScene extends Phaser.Scene {
12941261
const modText = this.add.text(0, -10, `${mod.icon} ${mod.name}`, {
12951262
fontFamily: 'monospace',
12961263
fontSize: '20px',
1297-
color: `#${mod.color.toString(16).padStart(6, '0')}`,
1264+
color: this.hexToColorStr(mod.color),
12981265
fontStyle: 'bold'
12991266
}).setOrigin(0.5);
13001267

@@ -1490,7 +1457,7 @@ export default class ArenaScene extends Phaser.Scene {
14901457
const bossAnnounce = this.add.text(400, 150, `${bossData.name}\nHAS APPEARED!`, {
14911458
fontFamily: 'monospace',
14921459
fontSize: '24px',
1493-
color: `#${bossData.color.toString(16).padStart(6, '0')}`,
1460+
color: this.hexToColorStr(bossData.color),
14941461
fontStyle: 'bold',
14951462
align: 'center',
14961463
stroke: '#000000',
@@ -1765,7 +1732,7 @@ export default class ArenaScene extends Phaser.Scene {
17651732
const miniBossAnnounce = this.add.text(400, 150, `⚡ ${miniBossData.name} ⚡`, {
17661733
fontFamily: 'monospace',
17671734
fontSize: '20px',
1768-
color: `#${miniBossData.color.toString(16).padStart(6, '0')}`,
1735+
color: this.hexToColorStr(miniBossData.color),
17691736
fontStyle: 'bold',
17701737
stroke: '#000000',
17711738
strokeThickness: 3
@@ -3043,7 +3010,7 @@ export default class ArenaScene extends Phaser.Scene {
30433010
const evoText = this.add.text(400, 250, `⚡ WEAPON EVOLVED! ⚡\n${evolved.name}`, {
30443011
fontFamily: 'monospace',
30453012
fontSize: '28px',
3046-
color: `#${evolved.color.toString(16).padStart(6, '0')}`,
3013+
color: this.hexToColorStr(evolved.color),
30473014
fontStyle: 'bold',
30483015
align: 'center',
30493016
stroke: '#000000',
@@ -4436,4 +4403,27 @@ export default class ArenaScene extends Phaser.Scene {
44364403
}
44374404
});
44384405
}
4406+
4407+
/**
4408+
* Convert a Phaser hex color (0xRRGGBB) to a CSS color string (#RRGGBB).
4409+
* Single source of truth for color format conversion across the codebase.
4410+
*/
4411+
hexToColorStr(hex) {
4412+
return `#${hex.toString(16).padStart(6, '0')}`;
4413+
}
4414+
4415+
/**
4416+
* Get the CSS color string for any weapon (base, evolved, or legendary).
4417+
* Derives from weaponTypes and evolutionRecipes — no duplicate color map needed.
4418+
*/
4419+
getWeaponColorStr(weaponType) {
4420+
const baseDef = this.weaponTypes[weaponType];
4421+
if (baseDef) return this.hexToColorStr(baseDef.color);
4422+
4423+
for (const recipe of Object.values(this.evolutionRecipes)) {
4424+
if (recipe.result === weaponType) return this.hexToColorStr(recipe.color);
4425+
}
4426+
4427+
return '#00ffff'; // fallback — basic weapon color
4428+
}
44394429
}

0 commit comments

Comments
 (0)