diff --git a/config.js b/config.js
index 485ddf8..cf1af43 100644
--- a/config.js
+++ b/config.js
@@ -157,73 +157,141 @@ function GenerateName() {
return result.charAt(0).toUpperCase() + result.slice(1);
}
+function LocalStorage() {
+ this.getItem = function (key, callback) {
+ var result = window.localStorage.getItem(key);
+ if (callback)
+ callback(result);
+ };
+
+ this.setItem = function (key, value, callback) {
+ window.localStorage.setItem(key, value);
+ if (callback)
+ callback();
+ };
+
+ this.removeItem = function (key) {
+ window.localStorage.removeItem(key);
+ };
+}
+
+
function CookieStorage() {
- this.getItem = function(key) {
- try {
- var result;
- $.each(document.cookie.split(";"), function (i,cook) {
- if (cook.split("=")[0] === key)
- result = unescape(cook.split("=")[1]);
- });
- return result;
- } catch (err) {
- // Probably a JSON parse error
- return null;
- }
+ this.getItem = function(key, callback) {
+ var result;
+ $.each(document.cookie.split(";"), function (i,cook) {
+ if (cook.split("=")[0] === key)
+ result = unescape(cook.split("=")[1]);
+ });
+ if (callback)
+ setTimeout(function () { callback(result); }, 0);
+ return result;
};
- this.setItem = function (key, value) {
+ this.setItem = function (key, value, callback) {
document.cookie = key + "=" + escape(value);
+ if (callback)
+ setTimeout(callback, 0);
};
this.removeItem = function (key) {
document.cookie = key + "=; expires=Thu, 01-Jan-70 00:00:01 GMT;";
};
}
-
-var storage = window.localStorage;
-if (!storage)
- storage = new CookieStorage();
+function SqlStorage() {
+ this.async = true;
-function loadRoster() {
- if (storage) {
- var r = storage.getItem("roster");
- if (r) {
+ this.db = window.openDatabase("pq", "", "Progress Quest", 2500);
+
+ this.db.transaction(function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS Storage(key TEXT UNIQUE, value TEXT)");
+ });
+
+ this.getItem = function(key, callback) {
+ this.db.transaction(function (tx) {
+ tx.executeSql("SELECT value FROM Storage WHERE key=?", [key], function(tx, rs) {
+ if (rs.rows.length)
+ callback(rs.rows.item(0).value);
+ else
+ callback();
+ });
+ });
+ };
+
+ this.setItem = function (key, value, callback) {
+ this.db.transaction(function (tx) {
+ tx.executeSql("INSERT OR REPLACE INTO Storage (key,value) VALUES (?,?)",
+ [key, value],
+ callback);
+ });
+ };
+
+ this.removeItem = function (key) {
+ this.db.transaction(function (tx) {
+ tx.executeSql("DELETE FROM Storage WHERE key=?", [key]);
+ });
+ };
+}
+
+var iPad = navigator.userAgent.match(/iPad/);
+var iPod = navigator.userAgent.match(/iPod/);
+var iPhone = navigator.userAgent.match(/iPhone/);
+var iOS = iPad || iPod || iPhone;
+
+var storage = ((window.localStorage && !iOS) ? new LocalStorage() :
+ window.openDatabase ? new SqlStorage() :
+ new CookieStorage());
+
+storage.loadRoster = function (callback) {
+ function gotItem(value) {
+ if (value) {
try {
- return JSON.parse(r);
+ value = JSON.parse(value);
} catch (err) {
// aight
}
}
+ storage.games = value || {};
+ callback(storage.games);
}
- return {};
+ this.getItem("roster", gotItem);
}
-function loadSheet(name) {
- return loadRoster()[name];
+storage.loadSheet = function (name, callback) {
+ return this.loadRoster(function (games) {
+ if (callback)
+ callback(games[name]);
+ });
}
-function storeRoster(roster) {
- if (storage) {
- try {
- storage.setItem("roster", JSON.stringify(roster));
- return true;
- } catch (err) {
- if (err.toString().indexOf("QUOTA_EXCEEDED_ERR") != -1) {
- alert("This browser lacks storage capacity to save this game. This game can continue but cannot be saved. (Mobile Safari, I'll wager?)");
- storage = null;
- return false;
- }
+
+storage.storeRoster = function (roster, callback) {
+ this.games = roster;
+ try {
+ this.setItem("roster", JSON.stringify(roster), callback);
+ } catch (err) {
+ if (err.toString().indexOf("QUOTA_EXCEEDED_ERR") != -1) {
+ alert("This browser lacks storage capacity to save this game. This game can continue but cannot be saved. (Mobile Safari, I'll wager?)");
+ this.storeRoster = function (roster, callback) {
+ setTimeout(callback, 0);
+ };
+ setTimeout(callback, 0);
+ } else {
+ throw err;
}
}
}
-function addToRoster(newguy) {
- if (storage) {
- var r = loadRoster();
- r[newguy.Traits.Name] = newguy;
- storeRoster(r);
+storage.addToRoster = function (newguy, callback) {
+ if (this.games) {
+ this.games[newguy.Traits.Name] = newguy;
+ this.storeRoster(this.games, callback);
+ } else {
+ this.loadRoster(function () {
+ if (storage.games) // should always be true
+ storage.addToRoster(newguy, callback);
+ });
}
}
diff --git a/index.html b/index.html
index 81be992..81ef9b3 100644
--- a/index.html
+++ b/index.html
@@ -28,6 +28,13 @@ p {
#go {
font-size: 30pt;
}
+img {
+ border-style: none;
+}
+th { text-align: left; padding-right: 2em; }
+.good { color: #2a2; }
+.bad { color: #a22; }
+.ugly { color: #c82; }
@@ -62,16 +69,16 @@ coolant, and ported ProgrOS to that.
Between you, me, and that talking pony over there, this is pretty much
beta software at this point. Browser support breakdown:
-
-| Chrome, Firefox, Safari | Aces! Works great! We think!
- |
|---|
| Internet Explorer, Mobile Safari | Visual problems. Can't save games.
- |
|---|
| Opera | Crashes quite a bit.
+ |
|---|
+ | Chrome, Firefox, Safari
+ | Aces! Works great! We think!
+ |
+ | Opera, Mobile Safari
+ | Visual glitches.
+ |
+ | Internet Explorer
+ | Visual glitches. Can't save games.
|
diff --git a/main.css b/main.css
index 830ac40..1859ff0 100644
--- a/main.css
+++ b/main.css
@@ -118,3 +118,7 @@ tr { height: 15px; }
#Quests td, #Plots td {
padding-left: 0;
}
+
+body.iOS div.hint {
+ visibility: visible;
+}
diff --git a/main.html b/main.html
index 989667a..8e812d7 100644
--- a/main.html
+++ b/main.html
@@ -12,9 +12,8 @@
maximum-scale=1.0;
minimum-scale=1.0;
user-scalable=0;" />
-
-
+
+
@@ -157,7 +156,7 @@
Quests
@@ -170,7 +169,7 @@
@@ -180,7 +179,7 @@
diff --git a/main.js b/main.js
index e64a0f6..e96c3ec 100644
--- a/main.js
+++ b/main.js
@@ -959,45 +959,43 @@ function FormCreate() {
$(document).bind('beforeunload', function () {
if (!storage)
- return "Are you sure you want to quit? All your progress will be lost!";
+ 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");
}
- LoadCharacter();
+ var name = unescape(window.location.href.split('#')[1]);
+ storage.loadSheet(name, LoadGame);
}
+
+function pause(msec) {
+ window.showModalDialog("javascript:document.writeln ('')",
+ null,
+ "dialogWidth:0;dialogHeight:0;dialogHide:yes;unadorned:yes;"+
+ "status:no;scroll:no;center:no;dialogTop:-10000;dialogLeft:-10000");
+}
+
function quit() {
- SaveGame();
- window.location = "roster.html";
-}
-
-function LoadCharacter() {
- var result = true;
- var name = unescape(window.location.href.split('#')[1]);
- var sheet = loadSheet(name);
-
- if (!sheet) {
- alert("Error loading " + name);
- window.location = "roster.html";
- return;
- }
-
- if (!window.localStorage) {
- // Cookies can't hold a whole game save
- storage.removeItem("roster");
- storage = null;
- }
-
- LoadGame(sheet);
-
- if (document)
- $("#title").text("Progress Quest - " + GameSaveName());
+ $(window).unbind('unload');
+ SaveGame(function () { window.location = "roster.html"; });
}
@@ -1026,17 +1024,40 @@ function HotOrNot() {
}
-function SaveGame() {
+function SaveGame(callback) {
Log('Saving game: ' + GameSaveName());
HotOrNot();
game.date = ''+new Date();
game.stamp = +new Date();
game.seed = randseed();
- return addToRoster(game);
+ storage.addToRoster(game, callback);
}
function LoadGame(sheet) {
+ if (!sheet) {
+ alert("Error loading game");
+ window.location = "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();
+ $("title").text(title);
+ }
+
+
randseed(game.seed);
$.each(AllBars.concat(AllLists), function (i, e) { e.load(game); });
if (Kill)
@@ -1121,6 +1142,11 @@ function FormKeyDown(e) {
quit();
}
+ if (e.which == 19) { // ^S
+ SaveGame();
+ alert(JSON.stringify(game).length);
+ }
+
/*
if (e.which == 20) { // ^T
TaskBar.reposition(TaskBar.Max());
diff --git a/newguy.js b/newguy.js
index aeb80ef..4289b5d 100644
--- a/newguy.js
+++ b/newguy.js
@@ -205,9 +205,10 @@ function sold() {
newguy.Equips.Weapon = newguy.bestequip;
newguy.Equips.Hauberk = "-3 Burlap";
- addToRoster(newguy);
+ storage.addToRoster(newguy, function () {
+ window.location.href = "main.html#" + escape(newguy.Traits.Name);
+ });
- window.location.href = "main.html#" + escape(newguy.Traits.Name);
}
function cancel() {
diff --git a/roster.html b/roster.html
index 2598d13..e64bd15 100644
--- a/roster.html
+++ b/roster.html
@@ -64,7 +64,9 @@ a.x, a.sheet {
.banter {
font-size: 11pt;
}
-
+img {
+ border-style: none;
+}
diff --git a/roster.js b/roster.js
index d488484..b005a7e 100644
--- a/roster.js
+++ b/roster.js
@@ -1,18 +1,22 @@
-var games;
function load() {
- var roster = $("#roster");
- roster.empty();
if (!window.localStorage) {
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.");
return;
}
+ storage.loadRoster(loadGames);
+}
+
+function loadGames(games) {
+ var roster = $("#roster");
+ roster.empty();
+
var newone = window.location.href.split('#')[1];
var count = 0;
- games = loadRoster();
+
$.each(games, function (key, c) {
var name = c.Traits.Name;
@@ -24,7 +28,7 @@ function load() {
if (confirm("Terminate " + Pick(["faithful","noble","loyal","brave"])+
" " + name + "?")) {
delete games[name];
- storeRoster(games);
+ storage.storeRoster(games);
load();
}
});
@@ -58,8 +62,7 @@ function brag(sheet) {
}
function clearRoster() {
- storeRoster({});
- load();
+ storage.storeRoster({}, load);
}
$(document).ready(function () {
diff --git a/sim.js b/sim.js
index 12d9a98..da07e6e 100644
--- a/sim.js
+++ b/sim.js
@@ -30,6 +30,7 @@ var window = {
},
};
+var navigator = { userAgent: "v8" };
var location = window.location;
@@ -94,13 +95,13 @@ var timers = [{}];
var setInterval = global.setInterval = function (callback, interval) {
timers.push({callback:callback, interval:interval});
return timers.length-1;
-}
+};
+var setTimeout = setInterval; // TODO: distinguish!
load("config.js");
var cs = 0;
-for (var c in loadRoster())
- cs++;
+storage.loadRoster(function (cs) { for (var c in cs) cs++; });
print(cs, "characters");
load("newguy.js");
@@ -129,22 +130,28 @@ function charsheet(game) {
// It takes 18 sec to simulate 18 hours of play when I just checked (to
// level 10)
-for (var j = 1, t = 0; j < 11; ++j) {
+for (var j = 1, t = 0; j < 1001; ++j) {
t += LevelUpTime(j);
- //if (j % 100 == 0)
+ if (j % 100 == 0)
print(j, RoughTime(LevelUpTime(j))+",", RoughTime(t));
}
-var l = 0;
-for (var i = 0; i < 100000000; ++i) {
- if (game.Traits.Level != l) {
- SaveGame();
- charsheet(game);
- l = game.Traits.Level;
- //if (l >= 5) break;
+var tmpl = read("charsheet.txt");
+storage.loadSheet("Shienzid", function (sheet) {
+ game = sheet;
+ print(template(tmpl, sheet));
+
+ var l = 0;
+ for (var i = 0; i < 1000000000; ++i) {
+ if (game.Traits.Level != l) {
+ SaveGame();
+ charsheet(game);
+ l = game.Traits.Level;
+ //if (l >= 5) break;
+ }
+ //assert(timers.length == 2);// TODO: this is for simplicity
+ now += timers[1].interval;
+ timers[1].callback();
}
- //assert(timers.length == 2);// TODO: this is for simplicity
- now += timers[1].interval;
- timers[1].callback();
-}
+});