Creating Better Items – Roguelike Design part 4


Previously, on Whoo Boy I Have No Idea What I’m Doing, we built a function to generate some random weapons. We use a combination of PHP rand() and mySQL ORDER BY RAND() to get random attributes from our tables, and add it all up into something that can be used in our some-day game. It works, but it’s entirely too random for an actual game to use. We don’t want our character to encounter a Shocking and Flaming Vorpal Greatsword of Mighty Justice +3 in their first room!

Assigning Value

I wanted a quick way to determine the “power” of a weapon based on how many attributes were assigned to it, so I numbered all the attributes from 1 to 12 based on their probabilistic rarity:

AttributePower Level
Weapon1
Abstract Noun2
Prefix3
Adjective4
Bonus +15
Element6
Bonus +27
Bonus +38
Bonus +49
Bonus +510
Double Element11
Crippling 12

I supposed I’ve never mentioned those last two in previous posts. I decided it would be fun if a weapon could have more than 1 element. This would be super rare, but having a Flaming and Shocking Sword could be neat. I also decided that having all three would be super cool! I called it Crippling and took it a bit farther. Crippling provides bonuses for all three Element types as well as Holy/Evil! It’s super rare though, as it should be.

Okay, with the values assigned to each attribute, I sum them as I build the weapon:

  // prefix
  if (rand(0, 100) >= 75) {   // 25% of all weapons
    $sql = $pdo->query("SELECT * FROM weapon_prefix WHERE active = 1 ORDER BY RAND() LIMIT 1");
    $weapon_prefix = $sql->fetchAll(PDO::FETCH_ASSOC);
    $weapon = sumWeaponAttributes($weapon, $weapon_prefix[0]);
    $weapon['str'] += 3;
  }

In the end, I have a relative total strength. It really doesn’t reflect on the actual abilities of the weapon itself. You could end up with a bunch of negative modifiers but the weapon is still rated at power level 10. Really, this was never intended to be part of the game engine, it was just a way for me to quickly judge how the randomness was being applied over large sets of generated weapons.

BUT, I wanted to be able to generate weapons of a pre-determined level, so I did what any hack programmer would do and implemented a while loop!

////////////////////////////////////////////////////////////////////////////////
// fuction createWeapon()
////////////////////////////////////////////////////////////////////////////////
// Creates a fantasy weapon name
// takes no arguments, returns associative array
////////////////////////////////////////////////////////////////////////////////
function createValuedWeapon($min=0, $max=20) {
  $weapon['str'] = -1;
  while ($weapon['str'] < $min || $weapon['str'] > $max) {
    $weapon = createWeapon();
  }
  return $weapon;
}

Yeah, I did that. It’s pretty embarrassing on lots of different levels, but this was just helping me test the random generator… so I wasn’t worried about nefarious things like infinite looping. So, what is going on here? I give this function createValuedWeapon() two arguments $min and $max. I want a weapon with a str value between those two numbers… so, the while loop just goes on creating weapons one after another until it finds one that fits, and then gives us that weapon. The main problem, besides inefficiency, is that passing in some high numbers, say $min=50 $max=5000 means that this loop would run forever as there are no weapons that high.

The inefficiency is just lazy. Sure, this works when asking for 1 weapon, or even a few dozen. What happens if I want to generate a list of a few hundred? If I’m asking for high-power weapons, this loop will generate thousands, maybe hundreds of thousands, of weapons trying to fulfill my request. That can be very taxing very quick between the PHP processes and mySQL trying to process this.

I’m Just Sketching

So I shared some embarrassing code. I’ve shown a lot of embarrassing code as part of this series. I’ll continue to show embarrassing code going forward.

I approach coding very similarly to the way I approach art: I start with a rough sketch, then refine.

The important thing to take away from this is that it works. I got an idea out of my head, into code, and I was able to test it. I was able to see what my random numbers were doing very quickly without worrying about code structure or efficiencies. I’m not thinking about my function’s Big O or where I can optimize. I’m just getting thoughts into the machine so I can see what comes out the other side.

Now, Let’s Make it Better

Instead of demanding a weapon of a certain power level, what happens if we modify the randomness of our generator. This is ultimately the method I’m going to go with for the game engine, as it has some cool side effects.

  // prefix
  if ((rand(0, 100) * $power)  >= 75) {   // 25% of all weapons
    $sql = $pdo->query("SELECT * FROM weapon_prefix WHERE active = 1 ORDER BY RAND() LIMIT 1");
    $weapon_prefix = $sql->fetchAll(PDO::FETCH_ASSOC);
    $weapon = sumWeaponAttributes($weapon, $weapon_prefix[0]);
  }

Simply adding an argument to our function createWeapon($power=1) now allows us to modify the relative starting-point for our random number generators. By default, we multiply our random number by 1, which is to say that it stays the same… but, if I pass in $power = 1.2; we now have a 20% higher probability of gaining a weapon prefix!

If you visit noroguesallowed.com today, that’s exactly what you are doing when you choose a percentage in the Power modifier selector. I’ve modified all the if ( rand() ) conditionals to if ( rand() * $power ) so you gain that extra chance over all the different attributes.

As a gamer, my favorite side effect of this is that nothing is impossible with this method. The much more strict $str selector from before meant that you were guaranteed to receive a weapon within the provided values. It was impossible to receive anything outside of that range. With the randomness modifier, you should receive a weapon within a certain power range, but the outliers aren’t impossible. That first level character might just find that amazing Steel Shortsword +5 and you might find a Dull Knife in the last level of the game!

That’s all for now. I’ll let you figure out what that Tier means in the image above. We’ll talk about it next post. It’s live right now though, so go play with it!

https://noroguesallowed.com

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.