Code question: L5R and Ohm Models

So I’m working on a skill system for L5R and I’ve come across something of a conundrum. In L5R, characters are given ranks in various rings which in turn are informed by the ranks in their associated traits. For example: The Fire ring is formed of the Intelligence and Agility traits. Both ring and trait ranks are used in various rolls, abilities, and derived traits through out the system.

My first thought was to create collections for both traits and rings (with corresponding models for both) on the character, and a collection for traits on the ring model as well, but then it occurred to me that this might not be the most efficient way to do things. In fact is it even possible? I came up with the alternative of putting the trait collection directly on the ring model instead and simply creating a helper method that pulled those whenever I needed them.

Would love and appreciate some advice on which of these (or a third, as yet unconsidered approach) would be most efficient!

1 Like

Do the rings have other properties, or are they solely a number derived from the associated traits?

The rings are just a number, thinking about it… So what, an attribute on the character model?

I could be missing something (having never played the new L5R) but I think all you need is a method on the character object.

Assuming you have a collection of traits, you can do:

class L5RTrait < Ohm::Model
     attribute :rating
     # I assume there are other things about a trait you need to store; otherwise a model is overkill
end
class Character
   collection :l5r_traits, "AresMUSH::L5RTrait"
   
   def l5r_trait(name)
        # Finds a trait by name
   end
   def l5r_ring(name)
       if (name == "Fire")
          return l5r_trait("Intelligence").rating + l5r_trait("Agility").rating
       else
        ...
       end
   end
end

Something like that anyway.

Side note tip - notice that I named the attributes and methods on the character object as l5r_xxx. That’s just to avoid collisions with other definitions that might try to use a name like “trait” or “skill” or whatever.

You know… That… Honestly had never occurred to me. I did a very quick pass through to make sure that there wasn’t something /I/ was missing too, but no I think that’d work. There’s no real reason why it has to be stored on the character.

In this case it’s 4E so I’d be using .min, but yeah…

Thanks! That’s much simpler.

And yeah, I’ve been absorbing every scrap of information on the AresMUSH site and examining your other skill system plugins for about a week. Glad to see I at least got the naming conventions correct!

Though at the same time, I think I might want to take a second pass at the models just to make sure nothing else is extraneous.

Okay, this is what I’ve come up with based on what you suggested. It’s 1:30AM, so forgive any blaring inefficiencies.

    def calc_l5r_ring(char, ring)
      if ring == 'fire'
        calc = char.l5r_traits.select { |t| t[:name] == 'agility' || 'intelligence' }
                   .map { |t| t[:value] }
        return calc.min
      ...
      end
    end

Looks like you’re on the right track.

I’m not sure where you intended that method to live, but you have two options.

  • In the character class, in which case you don’t need the char parameter because you’ll be calling it like some_char_you_already_have.calc_l5r_ring('fire').
  • In the L5R module helper, in which case you’ll need to define it as def self.calc_l5r_ring(char, ring) and call it like L5R.calc_l5r_ring(some_char,'fire')

Also if l5r_traits is a collection of Ohm models, you’ll access the parts using t.name and t.value instead of the array versions t[:name]. What you have there would work if l5r_traits is a hash attribute as opposed to a separate model.

Both are totally valid ways of storing the traits; it really comes down to how much ancillary data is stored about the trait. For example in FS3 we have last XP spends, specializations, etc. It’s not just a name and a number. The more complex your data, the more likely you’ll want to store it as a model and not a hash.

Oh, damn. Yeah. Somewhere in my head I crossed reading the config file with pulling from the model. See 1:30AM above, most likely.

You’ve been a ton of help and I think that should be enough to get me going with this once I sleep and come back to it with fresh eyes.

Thank you so much!

1 Like

Another quick note on your code, if you do

t.name == 'agility' || 'intelligence'

it will always return true, you need to make the comparison twice or make an include check:

t.name == 'agility' || t.name == 'intelligence'

or

['agility', 'intelligence'].include? t.name
1 Like

Good catch! I knew about making that comparison twice (because I borked it once before), but yeah… 1:30 in the morning after a busy shift was not the time to be doing anything besides sleeping.

1 Like

Yeah, sorry, it wasn’t meant as a code review. I just know how nefarious unexpected conditionals (that aren’t technically syntactically wrong) can be and so I just wanted to make sure it was known.

2 Likes