├── .hgignore ├── Bliss.bmp ├── NOTES ├── README ├── back.jpg ├── charsheet.txt ├── cheat.js ├── closebox.psd ├── closedown.png ├── closeup.png ├── config.js ├── dice.jpg ├── dicebar.jpg ├── die.jpg ├── index.html ├── ipad-ad.jpg ├── ipad-ad.psd ├── jquery.js ├── jsl.conf ├── json2.js ├── logo.png ├── main.css ├── main.html ├── main.js ├── newguy.css ├── newguy.html ├── newguy.js ├── pas2js.sed ├── pq.gif ├── progros.css ├── publish.py ├── roster.html ├── roster.js ├── screenshot.bmp ├── screenshot2.bmp ├── shell.cc ├── sim.js ├── swords.bmp ├── swords.gif ├── touch-icon.png └── touch-icon.psd /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | local.storage 3 | v8 4 | -------------------------------------------------------------------------------- /Bliss.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rudle/progress-quest/b5d3424a6115c07186576afb8e5c5c19fe0894bd/Bliss.bmp -------------------------------------------------------------------------------- /NOTES: -------------------------------------------------------------------------------- 1 | // After launch 2 | // TODO: No local storage on IE - why? 3 | // TODO: Do something about background-size failure on IE. 4 | // TODO: Stop using HTML to save the game 5 | // TODO: Get fully operational in Opera 6 | // TODO: Online leader board 7 | // TODO: Server side saves 8 | // TODO: Active controls on fake Xp screen 9 | // TODO: Refactor logic to run with visible interface as a layer on top 10 | // TODO: Charsheet in some more pleasing delivery method 11 | // TODO: Use a cookie rather than a fragment to choose char? 12 | // TODO: Line up things on main.html perfectly 13 | // TODO: Finalize borders in sections of main 14 | // TODO: Better ipad support 15 | 16 | On a wrapper for jquery in v8 shell: 17 | http://unethicalblogger.com/node/186 18 | 19 | http://mrgan.tumblr.com/post/98547533/fullscreen-web-apps 20 | http://kentbrewster.com/backchannel/ 21 | 22 | DNA for double "Fetch me a sock" quests: 23 | [0.22078027506358922,0.7939568560104817,0.18758659553714097,1088812] 24 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Progress Quest - Web 2 | ==================== 3 | 4 | This is the in-browser edition of Progress Quest, ported from Delphi 5 | to JavaScript. 6 | 7 | - Progress Quest site: http://progressquest.com/ 8 | - Play it here: http://progressquest.com/play/ 9 | - Original version: https://bitbucket.org/grumdrig/pq 10 | 11 | The whole thing runs in the browser, on the client side, using HTML5 12 | storage and so forth; there's no server side other that hosting static 13 | files. To run your own copy, clone this repo, and open index.html. 14 | -------------------------------------------------------------------------------- /back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rudle/progress-quest/b5d3424a6115c07186576afb8e5c5c19fe0894bd/back.jpg -------------------------------------------------------------------------------- /charsheet.txt: -------------------------------------------------------------------------------- 1 | $Traits.Name the $Traits.Race [$hostname] 2 | "$motto" 3 | 4 | Level $Traits.Level $Traits.Class (exp. $ExpBar.position/$ExpBar.max) 5 | 6 | Plot stage: $bestplot ($PlotBar.hint) 7 | Quest: $bestquest ($QuestBar.hint) 8 | 9 | Stats: 10 | STR $Stats.STR 11 | CON $Stats.CON 12 | DEX $Stats.DEX 13 | INT $Stats.INT 14 | WIS $Stats.WIS HP Max $Stats.HP_Max 15 | CHA $Stats.CHA MP Max $Stats.MP_Max 16 | 17 | Equipment: 18 | $Equips.___ 19 | Spell Book: 20 | $Spells.___ 21 | Inventory ($EncumBar.hint): 22 | $Inventory.___ 23 | -- $date 24 | -- Progress Quest 6.3.web - http://progressquest.com/ 25 | -------------------------------------------------------------------------------- /cheat.js: -------------------------------------------------------------------------------- 1 | function Cheats() { 2 | if ($(".cheater").length) return; 3 | 4 | function cheat(label, effect) { 5 | $(" 29 | 30 | 31 | 32 |
33 |
34 | Race 35 |
36 | 37 |
38 |
39 |
40 | Class 41 |
42 | 43 |
44 | Stats 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
STR 13
CON 23
DEX 34
INT 12
WIS 4
CHA 12
 
Total 48
56 | 57 |
58 |
59 | 60 |
61 |
62 | 63 |
64 |
65 | 66 |
67 | 68 |
69 |
70 |
71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /newguy.js: -------------------------------------------------------------------------------- 1 | /* copyright (c)2002-2010 Eric Fredricksen all rights reserved */ 2 | 3 | 4 | function Roll(stat) { 5 | stats[stat] = 3 + Random(6) + Random(6) + Random(6); 6 | if (document) 7 | $("#"+stat).text(stats[stat]); 8 | return stats[stat]; 9 | } 10 | 11 | function Choose(n, k) { 12 | var result = n; 13 | var d = 1; 14 | for (var i = 2; i <= k; ++i) { 15 | result *= (1+n-i); 16 | d = d * i; 17 | } 18 | return result / d; 19 | } 20 | 21 | var stats = {}; 22 | var traits = {}; 23 | var total = 0; 24 | var seedHistory = []; 25 | 26 | function RollEm() { 27 | stats.seed = randseed(); 28 | total = 0; 29 | var best = -1; 30 | $.each(K.PrimeStats, function () { 31 | total += Roll(this); 32 | if (best < stats[this]) { 33 | best = stats[this]; 34 | stats.best = this; 35 | } 36 | }); 37 | stats['HP Max'] = Random(8) + stats.CON.div(6); 38 | stats['MP Max'] = Random(8) + stats.INT.div(6); 39 | 40 | var color = 41 | (total >= (63+18)) ? 'red' : 42 | (total > (4 * 18)) ? 'yellow' : 43 | (total <= (63-18)) ? 'grey' : 44 | (total < (3 * 18)) ? 'silver' : 45 | 'white'; 46 | 47 | if (document) { 48 | var Total = $("#Total"); 49 | Total.text(total); 50 | Total.css("background-color", color); 51 | 52 | $("#Unroll").attr("disabled", !seedHistory.length); 53 | } 54 | } 55 | 56 | function RerollClick() { 57 | seedHistory.push(stats.seed); 58 | RollEm(); 59 | } 60 | 61 | 62 | function UnrollClick() { 63 | randseed(seedHistory.pop()); 64 | RollEm(); 65 | } 66 | 67 | function fill(e, a, n) { 68 | var def = Random(a.length); 69 | for (var i = 0; i < a.length; ++i) { 70 | var v = a[i].split("|")[0]; 71 | var check = (def == i) ? " checked " : " "; 72 | if (def == i) traits[n] = v; 73 | if (document) { 74 | $("
").appendTo(e); 76 | } 77 | } 78 | } 79 | 80 | function NewGuyFormLoad() { 81 | seed = new Alea(); 82 | RollEm(); 83 | GenClick(); 84 | 85 | fill("#races", K.Races, "Race"); 86 | fill("#classes", K.Klasses, "Class"); 87 | 88 | if (document) { 89 | $("#Reroll").click(RerollClick); 90 | $("#Unroll").click(UnrollClick); 91 | $("#RandomName").click(GenClick); 92 | $('#Sold').click(sold); 93 | $('#quit').click(cancel); 94 | 95 | //var caption = 'Progress Quest - New Character'; 96 | //if (MainForm.GetHostName != '') 97 | // caption = caption + ' [' + MainForm.GetHostName + ']'; 98 | 99 | $("#Name").focus(); 100 | $("#Name").select(); 101 | } 102 | 103 | if (window.location.href.indexOf("?sold") > 0) 104 | sold(); // TODO: cheesy 105 | } 106 | 107 | 108 | if (document) 109 | $(document).ready(NewGuyFormLoad); 110 | 111 | /* Multiplayer: 112 | function TNewGuyForm_ParseSoldResponse(body) { 113 | if ((LowerCase(Split(body,0)) == 'ok')) { 114 | MainForm.SetPasskey(Split(body,1)); 115 | MainForm.SetLogin(GetAccount); 116 | MainForm.SetPassword(GetPassword); 117 | ModalResult = mrOk; 118 | } else { 119 | ShowMessage(body); 120 | } 121 | } 122 | 123 | function TNewGuyForm_GetAccount() { 124 | return Account.Visible ? Account.Text : ''; 125 | } 126 | 127 | function TNewGuyForm_GetPassword() { 128 | return (Password.Visible) ? Password.Text : ''; 129 | } 130 | 131 | function TNewGuyForm_SoldClick() { 132 | if (MainForm.GetHostAddr == '') { 133 | ModalResult = mrOk; 134 | } else { 135 | try { 136 | Screen.Cursor = crHourglass; 137 | try { 138 | if ((MainForm.Label8.Tag && 16) == 0 139 | ) url = MainForm.GetHostAddr 140 | else url = 'http://www.progressquest.com/create.php?'; 141 | // url = StringReplace(url, '.com/', '.com/dev/', []); 142 | if ((GetAccount() != '') || (GetPassword != '')) 143 | url = StuffString(url, 8, 0, GetAccount() + ':' + GetPassword() + '@'); 144 | args = 'cmd=create' + 145 | '&name=' + escape(Name.Text) + 146 | '&realm=' + escape(MainForm.GetHostName) + 147 | RevString; 148 | ParseSoldResponse(DownloadString(url + args)); 149 | } catch (EWebError) { 150 | ShowMessage('Error connecting to server'); 151 | } 152 | } finally { 153 | Screen.Cursor = crDefault; 154 | } 155 | } 156 | } 157 | */ 158 | 159 | function sold() { 160 | var newguy = { 161 | Traits: traits, 162 | dna: stats.seed, 163 | seed: stats.seed, 164 | birthday: ''+new Date(), 165 | birthstamp: +new Date(), 166 | Stats: stats, 167 | beststat: stats.best + " " + stats[stats.best], 168 | task: "", 169 | tasks: 0, 170 | elapsed: 0, 171 | bestequip: "Sharp Rock", 172 | Equips: {}, 173 | Inventory: [['Gold', 0]], 174 | Spells: [], 175 | act: 0, 176 | bestplot: "Prologue", 177 | Quests: [], 178 | questmonster: "", 179 | kill: "Loading....", 180 | ExpBar: { position: 0, max: LevelUpTime(1) }, 181 | EncumBar: { position: 0, max: stats.STR + 10 }, 182 | PlotBar: { position: 0, max: 26 }, 183 | QuestBar: { position: 0, max: 1 }, 184 | TaskBar: { position: 0, max: 2000 }, 185 | queue: [ 186 | 'task|10|Experiencing an enigmatic and foreboding night vision', 187 | "task|6|Much is revealed about that wise old bastard you'd underestimated", 188 | 'task|6|A shocking series of events leaves you alone and bewildered, but resolute', 189 | 'task|4|Drawing upon an unrealized reserve of determination, you set out on a long and dangerous journey', 190 | 'plot|2|Loading' 191 | ] 192 | }; 193 | 194 | if (document) { 195 | newguy.Traits.Name = $("#Name").val(); 196 | newguy.Traits.Race = $("input:radio[name=Race]:checked").val(); 197 | newguy.Traits.Class = $("input:radio[name=Class]:checked").val(); 198 | } 199 | newguy.Traits.Level = 1; 200 | 201 | newguy.date = newguy.birthday; 202 | newguy.stamp = newguy.birthstamp; 203 | 204 | $.each(K.Equips, function (i,equip) { newguy.Equips[equip] = ''; }); 205 | newguy.Equips.Weapon = newguy.bestequip; 206 | newguy.Equips.Hauberk = "-3 Burlap"; 207 | 208 | storage.addToRoster(newguy, function () { 209 | window.location.href = "main.html#" + escape(newguy.Traits.Name); 210 | }); 211 | } 212 | 213 | function cancel() { 214 | window.location.href = "roster.html"; 215 | } 216 | 217 | function GenClick() { 218 | traits.Name = GenerateName(); 219 | if (document) 220 | $("#Name").attr("value", traits.Name); 221 | } 222 | 223 | -------------------------------------------------------------------------------- /pas2js.sed: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sed -f 2 | 3 | # Convert Pascal syntax to Javascript 4 | 5 | s,{,/*,g 6 | s,},*/,g 7 | s/ = / == /g 8 | s/:=/=/g 9 | s/procedure/function/g 10 | s/begin/{/ 11 | s/end;/}/g 12 | s/end/}/g 13 | 14 | s/not/!/g 15 | s/ and / \&\& /g 16 | s/ or / || /g 17 | s/<>/!=/g 18 | s/ mod / % /g 19 | 20 | s/if /if (/g 21 | s/ then/)/g 22 | 23 | s/while /while (/g 24 | s/for /for (/g 25 | s/with /with (/g 26 | s/ do/)/ 27 | 28 | s/case /switch (/g 29 | /\Wcase\W/ s/ of/) {/g 30 | 31 | s/Result =/return/g 32 | 33 | /^function / s/): [A-Za-z][A-Za-z]*;/)/ 34 | /^function / s/: [A-Za-z][A-Za-z]*;/()/ 35 | #/^function / s/: [A-Za-z][A-Za-z]*//g 36 | 37 | /\Wfor\W/ s/ to /; i <= / 38 | /\Wfor\W/ s/)/; ++i)/ 39 | -------------------------------------------------------------------------------- /pq.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rudle/progress-quest/b5d3424a6115c07186576afb8e5c5c19fe0894bd/pq.gif -------------------------------------------------------------------------------- /progros.css: -------------------------------------------------------------------------------- 1 | /* RESET (via Boilerplate via Eric Meyer) */ 2 | 3 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, 4 | h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, 5 | code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, 6 | strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, 7 | ol, ul, li, fieldset, form, label, legend, table, caption, tbody, 8 | tfoot, thead, tr, th, td { 9 | margin: 0; 10 | padding: 0; 11 | border: 0; 12 | outline: 0; 13 | font-size: 100%; 14 | vertical-align: baseline; 15 | background: transparent; 16 | } 17 | body { line-height: 1; } 18 | ol, ul { list-style: none; } 19 | blockquote, q { quotes: none; } 20 | blockquote:before, blockquote:after, q:before, q:after { 21 | content: ''; content: none; 22 | } 23 | :focus { outline: 0; } 24 | ins { text-decoration: none; } 25 | del { text-decoration: line-through; } 26 | table { border-collapse: collapse; border-spacing: 0; } 27 | 28 | /* XULish */ 29 | 30 | .hbox > * { 31 | vertical-align: middle; 32 | } 33 | 34 | .vbox > * { 35 | display: block; 36 | } 37 | 38 | 39 | .hbox > *, .horizontal > *, td > * { 40 | display: inline-block; 41 | } 42 | 43 | 44 | .vbox { 45 | vertical-align: top; 46 | } 47 | 48 | td, th, .label, tr input, #titlebar, div { 49 | font-family: Tahoma, Helvetica, sans-serif; 50 | font-size: 8pt; 51 | } 52 | 53 | td, th { 54 | padding-left: 1ex; 55 | } 56 | 57 | th { 58 | border-bottom: solid 1px grey; 59 | background-color: #eee; 60 | text-align: left; 61 | font-weight: normal; 62 | } 63 | 64 | th:first-child { 65 | border-right: solid 1px grey; 66 | } 67 | 68 | 69 | #titlebar { 70 | background-color: #008; 71 | color: white; 72 | font-weight: bold; 73 | font-size: 9pt; 74 | padding-bottom: 4px; 75 | margin-bottom: 1ex; 76 | } 77 | 78 | #titlebar > img { 79 | vertical-align: bottom; 80 | margin: 2 0 0 2; 81 | } 82 | 83 | #quit { 84 | float: right; 85 | background: url(closedown.png); 86 | text-decoration: none; 87 | margin: 2px 2px 0 0; 88 | width: 16px; 89 | padding: 0; 90 | } 91 | 92 | #quit:active img { 93 | aopacity: 0; 94 | } 95 | 96 | .window { 97 | background-color: #e7e7e7; 98 | border: outset 2px silver; 99 | padding: 2px; 100 | text-align: left; 101 | display: inline-block; 102 | } 103 | 104 | 105 | body { 106 | background: url(back.jpg) no-repeat; 107 | background-size: 100% 100%; 108 | -webkit-background-size: 100% 100%; 109 | -o-background-size: 100% 100%; 110 | -moz-background-size: 100% 100%; 111 | background-attachment: fixed; 112 | text-align: center; 113 | margin-top: 1em; 114 | } 115 | 116 | div.scroll { 117 | overflow: scroll; 118 | height: 100px; 119 | width: 250px; 120 | overflow-x: hidden; 121 | } 122 | 123 | div.scroll > table { 124 | width: 234px; 125 | } 126 | 127 | .selected { 128 | background-color: yellow; 129 | } 130 | 131 | -------------------------------------------------------------------------------- /publish.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """Publish this game here to server""" 3 | 4 | import subprocess, sys 5 | 6 | manifest = [ 7 | "*.html", 8 | "json2.js", 9 | "config.js", 10 | "jquery.js", 11 | "main.js", 12 | "newguy.js", 13 | "roster.js", 14 | "*.gif", 15 | "back.jpg", 16 | "dicebar.jpg", 17 | "close*.png", 18 | "touch-icon.png", 19 | "ipad-ad.jpg", 20 | "*.css"] 21 | includes = sum(map(list, zip(["--include"] * len(manifest), manifest)), []) 22 | destination = "progressquest.com:www/progressquest.com/play/" 23 | 24 | print manifest 25 | args = ( 26 | ["rsync"] + 27 | sys.argv[1:] + 28 | [#"--dry-run", 29 | "--verbose", 30 | "--compress", 31 | "--checksum", 32 | "--recursive", 33 | "--partial", 34 | "--progress", 35 | #"--itemize-changes", 36 | "--delete", 37 | "--delete-excluded", 38 | "--times"] + 39 | includes + 40 | ["--exclude", "*", 41 | "./", 42 | destination]) 43 | 44 | print args 45 | output = subprocess.call(args) 46 | -------------------------------------------------------------------------------- /roster.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Progress Quest - Roster 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Progress Quest 16 | 17 | 18 |

Character Roster

19 | 20 |

21 |

22 | 23 | 24 | 25 | 30 | 31 |
26 | New Character
27 |
28 | 29 |
32 | 33 | 51 | 52 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /roster.js: -------------------------------------------------------------------------------- 1 | 2 | function load() { 3 | 4 | if (!window.localStorage) { 5 | roster.html("Hrumph: This browser does not support local storage. You can still play fast and loose: your character will live only as long as the game stays running in your browser."); 6 | return; 7 | } 8 | 9 | storage.loadRoster(loadGames); 10 | } 11 | 12 | function loadGames(games) { 13 | var roster = $("#roster"); 14 | roster.empty(); 15 | 16 | var newone = window.location.href.split('#')[1]; 17 | 18 | var count = 0; 19 | 20 | $.each(games, function (key, c) { 21 | var name = c.Traits.Name; 22 | 23 | var br = brag(c); 24 | roster.append(br); 25 | br.find("a.go").attr("href", "main.html#" + escape(name)); 26 | 27 | br.find("a.x").click(function () { 28 | if (confirm("Terminate " + Pick(["faithful","noble","loyal","brave"])+ 29 | " " + name + "?")) { 30 | delete games[name]; 31 | storage.storeRoster(games); 32 | load(); 33 | } 34 | }); 35 | 36 | br.find("a.sheet").click(function () { 37 | alert(template($("#sheet").html(), games[name])); 38 | // TODO: put in a window or whatev 39 | }); 40 | 41 | if (name === newone) 42 | br.addClass("lit"); 43 | 44 | /* 45 | var p = $("

"); 46 | p.appendTo(roster); 47 | p.text(JSON.stringify(c)); 48 | */ 49 | 50 | ++count; 51 | }); 52 | if (!count) 53 | roster.html("Games you start can be loaded from this page, but no saved games were found. Roll up a new character to get started."); 54 | } 55 | 56 | 57 | function brag(sheet) { 58 | var brag = $(template($("#badge").html(), sheet)); 59 | if (sheet.motto) 60 | brag.find(".bs").text('"' + sheet.motto + '"'); 61 | return brag; 62 | } 63 | 64 | function clearRoster() { 65 | storage.storeRoster({}, load); 66 | } 67 | 68 | $(document).ready(function () { 69 | 70 | load(); 71 | 72 | $("#roll").click(function () { 73 | window.location = "newguy.html"; 74 | }); 75 | 76 | $("#test").click(function () { 77 | window.location = "newguy.html?sold"; 78 | }); 79 | 80 | $("#clear").click(clearRoster); 81 | }); 82 | -------------------------------------------------------------------------------- /screenshot.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rudle/progress-quest/b5d3424a6115c07186576afb8e5c5c19fe0894bd/screenshot.bmp -------------------------------------------------------------------------------- /screenshot2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rudle/progress-quest/b5d3424a6115c07186576afb8e5c5c19fe0894bd/screenshot2.bmp -------------------------------------------------------------------------------- /shell.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2009 the V8 project authors. All rights reserved. 2 | // Redistribution and use in source and binary forms, with or without 3 | // modification, are permitted provided that the following conditions are 4 | // met: 5 | // 6 | // * Redistributions of source code must retain the above copyright 7 | // notice, this list of conditions and the following disclaimer. 8 | // * Redistributions in binary form must reproduce the above 9 | // copyright notice, this list of conditions and the following 10 | // disclaimer in the documentation and/or other materials provided 11 | // with the distribution. 12 | // * Neither the name of Google Inc. nor the names of its 13 | // contributors may be used to endorse or promote products derived 14 | // from this software without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | void RunShell(v8::Handle context); 36 | bool ExecuteString(v8::Handle source, 37 | v8::Handle name, 38 | bool print_result, 39 | bool report_exceptions); 40 | v8::Handle Print(const v8::Arguments& args); 41 | v8::Handle Read(const v8::Arguments& args); 42 | v8::Handle Write(const v8::Arguments& args); 43 | v8::Handle Load(const v8::Arguments& args); 44 | v8::Handle Quit(const v8::Arguments& args); 45 | v8::Handle Version(const v8::Arguments& args); 46 | v8::Handle ReadFile(const char* name); 47 | void ReportException(v8::TryCatch* handler); 48 | 49 | 50 | int RunMain(int argc, char* argv[]) { 51 | v8::V8::SetFlagsFromCommandLine(&argc, argv, true); 52 | v8::HandleScope handle_scope; 53 | // Create a template for the global object. 54 | v8::Handle global = v8::ObjectTemplate::New(); 55 | // Bind the global 'print' function to the C++ Print callback. 56 | global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print)); 57 | // Bind the global 'read' function to the C++ Read callback. 58 | global->Set(v8::String::New("read"), v8::FunctionTemplate::New(Read)); 59 | // Bind the global 'write' function to the C++ Write callback. 60 | global->Set(v8::String::New("write"), v8::FunctionTemplate::New(Write)); 61 | // Bind the global 'load' function to the C++ Load callback. 62 | global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load)); 63 | // Bind the 'quit' function 64 | global->Set(v8::String::New("quit"), v8::FunctionTemplate::New(Quit)); 65 | // Bind the 'version' function 66 | global->Set(v8::String::New("version"), v8::FunctionTemplate::New(Version)); 67 | // Create a new execution environment containing the built-in 68 | // functions 69 | v8::Handle context = v8::Context::New(NULL, global); 70 | // Enter the newly created execution environment. 71 | v8::Context::Scope context_scope(context); 72 | bool run_shell = (argc == 1); 73 | for (int i = 1; i < argc; i++) { 74 | const char* str = argv[i]; 75 | if (strcmp(str, "--shell") == 0) { 76 | run_shell = true; 77 | } else if (strcmp(str, "-f") == 0) { 78 | // Ignore any -f flags for compatibility with the other stand- 79 | // alone JavaScript engines. 80 | continue; 81 | } else if (strncmp(str, "--", 2) == 0) { 82 | printf("Warning: unknown flag %s.\nTry --help for options\n", str); 83 | } else if (strcmp(str, "-e") == 0 && i + 1 < argc) { 84 | // Execute argument given to -e option directly 85 | v8::HandleScope handle_scope; 86 | v8::Handle file_name = v8::String::New("unnamed"); 87 | v8::Handle source = v8::String::New(argv[i + 1]); 88 | if (!ExecuteString(source, file_name, false, true)) 89 | return 1; 90 | i++; 91 | } else { 92 | // Use all other arguments as names of files to load and run. 93 | v8::HandleScope handle_scope; 94 | v8::Handle file_name = v8::String::New(str); 95 | v8::Handle source = ReadFile(str); 96 | if (source.IsEmpty()) { 97 | printf("Error reading '%s'\n", str); 98 | return 1; 99 | } 100 | if (!ExecuteString(source, file_name, false, true)) 101 | return 1; 102 | } 103 | } 104 | if (run_shell) RunShell(context); 105 | return 0; 106 | } 107 | 108 | 109 | int main(int argc, char* argv[]) { 110 | int result = RunMain(argc, argv); 111 | v8::V8::Dispose(); 112 | return result; 113 | } 114 | 115 | 116 | // Extracts a C string from a V8 Utf8Value. 117 | const char* ToCString(const v8::String::Utf8Value& value) { 118 | return *value ? *value : ""; 119 | } 120 | 121 | 122 | // The callback that is invoked by v8 whenever the JavaScript 'print' 123 | // function is called. Prints its arguments on stdout separated by 124 | // spaces and ending with a newline. 125 | v8::Handle Print(const v8::Arguments& args) { 126 | bool first = true; 127 | for (int i = 0; i < args.Length(); i++) { 128 | v8::HandleScope handle_scope; 129 | if (first) { 130 | first = false; 131 | } else { 132 | printf(" "); 133 | } 134 | v8::String::Utf8Value str(args[i]); 135 | const char* cstr = ToCString(str); 136 | printf("%s", cstr); 137 | } 138 | printf("\n"); 139 | fflush(stdout); 140 | return v8::Undefined(); 141 | } 142 | 143 | 144 | // The callback that is invoked by v8 whenever the JavaScript 'read' 145 | // function is called. This function loads the content of the file named in 146 | // the argument into a JavaScript string. 147 | v8::Handle Read(const v8::Arguments& args) { 148 | if (args.Length() != 1) { 149 | return v8::ThrowException(v8::String::New("Bad parameters")); 150 | } 151 | v8::String::Utf8Value file(args[0]); 152 | if (*file == NULL) { 153 | return v8::ThrowException(v8::String::New("Error loading file")); 154 | } 155 | v8::Handle source = ReadFile(*file); 156 | if (source.IsEmpty()) { 157 | return v8::ThrowException(v8::String::New("Error loading file")); 158 | } 159 | return source; 160 | } 161 | 162 | // The callback that is invoked by v8 whenever the JavaScript 'read' 163 | // function is called. This function loads the content of the file named in 164 | // the argument into a JavaScript string. 165 | v8::Handle Write(const v8::Arguments& args) { 166 | if (args.Length() != 2) { 167 | return v8::ThrowException(v8::String::New("Bad parameters")); 168 | } 169 | v8::String::Utf8Value filename(args[0]); 170 | if (*filename == NULL) { 171 | return v8::ThrowException(v8::String::New("Error writing file")); 172 | } else { 173 | v8::HandleScope handle_scope; 174 | 175 | v8::String::Utf8Value str(args[1]); 176 | const char* cstr = ToCString(str); 177 | 178 | FILE* file = fopen(*filename, "wb"); 179 | fwrite(cstr, 1, strlen(cstr), file); 180 | fclose(file); 181 | } 182 | return v8::Undefined(); 183 | } 184 | 185 | 186 | // The callback that is invoked by v8 whenever the JavaScript 'load' 187 | // function is called. Loads, compiles and executes its argument 188 | // JavaScript file. 189 | v8::Handle Load(const v8::Arguments& args) { 190 | for (int i = 0; i < args.Length(); i++) { 191 | v8::HandleScope handle_scope; 192 | v8::String::Utf8Value file(args[i]); 193 | if (*file == NULL) { 194 | return v8::ThrowException(v8::String::New("Error loading file")); 195 | } 196 | v8::Handle source = ReadFile(*file); 197 | if (source.IsEmpty()) { 198 | return v8::ThrowException(v8::String::New("Error loading file")); 199 | } 200 | if (!ExecuteString(source, v8::String::New(*file), false, true)) { 201 | return v8::ThrowException(v8::String::New("Error executing file")); 202 | } 203 | } 204 | return v8::Undefined(); 205 | } 206 | 207 | 208 | // The callback that is invoked by v8 whenever the JavaScript 'quit' 209 | // function is called. Quits. 210 | v8::Handle Quit(const v8::Arguments& args) { 211 | // If not arguments are given args[0] will yield undefined which 212 | // converts to the integer value 0. 213 | int exit_code = args[0]->Int32Value(); 214 | exit(exit_code); 215 | return v8::Undefined(); 216 | } 217 | 218 | 219 | v8::Handle Version(const v8::Arguments& args) { 220 | return v8::String::New(v8::V8::GetVersion()); 221 | } 222 | 223 | 224 | // Reads a file into a v8 string. 225 | v8::Handle ReadFile(const char* name) { 226 | FILE* file = fopen(name, "rb"); 227 | if (file == NULL) return v8::Handle(); 228 | 229 | fseek(file, 0, SEEK_END); 230 | int size = ftell(file); 231 | rewind(file); 232 | 233 | char* chars = new char[size + 1]; 234 | chars[size] = '\0'; 235 | for (int i = 0; i < size;) { 236 | int read = fread(&chars[i], 1, size - i, file); 237 | i += read; 238 | } 239 | fclose(file); 240 | v8::Handle result = v8::String::New(chars, size); 241 | delete[] chars; 242 | return result; 243 | } 244 | 245 | 246 | // The read-eval-execute loop of the shell. 247 | void RunShell(v8::Handle context) { 248 | printf("V8 version %s\n", v8::V8::GetVersion()); 249 | static const int kBufferSize = 256; 250 | while (true) { 251 | char buffer[kBufferSize]; 252 | printf("> "); 253 | char* str = fgets(buffer, kBufferSize, stdin); 254 | if (str == NULL) break; 255 | v8::HandleScope handle_scope; 256 | ExecuteString(v8::String::New(str), 257 | v8::String::New("(shell)"), 258 | true, 259 | true); 260 | } 261 | printf("\n"); 262 | } 263 | 264 | 265 | // Executes a string within the current v8 context. 266 | bool ExecuteString(v8::Handle source, 267 | v8::Handle name, 268 | bool print_result, 269 | bool report_exceptions) { 270 | v8::HandleScope handle_scope; 271 | v8::TryCatch try_catch; 272 | v8::Handle script = v8::Script::Compile(source, name); 273 | if (script.IsEmpty()) { 274 | // Print errors that happened during compilation. 275 | if (report_exceptions) 276 | ReportException(&try_catch); 277 | return false; 278 | } else { 279 | v8::Handle result = script->Run(); 280 | if (result.IsEmpty()) { 281 | // Print errors that happened during execution. 282 | if (report_exceptions) 283 | ReportException(&try_catch); 284 | return false; 285 | } else { 286 | if (print_result && !result->IsUndefined()) { 287 | // If all went well and the result wasn't undefined then print 288 | // the returned value. 289 | v8::String::Utf8Value str(result); 290 | const char* cstr = ToCString(str); 291 | printf("%s\n", cstr); 292 | } 293 | return true; 294 | } 295 | } 296 | } 297 | 298 | 299 | void ReportException(v8::TryCatch* try_catch) { 300 | v8::HandleScope handle_scope; 301 | v8::String::Utf8Value exception(try_catch->Exception()); 302 | const char* exception_string = ToCString(exception); 303 | v8::Handle message = try_catch->Message(); 304 | if (message.IsEmpty()) { 305 | // V8 didn't provide any extra information about this error; just 306 | // print the exception. 307 | printf("%s\n", exception_string); 308 | } else { 309 | // Print (filename):(line number): (message). 310 | v8::String::Utf8Value filename(message->GetScriptResourceName()); 311 | const char* filename_string = ToCString(filename); 312 | int linenum = message->GetLineNumber(); 313 | printf("%s:%i: %s\n", filename_string, linenum, exception_string); 314 | // Print line of source code. 315 | v8::String::Utf8Value sourceline(message->GetSourceLine()); 316 | const char* sourceline_string = ToCString(sourceline); 317 | printf("%s\n", sourceline_string); 318 | // Print wavy underline (GetUnderline is deprecated). 319 | int start = message->GetStartColumn(); 320 | for (int i = 0; i < start; i++) { 321 | printf(" "); 322 | } 323 | int end = message->GetEndColumn(); 324 | for (int i = start; i < end; i++) { 325 | printf("^"); 326 | } 327 | printf("\n"); 328 | v8::String::Utf8Value stack_trace(try_catch->StackTrace()); 329 | if (stack_trace.length() > 0) { 330 | const char* stack_trace_string = ToCString(stack_trace); 331 | printf("%s\n", stack_trace_string); 332 | } 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /sim.js: -------------------------------------------------------------------------------- 1 | // This is an in-console simulation of Progress Quest. It's rather a 2 | // hack job. 3 | // 4 | // Run it with node.js like so: 5 | // $ node sim.js 6 | // or using a modified v8 shell, like so: 7 | // $ git clone git://github.com/grumdrig/v8 8 | // $ (cd v8; scons sample=shell) 9 | // $ v8/shell sim.js 10 | // 11 | // It's not realtime - simulation runs at maximum speed and the 12 | // virtual time elapsed is displayed at each level-up. 13 | 14 | var CHARACTER = "Shienzid"; 15 | //var guy = window.location.href.split("#")[1]; 16 | 17 | var window = { 18 | location: {href: "#Woogle"}, 19 | 20 | localStorage: { 21 | items: null, 22 | 23 | getItem: function(key) { 24 | if (!this.items) { 25 | try { 26 | this.items = JSON.parse(read("local.storage")); 27 | } catch (e) { 28 | print(e); 29 | this.items = {}; 30 | } 31 | } 32 | return this.items[key]; 33 | }, 34 | 35 | setItem: function (key, value) { 36 | this.items[key] = value; 37 | write("local.storage", JSON.stringify(this.items)); 38 | }, 39 | 40 | removeItem: function (key) { 41 | delete this.items[key]; 42 | write("local.storage", JSON.stringify(this.items)); 43 | } 44 | }, 45 | }; 46 | 47 | var navigator = { userAgent: "v8" }; 48 | 49 | var location = window.location; 50 | 51 | var $ = function () { return null; }; 52 | $.isFunction = function (obj) { 53 | return toString.call(obj) === "[object Function]"; 54 | }, 55 | $.isArray = function (obj) { 56 | return toString.call(obj) === "[object Array]"; 57 | } 58 | $.each = function (object, callback) { 59 | var name, i = 0, 60 | length = object.length, 61 | isObj = length === undefined || $.isFunction(object); 62 | 63 | if (isObj) { 64 | for (name in object) { 65 | if (callback.call(object[name], name, object[name]) === false) 66 | break; 67 | } 68 | } else { 69 | for (var value = object[0]; 70 | i < length && callback.call(value, i, value) !== false; 71 | value = object[++i]) { } 72 | } 73 | return object; 74 | } 75 | 76 | var document = null; 77 | 78 | if (typeof process !== "undefined") { 79 | // Node 80 | var fs = require("fs"); 81 | var sys = require("sys"); 82 | var load = function (filename) { 83 | var content = fs.readFileSync(filename); 84 | require("vm").runInThisContext(content, filename); 85 | //global.eval.call(global, String(content)); 86 | }; 87 | var print = function () { 88 | for (var i = 0, len = arguments.length; i < len; ++i) { 89 | sys.print(arguments[i] + " "); 90 | } 91 | sys.puts(""); 92 | }; 93 | var read = function (f) { return fs.readFileSync(f); }; 94 | var write = function (f,c) { fs.writeFileSync(f,c); }; 95 | 96 | global.window = window; 97 | global.document = document; 98 | global.navigator = navigator; 99 | global.$ = $; 100 | print("node"); 101 | } else { 102 | // V8 shell 103 | global = this; 104 | print("v8"); 105 | } 106 | 107 | var alert = global.alert = function (m) { print("ALERT: " + m); }; 108 | 109 | var now = 0; 110 | var timers = [{}]; 111 | var setInterval = global.setInterval = function (callback, interval) { 112 | timers.push({callback:callback, interval:interval}); 113 | return timers.length-1; 114 | }; 115 | var setTimeout = global.setTimeout = setInterval; // TODO: distinguish! 116 | 117 | load("config.js"); 118 | 119 | var cs = 0; 120 | storage.loadRoster(function (cs) { for (var c in cs) cs++; }); 121 | print(cs, "characters"); 122 | 123 | 124 | var timeGetTime = global.timeGetTime = function () { 125 | return now; 126 | } 127 | 128 | load("main.js"); 129 | FormCreate(); 130 | 131 | function charsheet(game) { 132 | print(game.Traits.Name, 133 | game.Traits.Level, 134 | game.tasks, 135 | RoughTime(game.elapsed)); 136 | } 137 | 138 | // It takes 18 sec to simulate 18 hours of play when I just checked (to 139 | // level 10) 140 | 141 | for (var j = 1, t = 0; j < 1001; ++j) { 142 | t += LevelUpTime(j); 143 | if (j % 100 == 0) 144 | print(j, RoughTime(LevelUpTime(j))+",", RoughTime(t)); 145 | } 146 | 147 | var tmpl = read("charsheet.txt"); 148 | storage.loadSheet(CHARACTER, function (sheet) { 149 | if (!sheet) { 150 | load("newguy.js"); 151 | NewGuyFormLoad(); 152 | traits.Name = CHARACTER; 153 | sold(); 154 | sheet = storage.games[CHARACTER]; 155 | } 156 | //write("local.storage", JSON.stringify(window.localStorage.items)); 157 | 158 | game = sheet; 159 | LoadGame(game); 160 | print(template(''+tmpl, sheet)); 161 | 162 | var l = 0; 163 | for (var i = 0; i < 1000000000; ++i) { 164 | if (game.Traits.Level != l) { 165 | SaveGame(); 166 | charsheet(game); 167 | l = game.Traits.Level; 168 | //if (l >= 5) break; 169 | } 170 | //assert(timers.length == 2);// TODO: this is for simplicity 171 | now += timers[1].interval; 172 | timers[1].callback(); 173 | } 174 | }); 175 | 176 | -------------------------------------------------------------------------------- /swords.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rudle/progress-quest/b5d3424a6115c07186576afb8e5c5c19fe0894bd/swords.bmp -------------------------------------------------------------------------------- /swords.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rudle/progress-quest/b5d3424a6115c07186576afb8e5c5c19fe0894bd/swords.gif -------------------------------------------------------------------------------- /touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rudle/progress-quest/b5d3424a6115c07186576afb8e5c5c19fe0894bd/touch-icon.png -------------------------------------------------------------------------------- /touch-icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rudle/progress-quest/b5d3424a6115c07186576afb8e5c5c19fe0894bd/touch-icon.psd --------------------------------------------------------------------------------