Ruby Scripting
#11
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...)

[Image: QrnbKlx.jpg]
[Image: sGz1ErF.png]    [Image: liM4ikn.png]    [Image: fdzKgZA.png]    [Image: sj0H81z.png]
[Image: QL7oRau.png]    [Image: uSqjY09.png]    [Image: GAA3qE9.png]    [Image: 2Hmnx1G.png]    [Image: BwtNdKw.png%5B]
  Above are clickable links
Reply }
#12
(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

Thinking What a mess made in hell!
Happy with a sweat OK, that case is even worse than the one previously presented on my previous post.
There is a caveat. Oops! 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? Who Knows?
It is more like a yes and not or a German jein. Laughing

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. Serious

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! Shocked

Yet, there IS a caveat you should care about! Confused

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

Who Knows? Why it doesn't?

The answer is simple, it is because of its Hal 2000 scope!
It cannot see where the CONSTANTS are located. You have reduced the scope to that Thing class only! Happy with a sweat
This means you will need to call all of its module's CONSTANTS like this:

Code:
Some::CONSTANT

And that is, my friends, a very unpractical way to achieve a simple goal. Winking

Thinking Perhaps I could submit a request to the Ruby Core devs to make them forbid that inclusion in recent versions of Ruby 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.

[Image: SP1-Scripter.png]
[Image: SP1-Writer.png]
[Image: SP1-Poet.png]
[Image: SP1-PixelArtist.png]
[Image: SP1-Reporter.png]

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! Laughing + Tongue sticking out

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
Reply }
#13
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. Winking

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 Shocked once we start dealing with a specific Window class. Detective 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. Happy with a sweat Honestly, I got to tell you that it is not elegant at all. Confused 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! Shocked 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 Serious recommend you to only include the modules in Baby 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.

[Image: SP1-Scripter.png]
[Image: SP1-Writer.png]
[Image: SP1-Poet.png]
[Image: SP1-PixelArtist.png]
[Image: SP1-Reporter.png]

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! Laughing + Tongue sticking out

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
Reply }
#14
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. Confused

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. Winking 100% guaranteed! Grinning
Still, it is a huge mistake under most circumstances. Confused

The reasoning behind it is QUITE SIMPLE indeed.
How do you know if the test is always working as intended if it never fails?
Thinking 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.
Happy with a sweat I mean, if you always get a true-ish value back, why bother at all? Sarcasm
You could simply remove it and the script would work as usually does. Happy with a sweat

Keep in mind that any object other than nil and false are treated as true by default.

Sad But I need it to reject certain requests because it did not hit the 100% accuracy I need in my skill, item consumption, etc.
Detective If that is true, then you better delete the "|| number" part at once.
Sad I cannot. I need it to also work if it hits the 50% mark...
Yellow Card 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

Detective 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 Ruby specific at all.
Stop mixing language syntaxes right there! Stop 
Don't do it ever again! Angry
It is for your own benefit, anyway. Happy with a sweat
"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.

[Image: SP1-Scripter.png]
[Image: SP1-Writer.png]
[Image: SP1-Poet.png]
[Image: SP1-PixelArtist.png]
[Image: SP1-Reporter.png]

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! Laughing + Tongue sticking out

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
Reply }
#15
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 Ghost 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! Incredible 

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 Dog biting them back.

Aliasing Can Be Dangerous... Sometimes Tongue sticking out

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. Shocked

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.

  1. The file could not be loaded and breaks your game.
  2. 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?
Confused How many times would you need to make the same call from many different events? Incredible

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. Tongue sticking out 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! Grinning
And nope, it will not make your Gamer game crash ever again... if you know what you are doing! Winking

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. Shocked
As long as a method with the same name DOES exist. Winking

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

Thinking But I need my new @variable to be set to true! Incredible 

Then you've got a problem, my friend! Laughing
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. Happy with a sweat
"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.

[Image: SP1-Scripter.png]
[Image: SP1-Writer.png]
[Image: SP1-Poet.png]
[Image: SP1-PixelArtist.png]
[Image: SP1-Reporter.png]

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! Laughing + Tongue sticking out

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
Reply }
#16
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.

Thinking 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. Tongue sticking out

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.

Thinking 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)

Incredible 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! Shocked

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. Winking
"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.

[Image: SP1-Scripter.png]
[Image: SP1-Writer.png]
[Image: SP1-Poet.png]
[Image: SP1-PixelArtist.png]
[Image: SP1-Reporter.png]

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! Laughing + Tongue sticking out

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
Reply }
#17
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? Thinking

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

Happy with a sweat 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.

Thinking Is there another way to do the same?
Grinning 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. Laughing

Thinking 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. Laughing

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. Tongue sticking out

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. Angry

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 Dalek nil's it right away. Yeah, like a good old Dalek 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. Winking

Sad 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. Face Palm 

Incredible 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 Orson ruckus than what your code was supposed to solve in the first place. Laughing
"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.

[Image: SP1-Scripter.png]
[Image: SP1-Writer.png]
[Image: SP1-Poet.png]
[Image: SP1-PixelArtist.png]
[Image: SP1-Reporter.png]

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! Laughing + Tongue sticking out

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
Reply }
#18
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.

Happy with a sweat 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 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.

[Image: SP1-Scripter.png]
[Image: SP1-Writer.png]
[Image: SP1-Poet.png]
[Image: SP1-PixelArtist.png]
[Image: SP1-Reporter.png]

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! Laughing + Tongue sticking out

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
Reply }
#19
Another Bad Practice!?
It's Never Enough for Alias!

Recently, a certain Dog 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 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.

Happy with a sweat But guess what? There are some caveats I should discuss here as well.

Thinking What if somebody else or a default script already calls that method in the child class and includes a super call inside? Confused Especially important if one of us comes later on and wants to edit that function from a custom script.

Indifferent 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! Happy with a sweat
B's introduction has totally changed now.

Thinking 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. Laughing

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.

[Image: sweathappy.gif] 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. Laughing
"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.

[Image: SP1-Scripter.png]
[Image: SP1-Writer.png]
[Image: SP1-Poet.png]
[Image: SP1-PixelArtist.png]
[Image: SP1-Reporter.png]

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! Laughing + Tongue sticking out

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
Reply }
#20
Some Oddities About new & initialize
And Should You Care About Types?

Ruby 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 Ruby works with objects mostly, it returns self instead after calling the initialize method.

Happy with a sweat 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 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 Ruby and initialize would be considered its constructor for being its very own variable creation handler.

Thinking 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. Happy with a sweat )

What's the advantanges of using new and initialize in Ruby?

Thinking 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? Happy with a sweat

The problem is that it is not always the case! Shocked

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! Angry

And that is not really an issue at all. Laughing

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 Sad and it could crash.

And it will crash indeed! Laughing

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 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. Sad
Angry Darn Number 1! You have got quite an ego!
At least the RPG::Weapon would sound like Doug Marcaida Doug after testing a knife or machete or sword. Laughing

Happy with a sweat 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. Shocked
"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.

[Image: SP1-Scripter.png]
[Image: SP1-Writer.png]
[Image: SP1-Poet.png]
[Image: SP1-PixelArtist.png]
[Image: SP1-Reporter.png]

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! Laughing + Tongue sticking out

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
Reply }


Possibly Related Threads…
Thread Author Replies Views Last Post
   Ruby - Behind the Scenes DerVVulfman 0 1,118 07-15-2023, 05:52 PM
Last Post: DerVVulfman
Information  Assorted Ruby chm documents. hanetzer 10 35,145 08-17-2020, 04:19 AM
Last Post: kyonides
Brick  Learn Ruby! greenraven 2 7,583 05-16-2014, 12:25 PM
Last Post: greenraven
   Creating Your Own Scripting System DerVVulfman 3 7,430 10-12-2009, 03:37 PM
Last Post: Alpha-Mad
   How to make interesting NPC's without scripting Third333Strike 0 3,828 12-06-2008, 04:59 PM
Last Post: Third333Strike



Users browsing this thread: 1 Guest(s)