简体   繁体   中英

Modifying variables in subclass instances

I've become hopelessly addicted to Screeps recently, and I refactored some code to make a task-based implementation. Tasks are things like "walk to and then harvest until you are at full capacity" and are based off of a single base task template written as an ES6-style class. Creeps can be assigned tasks through a wrapper (tasks.js) that loads the relevant task file and returns a new task instance.

Today I ran into a strange bug that makes me think I don't fully understand Javascript's inheritance model. Below is the relevant code:

Task.js: (base task class)

class Task {
    constructor(taskName) {
        // Parameters for the task
        this.name = taskName; // name of task
        this.quiet = false; // suppress console logging if true
        this.creep = [creep assigned to this task]
        this.target = [thing task operates on, e.g. "repair this wall"]
        ...
    }
    ...
    // Execute this task each tick. Returns nothing unless work is done.
    step() {
        ...
        if (creep.pos.inRangeTo(target, this.targetRange)) {
            var workResult = this.work();
            console.log(this.quiet) // < returns false, should be true?
            if (workResult != OK && this.quiet == false) {
                creep.log("Error: " + workResult); // < is printed when run
            }
            return workResult;
        } [else move to target]
    }
    ...
    // Task to perform when at the target
    work() {
        // overwrite this in child class
    }
}

module.exports = Task;

task_harvest.js: (harvesting task)

var Task = require('Task');

class taskHarvest extends Task {
    constructor() {
        super('harvest');
        // no mention of this.quiet here
    }
    ...
    work() {
        console.log("harvest:" + this.quiet);
        return this.creep.harvest(this.target);
    }
}
module.exports = taskHarvest;

tasks.js: (wrapper to generate a new task instance via a function call)

module.exports = function (taskName) {
    var TaskClass = require('task_' + taskName); // all tasks follow this naming pattern
    var taskInstance = new TaskClass;
    return taskInstance;
};

harvester.js: (behavior model for a harvester creep)

var tasks = require('tasks');
var roleHarvester = {
    ...
    harvest: function (creep) {
        var target = Game.getObjectById(creep.memory.assignment);
        var taskHarvest = tasks('harvest');
        taskHarvest.quiet = true;  // < this task shouldn't print anything
        creep.assign(taskHarvest, target); // assigns to creep.task
        return OK;
    },
    ...
    run: function (creep) { // executed every tick
        // execute the task
        creep.task.step();
    },
   ...
}

When I assign a creep to harvest from a source, I create a new task from task_harvest.js, set its quiet property to be true , and bind it and its target to the creep. Once the creep has a task it is instructed to run it until it becomes invalid (code not included above). The creep executes the task fine, but it still logs everything to the console.

I would think that in harvester.js, when I set taskHarvest.quiet = true; , the behavior imported from Task.js would see this.quiet as true . However, it seems as though that is not the case. In roleHarvester , running console.log(creep.task.quiet) returns true , but in Task , running console.log(this.quiet) when the creep is executing the assigned task gives false .

I could add quiet into the constructor as an optional parameter, but that's convoluted and I want to know why what I'm doing isn't working.

Nevermind, it actually wasn't an inheritance problem; it was a problem caused by the game mechanics: taskHarvest.quiet wasn't getting deleted each tick. Screeps only allows you to store JSON-serializable objects in memory, so I store the task settings in memory and reconstruct the task object each tick:

Object.defineProperty(Creep.prototype, 'task', {
    get: function () { // provide new task object recreated from literals stored in creep.memory.task
        if (this.memory.task != null) {
            var task = tasks(this.memory.task.name);
            task.creepName = this.memory.task.creepName;
            task.targetID = this.memory.task.targetID;
            task.data = this.memory.task.data; // < task.quiet is now task.data.quiet
            return task;
        } else {
            return null;
        }
    },
    set: function(newTask) {
        if (newTask != null) {
            this.log("use Creep.assign() to assign tasks. Creep.task = ___ should only be used to null a task.");
        } else {
            this.memory.task = newTask;
        }
    }
});

taskHarvest.quiet wasn't getting stored in memory, so it wouldn't persist past the first tick of the task. I now store all instance-level adjustable parameters in a task.data object, so task.quiet is now task.data.quiet . This fixed the problem; sorry for producing any confusion!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM