UNPKG

@zerospacegg/iolin

Version:

Pure TypeScript implementation of ZeroSpace game data processing (PKL-free)

662 lines 20.4 kB
/** * Ability - Base ability class for ZeroSpace abilities * Ported from ability.pkl with functional constructor pattern */ import { Child } from "./child.js"; import { slugify } from "./core.js"; import { Entity } from "./entity.js"; /** * Base Ability class - extends Entity with ability-specific properties * Usage: new AttackAbility("Sting", (ability) => { ability.range = 400; }) */ export class Ability extends Child { constructor() { super(...arguments); // Targeting and activation this.targets = []; this.targetMode = "unit"; this.hotkey = ""; this.bonusDamage = []; } /** Public subtype getter - just for JSON serialization */ get subtype() { return this.abilityType; } // Computed activation type based on ability type get activationType() { switch (this.subtype) { case "attack": case "heal": return "auto"; case "passive": case "faction-passive": case "faction-talent": return "permanent-effect"; case "death-trigger": return "trigger"; default: return "activated"; } } // Getter that returns targets in sorted order for consistent comparisons get sortedTargets() { return [...this.targets].sort(); } // Automatic autocast based on activationType or forced autocast get autocast() { if (this._forceAutocast !== undefined) { return this._forceAutocast; } return this.activationType === "auto"; } // Allow setting forceAutocast set forceAutocast(value) { this._forceAutocast = value; } /** * Apply this ability's effects to the parent entity * Must be implemented by all abilities */ apply() { // Default no-op implementation - override in subclasses } /** * Apply this ability's effects to a target entity * Used for cross-entity effects like buffs/debuffs * Defaults to no-op if not needed */ applyTo(entity) { // Default no-op implementation } get id() { if (this.parentId) { return `${this.parentId}/${this.slug}`; } return this.slug; } /** * JSON.stringify() calls this automatically */ toJSON() { return { ...super.toJSON(), abilityType: this.abilityType, activationType: this.activationType, parentSlug: this.parentSlug, parentId: this.parentId, targets: this.targets, targetMode: this.targetMode, requiresMode: this.requiresMode, togglesMode: this.togglesMode, hotkey: this.hotkey, reverseHotKey: this.reverseHotKey, healthCost: this.healthCost, energyCost: this.energyCost, energyType: this.energyType, cooldown: this.cooldown, cooldownBetweenShots: this.cooldownBetweenShots, cooldownAtBuild: this.cooldownAtBuild, duration: this.duration, range: this.range, delay: this.delay, shots: this.shots, volleys: this.volleys, bonusDamage: this.bonusDamage, splash: this.splash, armorPenetration: this.armorPenetration, closeupFirerate: this.closeupFirerate, autocast: this.autocast, // Internal game engine data NOT exported - accessed directly from TypeScript instances // (internalId) }; } } /** * Base FactionAbility class - extends Ability with faction ability properties * Usage: new FactionPassive("Resource Control", (ability) => { ability.abilityOf = "grell"; }) */ export class FactionAbility extends Ability { constructor() { super(); } get activationType() { switch (this.subtype) { case "passive": case "talent": return "permanent-effect"; default: return "activated"; } } toJSON() { return { ...super.toJSON(), abilityOf: this.abilityOf, charges: this.charges, teleport: this.teleport, }; } } /** * FactionPassive - Passive faction abilities * Usage: new FactionPassive("Resource Control", (passive) => { passive.abilityOf = "grell"; }) */ export class FactionPassive extends FactionAbility { constructor() { super(); } get slug() { return `passives/${slugify(this.name)}`; } get id() { return `faction/${this.abilityOf}/${this.slug}`; } get subtype() { return "passive"; } get abilityType() { return this.subtype; } } /** * TopbarAbility - Topbar faction abilities * Usage: new TopbarAbility({ name: "Orbital Strike", abilityOf: "grell" }) */ export class Topbar extends FactionAbility { constructor(props) { super(); if (props) { Object.assign(this, props); } } get slug() { return `topbar/${slugify(this.name)}`; } get id() { return `faction/${this.abilityOf}/${this.slug}`; } get subtype() { return "topbar"; } get abilityType() { return this.subtype; } toJSON() { return { ...super.toJSON(), topbarType: this.topbarType, slot: this.slot, requiredLevel: this.requiredLevel, }; } } /** * TalentAbility - Talent faction abilities * Usage: new TalentAbility("Enhanced Infusion", (talent) => { talent.abilityOf = "grell"; }) */ export class TalentAbility extends FactionAbility { constructor() { super(); } get slug() { return `talent-abilities/${slugify(this.name)}`; } get id() { return `faction/${this.abilityOf}/${this.slug}`; } get subtype() { return "talent"; } get abilityType() { return this.subtype; } } /** * FactionTalent - Faction-wide upgrades (not abilities) * Usage: new FactionTalent({ name: "Enhanced Bio-Growth", tier: "T2" }) */ export class FactionTalent extends Entity { // Static defaults as readonly fields get slug() { return `talents/${slugify(this.name)}`; } get id() { return `faction/${this.abilityOf}/${this.slug}`; } constructor(props) { super(); // Talent-specific properties this.tier = ""; this.level = 1; if (props) { Object.assign(this, props); // Explicitly handle name assignment to ensure it works if (props.name) { this.name = props.name; } } } get subtype() { return "talent"; } apply() { } toJSON() { return { ...super.toJSON(), tier: this.tier, level: this.level, abilityOf: this.abilityOf, }; } } /** * Attack Ability - Child class for attack abilities embedded in units * Usage: new AttackAbility("Claws", parentId, (attack) => { attack.damage = 6; }) */ export class Attack extends Ability { get type() { return "ability"; } get slug() { return `attacks/${slugify(this.name)}`; } // Auto-calculated damagePerSec getter based on PKL logic get damagePerSec() { if (this.damageOverTime != null) return undefined; if (this.damage == null || this.cooldown == null) return undefined; const amt = this.damage; const shots = this.shots ?? 1; const volleys = this.volleys ?? 1; return Math.round((amt * shots * volleys) / this.cooldown); } constructor(props = {}) { super(); // Static defaults as readonly fields this.abilityType = "attack"; // Default description implementation this.description = ""; // Set default autocast for attacks // autocast is now computed automatically from activationType this.targetMode = "gamepiece"; // attacks target units + buildings Object.assign(this, props); } toJSON() { return { ...super.toJSON(), abilityType: this.abilityType, description: this.description, damage: this.damage, gamagePerSec: this.damagePerSec, range: this.range, cooldown: this.cooldown, delay: this.delay, targets: this.targets, bonusDamage: this.bonusDamage, splash: this.splash, volleys: this.volleys, }; } } /** * Heal Ability - Child class for heal abilities embedded in units * Usage: new HealAbility("Regeneration", parentId, (heal) => { heal.healAmount = 10; }) */ export class Heal extends Ability { get type() { return "ability"; } get slug() { return `heals/${slugify(this.name)}`; } // Auto-calculated healingPerSec getter based on PKL logic get healingPerSec() { const healValue = this.healing ?? this.healAmount; if (this.healingOverTime != null) return undefined; if (healValue == null || this.cooldown == null) return undefined; const amt = healValue; const shots = this.shots ?? 1; const volleys = this.volleys ?? 1; return Math.round((amt * shots * volleys) / this.cooldown); } constructor(props) { super(); // Static defaults as readonly fields this.abilityType = "heal"; // Default description implementation this.description = ""; this.abilityType = "heal"; this.name = props.name; this.description = props.description || ""; Object.assign(this, props); } toJSON() { return { ...super.toJSON(), abilityType: this.abilityType, description: this.description, healAmount: this.healAmount, healPerSec: this.healingPerSec, range: this.range, cooldown: this.cooldown, duration: this.duration, targets: this.targets, energyCost: this.energyCost, }; } } /** * Spell Ability - Child class for spell abilities embedded in units * Usage: new Spell("Teleport", parentId, (spell) => { spell.energyCost = 50; }) */ export class Spell extends Ability { get type() { return "ability"; } get slug() { return `spells/${slugify(this.name)}`; } constructor(props) { super(); // Static defaults as readonly fields this.abilityType = "spell"; this.hotkey = ""; this.unlocked = true; this.delay = undefined; this.description = props.description; if (props.forceAutocast !== undefined) { this.forceAutocast = props.forceAutocast; } Object.assign(this, props); } // Auto-calculated damagePerSec getter based on PKL logic get damagePerSec() { if (this.damageOverTime != null) return undefined; if (this.damage == null || this.cooldown == null) return undefined; const amt = this.damage; const shots = this.shots ?? 1; const volleys = this.volleys ?? 1; return Math.round((amt * shots * volleys) / this.cooldown); } // Auto-calculated healingPerSec getter based on PKL logic get healingPerSec() { if (this.healingOverTime != null) return undefined; if (this.healing == null || this.cooldown == null) return undefined; const amt = this.healing; const shots = this.shots ?? 1; const volleys = this.volleys ?? 1; return Math.round((amt * shots * volleys) / this.cooldown); } toJSON() { return { ...super.toJSON(), abilityType: this.abilityType, description: this.description, energyCost: this.energyCost, cooldown: this.cooldown, range: this.range, duration: this.duration, targets: this.targets, hotkey: this.hotkey, unlocked: this.unlocked, damage: this.damage, delay: this.delay, healing: this.healing, }; } } /** * Death Trigger Ability - Child class for abilities that trigger on unit death * Usage: new DeathTrigger({ description: "Nuclear explosion", damage: 1000, delay: 1 }) */ export class DeathTrigger extends Ability { get type() { return "ability"; } get slug() { return `on-death/${slugify(this.name)}`; } // Death triggers have special activationType get activationType() { return "trigger"; } constructor(props) { super(); // Static defaults as readonly fields this.abilityType = "spell"; this.hotkey = ""; this.unlocked = true; this.delay = undefined; this.bonusDamage = []; this.description = props.description; Object.assign(this, props); } // Auto-calculated damagePerSec getter based on PKL logic get damagePerSec() { if (this.damageOverTime != null) return undefined; if (this.damage == null || this.cooldown == null) return undefined; const amt = this.damage; const shots = this.shots ?? 1; const volleys = this.volleys ?? 1; return Math.round((amt * shots * volleys) / this.cooldown); } // Auto-calculated healingPerSec getter based on PKL logic get healingPerSec() { if (this.healingOverTime != null) return undefined; if (this.healing == null || this.cooldown == null) return undefined; const amt = this.healing; const shots = this.shots ?? 1; const volleys = this.volleys ?? 1; return Math.round((amt * shots * volleys) / this.cooldown); } get subtype() { return "death-trigger"; } toJSON() { return { ...super.toJSON(), abilityType: this.abilityType, description: this.description, energyCost: this.energyCost, cooldown: this.cooldown, range: this.range, duration: this.duration, targets: this.targets, hotkey: this.hotkey, unlocked: this.unlocked, damage: this.damage, delay: this.delay, healing: this.healing, splash: this.splash, bonusDamage: this.bonusDamage, }; } } /** * Siege Ability - Child class for siege mode toggle abilities embedded in units * Usage: new SiegeAbility("Siege Mode", parentId, (siege) => { siege.togglesMode = "siege"; }) */ export class Siege extends Ability { get type() { return "ability"; } get slug() { return `siege/${slugify(this.name)}`; } constructor(props) { super(); // Static defaults as readonly fields this.abilityType = "toggle"; // Siege/toggle properties with proper declarations this.togglesMode = undefined; this.hotkey = ""; this.reverseHotKey = undefined; this.description = props.description; Object.assign(this, props); } toJSON() { return { ...super.toJSON(), abilityType: this.abilityType, description: this.description, togglesMode: this.togglesMode, duration: this.duration, cooldown: this.cooldown, targets: this.targets, hotkey: this.hotkey, reverseHotKey: this.reverseHotKey, }; } } /** * Weapon Switch Ability - Child class for weapon switching toggle abilities * Usage: new WeaponSwitch("Weapon Switch", parentId, (switch) => { switch.switchBetween = ["sword", "blaster"]; }) */ export class WeaponSwitch extends Ability { get type() { return "ability"; } get slug() { return `weapon-switch/${slugify(this.name)}`; } constructor(props) { super(); // Static defaults as readonly fields this.abilityType = "toggle"; // Weapon switch properties with proper declarations this.togglesMode = undefined; this.hotkey = ""; this.description = props.description; Object.assign(this, props); } toJSON() { return { ...super.toJSON(), abilityType: this.abilityType, description: this.description, togglesMode: this.togglesMode, switchBetween: this.switchBetween, cooldown: this.cooldown, targets: this.targets, hotkey: this.hotkey, }; } } /** * Upgrade Ability - Child class for upgrade abilities embedded in units * Usage: new UpgradeAbility("Fast Legs", parentId, (upgrade) => { upgrade.fluxCost = 70; }) */ export class Upgrade extends Child { get type() { return "ability"; } constructor(props = {}) { super(); Object.assign(this, props); } /** * Apply this upgrade's effects to the parent entity * Must be implemented by all upgrades */ apply() { // Default no-op implementation - override in subclasses } /** * Apply this upgrade's effects to a target entity * Used for cross-entity effects like auras or global buffs * Defaults to no-op if not needed */ applyTo(entity) { // Default no-op implementation } get subtype() { return "upgrade"; } toJSON() { return { ...super.toJSON(), description: this.description, tier: this.tier, fluxCost: this.fluxCost, hexiteCost: this.hexiteCost, researchTime: this.researchTime, }; } } /** * Passive Ability - Child class for passive abilities embedded in units * Usage: new PassiveAbility("Regeneration", parentId, (passive) => { passive.healPerSec = 2; }) */ export class Passive extends Ability { get type() { return "ability"; } get slug() { return `passives/${slugify(this.name)}`; } // Auto-calculated damagePerSec getter based on PKL logic get damagePerSec() { if (this.damageOverTime != null) return undefined; if (this.damage == null || this.cooldown == null) return undefined; const amt = this.damage; const shots = this.shots ?? 1; const volleys = this.volleys ?? 1; return Math.round((amt * shots * volleys) / this.cooldown); } // Auto-calculated healingPerSec getter based on PKL logic get healingPerSec() { const healValue = this.healing ?? this.healAmount; if (this.healingOverTime != null) return undefined; if (healValue == null || this.cooldown == null) return undefined; const amt = healValue; const shots = this.shots ?? 1; const volleys = this.volleys ?? 1; return Math.round((amt * shots * volleys) / this.cooldown); } constructor(props) { super(); // Static defaults as readonly fields this.abilityType = "passive"; this.duration = undefined; this.range = undefined; this.targets = []; this.unlocked = true; this.description = props.description; Object.assign(this, props); } toJSON() { return { ...super.toJSON(), abilityType: this.abilityType, description: this.description, damage: this.damage, healAmount: this.healAmount, healPerSec: this.healingPerSec, energyCost: this.energyCost, cooldown: this.cooldown, duration: this.duration, range: this.range, targets: this.targets, unlocked: this.unlocked, bonusDamage: this.bonusDamage, splash: this.splash, hotkey: this.hotkey, }; } } //# sourceMappingURL=ability.js.map