made mods stackable
This commit is contained in:
420
index.html
420
index.html
@@ -1,420 +0,0 @@
|
||||
<!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='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="#ccc" 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="#ccc" font-size="16px">
|
||||
<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>
|
||||
1301
js/bullets.js
1301
js/bullets.js
File diff suppressed because it is too large
Load Diff
174
js/engine.js
174
js/engine.js
@@ -1,174 +0,0 @@
|
||||
//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);
|
||||
});
|
||||
1023
js/game.js
1023
js/game.js
File diff suppressed because it is too large
Load Diff
233
js/index.js
233
js/index.js
@@ -1,233 +0,0 @@
|
||||
"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
|
||||
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)
|
||||
|
||||
Add field upgrade, and mod to a permanent display
|
||||
left side
|
||||
separate box below guns
|
||||
|
||||
|
||||
cap guns to 3
|
||||
can up the drop rate on guns, and lower ammo amount or drop rate
|
||||
cap mods to 2
|
||||
can up the drop rate a bit
|
||||
check if there are any double mod compatibility issues
|
||||
cap field to 1
|
||||
|
||||
what about no cap to mods?
|
||||
mods without caps can't have major negatives
|
||||
do I want to support a power ramping game play?
|
||||
more upgrades are OK as long as they change game play
|
||||
no flat damage, or flat defense buffs
|
||||
This makes skipping content a bad idea for the player
|
||||
Is that maybe good? No need to nerf content skipping buffs
|
||||
content skipping is a cool play style, but not core game play
|
||||
|
||||
|
||||
field power up effects
|
||||
field produces a whirlpool effect of force around player
|
||||
field allows player to hold and throw living mobs
|
||||
|
||||
Move mods, to power up object
|
||||
mods can be about more than the gun, defensive, traversal mods
|
||||
gun mod power ups
|
||||
bullet on mob damage effects
|
||||
add to the array mob.do new mob behaviors
|
||||
add a damage over time
|
||||
add a freeze
|
||||
fire a few smaller bullets
|
||||
killing a mob triggers: a spore bullet
|
||||
maybe you could replace the power method with a new one to get this to work
|
||||
negative mods for balancing
|
||||
self damage on fire
|
||||
knock back
|
||||
lower fire rate
|
||||
smaller bullets
|
||||
smaller explosions
|
||||
shorter lasting bullets
|
||||
|
||||
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
1593
js/level.js
File diff suppressed because it is too large
Load Diff
986
js/mobs.js
986
js/mobs.js
@@ -1,986 +0,0 @@
|
||||
//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);
|
||||
},
|
||||
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
|
||||
}
|
||||
};
|
||||
1445
js/player.js
1445
js/player.js
File diff suppressed because it is too large
Load Diff
233
js/powerups.js
233
js/powerups.js
@@ -1,233 +0,0 @@
|
||||
let powerUp = [];
|
||||
|
||||
const powerUps = {
|
||||
heal: {
|
||||
name: "heal",
|
||||
color: "#0f9",
|
||||
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)
|
||||
}
|
||||
},
|
||||
field: {
|
||||
name: "field",
|
||||
color: "#f9f",
|
||||
size() {
|
||||
return 40;
|
||||
},
|
||||
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: "#479",
|
||||
size() {
|
||||
return 42;
|
||||
},
|
||||
effect() {
|
||||
const previousMode = b.mod
|
||||
if (this.mode === null) { //this.mode is set if the power up has been ejected from player
|
||||
mode = b.mod //start with current mob
|
||||
while (mode === b.mod) {
|
||||
mode = Math.floor(Math.random() * b.mods.length)
|
||||
}
|
||||
b.mods[mode].effect(); //choose random upgrade that you don't already have
|
||||
} else {
|
||||
b.mods[this.mode].effect(); //set a predetermined power up
|
||||
}
|
||||
if (previousMode != null) { //pop the old field out in case player wants to swap back
|
||||
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, "mod", false, previousMode);
|
||||
}
|
||||
}
|
||||
},
|
||||
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.5 + 0.04 * Math.random())) / b.dmgScale);
|
||||
target.ammo += ammo;
|
||||
game.updateGunHUD();
|
||||
if (!game.lastLogTime) game.makeTextLog("+" + ammo + " ammo: " + target.name, 180);
|
||||
}
|
||||
}
|
||||
},
|
||||
gun: {
|
||||
name: "gun",
|
||||
color: "#0bf",
|
||||
size() {
|
||||
return 30;
|
||||
},
|
||||
effect() {
|
||||
//find what guns I don't have
|
||||
let options = [];
|
||||
if (b.activeGun === null) { //the first gun 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
|
||||
);
|
||||
}
|
||||
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.005) {
|
||||
powerUps.spawn(x, y, "field");
|
||||
return;
|
||||
}
|
||||
if (Math.random() < 0.005) {
|
||||
powerUps.spawn(x, y, "mod");
|
||||
return;
|
||||
}
|
||||
},
|
||||
spawnBossPowerUp(x, y) { //boss spawns field and gun mod upgrades
|
||||
if (mech.fieldMode === 0) {
|
||||
powerUps.spawn(x, y, "field")
|
||||
} else if (b.mod === null) {
|
||||
powerUps.spawn(x, y, "mod")
|
||||
} else if (Math.random() < 0.2) {
|
||||
powerUps.spawn(x, y, "mod")
|
||||
} else if (Math.random() < 0.2) {
|
||||
powerUps.spawn(x, y, "field");
|
||||
} else if (Math.random() < 0.15) {
|
||||
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) {
|
||||
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
1722
js/spawn.js
File diff suppressed because it is too large
Load Diff
237
style.css
237
style.css
@@ -1,237 +0,0 @@
|
||||
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);*/
|
||||
}
|
||||
|
||||
#mods {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
z-index: 2;
|
||||
font-size: 20px;
|
||||
color: #000;
|
||||
text-align: right;
|
||||
opacity: 0.5;
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user