113 lines
3.8 KiB
JavaScript
113 lines
3.8 KiB
JavaScript
/**
|
|
* Galaxy Strike Online - Galaxy System
|
|
* Procedurally generates and manages sectors
|
|
*/
|
|
class GalaxySystem {
|
|
constructor() {
|
|
this.GRID_W = 30;
|
|
this.GRID_H = 20;
|
|
this.TYPES = ['empty','empty','empty','asteroid','asteroid','trade_hub','npc_territory','ruins','void_rift'];
|
|
this._sectors = null;
|
|
}
|
|
|
|
/** Lazy-generate the galaxy grid once */
|
|
getSectors() {
|
|
if (!this._sectors) this._sectors = this._generate();
|
|
return this._sectors;
|
|
}
|
|
|
|
_generate() {
|
|
const sectors = {};
|
|
for (let y = 0; y < this.GRID_H; y++) {
|
|
for (let x = 0; x < this.GRID_W; x++) {
|
|
const id = `${x}_${y}`;
|
|
const distFromCenter = Math.hypot(x - 15, y - 10) / 18;
|
|
// Richer regions further from core
|
|
const typeRoll = Math.random();
|
|
let type = 'empty';
|
|
if (typeRoll > 0.70) type = 'asteroid';
|
|
if (typeRoll > 0.88) type = 'npc_territory';
|
|
if (typeRoll > 0.94) type = 'trade_hub';
|
|
if (typeRoll > 0.97 && distFromCenter > 0.4) type = 'ruins';
|
|
if (typeRoll > 0.99 && distFromCenter > 0.6) type = 'void_rift';
|
|
|
|
const richness = Math.random();
|
|
sectors[id] = {
|
|
id, x, y, type,
|
|
name: this._sectorName(x, y),
|
|
threat: Math.min(10, Math.floor(distFromCenter * 10 + Math.random() * 3)),
|
|
richness: type === 'asteroid' ? (0.3 + richness * 0.7) : 0,
|
|
owner: null,
|
|
explored: false,
|
|
};
|
|
}
|
|
}
|
|
// Always make start sector safe
|
|
sectors['15_10'] = { id:'15_10', x:15, y:10, type:'trade_hub', name:'New Haven', threat:0, richness:0, owner:null, explored:true };
|
|
return sectors;
|
|
}
|
|
|
|
_sectorName(x, y) {
|
|
const prefixes = ['Alpha','Beta','Gamma','Delta','Sigma','Tau','Zeta','Omega','Nova','Vega','Lyra','Cygni'];
|
|
const suffixes = ['Prime','Station','Reach','Deep','Expanse','Crossing','Rift','Gate','Void','Fields'];
|
|
const seed = (x * 31 + y * 17) % (prefixes.length * suffixes.length);
|
|
return prefixes[seed % prefixes.length] + ' ' + suffixes[Math.floor(seed / prefixes.length) % suffixes.length];
|
|
}
|
|
|
|
/** Returns the sector or null */
|
|
getSector(id) {
|
|
return this.getSectors()[id] || null;
|
|
}
|
|
|
|
/** Mark a sector explored by a player */
|
|
exploreSector(sectorId, playerData) {
|
|
const sector = this.getSector(sectorId);
|
|
if (!sector) throw new Error('Sector not found');
|
|
const explored = playerData.exploredSectors || [];
|
|
if (!explored.includes(sectorId)) {
|
|
explored.push(sectorId);
|
|
playerData.exploredSectors = explored;
|
|
}
|
|
return sector;
|
|
}
|
|
|
|
/** Return list of sectors visible to player (explored + adjacent) */
|
|
getVisibleSectors(playerData) {
|
|
const explored = new Set(playerData.exploredSectors || ['15_10']);
|
|
const visible = new Set(explored);
|
|
// Reveal adjacent sectors to all explored
|
|
for (const id of explored) {
|
|
const [x, y] = id.split('_').map(Number);
|
|
for (const [dx, dy] of [[-1,0],[1,0],[0,-1],[0,1]]) {
|
|
const nid = `${x+dx}_${y+dy}`;
|
|
if (this.getSector(nid)) visible.add(nid);
|
|
}
|
|
}
|
|
const all = this.getSectors();
|
|
return Array.from(visible).map(id => {
|
|
const s = { ...all[id] };
|
|
s.explored = explored.has(id);
|
|
if (!s.explored) {
|
|
// Fog: hide details of unexplored but adjacent sectors
|
|
s.name = '???';
|
|
s.richness = 0;
|
|
s.owner = null;
|
|
}
|
|
return s;
|
|
});
|
|
}
|
|
|
|
/** Player claims/sets base in a sector */
|
|
claimSector(sectorId, playerData) {
|
|
const sector = this.getSector(sectorId);
|
|
if (!sector) throw new Error('Sector not found');
|
|
if (sector.type === 'void_rift') throw new Error('Cannot claim Void Rift sectors');
|
|
sector.owner = playerData.userId;
|
|
this.exploreSector(sectorId, playerData);
|
|
playerData.homeSector = sectorId;
|
|
return sector;
|
|
}
|
|
}
|
|
|
|
module.exports = GalaxySystem;
|