Dealing with Multiple Characters with Different Attributes

Started by
6 comments, last by Alberth 6 years, 9 months ago

I'm trying to make a game where the characters are randomly generated and retain individual attributes over time. Right now, I'm working on the combat system. I've made it so the roles of "Attacker" and "Defender" will switch between the player character and the NPC they are fighting. Problem is, I'm unsure of how to organize it in an elegant way. All I can think of is doing a bunch of if & else statements every time I need to check. Could you suggest ways of doing this in an elegant way? If there is anything in my code that could be significantly simplified don't be afraid to point that out either.

 

Here is an example of one of the methods I could use help with. In it, I am taking the int type variable, "overall action score", or OAS, from a tuple, which then helps to determine the force of any attacks that land. On if and else statements, I am checking who is attacking, and calling one of their respective physical attributes.


    def forceFinder(self):
        """
        Determines force of strike from
        OAS and alacrity.
        """
        #subtract attacker OAS from defender OAS
        force = self._oasTuple[0] - self._oasTuple[1]
        #exponentially adds to force according to alacrity and
        #margin between attacker, and defender OAS.
        for i in range(0, force, 10):
            force *= 1.10
            if Combat._attacker == "pc":
                force += Combat._pcAttr["alacrity"] * 3
            else:
                force += Combat._npcAttr["alacrity"] * 3
        return int(force)

 

 

Advertisement

Python is not my strong suit so I apologize if this is vague, but...

What you want to do is store a reference to the PC (or NPC) in a variable. Then instead of having an if check, you just ask the variable for its alacrity stat directly. When the turn switches, you can point that variable to the "other" character instead, and off you go.

This way the code uses the same exact checks for everyone, but it can be "configured" to point to one character or another on the fly.

 

Hope that made some semblance of sense :-)

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

The typical approach is to have data that gets swapped out.

You might pass an index to a table for the source or target, you might pass an object for the source and the source for the target, or something else.

For example, adding that information to the object itself and accessing it generically:

force += Combat._attacker.Attr["alacrity"];

or perhaps:

force += Combat._attacker.CurrentAlacrity(Combat._defender);

or accessing it through another table:

force += CombatAttribs[Combat._attacker].["alacrity"];

 

I prefer the function version above, since it more easily allows you to add modifications based on the source and the target, such as a bonus versus dragons or penalty versus shopkeepers or whatever fits your game.

1 hour ago, ApochPiQ said:

Python is not my strong suit so I apologize if this is vague, but...

What you want to do is store a reference to the PC (or NPC) in a variable. Then instead of having an if check, you just ask the variable for its alacrity stat directly. When the turn switches, you can point that variable to the "other" character instead, and off you go.

This way the code uses the same exact checks for everyone, but it can be "configured" to point to one character or another on the fly.

 

Hope that made some semblance of sense :-)

So basically, you have an array for each character and switch the identifiers of the arrays based on who fills each role? If that's what you're saying I get it. It seems fairly simple, and would work. I'd like to examine other possibilities still.

22 minutes ago, frob said:

The typical approach is to have data that gets swapped out.

You might pass an index to a table for the source or target, you might pass an object for the source and the source for the target, or something else.

For example, adding that information to the object itself and accessing it generically:

force += Combat._attacker.Attr["alacrity"];

or perhaps:

force += Combat._attacker.CurrentAlacrity(Combat._defender);

or accessing it through another table:

force += CombatAttribs[Combat._attacker].["alacrity"];

 

I prefer the function version above, since it more easily allows you to add modifications based on the source and the target, such as a bonus versus dragons or penalty versus shopkeepers or whatever fits your game.

 

Thank you for the quick, and in-depth response. I'm sorry, but I don't understand Python 2. I'm guessing that's what these examples are since we never seem to end lines in Python 3.6 with semi-colons. I'm not sure how to make these examples work since I don't understand their formatting. I've never seen class-level variables called in that way, with two periods, to reach specific data. Could you please explain it or link to a website/video that can help me understand how to do the same thing in Python 3? It seems like some of your ways might be better than how I've interpreted ApochiPiQ's suggestion.

 

 

8 hours ago, RidiculousName said:

So basically, you have an array for each character and switch the identifiers of the arrays based on who fills each role? If that's what you're saying I get it. It seems fairly simple, and would work. I'd like to examine other possibilities still.

You can wrap the access code in a function


def get_property(combat, property):
    if combat._attacker == "pc":
      return combat._pcAttr[property]
    else:
      return combat._npcAttr[property]

which then gives


force += get_property(Combat, "alacrity") * 3

 

Frob folds the selection into the Combat object (his Pythoneese has some C++ influences, just ignore the semi-colons and the dots that look weird :) )

Instead of making "Combat.attacker" a string, make it the object you point to:


# Inside Combat

def assignAttacker(self, attacker_type):
    if attacker_type == "pc":
        self._attackerAttr = self._pcAttr
        self._defenderAttr = self._npcAttr
    else:
        self._attackerAttr = self._npcAttr
        self._defenderAttr = self._pcAttr

And now you can access Combat._attackerAttr[...]

 

As a note, the "_" prefix denotes a private variable (by convention) in Python, which basically means you should not access those variables from outside the methods of the class (like you do with Combat._pcAttr for example). Other Python programmers will get confused about it.

Unlike eg Java and C++, in Python an object variable is always prefixed by the containing object, that is, you cannot write "_pcAttr" to access the variable in Combat, you must always write something like "Combat._pcAttr" or "self._pcAttr". As such, there is no real need to differentiate between normal variables and object member variables, the "Combat." or "self." prefixes in front of the latter already do this.

 

 

Again, Alberth!? Every time I post a question you're there with amazingly well-reasoned and detailed answers. Thank you!

I'll take your advice. Only one thing still puzzles me bit.

Quote

As a note, the "_" prefix denotes a private variable (by convention) in Python, which basically means you should not access those variables from outside the methods of the class (like you do with Combat._pcAttr for example). Other Python programmers will get confused about it.

The method I gave as an example is held within the Combat class. Have I misunderstood you?

8 hours ago, RidiculousName said:

Again, Alberth!? Every time I post a question you're there with amazingly well-reasoned and detailed answers. Thank you!

Hmm, yeah, sorry about that. Likely that will continue to happen until you start asking questions about complicated component systems, data-oriented-design, and pretty much anything about GPUs or detailed performance stuff.

 

9 hours ago, RidiculousName said:

The method I gave as an example is held within the Combat class. Have I misunderstood you?

Good point, missed that. It would be fine then I guess, since it's only a convention anyway.

 

This topic is closed to new replies.

Advertisement