Added Damage Protector (visual feedback still missing)
This commit is contained in:
parent
7275288f13
commit
f62f2236f8
|
@ -16,12 +16,28 @@
|
|||
version="1.1"
|
||||
inkscape:version="0.92.1 r15371"
|
||||
sodipodi:docname="actions.svg"
|
||||
inkscape:export-filename="/home/michael/workspace/perso/spacetac/out/assets/images/equipment/shieldtransfer.png"
|
||||
inkscape:export-filename="/home/michael/workspace/perso/spacetac/out/assets/images/equipment/damageprotector.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90"
|
||||
viewBox="0 0 256 256">
|
||||
<defs
|
||||
id="defs4">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4953">
|
||||
<stop
|
||||
style="stop-color:#4191ac;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop4949" />
|
||||
<stop
|
||||
id="stop4973"
|
||||
offset="0.6337238"
|
||||
style="stop-color:#6d8688;stop-opacity:0.96470588" />
|
||||
<stop
|
||||
style="stop-color:#2e584f;stop-opacity:0.94901961"
|
||||
offset="1"
|
||||
id="stop4951" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient4958">
|
||||
|
@ -575,6 +591,42 @@
|
|||
y1="108.47467"
|
||||
x2="279.88568"
|
||||
y2="110.07293" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4953"
|
||||
id="radialGradient4955"
|
||||
cx="128"
|
||||
cy="128"
|
||||
fx="128"
|
||||
fy="128"
|
||||
r="110.79634"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter5465"
|
||||
x="-0.2172"
|
||||
width="1.4344"
|
||||
y="-0.2172"
|
||||
height="1.4344">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="1.6296207"
|
||||
id="feGaussianBlur5467" />
|
||||
</filter>
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
id="filter5535"
|
||||
x="-0.4452"
|
||||
width="1.8904"
|
||||
y="-0.4452"
|
||||
height="1.8904">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="0.52701551"
|
||||
id="feGaussianBlur5537" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
|
@ -583,11 +635,11 @@
|
|||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="3.8613642"
|
||||
inkscape:cy="88.185766"
|
||||
inkscape:zoom="2.8"
|
||||
inkscape:cx="138.46109"
|
||||
inkscape:cy="124.35486"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer10"
|
||||
inkscape:current-layer="layer11"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1030"
|
||||
|
@ -599,7 +651,8 @@
|
|||
inkscape:snap-nodes="true"
|
||||
inkscape:snap-bbox-midpoints="false"
|
||||
units="px"
|
||||
inkscape:snap-object-midpoints="true">
|
||||
inkscape:snap-object-midpoints="true"
|
||||
showguides="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3004"
|
||||
|
@ -1268,7 +1321,7 @@
|
|||
inkscape:groupmode="layer"
|
||||
id="layer10"
|
||||
inkscape:label="ShieldTransfer"
|
||||
style="display:inline">
|
||||
style="display:none">
|
||||
<g
|
||||
id="g4936"
|
||||
transform="matrix(0.86849352,0,0,0.86849352,21.648667,33.463089)">
|
||||
|
@ -1346,4 +1399,222 @@
|
|||
transform="matrix(-0.92576699,0.28645103,-0.28645103,-0.92576699,308.06375,180.7838)"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer11"
|
||||
inkscape:label="DamageProtector"
|
||||
style="display:inline">
|
||||
<circle
|
||||
style="fill:url(#radialGradient4955);fill-opacity:1;fill-rule:evenodd;stroke:#6c6c6c;stroke-width:7.49999961;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.57234043;stroke-miterlimit:4;stroke-dasharray:1.8749999,1.8749999;stroke-dashoffset:0"
|
||||
id="path4921"
|
||||
cx="128"
|
||||
cy="128"
|
||||
r="110.32759" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#d2ead7;stroke-width:7.49999952;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 128.79443,128.62609 74.10416,3.31455"
|
||||
id="path5363"
|
||||
inkscape:connector-curvature="0" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g4943"
|
||||
id="use4947"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="rotate(144.83146,129.4117,131.17457)" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#d2ead7;stroke-width:7.49999952;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 70.552838,87.430802 127.84742,128.15258"
|
||||
id="path5365"
|
||||
inkscape:connector-curvature="0" />
|
||||
<g
|
||||
id="g4943"
|
||||
transform="translate(-2.6042991,-2.6042994)">
|
||||
<circle
|
||||
r="20.341978"
|
||||
cy="91.692383"
|
||||
cx="73.867401"
|
||||
id="path4927"
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:12.08983517;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.41489366" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="use4939"
|
||||
d="M 44.634009,103.02611 C 46.255886,82.288564 62.977551,63.514951 81.20414,60.845617"
|
||||
style="fill:none;fill-rule:evenodd;stroke:#d2ead7;stroke-width:7.24609804;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#c0e6c8;stroke-width:6.38581514;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 47.991422,101.47181 C 49.420744,83.1963 64.157153,66.651557 80.219816,64.299136"
|
||||
id="path5469"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<circle
|
||||
style="fill:#b7d7c8;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.93749994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter5535)"
|
||||
id="path5473"
|
||||
cx="53.033005"
|
||||
cy="95.835587"
|
||||
r="1.420527"
|
||||
transform="matrix(1.7878646,1.3264786,-0.30857423,0.41590488,-10.61234,-8.0960556)" />
|
||||
<circle
|
||||
transform="matrix(1.7878646,1.3264786,-0.30857423,0.41590488,-10.61234,-8.0960556)"
|
||||
r="1.420527"
|
||||
cy="95.835587"
|
||||
cx="53.033005"
|
||||
id="circle5539"
|
||||
style="fill:#b7d7c8;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.93749994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter5535)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5541"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(3.3145629,-2.6042994)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5549"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(1.4205269,-7.5761437)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5551"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(6.6291258,-7.694521)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5553"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(1.1837725,-14.323647)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5555"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(7.5761437,-14.797156)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5559"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(14.442024,-16.691192)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5561"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(7.5761437,-21.189527)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5563"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(14.323647,-20.716018)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5565"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(13.258252,-27.108389)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5567"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(21.89979,-27.108389)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5569"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(28.173784,-30.422952)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5573"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(21.544659,-30.541329)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5575"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(27.93703,-18.821982)"
|
||||
style="opacity:0.64800002" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5577"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(18.940359,-3.9064491)"
|
||||
style="opacity:0.43399999" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5579"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(13.850138,-10.535575)"
|
||||
style="opacity:0.28000004" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#circle5539"
|
||||
id="use5581"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(6.5107485,2.3675449)"
|
||||
style="opacity:0.329" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#d2ead7;stroke-width:9.37499905;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 99.910392,155.37934 128.32093,128.15258"
|
||||
id="path5367"
|
||||
inkscape:connector-curvature="0" />
|
||||
<circle
|
||||
style="fill:#d3f8e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.6806401px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter5465)"
|
||||
id="path5361"
|
||||
cx="128"
|
||||
cy="128"
|
||||
r="9.0034294"
|
||||
transform="matrix(2.2706815,0,0,2.2706815,-162.64723,-162.64723)" />
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#g4943"
|
||||
id="use4945"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="rotate(-80.838515,124.57679,104.88647)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 59 KiB |
BIN
out/assets/images/equipment/damageprotector.png
Normal file
BIN
out/assets/images/equipment/damageprotector.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 55 KiB |
|
@ -329,5 +329,43 @@ module TS.SpaceTac {
|
|||
expect(battle.getTurnsBefore(battle.play_order[1])).toBe(1);
|
||||
expect(battle.getTurnsBefore(battle.play_order[2])).toBe(2);
|
||||
});
|
||||
|
||||
it("lists area effects", function () {
|
||||
let battle = new Battle();
|
||||
let ship = battle.fleets[0].addShip();
|
||||
|
||||
expect(imaterialize(battle.iAreaEffects(100, 50))).toEqual([]);
|
||||
|
||||
let drone1 = new Drone(ship);
|
||||
drone1.x = 120;
|
||||
drone1.y = 60;
|
||||
drone1.radius = 40;
|
||||
drone1.effects = [new DamageEffect(12)];
|
||||
battle.addDrone(drone1);
|
||||
let drone2 = new Drone(ship);
|
||||
drone2.x = 130;
|
||||
drone2.y = 70;
|
||||
drone2.radius = 20;
|
||||
drone2.effects = [new DamageEffect(14)];
|
||||
battle.addDrone(drone2);
|
||||
|
||||
expect(imaterialize(battle.iAreaEffects(100, 50))).toEqual([
|
||||
new DamageEffect(12)
|
||||
]);
|
||||
|
||||
let eq1 = ship.addSlot(SlotType.Weapon).attach(new Equipment(SlotType.Weapon));
|
||||
eq1.action = new ToggleAction(eq1, 0, 500, [new AttributeEffect("maneuvrability", 1)]);
|
||||
(<ToggleAction>eq1.action).activated = true;
|
||||
let eq2 = ship.addSlot(SlotType.Weapon).attach(new Equipment(SlotType.Weapon));
|
||||
eq2.action = new ToggleAction(eq2, 0, 500, [new AttributeEffect("maneuvrability", 2)]);
|
||||
(<ToggleAction>eq2.action).activated = false;
|
||||
let eq3 = ship.addSlot(SlotType.Weapon).attach(new Equipment(SlotType.Weapon));
|
||||
eq3.action = new ToggleAction(eq3, 0, 100, [new AttributeEffect("maneuvrability", 3)]);
|
||||
(<ToggleAction>eq3.action).activated = true;
|
||||
|
||||
expect(imaterialize(battle.iAreaEffects(100, 50))).toEqual([
|
||||
new DamageEffect(12), new AttributeEffect("maneuvrability", 1)
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -366,5 +366,17 @@ module TS.SpaceTac {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of area effects at a given location
|
||||
*/
|
||||
iAreaEffects(x: number, y: number): Iterator<BaseEffect> {
|
||||
let drones_in_range = ifilter(iarray(this.drones), drone => drone.isInRange(x, y));
|
||||
|
||||
return ichain(
|
||||
ichainit(imap(drones_in_range, drone => iarray(drone.effects))),
|
||||
ichainit(imap(this.iships(), ship => ship.iAreaEffects(x, y)))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,13 @@ module TS.SpaceTac {
|
|||
return `For ${this.duration} activation${this.duration > 1 ? "s" : ""}:\n${effects}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a location is in range
|
||||
*/
|
||||
isInRange(x: number, y: number): boolean {
|
||||
return Target.newFromLocation(x, y).getDistanceTo(this) <= this.radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of affected ships.
|
||||
*/
|
||||
|
|
|
@ -303,6 +303,16 @@ module TS.SpaceTac {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a toggle action.
|
||||
*/
|
||||
addToggleAction(power: LeveledValue, radius: LeveledValue, effects: EffectTemplate<BaseEffect>[]): void {
|
||||
this.base_modifiers.push((equipment, level) => {
|
||||
let reffects = effects.map(effect => effect.generate(level));
|
||||
equipment.action = new ToggleAction(equipment, resolveForLevel(power, level), resolveForLevel(radius, level), reffects);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the template has any damage effect (to know if is an offensive weapon)
|
||||
*/
|
||||
|
|
|
@ -482,5 +482,24 @@ module TS.SpaceTac.Specs {
|
|||
expect(ship.sticky_effects.length).toEqual(0);
|
||||
expect(ship.getAttribute("power_capacity")).toEqual(5);
|
||||
});
|
||||
|
||||
it("lists active effects", function () {
|
||||
let ship = new Ship();
|
||||
expect(imaterialize(ship.ieffects())).toEqual([]);
|
||||
|
||||
let equipment = ship.addSlot(SlotType.Engine).attach(new Equipment(SlotType.Engine));
|
||||
expect(imaterialize(ship.ieffects())).toEqual([]);
|
||||
|
||||
equipment.effects.push(new AttributeEffect("precision", 4));
|
||||
expect(imaterialize(ship.ieffects())).toEqual([
|
||||
new AttributeEffect("precision", 4)
|
||||
]);
|
||||
|
||||
ship.addStickyEffect(new StickyEffect(new AttributeLimitEffect("precision", 2), 4));
|
||||
expect(imaterialize(ship.ieffects())).toEqual([
|
||||
new AttributeEffect("precision", 4),
|
||||
new AttributeLimitEffect("precision", 2)
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -678,27 +678,41 @@ module TS.SpaceTac {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator over all effects active for this ship.
|
||||
*
|
||||
* This includes:
|
||||
* - Permanent equipment effects
|
||||
* - Sticky effects
|
||||
* - Area effects at current location
|
||||
*/
|
||||
ieffects(): Iterator<BaseEffect> {
|
||||
let battle = this.getBattle();
|
||||
let area_effects = battle ? battle.iAreaEffects(this.arena_x, this.arena_y) : IEMPTY;
|
||||
return ichain(
|
||||
ichainit(imap(iarray(this.slots), slot => slot.attached ? iarray(slot.attached.effects) : IEMPTY)),
|
||||
imap(iarray(this.sticky_effects), effect => effect.base),
|
||||
area_effects
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterator over area effects from this ship impacting a location
|
||||
*/
|
||||
iAreaEffects(x: number, y: number): Iterator<BaseEffect> {
|
||||
let distance = Target.newFromShip(this).getDistanceTo({ x: x, y: y });
|
||||
return ichainit(imap(iarray(this.getAvailableActions()), action => {
|
||||
if (action instanceof ToggleAction && action.activated && distance <= action.radius) {
|
||||
return iarray(action.effects);
|
||||
} else {
|
||||
return IEMPTY;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// Collect all effects to apply for updateAttributes
|
||||
private collectEffects(code: string): BaseEffect[] {
|
||||
var result: BaseEffect[] = [];
|
||||
|
||||
this.slots.forEach(slot => {
|
||||
if (slot.attached) {
|
||||
slot.attached.effects.forEach(effect => {
|
||||
if (effect.code == code) {
|
||||
result.push(effect);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.sticky_effects.forEach(effect => {
|
||||
if (effect.base.code == code) {
|
||||
result.push(effect.base);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
return imaterialize(ifilter(this.ieffects(), effect => effect.code == code));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
0
src/core/actions/ToggleAction.spec.ts
Normal file
0
src/core/actions/ToggleAction.spec.ts
Normal file
77
src/core/actions/ToggleAction.ts
Normal file
77
src/core/actions/ToggleAction.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
/// <reference path="BaseAction.ts"/>
|
||||
|
||||
module TS.SpaceTac {
|
||||
/**
|
||||
* Action to toggle some effects on the ship or around it, until next turn start
|
||||
*/
|
||||
export class ToggleAction extends BaseAction {
|
||||
// Power consumption (activation only)
|
||||
power: number
|
||||
|
||||
// Effect radius
|
||||
radius: number
|
||||
|
||||
// Effects applied
|
||||
effects: BaseEffect[]
|
||||
|
||||
// Equipment cannot be null
|
||||
equipment: Equipment
|
||||
|
||||
// Current activation status
|
||||
activated = false
|
||||
|
||||
constructor(equipment: Equipment, power = 1, radius = 0, effects: BaseEffect[] = [], name = "(De)activate") {
|
||||
super("toggle-" + equipment.code, name, false, equipment);
|
||||
|
||||
this.power = power;
|
||||
this.radius = radius;
|
||||
this.effects = effects;
|
||||
}
|
||||
|
||||
getActionPointsUsage(ship: Ship, target: Target | null): number {
|
||||
return this.activated ? 0 : this.power;
|
||||
}
|
||||
|
||||
getRangeRadius(ship: Ship): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
getBlastRadius(ship: Ship): number {
|
||||
return this.radius;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the effects applied by this action
|
||||
*/
|
||||
getEffects(ship: Ship): [Ship, BaseEffect][] {
|
||||
let target = Target.newFromShip(ship);
|
||||
let result: [Ship, BaseEffect][] = [];
|
||||
let radius = this.getBlastRadius(ship);
|
||||
let battle = ship.getBattle();
|
||||
let ships = (radius && battle) ? battle.collectShipsInCircle(target, radius, true) : ((target.ship && target.ship.alive) ? [target.ship] : []);
|
||||
ships.forEach(ship => {
|
||||
this.effects.forEach(effect => result.push([ship, effect]));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
protected customApply(ship: Ship, target: Target) {
|
||||
this.activated = !this.activated;
|
||||
ship.addBattleEvent(new ToggleEvent(ship, this, this.activated));
|
||||
// TODO Refresh area effects
|
||||
}
|
||||
|
||||
getEffectsDescription(): string {
|
||||
if (this.effects.length == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
let desc = `When active (power usage ${this.power})`;
|
||||
let effects = this.effects.map(effect => {
|
||||
let suffix = this.radius ? `in ${this.radius}km radius` : "on owner ship";
|
||||
return "• " + effect.getDescription() + " " + suffix;
|
||||
});
|
||||
return `${desc}:\n${effects.join("\n")}`;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,5 +44,31 @@ module TS.SpaceTac.Specs {
|
|||
expect(new DamageEffect(10).getDescription()).toEqual("do 10 damage");
|
||||
expect(new DamageEffect(10, 5).getDescription()).toEqual("do 10-15 damage");
|
||||
});
|
||||
|
||||
it("applies damage modifiers", function () {
|
||||
let ship = new Ship();
|
||||
TestTools.setShipHP(ship, 1000, 1000);
|
||||
let damage = new DamageEffect(200);
|
||||
|
||||
expect(damage.getEffectiveDamage(ship)).toEqual([200, 0]);
|
||||
|
||||
spyOn(ship, "ieffects").and.returnValues(
|
||||
isingle(new DamageModifierEffect(-15)),
|
||||
isingle(new DamageModifierEffect(20)),
|
||||
isingle(new DamageModifierEffect(-150)),
|
||||
isingle(new DamageModifierEffect(180)),
|
||||
iarray([new DamageModifierEffect(10), new DamageModifierEffect(-15)]),
|
||||
isingle(new DamageModifierEffect(3))
|
||||
);
|
||||
|
||||
expect(damage.getEffectiveDamage(ship)).toEqual([170, 0]);
|
||||
expect(damage.getEffectiveDamage(ship)).toEqual([240, 0]);
|
||||
expect(damage.getEffectiveDamage(ship)).toEqual([0, 0]);
|
||||
expect(damage.getEffectiveDamage(ship)).toEqual([400, 0]);
|
||||
expect(damage.getEffectiveDamage(ship)).toEqual([190, 0]);
|
||||
|
||||
damage = new DamageEffect(40);
|
||||
expect(damage.getEffectiveDamage(ship)).toEqual([41, 0]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -20,6 +20,19 @@ module TS.SpaceTac {
|
|||
this.span = span;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply damage modifiers to get the final damage factor
|
||||
*/
|
||||
getFactor(ship: Ship): number {
|
||||
let percent = 0;
|
||||
iforeach(ship.ieffects(), effect => {
|
||||
if (effect instanceof DamageModifierEffect) {
|
||||
percent += effect.factor;
|
||||
}
|
||||
});
|
||||
return (clamp(percent, -100, 100) + 100) / 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the effective damage done to both shield and hull (in this order)
|
||||
*/
|
||||
|
@ -28,6 +41,9 @@ module TS.SpaceTac {
|
|||
var hull: number;
|
||||
var shield: number;
|
||||
|
||||
// Apply modifiers
|
||||
damage = Math.round(damage * this.getFactor(ship));
|
||||
|
||||
// Apply on shields
|
||||
if (damage >= ship.values.shield.get()) {
|
||||
shield = ship.values.shield.get();
|
||||
|
|
21
src/core/effects/DamageModifierEffect.ts
Normal file
21
src/core/effects/DamageModifierEffect.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/// <reference path="BaseEffect.ts"/>
|
||||
|
||||
module TS.SpaceTac {
|
||||
/**
|
||||
* Modify damage on ships.
|
||||
*/
|
||||
export class DamageModifierEffect extends BaseEffect {
|
||||
// Percent factor (ex: -15 for -15%)
|
||||
factor: number
|
||||
|
||||
constructor(factor = 0) {
|
||||
super("damagemod");
|
||||
|
||||
this.factor = factor;
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
return `damage ${this.factor}%`;
|
||||
}
|
||||
}
|
||||
}
|
83
src/core/equipments/DamageProtector.spec.ts
Normal file
83
src/core/equipments/DamageProtector.spec.ts
Normal file
|
@ -0,0 +1,83 @@
|
|||
module TS.SpaceTac.Equipments {
|
||||
describe("DamageProtector", function () {
|
||||
it("generates equipment based on level", function () {
|
||||
let template = new DamageProtector();
|
||||
|
||||
let equipment = template.generate(1);
|
||||
expect(equipment.requirements).toEqual({ "skill_time": 1 });
|
||||
expect(equipment.action).toEqual(new ToggleAction(equipment, 2, 300, [
|
||||
new DamageModifierEffect(-30)
|
||||
]));
|
||||
|
||||
equipment = template.generate(2);
|
||||
expect(equipment.requirements).toEqual({ "skill_time": 3 });
|
||||
expect(equipment.action).toEqual(new ToggleAction(equipment, 2, 310, [
|
||||
new DamageModifierEffect(-31)
|
||||
]));
|
||||
|
||||
equipment = template.generate(3);
|
||||
expect(equipment.requirements).toEqual({ "skill_time": 5 });
|
||||
expect(equipment.action).toEqual(new ToggleAction(equipment, 2, 320, [
|
||||
new DamageModifierEffect(-32)
|
||||
]));
|
||||
|
||||
equipment = template.generate(10);
|
||||
expect(equipment.requirements).toEqual({ "skill_time": 19 });
|
||||
expect(equipment.action).toEqual(new ToggleAction(equipment, 3, 390, [
|
||||
new DamageModifierEffect(-39)
|
||||
]));
|
||||
});
|
||||
|
||||
it("reduces damage around the ship", function () {
|
||||
let battle = new Battle();
|
||||
let ship1 = battle.fleets[0].addShip();
|
||||
ship1.upgradeSkill("skill_time");
|
||||
let protector = ship1.addSlot(SlotType.Weapon).attach(new DamageProtector().generate(1));
|
||||
TestTools.setShipAP(ship1, 10);
|
||||
let ship2 = battle.fleets[0].addShip();
|
||||
let ship3 = battle.fleets[0].addShip();
|
||||
ship1.setArenaPosition(0, 0);
|
||||
ship2.setArenaPosition(100, 0);
|
||||
ship3.setArenaPosition(800, 0);
|
||||
battle.playing_ship = ship1;
|
||||
ship1.playing = true;
|
||||
expect(ship1.getAvailableActions()).toEqual([
|
||||
protector.action,
|
||||
new EndTurnAction()
|
||||
]);
|
||||
|
||||
TestTools.setShipHP(ship1, 100, 0);
|
||||
TestTools.setShipHP(ship2, 100, 0);
|
||||
TestTools.setShipHP(ship3, 100, 0);
|
||||
|
||||
iforeach(battle.iships(), ship => new DamageEffect(10).applyOnShip(ship, ship1));
|
||||
|
||||
expect(ship1.getValue("power")).toEqual(10);
|
||||
expect(ship1.getValue("hull")).toEqual(90);
|
||||
expect(ship2.getValue("hull")).toEqual(90);
|
||||
expect(ship3.getValue("hull")).toEqual(90);
|
||||
|
||||
let result = protector.action.apply(ship1, null);
|
||||
expect(result).toBe(true);
|
||||
expect((<ToggleAction>protector.action).activated).toBe(true);
|
||||
|
||||
iforeach(battle.iships(), ship => new DamageEffect(10).applyOnShip(ship, ship1));
|
||||
|
||||
expect(ship1.getValue("power")).toEqual(8);
|
||||
expect(ship1.getValue("hull")).toEqual(83);
|
||||
expect(ship2.getValue("hull")).toEqual(83);
|
||||
expect(ship3.getValue("hull")).toEqual(80);
|
||||
|
||||
result = protector.action.apply(ship1, null);
|
||||
expect(result).toBe(true);
|
||||
expect((<ToggleAction>protector.action).activated).toBe(false);
|
||||
|
||||
iforeach(battle.iships(), ship => new DamageEffect(10).applyOnShip(ship, ship1));
|
||||
|
||||
expect(ship1.getValue("power")).toEqual(8);
|
||||
expect(ship1.getValue("hull")).toEqual(73);
|
||||
expect(ship2.getValue("hull")).toEqual(73);
|
||||
expect(ship3.getValue("hull")).toEqual(70);
|
||||
});
|
||||
});
|
||||
}
|
14
src/core/equipments/DamageProtector.ts
Normal file
14
src/core/equipments/DamageProtector.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/// <reference path="../LootTemplate.ts"/>
|
||||
|
||||
module TS.SpaceTac.Equipments {
|
||||
export class DamageProtector extends LootTemplate {
|
||||
constructor() {
|
||||
super(SlotType.Weapon, "Damage Protector", "Extend a time-displacement subfield, to reduce damage taken by ships around");
|
||||
|
||||
this.setSkillsRequirements({ "skill_time": istep(1, irepeat(2)) });
|
||||
this.addToggleAction(istep(2, irepeat(0.2)), istep(300, irepeat(10)), [
|
||||
new EffectTemplate(new DamageModifierEffect(), { factor: istep(-30, irepeat(-1)) })
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,19 +4,19 @@ module TS.SpaceTac.Equipments {
|
|||
let template = new PowerDepleter();
|
||||
|
||||
let equipment = template.generate(1);
|
||||
expect(equipment.requirements).toEqual({ "skill_photons": 1 });
|
||||
expect(equipment.requirements).toEqual({ "skill_antimatter": 1 });
|
||||
expect(equipment.action).toEqual(new FireWeaponAction(equipment, 4, 500, 0, [new StickyEffect(new AttributeLimitEffect("power_capacity", 3), 2, true)]));
|
||||
|
||||
equipment = template.generate(2);
|
||||
expect(equipment.requirements).toEqual({ "skill_photons": 2 });
|
||||
expect(equipment.requirements).toEqual({ "skill_antimatter": 2 });
|
||||
expect(equipment.action).toEqual(new FireWeaponAction(equipment, 4, 520, 0, [new StickyEffect(new AttributeLimitEffect("power_capacity", 3), 2, true)]));
|
||||
|
||||
equipment = template.generate(3);
|
||||
expect(equipment.requirements).toEqual({ "skill_photons": 3 });
|
||||
expect(equipment.requirements).toEqual({ "skill_antimatter": 3 });
|
||||
expect(equipment.action).toEqual(new FireWeaponAction(equipment, 4, 540, 0, [new StickyEffect(new AttributeLimitEffect("power_capacity", 3), 2, true)]));
|
||||
|
||||
equipment = template.generate(10);
|
||||
expect(equipment.requirements).toEqual({ "skill_photons": 10 });
|
||||
expect(equipment.requirements).toEqual({ "skill_antimatter": 10 });
|
||||
expect(equipment.action).toEqual(new FireWeaponAction(equipment, 4, 680, 0, [new StickyEffect(new AttributeLimitEffect("power_capacity", 3), 2, true)]));
|
||||
});
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
module TS.SpaceTac.Equipments {
|
||||
export class PowerDepleter extends LootTemplate {
|
||||
constructor() {
|
||||
super(SlotType.Weapon, "Power Depleter", "Direct-hit weapon that creates an energy well near the target, sucking its power surplus");
|
||||
super(SlotType.Weapon, "Power Depleter", "Direct-hit weapon that creates an antimatter well near the target, sucking its power surplus");
|
||||
|
||||
this.setSkillsRequirements({ "skill_photons": 1 });
|
||||
this.setSkillsRequirements({ "skill_antimatter": 1 });
|
||||
this.setCooldown(irepeat(2), irepeat(3));
|
||||
this.addFireAction(irepeat(4), istep(500, irepeat(20)), 0, [
|
||||
new StickyEffectTemplate(new AttributeLimitEffect("power_capacity"), { "value": irepeat(3) }, irepeat(2))
|
||||
|
|
|
@ -6,7 +6,7 @@ module TS.SpaceTac.Equipments {
|
|||
*/
|
||||
export class RepairDrone extends LootTemplate {
|
||||
constructor() {
|
||||
super(SlotType.Weapon, "Repair Drone", "Drone able to repair small hull breaches, remotely controlled by human pilots");
|
||||
super(SlotType.Weapon, "Repair Drone", "Drone able to repair small hull breaches, using quantum patches");
|
||||
|
||||
this.setSkillsRequirements({ "skill_quantum": 1 });
|
||||
this.setCooldown(irepeat(1), istep(3, irepeat(0.2)));
|
||||
|
|
21
src/core/events/ToggleEvent.ts
Normal file
21
src/core/events/ToggleEvent.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/// <reference path="BaseBattleEvent.ts"/>
|
||||
|
||||
module TS.SpaceTac {
|
||||
/**
|
||||
* Event logged when a toggle action is activated or deactivated
|
||||
*/
|
||||
export class ToggleEvent extends BaseLogShipEvent {
|
||||
// Pointer to the action
|
||||
action: BaseAction
|
||||
|
||||
// true for activation, false for deactivation
|
||||
activated: boolean
|
||||
|
||||
constructor(ship: Ship, action: BaseAction, activated: boolean) {
|
||||
super("toggle", ship);
|
||||
|
||||
this.action = action;
|
||||
this.activated = activated;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -103,6 +103,7 @@ module TS.SpaceTac.UI {
|
|||
this.loadImage("equipment/submunitionmissile.png");
|
||||
this.loadImage("equipment/repairdrone.png");
|
||||
this.loadImage("equipment/shieldtransfer.png");
|
||||
this.loadImage("equipment/damageprotector.png");
|
||||
|
||||
// Load ships
|
||||
this.loadShip("avenger");
|
||||
|
|
Loading…
Reference in a new issue