diff options
Diffstat (limited to 'html/tower/docs/renderer.js.html')
-rw-r--r-- | html/tower/docs/renderer.js.html | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/html/tower/docs/renderer.js.html b/html/tower/docs/renderer.js.html new file mode 100644 index 0000000..ed49c44 --- /dev/null +++ b/html/tower/docs/renderer.js.html @@ -0,0 +1,431 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>JSDoc: Source: renderer.js</title> + + <script src="scripts/prettify/prettify.js"> </script> + <script src="scripts/prettify/lang-css.js"> </script> + <!--[if lt IE 9]> + <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> + <![endif]--> + <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> + <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> +</head> + +<body> + +<div id="main"> + + <h1 class="page-title">Source: renderer.js</h1> + + + + + + + <section> + <article> + <pre class="prettyprint source linenums"><code>/** + * Rendering Module + * + * This module handles all game rendering operations using HTML5 Canvas. + * Demonstrates key game development patterns: + * 1. Layer-based rendering + * 2. Particle systems + * 3. Visual state feedback + * 4. Canvas state management + * + * @module renderer + */ + +/** + * Renders the game grid with path and hover previews + * Implements visual feedback for player actions + * + * @param {CanvasRenderingContext2D} ctx - Canvas rendering context + * @param {Array<Array<string>>} grid - Game grid state + */ +function renderGrid(ctx, grid) { + const cellSize = canvas.width / 20; + + // Draw grid lines for visual reference + ctx.strokeStyle = '#ccc'; + ctx.lineWidth = 1; + + for (let i = 0; i <= 20; i++) { + // Vertical lines + ctx.beginPath(); + ctx.moveTo(i * cellSize, 0); + ctx.lineTo(i * cellSize, canvas.height); + ctx.stroke(); + + // Horizontal lines + ctx.beginPath(); + ctx.moveTo(0, i * cellSize); + ctx.lineTo(canvas.width, i * cellSize); + ctx.stroke(); + } + + // Render grid cells with path highlighting + grid.forEach((row, y) => { + row.forEach((cell, x) => { + if (cell === 'path') { + ctx.fillStyle = '#f4a460'; + ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize); + } + }); + }); + + // Render tower placement preview + if (gameState.phase === GamePhase.PLACEMENT && draggedTowerType && hoverCell) { + const tower = TowerTypes[draggedTowerType]; + const canPlace = grid[hoverCell.y][hoverCell.x] === 'empty' && + gameState.playerCurrency >= tower.cost; + + // Visual feedback for placement validity + ctx.fillStyle = canPlace ? tower.color + '80' : 'rgba(255, 0, 0, 0.3)'; + ctx.fillRect( + hoverCell.x * cellSize, + hoverCell.y * cellSize, + cellSize, + cellSize + ); + + // Range indicator preview + ctx.beginPath(); + ctx.arc( + (hoverCell.x + 0.5) * cellSize, + (hoverCell.y + 0.5) * cellSize, + tower.range * cellSize, + 0, + Math.PI * 2 + ); + ctx.strokeStyle = canPlace ? tower.color + '40' : 'rgba(255, 0, 0, 0.2)'; + ctx.stroke(); + } +} + +/** + * Renders all enemies with health indicators and effects + * Implements visual state representation + * + * @param {CanvasRenderingContext2D} ctx - Canvas rendering context + * @param {Array<Object>} enemies - Array of enemy objects + */ +function renderEnemies(ctx, enemies) { + const cellSize = canvas.width / 20; + + enemies.forEach(enemy => { + // Health-based opacity for visual feedback + const healthPercent = enemy.currentHealth / enemy.maxHealth; + const opacity = 0.3 + (healthPercent * 0.7); + + // Dynamic color based on enemy state + const color = EnemyTypes[enemy.type].color; + const hexOpacity = Math.floor(opacity * 255).toString(16).padStart(2, '0'); + + // Draw enemy body with solid black border + ctx.beginPath(); + ctx.arc( + (enemy.position.x + 0.5) * cellSize, + (enemy.position.y + 0.5) * cellSize, + cellSize / 3, + 0, + Math.PI * 2 + ); + + // Fill with dynamic opacity + ctx.fillStyle = `${color}${hexOpacity}`; + ctx.fill(); + + // Add solid black border + ctx.strokeStyle = 'black'; + ctx.lineWidth = 2; + ctx.stroke(); + + // Range indicator for special enemy types + if (EnemyTypes[enemy.type].isRanged) { + ctx.beginPath(); + ctx.arc( + (enemy.position.x + 0.5) * cellSize, + (enemy.position.y + 0.5) * cellSize, + EnemyTypes[enemy.type].attackRange * cellSize, + 0, + Math.PI * 2 + ); + ctx.strokeStyle = `${EnemyTypes[enemy.type].color}40`; + ctx.stroke(); + } + }); +} + +/** + * Renders game UI elements with clean state management + * Implements heads-up display (HUD) pattern + * + * @param {CanvasRenderingContext2D} ctx - Canvas rendering context + * @param {Object} gameState - Current game state + */ +function renderUI(ctx, gameState) { + const padding = 20; + const lineHeight = 30; + const startY = padding; + const width = 200; + const height = lineHeight * 5; + + // Save the current canvas state + ctx.save(); + + // Reset any transformations + ctx.setTransform(1, 0, 0, 1, 0, 0); + + // Semi-transparent background for readability + ctx.fillStyle = 'rgba(255, 255, 255, 0.8)'; + ctx.fillRect(0, 0, width, height + padding); + + // Text rendering setup + ctx.fillStyle = 'black'; + ctx.font = '20px Arial'; + ctx.textAlign = 'left'; + ctx.textBaseline = 'top'; + + // Game state information + ctx.fillText(`Level: ${gameState.level}`, padding, startY); + ctx.fillText(`Currency: $${gameState.currency}`, padding, startY + lineHeight); + ctx.fillText(`Phase: ${gameState.phase}`, padding, startY + lineHeight * 2); + ctx.fillText(`Destroyed: ${gameState.enemiesDestroyed}`, padding, startY + lineHeight * 3); + ctx.fillText(`Escaped: ${gameState.enemiesEscaped}`, padding, startY + lineHeight * 4); + + // Restore the canvas state + ctx.restore(); +} + +function renderTowers(ctx, towers) { + const cellSize = canvas.width / 20; + + towers.forEach(tower => { + const healthPercent = tower.currentHealth / tower.maxHealth; + + // Draw tower body + ctx.fillStyle = tower.color + Math.floor(healthPercent * 255).toString(16).padStart(2, '0'); + ctx.fillRect( + tower.position.x * cellSize + cellSize * 0.1, + tower.position.y * cellSize + cellSize * 0.1, + cellSize * 0.8, + cellSize * 0.8 + ); + + // Draw ammo count + ctx.fillStyle = 'white'; + ctx.font = '12px Arial'; + ctx.textAlign = 'center'; + ctx.fillText( + tower.ammo, + (tower.position.x + 0.5) * cellSize, + (tower.position.y + 0.7) * cellSize + ); + + // Draw range indicator + if (gameState.phase === GamePhase.PLACEMENT) { + ctx.beginPath(); + ctx.arc( + (tower.position.x + 0.5) * cellSize, + (tower.position.y + 0.5) * cellSize, + tower.range * cellSize, + 0, + Math.PI * 2 + ); + ctx.strokeStyle = tower.color + '40'; + ctx.stroke(); + } + }); +} + +// Add new render function for particles +function renderParticles(ctx, particles) { + particles.forEach(particle => { + const age = performance.now() - particle.createdAt; + const lifePercent = age / particle.lifetime; + + if (lifePercent <= 1) { + if (particle.type === 'SLIME_TRAIL') { + // Calculate opacity based on lifetime and fade start + let opacity = 1; + if (lifePercent > particle.fadeStart) { + opacity = 1 - ((lifePercent - particle.fadeStart) / (1 - particle.fadeStart)); + } + opacity *= 0.3; // Make it translucent + + ctx.globalAlpha = opacity; + ctx.fillStyle = particle.color; + + // Draw a circular slime splat + ctx.beginPath(); + ctx.arc( + particle.position.x, + particle.position.y, + particle.size * (1 - lifePercent * 0.3), // Slightly shrink over time + 0, + Math.PI * 2 + ); + ctx.fill(); + + // Add some variation to the splat + for (let i = 0; i < 3; i++) { + const angle = (Math.PI * 2 * i) / 3; + const distance = particle.size * 0.4; + ctx.beginPath(); + ctx.arc( + particle.position.x + Math.cos(angle) * distance, + particle.position.y + Math.sin(angle) * distance, + particle.size * 0.4 * (1 - lifePercent * 0.3), + 0, + Math.PI * 2 + ); + ctx.fill(); + } + } else if (particle.type === 'AOE_EXPLOSION') { + // Draw expanding circle + const radius = particle.initialRadius + + (particle.finalRadius - particle.initialRadius) * lifePercent; + + // Draw multiple rings for better effect + const numRings = 3; + for (let i = 0; i < numRings; i++) { + const ringRadius = radius * (1 - (i * 0.2)); + const ringAlpha = (1 - lifePercent) * (1 - (i * 0.3)); + + ctx.beginPath(); + ctx.arc( + particle.position.x, + particle.position.y, + ringRadius, + 0, + Math.PI * 2 + ); + ctx.strokeStyle = particle.color; + ctx.lineWidth = particle.ringWidth * (1 - (i * 0.2)); + ctx.globalAlpha = ringAlpha; + ctx.stroke(); + } + + // Draw affected area + ctx.beginPath(); + ctx.arc( + particle.position.x, + particle.position.y, + radius, + 0, + Math.PI * 2 + ); + ctx.fillStyle = particle.color + '20'; // Very transparent fill + ctx.fill(); + } else { + // Original particle rendering + ctx.fillStyle = particle.color; + ctx.beginPath(); + ctx.arc( + particle.position.x, + particle.position.y, + particle.size * (1 - lifePercent), + 0, + Math.PI * 2 + ); + ctx.fill(); + } + } + }); + ctx.globalAlpha = 1; +} + +// Add new render function for projectiles +function renderProjectiles(ctx, projectiles) { + const cellSize = canvas.width / 20; + + projectiles.forEach(projectile => { + const age = performance.now() - projectile.createdAt; + const progress = age / projectile.lifetime; + + if (progress <= 1) { + // Draw projectile trail + ctx.beginPath(); + ctx.moveTo( + projectile.startPos.x * cellSize + cellSize / 2, + projectile.startPos.y * cellSize + cellSize / 2 + ); + + const currentX = projectile.startPos.x + (projectile.targetPos.x - projectile.startPos.x) * progress; + const currentY = projectile.startPos.y + (projectile.targetPos.y - projectile.startPos.y) * progress; + + ctx.lineTo( + currentX * cellSize + cellSize / 2, + currentY * cellSize + cellSize / 2 + ); + + ctx.strokeStyle = '#fff'; + ctx.lineWidth = 2; + ctx.stroke(); + + // Draw projectile head + ctx.beginPath(); + ctx.arc( + currentX * cellSize + cellSize / 2, + currentY * cellSize + cellSize / 2, + 4, + 0, + Math.PI * 2 + ); + ctx.fillStyle = '#fff'; + ctx.fill(); + } + }); +} + +// Update level complete message in game.js +function handleLevelComplete() { + gameState.phase = GamePhase.TRANSITION; + + // Calculate ammo bonus + let ammoBonus = 0; + gameState.towers.forEach(tower => { + ammoBonus += tower.ammo * 2; + }); + + const message = ` + Level ${gameState.level} Complete! + Current Money: $${gameState.currency} + Ammo Bonus: +$${ammoBonus} + Level Bonus: +$10 + + Ready for Level ${gameState.level + 1}? + `; + + setTimeout(() => { + if (confirm(message)) { + startNextLevel(); + } + }, 100); +} </code></pre> + </article> + </section> + + + + +</div> + +<nav> + <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-game.html">game</a></li><li><a href="module-gameState.html">gameState</a></li><li><a href="module-mechanics.html">mechanics</a></li><li><a href="module-path.html">path</a></li><li><a href="module-renderer.html">renderer</a></li><li><a href="module-uiHandlers.html">uiHandlers</a></li></ul> +</nav> + +<br class="clear"> + +<footer> + Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.3</a> on Mon Feb 17 2025 09:19:19 GMT-0500 (Eastern Standard Time) +</footer> + +<script> prettyPrint(); </script> +<script src="scripts/linenumber.js"> </script> +</body> +</html> |