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,610
Reaction score
976
First Language
Portuguese - Br
Primarily Uses
RMMV
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,039
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
258
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,610
Reaction score
976
First Language
Portuguese - Br
Primarily Uses
RMMV
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: 1)

Latest Threads

Latest Posts

Latest Profile Posts

This video really speaks to me...

Stream will be live shortly with a session of the interactive text adenture! Feel free to drop by!
I worked a lot more today and I am thinking about Youtube videos to make to my channel, but I am still looking for some content :D

But I am happy to be back at work :D
Update... no scam calls all day. I think they learned their lesson. And I'm working on a fake anti-piracy video, featuring a fan game I'm making in MV. If I had the permission to make the game a licensed game that I could sell (rather than having it totally free like fan games are required to be), I'd use a really cool anti-piracy screen...

Forum statistics

Threads
104,362
Messages
1,005,841
Members
135,879
Latest member
hjbkwjhef
Top