V0.1
14
Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Build
|
||||||
|
FROM rust as builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
WORKDIR /app/server
|
||||||
|
RUN cargo build --release --bin out_of_quiz
|
||||||
|
|
||||||
|
# Runner
|
||||||
|
FROM debian:bullseye-slim as runner
|
||||||
|
COPY --from=builder /app/server/target/release/out_of_quiz /app/out_of_quiz
|
||||||
|
COPY --from=builder /app/server/quotes.json /app/quotes.json
|
||||||
|
COPY --from=builder /app/client /app/static
|
||||||
|
WORKDIR app
|
||||||
|
CMD ["./out_of_quiz"]
|
||||||
99
README.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# DeepImpact
|
||||||
|
|
||||||
|
A musical missile command
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
### Level
|
||||||
|
|
||||||
|
Sprites: Background, boss, projectiles
|
||||||
|
|
||||||
|
Sound: Music
|
||||||
|
|
||||||
|
Text: Title, subtitle
|
||||||
|
|
||||||
|
|
||||||
|
-> Boss slowly rising until the end when it explode
|
||||||
|
|
||||||
|
### Pages
|
||||||
|
|
||||||
|
* Main menu
|
||||||
|
* Lobby (no instances, level selection, ready)
|
||||||
|
* Game (canvas)
|
||||||
|
|
||||||
|
### Gameplay
|
||||||
|
|
||||||
|
Control one bunker
|
||||||
|
|
||||||
|
Multiple turret type
|
||||||
|
* Slow steady, single fire, medium aim (medium damage)
|
||||||
|
* Rapid fire, burst, quick aim (small damage)
|
||||||
|
* Very slow, single shot, low aim (high damage)
|
||||||
|
|
||||||
|
Turret aim follow mouse with latency, cooldown between fires
|
||||||
|
|
||||||
|
|
||||||
|
## Tech
|
||||||
|
|
||||||
|
Music on client -> Effects (background)
|
||||||
|
Music on server -> Compute meteor spawn, player state (health), game state,
|
||||||
|
|
||||||
|
|
||||||
|
### States
|
||||||
|
|
||||||
|
Game -> LOBBY, RUNNING, END
|
||||||
|
Player -> UNKNOW, WAITING, PLAYING
|
||||||
|
|
||||||
|
### Messages
|
||||||
|
|
||||||
|
#### To server
|
||||||
|
|
||||||
|
* login (string)
|
||||||
|
* logout
|
||||||
|
* ready (weapon type)
|
||||||
|
* start (music title)
|
||||||
|
* fire (angle)
|
||||||
|
* music (file)
|
||||||
|
|
||||||
|
#### To Client
|
||||||
|
|
||||||
|
* userlist
|
||||||
|
* state (game state)
|
||||||
|
* update (game update)
|
||||||
|
* music (file)
|
||||||
|
* play (music title)
|
||||||
|
* ok (message)
|
||||||
|
* nok (message)
|
||||||
|
|
||||||
|
### Game Update
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
players: [
|
||||||
|
{
|
||||||
|
"name": string,
|
||||||
|
"health": number,
|
||||||
|
"weapon": string,
|
||||||
|
"angle": number
|
||||||
|
}
|
||||||
|
],
|
||||||
|
meteors: [
|
||||||
|
{
|
||||||
|
"x": number,
|
||||||
|
"y": number,
|
||||||
|
"speed": number,
|
||||||
|
"angle": number,
|
||||||
|
"health": number
|
||||||
|
}
|
||||||
|
],
|
||||||
|
bullets: [
|
||||||
|
{
|
||||||
|
"x": number,
|
||||||
|
"y": number,
|
||||||
|
"speed": number,
|
||||||
|
"angle": number,
|
||||||
|
"damage": number
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
6402
game_assets.svg
Normal file
|
After Width: | Height: | Size: 294 KiB |
2479
gameart.svg
Normal file
|
After Width: | Height: | Size: 100 KiB |
299
logo.svg
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="210mm"
|
||||||
|
height="297mm"
|
||||||
|
viewBox="0 0 210 297"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||||
|
sodipodi:docname="logo.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1"
|
||||||
|
inkscape:cx="322"
|
||||||
|
inkscape:cy="428.5"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1172"
|
||||||
|
inkscape:window-x="5120"
|
||||||
|
inkscape:window-y="240"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg5" />
|
||||||
|
<defs
|
||||||
|
id="defs2">
|
||||||
|
<rect
|
||||||
|
x="308.65609"
|
||||||
|
y="131.15946"
|
||||||
|
width="552.30195"
|
||||||
|
height="268.35604"
|
||||||
|
id="rect14473" />
|
||||||
|
<rect
|
||||||
|
x="360.9943"
|
||||||
|
y="142.68872"
|
||||||
|
width="489.61531"
|
||||||
|
height="255.90823"
|
||||||
|
id="rect11603" />
|
||||||
|
<rect
|
||||||
|
x="156.68823"
|
||||||
|
y="413.89331"
|
||||||
|
width="545.07996"
|
||||||
|
height="233.64795"
|
||||||
|
id="rect2637" />
|
||||||
|
<rect
|
||||||
|
x="46.04778"
|
||||||
|
y="326.76177"
|
||||||
|
width="633.93653"
|
||||||
|
height="206.56569"
|
||||||
|
id="rect7318" />
|
||||||
|
<inkscape:perspective
|
||||||
|
sodipodi:type="inkscape:persp3d"
|
||||||
|
inkscape:vp_x="0 : 148.5 : 1"
|
||||||
|
inkscape:vp_y="0 : 1000 : 0"
|
||||||
|
inkscape:vp_z="210 : 148.5 : 1"
|
||||||
|
inkscape:persp3d-origin="105 : 99 : 1"
|
||||||
|
id="perspective4010" />
|
||||||
|
</defs>
|
||||||
|
<rect
|
||||||
|
style="display:none;fill:#153a3a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6.75609;stroke-linecap:round;stroke-miterlimit:50;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect94537"
|
||||||
|
width="411.04791"
|
||||||
|
height="243.64622"
|
||||||
|
x="-75.004112"
|
||||||
|
y="-59.614521"
|
||||||
|
ry="1.4429886" />
|
||||||
|
<circle
|
||||||
|
style="display:none;fill:#246363;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6.54798;stroke-linecap:round;stroke-miterlimit:50;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="path95214"
|
||||||
|
cx="152.30928"
|
||||||
|
cy="70.663628"
|
||||||
|
r="55.097843" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Title"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<text
|
||||||
|
xml:space="preserve"
|
||||||
|
transform="matrix(0.56213569,0,0,0.56264716,-130.5064,-13.605236)"
|
||||||
|
id="text14471"
|
||||||
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:42.6667px;line-height:1.25;font-family:Cratense;-inkscape-font-specification:Cratense;white-space:pre;shape-inside:url(#rect14473);fill:#80cece;fill-opacity:1;fill-rule:nonzero"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_title.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96"><tspan
|
||||||
|
x="308.65625"
|
||||||
|
y="169.59237"
|
||||||
|
id="tspan95654"><tspan
|
||||||
|
style="font-family:'Karmatic Arcade';-inkscape-font-specification:'Karmatic Arcade'"
|
||||||
|
id="tspan95652">deep Impact</tspan></tspan></text>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer3"
|
||||||
|
inkscape:label="Meteor 1">
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:1.62481;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-7-5-3-5"
|
||||||
|
width="1.0734794"
|
||||||
|
height="12.438345"
|
||||||
|
x="6.8107634"
|
||||||
|
y="-7.2421951"
|
||||||
|
ry="0.2223013"
|
||||||
|
transform="matrix(0.91975648,-0.39248951,0.4148616,0.90988453,0,0)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_logo.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:1.55021;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-7-5-3"
|
||||||
|
width="0.8490712"
|
||||||
|
height="14.315023"
|
||||||
|
x="12.817691"
|
||||||
|
y="-10.573309"
|
||||||
|
ry="0.25584182"
|
||||||
|
transform="rotate(-23.80926)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_logo.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:0.777934;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-7-5-6-2"
|
||||||
|
width="1.3300996"
|
||||||
|
height="2.3011935"
|
||||||
|
x="12.69324"
|
||||||
|
y="-15.261557"
|
||||||
|
ry="0.041127518"
|
||||||
|
transform="rotate(-23.80926)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_logo.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:0.661433;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-7-5-6"
|
||||||
|
width="1.0703896"
|
||||||
|
height="2.0671964"
|
||||||
|
x="18.725452"
|
||||||
|
y="-7.624433"
|
||||||
|
ry="0.036945462"
|
||||||
|
transform="rotate(-23.80926)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_logo.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:1.08419;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-7-5"
|
||||||
|
width="0.67051047"
|
||||||
|
height="8.8666515"
|
||||||
|
x="19.074978"
|
||||||
|
y="-3.3311093"
|
||||||
|
ry="0.1584671"
|
||||||
|
transform="matrix(0.92196783,-0.38726647,0.42004421,0.90750364,0,0)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_logo.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:1.98445;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-7"
|
||||||
|
width="0.87039292"
|
||||||
|
height="22.8834"
|
||||||
|
x="25.420929"
|
||||||
|
y="-6.6066985"
|
||||||
|
ry="0.40897807"
|
||||||
|
transform="matrix(0.91447443,-0.40464368,0.40274239,0.91531337,0,0)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_logo.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:2.15637;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462"
|
||||||
|
width="0.81653529"
|
||||||
|
height="28.80228"
|
||||||
|
x="1.2494003"
|
||||||
|
y="-13.125158"
|
||||||
|
ry="0.51476187"
|
||||||
|
transform="rotate(-23.80926)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_logo.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<circle
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:5.05889;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="path846"
|
||||||
|
cx="13.725318"
|
||||||
|
cy="14.92935"
|
||||||
|
r="11.027036"
|
||||||
|
transform="rotate(-23.80926)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_logo.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer2"
|
||||||
|
inkscape:label="Meteor 2">
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:2.44724;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-7-5-3-5-2"
|
||||||
|
width="0.96742392"
|
||||||
|
height="31.310293"
|
||||||
|
x="135.92699"
|
||||||
|
y="43.462559"
|
||||||
|
ry="0.55958563"
|
||||||
|
transform="matrix(0.99911756,0.04200127,0.00336875,0.99999433,0,0)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_title.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:2.33405;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-7-5-3-6"
|
||||||
|
width="0.76461828"
|
||||||
|
height="36.03561"
|
||||||
|
x="141.1362"
|
||||||
|
y="29.615719"
|
||||||
|
ry="0.64403784"
|
||||||
|
transform="matrix(0.99996885,0.00789341,-0.00101012,0.99999949,0,0)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_title.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:1.17129;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-7-5-6-2-5"
|
||||||
|
width="1.1978012"
|
||||||
|
height="5.7928591"
|
||||||
|
x="141.02415"
|
||||||
|
y="17.81395"
|
||||||
|
ry="0.10353146"
|
||||||
|
transform="matrix(0.99996885,0.00789341,-0.00101012,0.99999949,0,0)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_title.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="display:inline;fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:0.995878;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-7-5-6-3"
|
||||||
|
width="0.96392328"
|
||||||
|
height="5.2038116"
|
||||||
|
x="146.45634"
|
||||||
|
y="37.039112"
|
||||||
|
ry="0.093003847"
|
||||||
|
transform="matrix(0.99996885,0.00789341,-0.00101012,0.99999949,0,0)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_title.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="display:inline;fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:1.63349;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-7-5-5"
|
||||||
|
width="0.60468328"
|
||||||
|
height="22.318123"
|
||||||
|
x="146.68213"
|
||||||
|
y="41.386951"
|
||||||
|
ry="0.3988753"
|
||||||
|
transform="matrix(0.99832806,0.05780217,0.00540923,0.99998537,0,0)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_title.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:2.33796;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-7-7"
|
||||||
|
width="0.47992402"
|
||||||
|
height="57.604813"
|
||||||
|
x="152.45551"
|
||||||
|
y="39.496159"
|
||||||
|
ry="1.0295283"
|
||||||
|
transform="matrix(0.99996681,0.00814733,-8.4605551e-4,0.99999964,0,0)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_title.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#04ab96;stroke-width:3.2061;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="rect4462-6"
|
||||||
|
width="0.7170341"
|
||||||
|
height="72.504791"
|
||||||
|
x="-131.86195"
|
||||||
|
y="26.454266"
|
||||||
|
ry="1.2958245"
|
||||||
|
transform="matrix(-0.99996724,0.00809469,9.8500077e-4,0.99999951,0,0)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_title.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<circle
|
||||||
|
style="fill:#368b8c;fill-opacity:1;stroke:#04ab96;stroke-width:4.55558;stroke-linecap:round;stroke-miterlimit:50;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="path846-3"
|
||||||
|
cx="142.12807"
|
||||||
|
cy="96.724251"
|
||||||
|
r="9.9299612"
|
||||||
|
transform="rotate(0.16178829)"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/client/assets/img/meteor_title.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 12 KiB |
416
skyline.svg
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="210mm"
|
||||||
|
height="297mm"
|
||||||
|
viewBox="0 0 210 297"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||||
|
sodipodi:docname="skyline.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.1326109"
|
||||||
|
inkscape:cx="607.88751"
|
||||||
|
inkscape:cy="293.12804"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1371"
|
||||||
|
inkscape:window-x="2560"
|
||||||
|
inkscape:window-y="32"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Calque 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.90893;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-6-5-5"
|
||||||
|
width="9.4300146"
|
||||||
|
height="51.796406"
|
||||||
|
x="79.405869"
|
||||||
|
y="86.540741"
|
||||||
|
ry="0.38549981"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:3.44426;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-3"
|
||||||
|
width="24.522516"
|
||||||
|
height="51.796406"
|
||||||
|
x="54.883354"
|
||||||
|
y="86.540733"
|
||||||
|
ry="0.467776"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.55631;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-6-5-5-7"
|
||||||
|
width="9.4300146"
|
||||||
|
height="34.427803"
|
||||||
|
x="123.9439"
|
||||||
|
y="103.97687"
|
||||||
|
ry="0.25623229"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:2.80803;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-3-0"
|
||||||
|
width="24.522516"
|
||||||
|
height="34.427803"
|
||||||
|
x="99.421387"
|
||||||
|
y="103.97687"
|
||||||
|
ry="0.31091926"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.34111;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-6-5-5-7-9"
|
||||||
|
width="2.7857001"
|
||||||
|
height="86.540543"
|
||||||
|
x="137.27295"
|
||||||
|
y="51.956684"
|
||||||
|
ry="0.64408654"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:2.41973;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-3-0-3"
|
||||||
|
width="7.244143"
|
||||||
|
height="86.540543"
|
||||||
|
x="130.02881"
|
||||||
|
y="51.956684"
|
||||||
|
ry="0.78155208"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#47b6b6;fill-opacity:1;stroke-width:2.89698;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-3-2"
|
||||||
|
width="32.192028"
|
||||||
|
height="27.913622"
|
||||||
|
x="72.979889"
|
||||||
|
y="110.64074"
|
||||||
|
ry="0.25208935"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#47b6b6;fill-opacity:1;stroke-width:2.51214;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-3-2-6"
|
||||||
|
width="16.680504"
|
||||||
|
height="40.508976"
|
||||||
|
x="133.37392"
|
||||||
|
y="97.895691"
|
||||||
|
ry="0.36583865"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#47b6b6;fill-opacity:1;stroke-width:3.35589;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-3-2-2"
|
||||||
|
width="27.709087"
|
||||||
|
height="43.517361"
|
||||||
|
x="27.189053"
|
||||||
|
y="93.471054"
|
||||||
|
ry="0.39300752"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846"
|
||||||
|
width="14.441299"
|
||||||
|
height="74.740639"
|
||||||
|
x="14.590917"
|
||||||
|
y="64.426338"
|
||||||
|
ry="0.67498648"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.75969;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848"
|
||||||
|
width="5.5533314"
|
||||||
|
height="74.740639"
|
||||||
|
x="27.732847"
|
||||||
|
y="64.420433"
|
||||||
|
ry="0.55626446"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:2.89866;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-9"
|
||||||
|
width="14.441299"
|
||||||
|
height="62.296345"
|
||||||
|
x="93.297371"
|
||||||
|
y="76.079025"
|
||||||
|
ry="0.56260145"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.60653;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-1"
|
||||||
|
width="5.5533314"
|
||||||
|
height="62.296345"
|
||||||
|
x="106.43931"
|
||||||
|
y="76.074104"
|
||||||
|
ry="0.46364659"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:2.9998;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3"
|
||||||
|
width="24.522516"
|
||||||
|
height="39.291203"
|
||||||
|
x="37.137337"
|
||||||
|
y="99.70401"
|
||||||
|
ry="0.35484084"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.66259;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-6"
|
||||||
|
width="9.4300146"
|
||||||
|
height="39.291203"
|
||||||
|
x="59.453403"
|
||||||
|
y="99.700905"
|
||||||
|
ry="0.29242861"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:1.89335;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7"
|
||||||
|
width="24.522516"
|
||||||
|
height="15.652031"
|
||||||
|
x="19.784502"
|
||||||
|
y="123.47013"
|
||||||
|
ry="0.14135429"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.04936;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-6-5"
|
||||||
|
width="9.4300146"
|
||||||
|
height="15.652031"
|
||||||
|
x="42.100567"
|
||||||
|
y="123.46889"
|
||||||
|
ry="0.11649177"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.90893;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-6-5-5-0"
|
||||||
|
width="9.4300146"
|
||||||
|
height="51.796406"
|
||||||
|
x="209.67577"
|
||||||
|
y="85.823242"
|
||||||
|
ry="0.38549981"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:3.44426;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-3-6"
|
||||||
|
width="24.522516"
|
||||||
|
height="51.796406"
|
||||||
|
x="185.15326"
|
||||||
|
y="85.823227"
|
||||||
|
ry="0.467776"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.55631;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-6-5-5-7-2"
|
||||||
|
width="9.4300146"
|
||||||
|
height="34.427803"
|
||||||
|
x="254.21381"
|
||||||
|
y="103.25937"
|
||||||
|
ry="0.25623229"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:2.80803;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-3-0-6"
|
||||||
|
width="24.522516"
|
||||||
|
height="34.427803"
|
||||||
|
x="229.69128"
|
||||||
|
y="103.25937"
|
||||||
|
ry="0.31091926"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.34111;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-6-5-5-7-9-1"
|
||||||
|
width="2.7857001"
|
||||||
|
height="86.540543"
|
||||||
|
x="267.54285"
|
||||||
|
y="51.239182"
|
||||||
|
ry="0.64408654"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:2.41973;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-3-0-3-8"
|
||||||
|
width="7.244143"
|
||||||
|
height="86.540543"
|
||||||
|
x="260.29871"
|
||||||
|
y="51.239182"
|
||||||
|
ry="0.78155208"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#47b6b6;fill-opacity:1;stroke-width:2.89698;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-3-2-7"
|
||||||
|
width="32.192028"
|
||||||
|
height="27.913622"
|
||||||
|
x="203.24979"
|
||||||
|
y="109.92323"
|
||||||
|
ry="0.25208935"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#47b6b6;fill-opacity:1;stroke-width:2.51214;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-3-2-6-9"
|
||||||
|
width="16.680504"
|
||||||
|
height="40.508976"
|
||||||
|
x="263.6438"
|
||||||
|
y="97.178192"
|
||||||
|
ry="0.36583865"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#47b6b6;fill-opacity:1;stroke-width:3.35589;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-3-2-2-2"
|
||||||
|
width="27.709087"
|
||||||
|
height="43.517361"
|
||||||
|
x="157.45895"
|
||||||
|
y="92.753555"
|
||||||
|
ry="0.39300752"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-0"
|
||||||
|
width="14.441299"
|
||||||
|
height="74.740639"
|
||||||
|
x="144.86081"
|
||||||
|
y="63.708832"
|
||||||
|
ry="0.67498648"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.75969;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-2"
|
||||||
|
width="5.5533314"
|
||||||
|
height="74.740639"
|
||||||
|
x="158.00275"
|
||||||
|
y="63.702927"
|
||||||
|
ry="0.55626446"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:2.89866;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-9-3"
|
||||||
|
width="14.441299"
|
||||||
|
height="62.296345"
|
||||||
|
x="223.56728"
|
||||||
|
y="75.361526"
|
||||||
|
ry="0.56260145"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.60653;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-1-7"
|
||||||
|
width="5.5533314"
|
||||||
|
height="62.296345"
|
||||||
|
x="236.70921"
|
||||||
|
y="75.356598"
|
||||||
|
ry="0.46364659"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:2.9998;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-5"
|
||||||
|
width="24.522516"
|
||||||
|
height="39.291203"
|
||||||
|
x="167.40724"
|
||||||
|
y="98.986511"
|
||||||
|
ry="0.35484084"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.66259;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-6-9"
|
||||||
|
width="9.4300146"
|
||||||
|
height="39.291203"
|
||||||
|
x="189.72331"
|
||||||
|
y="98.983398"
|
||||||
|
ry="0.29242861"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368b8c;stroke-width:1.89335;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846-3-7-2"
|
||||||
|
width="24.522516"
|
||||||
|
height="15.652031"
|
||||||
|
x="150.05441"
|
||||||
|
y="122.75262"
|
||||||
|
ry="0.14135429"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
<rect
|
||||||
|
style="fill:#245c5d;fill-opacity:1;stroke-width:1.04936;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect848-6-5-2"
|
||||||
|
width="9.4300146"
|
||||||
|
height="15.652031"
|
||||||
|
x="172.37047"
|
||||||
|
y="122.75139"
|
||||||
|
ry="0.11649177"
|
||||||
|
inkscape:export-filename="/home/thomsb/Projets/DeepImpact/src/assets/img/skyline.png"
|
||||||
|
inkscape:export-xdpi="96"
|
||||||
|
inkscape:export-ydpi="96" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/fonts/ChopinScript.otf
Normal file
BIN
src/assets/fonts/Cratense.ttf
Normal file
BIN
src/assets/fonts/Gabella.ttf
Normal file
BIN
src/assets/fonts/Revamped.otf
Normal file
BIN
src/assets/fonts/SketchedCassiusBroken.ttf
Normal file
BIN
src/assets/fonts/TRON.TTF
Normal file
BIN
src/assets/fonts/ka1.ttf
Executable file
BIN
src/assets/img/boss/e4.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
src/assets/img/boss/q4.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
src/assets/img/boss/r4.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
src/assets/img/boss/richard.jpg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
src/assets/img/boss/richard.png
Normal file
|
After Width: | Height: | Size: 145 KiB |
BIN
src/assets/img/boss/samurai.png
Normal file
|
After Width: | Height: | Size: 275 KiB |
BIN
src/assets/img/boss/t4.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
src/assets/img/custom.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
src/assets/img/effects/explosion.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
src/assets/img/meteor_logo.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
src/assets/img/meteor_title.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/img/meteors/diploma.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
src/assets/img/meteors/diplome.jpg
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
src/assets/img/meteors/meteor.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/assets/img/meteors/meteor_2.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/assets/img/meteors/meteor_3.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/assets/img/meteors/meteor_4.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
src/assets/img/meteors/meteor_full.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
src/assets/img/meteors/meteor_full2.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
src/assets/img/meteors/plane.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
src/assets/img/meteors/plane.png
Normal file
|
After Width: | Height: | Size: 131 KiB |
BIN
src/assets/img/meteors/plane_A.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/img/skyline.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/img/skyline_turret.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
src/assets/img/skyline_turret2.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
src/assets/img/turrets/bullet_fast.png
Normal file
|
After Width: | Height: | Size: 498 B |
BIN
src/assets/img/turrets/bullet_heavy.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/img/turrets/bullet_laser.png
Normal file
|
After Width: | Height: | Size: 985 B |
BIN
src/assets/img/turrets/bullet_standard.png
Normal file
|
After Width: | Height: | Size: 490 B |
BIN
src/assets/img/turrets/turret_fast.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/assets/img/turrets/turret_heavy.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
src/assets/img/turrets/turret_laser.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
src/assets/img/turrets/turret_standard.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/music/El Tigr3 - Black Star.flac
Executable file
BIN
src/assets/music/Igorrr - Very Noise.flac
Executable file
BIN
src/assets/music/Infected Mushroom - Guitarmass.flac
Executable file
BIN
src/assets/music/Open The Gates.mp3
Executable file
BIN
src/assets/music/The City Must Survive.flac
Executable file
BIN
src/assets/music/The Only Thing They Fear is V.flac
Normal file
BIN
src/assets/music/jump.mp3
Normal file
BIN
src/assets/music/music2.mp3
Normal file
BIN
src/assets/music/nightcall_8bit.mp3
Normal file
BIN
src/assets/sounds/557595__breviceps__minigun.wav
Normal file
BIN
src/assets/sounds/explosion.flac
Normal file
BIN
src/assets/sounds/explosion.mp3
Normal file
BIN
src/assets/sounds/explosion_big.mp3
Normal file
BIN
src/assets/sounds/turret_fast.mp3
Normal file
BIN
src/assets/sounds/turret_heavy.flac
Normal file
BIN
src/assets/sounds/turret_laser.mp3
Normal file
BIN
src/assets/sounds/turret_release.wav
Normal file
BIN
src/assets/sounds/turret_standard.mp3
Normal file
109
src/index.html
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="shortcut icon" href="assets/img/meteor_logo.png" type="image/png">
|
||||||
|
<link rel="stylesheet" href="style/style.css">
|
||||||
|
<title>Deep Impact</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas class="background" id="mainCanvas"></canvas>
|
||||||
|
|
||||||
|
<div class="page" id="loading" style="display: none;">
|
||||||
|
<h1>LOADING</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-container">
|
||||||
|
<div class="page main-menu" id="main-menu">
|
||||||
|
|
||||||
|
<img id="title" src="assets/img/meteor_title.png" alt="Deep Impact">
|
||||||
|
|
||||||
|
<div class="title-back-container">
|
||||||
|
<span class="title-back" id="title-back"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="level-select" id="menuContainer">
|
||||||
|
<template id="menuItem">
|
||||||
|
<button class="level-item">
|
||||||
|
<img>
|
||||||
|
<p></p>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template id="menuItemCustom">
|
||||||
|
<button class="level-item" onclick="customLevel()">
|
||||||
|
<img src="assets/img/custom.png" alt="Custom Level">
|
||||||
|
<p>Custom Level</p>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page custom-level" id="custom-level" style="display: none;">
|
||||||
|
<h2>Custom Level</h2>
|
||||||
|
<h3></h3>
|
||||||
|
<input type="file" name="Music" id="custom-music">
|
||||||
|
<select name="Theme" id="custom-theme">
|
||||||
|
<option value="meteor">Meteor</option>
|
||||||
|
</select>
|
||||||
|
<button id="custom-level-launch">Launch</button>
|
||||||
|
<button id="custom-level-back">Back to Main</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page game-over" id="game-over" style="display: none;">
|
||||||
|
<h2></h2>
|
||||||
|
<h3></h3>
|
||||||
|
<button id="game-over-back">damn</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="page main-game" id="main-game" style="display: none;">
|
||||||
|
<div class="main-game">
|
||||||
|
<canvas class="main-game-canvas" id="gameCanvas"></canvas>
|
||||||
|
<div class="main-game-title" id="main-game-title">
|
||||||
|
<h1></h1>
|
||||||
|
<h2></h2>
|
||||||
|
</div>
|
||||||
|
<div class="main-game-skyline-container">
|
||||||
|
<div class="main-game-skyline"></div>
|
||||||
|
<img class="main-game-skyline-turret" src="assets/img/skyline_turret2.png">
|
||||||
|
<div class="main-game-skyline"></div>
|
||||||
|
</div>
|
||||||
|
<div class="main-game-dashboard">
|
||||||
|
<div class="main-game-dashboard-inner">
|
||||||
|
<h2 id="health">HP: -1</h2>
|
||||||
|
|
||||||
|
<div class="main-game-dashboard-bullet" id="ammo-laser">
|
||||||
|
<h4>A</h4>
|
||||||
|
<img src="assets/img/turrets/bullet_laser.png" alt="Laser">
|
||||||
|
<h3>100</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-game-dashboard-bullet" id="ammo-fast">
|
||||||
|
<h4>Z</h4>
|
||||||
|
<img src="assets/img/turrets/bullet_fast.png" alt="Fast">
|
||||||
|
<h3>100</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-game-dashboard-bullet" id="ammo-standard">
|
||||||
|
<h4>E</h4>
|
||||||
|
<img src="assets/img/turrets/bullet_standard.png" alt="Standard">
|
||||||
|
<h3>100</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="main-game-dashboard-bullet" id="ammo-heavy">
|
||||||
|
<h4>R</h4>
|
||||||
|
<img src="assets/img/turrets/bullet_heavy.png" alt="Heavy">
|
||||||
|
<h3>100</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="range" id="volume" name="volume" value="5" min="0" max="10">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script type="module" src="scripts/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
258
src/scripts/game.js
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
import { levelList } from "./levelList.js"
|
||||||
|
import Player from "./player.js"
|
||||||
|
import { Bullet, Meteor, Turret } from "./gameObjects.js"
|
||||||
|
import { Renderer } from "./renderer.js"
|
||||||
|
import { setFastTurret, setHeavyTurret, setLaserTurret, setStandardTurret } from "./turretTypes.js"
|
||||||
|
import SoundEffect from "./soundEffect.js"
|
||||||
|
|
||||||
|
export default class Game {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.canvas = document.getElementById("mainCanvas")
|
||||||
|
this.renderer = new Renderer(this.canvas)
|
||||||
|
this.player = new Player()
|
||||||
|
this.levelList = levelList
|
||||||
|
this.currentLevel;
|
||||||
|
this.nextTurretToFire = 0
|
||||||
|
this.turretFire = false
|
||||||
|
this.health = 10
|
||||||
|
this.healthDisplay = document.getElementById("health")
|
||||||
|
this.showTitle = false
|
||||||
|
this.lastSlowUpdate = 0
|
||||||
|
|
||||||
|
this.ammoCurrent = ""
|
||||||
|
this.ammo = {
|
||||||
|
laser: 100,
|
||||||
|
fast: 100,
|
||||||
|
standard: 100,
|
||||||
|
heavy: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundEffect.setEffectVolume(0.3)
|
||||||
|
|
||||||
|
document.addEventListener("keydown", (event) => {
|
||||||
|
switch(event.key) {
|
||||||
|
case "p": this.renderer.showStats = !this.renderer.showStats; break
|
||||||
|
case "o": this.renderer.showCollisionBox = !this.renderer.showCollisionBox; break
|
||||||
|
case "a": this.setTurretTypeAll("laser"); break
|
||||||
|
case "z": this.setTurretTypeAll("fast"); break
|
||||||
|
case "e": this.setTurretTypeAll("standard"); break
|
||||||
|
case "r": this.setTurretTypeAll("heavy"); break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setGameCanvas(gameCanvas = true) {
|
||||||
|
let backgroundColor
|
||||||
|
if (gameCanvas) {
|
||||||
|
this.canvas = document.getElementById("gameCanvas")
|
||||||
|
backgroundColor = "#a9cdd2"
|
||||||
|
} else {
|
||||||
|
this.canvas = document.getElementById("mainCanvas")
|
||||||
|
backgroundColor = "#153a3a"
|
||||||
|
}
|
||||||
|
this.renderer = new Renderer(this.canvas, backgroundColor)
|
||||||
|
this.setSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadMainMenu(level) {
|
||||||
|
await level.loadLevel()
|
||||||
|
this.player.setMusic(level.musicPath)
|
||||||
|
this.player.start()
|
||||||
|
this.currentLevel = level
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadLevel(level) {
|
||||||
|
console.log('Set current level:', level.title)
|
||||||
|
|
||||||
|
this.player.stop()
|
||||||
|
|
||||||
|
await level.loadLevel()
|
||||||
|
await this.setupTurrets()
|
||||||
|
|
||||||
|
this.showTitle = true
|
||||||
|
const mainGameTitle = document.getElementById("main-game-title")
|
||||||
|
mainGameTitle.querySelector("h1").innerText = level.title
|
||||||
|
mainGameTitle.querySelector("h2").innerText = level.subtitle
|
||||||
|
this.setShowTitle(true)
|
||||||
|
setTimeout(() => this.setShowTitle(false), 10000)
|
||||||
|
|
||||||
|
this.player.setMusic(level.musicPath)
|
||||||
|
this.player.start()
|
||||||
|
|
||||||
|
this.canvas.addEventListener("mousemove", (event) => this.setTurretTarget(event))
|
||||||
|
this.canvas.addEventListener("mousedown", () => this.setTurretFire(true))
|
||||||
|
this.canvas.addEventListener("mouseup", () => this.setTurretFire(false))
|
||||||
|
this.canvas.addEventListener("dblclick", () => this.setTurretFire(!this.turretFire))
|
||||||
|
|
||||||
|
const volumeInput = document.getElementById("volume")
|
||||||
|
volumeInput.addEventListener("change", () => {
|
||||||
|
console.log('volume', volumeInput.value)
|
||||||
|
this.player.gain.gain.value = volumeInput.value / 10
|
||||||
|
})
|
||||||
|
|
||||||
|
this.setGameOver()
|
||||||
|
this.player.audio.addEventListener('ended', () => {
|
||||||
|
this.setGameOver(true)
|
||||||
|
this.health = 0
|
||||||
|
})
|
||||||
|
this.currentLevel = level
|
||||||
|
this.renderer.bossImg = this.currentLevel.bossImg
|
||||||
|
|
||||||
|
this.ammoCurrent = "standard"
|
||||||
|
this.ammo = {
|
||||||
|
laser: 100,
|
||||||
|
fast: 100,
|
||||||
|
standard: 100,
|
||||||
|
heavy: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setAmmoCounter()
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowTitle(show = false) {
|
||||||
|
document.getElementById("main-game-title").style.display = show ? "flex" : "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
setGameOver(win = false) {
|
||||||
|
const gameOverTitle = document.getElementById("game-over")
|
||||||
|
gameOverTitle.querySelector("h2").innerText = win ? "YOU HAVE OVERCOME THE ATTACK" : "YOU HAVE PERISHED"
|
||||||
|
gameOverTitle.querySelector("h3").innerText = win ? "Until next time" : "Not much to do left"
|
||||||
|
gameOverTitle.querySelector("button").innerText = win ? "yay" : "damn"
|
||||||
|
}
|
||||||
|
|
||||||
|
setSize(height, width) {
|
||||||
|
this.canvas.width = width ?? window.innerWidth
|
||||||
|
this.canvas.height = height ?? window.innerHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
async setupTurrets() {
|
||||||
|
const turretY = this.canvas.height - 155
|
||||||
|
const firstTurretX = (this.canvas.width - 1500) / 2 + 95
|
||||||
|
|
||||||
|
this.renderer.turrets.push(new Turret(firstTurretX, turretY))
|
||||||
|
this.renderer.turrets.push(new Turret(firstTurretX + 380, turretY - 15))
|
||||||
|
this.renderer.turrets.push(new Turret(firstTurretX + 835, turretY - 10))
|
||||||
|
this.renderer.turrets.push(new Turret(firstTurretX + 1265, turretY + 5))
|
||||||
|
|
||||||
|
for (const turret of this.renderer.turrets) {
|
||||||
|
await setStandardTurret(turret)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ammoCurrent = "standard"
|
||||||
|
}
|
||||||
|
|
||||||
|
setTurretTypeAll(type) {
|
||||||
|
this.ammoCurrent = type
|
||||||
|
this.renderer.turrets.forEach((_, i) => this.setTurretType(i, type))
|
||||||
|
}
|
||||||
|
|
||||||
|
async setTurretType(i, type) {
|
||||||
|
const turret = this.renderer.turrets[i]
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case "standard": await setStandardTurret(turret); break
|
||||||
|
case "fast": await setFastTurret(turret); break
|
||||||
|
case "laser": await setLaserTurret(turret); break
|
||||||
|
case "heavy": await setHeavyTurret(turret); break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTurretTarget(event) {
|
||||||
|
this.renderer.mouseX = event.offsetX
|
||||||
|
this.renderer.mouseY = event.offsetY
|
||||||
|
|
||||||
|
for (const turret of this.renderer.turrets) {
|
||||||
|
const dx = this.renderer.mouseX - turret.x
|
||||||
|
const dy = this.renderer.mouseY - turret.y
|
||||||
|
turret.angle = Math.atan2(dy, dx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTurretFire(fire) {
|
||||||
|
this.turretFire = fire
|
||||||
|
this.renderer.turrets.forEach((turret) => turret.animationRunning = fire)
|
||||||
|
|
||||||
|
// if (!fire) {
|
||||||
|
// SoundEffect.play(SoundEffect.turretRelease)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
setAmmoCounter() {
|
||||||
|
const ammoLaserCounter = document.getElementById("ammo-laser")
|
||||||
|
ammoLaserCounter.querySelector("h3").innerText = this.ammo.laser
|
||||||
|
|
||||||
|
const ammoFastCounter = document.getElementById("ammo-fast")
|
||||||
|
ammoFastCounter.querySelector("h3").innerText = this.ammo.fast
|
||||||
|
|
||||||
|
const ammoStandardCounter = document.getElementById("ammo-standard")
|
||||||
|
ammoStandardCounter.querySelector("h3").innerText = this.ammo.standard
|
||||||
|
|
||||||
|
const ammoHeavyCounter = document.getElementById("ammo-heavy")
|
||||||
|
ammoHeavyCounter.querySelector("h3").innerText = this.ammo.heavy
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnBullet() {
|
||||||
|
if (this.ammo[this.ammoCurrent] <= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const turret = this.renderer.turrets[this.nextTurretToFire]
|
||||||
|
const deltaLastShot = performance.now() - turret.lastShot
|
||||||
|
if (deltaLastShot > turret.firingRate) {
|
||||||
|
const angle = turret.angle - ((Math.floor(Math.random() * turret.spread) / 1000) * (Math.random() < 0.5 ? -1 : 1))
|
||||||
|
this.renderer.bullets.push(new Bullet(turret.x , turret.y, angle, turret.bulletSprite, turret.damage))
|
||||||
|
turret.lastShot = performance.now()
|
||||||
|
// SoundEffect.play(turret.sound)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nextTurretToFire = this.nextTurretToFire < this.renderer.turrets.length - 1 ? this.nextTurretToFire + 1 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnMeteor() {
|
||||||
|
// meteors[Math.floor(Math.random() * this.meteors.length)]
|
||||||
|
this.renderer.meteors.push(new Meteor(this.canvas.width, this.canvas.height, this.currentLevel.maxHealth, this.currentLevel.meteorImg, this.currentLevel.meteorImgList))
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// Frames
|
||||||
|
const now = performance.now()
|
||||||
|
if ((now - this.renderer.lastFrame) > (1000 / 60)) {
|
||||||
|
if (this.turretFire) {
|
||||||
|
this.spawnBullet()
|
||||||
|
}
|
||||||
|
|
||||||
|
const damageTaken = this.renderer.render()
|
||||||
|
|
||||||
|
if (damageTaken > 0 && this.currentLevel.title !== "MainMenu") {
|
||||||
|
SoundEffect.play(SoundEffect.explosionBig)
|
||||||
|
this.health -= damageTaken
|
||||||
|
}
|
||||||
|
|
||||||
|
this.healthDisplay.innerText = `${this.health} HP`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow updates
|
||||||
|
if ((now - this.lastSlowUpdate) > 1000) {
|
||||||
|
console.log('SLOW')
|
||||||
|
if (this.turretFire) {
|
||||||
|
console.log('SET AMMO', this.ammoCurrent, this.ammo)
|
||||||
|
this.ammo.laser += this.ammo.laser < 100 ? 5 : 0
|
||||||
|
this.ammo.fast += this.ammo.fast < 100 ? 5 : 0
|
||||||
|
this.ammo.standard += this.ammo.standard < 100 ? 5 : 0
|
||||||
|
this.ammo.heavy += this.ammo.heavy < 100 ? 5 : 0
|
||||||
|
|
||||||
|
this.ammo[this.ammoCurrent] -= 6
|
||||||
|
|
||||||
|
if (this.ammo[this.ammoCurrent] <= 0) {
|
||||||
|
this.ammo[this.ammoCurrent] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setAmmoCounter()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastSlowUpdate = now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
152
src/scripts/gameObjects.js
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
export class GameObject {
|
||||||
|
x = 0
|
||||||
|
y = 0
|
||||||
|
sizeX = 0
|
||||||
|
sizeY = 0
|
||||||
|
heightX = 0
|
||||||
|
heightY = 0
|
||||||
|
speed = 0
|
||||||
|
angle = 0
|
||||||
|
health = 0
|
||||||
|
spriteImg = new Image()
|
||||||
|
isAnimated = false
|
||||||
|
removeAfterAnimation = false
|
||||||
|
animationRunning = false
|
||||||
|
frames = 0
|
||||||
|
framesCounter = 0
|
||||||
|
|
||||||
|
get sprite() {
|
||||||
|
return this.spriteImg
|
||||||
|
}
|
||||||
|
|
||||||
|
step(tickToSimulate) {
|
||||||
|
for (let i = 0; i < tickToSimulate; i++) {
|
||||||
|
this.x = this.x + this.speed * Math.cos(this.angle)
|
||||||
|
this.y = this.y + this.speed * Math.sin(this.angle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Improve canvas performances
|
||||||
|
this.x = Math.floor(this.x)
|
||||||
|
this.y = Math.floor(this.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crude collision box
|
||||||
|
collideWith(gameObject) {
|
||||||
|
const x = this.x - this.sizeX
|
||||||
|
const y = this.y - this.sizeY
|
||||||
|
const a = x + this.sizeX * 2
|
||||||
|
const b = y + this.sizeY * 2
|
||||||
|
|
||||||
|
const x1 = gameObject.x - gameObject.sizeX
|
||||||
|
const y1 = gameObject.y - gameObject.sizeY
|
||||||
|
const a1 = x1 + gameObject.sizeX * 2
|
||||||
|
const b1 = y1 + gameObject.sizeY * 2
|
||||||
|
|
||||||
|
return !(a < x1 || a1 < x || b < y1 || b1 < y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Meteor extends GameObject {
|
||||||
|
constructor(maxX, maxY, maxHealth, meteorSprite, meteorSpriteList) {
|
||||||
|
super()
|
||||||
|
const xOrigin = Math.floor(Math.random() * maxX - 10) + 10
|
||||||
|
const xTarget = Math.floor(Math.random() * maxX - 20) + 20
|
||||||
|
|
||||||
|
const dx = xTarget - xOrigin
|
||||||
|
const dy = maxY
|
||||||
|
|
||||||
|
this.angle = Math.atan2(dy, dx)
|
||||||
|
this.speed = Math.floor(Math.random() * 0.5) + 0.1
|
||||||
|
this.health = Math.floor(Math.random() * (maxHealth - 1)) + 1
|
||||||
|
|
||||||
|
this.x = xOrigin
|
||||||
|
this.y = 0
|
||||||
|
this.sizeX = 25
|
||||||
|
this.sizeY = 25
|
||||||
|
this.heightX = 256
|
||||||
|
this.heightY = 256
|
||||||
|
this.meteorSprite = meteorSprite
|
||||||
|
this.meteorSpriteList = meteorSpriteList
|
||||||
|
this.isAnimated = false
|
||||||
|
this.removeAfterAnimation = false
|
||||||
|
this.animationRunning = false
|
||||||
|
this.frames = 4
|
||||||
|
this.framesCounter = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
get sprite() {
|
||||||
|
if (this.meteorSprite) {
|
||||||
|
return this.meteorSprite
|
||||||
|
} else if (this.health <= 10) {
|
||||||
|
return this.meteorSpriteList[0]
|
||||||
|
} else if (this.health <= 20) {
|
||||||
|
return this.meteorSpriteList[1]
|
||||||
|
} else if (this.health <= 30) {
|
||||||
|
return this.meteorSpriteList[2]
|
||||||
|
} else {
|
||||||
|
return this.meteorSpriteList[3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Bullet extends GameObject {
|
||||||
|
constructor(x, y, angle, bulletSprite, damage) {
|
||||||
|
super()
|
||||||
|
this.x = x
|
||||||
|
this.y = y
|
||||||
|
this.sizeX = 5
|
||||||
|
this.sizeY = 10
|
||||||
|
this.heightX = 0
|
||||||
|
this.heightY = 0
|
||||||
|
this.spriteImg= bulletSprite
|
||||||
|
this.speed = 1
|
||||||
|
this.angle = angle
|
||||||
|
this.health = 1
|
||||||
|
this.damage = damage
|
||||||
|
this.isAnimated = false
|
||||||
|
this.removeAfterAnimation = false
|
||||||
|
this.animationRunning = false
|
||||||
|
this.frames = 0
|
||||||
|
this.framesCounter = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Explosion extends GameObject {
|
||||||
|
constructor(x, y, explosionSprite) {
|
||||||
|
super()
|
||||||
|
this.x = x
|
||||||
|
this.y = y
|
||||||
|
this.sizeX = 50
|
||||||
|
this.sizeY = 50
|
||||||
|
this.heightX = 256
|
||||||
|
this.heightY = 256
|
||||||
|
this.spriteImg= explosionSprite
|
||||||
|
this.isAnimated = true
|
||||||
|
this.removeAfterAnimation = true
|
||||||
|
this.animationRunning = true
|
||||||
|
this.frames = 32
|
||||||
|
this.framesCounter = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Turret extends GameObject {
|
||||||
|
constructor(x, y) {
|
||||||
|
super()
|
||||||
|
this.x = x
|
||||||
|
this.y = y
|
||||||
|
this.sizeX = 25
|
||||||
|
this.sizeY = 35
|
||||||
|
this.heightX = 0
|
||||||
|
this.heightY = 0
|
||||||
|
this.spriteImg= null
|
||||||
|
this.bulletSprite = null
|
||||||
|
this.isAnimated = false
|
||||||
|
this.removeAfterAnimation = false
|
||||||
|
this.animationRunning = false
|
||||||
|
this.frames = 0
|
||||||
|
this.framesCounter = 0
|
||||||
|
this.firingRate = 0
|
||||||
|
this.spread = 0
|
||||||
|
this.damage = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
86
src/scripts/index.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import Game from './game.js'
|
||||||
|
import { Router } from './router.js'
|
||||||
|
import { preloadEveryThing } from './preloader.js'
|
||||||
|
import { mainMenuLevel } from './levelList.js'
|
||||||
|
|
||||||
|
const router = new Router()
|
||||||
|
const titleBack = document.getElementById("title-back")
|
||||||
|
let game
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
await preloadEveryThing()
|
||||||
|
init()
|
||||||
|
})
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
game = new Game()
|
||||||
|
router.showPage("mainMenu")
|
||||||
|
await loadMenu()
|
||||||
|
mainRenderLoop()
|
||||||
|
document.getElementById("custom-level-launch").addEventListener("click", () => setCustomLevel())
|
||||||
|
document.getElementById("custom-level-back").addEventListener("click", () => router.showPage("mainMenu"))
|
||||||
|
document.getElementById("game-over-back").addEventListener("click", () => router.showPage("mainMenu"))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function mainRenderLoop () {
|
||||||
|
game.player.analyse()
|
||||||
|
|
||||||
|
if (router.currentPage === "mainMenu") {
|
||||||
|
const valueBass = game.player.getRangeAverageRatio(0 ,10)
|
||||||
|
const scaleBass = (1.25 - 1) * valueBass
|
||||||
|
titleBack.style.transform = `scale(${1 + scaleBass}) translateZ(0)`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game.currentLevel) {
|
||||||
|
const value = game.player.getRangeAverageRatio(game.currentLevel.startingValue, game.currentLevel.nbValue)
|
||||||
|
if (value > game.currentLevel.threshold) {
|
||||||
|
game.spawnMeteor()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (game.health <= 0 && router.currentPage === "mainGame") {
|
||||||
|
game.player.stop()
|
||||||
|
game = new Game()
|
||||||
|
game.setGameCanvas(false)
|
||||||
|
await game.loadMainMenu(mainMenuLevel)
|
||||||
|
router.showPage("gameOver")
|
||||||
|
}
|
||||||
|
|
||||||
|
game.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(mainRenderLoop, game.canvas)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadMenu() {
|
||||||
|
const itemTemplate = document.getElementById("menuItem");
|
||||||
|
const itemCustomTemplate = document.getElementById("menuItemCustom");
|
||||||
|
|
||||||
|
const menuContainer = document.getElementById("menuContainer");
|
||||||
|
|
||||||
|
for (const level of game.levelList) {
|
||||||
|
const menuItem = document.importNode(itemTemplate.content, true)
|
||||||
|
|
||||||
|
menuItem.querySelector("img").src = level.bossPath
|
||||||
|
menuItem.querySelector("p").textContent = level.title
|
||||||
|
menuItem.querySelector("button").addEventListener("click", () => loadLevel(level))
|
||||||
|
|
||||||
|
menuContainer.appendChild(menuItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
const customItem = document.importNode(itemCustomTemplate.content, true)
|
||||||
|
customItem.querySelector("button").addEventListener("click", () => router.showPage("customLevel"))
|
||||||
|
menuContainer.appendChild(customItem)
|
||||||
|
|
||||||
|
game.setGameCanvas(false)
|
||||||
|
await game.loadMainMenu(mainMenuLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLevel(level) {
|
||||||
|
router.showPage("mainGame")
|
||||||
|
game.setGameCanvas(true)
|
||||||
|
game.loadLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCustomLevel() {
|
||||||
|
console.log('custom level')
|
||||||
|
}
|
||||||
32
src/scripts/level.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { preloadImage } from "./preloader.js"
|
||||||
|
|
||||||
|
export class Level {
|
||||||
|
constructor(title, subtitle, maxHealth, threshold, startingValue, nbValue, musicPath, meteorPath, bossPath) {
|
||||||
|
this.title = title
|
||||||
|
this.subtitle = subtitle
|
||||||
|
this.maxHealth = maxHealth
|
||||||
|
this.startingValue = startingValue
|
||||||
|
this.nbValue = nbValue
|
||||||
|
this.threshold = threshold
|
||||||
|
this.musicPath = musicPath
|
||||||
|
this.meteorPath = meteorPath
|
||||||
|
this.bossPath = bossPath
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadLevel() {
|
||||||
|
if (this.meteorPath) {
|
||||||
|
this.meteorImg = await preloadImage(this.meteorPath)
|
||||||
|
console.log('load custom meteor', this.meteorImg)
|
||||||
|
} else {
|
||||||
|
this.meteorImgList = []
|
||||||
|
this.meteorImgList.push(await preloadImage("assets/img/meteors/meteor.png"))
|
||||||
|
this.meteorImgList.push(await preloadImage("assets/img/meteors/meteor_2.png"))
|
||||||
|
this.meteorImgList.push(await preloadImage("assets/img/meteors/meteor_3.png"))
|
||||||
|
this.meteorImgList.push(await preloadImage("assets/img/meteors/meteor_4.png"))
|
||||||
|
console.log('load meteor list', this.meteorImgList)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bossImg = await preloadImage(this.bossPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
18
src/scripts/levelList.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Level } from "./level.js"
|
||||||
|
|
||||||
|
// Freq settings
|
||||||
|
// 0 ,10 bass
|
||||||
|
// 150, 40 med
|
||||||
|
// 400, 200 high
|
||||||
|
|
||||||
|
const levelList = [
|
||||||
|
new Level('Great Master', 'Bearer of the thousands', 20, 0.65, 0, 40,'assets/music/The Scene Is Dead (MASTER BOOT RECORD Remix).mp3', 'assets/img/meteors/diploma.png', 'assets/img/boss/r4.png'),
|
||||||
|
new Level('High Tech', 'Comin from above', 20, 0.65, 150, 40,'assets/music/ABIS & Signal & Tasha Baxter - The Wall (Buunshin Remix).flac', 'assets/img/meteors/plane_A.png', 'assets/img/boss/e4.png'),
|
||||||
|
new Level('Richard', '', 25, 0.75, 150, 40, 'assets/music/El Tigr3 - Black Star.flac', null, 'assets/img/boss/richard.png'),
|
||||||
|
new Level('V', '', 30, 0.75, 150, 40, 'assets/music/The Only Thing They Fear is V.flac', null, 'assets/img/boss/samurai.png'),
|
||||||
|
new Level('The End', 'Final attack', 40, 0.75, 90, 40, 'assets/music/The City Must Survive.flac', null, 'assets/img/meteors/meteor.png'),
|
||||||
|
]
|
||||||
|
|
||||||
|
const mainMenuLevel = new Level('MainMenu', '', 1, 0.95, 150, 40, 'assets/music/nightcall_8bit.mp3', null, 'assets/img/meteors/meteor.png')
|
||||||
|
|
||||||
|
export { levelList, mainMenuLevel }
|
||||||
86
src/scripts/player.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
export default class Player {
|
||||||
|
constructor() {
|
||||||
|
this.audioContext = new window.AudioContext()
|
||||||
|
this.gain = this.audioContext.createGain()
|
||||||
|
this.audio = {}
|
||||||
|
this.source = {}
|
||||||
|
|
||||||
|
this.analyser = this.audioContext.createAnalyser()
|
||||||
|
this.analyser.fftSize = 2048
|
||||||
|
|
||||||
|
this.startingScale = 0
|
||||||
|
this.pulseRatio = 1
|
||||||
|
this.maxValueHistory = 100
|
||||||
|
this.hzHistory = []
|
||||||
|
this.frequences = new Uint8Array(this.analyser.frequencyBinCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
createSourceFromAudioElement(audioElement) {
|
||||||
|
const source = this.audioContext.createMediaElementSource(audioElement)
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
connectSource(source) {
|
||||||
|
source.connect(this.gain)
|
||||||
|
this.gain.connect(this.analyser)
|
||||||
|
this.analyser.connect(this.audioContext.destination)
|
||||||
|
this.audio.addEventListener('ended', this.stop)
|
||||||
|
}
|
||||||
|
|
||||||
|
setMusic(trackUrl) {
|
||||||
|
this.audio = new Audio(trackUrl)
|
||||||
|
this.source = this.createSourceFromAudioElement(this.audio)
|
||||||
|
this.connectSource(this.source)
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
if (this.audioContext.state === 'suspended') {
|
||||||
|
this.audioContext.resume()
|
||||||
|
.then(() => this.audio.play())
|
||||||
|
} else {
|
||||||
|
this.audio.play()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.audio?.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
analyse() {
|
||||||
|
this.analyser.getByteFrequencyData(this.frequences)
|
||||||
|
for (let i = 0; i < this.frequences.length; i++) {
|
||||||
|
if (!this.hzHistory[i]) {
|
||||||
|
this.hzHistory[i] = []
|
||||||
|
}
|
||||||
|
if (this.hzHistory[i].length > this.maxValueHistory) {
|
||||||
|
this.hzHistory[i].shift()
|
||||||
|
}
|
||||||
|
this.hzHistory[i].push(this.frequences[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getRangeAverageRatio(startingValue, nbValue) {
|
||||||
|
let total = 0
|
||||||
|
for (let i = startingValue; i < nbValue + startingValue; i++) {
|
||||||
|
total += this.getFrequenceRatio(i)
|
||||||
|
}
|
||||||
|
return total / nbValue
|
||||||
|
}
|
||||||
|
|
||||||
|
getFrequenceRatio(index) {
|
||||||
|
let min = 255
|
||||||
|
let max = 0
|
||||||
|
this.hzHistory[index].forEach(value => {
|
||||||
|
if (value < min) {
|
||||||
|
min = value
|
||||||
|
}
|
||||||
|
if (value > max) {
|
||||||
|
max = value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const scale = max - min
|
||||||
|
const actualValue = this.frequences[index] - min
|
||||||
|
const percentage = scale === 0 ? 0 : actualValue / scale
|
||||||
|
return this.startingScale + this.pulseRatio * percentage
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/scripts/preloader.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { levelList, mainMenuLevel } from "./levelList.js"
|
||||||
|
|
||||||
|
function preloadImage(path) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const image = new Image()
|
||||||
|
image.src = path
|
||||||
|
image.onerror = (e) => {
|
||||||
|
console.error('fuk', path, e)
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
image.onload = resolve(image)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function preloadEveryThing() {
|
||||||
|
for (const level of levelList) {
|
||||||
|
await level.loadLevel()
|
||||||
|
console.log('Preloaded level:', level.title)
|
||||||
|
}
|
||||||
|
await mainMenuLevel.loadLevel()
|
||||||
|
console.log('Preloaded everything')
|
||||||
|
}
|
||||||
|
|
||||||
|
export { preloadImage, preloadEveryThing }
|
||||||
218
src/scripts/renderer.js
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
import { Explosion } from "./gameObjects.js"
|
||||||
|
import { preloadImage } from "./preloader.js"
|
||||||
|
import SoundEffect from "./soundEffect.js"
|
||||||
|
|
||||||
|
export class Renderer {
|
||||||
|
constructor(canvas, backgroundColor) {
|
||||||
|
this.canvas = canvas
|
||||||
|
this.context = this.canvas.getContext("2d", { alpha: false })
|
||||||
|
this.meteors = []
|
||||||
|
this.turrets = []
|
||||||
|
this.bullets = []
|
||||||
|
this.effects = []
|
||||||
|
this.mouseX = -50
|
||||||
|
this.mouseY = -50
|
||||||
|
this.lastFrame = 0
|
||||||
|
this.backgroundColor = backgroundColor
|
||||||
|
this.bossImg = new Image()
|
||||||
|
this.bossY = 0
|
||||||
|
|
||||||
|
this.showStats = false
|
||||||
|
this.showCollisionBox = false
|
||||||
|
|
||||||
|
this.loadSprites()
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadSprites() {
|
||||||
|
this.explosionImg = await preloadImage("assets/img/effects/explosion.png")
|
||||||
|
}
|
||||||
|
|
||||||
|
renderGameObjectSimple(gameObject) {
|
||||||
|
this.context.save()
|
||||||
|
this.context.translate(gameObject.x, gameObject.y)
|
||||||
|
this.context.rotate(gameObject.angle - Math.PI / 2)
|
||||||
|
this.context.drawImage(gameObject.sprite, 0, 0, 20, 20)
|
||||||
|
this.context.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
renderGameObject(gameObject) {
|
||||||
|
let destroy = false
|
||||||
|
let posX = gameObject.x
|
||||||
|
let posY = gameObject.y
|
||||||
|
|
||||||
|
const applyRotation = gameObject.angle !== 0
|
||||||
|
if (applyRotation) {
|
||||||
|
this.context.save()
|
||||||
|
const gameObjectCenterX = gameObject.x
|
||||||
|
const gameObjectCenterY = gameObject.y
|
||||||
|
this.context.translate(gameObjectCenterX, gameObjectCenterY)
|
||||||
|
this.context.rotate(gameObject.angle + Math.PI / 2)
|
||||||
|
this.context.translate(-gameObject.sizeX / 2, -gameObject.sizeY)
|
||||||
|
posX = 0
|
||||||
|
posY = 0
|
||||||
|
// console.log(gameObject.sizeX, gameObject.sizeY, gameObjectCenterX, gameObjectCenterY, gameObject.angle)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameObject.isAnimated) {
|
||||||
|
const frameX = gameObject.heightX * (gameObject.framesCounter % 8)
|
||||||
|
const frameY = gameObject.heightY * Math.floor(gameObject.framesCounter / 8)
|
||||||
|
this.context.drawImage(gameObject.sprite, frameX, frameY, gameObject.heightX, gameObject.heightY, posX, posY, gameObject.sizeX, gameObject.sizeY)
|
||||||
|
|
||||||
|
if (gameObject.animationRunning) {
|
||||||
|
gameObject.framesCounter++
|
||||||
|
if (gameObject.framesCounter === gameObject.frames) {
|
||||||
|
if (gameObject.removeAfterAnimation) {
|
||||||
|
destroy = true
|
||||||
|
} else {
|
||||||
|
gameObject.framesCounter = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.context.drawImage(gameObject.sprite, posX, posY, gameObject.sizeX, gameObject.sizeY)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (applyRotation) {
|
||||||
|
this.context.restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showCollisionBox) {
|
||||||
|
this.renderCollisionBox(gameObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
return destroy
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBackground() {
|
||||||
|
this.context.fillStyle = this.backgroundColor
|
||||||
|
this.context.fillRect(0, 0, this.canvas.width, this.canvas.height)
|
||||||
|
this.context.fill()
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDebug(startTime, deltaTime) {
|
||||||
|
const endTime = performance.now()
|
||||||
|
const fps = Math.round(1 / (deltaTime / 1000))
|
||||||
|
|
||||||
|
this.context.font = '25px serif';
|
||||||
|
this.context.fillStyle = 'black'
|
||||||
|
this.context.textAlign = 'start'
|
||||||
|
this.context.fillText(`FPS: ${fps}`, 10, 50)
|
||||||
|
this.context.fillText(`Delta: ${deltaTime}`, 10, 75)
|
||||||
|
this.context.fillText(`Frame time: ${endTime - startTime}`, 10, 100)
|
||||||
|
this.context.fillText(`Meteors: ${this.meteors.length}`, 10, 125)
|
||||||
|
this.context.fillText(`Bullets: ${this.bullets.length}`, 10, 150)
|
||||||
|
this.context.fillText(`Mouse x: ${this.mouseX} y:${this.mouseY}`, 10, 175)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTarget() {
|
||||||
|
this.context.beginPath()
|
||||||
|
this.context.arc(this.mouseX, this.mouseY, 25, 0, 2 * Math.PI, false)
|
||||||
|
this.context.lineWidth = 1
|
||||||
|
this.context.strokeStyle = 'red'
|
||||||
|
this.context.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
renderCollisionBox(gameObject) {
|
||||||
|
this.context.strokeRect(gameObject.x - gameObject.sizeX, gameObject.y - gameObject.sizeY, gameObject.sizeX * 2, gameObject.sizeY * 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMeteorLoop(deltaTime) {
|
||||||
|
let damageTaken = 0
|
||||||
|
for (let i = this.meteors.length - 1; i >= 0; i--) {
|
||||||
|
const meteor = this.meteors[i]
|
||||||
|
|
||||||
|
meteor.step(deltaTime)
|
||||||
|
|
||||||
|
if (meteor.y > this.canvas.height) {
|
||||||
|
damageTaken += 1
|
||||||
|
this.meteors.splice(i, 1)
|
||||||
|
} else if(meteor.x < 0 || meteor.x > this.canvas.width) {
|
||||||
|
this.meteors.splice(i, 1)
|
||||||
|
} else if (meteor.health <= 0) {
|
||||||
|
SoundEffect.play(SoundEffect.explosion)
|
||||||
|
this.effects.push(new Explosion(meteor.x, meteor.y, this.explosionImg))
|
||||||
|
this.meteors.splice(i, 1)
|
||||||
|
} else if (this.renderGameObject(meteor)) {
|
||||||
|
this.meteors.splice(i, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return damageTaken
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBulletLoop(deltaTime) {
|
||||||
|
for (let i = this.bullets.length - 1; i >= 0; i--) {
|
||||||
|
const bullet = this.bullets[i]
|
||||||
|
let collision = false
|
||||||
|
|
||||||
|
bullet.step(deltaTime)
|
||||||
|
|
||||||
|
if (
|
||||||
|
bullet.y > this.canvas.height || bullet.y < 0 || bullet.x > this.canvas.width || bullet.x < 0
|
||||||
|
) {
|
||||||
|
this.bullets.splice(i, 1)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
for (let j = this.meteors.length - 1; j >= 0; j--) {
|
||||||
|
const meteor = this.meteors[j]
|
||||||
|
collision = bullet.collideWith(meteor)
|
||||||
|
if (collision) {
|
||||||
|
this.bullets.splice(i, 1)
|
||||||
|
meteor.health -= bullet.damage
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!collision) {
|
||||||
|
this.renderGameObject(bullet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTurretLoop() {
|
||||||
|
for (const turret of this.turrets) {
|
||||||
|
this.renderGameObject(turret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEffectLoop(deltaTime) {
|
||||||
|
for (let i = this.effects.length - 1; i >= 0; i--) {
|
||||||
|
const effect = this.effects[i]
|
||||||
|
|
||||||
|
effect.step(deltaTime)
|
||||||
|
|
||||||
|
if (this.renderGameObject(effect)) {
|
||||||
|
this.effects.splice(i, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSun() {
|
||||||
|
this.context.drawImage(this.bossImg, this.canvas.width / 2, (this.canvas.height + 250) - this.bossY, 100, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const startTime = performance.now()
|
||||||
|
const deltaTime = startTime - this.lastFrame
|
||||||
|
|
||||||
|
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)
|
||||||
|
this.renderBackground()
|
||||||
|
this.renderSun()
|
||||||
|
|
||||||
|
const damageTaken = this.renderMeteorLoop(deltaTime)
|
||||||
|
this.renderBulletLoop(deltaTime)
|
||||||
|
this.renderTurretLoop()
|
||||||
|
this.renderEffectLoop(deltaTime)
|
||||||
|
|
||||||
|
this.renderTarget()
|
||||||
|
|
||||||
|
if (this.showStats) {
|
||||||
|
this.renderDebug(startTime, deltaTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastFrame = startTime
|
||||||
|
return damageTaken
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/scripts/router.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
export class Router {
|
||||||
|
currentPage = "none"
|
||||||
|
|
||||||
|
mainMenu = document.getElementById("main-menu")
|
||||||
|
customLevel = document.getElementById("custom-level")
|
||||||
|
loading = document.getElementById("loading")
|
||||||
|
mainGame = document.getElementById("main-game")
|
||||||
|
gameOver = document.getElementById("game-over")
|
||||||
|
|
||||||
|
hideEverything() {
|
||||||
|
this.mainMenu.style.display = "none"
|
||||||
|
this.customLevel.style.display = "none"
|
||||||
|
this.loading.style.display = "none"
|
||||||
|
this.mainGame.style.display = "none"
|
||||||
|
this.gameOver.style.display = "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
showPage(pageName) {
|
||||||
|
this.hideEverything()
|
||||||
|
this.currentPage = pageName
|
||||||
|
this[pageName].style.display = "flex"
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/scripts/soundEffect.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
export default class SoundEffect {
|
||||||
|
static turretLaser = new Audio("assets/sounds/turret_laser.mp3")
|
||||||
|
static turretFast = new Audio("assets/sounds/turret_fast.mp3")
|
||||||
|
static turretStandard = new Audio("assets/sounds/turret_standard.mp3")
|
||||||
|
static turretHeavy = new Audio("assets/sounds/turret_heavy.flac")
|
||||||
|
static explosion = new Audio("assets/sounds/explosion.mp3")
|
||||||
|
static explosionBig = new Audio("assets/sounds/explosion_big.mp3")
|
||||||
|
static turretRelease = new Audio("assets/sounds/turret_release.wav")
|
||||||
|
|
||||||
|
static soundList = [
|
||||||
|
SoundEffect.turretLaser,
|
||||||
|
SoundEffect.turretFast,
|
||||||
|
SoundEffect.turretStandard,
|
||||||
|
SoundEffect.turretHeavy,
|
||||||
|
SoundEffect.explosion,
|
||||||
|
SoundEffect.explosionBig,
|
||||||
|
SoundEffect.turretRelease
|
||||||
|
]
|
||||||
|
|
||||||
|
static setEffectVolume(volume) {
|
||||||
|
SoundEffect.soundList.forEach((sound) => sound.volume = volume)
|
||||||
|
}
|
||||||
|
|
||||||
|
static stopAll() {
|
||||||
|
SoundEffect.soundList.forEach((sound) => sound.pause())
|
||||||
|
}
|
||||||
|
|
||||||
|
static play(audio) {
|
||||||
|
audio.paused ? audio.play() : audio.currentTime = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/scripts/turretTypes.js
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import { preloadImage } from "./preloader.js"
|
||||||
|
import SoundEffect from "./soundEffect.js"
|
||||||
|
|
||||||
|
async function setStandardTurret(turret) {
|
||||||
|
turret.spriteImg= await preloadImage("assets/img/turrets/turret_standard.png")
|
||||||
|
turret.bulletSprite = await preloadImage("assets/img/turrets/bullet_standard.png")
|
||||||
|
turret.firingRate = 100
|
||||||
|
turret.lastShot = 0
|
||||||
|
turret.spread = 100
|
||||||
|
turret.damage = 20
|
||||||
|
turret.isAnimated = true
|
||||||
|
turret.removeAfterAnimation = false
|
||||||
|
turret.animationRunning = false
|
||||||
|
turret.frames = 8
|
||||||
|
turret.heightX = 256
|
||||||
|
turret.heightY = 256
|
||||||
|
turret.sound = SoundEffect.turretStandard
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setFastTurret(turret) {
|
||||||
|
turret.spriteImg= await preloadImage("assets/img/turrets/turret_fast.png")
|
||||||
|
turret.bulletSprite = await preloadImage("assets/img/turrets/bullet_fast.png")
|
||||||
|
turret.firingRate = 20
|
||||||
|
turret.lastShot = 0
|
||||||
|
turret.spread = 200
|
||||||
|
turret.damage = 10
|
||||||
|
turret.isAnimated = true
|
||||||
|
turret.removeAfterAnimation = false
|
||||||
|
turret.animationRunning = false
|
||||||
|
turret.frames = 8
|
||||||
|
turret.heightX = 256
|
||||||
|
turret.heightY = 256
|
||||||
|
turret.sound = SoundEffect.turretFast
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setHeavyTurret(turret) {
|
||||||
|
turret.spriteImg= await preloadImage("assets/img/turrets/turret_heavy.png")
|
||||||
|
turret.bulletSprite = await preloadImage("assets/img/turrets/bullet_heavy.png")
|
||||||
|
turret.firingRate = 200
|
||||||
|
turret.lastShot = 0
|
||||||
|
turret.spread = 50
|
||||||
|
turret.damage = 30
|
||||||
|
turret.isAnimated = true
|
||||||
|
turret.removeAfterAnimation = false
|
||||||
|
turret.animationRunning = false
|
||||||
|
turret.frames = 16
|
||||||
|
turret.heightX = 256
|
||||||
|
turret.heightY = 256
|
||||||
|
turret.sound = SoundEffect.turretHeavy
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setLaserTurret(turret) {
|
||||||
|
turret.spriteImg= await preloadImage("assets/img/turrets/turret_laser.png")
|
||||||
|
turret.bulletSprite = await preloadImage("assets/img/turrets/bullet_laser.png")
|
||||||
|
turret.firingRate = 0
|
||||||
|
turret.lastShot = 0
|
||||||
|
turret.spread = 10
|
||||||
|
turret.damage = 2
|
||||||
|
turret.isAnimated = true
|
||||||
|
turret.removeAfterAnimation = false
|
||||||
|
turret.animationRunning = false
|
||||||
|
turret.frames = 8
|
||||||
|
turret.heightX = 256
|
||||||
|
turret.heightY = 256
|
||||||
|
turret.sound = SoundEffect.turretLaser
|
||||||
|
}
|
||||||
|
|
||||||
|
export { setStandardTurret, setFastTurret, setHeavyTurret, setLaserTurret }
|
||||||
237
src/style/style.css
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'ka';
|
||||||
|
src: url('../assets/fonts/ka1.ttf');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'revamped';
|
||||||
|
src: url('../assets/fonts/Revamped.otf');
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
font-family: 'ka';
|
||||||
|
color: rgb(223, 223, 223);
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
border: 2px solid #24201e !important;
|
||||||
|
padding: 10px 15px;
|
||||||
|
font-size: 30px;
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: #04ab96;
|
||||||
|
background-color: #153a3a;
|
||||||
|
transition: 0.25s;
|
||||||
|
border: 6px solid;
|
||||||
|
border-color: #368b8c;
|
||||||
|
border-radius: 10px;
|
||||||
|
font: inherit;
|
||||||
|
font-size: 30px;
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0.5em;
|
||||||
|
padding: 10px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
box-shadow: inset 0 0 0 2.2em #24201e;
|
||||||
|
border-color: #24201e;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus {
|
||||||
|
box-shadow: inset 0 0 0 2.2em #461f1e;
|
||||||
|
border-color: #24201e;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
box-shadow: inset 0 0 0 2.2em #5a5a5a;
|
||||||
|
border-color: #24201e;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: -2;
|
||||||
|
/* filter: blur(5px); */
|
||||||
|
background-color: #153a3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-container {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-back {
|
||||||
|
height: 400px;
|
||||||
|
width: 400px;
|
||||||
|
background-color: #246363;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-back-container {
|
||||||
|
z-index: -1;
|
||||||
|
top: 0px;
|
||||||
|
left: 0px;
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-select {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-item img {
|
||||||
|
height: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.level-item p {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-canvas {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #a9cdd2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-skyline-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: end;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-skyline {
|
||||||
|
background-image: url("../assets/img/skyline.png");
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
background-size: contain;
|
||||||
|
width: 100%;
|
||||||
|
height: 68px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-skyline-turret {
|
||||||
|
width: 1500px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-dashboard {
|
||||||
|
height: 80px;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #264649;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-dashboard-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-dashboard-inner h2 {
|
||||||
|
margin: 30px;
|
||||||
|
margin-right: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-dashboard-inner input {
|
||||||
|
margin-left: 55px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-dashboard-bullet {
|
||||||
|
max-height: 60px;
|
||||||
|
margin: 15px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-dashboard-bullet img {
|
||||||
|
max-height: 60px;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-turret-selector {
|
||||||
|
margin: 30px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-turret-selector h2 {
|
||||||
|
margin: 0;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-title {
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1;
|
||||||
|
font-size: 30px;
|
||||||
|
margin-bottom: 500px;
|
||||||
|
color: black;
|
||||||
|
font-family: 'revamped';
|
||||||
|
animation: 4s anim-lineUp ease-out;
|
||||||
|
transition: opacity 2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-game-title h2 {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-lineUp {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(80%);
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0%);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0%);
|
||||||
|
}
|
||||||
|
}
|
||||||
257
turret.svg
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="210mm"
|
||||||
|
height="297mm"
|
||||||
|
viewBox="0 0 210 297"
|
||||||
|
version="1.1"
|
||||||
|
id="svg5"
|
||||||
|
sodipodi:docname="turret.svg"
|
||||||
|
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview7"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="1.6017536"
|
||||||
|
inkscape:cx="261.90045"
|
||||||
|
inkscape:cy="245.35608"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1403"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="layer1" />
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<g
|
||||||
|
inkscape:label="Calque 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<ellipse
|
||||||
|
style="fill:#1f4e4e;fill-opacity:1;stroke-width:2.87326;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="path2429-9"
|
||||||
|
cx="-39.799274"
|
||||||
|
cy="55.362583"
|
||||||
|
rx="15.211061"
|
||||||
|
ry="7.6665535"
|
||||||
|
transform="matrix(0.12902754,-0.99164101,0.90891401,0.41698361,0,0)" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1c4646;fill-opacity:1;stroke-width:3.42542;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect846"
|
||||||
|
width="109.59061"
|
||||||
|
height="20.250095"
|
||||||
|
x="29.194195"
|
||||||
|
y="63.018677"
|
||||||
|
ry="5.1613932" />
|
||||||
|
<circle
|
||||||
|
style="fill:#2a6a6a;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="path1068"
|
||||||
|
cx="91.835709"
|
||||||
|
cy="54.290508"
|
||||||
|
r="26.388836" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1c4646;fill-opacity:1;stroke-width:4.02856;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect1172"
|
||||||
|
width="77.157654"
|
||||||
|
height="33.978165"
|
||||||
|
x="56.722515"
|
||||||
|
y="49.399384"
|
||||||
|
ry="6.9964342" />
|
||||||
|
<rect
|
||||||
|
style="fill:#2a6a6a;fill-opacity:1;stroke-width:2.229;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect1584"
|
||||||
|
width="6.2768173"
|
||||||
|
height="38.153851"
|
||||||
|
x="89.892723"
|
||||||
|
y="0.88694668"
|
||||||
|
ry="0.6823135" />
|
||||||
|
<rect
|
||||||
|
style="fill:#2a6a6a;fill-opacity:1;stroke-width:2.91785;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect1688"
|
||||||
|
width="8.56147"
|
||||||
|
height="16.156712"
|
||||||
|
x="88.666779"
|
||||||
|
y="-11.73787"
|
||||||
|
ry="1.1692015" />
|
||||||
|
<rect
|
||||||
|
style="fill:#2a6a6a;fill-opacity:1;stroke-width:2.07668;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect1584-6"
|
||||||
|
width="6.2984138"
|
||||||
|
height="33.003654"
|
||||||
|
x="102.72283"
|
||||||
|
y="6.3914118"
|
||||||
|
ry="0.59021139" />
|
||||||
|
<rect
|
||||||
|
style="fill:#2a6a6a;fill-opacity:1;stroke-width:2.71845;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect1688-7"
|
||||||
|
width="8.5909271"
|
||||||
|
height="13.975797"
|
||||||
|
x="101.49266"
|
||||||
|
y="-0.60135365"
|
||||||
|
ry="1.0113769" />
|
||||||
|
<rect
|
||||||
|
style="fill:#2a6a6a;fill-opacity:1;stroke-width:2.07668;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect1584-6-5"
|
||||||
|
width="6.2984138"
|
||||||
|
height="33.003654"
|
||||||
|
x="76.715218"
|
||||||
|
y="6.4353666"
|
||||||
|
ry="0.59021139" />
|
||||||
|
<rect
|
||||||
|
style="fill:#2a6a6a;fill-opacity:1;stroke-width:2.71845;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect1688-7-3"
|
||||||
|
width="8.5909271"
|
||||||
|
height="13.975797"
|
||||||
|
x="75.485054"
|
||||||
|
y="-0.52603054"
|
||||||
|
ry="1.0113769" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1c4646;fill-opacity:1;stroke-width:3.22305;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect2325"
|
||||||
|
width="109.72138"
|
||||||
|
height="16.428574"
|
||||||
|
x="29.129381"
|
||||||
|
y="67.389717"
|
||||||
|
ry="1.3843635" />
|
||||||
|
<ellipse
|
||||||
|
style="fill:#2c6f6f;fill-opacity:1;stroke-width:2.19511;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="path2429"
|
||||||
|
cx="12.225694"
|
||||||
|
cy="67.713127"
|
||||||
|
rx="11.02937"
|
||||||
|
ry="6.1711597"
|
||||||
|
transform="matrix(0.80578104,-0.59221358,0.48609823,0.87390418,0,0)" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1f4e4e;fill-opacity:1;stroke-width:2.19678;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3364"
|
||||||
|
width="1.3929656"
|
||||||
|
height="15.085695"
|
||||||
|
x="1.4353182"
|
||||||
|
y="52.755768"
|
||||||
|
ry="0.70942533"
|
||||||
|
transform="matrix(0.70515715,-0.70905105,0.60559272,0.79577475,0,0)" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368686;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="59.967163"
|
||||||
|
y="54.652615"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1f4e4e;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-0"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="69.294838"
|
||||||
|
y="54.578323"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368686;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-6"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="69.294838"
|
||||||
|
y="54.578323"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1f4e4e;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-0-2"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="78.622513"
|
||||||
|
y="54.504032"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368686;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-6-6"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="78.622513"
|
||||||
|
y="54.504032"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1f4e4e;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-0-2-1"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="87.950188"
|
||||||
|
y="54.429741"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368686;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-6-6-8"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="87.950188"
|
||||||
|
y="54.429741"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1f4e4e;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-0-2-1-7"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="97.27787"
|
||||||
|
y="54.35545"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368686;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-6-6-8-9"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="97.27787"
|
||||||
|
y="54.35545"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1f4e4e;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-0-2-1-7-2"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="106.60555"
|
||||||
|
y="54.281158"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368686;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-6-6-8-0"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="106.60555"
|
||||||
|
y="54.281158"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1f4e4e;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-0-2-1-7-23"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="115.93322"
|
||||||
|
y="54.206867"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368686;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-6-6-8-0-7"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="115.93322"
|
||||||
|
y="54.206871"
|
||||||
|
ry="1.0159339" />
|
||||||
|
<rect
|
||||||
|
style="fill:#368686;fill-opacity:1;stroke-width:3.175;stroke-linecap:round;stroke-miterlimit:50;paint-order:stroke markers fill"
|
||||||
|
id="rect3779-0-2-1-7-23-5"
|
||||||
|
width="6.1063471"
|
||||||
|
height="6.3674345"
|
||||||
|
x="125.26089"
|
||||||
|
y="54.13258"
|
||||||
|
ry="1.0159339" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 8.9 KiB |