Posts: 11,193
Threads: 649
Joined: May 2009
That may run fine for a time. And yes, you were able to gain the value of MyModule:MyClass::A just fine.
However, recursion does occur. For any who wishes to go into their script editor and type this within the Main script (typically at the bottom), here is an example of said recursion being created:
Code: module MyModule
class MyClass
A = 'A'
B = 'B'
include MyModule
end
end
p MyModule::MyClass::MyClass::MyClass::MyClass::B
I am able to receive output, not just from MyModule::MyClass. But from MyModule::MyClass:MyClass (which is the contents of MyModule thrown into MyClass), and the contents of MyModule::MyClass::MyClass::MyClass (again, throwing the contents back in once more).
Honestly, the recursion is so bad, I could use this for the output:
Code: p MyModule::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::B
And with 15 instances of MyClass, one being within the previous over and over, it still yield the same result. The MyClass object is being duplicated over and over and over, taking up more memory as the program runs.
Anyone can try that example I posted and see for themselves.
Up is down, left is right and sideways is straight ahead. - Cord "Circle of Iron", 1978 (written by Bruce Lee and James Coburn... really...)
Above are clickable links
Posts: 4,592
Threads: 536
Joined: Dec 2009
02-03-2023, 04:33 AM
(This post was last modified: 02-03-2023, 05:00 AM by kyonides.
Edit Reason: Additional Comment
)
(02-03-2023, 03:52 AM)DerVVulfman Wrote: Code: module MyModule
class MyClass
A = 'A'
B = 'B'
include MyModule
end
end
p MyModule::MyClass::MyClass::MyClass::MyClass::B
...
Honestly, the recursion is so bad, I could use this for the output:
Code: p MyModule::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::MyClass::B
What a mess made in hell!
OK, that case is even worse than the one previously presented on my previous post.
There is a caveat. It will not cause the recursion on its own, instead it will do it if it is commanded to keep looking for it.
Is that a serious issue?
It is more like a yes and not or a German jein.
What I mean is that it is a TERRIBLE IDEA for sure, but it is not like a method recursion where you cannot stop it from running unless you had added a valid condition there to break the invisible loop.
This will not cause you an immediate issue like running low on memory per se, but you will NOT be able to undo it unless you quit the program or application.
And even before Wulfo friendly? hijacks my thread again for the same reason, I will explain something that the average joe might not notice.
The first time you call that, it might not be an issue. Nonetheless, it will keep getting ugly the more times you keep calling it because it was used, let us say, inside a specific method. Memory CANNOT be freed soon just as expected. This translates to crafting messy code that is inexcusable at all.
Should we care about it?
The answer is yes!
Simply NEVER EVER ATTEMPT to run a real life program with such a weird and buggy inclusion of a module in a nested class.
Before I finish my short dissertation on this matter, I got to add something else.
Whatever has been included in that module is ALREADY AVAILABLE in the nested classes.
You do not need to include the container module at all!
Yet, there IS a caveat you should care about!
Code: module Some
class Thing
end
end
The definition above will let you access everything you have defined in the Some module, but the one below does not ever let you get to its CONSTANTS.
Code: class Some::Thing
# some code here!
end
Why it doesn't?
The answer is simple, it is because of its scope!
It cannot see where the CONSTANTS are located. You have reduced the scope to that Thing class only!
This means you will need to call all of its module's CONSTANTS like this:
And that is, my friends, a very unpractical way to achieve a simple goal.
Perhaps I could submit a request to the Ruby Core devs to make them forbid that inclusion in recent versions of Ruby at least.
"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.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
Posts: 4,592
Threads: 536
Joined: Dec 2009
04-19-2023, 12:47 AM
(This post was last modified: 04-19-2023, 12:47 AM by kyonides.)
When to Use the Module Class' include Feature
I have seen many times that people kind of abuse of the :: scope operator when they are fiddling with modules. That inspired me to revisit the modules to make sure they stop repeating themselves like crazy.
Basic Setup Modules
Code: module SomeUser
module Config
DEFAULT_FONT_SIZE = 20
DEFAULT_TITLE = "Some Label"
end
end
So far there is nothing new to see here but everything changes once we start dealing with a specific Window class. Let us take a look at some fake Window_TitleCommand Add-on.
Code: class Window_TitleCommand
alias :some_user_config_win_ttl_comm_draw_item :draw_item
def default_font_size
SomeUser::Config::DEFAULT_FONT_SIZE
end
def draw_item(index)
some_user_config_win_ttl_comm_draw_item(index)
contents.font.size = self.default_font_size
text = SomeUser::Config::DEFAULT_TITLE
contents.draw_text(4, 26, contents_width, 24, text, 1)
end
end
As you can see above, we had to use the :: scope operator several times in our scriptlet. Honestly, I got to tell you that it is not elegant at all. The more Constants you include the worse it will look like.
Let's make some modifications to the original code.
Code: class Window_TitleCommand
include SomeUser::Config
alias :some_user_config_win_ttl_comm_draw_item :draw_item
def draw_item(index)
some_user_config_win_ttl_comm_draw_item(index)
contents.font.size = DEFAULT_FONT_SIZE
contents.draw_text(4, 26, contents_width, 24, DEFAULT_TITLE, 1)
end
end
Wunderbar! Wonderful!
Now it looks quite neat! And we can even skip the creation of needless methods only to call the same long line of code we could find inside the default_font_size over and over again.
There is a caveat, though. You should NOT do that if you are working on a class that inherits the methods you have altered there. This is especially true if at least one of its child classes does not need such modifications at all. I seriously recommend you to only include the modules in child classes and nowhere else unless you know what you are doing.
"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.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
Posts: 4,592
Threads: 536
Joined: Dec 2009
04-19-2023, 01:12 AM
(This post was last modified: 04-28-2023, 03:02 AM by kyonides.)
Assignment != Equality && Assignment != Identity && Equality != Identity
Let us review what an assignment actually is. It simply means that you pick either a Constant or a variable and set a value to that object and not any other around it. (Well, you could still do it but you got to have a valid reason for it or else do not even try to do that!)
The assignment operator is an = equal sign.
variable = actual_value
That means that whenever you use that variable or Constant, it will always return the last value you have assigned to that particular object. You should not alter a Constant's value, though.
Equality stands for something that has the save value as another object, be it a Constant or a variable.
Use two == equal signs to make the comparison between two objects that might be different or not.
Code: CONSTANT = 1
@variable = 1
CONSTANT == @variable #=> Returns true
The === identity operator
And finally, we got the identity operator === to help us define if an object has been placed on both sides of a given comparison.
Here you got to be careful because it is excesively strict when looking for any identical value or object.
Code: range = 1..8
range === 5 #=> Returns true
range === range #=> Returns... false!
range == range #=> Returns... true!
Object.new === Object.new #=> Returns false, they are two different Objects with their own IDs.
Whenever you are using the case when statement to double check if a give variable is equal to another, it will use the === identity operator by default.
As we could see in the first example of the identity checks, the first test returned a true-ish value because it used the === operator with EVERY SINGLE VALUE of that Range object until it hit number 5.
Honestly, you should use the == equality operator for if and unless statements mainly because there is no specific reason to do otherwise.
When using the case when statement, make sure you will be comparing the same kind of objects like numbers or classes. It will fail if you made a mistake and tested the value of a variable, like number 5, against a class like Integer, even if number 5 is an Integer object on its own right.
Other Operators
By the way, the != operator simply means Not Equal.
The !=== Not Identical operator does exist as well, but it would be quite weird for you to find a good use for it anywhere.
The && double et symbol you could find on the title of this very same post simply means and.
A Terrible But Good Looking Error That Can Make You Break Your Head
You will not be able to tell why it is not working as intended because it will make you think everything is fine.
Code: variable == 100 || 50
Yeah, everything looks terrific and it NEVER throws any error at your face at all.
Really guys, it never will. 100% guaranteed!
Still, it is a huge mistake under most circumstances.
The reasoning behind it is QUITE SIMPLE indeed.
How do you know if the test is always working as intended if it never fails?
So tell me why is that a problem for any novice scripter?
It is a huge problem because you wound up defining a conditional statement you never needed.
I mean, if you always get a true-ish value back, why bother at all?
You could simply remove it and the script would work as usually does.
Keep in mind that any object other than nil and false are treated as true by default.
But I need it to reject certain requests because it did not hit the 100% accuracy I need in my skill, item consumption, etc.
If that is true, then you better delete the "|| number" part at once.
I cannot. I need it to also work if it hits the 50% mark...
You get a yellow card for making such a noob mistake then.
Actually, the only way that would work is to define the condition like I do it right below this very same line.
Code: variable == 100 || variable == 50
I would suspect you come from a different programming language that allow you to do that or you simply watched some video tutorial that was not Ruby specific at all.
Stop mixing language syntaxes right there!
Don't do it ever again!
It is for your own benefit, anyway.
"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.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
Posts: 4,592
Threads: 536
Joined: Dec 2009
Classes & Instance Variables & Aliases
This is not the first I ever discuss the topic, but there is a good reason why I have to come back to that same place once again. There are people that every so often encounter the very same issue and for some reason they are oblivious to the actual cause of their scripting nightmares.
As we learned here, classes need to be defined at the beginning or reopened at any other moment. This second stage is critical to what I am going to criticize here.
I myself have made that mistake and I am not alone here. Even Hime has made it and few others as well.
Well, it has two very specific parts. The first one is we forget to tell people that our code has aliased a method present in an already existing class, either a default script or a famous custom one.
Why is this a problem?
For some strange reason, we think the average joe will understand simple concepts like how to use a script or how to properly implement it in their own projects. What a classical mistake!
The truth is that most of the forumers don't. They might not fully appreciate what we scripters have been doing for them, yet, they don't even suspect that their lack of knowledge is what is actually biting them back.
Aliasing Can Be Dangerous... Sometimes
When we alias a method, we are telling Ruby, or RGSS if you prefer, to call the previously defined method with the same old name we had BUT by using a brand new nickname. This can be done as many times as needed as long as we don't repeat those so called nicknames. (They are aliases, anyway.)
This is exactly the part where the blame shifts completely to the scripter.
We normally create brand new @instance variables there.
Here is an example.
Code: class MyClass
alias :my_alias_init :initialize
def initialize
my_alias_init
@my_new_variable = 1
@my_new_boolean = false
end
attr_accessor :my_new_variable, :my_new_boolean
end
Normally, this would be OK for we the scripters are totally used to this. Our customers aren't.
They don't know that once you want to test that new addition, you GOTTA GET RID OF OLD SAVED GAMES.
Yes, this time I am yelling at everybody.
Why should we get rid of old saved games?
When you first created your heroes or monsters on the map BEFORE you added the new script or script modification, those @instance variables didn't exist at all. Not even for a second!
Once you try to load the game, you can get 1 out of 2 outcomes.
- The file could not be loaded and breaks your game.
- You are indeed playing from the last saved point, YET, it crashes later on. Sometimes in less than 5 minutes or at the moment they are facing a tough enemy or even a poor slime!
Why does it fail to load all data?
That is because it never was there. When you add a new modification, those new variables won't be created UNLESS you start a new game session. Only then it will work!
Why does it work in new game sessions?
And the reason is simple. This time the game did find our new procedures included in our new script, the one with the @instance variables being declared so they get created just in time. Yes, declaring variables is the same as creating them for immediate or future use.
Isn't there any exception to this rule?
Yeah, there is some sort of except here. The thing is that you can't always count on it.
Under some circumstances you can create a brand new method inside that class and declare those @variables there. Then you can call that method via a script call. This means you need an event to process it for you.
Take a look at this example.
Code: class MyClass
alias :my_alias_init :initialize
def initialize
my_alias_init
my_new_method
end
def my_new_method
@my_new_variable = 1
@my_new_boolean = false
end
attr_accessor :my_new_variable, :my_new_boolean
end
In this case, the script call would be the following:
Code: my_class = MyClass.new
my_class.my_new_method
If you save your game right after that call, you should be safe for the rest of your game. Nonetheless, there is a caveat or two as well.
What if one saved game took place while visiting the Royal Palace?
And wasn't there another where you were searching for a custom item or weapon inside the whole Dark Wolf Dungeon?
How many times would you need to make the same call from many different events?
Is there any other solution to this convoluted problem?
Yes, there is just another one. Still, it can't always be applied to your very specific issue. Sorry about that!
If we are handling stuff that was already saved from the very beginning or the addition isn't new at all and can be easily found at some specific method, you can simply delete the new @variables that you created there and go call that original method as many times as needed.
Let us suppose that our original intention with this script was to keep track of the First Actor's Class in the Database.
Code: class MyClass
def my_new_variable
$game_actors[1].class_id
end
end
Now any other scripts, both new or old ones, can call my_class.my_new_variable and it will return a given Class ID.
This implies that it will quickly load that specific data you are requesting and that's it!
And nope, it will not make your game crash ever again... if you know what you are doing!
An Exceptional Case: Booleans
When dealing with booleans, meaning true or false or even nil in Ruby, you MIGHT not go through this predicament at all.
As long as a method with the same name DOES exist.
XP & VX Example:
Code: if my_class.my_new_boolean
print "My new boolean exists!"
else
print "It doesn't exist, yet. Even so, it is equal to nil."
end
VX ACE Example:
Code: if my_class.my_new_boolean
msgbox "My new boolean exists!"
else
msgbox "It doesn't exist, yet. Even so, it is equal to nil."
end
But I need my new @variable to be set to true!
Then you've got a problem, my friend!
Yeap, now you DO need to erase your saved game sessions.
Or you can see if you go through ALL of your sessions and look for a perfect place to make the script call to assign it a true value... if that is the value it is supposed to be holding at that particular point.
"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.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
Posts: 4,592
Threads: 536
Joined: Dec 2009
06-08-2023, 09:46 PM
(This post was last modified: 06-08-2023, 09:47 PM by kyonides.)
Revisiting the Array Class: Values & Arguments
We already know that Arrays can hold a list of values. That is why other languages call them lists as well.
How do we get access to every single value stored therein?
Let us beginning with the classical method, namely by call them one by one.
We keep calling the Array until we are fed up with it.
Code: map_id = $game_party.scp[0]
x = $game_party.scp[1]
y = $game_party.scp[2]
direction = $game_party.scp[3]
Since we are just applying assignments to those 4 local variables, nothing weird happens.
Did you know there are other ways to accomplish the same goal?
That piece of code can be reduced to the following:
Ruby:
Code: map_id, x, y, direction = $game_party.scp
$game_player.reserve_transfer(map_id, x, y, direction)
What happened there was that we used a multiple variable assignment or parallel assignment that will feed all those variables to your left with as many values as the $game_party.scp Array has.
The rest of the variables will get a nil variable if the array to the right is not long enough as to find an actual value. nil is its default return value for Arrays.
Yet, we could even do it this simple way...
Code: $game_player.reserve_transfer(*$game_party.scp)
What has happened here?
It is easy to explain. There the * asterisk is telling Ruby and RGSS that $game_party.scp should be treated as an Array and not as a single element or argument or parameter. So it gets treated as 4 parameters instead!
That only works that way IF you are passing it as an argument of a script call.
Method Definitions & Arrays as Arguments
This example will show you the exact opposite effect using the same * asterisk in a different context.
Code: class MyClass
def method1(arg1, arg2, *extra_args_array)
@arg1 = arg1
@arg2 = arg2
@pos1, @pos2, @pos3 = extra_args_array
end
end
As you can see there, the * asterisk treats any extra arguments as an Array that can be accessed the same 2 ways we have seen above. Just don't use an * asterisk in line 5.
"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.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
Posts: 4,592
Threads: 536
Joined: Dec 2009
06-12-2023, 06:29 AM
(This post was last modified: 07-14-2023, 10:41 PM by kyonides.)
Revisiting the Array Class: Special Iterators
It is quite normal to go through a short or large Array to find something very specific that does NOT involved an index aka position at all, but how do you do that?
Two of the most common iterators are the for loop and the each iterator.
I am leaving some examples of how they would let you return a Game_Actor that matches a very specific criterion or nil if that never happens.
The For Loop
XP Version
Code: class Game_Party
def find_aluxes
for actor in @actors
return actor if actor.id == 1
end
end
end
VX & VX Ace Version
Code: class Game_Party
def find_ralph_or_eric
for actor_id in @actors
return $game_actors[actor] if actor_id == 1
end
end
end
The Each Iterator
This is how an each iterator would look for Aluxes aka Alexis aka Alex in RMXP.
Code: class Game_Party
def find_aluxes
@actors.each {|actor| return actor if actor.id == 1 }
end
end
Of course, I could also have used a do plus some end keywords instead of curly braces there. Normally, you would use the keywords for iterators spanning 2+ lines of code on screen.
Is there another way to do the same?
Yes, there is just another method to accomplish the same goal. It is called the find iterator.
Code: class Game_Party
def find_aluxes
@actors.find {|actor| actor.id == 1 }
end
end
Once again, you got to be careful because it could also return a nil value if Aluxes was away having fun with some cat girl at that moment.
What if you need to return a non nil value at the end of the iterator?
You could just add a return true or return false or return 0 line of code outside that block and it would work fine. Keep in mind you can put there whatever you need as its "failed test" return value. Let us take a look at some RMXP's code to make my point clear here.
Code: class Game_Party
def all_dead?
# If number of party members is 0
if $game_party.actors.size == 0
return false
end
# If an actor is in the party with 0 or more HP
for actor in @actors
if actor.hp > 0
return false
end
end
# All members dead
return true
end
As you can see above, it is supposed to return false IF any actor is still alive but true otherwise.
Array's collect and map methods
The first thing I got to tell you here is that they are one and the same. map is nothing but collect's alias.
This is what collect does:
Code: class Game_Party
def actors_names
@actors.map {|actor| actor.name }
end
end
Code: class Game_Party
def members_names
@actors.map {|actor_id| $game_actors[actor_id].name }
end
end
What that code does is to retrieve all of the actors' names and keep them in a new Array object.
If you don't assign it to any variable, you lose the data and have to start all over again.
Array's select and reject methods
select simply does the same job collect does WITH an exception. It only cares about those values that matched the given criteria.
Code: class Game_Party
def alive_actors
@actors.select {|actor| !actor.hidden and actor.hp > 0 }
end
end
Code: class Game_Party
def alive_actors
members.select {|actor| !actor.hidden and actor.hp > 0 }
end
end
reject's purpose is the exact opposite as its name obviously implies.
A Real Life Situation & Application
Here is a piece of code I had found online that made me slightly sick for make the code look more convoluted that it ever should have in the first place.
Code: class Window_CardSelect < Window_SkillList
def make_item_list
@data = @cards
@data.each_with_index do |card, i|
if card.exclude_extra
@data[i] = nil
end
end
@data.compact!
end
end
As you can see above, there is a make_item_list method that retrieves the existing @cards in a deck but for reason it needs to get rid of some of them before displaying them on the Card Selection Window.
I find it problematic indeed.
First, it creates not a copy but a link or reference to the @cards Array.
Then it iterates through that list looking for an exclusion criterion using a variant of the each iterator we have discused already.
If the test passes, it nil's it right away. Yeah, like a good old Dalek would do it!
At the end of the process, the array calls its compact! method to get rid of all those nil values in a blink of an eye.
Why is this terrible?
The reason is simple. You made a huge effort to get rid of all of those so called extra cards... only to later find out that you actually made them disappear forever and ever AND there is a high potential for it to return a dreadful nil value instead if the array had no nil values at all.
What should we do now?
The solution is even more simple than you could ever foresee as an amateur scripter. Just take a look at this curious snippet that applies some of the knowledge we have acquired today while reading this post.
Code: class Window_CardSelect < Window_SkillList
def make_item_list
@data = @cards.reject{|card| card.exclude_extra }
end
end
That is ALL we needed to do to prevent it from destroying our deck for eternity or pretend it did while keeping it hidden from sight.
Now you know just another reason why you better pick the right method for your issue from the very beginning. Or else you will be causing more ruckus than what your code was supposed to solve in the first place.
"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.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
Posts: 4,592
Threads: 536
Joined: Dec 2009
07-14-2023, 10:37 PM
(This post was last modified: 07-14-2023, 10:45 PM by kyonides.)
Arrays, Blocks & Obscure Practices
Here's an awful example that shows us what can be done but should NEVER take place. You would only need to use a code like the following one:
Code: $game_party.actors.map do |actor|
actor.age
end.max
What it does wrong is calling a second method not inside but outside of the block as the next logical step to take once the block finishes executing itself.
Yes, it's still possible to call a method at the end of such a block but it's a bad practice. Some people would write it like this instead:
Code: $game_party.actors.map { |actor|
actor.age
}.max
It's also obscure for sure. The best idea would be to do something totally different and a lot simpler than any of those 2 very similar options.
Valid Option No. 1
Use 2 lines of code instead
Code: list = $game_party.actors.map {|actor| actor.age }
list.max
This one is the quickest way to obtain the maximum value in no time and is as valid as the following one.
Valid Option No. 2
Create a specific method and call it from the original one
Code: def actors_ages
$game_party.actors.map {|actor| actor.age }
end
def find_max_age
actors_ages.max
end
As you can see both options are clean and easily readable. Another advantage is that it will only throw an error if and only if there's an empty actor or the @actors array's original contents have been replaced by improper ones.
By the way, this example is only valid for RMXP because the @actors array contain actual actors. Starting RMVX they simply include Actor ID's so you would need to convert them to actors first and then extract their ages as a second step that's unavoidable.
Use $game_party.members or replace @actors with members.
If you ever paid attention to RMVX ACE default scripts, you might have noticed by now that they almost always rely on the second valid option I exposed above. Well, it's quite useful and can be called from pretty much anywhere without exposing it in an "illegal" way based on OOP principles.
And yes, in Ruby it is quite normal to encapsulate methods and create tons of wrappers for methods belonging to other objects. Game_Party class is a good example of it.
"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.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
Posts: 4,592
Threads: 536
Joined: Dec 2009
07-15-2023, 07:32 AM
(This post was last modified: 07-15-2023, 07:41 AM by kyonides.)
Another Bad Practice!?
It's Never Enough for Alias!
Recently, a certain Wulfo brought to my attention that you can truly alias a parent class method in one of its child classes. The following test sample will prove it to you.
Code: class A
def print_intro
print "I am the A class!\n"
end
end
class B < A
alias :print_new_intro :print_intro
def print_intro
print_new_intro
print "I am the B class!\n"
end
end
a = A.new
a.print_intro
b = B.new
b.print_intro
I was under the impression that it should fail immediately, BUT the thing is that it is ALLOWED by Ruby. I don't know why they would allow us to do it, yet, it's totally possible.
Just make sure you have declared the parent class properly. (You only need to do that ONCE in the script editor per child class.)
Thus, alias lets you alter both the child class or its parent class, mainly depending on which one you think it truly needs the aliasing. In this case B's parent class is A but we aliased it in the child class B.
Nonetheless, aliasing a parent class method when we already know that super method already exists and does the job without the issues alias experiences after a F12 reset, makes it totally illogical from a practical gamer's or developer's viewpoint.
But guess what? There are some caveats I should discuss here as well.
What if somebody else or a default script already calls that method in the child class and includes a super call inside? Especially important if one of us comes later on and wants to edit that function from a custom script.
Well, in such cases you can or should use an alias call there. At least if your plan is to keep the original method available for immediate use inside that child method. Of course, there's always the possibility that somebody else shows up and overwrites it and even calls the super method as if nothing had ever happened there.
Code: class B
def print_intro
super
print "The name's B class!\n"
end
end
b.print_intro
If you ever thought that it would simply replace the B class's code above, you were right about that!
B's introduction has totally changed now.
And what happens if somebody else comes along and decides to alias it AFTER the latest piece of code has been included in a game project?
That's exactly the moment when things can get a bit ugly.
Code: class B
alias :print_new_intro2 :print_intro
def print_intro
print_new_intro2
print "B, Class B...\n"
end
end
b.print_intro
Here you will see how our same old friend B class now prints A class's and B class's introductions plus the brand new one where it pretends to be some sort of scripted version of James Bond.
I told you beforehand that it could get messy, didn't I?
And yes, some times that would be the ideal outcome... unless you just pretended to call the parent method and not one of the previous definitions of the child method.
"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.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
Posts: 4,592
Threads: 536
Joined: Dec 2009
07-16-2023, 06:05 AM
(This post was last modified: 07-16-2023, 06:12 AM by kyonides.
Edit Reason: 3 Reasons
)
Some Oddities About new & initialize
And Should You Care About Types?
Ruby being an OOP has no specific operator for a new class alias a new copy of a class (its template). Instead it uses the new method that allows it to invoke that copy I was talking about.
Why don't we check for a moment how new works in another language like C++?
The new Operator!
Code: CPP_Class *pointer = new CPP_Class;
CPlusPlus.com Wrote:operator new can be called explicitly as a regular function, but in C++, new is an operator with a very specific behavior: An expression with the new operator, first calls function operator new (i.e., this function) with the size of its type specifier as first argument, and if this is successful, it then automatically initializes or constructs the object (if needed). Finally, the expression evaluates as a pointer to the appropriate type.
Since Ruby works with objects mostly, it returns self instead after calling the initialize method.
What's self? - A Reminder
self stands for the very own object. In this specific case it is the class being initialized or "constructed" there, if you prefer the C++ term instead. What this means is that in Ruby you can call initialize the constructor of Ruby classes during a conversation if you like. Many programmers would understand you in a timely fashion.
How that looks like in Ruby & CRuby?
Well, we already know the new and the initialize methods. new will invoke initialize by default, be it the internal implementation or a custom one you have provided in your script. Then initialize will return the final object, described here as self but representing pretty much any specific class depending on where does it belong to.
In CRuby you would call rb_class_new_instance which includes rb_obj_alloc (its allocator) before finally calling rb_obj_call_init (the function in charge of executing the initialize method of pretty much any Ruby class except for Array and Hash.)
So yes, there is some equivalent of the new operator in Ruby and initialize would be considered its constructor for being its very own variable creation handler.
Now I wonder why Matz never called it construct or constructor instead being a C / C++ programmer himself. (The original documentation in Japanese has been translated to English so it might have got lost in translation. )
What's the advantanges of using new and initialize in Ruby?
I guess I can come up with 3 reasons here. Its relatively easy to use by beginners and it can be customized by passing one or more arguments to its initialize method. Also its duck typing strategy makes you care more about what method you will call next than what type of object it actually is. If a given method does exist, Ruby will call it at once. You see, since it walks like a duck and quacks like a duck, it got to be a duck, right?
The problem is that it is not always the case!
Types & Custom Type Checks
RPG::Item and RPG::Weapon have both a method called name and another one named description. So you can call their name and descriptions methods and think for a moment that they are the same kind of object... but they are not!
And that is not really an issue at all.
You see, it comes handy for scripters to simply call an item's or a weapon's name method to display it on the window or sprite about to be drawn on screen.
What do we do if we need to get something like EVA in RPG::Armor ?
It does not exist in RPG::Weapon and it could crash.
And it will crash indeed!
What you do in this case is to check for types or classes.
In C and C++ types are declared in several places, be it in a function's return value or as part of declaring the arguments it will accept and so on. As you can see in our previous example, the class name is the type we were looking for.
In Ruby things are different. CRuby does include types but are not supposed to be used that often according to several opinions found online. Nonetheless, there is no limitation as to how many times you should invoke it. Just keep them to the bare minimum.
In pure Ruby you can check classes instead of types.
Or like VX ACE implemented that, it creates a common method like actor? and then places a true or false boolean return value as a quick battler's class identifier. Keep in mind that both Game_Actor and Game_Enemy are nothing but specialized Game_Battlers by default.
Check Classes
Code: def reply(value)
case value
when String
"SSShhh... tring!"
when RPG::Weapon
"It will ke'al!"
when RPG::Item
"Consume before: midnight"
when Integer
"I'm No. 1, you losers! :P"
end
end
Type reply(1) in a script call and it will soon call you a loser.
Darn Number 1! You have got quite an ego!
At least the RPG::Weapon would sound like Doug after testing a knife or machete or sword.
Here I used a case statement but it would also work with a typical if or unless conditional with their respective branches.
Just in case you did not know this, VX ACE runs this type of check whenever you try to retrieve or replace an actor's piece of equipment.
"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.
My Original Stories (available in English and Spanish)
List of Compiled Binary Executables I have published...
HiddenChest & Roole
Give me a free copy of your completed game if you include at least 3 of my scripts!
Just some scripts I've already published on the board...
KyoGemBoost XP VX & ACE, RandomEnkounters XP, KSkillShop XP, Kolloseum States XP, KEvents XP, KScenario XP & Gosu, KyoPrizeShop XP Mangostan, Kuests XP, KyoDiscounts XP VX, ACE & MV, KChest XP VX & ACE 2016, KTelePort XP, KSkillMax XP & VX & ACE, Gem Roulette XP VX & VX Ace, KRespawnPoint XP, VX & VX Ace, GiveAway XP VX & ACE, Klearance XP VX & ACE, KUnits XP VX, ACE & Gosu 2017, KLevel XP, KRumors XP & ACE, KMonsterPals XP VX & ACE, KStatsRefill XP VX & ACE, KLotto XP VX & ACE, KItemDesc XP & VX, KPocket XP & VX, OpenChest XP VX & ACE
|