07-17-2023, 04:01 AM (This post was last modified: 07-17-2023, 04:04 AM by kyonides.)
To Encapsulate? Or Not to Encapsulate?
That is the... Controversy!
On another board I wound up talking about breaking encapsulation.
What is encapsulation?
Is it a bad thing at all?
The term itself is defined as:
Wikipedia Wrote:Encapsulation is used to refer to one of two related but distinct notions, and sometimes to the combination thereof:
A language mechanism for restricting direct access to some of the object’s components.
A language construct that facilitates the bundling of data with the methods (or other functions) operating on that data.
That is limiting how you call and manipulate a given portion of code belonging to the object. Keep in mind that an object can be either a class or a module in Ruby. Even numbers are objects because they do own several methods.
The second part handles the way you implement your internal and external calls inside blocks called methods in Ruby and functions in C, C++ and Javascript. This is some sort of task management. A single method is NOT supposed to take care of EVERYTHING.
A bad example that is linked to the lack of modularity is any main method in scene scripts found in RMXP. main contains almost everything. (The usual exception would be its update method.) That changed in VX and VX ACE because they do feature many helper methods like pre_start and start or terminate there. Now every method has a task or a group of related tasks to perform inside every method.
How do we know we are breaking the encapsulation?
Have you asked a scripter to give you at least a hint on how to access any of the current scene's methods from the Game_Party or the Spriteset_Map or vice versa?
Guess what? You broke it!
You are only supposed to access the methods already provided by the script and that's it!
Oh yes, I know that it would make custom patches very difficult to be crafted under certain circumstances. Even so, you could accomplish the same goal in a totally indirect way instead.
And it is totally true that we got the attr_reader and attr_writer or attr_accessor methods to quickly make its variables accessible for free. But there's a catch!
What happens if you call the variable directly?
In order to make a point here, I will need to come up with some mockup code to better illustrate it.
Code:
class Game_System
alias :gm_sys_init :initialize
def initialize
gm_sys_init
@traps = []
end
attr_reader :traps
end
There I created a new traps method belonging to the Game_System class that is accessible via the $game_system global variable.
Am I breaking the encapsulation principle here?
Apparently not... but I could be in no time.
How could you do it?
Well, it could be as EASY as making the following call.
Code:
$game_system.traps[10] = BearTrap.new
There I have already altered the actual contents of the traps Array without caring about checks and controls.
Such cases demand from us the creation of custom methods to ensure we won't be leaving blank spaces or any foreign object that should not be found there like a number or a boolean.
A good but quite limiting example of this could be the $game_party.add_actor method that will limit the number of heroes that can join our party in RMXP or RMVX.
Yes, that means you got to create more and more wrapper methods to keep access to $game_system or $game_party methods safe.
A scripter's life is hard, guys!
"For God has not destined us for wrath, but for obtaining salvation through our Lord Jesus Christ," 1 Thessalonians 5:9
Maranatha!
The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.
This is something everybody should know about Patches and Bug Fixes.
Patches could also be named bug fixes depending on the issues that might have appeared while working on modifications of existing scripts. In other cases they might just alter or even replace the way the "original" script was supposed to work.
ALWAYS treat them as separate scripts. You'll thank me later on. Especially true if any patch you have included in the script editor ends up giving you headaches because it's not compatible with any other script there. Also true if it has some bugs that need some fixing.
If the Patch includes any mention of class SomeName or module AnotherName, it NEVER belongs to a script call event command. Create a new section in the script editor and paste it there.
Patches and Bug Fixes should always be placed in any free section BELOW their "parent" scripts, the scripts they partially modify or extend with new features.
"For God has not destined us for wrath, but for obtaining salvation through our Lord Jesus Christ," 1 Thessalonians 5:9
Maranatha!
The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.
08-28-2023, 03:34 AM (This post was last modified: 08-28-2023, 03:35 AM by kyonides.)
self && @instance_variables
In RGSSx scripts it is not impossible to find calls that begin with a self reserved word.
What is self?
As we had learned long time ago, self simply means the same class or module.
Why is it there?
In modules like Kernel or RPG::Cache alias Cache and the File class, you cannot call certain methods UNLESS you include the actual module or class name somehow.
While defining stuff pertaining to your module or class, you can avoid using its actual name and simply replace it with self. It is way more practical than typing the same name over and over again.
Why can't you call them in any other way?
That is because the method has become what we call a singleton method. Only a single instance will get access to the data it contains at any given moment. What instance? The module or class itself!
HEY! That's not true!
Huh? What do you mean by that?
I've seen it used in other circumstances and it's not referring to the module or class at all.
Oh, you mean those instances like self.contents and self.bitmap found in window scripts.
For starters, yeah...
Don't worry about it! That is easy to explain indeed.
What happened there is that some of those calls are seeking hidden methods, those written in C or C++, and there is no other way to access them or else they could be treated as simple local variables.
Code:
self.contents.font.size = 32
In this example, the use of self is optional. Just use contents and it will work fine.
Is there any difference between using one option or the other?
You got me here. Yes, there is one!
What takes place there is that contents alone forces Ruby to look for any instance of either a local variable OR a method called contents. While self.contents already informs the interpreter that you are looking for a method, be it a class or instance method. (Class methods are singletons by default.)
The first call would just create a local variable that will cease to exist the moment that method finishes its execution. The latter is ye old and trustworthy self.contents assignment method and it will keep the Bitmap object stored there for future references.
This is What Actually Made Me Write This Review!
There is a caveat that we scripters should know about just in case we still ignored it for any reason.
What happens very often in RGSS1, namely on RMXP, during battle is a very unusual phenomenon in Ruby scripting. This is one of the very few places where this has ever happened in decades.
classes like Game_Battler make several references to methods like self.damage instead of using @damage directly.
Why does this ever happen?
Well, I guess that scripters were not fully aware of how @instance variables were supposed to be used in a script. Thus, what I mean here is that they did something totally weird by calling self.damage instead of simply typing @damage whenever they want to calculate the actual damage or healing any given Game_Actor or Game_Enemy is about to receive at any moment.
But is it something really bad at all?
Hrm, I suppose that depends on what you want to accomplish there.
If you are planning to pre-calculate some basic damage, that might be helpful indeed. Otherwise using a plain @damage instance variable would suffice there, letting you to hit that bad mobster as hard as you can. Go for it, Arshes!
Other implementations like RGSS3 actually provides us with calls to methods in place of variables to let you calculate base prices, for instance. And nope, they do not use the self pseudo variable at all. They may or may not have the same name as the actual variable that is going to be used there. You see, once you choose to use a method there, you are free to name it they way you prefer to your heart's content.
And anybody can later alias the method at will.
The Conclusion
This is one of those cases where there is no way to get quite conclusive about what you are supposed to do now because people can tell you at a certain point that it is a matter of style or taste.
And they might be right about that no matter how weird that might sound like.
"For God has not destined us for wrath, but for obtaining salvation through our Lord Jesus Christ," 1 Thessalonians 5:9
Maranatha!
The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.
11-18-2023, 09:02 AM (This post was last modified: 11-18-2023, 09:14 PM by kyonides.
Edit Reason: Small Code Mod + Additional Statement
)
Moronic Design: Ruby & The Embedded Methods
Months ago there was a discussion on a topic that made some people not used to Ruby's nuances pretty mad indeed. The topic at hand was defining a method inside another method, pretty much embedding it without caring about the consequences of such a poor design. I heavily critized it because it will never ever make any sense in Ruby nor RGSS any version at all.
Now I am going to cover that here to let you know what actually happens when you try to imitate one of the effects, namely treating Ruby's methods as JavaScript's functions thinking they both are first class citizens in their respective programming languages.
The results might heavily disappoint some of you for not reflecting your wishes or fears as accurately as it was expected. Actually, the outcome is quite predictable while still retaining its nonsensical nature intact.
Conclusions:
Ruby has NEVER treated its methods as objects that can be easily and perfectly defined pretty much anywhere in your code like JavaScript functions do in later iterations of the RM engine.
The contents of embedded methods will not be accessible from within the "parent" method. They will just get defined as usual and will belong to that instance of the class, aka the current object, only and not to the fake parent method.
Ruby will parse and (re)define that weird internal method will take place every single time you call the so called parent method. (Ruby will only keep a reference to the last definition of a method. Here it will only replace the previous definition over and over again.)
There is no way you can make Ruby get in trouble by making a large chain of methods the way it allows you to chain constants in a weird mix of module / class & constant list kept together as a single but large script call that has no theoretical end as far as we scripters know.
There is no way we can ever get something meaningful and useful out of making this terrible mistake.
The Proof
We need to come up with some test code to make sure we can prove what I have exposed above as the only valid conclusions I could come up with after running it several times in a row.
So here it is!
Sample Code
Code:
class A
def hello
print "Hello!"
def bye
print "Bye!"
end
end
end
Now let us run it by pasting the following snippet right on a script section located before the Main script and lets launch the game after saving our changes to the script editor.
Test Code
Code:
print a = A.new
print a.hello
begin
print a.hello.bye
rescue
print "Failed to execute a.hello.bye"
end
a.bye
a.hello
a.bye
a.hello
Side Note:
If you print both the a.hello or a.bye calls, you will not get any error messages. Even so, removing the print call like I did in the code above can make you throw an error for relying on a rescue call inside an IRB binding. What this means is that a console window running the Ruby interpreter is not supposed to execute such a rescue statement. This is true for the 2.7 release at least.
Inline rescue calls work normally even while running the IRB on a console window aka shell.
What we are going to do now is test it on vanilla RMXP (or VX or ACE if you prefer) and also on HiddenChest game engine and just see what happens then. Will both engines return the same results?
What we should notice after reading the output above is that whenever a method gets defined, meaning being parsed and evaluated first, it will always return a nil object as its normal outcome.
Ruby 2.7's Output - HiddenChest
Code:
#<A:0x00007f1d4cf54810>
Hello!
bye
Hello!
Failed to execute a.hello.bye
Bye!
Hello!
bye
Bye!
Hello!
bye
OK, this is very similar to the first output, except that Ruby 2.7 returns a :bye symbol after defining any given method. For some reason still unknown to your servitor, it doesn't return anything readable after printing the bye method's return value.
Note:
After other people also ran the code and reviewing the default implementation of Ruby called either MRI or YARV depending on the version, using two consecutive print calls like I originally did will return a nil value or a whitespace accordingly.
In both cases you can either get a bye string representing the method's name by calling the usual print function or the actual :bye "symbol" (actually a string that keeps its typical colon at the beginning) if you call the p function instead.
"For God has not destined us for wrath, but for obtaining salvation through our Lord Jesus Christ," 1 Thessalonians 5:9
Maranatha!
The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.
04-16-2024, 06:57 AM (This post was last modified: 03-24-2025, 09:47 PM by kyonides.)
Using or Abusing of Game Logic:
The Perfect Excuse for Covering a Clear Case of Friendly Fire
Normally, we wouldn't combine a game skill or plot to cover a sinister plot that involves a casualty caused by some friendly fire.
Why would anybody ever do that, right?
Yet, somebody unintentionally managed to come up with the perfect scenario where the heroic platoon can no longer blame anybody else, like a devious foe, for hurting them. Why? I'll tell you why in a moment.
Code:
$game_party.members.each do |mem|
item = $data_skills[80]
mem.item_apply(mem, item)
end
That code above lets you apply a skill as an item, because that's how VX ACE implemented both of them, on a given target. In this case the target is a hero, starting from the party leader himself.
This is the first quake alarm that should shake us like crazy... if we ever care about these petty details, that is.
IF you pay close attention to that seemingly innocent piece of code, it has a serious flaw. A logical one indeed.
You see, both the actual attacker and its victim is... the same guy! The hero himself!
So now they all can claim that they were hit by an ally!
That's what we'd call an extreme but also laughable instance of friendly fire taking place under the hood.
How do I know that?
That's an easy one, guys!
I know that because both the attacker and the target are the same mem or party member in that specific order.
What Actually Happens There
Let's say your skill is physical by definition. That would mean that mages who normally have high MATK stats but not ATK ones could be seriously hurt by a warrior's skill while fighters like Orson wouldn't. If it's magical by nature and cast by a mage, then Orson with high DEF or PDEF but not MDEF would be the first casualty here. Of course, this might not take place if they also have high DEF / PDEF or MDEF stats.
Of course, if you simply don't mind about these technicalities, well, you're good to go!
My Suggestion
You better pick an enemy from the database to cast the spell instead.
And yes, it's that simple.
NOTES
Here the caster or attacker must be a Game_Battler or one of its child classes like Game_Enemy.
item doesn't need to be redefined every single time you pick another hero.
Code:
caster = Game_Enemy.new(0, 1) # Enemy ID is 1 here
item = $data_skills[80]
$game_party.members.each do |member|
member.item_apply(caster, item)
end
It doesn't have to be enemy #1 , obviously.
Keep in mind that now your heroes would no longer need to worry about not cleaning their guns properly following all local regulations.
"For God has not destined us for wrath, but for obtaining salvation through our Lord Jesus Christ," 1 Thessalonians 5:9
Maranatha!
The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.
Not long ago, a furry Wulfo noticed something about using strings and symbols in Ruby, either by running a vanilla RMXP game or my own custom engine HiddenChest. Without further delay, let's read his comments thoroughly.
Some Wulfo Once Said
For scripters of RUBY, I did a little bit of discovery.
Within RUBY... even available when using RPGMaker XP's RGSS... you have access to the OBJECT class. Not that you see it in the Scripts database. And the OBJECT class has a few methods. One of these methods is 'instance_variables' which would return an array listing all the variables specific for the class within.
Ergo, you would get instance variables representing the command window, the created sprite background, and a few others if used within "Scene_Title"
However, different versions of Ruby return this array differently.
With Ruby v 1.8.1 (used by both RPGMaker XP and VX at the very least), the names stored within the returned array are listed as strings:
But at some point, RUBY had changed, and the current versions (used by HiddenChest) now return the instance variables as their 'symbols', and not by their names as strings.
Now, this is important as older scripts that utilize this method (such as the RMXP SDK) to retrieve the names in string format. And yep, that can cause problems.
BUT, I found that there's no issue just converting the 'symbol' name into a string.
This STOOPID thing works. And lets what I'm doing work between both new and old RUBY versions.
I know, its not 'proper' to change a variable itself from one type to another (like changing a float to an integer), and if a variable needs to be 'defined' as a particular fixed type this wouldn't exactly work... requiring a second variable defined soley for 'string names'. But for the current RUBY version, it works.
Honestly, that's old news to me.
And there's something I'd really like to add to his remarks there. Adding the condition to not convert a string into a string if it's already a string object is quite weird, to say the least.
"string".to_s by itself will simply return self, this means it will return "string" after processing that line. It won't transform anything. Instead, it will return its default value, the string itself!!
I suspect this change might have happened in Ruby version 1.9 or later.
Based on old comments online, it seems that the Ruby on Rails aka Rails community and base code pushed Ruby core developers into implementing more and more symbols in the default MRI code to reduce the amount of unfrozen strings that needed to be frozen at runtime.
The Advantage of Relying on Symbols Not Strings
Symbols are unique by definition. Once created, there'll be just a single copy of it for the rest of the code's execution. This is great if you need unique identifiers for very specific things in your custom script. I can give you my Terms module script as an example. By using a single language symbol like :eng or :deu or :spa you can make many different arrays or hashes of strings quickly change your game's graphical user interface or GUI in no time. You'd only need to change a single instance variable in Terms module and you're done!
Fine, you'd also need to refresh your windows or sprites right away or you'd never notice any changes on screen.
"For God has not destined us for wrath, but for obtaining salvation through our Lord Jesus Christ," 1 Thessalonians 5:9
Maranatha!
The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.
03-24-2025, 07:50 PM (This post was last modified: 03-24-2025, 10:00 PM by kyonides.
Edit Reason: Updated Many Times Already
)
Effects of Relying on a Bad Explanation:
Getters, Setters & Wrapper Methods
When someone else was explaining how the getter and setter methods work in Ruby, the next thing this guy mentioned was that they were some sort of wrapper methods created around the corresponding variables to let the program access them. Later on you could also read that it limit the circumstances under which the variables can be altered by mistake, but is that true?
I've got some sad news for all those people that once thought it was true. No, that's not how it works and it doesn't prevent you from changing the value at any inconvenient moment and those methods can be accessed from ANYWHERE at ANY GIVEN TIME.
Thus, some var=(val) method isn't a wrapper of @some, it's its actual setter method. It simply was manually created instead of using Module's attr_writter shortcut. Even if the setter included a conditional statement, it'd remain as a setter.
If you don't want any scene or window or sprite to be able to alter those variables, then don't define their setter methods. Well, you could add a condition either in the line of code where you need to alter them externally OR add a condition inside the setter method to prevent it from ever being a nil value or a negative number or anything the like.
Is it a good idea of calling a manually defined getter or setter method a wrapper?
Nope, it isn't. It'd be quite terrible for setters. You better reserve that term for other instances where you'll definitely need it.
It'd be a wrapper method in Ruby IF:
It had another name or a different number of arguments like set_vars(a, b)
It's a custom spriteset class that lets you change all of its sprites' visibility state with a visible=(bool) method.
The Game_Party#add_actor method that will limit the number of heroes that can join our party in the RM engine.
Of course, there might be other ways to create wrappers that I haven't mentioned here like Game_Actors class being a wrapper class for a @data Array object that contains Game_Actor objects, i.e. $game_actors[1] calls the Game_Actors#[](index) method that internally returns the value of @data[index] or creates a brand new Game_Actor object.
Is there a way to easily prove that those getters and setters don't behave like mere wrappers?
Yes, there is! Let's use a simple test code.
Code:
class Person
def initialize
@age = 1
end
def age
print "Current age #{@age}\n"
@age
end
def age=(new_age)
print "Altering your age now!\n"
@age = new_age
end
attr_reader :age
attr_writer :age
# Or just call this instead: attr_accessor :age
end
The code above would define 2 methods for the same @age variable, its getter and its setter in that specific order. They are supposed to print something on screen on both RMXP and RMVX. In RMVX ACE it would require you to open the console window first.
But there's a catch! By calling the attr_reader and attr_writer methods, or simply the attr_accessor method, we have overwritten the getter and the setter we had manually created for the @age variable.
What that means is that now we won't ever be able to print anything on the popup window or the console window depending on which RM engine you're using.
OK, you still can #comment out the attr_ lines to be able to read those strings as first intended.
NOTE
I have added the initialize method to the Person class to make sure that the @age variable will never return a nil value, which is the default return value of any variable that has not been assigned a different value at some point.
Other Ways to Deal With It
Later on Wulfo showed some interest in adding some alternate code to let people handle the return of a nil value in a different way, especially if I had not defined the initialize method there.
The code I had proposed would look like this then:
Code:
class Person
def age
print "Current age #{@age}\n"
@age ||= 1
end
def age=(new_age)
print "Altering your age now!\n"
@age = new_age
end
attr_reader :age
attr_writer :age
# Or just call this instead: attr_accessor :age
end
What does that altered line of code do there?
Well, it's pretty simple indeed. If the @age has not been predefined, it will pick 1 as its default value. If at some point the value of @age has been altered, let's say that now it's 15, it will ignore 1 and return 15 instead.
Why? Because it's no longer equal to nil. Only nil values would get replaced using the ||= operator.
Yet, don't forget that the attr_ methods are still in use so they won't ever print anything on a popup window or the console window if any.
If it were able to print anything, you'll soon notice that the age getter method would print a nil (an empty string "") instead of a number as its current age.
Is that the only way we could have done it?
Nope, there's yet another way to deal with it, but it's not as efficient as using the ||= operator there.
Here's the code that Wulfo had suggested a while ago. It presupposes that there has been no attempt to define the initialize method so far.
Code:
class Person
def age
@age = 1 if @age.nil?
print "Current age #{@age}\n"
@age
end
def age=(new_age)
print "Altering your age now!\n"
@age = new_age
end
attr_reader :age
attr_writer :age
# Or just call this instead: attr_accessor :age
end
As you can see, the interpreter would have to always make the check in a separate line before printing the @age variable via print and then returning the value of @age... IF the attr_reader or attr_writer or attr_accessor methods had not been used there or had been #commented out.
You shouldn't underestimate that piece of code. It does work as intended, but the ||= operator makes it easy to skip it.
Nonetheless, there's a caveat!
The ||= operator won't let you preassign the value before printing the previous value of @age. So it does have a downside after all.
"For God has not destined us for wrath, but for obtaining salvation through our Lord Jesus Christ," 1 Thessalonians 5:9
Maranatha!
The Internet might be either your friend or enemy. It just depends on whether or not she has a bad hair day.