1
0
Fork 0
spacetac/src/scripts/game/Ship.ts

260 lines
8.6 KiB
TypeScript
Raw Normal View History

2014-12-29 00:00:00 +00:00
module SpaceTac.Game {
2015-01-07 00:00:00 +00:00
"use strict";
2014-12-29 00:00:00 +00:00
// A single ship in a Fleet
export class Ship {
// Fleet this ship is a member of
fleet: Fleet;
// Name of the ship
name: string;
2014-12-29 00:00:00 +00:00
// Current level
level: number;
// Number of shield points
shield: number;
// Number of hull points
hull: number;
// Position in the arena
arena_x: number;
arena_y: number;
// Facing direction in the arena
arena_angle: number;
// Initiative (high numbers will allow this ship to play sooner)
initiative: Attribute;
2014-12-31 00:00:00 +00:00
// Current number of action points
2015-01-19 00:00:00 +00:00
ap_current: Attribute;
2014-12-31 00:00:00 +00:00
2015-01-19 00:00:00 +00:00
// Initial number of action points, at the start of a battle
ap_initial: Attribute;
2014-12-31 00:00:00 +00:00
// Number of action points recovered by turn
2015-01-19 00:00:00 +00:00
ap_recover: Attribute;
2014-12-31 00:00:00 +00:00
// Number of action points used to make a 1.0 move
movement_cost: number;
2015-01-14 00:00:00 +00:00
// List of slots, able to contain equipment
slots: Slot[];
// Collection of available attributes
attributes: AttributeCollection;
// Create a new ship inside a fleet
constructor(fleet: Fleet = null, name: string = null) {
this.attributes = new AttributeCollection();
this.fleet = fleet;
this.name = name;
this.initiative = this.newAttribute(AttributeCode.Initiative);
this.initiative.setMaximal(1);
this.ap_current = this.newAttribute(AttributeCode.AP);
this.ap_initial = this.newAttribute(AttributeCode.AP_Initial);
this.ap_recover = this.newAttribute(AttributeCode.AP_Recovery);
this.movement_cost = 0.1;
2015-01-14 00:00:00 +00:00
this.slots = [];
2014-12-31 00:00:00 +00:00
if (fleet) {
fleet.addShip(this);
}
}
// Create and register an attribute
newAttribute(code: AttributeCode): Attribute {
return this.attributes.getRawAttr(code);
}
// Set position in the arena
2014-12-31 00:00:00 +00:00
// This does not consumes action points
setArenaPosition(x: number, y: number) {
this.arena_x = x;
this.arena_y = y;
}
// Set facing angle in the arena
setArenaFacingAngle(angle: number) {
this.arena_angle = angle;
}
// String repr
jasmineToString(): string {
return "Ship " + this.name;
}
// Make an initiative throw, to resolve play order in a battle
throwInitiative(gen: RandomGenerator): void {
this.initiative.set(gen.throw(this.initiative.maximal));
}
// Return the player owning this ship
getPlayer(): Player {
2015-01-19 00:00:00 +00:00
if (this.fleet) {
return this.fleet.player;
} else {
return null;
}
}
// get the current battle this ship is engaged in
getBattle(): Battle {
if (this.fleet) {
return this.fleet.battle;
} else {
return null;
}
}
2014-12-31 00:00:00 +00:00
2014-12-31 00:00:00 +00:00
// Get the list of actions available
// This list does not filter out actions unavailable due to insufficient AP, it only filters out
2014-12-31 00:00:00 +00:00
// actions that are not allowed/available at all on the ship
getAvailableActions(): BaseAction[] {
var actions: BaseAction[] = [];
this.slots.forEach((slot: Slot) => {
if (slot.attached && slot.attached.action) {
actions.push(slot.attached.action);
}
});
actions.push(new EndTurnAction());
return actions;
2014-12-31 00:00:00 +00:00
}
2015-01-19 00:00:00 +00:00
// Set an attribute value
// If offset is true, the value will be added to current value
// If log is true, an attribute event will be added to the battle log
setAttribute(attr: Attribute, value: number, offset: boolean = false, log: boolean = true) {
var changed: boolean;
if (offset) {
changed = attr.add(value);
} else {
changed = attr.set(value);
}
if (changed && log) {
var battle = this.getBattle();
if (battle) {
battle.log.add(new AttributeChangeEvent(this, attr));
}
}
}
// Initialize the action points counter
// This should be called once at the start of a battle
// If no value is provided, the attribute ap_initial will be used
initializeActionPoints(value: number = null): void {
if (value === null) {
value = this.ap_initial.current;
}
this.setAttribute(this.ap_current, value);
}
// Recover action points
// This should be called once at the start of a turn
// If no value is provided, the current attribute ap_recovery will be used
recoverActionPoints(value: number = null): void {
if (value === null) {
value = this.ap_recover.current;
}
this.setAttribute(this.ap_current, value, true);
}
2014-12-31 00:00:00 +00:00
// Consumes action points
2015-01-19 00:00:00 +00:00
useActionPoints(value: number): void {
this.setAttribute(this.ap_current, -value, true);
}
2014-12-31 00:00:00 +00:00
2015-01-19 00:00:00 +00:00
// Method called at the start of this ship turn
startTurn(first: boolean): void {
// Recompute attributes
this.updateAttributes();
2015-01-19 00:00:00 +00:00
// Manage action points
if (first) {
this.initializeActionPoints();
} else {
this.recoverActionPoints();
2014-12-31 00:00:00 +00:00
}
}
// Get the maximal position reachable in the arena with current action points
getLongestMove(x: number, y: number): number[] {
var dx = x - this.arena_x;
var dy = y - this.arena_y;
var length = Math.sqrt(dx * dx + dy * dy);
2015-01-19 00:00:00 +00:00
var max_length = this.ap_current.current / this.movement_cost;
2014-12-31 00:00:00 +00:00
if (max_length >= length) {
return [x, y];
} else {
var factor = max_length / length;
return [this.arena_x + dx * factor, this.arena_y + dy * factor];
}
}
// Move toward a location, consuming action points
moveTo(x: number, y: number): void {
var dest = this.getLongestMove(x, y);
var dx = dest[0] - this.arena_x;
var dy = dest[1] - this.arena_y;
var distance = Math.sqrt(dx * dx + dy * dy);
var cost = distance * this.movement_cost;
2015-01-26 00:00:00 +00:00
var angle = Math.atan2(y - this.arena_y, x - this.arena_x);
2014-12-31 00:00:00 +00:00
this.setArenaPosition(this.arena_x + dx, this.arena_y + dy);
2015-01-26 00:00:00 +00:00
this.setArenaFacingAngle(angle);
2014-12-31 00:00:00 +00:00
this.useActionPoints(cost);
}
// Add an empty equipment slot of the given type
addSlot(type: SlotType): Slot {
var result = new Slot(this, type);
this.slots.push(result);
return result;
}
// Update attributes, taking into account attached equipment and active effects
updateAttributes(): void {
// TODO Something more generic
// Compute new maximal values for attributes
var new_attrs = new AttributeCollection();
this.collectEffects("attrmax").forEach((effect: AttributeMaxEffect) => {
new_attrs.addValue(effect.attrcode, effect.value);
});
this.initiative.setMaximal(new_attrs.getValue(AttributeCode.Initiative));
this.ap_current.setMaximal(new_attrs.getValue(AttributeCode.AP));
// Compute new current values for attributes
new_attrs = new AttributeCollection();
this.collectEffects("attr").forEach((effect: AttributeMaxEffect) => {
new_attrs.addValue(effect.attrcode, effect.value);
});
this.ap_initial.set(new_attrs.getValue(AttributeCode.AP_Initial));
this.ap_recover.set(new_attrs.getValue(AttributeCode.AP_Recovery));
}
// Collect all effects to apply for updateAttributes
private collectEffects(code: string = null): BaseEffect[] {
var result: BaseEffect[] = [];
this.slots.forEach((slot: Slot) => {
if (slot.attached) {
slot.attached.permanent_effects.forEach((effect: BaseEffect) => {
if (effect.code === code) {
result.push(effect);
}
});
}
});
return result;
}
2014-12-29 00:00:00 +00:00
}
2015-01-07 00:00:00 +00:00
}