/** * Game State Module * * This module defines the game state and game phases * * @module gameState */ /** * Game phases * * @enum {string} * @readonly */ const GamePhase = { PLACEMENT: 'place', COMBAT: 'run' }; /** * Tower types * * @enum {string} * @readonly */ const TowerTypes = { BASIC: { name: 'Basic', cost: 5, range: 3, damage: 1, attackSpeed: 1, color: '#3498db', maxAmmo: 75 }, RAPID: { name: 'Fast', cost: 10, range: 2, damage: 1, attackSpeed: 3, color: '#16a085', maxAmmo: 50 }, SNIPER: { name: 'Distance', cost: 20, range: 6, damage: 2, attackSpeed: 0.5, color: '#8e44ad', maxAmmo: 50 }, GOOP: { name: 'Goop', cost: 20, range: 3, damage: 0, attackSpeed: 1, color: '#27ae60', special: 'slow', slowAmount: 0.75, maxAmmo: 25 }, AOE: { name: 'AOE', cost: 25, range: 2, damage: 3, attackSpeed: 0.25, color: '#d35400', special: 'aoe', aoeRadius: 2, maxAmmo: 25 } }; /** * Particle types * * @enum {string} * @readonly */ const ParticleTypes = { DEATH_PARTICLE: { lifetime: 1000, // milliseconds speed: 0.1, colors: ['#e74c3c', '#c0392b', '#d35400', '#e67e22'] }, PROJECTILE: { lifetime: 300, speed: 0.3, color: '#ecf0f1' }, AOE_EXPLOSION: { lifetime: 500, initialRadius: 10, finalRadius: 60, color: '#d35400', ringWidth: 3 }, SLIME_TRAIL: { lifetime: 800, color: '#27ae60', // Same as Goop tower size: 12, fadeStart: 0.2 // When the fade should begin (percentage of lifetime) } }; /** * Enemy types * * @enum {string} * @readonly */ const EnemyTypes = { BASIC: { color: '#c0392b', baseHealth: { min: 2, max: 6 }, speed: { min: 1, max: 1.5 }, damage: 0, isRanged: false }, RANGED: { color: '#2c3e50', baseHealth: { min: 1, max: 4 }, speed: { min: 0.7, max: 1.2 }, damage: 0.3, attackRange: 3, attackSpeed: 1, // attacks per second isRanged: true } }; /** * Creates a tower * * @param {string} type - Tower type * @param {Object} position - Position of the tower */ function createTower(type, position) { const towerType = TowerTypes[type]; return { ...towerType, type, position, lastAttackTime: 0, currentHealth: 10, maxHealth: 10, ammo: towerType.maxAmmo // Initialize ammo }; } /** * Creates an enemy * * @param {Object} startPosition - Starting position of the enemy */ function createEnemy(startPosition) { // 20% chance for ranged enemy const type = Math.random() < 0.2 ? 'RANGED' : 'BASIC'; const enemyType = EnemyTypes[type]; // Scale health ranges with level const levelScaling = 1 + (gameState.level - 1) * 0.25; // increase health by 25% per level const minHealth = Math.floor(enemyType.baseHealth.min * levelScaling); const maxHealth = Math.floor(enemyType.baseHealth.max * levelScaling); const health = Math.floor(Math.random() * (maxHealth - minHealth + 1)) + minHealth; return { position: { ...startPosition }, currentHealth: health, maxHealth: health, speed: enemyType.speed.min + Math.random() * (enemyType.speed.max - enemyType.speed.min), pathIndex: 0, type, lastAttackTime: 0, damage: enemyType.damage }; } /** * Creates a particle * * @param {string} type - Particle type * @param {Object} position - Position of the particle */ function createParticle(type, position, angle) { return { position: { ...position }, velocity: { x: Math.cos(angle) * type.speed, y: Math.sin(angle) * type.speed }, color: Array.isArray(type.colors) ? type.colors[Math.floor(Math.random() * type.colors.length)] : type.color, createdAt: performance.now(), lifetime: type.lifetime, size: 3 + Math.random() * 2 }; } /** * Game state * * @type {Object} */ const gameState = { grid: Array(20).fill().map(() => Array(20).fill('empty')), path: [], towers: [], enemies: [], currency: 100, phase: GamePhase.PLACEMENT, isGameOver: false, particles: [], projectiles: [], enemiesDestroyed: 0, enemiesEscaped: 0, level: 1, /** * Resets the game state */ resetGame() { this.grid = Array(20).fill().map(() => Array(20).fill('empty')); this.path = []; this.towers = []; this.enemies = []; this.currency = 100; this.phase = GamePhase.PLACEMENT; this.isGameOver = false; this.particles = []; this.projectiles = []; this.enemiesDestroyed = 0; this.enemiesEscaped = 0; this.level = 1; }, /** * Awards the enemy destroyed */ awardEnemyDestroyed() { this.enemiesDestroyed++; // Random reward between 1 and 3 const reward = Math.floor(Math.random() * 3) + 1; this.currency += reward; }, /** * Checks if the level is complete * * @returns {boolean} */ checkLevelComplete() { return this.enemies.length === 0 && enemiesRemaining === 0 && this.phase === GamePhase.COMBAT; }, /** * Advances to the next level */ advanceToNextLevel() { let ammoBonus = 0; this.towers.forEach(tower => { ammoBonus += tower.ammo * 0.25; }); this.currency += Math.floor(ammoBonus); // Round down to nearest whole number this.level++; this.phase = GamePhase.PLACEMENT; this.towers = []; this.enemies = []; this.projectiles = []; this.particles = []; this.grid = Array(20).fill().map(() => Array(20).fill('empty')); } };