[Ace] Attempting to write an enemy name pluralization script.

Status
Not open for further replies.

Chanto

Scripting Newbie
Member
Joined
Jul 31, 2013
Messages
21
Reaction score
1
First Language
English
Primarily Uses
Hello, all.  I'm currently attempting to write up my own script that will detect if there are multiple enemies of the same type in a troop and if so, account for that in the "<x> emerged!" message at the start of combat.  Currently I'm trying to do this through modification of the enemy_names method under the Game_Troop class.  As a test case, my enemy name is "Varg".  What this should be doing is making it so that it says "Vargs emerged!" if I have more than one in a troop.  But instead, it's still spitting out "Varg emerged!" even though there are four in the pack.

#-------------------------------------------------------------------------- # * Get Enemy Name Array # For display at start of battle. Overlapping names are pluralized. #-------------------------------------------------------------------------- def enemy_names names = [] #Array of names returned checkedTwice = [] #Array of names pluralized members.each do |enemy| #Loops through each member of the troop next unless enemy.alive? #Skips to next enemy if the current one is dead next if checkedTwice.include?(enemy.original_name)#Skips if already plural if names.include?(enemy.original_name) #If already checked once... checkedTwice.push(enemy.original_name)#Moves to pluralized list names.each do |name| #Checks each name in names[] if (name == enemy.original_name) #If it matches the current enemy...# name = pluralize(name) name = name + "s" #Changes name to pluralized version end #Ends if branch end #Ends pluralizer loop else #If the name hasn't been checked yet... names.push(enemy.original_name) #Adds it to names[] end #Ends checked-or-unchecked if/else end #Ends troop member-checking loop names #Returns names[] end #Ends definitionFrom what I can tell, I feel like the logic should be working fine, but for some reason, the pluralization is not being done, or it's being done wrong or...I don't know.  I have no idea how to follow the path of what this method's doing to see what's going wrong.  Anyone have any insight?  Something obvious I've overlooked?  The latter is entirely possible since I'm still rather new both Ruby and this whole scripting thing...

Thanks in advance for any help and advice.
 

Shaz

Veteran
Veteran
Joined
Mar 2, 2012
Messages
40,098
Reaction score
13,704
First Language
English
Primarily Uses
RMMV
I'm not sure why yours isn't working - I suspect your iteration through names MAY be updating a variable with the same value, and not the original names array at all. However, while I was attempting to find out, I saw that there's already some code that checks for plurals, and it uses a hash to check the number of a certain type of enemies. You can make use of this hash to do what you want, and in fewer lines than you're using.

Code:
  def enemy_names    names = []    members.each do |enemy|      next unless enemy.alive?      next if names.include?(enemy.original_name) || names.include?(enemy.original_name + "s")      n = @names_count[enemy.original_name] || 0      names.push(n > 1 ? enemy.original_name + "s" : enemy.original_name)    end    names  end
 
Last edited by a moderator:

Chanto

Scripting Newbie
Member
Joined
Jul 31, 2013
Messages
21
Reaction score
1
First Language
English
Primarily Uses
I'm not sure why yours isn't working - I suspect your iteration through names MAY be updating a variable with the same value, and not the original names array at all. However, while I was attempting to find out, I saw that there's already some code that checks for plurals, and it uses a hash to check the number of a certain type of enemies. You can make use of this hash to do what you want, and in fewer lines than you're using.

def enemy_names names = [] members.each do |enemy| next unless enemy.alive? next if names.include?(enemy.original_name) || names.include?(enemy.original_name + "s") n = @names_count[enemy.original_name] || 0 names.push(n > 1 ? enemy.original_name + "s" : enemy.original_name) end names end
Ah, thank you!  So far it does work as intended!  Now, I'm trying to avoid becoming satisfied with just plugging in other people's scripts without understanding just how they work, so I'm trying to analyze this line-by-line to make sure I "get it", but (probably due to inexperience) there's a few bits I don't quite get yet.  I think I might have the basic gist of 'em, but I'm not really sure how they work...  Specifically, I have questions about lines 6 & 7.

def enemy_names names = [] #Enemy names returned members.each do |enemy| #Iterates through each troop member next unless enemy.alive? #Skips to next enemy if the current one is dead next if names.include?(enemy.original_name) || names.include?(enemy.original_name + "s") #Skips to next loop if singular or pluralized name is already in array n = @names_count[enemy.original_name] || 0 #Initializes variable "n" to 0, increases it by one for each count of the current name in the troop? names.push(n > 1 ? enemy.original_name + "s" : enemy.original_name) #If n is greater than 1, pluralizes the name and adds it to the list, otherwise just adds the original name? end names endI think part of my problem is still being unfamiliar with the concept of hashes.  Is that what that "@names_count[enemy.original_name]" bit is?  How exactly does that little snippet work?

I'm also unfamiliar as of yet with line 7's syntax, but I think I understand it.  After the '?', it'll execute names.push with the code before the colon if (n > 1) is true, and the bit after the colon if it's false, is that right?
 

Shaz

Veteran
Veteran
Joined
Mar 2, 2012
Messages
40,098
Reaction score
13,704
First Language
English
Primarily Uses
RMMV
Line 6 ... take a look at the make_unique_names method a few lines above this method. It creates a hash, called @names_count, and populates it with the different enemy names, and the number of enemies with that name in the troop. The enemy name is the key to the hash (like an index is a key to an array), and the count of enemies is the corresponding value.

n = @names_count[enemy.original_name] || 0is saying to get the value from the @names_count array, where the key is the enemy name. If that isn't defined, then use 0 instead. There's no actual initializing or adding or counting going on here. It's just looking up the value that was calculated by the earlier method.Line 7 ... yes, it's a simple if test.

condition ? action if true : action if false
Code:
n > 1 ? enemy.original_name + "s" : enemy.original_name
is the same as saying
Code:
if n > 1  enemy.original_name + "s"else  enemy.original_nameend
It's a fast way of saying "do this, or do that" when "this" and "that" are simple statements. If you wanted to do a series of things, following the block format (if ... else ... end) is better.
 
Last edited by a moderator:

Chanto

Scripting Newbie
Member
Joined
Jul 31, 2013
Messages
21
Reaction score
1
First Language
English
Primarily Uses
Ahh, okay!  All right, so I definitely understood line 7 there, and I've got a better grasp of hashes now, I think, thank you for that!

So, you mention that @names_count is populated by the troop members in the make_unique_names function...

def make_unique_names members.each do |enemy| #Iterates through each member of troop next unless enemy.alive? #Skips to next member if current one is dead. next unless enemy.letter.empty? #Skips if letter has already been applied? n = @names_count[enemy.original_name] || 0 #Sets 'n' to the value held by the enemy's name in @names_count or zero enemy.letter = letter_table[n % letter_table.size] #Sets the enemy's letter variable to the value retrieved by letter_table when the proper-sized table is given 'n'? @names_count[enemy.original_name] = n + 1 #Increases the value stored by the current name key by one, or sets a key by this name in the array with value 1 if not there already? end members.each do |enemy| n = @names_count[enemy.original_name] || 0 enemy.plural = true if n >= 2 #Sets this enemy's plural flag to "true" if the name key holds a value of 2 or more end endLooking at this though, I'm not precisely sure how it does so.  My first assumption would have to be that it's done on line 7 since that's the only one that appears to be assigning something to the hash.  Am I correct to assume that before reaching that line for the first time, the hash is empty, then upon reaching it, it is automatically given the current enemy name as a key with value: 1?

For the record do have a few other questions about this function, but they're not entirely relevant right now.
 

Shaz

Veteran
Veteran
Joined
Mar 2, 2012
Messages
40,098
Reaction score
13,704
First Language
English
Primarily Uses
RMMV
At the bottom of the clear method, @names_count is initialized. It's set to data type hash, with no contents.


In the make_unique_names method, line 90 checks to see if the hash has an entry for the current enemy name. If it does, it puts the value (count) into the variable n. If it doesn't have an entry, it puts 0 into n.


On line 92, you're right - it places n+1 into the hash for that key.

hashname[key] = valuewill look for the entry in the hash for key. If it's there, it will save the value against the key. If it's not there, it will create the entry for key and assign its value.
So the first time it encounters a unique enemy in the troop, it'll create the key and give it a value of 1. Every time after that, when it encounters the same enemy, it'll look up the current value, increment it, and save the new value back into the hash for that key.


The next few lines (94-97) aren't relevant to your requirements, but you'll notice it flags the enemy as being plural if the count is greater than 1 (or, to be exact, greater than or equal to 2).


Since that method is run at the very start of battle, when the troop is first set up, and the @names_count is a class variable, we can then use it in any other Game_Troop methods that we want to. It is only updated at the start of battle - the counts are not changed as enemies are defeated, so it will always give you the number of enemies you encounter at the beginning of the battle.


Interestingly (and correctly), if you have any enemies set to appear halfway, they are NOT included in this start-of-level count. So if your troop consists of 4 Vargs, but only 1 is visible from the start and the other 3 set to appear halfway, your message at start of battle will be "Varg Emerged!"
 
Last edited by a moderator:

Chanto

Scripting Newbie
Member
Joined
Jul 31, 2013
Messages
21
Reaction score
1
First Language
English
Primarily Uses
Okay, great!  Man, I'm really glad I decided to start asking about this stuff here, I've learned quite a bit more already, XP.  Thank you very much for your help!  Now, my next goal is to upgrade this bit of code so that it checks for cases of specific enemy names that can't be pluralized by just adding "s" (Like, for example, Wolves), but that's for when I'm not up at 1:50 AM @__@.  I'll attempt this next bit on my own as much as I'm able to, though odds are I may be back with more confusion, just a warning.  XP

Thank you again!
 

Shaz

Veteran
Veteran
Joined
Mar 2, 2012
Messages
40,098
Reaction score
13,704
First Language
English
Primarily Uses
RMMV
haha - yeah, I did wonder about those cases :) I figured I wouldn't throw it at you while you were just trying to get it functional :D


If you're looking for a starting point ... if I were trying to do this, I'd make use of notes on the Enemies tab, and have something read it looking for a plural term. If not found, it would default to just adding 's' on the end. But if found, it would use that word instead. Since it's really hard to make a list of rules for when you should add s and when you shouldn't, and you don't really want to check through a dozen or more rules each time (only to potentially find one that falls through the cracks), it's probably easier to just manually highlight in the database any where you want handling different to the default.
 

Chanto

Scripting Newbie
Member
Joined
Jul 31, 2013
Messages
21
Reaction score
1
First Language
English
Primarily Uses
Heheh, I do appreciate your consideration toward not overloading me with info, XP

But yeah, when I was looking around for other examples of this kind of script, I did come upon a pluralization script that relied on note tags in the database per enemy to list their pluralized names.  I looked at it and...well, I'm definitely gonna need to bone up a bit on how note tags and such actually work, because I saw this:

def plural_name /^\s*plural[_\s]?name\s*[=:]\s*([^$\n\r]+)/i =~ note if Regexp.last_match != nil Regexp.last_match[1].stripand promptly panicked.  XP  But yeah, I was originally gonna make a method that checked for these individual cases and applied them as necessary, but note-tagging does seem much more efficient, thanks for the tip there, heheh.
 

Shaz

Veteran
Veteran
Joined
Mar 2, 2012
Messages
40,098
Reaction score
13,704
First Language
English
Primarily Uses
RMMV
Yep, good old regular expressions! The bane of my scripting :D I've seen quite a few different ways to use them, and I have my own (that's a little bit easier to read than that) - I'm not sure whether one way is necessarily any better than another, so I go with whatever is more understandable.
 

Chanto

Scripting Newbie
Member
Joined
Jul 31, 2013
Messages
21
Reaction score
1
First Language
English
Primarily Uses
So...I seem to be having issues again.  I made a few changes and added in a bit of note-tagging stuff and, well...



Now it's giving me this when I start up a battle test.  Let me explain a bit.

Here's the modified enemy_names script again

class Game_Troop < Game_Unit #-------------------------------------------------------------------------- # * Get Enemy Name Array # For display at start of battle. Overlapping names are pluralized. #-------------------------------------------------------------------------- def enemy_names names = [] #Enemy names returned members.each do |enemy| #Iterates through each troop member next unless enemy.alive? #Skips to next enemy if the current one is dead next if names.include?(enemy.original_name) || names.include?(enemy.original_name + "s") #Skips to next loop if singular or pluralized name is already in array n = @names_count[enemy.original_name] || 0 #Initializes variable "n" to the amount held by the current enemy name #in the @names_count hash, or 0 if unapplicable names.push(n > 1 ? (enemy.spec_plural ? enemy.spec_plural : enemy.original_name + "s") : enemy.original_name) #If n is greater than 1, pluralizes the name and adds it to the list, #otherwise just adds the original name end names endendIt's the same as last time, except I modified line 15 to, after checking 'n''s value, check if my new special pluralization function returns anything besides false.  If it does, then it should .push the returned string, but if not, it should do what it did previously and just add an 's'.

Here's my Special Pluralization function.

class RPG::Enemy #-------------------------------------------------------------------------- # * Special Plural # Finds special cases of enemy pluralization not covered by just adding # an 's' to the end. Mark these cases in the Enemy database notes with # "<plural = x>" where 'x' is the plural form. For example, for a wolf # enemy, you would add the note tag <plural = wolves>. #-------------------------------------------------------------------------- def spec_plural if @note =~ /<plural = (.*)>/i return $1 else return false end endendI'm not entirely sure what I'm doing wrong this time around...  I followed the instructions given in this thread.  Maybe there's something I missed?  I dunno.  =|
 

Shaz

Veteran
Veteran
Joined
Mar 2, 2012
Messages
40,098
Reaction score
13,704
First Language
English
Primarily Uses
RMMV
Did you save your project after editing the script and before going into battle test? It doesn't happen by default, so you have to do it manually.


Try a regular battle, rather than battle test, and see if the same error occurs.
 

Chanto

Scripting Newbie
Member
Joined
Jul 31, 2013
Messages
21
Reaction score
1
First Language
English
Primarily Uses
Yup, I always make sure to Apply & Save after making changes.  Just tried a Playtest and loading up a battle event from there...  Still got the same error, yeah.  Different memory address, but same error. >.<
 

Chanto

Scripting Newbie
Member
Joined
Jul 31, 2013
Messages
21
Reaction score
1
First Language
English
Primarily Uses
Posting again after the 72-hour limit to say that the issue has been figured out.  Had to change enemy.spec_plural to enemy.enemy.spec_plural, as suggested to me by Fomar:

enemy refers to a Game_Enemy object

enemy.enemy refers to the $data_enemies entry where the notetag is etc
So, I am pleased to say that I believe this issue is over and done with.  Next stop, overhauling the battle system... @_@

You guys may close this thread now if you'd like.
 
Last edited by a moderator:

Shaz

Veteran
Veteran
Joined
Mar 2, 2012
Messages
40,098
Reaction score
13,704
First Language
English
Primarily Uses
RMMV
This thread is being closed, due to being solved. If for some reason you would like this thread re-opened, please report this post and leave a message why. Thank you.


You don't have to wait 72 hours to request a topic to be closed when it's resolved. You just hit the Report button on the first post, and in the message, say it's resolved and ask for it to be closed. There's no waiting on that :)
 
Status
Not open for further replies.

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Posts

Latest Profile Posts

Do you Find Tilesetting or Looking for Tilesets/Plugins more fun? Personally I like making my tileset for my Game (Cretaceous Park TM) xD
How many parameters is 'too many'??
Yay, now back in action Happy Christmas time, coming back!






Back in action to develop the indie game that has been long overdue... Final Fallacy. A game that keeps on giving! The development never ends as the developer thinks to be the smart cookie by coming back and beginning by saying... "Oh bother, this indie game has been long overdue..." How could one resist such? No-one c
So I was playing with filters and this looked interesting...

Versus the normal look...

Kind of gives a very different feel. :LZSexcite:

Forum statistics

Threads
105,855
Messages
1,017,007
Members
137,563
Latest member
MinyakaAeon
Top