lvisintini / imperial-assault-data

An easy-to-use collection of data and images from the Imperial Assault Game by Fantasy Flight Games.
MIT License
18 stars 6 forks source link

Surge abilities, special abilities and passive abilities in deployment cards #8

Open valeriodigregorio opened 6 years ago

valeriodigregorio commented 6 years ago

Hi, would you be open to adding at least surge abilities to deployment cards? I'm writing a skirmish calculator (something like: Han vs Vader go run a monte carlo simulation of 5000 attacks). It would benefit from having at least surge abilities into the data model. I will then take into account passives and special abilities too. If you want I can help with the job of adding such information in your model. I believe it's pointless to make my own data model and then aggregate it with your at runtime using same IDs. Let me know! Thanks!

lvisintini commented 6 years ago

@valeriodigregorio I have no issue with you helping out. In fact, quite the contrary, I would love some help.

Just raise a PR for any contribution you would like to make.

Before you dive deep into the code, lets agree on a data structure that is flexible and extendable.

Deployment cards are not the only thing using surges for bonuses and it would be good to have all those things use the same structure. The same goes for passive and special abilities.

What do you suggest doing? What data structure would make sense for your project?

valeriodigregorio commented 6 years ago

@lvisintini I believe implementation should happen incrementally as I reckon it will require a big effort. My idea at the moment is the following, in example ID 22 is Chewie (this is the JSON I currently integrate to your for debugging purpose):

  {
    "id": 22,
    "defense_pool": ["black", "white"],
    "attack_type": "ranged",
    "attack_pool": ["blue", "red", "yellow"],
    "abilities": [
      {
        "type": "surge",
        "requirements": {"surge": 1},
        "effects": {"damage": 2}
      },
      {
        "type": "surge",
        "requirements": {"surge": 1},
        "effects": {"stun": 1}
      },
      {
        "type": "surge",
        "requirements": {"surge": 1},
        "effects": {"accuracy": 2}
      },
      {
        "type": "special",
        "name": "Slam",
        "description": "Choose 1 adjacent hostile figure and roll 1 red die. That figure suffers <damage> equal to the <damage> results. Then if it is a small figure, you may push it 1 space."
      },
      {
        "type": "passive",
        "name": "Protector",
        "description": "While a friendly figure is defending, and you are adjacent to the targeted space, apply +1 <block> to the defense results. Limit 1 \"Protector\" ability used per attack."
      }
    ]
  },

Basically I would be already ok with:

Abilities names and descriptions can be taken with an automation from TabletopAdmiral webpage JS source code.

One step further, we can describe special and passive abilities in a much deeper way. With reference to Chewie's Protector ability an example would be:

      {
        "type": "passive",
        "name": "Protector",
        "description": "While a friendly figure is defending, and you are adjacent to the targeted space, apply +1 <block> to the defense results. Limit 1 \"Protector\" ability used per attack.",
        "requirements": {
          "time": {
            "target": ["self"],
            "trigger": ["while_defending"]
          },
          "range": {
            "target": ["targeted_space"],
            "distance_min": 1,
            "distance_max": 1
          }
        },
        "effects": {
          "target": ["friendly", "defender"],
          "block": 1
        },
        "limitation": {
          "per_attack" : 1
        }
      }

However this is too much for what I need to do, but it's a description of the wider vision I have on the topic. I believe an event-driven AI could be able to take actual game decisions using such detailed model. But let's keep this ideal... :) It's good to plan for smaller increments keeping in mind the big picture, though.

lvisintini commented 6 years ago

You have given this quite a lot of thought.

I agree with the incremental implementation so long as each step is complete in its implementation (no entries with missing data) and consistent across files.

I have a few comments...

I like the first implementation much better that the the second. Simple is better.

I would have the attack stats be more like:

          "attack": {
            "type": "ranged",
            "pool": ["blue", "red", "yellow"]
          }

as it is easier to set "attack": null for all the deployment cards that do not have an attack attribute (like skirmish upgrades)

I would adapt things like {"stun": 1} to something like {"effect": "stun", "amount": 1} as it is very difficult to tests for consistency across all entries if you have variable property names for JSON objects. The resulting JSON schema adaptations will remain easy as well

I don't have a proposed structure myself at the moment. Sorry.

The second more-complex implementation of special abilities is perhaps a bit too much.

There is a few deployment cards there that will share the same special abilities, and any updates in one of them should be mirrored in all the deployment cards that have the same ability.

Here you could make the case that you would want to compile all special abilities into their own data file and reference them by name (or id) in order to make the whole thing DRY

Then, you would have to have an structure for special abilities that is common to all special abilities, something that will likely going to be to difficult to implement, read and maintain.

Perhaps the best approach is to keep the implementation of the special ability outside the data set.

You could map the special ability name to the function or method that handles the behaviour in your project.

That way the special ability is referenced in the dataset, the details of the implementation existing in the individual projects that use the dataset.

mmmm.. I need to think a bit more about this but right now I'm in the office.

At any rate, I would imagine that Cannonical names and adding the missing command cards take precedence over this for the moment.

valeriodigregorio commented 6 years ago

I agree on the attack: null comment and I propose to do the same for defense although the only sub-property for it would be "pool". At least it'll look the same and you can handle defense: null for Onar: Also I don't like the stun: 1 like you.

Just to recap we can have:

In example 1 pierce and stun (Jyn Odan):

     {
       "type": "surge",
       "requirements": {"surge": 1},
       "effects": {"pierce": 1},
       "conditions": ["stun"]
     },

Or Lando:

     {
       "type": "surge",
       "requirements": {"surge": 1},
       "conditions": ["stun", "hide"]
     },

This has to be read like:

if attacker.met(requirements):
   defender.apply(effects)
   if (total_dmg_past_defense > 0):
      defender.apply(conditions)

Well defender.apply(effects) oversimplified blast, recovery and others, but you got what I mean :)