Skip to content

Commit 48571f4

Browse files
authored
Creature Immunities and Resistances refactor (open5e#724)
* updated Creature model with fields to store immun/vuln/res display text * 'damages_and_resistances' field on CreatureSerializer now also contains Condition immunities * wrote script to migrating resistances and immunity display text from v1->v2 * ran get_creature_resistances_immunities_as_string.py on source data * updated tests * merged migrations from staging
1 parent 9d40cb1 commit 48571f4

22 files changed

+16708
-3506
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.1.2 on 2025-05-02 17:16
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('api_v2', '0035_document_weight_unit'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='creature',
15+
name='damage_vulnerabilities_display',
16+
field=models.CharField(blank=True, help_text="The Creature's damage vulnerabilities, formatted in a human-readable string", max_length=128, null=True),
17+
),
18+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.1.2 on 2025-05-02 17:33
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('api_v2', '0036_creature_damage_vulnerabilities_display'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='creature',
15+
name='damage_vulnerabilities_display',
16+
field=models.CharField(blank=True, default='', help_text="The Creature's damage vulnerabilities, formatted in a human-readable string", max_length=128, null=True),
17+
),
18+
]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Generated by Django 5.1.2 on 2025-05-02 17:38
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('api_v2', '0037_alter_creature_damage_vulnerabilities_display'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='creature',
15+
name='damage_immunities_display',
16+
field=models.CharField(blank=True, default='', help_text="The Creature's damage immunities, formatted as a human-readable string", max_length=128, null=True),
17+
),
18+
migrations.AddField(
19+
model_name='creature',
20+
name='damage_resistances_display',
21+
field=models.CharField(blank=True, default='', help_text="The Creature's damage resistances, formatted as a human-readable string", max_length=128, null=True),
22+
),
23+
migrations.AlterField(
24+
model_name='creature',
25+
name='damage_vulnerabilities_display',
26+
field=models.CharField(blank=True, default='', help_text="The Creature's damage vulnerabilities, formatted as a human-readable string", max_length=128, null=True),
27+
),
28+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.1.2 on 2025-05-02 18:06
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('api_v2', '0038_creature_damage_immunities_display_and_more'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='creature',
15+
name='condition_immunity_display',
16+
field=models.CharField(blank=True, default='', help_text="The Creature's condition immunities, formatted as a human-readable string", max_length=128, null=True),
17+
),
18+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.1.2 on 2025-05-02 18:10
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('api_v2', '0039_creature_condition_immunity_display'),
10+
]
11+
12+
operations = [
13+
migrations.RenameField(
14+
model_name='creature',
15+
old_name='condition_immunity_display',
16+
new_name='condition_immunities_display',
17+
),
18+
]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Generated by Django 5.1.2 on 2025-05-09 07:36
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('api_v2', '0039_alter_image_attribution'),
10+
('api_v2', '0040_rename_condition_immunity_display_creature_condition_immunities_display'),
11+
]
12+
13+
operations = [
14+
]

api_v2/models/creature.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,48 @@ class Creature(Object, HasAbilities, HasSenses, HasLanguage, HasSpeed, FromDocum
5353
help_text='The creature\'s allowed alignments.'
5454
)
5555

56+
57+
damage_vulnerabilities_display = models.CharField(
58+
max_length=128,
59+
blank=True,
60+
null=True,
61+
help_text='The Creature\'s damage vulnerabilities, formatted as a human-readable string',
62+
default=''
63+
)
64+
5665
damage_vulnerabilities = models.ManyToManyField(DamageType,
5766
related_name="creature_damage_vulnerabilities")
5867

68+
damage_immunities_display = models.CharField(
69+
max_length=128,
70+
blank=True,
71+
null=True,
72+
help_text='The Creature\'s damage immunities, formatted as a human-readable string',
73+
default=''
74+
)
75+
5976
damage_immunities = models.ManyToManyField(DamageType,
6077
related_name="creature_damage_immunities")
6178

79+
80+
damage_resistances_display = models.CharField(
81+
max_length=128,
82+
blank=True,
83+
null=True,
84+
help_text='The Creature\'s damage resistances, formatted as a human-readable string',
85+
default=''
86+
)
87+
6288
damage_resistances = models.ManyToManyField(DamageType,
6389
related_name="creature_damage_resistances")
6490

91+
condition_immunities_display = models.CharField(
92+
max_length=128,
93+
blank=True,
94+
null=True,
95+
help_text='The Creature\'s condition immunities, formatted as a human-readable string',
96+
default=''
97+
)
6598
condition_immunities = models.ManyToManyField(
6699
Condition,
67100
help_text="Conditions that this creature is immune to."

api_v2/serializers/creature.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,30 @@ class Meta:
110110
model = models.Creature
111111
fields = ['as_string', 'data']
112112

113+
class CreatureResistancesAndImmunitiesSerializer(GameContentSerializer):
114+
'''This serializer formats a Creature's damage modifier as a single obj '''
115+
damage_immunities_display = serializers.CharField()
116+
damage_immunities = DamageTypeSummarySerializer(many=True)
117+
damage_resistances_display = serializers.CharField()
118+
damage_resistances = DamageTypeSummarySerializer(many=True)
119+
damage_vulnerabilities_display = serializers.CharField()
120+
damage_vulnerabilities = DamageTypeSummarySerializer(many=True)
121+
condition_immunities_display = serializers.CharField()
122+
condition_immunities = ConditionSummarySerializer(many=True)
123+
124+
class Meta:
125+
model = models.Creature
126+
fields = [
127+
'damage_immunities_display',
128+
'damage_immunities',
129+
'damage_resistances_display',
130+
'damage_resistances',
131+
'damage_vulnerabilities_display',
132+
'damage_vulnerabilities',
133+
'condition_immunities_display',
134+
'condition_immunities',
135+
]
136+
113137
class CreatureSerializer(GameContentSerializer):
114138
'''The serializer for the Creature object.'''
115139

@@ -120,10 +144,7 @@ class CreatureSerializer(GameContentSerializer):
120144
saving_throws_all = serializers.SerializerMethodField()
121145
skill_bonuses = serializers.SerializerMethodField()
122146
skill_bonuses_all = serializers.SerializerMethodField()
123-
damage_immunities = DamageTypeSummarySerializer(many=True)
124-
damage_resistances = DamageTypeSummarySerializer(many=True)
125-
damage_vulnerabilities = DamageTypeSummarySerializer(many=True)
126-
condition_immunities = ConditionSummarySerializer(many=True)
147+
resistances_and_immunities = CreatureResistancesAndImmunitiesSerializer(source='*')
127148
actions = CreatureActionSerializer(many=True)
128149
traits = CreatureTraitSerializer(many=True, read_only=True)
129150
speed = serializers.SerializerMethodField()
@@ -168,12 +189,7 @@ class Meta:
168189
'skill_bonuses',
169190
'skill_bonuses_all',
170191
'passive_perception',
171-
'damage_immunities',
172-
'nonmagical_attack_immunity',
173-
'damage_resistances',
174-
'nonmagical_attack_resistance',
175-
'damage_vulnerabilities',
176-
'condition_immunities',
192+
'resistances_and_immunities',
177193
'normal_sight_range',
178194
'darkvision_range',
179195
'blindsight_range',

api_v2/tests/responses/TestObjects.test_creature_ancient_example.approved.json

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -179,17 +179,7 @@
179179
"category": "Monsters",
180180
"challenge_rating_decimal": "24.000",
181181
"challenge_rating_text": "24",
182-
"condition_immunities": [],
183182
"creaturesets": [],
184-
"damage_immunities": [
185-
{
186-
"key": "fire",
187-
"name": "Fire",
188-
"url": "http://localhost:8000/v2/damagetypes/fire/"
189-
}
190-
],
191-
"damage_resistances": [],
192-
"damage_vulnerabilities": [],
193183
"darkvision_range": 120.0,
194184
"document": {
195185
"gamesystem": {
@@ -249,10 +239,24 @@
249239
"wisdom": 2
250240
},
251241
"name": "Ancient Red Dragon",
252-
"nonmagical_attack_immunity": false,
253-
"nonmagical_attack_resistance": false,
254242
"normal_sight_range": 10560.0,
255243
"passive_perception": 26,
244+
"resistances_and_immunities": {
245+
"condition_immunities": [],
246+
"condition_immunities_display": "",
247+
"damage_immunities": [
248+
{
249+
"key": "fire",
250+
"name": "Fire",
251+
"url": "http://localhost:8000/v2/damagetypes/fire/"
252+
}
253+
],
254+
"damage_immunities_display": "fire",
255+
"damage_resistances": [],
256+
"damage_resistances_display": "",
257+
"damage_vulnerabilities": [],
258+
"damage_vulnerabilities_display": ""
259+
},
256260
"saving_throws": {
257261
"charisma": 13,
258262
"constitution": 16,

api_v2/tests/responses/TestObjects.test_creature_goblin_example.approved.json

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,7 @@
8282
"category": "Monsters",
8383
"challenge_rating_decimal": "0.250",
8484
"challenge_rating_text": "1/4",
85-
"condition_immunities": [],
8685
"creaturesets": [],
87-
"damage_immunities": [],
88-
"damage_resistances": [],
89-
"damage_vulnerabilities": [],
9086
"darkvision_range": 60.0,
9187
"document": {
9288
"gamesystem": {
@@ -201,10 +197,18 @@
201197
"wisdom": -1
202198
},
203199
"name": "Goblin",
204-
"nonmagical_attack_immunity": false,
205-
"nonmagical_attack_resistance": false,
206200
"normal_sight_range": 10560.0,
207201
"passive_perception": 9,
202+
"resistances_and_immunities": {
203+
"condition_immunities": [],
204+
"condition_immunities_display": "",
205+
"damage_immunities": [],
206+
"damage_immunities_display": "",
207+
"damage_resistances": [],
208+
"damage_resistances_display": "",
209+
"damage_vulnerabilities": [],
210+
"damage_vulnerabilities_display": ""
211+
},
208212
"saving_throws": {},
209213
"saving_throws_all": {
210214
"charisma": -1,

0 commit comments

Comments
 (0)