AdaptiveTactics / js /models.js
EphAsad's picture
Upload 61 files
076c3cb verified
/**
* Adaptive Tactics - Game Models
* Core data structures for units, weapons, tiles, and map data
*/
/**
* Weapon data structure
*/
export class Weapon {
constructor(data) {
this.id = data.id;
this.name = data.name;
this.kind = data.kind; // 'physical', 'magic', or 'heal'
this.might = data.might;
this.hit = data.hit;
this.minRange = data.minRange;
this.maxRange = data.maxRange;
this.heal = data.heal || 0;
}
/**
* Check if this weapon can reach a given range
*/
canReach(range) {
return range >= this.minRange && range <= this.maxRange;
}
/**
* Check if this is a healing weapon
*/
isHealingWeapon() {
return this.kind === 'heal';
}
}
/**
* Unit data structure
*/
export class Unit {
constructor(data, weapons) {
this.id = data.id;
this.name = data.name;
this.title = data.title || '';
this.sprite = data.sprite;
this.spriteBlend = data.spriteBlend || false;
this.faction = data.faction || 'player'; // 'player' or 'enemy'
// Stats
this.hp = data.stats.hp;
this.maxHp = data.stats.maxHp || data.stats.hp;
this.power = data.stats.power;
this.skill = data.stats.skill;
this.speed = data.stats.speed;
this.def = data.stats.def;
this.res = data.stats.res;
this.mov = data.stats.mov;
// Weapons
this.weaponIds = data.weapons || [];
this.equippedWeaponId = data.equippedWeapon || this.weaponIds[0];
this.weapons = weapons || {};
// Position (set during battle)
this.row = 0;
this.col = 0;
// State
this.acted = false;
this.alive = true;
}
/**
* Get the currently equipped weapon
*/
getEquippedWeapon() {
return this.weapons[this.equippedWeaponId] || null;
}
/**
* Get all available weapons
*/
getAvailableWeapons() {
return this.weaponIds.map(id => this.weapons[id]).filter(w => w);
}
/**
* Switch to a different weapon
*/
switchWeapon(weaponId) {
if (this.weaponIds.includes(weaponId)) {
this.equippedWeaponId = weaponId;
return true;
}
return false;
}
/**
* Take damage
*/
takeDamage(amount) {
this.hp = Math.max(0, this.hp - amount);
if (this.hp === 0) {
this.alive = false;
}
return this.hp;
}
/**
* Heal
*/
heal(amount) {
this.hp = Math.min(this.maxHp, this.hp + amount);
return this.hp;
}
/**
* Check if unit is a player unit
*/
isPlayer() {
return this.faction === 'player';
}
/**
* Check if unit is an enemy
*/
isEnemy() {
return this.faction === 'enemy';
}
/**
* Get position as [row, col]
*/
getPosition() {
return [this.row, this.col];
}
/**
* Set position
*/
setPosition(row, col) {
this.row = row;
this.col = col;
}
/**
* Get HP percentage
*/
getHpPercent() {
return (this.hp / this.maxHp) * 100;
}
}
/**
* Tile data structure
*/
export class Tile {
constructor(row, col, type = 'floor') {
this.row = row;
this.col = col;
this.type = type; // 'floor', 'bush', 'blocked', 'throne'
this.unit = null;
}
/**
* Check if tile is passable
*/
isPassable() {
return this.type !== 'blocked';
}
/**
* Check if tile is occupied
*/
isOccupied() {
return this.unit !== null;
}
/**
* Get terrain avoid bonus
*/
getAvoidBonus() {
switch (this.type) {
case 'bush': return 10;
case 'throne': return 15;
default: return 0;
}
}
/**
* Get terrain defense bonus
*/
getDefBonus() {
switch (this.type) {
case 'bush': return 1;
case 'throne': return 2;
default: return 0;
}
}
/**
* Check if this is the throne tile
*/
isThrone() {
return this.type === 'throne';
}
/**
* Place a unit on this tile
*/
placeUnit(unit) {
this.unit = unit;
if (unit) {
unit.setPosition(this.row, this.col);
}
}
/**
* Remove unit from this tile
*/
removeUnit() {
const unit = this.unit;
this.unit = null;
return unit;
}
}
/**
* Map data structure
*/
export class MapData {
constructor(data) {
this.id = data.id;
this.name = data.name;
this.rows = data.rows;
this.cols = data.cols;
// Store raw tile data for initialization
this.tileData = data.tiles;
this.playerSpawn = data.player_spawn;
this.enemyLayout = data.enemy_layout;
this.flexNodes = data.flex_nodes;
this.reinforcementEntries = data.reinforcement_entries;
// Tiles grid will be built during battle init
this.tiles = [];
}
/**
* Initialize the tile grid
*/
initTiles() {
this.tiles = [];
// Create all floor tiles first
for (let row = 1; row <= this.rows; row++) {
const rowTiles = [];
for (let col = 1; col <= this.cols; col++) {
rowTiles.push(new Tile(row, col, 'floor'));
}
this.tiles.push(rowTiles);
}
// Set blocked tiles
if (this.tileData.blocked) {
for (const [row, col] of this.tileData.blocked) {
this.getTile(row, col).type = 'blocked';
}
}
// Set bush tiles
if (this.tileData.bush) {
for (const [row, col] of this.tileData.bush) {
this.getTile(row, col).type = 'bush';
}
}
// Set throne tile
if (this.tileData.throne) {
const [row, col] = this.tileData.throne;
this.getTile(row, col).type = 'throne';
}
return this.tiles;
}
/**
* Get tile at position (1-indexed)
*/
getTile(row, col) {
if (row < 1 || row > this.rows || col < 1 || col > this.cols) {
return null;
}
return this.tiles[row - 1]?.[col - 1] || null;
}
/**
* Get all tiles as flat array
*/
getAllTiles() {
return this.tiles.flat();
}
/**
* Check if position is valid
*/
isValidPosition(row, col) {
return row >= 1 && row <= this.rows && col >= 1 && col <= this.cols;
}
/**
* Get Manhattan distance between two positions
*/
static getDistance(row1, col1, row2, col2) {
return Math.abs(row1 - row2) + Math.abs(col1 - col2);
}
}