mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-18 05:33:54 +01:00
Can now do things such as ctrl+c in Terminal without it auto focusing on the input. Implemented offline exp gain
This commit is contained in:
parent
962b057ff8
commit
e2316e4a1d
@ -26,9 +26,9 @@ TESTING TODO:
|
|||||||
|
|
||||||
Scripts tab that shows script stats
|
Scripts tab that shows script stats
|
||||||
Seems to work, at least the basics (for online production)
|
Seems to work, at least the basics (for online production)
|
||||||
|
Script offline progress
|
||||||
|
|
||||||
Tasks TODO:
|
Tasks TODO:
|
||||||
Script offline progress
|
|
||||||
ctrl+C functionality for all running command like hack(), analyze(), and tail
|
ctrl+C functionality for all running command like hack(), analyze(), and tail
|
||||||
Scroll all the way down when something is post()ed
|
Scroll all the way down when something is post()ed
|
||||||
Script logging functionality? Logs to internal "log file" (property of script itself)
|
Script logging functionality? Logs to internal "log file" (property of script itself)
|
||||||
|
@ -66,6 +66,10 @@
|
|||||||
<a href="#" id="create-program-menu-link"> Create Program </a>
|
<a href="#" id="create-program-menu-link"> Create Program </a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="faction-tab" style="visibility:hidden">
|
||||||
|
<a href="#" id="faction-menu-link"> Factions </a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="save-game-tab">
|
<li class="save-game-tab">
|
||||||
<a href="#" id="save-game-link"> Save Game </a>
|
<a href="#" id="save-game-link"> Save Game </a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -226,15 +226,18 @@ function evaluate(exp, workerScript) {
|
|||||||
if (moneyGained <= 0) {moneyGained = 0;}
|
if (moneyGained <= 0) {moneyGained = 0;}
|
||||||
|
|
||||||
server.moneyAvailable -= moneyGained;
|
server.moneyAvailable -= moneyGained;
|
||||||
Player.money += moneyGained;
|
Player.gainMoney(moneyGained);
|
||||||
workerScript.scriptRef.onlineMoneyMade += moneyGained;
|
workerScript.scriptRef.onlineMoneyMade += moneyGained;
|
||||||
|
|
||||||
Player.hacking_exp += expGainedOnSuccess;
|
Player.hacking_exp += expGainedOnSuccess;
|
||||||
|
workerScript.scriptRef.onlineExpGained += expGainedOnSuccess;
|
||||||
console.log("Script successfully hacked " + server.hostname + " for $" + moneyGained + " and " + expGainedOnSuccess + " exp");
|
console.log("Script successfully hacked " + server.hostname + " for $" + moneyGained + " and " + expGainedOnSuccess + " exp");
|
||||||
resolve("Hack success");
|
resolve("Hack success");
|
||||||
} else {
|
} else {
|
||||||
//Player only gains 25% exp for failure? TODO Can change this later to balance
|
//Player only gains 25% exp for failure? TODO Can change this later to balance
|
||||||
Player.hacking_exp += expGainedOnFailure;
|
Player.hacking_exp += expGainedOnFailure;
|
||||||
|
workerScript.scriptRef.onlineExpGained += expGainedOnFailure;
|
||||||
|
|
||||||
console.log("Script unsuccessful to hack " + server.hostname + ". Gained " + expGainedOnFailure + "exp");
|
console.log("Script unsuccessful to hack " + server.hostname + ". Gained " + expGainedOnFailure + "exp");
|
||||||
resolve("Hack failure");
|
resolve("Hack failure");
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,9 @@ function PlayerObject() {
|
|||||||
//Flag to let the engine know the player is starting a hack
|
//Flag to let the engine know the player is starting a hack
|
||||||
this.startAction = false;
|
this.startAction = false;
|
||||||
this.actionTime = 0;
|
this.actionTime = 0;
|
||||||
|
|
||||||
|
//Used to store the last update time.
|
||||||
|
this.lastUpdate = new Date().getTime();
|
||||||
};
|
};
|
||||||
|
|
||||||
PlayerObject.prototype.init = function() {
|
PlayerObject.prototype.init = function() {
|
||||||
@ -167,6 +170,12 @@ PlayerObject.prototype.analyze = function() {
|
|||||||
this.startAction = true;
|
this.startAction = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlayerObject.prototype.gainMoney = function(money) {
|
||||||
|
this.money += money;
|
||||||
|
this.total_money += money;
|
||||||
|
this.lifetime_money += money;
|
||||||
|
}
|
||||||
|
|
||||||
//Functions for saving and loading the Player data
|
//Functions for saving and loading the Player data
|
||||||
PlayerObject.prototype.toJSON = function() {
|
PlayerObject.prototype.toJSON = function() {
|
||||||
return Generic_toJSON("PlayerObject", this);
|
return Generic_toJSON("PlayerObject", this);
|
||||||
|
121
src/Script.js
121
src/Script.js
@ -71,24 +71,18 @@ function Script() {
|
|||||||
|
|
||||||
/* Properties to calculate offline progress. Only applies for infinitely looping scripts */
|
/* Properties to calculate offline progress. Only applies for infinitely looping scripts */
|
||||||
|
|
||||||
//Time it takes to execute one iteration of the entire script
|
|
||||||
//Each function takes CONSTANTS.CodeInstructionRunTime seconds,
|
|
||||||
//plus hacking time plus and sleep commands
|
|
||||||
this.executionTimeMillis = 0;
|
|
||||||
|
|
||||||
//Number of instructions ("lines") in the code. Any call ending in a ;
|
//Number of instructions ("lines") in the code. Any call ending in a ;
|
||||||
//is considered one instruction. Used to calculate executionTime
|
//is considered one instruction. Used to calculate ramUsage
|
||||||
this.numInstructions = 0;
|
this.numInstructions = 0;
|
||||||
|
|
||||||
//Which servers are hacked in one iteration of the script. May contain duplicates
|
|
||||||
this.serversHacked = [];
|
|
||||||
|
|
||||||
//Stats to display on the Scripts menu, and used to determine offline progress
|
//Stats to display on the Scripts menu, and used to determine offline progress
|
||||||
this.offlineRunningTime = 0; //Seconds
|
this.offlineRunningTime = 0; //Seconds
|
||||||
this.offlineMoneyMade = 0;
|
this.offlineMoneyMade = 0;
|
||||||
|
this.offlineExpGained = 0;
|
||||||
this.onlineRunningTime = 0; //Seconds
|
this.onlineRunningTime = 0; //Seconds
|
||||||
this.onlineMoneyMade = 0;
|
this.onlineMoneyMade = 0;
|
||||||
this.lastUpdate = 0;
|
this.onlineExpGained = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//Get the script data from the Script Editor and save it to the object
|
//Get the script data from the Script Editor and save it to the object
|
||||||
@ -107,8 +101,13 @@ Script.prototype.saveScript = function() {
|
|||||||
//Calculate/update number of instructions, ram usage, execution time, etc.
|
//Calculate/update number of instructions, ram usage, execution time, etc.
|
||||||
this.updateNumInstructions();
|
this.updateNumInstructions();
|
||||||
this.updateRamUsage();
|
this.updateRamUsage();
|
||||||
this.updateExecutionTime();
|
|
||||||
|
|
||||||
|
//Clear the stats when the script is updated
|
||||||
|
this.offlineRunningTime = 0; //Seconds
|
||||||
|
this.offlineMoneyMade = 0;
|
||||||
|
this.onlineRunningTime = 0; //Seconds
|
||||||
|
this.onlineMoneyMade = 0;
|
||||||
|
this.lastUpdate = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,61 +125,6 @@ Script.prototype.updateRamUsage = function() {
|
|||||||
this.ramUsage = this.numInstructions * .2;
|
this.ramUsage = this.numInstructions * .2;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Calculate the execution time of the script. This is used to calculate how much a script
|
|
||||||
//generates when the user is offline
|
|
||||||
//This is calculated based on the number of instructions and any calls to hack().
|
|
||||||
//Every instruction takes a flat time of X seconds (defined in Constants.js)
|
|
||||||
//Every hack instructions takes an ADDITIONAl time of however long it takes to hack that server
|
|
||||||
Script.prototype.updateExecutionTime = function() {
|
|
||||||
//TODO Maybe do this based on the average production/s of the script( which I'm adding in
|
|
||||||
//as a property)
|
|
||||||
/*
|
|
||||||
var executionTime = 0;
|
|
||||||
|
|
||||||
//Ever instruction takes CONSTANTS.CodeOfflineExecutionTime seconds
|
|
||||||
console.log("numInstructions: " + this.numInstructions.toString());
|
|
||||||
executionTime += (this.numInstrutions * CONSTANTS.CodeOfflineExecutionTime);
|
|
||||||
console.log("ExecutionTime after taking into account instructions: " + executionTime.toString());
|
|
||||||
|
|
||||||
//Search the code's text for every occurrence of hack()
|
|
||||||
hackIndices = getIndicesOf('hack("', this.code, true);
|
|
||||||
for (var i = 0; i < hackIndices.length; ++i) {
|
|
||||||
//Get the full hack() call substring
|
|
||||||
var startIndex = hackIndices[i];
|
|
||||||
console.log("startIndex: " + startIndex.toString());
|
|
||||||
var endIndex = startIndex;
|
|
||||||
|
|
||||||
while (this.code.substr(endIndex, 2) != ");") {
|
|
||||||
console.log("endIndex: " + endIndex.toString());
|
|
||||||
++endIndex;
|
|
||||||
if (endIndex == this.code.length - 1) {
|
|
||||||
//Bad code...
|
|
||||||
console.log("Could not find ending to hack call");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++endIndex; // This puts endIndex at the semicolon
|
|
||||||
|
|
||||||
var hackCall = this.code.substring(startIndex, endIndex);
|
|
||||||
console.log("hackCall: " + hackCall);
|
|
||||||
var argument = hackCall.match(/"([^']+)"/)[1]; //Extract the argument, which must be btw 2 quotes
|
|
||||||
|
|
||||||
//Check if its an ip or a hostname. Then get the server and calculate hack time accordingly
|
|
||||||
var server = null;
|
|
||||||
if (isValidIPAddress(argument)) {
|
|
||||||
server = AllServers[argument];
|
|
||||||
} else {
|
|
||||||
server = GetServerByHostname(argument);
|
|
||||||
}
|
|
||||||
console.log("Server hostname: " + server.hostname);
|
|
||||||
executionTime += scriptCalculateHackingTime(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.executionTimeMillis = executionTime * 1000;
|
|
||||||
console.log("Script calculated to have an offline execution time of " + executionTime.toString() + "seconds");
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
Script.prototype.toJSON = function() {
|
Script.prototype.toJSON = function() {
|
||||||
return Generic_toJSON("Script", this);
|
return Generic_toJSON("Script", this);
|
||||||
}
|
}
|
||||||
@ -209,8 +153,53 @@ loadAllRunningScripts = function() {
|
|||||||
var script = server.getScript(server.runningScripts[j]);
|
var script = server.getScript(server.runningScripts[j]);
|
||||||
if (script == null) {continue;}
|
if (script == null) {continue;}
|
||||||
addWorkerScript(script, server);
|
addWorkerScript(script, server);
|
||||||
|
|
||||||
|
//Offline production
|
||||||
|
scriptCalculateOfflineProduction(script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log("Loaded " + count.toString() + " running scripts");
|
console.log("Loaded " + count.toString() + " running scripts");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scriptCalculateOfflineProduction = function(script) {
|
||||||
|
//The Player object stores the last update time from when we were online
|
||||||
|
var thisUpdate = new Date().getTime();
|
||||||
|
var lastUpdate = Player.lastUpdate;
|
||||||
|
var timePassed = (thisUpdate - lastUpdate) / 1000; //Seconds
|
||||||
|
console.log("Offline for " + timePassed.toString() + " seconds");
|
||||||
|
|
||||||
|
//Calculate the "confidence" rating of the script's true production. This is based
|
||||||
|
//entirely off of time. We will arbitrarily say that if a script has been running for
|
||||||
|
//120 minutes (7200 sec) then we are completely confident in its ability
|
||||||
|
var confidence = (script.onlineRunningTime) / 7200;
|
||||||
|
if (confidence >= 1) {confidence = 1;}
|
||||||
|
console.log("onlineRunningTime: " + script.onlineRunningTime.toString());
|
||||||
|
console.log("Confidence: " + confidence.toString());
|
||||||
|
|
||||||
|
//A script's offline production will always be at most half of its online production.
|
||||||
|
var production = (1/2) * (script.onlineMoneyMade / script.onlineRunningTime) * timePassed;
|
||||||
|
production *= confidence;
|
||||||
|
|
||||||
|
var expGain = (1/2) * (script.onlineExpGained / script.onlineRunningTime) * timePassed;
|
||||||
|
expGain *= confidence;
|
||||||
|
|
||||||
|
//Account for production in Player and server
|
||||||
|
Player.gainMoney(production);
|
||||||
|
Player.hacking_exp += expGain;
|
||||||
|
|
||||||
|
var server = AllServers[script.server];
|
||||||
|
server.moneyAvailable -= production;
|
||||||
|
if (server.moneyAvailable < 0) {server.moneyAvailable = 0;}
|
||||||
|
|
||||||
|
//Update script stats
|
||||||
|
script.offlineMoneyMade += production;
|
||||||
|
script.offlineRunningTime += timePassed;
|
||||||
|
script.offlineExpGained += expGain;
|
||||||
|
|
||||||
|
//TODO EXP
|
||||||
|
|
||||||
|
//DEBUG
|
||||||
|
var serverName = AllServers[script.server].hostname;
|
||||||
|
console.log(script.filename + " from server " + serverName + " generated $" + production.toString() + " while offline");
|
||||||
|
}
|
@ -16,7 +16,7 @@ var hackProgressPost = function(input) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var postNetburnerText = function() {
|
var postNetburnerText = function() {
|
||||||
post("Netburner v1.0");
|
post("Netburner v0.1");
|
||||||
}
|
}
|
||||||
|
|
||||||
//Defines what happens when enter is pressed (keycode 13)
|
//Defines what happens when enter is pressed (keycode 13)
|
||||||
@ -37,14 +37,29 @@ $(document).keyup(function(event) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Keep terminal in focus
|
//Keep terminal in focus
|
||||||
|
terminalCtrlPressed = false;
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
if (Engine.currentPage == Engine.Page.Terminal) {
|
if (Engine.currentPage == Engine.Page.Terminal) {
|
||||||
$('.terminal-input').focus();
|
$('.terminal-input').focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$(document).keydown(function() {
|
$(document).keydown(function(e) {
|
||||||
if (Engine.currentPage == Engine.Page.Terminal) {
|
if (Engine.currentPage == Engine.Page.Terminal) {
|
||||||
|
if (e.which == 17) {
|
||||||
|
terminalCtrlPressed = true;
|
||||||
|
} else if (terminalCtrlPressed == true) {
|
||||||
|
//Don't focus
|
||||||
|
} else {
|
||||||
$('.terminal-input').focus();
|
$('.terminal-input').focus();
|
||||||
|
terminalCtrlPressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$(document).keyup(function(e) {
|
||||||
|
if (Engine.currentPage == Engine.Page.Terminal) {
|
||||||
|
if (e.which == 17) {
|
||||||
|
terminalCtrlPressed = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -79,7 +94,7 @@ var Terminal = {
|
|||||||
if (moneyGained <= 0) {moneyGained = 0;}
|
if (moneyGained <= 0) {moneyGained = 0;}
|
||||||
|
|
||||||
Player.getCurrentServer().moneyAvailable -= moneyGained;
|
Player.getCurrentServer().moneyAvailable -= moneyGained;
|
||||||
Player.money += moneyGained;
|
Player.gainMoney(moneyGained);
|
||||||
|
|
||||||
Player.hacking_exp += expGainedOnSuccess;
|
Player.hacking_exp += expGainedOnSuccess;
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ var Engine = {
|
|||||||
|
|
||||||
/* Display character info */
|
/* Display character info */
|
||||||
displayCharacterInfo: function() {
|
displayCharacterInfo: function() {
|
||||||
Engine.Display.characterInfo.innerHTML = 'Money: $' + Player.money + '<br><br>' +
|
Engine.Display.characterInfo.innerHTML = 'Money: $' + Player.money.toFixed(2) + '<br><br>' +
|
||||||
'Hacking Level: ' + Player.hacking_skill + '<br><br>' +
|
'Hacking Level: ' + Player.hacking_skill + '<br><br>' +
|
||||||
'Strength: ' + Player.strength + '<br><br>' +
|
'Strength: ' + Player.strength + '<br><br>' +
|
||||||
'Defense: ' + Player.defense + '<br><br>' +
|
'Defense: ' + Player.defense + '<br><br>' +
|
||||||
@ -162,7 +162,7 @@ var Engine = {
|
|||||||
'Agility: ' + Player.agility + '<br><br>' +
|
'Agility: ' + Player.agility + '<br><br>' +
|
||||||
'Charisma: ' + Player.charisma + '<br><br>' +
|
'Charisma: ' + Player.charisma + '<br><br>' +
|
||||||
'Servers owned: ' + Player.purchasedServers.length + '<br><br>' +
|
'Servers owned: ' + Player.purchasedServers.length + '<br><br>' +
|
||||||
'Hacking Experience: ' + Player.hacking_exp + '<br><br>';
|
'Hacking Experience: ' + Player.hacking_exp.toFixed(4) + '<br><br>';
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Functions used to update information on the Active Scripts page */
|
/* Functions used to update information on the Active Scripts page */
|
||||||
@ -170,7 +170,6 @@ var Engine = {
|
|||||||
|
|
||||||
//Creates and adds the <li> object for a given workerScript
|
//Creates and adds the <li> object for a given workerScript
|
||||||
addActiveScriptsItem: function(workerscript) {
|
addActiveScriptsItem: function(workerscript) {
|
||||||
console.log("addActiveScriptsItem called");
|
|
||||||
var item = document.createElement("li");
|
var item = document.createElement("li");
|
||||||
|
|
||||||
Engine.createActiveScriptsText(workerscript, item);
|
Engine.createActiveScriptsText(workerscript, item);
|
||||||
@ -217,17 +216,22 @@ var Engine = {
|
|||||||
|
|
||||||
//Server ip/hostname
|
//Server ip/hostname
|
||||||
var hostname = workerscript.getServer().hostname;
|
var hostname = workerscript.getServer().hostname;
|
||||||
var serverIpHostname = "Server: " + hostname + "(" + workerscript.serverIp + ")";
|
var serverIpHostname = "Server: " + hostname + " (" + workerscript.serverIp + ")";
|
||||||
|
|
||||||
//Online money/s
|
//Online
|
||||||
var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime;
|
var onlineMps = workerscript.scriptRef.onlineMoneyMade / workerscript.scriptRef.onlineRunningTime;
|
||||||
var onlineMpsText = "Online production: $" + onlineMps.toFixed(2) + "/second";
|
var onlineMpsText = "Online production: $" + onlineMps.toFixed(2) + "/second";
|
||||||
|
var onlineEps = workerscript.scriptRef.onlineExpGained / workerscript.scriptRef.onlineRunningTime;
|
||||||
|
var onlineEpsText = (Array(20).join(" ") + onlineEps.toFixed(4) + " exp/second").replace( / /g, " ");
|
||||||
|
|
||||||
//Offline money/s
|
//Offline
|
||||||
var offlineMps = workerscript.scriptRef.offlineMoneyMade / workerscript.scriptRef.offlineRunningTime;
|
var offlineMps = workerscript.scriptRef.offlineMoneyMade / workerscript.scriptRef.offlineRunningTime;
|
||||||
var offlineMpsText = "Offline production: $" + offlineMps.toFixed(2) + "/second";
|
var offlineMpsText = "Offline production: $" + offlineMps.toFixed(2) + "/second";
|
||||||
|
var offlineEps = workerscript.scriptRef.offlineExpGained / workerscript.scriptRef.offlineRunningTime;
|
||||||
|
var offlineEpsText = (Array(21).join(" ") + offlineEps.toFixed(4) + " exp/second").replace( / /g, " ");
|
||||||
|
|
||||||
itemText.innerHTML = serverIpHostname + "<br>" + onlineMpsText + "<br>" + offlineMpsText + "<br>";
|
itemText.innerHTML = serverIpHostname + "<br>" + onlineMpsText + "<br>" + onlineEpsText + "<br>" +
|
||||||
|
offlineMpsText + "<br>" + offlineEpsText + "<br>";
|
||||||
|
|
||||||
item.appendChild(itemText);
|
item.appendChild(itemText);
|
||||||
},
|
},
|
||||||
@ -246,6 +250,7 @@ var Engine = {
|
|||||||
//Update the game engine by the calculated number of cycles
|
//Update the game engine by the calculated number of cycles
|
||||||
Engine.updateGame(diff);
|
Engine.updateGame(diff);
|
||||||
Engine._lastUpdate = _thisUpdate - offset;
|
Engine._lastUpdate = _thisUpdate - offset;
|
||||||
|
Player.lastUpdate = _thisUpdate - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.requestAnimationFrame(Engine.idleTimer);
|
window.requestAnimationFrame(Engine.idleTimer);
|
||||||
@ -272,8 +277,6 @@ var Engine = {
|
|||||||
|
|
||||||
//Update the running time of all active scripts
|
//Update the running time of all active scripts
|
||||||
updateOnlineScriptTimes(numCycles);
|
updateOnlineScriptTimes(numCycles);
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
//Counters for the main event loop. Represent the number of game cycles are required
|
//Counters for the main event loop. Represent the number of game cycles are required
|
||||||
@ -360,7 +363,7 @@ var Engine = {
|
|||||||
if (Engine.loadSave()) {
|
if (Engine.loadSave()) {
|
||||||
console.log("Loaded game from save");
|
console.log("Loaded game from save");
|
||||||
CompanyPositions.init();
|
CompanyPositions.init();
|
||||||
loadAllRunningScripts();
|
loadAllRunningScripts(); //This also takes care of offline production
|
||||||
} else {
|
} else {
|
||||||
//No save found, start new game
|
//No save found, start new game
|
||||||
console.log("Initializing new game");
|
console.log("Initializing new game");
|
||||||
@ -397,9 +400,6 @@ var Engine = {
|
|||||||
//Active scripts list
|
//Active scripts list
|
||||||
Engine.ActiveScriptsList = document.getElementById("active-scripts-list");
|
Engine.ActiveScriptsList = document.getElementById("active-scripts-list");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Engine.Clickables.saveMainMenuButton = document.getElementById("save-game-link");
|
Engine.Clickables.saveMainMenuButton = document.getElementById("save-game-link");
|
||||||
Engine.Clickables.saveMainMenuButton.addEventListener("click", function() {
|
Engine.Clickables.saveMainMenuButton.addEventListener("click", function() {
|
||||||
Engine.saveGame();
|
Engine.saveGame();
|
||||||
|
Loading…
Reference in New Issue
Block a user