diff --git a/README.md b/README.md index 868a9e25..ba124fe7 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,26 @@ -Javascript Pseudo 3D Racer +Javascript-Racer ========================== -An Outrun-style pseudo-3d racing game in HTML5 and Javascript +An Outrun-style pseudo-3d racing game in HTML5 and pure Javascript (no JQuery), playable on desktop and mobile devices, and based on the awesome engine by [Jakes Gordon (Code InComplete)](https://github.com/jakesgordon/javascript-racer). - * [play the game](http://codeincomplete.com/projects/racer/v4.final.html) - * view the [source](https://github.com/jakesgordon/javascript-racer) +![javascript-racer screenshot night time](https://github.com/lrq3000/javascript-racer/raw/master/screenshots/screenshot1.png) +![javascript-racer screenshot daylight](https://github.com/lrq3000/javascript-racer/raw/master/screenshots/screenshot2.png) + + * [play the game](https://lrq3000.github.io/javascript-racer/v5.game.html) ([or the original game](http://codeincomplete.com/projects/racer/v4.final.html)) + * view the [source](https://github.com/lrq3000/javascript-racer) ([or the original engine source](https://github.com/jakesgordon/javascript-racer)) * read about [how it works](http://codeincomplete.com/posts/2012/6/22/javascript_racer/) -Incrementally built up in 4 parts: +Incrementally built up in 5 parts: - * play the [straight road demo](http://codeincomplete.com/projects/racer/v1.straight.html) - * play the [curves demo](http://codeincomplete.com/projects/racer/v2.curves.html) - * play the [hills demo](http://codeincomplete.com/projects/racer/v3.hills.html) - * play the [final version](http://codeincomplete.com/projects/racer/v4.final.html) + * play the [straight road demo](https://lrq3000.github.io/javascript-racer/v1.straight.html) + * play the [curves demo](https://lrq3000.github.io/javascript-racer/v2.curves.html) + * play the [hills demo](https://lrq3000.github.io/javascript-racer/v3.hills.html) + * play the [final version - fastest lap game mode](https://lrq3000.github.io/javascript-racer/v4.final.html) + * play the [final game - out of time game mode](https://lrq3000.github.io/javascript-racer/v5.game.html) With detailed descriptions of how each part works: + * read [the intro post](http://codeincomplete.com/posts/2012/6/22/javascript_racer/) * read more about [v1 - straight roads](http://codeincomplete.com/posts/2012/6/23/javascript_racer_v1_straight) * read more about [v2 - curves](http://codeincomplete.com/posts/2012/6/24/javascript_racer_v2_curves/) * read more about [v3 - hills](http://codeincomplete.com/posts/2012/6/26/javascript_racer_v3_hills/) @@ -35,8 +40,8 @@ Currently supported browsers include: * Chrome (v19+) works great, 60fps at high res... provided you dont have a bad GPU driver * IE9 - ok, 30fps at medium res... not great, but at least it works -The current state of mobile browser performance is pretty dismal. Dont expect this to be playable on -any mobile device. +The current state of mobile browser performance is better than before but still just barely enough to run the game. +Optimizing would surely help for mobile support. >> _NOTE: I havent actually spent anytime optimizing for performance yet. So it might be possible to make it play well on older browsers, but that's not really what this project is about._ @@ -62,8 +67,8 @@ really be considered just how to get started with a pseudo-3d racing game. If we were to try to turn it into a real game we would have to consider: * car sound fx - * better synchronized music - * full screen mode + * synchronized music change + * enhance full screen mode to include the HUD * HUD fx (flash on fastest lap, confetti, color coded speedometer, etc) * more accurate sprite collision * better car AI (steering, braking etc) @@ -79,13 +84,14 @@ If we were to try to turn it into a real game we would have to consider: * multiple stages, different maps * a lap map, with current position indicator * road splits and joins - * day/night cycle + * cars coming in opposite direction * weather effects * tunnels, bridges, clouds, walls, buildings * city, desert, ocean * add city of seattle and space needle to background * 'bad guys' - add some competetor drivers to race against as well as the 'traffic' - * different game modes - fastest lap, 1-on-1 racing, collect coins ? shoot bad guys ? + * different game modes - 1-on-1 racing, collect coins ? shoot bad guys ? + * a nice retro intro (using [codef](https://github.com/N0NameN0/CODEF) or [phaser](http://phaser.io/examples/v2/demoscene/atari-intro)?) * a whole lot of gameplay tuning * ... * ... @@ -99,13 +105,4 @@ Related Links License ======= -[MIT](http://en.wikipedia.org/wiki/MIT_License) license. - ->> NOTE: the music tracks included in this project are royalty free resources paid for and licensed -from [Lucky Lion Studios](http://luckylionstudios.com/). They are licensed ONLY for use in this -project and should not be reproduced. - ->> NOTE: the sprite graphics are placeholder graphics [borrowed](http://pixel.garoux.net/game/44) from the old -genesis version of outrun and used here as teaching examples. If there are any pixel artists out there who want to -provide original art to turn this into a real game please get in touch! - +[MIT](http://en.wikipedia.org/wiki/MIT_License) license including resources, except for some resources that are under creative commons or public domain (see thanks.txt). In any case, all assets are under permissive licenses allowing reuse and modification including for commercial purpose. diff --git a/common.css b/common.css index 5bb2d2b7..d0190d98 100644 --- a/common.css +++ b/common.css @@ -3,14 +3,15 @@ /* common styles used for v1 through v4 */ /****************************************/ -body { font-family: Arial, Helvetica, sans-serif; } -#stats { border: 2px solid black; } +body { font-family: Arial, Helvetica, sans-serif; background-color: black; color: white; } +a { color: magenta; } +#stats { border: 2px solid lime; } #controls { width: 28em; float: left; padding: 1em; font-size: 0.7em; } #controls th { text-align: right; vertical-align: middle; } -#instructions { clear: left; float: left; width: 17em; padding: 1em; border: 1px solid black; box-shadow: 0 0 5px black; } -#racer { position: relative; z-index: 0; width: 640px; height: 480px; margin-left: 20em; border: 2px solid black; } -#canvas { position: absolute; z-index: 0; width: 640px; height: 480px; z-index: 0; background-color: #72D7EE; } -#mute { background-position: 0px 0px; width: 32px; height: 32px; background: url(images/mute.png); display: inline-block; cursor: pointer; position: absolute; margin-left: 20em; } +#instructions { clear: left; float: left; width: 17em; padding: 1em; border: 1px solid black; box-shadow: 0 0 5px lime; } +#racer { position: relative; z-index: 0; width: 640px; height: 480px; margin-left: 20em; border: 2px solid magenta; } +#canvas { position: absolute; z-index: 0; width: 640px; height: 480px; z-index: 0; background-color: black; /* default color when drawing out of bounds (eg, we are up a high hill and the background is too low)*/ } +#mute { background-position: 0px 0px; width: 32px; height: 32px; background: url(images/mute.png); display: inline-block; cursor: pointer; margin-left: 20em; } #mute.on { background-position: -32px 0px; } /**************************************************/ @@ -20,9 +21,87 @@ body { font-family: Arial, Helvetica, sans-serif; } #hud { position: absolute; z-index: 1; width: 640px; padding: 5px 0; font-family: Verdana, Geneva, sans-serif; font-size: 0.8em; background-color: rgba(255,0,0,0.4); color: black; border-bottom: 2px solid black; box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; } #hud .hud { background-color: rgba(255,255,255,0.6); padding: 5px; border: 1px solid black; margin: 0 5px; transition-property: background-color; transition-duration: 2s; -webkit-transition-property: background-color; -webkit-transition-duration: 2s; } #hud #speed { float: right; } -#hud #current_lap_time { float: left; } +#hud #current_lap_time, #hud #current_level, #hud #turbo_left { float: left; } #hud #last_lap_time { float: left; display: none; } -#hud #fast_lap_time { display: block; width: 12em; margin: 0 auto; text-align: center; transition-property: background-color; transition-duration: 2s; -webkit-transition-property: background-color; -webkit-transition-duration: 2s; } +#hud #fast_lap_time, #hud #remaining_time { display: block; width: 12em; margin: 0 auto; text-align: center; transition-property: background-color; transition-duration: 2s; -webkit-transition-property: background-color; -webkit-transition-duration: 2s; } #hud .value { color: black; font-weight: bold; } #hud .fastest { background-color: rgba(255,215,0,0.5); } +#hud .magenta { background-color: magenta; } + +@keyframes anim-heartbeat { + 0% {font-size: 175%;} + 25% {font-size: 150%;} + 50% {font-size: 125%;} + 100% {font-size: 100%;} +} + +.warninglow { + color: red; + font-weight: bold; + animation-name: anim-heartbeat; + animation-duration: 5s; + animation-iteration-count: infinite; +} + +/* Fullscreen mode (on double click, managed in JS */ +#canvas:fullscreen { + width: auto; + height: 100%; /* height is smaller than width, so resize by height and adapt width automatically */ +} +#canvas:-webkit-full-screen { + width: auto; + height: 100%; +} + +#canvas:-moz-full-screen { + width: auto; + height: 100%; +} + +#canvas:-ms-fullscreen { + width: auto; + height: auto; + margin: auto; +} + +/* Mobile gamepad */ +#gamepad { + clear: left; width: 17em; border: 1px solid black; box-shadow: 0 0 5px lime; + margin-left: 20em; + width: 640px; + height: 300px; + background: rgba(255,0,0,0.4); + position: relative; + text-align: center; + display: inline-block; +} +.gamepad-button { + background: grey; + width: 33%; + height: 50%; + display: block; + float: left; + + /* Disable selection of text for gamepad buttons (so that user can press without selecting the text) */ + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + /* + Introduced in IE 10. + See http://ie.microsoft.com/testdrive/HTML5/msUserSelect/ + */ + -ms-user-select: none; + user-select: none; +} +.gamepad-button span { + display: inline-block; + vertical-align: middle; +} +.gamepad-button:active { background: blue; } +#gamepad-turbo { } +#gamepad-up { } +#gamepad-left { clear: both; } +#gamepad-right { float: right; } +#gamepad-down { } +*/ diff --git a/common.js b/common.js index 7b2068d0..8afa9ba2 100644 --- a/common.js +++ b/common.js @@ -107,6 +107,8 @@ var Game = { // a modified version of the game loop from my previous boulderdas Game.setKeyListener(options.keys); + Game.setDivListener(options.keys); + var canvas = options.canvas, // canvas render target is provided by caller update = options.update, // method to update game logic is provided by caller render = options.render, // method to render the game is provided by caller @@ -157,6 +159,7 @@ var Game = { // a modified version of the game loop from my previous boulderdas //--------------------------------------------------------------------------- setKeyListener: function(keys) { + // Setup listeners on keys to activate functions var onkey = function(keyCode, mode) { var n, k; for(n = 0 ; n < keys.length ; n++) { @@ -175,6 +178,30 @@ var Game = { // a modified version of the game loop from my previous boulderdas //--------------------------------------------------------------------------- + setDivListener: function(keys) { + // Setup listeners on div to activate functions (for mobile devices) + var n, k; + for(n = 0 ; n < keys.length ; n++) { + k = keys[n] + if (k.div) { + elt = document.getElementById(k.div); + if (elt) { // if the specified div element does not exist, just skip (probably the gamepad is not coded in the html) + if (k.mode == 'up') { + elt.onmouseup = k.action; + elt.addEventListener('mouseup', k.action); + elt.addEventListener('touchend', k.action); + } else { + elt.onmousedown = k.action; // fallback for old devices + elt.addEventListener('mousedown', k.action); + elt.addEventListener('touchstart', k.action); + } + } + } + } + }, + + //--------------------------------------------------------------------------- + stats: function(parentId, id) { // construct mr.doobs FPS counter - along with friendly good/bad/ok message box var result = new Stats(); @@ -266,7 +293,7 @@ var Render = { //--------------------------------------------------------------------------- - background: function(ctx, background, width, height, layer, rotation, offset) { + background: function(ctx, background, width, height, layer, rotation, offset, alpha) { rotation = rotation || 0; offset = offset || 0; @@ -284,9 +311,15 @@ var Render = { var destW = Math.floor(width * (sourceW/imageW)); var destH = height; + ctx.save(); // save the current drawing parameters + ctx.globalAlpha = alpha; // change alpha for next drawing + ctx.drawImage(background, sourceX, sourceY, sourceW, sourceH, destX, destY, destW, destH); if (sourceW < imageW) ctx.drawImage(background, layer.x, sourceY, imageW-sourceW, sourceH, destW-1, destY, width-destW, destH); + + // restore previous alpha and drawing parameters + ctx.restore() }, //--------------------------------------------------------------------------- @@ -350,23 +383,28 @@ var KEY = { A: 65, D: 68, S: 83, - W: 87 + W: 87, + SPACE: 32, + CTRL: 17, }; var COLORS = { - SKY: '#72D7EE', - TREE: '#005108', - FOG: '#005108', - LIGHT: { road: '#6B6B6B', grass: '#10AA10', rumble: '#555555', lane: '#CCCCCC' }, - DARK: { road: '#696969', grass: '#009A00', rumble: '#BBBBBB' }, - START: { road: 'white', grass: 'white', rumble: 'white' }, - FINISH: { road: 'black', grass: 'black', rumble: 'black' } + SKY: 'black', + TREE: 'cyan', + FOG: 'magenta', + LIGHT: { road: 'black', grass: 'purple', rumble: 'cyan', lane: 'cyan' }, + DARK: { road: 'black', grass: 'black', rumble: 'cyan' }, + START: { road: 'magenta', grass: 'magenta', rumble: 'magenta' }, + FINISH: { road: 'cyan', grass: 'cyan', rumble: 'cyan' } }; var BACKGROUND = { HILLS: { x: 5, y: 5, w: 1280, h: 480 }, SKY: { x: 5, y: 495, w: 1280, h: 480 }, - TREES: { x: 5, y: 985, w: 1280, h: 480 } + TREES: { x: 5, y: 985, w: 1280, h: 480 }, + HILLS2: { x: 5, y: 1475, w: 1280, h: 480 }, + SKY2: { x: 5, y: 1965, w: 1280, h: 480 }, + TREES2: { x: 5, y: 2445, w: 1280, h: 480 }, }; var SPRITES = { diff --git a/images/background.png b/images/background.png index 6590b026..ce592811 100644 Binary files a/images/background.png and b/images/background.png differ diff --git a/images/background.xcf b/images/background.xcf new file mode 100644 index 00000000..1dd2d1c5 Binary files /dev/null and b/images/background.xcf differ diff --git a/images/background/background.svg b/images/background/background.svg index 0bbcd8d6..1d7a33a9 100644 --- a/images/background/background.svg +++ b/images/background/background.svg @@ -2,6 +2,7 @@ + id="svg3147" + height="480" + width="1280"> + + + + + id="linearGradient3063"> + id="stop3059" /> + id="stop3061" /> + id="linearGradient6258"> + id="stop6254" /> + id="stop6256" /> + id="linearGradient6252"> + id="stop6248" /> + id="stop6250" /> + + + + + + + + + + + + + + + @@ -87,95 +163,6 @@ offset="1" id="stop4926" /> - - - - - - - - - - - - - - - - + + gradientTransform="matrix(0.99892656,0,0,1.0000017,0.78017874,-4.2463413e-4)" /> + + + + + + - @@ -299,7 +327,7 @@ image/svg+xml - + @@ -311,1611 +339,1226 @@ style="display:inline" sodipodi:insensitive="true"> - - - - + + + + sodipodi:nodetypes="ccccccccccccccccccccccccccccccc" /> - + style="fill:url(#linearGradient6260);fill-opacity:1;stroke:url(#linearGradient6246);stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + d="m 1278.7352,230.4301 c 0,0 -1277.74576329,0 -1277.74576329,0 M 1279.7433,230.99494 c 0,0 -1279.84671126,0 -1279.84671126,0 M 1280.7548,231.56164 c 0,0 -1281.9546591,0 -1281.9546591,0 M 1281.7695,232.13021 c 0,0 -1284.0694245,0 -1284.0694245,0 M 1282.7877,232.70066 c 0,0 -1286.1913254,0 -1286.1913254,0 M 1283.8092,233.27299 c 0,0 -1288.3201799,0 -1288.3201799,0 M 1284.8341,233.84723 c 0,0 -1290.4561062,0 -1290.4561062,0 M 1285.8623,234.42337 c 0,0 -1292.5990228,0 -1292.5990228,0 M 1286.8941,235.00143 c 0,0 -1294.7492476,0 -1294.7492476,0 M 1287.9292,235.58141 c 0,0 -1296.9064994,0 -1296.9064994,0 M 1288.9678,236.16333 c 0,0 -1299.070997,0 -1299.070997,0 M 1290.0099,236.7472 c 0,0 -1301.242759,0 -1301.242759,0 m 1302.288359,0.58582 c 0,0 -1303.421804,0 -1303.421804,0 m 1304.470804,0.58779 c 0,0 -1305.608052,0 -1305.608052,0 m 1306.660652,0.58976 c 0,0 -1307.801722,0 -1307.801722,0 m 1308.857922,0.59175 c 0,0 -1310.002832,0 -1310.002832,0 M 1295.273,239.69606 c 0,0 -1312.211303,0 -1312.211303,0 m 1313.274603,0.59575 c 0,0 -1314.427253,0 -1314.427253,0 m 1315.494053,0.59776 c 0,0 -1316.650603,0 -1316.650603,0 m 1317.721103,0.59979 c 0,0 -1318.881573,0 -1318.881573,0 m 1319.955773,0.60183 c 0,0 -1321.120182,0 -1321.120182,0 m 1322.197882,0.60387 c 0,0 -1323.366249,0 -1323.366249,0 M 1301.707,243.30099 c 0,0 -1325.620098,0 -1325.620098,0 m 1326.705198,0.608 c 0,0 -1327.881545,0 -1327.881545,0 M 1303.881,244.51906 c 0,0 -1330.150812,0 -1330.150812,0 m 1331.243412,0.61216 c 0,0 -1332.427823,0 -1332.427823,0 m 1333.524123,0.61426 c 0,0 -1334.712594,0 -1334.712594,0 M 1307.17,246.36186 c 0,0 -1337.005249,0 -1337.005249,0 m 1338.109149,0.61849 c 0,0 -1339.305807,0 -1339.305807,0 m 1340.413407,0.62063 c 0,0 -1341.614192,0 -1341.614192,0 m 1342.725792,0.62277 c 0,0 -1343.930721,0 -1343.930721,0 m 1345.046021,0.62492 c 0,0 -1346.255118,0 -1346.255118,0 m 1347.374318,0.62709 c 0,0 -1348.587607,0 -1348.587607,0 m 1349.710707,0.62926 c 0,0 -1350.928206,0 -1350.928206,0 m 1352.055206,0.63146 c 0,0 -1353.27694,0 -1353.27694,0 m 1354.40794,0.63365 c 0,0 -1355.633928,0 -1355.633928,0 m 1356.768828,0.63586 c 0,0 -1357.999093,0 -1357.999093,0 m 1359.137893,0.63809 c 0,0 -1360.372461,0 -1360.372461,0 M 1319.5252,253.2844 c 0,0 -1362.75415,0 -1362.75415,0 m 1363.90105,0.64257 c 0,0 -1365.144287,0 -1365.144287,0 M 1321.823,254.57179 c 0,0 -1367.54279,0 -1367.54279,0 m 1368.69769,0.6471 c 0,0 -1369.949687,0 -1369.949687,0 m 1371.108687,0.64937 c 0,0 -1372.365097,0 -1372.365097,0 M 1325.3,256.51993 c 0,0 -1374.789045,0 -1374.789045,0 m 1375.956245,0.65398 c 0,0 -1377.221557,0 -1377.221557,0 m 1378.392857,0.6563 c 0,0 -1379.662652,0 -1379.662652,0 M 1328.814,258.48883 c 0,0 -1382.112459,0 -1382.112459,0 M 1329.9937,259.1498 c 0,0 -1384.570997,0 -1384.570997,0 m 1385.754897,0.66333 c 0,0 -1387.038296,0 -1387.038296,0 m 1388.226396,0.66569 c 0,0 -1389.514374,0 -1389.514374,0 m 1390.706774,0.66807 c 0,0 -1391.999359,0 -1391.999359,0 m 1393.195959,0.67047 c 0,0 -1394.493178,0 -1394.493178,0 m 1395.694178,0.67287 c 0,0 -1396.99605,0 -1396.99605,0 m 1398.20125,0.6753 c 0,0 -1399.507807,0 -1399.507807,0 m 1400.717407,0.67772 c 0,0 -1402.028668,0 -1402.028668,0 m 1403.242668,0.68018 c 0,0 -1404.558662,0 -1404.558662,0 m 1405.776962,0.68263 c 0,0 -1407.097716,0 -1407.097716,0 m 1408.320516,0.6851 c 0,0 -1409.646051,0 -1409.646051,0 m 1410.873251,0.68759 c 0,0 -1412.203599,0 -1412.203599,0 m 1413.435299,0.69009 c 0,0 -1414.770481,0 -1414.770481,0 m 1416.006581,0.69261 c 0,0 -1417.346627,0 -1417.346627,0 m 1418.587327,0.69513 c 0,0 -1419.93226,0 -1419.93226,0 m 1421.17746,0.69767 c 0,0 -1422.527309,0 -1422.527309,0 m 1423.777009,0.70022 c 0,0 -1425.131804,0 -1425.131804,0 m 1426.386104,0.7028 c 0,0 -1427.745866,0 -1427.745866,0 m 1429.004866,0.70537 c 0,0 -1430.369628,0 -1430.369628,0 m 1431.633228,0.70798 c 0,0 -1433.003013,0 -1433.003013,0 M 1354.5013,272.8812 c 0,0 -1435.646051,0 -1435.646051,0 m 1436.919051,0.71322 c 0,0 -1438.298972,0 -1438.298972,0 m 1439.576572,0.71585 c 0,0 -1440.9616,0 -1440.9616,0 m 1442.244,0.71851 c 0,0 -1443.634168,0 -1443.634168,0 m 1444.921268,0.72118 c 0,0 -1446.3166,0 -1446.3166,0 m 1447.6086,0.72386 c 0,0 -1449.00913,0 -1449.00913,0 m 1450.30583,0.72657 c 0,0 -1451.711581,0 -1451.711581,0 m 1453.013181,0.72927 c 0,0 -1454.424186,0 -1454.424186,0 m 1455.730686,0.73202 c 0,0 -1457.146976,0 -1457.146976,0 m 1458.458376,0.73475 c 0,0 -1459.879976,0 -1459.879976,0 m 1461.196276,0.73752 c 0,0 -1462.623222,0 -1462.623222,0 m 1463.944522,0.74029 c 0,0 -1465.376837,0 -1465.376837,0 m 1466.703037,0.74309 c 0,0 -1468.140756,0 -1468.140756,0 m 1469.472056,0.74589 c 0,0 -1470.915212,0 -1470.915212,0 M 1372.761,283.11194 c 0,0 -1473.70013,0 -1473.70013,0 m 1475.04153,0.75156 c 0,0 -1476.49565,0 -1476.49565,0 m 1477.84205,0.75442 c 0,0 -1479.30169,0 -1479.30169,0 m 1480.65329,0.75729 c 0,0 -1482.11849,0 -1482.11849,0 m 1483.47529,0.76018 c 0,0 -1484.94608,0 -1484.94608,0 m 1486.30798,0.76308 c 0,0 -1487.78439,0 -1487.78439,0 m 1489.15159,0.76601 c 0,0 -1490.63366,0 -1490.63366,0 m 1492.00606,0.76895 c 0,0 -1493.49381,0 -1493.49381,0 m 1494.87151,0.7719 c 0,0 -1496.36499,0 -1496.36499,0 m 1497.74799,0.77488 c 0,0 -1499.24722,0 -1499.24722,0 m 1500.63552,0.77787 c 0,0 -1502.14054,0 -1502.14054,0 m 1503.53424,0.78088 c 0,0 -1505.04507,0 -1505.04507,0 m 1506.44417,0.7839 c 0,0 -1507.96086,0 -1507.96086,0 M 1390.605,293.1098 c 0,0 -1510.88794,0 -1510.88794,0 m 1512.29794,0.79001 c 0,0 -1513.82644,0 -1513.82644,0 m 1515.24194,0.79309 c 0,0 -1516.7764,0 -1516.7764,0 m 1518.1974,0.79618 c 0,0 -1519.73785,0 -1519.73785,0 m 1521.16445,0.79929 c 0,0 -1522.71093,0 -1522.71093,0 m 1524.14313,0.80244 c 0,0 -1525.69567,0 -1525.69567,0 m 1527.13337,0.80558 c 0,0 -1528.692,0 -1528.692,0 m 1530.1355,0.80875 c 0,0 -1531.70028,0 -1531.70028,0 m 1533.14938,0.81195 c 0,0 -1534.72032,0 -1534.72032,0 m 1536.17522,0.81515 c 0,0 -1537.75237,0 -1537.75237,0 m 1539.21297,0.81837 c 0,0 -1540.79636,0 -1540.79636,0 m 1542.26286,0.82163 c 0,0 -1543.85254,0 -1543.85254,0 m 1545.32474,0.82489 c 0,0 -1546.92073,0 -1546.92073,0 m 1548.39883,0.82818 c 0,0 -1550.00118,0 -1550.00118,0 m 1551.48518,0.83148 c 0,0 -1553.09393,0 -1553.09393,0 m 1554.58393,0.8348 c 0,0 -1556.19911,0 -1556.19911,0 m 1557.69501,0.83815 c 0,0 -1559.31665,0 -1559.31665,0 m 1560.81855,0.84152 c 0,0 -1562.44671,0 -1562.44671,0 m 1563.95471,0.8449 c 0,0 -1565.58942,0 -1565.58942,0 m 1567.10342,0.8483 c 0,0 -1568.74472,0 -1568.74472,0 m 1570.26492,0.85174 c 0,0 -1571.91285,0 -1571.91285,0 m 1573.43915,0.85518 c 0,0 -1575.09375,0 -1575.09375,0 m 1576.62625,0.85864 c 0,0 -1578.28755,0 -1578.28755,0 m 1579.82625,0.86214 c 0,0 -1581.49431,0 -1581.49431,0 m 1583.03931,0.86565 c 0,0 -1584.71416,0 -1584.71416,0 m 1586.26546,0.86918 c 0,0 -1587.94714,0 -1587.94714,0 m 1589.50484,0.87273 c 0,0 -1591.1934,0 -1591.1934,0 m 1592.7574,0.87631 c 0,0 -1594.45288,0 -1594.45288,0 m 1596.02328,0.8799 c 0,0 -1597.72572,0 -1597.72572,0 m 1599.30262,0.88353 c 0,0 -1601.01206,0 -1601.01206,0 m 1602.59556,0.88717 c 0,0 -1604.31205,0 -1604.31205,0 m 1605.90195,0.89083 c 0,0 -1607.62553,0 -1607.62553,0 m 1609.22203,0.89452 c 0,0 -1610.95274,0 -1610.95274,0 m 1612.55594,0.89823 c 0,0 -1614.29383,0 -1614.29383,0 m 1615.90363,0.90196 c 0,0 -1617.64875,0 -1617.64875,0 m 1619.26525,0.90572 c 0,0 -1621.01763,0 -1621.01763,0 m 1622.64093,0.90951 c 0,0 -1624.40063,0 -1624.40063,0 m 1626.03063,0.9133 c 0,0 -1627.79768,0 -1627.79768,0 m 1629.43458,0.91713 c 0,0 -1631.20905,0 -1631.20905,0 m 1632.85285,0.92099 c 0,0 -1634.63477,0 -1634.63477,0 m 1636.28537,0.92486 c 0,0 -1638.07479,0 -1638.07479,0 m 1639.73249,0.92876 c 0,0 -1641.52945,0 -1641.52945,0 m 1643.19405,0.93269 c 0,0 -1644.99861,0 -1644.99861,0 m 1646.67031,0.93664 c 0,0 -1648.48252,0 -1648.48252,0 m 1650.16132,0.94062 c 0,0 -1651.98121,0 -1651.98121,0 M 1459.9925,331.987 c 0,0 -1655.49485,0 -1655.49485,0 m 1657.18795,0.94864 c 0,0 -1659.02338,0 -1659.02338,0 m 1660.72368,0.9527 c 0,0 -1662.56695,0 -1662.56695,0 m 1664.27465,0.95677 c 0,0 -1666.12581,0 -1666.12581,0 m 1667.84071,0.96088 c 0,0 -1669.69982,0 -1669.69982,0 M 1468.5309,336.771 c 0,0 -1673.28931,0 -1673.28931,0 m 1675.01901,0.96917 c 0,0 -1676.89415,0 -1676.89415,0 m 1678.63145,0.97335 c 0,0 -1680.51469,0 -1680.51469,0 m 1682.25939,0.97757 c 0,0 -1684.15078,0 -1684.15078,0 m 1685.90308,0.9818 c 0,0 -1687.80267,0 -1687.80267,0 m 1689.56257,0.98608 c 0,0 -1691.47042,0 -1691.47042,0 m 1693.23802,0.99036 c 0,0 -1695.15417,0 -1695.15417,0 m 1696.92947,0.99469 c 0,0 -1698.854,0 -1698.854,0 m 1700.6371,0.99904 c 0,0 -1702.57003,0 -1702.57003,0 m 1704.36093,1.00342 c 0,0 -1706.30235,0 -1706.30235,0 m 1708.10115,1.00783 c 0,0 -1710.05109,0 -1710.05109,0 m 1711.85769,1.01227 c 0,0 -1713.81623,0 -1713.81623,0 m 1715.63093,1.01674 c 0,0 -1717.5981,0 -1717.5981,0 m 1719.4208,1.02123 c 0,0 -1721.39668,0 -1721.39668,0 m 1723.22738,1.02576 c 0,0 -1725.21202,0 -1725.21202,0 m 1727.05092,1.03032 c 0,0 -1729.04437,0 -1729.04437,0 m 1730.89147,1.0349 c 0,0 -1732.89379,0 -1732.89379,0 m 1734.74909,1.03953 c 0,0 -1736.76036,0 -1736.76036,0 m 1738.62406,1.04417 c 0,0 -1740.64432,0 -1740.64432,0 m 1742.51622,1.04885 c 0,0 -1744.54554,0 -1744.54554,0 m 1746.42594,1.05357 c 0,0 -1748.46437,0 -1748.46437,0 m 1750.35327,1.05831 c 0,0 -1752.40088,0 -1752.40088,0 m 1754.29828,1.06308 c 0,0 -1756.35513,0 -1756.35513,0 m 1758.26103,1.0679 c 0,0 -1760.32719,0 -1760.32719,0 m 1762.24179,1.07273 c 0,0 -1764.31731,0 -1764.31731,0 m 1766.24061,1.07761 c 0,0 -1768.32556,0 -1768.32556,0 m 1770.25766,1.08252 c 0,0 -1772.35211,0 -1772.35211,0 m 1774.29301,1.08746 c 0,0 -1776.39702,0 -1776.39702,0 m 1778.34672,1.09243 c 0,0 -1780.46035,0 -1780.46035,0 m 1782.41905,1.09744 c 0,0 -1784.54238,0 -1784.54238,0 m 1786.51008,1.10249 c 0,0 -1788.64316,0 -1788.64316,0 m 1790.61996,1.10756 c 0,0 -1792.76287,0 -1792.76287,0 m 1794.74877,1.11268 c 0,0 -1796.90158,0 -1796.90158,0 m 1798.89668,1.11783 c 0,0 -1801.05945,0 -1801.05945,0 m 1803.06375,1.12301 c 0,0 -1805.23654,0 -1805.23654,0 m 1807.25024,1.12823 c 0,0 -1809.43314,0 -1809.43314,0 m 1811.45614,1.13349 c 0,0 -1813.64922,0 -1813.64922,0 m 1815.68172,1.13879 c 0,0 -1817.88503,0 -1817.88503,0 m 1819.92703,1.14412 c 0,0 -1822.14067,0 -1822.14067,0 m 1824.19227,1.14948 c 0,0 -1826.41629,0 -1826.41629,0 m 1828.47749,1.1549 c 0,0 -1830.71197,0 -1830.71197,0 m 1832.78297,1.16034 c 0,0 -1835.02799,0 -1835.02799,0 m 1837.10869,1.16582 c 0,0 -1839.36432,0 -1839.36432,0 m 1841.45492,1.17135 c 0,0 -1843.72124,0 -1843.72124,0 m 1845.82174,1.17691 c 0,0 -1848.09881,0 -1848.09881,0 m 1850.20941,1.18251 c 0,0 -1852.49733,0 -1852.49733,0 m 1854.61793,1.18815 c 0,0 -1856.91676,0 -1856.91676,0 m 1859.04746,1.19383 c 0,0 -1861.35729,0 -1861.35729,0 m 1863.49829,1.19956 c 0,0 -1865.81919,0 -1865.81919,0 m 1867.97039,1.20533 c 0,0 -1870.30244,0 -1870.30244,0 m 1872.46404,1.21113 c 0,0 -1874.80733,0 -1874.80733,0 m 1876.97943,1.21698 c 0,0 -1879.33404,0 -1879.33404,0 m 1881.51654,1.22287 c 0,0 -1883.88254,0 -1883.88254,0 m 1886.07574,1.2288 c 0,0 -1888.45322,0 -1888.45322,0 m 1890.65702,1.23478 c 0,0 -1893.04607,0 -1893.04607,0 m 1895.26057,1.2408 c 0,0 -1897.66127,0 -1897.66127,0 m 1899.88667,1.24687 c 0,0 -1902.29909,0 -1902.29909,0 m 1904.53539,1.25298 c 0,0 -1906.95965,0 -1906.95965,0 m 1909.20695,1.25912 c 0,0 -1911.64309,0 -1911.64309,0 m 1913.90149,1.26534 c 0,0 -1916.34968,0 -1916.34968,0 m 1918.61908,1.27157 c 0,0 -1921.0793,0 -1921.0793,0 m 1923.36,1.27786 c 0,0 -1925.8324,0 -1925.8324,0 m 1928.1245,1.28423 c 0,0 -1930.60921,0 -1930.60921,0 m 1932.91261,1.29058 c 0,0 -1935.40963,0 -1935.40963,0 m 1937.72453,1.29702 c 0,0 -1940.23399,0 -1940.23399,0 m 1942.56049,1.30353 c 0,0 -1945.08255,0 -1945.08255,0 M 1601.285,411.152 c 0,0 -1949.9554,0 -1949.9554,0 m 1952.3052,1.31661 c 0,0 -1954.85257,0 -1954.85257,0 m 1957.21437,1.32327 c 0,0 -1959.77463,0 -1959.77463,0 m 1962.14823,1.32993 c 0,0 -1964.72136,0 -1964.72136,0 m 1967.10706,1.33665 c 0,0 -1969.69321,0 -1969.69321,0 m 1972.09101,1.34346 c 0,0 -1974.69033,0 -1974.69033,0 m 1977.10023,1.35027 c 0,0 -1979.71273,0 -1979.71273,0 m 1982.13493,1.35716 c 0,0 -1984.76075,0 -1984.76075,0 m 1987.19545,1.36412 c 0,0 -1989.83474,0 -1989.83474,0 m 1992.28184,1.37109 c 0,0 -1994.93461,0 -1994.93461,0 m 1997.39431,1.37813 c 0,0 -2000.06072,0 -2000.06072,0 m 2002.53312,1.38526 c 0,0 -2005.21332,0 -2005.21332,0 m 2007.69842,1.39239 c 0,0 -2010.39241,0 -2010.39241,0 m 2012.89041,1.3996 c 0,0 -2015.59835,0 -2015.59835,0 m 2018.10935,1.4069 c 0,0 -2020.8314,0 -2020.8314,0 m 2023.3554,1.41419 c 0,0 -2026.09158,0 -2026.09158,0 m 2028.62878,1.4216 c 0,0 -2031.37929,0 -2031.37929,0 m 2033.92979,1.42902 c 0,0 -2036.69463,0 -2036.69463,0 m 2039.25853,1.43651 c 0,0 -2042.03789,0 -2042.03789,0 m 2044.61529,1.4441 c 0,0 -2047.40932,0 -2047.40932,0 m 2050.00022,1.45168 c 0,0 -2052.80894,0 -2052.80894,0 m 2055.41364,1.45937 c 0,0 -2058.2372,0 -2058.2372,0 m 2060.8557,1.46713 c 0,0 -2063.6943,0 -2063.6943,0 m 2066.3267,1.4749 c 0,0 -2069.18034,0 -2069.18034,0 m 2071.82674,1.48276 c 0,0 -2074.69558,0 -2074.69558,0 m 2077.35618,1.49072 c 0,0 -2080.24042,0 -2080.24042,0 m 2082.91522,1.49868 c 0,0 -2085.81485,0 -2085.81485,0 m 2088.50405,1.50673 c 0,0 -2091.41927,0 -2091.41927,0 m 2094.12307,1.51489 c 0,0 -2097.05406,0 -2097.05406,0 m 2099.77236,1.52303 c 0,0 -2102.71912,0 -2102.71912,0 m 2105.45212,1.53129 c 0,0 -2108.41485,0 -2108.41485,0 m 2111.16275,1.53964 c 0,0 -2114.14163,0 -2114.14163,0 m 2116.90443,1.54799 c 0,0 -2119.89948,0 -2119.89948,0 m 2122.67748,1.55645 c 0,0 -2125.68889,0 -2125.68889,0 m 2128.48209,1.565 c 0,0 -2131.51004,0 -2131.51004,0 m 2134.31844,1.57357 c 0,0 -2137.36297,0 -2137.36297,0 m 2140.18697,1.58223 c 0,0 -2143.24827,0 -2143.24827,0 m 2146.08787,1.591 c 0,0 -2149.16613,0 -2149.16613,0 m 2152.02133,1.59978 c 0,0 -2155.11657,0 -2155.11657,0 m 2157.98777,1.60866 c 0,0 -2161.10021,0 -2161.10021,0 m 2163.98731,1.61765 c 0,0 -2167.11713,0 -2167.11713,0 M 1708.39,471.16207 c 0,0 -2173.16758,0 -2173.16758,0 m 2176.08708,1.63576 c 0,0 -2179.25194,0 -2179.25194,0 m 2182.18794,1.64498 c 0,0 -2185.37064,0 -2185.37064,0 m 2188.32304,1.65421 c 0,0 -2191.5236,0 -2191.5236,0 m 2194.4927,1.66355 c 0,0 -2197.71133,0 -2197.71133,0 m 2200.69723,1.67301 c 0,0 -2203.93415,0 -2203.93415,0 M 1278.7352,230.4301 c 0,0 444.4177,249.00348 444.4177,249.00348 M 1265.4254,230.4301 c 0,0 434.7699,249.00348 434.7699,249.00348 M 1252.1155,230.4301 c 0,0 425.1221,249.00348 425.1221,249.00348 M 1238.8057,230.4301 c 0,0 415.4743,249.00348 415.4743,249.00348 M 1225.4958,230.4301 c 0,0 405.8266,249.00348 405.8266,249.00348 M 1212.1859,230.4301 c 0,0 396.1787,249.00348 396.1787,249.00348 M 1198.8761,230.4301 c 0,0 386.5309,249.00348 386.5309,249.00348 M 1185.5662,230.4301 c 0,0 376.8831,249.00348 376.8831,249.00348 M 1172.2564,230.4301 c 0,0 367.2354,249.00348 367.2354,249.00348 M 1158.9466,230.4301 c 0,0 357.5875,249.00348 357.5875,249.00348 M 1145.6367,230.4301 c 0,0 347.9397,249.00348 347.9397,249.00348 M 1132.3268,230.4301 c 0,0 338.292,249.00348 338.292,249.00348 M 1119.017,230.4301 c 0,0 328.6441,249.00348 328.6441,249.00348 M 1105.7072,230.4301 c 0,0 318.9963,249.00348 318.9963,249.00348 M 1092.3973,230.4301 c 0,0 309.3485,249.00348 309.3485,249.00348 M 1079.0875,230.4301 c 0,0 299.7007,249.00348 299.7007,249.00348 M 1065.7776,230.4301 c 0,0 290.053,249.00348 290.053,249.00348 M 1052.4677,230.4301 c 0,0 280.4052,249.00348 280.4052,249.00348 M 1039.1579,230.4301 c 0,0 270.7575,249.00348 270.7575,249.00348 M 1025.848,230.4301 c 0,0 261.1096,249.00348 261.1096,249.00348 M 1012.5382,230.4301 c 0,0 251.4618,249.00348 251.4618,249.00348 M 999.22835,230.4301 c 0,0 241.81395,249.00348 241.81395,249.00348 M 985.91851,230.4301 c 0,0 232.16619,249.00348 232.16619,249.00348 M 972.60861,230.4301 c 0,0 222.51839,249.00348 222.51839,249.00348 M 959.29875,230.4301 c 0,0 212.87055,249.00348 212.87055,249.00348 M 945.98896,230.4301 c 0,0 203.22284,249.00348 203.22284,249.00348 M 932.67906,230.4301 c 0,0 193.57504,249.00348 193.57504,249.00348 M 919.36919,230.4301 c 0,0 183.92721,249.00348 183.92721,249.00348 M 906.05935,230.4301 c 0,0 174.27945,249.00348 174.27945,249.00348 M 892.74951,230.4301 c 0,0 164.63159,249.00348 164.63159,249.00348 M 879.43967,230.4301 c 0,0 154.98383,249.00348 154.98383,249.00348 M 866.1298,230.4301 c 0,0 145.336,249.00348 145.336,249.00348 M 852.81996,230.4301 c 0,0 135.68823,249.00348 135.68823,249.00348 M 839.51012,230.4301 c 0,0 126.04044,249.00348 126.04044,249.00348 M 826.20025,230.4301 c 0,0 116.39264,249.00348 116.39264,249.00348 M 812.89041,230.4301 c 0,0 106.74485,249.00348 106.74485,249.00348 M 799.58051,230.4301 c 0,0 97.09702,249.00348 97.09702,249.00348 M 786.27075,230.4301 c 0,0 87.44929,249.00348 87.44929,249.00348 M 772.96086,230.4301 c 0,0 77.80146,249.00348 77.80146,249.00348 M 759.65102,230.4301 c 0,0 68.15367,249.00348 68.15367,249.00348 M 746.34115,230.4301 c 0,0 58.50587,249.00348 58.50587,249.00348 M 733.03131,230.4301 c 0,0 48.85808,249.00348 48.85808,249.00348 M 719.72147,230.4301 c 0,0 39.21029,249.00348 39.21029,249.00348 M 706.41162,230.4301 c 0,0 29.56251,249.00348 29.56251,249.00348 M 693.1017,230.4301 c 0,0 19.91466,249.00348 19.91466,249.00348 M 679.79191,230.4301 c 0,0 10.26691,249.00348 10.26691,249.00348 M 666.48202,230.4301 c 0,0 0.61908,249.00348 0.61908,249.00348 M 653.17226,230.4301 c 0,0 -9.02865,249.00348 -9.02865,249.00348 m -4.2813,-249.00348 c 0,0 -18.67651,249.00348 -18.67651,249.00348 m 5.36667,-249.00348 c 0,0 -28.3243,249.00348 -28.3243,249.00348 M 613.24265,230.4301 c 0,0 -37.97207,249.00348 -37.97207,249.00348 M 599.93276,230.4301 c 0,0 -47.6199,249.00348 -47.6199,249.00348 M 586.62292,230.4301 c 0,0 -57.26769,249.00348 -57.26769,249.00348 M 573.31305,230.4301 c 0,0 -66.91549,249.00348 -66.91549,249.00348 m 53.6057,-249.00348 c 0,0 -76.56324,249.00348 -76.56324,249.00348 M 546.69337,230.4301 c 0,0 -86.21107,249.00348 -86.21107,249.00348 M 533.38347,230.4301 c 0,0 -95.85889,249.00348 -95.85889,249.00348 M 520.07366,230.4301 c 0,0 -105.50667,249.00348 -105.50667,249.00348 M 506.76381,230.4301 c 0,0 -115.15445,249.00348 -115.15445,249.00348 M 493.45397,230.4301 c 0,0 -124.80224,249.00348 -124.80224,249.00348 M 480.14413,230.4301 c 0,0 -134.45003,249.00348 -134.45003,249.00348 M 466.83426,230.4301 c 0,0 -144.09783,249.00348 -144.09783,249.00348 M 453.52442,230.4301 c 0,0 -153.74562,249.00348 -153.74562,249.00348 M 440.21453,230.4301 c 0,0 -163.39345,249.00348 -163.39345,249.00348 M 426.90477,230.4301 c 0,0 -173.04118,249.00348 -173.04118,249.00348 M 413.59482,230.4301 c 0,0 -182.68905,249.00348 -182.68905,249.00348 M 400.28503,230.4301 c 0,0 -192.3368,249.00348 -192.3368,249.00348 M 386.97516,230.4301 c 0,0 -201.9846,249.00348 -201.9846,249.00348 M 373.66532,230.4301 c 0,0 -211.63239,249.00348 -211.63239,249.00348 M 360.35542,230.4301 c 0,0 -221.28021,249.00348 -221.28021,249.00348 M 347.04561,230.4301 c 0,0 -230.92799,249.00348 -230.92799,249.00348 M 333.73577,230.4301 c 0,0 -240.575775,249.00348 -240.575775,249.00348 M 320.42587,230.4301 c 0,0 -250.223596,249.00348 -250.223596,249.00348 M 307.11601,230.4301 c 0,0 -259.871412,249.00348 -259.871412,249.00348 M 293.80616,230.4301 c 0,0 -269.519191,249.00348 -269.519191,249.00348 M 280.49632,230.4301 c 0,0 -279.1669809,249.00348 -279.1669809,249.00348 M 267.18648,230.4301 c 0,0 -288.81477,249.00348 -288.81477,249.00348 M 253.87667,230.4301 c 0,0 -298.462544,249.00348 -298.462544,249.00348 M 240.56677,230.4301 c 0,0 -308.110365,249.00348 -308.110365,249.00348 M 227.25693,230.4301 c 0,0 -317.758155,249.00348 -317.758155,249.00348 M 213.94706,230.4301 c 0,0 -327.40596,249.00348 -327.40596,249.00348 M 200.63727,230.4301 c 0,0 -337.05371,249.00348 -337.05371,249.00348 M 187.32732,230.4301 c 0,0 -346.70157,249.00348 -346.70157,249.00348 M 174.01754,230.4301 c 0,0 -356.34933,249.00348 -356.34933,249.00348 M 160.70767,230.4301 c 0,0 -365.99713,249.00348 -365.99713,249.00348 M 147.39783,230.4301 c 0,0 -375.64492,249.00348 -375.64492,249.00348 M 134.08793,230.4301 c 0,0 -385.29275,249.00348 -385.29275,249.00348 M 120.77812,230.4301 c 0,0 -394.94052,249.00348 -394.94052,249.00348 M 107.46828,230.4301 c 0,0 -404.58831,249.00348 -404.58831,249.00348 M 94.158434,230.4301 c 0,0 -414.236094,249.00348 -414.236094,249.00348 M 80.848566,230.4301 c 0,0 -423.883896,249.00348 -423.883896,249.00348 M 67.538724,230.4301 c 0,0 -433.531684,249.00348 -433.531684,249.00348 M 54.228829,230.4301 c 0,0 -443.179509,249.00348 -443.179509,249.00348 M 40.919041,230.4301 c 0,0 -452.827261,249.00348 -452.827261,249.00348 M 27.60912,230.4301 c 0,0 -462.47511,249.00348 -462.47511,249.00348 M 14.299332,230.4301 c 0,0 -472.122862,249.00348 -472.122862,249.00348 M 0.98943671,230.4301 c 0,0 -481.77068671,249.00348 -481.77068671,249.00348" + style="fill:none;stroke:#ff00f0;stroke-width:0.99885041px;stroke-opacity:0" + id="path1074" + inkscape:connector-curvature="0" /> + id="path2135" + style="fill:none;stroke:#ff00ff;stroke-width:1px;stroke-opacity:1" + d="m 1278.7366,230.43015 c 0,0 -1277.74849826,0 -1277.74849826,0 M 1282.789,232.70071 c 0,0 -1286.1939666,0 -1286.1939666,0 M 1286.8954,235.00148 c 0,0 -1294.7518951,0 -1294.7518951,0 M 1291.0568,237.33307 c 0,0 -1303.424458,0 -1303.424458,0 m 1307.641958,2.36304 c 0,0 -1312.213963,0 -1312.213963,0 m 1316.488763,2.39513 c 0,0 -1321.122848,0 -1321.122848,0 m 1325.456148,2.42787 c 0,0 -1330.153587,0 -1330.153587,0 M 1308.2752,246.9804 c 0,0 -1339.308487,0 -1339.308487,0 M 1312.729,249.4758 c 0,0 -1348.590393,0 -1348.590393,0 M 1317.245,252.00604 c 0,0 -1358.001887,0 -1358.001887,0 m 1362.581287,2.56579 c 0,0 -1367.54559,0 -1367.54559,0 m 1372.18979,2.60212 c 0,0 -1377.224363,0 -1377.224363,0 M 1331.179,259.81316 c 0,0 -1387.041108,0 -1387.041108,0 m 1391.819208,2.67711 c 0,0 -1396.998871,0 -1396.998871,0 m 1401.846071,2.71582 c 0,0 -1407.100643,0 -1407.100643,0 m 1412.018443,2.75539 c 0,0 -1417.34956,0 -1417.34956,0 M 1350.712,270.7573 c 0,0 -1427.748808,0 -1427.748808,0 m 1432.812508,2.83715 c 0,0 -1438.30182,0 -1438.30182,0 m 1443.44092,2.8794 c 0,0 -1449.011984,0 -1449.011984,0 m 1454.228284,2.92261 c 0,0 -1459.882939,0 -1459.882939,0 m 1465.178039,2.96679 c 0,0 -1470.918181,0 -1470.918181,0 m 1476.293881,3.01198 c 0,0 -1482.12146,0 -1482.12146,0 m 1487.57976,3.05822 c 0,0 -1493.4968,0 -1493.4968,0 m 1499.0395,3.10553 c 0,0 -1505.04807,0 -1505.04807,0 m 1510.67717,3.15393 c 0,0 -1516.7794,0 -1516.7794,0 m 1522.497,3.2035 c 0,0 -1528.69511,0 -1528.69511,0 m 1534.50321,3.25422 c 0,0 -1540.79948,0 -1540.79948,0 m 1546.70028,3.30617 c 0,0 -1553.09705,0 -1553.09705,0 m 1559.09275,3.35937 c 0,0 -1565.59245,0 -1565.59245,0 m 1571.68555,3.41386 c 0,0 -1578.29069,0 -1578.29069,0 m 1584.48329,3.46969 c 0,0 -1591.19645,0 -1591.19645,0 m 1597.49125,3.52691 c 0,0 -1604.3151,0 -1604.3151,0 m 1610.7146,3.58554 c 0,0 -1617.65191,0 -1617.65191,0 m 1624.15861,3.64566 c 0,0 -1631.21222,0 -1631.21222,0 m 1637.82892,3.7073 c 0,0 -1645.00179,0 -1645.00179,0 m 1651.73139,3.77051 c 0,0 -1659.02657,0 -1659.02657,0 m 1665.87187,3.83535 c 0,0 -1673.29251,0 -1673.29251,0 m 1680.25651,3.90189 c 0,0 -1687.80587,0 -1687.80587,0 m 1694.89177,3.97016 c 0,0 -1702.57324,0 -1702.57324,0 m 1709.78424,4.04026 c 0,0 -1717.60132,0 -1717.60132,0 m 1724.94082,4.11221 c 0,0 -1732.89712,0 -1732.89712,0 m 1740.36842,4.18611 c 0,0 -1748.4677,0 -1748.4677,0 m 1756.0745,4.26202 c 0,0 -1764.32065,0 -1764.32065,0 m 1772.06665,4.34001 c 0,0 -1780.4637,0 -1780.4637,0 m 1788.3528,4.42017 c 0,0 -1796.90493,0 -1796.90493,0 m 1804.94103,4.50256 c 0,0 -1813.65258,0 -1813.65258,0 m 1821.83988,4.58728 c 0,0 -1830.71534,0 -1830.71534,0 m 1839.05814,4.67441 c 0,0 -1848.10219,0 -1848.10219,0 m 1856.60509,4.76406 c 0,0 -1865.82258,0 -1865.82258,0 m 1874.48998,4.8563 c 0,0 -1883.88594,0 -1883.88594,0 m 1892.72294,4.95124 c 0,0 -1902.3026,0 -1902.3026,0 m 1911.314,5.04901 c 0,0 -1921.08282,0 -1921.08282,0 m 1930.27392,5.14969 c 0,0 -1940.23753,0 -1940.23753,0 m 1949.61373,5.25342 c 0,0 -1959.77805,0 -1959.77805,0 m 1969.34515,5.36032 c 0,0 -1979.71627,0 -1979.71627,0 m 1989.47997,5.4705 c 0,0 -2000.06429,0 -2000.06429,0 m 2010.03069,5.58412 c 0,0 -2020.83485,0 -2020.83485,0 m 2031.01055,5.70133 c 0,0 -2042.04147,0 -2042.04147,0 m 2052.43297,5.82225 c 0,0 -2063.69785,0 -2063.69785,0 m 2074.31215,5.94707 c 0,0 -2085.81853,0 -2085.81853,0 m 2096.66273,6.07594 c 0,0 -2108.41846,0 -2108.41846,0 m 2119.50036,6.20905 c 0,0 -2131.51362,0 -2131.51362,0 m 2142.84092,6.34659 c 0,0 -2155.12028,0 -2155.12028,0 m 2166.70128,6.48873 c 0,0 -2179.25568,0 -2179.25568,0 m 2191.09908,6.63572 c 0,0 -2203.93785,0 -2203.93785,0 M 1278.7366,230.43015 c 0,0 444.4182,249.00326 444.4182,249.00326 M 1225.497,230.43015 c 0,0 405.827,249.00326 405.827,249.00326 M 1172.2575,230.43015 c 0,0 367.2358,249.00326 367.2358,249.00326 M 1119.018,230.43015 c 0,0 328.6445,249.00326 328.6445,249.00326 m -381.884,-249.00326 c 0,0 290.0533,249.00326 290.0533,249.00326 M 1012.539,230.43015 c 0,0 251.462,249.00326 251.462,249.00326 M 959.29945,230.43015 c 0,0 212.87085,249.00326 212.87085,249.00326 M 906.05993,230.43015 c 0,0 174.27967,249.00326 174.27967,249.00326 M 852.82041,230.43015 c 0,0 135.68841,249.00326 135.68841,249.00326 M 799.58089,230.43015 c 0,0 97.09719,249.00326 97.09719,249.00326 M 746.34137,230.43015 c 0,0 58.50597,249.00326 58.50597,249.00326 M 693.10185,230.43015 c 0,0 19.91474,249.00326 19.91474,249.00326 M 639.86233,230.43015 c 0,0 -18.67648,249.00326 -18.67648,249.00326 M 586.62281,230.43015 c 0,0 -57.2677,249.00326 -57.2677,249.00326 m 4.02818,-249.00326 c 0,0 -95.85892,249.00326 -95.85892,249.00326 m 42.6194,-249.00326 c 0,0 -134.45014,249.00326 -134.45014,249.00326 m 81.21062,-249.00326 c 0,0 -173.04137,249.00326 -173.04137,249.00326 M 373.66474,230.43015 c 0,0 -211.6326,249.00326 -211.6326,249.00326 M 320.42522,230.43015 c 0,0 -250.223821,249.00326 -250.223821,249.00326 M 267.1857,230.43015 c 0,0 -288.815043,249.00326 -288.815043,249.00326 M 213.94618,230.43015 c 0,0 -327.40627,249.00326 -327.40627,249.00326 M 160.70666,230.43015 c 0,0 -365.99749,249.00326 -365.99749,249.00326 M 107.46714,230.43015 c 0,0 -404.58871,249.00326 -404.58871,249.00326 M 54.227621,230.43015 c 0,0 -443.179931,249.00326 -443.179931,249.00326 M 0.98810174,230.43015 c 0,0 -481.77115174,249.00326 -481.77115174,249.00326" + inkscape:connector-curvature="0" /> + + + diff --git a/images/background/background2.svg b/images/background/background2.svg new file mode 100644 index 00000000..1c56712e --- /dev/null +++ b/images/background/background2.svg @@ -0,0 +1,2856 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/background/grid.png b/images/background/grid.png new file mode 100644 index 00000000..a19ca45a Binary files /dev/null and b/images/background/grid.png differ diff --git a/images/background/hills.png b/images/background/hills.png deleted file mode 100755 index f1d2be53..00000000 Binary files a/images/background/hills.png and /dev/null differ diff --git a/images/background/mountains.png b/images/background/mountains.png new file mode 100644 index 00000000..6ca7473c Binary files /dev/null and b/images/background/mountains.png differ diff --git a/images/background/mountains2.png b/images/background/mountains2.png new file mode 100644 index 00000000..91b3c0b8 Binary files /dev/null and b/images/background/mountains2.png differ diff --git a/images/background/sky.png b/images/background/sky.png index 26c332df..45b26748 100755 Binary files a/images/background/sky.png and b/images/background/sky.png differ diff --git a/images/background/sky2.png b/images/background/sky2.png new file mode 100644 index 00000000..14d36a18 Binary files /dev/null and b/images/background/sky2.png differ diff --git a/images/background/trees.png b/images/background/trees.png deleted file mode 100755 index 57fb422e..00000000 Binary files a/images/background/trees.png and /dev/null differ diff --git a/images/background/trees2.png b/images/background/trees2.png new file mode 100644 index 00000000..592e655d Binary files /dev/null and b/images/background/trees2.png differ diff --git a/images/sprites.png b/images/sprites.png index 0b302ae6..88233dd5 100644 Binary files a/images/sprites.png and b/images/sprites.png differ diff --git a/images/sprites.svg b/images/sprites.svg new file mode 100644 index 00000000..7f970d01 --- /dev/null +++ b/images/sprites.svg @@ -0,0 +1,29302 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code inComplete + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code inComplete + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + only 50c! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Want aNICE CREAM? + + + + + + + + + + + + + + + + + + + + + + + + + + That'sBANANAS! + + + + + + + + + + + + TV RULESTHE NATION + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Retro + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + RX26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CD83 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DO98 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EY42 + + + + + + + + + + + + + + + + + + + + + + + + + AB12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TIME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TIME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TIME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TIME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TIME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TIME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/sprites/billboard01.png b/images/sprites/billboard01.png deleted file mode 100755 index 9c100cd4..00000000 Binary files a/images/sprites/billboard01.png and /dev/null differ diff --git a/images/sprites/billboard02.png b/images/sprites/billboard02.png deleted file mode 100755 index 1db594e2..00000000 Binary files a/images/sprites/billboard02.png and /dev/null differ diff --git a/images/sprites/billboard03.png b/images/sprites/billboard03.png deleted file mode 100755 index 9a43480a..00000000 Binary files a/images/sprites/billboard03.png and /dev/null differ diff --git a/images/sprites/billboard04.png b/images/sprites/billboard04.png deleted file mode 100755 index abfd4b14..00000000 Binary files a/images/sprites/billboard04.png and /dev/null differ diff --git a/images/sprites/billboard05.png b/images/sprites/billboard05.png deleted file mode 100755 index c470e0d0..00000000 Binary files a/images/sprites/billboard05.png and /dev/null differ diff --git a/images/sprites/billboard06.png b/images/sprites/billboard06.png deleted file mode 100755 index 0ba9efb4..00000000 Binary files a/images/sprites/billboard06.png and /dev/null differ diff --git a/images/sprites/billboard07.png b/images/sprites/billboard07.png deleted file mode 100755 index 646c95b0..00000000 Binary files a/images/sprites/billboard07.png and /dev/null differ diff --git a/images/sprites/billboard08.png b/images/sprites/billboard08.png deleted file mode 100755 index 50f59eb0..00000000 Binary files a/images/sprites/billboard08.png and /dev/null differ diff --git a/images/sprites/billboard09.png b/images/sprites/billboard09.png deleted file mode 100755 index 976f6e5c..00000000 Binary files a/images/sprites/billboard09.png and /dev/null differ diff --git a/images/sprites/boulder1.png b/images/sprites/boulder1.png deleted file mode 100755 index fe98901c..00000000 Binary files a/images/sprites/boulder1.png and /dev/null differ diff --git a/images/sprites/boulder2.png b/images/sprites/boulder2.png deleted file mode 100755 index 67b62f6b..00000000 Binary files a/images/sprites/boulder2.png and /dev/null differ diff --git a/images/sprites/boulder3.png b/images/sprites/boulder3.png deleted file mode 100755 index 637d69a6..00000000 Binary files a/images/sprites/boulder3.png and /dev/null differ diff --git a/images/sprites/bush1.png b/images/sprites/bush1.png deleted file mode 100755 index b22d74da..00000000 Binary files a/images/sprites/bush1.png and /dev/null differ diff --git a/images/sprites/bush2.png b/images/sprites/bush2.png deleted file mode 100755 index 61d4b3c1..00000000 Binary files a/images/sprites/bush2.png and /dev/null differ diff --git a/images/sprites/cactus.png b/images/sprites/cactus.png deleted file mode 100755 index a52df7e9..00000000 Binary files a/images/sprites/cactus.png and /dev/null differ diff --git a/images/sprites/car01.png b/images/sprites/car01.png deleted file mode 100755 index 8efdb849..00000000 Binary files a/images/sprites/car01.png and /dev/null differ diff --git a/images/sprites/car02.png b/images/sprites/car02.png deleted file mode 100755 index 54ab5a16..00000000 Binary files a/images/sprites/car02.png and /dev/null differ diff --git a/images/sprites/car03.png b/images/sprites/car03.png deleted file mode 100755 index dbc629ed..00000000 Binary files a/images/sprites/car03.png and /dev/null differ diff --git a/images/sprites/car04.png b/images/sprites/car04.png deleted file mode 100755 index 611b7ab3..00000000 Binary files a/images/sprites/car04.png and /dev/null differ diff --git a/images/sprites/column.png b/images/sprites/column.png deleted file mode 100755 index 6b1e0586..00000000 Binary files a/images/sprites/column.png and /dev/null differ diff --git a/images/sprites/dead_tree1.png b/images/sprites/dead_tree1.png deleted file mode 100755 index 399c9f6a..00000000 Binary files a/images/sprites/dead_tree1.png and /dev/null differ diff --git a/images/sprites/dead_tree2.png b/images/sprites/dead_tree2.png deleted file mode 100755 index ad7e52e8..00000000 Binary files a/images/sprites/dead_tree2.png and /dev/null differ diff --git a/images/sprites/palm_tree.png b/images/sprites/palm_tree.png deleted file mode 100755 index 8044b5ce..00000000 Binary files a/images/sprites/palm_tree.png and /dev/null differ diff --git a/images/sprites/player_left.png b/images/sprites/player_left.png deleted file mode 100755 index 457d943a..00000000 Binary files a/images/sprites/player_left.png and /dev/null differ diff --git a/images/sprites/player_right.png b/images/sprites/player_right.png deleted file mode 100755 index ca26b0aa..00000000 Binary files a/images/sprites/player_right.png and /dev/null differ diff --git a/images/sprites/player_straight.png b/images/sprites/player_straight.png deleted file mode 100755 index ef1c8cd2..00000000 Binary files a/images/sprites/player_straight.png and /dev/null differ diff --git a/images/sprites/player_uphill_left.png b/images/sprites/player_uphill_left.png deleted file mode 100755 index 8aaa6cf7..00000000 Binary files a/images/sprites/player_uphill_left.png and /dev/null differ diff --git a/images/sprites/player_uphill_right.png b/images/sprites/player_uphill_right.png deleted file mode 100755 index 3e3dfdb9..00000000 Binary files a/images/sprites/player_uphill_right.png and /dev/null differ diff --git a/images/sprites/player_uphill_straight.png b/images/sprites/player_uphill_straight.png deleted file mode 100755 index 634d7a07..00000000 Binary files a/images/sprites/player_uphill_straight.png and /dev/null differ diff --git a/images/sprites/semi.png b/images/sprites/semi.png deleted file mode 100755 index f679c1bb..00000000 Binary files a/images/sprites/semi.png and /dev/null differ diff --git a/images/sprites/stump.png b/images/sprites/stump.png deleted file mode 100755 index 6319df26..00000000 Binary files a/images/sprites/stump.png and /dev/null differ diff --git a/images/sprites/tree1.png b/images/sprites/tree1.png deleted file mode 100755 index 95440940..00000000 Binary files a/images/sprites/tree1.png and /dev/null differ diff --git a/images/sprites/tree2.png b/images/sprites/tree2.png deleted file mode 100755 index d3c8a120..00000000 Binary files a/images/sprites/tree2.png and /dev/null differ diff --git a/images/sprites/truck.png b/images/sprites/truck.png deleted file mode 100755 index d72b5203..00000000 Binary files a/images/sprites/truck.png and /dev/null differ diff --git a/index.html b/index.html index e61ae58f..47e21a0f 100644 --- a/index.html +++ b/index.html @@ -2,6 +2,7 @@ Javascript Racer + @@ -11,7 +12,8 @@
  • Version 1 - Straight
  • Version 2 - Curves
  • Version 3 - Hills
  • -
  • Version 4 - Final
  • +
  • Version 4 - Final (fastest lap gamemode)
  • +
  • Version 5 - Game (out of time gamemode)
  • diff --git a/music/jsracer_soundtrack.mp3 b/music/jsracer_soundtrack.mp3 new file mode 100644 index 00000000..705ce9c4 Binary files /dev/null and b/music/jsracer_soundtrack.mp3 differ diff --git a/music/jsracer_soundtrack.ogg b/music/jsracer_soundtrack.ogg new file mode 100644 index 00000000..94a156c6 Binary files /dev/null and b/music/jsracer_soundtrack.ogg differ diff --git a/music/racer.mp3 b/music/racer.mp3 deleted file mode 100755 index 15a5c83e..00000000 Binary files a/music/racer.mp3 and /dev/null differ diff --git a/music/racer.ogg b/music/racer.ogg deleted file mode 100755 index d85ddfeb..00000000 Binary files a/music/racer.ogg and /dev/null differ diff --git a/racer.js b/racer.js new file mode 100644 index 00000000..0d713282 --- /dev/null +++ b/racer.js @@ -0,0 +1,925 @@ +function racer(gamemode) { + var fps = 60; // how many 'update' frames per second + var step = 1/fps; // how long is each frame (in seconds) + var width = 1024; // logical canvas width + var height = 768; // logical canvas height + var centrifugal = 0.3; // centrifugal force multiplier when going around curves + var offRoadDecel = 0.99; // speed multiplier when off road (e.g. you lose 2% speed each update frame) + var skySpeed = 0.001; // background sky layer scroll speed when going around curve (or up hill) + var hillSpeed = 0.002; // background hill layer scroll speed when going around curve (or up hill) + var treeSpeed = 0.003; // background tree layer scroll speed when going around curve (or up hill) + var skyOffset = 0; // current sky scroll offset + var hillOffset = 0; // current hill scroll offset + var treeOffset = 0; // current tree scroll offset + var segments = []; // array of road segments + var cars = []; // array of cars on the road + var stats = Game.stats('fps'); // mr.doobs FPS counter + var canvas = Dom.get('canvas'); // our canvas... + var ctx = canvas.getContext('2d'); // ...and its drawing context + var background = null; // our background image (loaded below) + var sprites = null; // our spritesheet (loaded below) + var resolution = null; // scaling factor to provide resolution independence (computed) + var roadWidth = 2000; // actually half the roads width, easier math if the road spans from -roadWidth to +roadWidth + var segmentLength = 200; // length of a single segment + var rumbleLength = 3; // number of segments per red/white rumble strip + var trackLength = null; // z length of entire track (computed) + var lanes = 3; // number of lanes + var fieldOfView = 100; // angle (degrees) for field of view + var cameraHeight = 1000; // z height of camera + var cameraDepth = null; // z distance camera is from screen (computed) + var drawDistance = 300; // number of segments to draw + var playerX = 0; // player x offset from center of road (-1 to 1 to stay independent of roadWidth) + var playerZ = null; // player relative z distance from camera (computed) + var fogDensity = 5; // exponential fog density + var position = 0; // current camera Z position (add playerZ to get player's absolute Z position) + var speed = 0; // current speed + var maxSpeed = segmentLength/step; // top speed (ensure we can't move more than 1 segment in a single frame to make collision detection easier) + var accel = maxSpeed/5; // acceleration rate - tuned until it 'felt' right + var breaking = -maxSpeed; // deceleration rate when braking + var decel = -maxSpeed/5; // 'natural' deceleration rate when neither accelerating, nor braking + var offRoadDecel = -maxSpeed/2; // off road deceleration is somewhere in between + var offRoadLimit = maxSpeed/4; // limit when off road deceleration no longer applies (e.g. you can always go at least this speed even when off road) + var totalCars = 20; // total number of cars on the road + var currentLapTime = 0; // current lap time + var lastLapTime = null; // last lap time + var enableTilt = false; // enable horizon tilt + var currentRotation = 0; // horizon tilt initialization + var randomTrack = true; // enable random procedural generation of the track + var randomTrackLength = 5; // if random track is enable, how many track segments/constructs to build? + //var gamemode = 1; // Gamemode: 0: fastest lap, 1: out of time DEPRECATED: defined as argument now + + // Gamemode 1: out of time + var remainingTime = 0; // internal variable - remaining time left to pass the next finish line or it's game over, will be calculated automatically + var difficultyStart = 4; // Starting difficulty (track length) + var difficultyIncrement = 0.5; // How much to increment the difficulty (and track length) each time player finish a track? + var difficultyGap = 2.0; // After how many track finishes do we start to increase the difficulty in terms of number of cars on road, number of turns, etc + var difficultyMax = 14; // Maximum difficulty, after this there will be no increase in difficulty + var difficultyCurrent = difficultyStart; // Current difficulty (will be modified ingame) + var remainingTimeIncrease = 0.00009; // Multiplier of the trackLength to get seconds that will be added to the remainingTime, in other words this defines the time left to the player to finish the track proportionally to the track length (a higher value makes the game easier) + var remainingTimeStartBonus = 2.0; // Multiplier of the remaining time given for the first level (to make game easier for newscomers), also because the player has no momentum at the beginning + var remainingTimeThreshold = 20; // When only this amount of time is left, the remaining time HUD will be highlighted (set to 0 to disable) + var currentLevel = 0; // Internal variable, just a value to display the current level + var gameOverFlag = false; // this flag will be set if game over was triggered + var changeBackgroundEvery = 3; // change the background image every few levels, set to 0 to disable + var changeBackgroundCurrentAlpha = 0.0; // internal variable from 0.0 to 1.0 to specify the current state of background switching (via progressing blending animation) + var currentBackground = 0; // internal variable to track which background we currently draw + var changeBackgroundFlag = false; // internal variable to start the background change + var turboLeft = 3; // number of turbos left + var turboDuration = 10.0; // duration of turbo in seconds + var turboAnimation = 2.0; // duration of animation to do progressive increase/decrease of fov + var turboFovIncrement = 1.4; // multiplier of fov during turbo + var turboMaxSpeed = maxSpeed * 1.5; // maximum speed under turbo + var turboGiveEvery = changeBackgroundEvery; // give a new turbo every x levels (set to 0 to disable) + var turboCentrifugal = centrifugal/2; // torque when under turbo (else the player cannot turn in curves) + var turboTriggered = false; // internal variable - turbo triggered by player? + var turboTimeDone = 0.0; // internal variable - turbo being consumed, since how much time (allow to do animation and such) + var turboCurrentFov = fieldOfView; // internal variable - current fov while doing turbo + + var keyLeft = false; + var keyRight = false; + var keyFaster = false; + var keySlower = false; + + // Add variables to update HUD + var hud = { + speed: { value: null, dom: Dom.get('speed_value') }, + current_lap_time: { value: null, dom: Dom.get('current_lap_time_value') }, + current_level: { value: null, dom: Dom.get('current_level_value') }, + remaining_time: { value: null, dom: Dom.get('remaining_time_value') }, + last_lap_time: { value: null, dom: Dom.get('last_lap_time_value') }, + fast_lap_time: { value: null, dom: Dom.get('fast_lap_time_value') }, + } + + if (gamemode == 1) { + // Out of time gamemode-only HUD elements + hud["turbo_left"] = { value: null, dom: Dom.get('turbo_left_value') } + } + + // Hide either the current lap time or the remaining time HUD meter according to the selected gamemode + if (gamemode == 1) { + document.getElementById('current_lap_time').style.display = 'none'; + document.getElementById('fast_lap_time').style.display = 'none'; + document.getElementById('last_lap_time').style.display = 'none'; + } else { + try { + document.getElementById('remaining_time').style.display = 'none'; + document.getElementById('current_level').style.display = 'none'; + document.getElementById('turbo_left').style.display = 'none'; + } catch(exc) {}; + } + + //========================================================================= + // UPDATE THE GAME WORLD + //========================================================================= + + function update(dt) { + + var n, car, carW, sprite, spriteW; + var playerSegment = findSegment(position+playerZ); + var playerW = SPRITES.PLAYER_STRAIGHT.w * SPRITES.SCALE; + var speedPercent = speed/maxSpeed; + var dx = dt * 2 * speedPercent; // at top speed, should be able to cross from left to right (-1 to 1) in 1 second + var startPosition = position; + + updateCars(dt, playerSegment, playerW); + + position = Util.increase(position, dt * speed, trackLength); + + if (!gameOverFlag) { + if (keyLeft) + playerX = playerX - dx; + else if (keyRight) + playerX = playerX + dx; + } + + if (turboTriggered && gamemode == 1) { + // give more torque under turbo + playerX = playerX - (dx * speedPercent * playerSegment.curve * turboCentrifugal); + } else { + // else manage the torque as usual + playerX = playerX - (dx * speedPercent * playerSegment.curve * centrifugal); + } + + if (!gameOverFlag) { + if (keyFaster) + speed = Util.accelerate(speed, accel, dt); + else if (keySlower) + speed = Util.accelerate(speed, breaking, dt); + else + speed = Util.accelerate(speed, decel, dt); + } else { // game over flag, just decelerate the car until full stop + speed = Util.accelerate(speed, decel, dt); + } + + + if ((playerX < -1) || (playerX > 1)) { + + if (speed > offRoadLimit) + speed = Util.accelerate(speed, offRoadDecel, dt); + + for(n = 0 ; n < playerSegment.sprites.length ; n++) { + sprite = playerSegment.sprites[n]; + spriteW = sprite.source.w * SPRITES.SCALE; + if (Util.overlap(playerX, playerW, sprite.offset + spriteW/2 * (sprite.offset > 0 ? 1 : -1), spriteW)) { + speed = maxSpeed/5; + position = Util.increase(playerSegment.p1.world.z, -playerZ, trackLength); // stop in front of sprite (at front of segment) + break; + } + } + } + + for(n = 0 ; n < playerSegment.cars.length ; n++) { + car = playerSegment.cars[n]; + carW = car.sprite.w * SPRITES.SCALE; + if (speed > car.speed) { + if (Util.overlap(playerX, playerW, car.offset, carW, 0.8)) { + speed = car.speed * (car.speed/speed); + position = Util.increase(car.z, -playerZ, trackLength); + break; + } + } + } + + playerX = Util.limit(playerX, -3, 3); // dont ever let it go too far out of bounds + if (!turboTriggered) { + // Normal speed limit, no turbo + speed = Util.limit(speed, 0, maxSpeed); // or exceed maxSpeed + } else if (gamemode == 1) { + // Turbo management + speed = Util.limit(speed, 0, turboMaxSpeed); // do not exceed turbo max speed + accel = turboMaxSpeed / 3; // increase acceleration + turboTimeDone += dt; // increase the current consumed time of turbo + if (turboTimeDone < turboDuration) { + // if turbo time is left, we can continue + if (turboTimeDone < turboAnimation) { + // turbo initialization animation, increase fov + turboFov = fieldOfView * turboFovIncrement; + if (turboCurrentFov < turboFov) { + turboCurrentFov += (turboFov - fieldOfView) * (dt/turboAnimation); + updateFOV(turboCurrentFov); + } + } else if (turboDuration <= (turboTimeDone + turboAnimation)) { + // turbo end animation, decrease fov + if (turboCurrentFov > fieldOfView) { + turboCurrentFov -= (turboFov - fieldOfView) * (dt/turboAnimation); + updateFOV(turboCurrentFov); + } + if (speed > maxSpeed) { + // also decrease speed gradually + speed -= (turboMaxSpeed - maxSpeed) * (dt/turboAnimation)*3; // *3 is magic value to overcome the fact that the car will still get acceleration next frame and is still capped at turboMaxSpeed (see Util.limit above). By multiplying by 2 we cancel the next increase. + } + } + } else { + // no turbo time left, disable the turbo mode + turboTriggered = false; + updateFOV(fieldOfView); // reinit fieldOfView + } + } + + skyOffset = Util.increase(skyOffset, skySpeed * playerSegment.curve * (position-startPosition)/segmentLength, 1); + hillOffset = Util.increase(hillOffset, hillSpeed * playerSegment.curve * (position-startPosition)/segmentLength, 1); + treeOffset = Util.increase(treeOffset, treeSpeed * playerSegment.curve * (position-startPosition)/segmentLength, 1); + + if (position > playerZ) { + if (currentLapTime && (startPosition < playerZ)) { // arrived at finish line, update last lap time + generate new track if enabled + if (gamemode == 1) { // Out of time gamemode + // Increase level (only useful for display, internally we use difficultyCurrent) + currentLevel += 1; + // Give the player some more time + var remainingTimePast = remainingTime; + remainingTime += trackLength * remainingTimeIncrease; + if ((remainingTimePast < remainingTimeThreshold) & (remainingTime > remainingTimeThreshold)) { + Dom.removeClassName('remaining_time_value', 'warninglow'); // remove any warning if there was one + Dom.addClassName('remaining_time_value', 'value'); + } + // Increase current difficulty unless we are already at the max + if (difficultyCurrent < difficultyMax) { + difficultyCurrent += difficultyIncrement; + } + if (randomTrack) { // generate procedurally a new track when arriving at the finish line according to difficulty + // Generate a new track length according to difficulty + randomTrackLength = Math.floor(difficultyCurrent); + // Regenerate the new track + resetRoad(randomTrack, randomTrackLength); + // If we crossed the difficulty gap (ie, every few levels), then we increase the number of cars + if (((difficultyCurrent % difficultyGap) == 0) & (difficultyCurrent < difficultyMax)) { + // Double the number of cars (keep in mind the track extended and we kept the same number of cars, so it's not too much to double) + totalCars += Math.floor(totalCars); + // And we redraw all cars TODO: make it look better (cars on screen at finish line will disappear) + resetCars(); + } + } + // Change background if enabled and we have passed enough levels + if ((changeBackgroundEvery > 0) & (currentLevel % changeBackgroundEvery == 0)) { + changeBackgroundFlag = true; + } + // Add a turbo if passed enough levels + if ((turboGiveEvery > 0) & (currentLevel % turboGiveEvery == 0)) { + turboLeft += 1; + } + } else { // fastest lap time gamemode + lastLapTime = currentLapTime; + currentLapTime = 0; + if ((lastLapTime <= Util.toFloat(Dom.storage.fast_lap_time)) | (Util.toFloat(Dom.storage.fast_lap_time) == 0)) { + Dom.storage.fast_lap_time = lastLapTime; + updateHud('fast_lap_time', formatTime(lastLapTime)); + Dom.addClassName('fast_lap_time', 'fastest'); + Dom.addClassName('last_lap_time', 'fastest'); + } else { + Dom.removeClassName('fast_lap_time', 'fastest'); + Dom.removeClassName('last_lap_time', 'fastest'); + } + updateHud('last_lap_time', formatTime(lastLapTime)); + Dom.show('last_lap_time'); + } + } else { + // Else we are not yet at the finish line, we increase the time/decrease remaining time + currentLapTime += dt; + if (remainingTime > 0) { + remainingTime -= dt; + } else { + remainingTime = 0; + if (currentLevel == 0) { // first level, we give some time to the player + remainingTime += trackLength * remainingTimeIncrease * remainingTimeStartBonus; + } + } + + // Highlight remaining time if quite low + if ((gamemode == 1) & (remainingTime < remainingTimeThreshold)) { + Dom.removeClassName('remaining_time_value', 'value'); + Dom.addClassName('remaining_time_value', 'warninglow'); + } + + // Highlight turbo when in use + if (gamemode == 1) { + if (turboTriggered) { + Dom.addClassName('turbo_left', 'magenta'); + } else { + Dom.removeClassName('turbo_left', 'magenta'); + } + } + + // Call game over if conditions are met + if ((gamemode == 1) & (remainingTime <= 0)) { // gamemode out of time and no remaining time left + gameOverFlag = true; + } + } + } + + // Update HUD + updateHud('speed', 5 * Math.round(speed/500)); + if (gamemode == 1) { + updateHud('remaining_time', formatTime(remainingTime)); + updateHud('current_level', currentLevel); + updateHud('turbo_left', turboLeft); + } else { + updateHud('current_lap_time', formatTime(currentLapTime)); + } + } + + //------------------------------------------------------------------------- + + function updateCars(dt, playerSegment, playerW) { + var n, car, oldSegment, newSegment; + for(n = 0 ; n < cars.length ; n++) { + car = cars[n]; + oldSegment = findSegment(car.z); + car.offset = car.offset + updateCarOffset(car, oldSegment, playerSegment, playerW); + car.z = Util.increase(car.z, dt * car.speed, trackLength); + car.percent = Util.percentRemaining(car.z, segmentLength); // useful for interpolation during rendering phase + newSegment = findSegment(car.z); + if (oldSegment != newSegment) { + index = oldSegment.cars.indexOf(car); + oldSegment.cars.splice(index, 1); + newSegment.cars.push(car); + } + } + } + + function updateCarOffset(car, carSegment, playerSegment, playerW) { + + var i, j, dir, segment, otherCar, otherCarW, lookahead = 20, carW = car.sprite.w * SPRITES.SCALE; + + // optimization, dont bother steering around other cars when 'out of sight' of the player + if ((carSegment.index - playerSegment.index) > drawDistance) + return 0; + + for(i = 1 ; i < lookahead ; i++) { + segment = segments[(carSegment.index+i)%segments.length]; + + if ((segment === playerSegment) && (car.speed > speed) && (Util.overlap(playerX, playerW, car.offset, carW, 1.2))) { + if (playerX > 0.5) + dir = -1; + else if (playerX < -0.5) + dir = 1; + else + dir = (car.offset > playerX) ? 1 : -1; + return dir * 1/i * (car.speed-speed)/maxSpeed; // the closer the cars (smaller i) and the greated the speed ratio, the larger the offset + } + + for(j = 0 ; j < segment.cars.length ; j++) { + otherCar = segment.cars[j]; + otherCarW = otherCar.sprite.w * SPRITES.SCALE; + if ((car.speed > otherCar.speed) && Util.overlap(car.offset, carW, otherCar.offset, otherCarW, 1.2)) { + if (otherCar.offset > 0.5) + dir = -1; + else if (otherCar.offset < -0.5) + dir = 1; + else + dir = (car.offset > otherCar.offset) ? 1 : -1; + return dir * 1/i * (car.speed-otherCar.speed)/maxSpeed; + } + } + } + + // if no cars ahead, but I have somehow ended up off road, then steer back on + if (car.offset < -0.9) + return 0.1; + else if (car.offset > 0.9) + return -0.1; + else + return 0; + } + + //------------------------------------------------------------------------- + + function updateHud(key, value) { // accessing DOM can be slow, so only do it if value has changed + if (hud[key].value !== value) { + hud[key].value = value; + Dom.set(hud[key].dom, value); + } + } + + function formatTime(dt) { + var minutes = Math.floor(dt/60); + var seconds = Math.floor(dt - (minutes * 60)); + var tenths = Math.floor(10 * (dt - Math.floor(dt))); + if (minutes > 0) + return minutes + "." + (seconds < 10 ? "0" : "") + seconds + "." + tenths; + else + return seconds + "." + tenths; + } + + //========================================================================= + // RENDER THE GAME WORLD + //========================================================================= + + function render() { + + var baseSegment = findSegment(position); + var basePercent = Util.percentRemaining(position, segmentLength); + var playerSegment = findSegment(position+playerZ); + var playerPercent = Util.percentRemaining(position+playerZ, segmentLength); + var playerY = Util.interpolate(playerSegment.p1.world.y, playerSegment.p2.world.y, playerPercent); + var maxy = height; + + var x = 0; + var dx = - (baseSegment.curve * basePercent); + + // Clear the canvas + ctx.clearRect(0, 0, width, height); + + // Order the background layers + if (currentBackground == 0) { + // Build the list of positions in the image to extract the appropriate background + // Depending on the current background, load as current the night or day version + background_pos_cur = [BACKGROUND.SKY, BACKGROUND.HILLS, BACKGROUND.TREES]; + background_pos_next = [BACKGROUND.SKY2, BACKGROUND.HILLS2, BACKGROUND.TREES2]; + } else { + background_pos_cur = [BACKGROUND.SKY2, BACKGROUND.HILLS2, BACKGROUND.TREES2]; + background_pos_next = [BACKGROUND.SKY, BACKGROUND.HILLS, BACKGROUND.TREES]; + } + // Draw the background layers + if (!changeBackgroundFlag) { + // No switching, we draw one set of backgrounds + Render.background(ctx, background, width, height, background_pos_cur[0], skyOffset, resolution * skySpeed * playerY, 1.0); + Render.background(ctx, background, width, height, background_pos_cur[1], hillOffset, resolution * hillSpeed * playerY, 1.0); + Render.background(ctx, background, width, height, background_pos_cur[2], treeOffset, resolution * treeSpeed * playerY, 1.0); + } else { + // else we are in the process of switching, do a progressive blending + // continue the blending + changeBackgroundCurrentAlpha += 0.01; // increase the alpha for one, and decrease for the next background set + Render.background(ctx, background, width, height, background_pos_cur[0], skyOffset, resolution * skySpeed * playerY, 1.0-changeBackgroundCurrentAlpha); + Render.background(ctx, background, width, height, background_pos_cur[1], hillOffset, resolution * hillSpeed * playerY, 1.0-changeBackgroundCurrentAlpha); + Render.background(ctx, background, width, height, background_pos_cur[2], treeOffset, resolution * treeSpeed * playerY, 1.0-changeBackgroundCurrentAlpha); + Render.background(ctx, background, width, height, background_pos_next[0], skyOffset, resolution * skySpeed * playerY, changeBackgroundCurrentAlpha); + Render.background(ctx, background, width, height, background_pos_next[1], hillOffset, resolution * hillSpeed * playerY, changeBackgroundCurrentAlpha); + Render.background(ctx, background, width, height, background_pos_next[2], treeOffset, resolution * treeSpeed * playerY, changeBackgroundCurrentAlpha); + if (changeBackgroundCurrentAlpha >= 1.0) { + // blending is done, disable the flags and reinit all related vars + // Note: it is important to still do the drawing (and not put it in an if statement) because else the last drawing won't be done, there will be no background for a split-second and this will produce a flickering effect + currentBackground = (currentBackground + 1) % 2 + changeBackgroundCurrentAlpha = 0.0; + changeBackgroundFlag = false; + } + } + + var n, i, segment, car, sprite, spriteScale, spriteX, spriteY; + + for(n = 0 ; n < drawDistance ; n++) { + + segment = segments[(baseSegment.index + n) % segments.length]; + segment.looped = segment.index < baseSegment.index; + segment.fog = Util.exponentialFog(n/drawDistance, fogDensity); + segment.clip = maxy; + + Util.project(segment.p1, (playerX * roadWidth) - x, playerY + cameraHeight, position - (segment.looped ? trackLength : 0), cameraDepth, width, height, roadWidth); + Util.project(segment.p2, (playerX * roadWidth) - x - dx, playerY + cameraHeight, position - (segment.looped ? trackLength : 0), cameraDepth, width, height, roadWidth); + + x = x + dx; + dx = dx + segment.curve; + + if ((segment.p1.camera.z <= cameraDepth) || // behind us + (segment.p2.screen.y >= segment.p1.screen.y) || // back face cull + (segment.p2.screen.y >= maxy)) // clip by (already rendered) hill + continue; + + Render.segment(ctx, width, lanes, + segment.p1.screen.x, + segment.p1.screen.y, + segment.p1.screen.w, + segment.p2.screen.x, + segment.p2.screen.y, + segment.p2.screen.w, + segment.fog, + segment.color); + + maxy = segment.p1.screen.y; + } + + for(n = (drawDistance-1) ; n > 0 ; n--) { + segment = segments[(baseSegment.index + n) % segments.length]; + + for(i = 0 ; i < segment.cars.length ; i++) { + car = segment.cars[i]; + sprite = car.sprite; + spriteScale = Util.interpolate(segment.p1.screen.scale, segment.p2.screen.scale, car.percent); + spriteX = Util.interpolate(segment.p1.screen.x, segment.p2.screen.x, car.percent) + (spriteScale * car.offset * roadWidth * width/2); + spriteY = Util.interpolate(segment.p1.screen.y, segment.p2.screen.y, car.percent); + Render.sprite(ctx, width, height, resolution, roadWidth, sprites, car.sprite, spriteScale, spriteX, spriteY, -0.5, -1, segment.clip); + } + + for(i = 0 ; i < segment.sprites.length ; i++) { + sprite = segment.sprites[i]; + spriteScale = segment.p1.screen.scale; + spriteX = segment.p1.screen.x + (spriteScale * sprite.offset * roadWidth * width/2); + spriteY = segment.p1.screen.y; + Render.sprite(ctx, width, height, resolution, roadWidth, sprites, sprite.source, spriteScale, spriteX, spriteY, (sprite.offset < 0 ? -1 : 0), -1, segment.clip); + } + + if (segment == playerSegment) { + Render.player(ctx, width, height, resolution, roadWidth, sprites, speed/maxSpeed, + cameraDepth/playerZ, + width/2, + (height/2) - (cameraDepth/playerZ * Util.interpolate(playerSegment.p1.camera.y, playerSegment.p2.camera.y, playerPercent) * height/2), + speed * (keyLeft ? -1 : keyRight ? 1 : 0), + playerSegment.p2.world.y - playerSegment.p1.world.y); + } + } + + // start horizon tilt + if (enableTilt) { + rotation=0; + if (baseSegment.curve==0) { + rotation=-currentRotation; + currentRotation=0; + } else { + newrot = Math.round(baseSegment.curve*speed/maxSpeed*100)/100; + rotation=newrot - currentRotation ; + currentRotation = newrot ; + } + if (rotation!=0) { + //ctx.save(); // doesn't help with moire problem + ctx.translate(canvas.width/2,canvas.height/2); + ctx.rotate(-rotation*(Math.PI/90)); + ctx.translate(-canvas.width/2,-canvas.height/2); + //ctx.restore(); + } + } + + // Draw "Game Over" screen + if (gameOverFlag) { + ctx.font = "3em Arial"; + ctx.fillStyle = "magenta"; + ctx.textAlign = "center"; + ctx.fillText("GAME OVER", canvas.width/2, canvas.height/2); + ctx.fillText("(refresh to restart)", canvas.width/2, canvas.height/1.5); + } + } + + function findSegment(z) { + return segments[Math.floor(z/segmentLength) % segments.length]; + } + + //========================================================================= + // BUILD ROAD GEOMETRY + //========================================================================= + + function lastY() { return (segments.length == 0) ? 0 : segments[segments.length-1].p2.world.y; } + + function addSegment(curve, y) { + var n = segments.length; + segments.push({ + index: n, + p1: { world: { y: lastY(), z: n *segmentLength }, camera: {}, screen: {} }, + p2: { world: { y: y, z: (n+1)*segmentLength }, camera: {}, screen: {} }, + curve: curve, + sprites: [], + cars: [], + color: Math.floor(n/rumbleLength)%2 ? COLORS.DARK : COLORS.LIGHT + }); + } + + function addSprite(n, sprite, offset) { + segments[n].sprites.push({ source: sprite, offset: offset }); + } + + function addRoad(enter, hold, leave, curve, y) { + var startY = lastY(); + var endY = startY + (Util.toInt(y, 0) * segmentLength); + var n, total = enter + hold + leave; + for(n = 0 ; n < enter ; n++) + addSegment(Util.easeIn(0, curve, n/enter), Util.easeInOut(startY, endY, n/total)); + for(n = 0 ; n < hold ; n++) + addSegment(curve, Util.easeInOut(startY, endY, (enter+n)/total)); + for(n = 0 ; n < leave ; n++) + addSegment(Util.easeInOut(curve, 0, n/leave), Util.easeInOut(startY, endY, (enter+hold+n)/total)); + } + + var ROAD = { + LENGTH: { NONE: 0, SHORT: 25, MEDIUM: 50, LONG: 100 }, + HILL: { NONE: 0, LOW: 20, MEDIUM: 40, HIGH: 60 }, + CURVE: { NONE: 0, EASY: 2, MEDIUM: 4, HARD: 6 } + }; + + function addStraight(num) { + num = num || ROAD.LENGTH.MEDIUM; + addRoad(num, num, num, 0, 0); + } + + function addHill(num, height) { + num = num || ROAD.LENGTH.MEDIUM; + height = height || ROAD.HILL.MEDIUM; + addRoad(num, num, num, 0, height); + } + + function addCurve(num, curve, height) { + num = num || ROAD.LENGTH.MEDIUM; + curve = curve || ROAD.CURVE.MEDIUM; + height = height || ROAD.HILL.NONE; + addRoad(num, num, num, curve, height); + } + + function addLowRollingHills(num, height) { + num = num || ROAD.LENGTH.SHORT; + height = height || ROAD.HILL.LOW; + addRoad(num, num, num, 0, height/2); + addRoad(num, num, num, 0, -height); + addRoad(num, num, num, ROAD.CURVE.EASY, height); + addRoad(num, num, num, 0, 0); + addRoad(num, num, num, -ROAD.CURVE.EASY, height/2); + addRoad(num, num, num, 0, 0); + } + + function addSCurves() { + addRoad(ROAD.LENGTH.MEDIUM, ROAD.LENGTH.MEDIUM, ROAD.LENGTH.MEDIUM, -ROAD.CURVE.EASY, ROAD.HILL.NONE); + addRoad(ROAD.LENGTH.MEDIUM, ROAD.LENGTH.MEDIUM, ROAD.LENGTH.MEDIUM, ROAD.CURVE.MEDIUM, ROAD.HILL.MEDIUM); + addRoad(ROAD.LENGTH.MEDIUM, ROAD.LENGTH.MEDIUM, ROAD.LENGTH.MEDIUM, ROAD.CURVE.EASY, -ROAD.HILL.LOW); + addRoad(ROAD.LENGTH.MEDIUM, ROAD.LENGTH.MEDIUM, ROAD.LENGTH.MEDIUM, -ROAD.CURVE.EASY, ROAD.HILL.MEDIUM); + addRoad(ROAD.LENGTH.MEDIUM, ROAD.LENGTH.MEDIUM, ROAD.LENGTH.MEDIUM, -ROAD.CURVE.MEDIUM, -ROAD.HILL.MEDIUM); + } + + function addBumps() { + addRoad(10, 10, 10, 0, 5); + addRoad(10, 10, 10, 0, -2); + addRoad(10, 10, 10, 0, -5); + addRoad(10, 10, 10, 0, 8); + addRoad(10, 10, 10, 0, 5); + addRoad(10, 10, 10, 0, -7); + addRoad(10, 10, 10, 0, 5); + addRoad(10, 10, 10, 0, -2); + } + + function addDownhillToEnd(num) { + num = num || 200; + addRoad(num, num, num, -ROAD.CURVE.EASY, -lastY()/segmentLength); + } + + function resetRoad(random, mintracklength) { + segments = []; + + random = true; + if (random==true) { + // Build the list of possible constructs + var constructs = ['straight', 'scurves', 'curve', 'bumps', 'hill', 'lowrollinghills']; + // Minimum track length needs to be 2 (between start and end), else it will fail + if (!mintracklength) { + mintracklength = 2; + } + + // Build start part of the track + addStraight(ROAD.LENGTH.LONG); // use a long road length to hide the regeneration of the next track when arriving at finish line. TODO: fix this by preloading in parallel the new track, and when riding the last construct before the finish line, modify the rendering functions to render the new track + + // Procedurally and randomly build the rest of the track + i = -1; + while ((i+=1) < mintracklength) { // TODO: sometimes, 2 tracks are not enough and the loading fails, try to find out why? (there must be an incompatibility between constructs somewhere) + // Pick randomly a construct + var randc = constructs[Math.floor(Math.random() * constructs.length)]; + if (randc == 'straight') { + var posvals = [ROAD.LENGTH.SHORT, ROAD.LENGTH.MEDIUM, ROAD.LENGTH.LONG, null]; + var randval = posvals[Math.floor(Math.random() * posvals.length)]; + addStraight(randval); + } else if (randc == 'scurves') { + addSCurves(); + } else if (randc == 'curve') { + var posroad = [ROAD.LENGTH.SHORT, ROAD.LENGTH.MEDIUM, ROAD.LENGTH.LONG]; + var randroad = posroad[Math.floor(Math.random() * posroad.length)]; + var poscurve = [ROAD.CURVE.SHORT, ROAD.CURVE.MEDIUM, ROAD.CURVE.LONG]; + var randcurve = poscurve[Math.floor(Math.random() * poscurve.length)]; + var poshill = [ROAD.HILL.LOW, ROAD.HILL.MEDIUM, ROAD.HILL.HIGH]; + var randhill = poshill[Math.floor(Math.random() * poshill.length)]; + addCurve(randroad, randcurve, randhill); + } else if (randc == 'bumps') { + addBumps(); + } else if (randc == 'hill') { + var posroad = [ROAD.LENGTH.SHORT, ROAD.LENGTH.MEDIUM, ROAD.LENGTH.LONG]; + var randroad = posroad[Math.floor(Math.random() * posroad.length)]; + var poshill = [ROAD.HILL.LOW, ROAD.HILL.MEDIUM, ROAD.HILL.HIGH]; + var randhill = poshill[Math.floor(Math.random() * poshill.length)]; + addHill(randroad, randhill); + } else if (randc == 'curve') { + addLowRollingHills(); + } + } + // Build end part of the track + addDownhillToEnd(); + } else { + addStraight(ROAD.LENGTH.SHORT); + addLowRollingHills(); + addSCurves(); + addCurve(ROAD.LENGTH.MEDIUM, ROAD.CURVE.MEDIUM, ROAD.HILL.LOW); + addBumps(); + addLowRollingHills(); + addCurve(ROAD.LENGTH.LONG*2, ROAD.CURVE.MEDIUM, ROAD.HILL.MEDIUM); + addStraight(); + addHill(ROAD.LENGTH.MEDIUM, ROAD.HILL.HIGH); + addSCurves(); + addCurve(ROAD.LENGTH.LONG, -ROAD.CURVE.MEDIUM, ROAD.HILL.NONE); + addHill(ROAD.LENGTH.LONG, ROAD.HILL.HIGH); + addCurve(ROAD.LENGTH.LONG, ROAD.CURVE.MEDIUM, -ROAD.HILL.LOW); + addBumps(); + addHill(ROAD.LENGTH.LONG, -ROAD.HILL.MEDIUM); + addStraight(); + addSCurves(); + addDownhillToEnd(); + } + + try { // workaround for the exception raised sometimes when the tracklength is too small (<5). TODO: fix the root cause of this. + resetSprites(); // reset (or create) the environmental sprites + } catch (exc) {console.log(exc);} + //resetCars(); // don't necessarily reset cars, if we generate procedurally we just want the cars to continue + + segments[findSegment(playerZ).index + 2].color = COLORS.START; + segments[findSegment(playerZ).index + 3].color = COLORS.START; + for(var n = 0 ; n < rumbleLength ; n++) + segments[segments.length-1-n].color = COLORS.FINISH; + + trackLength = segments.length * segmentLength; + } + + function resetSprites() { + var n, i; + + addSprite(20, SPRITES.BILLBOARD07, -1); + addSprite(40, SPRITES.BILLBOARD06, -1); + addSprite(60, SPRITES.BILLBOARD08, -1); + addSprite(80, SPRITES.BILLBOARD09, -1); + addSprite(100, SPRITES.BILLBOARD01, -1); + addSprite(120, SPRITES.BILLBOARD02, -1); + addSprite(140, SPRITES.BILLBOARD03, -1); + addSprite(160, SPRITES.BILLBOARD04, -1); + addSprite(180, SPRITES.BILLBOARD05, -1); + + addSprite(240, SPRITES.BILLBOARD07, -1.2); + addSprite(240, SPRITES.BILLBOARD06, 1.2); + addSprite(segments.length - 25, SPRITES.BILLBOARD07, -1.2); + addSprite(segments.length - 25, SPRITES.BILLBOARD06, 1.2); + + for(n = 10 ; n < 200 ; n += 4 + Math.floor(n/100)) { + addSprite(n, SPRITES.PALM_TREE, 0.5 + Math.random()*0.5); + addSprite(n, SPRITES.PALM_TREE, 1 + Math.random()*2); + } + + for(n = 250 ; n < 1000 ; n += 5) { + addSprite(n, SPRITES.COLUMN, 1.1); + addSprite(n + Util.randomInt(0,5), SPRITES.TREE1, -1 - (Math.random() * 2)); + addSprite(n + Util.randomInt(0,5), SPRITES.TREE2, -1 - (Math.random() * 2)); + } + + for(n = 200 ; n < segments.length ; n += 3) { + addSprite(n, Util.randomChoice(SPRITES.PLANTS), Util.randomChoice([1,-1]) * (2 + Math.random() * 5)); + } + + var side, sprite, offset; + for(n = 1000 ; n < (segments.length-50) ; n += 100) { + side = Util.randomChoice([1, -1]); + addSprite(n + Util.randomInt(0, 50), Util.randomChoice(SPRITES.BILLBOARDS), -side); + for(i = 0 ; i < 20 ; i++) { + sprite = Util.randomChoice(SPRITES.PLANTS); + offset = side * (1.5 + Math.random()); + addSprite(n + Util.randomInt(0, 50), sprite, offset); + } + + } + + } + + function resetCars() { + cars = []; + var n, car, segment, offset, z, sprite, speed; + for (var n = 0 ; n < totalCars ; n++) { + offset = Math.random() * Util.randomChoice([-0.8, 0.8]); + z = (Math.floor(Math.random() * (segments.length-400)) * segmentLength) + (100*segmentLength); // ensure that cars do not respawn just in front of the player, so we generate cars in all segments except the first and last (after and before finish line) - TODO: fix me in a more elegant way, without using constants + sprite = Util.randomChoice(SPRITES.CARS); + speed = maxSpeed/4 + Math.random() * maxSpeed/(sprite == SPRITES.SEMI ? 4 : 2); + car = { offset: offset, z: z, sprite: sprite, speed: speed }; + segment = findSegment(car.z); + segment.cars.push(car); + cars.push(car); + } + } + + //========================================================================= + // THE GAME LOOP + //========================================================================= + + Game.run({ + canvas: canvas, render: render, update: update, stats: stats, step: step, + images: ["background", "sprites"], + keys: [ + { keys: [KEY.LEFT, KEY.A], div: 'gamepad-left', mode: 'down', action: function() { keyLeft = true; } }, + { keys: [KEY.RIGHT, KEY.D], div: 'gamepad-right', mode: 'down', action: function() { keyRight = true; } }, + { keys: [KEY.UP, KEY.W], div: 'gamepad-up', mode: 'down', action: function() { keyFaster = true; } }, + { keys: [KEY.DOWN, KEY.S], div: 'gamepad-down', mode: 'down', action: function() { keySlower = true; } }, + { keys: [KEY.LEFT, KEY.A], div: 'gamepad-left', mode: 'up', action: function() { keyLeft = false; } }, + { keys: [KEY.RIGHT, KEY.D], div: 'gamepad-right', mode: 'up', action: function() { keyRight = false; } }, + { keys: [KEY.UP, KEY.W], div: 'gamepad-up', mode: 'up', action: function() { keyFaster = false; } }, + { keys: [KEY.DOWN, KEY.S], div: 'gamepad-down', mode: 'up', action: function() { keySlower = false; } }, + { keys: [KEY.SPACE, KEY.CTRL], div: 'gamepad-turbo', mode: 'down', action: triggerTurbo } + ], + ready: function(images) { + background = images[0]; + sprites = images[1]; + reset(); + Dom.storage.fast_lap_time = Dom.storage.fast_lap_time || 180; + updateHud('fast_lap_time', formatTime(Util.toFloat(Dom.storage.fast_lap_time))); + // Allow to put in fullscreen + var e = document.getElementById('canvas'); + e.ondblclick = fullscreenOnClick; + } + }); + + function reset(options) { + options = options || {}; + canvas.width = width = Util.toInt(options.width, width); + canvas.height = height = Util.toInt(options.height, height); + lanes = Util.toInt(options.lanes, lanes); + roadWidth = Util.toInt(options.roadWidth, roadWidth); + cameraHeight = Util.toInt(options.cameraHeight, cameraHeight); + drawDistance = Util.toInt(options.drawDistance, drawDistance); + fogDensity = Util.toInt(options.fogDensity, fogDensity); + fieldOfView = Util.toInt(options.fieldOfView, fieldOfView); + segmentLength = Util.toInt(options.segmentLength, segmentLength); + rumbleLength = Util.toInt(options.rumbleLength, rumbleLength); + cameraDepth = 1 / Math.tan((fieldOfView/2) * Math.PI/180); + playerZ = (cameraHeight * cameraDepth); + resolution = height/480; + refreshTweakUI(); + + if ((segments.length==0) || (options.segmentLength) || (options.rumbleLength)) { + resetRoad(randomTrack, randomTrackLength); // only rebuild road when necessary + resetCars(); + } + } + + function updateFOV(fov) { + cameraDepth = 1 / Math.tan((fov/2) * Math.PI/180); + playerZ = (cameraHeight * cameraDepth); + } + + //========================================================================= + // TWEAK UI HANDLERS + //========================================================================= + + Dom.on('resolution', 'change', function(ev) { + var w, h, ratio; + switch(ev.target.options[ev.target.selectedIndex].value) { + case 'fine': w = 1280; h = 960; ratio=w/width; break; + case 'high': w = 1024; h = 768; ratio=w/width; break; + case 'medium': w = 640; h = 480; ratio=w/width; break; + case 'low': w = 480; h = 360; ratio=w/width; break; + } + reset({ width: w, height: h }) + Dom.blur(ev); + }); + + Dom.on('lanes', 'change', function(ev) { Dom.blur(ev); reset({ lanes: ev.target.options[ev.target.selectedIndex].value }); }); + Dom.on('roadWidth', 'change', function(ev) { Dom.blur(ev); reset({ roadWidth: Util.limit(Util.toInt(ev.target.value), Util.toInt(ev.target.getAttribute('min')), Util.toInt(ev.target.getAttribute('max'))) }); }); + Dom.on('cameraHeight', 'change', function(ev) { Dom.blur(ev); reset({ cameraHeight: Util.limit(Util.toInt(ev.target.value), Util.toInt(ev.target.getAttribute('min')), Util.toInt(ev.target.getAttribute('max'))) }); }); + Dom.on('drawDistance', 'change', function(ev) { Dom.blur(ev); reset({ drawDistance: Util.limit(Util.toInt(ev.target.value), Util.toInt(ev.target.getAttribute('min')), Util.toInt(ev.target.getAttribute('max'))) }); }); + Dom.on('fieldOfView', 'change', function(ev) { Dom.blur(ev); reset({ fieldOfView: Util.limit(Util.toInt(ev.target.value), Util.toInt(ev.target.getAttribute('min')), Util.toInt(ev.target.getAttribute('max'))) }); }); + Dom.on('fogDensity', 'change', function(ev) { Dom.blur(ev); reset({ fogDensity: Util.limit(Util.toInt(ev.target.value), Util.toInt(ev.target.getAttribute('min')), Util.toInt(ev.target.getAttribute('max'))) }); }); + + function refreshTweakUI() { + Dom.get('lanes').selectedIndex = lanes-1; + Dom.get('currentRoadWidth').innerHTML = Dom.get('roadWidth').value = roadWidth; + Dom.get('currentCameraHeight').innerHTML = Dom.get('cameraHeight').value = cameraHeight; + Dom.get('currentDrawDistance').innerHTML = Dom.get('drawDistance').value = drawDistance; + Dom.get('currentFieldOfView').innerHTML = Dom.get('fieldOfView').value = fieldOfView; + Dom.get('currentFogDensity').innerHTML = Dom.get('fogDensity').value = fogDensity; + } + + function fullscreenOnClick() { + // Manage full screen mode on double click + // from: https://www.sitepoint.com/use-html5-full-screen-api/ + if (document.fullscreenElement || + document.webkitFullscreenElement || + document.mozFullScreenElement || + document.msFullscreenElement + ) { + // exit full-screen + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } + } else { + // go full-screen + var e = document.getElementById('canvas'); + if (e.requestFullscreen) { + e.requestFullscreen(); + } else if (e.webkitRequestFullscreen) { + e.webkitRequestFullscreen(); + } else if (e.mozRequestFullScreen) { + e.mozRequestFullScreen(); + } else if (e.msRequestFullscreen) { + e.msRequestFullscreen(); + } + } + } + + function triggerTurbo() { + // turbo trigger function + if (gamemode == 1 && turboLeft > 0 && !turboTriggered) { + turboCurrentFov = fieldOfView; + turboTimeDone = 0.0; + turboTriggered = true; + turboLeft -= 1; + } + } + //========================================================================= +} diff --git a/screenshots/screenshot1.png b/screenshots/screenshot1.png new file mode 100644 index 00000000..6c52cfe0 Binary files /dev/null and b/screenshots/screenshot1.png differ diff --git a/screenshots/screenshot2.png b/screenshots/screenshot2.png new file mode 100644 index 00000000..ec7427df Binary files /dev/null and b/screenshots/screenshot2.png differ diff --git a/thanks.txt b/thanks.txt new file mode 100644 index 00000000..74a4f920 --- /dev/null +++ b/thanks.txt @@ -0,0 +1,25 @@ +Big thank you for the resources under creative commons to the following people: +Graphic Art: + * GDJ for the futuristic city in the background (licensed under CC0): https://pixabay.com/fr/avenir-futuriste-science-fiction-1751262/ +Music: + * Aries Beats for the amazing music tracks ("Retro Wave" and "Night Ride") licensed under Creative Commons CC-BY 3.0, checkout his channel for more: https://www.youtube.com/channel/UCVz37WOEw1mPyIpx8xPHBmg + * TeknoAXE for the amazing "Edge of Tomorrow" music track! (licensed under Creative Commons Attribution 4.0 International License). Checkout his channel for more: https://www.youtube.com/channel/UCtgf00GvfFQVsYBA7V7RwUw +Sprites: +* Textured ball by Firkin: https://openclipart.org/detail/298970/textured-ball +* Hot Dog by oksmith: https://openclipart.org/detail/298968/hot-dog +* Palm Tree by Kasahorow: https://openclipart.org/detail/297853/palm-tree +* Antenna Square by jcartier: https://openclipart.org/detail/17312/antenna-square +* Pineapple by johnny_automatic: https://openclipart.org/detail/1974/pineapple +* Miniature coconut palm by johnny_automatic: https://openclipart.org/detail/6194/miniature-coconut-palm +* Ice cream 1 by Firkin: https://openclipart.org/detail/237386/ice-cream-1 +* Bananas by nicubunu: https://openclipart.org/detail/12987/bananas +* Bananeira by dinhochiz: https://openclipart.org/detail/204886/bananeira +* Mushrooms 1 by opk: https://openclipart.org/detail/173450/mushrooms-1 +* Comic TV by Chrisdesign: https://openclipart.org/detail/8366/comic-tv +* Vintage Sign by laurianne: https://openclipart.org/detail/126265/vintage-sign +* Silver robot by Pippi2011: https://openclipart.org/detail/183281/silver-robot +* The rest by Lrq3000 using Inkscape tutorials by Nick Saporito: https://www.youtube.com/channel/UCEQXp_fcqwPcqrzNtWJ1w9w +Background: +* Seaside Sunrise by Viscious-Speed: https://openclipart.org/detail/234135/seaside-sunrise +* Jakes Gordon for the trees during daytime +* Retro night-time background by Lrq3000 diff --git a/v1.straight.html b/v1.straight.html index 112a7b09..c33aaeff 100644 --- a/v1.straight.html +++ b/v1.straight.html @@ -15,7 +15,8 @@ straight | curves | hills | - final + fastest lap | + out of time @@ -75,8 +76,8 @@ diff --git a/v2.curves.html b/v2.curves.html index 0ef09153..9d765506 100644 --- a/v2.curves.html +++ b/v2.curves.html @@ -15,7 +15,8 @@ straight | curves | hills | - final + fastest lap | + out of time @@ -75,8 +76,8 @@ diff --git a/v3.hills.html b/v3.hills.html index 18810357..783b3ad9 100644 --- a/v3.hills.html +++ b/v3.hills.html @@ -15,7 +15,8 @@ straight | curves | hills | - final + fastest lap | + out of time @@ -75,8 +76,8 @@ diff --git a/v4.final.html b/v4.final.html index 81d69357..725311ec 100644 --- a/v4.final.html +++ b/v4.final.html @@ -15,7 +15,8 @@ straight | curves | hills | - final + fastest lap | + out of time @@ -65,8 +66,16 @@

    Use the arrow keys to drive the car.

    +

    Try to make the fastest time record!

    +

    Double click for fullscreen.

    + + +
    0 mph @@ -80,608 +89,19 @@ Loading...
    - - +
    +
    Turbo
    +
    ^
    +
    <
    +
    >
    +
    v
    +
    + diff --git a/v5.game.html b/v5.game.html new file mode 100644 index 00000000..d2e1038a --- /dev/null +++ b/v5.game.html @@ -0,0 +1,111 @@ + + + + + Javascript Racer - v5 (game) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + straight | + curves | + hills | + fastest lap | + out of time +
    + +
    + +
    + +
    +

    Use the arrow keys to drive the car.

    +

    Cross the finish line before time runs out.

    +

    Double click for fullscreen. Press CONTROL for turbo.

    +
    + + + + +
    +
    + 0 mph + Time: 0.0 + Level: 0 + Turbo: 0 + Time: 0.0 + Last Lap: 0.0 + Fastest Lap: 0.0 +
    + + Sorry, this example cannot be run because your browser does not support the <canvas> element + + Loading... +
    + +
    +
    Turbo
    +
    ^
    +
    <
    +
    >
    +
    v
    +
    + + + + + + + +