Starting Over – Roguelike Design part 8


Yes the title of this article is a bit dramatic… you call it click-bait if you want. The reality however, is that I deleted nearly 95% of the item generation code and replaced it. That’s what I’ve been doing this past week. In the last article, I made some bold statements that led me down a dark path.

The problem was that I was trying to fit the armor generation procedures into the same model I developed for weapons… and they are entirely different things.

Are they really that different? The answer I came to realize this week is “no”. No they are not. I wrote in that article that I had finished the shield generation code… and I had. I started on it just like I had with armor. I copied the weapon generation code, changed the references to weapon tables in the database, deleted the parts that didn’t work, and began massaging what was left into the shield code.

If you find yourself copy/pasting entire functions to address subtle changes you are doing something very, very wrong. It was too late for me. I had already done this twice, and I was going to do it a third time for trinkets. I held off on releasing anything new until I had re-written it all

v0.1.6 – Shields and Trinkets

If you visit the site today, you’re seeing the new code. On the front-end, very little has changed. You can now select shields and trinkets from the item dropdown. That’s likely the only change you’ll notice… but the entire back-end that generates those items has been re-built nearly from scratch.

Trinket and Shield Icons

In my head, shields and trinkets were dissimilar enough from armor and weapons that I needed to create an entire new routine for them… but I wanted all items to have the same attributes: attack, defense, magic, etc. This should have been the first sign that I was doing things wrong.

I’m a procedural programmer. I started learning software in the late 80’s and that’s simply how it was done. I been self taught ever since, and I have a hard time understanding Object Oriented Programming. That’s not to say that I don’t use Objects. I very much do. What I am saying is that I don’t think in Objects. When I think of a bit of code, I don’t look at it like I do a physical thing. I don’t “see” the connections between it and similar items. I don’t mentally link like-and-like with common functionality.

However, what I’ve been building with my item code is an associative array of values that represent that item in game turns… I’m building an Object without using Object Oriented code.

This isn’t the point in the article where I confess my sins and tell you that I have converted my code to OOP. What I have done, however, is use OOP principals to model my code.

What I’ve learned in the past week is that the true power of OOP is not in the objects themselves (they are wonderful though!), but in how it forces the creation of new code into neatly compartmentalized functions. If you look back at part 6 of this series, I shared the entire weapon creation function. It was quick and dirty. It was doing way too much. It was a prototype. It proved a lot of concepts by doing things wrong. It created an object array, modified that array, calculated statistics, output logging… it did all the things… and all of it badly.

Getters and Setters

One of the key tenets of OOP is the use of getters and setters. They allow for the accessing (get) of data from an object or the mutation (set) of that data. If you ever need to modify an object, you should be using a setter to do so. If you ever want to know something about an object, you should use a getter. These are things you have to create. They aren’t magically created for (in most languages/environments) and they allow for you to define how the data is stored and retrieved.

Maybe you want to store some personal information in your object in an encoded string for a base level of obfuscation.

$object->message = 'dGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw==';

You’d have to remember to encode your message before storing it every time you want to change that message. If you wrote a simple setter function, however, you can have your message encoded and stored all in one step. Instead of manually assigning as we did above, we call the method setMessage instead and our message is encoded and stored.

class object {
  public function setMessage(str $message) {
      $this->message = base64encode($message);
  }
}

$object->setMessage('this is an encoded string');

To reverse it, we add a getter to our function and now we no longer have to concern ourselves with remembering to encode or decode our message when we need it.

class object {
  public function setMessage(str $message) {
      $this->message = base64encode($message);
  }
  public function getMessage() {
      return base64decode($this->message);
  }
}

echo $object->getMessage();
'this is an encoded string'

I’m not using Objects but I am working with arrays, and I should have stuck to these principals earlier in the design. Adhering to these tenets would have really helped me solidify the processes in more generic terms instead of just copy/pasting code. If you’re curious, PHP multidimensional arrays are extremely similar to objects. They are containers with named registers where you can store things. That’s basically an object. The main difference is that the container doesn’t carry it’s methods with it. So I have to remember to include the correct functions so that I can act on those arrays as needed.

$object = array();
function setMessage($object, $message) {
  $object['message'] = base64encode($message);
}

setMessage($object, 'this is an encoded string');

Immutable Items

In part 2 of this series, I shared this “magic” function I was using to set item attributes at creation time.

function sumWeaponAttributes($weapon, $array) {
  foreach($array as $key=>$value) {
    if (array_key_exists($key, $weapon)) {
      $weapon[$key] += $value;
    }
  }
  return $weapon;
}

While magic at the time, this set a bad precedent for how I created items from this point forward. What I was doing here, was totaling each item property whenever a new attribute was assigned to that item. I did the same for the item name. In the end, I wound up with an item that had a single value for each property. That was fine at the time, but what would happen if I wanted to remove or change a property?

Say I had generated a Flaming Sword of Truth +2 and at some point in the future, I want to change it to be a Flaming Sword of Glory +2. How would I go about that? Well, I’d have to figure out what Truth added and subtract that. Then I’d have to figure out what glory adds and add that. Then I’d have to find the word Truth in the name string and replace it with glory. That’s a nightmare. If I had used getters and setters to begin with, I would have saved myself a lot of headache.

Now, each property (Element, Prefix, Type, Adjective, Abstract Noun, Bonus) are stored as individual properties in the item. If want to know something like attack, or the name, I have a getter function that can build that for me. It’s now trivial to change a weapon over time by dropping a property or adding a new one. I don’t have to do anything complex.

This is how it should have been all along. This is why OOP is such a foundational paradigm. It forces you to think about these things long before you get stuck down a deep dark hole of spaghetti code.

Conclusion

I’m aware that I just wrote a whole article with very vague talk about principals and I didn’t share much actual code. The lessons that I learned this week weren’t about the code, but rather the principals I used to (or didn’t use) get my code working. I do have a few bits and pieces I’ll share in the next few articles about specific technical problems that challenged me.

It is very likely you won’t see much change on the site for quite some time. The next parts of this project will take a very long time to get working. I’ll sill be writing articles about all of it, but I’m afraid there won’t be much in the way of tangible, hands-on, changes to the site that you’ll be able to play with. If you want to see the code, let me know and I’ll send you an invite to my private repo. I’m keeping it private for the time being, but fully intend to open it up at some point in the future… once we get closer to a playable game.

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.