I'm still a bit confused by the record/playback saving model. If you have thousands of turns will the code have to go through them all one by one resolving them each time? It seems like it might be quite an inefficient way of doing things with AI and everything, also I imagine any bugs at all would cause a lot of issues as the rest of the playback would be broken from that point on. I can't seem to get my head around the idea properly. I have got an animation system in place, but it would be possible to add a "playback" flag which skips to the end of any animations without playing them so I think it would still be possible.
I have managed to implement a different load/save method that seems to work ok.
Each class has a create() and a restoreFromSave(_saveObject) method.
The first one is used when creating the object for the first time and the second when restoring from a save file.
It seems to work without too many problems, but every single new variable I add to each class needs to be stored and reloaded properly which is a bit of a pain. Is that normal for a game of this type?
I am writing the game in Javascript which doesn't have a way of serialising "classes" unfortunately. I'm not really using much OOP to be honest, just have javascript style "classes" that can store data and contain functions as well which I've realised can't all be saved together.
I too am developing roguelikes in javascript. My project (strife) has been developed with a very similar method of load/save implementation. Here is an example of a class that I load/save:
https://github.com/gregbillings/strife/blob/master/src/js/resource.js.
I have a central function that performs the basic copy for all savable objects. (
https://github.com/gregbillings/strife/blob/master/src/js/jsRL.js#L728)
Each "class" keeps a list of shallow fields that are trivially copyable. The save/load functions on the class take care of the other variables (usually savable classes/arrays themselves). the 'constructor' for the class also includes a check to see if the first passed variable is actually a saved instance. If so, we pass it through to the load function. This results in being able to pass a saved version of the object when instantiating a new instance of the object in question.
e.g.
//create new object
var life = new jsRL.resource('HP', 'red', 15, 15, 1 / 800 ); //name, color, max, current, recovery rate/time
//save the object:
var save = JSON.stringify(life.save());
//save now looks like this: "{"name":"HP","displayColor":"red","current":15,"max":15,"recoveryTime":{"base":0.00125,"delta":0},"recoveryBucket":0}"
//restore object
life = new jsRL.resource(JSON.parse(save));
This way, adding a trivial variable with a basic data-type is easily done by simply appending it to the list of shallow variables specific to your class you are saving/loading. While still annoying, it is significantly less so than a pure ad-hoc implementation.
As for random number generation: I found a useful prng called seedrandom (see
https://github.com/gregbillings/strife/blob/master/src/js/seedrandom.js for the copy I use.) It allows you to instantiate a new random with a given seed to always get the same results. With some modifications, it could be made to allow you to save it's current state into a json object to be saved and loaded again at a future date.
As far as securing the environment against a malicious user goes, the javascript environment is a particularly poor choice for attack-resistant implementation.
If people want to play honestly, they will do so, if they don't, they will find ways to circumvent whatever you can throw at them. The only real exception to this is server + dumb client interactions... but that is a topic for another time.
In short, my approach is to assume that if a player wants to scum, or modify their "1d4" sword to be a "1d4+100" sword, they can do that. Hardcore rl players would never do something like that, though... as they are looking to beat the game, not the code.