diff options
-rw-r--r-- | craftmine.php | 17 | ||||
-rw-r--r-- | css/Symbola.ttf | bin | 0 -> 2188952 bytes | |||
-rw-r--r-- | css/craftmine.css | 12 | ||||
-rw-r--r-- | data/save/.gitignore | 1 | ||||
-rw-r--r-- | inc/Inventory.inc | 6 | ||||
-rw-r--r-- | inc/Item.inc | 12 | ||||
-rw-r--r-- | inc/messages.inc | 18 | ||||
-rw-r--r-- | inc/mine.inc | 2 | ||||
-rw-r--r-- | inc/savegame.inc | 92 | ||||
-rw-r--r-- | inc/shop.inc | 1 | ||||
-rw-r--r-- | index.xhtml | 18 | ||||
-rw-r--r-- | js/craftmine.js | 8 | ||||
-rw-r--r-- | js/gui.js | 6 | ||||
-rw-r--r-- | js/savegame.js | 42 |
14 files changed, 217 insertions, 18 deletions
diff --git a/craftmine.php b/craftmine.php index 72817cc..242e1fd 100644 --- a/craftmine.php +++ b/craftmine.php @@ -5,8 +5,12 @@ require_once("inc/guild.inc"); require_once("inc/shop.inc"); require_once("inc/craftmine.inc"); require_once("inc/dungeon.inc"); +require_once("inc/savegame.inc"); + session_start(); // Must be placed *BEFORE* $_SESSION is actually used and *AFTER* all classes are imported +$op = ""; + /** * Indique au client une message requete. */ @@ -27,14 +31,16 @@ function reportBadRequest() { exit(); } if (!isset($_POST["op"])) { - reportBadRequest(); -} + if(!isset($_GET["op"])) + reportBadRequest(); + else + $op = $_GET["op"]; +} else $op = $_POST["op"]; /** * On récupère l'opération à exécuter et on le fait. */ -$op = $_POST["op"]; switch($op) { case "withdrawMine": withdrawMine(); break; case "createGuild": createGuild(); break; @@ -45,6 +51,11 @@ switch($op) { case "buildDungeon" : buildDungeon(); break; case "launchDungeon" : launchDungeon(); break; case "exitDungeon" : exitDungeon(); break; + case "saveGame": saveGame(); break; + case "downSave": downSave(); break; + case "listSaves": listSaves(); break; + case "loadSave": loadSave(); break; + case "deleteSave": deleteSave(); break; default: reportBadRequest(); } diff --git a/css/Symbola.ttf b/css/Symbola.ttf Binary files differnew file mode 100644 index 0000000..51d9a88 --- /dev/null +++ b/css/Symbola.ttf diff --git a/css/craftmine.css b/css/craftmine.css index 2a51ff7..c459baa 100644 --- a/css/craftmine.css +++ b/css/craftmine.css @@ -1,9 +1,21 @@ +@font-face { + font-family: "Symbola"; + font-weight: bold; + src: url('Symbola.ttf'); +} .tab-pane { display: none; } .tab-pane:target { display: block; } +.list-inline > li { + padding-top: 5px; + padding-bottom: 5px; +} .item-icon { + display:inline-block; + width: 1em; font-size: 3em; + font-family: "Symbola"; } diff --git a/data/save/.gitignore b/data/save/.gitignore new file mode 100644 index 0000000..4fcbff5 --- /dev/null +++ b/data/save/.gitignore @@ -0,0 +1 @@ +*.save.xml diff --git a/inc/Inventory.inc b/inc/Inventory.inc index 20c63bf..024a4fe 100644 --- a/inc/Inventory.inc +++ b/inc/Inventory.inc @@ -45,7 +45,11 @@ class Inventory { $inv = self::get(); $inv->_removeItem($item); } - + + public function addToXML($root) { + foreach($this->items as $item) + $item->addToXML($root); + } } ?> diff --git a/inc/Item.inc b/inc/Item.inc index bf77818..8e90998 100644 --- a/inc/Item.inc +++ b/inc/Item.inc @@ -12,6 +12,18 @@ class Item { $this->icon = $icon; $this->desc = $desc; } + + function addToXML($root) { + $item = $root->addChild("item"); + $item->addChild("name", $this->name); + $item->addChild("cost", $this->cost); + $item->addChild("icon", $this->icon); + $item->addChild("desc", $this->desc); + } + + public static function fromXML($xml) { + return new static((string)$xml->name, +(string)$xml->cost /* convert to number */, (string)$xml->icon, (string)$xml->desc); + } } ?> diff --git a/inc/messages.inc b/inc/messages.inc index c24bd18..c51adba 100644 --- a/inc/messages.inc +++ b/inc/messages.inc @@ -7,12 +7,26 @@ $messages = array( "guild_not_yet_created" => "You need to create a guild first.", "guild_already_built" => "You have aready built a guild.", "dungeon_already_available" => "You can already access the dungeon" + + "gamesave_ok" => "Game saved.", + "gamesave_error" => "An error occured when trying to save the game.", + "gamesave_not_found" => "Couldn't find the specified save file.", + "gamesave_delete_fail" => "Couldn't delete the specified save file.", + "gamesave_delete_success" => "Game save successfully removed from server", ); -function sendError($msg) { +function sendMessage($type, $msg) { global $messages; $text = $messages[$msg]; - echo json_encode(array("error" => $text)); + echo json_encode(array($type => $text)); +} + +function sendError($msg) { + sendMessage("error", $msg); +} + +function sendInfo($msg) { + sendMessage("info", $msg); } ?> diff --git a/inc/mine.inc b/inc/mine.inc index 94a2c66..752fc69 100644 --- a/inc/mine.inc +++ b/inc/mine.inc @@ -1,7 +1,7 @@ <?php function initCraftMine() { - $_SESSION["mine"] = array("mine" => 0, "gold" => 0, "miners" => 0); + $_SESSION["mine"] = array("gold" => 0, "miners" => 0); } function withdrawMine() { diff --git a/inc/savegame.inc b/inc/savegame.inc new file mode 100644 index 0000000..1fa95b0 --- /dev/null +++ b/inc/savegame.inc @@ -0,0 +1,92 @@ +<?php + +require_once("inc/messages.inc"); + +define("SAVEDIR", "data/save"); + +function genXML($table, $xml) { + foreach($table as $k => $v) { + if(is_object($v)) { // Object: either Item or Inventory + if(is_callable(array($v, "addToXML"))) // Check if the object has a addToXML method + $v->addToXML($xml->addChild($k)); + } elseif(is_array($v)) // Nested array + genXML($v, $xml->addChild($k)); + else // Single value (numeric or string) + $xml->addChild($k, $v); + } +} + +function genSave() { + $save = new SimpleXMLElement("<save/>"); + genXML($_SESSION, $save); + return $save; +} + +function genFilename() { + return "craftmine-".date("d-m-Y_H-i-s").".save.xml"; +} + +function saveGame() { + $save = genSave(); + if($save->asXML(SAVEDIR."/".genFilename())) sendInfo("gamesave_ok"); + else sendError("gamesave_error"); +} + +function downSave() { + $save = ""; + $filename = ""; + + if(empty($_GET["filename"])) { + $filename = genFilename(); + $save = genSave()->asXML(); // Send current game save file if no file specified + } else { + $filename = $_GET["filename"]; + $save = file_get_contents(SAVEDIR . "/" . $filename); + if(empty($save)) return; // Probably file not found + } + + header("Content-Type: application/xml"); // Force browser to intepret the file as XML + header("Content-Disposition: attachment; filename=".$filename); // Force download with specific filename + header("Cache-Control: no-cache"); // Avoid storing save file in proxy/browser cache + echo $save; +} + +function listSaves() { + chdir(SAVEDIR); // Go to SAVEDIR folder, avoiding leading folder name in file list + echo json_encode(glob("*.save.xml")); +} + +function parseSave($xml, &$table) { // Passing $table by reference + foreach($xml as $k => $v) { + if($v->count() == 0) { // No child, treat as string + $v = (string)$v; + if(is_numeric($v)) $v = +$v; // If it is in fact a number, treat it that way using PHP unary '+' coercion + $table[$k] = $v; + } elseif($k == "inventory") { // Special case for inventory: objects need to be created + foreach($v as $item) Inventory::addItem(Item::fromXML($item)); + } else { // If nested array + $table[$k] = array(); + parseSave($v, $table[$k]); + } // Other types unsupported (unused) + } +} + +function deleteSave() { + if(empty($_POST["filename"])) return; + $path = SAVEDIR . "/" . basename($_POST["filename"]); // remove any leading directory + if(file_exists($path) && unlink($path)) + sendInfo("gamesave_delete_success"); + else sendError("gamesave_delete_fail"); +} + +function loadSave() { + if(empty($_POST["filename"])) return; + $xml = simplexml_load_file(SAVEDIR . "/" . $_POST["filename"]); + if(empty($xml)) { + sendError("gamesave_not_found"); + return; + } + $_SESSION = array(); // drop current game + parseSave($xml, $_SESSION); +} +?> diff --git a/inc/shop.inc b/inc/shop.inc index 83b08b0..49ee20b 100644 --- a/inc/shop.inc +++ b/inc/shop.inc @@ -48,7 +48,6 @@ function buildShop() { } elseif(debitAccount($shop["cost"])) { initShop(); - $_SESSION["mine"]["gold"] -= $shop["cost"]; echo json_encode($shop); } } diff --git a/index.xhtml b/index.xhtml index 2745c8a..ed3e7a6 100644 --- a/index.xhtml +++ b/index.xhtml @@ -16,7 +16,7 @@ <script type="text/javascript" charset="utf-8" src="js/shop.js"></script> <script type="text/javascript" charset="utf-8" src="js/gui.js"></script> <script type="text/javascript" charset="utf-8" src="js/dungeon.js"></script> - + <script type="text/javascript" charset="utf-8" src="js/savegame.js"></script> </head> <body onload="init()" onhashchange="changeTab()"> <div class="container-fluid"> @@ -34,6 +34,8 @@ <form class="form-horizontal" method="post" action="craftmine.php"> <button class="btn btn-default" type="button" name="withdraw" onclick="withdrawMine()">Withdraw</button> <button class="btn btn-default" type="button" name="HireMiner" onclick="hireMiner()">Hire one miner</button> + <button class="btn btn-default" type="button" onclick="saveGame()">Save game</button> + <button class="btn btn-default" type="button" onclick="downGame()">Download game</button> </form> </div> </div> @@ -64,6 +66,7 @@ <li role="presentation"><a href="#tab2" data-toggle="tab">Shop</a></li> <li role="presentation"><a href="#tab3" data-toggle="tab">Inventory</a></li> <li role="presentation"><a href="#tab4" data-toggle="tab">Dungeon</a></li> + <li role="presentation" class="pull-right"><a href="#tab5" data-toggle="tab" onclick="listSaves()">Save</a></li> </ul> <div class="tab-content"> <div class="tab-pane" id="tab1"> @@ -83,11 +86,18 @@ <div class="tab-pane" id="tab4"> <h4>Look at how poor you are! You can't access the dungeon, it is only for the elite.</h4> </div> + <div class="tab-pane" id="tab5"> + <h4>Saved games:</h4> + <div class="form" id="listsaves"> + </div> + <ul class="list-inline"> + <li><button class="btn btn-default" type="button" onclick="loadSave()">Load</button></li> + <li><button class="btn btn-default" type="button" onclick="downloadSave()">Download</button></li> + <li><button class="btn btn-default" type="button" onclick="deleteSave()">Delete</button></li> + </ul> + </div> </div> </div> - - - </div> </div> </div> diff --git a/js/craftmine.js b/js/craftmine.js index 7c37e59..ffc9b5e 100644 --- a/js/craftmine.js +++ b/js/craftmine.js @@ -14,7 +14,10 @@ function sendRequest(url, params, callback) { xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.onreadystatechange = function() { if(xhr.readyState == XMLHttpRequest.DONE && xhr.status == "200") { - var data = JSON.parse(xhr.responseText); + var data = ""; + if(xhr.responseText) data = JSON.parse(xhr.responseText); + if(data.info) + showInfo(data.info); if(data.error) { showError(data.error); return; @@ -50,11 +53,12 @@ function withdrawMine() { function initCraftMine() { sendRequest("craftmine.php", "op=getCraftMine", function(ret) { data.gold = parseInt(ret.gold); // Server's response is a string + data.mine = 0; // Reset mine if(ret.shop) displayShop(ret.shop); displayInventory(ret.inventory); if(ret.dungeon) displayDungeon(); data.miners = parseInt(ret.miners); - updateData("gold","miners"); + updateData("gold", "mine", "miners"); }) } @@ -6,10 +6,8 @@ function changeTab() { var id = window.location.hash.substr(4); var tabs = document.querySelectorAll("#tabs-panel > ul > li"); for(var i=0; i < tabs.length; i++) { - if(i == id-1) - tabs[i].className = "active"; - else - tabs[i].className = ""; + if(i == id-1) tabs[i].classList.add("active"); // Doesn't work with IE < 10 (and Opera Mini), but who cares? + else tabs[i].classList.remove("active"); } } diff --git a/js/savegame.js b/js/savegame.js new file mode 100644 index 0000000..4a08f91 --- /dev/null +++ b/js/savegame.js @@ -0,0 +1,42 @@ +function listSaves() { + sendRequest("craftmine.php", "op=listSaves", function(ret) { + var tmphtml="" + for(var i=0; i<ret.length; i++) { + tmphtml += "<label class=\"radio\"><input name=\"saveRadio\" value=\"" + i + "\" type=\"radio\" />" + ret[i] + "</label>\n"; + } + console.log(tmphtml); + document.getElementById("listsaves").innerHTML = tmphtml; + }); +} + +function getCheckedSave() { + var radios = document.getElementsByName('saveRadio'); + for (var i = 0, length = radios.length; i < length; i++) { + if (radios[i].checked) return radios[i].parentNode.textContent; + } + return -1; +} + +function loadSave() { + sendRequest("craftmine.php", "op=loadSave&filename="+getCheckedSave(), function(ret) { + initCraftMine(); + }); +} + +function downloadSave() { + window.open("craftmine.php?op=downSave&filename="+getCheckedSave(), "_blank"); +} + +function deleteSave() { + sendRequest("craftmine.php", "op=deleteSave&filename="+getCheckedSave(), function(ret) { + listSaves(); + }); +} + +function saveGame() { + sendRequest("craftmine.php", "op=saveGame"); +} + +function downGame() { + window.open("craftmine.php?op=downSave", "_blank"); +} |