1
0
Fork 0

Added drone display

This commit is contained in:
Michaël Lemaire 2017-02-08 19:54:02 +01:00
parent fbf3181920
commit eb75658a10
11 changed files with 174 additions and 54 deletions

View file

@ -21,15 +21,40 @@ After making changes to sources, you need to recompile:
npm run build
## Attributes
## Ships
### In-combat values (HSP)
In combat, a ship's vitals are represented by the HSP system (Hull-Shield-Power):
* **Initiative** - Ability to play before other ships in the play order
* **Hull** - Amout of damage that a ship can receive before having to shut down all its systems
* **Shield** - Amount of damage that the shield equipments may absorb to protect the Hull
* **Power** - Available action points (some actions require more power than others)
These values will be changed by various effects (usage of equipments, sustained damage...).
### Attributes
Attributes represent a ship's ability to use its HSP system:
* **Initiative** - Ability to play before other ships in the play order
* **Hull capacity** - Maximal Hull value (when the battle starts)
* **Shield capacity** - Maximal Shield value (when the battle starts)
* **Power capacity** - Maximal Power value
* **Initial power** - Power immediately available at the start of battle
* **Power recovery** - Power generated at the end of a ship's turn
## Skills
These attributes are the sum of all currently applied effects (being permanent by an equipped item,
or a temporary effect caused by a weapon or a drone).
For example, a ship that equips a power generator with "power recovery +3", but has a sticky effect
of "power recovery -1" from a previous weapon hit, will have an effective power recovery of 2.
Attributes may also be upgraded permanently during level up.
### Skills
Skills represent a ship's ability to use equipments:
* **Materials** - Usage of physical materials such as bullets, shells...
* **Electronics** - Components of computers and communication
@ -38,6 +63,16 @@ After making changes to sources, you need to recompile:
* **Gravity** - Interaction with gravitational forces
* **Time** - Manipulation of time
Each equipment has minimal skill requirements to be used. For example, a weapon may require "materials >= 2"
and "energy >= 3" to be equipped. A ship that does not meet these requirements will not be able to use
the equipment.
As for attributes, skill values are controlled by equipments, effects and level up.
If an equipped item has a requirement of "time >= 2", that the ship has time skill of exactly 2, and that a
temporary effect of "time -1" is active, the requirement is no longer fulfilled and the equipped item
is then temporarily disabled (no more effects and cannot be used), until the "time -1" effect is lifted.
## Drones
Drones are static objects, deployed by ships, that apply effects in a circular zone around themselves.
@ -50,6 +85,9 @@ Drone effects are applied :
Drones are fully autonomous, and once deployed, are not controlled by their owner ship.
They are small and cannot be the direct target of weapons. They are not affected by area effects,
except for area damage and area effects specifically designed for drones.
A drone lasts for a given number of turns, counting down each time its owner's turn starts.
When reaching the number of turns, the drone is destroyed (before the owner's turn is started).
For example, a drone with 1-turn duration will destroy just before the next turn of its owner.

8
TODO
View file

@ -1,6 +1,10 @@
* Restore serialization
* Drones: add sprite, radius and tooltip
* Drones: add tooltip
* Drones: add hull points and take area damage
* Organize arena objects and information in layers
* Allow to cancel last moves
* Add more visual effects to weapons, hits and explosions
* Add keyboard shortcuts for actions
* Effect should be random in a range (eg. "damage target 50-75")
* Add an overload/cooling system
* Add auto-move to attack
@ -19,4 +23,4 @@
* Add a victory screen, with loot display
* Add retreat from battle
* Map : restore ability to jump to other systems
* Map : add stores
* Map : add stores and shipyards

View file

@ -110,14 +110,18 @@ module TS.SpaceTac.Game {
let [drone, effect] = newTestDrone(0, 0, 5, owner);
drone.duration = 2;
let result = drone.onTurnStart(other);
expect(result).toBe(true);
result = drone.onTurnStart(owner);
expect(result).toBe(true);
result = drone.onTurnStart(other);
expect(result).toBe(true);
result = drone.onTurnStart(owner);
expect(result).toBe(false);
let battle = new Battle();
spyOn(owner, "getBattle").and.returnValue(battle);
let removeDrone = spyOn(battle, "removeDrone").and.callThrough();
drone.onTurnStart(other);
expect(removeDrone).not.toHaveBeenCalled();
drone.onTurnStart(owner);
expect(removeDrone).not.toHaveBeenCalled();
drone.onTurnStart(other);
expect(removeDrone).not.toHaveBeenCalled();
drone.onTurnStart(owner);
expect(removeDrone).toHaveBeenCalledWith(drone);
});
});
}

View file

@ -3,6 +3,9 @@ module TS.SpaceTac.Game {
* Drones are static objects that apply effects in a circular zone around themselves.
*/
export class Drone {
// Code of the drone
code: string;
// Ship that deployed the drone
owner: Ship;
@ -23,8 +26,9 @@ module TS.SpaceTac.Game {
// Ships starting their turn the radius
inside_at_start: Ship[] = [];
constructor(owner: Ship) {
constructor(owner: Ship, code = "drone") {
this.owner = owner;
this.code = code;
}
/**
@ -56,16 +60,20 @@ module TS.SpaceTac.Game {
/**
* Called when a ship turn starts
*
* Returns false if the drone should be destroyed
*/
onTurnStart(ship: Ship): boolean {
onTurnStart(ship: Ship) {
if (ship == this.owner) {
if (this.duration <= 1) {
return false;
} else {
this.duration--;
this.duration--;
}
if (this.duration <= 0) {
if (this.owner) {
let battle = this.owner.getBattle();
if (battle) {
battle.removeDrone(this);
}
}
return;
}
if (ship.isInCircle(this.x, this.y, this.radius)) {
@ -74,14 +82,13 @@ module TS.SpaceTac.Game {
} else {
remove(this.inside_at_start, ship);
}
return true;
}
/**
* Called when a ship turn ends
*/
onTurnEnd(ship: Ship) {
if (ship.isInCircle(this.x, this.y, this.radius) && contains(this.inside_at_start, ship)) {
if (this.duration > 0 && ship.isInCircle(this.x, this.y, this.radius) && contains(this.inside_at_start, ship)) {
this.singleApply(ship);
}
}
@ -90,7 +97,7 @@ module TS.SpaceTac.Game {
* Called after a ship moved
*/
onShipMove(ship: Ship) {
if (ship.isInCircle(this.x, this.y, this.radius)) {
if (this.duration > 0 && ship.isInCircle(this.x, this.y, this.radius)) {
if (add(this.inside, ship)) {
this.singleApply(ship);
}

View file

@ -35,6 +35,7 @@ module TS.SpaceTac.Game {
battle.playing_ship = ship;
TestTools.setShipAP(ship, 3);
let equipment = new Equipment();
equipment.code = "testdrone";
equipment.distance = 8;
equipment.ap_usage = 2;
equipment.duration = 2;
@ -48,6 +49,7 @@ module TS.SpaceTac.Game {
expect(battle.drones.length).toBe(1);
let drone = battle.drones[0];
expect(drone.code).toEqual("testdrone");
expect(drone.duration).toEqual(2);
expect(drone.owner).toBe(ship);
expect(drone.x).toEqual(5);

View file

@ -16,7 +16,7 @@ module TS.SpaceTac.Game {
}
protected customApply(battle: Battle, ship: Ship, target: Target): boolean {
let drone = new Drone(ship);
let drone = new Drone(ship, this.equipment.code);
drone.x = target.x;
drone.y = target.y;
drone.radius = this.equipment.blast;

View file

@ -15,6 +15,7 @@ module TS.SpaceTac.Game.Equipments {
expect(battle.drones.length).toBe(1);
let drone = battle.drones[0];
expect(drone.duration).toBe(1);
ship.setAttribute("hull_capacity", 100);
ship.setValue("hull", 85);
drone.singleApply(ship);

View file

@ -10,6 +10,7 @@ module TS.SpaceTac.Game.Equipments {
this.min_level = new IntegerRange(1, 4);
this.setLifetime(1, 1);
this.setDeployDistance(50, 100);
this.setEffectRadius(40, 80);
this.setPowerConsumption(4, 5);

View file

@ -15,7 +15,10 @@ module TS.SpaceTac.View {
private battleview: BattleView;
// List of ship sprites
private ship_sprites: ArenaShip[];
private ship_sprites: ArenaShip[] = [];
// List of drone sprites
private drone_sprites: ArenaDrone[] = [];
// Currently hovered ship
private hovered: ArenaShip;
@ -27,7 +30,6 @@ module TS.SpaceTac.View {
super(battleview.game);
this.battleview = battleview;
this.ship_sprites = [];
this.playing = null;
this.hovered = null;
this.range_hint = null;
@ -129,5 +131,30 @@ module TS.SpaceTac.View {
this.battleview.gameui.audio.playOnce("battle-ship-change");
}
// Spawn a new drone
addDrone(drone: Game.Drone): void {
if (!any(this.drone_sprites, sprite => sprite.drone == drone)) {
let sprite = new ArenaDrone(this.battleview, drone);
this.addChild(sprite);
this.drone_sprites.push(sprite);
sprite.position.set(drone.owner.arena_x, drone.owner.arena_y);
this.game.tweens.create(sprite.position).to({ x: drone.x, y: drone.y }, 1800, Phaser.Easing.Sinusoidal.InOut, true, 200);
} else {
console.error("Drone added twice to arena", drone);
}
}
// Remove a destroyed drone
removeDrone(drone: Game.Drone): void {
let sprite = first(this.drone_sprites, sprite => sprite.drone == drone);
if (sprite) {
remove(this.drone_sprites, sprite);
sprite.destroy();
} else {
console.error("Drone not found in arena for removal", drone);
}
}
}
}

View file

@ -0,0 +1,33 @@
module TS.SpaceTac.View {
/**
* Drone sprite in the arena
*/
export class ArenaDrone extends Phaser.Group {
// Link to displayed drone
drone: Game.Drone;
// Sprite
sprite: Phaser.Button;
// Radius
radius: Phaser.Graphics;
constructor(battleview: BattleView, drone: Game.Drone) {
super(battleview.game);
this.drone = drone;
this.radius = new Phaser.Graphics(this.game, 0, 0);
this.radius.lineStyle(3, 0xe9f2f9, 0.5);
this.radius.beginFill(0xe9f2f9, 0.0);
this.radius.drawCircle(0, 0, drone.radius * 2);
this.radius.endFill();
this.addChild(this.radius);
this.sprite = new Phaser.Button(this.game, 0, 0, `battle-actions-deploy-${drone.code}`);
this.sprite.anchor.set(0.5, 0.5);
this.sprite.scale.set(0.1, 0.1);
this.addChild(this.sprite);
}
}
}

View file

@ -30,33 +30,26 @@ module TS.SpaceTac.View {
processBattleEvent(event: Game.BaseLogEvent) {
console.log("Battle event", event);
switch (event.code) {
case "ship_change":
this.processShipChangeEvent(<Game.ShipChangeEvent>event);
break;
case "damage":
this.processDamageEvent(<Game.DamageEvent>event);
break;
case "move":
this.processMoveEvent(<Game.MoveEvent>event);
break;
case "value":
this.processValueChangedEvent(<Game.ValueChangeEvent>event);
break;
case "death":
this.processDeathEvent(<Game.DeathEvent>event);
break;
case "fire":
this.processFireEvent(<Game.FireEvent>event);
break;
case "endbattle":
this.processEndBattleEvent(<Game.EndBattleEvent>event);
break;
case "effectadd":
case "effectduration":
case "effectdel":
this.processEffectEvent(event);
break;
if (event instanceof Game.ShipChangeEvent) {
this.processShipChangeEvent(event);
} else if (event instanceof Game.MoveEvent) {
this.processMoveEvent(event);
} else if (event instanceof Game.ValueChangeEvent) {
this.processValueChangedEvent(event);
} else if (event instanceof Game.DeathEvent) {
this.processDeathEvent(event);
} else if (event instanceof Game.FireEvent) {
this.processFireEvent(event);
} else if (event instanceof Game.DamageEvent) {
this.processDamageEvent(event);
} else if (event instanceof Game.EndBattleEvent) {
this.processEndBattleEvent(event);
} else if (event instanceof Game.DroneDeployedEvent) {
this.processDroneDeployedEvent(event);
} else if (event instanceof Game.DroneDestroyedEvent) {
this.processDroneDestroyedEvent(event);
} else if (event.code == "effectadd" || event.code == "effectduration" || event.code == "effectdel") {
this.processEffectEvent(event);
}
}
@ -150,5 +143,15 @@ module TS.SpaceTac.View {
item.updateEffects();
}
}
// New drone deployed
private processDroneDeployedEvent(event: Game.DroneDeployedEvent): void {
this.view.arena.addDrone(event.drone);
}
// Drone destroyed
private processDroneDestroyedEvent(event: Game.DroneDestroyedEvent): void {
this.view.arena.removeDrone(event.drone);
}
}
}