Okay so I was trying to simplify my code:
Old Code: def draw_enemy_hp(enemy, x, y, hidden)
colour = STAT_BAR_COLOURS[:hp_bar]
draw_enemy_bar(x, y, 120, 10, enemy.maxhp, ENEMY_STATS[:maxhp], colour)
contents.font.color = system_color
contents.draw_text(x, y-22, 24, 32, "#{$data_system.words.hp}:")
contents.font.color = normal_color
rect = Rect.new(x + 24, y-22, 72, 32)
unless hidden
contents.draw_text(rect, enemy.maxhp.group)
else
contents.draw_text(rect, '???')
end
end
def draw_enemy_sp(enemy, x, y, hidden)
colour = STAT_BAR_COLOURS[:hp_bar]
draw_enemy_bar(x, y, 120, 10, enemy.maxsp, ENEMY_STATS[:maxsp], colour)
contents.font.color = system_color
contents.draw_text(x, y-22, 24, 32, "#{$data_system.words.sp}:")
contents.font.color = normal_color
rect = Rect.new(x + 24, y-22, 72, 32)
unless hidden
contents.draw_text(rect, enemy.maxhp.group)
else
contents.draw_text(rect, '???')
end
end
New Code:
DEF = [
['hp', :hp_bar, 'enemy.maxhp', :maxhp],
['sp', :sp_bar, 'enemy.maxsp', :maxsp],
]
(DEF.size).times do |i|
eval "def draw_enemy_#{DEF[i][0]}(enemy, x, y, hidden)
value = #{DEF[i][2]}
max = ENEMY_STATS[#{DEF[i][3]}]
draw_enemy_bar(x, y, 120, 10, value, max, STAT_BAR_COLOURS[#{DEF[i][1]}])
contents.font.color = system_color
contents.draw_text(x, y-22, 24, 32, $data_system.words.#{DEF[i][0]})
contents.font.color = normal_color
rect = Rect.new(x + 24, y-22, 72, 32)
unless hidden
contents.draw_text(rect, #{DEF[i][2]}.group)
else
contents.draw_text(rect, '???')
end
end"
end
And for some reason I kept getting an error, well to my eyes I don't really see how I'm getting this error. So if anyone else can help spot it that would be great. If you need anymore info I'll post it.
*fixes* Use [code] tags for code.
You seem to have a comma at the end of line 3 which basically adds a nil value as 3rd element into your array.
Nope I still get the same error
Quote
Script 'Window_Bestiary_Info' line 32: NameError occurred.
undefined local variable or method 'maxhp' for #<Window_Bestiary_Info:0x31c9d60>
Also I don't know why the error is pointing there the only thing on that line is:
draw_enemy_hp(@enemy, 4, bar_Y=286, @hidden)
Maybe you should just use the classic way (or make a method that takes a few more parameters), because using eval in such a way is really bad design.
EDIT: You can try putting the symbols in strings as well:
['hp', ':hp_bar', 'enemy.maxhp', ':maxhp'],
oh okay I'll test it when I get home.
Nope doesn't work, I guess I'll just stick with the normal way :P . thanks for the help though.
well, I applaud you for being clever and trying to define both methods at once but I would hardly consider this "simplification" of your code. but as you see here Eval has some very interesting scope considerations to take into account. some how "enemy" in your evaled code was an instance of a Window_Bestiary_Info I'm not sure how it happened.
Using eval in general is bad practice for multiple reasons but it does have a place. I'd advise you to read these two pages.
http://stackoverflow.com/questions/1902744/when-is-eval-in-ruby-justified
http://blog.grayproductions.net/articles/eval_isnt_quite_pure_evil
general rule of thumb:
if your using eval to reduce the number of lines of code you have, your doing it wrong.
if your using it to deal with a dynamic system where your program must adapt at run-time then it's good tool, but use wisely.
If you really want to get clever you could write a generic bar drawing method and use something like this
def draw_enemy_stat(enemy, x, y, hidden, stat)
...
value = enemy.instance_variable_get(stat)
draw_enemy_bar(x, y, 120, 10, value, ENEMY_STATS[stat], colour)
...
end
note that you can turn strings to symbols with to_sym so you can build a symbol form a string
stat = "hp"
enemy.instance_variable_get(("max" + stat).to_sym)
STAT_BAR_COLOURS[(stat + "_bar").to_sym]
note that the above assumes that maxhp and maxsp are instance variables (it's been too long since I used XP so I cant remember) if the value is handeled by getter and setter methods (ie. attr_accessor, attr_reader, and attr_writer) you will need to use send.
wait so where does the line go, because even after I put it in still got the same error message that maxhp wasn't an instance variables Scratch that, I got the error to go away but now both the sp and hp guages say the same thing. Plus instead of saying HP: & SP: from the database. It now prints RPG:Sy... (Can't read the rest).
def draw_enemy_health(enemy, x, y, type, hidden)
colour = STAT_BAR_COLOURS[(type + "_bar").to_sym]
value = enemy.send(:maxhp)
max = ENEMY_STATS[("max" + type).to_sym]
draw_enemy_bar(x, y, 120, 10, value, max, colour)
contents.font.color = system_color
contents.draw_text(x, y-22, 24, 32, "#{$data_system.words.type}:")
contents.font.color = normal_color
rect = Rect.new(x + 24, y-22, 72, 32)
unless hidden
contents.draw_text(rect, value.group)
else
contents.draw_text(rect, '???')
end
end
----------------
So practically from reading those findings you posted: eval() is evil as it can ruin your hardware if used extensively (something like that), though it's alright to use it sometimes, but rarely should you use it.
well because your using enemy.send(:maxhp) you need to build that symbol from a string using the type like you do the rest to get both HP and SP bars. don't just cut and past my code I gave it as examples not exactly how to do it.
and eval isn't evil (but that is the common over-generalization) it is meirly a powerfully tool that can be misused very easyily
I think that one article has a few pretty good comments on it. Using eval for code definition is bad design, but using it to run dynamically created code or allow some sort of Ruby prompt (like I allowed in RMX-OS if somebody is an admin) are great ways where eval is not only useful and is considered good design, it's sometimes also the only possible solution (or the cleanest anyway).
PHP has an eval equivilant. When running a web server, one definitely does not want to allow unchecked user input to be allowed to run in eval because it effectively runs as shell on the system, which can be dangerous.
But running eval in RMXP doesnt have the same security needs and boils down to how it needs to be used. Eval threw me for a loop with Zeriab's caterpillar where he used eval to create a bunch of definitions dynamically, and applied the same change to each dynamic definition. Space saver, and in that situation I dont think it was required.
I also used eval in Timed User Inputs to allow scripts to run at specific times in different scenes. It became necessary to allow access to eval to process scripts after you change scenes to a non eventable / scriptable scene (like Menu) and not modify the scene itself in any way.
Eval does have very legit uses.
Okay thank you for the help Ryex as I got this code to work:
def draw_enemy_health(enemy, x, y, type, hidden)
colour = STAT_BAR_COLOURS[(type + "_bar").to_sym]
value = enemy.send(("max" + type).to_sym)
max = ENEMY_STATS[("max" + type).to_sym]
draw_enemy_bar(x, y, 120, 10, value, max, colour)
contents.font.color = system_color
contents.draw_text(x, y-22, 24, 32, eval("$data_system.words.#{type}") + ":")
contents.font.color = normal_color
rect = Rect.new(x + 24, y-22, 72, 32)
unless hidden
contents.draw_text(rect, value.group)
else
contents.draw_text(rect, '???')
end
end
And thank you everyone for giving more info on eval()