[RMVX ACE] NOMETHOD ERROR OCCURED. UNDEFINED METHOD [] FOR NIL:NILCLASS
Posts
Yeah, I'm having it rough with scripting lately. So basically, I have a Yanfly script that affects the battle system, and in battle it works fine, but when I walk around on the map, I get a few steps and then I get the error outlined above, and then it points me here;
Here is the entire (altered from its original) script for reference.
http://pastebin.com/WptB7fuF
#-------------------------------------------------------------------------- # alias method: on_turn_end #-------------------------------------------------------------------------- alias game_battler_on_turn_end_ftb on_turn_end def on_turn_end game_battler_on_turn_end_ftb init_ftb_actions @has_defended = false #grs if self.is_a?(Game_Actor) $game_temp.current_decays[self.id][1] = 0 end end
Here is the entire (altered from its original) script for reference.
http://pastebin.com/WptB7fuF
Seems like "current_decays" isn't being initialized properly—the script doesn't know it's an array yet. In fact, it doesn't seem to know that it's anything yet—it's being reported as nil class.
If you can find where current_decays is inside $game_temp, and make sure that it's being initialized there, then the script should work.
If you can find where current_decays is inside $game_temp, and make sure that it's being initialized there, then the script should work.
The weird thing is, it works if it's
...but not if it reads after an addition...
What's the difference that additional modification is making?
#-------------------------------------------------------------------------- # alias method: on_turn_end #-------------------------------------------------------------------------- alias game_battler_on_turn_end_ftb on_turn_end def on_turn_end game_battler_on_turn_end_ftb init_ftb_actions if self.is_a?(Game_Actor) $game_temp.current_decays[self.id][1] = 0 end end
...but not if it reads after an addition...
#-------------------------------------------------------------------------- # alias method: on_turn_end #-------------------------------------------------------------------------- alias game_battler_on_turn_end_ftb on_turn_end def on_turn_end game_battler_on_turn_end_ftb init_ftb_actions @has_defended = false #grs if self.is_a?(Game_Actor) $game_temp.current_decays[self.id][1] = 0 end
What's the difference that additional modification is making?
Wait a second.. that block of code is missing an "end". "If self.is_a?(Game_Actor)" isn't enclosed properly.
Try placing an "end" on the next line after "$game_temp.current_decays[self.id][1] = 0".
It's difficult to spot, because that block of code isn't indented in a clear way. I can't tell if that final "end" already there in the code is enclosing that "def" or the entire class, or what. That "if" clause seems left open.
...If that doesn't work, then that one line, "@has_defended = false" must be acting like a switch in the original on_turn_end. But see if that "end" isn't truly missing... that's weird.
(Here's how I'd expect the code to look:
#--------------------------------------------------------------------------
# alias method: on_turn_end
#--------------------------------------------------------------------------
Or even,
It's difficult to tell what's inside that block and what isn't.
Try placing an "end" on the next line after "$game_temp.current_decays[self.id][1] = 0".
It's difficult to spot, because that block of code isn't indented in a clear way. I can't tell if that final "end" already there in the code is enclosing that "def" or the entire class, or what. That "if" clause seems left open.
...If that doesn't work, then that one line, "@has_defended = false" must be acting like a switch in the original on_turn_end. But see if that "end" isn't truly missing... that's weird.
(Here's how I'd expect the code to look:
#--------------------------------------------------------------------------
# alias method: on_turn_end
#--------------------------------------------------------------------------
alias game_battler_on_turn_end_ftb on_turn_end
def on_turn_end
game_battler_on_turn_end_ftb
init_ftb_actions
@has_defended = false #grs
end
if self.is_a?(Game_Actor)
$game_temp.current_decays[self.id][1] = 0
end
end
Or even,
alias game_battler_on_turn_end_ftb on_turn_end
def on_turn_end
game_battler_on_turn_end_ftb
init_ftb_actions
@has_defended = false #grs
if self.is_a?(Game_Actor)
$game_temp.current_decays[self.id][1] = 0
end
end
end
It's difficult to tell what's inside that block and what isn't.
Yeah, I tried that at first too, and then I get this when I start a game (but test battles still work)


I know this is improper, but for situations like this I created a NilClass and defined some methods to stop errors.
class NilClass
def initialize
super
end
def [](arg)
nil
end
def erase
nil
end
def name
nil
end
def dispose
nil
end
def update
nil
end
def width
nil
end
def height
nil
end
end
Looking at the original code on the pastebin site, that block of code is definitely missing an "end", either for the "def" or that "if" clause. But, if when you correct it, it gives an error, I don't know how to fix that.
You have to find out what it's expecting there ("$end" ?) and give it to the program.
You have to find out what it's expecting there ("$end" ?) and give it to the program.
The only thing I can think of, is that the "is_a" method statement is so simple, that it's just returning true or false, and not getting compared to anything, so the parsing engine just goes, "right, naturally," as if you had typed "1 == 1" or even just "true" on a line all by itself.
Try doing "If self.is_a?(Game_Actor) == true", even though it might seem redundant, then add in that missing "end".
Try doing "If self.is_a?(Game_Actor) == true", even though it might seem redundant, then add in that missing "end".
No go.
How about this; here are the other scripts the 'decays' line of code reside in;
http://pastebin.com/nPhthcx2
http://pastebin.com/GszLbtJ2
How about this; here are the other scripts the 'decays' line of code reside in;
http://pastebin.com/nPhthcx2
http://pastebin.com/GszLbtJ2
Taking a fresh look at the problem, the whole reason this is happening in the first place, is because this battle-oriented block of code is being called outside of battle.
Let's assume Yanfly knew what he was doing, and having that open "if" there is some kind of Ruby magic. Try adding a simple switch to this script. Call it $simple_switch_battle_on_off or another highly specific name, making sure to use that "$" for a global variable. Then, go to where the battle is initialized (Scene_Battle?), and set it to "true" under "def initialize". Next, go to where the battle wraps up, experience is allotted, etc. and set it to "false".
Now, go to our problematic block of code and place the entire thing inside an "if" clause, with $simple_switch_battle_on_off as the gatekeeper. See if the problem isn't quarantined in that way, at least.
Let's assume Yanfly knew what he was doing, and having that open "if" there is some kind of Ruby magic. Try adding a simple switch to this script. Call it $simple_switch_battle_on_off or another highly specific name, making sure to use that "$" for a global variable. Then, go to where the battle is initialized (Scene_Battle?), and set it to "true" under "def initialize". Next, go to where the battle wraps up, experience is allotted, etc. and set it to "false".
Now, go to our problematic block of code and place the entire thing inside an "if" clause, with $simple_switch_battle_on_off as the gatekeeper. See if the problem isn't quarantined in that way, at least.
Our main goal is sealing this problem block of code off outside of battle. We can do that with an "if" clause that we make ourselves.
First, we need to find a place to initialize our switch. Hell, we can do it right in the script called "Main", the last script in the Script Editor list. Put "$yanfly_battle_quarantine = true" right after the "begin" in that script.
Next, you have to find a place to disable this switch for battle, making it equal "false" (because we don't want to quarantine this block of code when we're in battle). I don't know where it is, personally... maybe under "def initialize" in Scene_Battle?
(The "def initialize"s in each class get called whenever a new instance of that class is made. For example, if you were to type "example = Game_Actor.new", this would create a new instance of the Game_Actor class, and the "def initialize" in that script would run automatically right then and there.)
Next, we have to find a place to re-enable the quarantine, somewhere where the battle ends. I don't know where this is either... I think you might find something if you comb through the last parts of Scene_Battle. Look for @exp or @gold, something that's increasing the player's experience points or gold. Then, right there, put "$yanfly_battle_quarantine = true" again. Somewhere. It can be on any line around there.
Finally, just seal off the block of code with the clause:
It's a bodge, but give it a try. (I don't have VX Ace, I'm basing this off of what I know of RPG Maker XP.)
First, we need to find a place to initialize our switch. Hell, we can do it right in the script called "Main", the last script in the Script Editor list. Put "$yanfly_battle_quarantine = true" right after the "begin" in that script.
Next, you have to find a place to disable this switch for battle, making it equal "false" (because we don't want to quarantine this block of code when we're in battle). I don't know where it is, personally... maybe under "def initialize" in Scene_Battle?
(The "def initialize"s in each class get called whenever a new instance of that class is made. For example, if you were to type "example = Game_Actor.new", this would create a new instance of the Game_Actor class, and the "def initialize" in that script would run automatically right then and there.)
Next, we have to find a place to re-enable the quarantine, somewhere where the battle ends. I don't know where this is either... I think you might find something if you comb through the last parts of Scene_Battle. Look for @exp or @gold, something that's increasing the player's experience points or gold. Then, right there, put "$yanfly_battle_quarantine = true" again. Somewhere. It can be on any line around there.
Finally, just seal off the block of code with the clause:
if $yanfly_battle_quarantine == false
alias game_battler_on_turn_end_ftb on_turn_end
def on_turn_end
game_battler_on_turn_end_ftb
init_ftb_actions
@has_defended = false #grs
if self.is_a?(Game_Actor)
$game_temp.current_decays[self.id][1] = 0
end
end
It's a bodge, but give it a try. (I don't have VX Ace, I'm basing this off of what I know of RPG Maker XP.)
It's not working, but I'm not sure what I'm doing is correct (I'm not sure what I'm doing, period)
I feel there should be a simpler way to tackle this. I posted this issue in another help forum and they said this
I feel there should be a simpler way to tackle this. I posted this issue in another help forum and they said this
author=quote
The script does not seem to contain the section where the current_decoys property is initialized/made accessible. It's probably part of another script of yours. Make sure it is properly initialized:
In order to make
$game_temp.current_decoys...
work, current_decoys must be an Array/Hash or something else that supports . In order to make
$game_temp.current_decoys...
work, the entry corresponding to index self.id must be an Array/Hash/... too (uninitialized array entries are considered nil, so if you used an array, make sure it has a suitable length).
Unlike the other $game_... objects, $game_temp is not saved within your savestates, so I'd say it would be unlikely that there is an issue with an old savefile, though it's possible of course.
However, this also means that whatever you store within $game_temp is lost when you quit the game. Also, its current content is carried over when you return to the title and then load a different savestate, so make sure this behaviour does not cause other problems.
In the error the program gave you at the very beginning, "Undefined method [ ] for nil:NilClass", the program is expecting an array ("[ ]") to be there, and there isn't one. So, at that very moment that the program reaches that spot in the code, either "$game_temp.current_decays" doesn't exist (doubtful), or "$game_temp.current_decays[self.id]" doesn't exist.
See the original line, how it has two [ ] right next to each other... "[self.id][1]"? That's called a nested array... one array inside of another. It's like this:
What's going on in the problem block of code... is that whatever array "self.id" is pointing to doesn't exist any longer. Let's say self.id is equal to 5. If there's nothing in index 5, say, because the battle is over and all of the Battlers have been freed from the game's memory, the game can't reference [1] like the code is instructing it to, because there's nothing there. So, that's why the program crashes... because there's no array there at that time.
Let's try this next: We'll make sure that self.id is never pointing to nothing (nil).
See the original line, how it has two [ ] right next to each other... "[self.id][1]"? That's called a nested array... one array inside of another. It's like this:
an_array = [0, 1, 2, 3]
another_array = ["a", "b", "c", "d"]
yet_another_array = [91, 92, 93, 94]
all_of_those_arrays = [an_array, another_array, yet_another_array]
#Remember... the first index of an array is 0, not 1
print "#{all_of_those_arrays[0]}" #=> This will output an_array in its entirety.
print "#{all_of_those_arrays[0][0]}" #=> This will output 0, an_arrays's first element.
print "#{all_of_those_arrays[1][3]}" #=> This will output "d".
print "#{all_of_those_arrays[2][2]}" #=> This will output 93.
What's going on in the problem block of code... is that whatever array "self.id" is pointing to doesn't exist any longer. Let's say self.id is equal to 5. If there's nothing in index 5, say, because the battle is over and all of the Battlers have been freed from the game's memory, the game can't reference [1] like the code is instructing it to, because there's nothing there. So, that's why the program crashes... because there's no array there at that time.
Let's try this next: We'll make sure that self.id is never pointing to nothing (nil).
#--------------------------------------------------------------------------
# alias method: on_turn_end
#--------------------------------------------------------------------------
alias game_battler_on_turn_end_ftb on_turn_end
def on_turn_end
game_battler_on_turn_end_ftb
init_ftb_actions
@has_defended = false #grs
if self.is_a?(Game_Actor)
if $game_temp.current_decays[self.id] != nil #Make sure whatever index self.id is pointing to actually contains something.
$game_temp.current_decays[self.id][1] = 0
end
end
end
It appears to work now! I'm going to run some more tests on it before I jump up and click my heels together (and thank you).
I am equally parts curious and terrified of why your game is running code intended for battle when the player is on a map (although I'm at work and can't check the default scripts and can't confirm that Ace does this vanilla). I don't see anything in the provided scripts that runs on_turn_end either. If (read: When) it comes up again try to track what you did since starting a test play to see what it takes to recreate the bug. Being able to reproduce an issue helps a ton when trying to find and fix the problem. I can give instructions on how to get a stacktrace too which should help a lot knowing what functions are calling what when the on_turn_end code is executed.
Annnndddd it's back. Fuck. Basically, the error pops up when I make a certain number of steps on the map outside of battle. In battle everything works completely fine.
LockeZ
I'd really like to get rid of LockeZ. His play style is way too unpredictable. He's always like this too. If he ran a country, he'd just kill and imprison people at random until crime stopped.
5958
The supposed problems with the missing "end" statements are just you indenting your changes poorly, and then that poor indentation causing you to miss one when copying and pasting the code from the script to the thread. Not a big deal. The script itself isn't missing anything of the sort.
Here's the real problem. As your error stated, $game_temp.current_decays is nil, and so it can't get an index of it. So the question is, where is $game_temp.current_decays being initialized?
The answer: In your Skill Decay script, starting at line 154, is where it's being initialized. It looks like this:
You'll immediately notice that's happening inside the BattleManager. Whoops. So if the player hasn't gotten in any battles since loading their game, this variable isn't initialized yet. That causes the error.
The reason it's initialized there is because it's reset at the beginning of each battle, and the original author never considered that it might be used outside of battle.
There are a few different ways to solve this depending on what behavior you want for the decays. Do you still want them to reset at the beginning of each battle? Or would you rather have them reset at the end of each battle, so that decays caused by using skills between battles will continue into the next battle? Or never?
Here's the real problem. As your error stated, $game_temp.current_decays is nil, and so it can't get an index of it. So the question is, where is $game_temp.current_decays being initialized?
The answer: In your Skill Decay script, starting at line 154, is where it's being initialized. It looks like this:
module BattleManager #-------------------------------------------------------------------------- # ● Setups #-------------------------------------------------------------------------- class << self ; alias_method(:krx_decay_bm_setup, :setup) ; end def self.setup(troop_id, can_escape = true, can_lose = false) krx_decay_bm_setup(troop_id, can_escape, can_lose) $game_temp.current_decays = {} for i in 0..999 $game_temp.current_decays[i] = {} end end end
You'll immediately notice that's happening inside the BattleManager. Whoops. So if the player hasn't gotten in any battles since loading their game, this variable isn't initialized yet. That causes the error.
The reason it's initialized there is because it's reset at the beginning of each battle, and the original author never considered that it might be used outside of battle.
There are a few different ways to solve this depending on what behavior you want for the decays. Do you still want them to reset at the beginning of each battle? Or would you rather have them reset at the end of each battle, so that decays caused by using skills between battles will continue into the next battle? Or never?
Ah.
Well, let's factor in a few things. The original purpose of the script as written by the author, was to generate a progressive 'decay' of certain skills so that the player would be discouraged from spamming them. However, I had the script edited to serve some additional purposes;
-Instead of a given skill, I wanted the 'Attack' skill, i.e., a regular attack, to decay per use
-I wanted this to be independent of Actors; meaning, I didn't want Actor A's Attack to decay just because Actor B attacked
-Most importantly, I wanted the decay to reset just every turn. Originally, the script is set up to reset the decay at the end of the battle, but this is a no go for me. In my battle system, one can 'Attack' a few times a turn, so I wanted the regular Attack decay to be character specific until just the end of the turn. Then it resets. This is why the script is linked with Yanfly's FTB Script, I presume.
However, the script, as modified (not out of the box), already does all of this except for the side effect of it shitting the bed outside of battle.
Well, let's factor in a few things. The original purpose of the script as written by the author, was to generate a progressive 'decay' of certain skills so that the player would be discouraged from spamming them. However, I had the script edited to serve some additional purposes;
-Instead of a given skill, I wanted the 'Attack' skill, i.e., a regular attack, to decay per use
-I wanted this to be independent of Actors; meaning, I didn't want Actor A's Attack to decay just because Actor B attacked
-Most importantly, I wanted the decay to reset just every turn. Originally, the script is set up to reset the decay at the end of the battle, but this is a no go for me. In my battle system, one can 'Attack' a few times a turn, so I wanted the regular Attack decay to be character specific until just the end of the turn. Then it resets. This is why the script is linked with Yanfly's FTB Script, I presume.
However, the script, as modified (not out of the box), already does all of this except for the side effect of it shitting the bed outside of battle.
















