Sanity check: Am I serializing my objects and classes properly?

I am getting some problems regarding classes in my game, although I am serializing things more appropriately, I feel that something is still not quite correct. Here the class…

class Player {
    constructor(dataObj: dataObj) {
        if (!dataObj) {
            throw new TypeError("Data property undefined!")
        }

        this.data = dataObj;
    }

    // Return a new instance containing our own data.
    clone() {
        return new Player(clone(this.data))
    }

    toJSON() {
        return JSON.reviveWrapper('new setup.Player($ReviveData$)', clone(this.data))
    }

    learnSpell = (newSpell: Spell) => {
        const _spell = clone(newSpell)

        this.data.spells.push(_spell)
    }

    getMove = () => {
        const _player = variables().player;

        if (_player.data.equipments.rightHand[0]) {
            const _equipment: Weapon = _player.data.equipments.rightHand[0];

            _player.data.myCurMovement = {
                type    : _equipment.type,
                name    : _equipment.name,
                element : _equipment.elemental.element,
                damage  : _equipment.elemental.damage(),
                setEffect() {
                    // Basic damage calc, based on enemies primary resistance status
                    let _damage = Math.round(this.damage - setup.selectedEnemy.data.resistances[this.element])

                    // enemies armor calc
                    _damage -= Math.floor(setup.selectedEnemy.data.status.armor - _player.data.attributes.armorPen)
    
                    // Hit chance for the player, based on the enemy/player agility
                    let _hitChance = random(0, setup.selectedEnemy.agility)
    
                    if (_player.agility > _hitChance) {
                        setup.selectedEnemy.health -= _damage;
            
                        switch(this.type) {
                            case 'sword':
                                return `You wield your sword and in an instant you use your speed, appearing before your enemy and connecting the sword to his body causing ${_damage} damage!.`
                        }
                    }
                    else {
                        return `You try to hit your opponent with your ${this.name}, applying maximum strength, but at the last moment he manages to escape the attack.`;
                    }
                }
            }
        }
        else {
            _player.data.myCurMovement = {
                type    : "neutral",
                name    : "none",
                element : "physical",
                damage  : _player.force,
                setEffect() {
                    let _damage = Math.floor(this.damage - setup.selectedEnemy.data.resistances[this.element])
    
                    let _hitChance = random(0, setup.selectedEnemy.agility)
    
                    if (_player.agility > _hitChance) {
                        setup.selectedEnemy.health -= _damage;
            
                        return `You hit your opponent with your fists, applying maximum strength, doing ${_damage} damage!`;
                    }
                    else {
                        return `You try to hit your opponent with your fists, applying maximum strength, but at the last moment he manages to escape the attack.`;
                    }
                }
            }
        }
    }

    // Runs the effects received from enemies
    runTempEffect = () => {
        const _tempEffect = variables().player.data.tempEffect;

        _tempEffect.forEach((effect, index) => {
            if (effect.multiEffect) {
                effect.setEffect()
            }

            effect.turns -= 1;

            if (effect.turns <= 0) {
                effect.removeEffect()

                _tempEffect.splice(index, 1)
                _tempEffect[index]
                    ? _tempEffect[index].turns--
                    : ''
            }
        })
    }

    getSpell = (spell: Spell) => {
        const _player = this;
        const _spell = spell.prop;

        _spell.countdown[1] = _spell.countdown[0];

        _player.data.myCurSpell = {
            name    : _spell.name,
            element : _spell.element,
            desc    : _spell.desc,
            panel   : _spell.panel,
            cost    : _spell.cost,
            damage  : _spell.damage,
            setEffect : _spell.effect,
            runEffect() {
                const output = this.setEffect()

                setup.getBattleMenu()

                return output;
            }
        }
    }

    hasShield = () => {
        if (typeof this.data.equipments.leftHand === 'object') {
            return true;
        }
    }

    /*
        Inventory functions
    */
    addItem = (_item: Weapon | Shield | Equipment) => {
        this.data.inventory.push(_item)
    }

    get invItems() {
        const _inventory = this.data.inventory;
        const _output: string[] = []

        if (_inventory.length > 0) {
            _inventory.forEach((item) => {
                _output.push(item.name)
            })

            return _output;
        }
        else {
            return 'You have no items.';
        }
    }

    /* 
        Getters and setters
    */
    get name() {
        return this.data.firstName;
    }

    get surname() {
        return this.data.secondName;
    }

    get sex() {
        return this.data.sex;
    }
    set sex(value) {
        this.data.sex = value;
    }

    get race() {
        return this.data.race;
    }
    set race(value) {
        this.data.race = value;
    }

    get classDesc() {
        const desc = this.data.sex + ' ' + this.data.race + ',' + ' ' + this.data.class
    
        return desc;
    }
  
    // Experience getter
    get experience() {
        return this.data.status.experience;
    }
    set experience(value) {
        if (typeof value !== 'number') {
            throw new TypeError(`Typeof ${value} is invalid!`)
        }
  
        this.data.status.experience = value;
    }
  
    get maxExperience() {
        return this.data.status.maxExperience;
    }
    set maxExperience(value) {
        if (typeof value !== 'number') {
            throw new TypeError(`Typeof ${value} is invalid!`)
        }
    
        this.data.status.maxExperience = value;
    }

    // Body traits
    get eyeColor() {
        return this.data.body.eyeColor;
    }
    set eyeColor(value) {
        this.data.body.eyeColor = value;
    }
  
    get hairColor() {
        return this.data.body.hairColor;
    }
    set hairColor(value) {
        this.data.body.hairColor = value;
    }
  
    get skinColor() {
        return this.data.body.skinColor;
    }
    set skinColor(value) {
        this.data.body.skinColor = value;
    }

    get health() {
        return this.data.status.health;
    }
    set health(value: number) {
        if (typeof value !== 'number') {
            throw new TypeError(`Typeof ${value} is invalid!`)
        }

        this.data.status.health = value;
    }
  
    get maxHealth() {
        return this.data.status.maxHealth;
    }
    set maxHealth(value: number) {
        this.data.status.maxHealth = value;
    }
  
    get energy() {
        return this.data.status.energy;
    }
    set energy(value) {
        if (typeof value !== 'number') {
            throw new TypeError(`Typeof ${value} is invalid!`)
        }
  
        this.data.status.energy = value;
    }
  
    get maxEnergy() {
        return this.data.status.maxEnergy
    }
    set maxEnergy(value) {
        if (typeof value !== 'number') {
            throw new TypeError(`Typeof ${value} is invalid!`)
        }
    
        this.data.status.maxEnergy = value;
    }

    // CurMovement
    get myCurMovement() {
        return this.data.myCurMovement;
    }
    set myCurMovement(value) {
        this.data.myCurMovement = value;
    }

    // myCurSpell
    get myCurSpell() {
        return this.data.myCurSpell;
    }
    set myCurSpell(value) {
        this.data.myCurSpell = value;
    }

    // TempEffect getters
    get tempEffect() {
        return this.data.tempEffect;
    }

    // Get spells
    get spells() {
        return this.data.spells;
    }

    // Battle attributes
    get force() {
        return this.data.attributes.force;
    }
    set force(value: number) {
        this.data.attributes.force = value;
    }

    get endurance() {
        return this.data.attributes.endurance;
    } 
    set endurance(value: number) {
        this.data.attributes.endurance = value;
    }

    get agility() {
        return this.data.attributes.agility;
    } 
    set agility(value: number) {
        this.data.attributes.agility = value;
    }

    get intelect() {
        return this.data.attributes.intelect;
    }
    set intelect(value: number) {
        this.data.attributes.intelect = value;
    }

    // Item getters
    get weapon(): any {
        if (this.data.equipments.rightHand) {
            let weapon;

            typeof this.data.equipments.rightHand[0] === 'object'
                ? weapon = this.data.equipments.rightHand[0]
                : weapon = false
            
            return weapon;
        }
    }
    set weapon(value: any) {
        if (typeof this.data.equipments.rightHand[0] === 'object') {
            this.data.equipments.rightHand[0] = value;
        }
    }

    get attackType(): string {
        if (typeof this.data.equipments.rightHand[0] === 'object') {
            switch(this.data.equipments.rightHand[0].type) {
                case "sword":
                    return "Slash"
                default:
                    return "Attack"
            }
        }
        else {
            return "Attack";
        }
    }
}

During the execution of the getMove function in combat, errors start to appear, such as “random()” is undefined, and “variables” is undefined.

Does this lead me to think that the functions should also be inside the .data object to be serialized correctly? I still don’t fully understand how it works.

Detail, the problems happen only after the save is loaded, so I strongly think that this is due to an error in serialization.

It’s not due to serialization. Not seeing internals is a scope issue.

Questions:

  1. Where is the class being defined?
  2. How are you adding it to setup?

[EDIT]
Separate question. Why are you defining some of the methods via assigning an anonymous arrow function to a property:

learnSpell = (newSpell: Spell) => {

Rather than using the standard syntax:

learnSpell(newSpell: Spell) {
1 Like

It is defined in the Player.ts file, and to be set up in setup is in another file. classScope.js.

See my edit above for a separate question.

I’m asking how it’s being included in your project, not what file it’s in.

I assume that file is being compiled into a bundle, so how is that bundle being included in your project?

What does the actual code look like?

Ah yes, the compiler compiles all the TS code to JS, in the scripts output folder, so based on that the game code is compiled. The appearance of this in the JS code is this.

class Player {
    constructor(dataObj) {
        if (!dataObj) {
            throw new TypeError("Data property undefined!");
        }
        this.data = dataObj;
    }
    // Return a new instance containing our own data.
    clone() {
        return new Player(clone(this.data));
    }
    toJSON() {
        return JSON.reviveWrapper('new setup.Player($ReviveData$)', clone(this.data));
    }
    learnSpell(newSpell) {
        const _spell = clone(newSpell);
        this.data.spells.push(_spell);
    }
    getMove() {
        const _player = variables().player;
        if (_player.data.equipments.rightHand[0]) {
            const _equipment = _player.data.equipments.rightHand[0];
            _player.data.myCurMovement = {
                type: _equipment.type,
                name: _equipment.name,
                element: _equipment.elemental.element,
                damage: _equipment.elemental.damage(),
                setEffect() {
                    // Basic damage calc, based on enemies primary resistance status
                    let _damage = Math.round(this.damage - setup.selectedEnemy.data.resistances[this.element]);
                    // enemies armor calc
                    _damage -= Math.floor(setup.selectedEnemy.data.status.armor - _player.data.attributes.armorPen);
                    // Hit chance for the player, based on the enemy/player agility
                    let _hitChance = random(0, setup.selectedEnemy.agility);
                    if (_player.agility > _hitChance) {
                        setup.selectedEnemy.health -= _damage;
                        switch (this.type) {
                            case 'sword':
                                return `You wield your sword and in an instant you use your speed, appearing before your enemy and connecting the sword to his body causing ${_damage} damage!.`;
                        }
                    }
                    else {
                        return `You try to hit your opponent with your ${this.name}, applying maximum strength, but at the last moment he manages to escape the attack.`;
                    }
                }
            };
        }
        else {
            _player.data.myCurMovement = {
                type: "neutral",
                name: "none",
                element: "physical",
                damage: _player.force,
                setEffect() {
                    let _damage = Math.floor(this.damage - setup.selectedEnemy.data.resistances[this.element]);
                    let _hitChance = random(0, setup.selectedEnemy.agility);
                    if (_player.agility > _hitChance) {
                        setup.selectedEnemy.health -= _damage;
                        return `You hit your opponent with your fists, applying maximum strength, doing ${_damage} damage!`;
                    }
                    else {
                        return `You try to hit your opponent with your fists, applying maximum strength, but at the last moment he manages to escape the attack.`;
                    }
                }
            };
        }
    }
    // Runs the effects received from enemies
    runTempEffect() {
        const _tempEffect = variables().player.data.tempEffect;
        _tempEffect.forEach((effect, index) => {
            if (effect.multiEffect) {
                effect.setEffect();
            }
            effect.turns -= 1;
            if (effect.turns <= 0) {
                effect.removeEffect();
                _tempEffect.splice(index, 1);
                _tempEffect[index]
                    ? _tempEffect[index].turns--
                    : '';
            }
        });
    }
    getSpell(spell) {
        const _player = this;
        const _spell = spell.prop;
        _spell.countdown[1] = _spell.countdown[0];
        _player.data.myCurSpell = {
            name: _spell.name,
            element: _spell.element,
            desc: _spell.desc,
            panel: _spell.panel,
            cost: _spell.cost,
            damage: _spell.damage,
            setEffect: _spell.effect,
            runEffect() {
                const output = this.setEffect();
                setup.getBattleMenu();
                return output;
            }
        };
    }
    hasShield() {
        if (typeof this.data.equipments.leftHand === 'object') {
            return true;
        }
    }
    /*
        Inventory functions
    */
    addItem(_item) {
        this.data.inventory.push(_item);
    }
    get invItems() {
        const _inventory = this.data.inventory;
        const _output = [];
        if (_inventory.length > 0) {
            _inventory.forEach((item) => {
                _output.push(item.name);
            });
            return _output;
        }
        else {
            return 'You have no items.';
        }
    }
    /*
        Getters and setters
    */
    get name() {
        return this.data.firstName;
    }
    get surname() {
        return this.data.secondName;
    }
    get sex() {
        return this.data.sex;
    }
    set sex(value) {
        this.data.sex = value;
    }
    get race() {
        return this.data.race;
    }
    set race(value) {
        this.data.race = value;
    }
    get classDesc() {
        const desc = this.data.sex + ' ' + this.data.race + ',' + ' ' + this.data.class;
        return desc;
    }
    // Experience getter
    get experience() {
        return this.data.status.experience;
    }
    set experience(value) {
        if (typeof value !== 'number') {
            throw new TypeError(`Typeof ${value} is invalid!`);
        }
        this.data.status.experience = value;
    }
    get maxExperience() {
        return this.data.status.maxExperience;
    }
    set maxExperience(value) {
        if (typeof value !== 'number') {
            throw new TypeError(`Typeof ${value} is invalid!`);
        }
        this.data.status.maxExperience = value;
    }
    // Body traits
    get eyeColor() {
        return this.data.body.eyeColor;
    }
    set eyeColor(value) {
        this.data.body.eyeColor = value;
    }
    get hairColor() {
        return this.data.body.hairColor;
    }
    set hairColor(value) {
        this.data.body.hairColor = value;
    }
    get skinColor() {
        return this.data.body.skinColor;
    }
    set skinColor(value) {
        this.data.body.skinColor = value;
    }
    get breasts() {
        return this.data.body.hasOwnProperty('breasts')
            ? this.data.body.breasts
            : '';
    }
    set breasts(value) {
        if (this.data.body.sex === variables().worldSexes.male) {
            this.data.body.penisSize = value;
        }
        else {
            delete this.data.body.penisSize;
        }
    }
    get penisSize() {
        return this.data.body.hasOwnProperty('penisSize')
            ? this.data.body.penisSize
            : '';
    }
    set penisSize(value) {
        if (this.data.body.sex === variables().worldSexes.male) {
            this.data.body.penisSize = value;
        }
        else {
            delete this.data.body.penisSize;
        }
    }
    // Battle statuses, health, energy and lust
    get health() {
        return this.data.status.health;
    }
    set health(value) {
        if (typeof value !== 'number') {
            throw new TypeError(`Typeof ${value} is invalid!`);
        }
        this.data.status.health = value;
    }
    get maxHealth() {
        return this.data.status.maxHealth;
    }
    set maxHealth(value) {
        this.data.status.maxHealth = value;
    }
    get energy() {
        return this.data.status.energy;
    }
    set energy(value) {
        if (typeof value !== 'number') {
            throw new TypeError(`Typeof ${value} is invalid!`);
        }
        this.data.status.energy = value;
    }
    get maxEnergy() {
        return this.data.status.maxEnergy;
    }
    set maxEnergy(value) {
        if (typeof value !== 'number') {
            throw new TypeError(`Typeof ${value} is invalid!`);
        }
        this.data.status.maxEnergy = value;
    }
    get lust() {
        return this.data.status.lust;
    }
    set lust(value) {
        this.data.status.lust = value;
    }
    get maxLust() {
        return this.data.status.maxLust;
    }
    set maxLust(value) {
        this.data.status.maxLust = value;
    }
    // CurMovement
    get myCurMovement() {
        return this.data.myCurMovement;
    }
    set myCurMovement(value) {
        this.data.myCurMovement = value;
    }
    // myCurSpell
    get myCurSpell() {
        return this.data.myCurSpell;
    }
    set myCurSpell(value) {
        this.data.myCurSpell = value;
    }
    // TempEffect getters
    get tempEffect() {
        return this.data.tempEffect;
    }
    // Get spells
    get spells() {
        return this.data.spells;
    }
    // Battle attributes
    get force() {
        return this.data.attributes.force;
    }
    set force(value) {
        this.data.attributes.force = value;
    }
    get endurance() {
        return this.data.attributes.endurance;
    }
    set endurance(value) {
        this.data.attributes.endurance = value;
    }
    get agility() {
        return this.data.attributes.agility;
    }
    set agility(value) {
        this.data.attributes.agility = value;
    }
    get intelect() {
        return this.data.attributes.intelect;
    }
    set intelect(value) {
        this.data.attributes.intelect = value;
    }
    // Item getters
    get weapon() {
        if (this.data.equipments.rightHand) {
            let weapon;
            typeof this.data.equipments.rightHand[0] === 'object'
                ? weapon = this.data.equipments.rightHand[0]
                : weapon = false;
            return weapon;
        }
    }
    set weapon(value) {
        if (typeof this.data.equipments.rightHand[0] === 'object') {
            this.data.equipments.rightHand[0] = value;
        }
    }
    get attackType() {
        if (typeof this.data.equipments.rightHand[0] === 'object') {
            switch (this.data.equipments.rightHand[0].type) {
                case "sword":
                    return "Slash";
                default:
                    return "Attack";
            }
        }
        else {
            return "Attack";
        }
    }
}
//# sourceMappingURL=Player.js.map

I changed the functions according to what you indicated.

And for set the classes on the game scope…

// Player class
setup.Player = Player;

// Enemies class
setup.Enemy = Enemy;

// Equipments classes
setup.Weapon = Weapon;
setup.Shield = Shield;
setup.Equipment = Equipment;

// Spell class
setup.Spell = Spell;

How you you passing that directory to Tweego? What’s the command line look like?

As far as I know, there are two steps in the compiler for that. After TS is compiled for JS in the scripts folder, in the JSON file, the excerpt related to this is it

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "open": "opn",
    "gulp": "gulp",
    "lint": "tsc && gulp lint",
    "lint-js": "tsc && gulp lint",
    "lint-css": "gulp validate",
    "build": "tsc && gulp build && tweego -f $npm_package_config_format -m src/modules/ --head=src/head-content.html -o dist/index.html project && opn dist/index.html",
    "testmode": "tsc && gulp build && tweego -f $npm_package_config_format -t -m src/modules/ --head=src/head-content.html -o dist/index.html project && opn dist/index.html",
    "tweegobuild": "tsc && tweego -f $npm_package_config_format -m src/modules/ --head=src/modules/head-content.html -o dist/index.html project",
    "build-win": "tsc && gulp build && tweego -f %npm_package_config_format% -m src/modules/ --head=src/head-content.html -o dist/index.html project && opn dist/index.html",
    "testmode-win": "tsc && gulp build && tweego -f %npm_package_config_format% -t -m src/modules/ --head=src/head-content.html -o dist/index.html project && opn dist/index.html",
    "tweegobuild-win": "tsc && tweego -f %npm_package_config_format% -m src/modules/ --head=src/head-content.html -o dist/index.html project"
  },