ES6 class not inherited after load save data?

Discussion in 'Learning Javascript' started by Rholin, Jun 12, 2019.

  1. Rholin

    Rholin Warper Member

    Messages:
    3
    Likes Received:
    1
    First Language:
    Chinese
    Update: I just found the solution:
    When the game load the save data, it uses JsonEx._decode() to rebuild the objects. To find the corresponding class constructor, the statement var constructor = window[value['@']]; is used:
    Code:
    JsonEx._decode = function(value, circular, registry) {
        var type = Object.prototype.toString.call(value);
        if (type === '[object Object]' || type === '[object Array]') {
            registry[value['@c']] = value;
            if (value['@']) {
                var constructor = window[value['@']];
                if (constructor) {
                    value = this._resetPrototype(value, constructor.prototype);
                }
            }
            for (var key in value) {
                if (value.hasOwnProperty(key)) {
                    if(value[key] && value[key]['@a']){
                        //object is array wrapper
                        var body = value[key]['@a'];
                        body['@c'] = value[key]['@c'];
                        value[key] = body;
                    }
                    if(value[key] && value[key]['@r']){
                        //object is reference
                        circular.push([key, value, value[key]['@r']])
                    }
                    value[key] = this._decode(value[key], circular, registry);
                }
            }
        }
        return value;
    };
    However, a ES6 declared class is not a property of window, so this method fails. Instead, we could use eval() to get the correct constructor of a ES6 declared class:
    Code:
    let constructor = window[value['@']] || eval(value['@']);
    




    I made an ES6 class to manage the actor parameters:
    Code:
    System.Param.Types = {
        mhp: 0, // max hp 500 + 100 * lv (600-8500)
        mmp: 1, // max mp 100 + 10 * lv (100-900)
        pat: 2, // physical attack 50 + 10 * lv (50-850)
        mat: 3, // magical attack 50 + 10 * lv (50-850)
        pdf: 4, // physical defense 20 + 8 * lv (20-660)
        mdf: 5, // magical defense 20 + 8 * lv (20-660)
        cri: 6, // critical chance 5 + 1 * lv (5-85)
        eva: 7, // evade chance 1 + 1 * lv (1-80)
        spd: 8  // speed 50 + 1 * lv (50-130)
    };
    
    class BasicParam {
        /**
         *
         * @param params {Array/Undefined}
         * @param owner {Game_BattlerBase}
         */
        constructor(params, owner) {
            if (params) {
                if (params.length === this.paramNum) {
                    this.values = params;
                } else {
                    throw 'invalid params length: ' + params.length;
                }
            } else {
                this.values = new Array(this.paramNum);
                this.clear();
            }
            if (owner) this._owner = owner;
        }
    
        get paramTypes() {
            return System.Param.Types;
        }
        get paramNum() {return Object.keys(this.paramTypes).length; }
        get owner() {return this._owner;}
    
        /**
         *
         * @param type {String/Number} type can be index integer or param's name
         * @returns {Number}
         */
        getValue(type) {
            let index = ((typeof type) === 'string')? this.paramTypes[type] : type;
            return this.values[index];
        }
    
        /**
         *
         * @param type {String/Number} type can be index integer or param's name
         * @param value {Number}
         */
        setValue(type, value) {
            let index = ((typeof type) === 'string')? this.paramTypes[type] : type;
            this.values[index] = value;
        }
    
        clear() {
            for (let i = 0; i < this.paramNum; i++) {
                this.values[i] = 0;
            }
        }
    
        /**
         *
         * @param adder {BasicParam}
         */
        addByParam(adder) {
            for (let i = 0; i < this.paramNum; i++) {
                this.values[i] += adder.values[i];
            }
        }
    
        /**
         *
         * @param scaler {BasicParam}
         */
        scaleByParam(scaler) {
            for (let i = 0; i < this.paramNum; i++) {
                this.values[i] += Math.floor(this.values[i] * scaler.values[i] / 100);
            }
        }
    
        /**
         *
         * @param type {String/Number} type can be index integer or param's name
         * @param value {Number}
         */
        addValue(type, value) {
            let index = ((typeof type) === 'string')? this.paramTypes[type] : type;
            this.values[index] += value;
        }
    }
    It works well when I test the game, but after I load a save data, the param of a character is not a BasicParam instance Any more, as is showed in the picture:
    [​IMG]
    does this mean I cannot use an ES6 class? or there is a way to solve that.
     
    Last edited: Jun 12, 2019
    #1
  2. mlogan

    mlogan Global Moderators Global Mod

    Messages:
    13,661
    Likes Received:
    7,531
    Location:
    Texas
    First Language:
    English
    Primarily Uses:
    RMMV

    I've moved this thread to Learning Javascript. Please be sure to post your threads in the correct forum next time. Thank you.

     
    #2
  3. Aloe Guvner

    Aloe Guvner Walrus Veteran

    Messages:
    1,509
    Likes Received:
    954
    Location:
    USA
    First Language:
    English
    Primarily Uses:
    RMMV
    #3
  4. Rholin

    Rholin Warper Member

    Messages:
    3
    Likes Received:
    1
    First Language:
    Chinese
    Thanks, I'll be cautious next time
     
    #4
  5. Rholin

    Rholin Warper Member

    Messages:
    3
    Likes Received:
    1
    First Language:
    Chinese
    the codes related are here:
    Code:
    System.Param.Game_BattlerBase_initMembers = Game_BattlerBase.prototype.initMembers;
    Game_BattlerBase.prototype.initMembers = function() {
        this._basicParam = new BasicParam(null, this);
        this._homeParam = new BasicParam(null, this);
        this._totalParam = new BasicParam(null, this);
        this._tempPlusParam = new BasicParam(null, this);
        this._tempScaleParam = new BasicParam(null, this);
        System.Param.Game_BattlerBase_initMembers.call(this);
    };
    
    Object.defineProperties(Game_BattlerBase.prototype, {
        totalParam: {
            get: function() {
                if (SceneManager._scene instanceof  Scene_Battle) {
                    return this._totalParam;
                } else {
                    return this._homeParam;
                }
            }
        }
    });
    
    
    
    
    I override some functions (include $gameActor.actor()) so they behave in a different way. But I think this is not important for the question because the codes work well if I start a new game. The problem should be related to load save data method

    Update: I found a solution and posted it in the main thread, thanks all the way!
     
    Last edited: Jun 12, 2019
    #5
    Aloe Guvner likes this.
  6. Aloe Guvner

    Aloe Guvner Walrus Veteran

    Messages:
    1,509
    Likes Received:
    954
    Location:
    USA
    First Language:
    English
    Primarily Uses:
    RMMV
    Ah ok, I thought there could be an issue with getting a reference to the correct actor but if you overwrote Game_Actors.prototype.actor then that's fine.

    Good catch, it's one of the few differences between the "class" keyword and the "old" way of creating a function constructor.

    If you wanted to avoid eval, you could explicitly set the window object to have a reference to the constructor of your class:

    Code:
    window.BasicParam = class BasicParam { ... } 
    Another way would be to keep all of your custom classes that you need to get the constructors for in their own object, like this:

    Code:
    const myClasses = {
       BasicParam: class BasicParam { ... }
    } 
    In this case "eval" is probably fine but generally you'll want to avoid it.
     
    #6

Share This Page