Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Spell Dictionary and reroute everything through it #183

Open
ACG8 opened this issue Mar 1, 2015 · 7 comments
Open

Create Spell Dictionary and reroute everything through it #183

ACG8 opened this issue Mar 1, 2015 · 7 comments
Assignees
Milestone

Comments

@ACG8
Copy link
Collaborator

ACG8 commented Mar 1, 2015

I think we have just about reached the limit of what we can do by reading the set.xml files. To continue with the automation of the game's features, I think we will need to create our own spell definitions.

I am proposing a dictionary of spells. Each spell will contain the stats of that spell, including its special, unique effects encoded by functions, buffs that it provides, etc. These functions and buffs will be bundled with instructions to specified when they are used, and on which targets (similar to the events system that OCTGN has). For instance, a creature might have an effect that triggers when it receives flame damage, in which case any game effect that causes flame damage will see that trigger in the dictionary and call the associated function. Or it might provide a buff to all bird creatures in its zone, and will have flags indicating that the ability applies to creatures in that zone that have the bird subtype.

I have a decent idea of how to implement this, and I also think it may be possible to automate a significant portion of the work. This is definitely a Q2 project, at least, and will require a lot of work. The benefit is that it will make automating the rest of the game MUCH easier, and will reduce upkeep of code for new releases by a lot.

It may be possible to write the functions directly in the dictionary, which would be great for creatures with unique effects. No guarantees yet, though; I am still figuring this part out.

Once the new spell dictionary is created, we can see about modifying all existing functions to go through it instead of the set.xml. This will result in significant gains in speed, since we can store data in the spell dictionary in its processed form, rather than having to process it each time we want to read it. I think ideally, we should use XML data only for proxy generation.

Thoughts?

@sIKE23
Copy link
Owner

sIKE23 commented Mar 1, 2015

Get me a format for what we are wanting to do and lets start with a small set like CoK or FiF and see how that works?

@ACG8 ACG8 self-assigned this Mar 7, 2015
@ACG8
Copy link
Collaborator Author

ACG8 commented Mar 7, 2015

I'm getting close to a workable format. I think the each creature dictionary should store basic properties of the creature and also functions, tied to named events. Buffs will be handled as events that update a global "gameState" dictionary, containing all the information on the present state of the game. Each time a player makes a choice, the game state will be updated; this should greatly reduce lag because it will usually happen during a player action. The BC can then be rerouted through the gameState dictionary, as opposed to passing dictionary objects containing the attacker and defender a bunch of times (not to mention updating those between each combat step).

Here's an outline of my format idea at the moment, which may change as I figure out better ways to do this:

spellDict['cardname'] = {
Name:       <str>
GUID:       <str>
Type:       <str>       (possibly treat walls like other conjurations?)
Cost:       <int>       (use 0 and have event onComputeCost for X type costs)
Subtypes:   [<str>,...]
Traits:     [<dict>,...]    (treat attributes as traits too. X Life = Innate Life +X, X Armor = Armor +X, X Channeling = Channeling +X).
Defenses:   [<defenseDict>,...]
Actions:    [<actionDict>,...]
Damage Barrier: <attackDict>            (omit if no damage barrier)
<<Events>>  <func>
OnResolve:  (called when spell is cast and resolved successfully)
OnDestroy:  (called when spell is destroyed)
OnDoubleClick:  (called when player double-clicks on spell while face-up)
OnMarkerPlaced: (called when a marker is placed on spell)
OnTargeted: (called when spell is targeted by another spell)
OnComputeCost:  (called when the cost of this spell is computed)
OnComputeGameState: (called when the game state is computed. Buffs are encoded here)
OnUntargetedAction: (called when an untargeted TAB is called on this card)

The functions tied to events accept a dictionary as their argument, to allow for a one-argument function. Then you just unpack the dictionary in the 

function. For example:

BlueGremlinOnUntargetedAction(dict):
    source = Card(dict.get('Source'))
    <function that asks if you want to pay 1 mana to gain fast/teleport>

By default, a targeted action is a plain attack menu. Creatures with other options receive those as well via actions in the action dictionary

#Actions are things a creature can use their action for, or that a conjuration can do between action phases#
<actionDict> = {
Range:      [<int>,<int>]           (min,max. <nonetype> if non-targeted)
Target:     <str>               (options: Object,Zone,Zone Border,None; <noneType> if none)
Effect:     <func>              (attacks call attack function, others call a locally defined function
Argument:   <dict>              (dictionary-type argument appropriate to passed function)

<defenseDict> = {
Uses:       <int>               (<noneType> if infinite)
Minimum:    <int>
Restriction:    <str>='No Melee'/'No Ranged'
<<Events>>  <func>
OnGetDefenses:  (called when determining what defenses are available, e.g. Tarok's limitations
OnUse:      (called if defense is used, e.g. forcemaster's deflect)
OnSuccess:  (called if defense succeeds, e.g. spiked buckler)
OnFailure:  (called if defense fails)
}

@sIKE23 sIKE23 modified the milestone: Q2--2015 Release Mar 21, 2015
ACG8 added a commit that referenced this issue Mar 21, 2015
#183 - Made significant progress in the spell dictionary generator,
specifically with respect to parsing attacks.

#144 - Added Dissipate to list of Superlative traits

#137
ACG8 added a commit that referenced this issue Apr 7, 2015
#183 - Improvements to the set.xml parser, mainly in the handling of d12
effects and card traits.
ACG8 added a commit that referenced this issue Apr 7, 2015
#183 - Fixed some errors with the generator.

#137 - Dragonscale Hauberk
ACG8 added a commit that referenced this issue Apr 8, 2015
#183 - Now the spellDict generator is easier to use. Simply move the
set.xml file to the same location as datagenerator.py, then double click
on datagenerator.py. It will save the parsed data in "parsedCards.txt".
@ACG8
Copy link
Collaborator Author

ACG8 commented Apr 8, 2015

I decided to move the generator to the dev dropbox folder, since it isn't needed in the game files at all.

Right now, I am considering a few possible ways to handle information encoding. Maybe you can weigh in with your opinion on some of these.

  • Unique abilities will be handled with one-off functions coded specifically for that card. Obviously, I want to make as few of these functions as possible, and to reuse code as much as possible. At the moment, I am thinking of packaging functions in <function>,<argument> pairs, where <argument> is a dictionary object containing specific parameters for generic functions, allowing us to reuse general purpose functions for abilities that are used a lot, such as dice based healing. For example, suppose we have some function:
diceBasedHeal(argument)

We want Asyran Cleric to be able to call this function for 2 dice when declaring an action. So we package the following tuple in his dictionary:

...
'onSpecialAction1` : (diceBasedHeal,{Dice : 2})
...

(right now, I am thinking of handling unique creature actions as a list of special action names which is stored as a list in some dictionary parameter, e.g. specialActions: ["Healing Light"] in the case of the cleric. The names of the special actions would show up in the action list (along with attacks and generic actions like guarding), and then if one is chosen it results in calling the function associated with that onSpecialAction* entry in the spell dictionary. This is kind of a long parenthetical)

Then, when specialAction1 is called, the argument dictionary (packaged with a few other pieces of information such as the source and target of the ability) is passed to the generic diceBasedHeal function.

  • I am handling some things, such as Mage and Wall status, as subtypes, because I feel that is more appropriate. Mages and Walls are more properly creatures and conjurations (respectively), and their status as a Mage or Wall is better handled as a subtype.
  • Targeting (and possibly other things) could be handled with filters, similar to the deathMessages. This time, though, the filters would be formatted as statements, and using them would involve calling eval() on each statement. For example, if you have a spell that targets a non-mage living creature, you could do:
Target Restrictions : ["not 'Mage' in Subtype","Living in Traits","Type=='Creature'"]

Then, when running a targeting legality check, you would iterate over the (string) elements and return False if any one of them returns false under eval()

@sIKE23
Copy link
Owner

sIKE23 commented Apr 8, 2015

  1. I think that makes sense, healing via attack dice you should be able to
    roll an attack and then if it is [Healing] the target will += life instead
    of -= life am I missing something?
  2. That makes sense though I wonder how many exceptions you will have to
    put in for subtype == Mage instead of if type = Mage: type == Creature type
    of coding

3: Brilliant!

On Wed, Apr 8, 2015 at 3:06 PM, ACG8 notifications@github.com wrote:

I decided to move the generator to the dev dropbox folder, since it isn't
needed in the game files at all.

Right now, I am considering a few possible ways to handle information
encoding. Maybe you can weigh in with your opinion on some of these.

  1. Unique abilities will be handled with one-off functions coded
    specifically for that card. Obviously, I want to make as few of these
    functions as possible, and to reuse code as much as possible. At the
    moment, I am thinking of packaging functions in ,
    pairs, where is a dictionary object containing specific
    parameters for generic functions, allowing us to reuse general purpose
    functions for abilities that are used a lot, such as dice based healing.
    For example, suppose we have some function:

We want Asyran Cleric to be able to call this function for 2 dice when declaring an action. So we package the following tuple in his dictionary:

...
'onSpecialAction1` : (diceBasedHeal,{Dice : 2})
...

(right now, I am thinking of handling unique creature actions as a list of special action names which is stored as a list in some dictionary parameter, e.g. specialActions: ["Healing Light"] in the case of the cleric. The names of the special actions would show up in the action list (along with attacks and generic actions like guarding), and then if one is chosen it results in calling the function associated with that onSpecialAction* entry in the spell dictionary. This is kind of a long parenthetical)

Then, when specialAction1 is called, the argument dictionary (packaged with a few other pieces of information such as the source and target of the ability) is passed to the generic diceBasedHeal function.

  1. I am handling some things, such as Mage and Wall status, as subtypes, because I feel that is more appropriate. Mages and Walls are more properly creatures and conjurations (respectively), and their status as a Mage or Wall is better handled as a subtype.
  2. Targeting (and possibly other things) could be handled with filters, similar to the deathMessages. This time, though, the filters would be formatted as statements, and using them would involve calling eval() on each statement. For example, if you have a spell that targets a non-mage living creature, you could do:

Targets = ["not 'Mage' in Subtype","Living in Traits","Type=='Creature'"]

Then, when running a targeting legality check, you would iterate over the (string) elements and return False if any one of them returns false under eval()


Reply to this email directly or view it on GitHub
#183 (comment).

@ACG8
Copy link
Collaborator Author

ACG8 commented Apr 8, 2015

  1. I think that makes sense, healing via attack dice you should be able to
    roll an attack and then if it is [Healing] the target will += life instead
    of -= life am I missing something?

That is how I originally handled it, before I removed it from the BC. The problem with treating Heal as an attack is that attacks have a lot of baggage that comes with them, such as abilities that trigger on attacks, effects that boost attacks, etc. While I could code exceptions for all of those, I think it would be much cleaner to just make a separate function dedicated to healing effects.

  1. That makes sense though I wonder how many exceptions you will have to
    put in for subtype == Mage instead of if type = Mage: type == Creature type
    of coding

There may be some, certainly, but at the moment I already have loads of "if type in ["Creature","Mage"] checks, which just seems kind of ridiculous to me. I would much rather check if "Mage" in subtype

By the way, I intend to rewrite most of the BC from scratch to incorporate the spellDictionary. Much of the current code will be scrapped for new, better code, so building exceptions into existing code is not a major concern. The BC code should be much more concise once the spellDictionary is implemented, so it should not take as much time to rewrite it as it did originally (especially since I have a better idea of how to do it now).

@sIKE23
Copy link
Owner

sIKE23 commented Apr 8, 2015

  1. Healing - great!
  2. Mage - will have to be careful of Creature exclusions that do not
    include Mages. Otherwise you persuasive argument makes sense.
  3. I had guessed it would be easier to rewrite than mod what is there,
    especially if you embed code in the creatures spell dictionary.

On Wed, Apr 8, 2015 at 3:48 PM, ACG8 notifications@github.com wrote:

  1. I think that makes sense, healing via attack dice you should be
    able to roll an attack and then if it is [Healing] the target will += life
    instead of -= life am I missing something?

    That is how I originally handled it, before I removed it from the BC.
    The problem with treating Heal as an attack is that attacks have a lot of
    baggage that comes with them, such as abilities that trigger on attacks,
    effects that boost attacks, etc. While I could code exceptions for all of
    those, I think it would be much cleaner to just make a separate function
    dedicated to healing effects.

  2. That makes sense though I wonder how many exceptions you will have
    to put in for subtype == Mage instead of if type = Mage: type == Creature
    type of coding

    There may be some, certainly, but at the moment I already have loads of "if
    type in ["Creature","Mage"] checks, which just seems kind of ridiculous
    to me. I would much rather check if "Mage" in subtype

By the way, I intend to rewrite most of the BC from scratch to incorporate
the spellDictionary. Much of the current code will be scrapped for new,
better code, so building exceptions into existing code is not a major
concern. The BC code should be much more concise once the spellDictionary
is implemented, so it should not take as much time to rewrite it as it did
originally (especially since I have a better idea of how to do it now).


Reply to this email directly or view it on GitHub
#183 (comment).

@ACG8 ACG8 closed this as completed in 4239740 Apr 11, 2015
@ACG8
Copy link
Collaborator Author

ACG8 commented Apr 16, 2015

Oops, accidentally closed this.

@ACG8 ACG8 reopened this Apr 16, 2015
ACG8 added a commit that referenced this issue Apr 16, 2015
#183 - Began work on the code that will interpret the spelldictionary
and enable a much higher level of automation. To start with, I have
coded a guard function. I intend to test it with the guardian angel
having a unique guard function, as a proof of concept. Still a few
things that need to be worked in, of course.
ACG8 added a commit that referenced this issue Apr 21, 2015
I begin to demonstrate the use of the spellDictionary using Guardian
Angel's unique guarding-triggered ability. To try it out, just create a
Guardian Angel, press TAB while mousing over it, and then just follow
the buttons. There is no healing yet; this just demonstrates that the
spell dictionary method of storing functions in a dictionary works.
ACG8 added a commit that referenced this issue Apr 24, 2015
#183 - Now the card actions module can handle special, unique card
actions. As an example, I have coded Hand of Bim-Shalla's special
actions (they don't actually do anything at the moment; they just give
notifications to demonstrate that things work as desired).

#183 - Only creatures have "guard" as an option for their action.
ACG8 added a commit that referenced this issue Apr 25, 2015
#183 - Improved the formatting of the card actions menu. I can't get the
button text to center in a satisfying way, so I opted to left-justify it
instead.

#183 - Began work on attack module. There is a LOT to do, so don't
expect it to be finished any time soon.

Added Cleric and Adramelech as further examples.

#183 - Fixed bug that was preventing actor's name from displaying for
guard action.
ACG8 added a commit that referenced this issue May 3, 2015
#214 - Created a single function (okay, really two) and single global
variable to handle game memory of events with more flexibility than was
previously present.

#213 - Fully implemented targeting, at least for special abilities. To
see an example of how it works, do the following:

1. Create a Hand of Bim-Shalla and an Asyran Cleric
2. Press <TAB> on the hand of Bim-Shalla and select a blessing.
3. Follow the game's instructions from there.

For added fun, put a zombie token on the cleric.

Note that nothing actually happens yet; this is still just proof of
concept.

#178 - Created getTitle function, which currently does 2 things:
1. It cuts out secondary parts of a creature's name, e.g. "Adramelech,
Lord of Fire" becomes "Adramelech"
2. It puts the zombie prefix before a creature's name if zombified. For
example, an Asyran Cleric with a zombie token becomes "Zombie Asyran
Cleric"; "Adramelech" becomes "Zombie Adramelech" instead, etc. This
function should be used for in-game notifications.

#215 - Added function to compute the "Living" trait.

#183 - Further work fleshing out Hand of Bim-Shalla as an example of the
spell dictionary in action (see above)
ACG8 added a commit that referenced this issue Oct 19, 2015
Currently, it just allows Vine Snapper to guard. Expandable, though.
Eventually, this will come to encompass all of the card actions and
abilities.

Also relevant to:

#183, #213,  #214
ACG8 added a commit that referenced this issue Oct 21, 2015
Now the non-attack abilities of Asyran Cleric and Adramelech's Torment
work!

Also changed the spelldictionary access so that it doesn't give errors
for spells that aren't included.

Relevant for #176
ACG8 added a commit that referenced this issue Oct 23, 2015
@sIKE23 sIKE23 modified the milestones: Q4--2015 Release , V2 Dec 18, 2015
@sIKE23 sIKE23 removed this from the Q4--2015 Release milestone Dec 18, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants