1
0
Fork 0

Better display of emergency stasis

This commit is contained in:
Michaël Lemaire 2017-02-15 23:34:27 +01:00
parent 12ee9b823b
commit 471ca8d2d0
17 changed files with 267 additions and 77 deletions

2
TODO
View file

@ -6,12 +6,10 @@
* Drones: add hull points and take area damage
* Drones: fix not being removed when owner is in statis (owner's turn is skipped)
* More sound effects
* Do not apply effects on ships in stasis
* Add a battle log display
* Organize arena objects and information in layers
* Prevent arena effects information (eg. "shield -36") to overflow out of the arena
* Allow to cancel last moves
* Identify ships in emergency stasis more clearly
* Effect should be random in a range (eg. "damage target 50-75")
* Add an overload/cooling system
* Add auto-move to attack

View file

@ -890,11 +890,11 @@
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="2.2460937"
inkscape:cx="1019.8563"
inkscape:cy="245.98063"
inkscape:zoom="0.79411404"
inkscape:cx="713.78747"
inkscape:cy="706.929"
inkscape:document-units="px"
inkscape:current-layer="layer30"
inkscape:current-layer="layer32"
showgrid="false"
units="px"
showguides="true"
@ -2672,6 +2672,47 @@
style="font-size:20px;fill:#afe9c6;fill-opacity:1">Shield +50 (1 turn)</tspan></text>
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer32"
inkscape:label="Ship detail stasis"
style="display:none">
<use
style="display:inline;enable-background:new"
x="0"
y="0"
xlink:href="#rect4584"
id="use6171"
width="100%"
height="100%"
inkscape:export-filename="/home/michael/workspace/perso/spacetac/out/assets/images/battle/ship-tooltip-stasis.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:30px;line-height:23.4375px;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#aa0000;fill-opacity:1;stroke:none;stroke-width:0.9375px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="387.86609"
y="262.24658"
id="text6176"
inkscape:export-filename="/home/michael/workspace/perso/spacetac/out/assets/images/battle/ship-tooltip-stasis.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><tspan
sodipodi:role="line"
id="tspan6174"
x="387.86609"
y="262.24658"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:30px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';text-align:center;text-anchor:middle;fill:#aa0000;stroke-width:0.9375px">Emergency Stasis Protocol</tspan><tspan
sodipodi:role="line"
x="387.86609"
y="285.68408"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:30px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';text-align:center;text-anchor:middle;fill:#aa0000;stroke-width:0.9375px"
id="tspan6178" /><tspan
sodipodi:role="line"
x="387.86609"
y="309.12158"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:30px;font-family:sans-serif;-inkscape-font-specification:'sans-serif Bold';text-align:center;text-anchor:middle;fill:#aa0000;stroke-width:0.9375px"
id="tspan6180">ACTIVATED</tspan></text>
</g>
</g>
<g
inkscape:groupmode="layer"

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 116 KiB

View file

@ -22,6 +22,30 @@
inkscape:export-ydpi="96.000008">
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient4554">
<stop
style="stop-color:#000000;stop-opacity:0.8425532"
offset="0"
id="stop4550" />
<stop
id="stop4562"
offset="0.85630518"
style="stop-color:#29233a;stop-opacity:0.91063827" />
<stop
id="stop4560"
offset="0.88856322"
style="stop-color:#4f495f;stop-opacity:0.87843138" />
<stop
id="stop4558"
offset="0.90909111"
style="stop-color:#29233a;stop-opacity:0.88235294;" />
<stop
style="stop-color:#29233a;stop-opacity:0.28297871"
offset="1"
id="stop4552" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient4539">
@ -163,6 +187,17 @@
r="152.61015"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,1.9515076)" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4554"
id="radialGradient4556"
cx="61.964287"
cy="65.76786"
fx="61.964287"
fy="65.76786"
r="30.446428"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0997067,0,0,1.0997067,-4.1425418,-8.3253595)" />
</defs>
<sodipodi:namedview
id="base"
@ -171,11 +206,11 @@
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="133.78339"
inkscape:cy="80.621482"
inkscape:zoom="7.9195959"
inkscape:cx="61.820614"
inkscape:cy="63.238329"
inkscape:document-units="px"
inkscape:current-layer="layer6"
inkscape:current-layer="layer5"
showgrid="false"
units="px"
inkscape:measure-start="35.2291,59.599"
@ -371,11 +406,22 @@
<g
inkscape:groupmode="layer"
id="layer5"
inkscape:label="Stasis" />
inkscape:label="Stasis">
<circle
style="fill:url(#radialGradient4556);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.10143852;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4548"
cx="64"
cy="64"
r="33.482143"
inkscape:export-filename="/home/michael/workspace/perso/spacetac/out/assets/images/battle/arena/statis.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
</g>
<g
inkscape:groupmode="layer"
id="layer6"
inkscape:label="Blast">
inkscape:label="Blast"
style="display:none">
<circle
style="opacity:1;fill:url(#radialGradient4541);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4.34724236;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path4533"

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -108,10 +108,10 @@ module TS.SpaceTac {
}
// Collect all ships within a given radius of a target
collectShipsInCircle(center: Target, radius: number): Ship[] {
collectShipsInCircle(center: Target, radius: number, alive_only = false): Ship[] {
var result: Ship[] = [];
this.play_order.forEach((ship: Ship) => {
if (Target.newFromShip(ship).getDistanceTo(center) <= radius) {
this.play_order.forEach(ship => {
if ((ship.alive || !alive_only) && Target.newFromShip(ship).getDistanceTo(center) <= radius) {
result.push(ship);
}
});
@ -240,6 +240,15 @@ module TS.SpaceTac {
log.add(event);
});
// Indicate emergency stasis
this.play_order.forEach(ship => {
if (!ship.alive) {
let event = new DeathEvent(ship);
event.initial = true;
log.add(event);
}
});
// Simulate drones deployment
this.drones.forEach(drone => {
let event = new DroneDeployedEvent(drone);

View file

@ -44,6 +44,7 @@ module TS.SpaceTac {
* This does not check if the ships are in range.
*/
apply(ships: Ship[], log = true) {
ships = ships.filter(ship => ship.alive);
if (ships.length > 0) {
let battle = this.owner.getBattle();
if (battle && log) {

View file

@ -161,11 +161,13 @@ module TS.SpaceTac {
getAvailableActions(): BaseAction[] {
var actions: BaseAction[] = [];
this.slots.forEach((slot: Slot) => {
if (slot.attached && slot.attached.action) {
actions.push(slot.attached.action);
}
});
if (this.alive) {
this.slots.forEach((slot: Slot) => {
if (slot.attached && slot.attached.action) {
actions.push(slot.attached.action);
}
});
}
actions.push(new EndTurnAction());
return actions;
@ -259,10 +261,12 @@ module TS.SpaceTac {
// This should be called once at the end 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.attributes.power_recovery.get();
if (this.alive) {
if (value === null) {
value = this.attributes.power_recovery.get();
}
this.setValue("power", value, true);
}
this.setValue("power", value, true);
}
// Consumes action points
@ -293,15 +297,17 @@ module TS.SpaceTac {
}
this.playing = true;
// Recompute attributes
this.updateAttributes();
if (this.alive) {
// Recompute attributes
this.updateAttributes();
// Apply sticky effects
this.sticky_effects.forEach(effect => effect.startTurn(this));
this.cleanStickyEffects();
// Apply sticky effects
this.sticky_effects.forEach(effect => effect.startTurn(this));
this.cleanStickyEffects();
// Broadcast to drones
this.forEachDrone(drone => drone.onTurnStart(this));
// Broadcast to drones
this.forEachDrone(drone => drone.onTurnStart(this));
}
}
// Method called at the end of this ship turn
@ -312,16 +318,18 @@ module TS.SpaceTac {
}
this.playing = false;
// Broadcast to drones
this.forEachDrone(drone => drone.onTurnEnd(this));
if (this.alive) {
// Broadcast to drones
this.forEachDrone(drone => drone.onTurnEnd(this));
// Recover action points for next turn
this.updateAttributes();
this.recoverActionPoints();
// Recover action points for next turn
this.updateAttributes();
this.recoverActionPoints();
// Apply sticky effects
this.sticky_effects.forEach(effect => effect.endTurn(this));
this.cleanStickyEffects();
// Apply sticky effects
this.sticky_effects.forEach(effect => effect.endTurn(this));
this.cleanStickyEffects();
}
}
/**
@ -330,9 +338,11 @@ module TS.SpaceTac {
* Pay attention to pass a copy, not the original equipment effect, because it will be modified
*/
addStickyEffect(effect: StickyEffect, log = true): void {
this.sticky_effects.push(effect);
if (log) {
this.addBattleEvent(new EffectAddedEvent(this, effect));
if (this.alive) {
this.sticky_effects.push(effect);
if (log) {
this.addBattleEvent(new EffectAddedEvent(this, effect));
}
}
}
@ -389,6 +399,9 @@ module TS.SpaceTac {
// Set the death status on this ship
setDead(log: boolean = true): void {
this.alive = false;
this.values.hull.set(0);
this.values.shield.set(0);
this.values.power.set(0);
if (log) {
this.addBattleEvent(new DeathEvent(this));
}
@ -463,27 +476,31 @@ module TS.SpaceTac {
// Update attributes, taking into account attached equipment and active effects
updateAttributes(): void {
// Sum all attribute effects
var new_attrs = new ShipAttributes();
this.collectEffects("attr").forEach((effect: AttributeEffect) => {
new_attrs[effect.attrcode].add(effect.value);
});
if (this.alive) {
// Sum all attribute effects
var new_attrs = new ShipAttributes();
this.collectEffects("attr").forEach((effect: AttributeEffect) => {
new_attrs[effect.attrcode].add(effect.value);
});
// Apply limit attributes
this.collectEffects("attrlimit").forEach((effect: AttributeLimitEffect) => {
new_attrs[effect.attrcode].setMaximal(effect.value);
});
// Apply limit attributes
this.collectEffects("attrlimit").forEach((effect: AttributeLimitEffect) => {
new_attrs[effect.attrcode].setMaximal(effect.value);
});
// TODO better typing
iteritems(<any>new_attrs, (key, value) => {
this.setAttribute(<keyof ShipAttributes>key, (<ShipAttribute>value).get());
});
// TODO better typing
iteritems(<any>new_attrs, (key, value) => {
this.setAttribute(<keyof ShipAttributes>key, (<ShipAttribute>value).get());
});
}
}
// Fully restore hull and shield
restoreHealth(): void {
this.values.hull.set(this.attributes.hull_capacity.get());
this.values.shield.set(this.attributes.shield_capacity.get());
if (this.alive) {
this.values.hull.set(this.attributes.hull_capacity.get());
this.values.shield.set(this.attributes.shield_capacity.get());
}
}
// Collect all effects to apply for updateAttributes

View file

@ -0,0 +1,47 @@
/// <reference path="../effects/BaseEffect.ts" />
module TS.SpaceTac {
describe("FireWeaponAction", function () {
it("constructs correctly", function () {
let equipment = new Equipment(SlotType.Weapon, "testweapon");
let action = new FireWeaponAction(equipment);
expect(action.code).toEqual("fire-testweapon");
expect(action.name).toEqual("Fire");
expect(action.equipment).toBe(equipment);
expect(action.needs_target).toBe(true);
});
it("applies effects to alive ships in blast radius", function () {
let fleet = new Fleet();
let ship = new Ship(fleet);
let equipment = new Equipment(SlotType.Weapon, "testweapon");
equipment.blast = 10;
equipment.ap_usage = 5;
equipment.distance = 100;
let effect = new BaseEffect("testeffect");
let mock_apply = spyOn(effect, "applyOnShip").and.stub();
equipment.target_effects.push(effect);
let action = new FireWeaponAction(equipment, true);
TestTools.setShipAP(ship, 10);
let ship1 = new Ship(fleet);
ship1.setArenaPosition(65, 72);
let ship2 = new Ship(fleet);
ship2.setArenaPosition(45, 48);
let ship3 = new Ship(fleet);
ship3.setArenaPosition(45, 48);
ship3.alive = false;
let battle = new Battle(fleet);
battle.play_order = [ship, ship1, ship2, ship3];
battle.playing_ship = ship;
fleet.setBattle(battle);
action.apply(battle, ship, Target.newFromLocation(50, 50));
expect(mock_apply).toHaveBeenCalledTimes(1);
expect(mock_apply).toHaveBeenCalledWith(ship2);
});
});
}

View file

@ -44,8 +44,8 @@ module TS.SpaceTac {
// Collect affected ships
if (blast) {
affected = affected.concat(battle.collectShipsInCircle(target, blast));
} else if (target.ship) {
affected = affected.concat(battle.collectShipsInCircle(target, blast, true));
} else if (target.ship && target.ship.alive) {
affected.push(target.ship);
}

View file

@ -43,11 +43,13 @@ module TS.SpaceTac.UI {
this.loadImage("battle/ship-tooltip-own.png");
this.loadImage("battle/ship-tooltip-enemy.png");
this.loadImage("battle/ship-tooltip-effect.png");
this.loadImage("battle/ship-tooltip-stasis.png");
this.loadImage("battle/arena/ship-hover.png");
this.loadImage("battle/arena/ship-normal-enemy.png");
this.loadImage("battle/arena/ship-normal-own.png");
this.loadImage("battle/arena/ship-playing-enemy.png");
this.loadImage("battle/arena/ship-playing-own.png");
this.loadImage("battle/arena/stasis.png");
this.loadImage("battle/actions/move.png");
this.loadImage("battle/actions/endturn.png");
this.loadImage("battle/actions/fire-gatlinggun.png");

View file

@ -97,8 +97,7 @@ module TS.SpaceTac.UI {
markAsDead(ship: Ship): void {
var sprite = this.findShipSprite(ship);
if (sprite) {
sprite.alpha = 0.5;
sprite.displayEffect("stasis", false);
sprite.setDead(true);
}
}

View file

@ -10,6 +10,9 @@ module TS.SpaceTac.UI {
// Ship sprite
sprite: Phaser.Button;
// Statis effect
stasis: Phaser.Image;
// Hover effect
hover: Phaser.Image;
@ -33,6 +36,12 @@ module TS.SpaceTac.UI {
this.sprite.anchor.set(0.5, 0.5);
this.addChild(this.sprite);
// Add ship sprite
this.stasis = new Phaser.Image(this.game, 0, 0, "battle-arena-stasis");
this.stasis.anchor.set(0.5, 0.5);
this.stasis.visible = false;
this.addChild(this.stasis);
// Add playing effect
this.frame = new Phaser.Image(this.game, 0, 0, `battle-arena-ship-normal-${this.enemy ? "enemy" : "own"}`, 0);
this.frame.anchor.set(0.5, 0.5);
@ -55,14 +64,6 @@ module TS.SpaceTac.UI {
this.position.set(ship.arena_x, ship.arena_y);
}
update() {
/*if (this.prevx != this.x || this.prevy != this.y) {
this.sprite.rotation = Math.atan2(this.y - this.prevy, this.x - this.prevx);
}
this.prevx = this.x;
this.prevy = this.y;*/
}
// Set the hovered state on this ship
// This will toggle the hover effect
setHovered(hovered: boolean) {
@ -75,6 +76,17 @@ module TS.SpaceTac.UI {
this.frame.loadTexture(`battle-arena-ship-${playing ? "playing" : "normal"}-${this.enemy ? "enemy" : "own"}`);
}
/**
* Activate the dead effect (stasis)
*/
setDead(dead = true) {
if (dead) {
this.displayEffect("stasis", false);
}
this.frame.alpha = dead ? 0.5 : 1.0;
Animation.setVisibility(this.game, this.stasis, dead, 400);
}
/**
* Move the sprite to a location
*

View file

@ -75,6 +75,7 @@ module TS.SpaceTac.UI {
this.action_bar = new ActionBar(this);
this.ship_list = new ShipList(this);
this.ship_tooltip = new ShipTooltip(this);
this.add.existing(this.ship_tooltip);
// Start processing the battle log
this.log_processor = new LogProcessor(this);
@ -130,7 +131,9 @@ module TS.SpaceTac.UI {
// Method called when cursor starts hovering over a ship (or its icon)
cursorOnShip(ship: Ship): void {
this.setShipHovered(ship);
if (!this.targetting || ship.alive) {
this.setShipHovered(ship);
}
}
// Method called when cursor stops hovering over a ship (or its icon)

View file

@ -140,6 +140,10 @@ module TS.SpaceTac.UI {
}
this.view.arena.markAsDead(event.ship);
this.view.ship_list.markAsDead(event.ship);
if (!event.initial) {
this.delayNextEvents(1000);
}
}
// Weapon used
@ -177,7 +181,7 @@ module TS.SpaceTac.UI {
// New drone deployed
private processDroneDeployedEvent(event: DroneDeployedEvent): void {
let duration = this.view.arena.addDrone(event.drone);
let duration = this.view.arena.addDrone(event.drone, !event.initial);
this.delayNextEvents(duration);
}

View file

@ -1,7 +1,8 @@
module TS.SpaceTac.UI {
// Tooltip to display ship information
export class ShipTooltip extends Phaser.Sprite {
export class ShipTooltip extends Phaser.Group {
battleview: BattleView;
background: Phaser.Image;
title: Phaser.Text;
attr_hull: Phaser.Text;
attr_shield: Phaser.Text;
@ -13,12 +14,16 @@ module TS.SpaceTac.UI {
attr_gravity: Phaser.Text;
attr_time: Phaser.Text;
active_effects: Phaser.Group;
stasis: Phaser.Image;
constructor(parent: BattleView) {
super(parent.game, 0, 0, "battle-ship-tooltip-own");
super(parent.game);
this.visible = false;
this.battleview = parent;
this.background = new Phaser.Image(this.game, 0, 0, "battle-ship-tooltip-own");
this.addChild(this.background);
this.title = new Phaser.Text(this.game, 250, 10, "", { font: "24pt Arial", fill: "#ffffff" });
this.title.anchor.set(0.5, 0);
this.addChild(this.title);
@ -59,17 +64,19 @@ module TS.SpaceTac.UI {
this.attr_time.anchor.set(0.5, 0.5);
this.addChild(this.attr_time);
this.stasis = new Phaser.Image(this.game, 16, 55, "battle-ship-tooltip-stasis");
this.stasis.visible = false;
this.addChild(this.stasis);
this.active_effects = new Phaser.Group(this.game);
this.addChild(this.active_effects);
parent.ui.add(this);
}
// Set current ship to display, null to hide
setShip(ship: Ship | null): void {
if (ship) {
var enemy = ship.getPlayer() != this.battleview.player;
this.loadTexture(`battle-ship-tooltip-${enemy ? "enemy" : "own"}`);
this.background.loadTexture(`battle-ship-tooltip-${enemy ? "enemy" : "own"}`);
// Find ship sprite to position next to it
var sprite = this.battleview.arena.findShipSprite(ship);
@ -109,7 +116,9 @@ module TS.SpaceTac.UI {
this.addEquipment(equipment, ship.sticky_effects.length + index);
});
Animation.fadeIn(this.game, this, 200, 0.9);
this.stasis.visible = !ship.alive;
Animation.fadeIn(this.game, this, 200);
} else {
Animation.fadeOut(this.game, this, 200);
}

View file

@ -170,7 +170,9 @@ module TS.SpaceTac.UI {
// Set the current target ship (when hovered)
setTargetShip(ship: Ship, dispatch: boolean = true): void {
this.setTarget(Target.newFromShip(ship), dispatch);
if (ship.alive) {
this.setTarget(Target.newFromShip(ship), dispatch);
}
}
// Set the current target in space (when hovered)