From a045772d70dd3e8ffb892a44c755f008cc7174b0 Mon Sep 17 00:00:00 2001
From: Arkerthan <arkerthan@gmail.com>
Date: Thu, 20 Feb 2020 16:02:16 +0100
Subject: [PATCH] add base class for savable classes

---
 devTools/FC.d.ts                      |  3 +-
 src/002-config/Savable.js             | 70 ++++++++++++++++++++++++
 src/004-base/arcologyBuilding.js      | 16 +++---
 src/arcologyBuilding/apartments.js    | 31 +----------
 src/arcologyBuilding/base.js          | 76 ++++-----------------------
 src/arcologyBuilding/manufacturing.js | 31 ++---------
 src/arcologyBuilding/markets.js       | 31 +----------
 src/arcologyBuilding/penthouse.js     | 31 +----------
 src/arcologyBuilding/shops.js         | 31 +----------
 9 files changed, 103 insertions(+), 217 deletions(-)
 create mode 100644 src/002-config/Savable.js

diff --git a/devTools/FC.d.ts b/devTools/FC.d.ts
index 356f276896c..2b0ae1d0490 100644
--- a/devTools/FC.d.ts
+++ b/devTools/FC.d.ts
@@ -27,6 +27,7 @@ declare namespace App {
 	namespace Corporate {}
 
 	namespace Data {
+		class Savable {}
 		namespace Pronouns {
 			class Definition {
 				pronoun: string;
@@ -51,7 +52,7 @@ declare namespace App {
 	namespace Interact {}
 
 	namespace MainView {}
-	
+
 	namespace RA {
 		class NumericTarget {
 			cond: string;
diff --git a/src/002-config/Savable.js b/src/002-config/Savable.js
new file mode 100644
index 00000000000..8e8d9a27422
--- /dev/null
+++ b/src/002-config/Savable.js
@@ -0,0 +1,70 @@
+/**
+ * The basic class for all classes that can be saved.
+ *
+ * All subclasses HAVE to implement get className(), _cleanUpConfigScheme() and clone(),
+ * in the SAME WAY they are implemented here.
+ *
+ * @type {App.Data.Savable}
+ */
+App.Data.Savable = class {
+	/**
+	 * Returns the name of the class including namespaces
+	 *
+	 * @returns {string}
+	 */
+	get className() { return "App.Data.Savable"; }
+
+	/**
+	 * Updates saved data in case of changes to the class.
+	 *
+	 * NOTE: new attributes do NOT need to be added here, as they are automatically added with default values.
+	 *
+	 * @param {object} config
+	 * @protected
+	 */
+	static _cleanupConfigScheme(config) {
+		// in subclasses _cleanupConfigScheme() must call
+		// super._cleanupConfigScheme(config)
+		// BC code
+	}
+
+	/**
+	 * @returns {App.Data.Savable}
+	 */
+	clone() {
+		return (new App.Data.Savable())._init(this);
+	}
+
+	/**
+	 * @param {object} config
+	 * @param {boolean} clean
+	 * @returns {App.Data.Savable}
+	 * @protected
+	 */
+	_init(config, clean = false) {
+		if (clean) {
+			App.Data.Savable._cleanupConfigScheme(config);
+		}
+
+		// Clone the given object's own properties into our own properties.
+		deepAssign(this, config);
+
+		// Return `this` to make usage easier.
+		return this;
+	}
+
+	/**
+	 * @returns {[]}
+	 */
+	toJSON() {
+		// Return a code string that will create a new instance containing our
+		// own data.
+		//
+		// NOTE: Supplying `this` directly as the `reviveData` parameter to the
+		// `JSON.reviveWrapper()` call will trigger out of control recursion in
+		// the serializer, so we must pass it a clone of our own data instead.
+		const ownData = {};
+		deepAssign(ownData, this);
+		return JSON.reviveWrapper(`(new ${this.className}())._init($ReviveData$, true)`, ownData);
+	}
+};
diff --git a/src/004-base/arcologyBuilding.js b/src/004-base/arcologyBuilding.js
index ebf2b84ea19..7646272569d 100644
--- a/src/004-base/arcologyBuilding.js
+++ b/src/004-base/arcologyBuilding.js
@@ -1,8 +1,9 @@
-App.Arcology.Cell.BaseCell = class {
+App.Arcology.Cell.BaseCell = class extends App.Data.Savable {
 	/**
 	 * @param {number} owner
 	 */
 	constructor(owner) {
+		super();
 		/**
 		 * 0: private
 		 * 1: player
@@ -170,11 +171,14 @@ App.Arcology.Cell.BaseCell = class {
 		return false;
 	}
 
-	/**
-	 * @protected
-	 * @param {object} config
-	 */
-	_cleanupConfigScheme(config) {
+	static _cleanupConfigScheme(config) {
+		super._cleanupConfigScheme(config);
 		// BC code
 	}
+
+	clone() {
+		return (new App.Arcology.Cell.BaseCell())._init(this);
+	}
+
+	get className() { return "App.Arcology.Cell.BaseCell"; }
 };
diff --git a/src/arcologyBuilding/apartments.js b/src/arcologyBuilding/apartments.js
index 6736236e3c1..6be9ace6312 100644
--- a/src/arcologyBuilding/apartments.js
+++ b/src/arcologyBuilding/apartments.js
@@ -109,41 +109,14 @@ App.Arcology.Cell.Apartment = class extends App.Arcology.Cell.BaseCell {
 		return true;
 	}
 
-	_init(config, clean = false) {
-		if (clean) {
-			this._cleanupConfigScheme(config);
-		}
-
-		// Clone the given object's own properties into our own properties.
-		deepAssign(this, config);
-
-		// Return `this` to make usage easier.
-		return this;
-	}
-
-	/**
-	 * @private
-	 * @param {object} config
-	 */
-	_cleanupConfigScheme(config) {
+	static _cleanupConfigScheme(config) {
 		super._cleanupConfigScheme(config);
 		// BC code
 	}
 
 	clone() {
-		// Return a new instance containing our own data.
 		return (new App.Arcology.Cell.Apartment())._init(this);
 	}
 
-	toJSON() {
-		// Return a code string that will create a new instance containing our
-		// own data.
-		//
-		// NOTE: Supplying `this` directly as the `reviveData` parameter to the
-		// `JSON.reviveWrapper()` call will trigger out of control recursion in
-		// the serializer, so we must pass it a clone of our own data instead.
-		const ownData = {};
-		deepAssign(ownData, this);
-		return JSON.reviveWrapper('(new App.Arcology.Cell.Apartment())._init($ReviveData$, true)', ownData);
-	}
+	get className() { return "App.Arcology.Cell.Apartment"; }
 };
diff --git a/src/arcologyBuilding/base.js b/src/arcologyBuilding/base.js
index c419f8eae69..c834e83b617 100644
--- a/src/arcologyBuilding/base.js
+++ b/src/arcologyBuilding/base.js
@@ -62,12 +62,13 @@ App.Arcology.defaultBuilding = function() {
  */
 App.Arcology.sectionOrder = ["penthouse", "spire", "shops", "apartments", "markets", "manufacturing"];
 
-App.Arcology.Section = class {
+App.Arcology.Section = class extends App.Data.Savable {
 	/**
 	 * @param {string} id unique ID
 	 * @param {Array<Array<App.Arcology.Cell.BaseCell>>} rows
 	 */
 	constructor(id, rows) {
+		super();
 		this.id = id;
 		this.rows = rows;
 	}
@@ -160,49 +161,24 @@ App.Arcology.Section = class {
 		return cells;
 	}
 
-	_init(config, clean = false) {
-		if (clean) {
-			this._cleanupConfigScheme(config);
-		}
-
-		// Clone the given object's own properties into our own properties.
-		deepAssign(this, config);
-
-		// Return `this` to make usage easier.
-		return this;
-	}
-
-	/**
-	 * @private
-	 * @param {object} config
-	 */
-	_cleanupConfigScheme(config) {
+	static _cleanupConfigScheme(config) {
+		super._cleanupConfigScheme(config);
 		// BC code
 	}
 
 	clone() {
-		// Return a new instance containing our own data.
 		return (new App.Arcology.Section())._init(this);
 	}
 
-	toJSON() {
-		// Return a code string that will create a new instance containing our
-		// own data.
-		//
-		// NOTE: Supplying `this` directly as the `reviveData` parameter to the
-		// `JSON.reviveWrapper()` call will trigger out of control recursion in
-		// the serializer, so we must pass it a clone of our own data instead.
-		const ownData = {};
-		deepAssign(ownData, this);
-		return JSON.reviveWrapper('(new App.Arcology.Section())._init($ReviveData$, true)', ownData);
-	}
+	get className() { return "App.Arcology.Section"; }
 };
 
-App.Arcology.Building = class {
+App.Arcology.Building = class extends App.Data.Savable {
 	/**
 	 * @param {Array<App.Arcology.Section>} sections
 	 */
 	constructor(sections) {
+		super();
 		this.sections = sections;
 	}
 
@@ -247,46 +223,14 @@ App.Arcology.Building = class {
 			}, []);
 	}
 
-	/**
-	 * @param {object} config
-	 * @param {boolean} clean
-	 * @returns {App.Arcology.Building}
-	 * @private
-	 */
-	_init(config, clean = false) {
-		if (clean) {
-			this._cleanupConfigScheme(config);
-		}
-
-		// Clone the given object's own properties into our own properties.
-		deepAssign(this, config);
-
-		// Return `this` to make usage easier.
-		return this;
-	}
-
-	/**
-	 * @private
-	 * @param {object} config
-	 */
-	_cleanupConfigScheme(config) {
+	static _cleanupConfigScheme(config) {
+		super._cleanupConfigScheme(config);
 		// BC code
 	}
 
 	clone() {
-		// Return a new instance containing our own data.
 		return (new App.Arcology.Building())._init(this);
 	}
 
-	toJSON() {
-		// Return a code string that will create a new instance containing our
-		// own data.
-		//
-		// NOTE: Supplying `this` directly as the `reviveData` parameter to the
-		// `JSON.reviveWrapper()` call will trigger out of control recursion in
-		// the serializer, so we must pass it a clone of our own data instead.
-		const ownData = {};
-		deepAssign(ownData, this);
-		return JSON.reviveWrapper('(new App.Arcology.Building())._init($ReviveData$, true)', ownData);
-	}
+	get className() { return "App.Arcology.Building"; }
 };
diff --git a/src/arcologyBuilding/manufacturing.js b/src/arcologyBuilding/manufacturing.js
index 39243cee0e7..bac40cf7491 100644
--- a/src/arcologyBuilding/manufacturing.js
+++ b/src/arcologyBuilding/manufacturing.js
@@ -272,41 +272,16 @@ App.Arcology.Cell.Manufacturing = class extends App.Arcology.Cell.BaseCell {
 		return ["Manufacturing", "Sweatshops", "Pens"].includes(this.type);
 	}
 
-	_init(config, clean = false) {
-		if (clean) {
-			this._cleanupConfigScheme(config);
-		}
-
-		// Clone the given object's own properties into our own properties.
-		deepAssign(this, config);
-
-		// Return `this` to make usage easier.
-		return this;
-	}
-
-	/**
-	 * @private
-	 * @param {object} config
-	 */
-	_cleanupConfigScheme(config) {
+	static _cleanupConfigScheme(config) {
 		super._cleanupConfigScheme(config);
 		// BC code
 	}
 
 	clone() {
-		// Return a new instance containing our own data.
 		return (new App.Arcology.Cell.Manufacturing())._init(this);
 	}
 
-	toJSON() {
-		// Return a code string that will create a new instance containing our
-		// own data.
-		//
-		// NOTE: Supplying `this` directly as the `reviveData` parameter to the
-		// `JSON.reviveWrapper()` call will trigger out of control recursion in
-		// the serializer, so we must pass it a clone of our own data instead.
-		const ownData = {};
-		deepAssign(ownData, this);
-		return JSON.reviveWrapper('(new App.Arcology.Cell.Manufacturing())._init($ReviveData$, true)', ownData);
+	get className() {
+		return "App.Arcology.Cell.Manufacturing";
 	}
 };
diff --git a/src/arcologyBuilding/markets.js b/src/arcologyBuilding/markets.js
index b2f0ccee21a..35bb94ba69f 100644
--- a/src/arcologyBuilding/markets.js
+++ b/src/arcologyBuilding/markets.js
@@ -134,41 +134,14 @@ App.Arcology.Cell.Market = class extends App.Arcology.Cell.BaseCell {
 		return this.type === "Markets";
 	}
 
-	_init(config, clean = false) {
-		if (clean) {
-			this._cleanupConfigScheme(config);
-		}
-
-		// Clone the given object's own properties into our own properties.
-		deepAssign(this, config);
-
-		// Return `this` to make usage easier.
-		return this;
-	}
-
-	/**
-	 * @private
-	 * @param {object} config
-	 */
-	_cleanupConfigScheme(config) {
+	static _cleanupConfigScheme(config) {
 		super._cleanupConfigScheme(config);
 		// BC code
 	}
 
 	clone() {
-		// Return a new instance containing our own data.
 		return (new App.Arcology.Cell.Market())._init(this);
 	}
 
-	toJSON() {
-		// Return a code string that will create a new instance containing our
-		// own data.
-		//
-		// NOTE: Supplying `this` directly as the `reviveData` parameter to the
-		// `JSON.reviveWrapper()` call will trigger out of control recursion in
-		// the serializer, so we must pass it a clone of our own data instead.
-		const ownData = {};
-		deepAssign(ownData, this);
-		return JSON.reviveWrapper('(new App.Arcology.Cell.Market())._init($ReviveData$, true)', ownData);
-	}
+	get className() { return "App.Arcology.Cell.Market"; }
 };
diff --git a/src/arcologyBuilding/penthouse.js b/src/arcologyBuilding/penthouse.js
index 587b1d1668d..7b40058f4ef 100644
--- a/src/arcologyBuilding/penthouse.js
+++ b/src/arcologyBuilding/penthouse.js
@@ -143,41 +143,14 @@ App.Arcology.Cell.Penthouse = class extends App.Arcology.Cell.BaseCell {
 		}
 	}
 
-	_init(config, clean = false) {
-		if (clean) {
-			this._cleanupConfigScheme(config);
-		}
-
-		// Clone the given object's own properties into our own properties.
-		deepAssign(this, config);
-
-		// Return `this` to make usage easier.
-		return this;
-	}
-
-	/**
-	 * @private
-	 * @param {object} config
-	 */
-	_cleanupConfigScheme(config) {
+	static _cleanupConfigScheme(config) {
 		super._cleanupConfigScheme(config);
 		// BC code
 	}
 
 	clone() {
-		// Return a new instance containing our own data.
 		return (new App.Arcology.Cell.Penthouse())._init(this);
 	}
 
-	toJSON() {
-		// Return a code string that will create a new instance containing our
-		// own data.
-		//
-		// NOTE: Supplying `this` directly as the `reviveData` parameter to the
-		// `JSON.reviveWrapper()` call will trigger out of control recursion in
-		// the serializer, so we must pass it a clone of our own data instead.
-		const ownData = {};
-		deepAssign(ownData, this);
-		return JSON.reviveWrapper('(new App.Arcology.Cell.Penthouse())._init($ReviveData$, true)', ownData);
-	}
+	get className() { return "App.Arcology.Cell.Penthouse"; }
 };
diff --git a/src/arcologyBuilding/shops.js b/src/arcologyBuilding/shops.js
index cc9cda2a3ab..6ac0f6c6b04 100644
--- a/src/arcologyBuilding/shops.js
+++ b/src/arcologyBuilding/shops.js
@@ -695,41 +695,14 @@ App.Arcology.Cell.Shop = class extends App.Arcology.Cell.BaseCell {
 		return this.type === "Shops";
 	}
 
-	_init(config, clean = false) {
-		if (clean) {
-			this._cleanupConfigScheme(config);
-		}
-
-		// Clone the given object's own properties into our own properties.
-		deepAssign(this, config);
-
-		// Return `this` to make usage easier.
-		return this;
-	}
-
-	/**
-	 * @private
-	 * @param {object} config
-	 */
-	_cleanupConfigScheme(config) {
+	static _cleanupConfigScheme(config) {
 		super._cleanupConfigScheme(config);
 		// BC code
 	}
 
 	clone() {
-		// Return a new instance containing our own data.
 		return (new App.Arcology.Cell.Shop())._init(this);
 	}
 
-	toJSON() {
-		// Return a code string that will create a new instance containing our
-		// own data.
-		//
-		// NOTE: Supplying `this` directly as the `reviveData` parameter to the
-		// `JSON.reviveWrapper()` call will trigger out of control recursion in
-		// the serializer, so we must pass it a clone of our own data instead.
-		const ownData = {};
-		deepAssign(ownData, this);
-		return JSON.reviveWrapper('(new App.Arcology.Cell.Shop())._init($ReviveData$, true)', ownData);
-	}
+	get className() { return "App.Arcology.Cell.Shop"; }
 };
-- 
GitLab