XGravity/Public/ecma/Strategies/SpawnStrategies.ecma.js

153 lines
6.0 KiB
JavaScript

"use strict";
const SpawnStrategies = (function(){
const STRATEGIES = {
// 1. SOBREVUELO RASANTE CON EASE-OUT CÚBICO
overflight : {
init : (enemy, config, xgravity) => {
const base_size = config.size || 1.8;
const duration = (typeof config.duration === "number")
? config.duration
: xgravity.settings.get("enemy_overflight_duration", null, 1.4);
const initial_scale = (typeof config.initial_scale === "number")
? config.initial_scale
: xgravity.settings.get("enemy_overflight_initial_scale", null, 6.5);
const target_angle = Math.random() * Math.PI * 2;
const target_dist = 4.0 + (Math.random() * 7.0);
const target_x = Math.cos(target_angle) * target_dist;
const target_y = Math.sin(target_angle) * target_dist;
const spawn_angle = Math.random() * Math.PI * 2;
const giant_half_size = (base_size * initial_scale) / 2;
const spawn_distance = 22.0 + giant_half_size + 2.0;
const spawn_x = Math.cos(spawn_angle) * spawn_distance;
const spawn_y = Math.sin(spawn_angle) * spawn_distance;
enemy.spawn_data = {
spawn_x, spawn_y,
target_x, target_y,
duration : Math.max(0.1, duration),
initial_scale
};
enemy.x = spawn_x;
enemy.y = spawn_y;
enemy.scale = initial_scale;
enemy.alpha = 0.4;
enemy.ready = false;
const dx = target_x - spawn_x;
const dy = target_y - spawn_y;
enemy.angle = Math.atan2(dy, dx) + (Math.PI / 2);
},
update : (enemy, delta_time) => {
const data = enemy.spawn_data;
const t = Math.min(1.0, enemy.age / data.duration);
const inv_t = 1.0 - t;
const ease_progress = 1.0 - (inv_t * inv_t * inv_t);
enemy.x = data.spawn_x + ((data.target_x - data.spawn_x) * ease_progress);
enemy.y = data.spawn_y + ((data.target_y - data.spawn_y) * ease_progress);
const speed_factor = (3.0 * inv_t * inv_t) / data.duration;
enemy.vx = (data.target_x - data.spawn_x) * speed_factor;
enemy.vy = (data.target_y - data.spawn_y) * speed_factor;
enemy.scale = data.initial_scale - ((data.initial_scale - 1.0) * ease_progress);
enemy.alpha = 0.4 + (ease_progress * 0.6);
if(t >= 1.0){
enemy.scale = 1.0;
enemy.alpha = 1.0;
enemy.ready = true;
const spd = Math.sqrt(enemy.vx * enemy.vx + enemy.vy * enemy.vy);
if(spd > 0.001){
enemy.vx = (enemy.vx / spd) * 3.2;
enemy.vy = (enemy.vy / spd) * 3.2;
}
}
}
},
// 2. EMERGENCIA DESDE EL FONDO ESPACIAL (DEEP SPACE)
deep_space : {
init : (enemy, config) => {
const spawn_angle = Math.random() * Math.PI * 2;
const r = Math.random() * 15.0;
enemy.x = Math.cos(spawn_angle) * r;
enemy.y = Math.sin(spawn_angle) * r;
enemy.scale = 0.0;
enemy.alpha = 0.3;
enemy.ready = false;
enemy.scale_speed = config.scale_speed || 0.65;
enemy.angle = Math.random() * Math.PI * 2;
},
update : (enemy, delta_time) => {
enemy.scale += enemy.scale_speed * delta_time;
enemy.alpha = 0.3 + (enemy.scale * 0.7);
if(enemy.scale >= 1.0){
enemy.scale = 1.0;
enemy.alpha = 1.0;
enemy.ready = true;
}
}
},
// 3. ENTRADA PERIMETRAL EXTERIOR (100% FUERA DE PANTALLA)
perimeter : {
init : (enemy, config) => {
const base_size = config.size || 1.8;
const spawn_angle = Math.random() * Math.PI * 2;
// Calculado holgadamente para que el sprite nunca corte el borde al nacer
const spawn_distance = 22.0 + (base_size / 2) + 2.0;
enemy.x = Math.cos(spawn_angle) * spawn_distance;
enemy.y = Math.sin(spawn_angle) * spawn_distance;
// Apunta hacia el cuadrante interior visible con ligera dispersión
const target_angle = spawn_angle + Math.PI + ((Math.random() - 0.5) * 0.7);
const speed = config.initial_speed || 4.2;
enemy.vx = Math.cos(target_angle) * speed;
enemy.vy = Math.sin(target_angle) * speed;
enemy.scale = 1.0;
enemy.alpha = 1.0;
enemy.ready = false;
enemy.angle = Math.atan2(enemy.vy, enemy.vx) + (Math.PI / 2);
},
update : (enemy, delta_time) => {
enemy.x += enemy.vx * delta_time;
enemy.y += enemy.vy * delta_time;
// Al entrar en el radio de combate (16 celdas), se activa para combate
const dist = Math.sqrt(enemy.x * enemy.x + enemy.y * enemy.y);
if(dist <= 16.0){
enemy.ready = true;
}
}
}
};
return {
get : (mode) => {
// Si le pasamos un array de modos, elige uno aleatoriamente
if(Array.isArray(mode)){
const pick = mode[Math.floor(Math.random() * mode.length)];
return STRATEGIES[pick] || STRATEGIES.overflight;
}
return STRATEGIES[mode] || STRATEGIES.overflight;
},
register : (name, strategy) => { STRATEGIES[name] = strategy; }
};
})();