Skip to content

Commit

Permalink
Merge pull request #560 from calumbell/feature/v2-rules
Browse files Browse the repository at this point in the history
Feature: V2 `/rules` endpoint
  • Loading branch information
augustjohnson authored Oct 10, 2024
2 parents 8bffdc9 + 7d73978 commit 5c98d41
Show file tree
Hide file tree
Showing 13 changed files with 253 additions and 3 deletions.
3 changes: 3 additions & 0 deletions api_v2/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,6 @@ class LanguageAdmin(admin.ModelAdmin):
admin.site.register(CharacterClass)

admin.site.register(Environment)

admin.site.register(Rule)
admin.site.register(RuleSet)
3 changes: 2 additions & 1 deletion api_v2/management/commands/buildindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def load_v1_content(self, model):

def load_v2_content(self, model):
results = []
standard_v2_models = ['Item','Spell','Creature','CharacterClass','Race','Feat','Condition','Background','Environment']
standard_v2_models = ['Item','Spell','Creature','CharacterClass','Race','Feat','Condition','Background','Environment', 'Rule']

if model.__name__ in standard_v2_models:
for o in model.objects.all():
Expand Down Expand Up @@ -147,6 +147,7 @@ def handle(self, *args, **options):
self.load_content(v2.Condition,"v2")
self.load_content(v2.Background,"v2")
self.load_content(v2.Environment,"v2")
self.load_content(v2.Rule, "v2")

# Take the content table's current data and load it into the index.
self.load_index()
Expand Down
41 changes: 41 additions & 0 deletions api_v2/migrations/0002_rulegroup_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Generated by Django 5.1.1 on 2024-10-08 21:31

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api_v2', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='RuleGroup',
fields=[
('name', models.CharField(help_text='Name of the item.', max_length=100)),
('desc', models.TextField(help_text='Description of the game content item. Markdown.')),
('key', models.CharField(help_text='Unique key for the Document.', max_length=100, primary_key=True, serialize=False)),
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api_v2.document')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Rule',
fields=[
('name', models.CharField(help_text='Name of the item.', max_length=100)),
('desc', models.TextField(help_text='Description of the game content item. Markdown.')),
('key', models.CharField(help_text='Unique key for the Document.', max_length=100, primary_key=True, serialize=False)),
('index', models.IntegerField(default=1)),
('initialHeaderLevel', models.IntegerField(choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)], default=1)),
('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api_v2.document')),
('ruleset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rules', to='api_v2.rulegroup')),
],
options={
'abstract': False,
},
),
]
17 changes: 17 additions & 0 deletions api_v2/migrations/0003_rename_rulegroup_ruleset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.1.1 on 2024-10-08 21:38

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('api_v2', '0002_rulegroup_rule'),
]

operations = [
migrations.RenameModel(
old_name='RuleGroup',
new_name='RuleSet',
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 5.1.1 on 2024-10-08 21:49

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api_v2', '0003_rename_rulegroup_ruleset'),
]

operations = [
migrations.AlterField(
model_name='rule',
name='index',
field=models.IntegerField(default=1, help_text="A rule's position in the list of rules of the parent RuleSet"),
),
migrations.AlterField(
model_name='rule',
name='initialHeaderLevel',
field=models.IntegerField(choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)], default=1, help_text='The header level to set rule title to'),
),
migrations.AlterField(
model_name='rule',
name='ruleset',
field=models.ForeignKey(help_text='The RuleSet which this Rule belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='rules', to='api_v2.ruleset'),
),
]
4 changes: 3 additions & 1 deletion api_v2/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,6 @@

from .environment import Environment

from .speed import HasSpeed
from .speed import HasSpeed

from .rule import Rule, RuleSet
37 changes: 37 additions & 0 deletions api_v2/models/rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from django.db import models
from .abstracts import HasName, HasDescription, key_field
from .document import FromDocument

class RuleSet(HasName, HasDescription, FromDocument):
"""
The RuleSet model contains a set of Rules as part of a larger article, or
as a chapter of a book.
"""
key = key_field()


class Rule(HasName, HasDescription, FromDocument):
""""
The Rule model contains information about a single rule from a larger RuleSet.
Each Rule is typically a paragraph or two long and might contain tables.
"""

key = key_field()

index = models.IntegerField(
default=1,
help_text="A rule's position in the list of rules of the parent RuleSet"
)

ruleset = models.ForeignKey(
RuleSet,
on_delete=models.CASCADE,
related_name='rules',
help_text="The RuleSet which this Rule belongs to"
)

initialHeaderLevel = models.IntegerField(
default=1,
choices=((i, i) for i in range(1,6)),
help_text="The header level to set rule title to"
)
4 changes: 3 additions & 1 deletion api_v2/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,6 @@
from .environment import EnvironmentSerializer

from .ability import AbilitySerializer
from .ability import SkillSerializer
from .ability import SkillSerializer

from .rule import RuleSerializer, RuleSetSerializer
17 changes: 17 additions & 0 deletions api_v2/serializers/rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
""" Serializer for the Rule model """

from rest_framework import serializers

from api_v2 import models

from .abstracts import GameContentSerializer

class RuleSerializer(GameContentSerializer):
class Meta:
model = models.Rule
fields = '__all__'

class RuleSetSerializer(GameContentSerializer):
class Meta:
model = models.RuleSet
fields = ['name', 'key', 'document', 'desc', 'rules']
2 changes: 2 additions & 0 deletions api_v2/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@

from .size import SizeViewSet

from .rule import RuleViewSet, RuleSetViewSet

from .enum import get_enums

from .environment import EnvironmentViewSet
Expand Down
14 changes: 14 additions & 0 deletions api_v2/views/rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from rest_framework import viewsets

from django_filters import FilterSet

from api_v2 import models
from api_v2 import serializers

class RuleViewSet(viewsets.ReadOnlyModelViewSet):
queryset = models.Rule.objects.all()
serializer_class = serializers.RuleSerializer

class RuleSetViewSet(viewsets.ReadOnlyModelViewSet):
queryset = models.RuleSet.objects.all()
serializer_class = serializers.RuleSetSerializer
83 changes: 83 additions & 0 deletions data/v2/wizards-of-the-coast/srd/Rule.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
[
{
"model": "api_v2.ruleset",
"pk": "srd_damage-and-healing",
"fields": {
"name": "Damage and Healing",
"document": "srd",
"desc": "Injury and the risk of death are constant companions of those who explore fantasy gaming worlds. The thrust of a sword, a well-placed arrow, or a blast of flame from a srd:fireball spell all have the potential to damage, or even kill, the hardiest of creatures."
}
},
{
"model": "api_v2.rule",
"pk": "srd_damage-and-healing_hit-points",
"fields": {
"ruleset": "srd_damage-and-healing",
"document": "srd",
"name": "Hit Points",
"index": 1,
"initialHeaderLevel": 2,
"desc": "Hit points represent a combination of physical and mental durability, the will to live, and luck. Creatures with more hit points are more difficult to kill. Those with fewer hit points are more fragile.\n\nA creature's current hit points (usually just called hit points) can be any number from the creature's hit point maximum down to 0. This number changes frequently as a creature takes damage or receives healing.\n\nWhenever a creature takes damage, that damage is subtracted from its hit points. The loss of hit points has no effect on a creature's capabilities until the creature drops to 0 hit points.\n\n## Damage Rolls\n\nEach weapon, spell, and harmful monster ability specifies the damage it deals. You roll the damage die or dice, add any modifiers, and apply the damage to your target. Magic weapons, special abilities, and other factors can grant a bonus to damage. With a penalty, it is possible to deal 0 damage, but never negative damage. When attacking with a **weapon**, you add your ability modifier---the same modifier used for the attack roll---to the damage. A **spell** tells you which dice to roll for damage and whether to add any modifiers.\n\nIf a spell or other effect deals damage to **more** **than one target** at the same time, roll the damage once for all of them. For example, when a wizard casts srd:fireball or a cleric casts srd:flame-strike, the spell's damage is rolled once for all creatures caught in the blast."
}
},
{
"model": "api_v2.rule",
"pk": "srd_damage-and-healing_damage-rolls",
"fields": {
"ruleset": "srd_damage-and-healing",
"document": "srd",
"name": "Damage Rolls",
"index": 2,
"initialHeaderLevel": 2,
"desc": "Each weapon, spell, and harmful monster ability specifies the damage it deals. You roll the damage die or dice, add any modifiers, and apply the damage to your target. Magic weapons, special abilities, and other factors can grant a bonus to damage. With a penalty, it is possible to deal 0 damage, but never negative damage. When attacking with a **weapon**, you add your ability modifier---the same modifier used for the attack roll---to the damage. A **spell** tells you which dice to roll for damage and whether to add any modifiers.\n\nIf a spell or other effect deals damage to **more** **than one target** at the same time, roll the damage once for all of them. For example, when a wizard casts srd:fireball or a cleric casts srd:flame-strike, the spell's damage is rolled once for all creatures caught in the blast."
}
},
{
"model": "api_v2.rule",
"pk": "srd_damage-and-healing_critical-hits",
"fields": {
"ruleset": "srd_damage-and-healing",
"document": "srd",
"name": "Critical Hits",
"index": 3,
"initialHeaderLevel": 3,
"desc": "Each weapon, spell, and harmful monster ability specifies the damage it deals. You roll the damage die or dice, add any modifiers, and apply the damage to your target. Magic weapons, special abilities, and other factors can grant a bonus to damage. With a penalty, it is possible to deal 0 damage, but never negative damage. When attacking with a **weapon**, you add your ability modifier---the same modifier used for the attack roll---to the damage. A **spell** tells you which dice to roll for damage and whether to add any modifiers.\n\nIf a spell or other effect deals damage to **more** **than one target** at the same time, roll the damage once for all of them. For example, when a wizard casts srd:fireball or a cleric casts srd:flame-strike, the spell's damage is rolled once for all creatures caught in the blast."
}
},
{
"model": "api_v2.rule",
"pk": "srd_damage-and-healing_damage-types",
"fields": {
"ruleset": "srd_damage-and-healing",
"document": "srd",
"name": "Damage Types",
"index": 4,
"initialHeaderLevel": 3,
"desc": "Different attacks, damaging spells, and other harmful effects deal different types of damage. Damage types have no rules of their own, but other rules, such as damage resistance, rely on the types.\n\nThe damage types follow, with examples to help a GM assign a damage type to a new effect.\n\n**Acid.** The corrosive spray of a black dragon's breath and the dissolving enzymes secreted by a black pudding deal acid damage.\n\n**Bludgeoning.** Blunt force attacks---hammers, falling, constriction, and the like---deal bludgeoning damage.\n\n**Cold.** The infernal chill radiating from an ice devil's spear and the frigid blast of a white dragon's breath deal cold damage.\n\n**Fire.** Red dragons breathe fire, and many spells conjure flames to deal fire damage.\n\n**Force.** Force is pure magical energy focused into a damaging form. Most effects that deal force damage are spells, including _magic missile_ and _spiritual weapon_.\n\n**Lightning.** A _lightning bolt_ spell and a blue dragon's breath deal lightning damage.\n\n**Necrotic.** Necrotic damage, dealt by certain undead and a spell such as _chill touch_, withers matter and even the soul.\n\n**Piercing.** Puncturing and impaling attacks, including spears and monsters' bites, deal piercing damage.\n\n**Poison.** Venomous stings and the toxic gas of a green dragon's breath deal poison damage.\n\n**Psychic.** Mental abilities such as a mind flayer's psionic blast deal psychic damage.\n\n**Radiant.** Radiant damage, dealt by a cleric's _flame strike_ spell or an angel's smiting weapon, sears the flesh like fire and overloads the spirit with power.\n\n**Slashing.** Swords, axes, and monsters' claws deal slashing damage.\n\n**Thunder.** A concussive burst of sound, such as the effect of the srd:thunderwave spell, deals thunder damage."
}
},
{
"model": "api_v2.rule",
"pk": "srd_damage-and-healing_damage-types",
"fields": {
"ruleset": "srd_damage-and-healing",
"document": "srd",
"name": "Damage Types",
"index": 5,
"initialHeaderLevel": 3,
"desc": "Different attacks, damaging spells, and other harmful effects deal different types of damage. Damage types have no rules of their own, but other rules, such as damage resistance, rely on the types.\n\nThe damage types follow, with examples to help a GM assign a damage type to a new effect.\n\n**Acid.** The corrosive spray of a black dragon's breath and the dissolving enzymes secreted by a black pudding deal acid damage.\n\n**Bludgeoning.** Blunt force attacks---hammers, falling, constriction, and the like---deal bludgeoning damage.\n\n**Cold.** The infernal chill radiating from an ice devil's spear and the frigid blast of a white dragon's breath deal cold damage.\n\n**Fire.** Red dragons breathe fire, and many spells conjure flames to deal fire damage.\n\n**Force.** Force is pure magical energy focused into a damaging form. Most effects that deal force damage are spells, including _magic missile_ and _spiritual weapon_.\n\n**Lightning.** A _lightning bolt_ spell and a blue dragon's breath deal lightning damage.\n\n**Necrotic.** Necrotic damage, dealt by certain undead and a spell such as _chill touch_, withers matter and even the soul.\n\n**Piercing.** Puncturing and impaling attacks, including spears and monsters' bites, deal piercing damage.\n\n**Poison.** Venomous stings and the toxic gas of a green dragon's breath deal poison damage.\n\n**Psychic.** Mental abilities such as a mind flayer's psionic blast deal psychic damage.\n\n**Radiant.** Radiant damage, dealt by a cleric's _flame strike_ spell or an angel's smiting weapon, sears the flesh like fire and overloads the spirit with power.\n\n**Slashing.** Swords, axes, and monsters' claws deal slashing damage.\n\n**Thunder.** A concussive burst of sound, such as the effect of the srd:thunderwave spell, deals thunder damage."
}
},
{
"model": "api_v2.rule",
"pk": "srd_damage-and-healing_resistance-and-vulnerability",
"fields": {
"ruleset": "srd_damage-and-healing",
"document": "srd",
"name": "Damage Resistance and Vulnerability",
"index": 6,
"initialHeaderLevel": 2,
"desc": "Some creatures and objects are exceedingly difficult or unusually easy to hurt with certain types of damage.\n\nIf a creature or an object has **resistance** to a damage type, damage of that type is halved against it. If a creature or an object has **vulnerability** to a damage type, damage of that type is doubled against it.\n\nResistance and then vulnerability are applied after all other modifiers to damage. For example, a creature has resistance to bludgeoning damage and is hit by an attack that deals 25 bludgeoning damage. The creature is also within a magical aura that reduces all damage by 5. The 25 damage is first reduced by 5 and then halved, so the creature takes 10 damage.\n\nMultiple instances of resistance or vulnerability that affect the same damage type count as only one instance. For example, if a creature has resistance to fire damage as well as resistance to all nonmagical damage, the damage of a nonmagical fire is reduced by half against the creature, not reduced by three--- quarters."
}
}
]
2 changes: 2 additions & 0 deletions server/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
router_v2.register(r'environments',views_v2.EnvironmentViewSet)
router_v2.register(r'abilities',views_v2.AbilityViewSet)
router_v2.register(r'skills',views_v2.SkillViewSet)
router_v2.register(r'rules', views_v2.RuleViewSet)
router_v2.register(r'rulesets', views_v2.RuleSetViewSet)

router_search = routers.DefaultRouter()

Expand Down

0 comments on commit 5c98d41

Please sign in to comment.