Advertisement

What do you guys use as a key in RPG data structures?

Started by August 25, 2015 01:01 AM
27 comments, last by ankhd 9 years, 4 months ago

So the wrapper only modifies the already-existing methods of the wrapped? That is, it doesn't add or remove any actual members?

It can do whatever you want, this is C++. A wrapper is just a concept, not a language feature.

You can't modify, add or remove the methods on an object at runtime in C++ (well, I guess you could overwrite the v-table on an object, but we're not talking about that here).

What smr was saying is that you might have an object A that implements an IFoo interface, and another object B that contains a reference to an instance of A as one of its members. B might also implement IFoo, and just forward all IFoo methods calls onto A, perhaps executing some logic before and after. Or for some IFoo methods, it might not bother forwarding the call onto A at all. You can do whatever you want.

Sorry for being a little dense—I'm just not quite wrapping my head around this. (huehuehuehuehuehuehue)


class Item {
    Item(std::string name) : durability_(100) { name_ = name; }
    void break() { std::cout << "Your item broke!"; }
    int durability_;
    std::string name_;
};

// To-be wrapped
class Weapon : public Item {
    Weapon(std::string name) : Item(name) {}
    void chip() { durability_ -= 5; }
    void rust() { durability_ -= 7; }
}

// Wrapper
class StrongWeapon : public Item {
    StrongWeapon(std::string name) : Item(name) {}
    Weapon* baseWeapon = new Weapon;
    void chip() { durability_ -= 2; }
};

// Wrapper
class GlowingWeapon : public Item { 
    GlowingWeapon(std::string name) : Item(name) {}
    Weapon* baseWeapon = new Weapon;
    void glow() { std::cout << "You're blinded by your own weapon!"; }
};

Do I have the setup right?

The idea is that I can now have a strong weapon, a glowing weapon, or a strong-glowing weapon, right? I've seen code like this in all the tutorials, but it seems to either go on for 350 more lines, or just stop there.

I guess I'm not sure how to use these wrappers. How does a GlowingWeapon access the rust() method? Is it going to be something like: GlowingWeapon->baseWeapon->rust() ? Or just GlowingWeapon->rust() ? If it's the latter case, how (and why) does this work? How do I create a Strong, Glowing Weapon?

Also, is it true that anything that takes a Weapon (or a pointer to a Weapon) will also be able to take a GlowingWeapon, or StrongWeapon (or both?)

I understand that "one class wraps another to change functionality," but beyond that I'm totally confused, and having a really hard time finding clear, succinct tutorials.

(Thanks for your patience, guys.)

Advertisement

Typing on my phone here but here's a contrived example of a wrapper implementation:

Also forgive my very rusty c++


class A 
{
public:
    virtual int addOne(int x)=0;
    virtual ~A(){}
}

class DoublingWrapper : 
    public A 
{
private:
    A* _a;

public:
    DoublingWrapper(A* a) {
        _a = a;
    }

    int addOne(int x) {
        int y = _a->addOne(x);
        return y*2;
    }
}


I guess I'm not sure how to use these wrappers. How does a GlowingWeapon access the rust() method? Is it going to be something like: GlowingWeapon->baseWeapon->rust() ? Or just GlowingWeapon->rust() ? If it's the latter case, how (and why) does this work? How do I create a Strong, Glowing Weapon?

Honestly, I don't think wrappers are the most straightforward way to achieve your goals.

A strong weapon is simply a weapon with different, better, stats.

A glowing weapon is simply a weapon with different information about how it should be rendered.

They could actually be the same class with different data.

Look at your chip() and rust() methods. They logic is the same in all cases, only the data is different (the amount by which durability is reduced). This is just different data, not different functionality.

If I were you, based on your requirements so far, I would just have a single Weapon class with fields for the things you need. If you have code that needs to reason in a unified way over weapons and other game Items (for instance, to display them with a name and icon in a GUI), then this Weapon can inherit from your base Item class.

If anyone reading this thread is similarly confused about decorators, this article really helped me. I revisited the (totally wrong) implementation of the Weapon wrappers that I attempted above.


#include <string>
#include <iostream>

// Superclass
class Item {
    public:
        Item() : durability_(100) {}
        void crumble() { std::cout << "Your item broke!\n"; }
        int durability_;
};

// To-be wrapped
class Weapon : public Item {
    public:
        Weapon() : Item() {}
        virtual void chip() { durability_ -= 6; }
        virtual void raise() { 
            std::cout << "The sword is raised into the air\n";}
};

// Decorator SuperClass
class WeaponDecorator : public Weapon {
    public:
        WeaponDecorator(Weapon* baseWeapon) : baseWeapon_(baseWeapon) {}
        virtual void chip() { baseWeapon_->chip(); }
        virtual void raise() { baseWeapon_->raise(); }
    private:
        Weapon* baseWeapon_;
};

// Decorators (which take after WeaponDecorator superclass)
// Strong weapon halves the durability loss for chip()
class StrongWeapon : public WeaponDecorator { 
    public:
        StrongWeapon(Weapon* baseWeapon) : WeaponDecorator(baseWeapon) {}
        virtual void chip() {
            WeaponDecorator::chip();
            durability_ += 3;
        }
};

// Fire weapon sets the weapon aflame
class FireWeapon : public WeaponDecorator { 
    public:
        FireWeapon(Weapon* baseWeapon) : WeaponDecorator(baseWeapon) {}
        virtual void raise() {
            WeaponDecorator::raise();
            std::cout << "It's on fire!\n";
        }
};

int main() {
    Weapon* baseWeapon = new Weapon;
    WeaponDecorator* enhancedWeapon 
        = new StrongWeapon(
            new FireWeapon(baseWeapon));

    enhancedWeapon->raise();
    enhancedWeapon->crumble();

    return 0;
}

The output:


The sword is raised into the air
It's on fire!
Your item broke!

I'd explain more in-depth, but I'm pretty sure everyone in this thread already knows what a decorator is.

Now to go back and read the relevant posts.

Thanks for the help, everyone.

Honestly, I don't think wrappers are the most straightforward way to achieve your goals.

They're not, no.

I was just using them as an example, though.

Advertisement

Just to be clear -- decorator is not a good choice to use for making permanent (even semi-permanent) changes to the decorated object -- decorator is a decent pattern when you want to temporarily change something about an object who's original form you want to retain. In the context of an RPG, decorator is a reasonable pattern for, say, status effects like curses or time-limited spell effects -- its a reasonable choice because in both cases you want to restore the original object when the curse is removed or when the effect of the spell wears off. In the context of an RPG, decorator is not a reasonable choice for leveling up a character or item, or for affecting any permanent/semi-permanent change to the affected object itself (for example, decorator is a poor choice for modeling a weapon's decreasing durability as its used.)

throw table_exception("(? ???)? ? ???");

Why don't you just download source codes and see how others did it? There are tons of very good quality source codes for roguelikes, one look at how they did it and it all become much, much more clear.

As they say, don't reinvent the wheel :)

Stellar Monarch (4X, turn based, released): GDN forum topic - Twitter - Facebook - YouTube

Free the memory when your done.

This topic is closed to new replies.

Advertisement