added spore mod

This commit is contained in:
landgreen
2019-10-30 06:00:08 -07:00
parent db35278dc1
commit f7674f4ffd
11 changed files with 9430 additions and 0 deletions

422
index.html Normal file
View File

@@ -0,0 +1,422 @@
<!DOCTYPE html>
<html>
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-113454647-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-113454647-1');
</script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Browser-based side scrolling video game with the matter.js physics engine.">
<meta name="author" content="Ross Landgreen">
<meta property="og:description" content="Browser-based side scrolling video game with the matter.js physics engine.">
<meta property="og:title" content="n-gon">
<meta name="twitter:title" content="n-gon">
<meta name="twitter:description" content="Browser-based side scrolling video game with the matter.js physics engine.">
<title>n-gon</title>
<link rel="stylesheet" href="style.css">
<link rel='shortcut icon' href='favicon.ico' type='image/x-icon' />
</head>
<body>
<!-- <body oncontextmenu="return false"> -->
<div id='guns'></div>
<div id='field'></div>
<div id='mods'></div>
<div id="text-log"></div>
<div id="fade-out"></div>
<div id="health-bg"></div>
<div id="health"></div>
<div id="dmg"></div>
<!-- guns -->
<!-- <audio id="snare2" src="sounds\guns\snare2.ogg" preload="auto"></audio>
<audio id="airgun" src="sounds\guns\airgun.ogg" preload="auto"></audio>
<audio id="basssnaredrum" src="sounds\guns\basssnaredrum.ogg" preload="auto"></audio>
<audio id="sniper" src="sounds\guns\sniper.ogg" preload="auto"></audio>
<audio id="glock" src="sounds\guns\glock.ogg" preload="auto"></audio>
<audio id="launcher" src="sounds\guns\launcher2.ogg" preload="auto"></audio> -->
<!-- player walk -->
<!-- <audio id="walk1" src="sounds\mech\walk1.ogg" preload="auto"></audio>
<audio id="walk2" src="sounds\mech\walk2.ogg" preload="auto"></audio>
<audio id="walk3" src="sounds\mech\walk3.ogg" preload="auto"></audio>
<audio id="walk4" src="sounds\mech\walk4.ogg" preload="auto"></audio>
<audio id="walk5" src="sounds\mech\walk5.ogg" preload="auto"></audio>
<audio id="walk6" src="sounds\mech\walk6.ogg" preload="auto"></audio>
<audio id="walk7" src="sounds\mech\walk7.ogg" preload="auto"></audio>
<audio id="walk8" src="sounds\mech\walk8.ogg" preload="auto"></audio>
<audio id="walk9" src="sounds\mech\walk9.ogg" preload="auto"></audio>
<audio id="walk10" src="sounds\mech\walk10.ogg" preload="auto"></audio> -->
<!-- player dmg -->
<!-- <audio id="dmg0" src="sounds\dmg\dmg0.ogg" preload="auto"></audio>
<audio id="dmg1" src="sounds\dmg\dmg1.ogg" preload="auto"></audio>
<audio id="dmg2" src="sounds\dmg\dmg2.ogg" preload="auto"></audio>
<audio id="dmg3" src="sounds\dmg\dmg3.ogg" preload="auto"></audio> -->
<!-- other -->
<!-- <audio id="boom" src="sounds\boom.ogg" preload="auto"></audio>
<audio id="powerup" src="sounds\powerup4.ogg" preload="auto"></audio>
<audio id="no" src="sounds\no.ogg" preload="auto"></audio>
<audio id="click" src="sounds\click.ogg" preload="auto"></audio>
<audio id="ammo" src="sounds\ammo.ogg" preload="auto"></audio> -->
<canvas id="canvas"></canvas>
<!-- ********** intro page ***********************************************
******************************************************************************* -->
<div id="controls">
<details>
<summary>info</summary>
<div id="controls-div">
<table>
<tr>
<td>FIRE</td>
<td>left mouse</td>
<td></td>
</tr>
<tr>
<td>FIELD</td>
<td>right mouse / spacebar</td>
</tr>
<tr>
<td>MOVE</td>
<td>WASD / arrows</td>
</tr>
<tr>
<td>GUNS</td>
<td>Q / E / mouse wheel</td>
</tr>
<tr>
<td>ZOOM</td>
<td>+ / -</td>
</tr>
<tr>
<td>PAUSE</td>
<td>P</td>
</tr>
<!-- <tr>
<td>FULL SCREEN</td>
<td>enter</td>
</tr> -->
</table>
<br>
<a href="https://github.com/landgreen/n-gon">
<svg viewBox="0 0 100 16" xmlns="http://www.w3.org/2000/svg" fill="#1B1F23">
<path d="M8 0C3.58 0 0 3.58 0 8C0 11.54 2.29 14.53 5.47 15.59C5.87 15.66 6.02 15.42 6.02 15.21C6.02 15.02 6.01 14.39 6.01 13.72C4 14.09 3.48 13.23 3.32 12.78C3.23 12.55 2.84 11.84 2.5 11.65C2.22 11.5 1.82 11.13 2.49 11.12C3.12 11.11 3.57 11.7 3.72 11.94C4.44 13.15 5.59 12.81 6.05 12.6C6.12 12.08 6.33 11.73 6.56 11.53C4.78 11.33 2.92 10.64 2.92 7.58C2.92 6.71 3.23 5.99 3.74 5.43C3.66 5.23 3.38 4.41 3.82 3.31C3.82 3.31 4.49 3.1 6.02 4.13C6.66 3.95 7.34 3.86 8.02 3.86C8.7 3.86 9.38 3.95 10.02 4.13C11.55 3.09 12.22 3.31 12.22 3.31C12.66 4.41 12.38 5.23 12.3 5.43C12.81 5.99 13.12 6.7 13.12 7.58C13.12 10.65 11.25 11.33 9.47 11.53C9.76 11.78 10.01 12.26 10.01 13.01C10.01 14.08 10 14.94 10 15.21C10 15.42 10.15 15.67 10.55 15.59C13.71 14.53 16 11.53 16 8C16 3.58 12.42 0 8 0Z" />
<g stroke='none' font-size="8px" font-family="Arial Black, sans-serif">
<text x="19" y="11">Github</text>
</g>
</svg>
</a>
<p>Written by Ross Landgreen</p>
</div>
</details>
</div>
<style>
.fade-in {
opacity: 0;
animation: 4s ease 3s normal forwards 1 fadein;
}
@keyframes fadein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes dash {
to {
stroke-dashoffset: 0;
}
}
.draw-lines {
stroke-dasharray: 20;
stroke-dashoffset: 20;
animation: dash 4.8s ease-in forwards;
}
.draw-lines-box-1 {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: dash 10s ease-in forwards;
animation-delay: 0s;
}
.draw-lines-box-2 {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: dash 5.2s ease-in forwards;
animation-delay: 2s;
}
.draw-lines-box-3 {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: dash 2.3s ease-in forwards;
animation-delay: 3.1s;
}
.draw-lines3 {
stroke-dasharray: 3000;
stroke-dashoffset: 3000;
animation: dash 6.2s ease-in forwards;
}
.draw-lines4 {
stroke-dasharray: 300;
stroke-dashoffset: 300;
animation: dash 5s ease-in forwards;
}
</style>
<svg id='splash' class="intro" viewBox="0 0 800 800" onclick="game.startGame()">
<!-- title -->
<!-- <g class="fade-in" transform="translate(100,210) scale(34)" fill='#bbb' stroke='none'>
<path d="M0 0 h1 v0.2 h1.7 l0.3 0.3 v2.6 h-1 v-1.7 h-1 v1.7 h-1 z" fill="rgb(50,200,255)" />
<rect x="4" y="1.25" width="1" height="0.5" rx='0.03' />
<path transform="translate(6.9,0) scale(1.25)" d="M0 0 h1 l 0.7 0.7 v2.3 l-0.2 0.2 h-1.8 v-0.5 h1.4 L 1.1 2.4 h-1.1 l -0.7 -0.7 v-1 l 0.7 -0.7 Z"
fill='rgb(255,70,140)' />
<path transform="translate(10.9,0) scale(1.25)" d="M0 0 h1 l 0.7 0.7 v1 l -0.7 0.7 h-1 l -0.7 -0.7 v-1 l 0.7 -0.7 Z"
fill="none" />
<path transform="translate(14,0)" d="M0 0 h1 v0.2 h1.7 l0.3 0.3 v2.6 h-1 v-1.7 h-1 v1.7 h-1 z" fill='rgb(10,220,190)'
/>
</g> -->
<g class="fade-in" transform="translate(100,210) scale(34)" fill='#bbb' stroke='none'>
<path d="M0 0 h1 v0.2 h1.7 l0.3 0.3 v2.6 h-1 v-1.7 h-1 v1.7 h-1 z" />
<rect x="4" y="1.25" width="1" height="0.5" rx='0.03' />
<path transform="translate(6.9,0) scale(1.25)" d="M0 0 h1 l 0.7 0.7 v2.3 l-0.2 0.2 h-1.8 v-0.5 h1.4 L 1.1 2.4 h-1.1 l -0.7 -0.7 v-1 l 0.7 -0.7 Z" />
<path transform="translate(10.9,0) scale(1.25)" d="M0 0 h1 l 0.7 0.7 v1 l -0.7 0.7 h-1 l -0.7 -0.7 v-1 l 0.7 -0.7 Z" />
<path transform="translate(14,0)" d="M0 0 h1 v0.2 h1.7 l0.3 0.3 v2.6 h-1 v-1.7 h-1 v1.7 h-1 z" />
</g>
<g class="draw-lines" transform="translate(100,210) scale(34)" fill='none' stroke='#222' stroke-linejoin="round" stroke-linecap="round">
<path d="M0 0 h1 v0.2 h1.7 l0.3 0.3 v2.6 h-1 v-1.7 h-1 v1.7 h-1 z" stroke-width='0.0875' />
<rect x="4" y="1.25" width="1" height="0.5" stroke-width='0.0875' rx='0.03' />
<path transform="translate(6.9,0) scale(1.25)" d="M0 0 h1 l 0.7 0.7 v2.3 l-0.2 0.2 h-1.8 v-0.5 h1.4 L 1.1 2.4 h-1.1 l -0.7 -0.7 v-1 l 0.7 -0.7 Z" stroke-width='0.07' />
<path transform="translate(10.9,0) scale(1.25)" d="M0 0 h1 l 0.7 0.7 v1 l -0.7 0.7 h-1 l -0.7 -0.7 v-1 l 0.7 -0.7 Z" stroke-width='0.07' />
<path transform="translate(14,0)" d="M0 0 h1 v0.2 h1.7 l0.3 0.3 v2.6 h-1 v-1.7 h-1 v1.7 h-1 z" stroke-width='0.0875' />
</g>
<!-- mouse -->
<g class="draw-lines3" transform="translate(290,430) scale(0.28)" stroke-linecap="round" stroke-linejoin="round" stroke-width="10px" stroke="#222" fill="none">
<path class="fade-in" d="M832.41,106.64 V323.55 H651.57 V256.64 c0-82.5,67.5-150,150-150 Z" fill="rgb(0, 200, 255)" stroke="none" />
<path d="M827,112 h30 a140,140,0,0,1,140,140 v268 a140,140,0,0,1-140,140 h-60 a140,140,0,0,1-140-140v-268 a140,140,0,0,1,140-140h60" />
<path d="M657 317 h 340 h-170 v-207 s 21 -59, -5 -59 S 807 7, 807 7" />
<ellipse fill="#fff" cx="827.57" cy="218.64" rx="29" ry="68" />
</g>
<!-- keys -->
<g transform="translate(195,480) scale(0.8)">
<g fill='none' stroke='#222' stroke-width="3.5" stroke-linejoin="round">
<path d="M0 60 h60 v-60 h-60 v60" class="draw-lines-box-1" />
<path d="M70 60 h60 v-60 h-60 v60" class="draw-lines-box-2" />
<path d="M140 60 h60 v-60 h-60 v60" class="draw-lines-box-3" />
<path d="M0 70 h60 v60 h-60 v-60" class="draw-lines-box-1" />
<path d="M70 70 h60 v60 h-60 v-60" class="draw-lines-box-2" />
<path d="M140 70 h60 v60 h-60 v-60" class="draw-lines-box-3" />
<!-- <rect x="0" y="0" width="60" height="60" rx='3' /> -->
<!-- <rect x="140" y="0" width="60" height="60" rx='3' /> -->
<!-- <rect x="0" y="70" width="60" height="60" rx='3' /> -->
<!-- <rect x="70" y="0" width="60" height="60" rx='3' /> -->
<!-- <rect x="70" y="70" width="60" height="60" rx='3' /> -->
<!-- <rect x="140" y="70" width="60" height="60" rx='3' /> -->
</g>
<g class="draw-lines4" text-anchor="middle" stroke='#000' fill='none' stroke-width="2" font-size="38px" font-family="Arial Black, sans-serif">
<!-- <text class="fade-in" fill='#aaa' stroke="none" x="30" y="45">Q</text>
<text class="fade-in" fill='#aaa' stroke="none" x="170" y="45">E</text> -->
<text x="30" y="45" stroke-width="2">Q</text>
<text x="170" y="45" stroke-width="2">E</text>
<text x="100" y="45">W</text>
<text x="100" y="113">S</text>
<text x="170" y="113">D</text>
<text x="30" y="113">A</text>
<!-- <text class="fade-in" fill='#999' x="100" y="45">W</text>
<text class="fade-in" fill='#999' x="100" y="113">S</text>
<text class="fade-in" fill='#999' x="170" y="113">D</text>
<text class="fade-in" fill='#999' x="30" y="113">A</text> -->
</g>
</g>
<g class="fade-in" fill="none" stroke="#aaa" stroke-width="1">
<path d="M 254 433.5 h-35.5 v40" />
<path d="M 295 433.5 h36.5 v40" />
<path d="M 274 625 v-35" />
<path d="M 430.5 442 v50 h38" />
<path d="M 612.5 442 v50 h-38" />
</g>
<g class="fade-in" stroke="none" fill="#aaa" font-size="16px">
<text x="253" y="422">switch</text>
<text x="257" y="438">guns</text>
<text x="255" y="638">move</text>
<text x="420" y="438">fire</text>
<text x="599" y="438">field</text>
</g>
<g id="gamepad" transform="translate(700,700) scale(0.2)" style="display: none;" stroke="#333" stroke-width="0.5" fill="#444">
<path style="fill:#FB7686;" d="M122.578,86.897H68.276c-6.184,0-11.196,5.013-11.196,11.196v12.126h76.692V98.091
C133.772,91.908,128.76,86.897,122.578,86.897z" />
<rect x="57.08" y="100.766" style="opacity:0.4;fill:#FF4E64;enable-background:new ;" width="76.69" height="9.453" />
<path style="fill:#FB7686;" d="M389.422,86.897h54.303c6.182,0,11.194,5.013,11.194,11.196v12.126h-76.693V98.091
C378.228,91.908,383.241,86.897,389.422,86.897z" />
<rect x="378.229" y="100.766" style="opacity:0.4;fill:#FF4E64;enable-background:new ;" width="76.69" height="9.453" />
<rect x="101.947" y="130.449" style="fill:#89BBE4;" width="308.094" height="144.099" />
<path style="opacity:0.1;fill:#145587;enable-background:new ;" d="M336.726,130.443l-6.391,28.154
c-6.438,28.363-7.324,57.602-2.745,86.134H184.41c4.577-28.531,3.692-57.77-2.746-86.134l-6.39-28.154h-73.323v144.103h308.097
V130.443H336.726z" />
<g>
<path style="fill:#89BBE4;" d="M168.235,158.598l-6.39-28.154c-4.031-12.081-15.338-20.226-28.073-20.226H57.08
c-4.862,0-9.582,1.835-13.034,5.259c-9.65,9.571-15.848,22.071-17.629,35.539L0.612,346.264
c-5.519,41.762,26.979,78.838,69.103,78.838c31.406,0,58.935-21.004,67.23-51.296l27.18-99.262l0,0
C175.524,236.906,176.94,196.95,168.235,158.598z" />
<path style="fill:#89BBE4;" d="M511.387,346.264l-25.806-195.25c-1.78-13.468-7.978-25.967-17.629-35.539
c-3.452-3.424-8.172-5.259-13.034-5.259h-76.693c-12.734,0-24.041,8.146-28.072,20.226l-6.39,28.154
c-8.706,38.353-7.288,78.308,4.109,115.948l0,0l27.18,99.262c8.296,30.292,35.823,51.296,67.23,51.296
C484.408,425.102,516.908,388.026,511.387,346.264z" />
</g>
<circle style="fill:#ACDFEA;" cx="189.252" cy="285.582" r="52.611" />
<circle style="opacity:0.3;fill:#145587;enable-background:new ;" cx="183.371" cy="293.273" r="25.611" />
<circle style="fill:#ACDFEA;" cx="322.747" cy="285.582" r="52.611" />
<circle style="opacity:0.3;fill:#145587;enable-background:new ;" cx="316.866" cy="293.273" r="25.611" />
<circle style="fill:#384148;" cx="322.747" cy="285.582" r="25.611" />
<g>
<path style="fill:#1F84CE;" d="M108.027,142.799H86.073c-4.334,0-7.848,3.514-7.848,7.849v9.17c0,2.082,0.826,4.077,2.298,5.549
l11.3,11.3c3.064,3.065,8.034,3.065,11.1,0l10.653-10.652c1.471-1.472,2.298-3.469,2.298-5.549v-9.818
C115.875,146.314,112.361,142.799,108.027,142.799z" />
<path style="fill:#1F84CE;" d="M76.951,190.893l-10.653-10.652c-1.472-1.472-3.468-2.3-5.549-2.3h-9.818
c-4.334,0-7.849,3.514-7.849,7.849v21.953c0,4.335,3.514,7.849,7.849,7.849h9.17c2.082,0,4.077-0.828,5.549-2.3l11.302-11.3
C80.016,198.929,80.016,193.96,76.951,190.893z" />
<path style="fill:#1F84CE;" d="M113.576,228.168l-11.3-11.3c-3.065-3.065-8.034-3.065-11.1,0l-10.653,10.652
c-1.472,1.472-2.298,3.468-2.298,5.55v9.817c0,4.334,3.514,7.848,7.849,7.848h21.953c4.334,0,7.849-3.513,7.849-7.848v-9.171
C115.875,231.636,115.048,229.64,113.576,228.168z" />
<path style="fill:#1F84CE;" d="M143.169,177.943h-9.171c-2.081,0-4.077,0.826-5.549,2.298l-11.3,11.3
c-3.064,3.064-3.064,8.034,0,11.1l10.653,10.653c1.472,1.471,3.468,2.298,5.55,2.298h9.818c4.334,0,7.848-3.513,7.848-7.848
v-21.953C151.018,181.457,147.504,177.943,143.169,177.943z" />
<circle style="fill:#1F84CE;" cx="414.952" cy="155.839" r="18.825" />
</g>
<circle style="fill:#F9C526;" cx="374.025" cy="196.766" r="18.825" />
<circle style="fill:#54B8AC;" cx="414.952" cy="237.693" r="18.825" />
<circle style="fill:#FFFFFF;" cx="455.879" cy="196.766" r="18.825" />
<circle style="fill:#384148;" cx="189.252" cy="285.582" r="25.611" />
<g>
<path style="fill:#FFFFFF;" d="M226.267,190.234h-23.773c-4.57,0-8.276-3.705-8.276-8.276s3.705-8.276,8.276-8.276h23.773
c4.57,0,8.276,3.705,8.276,8.276S230.838,190.234,226.267,190.234z" />
<path style="fill:#FFFFFF;" d="M309.506,190.234h-23.773c-4.572,0-8.276-3.705-8.276-8.276s3.704-8.276,8.276-8.276h23.773
c4.572,0,8.276,3.705,8.276,8.276S314.077,190.234,309.506,190.234z" />
</g>
</g>
<!-- <g id="gamepad" transform="translate(640,640) scale(0.3)" style="display: none;" stroke="#333" stroke-width="0.5" fill="#444">
<path d="M115.824,187.891c-2.95,2.95-4.576,6.872-4.576,11.045c0,4.172,1.625,8.095,4.575,11.045l10.322,10.322
c2.95,2.949,6.872,4.573,11.042,4.573h9.511c8.612,0,15.619-7.007,15.619-15.619v-21.268c0-8.612-7.007-15.619-15.619-15.619
h-8.884c-4.171,0-8.093,1.625-11.044,4.575L115.824,187.891z M137.986,188.403h8.298v20.438h-8.926l-9.905-9.906L137.986,188.403
z" />
<path d="M91.29,184.819c2.95,2.951,6.873,4.576,11.046,4.576c4.172,0,8.095-1.625,11.044-4.575l10.32-10.318
c2.95-2.95,4.575-6.873,4.575-11.044v-9.511c0-8.613-7.007-15.62-15.62-15.62H91.388c-8.612,0-15.619,7.007-15.619,15.62v8.883
c0,4.172,1.625,8.095,4.575,11.045L91.29,184.819z M91.802,154.358h0.001h20.438v8.926l-9.906,9.905l-10.533-10.533V154.358z" />
<path d="M88.221,209.352c6.089-6.09,6.089-15.999,0-22.089c0-0.001,0-0.001-0.001-0.001L77.9,176.944
c-2.95-2.951-6.873-4.576-11.045-4.576h-9.51c-8.613,0-15.62,7.008-15.62,15.62v21.266c0,8.613,7.007,15.62,15.62,15.62h8.883
c4.172,0,8.094-1.625,11.045-4.575L88.221,209.352z M66.055,208.841h-8.297v-20.439h8.926l9.906,9.906L66.055,208.841z" />
<path d="M112.753,212.424c-2.95-2.95-6.872-4.576-11.045-4.576c-4.172,0-8.095,1.625-11.044,4.575l-10.32,10.318
c-2.95,2.95-4.575,6.873-4.575,11.045v9.51c0,8.612,7.007,15.619,15.62,15.619h21.266c8.614,0,15.62-7.007,15.62-15.619v-8.884
c0-4.172-1.625-8.094-4.575-11.045L112.753,212.424z M112.241,242.884H91.802v-8.926l9.906-9.905l10.533,10.533V242.884z" />
<path d="M449.624,172.369c-14.477,0-26.254,11.778-26.254,26.253c0,14.477,11.778,26.254,26.254,26.254
c14.476,0,26.253-11.778,26.253-26.254C475.877,184.145,464.099,172.369,449.624,172.369z M449.624,208.841
c-5.635,0-10.22-4.585-10.22-10.22s4.585-10.219,10.22-10.219c5.634,0,10.219,4.585,10.219,10.219
S455.259,208.841,449.624,208.841z" />
<path d="M409.979,212.012c-14.477,0-26.253,11.778-26.253,26.253c0,14.477,11.778,26.254,26.253,26.254
s26.253-11.778,26.253-26.254C436.232,223.79,424.455,212.012,409.979,212.012z M409.979,248.485
c-5.634,0-10.219-4.585-10.219-10.22s4.585-10.219,10.219-10.219c5.635,0,10.219,4.585,10.219,10.219
S415.615,248.485,409.979,248.485z" />
<path d="M511.349,342.391l-24.999-189.143c-1.947-14.73-8.829-28.605-19.38-39.069c-2.853-2.831-6.397-4.93-10.254-6.16v-4.988
c0-10.401-8.462-18.862-18.862-18.862h-52.604c-10.401,0-18.862,8.461-18.862,18.862v4.631
c-10.461,2.335-19.426,9.143-24.443,18.692H170.057c-5.018-9.55-13.983-16.358-24.443-18.692v-4.631
c0-10.401-8.462-18.862-18.862-18.862H74.147c-10.401,0-18.862,8.461-18.862,18.862v4.988c-3.858,1.231-7.401,3.33-10.254,6.16
c-10.55,10.464-17.433,24.339-19.38,39.069L0.652,342.391c-2.851,21.572,3.74,43.329,18.083,59.692
c14.343,16.363,35.049,25.747,56.809,25.747c33.935,0,63.897-22.86,72.86-55.591l10.502-38.356
c9.314,6.157,20.461,9.754,32.437,9.754c32.523,0,58.983-26.459,58.983-58.984c0-0.896-0.028-1.786-0.067-2.672h11.487
c-0.04,0.886-0.067,1.777-0.067,2.672c0,32.523,26.46,58.984,58.984,58.984c11.976,0,23.123-3.598,32.437-9.755l10.502,38.356
c8.963,32.731,38.923,55.591,72.859,55.591c21.76,0,42.466-9.385,56.809-25.747C507.608,385.721,514.199,363.964,511.349,342.391
z M385.25,100.203h52.604c1.56,0,2.828,1.269,2.828,2.828v3.73h-58.26v-3.73h-0.001
C382.422,101.471,383.69,100.203,385.25,100.203z M71.319,103.031c0-1.56,1.269-2.828,2.828-2.828h52.603
c1.558,0,2.828,1.269,2.828,2.828v3.73h-58.26V103.031z M132.937,368.005c-7.06,25.784-30.661,43.792-57.394,43.792
c-17.141,0-33.451-7.393-44.75-20.283c-11.299-12.89-16.49-30.029-14.245-47.021l25-189.144
c1.484-11.23,6.731-21.808,14.775-29.786c1.773-1.759,4.317-2.769,6.979-2.769h74.293c8.778,0,16.557,5.518,19.472,13.769
l6.095,26.855c5.027,22.148,6.454,44.778,4.258,67.342c-20.634,9.195-35.061,29.885-35.061,53.895
c0,14.078,4.964,27.014,13.225,37.166L132.937,368.005z M191.342,327.606c-23.682,0-42.95-19.266-42.95-42.95
s19.266-42.95,42.95-42.95c23.682,0,42.949,19.266,42.949,42.95S215.023,327.606,191.342,327.606z M247.267,265.95
c-7.841-23.379-29.94-40.278-55.925-40.278c-2.527,0-5.012,0.177-7.457,0.489c1.561-22.23-0.135-44.477-5.086-66.29
l-3.968-17.481h162.339l-3.967,17.481c-4.952,21.813-6.648,44.06-5.086,66.29c-2.445-0.31-4.93-0.489-7.457-0.489
c-25.985,0-48.084,16.898-55.926,40.278H247.267z M320.66,327.606c-23.682,0-42.95-19.266-42.95-42.95s19.266-42.95,42.95-42.95
c23.681,0,42.949,19.266,42.949,42.95S344.341,327.606,320.66,327.606z M481.209,391.514
c-11.299,12.89-27.61,20.283-44.751,20.283c-26.732,0-50.334-18.008-57.394-43.791l-12.646-46.183
c8.261-10.152,13.225-23.088,13.225-37.166c0-24.009-14.427-44.7-35.061-53.895c-2.197-22.564-0.769-45.195,4.258-67.342
l6.095-26.855c2.914-8.251,10.693-13.769,19.471-13.769h74.294c2.662,0,5.206,1.009,6.98,2.769
c8.043,7.977,13.29,18.556,14.774,29.786l24.999,189.142C497.699,361.486,492.506,378.625,481.209,391.514z" />
<path d="M191.342,251.823c-18.105,0-32.832,14.729-32.832,32.833s14.729,32.833,32.832,32.833
c18.105,0,32.832-14.729,32.832-32.833C224.175,266.551,209.446,251.823,191.342,251.823z M191.342,301.455
c-9.263,0-16.798-7.536-16.798-16.799s7.536-16.799,16.798-16.799c9.263,0,16.798,7.536,16.798,16.799
S200.605,301.455,191.342,301.455z" />
<path d="M396.588,198.622c0-14.476-11.778-26.253-26.253-26.253c-14.477,0-26.253,11.778-26.253,26.253
c0,14.477,11.778,26.254,26.253,26.254C384.81,224.875,396.588,213.098,396.588,198.622z M370.334,208.841
c-5.634,0-10.219-4.585-10.219-10.22s4.585-10.219,10.219-10.219c5.635,0,10.219,4.585,10.219,10.219
S375.97,208.841,370.334,208.841z" />
<path d="M227.198,176.258h-23.028c-4.427,0-8.017,3.589-8.017,8.017c0,4.427,3.589,8.017,8.017,8.017h23.028
c4.427,0,8.017-3.59,8.017-8.017C235.215,179.848,231.625,176.258,227.198,176.258z" />
<path d="M284.804,192.293h23.029c4.427,0,8.017-3.59,8.017-8.017c0-4.428-3.59-8.017-8.017-8.017h-23.029
c-4.429,0-8.017,3.589-8.017,8.017C276.787,188.703,280.375,192.293,284.804,192.293z" />
<path d="M320.66,251.823c-18.105,0-32.833,14.729-32.833,32.833s14.729,32.833,32.833,32.833
c18.104,0,32.832-14.729,32.832-32.833C353.492,266.551,338.763,251.823,320.66,251.823z M320.66,301.455
c-9.263,0-16.799-7.536-16.799-16.799s7.536-16.799,16.799-16.799c9.262,0,16.798,7.536,16.798,16.799
S329.922,301.455,320.66,301.455z" />
<path d="M409.979,185.23c14.476,0,26.253-11.778,26.253-26.254c0-14.476-11.778-26.253-26.253-26.253
c-14.477,0-26.253,11.778-26.253,26.253C383.726,173.454,395.503,185.23,409.979,185.23z M409.979,148.758
c5.635,0,10.219,4.585,10.219,10.219s-4.584,10.22-10.219,10.22c-5.634,0-10.219-4.585-10.219-10.22
C399.76,153.343,404.345,148.758,409.979,148.758z" />
</g> -->
<!-- <g id="gamepad" transform="translate(640,640) scale(2)" style="display: none;">
<path d="M44.3,49.8v-3.7c0-0.3-0.1-0.5-0.3-0.7s-0.4-0.3-0.7-0.3h-5.5v-5.5c0-0.3-0.1-0.5-0.3-0.7
s-0.4-0.3-0.7-0.3h-3.7c-0.3,0-0.5,0.1-0.7,0.3s-0.3,0.4-0.3,0.7v5.5h-5.5c-0.3,0-0.5,0.1-0.7,0.3S26,45.9,26,46.2v3.7
c0,0.3,0.1,0.5,0.3,0.7s0.4,0.3,0.7,0.3h5.5v5.5c0,0.3,0.1,0.5,0.3,0.7s0.4,0.3,0.7,0.3H37c0.3,0,0.5-0.1,0.7-0.3s0.3-0.4,0.3-0.7
v-5.5h5.5c0.3,0,0.5-0.1,0.7-0.3S44.3,50.1,44.3,49.8z M60.8,51.7c0-1-0.4-1.9-1.1-2.6C59,48.4,58.2,48,57.2,48s-1.9,0.4-2.6,1.1
s-1.1,1.6-1.1,2.6s0.4,1.9,1.1,2.6c0.7,0.7,1.6,1.1,2.6,1.1s1.9-0.4,2.6-1.1C60.5,53.5,60.8,52.7,60.8,51.7z M68.2,44.3
c0-1-0.4-1.9-1.1-2.6c-0.7-0.7-1.6-1.1-2.6-1.1s-1.9,0.4-2.6,1.1c-0.7,0.7-1.1,1.6-1.1,2.6s0.4,1.9,1.1,2.6
c0.7,0.7,1.6,1.1,2.6,1.1s1.9-0.4,2.6-1.1C67.8,46.2,68.2,45.3,68.2,44.3z M75.5,48c0,4-1.4,7.5-4.3,10.4
c-2.9,2.9-6.3,4.3-10.4,4.3c-3.7,0-6.9-1.2-9.7-3.7h-6.3c-2.8,2.4-6,3.7-9.7,3.7c-4,0-7.5-1.4-10.4-4.3c-2.9-2.9-4.3-6.3-4.3-10.4
s1.4-7.5,4.3-10.4c2.9-2.9,6.3-4.3,10.4-4.3h25.7c4,0,7.5,1.4,10.4,4.3C74.1,40.5,75.5,44,75.5,48z" fill="#888" stroke="#222" />
</g> -->
</svg>
<script src='lib/matter.min.js'></script>
<script src='lib/decomp.min.js'></script>
<script src='lib/randomColor.min.js'></script>
<script src="js/game.js"></script>
<script src="js/player.js"></script>
<script src="js/powerups.js"></script>
<script src="js/bullets.js"></script>
<script src="js/mobs.js"></script>
<script src="js/spawn.js"></script>
<script src="js/level.js"></script>
<script src="js/engine.js"></script>
<script src="js/index.js"></script>
</body>
</html>

1366
js/bullets.js Normal file

File diff suppressed because it is too large Load Diff

174
js/engine.js Normal file
View File

@@ -0,0 +1,174 @@
//matter.js ***********************************************************
// module aliases
const Engine = Matter.Engine,
World = Matter.World,
Events = Matter.Events,
Composites = Matter.Composites,
Composite = Matter.Composite,
Constraint = Matter.Constraint,
Vertices = Matter.Vertices,
Query = Matter.Query,
Body = Matter.Body,
Bodies = Matter.Bodies;
// create an engine
const engine = Engine.create();
engine.world.gravity.scale = 0; //turn off gravity (it's added back in later)
// engine.velocityIterations = 100
// engine.positionIterations = 100
// engine.enableSleeping = true
// matter events *********************************************************
//************************************************************************
//************************************************************************
//************************************************************************
function playerOnGroundCheck(event) {
//runs on collisions events
function enter() {
mech.numTouching++;
if (!mech.onGround) mech.enterLand();
}
const pairs = event.pairs;
for (let i = 0, j = pairs.length; i != j; ++i) {
let pair = pairs[i];
if (pair.bodyA === jumpSensor) {
mech.standingOn = pair.bodyB; //keeping track to correctly provide recoil on jump
enter();
} else if (pair.bodyB === jumpSensor) {
mech.standingOn = pair.bodyA; //keeping track to correctly provide recoil on jump
enter();
}
}
mech.numTouching = 0;
}
function playerOffGroundCheck(event) {
//runs on collisions events
function enter() {
if (mech.onGround && mech.numTouching === 0) mech.enterAir();
}
const pairs = event.pairs;
for (let i = 0, j = pairs.length; i != j; ++i) {
if (pairs[i].bodyA === jumpSensor) {
enter();
} else if (pairs[i].bodyB === jumpSensor) {
enter();
}
}
}
function playerHeadCheck(event) {
//runs on collisions events
if (mech.crouch) {
mech.isHeadClear = true;
const pairs = event.pairs;
for (let i = 0, j = pairs.length; i != j; ++i) {
if (pairs[i].bodyA === headSensor) {
mech.isHeadClear = false;
} else if (pairs[i].bodyB === headSensor) {
mech.isHeadClear = false;
}
}
}
}
function mobCollisionChecks(event) {
const pairs = event.pairs;
for (let i = 0, j = pairs.length; i != j; i++) {
for (let k = 0; k < mob.length; k++) {
if (mob[k].alive && mech.alive) {
if (pairs[i].bodyA === mob[k]) {
collide(pairs[i].bodyB);
break;
} else if (pairs[i].bodyB === mob[k]) {
collide(pairs[i].bodyA);
break;
}
function collide(obj) {
//player and mob collision
if (obj === playerBody || obj === playerHead) {
if (mech.damageImmune < mech.cycle) {
//player is immune to mob collision damage for 30 cycles
mech.damageImmune = mech.cycle + 30;
mob[k].foundPlayer();
let dmg = Math.min(Math.max(0.025 * Math.sqrt(mob[k].mass), 0.05), 0.3) * game.dmgScale; //player damage is capped at 0.3*dmgScale of 1.0
mech.damage(dmg);
if (mob[k].onHit) mob[k].onHit(k);
game.drawList.push({
//add dmg to draw queue
x: pairs[i].activeContacts[0].vertex.x,
y: pairs[i].activeContacts[0].vertex.y,
radius: dmg * 500,
color: game.mobDmgColor,
time: game.drawTime
});
}
//extra kick between player and mob
//this section would be better with forces but they don't work...
let angle = Math.atan2(player.position.y - mob[k].position.y, player.position.x - mob[k].position.x);
Matter.Body.setVelocity(player, {
x: player.velocity.x + 8 * Math.cos(angle),
y: player.velocity.y + 8 * Math.sin(angle)
});
Matter.Body.setVelocity(mob[k], {
x: mob[k].velocity.x - 8 * Math.cos(angle),
y: mob[k].velocity.y - 8 * Math.sin(angle)
});
return;
}
//bullet mob collisions
if (obj.classType === "bullet" && obj.speed > obj.minDmgSpeed) {
mob[k].foundPlayer();
const dmg = b.dmgScale * (obj.dmg + 0.15 * obj.mass * Matter.Vector.magnitude(Matter.Vector.sub(mob[k].velocity, obj.velocity)));
// console.log(dmg)
mob[k].damage(dmg);
obj.onDmg(); //some bullets do actions when they hits things, like despawn
game.drawList.push({
//add dmg to draw queue
x: pairs[i].activeContacts[0].vertex.x,
y: pairs[i].activeContacts[0].vertex.y,
radius: Math.sqrt(dmg) * 40,
color: game.playerDmgColor,
time: game.drawTime
});
return;
}
//mob and body collisions
if (obj.classType === "body" && obj.speed > 5) {
const v = Matter.Vector.magnitude(Matter.Vector.sub(mob[k].velocity, obj.velocity));
if (v > 8) {
let dmg = b.dmgScale * v * Math.sqrt(obj.mass) * 0.05;
mob[k].damage(dmg);
if (mob[k].distanceToPlayer2() < 1000000) mob[k].foundPlayer();
game.drawList.push({
//add dmg to draw queue
x: pairs[i].activeContacts[0].vertex.x,
y: pairs[i].activeContacts[0].vertex.y,
radius: Math.sqrt(dmg) * 40,
color: game.playerDmgColor,
time: game.drawTime
});
return;
}
}
}
}
}
}
}
//determine if player is on the ground
Events.on(engine, "collisionStart", function (event) {
playerOnGroundCheck(event);
playerHeadCheck(event);
mobCollisionChecks(event);
});
Events.on(engine, "collisionActive", function (event) {
playerOnGroundCheck(event);
playerHeadCheck(event);
});
Events.on(engine, "collisionEnd", function (event) {
playerOffGroundCheck(event);
});

1028
js/game.js Normal file

File diff suppressed because it is too large Load Diff

197
js/index.js Normal file
View File

@@ -0,0 +1,197 @@
"use strict";
/* TODO: *******************************************
*****************************************************
make power ups keep moving to player if the field is turned off
levels spawn by having the map aspects randomly fly into place
new map with repeating endlessness
get ideas from Manifold Garden game
if falling, get teleported above the map
I tried it, but had trouble getting the camera to adjust to the teleportation
this can apply to blocks mobs, and power ups as well
Find a diegetic way to see player damage (and or field meter too)
a health meter, like the field meter above player? (doesn't work with the field meter)
field power up effects
field allows player to hold and throw living mobs
mod power ups ideas
bullet on mob damage effects
add to the array mob.do new mob behaviors
add a damage over time
add a freeze
give mobs more animal-like behaviors
like rainworld
give mobs something to do when they don't see player
explore map
eat power ups
drop power up (if killed after eating one)
mobs some times aren't aggressive
when low on life or after taking a large hit
mobs can fight each other
this might be hard to code
isolated mobs try to group up.
game mechanics
mechanics that support the physics engine
add rope/constraint
get ideas from game: limbo / inside
environmental hazards
laser
lava
button / switch
door
fizzler
moving platform
map zones
water
low friction ground
bouncy ground
track foot positions with velocity better as the player walks/crouch/runs
Boss ideas
boss grows and spilt, if you don't kill it fast
sensor that locks you in after you enter the boss room
boss that eats other mobs and gains stats from them
chance to spawn on any level (past level 5)
boss that knows how to shoot (player) bullets that collide with player
overwrite custom engine collision bullet mob function.
// collision info:
category mask
powerUp: 0x100000 0x100001
body: 0x010000 0x011111
player: 0x001000 0x010011
bullet: 0x000100 0x010011
mob: 0x000010 0x011111
mobBullet: 0x000010 0x011101
mobShield: 0x000010 0x001100
map: 0x000001 0x111111
*/
//set up canvas
var canvas = document.getElementById("canvas");
//using "const" causes problems in safari when an ID shares the same name.
const ctx = canvas.getContext("2d");
document.body.style.backgroundColor = "#fff";
//disable pop up menu on right click
document.oncontextmenu = function () {
return false;
}
function setupCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.width2 = canvas.width / 2; //precalculated because I use this often (in mouse look)
canvas.height2 = canvas.height / 2;
canvas.diagonal = Math.sqrt(canvas.width2 * canvas.width2 + canvas.height2 * canvas.height2);
ctx.font = "15px Arial";
ctx.lineJoin = "round";
ctx.lineCap = "round";
// ctx.lineCap='square';
game.setZoom();
}
setupCanvas();
window.onresize = () => {
setupCanvas();
};
//mouse move input
document.body.addEventListener("mousemove", (e) => {
game.mouse.x = e.clientX;
game.mouse.y = e.clientY;
});
document.body.addEventListener("mouseup", (e) => {
// game.buildingUp(e); //uncomment when building levels
game.mouseDown = false;
// console.log(e)
if (e.which === 3) {
game.mouseDownRight = false;
} else {
game.mouseDown = false;
}
});
document.body.addEventListener("mousedown", (e) => {
if (e.which === 3) {
game.mouseDownRight = true;
} else {
game.mouseDown = true;
}
});
//keyboard input
const keys = [];
document.body.addEventListener("keydown", (e) => {
keys[e.keyCode] = true;
game.keyPress();
});
document.body.addEventListener("keyup", (e) => {
keys[e.keyCode] = false;
});
document.body.addEventListener("wheel", (e) => {
if (e.deltaY > 0) {
game.nextGun();
} else {
game.previousGun();
}
}, {
passive: true
});
// function playSound(id) {
// //play sound
// if (false) {
// //sounds are turned off for now
// // if (document.getElementById(id)) {
// var sound = document.getElementById(id); //setup audio
// sound.currentTime = 0; //reset position of playback to zero //sound.load();
// sound.play();
// }
// }
function shuffle(array) {
var currentIndex = array.length,
temporaryValue,
randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
//main loop ************************************************************
//**********************************************************************
function cycle() {
if (!game.paused) requestAnimationFrame(cycle);
const now = Date.now();
const elapsed = now - game.then; // calc elapsed time since last loop
if (elapsed > game.fpsInterval) { // if enough time has elapsed, draw the next frame
game.then = now - (elapsed % game.fpsInterval); // Get ready for next frame by setting then=now. Also, adjust for fpsInterval not being multiple of 16.67
game.loop();
}
}

1593
js/level.js Normal file

File diff suppressed because it is too large Load Diff

990
js/mobs.js Normal file
View File

@@ -0,0 +1,990 @@
//create array of mobs
let mob = [];
//method to populate the array above
const mobs = {
loop() {
let i = mob.length;
while (i--) {
if (mob[i].alive) {
mob[i].do();
} else {
mob[i].replace(i); //removing mob and replace with body, this is done here to avoid an array index bug with drawing I think
}
}
},
draw() {
ctx.lineWidth = 2;
let i = mob.length;
while (i--) {
ctx.beginPath();
const vertices = mob[i].vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1, len = vertices.length; j < len; ++j) {
ctx.lineTo(vertices[j].x, vertices[j].y);
}
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.fillStyle = mob[i].fill;
ctx.strokeStyle = mob[i].stroke;
ctx.fill();
ctx.stroke();
}
},
alert(range) {
range = range * range;
for (let i = 0; i < mob.length; i++) {
if (mob[i].distanceToPlayer2() < range) mob[i].locatePlayer();
}
},
startle(amount) {
for (let i = 0; i < mob.length; i++) {
if (!mob[i].seePlayer.yes) {
mob[i].force.x += amount * mob[i].mass * (Math.random() - 0.5);
mob[i].force.y += amount * mob[i].mass * (Math.random() - 0.5);
}
}
},
//**********************************************************************************************
//**********************************************************************************************
spawn(xPos, yPos, sides, radius, color) {
let i = mob.length;
mob[i] = Matter.Bodies.polygon(xPos, yPos, sides, radius, {
//inertia: Infinity, //prevents rotation
mob: true,
density: 0.001,
//friction: 0,
frictionAir: 0.005,
//frictionStatic: 0,
restitution: 0.5,
collisionFilter: {
group: 0,
category: 0x000010,
mask: 0x011111
},
onHit: undefined,
alive: true,
index: i,
health: 1,
accelMag: 0.001,
cd: 0, //game cycle when cooldown will be over
delay: 60, //static: time between cooldowns
fill: color,
stroke: "#000",
seePlayer: {
yes: false,
recall: 0,
position: {
x: xPos,
y: yPos
}
},
radius: radius,
spawnPos: {
x: xPos,
y: yPos
},
seeAtDistance2: 4000000, //sqrt(4000000) = 2000 = max seeing range
distanceToPlayer() {
const dx = this.position.x - player.position.x;
const dy = this.position.y - player.position.y;
return Math.sqrt(dx * dx + dy * dy);
},
distanceToPlayer2() {
const dx = this.position.x - player.position.x;
const dy = this.position.y - player.position.y;
return dx * dx + dy * dy;
},
gravity() {
this.force.y += this.mass * this.g;
},
seePlayerFreq: Math.round((30 + 30 * Math.random()) * game.lookFreqScale), //how often NPC checks to see where player is, lower numbers have better vision
foundPlayer() {
this.locatePlayer();
if (!this.seePlayer.yes) {
this.alertNearByMobs();
this.seePlayer.yes = true;
}
},
lostPlayer() {
this.seePlayer.yes = false;
this.seePlayer.recall -= this.seePlayerFreq;
if (this.seePlayer.recall < 0) this.seePlayer.recall = 0;
},
memory: 120, //default time to remember player's location
locatePlayer() {
if (!mech.isStealth) {
// updates mob's memory of player location
this.seePlayer.recall = this.memory + Math.round(this.memory * Math.random()); //seconds before mob falls a sleep
this.seePlayer.position.x = player.position.x;
this.seePlayer.position.y = player.position.y;
}
},
// locatePlayerByDist() {
// if (this.distanceToPlayer2() < this.locateRange) {
// this.locatePlayer();
// }
// },
seePlayerCheck() {
if (!(game.cycle % this.seePlayerFreq)) {
if (
this.distanceToPlayer2() < this.seeAtDistance2 &&
Matter.Query.ray(map, this.position, this.mechPosRange()).length === 0 &&
Matter.Query.ray(body, this.position, this.mechPosRange()).length === 0
) {
this.foundPlayer();
} else if (this.seePlayer.recall) {
this.lostPlayer();
}
}
},
seePlayerCheckByDistance() {
if (!(game.cycle % this.seePlayerFreq)) {
if (this.distanceToPlayer2() < this.seeAtDistance2) {
this.foundPlayer();
} else if (this.seePlayer.recall) {
this.lostPlayer();
}
}
},
seePlayerByDistOrLOS() {
if (!(game.cycle % this.seePlayerFreq)) {
if (
this.distanceToPlayer2() < this.seeAtDistance2 ||
(Matter.Query.ray(map, this.position, this.mechPosRange()).length === 0 && Matter.Query.ray(body, this.position, this.mechPosRange()).length === 0)
) {
this.foundPlayer();
} else if (this.seePlayer.recall) {
this.lostPlayer();
}
}
},
seePlayerByDistAndLOS() {
if (!(game.cycle % this.seePlayerFreq)) {
if (
this.distanceToPlayer2() < this.seeAtDistance2 &&
(Matter.Query.ray(map, this.position, this.mechPosRange()).length === 0 && Matter.Query.ray(body, this.position, this.mechPosRange()).length === 0)
) {
this.foundPlayer();
} else if (this.seePlayer.recall) {
this.lostPlayer();
}
}
},
isLookingAtPlayer(threshold) {
const diff = Matter.Vector.normalise(Matter.Vector.sub(player.position, this.position));
//make a vector for the mob's direction of length 1
const dir = {
x: Math.cos(this.angle),
y: Math.sin(this.angle)
};
//the dot product of diff and dir will return how much over lap between the vectors
const dot = Matter.Vector.dot(dir, diff);
// console.log(Math.cos(dot)*180/Math.PI)
if (dot > threshold) {
return true;
} else {
return false;
}
},
lookRange: 0.2 + Math.random() * 0.2,
lookTorque: 0.0000004 * (Math.random() > 0.5 ? -1 : 1),
seePlayerByLookingAt() {
if (!(game.cycle % this.seePlayerFreq) && (this.seePlayer.recall || this.isLookingAtPlayer(this.lookRange))) {
if (
this.distanceToPlayer2() < this.seeAtDistance2 &&
Matter.Query.ray(map, this.position, this.mechPosRange()).length === 0 &&
Matter.Query.ray(body, this.position, this.mechPosRange()).length === 0
) {
this.foundPlayer();
} else if (this.seePlayer.recall) {
this.lostPlayer();
}
}
//if you don't recall player location rotate and draw to show where you are looking
if (!this.seePlayer.recall) {
this.torque = this.lookTorque * this.inertia;
//draw
const range = Math.PI * this.lookRange;
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, this.radius * 2.5, this.angle - range, this.angle + range);
ctx.arc(this.position.x, this.position.y, this.radius * 1.4, this.angle + range, this.angle - range, true);
ctx.fillStyle = "rgba(0,0,0,0.07)";
ctx.fill();
}
},
mechPosRange() {
return {
x: player.position.x, // + (Math.random() - 0.5) * 50,
y: player.position.y + (Math.random() - 0.5) * 110
};
//mob vision for testing
// ctx.beginPath();
// ctx.lineWidth = "5";
// ctx.strokeStyle = "#ff0";
// ctx.moveTo(this.position.x, this.position.y);
// ctx.lineTo(targetPos.x, targetPos.y);
// ctx.stroke();
// return targetPos;
},
laserBeam() {
if (game.cycle % 7 && this.seePlayer.yes) {
ctx.setLineDash([125 * Math.random(), 125 * Math.random()]);
// ctx.lineDashOffset = 6*(game.cycle % 215);
if (this.distanceToPlayer() < this.laserRange) {
//if (Math.random()>0.2 && this.seePlayer.yes && this.distanceToPlayer2()<800000) {
mech.damage(0.0003 * game.dmgScale);
if (mech.fieldMeter > 0.1) mech.fieldMeter -= 0.005
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(mech.pos.x, mech.pos.y);
ctx.lineTo(mech.pos.x + (Math.random() - 0.5) * 3000, mech.pos.y + (Math.random() - 0.5) * 3000);
ctx.lineWidth = 2;
ctx.strokeStyle = "rgb(255,0,170)";
ctx.stroke();
ctx.beginPath();
ctx.arc(mech.pos.x, mech.pos.y, 40, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(255,0,170,0.15)";
ctx.fill();
}
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, this.laserRange * 0.9, 0, 2 * Math.PI);
ctx.strokeStyle = "rgba(255,0,170,0.5)";
ctx.lineWidth = 1;
ctx.stroke();
ctx.setLineDash([]);
}
},
laser() {
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[j],
v2: vertices[j + 1]
};
}
}
}
results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) {
best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[0],
v2: vertices[len]
};
}
}
}
};
if (this.seePlayer.recall) {
this.torque = this.lookTorque * this.inertia * 2;
const seeRange = 2500;
best = {
x: null,
y: null,
dist2: Infinity,
who: null,
v1: null,
v2: null
};
const look = {
x: this.position.x + seeRange * Math.cos(this.angle),
y: this.position.y + seeRange * Math.sin(this.angle)
};
vertexCollision(this.position, look, map);
vertexCollision(this.position, look, body);
vertexCollision(this.position, look, [player]);
// hitting player
if (best.who === player) {
dmg = 0.004 * game.dmgScale;
mech.damage(dmg);
//draw damage
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(best.x, best.y, dmg * 2000, 0, 2 * Math.PI);
ctx.fill();
}
//draw beam
if (best.dist2 === Infinity) {
best = look;
}
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "#f00"; // Purple path
ctx.lineWidth = 1;
ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([0, 0]);
}
},
searchSpring() {
ctx.beginPath();
ctx.arc(this.cons.pointA.x, this.cons.pointA.y, 6, 0, 2 * Math.PI);
ctx.arc(this.cons2.pointA.x, this.cons2.pointA.y, 6, 0, 2 * Math.PI);
// ctx.arc(this.cons.bodyB.position.x, this.cons.bodyB.position.y,6,0,2*Math.PI);
ctx.fillStyle = "#222";
ctx.fill();
if (!(game.cycle % this.seePlayerFreq)) {
if (
(this.seePlayer.recall || this.isLookingAtPlayer(this.lookRange)) &&
this.distanceToPlayer2() < this.seeAtDistance2 &&
Matter.Query.ray(map, this.position, player.position).length === 0 &&
Matter.Query.ray(body, this.position, player.position).length === 0
) {
this.foundPlayer();
if (!(game.cycle % (this.seePlayerFreq * 2))) {
this.springTarget.x = this.seePlayer.position.x;
this.springTarget.y = this.seePlayer.position.y;
this.cons.length = -200;
this.cons2.length = 100 + 1.5 * this.radius;
} else {
this.springTarget2.x = this.seePlayer.position.x;
this.springTarget2.y = this.seePlayer.position.y;
this.cons.length = 100 + 1.5 * this.radius;
this.cons2.length = -200;
}
} else if (this.seePlayer.recall) {
this.lostPlayer();
}
}
//if you don't recall player location rotate and draw to show where you are looking
if (!this.seePlayer.recall) {
this.torque = this.lookTorque * this.inertia;
//draw
const range = Math.PI * this.lookRange;
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, this.radius * 2.5, this.angle - range, this.angle + range);
ctx.arc(this.position.x, this.position.y, this.radius * 1.4, this.angle + range, this.angle - range, true);
ctx.fillStyle = "rgba(0,0,0,0.07)";
ctx.fill();
//spring to random place on map
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[j],
v2: vertices[j + 1]
};
}
}
}
results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) {
best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[0],
v2: vertices[len]
};
}
}
}
};
const seeRange = 3000;
if (!(game.cycle % (this.seePlayerFreq * 10))) {
best = {
x: null,
y: null,
dist2: Infinity,
who: null,
v1: null,
v2: null
};
const look = {
x: this.position.x + seeRange * Math.cos(this.angle),
y: this.position.y + seeRange * Math.sin(this.angle)
};
vertexCollision(this.position, look, map);
if (best.dist2 != Infinity) {
this.springTarget.x = best.x;
this.springTarget.y = best.y;
this.cons.length = 100 + 1.5 * this.radius;
this.cons2.length = 100 + 1.5 * this.radius;
}
}
if (!((game.cycle + this.seePlayerFreq * 5) % (this.seePlayerFreq * 10))) {
best = {
x: null,
y: null,
dist2: Infinity,
who: null,
v1: null,
v2: null
};
const look = {
x: this.position.x + seeRange * Math.cos(this.angle),
y: this.position.y + seeRange * Math.sin(this.angle)
};
vertexCollision(this.position, look, map);
if (best.dist2 != Infinity) {
this.springTarget2.x = best.x;
this.springTarget2.y = best.y;
this.cons.length = 100 + 1.5 * this.radius;
this.cons2.length = 100 + 1.5 * this.radius;
}
}
}
},
alertNearByMobs() {
//this.alertRange2 is set at the very bottom of this mobs, after mob is made
for (let i = 0; i < mob.length; i++) {
if (!mob[i].seePlayer.recall && Matter.Vector.magnitudeSquared(Matter.Vector.sub(this.position, mob[i].position)) < this.alertRange2) {
mob[i].locatePlayer();
}
}
//add alert to draw queue
// game.drawList.push({
// x: this.position.x,
// y: this.position.y,
// radius: Math.sqrt(this.alertRange2),
// color: "rgba(0,0,0,0.02)",
// time: game.drawTime
// });
},
zoom() {
this.zoomMode--;
if (this.zoomMode > 150) {
this.drawTrail();
if (this.seePlayer.recall) {
//attraction to player
const forceMag = this.accelMag * this.mass;
const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x);
this.force.x += forceMag * Math.cos(angle);
this.force.y += forceMag * Math.sin(angle);
}
} else if (this.zoomMode < 0) {
this.zoomMode = 300;
this.setupTrail();
}
},
setupTrail() {
this.trail = [];
for (let i = 0; i < this.trailLength; ++i) {
this.trail.push({
x: this.position.x,
y: this.position.y
});
}
},
drawTrail() {
//dont' forget to run setupTrail() after mob spawn
const t = this.trail;
const len = t.length;
t.pop();
t.unshift({
x: this.position.x,
y: this.position.y
});
//draw
ctx.strokeStyle = this.trailFill;
ctx.beginPath();
// ctx.moveTo(t[0].x, t[0].y);
// ctx.lineTo(t[0].x, t[0].y);
// ctx.globalAlpha = 0.2;
// ctx.lineWidth = this.radius * 3;
// ctx.stroke();
ctx.globalAlpha = 0.5 / len;
ctx.lineWidth = this.radius * 1.95;
for (let i = 0; i < len; ++i) {
// ctx.lineWidth *= 0.96;
ctx.lineTo(t[i].x, t[i].y);
ctx.stroke();
}
ctx.globalAlpha = 1;
},
curl(range = 1000, mag = -10) {
//cause all mobs, and bodies to rotate in a circle
applyCurl = function (center, array) {
for (let i = 0; i < array.length; ++i) {
const sub = Matter.Vector.sub(center, array[i].position)
const radius2 = Matter.Vector.magnitudeSquared(sub);
//if too close, like center mob or shield, don't curl // if too far don't curl
if (radius2 < range * range && radius2 > 10000) {
const curlVector = Matter.Vector.mult(Matter.Vector.perp(Matter.Vector.normalise(sub)), mag)
//apply curl force
Matter.Body.setVelocity(array[i], {
x: array[i].velocity.x * 0.94 + curlVector.x * 0.06,
y: array[i].velocity.y * 0.94 + curlVector.y * 0.06
})
// //draw curl
// ctx.beginPath();
// ctx.moveTo(array[i].position.x, array[i].position.y);
// ctx.lineTo(array[i].position.x + curlVector.x * 10, array[i].position.y + curlVector.y * 10);
// ctx.lineWidth = 2;
// ctx.strokeStyle = "#000";
// ctx.stroke();
}
}
}
applyCurl(this.position, mob);
applyCurl(this.position, body);
applyCurl(this.position, powerUp);
// applyCurl(this.position, bullet); // too powerful, just stops all bullets need to write a curl function just for bullets
// applyCurl(this.position, [player]);
//draw limit
// ctx.beginPath();
// ctx.arc(this.position.x, this.position.y, range, 0, 2 * Math.PI);
// ctx.fillStyle = "rgba(55,255,255, 0.1)";
// ctx.fill();
},
pullPlayer() {
if (this.seePlayer.yes && Matter.Vector.magnitudeSquared(Matter.Vector.sub(this.position, player.position)) < 1000000) {
const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x);
player.force.x -= game.accelScale * 1.13 * Math.cos(angle) * (mech.onGround ? 2 * player.mass * game.g : player.mass * game.g);
player.force.y -= game.accelScale * 0.84 * player.mass * game.g * Math.sin(angle);
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(mech.pos.x, mech.pos.y);
ctx.lineWidth = Math.min(60, this.radius * 2);
ctx.strokeStyle = "rgba(0,0,0,0.5)";
ctx.stroke();
ctx.beginPath();
ctx.arc(mech.pos.x, mech.pos.y, 40, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(0,0,0,0.3)";
ctx.fill();
}
},
repelBullets() {
if (this.seePlayer.yes) {
ctx.lineWidth = "8";
ctx.strokeStyle = this.fill;
ctx.beginPath();
for (let i = 0, len = bullet.length; i < len; ++i) {
const dx = bullet[i].position.x - this.position.x;
const dy = bullet[i].position.y - this.position.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 500) {
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(bullet[i].position.x, bullet[i].position.y);
const angle = Math.atan2(dy, dx);
const mag = (1500 * bullet[i].mass * game.g) / dist;
bullet[i].force.x += mag * Math.cos(angle);
bullet[i].force.y += mag * Math.sin(angle);
}
}
ctx.stroke();
}
},
attraction() {
//accelerate towards the player
if (this.seePlayer.recall) {
// && dx * dx + dy * dy < 2000000) {
const forceMag = this.accelMag * this.mass;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
this.force.x += forceMag * Math.cos(angle);
this.force.y += forceMag * Math.sin(angle);
}
},
repulsionRange: 500000,
repulsion() {
//accelerate towards the player
if (this.seePlayer.recall && this.distanceToPlayer2() < this.repulsionRange) {
// && dx * dx + dy * dy < 2000000) {
const forceMag = this.accelMag * this.mass;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
this.force.x -= 2 * forceMag * Math.cos(angle);
this.force.y -= 2 * forceMag * Math.sin(angle); // - 0.0007 * this.mass; //antigravity
}
},
hop() {
//accelerate towards the player after a delay
if (this.cd < game.cycle && this.seePlayer.recall && this.speed < 1) {
this.cd = game.cycle + this.delay;
const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
this.force.x += forceMag * Math.cos(angle);
this.force.y += forceMag * Math.sin(angle) - 0.04 * this.mass; //antigravity
}
},
hoverOverPlayer() {
if (this.seePlayer.recall) {
// vertical positioning
const rangeY = 250;
if (this.position.y > this.seePlayer.position.y - this.hoverElevation + rangeY) {
this.force.y -= this.accelMag * this.mass;
} else if (this.position.y < this.seePlayer.position.y - this.hoverElevation - rangeY) {
this.force.y += this.accelMag * this.mass;
}
// horizontal positioning
const rangeX = 150;
if (this.position.x > this.seePlayer.position.x + this.hoverXOff + rangeX) {
this.force.x -= this.accelMag * this.mass;
} else if (this.position.x < this.seePlayer.position.x + this.hoverXOff - rangeX) {
this.force.x += this.accelMag * this.mass;
}
}
// else {
// this.gravity();
// }
},
grow() {
if (!mech.isBodiesAsleep) {
if (this.seePlayer.recall) {
if (this.radius < 80) {
const scale = 1.01;
Matter.Body.scale(this, scale, scale);
this.radius *= scale;
// this.torque = -0.00002 * this.inertia;
this.fill = `hsl(144, ${this.radius}%, 50%)`;
}
} else {
if (this.radius > 15) {
const scale = 0.99;
Matter.Body.scale(this, scale, scale);
this.radius *= scale;
this.fill = `hsl(144, ${this.radius}%, 50%)`;
}
}
}
},
search() {
//be sure to declare searchTarget in mob spawn
//accelerate towards the searchTarget
if (!this.seePlayer.recall) {
const newTarget = function (that) {
if (Math.random() < 0.05) {
that.searchTarget = player.position; //chance to target player
} else {
//target random body
that.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position;
}
};
const sub = Matter.Vector.sub(this.searchTarget, this.position);
if (Matter.Vector.magnitude(sub) > this.radius * 2) {
// ctx.beginPath();
// ctx.strokeStyle = "#aaa";
// ctx.moveTo(this.position.x, this.position.y);
// ctx.lineTo(this.searchTarget.x,this.searchTarget.y);
// ctx.stroke();
//accelerate at 0.1 of normal acceleration
this.force = Matter.Vector.mult(Matter.Vector.normalise(sub), this.accelMag * this.mass * 0.2);
} else {
//after reaching random target switch to new target
newTarget(this);
}
//switch to a new target after a while
if (!(game.cycle % (this.seePlayerFreq * 15))) {
newTarget(this);
}
}
},
strike() {
//teleport to player when close enough on CD
if (this.seePlayer.recall && this.cd < game.cycle) {
const dist = Matter.Vector.sub(this.seePlayer.position, this.position);
const distMag = Matter.Vector.magnitude(dist);
if (distMag < 430) {
this.cd = game.cycle + this.delay;
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
Matter.Body.translate(this, Matter.Vector.mult(Matter.Vector.normalise(dist), distMag - 20 - radius));
ctx.lineTo(this.position.x, this.position.y);
ctx.lineWidth = radius * 2;
ctx.strokeStyle = this.fill; //"rgba(0,0,0,0.5)"; //'#000'
ctx.stroke();
}
}
},
blink() {
//teleport towards player as a way to move
if (this.seePlayer.recall && !(game.cycle % this.blinkRate)) {
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
const dist = Matter.Vector.sub(this.seePlayer.position, this.position);
const distMag = Matter.Vector.magnitude(dist);
const unitVector = Matter.Vector.normalise(dist);
const rando = (Math.random() - 0.5) * 50;
if (distMag < this.blinkLength) {
Matter.Body.translate(this, Matter.Vector.mult(unitVector, distMag + rando));
} else {
Matter.Body.translate(this, Matter.Vector.mult(unitVector, this.blinkLength + rando));
}
ctx.lineTo(this.position.x, this.position.y);
ctx.lineWidth = radius * 2;
ctx.strokeStyle = this.stroke; //"rgba(0,0,0,0.5)"; //'#000'
ctx.stroke();
}
},
drift() {
//teleport towards player as a way to move
if (this.seePlayer.recall && !(game.cycle % this.blinkRate)) {
// && !mech.lookingAtMob(this,0.5)){
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
const dist = Matter.Vector.sub(this.seePlayer.position, this.position);
const distMag = Matter.Vector.magnitude(dist);
const vector = Matter.Vector.mult(Matter.Vector.normalise(dist), this.blinkLength);
if (distMag < this.blinkLength) {
Matter.Body.setPosition(this, this.seePlayer.position);
Matter.Body.translate(this, {
x: (Math.random() - 0.5) * 50,
y: (Math.random() - 0.5) * 50
});
} else {
vector.x += (Math.random() - 0.5) * 200;
vector.y += (Math.random() - 0.5) * 200;
Matter.Body.translate(this, vector);
}
ctx.lineTo(this.position.x, this.position.y);
ctx.lineWidth = radius * 2;
ctx.strokeStyle = this.stroke;
ctx.stroke();
}
},
bomb() {
//throw a mob/bullet at player
if (
!(game.cycle % this.fireFreq) &&
Math.abs(this.position.x - this.seePlayer.position.x) < 400 && //above player
Matter.Query.ray(map, this.position, this.mechPosRange()).length === 0 && //see player
Matter.Query.ray(body, this.position, this.mechPosRange()).length === 0
) {
spawn.bullet(this.position.x, this.position.y + this.radius * 0.5, 10 + Math.ceil(this.radius / 15), 5);
//add spin and speed
Matter.Body.setAngularVelocity(mob[mob.length - 1], (Math.random() - 0.5) * 0.5);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x,
y: this.velocity.y
});
//spin for mob as well
Matter.Body.setAngularVelocity(this, (Math.random() - 0.5) * 0.25);
}
},
fire() {
if (!mech.isBodiesAsleep) {
const setNoseShape = () => {
const mag = this.radius + this.radius * this.noseLength;
this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag;
this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag;
};
//throw a mob/bullet at player
if (this.seePlayer.recall) {
//set direction to turn to fire
if (!(game.cycle % this.seePlayerFreq)) {
this.fireDir = Matter.Vector.normalise(Matter.Vector.sub(this.seePlayer.position, this.position));
this.fireDir.y -= Math.abs(this.seePlayer.position.x - this.position.x) / 1600; //gives the bullet an arc
this.fireAngle = Math.atan2(this.fireDir.y, this.fireDir.x);
}
//rotate towards fireAngle
const angle = this.angle + Math.PI / 2;
c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y;
const threshold = 0.1;
if (c > threshold) {
this.torque += 0.000004 * this.inertia;
} else if (c < -threshold) {
this.torque -= 0.000004 * this.inertia;
} else if (this.noseLength > 1.5) {
//fire
spawn.bullet(this.vertices[1].x, this.vertices[1].y, 5 + Math.ceil(this.radius / 15), 5);
const v = 15;
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v + Math.random(),
y: this.velocity.y + this.fireDir.y * v + Math.random()
});
this.noseLength = 0;
// recoil
this.force.x -= 0.005 * this.fireDir.x * this.mass;
this.force.y -= 0.005 * this.fireDir.y * this.mass;
}
if (this.noseLength < 1.5) this.noseLength += this.fireFreq;
setNoseShape();
} else if (this.noseLength > 0.1) {
this.noseLength -= this.fireFreq / 2;
setNoseShape();
}
// else if (this.noseLength < -0.1) {
// this.noseLength += this.fireFreq / 4;
// setNoseShape();
// }
}
},
turnToFacePlayer() {
//turn to face player
const dx = player.position.x - this.position.x;
const dy = -player.position.y + this.position.y;
const dist = this.distanceToPlayer();
const angle = this.angle + Math.PI / 2;
c = Math.cos(angle) * dx - Math.sin(angle) * dy;
// if (c > 0.04) {
// Matter.Body.rotate(this, 0.01);
// } else if (c < 0.04) {
// Matter.Body.rotate(this, -0.01);
// }
if (c > 0.04 * dist) {
this.torque += 0.002 * this.mass;
} else if (c < 0.04) {
this.torque -= 0.002 * this.mass;
}
},
facePlayer() {
const unitVector = Matter.Vector.normalise(Matter.Vector.sub(this.seePlayer.position, this.position));
const angle = Math.atan2(unitVector.y, unitVector.x);
Matter.Body.setAngle(this, angle - Math.PI);
},
explode() {
mech.damage(Math.min(Math.max(0.02 * Math.sqrt(this.mass), 0.01), 0.35) * game.dmgScale);
this.dropPowerUp = false;
this.death(); //death with no power up or body
},
timeLimit() {
if (!mech.isBodiesAsleep) {
this.timeLeft--;
if (this.timeLeft < 0) {
this.dropPowerUp = false;
this.death(); //death with no power up
}
}
},
healthBar() {
//draw health bar
if (this.seePlayer.recall) {
// && this.health < 1
const h = this.radius * 0.3;
const w = this.radius * 2;
const x = this.position.x - w / 2;
const y = this.position.y - w * 0.7;
ctx.fillStyle = "rgba(100, 100, 100, 0.3)";
ctx.fillRect(x, y, w, h);
ctx.fillStyle = "rgba(255,0,0,0.7)";
ctx.fillRect(x, y, w * this.health, h);
}
},
damage(dmg) {
this.health -= dmg / Math.sqrt(this.mass);
//this.fill = this.color + this.health + ')';
if (this.health < 0.1) this.death();
this.onDamage(this); //custom damage effects
if (b.modEnergySiphon) mech.fieldMeter += dmg * b.modEnergySiphon
if (b.modHealthDrain) mech.addHealth(dmg * b.modHealthDrain)
},
onDamage() {
// a placeholder for custom effects on mob damage
//to use declare custom method in mob spawn
},
onDeath() {
// a placeholder for custom effects on mob death
//to use declare custom method in mob spawn
},
leaveBody: true,
dropPowerUp: true,
death() {
this.onDeath(this); //custom death effects
this.removeConsBB();
this.alive = false;
if (this.dropPowerUp) {
powerUps.spawnRandomPowerUp(this.position.x, this.position.y, this.mass, radius);
if (b.modSpores && Math.random() < 0.4) b.spore(this) //spawn drone
}
},
removeConsBB() {
for (let i = 0, len = consBB.length; i < len; ++i) {
if (consBB[i].bodyA === this) {
if (consBB[i].bodyB.shield) {
consBB[i].bodyB.do = function () {
this.death();
};
}
consBB[i].bodyA = consBB[i].bodyB;
consBB.splice(i, 1);
this.removeConsBB();
break;
} else if (consBB[i].bodyB === this) {
if (consBB[i].bodyA.shield) {
consBB[i].bodyA.do = function () {
this.death();
};
}
consBB[i].bodyB = consBB[i].bodyA;
consBB.splice(i, 1);
this.removeConsBB();
break;
}
}
},
removeCons() {
for (let i = 0, len = cons.length; i < len; ++i) {
if (cons[i].bodyA === this) {
cons[i].bodyA = cons[i].bodyB;
cons.splice(i, 1);
this.removeCons();
break;
} else if (cons[i].bodyB === this) {
cons[i].bodyB = cons[i].bodyA;
cons.splice(i, 1);
this.removeCons();
break;
}
}
},
//replace dead mob with a regular body
replace(i) {
if (this.leaveBody) {
const len = body.length;
body[len] = Matter.Bodies.fromVertices(this.position.x, this.position.y, this.vertices);
Matter.Body.setVelocity(body[len], this.velocity);
Matter.Body.setAngularVelocity(body[len], this.angularVelocity);
body[len].collisionFilter.category = 0x010000;
body[len].collisionFilter.mask = 0x011111;
// body[len].collisionFilter.category = body[len].collisionFilter.category //0x000001;
// body[len].collisionFilter.mask = body[len].collisionFilter.mask //0x011111;
//large mobs or too many bodies go intangible and fall until removed from game to help performance
if (body[len].mass > 10 || 40 + 30 * Math.random() < body.length) {
body[len].collisionFilter.mask = 0x001100;
}
body[len].classType = "body";
World.add(engine.world, body[len]); //add to world
}
Matter.World.remove(engine.world, this);
mob.splice(i, 1);
}
});
mob[i].alertRange2 = Math.pow(mob[i].radius * 3 + 200, 2);
World.add(engine.world, mob[i]); //add to world
}
};

1451
js/player.js Normal file

File diff suppressed because it is too large Load Diff

232
js/powerups.js Normal file
View File

@@ -0,0 +1,232 @@
let powerUp = [];
const powerUps = {
heal: {
name: "heal",
color: "#0fb",
size() {
return 40 * Math.sqrt(0.1 + Math.random() * 0.5);
},
effect() {
let heal = (this.size / 40) ** 2
heal = Math.min(1 - mech.health, heal)
mech.addHealth(heal);
if (!game.lastLogTime && heal > 0) game.makeTextLog('heal for ' + (heal * 100).toFixed(0) + '%', 180)
}
},
ammo: {
name: "ammo",
color: "#467",
size() {
return 17;
},
effect() {
//only get ammo for guns player has
let target;
// console.log(b.inventory.length)
if (b.inventory.length > 0) {
//add ammo to a gun in inventory
target = b.guns[b.inventory[Math.floor(Math.random() * (b.inventory.length))]];
//try 3 more times to give ammo to a gun with ammo, not Infinity
if (target.ammo === Infinity) {
target = b.guns[b.inventory[Math.floor(Math.random() * (b.inventory.length))]]
if (target.ammo === Infinity) {
target = b.guns[b.inventory[Math.floor(Math.random() * (b.inventory.length))]]
if (target.ammo === Infinity) target = b.guns[b.inventory[Math.floor(Math.random() * (b.inventory.length))]]
}
}
} else {
//if you don't have any guns just add ammo to a random gun you don't have yet
target = b.guns[Math.floor(Math.random() * b.guns.length)];
}
if (target.ammo === Infinity) {
mech.fieldMeter = 1;
if (!game.lastLogTime) game.makeTextLog("+energy", 180);
} else {
//ammo given scales as mobs take more hits to kill
const ammo = Math.ceil((target.ammoPack * (0.6 + 0.04 * Math.random())) / b.dmgScale);
target.ammo += ammo;
game.updateGunHUD();
if (!game.lastLogTime) game.makeTextLog("+" + ammo + " ammo: " + target.name, 180);
}
}
},
field: {
name: "field",
color: "#0bf",
size() {
return 45;
},
effect() {
const previousMode = mech.fieldMode
if (!this.mode) { //this.mode is set if the power up has been ejected from player
mode = mech.fieldMode
while (mode === mech.fieldMode) {
mode = Math.ceil(Math.random() * (mech.fieldUpgrades.length - 1))
}
mech.fieldUpgrades[mode].effect(); //choose random field upgrade that you don't already have
} else {
mech.fieldUpgrades[this.mode].effect(); //set a predetermined power up
}
//pop the old field out in case player wants to swap back
if (previousMode !== 0) {
mech.fieldCDcycle = mech.cycle + 40; //trigger fieldCD to stop power up grab automatic pick up of spawn
powerUps.spawn(mech.pos.x, mech.pos.y - 15, "field", false, previousMode);
}
}
},
mod: {
name: "mod",
color: "#a8f",
size() {
return 42;
},
effect() {
//find what mods I don't have
let options = [];
for (let i = 0; i < b.mods.length; i++) {
if (!b.mods[i].have) options.push(i);
}
//give a random mod from the mods I don't have
if (options.length > 0) {
let newMod = options[Math.floor(Math.random() * options.length)]
b.giveMod(newMod)
game.makeTextLog(`<strong style='font-size:30px;'>${b.mods[newMod].name}</strong><br> <p>${b.mods[newMod].description}</p>`, 1200);
} else {
//what should happen if you have all the mods?
}
}
},
gun: {
name: "gun",
color: "#37a",
size() {
return 35;
},
effect() {
//find what guns I don't have
let options = [];
if (b.activeGun === null) { //choose the first gun to be one that is good for the early game
options = [0, 1, 2, 3, 4, 5, 6, 8, 9, 12]
} else {
for (let i = 0; i < b.guns.length; ++i) {
if (!b.guns[i].have) options.push(i);
}
}
//give player a gun they don't already have if possible
if (options.length > 0) {
let newGun = options[Math.floor(Math.random() * options.length)];
// newGun = 4; //makes every gun you pick up this type //enable for testing one gun
if (b.activeGun === null) {
b.activeGun = newGun //if no active gun switch to new gun
game.makeTextLog(
// "<br><br><br><br><div class='wrapper'> <div class = 'grid-box'><strong>left mouse</strong>: fire weapon</div> <div class = 'grid-box'> <span class = 'mouse'><span class='mouse-line'></span></span> </div></div>",
"Use <strong>left mouse</strong> to fire weapon.",
Infinity
);
}
game.makeTextLog(`<strong style='font-size:30px;'>${b.guns[newGun].name}</strong><br><span class='faded'>(left click)</span><p>${b.guns[newGun].description}</p>`, 1000);
// if (b.inventory.length === 1) { //on the second gun pick up tell player how to change guns
// game.makeTextLog(`(<strong>Q</strong>, <strong>E</strong>, and <strong>mouse wheel</strong> change weapons)<br><br><strong style='font-size:30px;'>${b.guns[newGun].name}</strong><br><span class='faded'>(left click)</span><p>${b.guns[newGun].description}</p>`, 1000);
// } else {
// game.makeTextLog(`<strong style='font-size:30px;'>${b.guns[newGun].name}</strong><br><span class='faded'>(left click)</span><p>${b.guns[newGun].description}</p>`, 1000);
// }
b.guns[newGun].have = true;
b.inventory.push(newGun);
b.guns[newGun].ammo += b.guns[newGun].ammoPack * 2;
game.makeGunHUD();
} else {
//if you have all guns then get ammo
const ammoTarget = Math.floor(Math.random() * (b.guns.length));
const ammo = Math.ceil(b.guns[ammoTarget].ammoPack * 2);
b.guns[ammoTarget].ammo += ammo;
game.updateGunHUD();
game.makeTextLog("+" + ammo + " ammo: " + b.guns[ammoTarget].name, 180);
}
}
},
spawnRandomPowerUp(x, y) { //mostly used after mob dies
if (Math.random() * Math.random() - 0.25 > Math.sqrt(mech.health) || Math.random() < 0.04) { //spawn heal chance is higher at low health
powerUps.spawn(x, y, "heal");
return;
}
if (Math.random() < 0.19) {
if (b.inventory.length > 0) powerUps.spawn(x, y, "ammo");
return;
}
if (Math.random() < 0.004 * (5 - b.inventory.length)) { //a new gun has a low chance for each not acquired gun to drop
powerUps.spawn(x, y, "gun");
return;
}
if (Math.random() < 0.008) {
powerUps.spawn(x, y, "mod");
return;
}
if (Math.random() < 0.005) {
powerUps.spawn(x, y, "field");
return;
}
},
spawnBossPowerUp(x, y) { //boss spawns field and gun mod upgrades
if (mech.fieldMode === 0) {
powerUps.spawn(x, y, "field")
} else if (Math.random() < 0.35) {
powerUps.spawn(x, y, "mod")
} else if (Math.random() < 0.27) {
powerUps.spawn(x, y, "field");
} else if (Math.random() < 0.04 * (7 - b.inventory.length)) { //a new gun has a low chance for each not acquired gun to drop
powerUps.spawn(x, y, "gun")
} else if (mech.health < 0.5) {
powerUps.spawn(x, y, "heal");
} else {
powerUps.spawn(x, y, "ammo");
}
},
chooseRandomPowerUp(x, y) { //100% chance to drop a random power up //used in spawn.debris
if (Math.random() < 0.5) {
powerUps.spawn(x, y, "heal", false);
} else {
powerUps.spawn(x, y, "ammo", false);
}
},
spawnStartingPowerUps(x, y) { //used for map specific power ups, mostly to give player a starting gun
if (b.inventory.length < 2) {
powerUps.spawn(x, y, "gun", false); //starting gun
} else {
powerUps.spawnRandomPowerUp(x, y);
powerUps.spawnRandomPowerUp(x, y);
powerUps.spawnRandomPowerUp(x, y);
powerUps.spawnRandomPowerUp(x, y);
}
},
spawn(x, y, target, moving = true, mode = null) {
let i = powerUp.length;
target = powerUps[target];
size = target.size();
powerUp[i] = Matter.Bodies.polygon(x, y, 0, size, {
density: 0.001,
frictionAir: 0.01,
restitution: 0.8,
inertia: Infinity, //prevents rotation
collisionFilter: {
group: 0,
category: 0x100000,
mask: 0x100001
},
color: target.color,
effect: target.effect,
mode: mode,
name: target.name,
size: size
});
if (moving) {
Matter.Body.setVelocity(powerUp[i], {
x: (Math.random() - 0.5) * 15,
y: Math.random() * -9 - 3
});
}
World.add(engine.world, powerUp[i]); //add to world
},
};

1722
js/spawn.js Normal file

File diff suppressed because it is too large Load Diff

255
style.css Normal file
View File

@@ -0,0 +1,255 @@
body {
font-family: "Helvetica", "Arial", sans-serif;
margin: 0;
overflow: hidden;
background-color: #fff;
user-select: none;
/*cursor: crosshair;*/
}
canvas {
position: absolute;
top: 0;
left: 0;
z-index: 0;
}
#splash {
user-select: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
}
*:focus {
outline: none;
}
table {
border-collapse: collapse;
/* border: 1px solid #eee; */
width: 360px;
/* background-color: #ddd; */
}
summary {
font-size: 1.2em;
}
#controls {
position: absolute;
bottom: 0px;
left: 1px;
z-index: 12;
font-size: 1.3em;
/* background-color: #ccc; */
/* border-radius: 5px; */
}
#controls-div {
padding: 10px;
border-radius: 8px;
background-color: #ddd;
}
#dmg {
position: absolute;
z-index: 2;
width: 100%;
height: 100%;
display: none;
background-color: #f67;
opacity: 0;
transition: opacity 1s;
}
#health-bg {
position: absolute;
top: 15px;
left: 15px;
height: 20px;
width: 300px;
background-color: #000;
opacity: 0.1;
z-index: 1;
pointer-events: none;
display: none;
}
#health {
position: absolute;
top: 15px;
left: 15px;
height: 20px;
width: 0px;
transition: width 1s ease-out;
opacity: 0.6;
z-index: 2;
pointer-events: none;
background-color: #f00;
}
.low-health {
animation: blink 250ms infinite alternate;
}
@keyframes blink {
from {
opacity: 0.9;
}
to {
opacity: 0.2;
}
}
#fade-out {
position: absolute;
z-index: 2;
width: 100%;
height: 100%;
background-color: #fff;
opacity: 0;
transition: opacity 5s;
pointer-events: none;
}
#guns {
position: absolute;
top: 40px;
left: 15px;
z-index: 2;
font-size: 23px;
color: #111;
background-color: rgba(255, 255, 255, 0.4);
user-select: none;
pointer-events: none;
padding: 0px 5px 0px 5px;
border-radius: 5px;
/*border: 2px solid rgba(0, 0, 0, 0.4);*/
}
#field {
position: absolute;
top: 15px;
right: 15px;
z-index: 2;
font-size: 23px;
color: #000;
text-align: right;
opacity: 0.7;
line-height: 140%;
background-color: rgba(190, 210, 245, 0.25);
user-select: none;
pointer-events: none;
padding: 0px 5px 0px 5px;
border-radius: 5px;
/*border: 2px solid rgba(0, 0, 0, 0.4);*/
}
#mods {
position: absolute;
top: 60px;
right: 15px;
z-index: 2;
font-size: 20px;
color: #000;
text-align: right;
opacity: 0.35;
line-height: 140%;
background-color: rgba(255, 255, 255, 0.4);
user-select: none;
pointer-events: none;
padding: 0px 5px 0px 5px;
border-radius: 5px;
/*border: 2px solid rgba(0, 0, 0, 0.4);*/
}
#text-log {
position: absolute;
top: 20px;
left: 0;
width: 100%;
line-height: 150%;
text-align: center;
z-index: 2;
font-size: 1.25em;
color: #000;
opacity: 0;
transition: opacity 0.5s;
pointer-events: none;
user-select: none;
}
.box {
padding: 3px 8px 3px 8px;
border: 2px solid #444;
border-radius: 4px;
background-color: rgba(255, 255, 255, 0.5);
}
.faded {
opacity: 0.5;
font-size: 85%;
}
.wrapper {
display: grid;
grid-template-columns: 360px 10px;
align-self: center;
justify-content: center;
}
.grid-box {
align-self: center;
justify-self: center;
}
.mouse {
color: #ccc;
position: relative;
padding: 37px 30px 37px 30px;
border-radius: 25px;
border: 2px solid #444;
background-color: rgba(255, 255, 255, 0.5);
}
.mouse:after {
content: "";
position: absolute;
z-index: 1;
top: 4px;
left: 26px;
border-radius: 25px;
/* background: #444; */
border: 2px solid #444;
width: 4px;
height: 20px;
border-radius: 10px / 25px;
}
.mouse-line {
position: relative;
top: 30px;
left: 0px;
}
.mouse-line:after {
content: "";
position: absolute;
z-index: 1;
top: -35px;
left: -30px;
width: 60px;
height: 2px;
border-radius: 8px;
background: #444;
}
.right {
text-align: right;
}