/**
* 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'));
}
};