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

Monster Data is missing Armor Type #280

Closed
Redmega opened this issue Sep 24, 2020 · 32 comments
Closed

Monster Data is missing Armor Type #280

Redmega opened this issue Sep 24, 2020 · 32 comments
Assignees

Comments

@Redmega
Copy link
Contributor

Redmega commented Sep 24, 2020

The SRD includes armor types for monsters, such as:

Aboleth: 17 (natural armor)

Thug: 11 (leather armor)

The armor type is not provided along with the monster data. I propose we add it as an armor_type field.

@bagelbits
Copy link
Collaborator

I think that sounds reasonable. It will be a lot of manual entry though. But I don't think that can be avoided.

@Redmega
Copy link
Contributor Author

Redmega commented Sep 24, 2020

Might be a way to use pdf.js on the srd pdf and extract it by regex.

NAME HERE.*\n.*\n.*\n // Number of lines down should be uniform
/Armor.*Class:.*\d+.*(\(.*\))/

@bagelbits
Copy link
Collaborator

I think it might be more complicated but if you want to take a shot, feel free!

@txtsd
Copy link

txtsd commented Oct 11, 2020

Let me take care of this.

@bagelbits
Copy link
Collaborator

I'm going to repeat here what I wrote on the PR so it's on the original issue. I think all of these:

'10 in humanoid form, 11 (natural armor) in bear and hybrid form',
 '10 in humanoid form, 11 (natural armor) in boar or hybrid form',
 '11 in humanoid form, 12 (natural armor) in wolf or hybrid form',
 '14 (natural armor), 11 while prone',
 '15 with _mage armor_',
 '16 with _barkskin_',

mean that how we do AC is flawed for monsters.

@Redmega
Copy link
Contributor Author

Redmega commented Oct 12, 2020

(Reposting here as well)

armor_type here seems to just imply "Whatever's in the parentheses after the armor class."

I think there's two paths this can take: Assume the above, and submit as is (so things like "armor_type": "12 with mage armor" will exist), or figure out an object structure.

My vote would be for something like this:

{
"armor_type": null,
"armor_special": "15 with mage armor"
},
{
"armor_class": 14,
"armor_type": "natural armor",
"armor_special": "11 while prone"
}

fwiw I don't think we should have markdown italics in this field as none of the other fields have markdown (language, senses, etc)

@bagelbits
Copy link
Collaborator

bagelbits commented Oct 12, 2020

fwiw I don't think we should have markdown italics in this field as none of the other fields have markdown (language, senses, etc)

Yeah. Markdown only really makes sense in the human readable description fields.

@bagelbits
Copy link
Collaborator

I think that first one would end up being:

{
"armor_class": 12
"armor_type": "natural",
"armor_special": "15 with mage armor"
},

How would you want to handle multiple ACs like for Werebear:
'10 in humanoid form, 11 (natural armor) in bear and hybrid form',

I'm kind of leaning towards @txtsd's original approach that they suggest in their PR. An alternative approach could be:

{
  "armor": [
    {
      "armor_class": 12,
    },
    {
      "armor_class": 15,
      "notes": "with mage armor"
    }
  ]
}

{
  "armor": [
    {
      "armor_class": 14,
      "armor_type": "natural armor"
    },
    {
      "armor_class": 11,
      "notes": "while prone"
    }
  ]
}

{
  "armor": [
    {
      "armor_class": 10,
      "notes": "in humanoid form"
    },
    {
      "armor_class": 11,
      "armor_type": "natural armor",
      "notes": "in bear form"
    },
    {
      "armor_class": 11,
      "armor_type": "natural armor",
      "notes": "in hybrid form"
    }
  ]
}

@bagelbits
Copy link
Collaborator

Though I'm not 100% sold on it.

@Redmega
Copy link
Contributor Author

Redmega commented Oct 12, 2020

So would a monster without an armor type look like this?

"armor": [
    {
      "armor_class": 14,
    }
]

I could get on board with that

@bagelbits
Copy link
Collaborator

Yeah... I guess so.

@bagelbits
Copy link
Collaborator

It feels the most shape consistent, but I'm honestly open to a better suggestion.

@txtsd
Copy link

txtsd commented Oct 13, 2020

Okay I removed the markdown.

I feel like the notes key is geared toward a human reader, whereas the way I had it structured would be geared toward programmatic access. If we ever add more OGL data that is not in the SRD, we'll be able to follow the same format for whatever "conditions" they have.

@bagelbits
Copy link
Collaborator

@txtsd I think your way probably makes the most sense. It might get refined further though. But it feels like the right direction.

@txtsd
Copy link

txtsd commented Oct 14, 2020

Good. Is the armor_special key's name okay, or would you like to replace it with something else?
EDIT: Okay I see your comment on the PR. We'll go with that.

@bagelbits
Copy link
Collaborator

@txtsd Sorry that we're talking in two places. I'm not sure if this will be the correct approach but it at least gets us to the right direction.

@txtsd
Copy link

txtsd commented Oct 14, 2020

That's alright. As long as it's in the right direction.

@JaysonMendoza
Copy link

Heya, Sorry for poping in since I'm fairly new here. In reading this I would suggest we consider how the API might be used in a program, so with that being said I am with txtsd in thinking that notes should be limited to "human readable" text that is not used to provide important data.

I like the idea of the armor key holding an array of document values that describte the armor attributes. I also recognize the need to indicate that some have an associated condition or state, especially with shape changes or fighting stances that are common to many creature types.

So I would propose that we add a new key to the main creature api document that might look something like

"states" : ["humanoid","bear form","hybrid form"]

I also considered implying a default state, but I think we can just leave it at state index 0 since the DM would choose this.

This state key would represent an enumeration type declaration. I would propose that ANY subdocument may use the

"required_state"

key and include an array of states that enable the use of this document.

"armor": [
    {
      "armor_class": 10,
      "armor_type" : "natural",
      "required_state" : [ "humaniod" ]
    },
    {
      "armor_class": 11,
      "armor_type": "natural",
      "required_state" : [ "bear form","hybrid form" ]
    }
  ]

This could also work to simplify the "actions" key too

"actions": [
		{
			"name": "Multiattack",
			"desc": "In bear form, the werebear makes two claw attacks. In humanoid form, it makes two greataxe attacks. In hybrid form, it can attack like a bear or a humanoid.",
			"options": {
				"choose": 1,
				"from": [
					[
						{
							"name": "Claw",
							"required_state" : [ "bear form","hybrid form" ],
							"count": 2,
							"type": "melee"
						}
					],
					[
						{
							"name": "Greataxe",
							"required_state" : [ "bear form","hybrid form" ],
							"count": 2,
							"type": "melee"
						}
					],
					[
						{
							"name": "Greataxe",
							"required_state" : [ "hybrid form" ],
							"count": 1,
							"type": "melee"
						},
						{
							"name": "Claw",
							"required_state" : [ "hybrid form" ],
							"count": 1,
							"type": "melee"
						}
					]
				]
			},
			"damage": []
		}

This design choice would also be useful for things like fighting styles, and can help a programmer handle monsters with various states by detecting if the state array length. It can be implicit that if the states key has no value, then we don't need to check for conditions.

Just a thought, and thanks for hearing me out!

@bagelbits
Copy link
Collaborator

bagelbits commented Dec 17, 2020

Heya, Sorry for poping in since I'm fairly new here.

Please don't apologize! All opinions are welcome.

@fergcb
Copy link
Member

fergcb commented Dec 18, 2020

@drazev the approach you're describing makes sense, I think most of us agree with a lot of what you're saying. Chris is right, don't be sorry for putting your opinions out there and helping us out!

I don't know if you've read the PR linked to this issue, #297? If not, it might give you some more food for thought.

I prefer the solution we were arriving at in the #297, summarised by my last comment on the PR. I think it's more flexible and a fair bit more computer-friendly. What are your thoughts?

@ogregoire
Copy link
Collaborator

@drazev Thank you, this is indeed a good idea, and it was something that I thought about when I faced the issue, but I fear that the API would be too cluttered with "required_states" field, as you already showed. To me, it adds complexity to both the data and a data application that would use it: you'd need to filter every single field based on the form of the monster. This is why I created #304 "Split lycanthropes into each forms", so that it's basically just a new monster with just a few references to its other forms. It's much, much easier to make links between monsters than adding filters to each field.

@JaysonMendoza
Copy link

@ogregoire Actually I think I like #304 for the same reasons. It can allow a program to just get the version they need. Your idea for a forms key would be ideal if it links to the other document versions the program would also need to pull for that monster.

However, I still think you have something on API clutter too that would not necessarly be solved by taking that approach. Ideally it would be nice if the API used the same approach for any conditional form/state/style/etc. As an example this solution works for the specific problem of multi form monsters. However, monsters that just have different spell or style options that change different attributes would require a seperate solution. I just thought it may be good to take this as an opportunity to step back and review a higher level approach to a common problem of conditioal effects which could affect any element on the stat sheet

I do not know exactly what that could look like exactly. However, taking my prevous idea as a point of reference it might look something like this. Maybe take a look at what I am getting at and see if there is anything ideas that can be used?

I just took the werebear API pull and did a remodel to illustrate how things could look if some elements of the state concept were adopted to be uinversal.

The concept I was going for would generally place documents in lists, and only allow at most three levels of this.

  1. First level keys determine the high level attribute. Only unchanging values like CR will be a value.
  2. The values will generally be a list of documents. Those documents represent individual configurations or options related to that key
  3. Values to second level key will either be a base date type, or a list of documents. This list-document level is the last tier.
  4. If the second level key has a list of documents, those documents should be references to another document.
{
	"index": "werebear",
	"name": "Werebear",
	"size": "Medium",
	"type": "humanoid",
	"subtype": "human",
	"alignment": "neutral good",
	"armor_class": 10,
	"hit_points": 135,
    "hit_dice": "18d8",
    "states" : ["humanoid form","bear form","hybrid form"],
    "speed": 
        [
            {
                "required_state" : [ "humanoid form"],
                "walk": "30 ft."
            },
            {
                "required_state" : ["hybrid form"],
                "walk": "40 ft.",
                "climb": "30 ft."
            }
            ,
            {
                "required_state" : ["bear form"],
                "walk": "40 ft.",
                "climb": "30 ft."            
            }
        ],
    "stat_blocks" : 
        [
            { //No required state here means it has no condition
                "strength": 19,
                "dexterity": 10,
                "constitution": 17,
                "intelligence": 11,
                "wisdom": 12,
                "charisma": 12
            }
        ],
    "proficiencies": 
        [
            {
                "value": 7,
                "proficiency": {
                    "index": "skill-perception",
                    "name": "Skill: Perception",
                    "url": "/api/proficiencies/skill-perception"
                }
            }
        ],
    "resistance_levels" :
        [
            {   //If we wanted to make this state based, we could add "required_state" key and then list multiple
                //We should not list a key if it's empty. All libraries allow for checking if a key exists
                "damage_vulnerabilities": [],
                "damage_resistances": [],
                "damage_immunities": 
                    [
                        "bludgeoning, piercing, and slashing from nonmagical attacks not made with silvered weapons"
                    ],
                "condition_immunities": []
            }
        ],
    "senses": 
        [
            {
                "passive_perception": 17
            }
        ],
    "languages": 
        [
            {
                "required_state" : [ "hybrid form","humanoid"],
                "language": ["Common"]
            }
        ],
	"challenge_rating": 5,
	"xp": 1800,
	"special_abilities": [
		{
			"name": "Shapechanger",
			"desc": "The werebear can use its action to polymorph into a Large bear-humanoid hybrid or into a Large bear, or back into its true form, which is humanoid. Its statistics, other than its size and AC, are the same in each form. Any equipment it is wearing or carrying isn't transformed. It reverts to its true form if it dies."
		},
		{
			"name": "Keen Smell",
			"desc": "The werebear has advantage on Wisdom (Perception) checks that rely on smell."
		}
	],
	"actions": [
        {
            "required_state" : [ "bear form","hybrid form"],
            "name": "Multiattack",
            "desc": "In bear form, the werebear makes two claw attacks. In humanoid form, it makes two greataxe attacks. In hybrid form, it can attack like a bear or a humanoid.",
            "attack_options" : 
                [
                    {
                        "action" : "Claw",
                        "count" : 2
                    }   
                ]
        },
        {
            "required_state" : [ "humanoid form","hybrid form"],
            "name": "Multiattack",
            "desc": "In bear form, the werebear makes two claw attacks. In humanoid form, it makes two greataxe attacks. In hybrid form, it can attack like a bear or a humanoid.",
            "attack_options" : 
                [
                    {
                        "action" : "Greataxe",
                        "count" : 2
                    }   
                ]
        },
		{
            "required_state" : [ "bear form","hybrid form"],
            "name": "Bite (Bear or Hybrid Form Only)",
			"desc": "Melee Weapon Attack: +7 to hit, reach 5 ft., one target. Hit: 15 (2d10 + 4) piercing damage. If the target is a humanoid, it must succeed on a DC 14 Constitution saving throw or be cursed with werebear lycanthropy.",
			"attack_bonus": 7,
			"damage": [
				{
					"damage_type": {
						"index": "piercing",
						"name": "Piercing",
						"url": "/api/damage-types/piercing"
					},
					"damage_dice": "2d10+4"
				}
			]
		},
		{
            "required_state" : [ "bear form","hybrid form"],
			"name": "Claw (Bear or Hybrid Form Only)",
			"desc": "Melee Weapon Attack: +7 to hit, reach 5 ft., one target. Hit: 13 (2d8 + 4) slashing damage.",
			"attack_bonus": 7,
			"damage": [
				{
					"damage_type": {
						"index": "slashing",
						"name": "Slashing",
						"url": "/api/damage-types/slashing"
					},
					"damage_dice": "2d8+4"
				}
			]
		},
		{
            "required_state" : [ "humanoid form","hybrid form"],
            "name": "Greataxe (Humanoid or Hybrid Form Only)",
			"desc": "Melee Weapon Attack: +7 to hit, reach 5 ft., one target. Hit: 10 (1d12 + 4) slashing damage.",
			"attack_bonus": 7,
			"damage": [
				{
					"damage_type": {
						"index": "slashing",
						"name": "Slashing",
						"url": "/api/damage-types/slashing"
					},
					"damage_dice": "1d12+4"
				}
			]
		}
	],
	"url": "/api/monsters/werebear"
}

@ogregoire
Copy link
Collaborator

I understand what you mean, but the main drawback is that everything is now an array to be filtered. A monster has two forms and each has a different AC? Bam, AC is now an array for all monsters. A monster with two forms has two sets of ability scores? Bam, the ability scores and the derivative stats must now be arrays for all monsters.

Plus there seem to have very special wording like the multiattack in your example which requires weird filters and subfilters.

I understand your desire to introduce conditional data, and we already have this kind of structure in other file such as the race or the class one.

The first question we should have is what condition are we expecting, and then only we can try to find answers to those questions. But I have to admit that as of now, I don't see many conditional structures, except for lycanthropes, and I believe that my suggestion in #304 is superior.

@bagelbits
Copy link
Collaborator

I'm mostly on the same page as @ogregoire for this as well. I feel like trying to solve problems on an outlier which cause us to completely change our structure smells like premature abstraction to me. I think splitting the lycanthropes feels like a more reasonable step forward.

@JaysonMendoza
Copy link

Fair enough! #304 is a great solution for this particular problem. I do see your point that right now conditionals are not a big problem. I guess we can revisit it in the future if further expansions make them more abundant.

I see your solution also gets rid of using notes for data which may need to be used by a program which is also great.

@bagelbits
Copy link
Collaborator

@fergcb Circling back to this based on #297, would this be the type shape that we'd want now that monsters have been split into their forms?

type ArmorClass = (ArmorClassNatural | ArmorClassArmor | ArmorClassSpell | ArmorClassCondition)[];
type ArmorClassNatural = {
  type: "natural";
  value: number;
  desc?: string;
}
type ArmorClassArmor = {
  type: "armor";
  value: number;
  armor_type: APIReference; // Equipment
  desc?: string;
}
type ArmorClassSpell = {
  type: "spell";
  value: number;
  spell: APIReference; // Spell
  desc?: string;
}
type ArmorClassCondition = {
  type: "condition";
  value: number;
  condition: APIReference; // Condition
  desc?: string;
}

@fergcb
Copy link
Member

fergcb commented Dec 10, 2022

would this be the type shape that we'd want now that monsters have been split into their forms?

@bagelbits Yeah, this looks good!

@bagelbits
Copy link
Collaborator

Slight adjustment:

type ArmorClass = (ArmorClassNatural | ArmorClassArmor | ArmorClassSpell | ArmorClassCondition)[];
type ArmorClassNatural = {
  type: "natural";
  value: number;
  desc?: string;
}
type ArmorClassArmor = {
  type: "armor";
  value: number;
  armor: APIReference[]; // Equipment
  desc?: string;
}
type ArmorClassSpell = {
  type: "spell";
  value: number;
  spell: APIReference; // Spell
  desc?: string;
}
type ArmorClassCondition = {
  type: "condition";
  value: number;
  condition: APIReference; // Condition
  desc?: string;
}

@bagelbits
Copy link
Collaborator

I realized that some monsters don't have natural armor, they just have their DEX mod. So that might make more sense as a base AC:

type ArmorClass = (ArmorClassDex | ArmorClassNatural | ArmorClassArmor | ArmorClassSpell | ArmorClassCondition)[];
type ArmorClassDex = {
  type: "dex";
  value: number;
};
type ArmorClassNatural = {
  type: "natural";
  value: number;
  desc?: string;
}
type ArmorClassArmor = {
  type: "armor";
  value: number;
  armor: APIReference[]; // Equipment
  desc?: string;
}
type ArmorClassSpell = {
  type: "spell";
  value: number;
  spell: APIReference; // Spell
  desc?: string;
}
type ArmorClassCondition = {
  type: "condition";
  value: number;
  condition: APIReference; // Condition
  desc?: string;
}

@bagelbits
Copy link
Collaborator

I started a branch and am slowly converting monsters over. Feel free to cut PRs against this branch if you want to help out. Try to keep them in reviewable sized chunks.

@bagelbits bagelbits self-assigned this Dec 13, 2022
@bagelbits
Copy link
Collaborator

@Redmega @fergcb #518 Is all done and has all of the data added in. This will be a breaking change. I'll need to do the API changes as well but that can be another day.

@bagelbits
Copy link
Collaborator

JK. I wrote the API change too!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants