diff --git a/index.html b/index.html index ba161ae..71c6ecd 100644 --- a/index.html +++ b/index.html @@ -14,6 +14,6 @@ - + diff --git a/src/ai.js b/src/ai.js index 78091f9..93b8261 100644 --- a/src/ai.js +++ b/src/ai.js @@ -1,5 +1,5 @@ -import {PLAYER_MAX_ENERGY, PLAYER_INITIAL_POWER, PLAYER_ENERGY_POWER_MULTIPLIER} from './constants.js'; -import {randomInt} from './math.js'; +import {PLAYER_MAX_ENERGY, PLAYER_INITIAL_POWER, PLAYER_ENERGY_POWER_MULTIPLIER} from './constants.js?v=4'; +import {randomInt} from './math.js?v=4'; export const AI_TYPES = { diff --git a/src/constants.js b/src/constants.js index 501fd5e..54e5f14 100644 --- a/src/constants.js +++ b/src/constants.js @@ -24,22 +24,6 @@ export const PLAYER_COLORS = [ export const PLAYER_STARTING_WEAPONS = [ {type: 'babyMissile', ammo:Infinity}, - {type: 'missile', ammo:5}, - {type: 'babyNuke', ammo:3}, - {type: 'nuke', ammo:1}, - {type: 'babyRoller', ammo:5}, - {type: 'roller', ammo:3}, - {type: 'superRoller', ammo:1}, - {type: 'mirv', ammo:3}, - {type: 'xmirv', ammo:1}, - {type: 'leapfrog', ammo:3}, - {type: 'superLeapfrog', ammo:1}, - {type: 'smallDirt', ammo:5}, - {type: 'dirt', ammo:3}, - {type: 'largeDirt', ammo:1}, - {type: 'smallDigBomb', ammo:5}, - {type: 'digBomb', ammo:3}, - {type: 'largeDigBomb', ammo:1}, {type: 'tracer', ammo:Infinity}, ] @@ -87,10 +71,6 @@ export const DEATH_SPECS = [ {type: 'dirtCone', r: H}, ]; -export const PLAYER_STARTING_TOOLS = [ - {type: 'parachute', ammo: 1}, -]; - export const MAX_WIND = 25; export const DEFAULT_KEYPRESS_DELAY = 150; @@ -135,3 +115,37 @@ export const FONT_HEIGHT = 5; export const SOUND_MUSIC_VOLUME = 0.3; export const SOUND_SFX_VOLUME = 0.1; + +export const PLAYER_STARTING_TOOLS = [ +]; + +export const PANEL_WIDTH = 400; +export const PANEL_HEIGHT = 300; +export const PANEL_FONT_SIZE = 16; +export const PANEL_BG_COLOR = '#666'; +export const PANEL_BORDER_COLOR = '#333'; + +export const STARTING_SCORE = 100; +export const SCORE_PER_KILL = 20; +export const SCORE_FOR_WIN = 100; + +export const MARKET_ITEMS = { + babyMissile: { price: 0, ammo: Infinity }, + missile: { price: 15, ammo: 5 }, + babyNuke: { price: 25, ammo: 3 }, + nuke: { price: 50, ammo: 1 }, + babyRoller: { price: 15, ammo: 5 }, + roller: { price: 25, ammo: 3 }, + superRoller: { price: 40, ammo: 1 }, + mirv: { price: 25, ammo: 3 }, + xmirv: { price: 50, ammo: 1 }, + leapfrog: { price: 25, ammo: 3 }, + superLeapfrog: { price: 40, ammo: 1 }, + smallDirt: { price: 10, ammo: 5 }, + dirt: { price: 20, ammo: 3 }, + largeDirt: { price: 35, ammo: 1 }, + smallDigBomb: { price: 10, ammo: 5 }, + digBomb: { price: 20, ammo: 3 }, + largeDigBomb: { price: 35, ammo: 1 }, + parachute: { price: 30, ammo: 1 }, +}; diff --git a/src/gfx.js b/src/gfx.js index 310ca8e..f39bbc5 100644 --- a/src/gfx.js +++ b/src/gfx.js @@ -1,6 +1,6 @@ -import {splitWords} from './utils.js'; -import {parable} from './math.js'; -import {FONT_HEIGHT, FONT_WIDTH} from './constants.js'; +import {splitWords} from './utils.js?v=4'; +import {parable} from './math.js?v=4'; +import {FONT_HEIGHT, FONT_WIDTH} from './constants.js?v=4'; export function createCanvas(width, height) { const canvas = document.createElement('canvas'); diff --git a/src/input.js b/src/input.js index 7d55106..6069349 100644 --- a/src/input.js +++ b/src/input.js @@ -1,4 +1,4 @@ -import {DEFAULT_KEYPRESS_DELAY} from './constants.js'; +import {DEFAULT_KEYPRESS_DELAY} from './constants.js?v=4'; const input = {}; let lastKeypressTime = 0; diff --git a/src/main.js b/src/main.js index dcb70ce..1c53f02 100644 --- a/src/main.js +++ b/src/main.js @@ -1,14 +1,15 @@ -import {AI_TYPES} from './ai.js'; -import {DEATH_SPECS, EXPLOSION_SHAKE_REDUCTION_FACTOR, H, MAX_EXPLOSION_SHAKE_FACTOR, MAX_WIND, PARTICLE_AMOUNT, PARTICLE_FADE_AMOUNT, PARTICLE_MAX_POWER_FACTOR, PARTICLE_MIN_LIFETIME, PARTICLE_MIN_POWER_FACTOR, PARTICLE_POWER_REDUCTION_FACTOR, PARTICLE_TIME_FACTOR, PARTICLE_WIND_REDUCTION_FACTOR, PLAYER_ANGLE_FAST_INCREMENT, PLAYER_ANGLE_INCREMENT, PLAYER_ANGLE_TICK_SOUND_INTERVAL, PLAYER_COLORS, PLAYER_ENERGY_POWER_MULTIPLIER, PLAYER_EXPLOSION_PARTICLE_POWER, PLAYER_FALL_DAMAGE_FACTOR, PLAYER_FALL_DAMAGE_HEIGHT, PLAYER_INITIAL_POWER, PLAYER_MAX_ENERGY, PLAYER_POWER_FAST_INCREMENT, PLAYER_POWER_INCREMENT, PLAYER_POWER_TICK_SOUND_INTERVAL, PLAYER_STARTING_TOOLS, PLAYER_STARTING_WEAPONS, PLAYER_TANK_BOUNDING_RADIUS, PLAYER_TANK_Y_FOOTPRINT, SHIELD_TYPES, TRAJECTORY_FADE_SPEED, TRAJECTORY_FLOAT_SPEED, W, WEAPON_TYPES, Z} from './constants.js'; -import {createCanvas, drawLine, drawRect, drawSemiCircle, drawText, loop, plot, strokeCircle} from './gfx.js'; -import {afterKeyDelay, key} from './input.js'; -import {clamp, deg2rad, distance, parable, random, randomInt, vec, wrap} from './math.js'; -import {PROJECTILE_TYPES} from './projectiles.js'; -import {generateSky} from './sky.js'; -import {playTickSound} from './sound.js'; -import {clipTerrain, closestLand, collapseTerrain, generateTerrain, isTerrain, landHeight} from './terrain.js'; -import {sample, shuffle} from './utils.js'; -import {EXPLOSION_TYPES} from './weapons.js'; +import {AI_TYPES} from './ai.js?v=4'; +import {DEATH_SPECS, EXPLOSION_SHAKE_REDUCTION_FACTOR, H, MAX_EXPLOSION_SHAKE_FACTOR, MAX_WIND, PARTICLE_AMOUNT, PARTICLE_FADE_AMOUNT, PARTICLE_MAX_POWER_FACTOR, PARTICLE_MIN_LIFETIME, PARTICLE_MIN_POWER_FACTOR, PARTICLE_POWER_REDUCTION_FACTOR, PARTICLE_TIME_FACTOR, PARTICLE_WIND_REDUCTION_FACTOR, PLAYER_ANGLE_FAST_INCREMENT, PLAYER_ANGLE_INCREMENT, PLAYER_ANGLE_TICK_SOUND_INTERVAL, PLAYER_COLORS, PLAYER_ENERGY_POWER_MULTIPLIER, PLAYER_EXPLOSION_PARTICLE_POWER, PLAYER_FALL_DAMAGE_FACTOR, PLAYER_FALL_DAMAGE_HEIGHT, PLAYER_INITIAL_POWER, PLAYER_MAX_ENERGY, PLAYER_POWER_FAST_INCREMENT, PLAYER_POWER_INCREMENT, PLAYER_POWER_TICK_SOUND_INTERVAL, PLAYER_STARTING_TOOLS, PLAYER_STARTING_WEAPONS, PLAYER_TANK_BOUNDING_RADIUS, PLAYER_TANK_Y_FOOTPRINT, SHIELD_TYPES, TRAJECTORY_FADE_SPEED, TRAJECTORY_FLOAT_SPEED, W, WEAPON_TYPES, Z, STARTING_SCORE, SCORE_PER_KILL, SCORE_FOR_WIN, MARKET_ITEMS} from './constants.js?v=4'; +import {createCanvas, drawLine, drawRect, drawSemiCircle, drawText, loop, plot, strokeCircle} from './gfx.js?v=4'; +import {afterKeyDelay, key} from './input.js?v=4'; +import {clamp, deg2rad, distance, parable, random, randomInt, vec, wrap} from './math.js?v=4'; +import {PROJECTILE_TYPES} from './projectiles.js?v=4'; +import {generateSky} from './sky.js?v=4'; +import {playTickSound} from './sound.js?v=4'; +import {clipTerrain, closestLand, collapseTerrain, generateTerrain, isTerrain, landHeight} from './terrain.js?v=4'; +import {sample, shuffle} from './utils.js?v=4'; +import {EXPLOSION_TYPES} from './weapons.js?v=4'; +import {drawPanelBg, drawPanelTitle, drawPanelText, drawPanelDivider, drawPanelMenu, getPanelBounds} from './panel.js?v=4'; let state = 'start-game'; @@ -23,6 +24,13 @@ let trajectories = []; let idle = false; let winner; +let score = 0; +let round = 0; +let totalRounds = 1; +let selectedPlayers = 6; +let selectedTerrain = null; +let menuState = null; + // Music // const music = createAudioLoop('assets/battle.mp3'); @@ -39,7 +47,6 @@ framebuffer.canvas.style.height = `${H * Z}px`; document.body.appendChild(framebuffer.canvas); function init() { - state = 'start-game'; players = []; currentPlayer = 0; projectiles = []; @@ -55,8 +62,87 @@ function init() { initPlayers(); } -function initPlayers() { - for (let i=0; i ({...x})), // FIXME: Ghetto clone - weapons: PLAYER_STARTING_WEAPONS.map(x => ({...x})), // FIXME: Ghetto clone + tools: [{type: 'parachute', ammo: 0}], + weapons: PLAYER_STARTING_WEAPONS.map(x => ({...x})), + currentWeapon: 0, + energy: PLAYER_MAX_ENERGY, + shield: null, + ai: i !== 0 ? sample(Object.keys(AI_TYPES)) : undefined, + parachute: null, + fallHeight: 0, + score: 0, + kills: 0, + deaths: 0, + shotsFired: 0, + wins: 0, + }); + } +} + +function updateMarket() { + const marketItems = Object.keys(MARKET_ITEMS); + + if (!menuState) { + menuState = { + selected: 0, + scrollOffset: 0, + }; + players.forEach(p => { + if (p.ai) aiBuy(p); + }); + } + + if (key('ArrowUp')) { + if (afterKeyDelay()) { + menuState.selected = (menuState.selected - 1 + marketItems.length) % marketItems.length; + if (menuState.selected < menuState.scrollOffset) menuState.scrollOffset = menuState.selected; + if (menuState.selected >= menuState.scrollOffset + 10) menuState.scrollOffset = menuState.selected - 9; + playTickSound(); + } + } + else if (key('ArrowDown')) { + if (afterKeyDelay()) { + menuState.selected = (menuState.selected + 1) % marketItems.length; + if (menuState.selected >= menuState.scrollOffset + 10) menuState.scrollOffset = menuState.selected - 9; + if (menuState.selected < menuState.scrollOffset) menuState.scrollOffset = menuState.selected; + playTickSound(); + } + } + else if (key(' ')) { + if (afterKeyDelay()) { + const item = marketItems[menuState.selected]; + const itemData = MARKET_ITEMS[item]; + const humanPlayer = players.find(p => !p.ai); + if (humanPlayer && score >= itemData.price && item !== 'babyMissile') { + score -= itemData.price; + let existingWeapon = humanPlayer.weapons.find(w => w.type === item); + if (existingWeapon) { + existingWeapon.ammo += itemData.ammo; + } else { + humanPlayer.weapons.push({type: item, ammo: itemData.ammo}); + } + playTickSound(); + } + } + } + else if (key('Enter')) { + if (afterKeyDelay()) { + menuState = null; + state = 'round-start'; + } + } + else { + idle = true; + } +} + +function aiBuy(player) { + player.score = STARTING_SCORE; + const marketItems = Object.keys(MARKET_ITEMS).filter(x => x !== 'babyMissile' && x !== 'tracer'); + let attempts = 0; + while (player.score > 0 && attempts < 20) { + const item = sample(marketItems); + const itemData = MARKET_ITEMS[item]; + if (player.score >= itemData.price) { + player.score -= itemData.price; + let existingWeapon = player.weapons.find(w => w.type === item); + if (existingWeapon) { + existingWeapon.ammo += itemData.ammo; + } else { + player.weapons.push({type: item, ammo: itemData.ammo}); + } + } + attempts++; + } +} + +function initPlayers() { + const playerCount = selectedPlayers; + for (let i=0; i ({...x})), + weapons: PLAYER_STARTING_WEAPONS.map(x => ({...x})), currentWeapon: 0, energy: PLAYER_MAX_ENERGY, shield: {type:'springShield', energy:SHIELD_TYPES.springShield.energy}, ai: i !== 0 ? sample(Object.keys(AI_TYPES)) : undefined, parachute: null, fallHeight: 0, + score: 0, + kills: 0, + deaths: 0, + shotsFired: 0, + wins: 0, }); } - // Randomize positions players = shuffle(players); - // Positions - for (let i=0; i W/2 ? 45 : 180-45; clipTerrain(terrain, (ctx) => drawRect(ctx, player.x-4, 0, 8, player.y, ctx.color)); @@ -99,6 +292,19 @@ function update() { updateParticles(); if (state === 'start-game') { + initNewGame(); + } + + else if (state === 'start-menu') { + updateStartMenu(); + } + + else if (state === 'market') { + updateMarket(); + } + + else if (state === 'round-start') { + round++; init(); state = 'start-turn'; } @@ -172,6 +378,7 @@ function update() { const {projectile} = WEAPON_TYPES[weapon.type]; const projectileType = PROJECTILE_TYPES[projectile.type]; weapon.ammo -= 1; + player.shotsFired++; projectileType.create(projectile, player, weapon, px, py, a, p, wind) .forEach(x => projectiles.push(x)); @@ -266,6 +473,7 @@ function update() { explosions.push(explosionType.create(explosionSpec, x, y)); createParticles(x, y, PLAYER_EXPLOSION_PARTICLE_POWER, c); dyingPlayer.dead = true; + dyingPlayer.deaths++; state = 'explosions'; } @@ -273,10 +481,11 @@ function update() { const alivePlayers = players.filter(x => !x.dead); if (alivePlayers.length === 0) { - return state = 'game-over'; + return state = 'round-end'; } else if (alivePlayers.length === 1) { winner = alivePlayers[0]; - return state = 'player-win'; + winner.wins++; + return state = 'round-end'; } for (let player of players) { @@ -296,13 +505,36 @@ function update() { state = 'start-turn'; } - else if (state === 'player-win') { - if (key('Enter')) state = 'start-game'; + else if (state === 'round-end') { + if (!menuState) { + menuState = {scoreAwarded: false}; + for (let player of players) { + player.score += player.kills * SCORE_PER_KILL; + } + if (winner) { + winner.score += SCORE_FOR_WIN; + } + menuState.scoreAwarded = true; + } + if (key('Enter')) { + if (afterKeyDelay()) { + menuState = null; + if (round < totalRounds) { + state = 'market'; + } else { + state = 'game-over'; + } + } + } idle = true; } else if (state === 'game-over') { - if (key('Enter')) state = 'start-game'; + if (key('Enter')) { + if (afterKeyDelay()) { + state = 'start-game'; + } + } idle = true; } @@ -381,7 +613,7 @@ export function isTankShield(x, y) { } function draw() { - if (idle && particles.length===0) return; + if (idle && particles.length===0 && state !== 'start-menu' && state !== 'market' && state !== 'round-end' && state !== 'game-over') return; foreground.clearRect(0, 0, W, H); drawTrajectories(); @@ -395,6 +627,16 @@ function draw() { framebuffer.drawImage(c.canvas, 0, 0); } + if (state === 'start-menu') { + drawStartMenuPanel(); + } else if (state === 'market') { + drawMarketPanel(); + } else if (state === 'round-end') { + drawRoundEndPanel(); + } else if (state === 'game-over') { + drawGameOverPanel(); + } + drawScreenShake(); } @@ -494,6 +736,113 @@ function drawParticles() { foreground.globalAlpha = 1; } +function drawStartMenuPanel() { + drawPanelBg(framebuffer); + drawPanelTitle(framebuffer, 'RAVAGED PLANET'); + + const playerCounts = [2, 3, 4, 5, 6]; + const roundCounts = [1, 3, 5, 10]; + const terrains = ['Random', 'Mountain', 'Sand']; + + const y = 100; + const labels = ['Players', 'Rounds', 'Terrain']; + const values = [ + playerCounts[menuState.values[0]], + roundCounts[menuState.values[1]], + terrains[menuState.values[2]], + ]; + + for (let i=0; i ' : ' '; + const color = isSelected ? 'yellow' : 'white'; + drawPanelText(framebuffer, `${prefix}${labels[i]}: ${values[i]}`, 160, y + i * 30, color); + } + + drawPanelDivider(framebuffer, 230); + drawPanelText(framebuffer, 'Press ENTER to start', 160, 250, 'white'); +} + +function drawMarketPanel() { + drawPanelBg(framebuffer); + drawPanelTitle(framebuffer, 'MARKET'); + + const humanPlayer = players.find(p => !p.ai); + drawPanelText(framebuffer, `Score: ${score}`, 160, 90, 'yellow'); + + const marketItems = Object.keys(MARKET_ITEMS); + const y = 110; + const maxVisible = 8; + + for (let i=0; i= marketItems.length) break; + + const item = marketItems[itemIndex]; + const itemData = MARKET_ITEMS[item]; + const isSelected = menuState && menuState.selected === itemIndex; + const prefix = isSelected ? '> ' : ' '; + const color = isSelected ? 'yellow' : 'white'; + + let ammoText = ''; + if (humanPlayer) { + const weapon = humanPlayer.weapons.find(w => w.type === item); + if (weapon) { + ammoText = weapon.type === 'babyMissile' || weapon.type === 'tracer' ? '(owned)' : `(x${weapon.ammo})`; + } else { + ammoText = '(none)'; + } + } + + const priceText = itemData.price === 0 ? 'FREE' : `$${itemData.price}`; + drawPanelText(framebuffer, `${prefix}${item}: ${priceText} ${ammoText}`, 140, y + i * 20, color); + } + + drawPanelDivider(framebuffer, 280); + drawPanelText(framebuffer, 'SPACE: Buy ENTER: Start Round', 160, 290, 'white'); +} + +function drawRoundEndPanel() { + drawPanelBg(framebuffer); + + if (winner) { + drawPanelTitle(framebuffer, `Round ${round} Complete!`); + drawPanelText(framebuffer, `Winner: ${winner.name}`, 160, 100, winner.c); + drawPanelText(framebuffer, `Kills: ${winner.kills}`, 160, 130, 'white'); + drawPanelText(framebuffer, `Round Score: ${winner.kills * SCORE_PER_KILL + SCORE_FOR_WIN}`, 160, 160, 'white'); + } else { + drawPanelTitle(framebuffer, `Round ${round} Complete!`); + drawPanelText(framebuffer, 'Nobody survived!', 160, 100, 'white'); + } + + drawPanelText(framebuffer, `Total Score: ${score}`, 160, 200, 'yellow'); + + drawPanelDivider(framebuffer, 240); + if (round < totalRounds) { + drawPanelText(framebuffer, 'Press ENTER for next round', 160, 260, 'white'); + } else { + drawPanelText(framebuffer, 'Press ENTER for final results', 160, 260, 'white'); + } +} + +function drawGameOverPanel() { + drawPanelBg(framebuffer); + drawPanelTitle(framebuffer, 'GAME OVER'); + + const y = 80; + const sortedPlayers = [...players].sort((a, b) => b.wins - a.wins || b.kills - a.kills); + + for (let i=0; i=0?'>':''}`, W-8, 8, 'white', 'right'); + drawText(foreground, `Round: ${round}/${totalRounds} Score: ${score}`, W-8, 18, 'white', 'right'); } loop(() => { diff --git a/src/panel.js b/src/panel.js new file mode 100644 index 0000000..3acd972 --- /dev/null +++ b/src/panel.js @@ -0,0 +1,54 @@ +import {PANEL_WIDTH, PANEL_HEIGHT, PANEL_FONT_SIZE, PANEL_BG_COLOR, PANEL_BORDER_COLOR, W, H, FONT_WIDTH} from './constants.js?v=4'; +import {drawRect, drawLine} from './gfx.js?v=4'; + +const PANEL_X = (W - PANEL_WIDTH) / 2; +const PANEL_Y = (H - PANEL_HEIGHT) / 2; + +export function drawPanelBg(ctx) { + ctx.fillStyle = PANEL_BG_COLOR; + ctx.fillRect(PANEL_X, PANEL_Y, PANEL_WIDTH, PANEL_HEIGHT); + + ctx.strokeStyle = PANEL_BORDER_COLOR; + ctx.lineWidth = 2; + ctx.strokeRect(PANEL_X + 1, PANEL_Y + 1, PANEL_WIDTH - 2, PANEL_HEIGHT - 2); + + ctx.strokeStyle = '#999'; + ctx.lineWidth = 1; + ctx.strokeRect(PANEL_X + 3, PANEL_Y + 3, PANEL_WIDTH - 6, PANEL_HEIGHT - 6); +} + +export function drawPanelTitle(ctx, text) { + const x = PANEL_X + PANEL_WIDTH / 2; + const y = PANEL_Y + 20; + drawPanelText(ctx, text, x, y, 'white', 'center'); +} + +export function drawPanelText(ctx, text, x, y, color = 'white', align = 'left') { + ctx.font = `${PANEL_FONT_SIZE}px text`; + ctx.textBaseline = 'top'; + ctx.textAlign = align; + ctx.fillStyle = 'black'; + ctx.fillText(text, x + 1, y + 1); + ctx.fillStyle = color; + ctx.fillText(text, x, y); +} + +export function drawPanelDivider(ctx, y) { + const x1 = PANEL_X + 20; + const x2 = PANEL_X + PANEL_WIDTH - 20; + drawLine(ctx, x1, y, x2, y, '#999'); +} + +export function drawPanelMenu(ctx, items, selectedIndex, y) { + for (let i = 0; i < items.length; i++) { + const itemY = y + i * 20; + const isSelected = i === selectedIndex; + const prefix = isSelected ? '> ' : ' '; + const color = isSelected ? 'yellow' : 'white'; + drawPanelText(ctx, prefix + items[i], PANEL_X + 40, itemY, color); + } +} + +export function getPanelBounds() { + return {x: PANEL_X, y: PANEL_Y, w: PANEL_WIDTH, h: PANEL_HEIGHT}; +} diff --git a/src/projectiles.js b/src/projectiles.js index 78fc6bd..a1a3a86 100644 --- a/src/projectiles.js +++ b/src/projectiles.js @@ -1,10 +1,10 @@ -import {H, PROJECTILE_ITERATIONS_PER_FRAME, PROJECTILE_ITERATION_PROGRESS, PROJECTILE_MAX_SOUND_FREQUENCY, PROJECTILE_MIN_SOUND_FREQUENCY, PROJECTILE_POWER_REDUCTION_FACTOR, PROJECTILE_WIND_REDUCTION_FACTOR, WEAPON_TYPES} from './constants.js'; -import {checkLineWith, drawLineVirtual} from './gfx.js'; -import {createParticles, isTank, isTankShield} from './main.js'; -import {deg2rad, parable} from './math.js'; -import {audio, createOsc} from './sound.js'; -import {isTerrain, landHeight} from './terrain.js'; -import {EXPLOSION_TYPES} from './weapons.js'; +import {H, PROJECTILE_ITERATIONS_PER_FRAME, PROJECTILE_ITERATION_PROGRESS, PROJECTILE_MAX_SOUND_FREQUENCY, PROJECTILE_MIN_SOUND_FREQUENCY, PROJECTILE_POWER_REDUCTION_FACTOR, PROJECTILE_WIND_REDUCTION_FACTOR, WEAPON_TYPES} from './constants.js?v=4'; +import {checkLineWith, drawLineVirtual} from './gfx.js?v=4'; +import {createParticles, isTank, isTankShield} from './main.js?v=4'; +import {deg2rad, parable} from './math.js?v=4'; +import {audio, createOsc} from './sound.js?v=4'; +import {isTerrain, landHeight} from './terrain.js?v=4'; +import {EXPLOSION_TYPES} from './weapons.js?v=4'; export const PROJECTILE_TYPES = { diff --git a/src/sky.js b/src/sky.js index 2ff48c3..401673b 100644 --- a/src/sky.js +++ b/src/sky.js @@ -1,7 +1,7 @@ -import {SKY_COLORS} from './constants.js'; -import {drawRect} from './gfx.js'; -import {gradient} from './math.js'; -import {sample} from './utils.js'; +import {SKY_COLORS} from './constants.js?v=4'; +import {drawRect} from './gfx.js?v=4'; +import {gradient} from './math.js?v=4'; +import {sample} from './utils.js?v=4'; export function generateSky(ctx) { const {width, height} = ctx.canvas; diff --git a/src/sound.js b/src/sound.js index cbd2a44..e7cf58e 100644 --- a/src/sound.js +++ b/src/sound.js @@ -1,4 +1,4 @@ -import {SOUND_MUSIC_VOLUME, SOUND_SFX_VOLUME} from './constants.js'; +import {SOUND_MUSIC_VOLUME, SOUND_SFX_VOLUME} from './constants.js?v=4'; // @ts-ignore export const audio = new (window.AudioContext || window.webkitAudioContext)(); diff --git a/src/terrain.js b/src/terrain.js index d9c3a75..4de5841 100644 --- a/src/terrain.js +++ b/src/terrain.js @@ -1,6 +1,6 @@ -import {clipCanvas, drawCircle, drawRect} from './gfx.js'; -import {clamp, coords2index, random, randomInt} from './math.js'; -import {sample} from './utils.js'; +import {clipCanvas, drawCircle, drawRect} from './gfx.js?v=4'; +import {clamp, coords2index, random, randomInt} from './math.js?v=4'; +import {sample} from './utils.js?v=4'; let cachedImageData; diff --git a/src/utils.js b/src/utils.js index 5b28048..fe85694 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,4 +1,4 @@ -import {randomInt} from './math.js'; +import {randomInt} from './math.js?v=4'; export function sample(arr) { return arr[randomInt(0, arr.length-1)]; diff --git a/src/weapons.js b/src/weapons.js index 99bf176..59b1b5a 100644 --- a/src/weapons.js +++ b/src/weapons.js @@ -1,8 +1,8 @@ -import {PLAYER_TANK_BOUNDING_RADIUS, PLAYER_TANK_Y_FOOTPRINT, SHIELD_TYPES} from './constants.js'; -import {drawCircle, plot} from './gfx.js'; -import {clamp, cycle, distance, randomInt} from './math.js'; -import {audio, createOsc} from './sound.js'; -import {clipTerrain} from './terrain.js'; +import {PLAYER_TANK_BOUNDING_RADIUS, PLAYER_TANK_Y_FOOTPRINT, SHIELD_TYPES} from './constants.js?v=4'; +import {drawCircle, plot} from './gfx.js?v=4'; +import {clamp, cycle, distance, randomInt} from './math.js?v=4'; +import {audio, createOsc} from './sound.js?v=4'; +import {clipTerrain} from './terrain.js?v=4'; export function drawExplosion(ctx, x, y, r) { const color = 255 - (16 * (r % 16));