RMMZ Best way to access a Scene variable from a Window

PMantis13

Caipirinha lover.
Veteran
Joined
Aug 29, 2020
Messages
31
Reaction score
18
First Language
PT-BR
Primarily Uses
RMMZ
I just created a new Window extending from Window_Command, so I'm going to call the makeCommandList function, adding the commands I want.

The thing is, these commands should be created dynamically based on another choice the user made on a previous Window_Command. The first part is done, my Scene has an Array with the commands the Window should add, but what is the best way to access this Array from the Window? I mean, the Window shouldn't hold game logic, so it doesn't make sense to assign the Array to a prop on the Window class, right?
 

Eliaquim

Hakuen Studio
Veteran
Joined
May 22, 2018
Messages
1,695
Reaction score
1,113
First Language
Portuguese - Br
Primarily Uses
RMMZ
Hi!
I think you should be safe if you do something like this:

SceneManager._scene > It will access the current scene.

So if you created a variable in your scene that will define what commands to create(ex: this._myVariable), you can do this:

SceneManager._scene._myVariable > It will return the value.

This is the most simple way I can think.
Or you can pass the array to the window when you instantiate it.
 

PMantis13

Caipirinha lover.
Veteran
Joined
Aug 29, 2020
Messages
31
Reaction score
18
First Language
PT-BR
Primarily Uses
RMMZ
Hi!
I think you should be safe if you do something like this:

SceneManager._scene > It will access the current scene.

So if you created a variable in your scene that will define what commands to create(ex: this._myVariable), you can do this:

SceneManager._scene._myVariable > It will return the value.

This is the most simple way I can think.
Or you can pass the array to the window when you instantiate it.
I'm torn between these two solutions, as they don't seem to be the best practice. The first uses a private property (SceneManager._scene) that should not be used outside of the SceneManager scope. The other one injects game logic on a Window whose purpose is dealing with content displayment, loaded from a Scene.

Maybe I'm overthinking a little bit :(
 

nio kasgami

VampCat
Veteran
Joined
May 21, 2013
Messages
8,949
Reaction score
3,042
First Language
French
Primarily Uses
RMMV
It is not the safest way the way you could access that is :
Code:
Scene_Dummy {

  sendData(){
   this.YourWindow.fetchData(this);
  }
}
this is a 'safe' way of transferring your class data. PER say u shouldn't be needing to access the whole class.
 

 Masked 

Assistant
Veteran
Joined
Oct 28, 2015
Messages
90
Reaction score
261
First Language
Portuguese
Primarily Uses
RMMZ
I'm torn between these two solutions, as they don't seem to be the best practice. The first uses a private property (SceneManager._scene) that should not be used outside of the SceneManager scope.
You're absolutely right!

Since JS doesn't actually enforce access control, people seem to be more prone to ignore privacy, but that's bad and comes back to bite you in the ass later if you're not careful.

Back to your question:

The thing is, these commands should be created dynamically based on another choice the user made on a previous Window_Command. The first part is done, my Scene has an Array with the commands the Window should add, but what is the best way to access this Array from the Window? I mean, the Window shouldn't hold game logic, so it doesn't make sense to assign the Array to a prop on the Window class, right?
The answer here is not to make the Window access the array, but to add the commands from the array into the window.

What Window_Command does is basically maintain a list of (name, symbol) pairs, where name is what you see on the screen and symbol is used internally to handle the actual command (this is the important part). Usually it's constructed statically on makeCommandList, which fundamentally works as some kind of "abstract constructor", but you could just as easily use it to build the command list from a list of your own. Like this:

Code:
function Window_MyCommands() {
    this.initialize(...arguments);
}

Window_MyCommands.prototype = Object.create(Window_Command.prototype);
Window_MyCommands.prototype.constructor = Window_MyCommands;

Window_MyCommands.prototype.initialize = function(rect) {
    Window_Command.prototype.initialize.call(this, rect);
    this._commands = [];
};

Window_MyCommands.prototype.addCustomCommand = function(name, symbol) {
    this._commands.push({ name, symbol });
};

Window_MyCommands.prototype.makeCommandList = function() {
    this._commands.forEach(({ name, symbol }) => this.addCommand(name, symbol));
};
Then you can add new commands to your window like this:
Code:
let myWindow = new Window_MyCommands(someRect);

// ... Then, somewhere else when you've decided the commands you need:

myWindow.addCustomCommand('Command 1', 'cmd1');
myWindow.addCustomCommand('Command 2', 'cmd2');
myWindow.addCustomCommand('Command 3', 'cmd3');
myWindow.refresh(); // This call is important, it's what actually calls makeCommandList & co.
The only issue now is that these commands do nothing when you click on them. To solve this, you'd use setHandler from Window_Selectable:
Code:
myWindow.setHandler('cmd1', () => console.log('Called 1!'));
myWindow.setHandler('cmd2', () => console.log('Called 2!'));
myWindow.setHandler('cmd3', () => console.log('Called 3!'));
And that's it. Encapsulation is preserved and we didn't put any game logic into the Window itself, only as a callback from the Scene via setHandler.
 

Eliaquim

Hakuen Studio
Veteran
Joined
May 22, 2018
Messages
1,695
Reaction score
1,113
First Language
Portuguese - Br
Primarily Uses
RMMZ
Maybe I'm overthinking a little bit
I think you are right, to chase the best practice. But I also think you are overthinking it a little bit.
At least for me, in the context of Rpg Maker, I don't see a problem with that.
Or you can stick to the pattern that the current core files already do, that I think it will do the same logic as pass the array to the window though:

JavaScript:
Scene_Equip.prototype.refreshActor = function() {
    const actor = this.actor();
    this._statusWindow.setActor(actor); // pass the actor to the window
    this._slotWindow.setActor(actor);
    this._itemWindow.setActor(actor);
};

Window_Status.prototype.setActor = function(actor) { // function to get the actor through the scene
    if (this._actor !== actor) {
        this._actor = actor;
        this.refresh();
    }
};
 

PMantis13

Caipirinha lover.
Veteran
Joined
Aug 29, 2020
Messages
31
Reaction score
18
First Language
PT-BR
Primarily Uses
RMMZ
You're absolutely right!

Since JS doesn't actually enforce access control, people seem to be more prone to ignore privacy, but that's bad and comes back to bite you in the ass later if you're not careful.

Back to your question:



The answer here is not to make the Window access the array, but to add the commands from the array into the window.

What Window_Command does is basically maintain a list of (name, symbol) pairs, where name is what you see on the screen and symbol is used internally to handle the actual command (this is the important part). Usually it's constructed statically on makeCommandList, which fundamentally works as some kind of "abstract constructor", but you could just as easily use it to build the command list from a list of your own. Like this:

Code:
function Window_MyCommands() {
    this.initialize(...arguments);
}

Window_MyCommands.prototype = Object.create(Window_Command.prototype);
Window_MyCommands.prototype.constructor = Window_MyCommands;

Window_MyCommands.prototype.initialize = function(rect) {
    Window_Command.prototype.initialize.call(this, rect);
    this._commands = [];
};

Window_MyCommands.prototype.addCustomCommand = function(name, symbol) {
    this._commands.push({ name, symbol });
};

Window_MyCommands.prototype.makeCommandList = function() {
    this._commands.forEach(({ name, symbol }) => this.addCommand(name, symbol));
};
Then you can add new commands to your window like this:
Code:
let myWindow = new Window_MyCommands(someRect);

// ... Then, somewhere else when you've decided the commands you need:

myWindow.addCustomCommand('Command 1', 'cmd1');
myWindow.addCustomCommand('Command 2', 'cmd2');
myWindow.addCustomCommand('Command 3', 'cmd3');
myWindow.refresh(); // This call is important, it's what actually calls makeCommandList & co.
The only issue now is that these commands do nothing when you click on them. To solve this, you'd use setHandler from Window_Selectable:
Code:
myWindow.setHandler('cmd1', () => console.log('Called 1!'));
myWindow.setHandler('cmd2', () => console.log('Called 2!'));
myWindow.setHandler('cmd3', () => console.log('Called 3!'));
And that's it. Encapsulation is preserved and we didn't put any game logic into the Window itself, only as a callback from the Scene via setHandler.
My god, that's exactly what I was looking for, I just couldn't think on a solution. I'm struggling a little bit since the docs are nonexistent hehe. Didn't know about the refresh method, that's a life savior in my case. Thank you!
 

Users Who Are Viewing This Thread (Users: 0, Guests: 2)

Latest Threads

Latest Posts

Latest Profile Posts

People3_5 and People3_8 added!

so hopefully tomorrow i get to go home from the hospital i've been here for 5 days already and it's driving me mad. I miss my family like crazy but at least I get to use my own toiletries and my own clothes. My mom is coming to visit soon i can't wait to see her cause i miss her the most. :kaojoy:
Couple hours of work. Might use in my game as a secret find or something. Not sure. Fancy though no? :D
Holy stink, where have I been? Well, I started my temporary job this week. So less time to spend on game design... :(
Cartoonier cloud cover that better fits the art style, as well as (slightly) improved blending/fading... fading clouds when there are larger patterns is still somewhat abrupt for some reason.

Forum statistics

Threads
105,868
Messages
1,017,090
Members
137,587
Latest member
Usagiis
Top