1

I have a game I've been working on for awhile. The core is C++, but I'm using Python for scripting and for Attacks/StatusEffects/Items etc.

I've kind of coded myself into a corner where I'm having to use eval to get the behaviout I want. Here's how it's arising:

I have an xml document that I use to spec attacks, like so:

<Attack name="Megiddo Flare" mp="144" accuracy="1.5" targetting="EnemyParty">
    <Flags>
        <IgnoreElements/>
        <Unreflectable/>
        <ConstantDamage/>
        <LockedTargetting/>
    </Flags>
    <Components>
        <ElementalWeightComponent>
            <Element name="Fire" weight="0.5"/> 
        </ElementalWeightComponent>
        <ConstantDamageCalculatorComponent damage="9995" index="DamageCalculatorComponent"/>    
    </Components>
</Attack>

I parse this file in python, and build my Attacks. Each Attack consist of any number of Components to implement different behaviour. In this Attack's case, I implement a DamageCalculatorComponent, telling python to use the ConstantDamage variant. I implement all these components in my script files. This is all well and good for component types I'm going to use often. There are some attacks where that attack will be the only attack to use that particular Component Variant. Rather then adding the component to my script files, I wanted to be able to specify the Component class in the xml file.

For instance, If I were to implement the classic White Wind attack from Final Fantasy (restores team HP by the amount of HP of the attacker)

<Attack name="White Wind" mp="41" targetting="AnyParty">
    <Flags>
        <LockedTargetting/>
    </Flags>
    <Components>
        <CustomComponent index="DamageCalculatorComponent">
            <![CDATA[
                class WhiteWindDamageComponent(DamageCalculatorComponent):
                    def __init__(self, Owner):
                        DamageCalculatorComponent.__init__(self, Owner)

                    def CalculateDamage(self, Action, Mechanics):
                        Dmg = 0
                        character = Action.GetUsers().GetFirst()
                        SM = character.GetComponent("StatManagerComponent")
                        if (SM != None):
                            Dmg = -SM.GetCurrentHP()
                        return Dmg
                return WhiteWindDamageComponent(Owner)
            ]]>     
        </CustomComponent>
    </Components>
</Attack>

I was wondering if there might be a better way to do this? The only other way I can see is too put every possible Component variant definition into my python files, and expand my Component creators to check for the additional variant. Seems abit wasteful for a single use Component. Is there a better/safer alternative to generating types dynamically, or perhaps another solution I'm not seeing?

Thanks in advance

megatron2
  • 75
  • 5
  • 2
    I think having the component definition in a separate python module seems a much better idea, compared to having to use eval. If you are going to have to supply the Python code anyway, why not have it in a Python module? – MAK Apr 27 '12 at 20:09
  • I wonder if a better solution would to not do xml but have python classes with a metaclass that adds restrictions similar to what an xsd would do... – Doug T. Apr 27 '12 at 20:11
  • 1
    Seconding MAK and Doug T. Unless there's additional information you've omitted that justifies using XML for this -- don't. – senderle Apr 27 '12 at 20:14
  • I concur. To quote the highly recommended [Python Is Not Java](http://dirtsimple.org/2004/12/python-is-not-java.html): "XML is not the answer. It is not even the question." –  Apr 27 '12 at 20:18
  • I was using XML because I started working on my tool chain, and editing/manipulating XML files is really easy in C#/WPF. It seemed a quick, easy, format for interchange between my python scripts, C++ core and C# tools. Pure python would be hard to provide a good front end for. I'm going to end up with a few hundred attacks that I'm going to want to tweak a lot for balancing, and having a frontend will be easier then editing the raw files both for me and the other people working on the project – megatron2 Apr 27 '12 at 20:19

1 Answers1

2

Inline Python is bad because

  • Your source code files cannot be understood by Python source code editors and you will miss syntax highlighting

  • All other tools, like pylint, which can be used to lint and validate source code will fail also

Alternative

In element <CustomComponent index="DamageCalculatorComponent">

... add parameter script:

<CustomComponent index="DamageCalculatorComponent" script="damager.py">

Then add file damager.py somewhere along the file system.

Load it as described here: What is an alternative to execfile in Python 3?

In your main game engine code construct the class out of loaded system module like:

 damager_class = sys.modules["mymodulename"].my_factory_function()

All Python modules must share some kind of agreed class names / entry point functions.

If you want to have really pluggable architecture, use Python eggs and setup.py entry points

Example

Community
  • 1
  • 1
Mikko Ohtamaa
  • 82,057
  • 50
  • 264
  • 435