1
0
Fork 0

Continued work on battle plan display

This commit is contained in:
Michaël Lemaire 2019-05-16 01:04:55 +02:00
parent a67f0a4f1d
commit 63729f8537
22 changed files with 611 additions and 872 deletions

View File

@ -25,6 +25,22 @@
enable-background="new">
<defs
id="defs4">
<linearGradient
inkscape:collect="always"
id="linearGradient2119">
<stop
style="stop-color:#6690a4;stop-opacity:0"
offset="0"
id="stop2115" />
<stop
id="stop2125"
offset="0.5"
style="stop-color:#6690a4;stop-opacity:1;" />
<stop
style="stop-color:#6690a4;stop-opacity:0"
offset="1"
id="stop2117" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient10876">
@ -1592,19 +1608,6 @@
stdDeviation="5.9752511"
id="feGaussianBlur5167" />
</filter>
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter6697"
x="-0.027217617"
width="1.0544352"
y="-0.12710458"
height="1.2542092">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="14.534661"
id="feGaussianBlur6699" />
</filter>
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
@ -1757,6 +1760,80 @@
x2="1883.7325"
y2="207.43018"
gradientTransform="translate(3.0517578e-5,2.6187973)" />
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter1750"
x="-0.1164"
width="1.2328"
y="-0.1164"
height="1.2328">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="3.8752988"
id="feGaussianBlur1752" />
</filter>
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter1892"
x="-0.027217617"
width="1.0544352"
y="-0.12710458"
height="1.2542092">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="14.534661"
id="feGaussianBlur1894" />
</filter>
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter1955"
x="-0.050050774"
width="1.1001015"
y="-0.051593727"
height="1.1031875">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="5.8998449"
id="feGaussianBlur1957" />
</filter>
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter2023"
x="-0.088778358"
width="1.1775567"
y="-0.035046048"
height="1.0700921">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="10.464944"
id="feGaussianBlur2025" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2119"
id="linearGradient2121"
x1="1711.952"
y1="74.84767"
x2="1861.0587"
y2="74.84767"
gradientUnits="userSpaceOnUse" />
<filter
inkscape:collect="always"
style="color-interpolation-filters:sRGB"
id="filter2187"
x="-0.035441109"
width="1.0708822"
y="-0.11336306"
height="1.2267261">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="2.4381933"
id="feGaussianBlur2189" />
</filter>
</defs>
<sodipodi:namedview
id="base"
@ -1765,11 +1842,11 @@
borderopacity="1"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="0.49999998"
inkscape:cx="936.46786"
inkscape:cy="679.54174"
inkscape:zoom="0.99999996"
inkscape:cx="1356.0567"
inkscape:cy="369.52755"
inkscape:document-units="px"
inkscape:current-layer="layer12"
inkscape:current-layer="g1980"
showgrid="false"
units="px"
showguides="false"
@ -2119,7 +2196,7 @@
<g
id="g6654"
transform="translate(0,-14.826785)"
style="opacity:0.15899999;filter:url(#filter6697)">
style="opacity:0.15899999;filter:url(#filter1892)">
<rect
transform="scale(1,-1)"
y="-1001.035"
@ -2314,138 +2391,363 @@
x="1696.0001"
y="51.99995" />
<g
id="g10865"
style="fill:url(#linearGradient10878);fill-opacity:1;stroke:none">
<rect
y="162.88245"
x="1393.3435"
height="143.33305"
width="490.38892"
id="rect10862"
style="opacity:1;fill:url(#linearGradient10886);fill-opacity:1;stroke:none;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
id="g2250">
<use
height="100%"
width="100%"
transform="translate(0,236.88076)"
id="use10867"
xlink:href="#g10865"
y="0"
x="0" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
x="1781.8724"
y="383.07306"
id="text10984"><tspan
sodipodi:role="line"
id="tspan10982"
x="1781.8724"
y="383.07306"
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px">Move</tspan></text>
</g>
<g
id="g2013"
transform="translate(16.970651,12)">
<g
id="g1980">
<path
style="display:inline;opacity:1;fill:#040404;fill-opacity:1;stroke:#3a6479;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
d="m 1650.7451,813.2182 h 227.3971 v 131.72173 h -227.3971 z"
id="rect10858-8" />
<g
id="g6654-6"
transform="matrix(0.71087346,0,0,-0.16817305,1272.6832,987.16528)"
style="display:inline;opacity:0.244;filter:url(#filter2023);enable-background:new">
<rect
transform="scale(1,-1)"
y="-1001.035"
x="550.31677"
height="43.911194"
width="282.90527"
id="rect6642-0"
style="opacity:1;fill:#3a6479;fill-opacity:1;stroke:none;stroke-width:1.45974934;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.47096773" />
<use
height="100%"
width="100%"
transform="translate(0,-84.092779)"
id="use6644-6"
xlink:href="#rect6642-0"
y="0"
x="0" />
<use
height="100%"
width="100%"
transform="translate(0,-168.18557)"
id="use6646-2"
xlink:href="#rect6642-0"
y="0"
x="0" />
<use
height="100%"
width="100%"
transform="translate(0,-252.27828)"
id="use6648-6"
xlink:href="#rect6642-0"
y="0"
x="0" />
<use
x="0"
y="0"
xlink:href="#rect6642-0"
id="use1959"
transform="translate(0,-336.37106)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#rect6642-0"
id="use1961"
transform="translate(0,-420.46382)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#rect6642-0"
id="use1963"
transform="translate(0,-504.55659)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#rect6642-0"
id="use1965"
transform="translate(0,-588.64935)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#rect6642-0"
id="use1967"
transform="translate(0,-672.74209)"
width="100%"
height="100%" />
</g>
</g>
<g
id="g1990">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
x="1671.7867"
y="844.14001"
id="text5185-6-2-8-3"><tspan
sodipodi:role="line"
id="tspan5183-2-2-8-8"
x="1671.7867"
y="844.14001"
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px">Power available</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
x="1694.7555"
y="901.44666"
id="text5185-6-2-8-3-3"><tspan
sodipodi:role="line"
id="tspan5183-2-2-8-8-3"
x="1694.7555"
y="901.44666"
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px">Power used</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
x="1758.5397"
y="872.79333"
id="text5185-6-2-8-3-38"><tspan
sodipodi:role="line"
id="tspan5183-2-2-8-8-0"
x="1758.5397"
y="872.79333"
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px">7</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
x="1757.8436"
y="930.09998"
id="text5185-6-2-8-3-38-4"><tspan
sodipodi:role="line"
id="tspan5183-2-2-8-8-0-7"
x="1757.8436"
y="930.09998"
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px">5</tspan></text>
</g>
</g>
<g
id="g2245">
<g
style="fill:url(#linearGradient10878);fill-opacity:1;stroke:none"
id="g10865">
<g
id="g2171">
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path2127"
d="m 1746.623,111.26366 h 137.1095 v 51.61879 H 1718.623 Z"
style="opacity:1;fill:#131d23;fill-opacity:1;stroke:none;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter2187)" />
<path
style="opacity:1;fill:#1b3b4b;fill-opacity:1;stroke:none;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1746.623,111.26366 h 137.1095 v 51.61879 H 1718.623 Z"
id="rect10882"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
<rect
style="opacity:1;fill:url(#linearGradient10886);fill-opacity:1;stroke:none;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect10862"
width="490.38892"
height="143.33305"
x="1393.3435"
y="162.88245" />
</g>
<text
id="text5185-6-2-8"
y="147.07306"
x="1765.1615"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
xml:space="preserve"><tspan
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px"
y="147.07306"
x="1765.1615"
id="tspan5183-2-2-8"
sodipodi:role="line">Passive</tspan></text>
<text
id="text11094"
y="227.80072"
x="1693.5588"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:20px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
xml:space="preserve"><tspan
id="tspan11096"
style="font-size:21.33333397px;line-height:20px;text-align:center;text-anchor:middle;fill:#9fc4d6;fill-opacity:1"
y="227.80072"
x="1693.5588"
sodipodi:role="line">No available action</tspan><tspan
id="tspan11104"
style="font-size:21.33333397px;line-height:20px;text-align:center;text-anchor:middle;fill:#9fc4d6;fill-opacity:1"
y="253.73456"
x="1693.5588"
sodipodi:role="line">of this type</tspan></text>
</g>
<g
id="g2266">
<use
height="100%"
width="100%"
transform="translate(0,473.76157)"
id="use10869"
xlink:href="#g10865"
y="0"
x="0" />
<text
id="text5185-6-2-8-8"
y="619"
x="1776.3412"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
xml:space="preserve"><tspan
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px"
y="619"
x="1776.3412"
id="tspan5183-2-2-8-6"
sodipodi:role="line">Active</tspan></text>
<g
transform="translate(0,237.58789)"
id="g1832">
<g
transform="translate(1654.2529,534.05733)"
id="g5177-4-7"
style="display:inline;enable-background:new"
inkscape:export-filename="/home/michael/workspace/spacetac/data/stage1/image/menu/button.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<path
style="fill:#3a6479;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.56945151px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 218.92561,-63.492682 186.5282,-7.378725 H 91.451531 l -32.39741,-56.113957 32.39741,-56.113948 H 186.5282 Z"
id="path5068-0-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#1b3b4b;fill-opacity:1;fill-rule:evenodd;stroke:#6690a4;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 65.633371,-63.493012 c 9.70143,16.80661 19.40286,33.61321 29.1043,50.419815 H 183.2415 c 9.70181,-16.806605 19.40361,-33.613205 29.10542,-50.419815 -9.70181,-16.80623 -19.40361,-33.61247 -29.10542,-50.418698 H 94.737671 c -9.70144,16.806228 -19.40287,33.612468 -29.1043,50.418698 z"
id="path5071-9-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#f4f4f4;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 140.30091,-108.21719 H 98.024261 c -6.6381,11.501668 -13.27619,23.003358 -19.91429,34.505038"
id="path5083-1-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<rect
style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.66971588;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter1750)"
id="rect1572"
width="79.903069"
height="79.903069"
x="99.03862"
y="-103.44398" />
<rect
y="-103.44398"
x="99.03862"
height="79.903069"
width="79.903069"
id="rect1754"
style="opacity:1;fill:#3a6479;fill-opacity:1;stroke:none;stroke-width:4.66971588;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0"
id="path2037"
d="m 184.17236,-63.492702 c 4.31847,7.481244 8.63693,14.962483 12.9554,22.443724 h 0.22428 c 4.31863,-7.481241 8.63726,-14.96248 12.9559,-22.443724 -4.31864,-7.481075 -8.63727,-14.962154 -12.9559,-22.443227 h -0.22428 c -4.31847,7.481073 -8.63693,14.962152 -12.9554,22.443227 z"
style="fill:#1b3b4b;fill-opacity:1;fill-rule:evenodd;stroke:#6690a4;stroke-width:1.3341732;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
<use
transform="translate(2085.0001,116.63784)"
style="display:inline;enable-background:new"
x="0"
y="0"
xlink:href="#image5902"
id="use1808"
width="100%"
height="100%" />
<text
id="text5185-6-2-8-3-38-4-7"
y="478.92749"
x="1844.9592"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
xml:space="preserve"><tspan
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px"
y="478.92749"
x="1844.9592"
id="tspan5183-2-2-8-8-0-7-9"
sodipodi:role="line">5</tspan></text>
</g>
</g>
<g
transform="translate(-5.0909823,236.46289)"
style="display:inline;enable-background:new"
id="g2079">
<path
style="opacity:1;fill:#1b3b4b;fill-opacity:1;stroke:none;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1746.623,111.26366 137.1095,0 v 51.61879 H 1718.623 Z"
id="rect10882"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
id="path2065"
d="m 1712.9662,74.84767 h 147.0783"
style="fill:none;stroke:url(#linearGradient2121);stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<use
style="opacity:0.75"
height="100%"
width="100%"
transform="translate(0,6.5833133)"
id="use2069"
xlink:href="#path2065"
y="0"
x="0" />
<use
style="opacity:0.5"
height="100%"
width="100%"
transform="translate(0,13.166688)"
id="use2071"
xlink:href="#path2065"
y="0"
x="0" />
<use
style="opacity:0.25"
height="100%"
width="100%"
transform="translate(0,19.750001)"
id="use2073"
xlink:href="#path2065"
y="0"
x="0" />
</g>
<use
x="0"
y="0"
xlink:href="#g10865"
id="use10867"
transform="translate(0,236.88076)"
xlink:href="#g2079"
id="use2099"
transform="translate(0,236.87501)"
width="100%"
height="100%" />
<use
x="0"
y="0"
xlink:href="#g10865"
id="use10869"
transform="translate(0,473.76157)"
xlink:href="#g2079"
id="use2101"
transform="translate(0,473.75002)"
width="100%"
height="100%" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
x="1765.1615"
y="147.07306"
id="text5185-6-2-8"><tspan
sodipodi:role="line"
id="tspan5183-2-2-8"
x="1765.1615"
y="147.07306"
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px">Passive</tspan></text>
<text
id="text10984"
y="383.07306"
x="1781.8724"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
xml:space="preserve"><tspan
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px"
y="383.07306"
x="1781.8724"
id="tspan10982"
sodipodi:role="line">Move</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
x="1776.3412"
y="619"
id="text5185-6-2-8-8"><tspan
sodipodi:role="line"
id="tspan5183-2-2-8-6"
x="1776.3412"
y="619"
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px">Active</tspan></text>
<rect
style="display:inline;opacity:1;fill:#040404;fill-opacity:1;stroke:#3a6479;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new"
id="rect10858-8"
width="227.39709"
height="131.72173"
x="1650.7451"
y="813.2182" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
x="1671.7867"
y="844.14001"
id="text5185-6-2-8-3"><tspan
sodipodi:role="line"
id="tspan5183-2-2-8-8"
x="1671.7867"
y="844.14001"
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px">Power available</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
x="1694.7555"
y="901.44666"
id="text5185-6-2-8-3-3"><tspan
sodipodi:role="line"
id="tspan5183-2-2-8-8-3"
x="1694.7555"
y="901.44666"
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px">Power used</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
x="1758.5397"
y="872.79333"
id="text5185-6-2-8-3-38"><tspan
sodipodi:role="line"
id="tspan5183-2-2-8-8-0"
x="1758.5397"
y="872.79333"
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px">7</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:12.5px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;display:inline;fill:#9fc4d6;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;enable-background:new"
x="1757.8436"
y="930.09998"
id="text5185-6-2-8-3-38-4"><tspan
sodipodi:role="line"
id="tspan5183-2-2-8-8-0-7"
x="1757.8436"
y="930.09998"
style="font-size:24px;fill:#9fc4d6;fill-opacity:1;stroke-width:0.5px">5</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:20px;font-family:DAGGERSQUARE;-inkscape-font-specification:DAGGERSQUARE;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="1693.5588"
y="227.80072"
id="text11094"><tspan
sodipodi:role="line"
x="1693.5588"
y="227.80072"
style="font-size:21.33333397px;line-height:20px;text-align:center;text-anchor:middle;fill:#9fc4d6;fill-opacity:1"
id="tspan11096">No available action</tspan><tspan
sodipodi:role="line"
x="1693.5588"
y="253.73456"
style="font-size:21.33333397px;line-height:20px;text-align:center;text-anchor:middle;fill:#9fc4d6;fill-opacity:1"
id="tspan11104">of this type</tspan></text>
</g>
<g
inkscape:groupmode="layer"
@ -3284,12 +3586,12 @@
<image
sodipodi:absref="/home/michael/workspace/spacetac/graphics/ui/pics/portrait.png"
xlink:href="pics/portrait.png"
width="183.97668"
height="183.97649"
preserveAspectRatio="none"
id="image6587"
y="497.55182"
x="465.07187"
y="497.55182" />
id="image6587"
preserveAspectRatio="none"
height="183.97649"
width="183.97668" />
</g>
</g>
<g
@ -3413,13 +3715,13 @@
<image
sodipodi:absref="/home/michael/workspace/spacetac/graphics/ui/pics/action.png"
xlink:href="pics/action.png"
style="display:inline;enable-background:new"
width="83.066681"
height="83.066681"
preserveAspectRatio="none"
id="image5902"
y="312.3288"
x="-333.53336"
y="312.3288" />
id="image5902"
preserveAspectRatio="none"
height="83.066681"
width="83.066681"
style="display:inline;enable-background:new" />
<g
id="g10025-6"
style="display:inline;fill:none;enable-background:new"
@ -3720,14 +4022,14 @@
<image
sodipodi:absref="/home/michael/workspace/spacetac/graphics/ui/pics/sprite.png"
xlink:href="pics/sprite.png"
width="84.542961"
height="84.542961"
preserveAspectRatio="none"
id="image9266"
x="-410.59262"
y="236.27179"
transform="translate(0,-27.637839)"
style="enable-background:new;display:inline"
transform="translate(0,-27.637839)" />
y="236.27179"
x="-410.59262"
id="image9266"
preserveAspectRatio="none"
height="84.542961"
width="84.542961" />
<g
style="enable-background:new;display:inline"
id="g8766"
@ -4327,13 +4629,13 @@
<image
sodipodi:absref="/home/michael/workspace/spacetac/graphics/ui/pics/portrait.png"
xlink:href="pics/portrait.png"
width="76.394867"
height="76.394867"
preserveAspectRatio="none"
id="image5590"
x="816.54016"
style="display:inline;enable-background:new"
y="313.90408"
style="display:inline;enable-background:new" />
x="816.54016"
id="image5590"
preserveAspectRatio="none"
height="76.394867"
width="76.394867" />
</g>
<use
transform="translate(96.619495)"

Before

Width:  |  Height:  |  Size: 178 KiB

After

Width:  |  Height:  |  Size: 190 KiB

View File

@ -1,29 +0,0 @@
module TK.SpaceTac.Specs {
function checkLocation(check: TestContext, got: IArenaLocation, expected_x: number, expected_y: number) {
check.equals(got.x, expected_x, `x differs (${got.x},${got.y}) (${expected_x},${expected_y})`);
check.equals(got.y, expected_y, `y differs (${got.x},${got.y}) (${expected_x},${expected_y})`);
}
testing("HexagonalArenaGrid", test => {
test.case("snaps coordinates to the nearest grid point, on a biased grid", check => {
let grid = new HexagonalArenaGrid(4, 0.75);
checkLocation(check, grid.snap({ x: 0, y: 0 }), 0, 0);
checkLocation(check, grid.snap({ x: 1, y: 0 }), 0, 0);
checkLocation(check, grid.snap({ x: 1.9, y: 0 }), 0, 0);
checkLocation(check, grid.snap({ x: 2.1, y: 0 }), 4, 0);
checkLocation(check, grid.snap({ x: 1, y: 1 }), 0, 0);
checkLocation(check, grid.snap({ x: 1, y: 2 }), 2, 3);
checkLocation(check, grid.snap({ x: -1, y: -1 }), 0, 0);
checkLocation(check, grid.snap({ x: -2, y: -2 }), -2, -3);
checkLocation(check, grid.snap({ x: -3, y: -1 }), -4, 0);
checkLocation(check, grid.snap({ x: 6, y: -5 }), 8, -6);
});
test.case("snaps coordinates to the nearest grid point, on a regular grid", check => {
let grid = new HexagonalArenaGrid(10);
checkLocation(check, grid.snap({ x: 0, y: 0 }), 0, 0);
checkLocation(check, grid.snap({ x: 8, y: 0 }), 10, 0);
checkLocation(check, grid.snap({ x: 1, y: 6 }), 5, 10 * Math.sqrt(0.75));
});
});
}

View File

@ -1,34 +0,0 @@
module TK.SpaceTac {
/**
* Abstract grid for the arena where the battle takes place
*
* The grid is used to snap arena coordinates for ships and targets
*/
export interface IArenaGrid {
snap(loc: IArenaLocation): IArenaLocation;
}
/**
* Hexagonal unbounded arena grid
*
* This grid is composed of regular hexagons where all vertices are at a same distance "unit" of the hexagon center
*/
export class HexagonalArenaGrid implements IArenaGrid {
private yunit: number;
constructor(private unit: number, private yfactor = Math.sqrt(0.75)) {
this.yunit = unit * yfactor;
}
snap(loc: IArenaLocation): IArenaLocation {
let yr = Math.round(loc.y / this.yunit);
let xr: number;
if (yr % 2 == 0) {
xr = Math.round(loc.x / this.unit);
} else {
xr = Math.round((loc.x - 0.5 * this.unit) / this.unit) + 0.5;
}
return new ArenaLocation((xr * this.unit) || 0, (yr * this.yunit) || 0);
}
}
}

View File

@ -3,9 +3,6 @@ module TK.SpaceTac {
* A turn-based battle between fleets
*/
export class Battle {
// Grid for the arena
grid?: IArenaGrid
// Battle outcome, if the battle has ended
outcome: BattleOutcome | null = null
@ -34,15 +31,11 @@ module TK.SpaceTac {
// Size of the battle area
width: number
height: number
border = 50
ship_separation = 100
// Indicator that an AI is playing
ai_playing = false
constructor(fleet1 = new Fleet(new Player("Attacker")), fleet2 = new Fleet(new Player("Defender")), width = 1808, height = 948) {
this.grid = new HexagonalArenaGrid(50);
constructor(fleet1 = new Fleet(new Player("Attacker")), fleet2 = new Fleet(new Player("Defender")), width = 1920, height = 1080) {
this.fleets = [fleet1, fleet2];
this.ships = new RObjectContainer(fleet1.ships.concat(fleet2.ships));
this.play_order = [];

View File

@ -58,5 +58,62 @@ module TK.SpaceTac.Specs {
{ action: action1.id, category: action1.getCategory(), target: Target.newFromShip(ship) }
]);
});
test.case("replaces existing actions", check => {
const battle = new Battle();
const ship = battle.fleets[0].addShip();
const action1 = ship.actions.addCustom(new BaseAction());
const action2 = ship.actions.addCustom(new BaseAction());
const planning = new BattlePlanning(battle);
check.equals(planning.getShipPlan(ship).actions, []);
planning.addAction(ship, action1, Target.newFromShip(ship));
check.equals(planning.getShipPlan(ship).actions, [
{ action: action1.id, category: action1.getCategory(), target: Target.newFromShip(ship) }
]);
planning.addAction(ship, action2, Target.newFromShip(ship));
check.equals(planning.getShipPlan(ship).actions, [
{ action: action1.id, category: action1.getCategory(), target: Target.newFromShip(ship) },
{ action: action2.id, category: action2.getCategory(), target: Target.newFromShip(ship) }
]);
planning.addAction(ship, action1, Target.newFromLocation(5, 1));
check.equals(planning.getShipPlan(ship).actions, [
{ action: action2.id, category: action2.getCategory(), target: Target.newFromShip(ship) },
{ action: action1.id, category: action1.getCategory(), target: Target.newFromLocation(5, 1) }
]);
});
test.case("replaces other similar actions", check => {
const battle = new Battle();
const ship = battle.fleets[0].addShip();
const active1 = ship.actions.addCustom(new TriggerAction());
const active2 = ship.actions.addCustom(new TriggerAction());
const move1 = ship.actions.addCustom(new MoveAction());
const move2 = ship.actions.addCustom(new MoveAction());
const planning = new BattlePlanning(battle);
planning.addAction(ship, active1);
check.equals(planning.getShipPlan(ship).actions, [
{ action: active1.id, category: active1.getCategory(), target: undefined }
]);
planning.addAction(ship, active2);
check.equals(planning.getShipPlan(ship).actions, [
{ action: active2.id, category: active2.getCategory(), target: undefined }
]);
planning.addAction(ship, move1);
check.equals(planning.getShipPlan(ship).actions, [
{ action: active2.id, category: active2.getCategory(), target: undefined },
{ action: move1.id, category: move1.getCategory(), target: undefined }
]);
planning.addAction(ship, move2);
check.equals(planning.getShipPlan(ship).actions, [
{ action: active2.id, category: active2.getCategory(), target: undefined },
{ action: move2.id, category: move2.getCategory(), target: undefined }
]);
});
});
}

View File

@ -37,11 +37,12 @@ namespace TK.SpaceTac {
*/
addAction(ship: Ship, action: BaseAction, target?: Target) {
const plan = this.getShipPlan(ship);
if (any(plan.actions, iaction => action.is(iaction.action))) {
// TODO replace (or remove if toggle action ?)
if (action.getCategory() == ActionCategory.PASSIVE) {
plan.actions = plan.actions.filter(iaction => !action.is(iaction.action));
} else {
plan.actions.push({ action: action.id, category: action.getCategory(), target });
plan.actions = plan.actions.filter(iaction => iaction.category != action.getCategory());
}
plan.actions.push({ action: action.id, category: action.getCategory(), target });
}
/**

View File

@ -1,43 +0,0 @@
module TK.SpaceTac.Specs {
testing("ExclusionAreas", test => {
test.case("constructs from a ship or battle", check => {
let battle = new Battle();
battle.border = 17;
battle.ship_separation = 31;
let ship1 = battle.fleets[0].addShip();
ship1.setArenaPosition(12, 5);
let ship2 = battle.fleets[1].addShip();
ship2.setArenaPosition(43, 89);
let exclusion = ExclusionAreas.fromBattle(battle);
check.equals(exclusion.hard_border, 17);
check.equals(exclusion.effective_obstacle, 31);
check.equals(exclusion.obstacles, [new ArenaLocationAngle(12, 5), new ArenaLocationAngle(43, 89)]);
exclusion = ExclusionAreas.fromBattle(battle, [ship1], 120);
check.equals(exclusion.hard_border, 17);
check.equals(exclusion.effective_obstacle, 120);
check.equals(exclusion.obstacles, [new ArenaLocationAngle(43, 89)]);
exclusion = ExclusionAreas.fromBattle(battle, [ship2], 10);
check.equals(exclusion.hard_border, 17);
check.equals(exclusion.effective_obstacle, 31);
check.equals(exclusion.obstacles, [new ArenaLocationAngle(12, 5)]);
exclusion = ExclusionAreas.fromShip(ship1);
check.equals(exclusion.hard_border, 17);
check.equals(exclusion.effective_obstacle, 31);
check.equals(exclusion.obstacles, [new ArenaLocationAngle(43, 89)]);
exclusion = ExclusionAreas.fromShip(ship2, 99);
check.equals(exclusion.hard_border, 17);
check.equals(exclusion.effective_obstacle, 99);
check.equals(exclusion.obstacles, [new ArenaLocationAngle(12, 5)]);
exclusion = ExclusionAreas.fromShip(ship2, 10, false);
check.equals(exclusion.hard_border, 17);
check.equals(exclusion.effective_obstacle, 31);
check.equals(exclusion.obstacles, [new ArenaLocationAngle(12, 5), new ArenaLocationAngle(43, 89)]);
})
})
}

View File

@ -1,96 +0,0 @@
module TK.SpaceTac {
/**
* Helper for working with exclusion areas (areas where a ship cannot go)
*
* There are three types of exclusion:
* - Hard border exclusion, that prevents a ship from being too close to the battle edges
* - Hard obstacle exclusion, that prevents two ships from being too close to each other
* - Soft obstacle exclusion, usually associated with an engine, that prevents a ship from moving too close to others
*/
export class ExclusionAreas {
xmin: number
xmax: number
ymin: number
ymax: number
active: boolean
hard_border = 50
hard_obstacle = 100
effective_obstacle = this.hard_obstacle
obstacles: ArenaLocation[] = []
constructor(width: number, height: number) {
this.xmin = 0;
this.xmax = width - 1;
this.ymin = 0;
this.ymax = height - 1;
this.active = width > 0 && height > 0;
}
/**
* Build an exclusion helper from a battle.
*/
static fromBattle(battle: Battle, ignore_ships: Ship[] = [], soft_distance = 0): ExclusionAreas {
let result = new ExclusionAreas(battle.width, battle.height);
result.hard_border = battle.border;
result.hard_obstacle = battle.ship_separation;
let obstacles = imap(ifilter(battle.iships(true), ship => !contains(ignore_ships, ship)), ship => ship.location);
result.configure(imaterialize(obstacles), soft_distance);
return result;
}
/**
* Build an exclusion helper for a ship.
*
* If *ignore_self* is True, the ship will itself not be included in exclusion areas.
*/
static fromShip(ship: Ship, soft_distance = 0, ignore_self = true): ExclusionAreas {
let battle = ship.getBattle();
if (battle) {
return ExclusionAreas.fromBattle(battle, ignore_self ? [ship] : [], soft_distance);
} else {
return new ExclusionAreas(0, 0);
}
}
/**
* Configure the areas for next check calls.
*/
configure(obstacles: ArenaLocation[], soft_distance: number) {
this.obstacles = obstacles;
this.effective_obstacle = Math.max(soft_distance, this.hard_obstacle);
}
/**
* Keep a location outside exclusion areas, when coming from a source.
*
* It will return the furthest location on the [source, location] segment, that is not inside an exclusion
* area.
*/
stopBefore(location: ArenaLocation, source: ArenaLocation): ArenaLocation {
if (!this.active) {
return location;
}
let target = Target.newFromLocation(location.x, location.y);
// Keep out of arena borders
target = target.keepInsideRectangle(this.xmin + this.hard_border, this.ymin + this.hard_border,
this.xmax - this.hard_border, this.ymax - this.hard_border,
source.x, source.y);
// Apply collision prevention
let obstacles = sorted(this.obstacles, (a, b) => cmp(arenaDistance(a, source), arenaDistance(b, source), true));
obstacles.forEach(s => {
let new_target = target.moveOutOfCircle(s.x, s.y, this.effective_obstacle, source.x, source.y);
if (target != new_target && arenaDistance(s, source) < this.effective_obstacle) {
// Already inside the nearest ship's exclusion area
target = Target.newFromLocation(source.x, source.y);
} else {
target = new_target;
}
});
return new ArenaLocation(target.x, target.y);
}
}
}

View File

@ -107,32 +107,6 @@ module TK.SpaceTac.Specs {
]);
});
test.case("accounts for exclusion areas for the approach", check => {
let [ship, simulator, action] = simpleWeaponCase(100, 5, 1, 50);
ship.setArenaPosition(300, 200);
let battle = new Battle();
battle.fleets[0].addShip(ship);
let ship1 = battle.fleets[0].addShip();
let moveaction = nn(simulator.findEngine());
(<any>moveaction).safety_distance = 30;
battle.ship_separation = 30;
check.same(simulator.getApproach(moveaction, Target.newFromLocation(350, 200), 100), ApproachSimulationError.NO_MOVE_NEEDED);
check.same(simulator.getApproach(moveaction, Target.newFromLocation(400, 200), 100), ApproachSimulationError.NO_MOVE_NEEDED);
check.equals(simulator.getApproach(moveaction, Target.newFromLocation(500, 200), 100), new Target(400, 200));
ship1.setArenaPosition(420, 200);
check.patch(simulator, "scanCircle", () => iarray([
new Target(400, 200),
new Target(410, 200),
new Target(410, 230),
new Target(420, 210),
new Target(480, 260),
]));
check.equals(simulator.getApproach(moveaction, Target.newFromLocation(500, 200), 100), new Target(410, 230));
});
test.case("moves to get in range, even if not enough AP to fire", check => {
let [ship, simulator, action] = simpleWeaponCase(8, 3, 2, 5);
let result = simulator.simulateAction(action, new Target(ship.arena_x + 18, ship.arena_y, null));

View File

@ -133,7 +133,6 @@ module TK.SpaceTac {
result.move_location = Target.newFromShip(this.ship);
if (action instanceof MoveAction) {
let corrected_target = action.applyReachableRange(this.ship, target, move_margin);
corrected_target = action.applyExclusion(this.ship, corrected_target);
if (corrected_target) {
result.need_move = target.getDistanceTo(this.ship.location) > 0;
move_target = corrected_target;

View File

@ -64,18 +64,6 @@ module TK.SpaceTac {
return new Target(x, y, null);
}
/**
* Snap to battle grid
*/
snap(grid: IArenaGrid): Target {
if (this.ship_id) {
return this;
} else {
let location = grid.snap(this);
return Target.newFromLocation(location.x, location.y);
}
}
// Get distance to another target
getDistanceTo(other: { x: number, y: number }): number {
var dx = other.x - this.x;

View File

@ -60,64 +60,6 @@ module TK.SpaceTac.Specs {
]);
});
test.case("can't move too much near another ship", check => {
var battle = TestTools.createBattle(1, 1);
var ship = battle.fleets[0].ships[0];
var enemy = battle.fleets[1].ships[0];
TestTools.setShipModel(ship, 100, 0, 100);
ship.setArenaPosition(500, 500);
enemy.setArenaPosition(1000, 500);
var action = new MoveAction("Engine", { distance_per_power: 1000, safety_distance: 200 });
var result = action.checkLocationTarget(ship, Target.newFromLocation(700, 500));
check.equals(result, Target.newFromLocation(700, 500));
result = action.checkLocationTarget(ship, Target.newFromLocation(800, 500));
check.equals(result, Target.newFromLocation(800, 500));
result = action.checkLocationTarget(ship, Target.newFromLocation(900, 500));
check.equals(result, Target.newFromLocation(800, 500));
result = action.checkLocationTarget(ship, Target.newFromLocation(1000, 500));
check.equals(result, Target.newFromLocation(800, 500));
result = action.checkLocationTarget(ship, Target.newFromLocation(1200, 500));
check.equals(result, Target.newFromLocation(1200, 500));
});
test.case("exclusion radius is applied correctly over two ships", check => {
var battle = TestTools.createBattle(1, 2);
var ship = battle.fleets[0].ships[0];
var enemy1 = battle.fleets[1].ships[0];
var enemy2 = battle.fleets[1].ships[1];
TestTools.setShipModel(ship, 100, 0, 100);
enemy1.setArenaPosition(0, 800);
enemy2.setArenaPosition(0, 1000);
var action = new MoveAction("Engine", { distance_per_power: 1000, safety_distance: 150 });
var result = action.checkLocationTarget(ship, Target.newFromLocation(0, 1100));
check.equals(result, Target.newFromLocation(0, 650));
});
test.case("exclusion radius does not make the ship go back", check => {
var battle = TestTools.createBattle(1, 2);
var ship = battle.fleets[0].ships[0];
var enemy1 = battle.fleets[1].ships[0];
var enemy2 = battle.fleets[1].ships[1];
TestTools.setShipModel(ship, 100, 0, 100);
enemy1.setArenaPosition(0, 500);
enemy2.setArenaPosition(0, 800);
var action = new MoveAction("Engine", { distance_per_power: 1000, safety_distance: 600 });
let result = action.checkLocationTarget(ship, Target.newFromLocation(0, 1000));
check.equals(result, null);
result = action.checkLocationTarget(ship, Target.newFromLocation(0, 1400));
check.equals(result, Target.newFromLocation(0, 1400));
});
test.case("builds a textual description", check => {
let action = new MoveAction("Engine", { distance_per_power: 58, safety_distance: 0 });
check.equals(action.getEffectsDescription(), "Move: 58km per power point");

View File

@ -101,24 +101,6 @@ module TK.SpaceTac {
return power * this.distance_per_power;
}
/**
* Get an exclusion helper for this move action
*/
getExclusionAreas(ship: Ship): ExclusionAreas {
return ExclusionAreas.fromShip(ship, this.safety_distance);
}
/**
* Apply exclusion areas (neer arena borders, or other ships)
*/
applyExclusion(ship: Ship, target: Target): Target {
let exclusion = this.getExclusionAreas(ship);
let destination = exclusion.stopBefore(new ArenaLocation(target.x, target.y), ship.location);
target = Target.newFromLocation(destination.x, destination.y);
return target;
}
/**
* Apply reachable range, with remaining power
*/
@ -130,7 +112,6 @@ module TK.SpaceTac {
checkLocationTarget(ship: Ship, target: Target): Target | null {
target = this.applyReachableRange(ship, target);
target = this.applyExclusion(ship, target);
return target.getDistanceTo(ship.location) > 0 ? target : null;
}

View File

@ -22,19 +22,5 @@ module TK.SpaceTac.Specs {
check.equals(ship1b.location, new ArenaLocationAngle(262, 100));
check.equals(ship2a.location, new ArenaLocationAngle(100, 292));
})
test.case("does not push a ship inside a hard exclusion area", check => {
let battle = new Battle();
let ship1a = battle.fleets[0].addShip();
ship1a.setArenaPosition(100, 100);
let ship2a = battle.fleets[1].addShip();
ship2a.setArenaPosition(100, 200);
let ship2b = battle.fleets[1].addShip();
ship2b.setArenaPosition(100, 350);
let effect = new RepelEffect(85);
battle.applyDiffs(effect.getOnDiffs(ship2a, ship1a));
check.equals(ship2a.location, new ArenaLocationAngle(100, 250));
})
})
}

View File

@ -17,8 +17,7 @@ module TK.SpaceTac {
if (ship != source && !any(ship.getEffects(), effect => effect instanceof PinnedEffect && effect.hard)) {
let angle = arenaAngle(source.location, ship.location);
let destination = new ArenaLocation(ship.arena_x + Math.cos(angle) * this.value, ship.arena_y + Math.sin(angle) * this.value);
let exclusions = ExclusionAreas.fromShip(ship);
destination = exclusions.stopBefore(destination, ship.location);
// TODO Apply collisions
// TODO Apply area effect adding/removal
return [
new ShipMoveDiff(ship, ship.location, new ArenaLocationAngle(destination.x, destination.y, ship.arena_angle))

View File

@ -45,9 +45,6 @@ module TK.SpaceTac.UI {
// Targetting mode (null if we're not in this mode)
targetting!: Targetting
// Ship list
ship_list!: ShipList
// Action bar
action_bar!: ActionBar
@ -116,10 +113,6 @@ module TK.SpaceTac.UI {
// Add UI elements
this.action_bar = new ActionBar(this);
this.action_bar.setPosition(0, this.getHeight() - 132);
this.ship_list = new ShipList(this, this.battle, this.player, this.toggle_tactical_mode, this,
this.layer_borders, this.getWidth() - 112, 0);
this.ship_list.bindToLog(this.log_processor);
this.ship_tooltip = new ShipTooltip(this);
this.character_sheet = new CharacterSheet(this, CharacterSheetMode.DISPLAY);
this.character_sheet.moveToLayer(this.layer_sheets);
@ -132,7 +125,6 @@ module TK.SpaceTac.UI {
this.audio.startMusic("mechanolith", 0.2);
// Key mapping
this.inputs.bind("t", "Show tactical view", () => this.ship_list.info_button.toggle());
this.inputs.bind("Enter", "Validate action", () => this.validationPressed());
this.inputs.bind(" ", "Validate action", () => this.validationPressed());
this.inputs.bind("Escape", "Cancel action", () => this.action_bar.actionEnded());
@ -311,7 +303,6 @@ module TK.SpaceTac.UI {
setShipHovered(ship: Ship | null): void {
this.ship_hovered = ship;
this.arena.setShipHovered(ship);
this.ship_list.setHovered(ship);
if (ship) {
this.ship_tooltip.setShip(ship);

View File

@ -307,7 +307,6 @@ module TK.SpaceTac.UI {
this.view.setShipHovered(null);
}
this.view.arena.markAsDead(dead_ship);
this.view.ship_list.refresh();
if (speed) {
await this.view.timer.sleep(2000 / speed);
}

View File

@ -1,4 +1,7 @@
module TK.SpaceTac.UI {
const PLAN_COLOR = 0xa5b7da
const PLAN_COLOR_HL = 0xdde6f9
/**
* Displays and maintain a battle plan
*/
@ -48,27 +51,102 @@ module TK.SpaceTac.UI {
}
private updateShip(plan: ShipPlan, parent: UIContainer) {
const move = first(plan.actions, action => action.category == ActionCategory.MOVE);
const ship = this.battle ? this.battle.getShip(plan.ship) : null;
if (ship) {
const move = first(plan.actions, action => action.category === ActionCategory.MOVE);
this.updateMoveAction(ship, move, parent);
const final_location = (move && move.target) ? move.target : ship.location;
const active = first(plan.actions, action => action.category === ActionCategory.ACTIVE);
this.updateActiveAction(ship, final_location, active, parent);
} else {
console.error("Ship not found to update actions", plan);
}
}
private updateMoveAction(ship: Ship, action: ActionPlan | null, parent: UIContainer) {
const child = parent.getByName("move");
const graphics = child ? as(UIGraphics, child) : parent.getBuilder().graphics("move");
private updateMoveAction(ship: Ship, plan: ActionPlan | null, parent: UIContainer) {
let child = parent.getByName("moveline");
const graphics = child ? as(UIGraphics, child) : parent.getBuilder().graphics("moveline");
graphics.clear();
if (action && action.target) {
if (plan && plan.target) {
graphics.addLine({
start: ship.location,
end: action.target,
end: plan.target,
width: 5,
color: 0xa5b7da
color: PLAN_COLOR
});
}
child = parent.getByName("moveghost");
const ghost = child ? as(UIImage, child) : parent.getBuilder().image(`ship-${ship.model.code}-sprite`, 0, 0, true);
ghost.setName("moveghost");
if (plan && plan.target) {
ghost.setVisible(true);
ghost.setPosition(plan.target.x, plan.target.y);
ghost.setTint(PLAN_COLOR);
} else {
ghost.setVisible(false);
}
}
private updateActiveAction(ship: Ship, from: IArenaLocation, plan: ActionPlan | null, parent: UIContainer) {
let child = parent.getByName("activearea");
const graphics = child ? as(UIGraphics, child) : parent.getBuilder().graphics("activearea");
graphics.clear();
const action = plan ? ship.actions.getById(plan.action) : null;
if (action && plan) {
let radius = 0;
let angle = 0;
if (action instanceof TriggerAction) {
if (action.angle) {
angle = (action.angle * 0.5) * Math.PI / 180;
radius = action.range;
} else {
radius = action.blast;
}
} else if (action instanceof DeployDroneAction) {
radius = action.drone_radius;
} else if (action instanceof ToggleAction) {
radius = action.radius;
}
if (radius) {
if (angle) {
const base_angle = plan.target ? Math.atan2(plan.target.y - from.y, plan.target.x - from.x) : 0;
graphics.addCircleArc({
center: from,
radius,
angle: { start: base_angle - angle, span: angle * 2 },
fill: { color: PLAN_COLOR, alpha: 0.2 },
border: { color: PLAN_COLOR_HL, alpha: 0.6, width: 2 },
});
graphics.addCircleArc({
center: from,
radius: radius * 0.95,
angle: { start: base_angle - angle * 0.95, span: angle * 1.9 },
fill: { color: PLAN_COLOR, alpha: 0.1 },
border: { color: PLAN_COLOR_HL, alpha: 0.3, width: 1 },
});
} else {
const center = plan ? plan.target : from;
graphics.addCircle({
center,
radius,
fill: { color: PLAN_COLOR, alpha: 0.2 },
border: { color: PLAN_COLOR_HL, alpha: 0.6, width: 2 },
});
graphics.addCircle({
center,
radius: radius * 0.95,
fill: { color: PLAN_COLOR, alpha: 0.1 },
border: { color: PLAN_COLOR_HL, alpha: 0.3, width: 1 },
});
}
}
}
}
}
}

View File

@ -54,20 +54,6 @@ module TK.SpaceTac.UI {
this.info.fillStyle(yescolor);
this.info.fillCircle(ship.arena_x, ship.arena_y, radius);
if (action instanceof MoveAction) {
let exclusions = action.getExclusionAreas(ship);
this.info.fillStyle(nocolor);
this.info.fillRect(0, 0, this.width, exclusions.hard_border);
this.info.fillRect(0, this.height - exclusions.hard_border, this.width, exclusions.hard_border);
this.info.fillRect(0, exclusions.hard_border, exclusions.hard_border, this.height - exclusions.hard_border * 2);
this.info.fillRect(this.width - exclusions.hard_border, exclusions.hard_border, exclusions.hard_border, this.height - exclusions.hard_border * 2);
exclusions.obstacles.forEach(obstacle => {
this.info.fillCircle(obstacle.x, obstacle.y, exclusions.effective_obstacle);
});
}
this.info.visible = true;
} else {
this.info.visible = false;

View File

@ -1,88 +0,0 @@
module TK.SpaceTac.UI.Specs {
testing("ShipList", test => {
let testgame = setupEmptyView(test);
function createList(): ShipList {
let view = testgame.view;
let battle = new Battle();
let player = new Player();
battle.fleets[0].setPlayer(player);
let tactical_mode = new Toggle();
let ship_buttons = {
cursorOnShip: nop,
cursorOffShip: nop,
cursorClicked: nop,
};
let list = new ShipList(view, battle, player, tactical_mode, ship_buttons);
return list;
}
test.case("handles play position of ships", check => {
let list = createList();
let battle = list.battle;
check.in("initial", check => {
check.equals(list.items.length, 0, "no item at first");
});
let ship = battle.fleets[0].addShip();
TestTools.setShipModel(ship, 10, 0);
list.setShipsFromBattle(battle, false);
check.in("one ship added but not in play order", check => {
check.equals(list.items.length, 1, "item count");
check.equals(list.items[0].visible, false, "ship card not visible");
});
battle.throwInitiative();
list.refresh(0);
check.in("ship now in play order", check => {
check.equals(list.items[0].visible, true, "ship card visible");
});
ship = battle.fleets[1].addShip();
TestTools.setShipModel(ship, 10, 0);
battle.throwInitiative();
list.setShipsFromBattle(battle, false);
check.in("ship added in the other fleet", check => {
check.equals(list.items.length, 2, "item count");
check.equals(nn(list.findItem(battle.play_order[0])).location, { x: 2, y: 843 }, "first ship position");
check.equals(nn(list.findItem(battle.play_order[1])).location, { x: 2, y: 744 }, "second ship position");
});
battle.setPlayingShip(battle.play_order[0]);
list.refresh(0);
check.in("started", check => {
check.equals(nn(list.findItem(battle.play_order[0])).location, { x: -14, y: 962 }, "first ship position");
check.equals(nn(list.findItem(battle.play_order[1])).location, { x: 2, y: 843 }, "second ship position");
});
battle.advanceToNextShip();
list.refresh(0);
check.in("end turn", check => {
check.equals(nn(list.findItem(battle.play_order[0])).location, { x: 2, y: 843 }, "first ship position");
check.equals(nn(list.findItem(battle.play_order[1])).location, { x: -14, y: 962 }, "second ship position");
});
ship = battle.fleets[1].addShip();
TestTools.setShipModel(ship, 10, 0);
battle.throwInitiative();
battle.setPlayingShip(battle.play_order[0]);
list.setShipsFromBattle(battle, false);
check.in("third ship added", check => {
check.equals(list.items.length, 3, "item count");
check.equals(nn(list.findItem(battle.play_order[0])).location, { x: -14, y: 962 }, "first ship position");
check.equals(nn(list.findItem(battle.play_order[1])).location, { x: 2, y: 843 }, "second ship position");
check.equals(nn(list.findItem(battle.play_order[2])).location, { x: 2, y: 744 }, "third ship position");
});
let dead = battle.play_order[1];
dead.setDead();
list.refresh(0);
check.in("ship dead", check => {
check.equals(list.items.length, 3, "item count");
check.equals(nn(list.findItem(battle.play_order[0])).location, { x: -14, y: 962 }, "first ship position");
check.equals(nn(list.findItem(dead)).location, { x: 200, y: 843 }, "dead ship position");
check.equals(nn(list.findItem(battle.play_order[1])).location, { x: 2, y: 843 }, "second ship position");
});
});
});
}

View File

@ -1,156 +0,0 @@
module TK.SpaceTac.UI {
/**
* Side bar with all playing ships, sorted by play order
*/
export class ShipList {
// Link to the parent view
view: BaseView
// Current battle
battle: Battle
// Current player
player: Player
// Interface for acting as ship button
ship_buttons: IShipButton
// Container
container: UIContainer
// List of ship items
items: ShipListItem[]
// Hovered ship
hovered: ShipListItem | null
// Info button
info_button: UIButton
constructor(view: BaseView, battle: Battle, player: Player, tactical_mode: Toggle, ship_buttons: IShipButton, parent?: UIContainer, x = 0, y = 0) {
let builder = new UIBuilder(view, parent);
this.container = builder.container("shiplist", x, y);
builder = builder.in(this.container);
let bg = builder.image("battle-shiplist-background", 0, 0);
bg.setInteractive();
this.view = view;
this.battle = battle;
this.player = player;
this.ship_buttons = ship_buttons;
this.items = [];
this.hovered = null;
this.info_button = builder.button("battle-shiplist-info-button", 0, 0, undefined, "Tactical display", on => tactical_mode.manipulate("shiplist")(on));
this.setShipsFromBattle(battle);
}
/**
* Clear all ship cards
*/
clearAll(): void {
this.items.forEach(ship => ship.destroy());
this.items = [];
}
/**
* Rebuild the ship list from an ongoing battle
*/
setShipsFromBattle(battle: Battle, animate = true): void {
this.clearAll();
iforeach(battle.iships(true), ship => this.addShip(ship));
this.refresh(animate ? 1 : 0);
}
/**
* Bind to a log processor, to watch for events
*/
bindToLog(log: LogProcessor): void {
log.watchForShipChange(ship => {
return {
foreground: async (speed: number) => {
this.refresh(speed);
}
}
});
log.register(diff => {
if (diff instanceof ShipDamageDiff) {
return {
background: async () => {
let item = this.findItem(diff.ship_id);
if (item) {
item.setDamageHit();
}
}
}
} else {
return {};
}
})
}
/**
* Add a ship card
*/
addShip(ship: Ship): ShipListItem {
var owned = ship.isPlayedBy(this.player);
var result = new ShipListItem(this, 200, this.container.height / 2, ship, owned, this.ship_buttons);
this.items.push(result);
this.container.add(result);
return result;
}
/**
* Find the item (card) that displays a given ship
*/
findItem(ship: Ship | RObjectId | null): ShipListItem | null {
return first(this.items, item => item.ship.is(ship));
}
/**
* Update the locations of all items
*/
refresh(speed = 1): void {
let duration = speed ? (1000 / speed) : 0;
this.items.forEach(item => {
if (item.ship.alive) {
let position = this.battle.getPlayOrder(item.ship);
if (position < 0) {
item.visible = false;
} else {
if (position == 0) {
item.moveAt(-14, 962, duration);
} else {
item.moveAt(2, 942 - position * 99, duration);
}
item.visible = true;
item.setZ(99 - position);
}
} else {
item.setZ(100);
item.moveAt(200, item.y, duration);
}
});
}
/**
* Set the currently hovered ship
*/
setHovered(ship: Ship | null): void {
if (this.hovered) {
this.hovered.setHovered(false);
this.hovered = null;
}
if (ship) {
this.hovered = this.findItem(ship);
if (this.hovered) {
this.hovered.setHovered(true);
}
}
}
}
}

View File

@ -1,91 +0,0 @@
module TK.SpaceTac.UI {
/**
* One item in a ship list (used in BattleView)
*/
export class ShipListItem extends UIContainer {
// Reference to the view
view: BaseView
// Reference to the ship game object
ship: Ship
// Player indicator
player_indicator: UIImage
// Portrait
portrait: UIImage
// Damage flashing indicator
damage_indicator: UIImage
// Hover indicator
hover_indicator: UIImage
// Create a ship button for the battle ship list
constructor(list: ShipList, x: number, y: number, ship: Ship, owned: boolean, ship_buttons: IShipButton) {
// TODO Make it an UIButton
super(list.view, x, y);
this.view = list.view;
this.ship = ship;
let builder = new UIBuilder(list.view, this);
builder.image("battle-shiplist-item-background");
this.player_indicator = builder.image(owned ? "battle-hud-ship-own-mini" : "battle-hud-ship-enemy-mini", 102, 52, true);
this.player_indicator.setAngle(-90);
this.portrait = builder.image(`ship-${ship.model.code}-sprite`, 52, 52, true);
this.portrait.setScale(0.8)
this.portrait.setAngle(180);
this.damage_indicator = builder.image("battle-shiplist-damage", 8, 9);
this.damage_indicator.visible = false;
this.hover_indicator = builder.image("battle-shiplist-hover", 7, 8);
this.hover_indicator.visible = false;
this.view.inputs.setHoverClick(this,
() => ship_buttons.cursorOnShip(ship),
() => ship_buttons.cursorOffShip(ship),
() => ship_buttons.cursorClicked()
);
}
get location(): { x: number, y: number } {
return { x: this.x, y: this.y };
}
/**
* Flash a damage indicator
*/
setDamageHit() {
this.view.tweens.add({
targets: this.damage_indicator,
duration: 100,
alpha: 1,
repeat: 2,
yoyo: true
});
}
/**
* Move to a given location on screen
*/
moveAt(x: number, y: number, duration: number) {
if (duration && (this.x != x || this.y != y)) {
this.view.animations.addAnimation<UIContainer>(this, { x: x, y: y }, duration);
} else {
this.x = x;
this.y = y;
}
}
/**
* Set the hovered status
*/
setHovered(hovered: boolean) {
this.view.animations.setVisible(this.hover_indicator, hovered, 200);
}
}
}