shield and parachute need to be purchased. some fx added
This commit is contained in:
1 parent
63ddd0176f
commit
ba794804e3
4 files changed
+181
-58
No files matched your search
+4
-3
@@ -24,7 +24,6 @@ export const PLAYER_COLORS = [
|
|||||||
|
|
||||||
export const PLAYER_STARTING_WEAPONS = [
|
export const PLAYER_STARTING_WEAPONS = [
|
||||||
{type: 'babyMissile', ammo:Infinity},
|
{type: 'babyMissile', ammo:Infinity},
|
||||||
{type: 'tracer', ammo:Infinity},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
export const WEAPON_TYPES = {
|
export const WEAPON_TYPES = {
|
||||||
@@ -93,7 +92,7 @@ export const PLAYER_POWER_TICK_SOUND_INTERVAL = 2;
|
|||||||
|
|
||||||
export const PROJECTILE_POWER_REDUCTION_FACTOR = 10;
|
export const PROJECTILE_POWER_REDUCTION_FACTOR = 10;
|
||||||
export const PROJECTILE_WIND_REDUCTION_FACTOR = 10;
|
export const PROJECTILE_WIND_REDUCTION_FACTOR = 10;
|
||||||
export const PROJECTILE_ITERATIONS_PER_FRAME = 30;
|
export const PROJECTILE_ITERATIONS_PER_FRAME = 20;
|
||||||
export const PROJECTILE_ITERATION_PROGRESS = 0.01;
|
export const PROJECTILE_ITERATION_PROGRESS = 0.01;
|
||||||
export const PROJECTILE_MIN_SOUND_FREQUENCY = 220;
|
export const PROJECTILE_MIN_SOUND_FREQUENCY = 220;
|
||||||
export const PROJECTILE_MAX_SOUND_FREQUENCY = 2200;
|
export const PROJECTILE_MAX_SOUND_FREQUENCY = 2200;
|
||||||
@@ -137,6 +136,7 @@ export const SCORE_FOR_WIN = 100;
|
|||||||
|
|
||||||
export const MARKET_ITEMS = {
|
export const MARKET_ITEMS = {
|
||||||
babyMissile: { price: 0, ammo: Infinity },
|
babyMissile: { price: 0, ammo: Infinity },
|
||||||
|
tracer: { price: 5, ammo: 30 },
|
||||||
missile: { price: 15, ammo: 5 },
|
missile: { price: 15, ammo: 5 },
|
||||||
babyNuke: { price: 25, ammo: 3 },
|
babyNuke: { price: 25, ammo: 3 },
|
||||||
nuke: { price: 50, ammo: 1 },
|
nuke: { price: 50, ammo: 1 },
|
||||||
@@ -155,5 +155,6 @@ export const MARKET_ITEMS = {
|
|||||||
smallDigBomb: { price: 10, ammo: 5 },
|
smallDigBomb: { price: 10, ammo: 5 },
|
||||||
digBomb: { price: 20, ammo: 3 },
|
digBomb: { price: 20, ammo: 3 },
|
||||||
largeDigBomb: { price: 35, ammo: 1 },
|
largeDigBomb: { price: 35, ammo: 1 },
|
||||||
parachute: { price: 30, ammo: 1 },
|
parachute: { price: 20, ammo: 5 },
|
||||||
|
shield: { price: 50, ammo: 1 },
|
||||||
};
|
};
|
||||||
+98
-25
@@ -36,6 +36,8 @@ let totalRounds = 1;
|
|||||||
let selectedPlayers = 6;
|
let selectedPlayers = 6;
|
||||||
let selectedTerrain = null;
|
let selectedTerrain = null;
|
||||||
let menuState = null;
|
let menuState = null;
|
||||||
|
let tracerMode = true;
|
||||||
|
export {tracerMode};
|
||||||
|
|
||||||
// Music
|
// Music
|
||||||
// const music = createAudioLoop('assets/battle.mp3');
|
// const music = createAudioLoop('assets/battle.mp3');
|
||||||
@@ -79,21 +81,22 @@ function initNewGame() {
|
|||||||
napalmEmitter = null;
|
napalmEmitter = null;
|
||||||
menuState = {
|
menuState = {
|
||||||
selected: 0,
|
selected: 0,
|
||||||
values: [1, 0, 0],
|
values: [1, 0, 0, 0],
|
||||||
};
|
};
|
||||||
state = 'start-menu';
|
state = 'start-menu';
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStartMenu() {
|
function updateStartMenu() {
|
||||||
const options = ['Players', 'Rounds', 'Terrain'];
|
const options = ['Players', 'Rounds', 'Terrain', 'Tracer'];
|
||||||
const playerCounts = [2, 3, 4, 5, 6];
|
const playerCounts = [2, 3, 4, 5, 6];
|
||||||
const roundCounts = [1, 3, 5, 10];
|
const roundCounts = [1, 3, 5, 10];
|
||||||
const terrains = ['Random', 'Mountain', 'Sand'];
|
const terrains = ['Random', 'Mountain', 'Sand'];
|
||||||
|
const tracerOptions = ['On', 'Off'];
|
||||||
|
|
||||||
if (!menuState) {
|
if (!menuState) {
|
||||||
menuState = {
|
menuState = {
|
||||||
selected: 0,
|
selected: 0,
|
||||||
values: [selectedPlayers, totalRounds, selectedTerrain || 0],
|
values: [selectedPlayers, totalRounds, selectedTerrain || 0, tracerMode ? 0 : 1],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +121,8 @@ function updateStartMenu() {
|
|||||||
menuState.values[1] = (menuState.values[1] - 1 + roundCounts.length) % roundCounts.length;
|
menuState.values[1] = (menuState.values[1] - 1 + roundCounts.length) % roundCounts.length;
|
||||||
} else if (opt === 2) {
|
} else if (opt === 2) {
|
||||||
menuState.values[2] = (menuState.values[2] - 1 + terrains.length) % terrains.length;
|
menuState.values[2] = (menuState.values[2] - 1 + terrains.length) % terrains.length;
|
||||||
|
} else if (opt === 3) {
|
||||||
|
menuState.values[3] = menuState.values[3] === 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
playTickSound();
|
playTickSound();
|
||||||
}
|
}
|
||||||
@@ -131,6 +136,8 @@ function updateStartMenu() {
|
|||||||
menuState.values[1] = (menuState.values[1] + 1) % roundCounts.length;
|
menuState.values[1] = (menuState.values[1] + 1) % roundCounts.length;
|
||||||
} else if (opt === 2) {
|
} else if (opt === 2) {
|
||||||
menuState.values[2] = (menuState.values[2] + 1) % terrains.length;
|
menuState.values[2] = (menuState.values[2] + 1) % terrains.length;
|
||||||
|
} else if (opt === 3) {
|
||||||
|
menuState.values[3] = menuState.values[3] === 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
playTickSound();
|
playTickSound();
|
||||||
}
|
}
|
||||||
@@ -140,6 +147,7 @@ function updateStartMenu() {
|
|||||||
selectedPlayers = playerCounts[menuState.values[0]];
|
selectedPlayers = playerCounts[menuState.values[0]];
|
||||||
totalRounds = roundCounts[menuState.values[1]];
|
totalRounds = roundCounts[menuState.values[1]];
|
||||||
selectedTerrain = menuState.values[2] === 0 ? null : terrains[menuState.values[2]].toLowerCase();
|
selectedTerrain = menuState.values[2] === 0 ? null : terrains[menuState.values[2]].toLowerCase();
|
||||||
|
tracerMode = menuState.values[3] === 0;
|
||||||
menuState = null;
|
menuState = null;
|
||||||
score = STARTING_SCORE;
|
score = STARTING_SCORE;
|
||||||
initAllPlayerStats();
|
initAllPlayerStats();
|
||||||
@@ -159,8 +167,12 @@ function initAllPlayerStats() {
|
|||||||
x:0, y:0, a:0,
|
x:0, y:0, a:0,
|
||||||
c: color, cb: borderColor,
|
c: color, cb: borderColor,
|
||||||
p: PLAYER_INITIAL_POWER,
|
p: PLAYER_INITIAL_POWER,
|
||||||
tools: [{type: 'parachute', ammo: 0}],
|
tools: [],
|
||||||
weapons: PLAYER_STARTING_WEAPONS.map(x => ({...x})),
|
weapons: (() => {
|
||||||
|
const w = PLAYER_STARTING_WEAPONS.map(x => ({...x}));
|
||||||
|
if (tracerMode) w.push({type: 'tracer', ammo: Infinity});
|
||||||
|
return w;
|
||||||
|
})(),
|
||||||
currentWeapon: 0,
|
currentWeapon: 0,
|
||||||
energy: PLAYER_MAX_ENERGY,
|
energy: PLAYER_MAX_ENERGY,
|
||||||
shield: null,
|
shield: null,
|
||||||
@@ -178,7 +190,7 @@ function initAllPlayerStats() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateMarket() {
|
function updateMarket() {
|
||||||
const marketItems = Object.keys(MARKET_ITEMS);
|
const marketItems = Object.keys(MARKET_ITEMS).filter(item => !(tracerMode && item === 'tracer'));
|
||||||
|
|
||||||
if (!menuState) {
|
if (!menuState) {
|
||||||
menuState = {
|
menuState = {
|
||||||
@@ -211,7 +223,20 @@ function updateMarket() {
|
|||||||
const item = marketItems[menuState.selected];
|
const item = marketItems[menuState.selected];
|
||||||
const itemData = MARKET_ITEMS[item];
|
const itemData = MARKET_ITEMS[item];
|
||||||
const humanPlayer = players.find(p => !p.ai);
|
const humanPlayer = players.find(p => !p.ai);
|
||||||
if (humanPlayer && score >= itemData.price && item !== 'babyMissile' && WEAPON_TYPES[item]) {
|
if (!humanPlayer || score < itemData.price) return;
|
||||||
|
if (item === 'babyMissile') return;
|
||||||
|
if (item === 'parachute') {
|
||||||
|
score -= itemData.price;
|
||||||
|
let t = humanPlayer.tools.find(x => x.type === 'parachute');
|
||||||
|
if (t) t.ammo += itemData.ammo;
|
||||||
|
else humanPlayer.tools.push({type: 'parachute', ammo: itemData.ammo});
|
||||||
|
playTickSound();
|
||||||
|
} else if (item === 'shield') {
|
||||||
|
if (humanPlayer.shield) return;
|
||||||
|
score -= itemData.price;
|
||||||
|
humanPlayer.shield = {type:'shield', energy:SHIELD_TYPES.shield.energy};
|
||||||
|
playTickSound();
|
||||||
|
} else if (WEAPON_TYPES[item]) {
|
||||||
score -= itemData.price;
|
score -= itemData.price;
|
||||||
let existingWeapon = humanPlayer.weapons.find(w => w.type === item);
|
let existingWeapon = humanPlayer.weapons.find(w => w.type === item);
|
||||||
if (existingWeapon) {
|
if (existingWeapon) {
|
||||||
@@ -228,7 +253,24 @@ function updateMarket() {
|
|||||||
const item = marketItems[menuState.selected];
|
const item = marketItems[menuState.selected];
|
||||||
const itemData = MARKET_ITEMS[item];
|
const itemData = MARKET_ITEMS[item];
|
||||||
const humanPlayer = players.find(p => !p.ai);
|
const humanPlayer = players.find(p => !p.ai);
|
||||||
if (humanPlayer && item !== 'babyMissile' && item !== 'tracer') {
|
if (!humanPlayer) return;
|
||||||
|
if (item === 'babyMissile') return;
|
||||||
|
if (item === 'parachute') {
|
||||||
|
const t = humanPlayer.tools.find(x => x.type === 'parachute');
|
||||||
|
if (t && t.ammo > 0) {
|
||||||
|
const refund = itemData.price / itemData.ammo;
|
||||||
|
score += refund;
|
||||||
|
t.ammo--;
|
||||||
|
if (t.ammo <= 0) humanPlayer.tools = humanPlayer.tools.filter(x => x.type !== 'parachute');
|
||||||
|
playTickSound();
|
||||||
|
}
|
||||||
|
} else if (item === 'shield') {
|
||||||
|
if (humanPlayer.shield) {
|
||||||
|
score += itemData.price;
|
||||||
|
humanPlayer.shield = null;
|
||||||
|
playTickSound();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
const weapon = humanPlayer.weapons.find(w => w.type === item);
|
const weapon = humanPlayer.weapons.find(w => w.type === item);
|
||||||
if (weapon && weapon.ammo > 0) {
|
if (weapon && weapon.ammo > 0) {
|
||||||
const refund = itemData.price / itemData.ammo;
|
const refund = itemData.price / itemData.ammo;
|
||||||
@@ -258,18 +300,26 @@ function updateMarket() {
|
|||||||
|
|
||||||
function aiBuy(player) {
|
function aiBuy(player) {
|
||||||
player.score = STARTING_SCORE;
|
player.score = STARTING_SCORE;
|
||||||
const marketItems = Object.keys(MARKET_ITEMS).filter(x => x !== 'babyMissile' && x !== 'tracer');
|
const marketItems = Object.keys(MARKET_ITEMS).filter(x => x !== 'babyMissile' && !(tracerMode && x === 'tracer') && (WEAPON_TYPES[x] || x === 'parachute' || x === 'shield'));
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
while (player.score > 0 && attempts < 20) {
|
while (player.score > 0 && attempts < 20) {
|
||||||
const item = sample(marketItems);
|
const item = sample(marketItems);
|
||||||
const itemData = MARKET_ITEMS[item];
|
const itemData = MARKET_ITEMS[item];
|
||||||
if (player.score >= itemData.price) {
|
if (player.score >= itemData.price) {
|
||||||
player.score -= itemData.price;
|
player.score -= itemData.price;
|
||||||
let existingWeapon = player.weapons.find(w => w.type === item);
|
if (item === 'parachute') {
|
||||||
if (existingWeapon) {
|
let t = player.tools.find(x => x.type === 'parachute');
|
||||||
existingWeapon.ammo += itemData.ammo;
|
if (t) t.ammo += itemData.ammo;
|
||||||
|
else player.tools.push({type: 'parachute', ammo: itemData.ammo});
|
||||||
|
} else if (item === 'shield') {
|
||||||
|
if (!player.shield) player.shield = {type:'shield', energy:SHIELD_TYPES.shield.energy};
|
||||||
} else {
|
} else {
|
||||||
player.weapons.push({type: item, ammo: itemData.ammo});
|
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++;
|
attempts++;
|
||||||
@@ -290,7 +340,7 @@ function initPlayers() {
|
|||||||
player.p = PLAYER_INITIAL_POWER;
|
player.p = PLAYER_INITIAL_POWER;
|
||||||
player.currentWeapon = 0;
|
player.currentWeapon = 0;
|
||||||
player.energy = PLAYER_MAX_ENERGY;
|
player.energy = PLAYER_MAX_ENERGY;
|
||||||
player.shield = {type:'springShield', energy:SHIELD_TYPES.springShield.energy};
|
if (player.shield) player.shield.energy = SHIELD_TYPES[player.shield.type].energy;
|
||||||
player.parachute = null;
|
player.parachute = null;
|
||||||
player.fallHeight = 0;
|
player.fallHeight = 0;
|
||||||
player.kills = 0;
|
player.kills = 0;
|
||||||
@@ -309,11 +359,15 @@ function initPlayers() {
|
|||||||
x:0, y:0, a:0,
|
x:0, y:0, a:0,
|
||||||
c: color, cb: borderColor,
|
c: color, cb: borderColor,
|
||||||
p: PLAYER_INITIAL_POWER,
|
p: PLAYER_INITIAL_POWER,
|
||||||
tools: [{type: 'parachute', ammo: 0}],
|
tools: [],
|
||||||
weapons: PLAYER_STARTING_WEAPONS.map(x => ({...x})),
|
weapons: (() => {
|
||||||
|
const w = PLAYER_STARTING_WEAPONS.map(x => ({...x}));
|
||||||
|
if (tracerMode) w.push({type: 'tracer', ammo: Infinity});
|
||||||
|
return w;
|
||||||
|
})(),
|
||||||
currentWeapon: 0,
|
currentWeapon: 0,
|
||||||
energy: PLAYER_MAX_ENERGY,
|
energy: PLAYER_MAX_ENERGY,
|
||||||
shield: {type:'springShield', energy:SHIELD_TYPES.springShield.energy},
|
shield: null,
|
||||||
ai: i !== 0 ? sample(Object.keys(AI_TYPES)) : undefined,
|
ai: i !== 0 ? sample(Object.keys(AI_TYPES)) : undefined,
|
||||||
parachute: null,
|
parachute: null,
|
||||||
fallHeight: 0,
|
fallHeight: 0,
|
||||||
@@ -467,7 +521,7 @@ function update() {
|
|||||||
EXPLOSION_SHAKE_REDUCTION_FACTOR
|
EXPLOSION_SHAKE_REDUCTION_FACTOR
|
||||||
);
|
);
|
||||||
|
|
||||||
if (explosionType.update(explosion)) continue;
|
if (explosionType.update(explosion, dt)) continue;
|
||||||
screenShake = 0;
|
screenShake = 0;
|
||||||
explosionType.clip(explosion, terrain);
|
explosionType.clip(explosion, terrain);
|
||||||
explosionType.stop(explosion);
|
explosionType.stop(explosion);
|
||||||
@@ -1003,7 +1057,17 @@ function fadeTrajectories() {
|
|||||||
function drawProjectile() {
|
function drawProjectile() {
|
||||||
if (!projectiles.length) return;
|
if (!projectiles.length) return;
|
||||||
for (let projectile of projectiles) {
|
for (let projectile of projectiles) {
|
||||||
plot(foreground, clamp(0, projectile.x, W-1), clamp(0, projectile.y, H-1), 'white');
|
const color = projectile.player ? projectile.player.c : 'white';
|
||||||
|
if (!tracerMode && projectile.weapon?.type !== 'tracer') {
|
||||||
|
for (let i=0; i < (projectile.trail?.length || 0); i++) {
|
||||||
|
const t = projectile.trail[i];
|
||||||
|
const alpha = (i + 1) / (projectile.trail.length + 1);
|
||||||
|
foreground.globalAlpha = alpha * 0.5;
|
||||||
|
plot(foreground, clamp(0, Math.round(t.x), W-1), clamp(0, Math.round(t.y), H-1), color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreground.globalAlpha = 1;
|
||||||
|
plot(foreground, clamp(0, Math.round(projectile.x), W-1), clamp(0, Math.round(projectile.y), H-1), 'white');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1030,13 +1094,15 @@ function drawStartMenuPanel() {
|
|||||||
const playerCounts = [2, 3, 4, 5, 6];
|
const playerCounts = [2, 3, 4, 5, 6];
|
||||||
const roundCounts = [1, 3, 5, 10];
|
const roundCounts = [1, 3, 5, 10];
|
||||||
const terrains = ['Random', 'Mountain', 'Sand'];
|
const terrains = ['Random', 'Mountain', 'Sand'];
|
||||||
|
const tracerOptions = ['On', 'Off'];
|
||||||
|
|
||||||
const y = 100;
|
const y = 100;
|
||||||
const labels = ['Players', 'Rounds', 'Terrain'];
|
const labels = ['Players', 'Rounds', 'Terrain', 'Tracer'];
|
||||||
const values = [
|
const values = [
|
||||||
playerCounts[menuState.values[0]],
|
playerCounts[menuState.values[0]],
|
||||||
roundCounts[menuState.values[1]],
|
roundCounts[menuState.values[1]],
|
||||||
terrains[menuState.values[2]],
|
terrains[menuState.values[2]],
|
||||||
|
tracerOptions[menuState.values[3]],
|
||||||
];
|
];
|
||||||
|
|
||||||
for (let i=0; i<labels.length; i++) {
|
for (let i=0; i<labels.length; i++) {
|
||||||
@@ -1057,7 +1123,7 @@ function drawMarketPanel() {
|
|||||||
const humanPlayer = players.find(p => !p.ai);
|
const humanPlayer = players.find(p => !p.ai);
|
||||||
drawPanelText(framebuffer, `Score: ${score}`, 160, 100, 'yellow');
|
drawPanelText(framebuffer, `Score: ${score}`, 160, 100, 'yellow');
|
||||||
|
|
||||||
const marketItems = Object.keys(MARKET_ITEMS);
|
const marketItems = Object.keys(MARKET_ITEMS).filter(item => !(tracerMode && item === 'tracer'));
|
||||||
const y = 120;
|
const y = 120;
|
||||||
const maxVisible = 8;
|
const maxVisible = 8;
|
||||||
|
|
||||||
@@ -1073,11 +1139,18 @@ function drawMarketPanel() {
|
|||||||
|
|
||||||
let ammoText = '';
|
let ammoText = '';
|
||||||
if (humanPlayer) {
|
if (humanPlayer) {
|
||||||
const weapon = humanPlayer.weapons.find(w => w.type === item);
|
if (item === 'parachute') {
|
||||||
if (weapon) {
|
const t = humanPlayer.tools.find(x => x.type === 'parachute');
|
||||||
ammoText = weapon.type === 'babyMissile' || weapon.type === 'tracer' ? '(owned)' : `(x${weapon.ammo})`;
|
ammoText = t ? `(x${t.ammo})` : '(none)';
|
||||||
|
} else if (item === 'shield') {
|
||||||
|
ammoText = humanPlayer.shield ? '(owned)' : '(none)';
|
||||||
} else {
|
} else {
|
||||||
ammoText = '(none)';
|
const weapon = humanPlayer.weapons.find(w => w.type === item);
|
||||||
|
if (weapon) {
|
||||||
|
ammoText = weapon.type === 'babyMissile' ? '(owned)' : `(x${weapon.ammo})`;
|
||||||
|
} else {
|
||||||
|
ammoText = '(none)';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+62
-20
@@ -1,7 +1,7 @@
|
|||||||
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=6';
|
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, PLAYER_TANK_Y_FOOTPRINT} from './constants.js?v=6';
|
||||||
import {checkLineWith, drawLineVirtual} from './gfx.js?v=6';
|
import {checkLineWith, drawLineVirtual} from './gfx.js?v=6';
|
||||||
import {createParticles, isTank, isTankShield, spawnNapalm} from './main.js?v=6';
|
import {createParticles, isTank, isTankShield, spawnNapalm, tracerMode} from './main.js?v=6';
|
||||||
import {deg2rad, parable} from './math.js?v=6';
|
import {deg2rad, parable, rad2deg, wrap} from './math.js?v=6';
|
||||||
import {audio, createOsc} from './sound.js?v=6';
|
import {audio, createOsc} from './sound.js?v=6';
|
||||||
import {isTerrain, landHeight} from './terrain.js?v=6';
|
import {isTerrain, landHeight} from './terrain.js?v=6';
|
||||||
import {EXPLOSION_TYPES} from './weapons.js?v=6';
|
import {EXPLOSION_TYPES} from './weapons.js?v=6';
|
||||||
@@ -18,6 +18,7 @@ export const PROJECTILE_TYPES = {
|
|||||||
player, weapon,
|
player, weapon,
|
||||||
x:ox, y:oy, ox, oy, a, p,
|
x:ox, y:oy, ox, oy, a, p,
|
||||||
t: 0, osc, wind,
|
t: 0, osc, wind,
|
||||||
|
trail: [],
|
||||||
}];
|
}];
|
||||||
},
|
},
|
||||||
stop(projectile) {
|
stop(projectile) {
|
||||||
@@ -56,8 +57,23 @@ export const PROJECTILE_TYPES = {
|
|||||||
exploded = true;
|
exploded = true;
|
||||||
break;
|
break;
|
||||||
} else if (shieldHit.shieldType.projectileEffect === 'spring') {
|
} else if (shieldHit.shieldType.projectileEffect === 'spring') {
|
||||||
|
const cx = shieldHit.player.x;
|
||||||
|
const cy = shieldHit.player.y + PLAYER_TANK_Y_FOOTPRINT;
|
||||||
|
const nx = x - cx;
|
||||||
|
const ny = y - cy;
|
||||||
|
const len = Math.sqrt(nx * nx + ny * ny);
|
||||||
|
const ux = nx / len;
|
||||||
|
const uy = ny / len;
|
||||||
|
const rad = deg2rad(projectile.a);
|
||||||
|
const vx = -Math.cos(rad);
|
||||||
|
const vy = -Math.sin(rad);
|
||||||
|
const dot = vx * ux + vy * uy;
|
||||||
|
const rx = vx - 2 * dot * ux;
|
||||||
|
const ry = vy - 2 * dot * uy;
|
||||||
|
const theta = Math.atan2(ry, rx);
|
||||||
|
projectile.a = wrap(0, Math.round(180 - rad2deg(theta)), 180);
|
||||||
projectile.ox = projectile.x;
|
projectile.ox = projectile.x;
|
||||||
projectile.oy = projectile.y -1;
|
projectile.oy = projectile.y - 1;
|
||||||
projectile.t = 0;
|
projectile.t = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -82,15 +98,20 @@ export const PROJECTILE_TYPES = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let trajectory = drawLineVirtual(
|
projectile.trail.push({x: prevProjectile.x, y: prevProjectile.y});
|
||||||
prevProjectile.x, prevProjectile.y,
|
if (projectile.trail.length > 10) projectile.trail.shift();
|
||||||
projectile.x, projectile.y, player.c,
|
|
||||||
);
|
|
||||||
|
|
||||||
trajectory
|
if (tracerMode || weapon.type === 'tracer') {
|
||||||
.slice(0, trajectory.length-1) // Cut last pixel to prevent overlap
|
let trajectory = drawLineVirtual(
|
||||||
.map(x => ({...x, a:255})) // Add alpha to all lines
|
prevProjectile.x, prevProjectile.y,
|
||||||
.forEach(x => trajectories.push(x));
|
projectile.x, projectile.y, player.c,
|
||||||
|
);
|
||||||
|
|
||||||
|
trajectory
|
||||||
|
.slice(0, trajectory.length-1)
|
||||||
|
.map(x => ({...x, a:255}))
|
||||||
|
.forEach(x => trajectories.push(x));
|
||||||
|
}
|
||||||
|
|
||||||
return !exploded;
|
return !exploded;
|
||||||
},
|
},
|
||||||
@@ -108,6 +129,7 @@ export const PROJECTILE_TYPES = {
|
|||||||
t: 0, osc, wind,
|
t: 0, osc, wind,
|
||||||
state: 'flying',
|
state: 'flying',
|
||||||
d: 0,
|
d: 0,
|
||||||
|
trail: [],
|
||||||
}];
|
}];
|
||||||
},
|
},
|
||||||
stop(projectile) {
|
stop(projectile) {
|
||||||
@@ -160,6 +182,21 @@ export const PROJECTILE_TYPES = {
|
|||||||
finished = true;
|
finished = true;
|
||||||
break;
|
break;
|
||||||
} else if (shieldHit.shieldType.projectileEffect === 'spring') {
|
} else if (shieldHit.shieldType.projectileEffect === 'spring') {
|
||||||
|
const cx = shieldHit.player.x;
|
||||||
|
const cy = shieldHit.player.y + PLAYER_TANK_Y_FOOTPRINT;
|
||||||
|
const nx = x - cx;
|
||||||
|
const ny = y - cy;
|
||||||
|
const len = Math.sqrt(nx * nx + ny * ny);
|
||||||
|
const ux = nx / len;
|
||||||
|
const uy = ny / len;
|
||||||
|
const rad = deg2rad(projectile.a);
|
||||||
|
const vx = -Math.cos(rad);
|
||||||
|
const vy = -Math.sin(rad);
|
||||||
|
const dot = vx * ux + vy * uy;
|
||||||
|
const rx = vx - 2 * dot * ux;
|
||||||
|
const ry = vy - 2 * dot * uy;
|
||||||
|
const theta = Math.atan2(ry, rx);
|
||||||
|
projectile.a = wrap(0, Math.round(180 - rad2deg(theta)), 180);
|
||||||
projectile.ox = projectile.x;
|
projectile.ox = projectile.x;
|
||||||
projectile.oy = projectile.y - 10;
|
projectile.oy = projectile.y - 10;
|
||||||
projectile.t = 0;
|
projectile.t = 0;
|
||||||
@@ -221,15 +258,20 @@ export const PROJECTILE_TYPES = {
|
|||||||
);
|
);
|
||||||
projectile.osc.frequency.setValueAtTime(f, audio.currentTime);
|
projectile.osc.frequency.setValueAtTime(f, audio.currentTime);
|
||||||
|
|
||||||
let trajectory = drawLineVirtual(
|
projectile.trail.push({x: prevProjectile.x, y: prevProjectile.y});
|
||||||
prevProjectile.x, prevProjectile.y,
|
if (projectile.trail.length > 10) projectile.trail.shift();
|
||||||
projectile.x, projectile.y, player.c,
|
|
||||||
);
|
|
||||||
|
|
||||||
trajectory
|
if (tracerMode || weapon.type === 'tracer') {
|
||||||
.slice(0, trajectory.length-1) // Cut last pixel to prevent overlap
|
let trajectory = drawLineVirtual(
|
||||||
.map(x => ({...x, a:255})) // Add alpha to all lines
|
prevProjectile.x, prevProjectile.y,
|
||||||
.forEach(x => trajectories.push(x));
|
projectile.x, projectile.y, player.c,
|
||||||
|
);
|
||||||
|
|
||||||
|
trajectory
|
||||||
|
.slice(0, trajectory.length-1)
|
||||||
|
.map(x => ({...x, a:255}))
|
||||||
|
.forEach(x => trajectories.push(x));
|
||||||
|
}
|
||||||
|
|
||||||
return !finished;
|
return !finished;
|
||||||
},
|
},
|
||||||
|
|||||||
+17
-10
@@ -5,8 +5,15 @@ import {audio, createOsc} from './sound.js?v=6';
|
|||||||
import {clipTerrain} from './terrain.js?v=6';
|
import {clipTerrain} from './terrain.js?v=6';
|
||||||
|
|
||||||
export function drawExplosion(ctx, x, y, r) {
|
export function drawExplosion(ctx, x, y, r) {
|
||||||
const color = 255 - (16 * (r % 16));
|
const step = Math.max(1, Math.floor(r / 12));
|
||||||
drawCircle(ctx, x, y, r, `rgb(${color}, 0, 0)`);
|
for (let i = r; i > 0; i -= step) {
|
||||||
|
const t = 1 - (i / r);
|
||||||
|
const base = 60 + Math.floor(t * 195);
|
||||||
|
const flicker = Math.floor(Math.sin(r * 0.3 + i * 0.7) * 35);
|
||||||
|
const red = clamp(0, base + flicker, 255);
|
||||||
|
const green = clamp(0, Math.floor(flicker * 0.3 + t * 25), 50);
|
||||||
|
drawCircle(ctx, x, y, i, `rgb(${red}, ${green}, 0)`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function drawDirt(ctx, x, y, r, c) {
|
export function drawDirt(ctx, x, y, r, c) {
|
||||||
@@ -31,8 +38,8 @@ export const EXPLOSION_TYPES = {
|
|||||||
osc.start();
|
osc.start();
|
||||||
return {type:'blast', x, y, r, cr:0, osc};
|
return {type:'blast', x, y, r, cr:0, osc};
|
||||||
},
|
},
|
||||||
update(explosion) {
|
update(explosion, dt) {
|
||||||
return ++explosion.cr < explosion.r;
|
return (explosion.cr += dt * 30) < explosion.r;
|
||||||
},
|
},
|
||||||
draw(explosion, foreground) {
|
draw(explosion, foreground) {
|
||||||
const {x, y, cr, osc} = explosion;
|
const {x, y, cr, osc} = explosion;
|
||||||
@@ -70,8 +77,8 @@ export const EXPLOSION_TYPES = {
|
|||||||
const {osc} = explosion;
|
const {osc} = explosion;
|
||||||
osc.stop();
|
osc.stop();
|
||||||
},
|
},
|
||||||
update(explosion) {
|
update(explosion, dt) {
|
||||||
return ++explosion.cr < explosion.r;
|
return (explosion.cr += dt * 30) < explosion.r;
|
||||||
},
|
},
|
||||||
draw(explosion, foreground, terrain) {
|
draw(explosion, foreground, terrain) {
|
||||||
const {x, y, cr, osc} = explosion;
|
const {x, y, cr, osc} = explosion;
|
||||||
@@ -97,8 +104,8 @@ export const EXPLOSION_TYPES = {
|
|||||||
const {osc} = explosion;
|
const {osc} = explosion;
|
||||||
osc.stop();
|
osc.stop();
|
||||||
},
|
},
|
||||||
update(explosion) {
|
update(explosion, dt) {
|
||||||
return ++explosion.cr < explosion.r;
|
return (explosion.cr += dt * 30) < explosion.r;
|
||||||
},
|
},
|
||||||
draw(explosion, foreground, terrain) {
|
draw(explosion, foreground, terrain) {
|
||||||
const {x, y, cr, osc} = explosion;
|
const {x, y, cr, osc} = explosion;
|
||||||
@@ -125,8 +132,8 @@ export const EXPLOSION_TYPES = {
|
|||||||
const {osc} = explosion;
|
const {osc} = explosion;
|
||||||
osc.stop();
|
osc.stop();
|
||||||
},
|
},
|
||||||
update(explosion) {
|
update(explosion, dt) {
|
||||||
return ++explosion.cr < Math.min(explosion.r, explosion.y);
|
return (explosion.cr += dt * 30) < Math.min(explosion.r, explosion.y);
|
||||||
},
|
},
|
||||||
draw(explosion, foreground, terrain) {
|
draw(explosion, foreground, terrain) {
|
||||||
const {x, y, cr, osc, pattern} = explosion;
|
const {x, y, cr, osc, pattern} = explosion;
|
||||||
|
|||||||
Reference in new issue
Block a user