summaryrefslogtreecommitdiff
path: root/orig/pq-web/main.js
diff options
context:
space:
mode:
Diffstat (limited to 'orig/pq-web/main.js')
-rw-r--r--orig/pq-web/main.js1187
1 files changed, 1187 insertions, 0 deletions
diff --git a/orig/pq-web/main.js b/orig/pq-web/main.js
new file mode 100644
index 0000000..6395bbb
--- /dev/null
+++ b/orig/pq-web/main.js
@@ -0,0 +1,1187 @@
+// Copyright (c)2002-2010 Eric Fredricksen <e@fredricksen.net> all rights reserved
+
+var game = {};
+var lasttick, timerid;
+
+function timeGetTime() {
+ return new Date().getTime();
+}
+
+function StartTimer() {
+ if (!timerid) {
+ lasttick = timeGetTime();
+ timerid = setTimeout(Timer1Timer, 100);
+ }
+}
+
+function StopTimer() {
+ clearTimeout(timerid);
+ timerid = null;
+}
+
+function Q(s) {
+ game.queue.push(s);
+ Dequeue();
+}
+
+function TaskDone() {
+ return TaskBar.done();
+}
+
+function Odds(chance, outof) {
+ return Random(outof) < chance;
+}
+
+function RandSign() {
+ return Random(2) * 2 - 1;
+}
+
+function RandomLow(below) {
+ return Min(Random(below), Random(below));
+}
+
+function PickLow(s) {
+ return s[RandomLow(s.length)];
+}
+
+function Copy(s, b, l) {
+ return s.substr(b-1, l);
+}
+
+function Length(s) {
+ return s.length;
+}
+
+function Starts(s, pre) {
+ return 0 === s.indexOf(pre);
+}
+
+function Ends(s, e) {
+ return Copy(s, 1+Length(s)-Length(e), Length(e)) == e;
+}
+
+function Plural(s) {
+ if (Ends(s,'y'))
+ return Copy(s,1,Length(s)-1) + 'ies';
+ else if (Ends(s,'us'))
+ return Copy(s,1,Length(s)-2) + 'i';
+ else if (Ends(s,'ch') || Ends(s,'x') || Ends(s,'s') || Ends(s, 'sh'))
+ return s + 'es';
+ else if (Ends(s,'f'))
+ return Copy(s,1,Length(s)-1) + 'ves';
+ else if (Ends(s,'man') || Ends(s,'Man'))
+ return Copy(s,1,Length(s)-2) + 'en';
+ else return s + 's';
+}
+
+function Split(s, field, separator) {
+ return s.split(separator || "|")[field];
+}
+
+function Indefinite(s, qty) {
+ if (qty == 1) {
+ if (Pos(s.charAt(0), 'AEIOUÜaeiouü') > 0)
+ return 'an ' + s;
+ else
+ return 'a ' + s;
+ } else {
+ return IntToStr(qty) + ' ' + Plural(s);
+ }
+}
+
+function Definite(s, qty) {
+ if (qty > 1)
+ s = Plural(s);
+ return 'the ' + s;
+}
+
+function prefix(a, m, s, sep) {
+ if (sep == undefined) sep = ' ';
+ m = Abs(m);
+ if (m < 1 || m > a.length) return s; // In case of screwups
+ return a[m-1] + sep + s;
+}
+
+function Sick(m, s) {
+ m = 6 - Abs(m);
+ return prefix(['dead','comatose','crippled','sick','undernourished'], m, s);
+}
+
+
+function Young(m, s) {
+ m = 6 - Abs(m);
+ return prefix(['foetal','baby','preadolescent','teenage','underage'], m, s);
+}
+
+
+function Big(m, s) {
+ return prefix(['greater','massive','enormous','giant','titanic'], m, s);
+}
+
+function Special(m, s) {
+ if (Pos(' ', s) > 0)
+ return prefix(['veteran','cursed','warrior','undead','demon'], m, s);
+ else
+ return prefix(['Battle-','cursed ','Were-','undead ','demon '], m, s, '');
+}
+
+function InterplotCinematic() {
+ switch (Random(3)) {
+ case 0:
+ Q('task|1|Exhausted, you arrive at a friendly oasis in a hostile land');
+ Q('task|2|You greet old friends and meet new allies');
+ Q('task|2|You are privy to a council of powerful do-gooders');
+ Q('task|1|There is much to be done. You are chosen!');
+ break;
+ case 1:
+ Q('task|1|Your quarry is in sight, but a mighty enemy bars your path!');
+ var nemesis = NamedMonster(GetI(Traits,'Level')+3);
+ Q('task|4|A desperate struggle commences with ' + nemesis);
+ var s = Random(3);
+ for (var i = 1; i <= Random(1 + game.act + 1); ++i) {
+ s += 1 + Random(2);
+ switch (s % 3) {
+ case 0: Q('task|2|Locked in grim combat with ' + nemesis); break;
+ case 1: Q('task|2|' + nemesis + ' seems to have the upper hand'); break;
+ case 2: Q('task|2|You seem to gain the advantage over ' + nemesis); break;
+ }
+ }
+ Q('task|3|Victory! ' + nemesis + ' is slain! Exhausted, you lose conciousness');
+ Q('task|2|You awake in a friendly place, but the road awaits');
+ break;
+ case 2:
+ var nemesis2 = ImpressiveGuy();
+ Q("task|2|Oh sweet relief! You've reached the protection of the good " + nemesis2);
+ Q('task|3|There is rejoicing, and an unnerving encouter with ' + nemesis2 + ' in private');
+ Q('task|2|You forget your ' + BoringItem() + ' and go back to get it');
+ Q("task|2|What's this!? You overhear something shocking!");
+ Q('task|2|Could ' + nemesis2 + ' be a dirty double-dealer?');
+ Q('task|3|Who can possibly be trusted with this news!? ... Oh yes, of course');
+ break;
+ }
+ Q('plot|1|Loading');
+}
+
+
+function StrToInt(s) {
+ return parseInt(s, 10);
+}
+
+function IntToStr(i) {
+ return i + "";
+}
+
+function NamedMonster(level) {
+ var lev = 0;
+ var result = '';
+ for (var i = 0; i < 5; ++i) {
+ var m = Pick(K.Monsters);
+ if (!result || (Abs(level-StrToInt(Split(m,1))) < Abs(level-lev))) {
+ result = Split(m,0);
+ lev = StrToInt(Split(m,1));
+ }
+ }
+ return GenerateName() + ' the ' + result;
+}
+
+function ImpressiveGuy() {
+ return Pick(K.ImpressiveTitles) +
+ (Random(2) ? ' of the ' + Pick(K.Races) : ' of ' + GenerateName());
+}
+
+function MonsterTask(level) {
+ var definite = false;
+ for (var i = level; i >= 1; --i) {
+ if (Odds(2,5))
+ level += RandSign();
+ }
+ if (level < 1) level = 1;
+ // level = level of puissance of opponent(s) we'll return
+
+ var monster, lev;
+ if (Odds(1,25)) {
+ // Use an NPC every once in a while
+ monster = ' ' + Split(Pick(K.Races), 0);
+ if (Odds(1,2)) {
+ monster = 'passing' + monster + ' ' + Split(Pick(K.Klasses), 0);
+ } else {
+ monster = PickLow(K.Titles) + ' ' + GenerateName() + ' the' + monster;
+ definite = true;
+ }
+ lev = level;
+ monster = monster + '|' + IntToStr(level) + '|*';
+ } else if (game.questmonster && Odds(1,4)) {
+ // Use the quest monster
+ monster = K.Monsters[game.questmonsterindex];
+ lev = StrToInt(Split(monster,1));
+ } else {
+ // Pick the monster out of so many random ones closest to the level we want
+ monster = Pick(K.Monsters);
+ lev = StrToInt(Split(monster,1));
+ for (var ii = 0; ii < 5; ++ii) {
+ var m1 = Pick(K.Monsters);
+ if (Abs(level-StrToInt(Split(m1,1))) < Abs(level-lev)) {
+ monster = m1;
+ lev = StrToInt(Split(monster,1));
+ }
+ }
+ }
+
+ var result = Split(monster,0);
+ game.task = 'kill|' + monster;
+
+ var qty = 1;
+ if (level-lev > 10) {
+ // lev is too low. multiply...
+ qty = Math.floor((level + Random(lev)) / Max(lev,1));
+ if (qty < 1) qty = 1;
+ level = Math.floor(level / qty);
+ }
+
+ if ((level - lev) <= -10) {
+ result = 'imaginary ' + result;
+ } else if ((level-lev) < -5) {
+ i = 10+(level-lev);
+ i = 5-Random(i+1);
+ result = Sick(i,Young((lev-level)-i,result));
+ } else if (((level-lev) < 0) && (Random(2) == 1)) {
+ result = Sick(level-lev,result);
+ } else if (((level-lev) < 0)) {
+ result = Young(level-lev,result);
+ } else if ((level-lev) >= 10) {
+ result = 'messianic ' + result;
+ } else if ((level-lev) > 5) {
+ i = 10-(level-lev);
+ i = 5-Random(i+1);
+ result = Big(i,Special((level-lev)-i,result));
+ } else if (((level-lev) > 0) && (Random(2) == 1)) {
+ result = Big(level-lev,result);
+ } else if (((level-lev) > 0)) {
+ result = Special(level-lev,result);
+ }
+
+ lev = level;
+ level = lev * qty;
+
+ if (!definite) result = Indefinite(result, qty);
+ return { 'description': result, 'level': level };
+}
+
+function LowerCase(s) {
+ return s.toLowerCase();
+}
+
+function ProperCase(s) {
+ return Copy(s,1,1).toUpperCase() + Copy(s,2,10000);
+}
+
+function EquipPrice() {
+ return 5 * GetI(Traits,'Level') * GetI(Traits,'Level') +
+ 10 * GetI(Traits,'Level') +
+ 20;
+}
+
+function Dequeue() {
+ while (TaskDone()) {
+ if (Split(game.task,0) == 'kill') {
+ if (Split(game.task,3) == '*') {
+ WinItem();
+ } else if (Split(game.task,3)) {
+ Add(Inventory,LowerCase(Split(game.task,1) + ' ' +
+ ProperCase(Split(game.task,3))),1);
+ }
+ } else if (game.task == 'buying') {
+ // buy some equipment
+ Add(Inventory,'Gold',-EquipPrice());
+ WinEquip();
+ } else if ((game.task == 'market') || (game.task == 'sell')) {
+ if (game.task == 'sell') {
+ var amt = GetI(Inventory, 1) * GetI(Traits,'Level');
+ if (Pos(' of ', Inventory.label(1)) > 0)
+ amt *= (1+RandomLow(10)) * (1+RandomLow(GetI(Traits,'Level')));
+ Inventory.remove1();
+ Add(Inventory, 'Gold', amt);
+ }
+ if (Inventory.length() > 1) {
+ Inventory.scrollToTop();
+ Task('Selling ' + Indefinite(Inventory.label(1), GetI(Inventory,1)),
+ 1 * 1000);
+ game.task = 'sell';
+ break;
+ }
+ }
+
+ var old = game.task;
+ game.task = '';
+ if (game.queue.length > 0) {
+ var a = Split(game.queue[0],0);
+ var n = StrToInt(Split(game.queue[0],1));
+ var s = Split(game.queue[0],2);
+ if (a == 'task' || a == 'plot') {
+ game.queue.shift();
+ if (a == 'plot') {
+ CompleteAct();
+ s = 'Loading ' + game.bestplot;
+ }
+ Task(s, n * 1000);
+ } else {
+ throw 'bah!' + a;
+ }
+ } else if (EncumBar.done()) {
+ Task('Heading to market to sell loot',4 * 1000);
+ game.task = 'market';
+ } else if ((Pos('kill|',old) <= 0) && (old != 'heading')) {
+ if (GetI(Inventory, 'Gold') > EquipPrice()) {
+ Task('Negotiating purchase of better equipment', 5 * 1000);
+ game.task = 'buying';
+ } else {
+ Task('Heading to the killing fields', 4 * 1000);
+ game.task = 'heading';
+ }
+ } else {
+ var nn = GetI(Traits, 'Level');
+ var t = MonsterTask(nn);
+ var InventoryLabelAlsoGameStyleTag = 3;
+ nn = Math.floor((2 * InventoryLabelAlsoGameStyleTag * t.level * 1000) / nn);
+ Task('Executing ' + t.description, nn);
+ }
+ }
+}
+
+
+function Put(list, key, value) {
+ if (typeof key === typeof 1)
+ key = list.label(key);
+
+ if (list.fixedkeys) {
+ game[list.id][key] = value;
+ } else {
+ var i = 0;
+ for (; i < game[list.id].length; ++i) {
+ if (game[list.id][i][0] === key) {
+ game[list.id][i][1] = value;
+ break;
+ }
+ }
+ if (i == game[list.id].length)
+ game[list.id].push([key,value]);
+ }
+
+ list.PutUI(key, value);
+
+ if (key === 'STR')
+ EncumBar.reset(10 + value, EncumBar.Position());
+
+ if (list === Inventory) {
+ var cubits = 0;
+ $.each(game.Inventory.slice(1), function (index, item) {
+ cubits += StrToInt(item[1]);
+ });
+ EncumBar.reposition(cubits);
+ }
+}
+
+
+function ProgressBar(id, tmpl) {
+ this.id = id;
+ this.bar = $("#"+ id + " > .bar");
+ this.tmpl = tmpl;
+
+ this.Max = function () { return game[this.id].max; };
+ this.Position = function () { return game[this.id].position; };
+
+ this.reset = function (newmax, newposition) {
+ game[this.id].max = newmax;
+ this.reposition(newposition || 0);
+ };
+
+ this.reposition = function (newpos) {
+ game[this.id].position = Min(newpos, this.Max());
+
+ // Recompute hint
+ game[this.id].percent = (100 * this.Position()).div(this.Max());
+ game[this.id].remaining = Math.floor(this.Max() - this.Position());
+ game[this.id].time = RoughTime(this.Max() - this.Position());
+ game[this.id].hint = template(this.tmpl, game[this.id]);
+
+ // Update UI
+ if (this.bar) {
+ var p = this.Max() ? 100 * this.Position() / this.Max() : 0;
+ this.bar.css("width", p + "%");
+ this.bar.parent().find(".hint").text(game[this.id].hint);
+ }
+ };
+
+ this.increment = function (inc) {
+ this.reposition(this.Position() + inc);
+ };
+
+ this.done = function () {
+ return this.Position() >= this.Max();
+ };
+
+ this.load = function (game) {
+ this.reposition(this.Position());
+ };
+}
+
+
+
+function Key(tr) {
+ return $(tr).children().first().text();
+}
+
+function Value(tr) {
+ return $(tr).children().last().text();
+}
+
+
+
+function ListBox(id, columns, fixedkeys) {
+ this.id = id;
+ this.box = $("tbody#_, #_ tbody".replace(/_/g, id));
+ this.columns = columns;
+ this.fixedkeys = fixedkeys;
+
+ this.AddUI = function (caption) {
+ if (!this.box) return;
+ var tr = $("<tr><td><input type=checkbox disabled> " +
+ caption + "</td></tr>");
+ tr.appendTo(this.box);
+ tr.each(function () {this.scrollIntoView();});
+ return tr;
+ };
+
+ this.ClearSelection = function () {
+ if (this.box)
+ this.box.find("tr").removeClass("selected");
+ };
+
+ this.PutUI = function (key, value) {
+ if (!this.box) return;
+ var item = this.rows().filter(function (index) {
+ return Key(this) === key;
+ });
+ if (!item.length) {
+ item = $("<tr><td>" + key + "</td><td/></tr>");
+ this.box.append(item);
+ }
+
+ item.children().last().text(value);
+ item.addClass("selected");
+ item.each(function () {this.scrollIntoView();});
+ };
+
+ this.scrollToTop = function () {
+ if (this.box)
+ this.box.parents(".scroll").scrollTop(0);
+ };
+
+ this.rows = function () {
+ return this.box.find("tr").has("td");
+ };
+
+ this.CheckAll = function (butlast) {
+ if (this.box) {
+ if (butlast)
+ this.rows().find("input:checkbox").not(':last').attr("checked","true");
+ else
+ this.rows().find("input:checkbox").attr("checked","true");
+ }
+ };
+
+ this.length = function () {
+ return (this.fixedkeys || game[this.id]).length;
+ };
+
+ this.remove0 = function (n) {
+ if (game[this.id])
+ game[this.id].shift();
+ if (this.box)
+ this.box.find("tr").first().remove();
+ };
+
+ this.remove1 = function (n) {
+ var t = game[this.id].shift();
+ game[this.id].shift();
+ game[this.id].unshift(t);
+ if (this.box)
+ this.box.find("tr").eq(1).remove();
+ };
+
+
+ this.load = function (game) {
+ var that = this;
+ var dict = game[this.id];
+ if (this.fixedkeys) {
+ $.each(this.fixedkeys, function (index, key) {
+ that.PutUI(key, dict[key]);
+ });
+ } else {
+ $.each(dict, function (index, row) {
+ if (that.columns == 2)
+ that.PutUI(row[0], row[1]);
+ else
+ that.AddUI(row);
+ });
+ }
+ };
+
+
+ this.label = function (n) {
+ return this.fixedkeys ? this.fixedkeys[n] : game[this.id][n][0];
+ };
+}
+
+
+var ExpBar, PlotBar, TaskBar, QuestBar, EncumBar;
+var Traits,Stats,Spells,Equips,Inventory,Plots,Quests;
+var Kill;
+var AllBars, AllLists;
+
+
+function StrToIntDef(s, def) {
+ var result = parseInt(s, 10);
+ return isNaN(result) ? def : result;
+}
+
+
+if (document)
+ $(document).ready(FormCreate);
+
+
+function WinSpell() {
+ AddR(Spells, K.Spells[RandomLow(Min(GetI(Stats,'WIS')+GetI(Traits,'Level'),
+ K.Spells.length))], 1);
+}
+
+function LPick(list, goal) {
+ var result = Pick(list);
+ for (var i = 1; i <= 5; ++i) {
+ var best = StrToInt(Split(result, 1));
+ var s = Pick(list);
+ var b1 = StrToInt(Split(s,1));
+ if (Abs(goal-best) > Abs(goal-b1))
+ result = s;
+ }
+ return result;
+}
+
+function Abs(x) {
+ if (x < 0) return -x; else return x;
+}
+
+function WinEquip() {
+ var posn = Random(Equips.length());
+
+ if (!posn) {
+ stuff = K.Weapons;
+ better = K.OffenseAttrib;
+ worse = K.OffenseBad;
+ } else {
+ better = K.DefenseAttrib;
+ worse = K.DefenseBad;
+ stuff = (posn == 1) ? K.Shields: K.Armors;
+ }
+ var name = LPick(stuff, GetI(Traits,'Level'));
+ var qual = StrToInt(Split(name,1));
+ name = Split(name,0);
+ var plus = GetI(Traits,'Level') - qual;
+ if (plus < 0) better = worse;
+ var count = 0;
+ while (count < 2 && plus) {
+ var modifier = Pick(better);
+ qual = StrToInt(Split(modifier, 1));
+ modifier = Split(modifier, 0);
+ if (Pos(modifier, name) > 0) break; // no repeats
+ if (Abs(plus) < Abs(qual)) break; // too much
+ name = modifier + ' ' + name;
+ plus -= qual;
+ ++count;
+ }
+ if (plus) name = plus + ' ' + name;
+ if (plus > 0) name = '+' + name;
+
+ Put(Equips, posn, name);
+ game.bestequip = name;
+ if (posn > 1) game.bestequip += ' ' + Equips.label(posn);
+}
+
+
+function Square(x) { return x * x; }
+
+function WinStat() {
+ var i;
+ if (Odds(1,2)) {
+ i = Pick(K.Stats);
+ } else {
+ // Favor the best stat so it will tend to clump
+ var t = 0;
+ $.each(K.Stats, function (index, key) {
+ t += Square(GetI(Stats, key));
+ });
+ t = Random(t);
+ $.each(K.Stats, function (index, key) {
+ i = key;
+ t -= Square(GetI(Stats, key));
+ if (t < 0) return false;
+ });
+ }
+ Add(Stats, i, 1);
+}
+
+function SpecialItem() {
+ return InterestingItem() + ' of ' + Pick(K.ItemOfs);
+}
+
+function InterestingItem() {
+ return Pick(K.ItemAttrib) + ' ' + Pick(K.Specials);
+}
+
+function BoringItem() {
+ return Pick(K.BoringItems);
+}
+
+function WinItem() {
+ Add(Inventory, SpecialItem(), 1);
+}
+
+function CompleteQuest() {
+ QuestBar.reset(50 + RandomLow(1000));
+ if (Quests.length()) {
+ Log('Quest completed: ' + game.bestquest);
+ Quests.CheckAll();
+ [WinSpell,WinEquip,WinStat,WinItem][Random(4)]();
+ }
+ while (Quests.length() > 99)
+ Quests.remove0();
+
+ game.questmonster = '';
+ var caption;
+ switch (Random(5)) {
+ case 0:
+ var level = GetI(Traits,'Level');
+ var lev = 0;
+ for (var i = 1; i <= 4; ++i) {
+ var montag = Random(K.Monsters.length);
+ var m = K.Monsters[montag];
+ var l = StrToInt(Split(m,1));
+ if (i == 1 || Abs(l - level) < Abs(lev - level)) {
+ lev = l;
+ game.questmonster = m;
+ game.questmonsterindex = montag;
+ }
+ }
+ caption = 'Exterminate ' + Definite(Split(game.questmonster,0),2);
+ break;
+ case 1:
+ caption = 'Seek ' + Definite(InterestingItem(), 1);
+ break;
+ case 2:
+ caption = 'Deliver this ' + BoringItem();
+ break;
+ case 3:
+ caption = 'Fetch me ' + Indefinite(BoringItem(), 1);
+ break;
+ case 4:
+ var mlev = 0;
+ level = GetI(Traits,'Level');
+ for (var ii = 1; ii <= 2; ++ii) {
+ montag = Random(K.Monsters.length);
+ m = K.Monsters[montag];
+ l = StrToInt(Split(m,1));
+ if ((ii == 1) || (Abs(l - level) < Abs(mlev - level))) {
+ mlev = l;
+ game.questmonster = m;
+ }
+ }
+ caption = 'Placate ' + Definite(Split(game.questmonster,0),2);
+ game.questmonster = ''; // We're trying to placate them, after all
+ break;
+ }
+ if (!game.Quests) game.Quests = [];
+ while (game.Quests.length > 99) game.Quests.shift();
+ game.Quests.push(caption);
+ game.bestquest = caption;
+ Quests.AddUI(caption);
+
+
+ Log('Commencing quest: ' + caption);
+
+ SaveGame();
+}
+
+function toRoman(n) {
+ if (!n) return "N";
+ var s = "";
+ function _rome(dn,ds) {
+ if (n >= dn) {
+ n -= dn;
+ s += ds;
+ return true;
+ } else return false;
+ }
+ if (n < 0) {
+ s = "-";
+ n = -n;
+ }
+ while (_rome(1000,"M")) {0;}
+ _rome(900,"CM");
+ _rome(500,"D");
+ _rome(400,"CD");
+ while (_rome(100,"C")) {0;}
+ _rome(90,"XC");
+ _rome(50,"L");
+ _rome(40,"XL");
+ while (_rome(10,"X")) {0;}
+ _rome(9,"IX");
+ _rome(5,"V");
+ _rome(4,"IV");
+ while (_rome(1,"I")) {0;}
+ return s;
+}
+
+function toArabic(s) {
+ n = 0;
+ s = s.toUpperCase();
+ function _arab(ds,dn) {
+ if (!Starts(s, ds)) return false;
+ s = s.substr(ds.length);
+ n += dn;
+ return true;
+ }
+ while (_arab("M",1000)) {0;}
+ _arab("CM",900);
+ _arab("D",500);
+ _arab("CD",400);
+ while (_arab("C",100)) {0;}
+ _arab("XC",90);
+ _arab("L",50);
+ _arab("XL",40);
+ while (_arab("X",10)) {0;}
+ _arab("IX",9);
+ _arab("V",5);
+ _arab("IV",4);
+ while (_arab("I",1)) {0;}
+ return n;
+}
+
+function CompleteAct() {
+ Plots.CheckAll();
+ game.act += 1;
+ PlotBar.reset(60 * 60 * (1 + 5 * game.act)); // 1 hr + 5/act
+ Plots.AddUI((game.bestplot = 'Act ' + toRoman(game.act)));
+
+ if (game.act > 1) {
+ WinItem();
+ WinEquip();
+ }
+
+ Brag('act');
+}
+
+
+function Log(line) {
+ if (game.log)
+ game.log[+new Date()] = line;
+ // TODO: and now what?
+}
+
+function Task(caption, msec) {
+ game.kill = caption + "...";
+ if (Kill)
+ Kill.text(game.kill);
+ Log(game.kill);
+ TaskBar.reset(msec);
+}
+
+function Add(list, key, value) {
+ Put(list, key, value + GetI(list,key));
+
+ /*$IFDEF LOGGING*/
+ if (!value) return;
+ var line = (value > 0) ? "Gained" : "Lost";
+ if (key == 'Gold') {
+ key = "gold piece";
+ line = (value > 0) ? "Got paid" : "Spent";
+ }
+ if (value < 0) value = -value;
+ line = line + ' ' + Indefinite(key, value);
+ Log(line);
+ /*$ENDIF*/
+}
+
+function AddR(list, key, value) {
+ Put(list, key, toRoman(value + toArabic(Get(list,key))));
+}
+
+function Get(list, key) {
+ if (list.fixedkeys) {
+ if (typeof key === typeof 1)
+ key = list.fixedkeys[key];
+ return game[list.id][key];
+ } else if (typeof key === typeof 1) {
+ if (key < game[list.id].length)
+ return game[list.id][key][1];
+ else
+ return "";
+ } else {
+ for (var i = 0; i < game[list.id].length; ++i) {
+ if (game[list.id][i][0] === key)
+ return game[list.id][i][1];
+ }
+ return "";
+ }
+}
+
+function GetI(list, key) {
+ return StrToIntDef(Get(list,key), 0);
+}
+
+function Min(a,b) {
+ return a < b ? a : b;
+}
+
+function Max(a,b) {
+ return a > b ? a : b;
+}
+
+function LevelUp() {
+ Add(Traits,'Level',1);
+ Add(Stats,'HP Max', GetI(Stats,'CON').div(3) + 1 + Random(4));
+ Add(Stats,'MP Max', GetI(Stats,'INT').div(3) + 1 + Random(4));
+ WinStat();
+ WinStat();
+ WinSpell();
+ ExpBar.reset(LevelUpTime(GetI(Traits,'Level')));
+ Brag('level');
+}
+
+function ClearAllSelections() {
+ $.each(AllLists, function () {this.ClearSelection();});
+}
+
+function RoughTime(s) {
+ if (s < 120) return s + ' seconds';
+ else if (s < 60 * 120) return s.div(60) + ' minutes';
+ else if (s < 60 * 60 * 48) return s.div(3600) + ' hours';
+ else if (s < 60 * 60 * 24 * 60) return s.div(3600 * 24) + ' days';
+ else if (s < 60 * 60 * 24 * 30 * 24) return s.div(3600 * 24 * 30) +" months";
+ else return s.div(3600 * 24 * 30 * 12) + " years";
+
+}
+
+function Pos(needle, haystack) {
+ return haystack.indexOf(needle) + 1;
+}
+
+var dealing = false;
+
+function Timer1Timer() {
+ timerid = null; // Event has fired
+ if (TaskBar.done()) {
+ game.tasks += 1;
+ game.elapsed += TaskBar.Max().div(1000);
+
+ ClearAllSelections();
+
+ if (game.kill == 'Loading....')
+ TaskBar.reset(0); // Not sure if this is still the ticket
+
+ // gain XP / level up
+ var gain = Pos('kill|', game.task) == 1;
+ if (gain) {
+ if (ExpBar.done())
+ LevelUp();
+ else
+ ExpBar.increment(TaskBar.Max() / 1000);
+ }
+
+ // advance quest
+ if (gain && game.act >= 1) {
+ if (QuestBar.done() || !Quests.length()) {
+ CompleteQuest();
+ } else {
+ QuestBar.increment(TaskBar.Max() / 1000);
+ }
+ }
+
+ // advance plot
+ if (gain || !game.act) {
+ if (PlotBar.done())
+ InterplotCinematic();
+ else
+ PlotBar.increment(TaskBar.Max() / 1000);
+ }
+
+ Dequeue();
+ } else {
+ var elapsed = timeGetTime() - lasttick;
+ if (elapsed > 100) elapsed = 100;
+ if (elapsed < 0) elapsed = 0;
+ TaskBar.increment(elapsed);
+ }
+
+ StartTimer();
+}
+
+function FormCreate() {
+ ExpBar = new ProgressBar("ExpBar", "$remaining XP needed for next level");
+ EncumBar = new ProgressBar("EncumBar", "$position/$max cubits");
+ PlotBar = new ProgressBar("PlotBar", "$time remaining");
+ QuestBar = new ProgressBar("QuestBar", "$percent% complete");
+ TaskBar = new ProgressBar("TaskBar", "$percent%");
+
+ AllBars = [ExpBar,PlotBar,TaskBar,QuestBar,EncumBar];
+
+ Traits = new ListBox("Traits", 2, K.Traits);
+ Stats = new ListBox("Stats", 2, K.Stats);
+ Spells = new ListBox("Spells", 2);
+ Equips = new ListBox("Equips", 2, K.Equips);
+ Inventory = new ListBox("Inventory", 2);
+ Plots = new ListBox("Plots", 1);
+ Quests = new ListBox("Quests", 1);
+
+ Plots.load = function (sheet) {
+ for (var i = Max(0, game.act-99); i <= game.act; ++i)
+ this.AddUI(i ? 'Act ' + toRoman(i) : "Prologue");
+
+ };
+
+ AllLists = [Traits,Stats,Spells,Equips,Inventory,Plots,Quests];
+
+ if (document) {
+ Kill = $("#Kill");
+
+ $("#quit").click(quit);
+
+ $(document).keypress(FormKeyDown);
+
+ $(document).bind('beforeunload', function () {
+ if (!storage)
+ return "Are you sure you want to quit? All your progress will be lost!";
+ });
+
+ $(window).unload(function (event) {
+ StopTimer();
+ SaveGame();
+ if (storage.async) {
+ // Have to give SQL transaction a chance to complete
+ if (window.showModalDialog)
+ pause(100);
+
+ // Just accept some data loss - alert is too ugly. Maybe increase save
+ // frequency.
+ // else alert("Game saved");
+ }
+ });
+
+ if (iOS) $("body").addClass("iOS");
+ }
+
+ var name = unescape(window.location.href.split('#')[1]);
+ storage.loadSheet(name, LoadGame);
+}
+
+
+
+function pause(msec) {
+ window.showModalDialog("javascript:document.writeln ('<script>window.setTimeout(" +
+ "function () { window.close(); }," + msec + ");</script>')",
+ null,
+ "dialogWidth:0;dialogHeight:0;dialogHide:yes;unadorned:yes;"+
+ "status:no;scroll:no;center:no;dialogTop:-10000;dialogLeft:-10000");
+}
+
+function quit() {
+ $(window).unbind('unload');
+ SaveGame(function () { window.location.href = "roster.html"; });
+}
+
+
+function HotOrNot() {
+ // Figure out which spell is best
+ if (Spells.length()) {
+ var flat = 1; // Flattening constant
+ var best = 0, i;
+ for (i = 1; i < Spells.length(); ++i) {
+ if ((i+flat) * toArabic(Get(Spells,i)) >
+ (best+flat) * toArabic(Get(Spells,best)))
+ best = i;
+ }
+ game.bestspell = Spells.label(best) + ' ' + Get(Spells, best);
+ } else {
+ game.bestspell = '';
+ }
+
+ /// And which stat is best?
+ best = 0;
+ for (i = 1; i <= 5; ++i) {
+ if (GetI(Stats,i) > GetI(Stats,best))
+ best = i;
+ }
+ game.beststat = Stats.label(best) + ' ' + GetI(Stats, best);
+}
+
+
+function SaveGame(callback) {
+ Log('Saving game: ' + GameSaveName());
+ HotOrNot();
+ game.date = ''+new Date();
+ game.stamp = +new Date();
+ game.seed = randseed();
+ storage.addToRoster(game, callback);
+}
+
+function LoadGame(sheet) {
+ if (!sheet) {
+ alert("Error loading game");
+ window.location.href = "roster.html";
+ return;
+ }
+
+ game = sheet;
+
+ /*
+ if (!window.localStorage) {
+ // Cookies can't hold a whole game save
+ storage.removeItem("roster");
+ storage = null;
+ }
+*/
+
+ if (document) {
+ var title = "Progress Quest - " + GameSaveName();
+ $("#title").text(title);
+ if (iOS) title = GameSaveName();
+ document.title = title;
+ }
+
+
+ randseed(game.seed);
+ $.each(AllBars.concat(AllLists), function (i, e) { e.load(game); });
+ if (Kill)
+ Kill.text(game.kill);
+ ClearAllSelections();
+ $.each([Plots,Quests], function () {
+ this.CheckAll(true);
+ });
+ Log('Loaded game: ' + game.Traits.Name);
+ if (!game.elapsed)
+ Brag('start');
+ StartTimer();
+}
+
+function GameSaveName() {
+ if (!game.saveName) {
+ game.saveName = Get(Traits, 'Name');
+ if (game.realm)
+ game.saveName += ' [' + game.realm + ']';
+ }
+ return game.saveName;
+}
+
+
+function InputBox(message, def) {
+ var i = prompt(message, def || '');
+ return (i !== null) ? i : def;
+}
+
+function ToDna(s) {
+ s = s + "";
+ var code = {
+ '0': "AT",
+ '1': "AG",
+ '2': "AC",
+ '3': "TA",
+ '4': "TG",
+ '5': "TC",
+ '6': "GA",
+ '7': "GT",
+ '8': "GC",
+ '9': "CA",
+ ',': "CT",
+ '.': "CG"
+ };
+ var r = "";
+ for (var i = 0; i < s.length; ++i) {
+ r += code[s[i]];
+ if (i && (i % 4) == 0) r += " ";
+ }
+ return r;
+}
+
+function FormKeyDown(e) {
+ var key = String.fromCharCode(e.which);
+
+ if (key === 'd') {
+ alert("Your character's genome is " + ToDna(game.dna + ""));
+ }
+
+ if (game.isonline) {
+ if (key === 'b') {
+ Brag('brag');
+ //Navigate(GetHostAddr() + 'name=' + UrlEncode(Get(Traits,'Name')));
+ }
+
+ if (key === 'g') {
+ game.guild = InputBox('Choose a guild.\r\rMake sure you undestand the guild rules before you join one. To learn more about guilds, visit http://progressquest.com/guilds.php', game.guild);
+ Brag("guild");
+ }
+
+ if (key === 'm') {
+ game.motto = InputBox('Declare your motto!', game.motto);
+ Brag('motto');
+ }
+ }
+
+ if (key === 'p') {
+ if (timerid) {
+ $('#paused').css('display', 'block');
+ StopTimer();
+ } else {
+ $('#paused').css('display', '');
+ StartTimer();
+ }
+ }
+
+ if (key === 'q') {
+ quit();
+ }
+
+ if (key === 's') {
+ SaveGame();
+ alert('Saved (' + JSON.stringify(game).length + ' bytes).');
+ }
+
+ /*
+ if (key === 't') {
+ TaskBar.reposition(TaskBar.Max());
+ }
+ */
+}
+
+function Navigate(url) {
+ window.open(url);
+}
+
+function LFSR(pt, salt) {
+ var result = salt;
+ for (var k = 1; k <= Length(pt); ++k)
+ result = Ord(pt[k]) ^ (result << 1) ^ (1 && ((result >> 31) ^ (result >> 5)));
+ for (var kk = 1; kk <= 10; ++kk)
+ result = (result << 1) ^ (1 && ((result >> 31) ^ (result >> 5)));
+}
+
+
+function Brag(trigger) {
+ SaveGame();
+ if (game.isonline) {
+ game.bragtrigger = trigger;
+ $.post("webrag.php", game, function (data, textStatus, request) {
+ if (data.alert)
+ alert(data.alert);
+ }, "json");
+ }
+}
+