SpacePi js13k
How do I make an spacepi js13k?
This my game entry for js13k. The official entry can be found here: http://js13kgames.com/entries/spacepi/. What is a spacepi js13k? How do you make a spacepi js13k? This script and codes were developed by Jack Rugile on 31 July 2022, Sunday.
SpacePi js13k - Script Codes HTML Codes
<!DOCTYPE html>
<html >
<head> <meta charset="UTF-8"> <title>SpacePi js13k</title> <link rel="stylesheet" href="css/style.css">
</head>
<body> <div id="game-wrap">
<div id="main" class="menu"> <h1><strong>Space</strong>Pi</h1> <p class="desc">Created by <a href="http://jackrugile.com" target="_blank">Jack Rugile</a> for <a href="http://js13kgames.com/" target="_blank">js13kGames</a></p> <div id="no-can"> <div class="divider"></div> <p>Your browser does not support canvas. To play the game, download the latest version of a browser that supports canvas, such as <a href="https://google.com/chrome" target="_blank">Google Chrome</a> or <a href="http://mozilla.org/firefox" target="_blank">Mozilla Firefox</a>.</p> </div> <div id="home-wrap"> <div id="guide" class="clearfix"> <div class="divider"></div> <div class="left"> <h3>Overview</h3> <ul> <li><strong>Objective</strong> - Create lines to defend against enemy lines. Protect your base until it has grown to its target radius. Collect coins to purchase upgrades. Collect powerups to gain benefits: <span class="pwrup1">Slow Enemies<i class="tip">Enemy movement and spawning are slowed to half speed</i></span>, <span class="pwrup2">Unlimited Power<i class="tip">Unlimited line creation power</i></span>, <span class="pwrup3">Base Generation Boost<i class="tip">Base growth is doubled</i></span>, and <span class="pwrup4">Coin Scatter<i class="tip">Coins are scattered across the level</i></span>. </li> <li><strong>Controls</strong> - Click and drag to create defensive lines, which is limited by your available power. Press the spacebar to pause.</li> <li><strong>Saving</strong> - Game data is saved automatically at the end of levels and after upgrade purchases.<li> <li><strong>Scoring</strong> - The lower the better. Beat the levels faster, create fewer lines, create shorter lines, and take less damage to get a better score.<li> </ul> </div> <!-- end .left --> <div id="map"> <div class="completion"> <span>Completion</span> </div> <div class="power"> <span>Power</span> </div> <div class="score"> <span>Score</span> </div> <div class="profit"> <span>Profit</span> </div> <div class="base"> <span>Base</span> </div> <div class="base-goal"> <span>Target Radius</span> </div> <div class="good-line"> <span>Defensive Line</span> </div> <div class="bad-line"> <span>Enemy Line</span> </div> </div> <div class="divider"></div> </div> <a href="#" class="button" id="play-game-button"><span>Play Game</span></a> <a href="#" class="button" id="overall-stats-button"><span>Overall Stats</span></a> <a href="#" class="button" id="reset-game-data-button"><span>Reset Game Data</span></a> </div><!-- end #home-wrap -->
</div>
<div id="pause" class="menu"> <h2 id="paused-title">Paused</h2> <a href="#" class="button" id="resume-level-button"><span>Resume Level</span></a> <a href="#" class="button" id="quit-level-button"><span>Quit Level</span></a>
</div>
<div id="upgrades-levels" class="menu"> <p id="funds">$100</p> <h2>Upgrades</h2> <div class="up-list clearfix"> <div class="upgrade-group"> <a href="#" class="upgrade button" data-parent="power" data-specific="capacity"> <span class="clearfix"> <div class="title">Power Capacity</div> <div class="cost"></div> <div class="lev lev1"></div> <div class="lev lev2"></div> <div class="lev lev3"></div> <div class="lev lev4"></div> <div class="lev lev5"></div> <div class="value lev0">100px Total</div> <div class="value lev1">200px Total</div> <div class="value lev2">350px Total</div> <div class="value lev3">500px Total</div> <div class="value lev4">750px Total</div> <div class="value lev5">1000px Total</div> <p class="tip">Increases the capacity of your power, which is used to create defensive lines.</p> </span> </a> <a href="#" class="upgrade button" data-parent="power" data-specific="generation"> <span class="clearfix"> <div class="title">Power Generation</div> <div class="cost"></div> <div class="lev lev1"></div> <div class="lev lev2"></div> <div class="lev lev3"></div> <div class="lev lev4"></div> <div class="lev lev5"></div> <div class="value lev0">30px/Second</div> <div class="value lev1">60px/Second</div> <div class="value lev2">90px/Second</div> <div class="value lev3">120px/Second</div> <div class="value lev4">150px/Second</div> <div class="value lev5">180px/Second</div> <p class="tip">Increases the generation of your power, which is used to create defensive lines.</p> </span> </a> </div> <div class="upgrade-group"> <a href="#" class="upgrade button" data-parent="base" data-specific="resistance"> <span class="clearfix"> <div class="title">Base Resistance</div> <div class="cost"></div> <div class="lev lev1"></div> <div class="lev lev2"></div> <div class="lev lev3"></div> <div class="lev lev4"></div> <div class="lev lev5"></div> <div class="value lev0">0 Damage</div> <div class="value lev1">-1 Damage</div> <div class="value lev2">-2 Damage</div> <div class="value lev3">-3 Damage</div> <div class="value lev4">-4 Damage</div> <div class="value lev5">-5 Damage</div> <p class="tip">Increases the resistance of your base, which decreases damage from enemy lines.</p> </span> </a> <a href="#" class="upgrade button" data-parent="base" data-specific="generation"> <span class="clearfix"> <div class="title">Base Generation</div> <div class="cost"></div> <div class="lev lev1"></div> <div class="lev lev2"></div> <div class="lev lev3"></div> <div class="lev lev4"></div> <div class="lev lev5"></div> <div class="value lev0">1.2px/Second</div> <div class="value lev1">1.8px/Second</div> <div class="value lev2">2.4px/Second</div> <div class="value lev3">3px/Second</div> <div class="value lev4">3.6px/Second</div> <div class="value lev5">4.2px/Second</div> <p class="tip">Increases the generation of your base, allowing it to reach its target radius faster.</p> </span> </a> </div> <div class="upgrade-group"> <a href="#" class="upgrade button" data-parent="powerups" data-specific="chance"> <span class="clearfix"> <div class="title">Powerup Chance</div> <div class="cost"></div> <div class="lev lev1"></div> <div class="lev lev2"></div> <div class="lev lev3"></div> <div class="lev lev4"></div> <div class="lev lev5"></div> <div class="value lev0">2% Chance</div> <div class="value lev1">3% Chance</div> <div class="value lev2">4% Chance</div> <div class="value lev3">5% Chance</div> <div class="value lev4">6% Chance</div> <div class="value lev5">7% Chance</div> <p class="tip">Increases the chance of enemy lines dropping powerups.</p> </span> </a> <a href="#" class="upgrade button" data-parent="powerups" data-specific="duration"> <span class="clearfix"> <div class="title">Powerup Duration</div> <div class="cost"></div> <div class="lev lev1"></div> <div class="lev lev2"></div> <div class="lev lev3"></div> <div class="lev lev4"></div> <div class="lev lev5"></div> <div class="value lev0">3 Seconds</div> <div class="value lev1">3.5 Seconds</div> <div class="value lev2">4 Seconds</div> <div class="value lev3">4.5 Seconds</div> <div class="value lev4">5 Seconds</div> <div class="value lev5">5.5 Seconds</div> <p class="tip">Increases the duration of powerups.</p> </span> </a> </div> <div class="upgrade-group"> <a href="#" class="upgrade button" data-parent="magnet" data-specific="range"> <span class="clearfix"> <div class="title">Magnet Range</div> <div class="cost"></div> <div class="lev lev1"></div> <div class="lev lev2"></div> <div class="lev lev3"></div> <div class="lev lev4"></div> <div class="lev lev5"></div> <div class="value lev0">50px Radius</div> <div class="value lev1">100px Radius</div> <div class="value lev2">150px Radius</div> <div class="value lev3">200px Radius</div> <div class="value lev4">300px Radius</div> <div class="value lev5">400px Radius</div> <p class="tip">Increases the range of your magnet, which collects coins and powerups.</p> </span> </a> <a href="#" class="upgrade button" data-parent="magnet" data-specific="power"> <span class="clearfix"> <div class="title">Magnet Power</div> <div class="cost"></div> <div class="lev lev1"></div> <div class="lev lev2"></div> <div class="lev lev3"></div> <div class="lev lev4"></div> <div class="lev lev5"></div> <div class="value lev0">60px/Second</div> <div class="value lev1">120px/Second</div> <div class="value lev2">180px/Second</div> <div class="value lev3">240px/Second</div> <div class="value lev4">300px/Second</div> <div class="value lev5">360px/Second</div> <p class="tip">Increases the power of your magnet, which collects coins and powerups.</p> </span> </a> </div> <div class="upgrade-group last"> <a href="#" class="upgrade button" data-parent="coins" data-specific="chance"> <span class="clearfix"> <div class="title">Coin Chance</div> <div class="cost"></div> <div class="lev lev1"></div> <div class="lev lev2"></div> <div class="lev lev3"></div> <div class="lev lev4"></div> <div class="lev lev5"></div> <div class="value lev0">50% Chance</div> <div class="value lev1">55% Chance</div> <div class="value lev2">60% Chance</div> <div class="value lev3">65% Chance</div> <div class="value lev4">70% Chance</div> <div class="value lev5">75% Chance</div> <p class="tip">Increases the chance of enemy lines dropping coins.</p> </span> </a> <a href="#" class="upgrade button" data-parent="coins" data-specific="amount"> <span class="clearfix"> <div class="title">Coin Amount</div> <div class="cost"></div> <div class="lev lev1"></div> <div class="lev lev2"></div> <div class="lev lev3"></div> <div class="lev lev4"></div> <div class="lev lev5"></div> <div class="value lev0">1 Coin</div> <div class="value lev1">1-2 Coins</div> <div class="value lev2">1-3 Coins</div> <div class="value lev3">1-4 Coins</div> <div class="value lev4">1-5 Coins</div> <div class="value lev5">1-6 Coins</div> <p class="tip">Increases the amount of coins dropped by enemy lines.</p> </span> </a> </div> </div> <div class="divider"></div> <p id="level-status"> <strong><span id="completed-levels"></span>/13</strong> Completed - <em><strong><span id="perfect-levels"></span>/13</strong> Perfect</em> </p> <h2>Levels <span class="level-notes">i<p class="tip">Best scores are shown for each level. <strong>Lower is better.</strong> Levels marked with a "P", for Perfect, were beaten without any base hits.</p></span></h2> <div class="level-list clearfix"> <a href="#" rel="0" class="level button"><span>1<i>P</i><small class="level-score"></small></span><p class="tip">"Noobs Welcome"<br/><strong>$5</strong>/Coin</p></a> <a href="#" rel="1" class="level button"><span>2<i>P</i><small class="level-score"></small></span><p class="tip">"No Sweat"</strong><br/><strong>$10</strong>/Coin</p></a> <a href="#" rel="2" class="level button"><span>3<i>P</i><small class="level-score"></small></span><p class="tip">"Yep, Still Easy"<br/><strong>$15</strong>/Coin</p></a> <a href="#" rel="3" class="level button"><span>4<i>P</i><small class="level-score"></small></span><p class="tip">"Piece of js13cake"<br/><strong>$20</strong>/Coin</p></a> <a href="#" rel="4" class="level button"><span>5<i>P</i><small class="level-score"></small></span><p class="tip">"You Got This"<br/><strong>$25</strong>/Coin</p></a> <a href="#" rel="5" class="level button"><span>6<i>P</i><small class="level-score"></small></span><p class="tip">"Ok, Some Sweat"<br/><strong>$30</strong>/Coin</p></a> <a href="#" rel="6" class="level button"><span>7<i>P</i><small class="level-score"></small></span><p class="tip">"Wait, Hold On"<br/><strong>$35</strong>/Coin</p></a> <a href="#" rel="7" class="level button"><span>8<i>P</i><small class="level-score"></small></span><p class="tip">"Beware"<br/><strong>$40</strong>/Coin</p></a> <a href="#" rel="8" class="level button"><span>9<i>P</i><small class="level-score"></small></span><p class="tip">"Lots of Sweat"<br/><strong>$45</strong>/Coin</p></a> <a href="#" rel="9" class="level button"><span>10<i>P</i><small class="level-score"></small></span><p class="tip">"Clickfest"<br/><strong>$50</strong>/Coin</p></a> <a href="#" rel="10" class="level button"><span>11<i>P</i><small class="level-score"></small></span><p class="tip">"Don't Break Your Mouse"<br/><strong>$55</strong>/Coin</p></a> <a href="#" rel="11" class="level button"><span>12<i>P</i><small class="level-score"></small></span><p class="tip">"No Light at the End<br />of the Carpal Tunnel"<br/><strong>$60</strong>/Coin</p></a> <a href="#" rel="12" class="level button"><span>13<i>P</i><small class="level-score"></small></span><p class="tip">"Hope You're Not as<br />Sensitive as Your Mouse"<br/><strong>$65</strong>/Coin</p></a> </div> <div class="divider"></div> <a href="#" class="button" id="return-to-menu-button"><span>Return to Menu</span></a>
</div>
<div id="level-stats" class="menu"> <h2 id="stats-status"></h2> <table> <tr> <th colspan="2">Level Stats</th> </tr> <tr> <td>Duration:</td> <td id="stats-duration"></td> </tr> <tr> <td>Lines Created:</td> <td id="stats-lines-created"></td> </tr> <tr> <td>Total Line Length:</td> <td id="stats-total-line-length"></td> </tr> <tr> <td>Base Hits:</td> <td id="stats-base-hits"></td> </tr> <tr> <td>Powerups Collected:</td> <td id="stats-powerups-collected"></td> </tr> <tr> <td>Coins Collected:</td> <td id="stats-coins-collected"></td> </tr> <tr> <td>Profit:</td> <td id="stats-profit"></td> </tr> <tr> <td>Score:</td> <td id="stats-score"></td> </tr> </table> <div class="divider"></div> <a href="#" class="button" id="retry-level-button"><span>Retry Level</span></a> <a href="#" class="button" id="return-to-upgrades-levels-button"><span>Return to Upgrades/Levels</span></a>
</div>
<div id="overall-stats" class="menu"> <h1><strong>Space</strong>Pi</h1> <p class="desc">Created by <a href="http://jackrugile.com" target="_blank">Jack Rugile</a> for <a href="http://js13kgames.com/" target="_blank">js13kGames</a></p> <div class="divider"></div> <table> <tr> <th colspan="2">Overall Stats</th> </tr> <tr> <td>Total Lines Created:</td> <td id="overall-lines-created"></td> </tr> <tr> <td>Total Line Length:</td> <td id="overall-line-length"></td> </tr> <tr> <td>Total Base Hits:</td> <td id="overall-base-hits"></td> </tr> <tr> <td>Total Levels Played:</td> <td id="overall-levels-played"></td> </tr> <tr> <td>Total Time Played:</td> <td id="overall-time-played"></td> </tr> </table> <div class="divider"></div> <a href="#" class="button" id="return-to-menu-button2"><span>Return to Menu</span></a>
</div>
<canvas id="c" height="600" width="960"></canvas>
</div><!-- end #game-wrap --> <script src="js/index.js"></script>
</body>
</html>
SpacePi js13k - Script Codes CSS Codes
/* General */
* { margin: 0; padding: 0;
}
body { background: #000; color: #aaa; font: 100%/20px 'helvetica neue', helvetica, arial, sans-serif; text-align: center; text-shadow: 0 -1px 0 #000;
}
h2 { color: #fff; font-size: 20px; font-weight: normal; margin: 0 0 20px;
}
a { color: #fff; font-weight: bold; text-decoration: none;
}
a:focus { outline: none;
}
/* Structure */
#game-wrap { background: #000; background: -webkit-radial-gradient(center center, circle contain, #111 0%, #000 100%); background: -moz-radial-gradient(center center, circle contain, #111 0%, #000 100%); background: -o-radial-gradient(center center, circle contain, #111 0%, #000 100%); background: radial-gradient(center center, circle contain, #111 0%, #000 100%); border: 1px solid #222; height: 600px; left: 50%; margin: -301px 0 0 -481px; position: absolute; top: 50%; width: 960px;
}
canvas { cursor: crosshair; display: block; left: 0; position: absolute; top: 0;
}
/* Header */
h1 { color: #2f2; font-size: 30px; font-weight: normal; line-height: 30px; margin: 0 0 10px;
}
h1 strong { color: #fff;
}
p.desc { color: #666; font-size: 11px; margin: 0 0 20px;
}
p.desc a { color: #999;
}
p.desc a:hover { color: #fff;
}
/* Menus */
.menu { background: #111; height: 550px; left: 0; padding: 50px 50px 0; position: absolute; top: 0; width: 860px; z-index: 2;
}
#pause,
#upgrades-levels,
#level-stats,
#retry-level,
#overall-stats { display: none;
}
#pause { background: rgba(20,20,20,.5);
}
#upgrades-levels { text-align: left;
}
#upgrades-levels h2 { text-align: left;
}
#level-stats .divider,
#overall-stats .divider{ margin: 0 auto 20px; width: 350px;
}
/* Guide */
#guide { margin: 0 auto; text-align: left; width: auto;
}
#guide .left { float: left; width: 440px;
}
#guide h3 { color: #2f2; font-size: 18px; font-weight: normal; margin: 0 0 10px;
}
#guide ul { font-size: 11px; margin: 0 0 20px;
}
#guide li { list-style: none; margin: 0 0 7px;
}
#guide li strong { color: #fff;
}
#guide .pwrup1,
#guide .pwrup2,
#guide .pwrup3,
#guide .pwrup4 { cursor: help; padding: 0 0 0 0; position: relative;
}
#guide .pwrup1:hover .tip,
#guide .pwrup2:hover .tip,
#guide .pwrup3:hover .tip,
#guide .pwrup4:hover .tip { display: block; font-style: normal;
}
#guide .pwrup1 { color: hsl(205, 100%, 50%); }
#guide .pwrup2 { color: hsl(0, 100%, 50%); }
#guide .pwrup3 { color: hsl(300, 100%, 50%); }
#guide .pwrup4 { color: hsl(60, 100%, 50%); }
/* Map */
#map { background: #000; border: 1px solid #222; float: right; height: 236px; margin: 0 0 20px; position: relative; width: 378px;
}
#map div { background: #999; position: absolute;
}
#map span { color: #999; font-size: 10px; line-height: 10px; position: absolute; width: 100%;
}
#map .completion { height: 4px; left: 10px; top: 10px; width: 70px;
} #map .completion span { left: 0; top: 8px; }
#map .power { height: 4px; left: 90px; top: 10px; width: 70px;
} #map .power span { left: 0; top: 8px; }
#map .score { height: 4px; right: 60px; top: 10px; width: 40px;
} #map .score span { left: 0; top: 8px; }
#map .profit { height: 4px; right: 10px; top: 10px; width: 40px;
} #map .profit span { left: 0; top: 8px; }
#map .base { background: #2f2; border-radius: 100%; height: 20px; left: 50%; margin: -10px 0 0 -10px; top: 50%; width: 20px;
} #map .base span { left: 0; top: 24px; }
#map .base-goal { background: none; border: 2px solid #131; border-radius: 100%; height: 76px; left: 50%; margin: -40px 0 0 -40px; top: 50%; width: 76px;
} #map .base-goal span { left: 100%; padding: 0 0 0 6px; top: 33px; }
#map .good-line { height: 60px; left: 115px; margin: -30px 0 0 0; top: 50%; width: 1px;
} #map .good-line span { left: -25px; text-align: center; top: 64px; width: 50px; }
#map .bad-line { background: #f22; height: 1px; left: 20px; top: 50%; width: 60px;
} #map .bad-line span { left: 0; text-align: center; top: 5px; } #map .bad-line:after { border-left: 5px solid #f22; border-bottom: 5px solid transparent; border-top: 5px solid transparent; content: ''; display: block; margin: -5px 0 0 0; position: absolute; right: -5px; top: 50%; }
/* Buttons */
.button { background: #171717; background: -webkit-linear-gradient(#222, #111); background: -moz-linear-gradient(#222, #111); background: -o-linear-gradient(#222, #111); background: linear-gradient(#222, #111); border: 1px solid #000; border-radius: 2px; box-shadow: 0 1px 0 #222; display: block; height: 30px; line-height: 30px; margin: 0 auto 10px; text-align: center; width: 348px;
}
.button span { border-radius: 2px; box-shadow: inset 0 1px 0 #333; color: #ccc; display: block; font-size: 11px; font-weight: bold; padding: 0 20px;
}
.button:hover span { background: rgba(255,255,255,.05); color: #fff;
}
.button:active span { background: #111; box-shadow: inset 0 0 10px #000; color: #999;
}
.button.disabled { cursor: default; opacity: .25;
}
.button.disabled span { color: #999;
}
.button.disabled:hover span{ background: none; color: #999; cursor: default;
}
.button.max:hover span { background: none; color: #ccc; cursor: default;
}
.button.disabled:active span,
.button.max:active span { background: none; box-shadow: none; cursor: default;
}
/* Upgrade List */
#funds { color: #fff; display: block; float: right; font-size: 20px; line-height: 20px; position: relative;
}
#funds.not-enough { color: #f22; top: -10px;
}
.up-list { margin: 0 0 20px;
}
.up-list .upgrade-group { float: left; margin: 0 15px 0 0;
}
.up-list .upgrade-group.last { margin-right: 0;
}
.up-list .button { height: auto; line-height: 20px; margin: 0 0 15px 0; position: relative; width: 158px;
}
.up-list .button:last-child { margin-bottom: 0;
}
.up-list .button span { padding: 10px 18px;
}
.up-list .button .cost { font-weight: normal; margin: 0 0 5px;
}
.up-list .button i { font-size: 11px; font-style: normal;
}
.up-list .button .available { color: #2f2;
}
.up-list .button .unavailable { color: #f22;
}
.up-list .button .lev { background: #292929; background: -webkit-linear-gradient(#444, #111); background: -moz-linear-gradient(#444, #111); background: -o-linear-gradient(#444, #111); background: linear-gradient(#444, #111); border: 1px solid #000; border-radius: 50%; display: inline-block; height: 7px; margin: 0 4px; width: 7px;
}
.up-list .upg1 .lev.lev1, .up-list .upg2 .lev.lev1, .up-list .upg2 .lev.lev2, .up-list .upg3 .lev.lev1, .up-list .upg3 .lev.lev2, .up-list .upg3 .lev.lev3, .up-list .upg4 .lev.lev1, .up-list .upg4 .lev.lev2, .up-list .upg4 .lev.lev3, .up-list .upg4 .lev.lev4 { background: #fff; background: -webkit-linear-gradient(#eee, #444); background: -moz-linear-gradient(#eee, #444); background: -o-linear-gradient(#eee, #444); background: linear-gradient(#eee, #444); box-shadow: inset 0 1px 0 #fff, 0 0 14px rgba(255,255,255,.4) ;
}
.up-list .upg5 .lev.lev1,
.up-list .upg5 .lev.lev2,
.up-list .upg5 .lev.lev3,
.up-list .upg5 .lev.lev4,
.up-list .upg5 .lev.lev5 { background: #4f4; background: -webkit-linear-gradient(#4f4, #050); background: -moz-linear-gradient(#4f4, #050); background: -o-linear-gradient(#4f4, #050); background: linear-gradient(#4f4, #050); box-shadow: inset 0 1px 0 rgba(255,255,255,.85), 0 0 14px rgba(0,255,0,.4) ;
}
.up-list .value { color: #777; display: none;
}
.up-list .upg0 .value.lev0,
.up-list .upg1 .value.lev1,
.up-list .upg2 .value.lev2,
.up-list .upg3 .value.lev3,
.up-list .upg4 .value.lev4,
.up-list .upg5 .value.lev5 { display: block;
}
.tip { background: #111; background: -webkit-linear-gradient(#222, #000); background: -moz-linear-gradient(#222, #000); background: -o-linear-gradient(#222, #000); background: linear-gradient(#222, #000); border: 1px solid #333; border-radius: 2px; bottom: 100%; box-shadow: 0 0 10px 1px #000; color: #ccc; display: none; font-size: 11px; font-weight: normal; left: 50%; line-height: 16px; margin: 0 0 16px -101px; padding: 8px 20px; position: absolute; text-align: left; width: 160px; z-index: 2;
}
.tip strong { color: #fff;
}
.tip:before,
.tip:after { border: 1px solid transparent; content: ''; height: 0; left: 50%; position: absolute; top: 100%; width: 0;
}
.tip:before { border-top-color: #333; border-width: 7px; margin-left: -7px; z-index: 1;
}
.tip:after { border-top-color: #000; border-width: 5px; margin-left: -5px; z-index: 2;
}
.up-list .button:hover .tip { display: block;
}
.up-list .button.disabled { cursor: default; opacity: 1;
}
.up-list .button.disabled span .availability { color: #f22;
}
.up-list .button.disabled:hover span { background: none; color: #999; cursor: default;
}
.up-list .button.max:hover span { background: none; color: #ccc; cursor: default;
}
.up-list .button.disabled:active span,
.up-list .button.max:active span { background: none; box-shadow: inset 0 1px 0 #333; cursor: default;
}
.up-list .button:hover .value { color: #aaa;
}
.up-list .button.max:hover .value { color: #777;
}
/* Level List */
#level-status { color: #aaa; display: block; float: right; font-size: 11px; line-height: 20px; position: relative; text-align: right;
}
#level-status strong { color: #fff; font-weight: bold;
}
#level-status em { color: #181; font-style: normal;
}
#level-status em strong { color: #2f2;
}
.level-list { clear: both; margin: 0 0 20px; position: relative;
}
.level { clear: none; float: left; height: 54px; margin: 0 11px 0 0; position: relative; width: 54px;
}
.level:last-child { margin-right: 0;
}
.level span { font-size: 14px; height: 44px; line-height: 18px; padding: 10px 0 0 0; position: relative; text-align: center;
}
.level span i { display: none;
}
.level.perfect-level span i { background: #222; background: -webkit-linear-gradient(#333, #111); background: -moz-linear-gradient(#333, #111); background: -o-linear-gradient(#333, #111); background: linear-gradient(#333, #111); border: 1px solid #000; border-radius: 100%; box-shadow: 0 0 3px rgba(0,0,0,.5); color: #999; display: block; font-style: normal; left: -6px; font-size: 10px; height: 16px; line-height: 16px; position: absolute; text-align: center; top: -6px; width: 16px;
}
.level.perfect-level:hover span i { color: #2f2;
}
.level.disabled .level-score { color: #444;
}
.level .level-score { color: #999; bottom: 12px; display: block; font-style: normal; font-weight: normal; left: 0; font-size: 10px; width: 54px;
}
.level:hover .level-score { color: #fff;
}
.level.disabled:hover .level-score { color: #444;}
.level-list .level.disabled span { color: #999 }
.level:nth-child(1) span { color: hsl(27, 100%, 50%); }
.level:nth-child(2) span { color: hsl(55, 100%, 50%); }
.level:nth-child(3) span { color: hsl(83, 100%, 50%); }
.level:nth-child(4) span { color: hsl(110, 100%, 50%); }
.level:nth-child(5) span { color: hsl(138, 100%, 50%); }
.level:nth-child(6) span { color: hsl(166, 100%, 50%); }
.level:nth-child(7) span { color: hsl(193, 100%, 50%); }
.level:nth-child(8) span { color: hsl(221, 100%, 55%); }
.level:nth-child(9) span { color: hsl(249, 100%, 60%); }
.level:nth-child(10) span { color: hsl(276, 100%, 55%); }
.level:nth-child(11) span { color: hsl(304, 100%, 50%); }
.level:nth-child(12) span { color: hsl(332, 100%, 50%); }
.level:nth-child(13) span { color: hsl(0, 100%, 50%); }
.level-list .button .tip { text-align: center;
}
.level-list .button:hover .tip { display: block;
}
.level-list .button.disabled:hover .tip { display: none;
}
.level-notes { background: #222; border: 1px solid #000; border-radius: 100%; cursor: help; color: #ccc; display: inline-block; font-size: 11px; font-weight: bold; height: 16px; line-height: 16px; position: relative; text-align: center; vertical-align: middle; width: 16px;
}
.level-notes:hover { background: #333; color: #fff;
}
.level-notes:hover .tip { display: block;
}
/* Level Stats */
.level-success { color: #2f2;
}
.level-fail { color: #f22;
}
.perfect { color: #2f2; font-weight: bold; margin: 0 0 0 5px;
}
table { border-collapse: separate; border-radius: 2px; border-spacing: 0; box-shadow: 0 1px 0 #222; margin: 0 auto 20px; width: 348px;
}
th { background: #171717; background: -webkit-linear-gradient(#222, #111); background: -moz-linear-gradient(#222, #111); background: -o-linear-gradient(#222, #111); background: linear-gradient(#222, #111); border: 1px solid #000; border-radius: 2px 2px 0 0; box-shadow: inset 0 1px 0 #333; color: #fff; font-size: 14px; padding: 6px 16px;
}
td { border-bottom: 1px solid #000; border-right: 1px solid #000; box-shadow: inset 0 1px 0 #333; color: #fff; font-size: 11px; font-weight: bold; padding: 6px 16px; text-align: left; width: 50%;
}
td:first-child { border-left: 1px solid #000; color: #ccc; font-weight: normal; text-align: right;
}
td .td-note { color: #ccc; font-style: italic; font-weight: normal;
}
td .td-note.na { color: #f22;
}
td .best-score { color: #2f2; font-weight: bold;
}
tr { background: #222; background: -webkit-linear-gradient(#292929, #1a1a1a); background: -moz-linear-gradient(#292929, #1a1a1a); background: -o-linear-gradient(#292929, #1a1a1a); background: linear-gradient(#292929, #1a1a1a);
}
tr:last-child td:first-child { border-radius: 0 0 0 2px;
}
tr:last-child td:last-child { border-radius: 0 0 2px 0;
}
tr:hover td { background: #333; box-shadow: inset 0 1px 0 #444;
}
/* Miscellaneous */
.clearfix:before, .clearfix:after { content: ''; display: table;
}
.clearfix:after { clear: both;
}
.clearfix { zoom: 1;
}
.divider { background: #000; border-bottom: 1px solid #222; clear: both; height: 1px; margin: 0 0 20px;
}
a,
.button span,
#funds,
td,
.level-notes,
.level-score,
.value{ -webkit-transition: all 300ms; -moz-transition: all 300ms; -o-transition: all 300ms; transition: all 300ms;
}
#no-can { display: none; margin: 0 auto; width: 500px;
}
#no-can p { font-size: 12px; text-align: center;
}
#no-can a { color: #4f4;
}
#no-can a:hover { color: #afa;
}
SpacePi js13k - Script Codes JS Codes
var SpacePi = function(){ // reference to the game var self = this; var doc = document; /* Utility / Mathematical / Miscellaneous */ // random range self.rand = function(a,b){return ~~((Math.random()*(b-a+1))+a);} // instersection check - source: http://www.kevlindev.com/gui/math/intersection/Intersection.js self.intersection = function(a1, a2, b1, b2) { var result; var ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x); var ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x); var u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y); if ( u_b != 0 ) { var ua = ua_t / u_b; var ub = ub_t / u_b; if ( 0 <= ua && ua <= 1 && 0 <= ub && ub <= 1 ) { result = { x: a1.x + ua * (a2.x - a1.x), y: a1.y + ua * (a2.y - a1.y) }; } else { result = false; } } else { result = false; } return result; }; // degrees to radians self.dToR = function(degrees){ return degrees * (Math.PI / 180); } // arc in rectangle collision detection self.arcInRect = function(arcx, arcy, arcr, rectx, recty, rectw, recth){ return !(arcx + arcr <= rectx || arcx - arcr >= rectx + rectw || arcy + arcr <= recty || arcy - arcr >= recty + recth); }; // pi2 for arcs self.pi2 = Math.PI * 2; // update, render, or destroy an entire array self.updateAll = function(a){ var i = a.length; while(i--){ a[i] && a[i].update(i); } } self.renderAll = function(a){ var i = a.length; while(i--){ a[i] && a[i].render(i); } } self.destroyAll = function(a){ var i = a.length; while(i--){ a[i] && a[i].destroy(i); } } // clear the canvas self.clear = function(){ self.ctx.clearRect(0, 0, self.cw, self.ch); } // add commas to large numbers - source: http://stackoverflow.com/questions/6392102/add-commas-to-javascript-output self.commas = function(nStr){ nStr += ''; x = nStr.split('.'); x1 = x[0]; x2 = x.length > 1 ? '.' + x[1] : ''; var rgx = /(\d+)(\d{3})/; while(rgx.test(x1)) { x1 = x1.replace(rgx, '$1' + ',' + '$2'); } return x1 + x2; } // class helpers - source: http://rockycode.com/blog/addremove-classes-raw-javascript/ self.hasClass = function(ele,cls){ return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)')); } self.addClass = function(ele,cls){ if (!self.hasClass(ele,cls)) ele.className += " "+cls; } self.rmvClass = function(ele,cls){ if (self.hasClass(ele,cls)) { var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)'); ele.className=ele.className.replace(reg,' '); } } // getElementById() shortener self.get = function(a){ return doc.getElementById(a); } // local storage helpers - source: http://stackoverflow.com/questions/2010892/storing-objects-in-html5-localstorage/3146971#3146971 Storage.prototype.setObject = function(key, value) { this.setItem(key, JSON.stringify(value)); } Storage.prototype.getObject = function(key) { var value = this.getItem(key); return value && JSON.parse(value); } Storage.prototype.removeObject = function(key) { this.removeItem(key); } /* Initialize Game */ self.initGame = function(){ self.gameWrap = self.get('game-wrap'); self.c = self.get('c'); doc.onselectstart = function(){ return false }; self.cw = c.width; self.ch = c.height; self.ctx = c.getContext('2d'); self.ctx.lineWidth = 1.5; self.ctx.globalCompositeOperation = 'lighter'; self.setupUser(); self.bindMenuEvents(); self.bindGameplayEvents(); self.initialDelay = 140; self.menuMode = true; } /* User */ self.setupUser = function(){ // get the user or create a user with defaults self.user = localStorage.getObject('spacePiUser') || { highestLevelBeaten: 0, levels: [ {score: 0, perfect: false}, {score: 0, perfect: false}, {score: 0, perfect: false}, {score: 0, perfect: false}, {score: 0, perfect: false}, {score: 0, perfect: false}, {score: 0, perfect: false}, {score: 0, perfect: false}, {score: 0, perfect: false}, {score: 0, perfect: false}, {score: 0, perfect: false}, {score: 0, perfect: false}, {score: 0, perfect: false} ], funds: 0, upgrades: { power: { capacity: 0, generation: 0 }, base: { resistance: 0, generation: 0 }, magnet: { range: 0, power: 0 }, coins: { chance: 0, amount: 0 }, powerups: { chance: 0, duration: 0 } }, overall: { linesCreated: 0, lineLength: 0, baseHits: 0, levelsPlayed: 0, timePlayed: 0 } }; self.syncDOM(); } self.updateUser = function(){ localStorage.setObject('spacePiUser', self.user); self.syncDOM(); } self.clearUser = function(){ localStorage.removeObject('spacePiUser'); self.setupUser(); self.syncDOM(); } /* Upgrade Reference */ self.upgrades = { power: { capacity: { value: [100, 200, 350, 500, 750, 1000], cost: [100, 1000, 2000, 8000, 12000] }, generation: { value: [.5, 1, 1.5, 2, 2.5, 3], cost: [100, 1000, 2000, 8000, 12000] } }, base: { resistance: { value: [0, 1, 2, 3, 4, 5], cost: [100, 1000, 2000, 8000, 12000] }, generation: { value: [.02, .03, .04, .05, .06, .07], cost: [100, 1000, 2000, 8000, 12000] } }, powerups: { chance: { value: [2, 3, 4, 5, 6, 7], cost: [50, 500, 2000, 4000, 8000] }, duration: { value: [3, 3.5, 4, 4.5, 5, 5.5], cost: [50, 500, 2000, 4000, 8000] } }, magnet: { range: { value: [50, 100, 150, 200, 300, 400], cost: [50, 500, 2000, 4000, 8000] }, power: { value: [1, 2, 3, 4, 5, 6], cost: [50, 500, 2000, 4000, 8000] } }, coins: { chance: { value: [50, 55, 60, 65, 70, 75], cost: [50, 500, 2000, 4000, 8000] }, amount: { value: [1, 2, 3, 4, 5, 6], cost: [50, 500, 2000, 4000, 8000] } } } self.checkMaxUpgrades = function(){ // coins are disabled once all upgrades are purchased return ( self.user.upgrades.power.capacity === 5 && self.user.upgrades.power.generation === 5 && self.user.upgrades.base.resistance === 5 && self.user.upgrades.base.generation === 5 && self.user.upgrades.powerups.chance === 5 && self.user.upgrades.powerups.duration === 5 && self.user.upgrades.magnet.range === 5 && self.user.upgrades.magnet.power === 5 && self.user.upgrades.coins.chance === 5 && self.user.upgrades.coins.amount === 5 ); } /* Powerup Reference */ self.powerupRef = [ { name: 'Slow Enemies', hue: 205 }, { name: 'Unlimited Power', hue: 0 }, { name: 'Base Generation Boost', hue: 300 }, { name: 'Coin Scatter', hue: 60 } ]; /* Level Reference */ self.levels = []; for(var z = 0; z < 13; z++){ self.levels.push({ badTick: 0, badTickMax: 90 - (z*6), badSpeedMin: 80 + (z*14), badSpeedMax: 120 + (z*14), badLengthMin: 142 - (z*8), badLengthMax: 132 - (z*8), badDamage: 7 + z, goal: 30 + (z*11) }); } /* Initialize Level */ self.initLevel = function(level){ self.level = level; self.levelPlaying = true; self.menuMode = false; // mouse self.mousedown = false; self.mx = self.cw/2; self.my = self.ch/2; // delta self.dt = 0; self.oldTime = Date.now(); // rumble self.rumble = false; self.rumbleLevel = 0; // entities self.tempPointLength = 0; self.tempPointStart = {x: 0, y: 0}; self.tempPointEnd = {x: 0, y: 0}; self.goodLines = []; self.goodLines.length = 0; self.badLines = []; self.badLines.length = 0; self.coins = []; self.coins.length = 0; self.powerups = []; self.powerups.length = 0; self.blasts = []; self.blasts.length = 0; self.debris = []; self.debris.length = 0; self.stars = []; self.stars.length = 0; self.orbs = []; self.orbs.length = 0; // base self.base = { x: self.cw / 2, y: self.ch / 2, radius: 2.1, goal: self.levels[self.level].goal, goalPulseAngle: 0, generation: self.upgrades.base.generation.value[self.user.upgrades.base.generation], flicker: 0 } // user interface self.ui = {}; // line power self.power = { capacity: self.upgrades.power.capacity.value[self.user.upgrades.power.capacity], current: self.upgrades.power.capacity.value[self.user.upgrades.power.capacity], rate: self.upgrades.power.generation.value[self.user.upgrades.power.generation] } // powerups self.pwrupTimer = 0; self.pwrupTimerMax = 0; self.pwrupActive = false; if(self.checkMaxUpgrades()){ self.upgradesMaxed = true; } else { self.upgradesMaxed = false; } // create starfield self.makeStarfield(); // level stats self.levelStats = { startTime: Date.now(), endTime: 0, pauseStartTime: 0, pauseTimeTotal: 0, goodLineTotal: 0, goodLineLength: 0, coinsCollected: 0, profit: 0, baseHits: 0, powerupsCollected: 0, score: 0, ticks: 0 } // coin powerup timing self.coinPowerupTick = 0; self.coinPowerupTickMax = 8; // set level hue self.levelHue = (self.level+1)*(360/13); // set level end timing self.levelEndStatus = [false, false]; self.levelEndTick = 0; self.levelEndTickMax = 100; self.syncDOM(); self.loop(); } /* Delta */ self.updateDelta = function(){ // attempt to normalize game speed across multiple FPS levels var newTime = Date.now(); self.dt = (newTime - self.oldTime)/16; self.dt = (self.dt > 10) ? 10 : self.dt; self.oldTime = newTime; } /* Rumble */ self.updateRumble = function(){ if((!self.levelEndStatus[0] || (self.levelEndStatus[0] && !self.levelEndStatus[1])) && self.rumbleLevel > 0){ self.rumbleLevel -= 1 * self.dt; } } /* Base */ self.updateBase = function(){ // update pulse angle if(self.base.goalPulseAngle < 360){ self.base.goalPulseAngle += 10 * (self.base.radius/self.base.goal) * self.dt; } else { self.base.goalPulseAngle = 0; } // grow the radius if it is smaller than max if(self.levelStats.ticks > self.initialDelay){ if(self.base.radius < self.base.goal && !self.levelEndStatus[0]){ var generation = (self.pwrupActive && self.powerupType === 2) ? (self.base.generation + (self.base.generation/2)) : self.base.generation; self.base.radius += generation * self.dt; } } // check for bad line collisions var i = self.badLines.length; while(i--){ var badLine = self.badLines[i]; var badPoint = badLine.p1; var dx = self.base.x - badPoint.x; var dy = self.base.y - badPoint.y; var dist = Math.sqrt(dx * dx + dy * dy); if(dist <= self.base.radius && !self.levelEndStatus[0]){ self.base.radius -= badLine.damage; // rumble self.rumbleLevel = 45; // good debris self.makeDebrisgroup(self.badLines[i].p1.x, self.badLines[i].p1.y, self.levelHue, 50, 1, 15); //bad debris self.makeDebrisgroup(self.badLines[i].p1.x, self.badLines[i].p1.y, (self.pwrupActive && self.powerupType === 0) ? 205 : 0, 50, 1, 15); self.makeDebrisLine(self.badLines[i].p1.x, self.badLines[i].p1.y, (self.pwrupActive && self.powerupType === 0) ? 205 : 0, 50, self.badLines[i].angle, self.badLines[i].length, 30); // blast self.makeBlastgroup(badPoint.x, badPoint.y); self.base.flicker = self.rand(5, 12); self.levelStats.baseHits++; self.badLines.splice(i, 1); } } // make orbs if(self.rand(0, 2) === 0){ self.orbs.push(new self.Orb()); } // decrement flicker if(self.base.flicker > 0){ self.base.flicker -= (1 * self.dt); } // level end success if(self.base.radius >= self.base.goal){ self.levelEndStatus = [true, true]; } // level end fail if(self.base.radius <= 2){ self.levelEndStatus = [true, false]; } } self.renderBase = function(){ // render base var newX = (self.base.flicker >= 1) ? self.base.x+self.rand(0, 3)-1.5 : self.base.x; var newY = (self.base.flicker >= 1) ? self.base.y+self.rand(0, 3)-1.5 : self.base.y; self.ctx.beginPath(); self.ctx.arc(newX, newY, (self.base.radius < 0) ? 0 : self.base.radius, 0, self.pi2, false); if(self.pwrupActive && self.powerupType === 2){ self.ctx.fillStyle = 'hsl('+360*((self.levelStats.ticks%20)/20)+', '+(50+(self.base.radius/self.base.goal)*50)+'%, '+(50+((Math.abs(180-self.base.goalPulseAngle))/180)*20)+'%)'; } else { self.ctx.fillStyle = 'hsl('+self.levelHue+', '+(50+(self.base.radius/self.base.goal)*50)+'%, '+(50+((Math.abs(180-self.base.goalPulseAngle))/180)*20)+'%)'; } self.ctx.fill(); // base highlight self.ctx.save(); self.ctx.beginPath(); self.ctx.arc(newX, newY, (self.base.radius < 0) ? 0 : self.base.radius, 0, self.pi2, false); self.ctx.clip(); self.ctx.beginPath(); self.ctx.arc(newX-self.base.radius/3, newY-self.base.radius/3, (self.base.radius < 0) ? 0 : self.base.radius*1.3, 0, self.pi2, false); var grad = self.ctx.createRadialGradient(newX-self.base.radius/3, newY-self.base.radius/3, 0, newX-self.base.radius/3, newY-self.base.radius/2, (self.base.radius < 0) ? 0 : self.base.radius*1.3); grad.addColorStop(0, 'rgba(255,255,255,.3)'); grad.addColorStop(1, 'rgba(255,255,255,0)'); self.ctx.fillStyle = grad; self.ctx.fill(); self.ctx.restore(); if(self.pwrupActive && self.powerupType === 2){ // extra powerup indication var newRadius = self.base.radius * ((self.base.goalPulseAngle)/360); newRadius = (newRadius <= 0) ? .01 : newRadius; self.ctx.beginPath(); self.ctx.arc(self.base.x, self.base.y, (newRadius < 0) ? 0 : newRadius, 0, self.pi2, false); self.ctx.strokeStyle = 'rgba(255,255,255,.5)'; self.ctx.stroke(); } if(self.base.flicker >= 1){ self.ctx.beginPath(); self.ctx.arc(newX, newY, (self.base.radius < 0) ? 0 : self.base.radius, 0, self.pi2, false); self.ctx.fillStyle = 'hsla(0, 0%, 100%, '+self.rand(25, 100)/100+')'; self.ctx.fill(); self.ctx.fillStyle = 'hsla(0, 0%, 100%, '+self.rand(1, 10)/100+')'; self.ctx.fillRect(0,0,self.cw, self.ch); } } self.renderBaseGoal = function(){ var goalRadius = 0; if(self.levelStats.ticks <= self.initialDelay){ goalRadius = (self.levelStats.ticks/self.initialDelay) * self.base.goal; } else { goalRadius = self.base.goal; } // render goal self.ctx.beginPath(); self.ctx.arc(self.base.x, self.base.y, (goalRadius < 0) ? 0 : goalRadius, 0, self.pi2, false); self.ctx.strokeStyle = 'hsla('+self.levelHue+', 100%, 20%, .35)'; self.ctx.stroke(); // render goal pulse self.ctx.lineWidth = 2; self.ctx.beginPath(); self.ctx.arc(self.base.x, self.base.y, (goalRadius < 0) ? 0 : goalRadius, self.dToR(self.base.goalPulseAngle), self.dToR(self.base.goalPulseAngle) + (Math.PI / 3), false); self.ctx.strokeStyle = 'hsl('+self.levelHue+', 100%, '+(10+((Math.abs(180-self.base.goalPulseAngle))/180)*20)+'%)'; self.ctx.stroke(); self.ctx.beginPath(); self.ctx.arc(self.base.x, self.base.y, (goalRadius < 0) ? 0 : goalRadius, self.dToR(self.base.goalPulseAngle+180), self.dToR(self.base.goalPulseAngle+180) + (Math.PI / 3), false); self.ctx.stroke(); self.ctx.lineWidth = 1.5; } /* Power */ self.updatePower = function(){ if(self.power.current < 0){ self.power.current = 0; } if(self.power.current < self.power.capacity){ self.power.current += self.power.rate * self.dt; } if(self.power.current > self.power.capacity){ self.power.current = self.power.capacity; } if(self.pwrupActive && self.powerupType === 1){ self.power.current = self.power.capacity; } if(self.mousedown){ var dx = self.mx - self.tempPointStart.x; var dy = self.my - self.tempPointStart.y; var dist = Math.sqrt(dx * dx + dy * dy); if(dist <= self.power.current || (self.pwrupActive && self.powerupType === 1)){ self.tempPointEnd = {x: self.mx, y: self.my}; self.tempPointLength = dist; } else { var angle = Math.atan2(dy, dx); // set second point based on first point, angle, and length self.tempPointEnd = { x: self.tempPointStart.x + Math.cos(angle) * self.power.current, y: self.tempPointStart.y + Math.sin(angle) * self.power.current }; var dx2 = self.tempPointEnd.x - self.tempPointStart.x; var dy2 = self.tempPointEnd.y - self.tempPointStart.y; self.temptPointLength = Math.sqrt(dx2 * dx2 + dy2 * dy2); } } } /* Tracer Line */ self.renderTracer = function(){ if(self.mousedown){ // potential self.ctx.beginPath(); self.ctx.moveTo(self.tempPointStart.x, self.tempPointStart.y); self.ctx.lineTo(self.mx, self.my); self.ctx.strokeStyle = '#222'; self.ctx.stroke(); // actual self.ctx.beginPath(); self.ctx.moveTo(self.tempPointStart.x, self.tempPointStart.y); self.ctx.lineTo(self.tempPointEnd.x, self.tempPointEnd.y); self.ctx.strokeStyle = '#fff'; self.ctx.stroke(); } } /* Good Lines */ self.goodLine = function(p1, p2){ this.p1 = p1; this.p2 = p2; var dx = p1.x - p2.x; var dy = p1.y - p2.y; this.length = Math.sqrt(dx * dx + dy * dy); this.angle = Math.atan2(dy, dx); // set pulse tracking this.pulse = 100; this.pulseToggle = false; this.pulseMin = 70; this.pulseMax = 100; self.levelStats.goodLineTotal++; self.levelStats.goodLineLength += this.length; } self.goodLine.prototype = { update: function(s){ // pulse toggling and tracking if(this.pulseToggle){ this.pulse += 2; if(this.pulse >= this.pulseMax){ this.pulseToggle = false; } } else { this.pulse -= 2; if(this.pulse <= this.pulseMin){ this.pulseToggle = true; } } var i = self.badLines.length; while(i--){ // check for intersection between good and bad line var intersection = self.intersection(this.p1, this.p2, self.badLines[i].p1, self.badLines[i].p2); if(intersection){ // add rumble self.rumbleLevel = 20; // create blasts self.makeBlastgroup(intersection.x, intersection.y); // destroy lines self.badLines[i].destroy(i, intersection.x, intersection.y); this.destroy(s, intersection.x, intersection.y); } } }, render: function(){ self.ctx.beginPath(); self.ctx.moveTo(this.p1.x, this.p1.y); self.ctx.lineTo(this.p2.x, this.p2.y); self.ctx.strokeStyle = 'hsl(0, 0%, '+this.pulse+'%)'; self.ctx.stroke(); }, destroy: function(i, x, y){ // create debris if(x && y){ self.makeDebrisgroup(x, y, 0, 100, 1, 15); } self.makeDebrisLine(this.p1.x, this.p1.y, 0, 100, this.angle, this.length, 30); // remove from array self.goodLines.splice(i, 1); } } /* bad Lines */ self.badLine = function(){ // determine random starting quadrant // 1 = top // 2 = right // 3 = bottom // 4 = left var quadrant = self.rand(1, 4); // plot random start point switch(quadrant){ case 1: this.p1 = { x: self.rand(0, self.cw), y: 0 } break; case 2: this.p1 = { x: self.cw, y: self.rand(0, self.ch) } break; case 3: this.p1 = { x: self.rand(0, self.cw), y: self.ch } break; case 4: this.p1 = { x: 0, y: self.rand(0, self.ch) } } // get angle from start point to center point var dx = self.cw/2 - this.p1.x; var dy = self.ch/2 - this.p1.y; this.angle = Math.atan2(dy, dx); // set random length this.length = self.rand(self.levels[self.level].badLengthMin, self.levels[self.level].badLengthMax); // set second point based on first point, angle, and length this.p2 = { x: this.p1.x - Math.cos(this.angle) * this.length, y: this.p1.y - Math.sin(this.angle) * this.length }; // set the speed and acceleration this.speed = self.rand(self.levels[self.level].badSpeedMin, self.levels[self.level].badSpeedMax)/100; // set the damage this.damage = self.levels[self.level].badDamage - self.upgrades.base.resistance.value[self.user.upgrades.base.resistance]; if(this.damage < 1){ this.damage = 1; } // set pulse tracking this.pulse = 45; this.pulseToggle = false; this.pulseMin = 30; this.pulseMax = 60; } self.badLine.prototype = { update: function(){ // update both line points simultaneously based on angle and speed var speed = (self.pwrupActive && self.powerupType === 0) ? this.speed/2 : this.speed; this.p1.x += (Math.cos(this.angle) * speed) * self.dt; this.p2.x += (Math.cos(this.angle) * speed) * self.dt; this.p1.y += (Math.sin(this.angle) * speed) * self.dt; this.p2.y += (Math.sin(this.angle) * speed) * self.dt; this.speed += ((self.base.radius/self.base.goal)/100); // pulse toggling and tracking if(this.pulseToggle){ this.pulse += 2; if(this.pulse >= this.pulseMax){ this.pulseToggle = false; } } else { this.pulse -= 2; if(this.pulse <= this.pulseMin){ this.pulseToggle = true; } } }, render: function(){ self.ctx.beginPath(); self.ctx.moveTo(this.p1.x, this.p1.y); self.ctx.lineTo(this.p2.x, this.p2.y); self.ctx.strokeStyle = (self.pwrupActive && self.powerupType === 0) ? 'hsl('+self.powerupRef[self.powerupType].hue+', 100%, '+this.pulse+'%)' : 'hsl(0, 100%, '+this.pulse+'%)'; self.ctx.stroke(); }, destroy: function(i, x, y){ // create debris if(x && y){ self.makeDebrisgroup(x, y, (self.pwrupActive && self.powerupType === 0) ? 205 : 0, 50, 1, 15); } self.makeDebrisLine(this.p1.x, this.p1.y, (self.pwrupActive && self.powerupType === 0) ? 205 : 0, 50, this.angle, this.length, 30); // create coins if(self.rand(1, 100) <= self.upgrades.coins.chance.value[self.user.upgrades.coins.chance] && !self.upgradesMaxed){ self.makeCoins(x || this.p1.x, y || this.p1.y, (self.level+1)*5, self.rand(1, self.upgrades.coins.amount.value[self.user.upgrades.coins.amount])); } // create powerups if(x && y){ if(self.rand(1, 100) <= self.upgrades.powerups.chance.value[self.user.upgrades.powerups.chance] && !self.pwrupActive){ var newType = (self.upgradesMaxed) ? self.rand(0,2) : self.rand(0,3); self.powerups.push(new self.Powerup(x, y, newType)); } } // remove from array self.badLines.splice(i, 1); } } self.makebadLines = function(){ if(self.levelStats.ticks > self.initialDelay){ var newMax = (self.pwrupActive && self.powerupType === 0) ? self.levels[self.level].badTickMax*2 : self.levels[self.level].badTickMax; if(self.levels[self.level].badTick >= newMax){ self.badLines.push(new self.badLine()); self.levels[self.level].badTick = 0; } else { self.levels[self.level].badTick += 1 * self.dt; } } } /* Coins */ self.Coin = function(x, y, value){ this.x = x; this.y = y; this.vx = (self.rand(0, 100)-50)/100; this.vy = (self.rand(0, 100)-50)/100; this.radius = 4; this.value = value; this.magnetized = false; this.xScale = 1; this.xScaleGrow = true; this.collected = false; } self.Coin.prototype = { update: function(i){ // update the xScale to create spinning effect if(this.xScaleGrow && this.xScale >= 1){ this.xScaleGrow = false; } else if(!this.xScaleGrow && this.xScale <= .1){ this.xScaleGrow = true; } if(this.xScaleGrow){ this.xScale += .05; } else { this.xScale -= .05; } // if the coin has not been collected yet if(!this.collected){ // handle magnet power var dx = self.mx - this.x; var dy = self.my - this.y; var dist = Math.sqrt(dx * dx + dy * dy); if(dist <= self.upgrades.magnet.range.value[self.user.upgrades.magnet.range]){ this.magnetized = true; var angle = Math.atan2(dy, dx); var mvx = Math.cos(angle); var mvy = Math.sin(angle); this.x += (mvx * self.upgrades.magnet.power.value[self.user.upgrades.magnet.power]) * self.dt; this.y += (mvy * self.upgrades.magnet.power.value[self.user.upgrades.magnet.power]) * self.dt; } else { this.magnetized = false; this.x += this.vx * self.dt; this.y += this.vy * self.dt; } // collect the coin if(dist <= 15){ self.levelStats.coinsCollected++; self.levelStats.profit += this.value; this.collected = true; this.magnetized = false; } // out of bounds, destroy coin if(!self.arcInRect(this.x, this.y, this.radius, 0, 0, self.cw, self.ch)){ self.coins.splice(i, 1); } } else { var dx = self.cw+4 - this.x; var dy = -4 - this.y; var dist = Math.sqrt(dx * dx + dy * dy); var angle = Math.atan2(dy, dx); var mvx = Math.cos(angle); var mvy = Math.sin(angle); this.x += (mvx*40) * self.dt; this.y += (mvy*40) * self.dt; if(!self.arcInRect(this.x, this.y, this.radius, 0, 0, self.cw, self.ch)){ self.coins.splice(i, 1); } } }, render: function(){ self.ctx.save(); self.ctx.translate(this.x, this.y); self.ctx.scale(this.xScale, 1) self.ctx.beginPath(); self.ctx.arc(0, 0, (this.radius < 0) ? 0 : this.radius, 0, self.pi2, false); self.ctx.fillStyle = (this.magnetized) ? 'hsl(60, 0%, '+(this.xScale*140)+'%)' : 'hsl('+self.levelHue+', 100%, '+(this.xScale*70)+'%)'; self.ctx.fill(); self.ctx.restore(); } } self.makeCoins = function(x, y, value, count){ while(count--){ setTimeout(function(){ self.coins.push(new self.Coin(x+self.rand(0,40)-20, y+self.rand(0,40)-20, value)); }, 10 * count); } } self.checkCoinPowerup = function(){ if(self.pwrupActive && self.powerupType === 3 && !self.upgradesMaxed){ if(self.coinPowerupTick < self.coinPowerupTickMax){ self.coinPowerupTick += 1 * self.dt; } else { self.coinPowerupTick = 0; self.makeCoins(self.rand(0, self.cw), self.rand(0, self.ch), (self.level+1)*5, 1); } } else { self.coinPowerupTick = 0; } } /* Powerups */ self.Powerup = function(x, y, type){ this.x = x; this.y = y; this.vx = (self.rand(0, 100)-50)/50; this.vy = (self.rand(0, 100)-50)/50; this.radius = 12; this.type = type; this.magnetized = false; this.angle = 0; } self.Powerup.prototype = { update: function(i){ if(this.angle < 360){ this.angle += 5; } else { this.angle = 0; } // handle magnet power var dx = self.mx - this.x; var dy = self.my - this.y; var dist = Math.sqrt(dx * dx + dy * dy); if(dist <= self.upgrades.magnet.range.value[self.user.upgrades.magnet.range]){ this.magnetized = true; var angle = Math.atan2(dy, dx); var mvx = Math.cos(angle); var mvy = Math.sin(angle); this.x += (mvx * (self.upgrades.magnet.power.value[self.user.upgrades.magnet.power])/4) * self.dt; this.y += (mvy * (self.upgrades.magnet.power.value[self.user.upgrades.magnet.power])/4) * self.dt; } else { this.magnetized = false; this.x += this.vx * self.dt; this.y += this.vy * self.dt; } // collect the powerup if(dist <= 15){ self.levelStats.powerupsCollected++; self.pwrupActive = true; self.powerupType = this.type; self.pwrupTimer = self.upgrades.powerups.duration.value[self.user.upgrades.powerups.duration] * 60; self.pwrupTimerMax = self.upgrades.powerups.duration.value[self.user.upgrades.powerups.duration] * 60; self.powerups.length = 0; } // out of bounds, destroy powerup if(!self.arcInRect(this, this.y, this.radius, 0, 0, self.cw, self.ch)){ self.powerups.splice(i, 1); } }, render: function(){ self.ctx.save(); self.ctx.translate(this.x, this.y); self.ctx.rotate(self.dToR(this.angle)); self.ctx.lineWidth = 6; var lightness = (50+(Math.abs(180-this.angle)/180)*15); self.ctx.strokeStyle = 'hsl('+self.powerupRef[this.type].hue+', 100%, '+lightness+'%)'; self.ctx.beginPath(); self.ctx.arc(0, 0, (this.radius < 0) ? 0 : this.radius, 0, self.dToR(60), false); self.ctx.stroke(); self.ctx.beginPath(); self.ctx.arc(0, 0, (this.radius < 0) ? 0 : this.radius, self.dToR(120), self.dToR(180), false); self.ctx.stroke(); self.ctx.beginPath(); self.ctx.arc(0, 0,(this.radius < 0) ? 0 : this.radius, self.dToR(240), self.dToR(300), false); self.ctx.stroke(); self.ctx.restore(); } } self.updateActivePowerup = function(){ if(self.pwrupActive){ if(self.pwrupTimer > 0){ self.pwrupTimer -= 1 * self.dt; } else { self.pwrupTimer = 0; self.pwrupTimerMax = 0; self.pwrupActive = false; } } }; /* Blasts */ self.Blast = function(x, y){ this.x = x; this.y = y; this.radius = self.rand(4, 18); } self.Blast.prototype = { update: function(i){ this.radius -= (.65 * self.dt); if(this.radius <= 0){ self.blasts.splice(i, 1); } }, render: function(){ var newRadius = this.radius+self.rand(0,2)/3; self.ctx.beginPath(); self.ctx.arc(this.x+self.rand(0,2)-1, this.y+self.rand(0,2)-1, (newRadius < 0) ? 0 : newRadius, 0, self.pi2, false); self.ctx.fillStyle = 'hsla('+self.levelHue+', 100%, '+self.rand(50, 80)+'%, '+self.rand(50, 100)/100+')'; self.ctx.fill(); } } self.makeBlastgroup = function(x, y){ self.blasts.push(new self.Blast(x, y)); self.blasts.push(new self.Blast(x+self.rand(0,10)-5, y+self.rand(0,10)-5)); self.blasts.push(new self.Blast(x+self.rand(0,10)-5, y+self.rand(0,10)-5)); } /* Debris */ self.Debris = function(x, y, hue, lightness, power){ this.x = x; this.y = y; this.size = self.rand(1, 3)/2; this.speed = (self.rand(0, power)-(power/2))/20; this.angle = self.rand(0, 360); this.hue = hue; this.saturation = 100; this.lightness = lightness; this.alpha = self.rand(75, 100)/100; this.decay = self.rand(50, 100)/5000; } self.Debris.prototype = { update: function(i){ var radians = self.dToR(this.angle); var vx = Math.cos(radians) * (this.speed * this.alpha); var vy = Math.sin(radians) * (this.speed * this.alpha); this.x += vx * self.dt; this.y += vy * self.dt; this.alpha -= (this.decay * self.dt); if(this.alpha <= 0){ self.debris.splice(i, 1); } }, render: function(){ self.ctx.fillStyle = 'hsla('+this.hue+', '+this.saturation+'%, '+this.lightness+'%, '+this.alpha+')'; self.ctx.fillRect(this.x, this.y, this.size, this.size); } } self.makeDebrisgroup = function(x, y, hue, lightness, maxRadius, count){ while(count--){ var newRadius = Math.random() * maxRadius; var newAngle = Math.random() * self.pi2; var newX = x + Math.cos(newAngle) * newRadius; var newY = y + Math.sin(newAngle) * newRadius; self.debris.push(new self.Debris(newX, newY, hue, lightness, 100)); } } self.makeDebrisLine = function(x, y, hue, lightness, angle, maxLength, count){ while(count--){ var newLength = self.rand(0, maxLength); var newX = x - Math.cos(angle) * newLength; var newY = y - Math.sin(angle) * newLength; self.debris.push(new self.Debris(newX, newY, hue, lightness, 20)); } } /* Orbs */ self.Orb = function(x, y){ this.x = self.base.x; this.y = self.base.y; this.vx = ((self.rand(0, 100)-50)/20000) * (self.base.radius); this.vy = ((self.rand(0, 100)-50)/20000) * (self.base.radius); this.radius = self.rand(1,4)/4; this.alpha = 0; this.alphaMax = self.rand(75, 100)/100; this.alphaTrigger = false; this.decay = self.rand(50, 100)/4000; this.growth = self.rand(50, 100)/5000; } self.Orb.prototype = { update: function(i){ this.x += this.vx * self.dt; this.y += this.vy * self.dt; if(this.alphaTrigger){ this.alpha -= (this.decay * self.dt); if(this.alpha <= 0){ self.orbs.splice(i, 1); } } else { this.alpha += (this.growth * self.dt); if(this.alpha >= this.alphaMax){ this.alphaTrigger = true; } } }, render: function(){ self.ctx.beginPath(); self.ctx.arc(this.x, this.y, (this.radius < 0) ? 0 : this.radius, 0, self.pi2, false); self.ctx.fillStyle = 'hsla('+self.levelHue+', '+(50+(self.base.radius/self.base.goal)*50)+'%, '+(50+((Math.abs(180-self.base.goalPulseAngle))/180)*20)+'%, '+this.alpha+')'; self.ctx.fill(); } } /* Stars */ self.Star = function(x, y, radius, speed){ this.x = x; this.y = y; this.speed = (speed/25); this.radius = radius; this.saturation = (30+(this.radius)*5); this.lightness = (8+this.radius*3); } self.Star.prototype = { update: function(i){ this.y += this.speed * self.dt; if(this.y - this.radius >= self.ch){ this.x = self.rand(0, self.cw-this.radius) this.y = -this.radius; } }, render: function(){ self.ctx.beginPath(); self.ctx.arc(this.x, this.y, (this.radius < 0) ? 0 : this.radius, 0, self.pi2, false); var flickerAdd = (self.rand(0, 30) === 0) ? self.rand(5, 15) : 0; self.ctx.fillStyle = 'hsl('+self.levelHue+', '+this.saturation+'%, '+(this.lightness+flickerAdd)+'%)'; self.ctx.fill(); } } self.makeStarfield = function(){ var base = .75; var inc = .25; var count = 7; while(count--){ var radius = base + inc; self.stars.push(new self.Star(self.rand(0, self.cw-radius), self.rand(0, self.ch-radius), radius, .75)); radius += inc; self.stars.push(new self.Star(self.rand(0, self.cw-radius), self.rand(0, self.ch-radius), radius, 2)); radius += inc; self.stars.push(new self.Star(self.rand(0, self.cw-radius), self.rand(0, self.ch-radius), radius, 3.25)); radius += inc; self.stars.push(new self.Star(self.rand(0, self.cw-radius), self.rand(0, self.ch-radius), radius, 4.5)); radius += inc; self.stars.push(new self.Star(self.rand(0, self.cw-radius), self.rand(0, self.ch-radius), radius, 5.75)); radius += inc; self.stars.push(new self.Star(self.rand(0, self.cw-radius), self.rand(0, self.ch-radius), radius, 7)); } } /* User Interface */ self.updateUI = function(){ var baseRadius = (self.base.radius >= 2) ? Math.round(self.base.radius) : 0; self.ui.ratioComplete = baseRadius+'/'+self.base.goal; self.ui.percentComplete = Math.round((baseRadius / self.base.goal) * 100); } self.renderUI = function(){ // level start label if(self.levelStats.ticks <= self.initialDelay){ self.ctx.save(); var alpha = (self.initialDelay - self.levelStats.ticks)/100; self.ctx.globalAlpha = (alpha > 1) ? 1 : alpha; self.ctx.globalCompositeOperation = 'source-over'; var labelH = 32 + self.level; self.ctx.font = 'bold '+labelH+'px arial'; self.ctx.textBaseline = 'middle'; self.ctx.textAlign = 'center'; self.ctx.beginPath(); self.ctx.arc(self.cw/2, self.ch/2, (self.base.goal < 0) ? 0 : self.base.goal, 0, self.pi2, false); self.ctx.fillStyle = '#000'; self.ctx.fill(); self.ctx.strokeStyle = '#171717'; self.ctx.stroke(); var grad = self.ctx.createLinearGradient(self.cw/2, self.ch/2 - labelH/2, self.cw/2, self.ch/2 + labelH/2); grad.addColorStop(0, 'hsla('+self.levelHue+', 100%, 70%, 1)'); grad.addColorStop(1, 'hsla('+self.levelHue+', 100%, 40%, 1)'); self.ctx.fillStyle = grad; self.ctx.fillText(self.level+1, self.cw/2, self.ch/2); self.ctx.restore(); } // render top left text self.ctx.fillStyle = '#aaa'; self.ctx.textAlign = 'left'; self.ctx.font = 'bold 10px arial'; self.ctx.fillText('Completion: '+self.ui.ratioComplete+' ('+self.ui.percentComplete+'%)', 12, 20); self.ctx.fillText('Power: '+Math.round(self.power.current)+'/'+self.power.capacity+' ('+Math.round((self.power.current/self.power.capacity) * 100)+'%)', 156, 20); // render top right text // determine current score var milliseconds = Date.now() - self.levelStats.startTime - self.levelStats.pauseTimeTotal; var minutes = Math.floor(milliseconds / 1000 / 60); var seconds = Math.floor(milliseconds / 1000) % 60; var displayScore = ''; displayScore = 0; displayScore += seconds * 100; displayScore += self.levelStats.goodLineTotal * 100; displayScore += self.levelStats.goodLineLength; displayScore += self.levelStats.baseHits * 500; // render profit var maxProfitW = 0; self.ctx.fillStyle = '#aaa'; self.ctx.textAlign = 'right'; self.ctx.font = 'bold 10px arial'; var profitLabelW = self.ctx.measureText('Profit'); self.ctx.fillText('Profit', self.cw-12, 20); self.ctx.fillStyle = '#fff'; self.ctx.font = 'bold 12px arial'; var profitValueW = self.ctx.measureText('$'+self.commas(self.levelStats.profit)); self.ctx.fillText('$'+self.commas(self.levelStats.profit), self.cw-12, 37); maxProfitW = Math.max(profitLabelW.width, profitValueW.width); // render score self.ctx.fillStyle = '#aaa'; self.ctx.textAlign = 'right'; self.ctx.font = 'bold 10px arial'; self.ctx.fillText('Score', self.cw-maxProfitW-36, 20); self.ctx.fillStyle = '#fff'; self.ctx.font = 'bold 12px arial'; self.ctx.fillText(self.commas(Math.round(displayScore)), self.cw-maxProfitW-36, 37); // render level bar self.ctx.fillStyle = '#222'; self.ctx.fillRect(12, 30, 120, 8); self.ctx.fillStyle = 'hsl('+self.levelHue+', '+(50+(self.base.radius/self.base.goal)*50)+'%, '+(50+((Math.abs(180-self.base.goalPulseAngle))/180)*20)+'%)'; self.ctx.fillRect(12, 30, (self.base.radius/self.base.goal)*120, 8); // bar highlight var grad = self.ctx.createLinearGradient(12, 30, 12, 34); grad.addColorStop(0, 'rgba(255,255,255,0)'); grad.addColorStop(1, 'rgba(255,255,255,.2)'); self.ctx.fillStyle = grad; self.ctx.fillRect(12, 30, 120, 4); // render power bar self.ctx.fillStyle = '#222'; self.ctx.fillRect(156, 30, 120, 8); self.ctx.fillStyle = 'hsl(0, 0%, '+(((self.power.current/self.power.capacity)*60))+'%)'; self.ctx.fillRect(156, 30, (self.power.current/self.power.capacity)*120, 8); // bar highlight grad = self.ctx.createLinearGradient(156, 30, 156, 34); grad.addColorStop(0, 'rgba(255,255,255,0)'); grad.addColorStop(1, 'rgba(255,255,255,.2)'); self.ctx.fillStyle = grad; self.ctx.fillRect(156, 30, 120, 4); // render powerup bar and text if(self.pwrupActive){ self.ctx.fillStyle = '#aaa'; self.ctx.textAlign = 'left'; self.ctx.font = 'bold 10px arial'; self.ctx.fillText(self.powerupRef[self.powerupType].name, 300, 20); self.ctx.fillStyle = '#222'; self.ctx.fillRect(300, 30, 120, 8); self.ctx.fillStyle = 'hsl('+self.powerupRef[self.powerupType].hue+', 100%, '+(20+((self.pwrupTimer/self.pwrupTimerMax)*30))+'%)'; self.ctx.fillRect(300, 30, (self.pwrupTimer/self.pwrupTimerMax)*120, 8); // bar highlight grad = self.ctx.createLinearGradient(300, 30, 300, 34); grad.addColorStop(0, 'rgba(255,255,255,0)'); grad.addColorStop(1, 'rgba(255,255,255,.2)'); self.ctx.fillStyle = grad; self.ctx.fillRect(300, 30, 120, 4); } } /* Level Ending */ self.levelEndCycle = function(){ if(self.levelEndStatus[0]){ if(self.levelEndTick >= self.levelEndTickMax){ self.endLevel(); } else { self.levelEndTick += 1 * self.dt; } self.ctx.globalCompositeOperation = 'source-over'; if(self.levelEndStatus[1]){ self.destroyAll(self.badLines); self.rumbleLevel += .01 * self.dt; self.ctx.fillStyle = 'rgba(255,255,255,'+self.levelEndTick/100+')'; } else { self.destroyAll(self.goodLines); self.ctx.fillStyle = 'rgba(70,0,0,'+self.levelEndTick/100+')'; } self.ctx.fillRect(0, 0, self.cw, self.ch); self.ctx.globalCompositeOperation = 'lighter'; } } self.endLevel = function(){ clearTimeout(self.loopTimeout); self.levelPlaying = false; self.levelStats.endTime = Date.now(); self.levelStatsCB(self.levelEndStatus[1]); if(self.level >= self.user.highestLevelBeaten && self.levelEndStatus[1]){ self.user.highestLevelBeaten = self.level+1; } if(self.levelEndStatus[1] && self.levelStats.baseHits === 0){ self.user.levels[self.level].perfect = true; } self.user.funds += self.levelStats.profit; self.user.overall.linesCreated += self.levelStats.goodLineTotal; self.user.overall.lineLength += Math.round(self.levelStats.goodLineLength); self.user.overall.baseHits += self.levelStats.baseHits; self.user.overall.levelsPlayed += 1; self.user.overall.timePlayed += (self.levelStats.endTime - self.levelStats.startTime - self.levelStats.pauseTimeTotal); self.updateUser(); } /* Gameplay Interaction / Event Binding */ self.mousedownCB = function(){ self.mousedown = true; if(!self.menuMode){ self.tempPointStart = {x: self.mx, y: self.my}; } } self.mouseupCB = function(){ self.mousedown = false; if(!self.menuMode){ // Make sure the mouse has moved if(self.tempPointStart.x != self.tempPointEnd.x || self.tempPointStart.y != self.tempPointEnd.y){ self.goodLines.push(new self.goodLine(self.tempPointStart, self.tempPointEnd)); self.power.current -= self.tempPointLength; } } } self.mousemoveCB = function(e){ self.mx = e.pageX - self.c.offsetLeft - self.gameWrap.offsetLeft; self.my = e.pageY - self.c.offsetTop - self.gameWrap.offsetTop; } self.bindGameplayEvents = function(){ doc.addEventListener('mousedown', self.mousedownCB, false); doc.addEventListener('mouseup', self.mouseupCB, false); doc.addEventListener('mousemove', self.mousemoveCB, false); } /* Sync Level and Upgrade DOM Elements to user object */ self.syncDOM = function(){ // funds self.fundsDisplay.innerHTML = (self.checkMaxUpgrades()) ? '$ Maxed' : '$'+self.commas(self.user.funds); // upgrades var i = self.upgradeButtons.length; while(i--){ var _this = self.upgradeButtons[i]; var upgradeParent = _this.getAttribute('data-parent'); var upgradeSpecific = _this.getAttribute('data-specific'); var upgradeLevel = self.user.upgrades[upgradeParent][upgradeSpecific]; var nextLevelCost = self.upgrades[upgradeParent][upgradeSpecific].cost[upgradeLevel] || false; // set level class self.rmvClass(_this, 'upg0'); self.rmvClass(_this, 'upg1'); self.rmvClass(_this, 'upg2'); self.rmvClass(_this, 'upg3'); self.rmvClass(_this, 'upg4'); self.rmvClass(_this, 'upg5'); self.addClass(_this, 'upg'+upgradeLevel); // set cost display var costSpan = _this.querySelectorAll('.cost'); var availability = (!nextLevelCost || nextLevelCost <= self.user.funds); var availabilityText = (availability) ? '<i class="available">Available</i>' : '<i class="unavailable">Unavailable</i>'; costSpan[0].innerHTML = (!nextLevelCost) ? 'Maxed' : '$'+self.commas(nextLevelCost)+' - '+availabilityText; // disable if can't afford if(!availability){ self.addClass(_this, 'disabled'); } else { self.rmvClass(_this, 'disabled') ; } // set max class if maxed if(!nextLevelCost){ self.addClass(_this, 'max'); } else { self.rmvClass(_this, 'max'); } } // levels i = self.levelButtons.length; while(i--){ if(i > self.user.highestLevelBeaten){ self.addClass(self.levelButtons[i], 'disabled'); } else { self.rmvClass(self.levelButtons[i], 'disabled'); } if(self.user.levels[i].perfect){ self.addClass(self.levelButtons[i], 'perfect-level'); } else { self.rmvClass(self.levelButtons[i], 'perfect-level'); } self.levelScores[i].innerHTML = (self.user.levels[i].score == 0) ? '∅' : self.commas(self.user.levels[i].score); } // set completed levels and perfect levels display var levelsPerfect = 0; var levelLength = self.user.levels.length; for(i = 0; i < levelLength; i++){ if(self.user.levels[i].perfect){ levelsPerfect++; } } self.completedLevelsDisplay.innerHTML = self.user.highestLevelBeaten; self.perfectLevelsDisplay.innerHTML = levelsPerfect; } /* Menu Interaction / Event Binding */ // menu content self.menus = doc.querySelectorAll('.menu'); self.mainMenu = self.get('main'); self.pauseMenu = self.get('pause'); self.upgradesLevelsMenu = self.get('upgrades-levels'); self.levelStatsMenu = self.get('level-stats'); self.overallStatsMenu = self.get('overall-stats'); // level stats content self.statsStatus = self.get('stats-status'); self.statsDuration = self.get('stats-duration'); self.statsLinesCreated = self.get('stats-lines-created'); self.statsTotalLineLength = self.get('stats-total-line-length'); self.statsBaseHits = self.get('stats-base-hits'); self.statsPowerupsCollected = self.get('stats-powerups-collected'); self.statsCoinsCollected = self.get('stats-coins-collected'); self.statsProfit = self.get('stats-profit'); self.statsScore = self.get('stats-score'); // overall stats content self.overallStatsLinesCreated = self.get('overall-lines-created'); self.overallStatsLineLength = self.get('overall-line-length'); self.overallStatsLevelsPlayed = self.get('overall-levels-played'); self.overallStatsTimePlayed = self.get('overall-time-played'); self.overallStatsBaseHits = self.get('overall-base-hits'); self.playGameButton = self.get('play-game-button'); self.retryLevelButton = self.get('retry-level-button'); self.returnToMenuButton = self.get('return-to-menu-button'); self.returnToMenuButton2 = self.get('return-to-menu-button2'); self.returnToUpgradesLevelsButton = self.get('return-to-upgrades-levels-button'); self.resumeLevelButton = self.get('resume-level-button'); self.quitLevelButton = self.get('quit-level-button'); self.resetGameDataButton = self.get('reset-game-data-button'); self.overallStatsButton = self.get('overall-stats-button'); self.upgradeButtons = doc.querySelectorAll('.upgrade'); self.levelButtons = doc.querySelectorAll('.level'); self.levelScores = doc.querySelectorAll('.level-score'); // misc self.completedLevelsDisplay = self.get('completed-levels'); self.perfectLevelsDisplay = self.get('perfect-levels'); self.fundsDisplay = self.get('funds'); self.pausedTitle = self.get('paused-title'); // hide all menus self.hideMenus = function(){ var i = self.menus.length; while(i--){ self.menus[i].style.display = 'none'; } } // button/menu callbacks self.playGameCB = function(e){ e.preventDefault(); self.hideMenus(); self.upgradesLevelsMenu.style.display = 'block'; } self.startLevelCB = function(e){ e.preventDefault(); if(!self.hasClass(this, 'disabled')){ self.hideMenus(); var levelNumber = parseInt(this.getAttribute('rel'), 10); self.initLevel(levelNumber); } } self.pauseLevelCB = function(e){ if(e.keyCode == '32' || e.type == 'click'){ if(self.levelPlaying){ e.preventDefault(); if(!self.menuMode){ // pause title self.pausedTitle.innerHTML = 'Level '+(self.level+1)+' Paused'; self.levelStats.pauseStartTime = Date.now(); self.menuMode = true; self.pauseMenu.style.display = 'block'; } else { self.hideMenus(); self.menuMode = false; self.oldTime = Date.now(); self.loop(); self.levelStats.pauseTimeTotal += Date.now() - self.levelStats.pauseStartTime; } } } } self.retryLevelCB = function(e){ e.preventDefault(); clearTimeout(self.loopTimeout); self.hideMenus(); self.initLevel(self.level); }; self.returnToMenuCB = function(e){ e.preventDefault(); clearTimeout(self.loopTimeout); self.hideMenus(); self.mainMenu.style.display = 'block'; }; self.returnToUpgradesLevelsCB = function(e){ e.preventDefault(); clearTimeout(self.loopTimeout); self.hideMenus(); self.upgradesLevelsMenu.style.display = 'block'; }; self.quitLevelCB = function(e){ e.preventDefault(); self.hideMenus(); self.menuMode = false; self.base.radius = 0; self.levelEndTick = self.levelEndTickMax; self.loop(); }; self.resetGameDataCB = function(e){ e.preventDefault(); var confirmation = confirm('Are you sure you want to reset your game data? This cannot be undone.'); if(confirmation){ self.clearUser(); } } self.upgradeButtonCB = function(e){ e.preventDefault(); if(e.target.className != 'tip'){ if(self.hasClass(this, 'disabled')){ self.addClass(self.fundsDisplay, 'not-enough'); setTimeout(function(){ self.rmvClass(self.fundsDisplay, 'not-enough'); }, 300); } if(!self.hasClass(this, 'disabled') && !self.hasClass(this, 'max')){ var upgradeParent = this.getAttribute('data-parent'); var upgradeSpecific = this.getAttribute('data-specific'); var upgradeLevel = self.user.upgrades[upgradeParent][upgradeSpecific]; var nextLevelCost = self.upgrades[upgradeParent][upgradeSpecific].cost[upgradeLevel] || false; self.user.upgrades[upgradeParent][upgradeSpecific]++; self.user.funds-= nextLevelCost; self.updateUser(); self.syncDOM(); } } } self.levelStatsCB = function(status){ self.menuMode = true; self.hideMenus(); var milliseconds = self.levelStats.endTime - self.levelStats.startTime - self.levelStats.pauseTimeTotal; var minutes = Math.floor(milliseconds / 1000 / 60); var seconds = Math.floor(milliseconds / 1000) % 60; var newBestScore = ''; self.levelStats.score = 0; self.levelStats.score += seconds * 100; self.levelStats.score += self.levelStats.goodLineTotal * 100; self.levelStats.score += self.levelStats.goodLineLength; self.levelStats.score += self.levelStats.baseHits * 500; // re render after score to display correctly seconds = (seconds < 10) ? '0'+seconds : seconds; if(status){ if(self.user.levels[self.level].score == 0 || (self.levelStats.score < self.user.levels[self.level].score)){ self.user.levels[self.level].score = Math.round(self.levelStats.score); newBestScore = '<br /><span class="best-score">New Best Score!</span>'; } } self.retryLevelButton.style.display = (status) ? 'none' : 'block'; self.statsStatus.innerHTML = (status) ? '<span class="level-success">Level '+(self.level+1)+' Completed Successfully' : '<span class="level-fail">Level '+(self.level+1)+' Failed</span>'; self.statsDuration.innerHTML = minutes+':'+seconds; self.statsLinesCreated.innerHTML = self.levelStats.goodLineTotal; self.statsTotalLineLength.innerHTML = (self.levelStats.goodLineTotal >= 1) ? Math.round(self.levelStats.goodLineLength)+'px <span class="td-note">('+Math.round(self.levelStats.goodLineLength / self.levelStats.goodLineTotal)+' Avg.)<\/span>' : 0; self.statsBaseHits.innerHTML = (status && self.levelStats.baseHits === 0) ? self.levelStats.baseHits+' <span class="perfect">Perfect!</span>' : self.levelStats.baseHits; self.statsPowerupsCollected.innerHTML = self.levelStats.powerupsCollected; self.statsCoinsCollected.innerHTML = self.levelStats.coinsCollected; self.statsProfit.innerHTML = '$'+self.commas(self.levelStats.profit); self.statsScore.innerHTML = (status) ? self.commas(Math.round(self.levelStats.score))+' <span class="td-note">(Lower is Better)</span>'+newBestScore : self.commas(Math.round(self.levelStats.score))+' <span class="td-note na">(Not Applied)</span>'; self.levelStatsMenu.style.display = 'block'; } self.overallStatsCB = function(e){ e.preventDefault(); self.menuMode = true; self.hideMenus(); var milliseconds = self.user.overall.timePlayed; var minutes = Math.floor(milliseconds / 1000 / 60); var seconds = Math.floor(milliseconds / 1000) % 60; seconds = (seconds < 10) ? '0'+seconds : seconds; self.overallStatsLinesCreated.innerHTML = self.commas(self.user.overall.linesCreated); self.overallStatsLineLength.innerHTML = self.commas(self.user.overall.lineLength)+' <em class="td-note">(pixels)</em><br /><em class="td-note">That\'s</em> '+((self.user.overall.lineLength / 72) / 12).toFixed(1)+' <em class="td-note">feet at 72ppi</em>'; self.overallStatsLevelsPlayed.innerHTML = self.commas(self.user.overall.levelsPlayed); self.overallStatsTimePlayed.innerHTML = minutes+':'+seconds+' <em class="td-note">(minutes:seconds)</em>'; self.overallStatsBaseHits.innerHTML = self.commas(self.user.overall.baseHits); self.overallStatsMenu.style.display = 'block'; } // bind button events self.bindMenuEvents = function(){ doc.addEventListener('keydown', self.pauseLevelCB, false); self.playGameButton.addEventListener('click', self.playGameCB, false); self.retryLevelButton.addEventListener('click', self.retryLevelCB, false); self.returnToMenuButton.addEventListener('click', self.returnToMenuCB, false); self.returnToMenuButton2.addEventListener('click', self.returnToMenuCB, false); self.returnToUpgradesLevelsButton.addEventListener('click', self.returnToUpgradesLevelsCB, false); self.resumeLevelButton.addEventListener('click', self.pauseLevelCB, false); self.quitLevelButton.addEventListener('click', self.quitLevelCB, false); self.resetGameDataButton.addEventListener('click', self.resetGameDataCB, false); self.overallStatsButton.addEventListener('click', self.overallStatsCB, false); // bind all upgrade buttons var i = self.upgradeButtons.length; while(i--){ self.upgradeButtons[i].addEventListener('click', self.upgradeButtonCB, false); } // bind all levels i = self.levelButtons.length; while(i--){ self.levelButtons[i].addEventListener('click', self.startLevelCB, false); } } /* Game Loop */ self.loop = function(){ // delta self.updateDelta(); //save before rumble if(self.rumbleLevel > 0){ self.rumble = true; self.ctx.save(); self.updateRumble(); self.ctx.translate(self.rand(0, self.rumbleLevel/4)-self.rumbleLevel/8, self.rand(0, self.rumbleLevel/4)-self.rumbleLevel/8); } // clear self.clear(); // stars self.updateAll(self.stars); self.renderAll(self.stars); // base goal below everything else self.renderBaseGoal() // coins self.updateAll(self.coins); self.renderAll(self.coins); self.checkCoinPowerup(); // powerups self.updateAll(self.powerups); self.renderAll(self.powerups); self.updateActivePowerup(); // bad lines self.makebadLines(); self.updateAll(self.badLines); self.renderAll(self.badLines); // power self.updatePower(); // good lines self.updateAll(self.goodLines); self.renderAll(self.goodLines); // base self.updateBase(); self.renderBase(); // orbs self.updateAll(self.orbs); self.renderAll(self.orbs); // blasts self.updateAll(self.blasts); self.renderAll(self.blasts); // debris self.updateAll(self.debris); self.renderAll(self.debris); // tracer self.renderTracer(); // user interface self.updateUI(); self.renderUI(); // level end cycle self.levelEndCycle(); // restore after rumble if(self.rumble){ self.ctx.restore(); self.rumble = false; } // increase level tick count self.levelStats.ticks++; // do it again if(!self.menuMode){ self.loopTimeout = setTimeout(self.loop, 16); } // Array length debugging //console.log('Orbs: ' + self.orbs.length); //console.log('Debris: ' + self.debris.length); //console.log('goodLines: ' + self.goodLines.length); //console.log('badLines: ' + self.badLines.length); //console.log('Blasts: ' + self.blasts.length); //console.log('Stars: ' + self.stars.length); //console.log('Powerups: ' + self.powerups.length); //console.log('Coins: ' + self.coins.length); } // end loop
} // end game
// check if canvas is supported
var z = document.createElement('canvas');
if(z.getContext && z.getContext('2d')){ var sp = new SpacePi(); sp.initGame();
} else { var homeWrap = document.getElementById('home-wrap'); var noCanvas = document.getElementById('no-can'); homeWrap.style.display = 'none'; noCanvas.style.display = 'block';
}
Developer | Jack Rugile |
Username | jackrugile |
Uploaded | July 31, 2022 |
Rating | 4 |
Size | 19,847 Kb |
Views | 28,336 |
Find the perfect freelance services for your business! Fiverr's mission is to change how the world works together. Fiverr connects businesses with freelancers offering digital services in 500+ categories. Find Developer!
Name | Size |
Canvas Waterfall | 3,324 Kb |
Rainbow Loader | 2,022 Kb |
Canvas Color Smoke | 3,446 Kb |
Canvas Fireworks | 6,200 Kb |
Glowing Pulse Form | 2,542 Kb |
Canvas Wave | 2,902 Kb |
Organic Circle Canvas Loader | 2,428 Kb |
Bouncing Ball Animation | 1,952 Kb |
Canvas Orbital Trails v2 | 3,421 Kb |
Canvas Radar | 3,074 Kb |
Jasper is the AI Content Generator that helps you and your team break through creative blocks to create amazing, original content 10X faster. Discover all the ways the Jasper AI Content Platform can help streamline your creative workflows. Start For Free!
Name | Username | Size |
Gradients | Karpovsystems | 2,394 Kb |
CSS Parent Selector | Tomhodgins | 2,143 Kb |
Blog | Rottingroom | 1,430 Kb |
Pruebas de d3.js | Juanmanuelcarnerero | 2,485 Kb |
Wavy Road with Dashes | Jonobr1 | 2,679 Kb |
About Us | Francescaedits | 1,902 Kb |
Login with flip 3d animation | Mariusbalaj | 3,014 Kb |
Coburg Banks SVG Logo | Mjtweaver | 3,875 Kb |
Impress JS Algorhytmic Generator | Jeffscottward | 7,906 Kb |
Promodoro | Bencarp | 1,712 Kb |
Surf anonymously, prevent hackers from acquiring your IP address, send anonymous email, and encrypt your Internet connection. High speed, ultra secure, and easy to use. Instant setup. Hide Your IP Now!