162 lines
6.2 KiB
JavaScript
162 lines
6.2 KiB
JavaScript
"use strict";
|
|
|
|
const CockpitModel = (function(){
|
|
|
|
const CockpitModel = function(xgravity){
|
|
|
|
const self = this;
|
|
let blink_timer = 0;
|
|
let alert_state = false;
|
|
const max_hull = xgravity.settings.get("base_mothership_hull_max", null, 100.0);
|
|
let current_hull = max_hull;
|
|
|
|
this.get_health_percentage = () => Math.max(0, current_hull / max_hull);
|
|
this.get_hull = () => current_hull;
|
|
this.get_max_hull = () => max_hull;
|
|
|
|
this.take_damage = (amount) => {
|
|
current_hull = Math.max(0, current_hull - amount);
|
|
self.set_alert(true);
|
|
return current_hull <= 0; // true si explota la cabina (Game Over)
|
|
};
|
|
|
|
this.repair = (amount) => {
|
|
current_hull = Math.min(max_hull, current_hull + amount);
|
|
};
|
|
|
|
this.update = (delta_time) => {
|
|
// Autoreparación progresiva regulada por dificultad
|
|
const base_repair = xgravity.settings.get("base_mothership_repair_rate", null, 0.75);
|
|
const preset = xgravity.settings.get_difficulty_preset();
|
|
const repair_mult = preset ? preset.mothership_repair_mult : 1.0;
|
|
|
|
if(current_hull < max_hull && current_hull > 0){
|
|
current_hull = Math.min(max_hull, current_hull + (base_repair * repair_mult * delta_time));
|
|
}
|
|
};
|
|
|
|
const get_cell_size = (width, height) => {
|
|
const cells = xgravity.settings.get("cells", null, 40);
|
|
return (width < height ? width : height) / cells;
|
|
};
|
|
|
|
this.set_alert = (state) => {
|
|
alert_state = Boolean(state);
|
|
};
|
|
|
|
this.get_alert = () => {
|
|
return alert_state;
|
|
};
|
|
|
|
this.render = (ctx, width, height, delta_time) => {
|
|
const cell_px = get_cell_size(width, height);
|
|
const cx = width / 2;
|
|
const cy = height / 2;
|
|
const min_dim = Math.min(width, height);
|
|
const max_dim = Math.max(width, height);
|
|
|
|
blink_timer += delta_time;
|
|
|
|
// Escotilla panorámica ampliada: desborda la dimensión menor
|
|
// para que el espacio exterior sea el protagonista absoluto.
|
|
const inner_radius = (min_dim / 2) * 1.18;
|
|
const outer_radius = max_dim * 0.85;
|
|
const frame_thickness = cell_px * 0.6;
|
|
|
|
ctx.save();
|
|
|
|
// 1. Efecto sutil de cristal panorámico en el área de combate
|
|
const glass_grad = ctx.createLinearGradient(cx - inner_radius, cy - inner_radius, cx + inner_radius, cy + inner_radius);
|
|
glass_grad.addColorStop(0, "rgba(56, 189, 248, 0.04)");
|
|
glass_grad.addColorStop(0.5, "rgba(255, 255, 255, 0.01)");
|
|
glass_grad.addColorStop(1, "rgba(2, 132, 199, 0.03)");
|
|
|
|
ctx.fillStyle = glass_grad;
|
|
ctx.beginPath();
|
|
ctx.arc(cx, cy, inner_radius, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
// 2. Anillo de la cúpula (Marco perimetral reforzado)
|
|
ctx.lineWidth = frame_thickness;
|
|
ctx.strokeStyle = "#0b1120";
|
|
ctx.beginPath();
|
|
ctx.arc(cx, cy, inner_radius + (frame_thickness / 2), 0, Math.PI * 2);
|
|
ctx.stroke();
|
|
|
|
// Bisel fino interior iluminado
|
|
ctx.lineWidth = cell_px * 0.06;
|
|
ctx.strokeStyle = "rgba(56, 189, 248, 0.3)";
|
|
ctx.beginPath();
|
|
ctx.arc(cx, cy, inner_radius, 0, Math.PI * 2);
|
|
ctx.stroke();
|
|
|
|
// 3. Montantes / Vigas radiales exteriores (8 vigas perimetrales)
|
|
const SECTORS = 8;
|
|
const beam_width = frame_thickness * 0.75;
|
|
|
|
for(let i = 0; i < SECTORS; i++){
|
|
const angle = (i * Math.PI * 2) / SECTORS + (Math.PI / 8);
|
|
const cos = Math.cos(angle);
|
|
const sin = Math.sin(angle);
|
|
|
|
const start_x = cx + cos * inner_radius;
|
|
const start_y = cy + sin * inner_radius;
|
|
const end_x = cx + cos * outer_radius;
|
|
const end_y = cy + sin * outer_radius;
|
|
|
|
// Viga estructural
|
|
ctx.lineWidth = beam_width;
|
|
ctx.strokeStyle = "#090d16";
|
|
ctx.beginPath();
|
|
ctx.moveTo(start_x, start_y);
|
|
ctx.lineTo(end_x, end_y);
|
|
ctx.stroke();
|
|
|
|
// Detalle central de viga
|
|
ctx.lineWidth = beam_width * 0.2;
|
|
ctx.strokeStyle = "#1e293b";
|
|
ctx.beginPath();
|
|
ctx.moveTo(start_x, start_y);
|
|
ctx.lineTo(end_x, end_y);
|
|
ctx.stroke();
|
|
|
|
// 4. Sensores / Remaches LED en la unión del anillo
|
|
const light_dist = inner_radius + (frame_thickness * 0.5);
|
|
const lx = cx + cos * light_dist;
|
|
const ly = cy + sin * light_dist;
|
|
const light_radius = cell_px * 0.12;
|
|
|
|
const is_lit = alert_state ? (Math.sin(blink_timer * 14) > 0) : ((Math.sin(blink_timer * 2 + i) > 0.2));
|
|
const glow_color = alert_state ? "#ef4444" : (i % 2 === 0 ? "#10b981" : "#0284c7");
|
|
|
|
ctx.fillStyle = is_lit ? glow_color : "#1e293b";
|
|
ctx.beginPath();
|
|
ctx.arc(lx, ly, light_radius, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
|
|
if(is_lit){
|
|
ctx.shadowColor = glow_color;
|
|
ctx.shadowBlur = cell_px * 0.4;
|
|
ctx.fillStyle = glow_color;
|
|
ctx.beginPath();
|
|
ctx.arc(lx, ly, light_radius * 0.6, 0, Math.PI * 2);
|
|
ctx.fill();
|
|
ctx.shadowBlur = 0;
|
|
}
|
|
}
|
|
|
|
// 5. Viñeteado de cabina muy sutil en los extremos de la pantalla
|
|
const vig_grad = ctx.createRadialGradient(cx, cy, inner_radius * 0.95, cx, cy, outer_radius);
|
|
vig_grad.addColorStop(0, "rgba(5, 7, 13, 0)");
|
|
vig_grad.addColorStop(0.6, "rgba(5, 7, 13, 0.25)");
|
|
vig_grad.addColorStop(1, "rgba(2, 4, 8, 0.7)");
|
|
ctx.fillStyle = vig_grad;
|
|
ctx.fillRect(0, 0, width, height);
|
|
|
|
ctx.restore();
|
|
};
|
|
|
|
};
|
|
|
|
return CockpitModel;
|
|
})(); |