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

610 add extra column to class table on character classes #614

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion api_v2/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ class RaceAdmin(admin.ModelAdmin):
RaceTraitInline,
]

class ClassFeatureItemInline(admin.TabularInline):
model = ClassFeatureItem

class ClassFeatureAdmin(admin.ModelAdmin):
inlines = [
ClassFeatureItemInline
]


class BackgroundBenefitInline(admin.TabularInline):
model = BackgroundBenefit
Expand Down Expand Up @@ -93,7 +101,7 @@ class LanguageAdmin(admin.ModelAdmin):
admin.site.register(Condition)

admin.site.register(ClassFeatureItem)
admin.site.register(ClassFeature)
admin.site.register(ClassFeature, admin_class=ClassFeatureAdmin)
admin.site.register(CharacterClass)

admin.site.register(Environment)
Expand Down
18 changes: 18 additions & 0 deletions api_v2/migrations/0018_characterclass_caster_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.2 on 2024-11-26 22:05

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api_v2', '0017_characterclass_saving_throws'),
]

operations = [
migrations.AddField(
model_name='characterclass',
name='caster_type',
field=models.CharField(blank=True, choices=[('FULL', 'Full'), ('HALF', 'Half'), ('NONE', 'None')], default=None, help_text='Type of caster. Options are full, half, none.', max_length=100, null=True),
),
]
19 changes: 19 additions & 0 deletions api_v2/migrations/0019_classfeatureitem_column.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 5.1.2 on 2024-12-01 15:06

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api_v2', '0018_characterclass_caster_type'),
]

operations = [
migrations.AddField(
model_name='classfeatureitem',
name='column',
field=models.BooleanField(default=False, help_text='Whether or not the field should be displayed as a column.'),
preserve_default=False,
),
]
22 changes: 22 additions & 0 deletions api_v2/migrations/0020_remove_classfeatureitem_column_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 5.1.2 on 2024-12-01 15:11

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api_v2', '0019_classfeatureitem_column'),
]

operations = [
migrations.RemoveField(
model_name='classfeatureitem',
name='column',
),
migrations.AddField(
model_name='classfeatureitem',
name='column_value',
field=models.CharField(blank=True, help_text='The value that should be displayed in the table column (where applicable).', max_length=20, null=True),
),
]
111 changes: 90 additions & 21 deletions api_v2/models/characterclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
from .abstracts import key_field
from .abilities import Ability
from .document import FromDocument
from .enums import DIE_TYPES
from .enums import DIE_TYPES, CASTER_TYPES
from drf_spectacular.utils import extend_schema_field, inline_serializer
from drf_spectacular.types import OpenApiTypes
from rest_framework import serializers



class ClassFeatureItem(models.Model):
"""This is the class for an individual class feature item, a subset of a class
feature. The name field is unused."""
feature."""

key = key_field()

Expand All @@ -24,6 +26,14 @@ class ClassFeatureItem(models.Model):
parent = models.ForeignKey('ClassFeature', on_delete=models.CASCADE)
level = models.IntegerField(validators=[MinValueValidator(0),MaxValueValidator(20)])

column_value = models.CharField(
# The value displayed in a column, or null if no value.
null=True,
blank=True,
max_length=20,
help_text='The value that should be displayed in the table column (where applicable).'
)

def __str__(self):
return "{} {} ({})".format(
self.parent.parent.name,
Expand All @@ -38,18 +48,25 @@ class ClassFeature(HasName, HasDescription, FromDocument):
parent = models.ForeignKey('CharacterClass',
on_delete=models.CASCADE)

def featureitems(self):
return self.classfeatureitem_set.exclude(column_value__isnull=False)

def columnitems(self):
return self.classfeatureitem_set.exclude(column_value__isnull=True)

def __str__(self):
return "{} ({})".format(self.name,self.parent.name)


class CharacterClass(HasName, FromDocument):
"""The model for a character class or subclass."""

subclass_of = models.ForeignKey('self',
default=None,
blank=True,
null=True,
on_delete=models.CASCADE)

hit_dice = models.CharField(
max_length=100,
default=None,
Expand All @@ -58,11 +75,18 @@ class CharacterClass(HasName, FromDocument):
choices=DIE_TYPES,
help_text='Dice notation hit dice option.')


saving_throws = models.ManyToManyField(Ability,
related_name="characterclass_saving_throws",
help_text='Saving throw proficiencies for this class.')

caster_type = models.CharField(
max_length=100,
default=None,
blank=True,
null=True,
choices=CASTER_TYPES,
help_text='Type of caster. Options are full, half, none.')

@property
@extend_schema_field(inline_serializer(
name="hit_points",
Expand Down Expand Up @@ -110,21 +134,68 @@ def features(self):
}
)
))
def levels(self):
"""Returns an array of level information for the given class."""
by_level = {}

for classfeature in self.classfeature_set.all():
for fl in classfeature.classfeatureitem_set.all():
if (str(fl.level)) not in by_level.keys():
by_level[str(fl.level)] = {}
by_level[str(fl.level)]['features'] = []

by_level[str(fl.level)]['features'].append(fl.parent.key)
by_level[str(fl.level)]['proficiency-bonus'] = self.proficiency_bonus(player_level=fl.level)
by_level[str(fl.level)]['level'] = fl.level

return by_level


def get_slots_by_player_level(self,level,caster_type):
# full is for a full caster, not including cantrips.
# full=False is for a half caster.
if level<0: # Invalid player level.
return None
if level>20: # Invalid player level.
return None

full = [[],
[0,2,0,0,0,0,0,0,0,0],
[0,3,0,0,0,0,0,0,0,0],
[0,4,2,0,0,0,0,0,0,0],
[0,4,3,0,0,0,0,0,0,0],
[0,4,3,2,0,0,0,0,0,0],
[0,4,3,3,0,0,0,0,0,0],
[0,4,3,3,1,0,0,0,0,0],
[0,4,3,3,2,0,0,0,0,0],
[0,4,3,3,3,1,0,0,0,0],
[0,4,3,3,3,2,0,0,0,0],
[0,4,3,3,3,2,1,0,0,0],
[0,4,3,3,3,2,1,0,0,0],
[0,4,3,3,3,2,1,1,0,0],
[0,4,3,3,3,2,1,1,0,0],
[0,4,3,3,3,2,1,1,1,0],
[0,4,3,3,3,2,1,1,1,0],
[0,4,3,3,3,2,1,1,1,1],
[0,4,3,3,3,3,1,1,1,1],
[0,4,3,3,3,3,2,1,1,1],
[0,4,3,3,3,3,2,2,1,1]
]

half = [[],
[0,0,0,0,0,0],
[0,2,0,0,0,0],
[0,3,0,0,0,0],
[0,3,0,0,0,0],
[0,4,2,0,0,0],
[0,4,2,0,0,0],
[0,4,3,0,0,0],
[0,4,3,0,0,0],
[0,4,3,2,0,0],
[0,4,3,2,0,0],
[0,4,3,3,0,0],
[0,4,3,3,0,0],
[0,4,3,3,1,0],
[0,4,3,3,1,0],
[0,4,3,3,2,0],
[0,4,3,3,2,0],
[0,4,3,3,3,1],
[0,4,3,3,3,1],
[0,4,3,3,3,2],
[0,4,3,3,3,2]
]

if caster_type=='FULL':
return full[level]
if caster_type=='HALF':
return half[level]
else:
return []

def proficiency_bonus(self, player_level):
# Consider as part of enums
Expand Down Expand Up @@ -152,5 +223,3 @@ def search_result_extra_fields(self):
"key": self.subclass_of.key
} if self.subclass_of else None
}

#TODO add verbose name plural
6 changes: 6 additions & 0 deletions api_v2/models/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@
("WEAPON", "Weapon"),
]

CASTER_TYPES = [
("FULL","Full"),
("HALF","Half"),
("NONE","None")
]

ACTION_TYPES = [
("ACTION", "Action"),
("REACTION","Reaction"),
Expand Down
19 changes: 16 additions & 3 deletions api_v2/serializers/characterclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,36 @@
from .abstracts import GameContentSerializer
from .document import DocumentSerializer


class ClassFeatureItemSerializer(GameContentSerializer):

class Meta:
model = models.ClassFeatureItem
fields = ['name','desc','type']
fields = ['level']

class ClassFeatureColumnItemSerializer(GameContentSerializer):
class Meta:
model = models.ClassFeatureItem
fields = ['level','column_value']

class ClassFeatureSerializer(GameContentSerializer):
key = serializers.ReadOnlyField()
featureitems = ClassFeatureItemSerializer(
many=True
)

columnitems = ClassFeatureColumnItemSerializer(
many=True
)

class Meta:
model = models.ClassFeature
fields = ['key', 'name', 'desc']
fields = ['key', 'name', 'desc','featureitems','columnitems']

class CharacterClassSerializer(GameContentSerializer):
key = serializers.ReadOnlyField()
features = ClassFeatureSerializer(
many=True, context={'request': {}})
levels = serializers.ReadOnlyField()
hit_points = serializers.ReadOnlyField()
document = DocumentSerializer()

Expand Down
Loading
Loading