Suggestion: Separate Character Gen from Character Storage #4

Open
namelessjon opened this Issue May 9, 2012 · 15 comments

Projects

None yet

2 participants

@namelessjon

At the moment, the Playerchar class is really a PlayercharGenerator, i.e. it does all the random rolling to make a character. It's obviously not appropriate to do this every time you use the same character. Of course, persistence, or at least storage in a program might be better handled through a library like virtus rather than rolling your own. But you could then look into extending the generator to cope with things like rolling for gold, or looking into how you can use things from the SRD in the code.

@alxjrvs
Owner

I am not entirely sure what you mean, though that can be likely traced to my ignorance of ruby.

My thought is that a class is a kind of object, and that this would allow me to create a new kind of object and assign it to a variable (or, when the time comes, a table in a database); So I can say "Alxjrvs = Playerchar.new("alex")" and it would simply create an object with 'name' as alex and a single statlist (that i can call - Alxjrvs.stat.str). How would your method change that, and how would you go about it?

@namelessjon

I didn't explain myself clearly enough.

Lets take your example of alxjrvs = Playerchar.new("alex"). You're making a new Playerchar instance, assigning the name "alex", but you're also randomly generating the stats for alex. While this is appropriate the first time you make alex in your code, the next time you make him, you probably want to have the same stats for the same character. But at the moment, the random stat generation is tied in to making the Playerchar instance. So every time you end up with a new randomly stat'd alex.

Instead, you could have two classes, one which you could call CharacterSheet, which just handles the storage for the name and the various stats (maybe also dynamically figuring out things like the stat mods, save mods, carrying capacity, whatever). Virtus which I mentioned earlier, could be handy for this as it gives you a nice declarative syntax and handles some conversions between types. These CharacterSheet instances can be used to hold both old and new characters in memory.

The other class (something like CharacterGenerator) can randomly generate a character. It probably will look quite similar to the current Playerchar class. It could perhaps make a new CharacterSheet instance with the stats too, or you could assign them some other way. This would also allow you, later, to make a different generator, which allows for rolling like 4d6 (keep highest 3) or whatever else is wanted.

Does what I'm suggesting seem clearer? Do you think it makes sense?

@alxjrvs
Owner

It is clearer, though I am still not as set as how different they are. In my example, if I keep referring to "alxjrvs" in the code, won't the stats be the same? The Playerchar object is generating a new Statlist object, one that will remain constant inside of the Playerchar object. So long as I do not reassign the "alxjrvs" variable elsewhere, Alxjrvs.stats should always return a statlist, right?

Put another way, wouldn't the CharacterSheet class just be an extension of Statlist to include other functions?

@alxjrvs
Owner

(By the way, if It wasn't obvious, I am seriously super-thankful you are interested in this. It's helping me a lot to be able to discuss this with people.)

@namelessjon

Put another way, wouldn't the CharacterSheet class just be an extension of Statlist to include other functions?

Yes and no. It will probably include more functions (or more objects, depending), but the important part here is to separate the the two behaviours. As it currently stands, making a new Statlist is intrinsically tied to randomly generating new stats. This means that you couldn't use it to represent an existing character's stats as the stats would be all wrong. And making a new Playerchar is intrinsically tied to making a new Statlist, too. Because you have character generation tied up with character representation, all you can do is make new characters.

If you separate out the concerns, you can both represent old characters, and make new ones.

(I'm glad you're finding this useful.)

@alxjrvs
Owner

I think this is an unknown-unknown situation for me. I get the abstraction, but I am not sure how it is an improvement, almost certainly because my scope in ruby is short. I apologize if I am being dumb-headed about this.

As I have it, Playerchar initializes by generating a character. Once I create that character ('Alex'), it wouldn't be initializing anymore - it would be referencing the already created statlist, would it not? So long as I don't overwrite that 'Alex' variable, that character remains specific and intact, correct?

Graphical representation of what I have:

Playerchar (Which makes a new) Statlist

You are suggesting ( I think):

CharacterGenerator (which creates a new) CharacterSheet (with) Statlist, Names, Etc.

I apologize again if I am being thick, but how, functionally, would these be different (other than the end of my current way being a Playerchar object, and in your way, it is a CharacterSheet? Is that separation important? In my noobish brain, it appears to be analogous. )

@namelessjon

Don't worry about that. And don't apologise for asking questions :) The ruby community would generally be better if more people asked questions rather than blindly copying. I'll try and show an example where the separation is important.

What you say about how if you don't reassign something else to alxjrvs is correct. When I was talking about an 'existing' character, I was referring to characters that exist beyond the scope of this library. One you might be playing in a game right now. You couldn't take Bob the Wizard's stats off his pen&paper sheet and copy them into some code using your library, to use all the wonderful character sheet functionality you're going to implement. This is because at the moment, you do:

bob = Playerchar.new('Bob the Wizard')

And poor Bob gets random stats. Creating a variable to store Bob in a program also requires that you generate Bob.

What I'm suggesting instead is that you have one class whose responsibility is to store characters (Playerchar, CharacterSheet, whatever the name you choose). This can use other classes to store bits of the sheet, it doesn't have to be a large class of doom. Then you have a second class which you can use to generate random stats, and probably looks a lot like the current Statlist class. Each class has a 'Single Responsibility', which generally helps in clean design.

@alxjrvs
Owner

Ah Ha!

I completely get your point now. I was only coming at this from the point of view representing the programs (and characters) as of yet written - I see what you mean now and it makes complete sense.

I can now envision a number of abstractions.

Lets say I have these classes: Playerchar, which holds a Player's Character. CharacterSheet, which is the "index" class (in that it holds all the stats, many of which are individual classes themselves). One of those is Statlist, which holds stats.

Would it be possible to set up something like Playerchar.roll, which is like Playerchar.new, except with random stats (chaning as such down the line)? Would that be sensible?

@namelessjon

Adding a class level helper function which generates a new character with random stats seems quite sensible, yes. Keeping in mind the idea of single responsibility, it should probably only call out to some other class which actually handles the generation, but having the method on there seems wise.

@alxjrvs
Owner

And what would that look like?

Lets say I use my current setup - how would I differentiate between "New playerchar with exisiting stats" and "Brand new rolled character?" I sense that this happens in the Statlist initialize, right?

@namelessjon

When I do things like this, I tend to have the initialize function just be concerned with storing the attributes. The random generation can be done with another function which generates the stats, then calls the constructor.

@alxjrvs
Owner

Now here's where my noobishness comes up: I am completely lost with how to have methods call certain values within a class.

So, for instance, how do I make it so that Player.re_roll 1) makes a new Playerchar and 2.) calls for Statlist to make a new Statlist object with special values for the variables?

You can see where I go wrong here: https://github.com/alxjrvs/Stattr/blob/charactersheet/lib/stattr.rb

@namelessjon

To make this a little easier, can I suggest you make a pull request against your own repository for that branch? That way, we can comment on the code in line, and keep them associated with the changes. While it might seem a little odd, it is how the github team work too.

If you reference this issue via 'Issue #4' somewhere in the pull request, we can have this discussion linked too.

Edit: You were already developing a branch, which I hadn't noticed.

@alxjrvs
Owner

That makes sense - I'll do that now.

@alxjrvs alxjrvs referenced this issue May 14, 2012
Merged

Charactersheet #5

@alxjrvs
Owner

#5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment