mirror of
https://github.com/Crocmagnon/charasheet.git
synced 2024-11-05 06:13:55 +01:00
Add models
This commit is contained in:
parent
6ea9e6a997
commit
e61e70e465
21 changed files with 930 additions and 1 deletions
|
@ -16,7 +16,7 @@
|
|||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (charasheet)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
|
|
4
.idea/misc.xml
Normal file
4
.idea/misc.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (charasheet)" project-jdk-type="Python SDK" />
|
||||
</project>
|
0
src/character/__init__.py
Normal file
0
src/character/__init__.py
Normal file
122
src/character/admin.py
Normal file
122
src/character/admin.py
Normal file
|
@ -0,0 +1,122 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from character import models
|
||||
|
||||
|
||||
@admin.register(models.Capability)
|
||||
class CapabilityAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "path", "rank", "limited", "spell"]
|
||||
list_filter = ["path", "path__profile", "path__race", "rank", "limited", "spell"]
|
||||
search_fields = ["name", "description"]
|
||||
|
||||
|
||||
@admin.register(models.Path)
|
||||
class PathAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "category", "related_to"]
|
||||
list_filter = ["category"]
|
||||
search_fields = ["name"]
|
||||
fieldsets = [
|
||||
(None, {"fields": ["name"]}),
|
||||
("Related to", {"fields": ["category", ("profile", "race")]}),
|
||||
("Notes", {"fields": ["notes"]}),
|
||||
]
|
||||
|
||||
def related_to(self, instance: models.Path) -> str:
|
||||
category = models.Path.Category(instance.category)
|
||||
if category == models.Path.Category.PROFILE:
|
||||
return str(instance.profile)
|
||||
elif category == models.Path.Category.RACE:
|
||||
return str(instance.race)
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
@admin.register(models.RacialCapability)
|
||||
class RacialCapabilityAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "race"]
|
||||
list_filter = ["race"]
|
||||
search_fields = ["name", "description"]
|
||||
|
||||
|
||||
@admin.register(models.Profile)
|
||||
class ProfileAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "life_dice", "magical_strength"]
|
||||
list_filter = ["life_dice", "magical_strength"]
|
||||
search_fields = ["name"]
|
||||
|
||||
|
||||
class RacialCapabilityInline(admin.TabularInline):
|
||||
model = models.RacialCapability
|
||||
extra = 0
|
||||
|
||||
|
||||
@admin.register(models.Race)
|
||||
class RaceAdmin(admin.ModelAdmin):
|
||||
list_display = ["name"]
|
||||
search_fields = ["name"]
|
||||
inlines = [RacialCapabilityInline]
|
||||
|
||||
|
||||
@admin.register(models.Character)
|
||||
class CharacterAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "player", "race", "profile", "level"]
|
||||
list_filter = ["race", "profile"]
|
||||
search_fields = ["name", "notes"]
|
||||
|
||||
fieldsets = [
|
||||
(
|
||||
"Character",
|
||||
{"fields": ["name", "player", "profile", "level", "race"]},
|
||||
),
|
||||
("Appearance", {"fields": ["gender", "age", "height", "weight"]}),
|
||||
(
|
||||
"Abilities",
|
||||
{
|
||||
"fields": [
|
||||
("value_strength", "modifier_strength"),
|
||||
("value_dexterity", "modifier_dexterity"),
|
||||
("value_constitution", "modifier_constitution"),
|
||||
("value_intelligence", "modifier_intelligence"),
|
||||
("value_wisdom", "modifier_wisdom"),
|
||||
("value_charisma", "modifier_charisma"),
|
||||
]
|
||||
},
|
||||
),
|
||||
(
|
||||
"Fight",
|
||||
{"fields": ["initiative", "attack_melee", "attack_range", "attack_magic"]},
|
||||
),
|
||||
("Health", {"fields": ["health_max", "health_remaining"]}),
|
||||
("Defense", {"fields": ["armor", "shield", "defense_misc", "defense"]}),
|
||||
("Weapons & equipment", {"fields": ["weapons", "equipment"]}),
|
||||
("Racial", {"fields": ["racial_capability"]}),
|
||||
("Capabilities", {"fields": ["capabilities"]}),
|
||||
("Luck", {"fields": ["luck_points_max", "luck_points_remaining"]}),
|
||||
("Mana", {"fields": ["mana_max", "mana_consumed", "mana_remaining"]}),
|
||||
("Notes", {"fields": ["notes"]}),
|
||||
]
|
||||
readonly_fields = [
|
||||
"modifier_strength",
|
||||
"modifier_dexterity",
|
||||
"modifier_constitution",
|
||||
"modifier_intelligence",
|
||||
"modifier_wisdom",
|
||||
"modifier_charisma",
|
||||
"initiative",
|
||||
"attack_melee",
|
||||
"attack_range",
|
||||
"attack_magic",
|
||||
"defense",
|
||||
"mana_max",
|
||||
"mana_remaining",
|
||||
]
|
||||
filter_horizontal = [
|
||||
"capabilities",
|
||||
"weapons",
|
||||
]
|
||||
|
||||
|
||||
@admin.register(models.Weapon)
|
||||
class WeaponAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "damage"]
|
||||
search_fields = ["name", "special", "damage"]
|
6
src/character/apps.py
Normal file
6
src/character/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CharacterConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "character"
|
376
src/character/migrations/0001_initial.py
Normal file
376
src/character/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,376 @@
|
|||
# Generated by Django 4.1.2 on 2022-10-28 21:52
|
||||
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
import django.db.models.functions.text
|
||||
import django_extensions.db.fields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Capability",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=100, unique=True)),
|
||||
(
|
||||
"created",
|
||||
django_extensions.db.fields.CreationDateTimeField(
|
||||
auto_now_add=True, verbose_name="created"
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
django_extensions.db.fields.ModificationDateTimeField(
|
||||
auto_now=True, verbose_name="modified"
|
||||
),
|
||||
),
|
||||
(
|
||||
"rank",
|
||||
models.PositiveSmallIntegerField(
|
||||
validators=[
|
||||
django.core.validators.MinValueValidator(1),
|
||||
django.core.validators.MaxValueValidator(5),
|
||||
]
|
||||
),
|
||||
),
|
||||
("limited", models.BooleanField(blank=True)),
|
||||
("spell", models.BooleanField(blank=True)),
|
||||
("description", models.TextField()),
|
||||
],
|
||||
options={
|
||||
"verbose_name_plural": "Capabilities",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Profile",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=100, unique=True)),
|
||||
(
|
||||
"created",
|
||||
django_extensions.db.fields.CreationDateTimeField(
|
||||
auto_now_add=True, verbose_name="created"
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
django_extensions.db.fields.ModificationDateTimeField(
|
||||
auto_now=True, verbose_name="modified"
|
||||
),
|
||||
),
|
||||
(
|
||||
"magical_strength",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("NON", "None"),
|
||||
("INT", "Intelligence"),
|
||||
("WIS", "Wisdom"),
|
||||
("CHA", "Charisma"),
|
||||
],
|
||||
default="NON",
|
||||
max_length=3,
|
||||
),
|
||||
),
|
||||
(
|
||||
"life_dice",
|
||||
models.PositiveSmallIntegerField(
|
||||
choices=[
|
||||
(4, "D4"),
|
||||
(6, "D6"),
|
||||
(8, "D8"),
|
||||
(10, "D10"),
|
||||
(12, "D12"),
|
||||
(20, "D20"),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Race",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=100, unique=True)),
|
||||
(
|
||||
"created",
|
||||
django_extensions.db.fields.CreationDateTimeField(
|
||||
auto_now_add=True, verbose_name="created"
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
django_extensions.db.fields.ModificationDateTimeField(
|
||||
auto_now=True, verbose_name="modified"
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Weapon",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=100, unique=True)),
|
||||
(
|
||||
"created",
|
||||
django_extensions.db.fields.CreationDateTimeField(
|
||||
auto_now_add=True, verbose_name="created"
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
django_extensions.db.fields.ModificationDateTimeField(
|
||||
auto_now=True, verbose_name="modified"
|
||||
),
|
||||
),
|
||||
("damage", models.CharField(blank=True, max_length=50)),
|
||||
("special", models.TextField(blank=True)),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="RacialCapability",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=100, unique=True)),
|
||||
(
|
||||
"created",
|
||||
django_extensions.db.fields.CreationDateTimeField(
|
||||
auto_now_add=True, verbose_name="created"
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
django_extensions.db.fields.ModificationDateTimeField(
|
||||
auto_now=True, verbose_name="modified"
|
||||
),
|
||||
),
|
||||
("description", models.TextField()),
|
||||
(
|
||||
"race",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="character.race"
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name_plural": "Racial capabilities",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Path",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=100, unique=True)),
|
||||
(
|
||||
"created",
|
||||
django_extensions.db.fields.CreationDateTimeField(
|
||||
auto_now_add=True, verbose_name="created"
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
django_extensions.db.fields.ModificationDateTimeField(
|
||||
auto_now=True, verbose_name="modified"
|
||||
),
|
||||
),
|
||||
(
|
||||
"category",
|
||||
models.CharField(
|
||||
choices=[
|
||||
("profile", "Profile"),
|
||||
("race", "Race"),
|
||||
("prestige", "Prestige"),
|
||||
],
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
("notes", models.TextField()),
|
||||
(
|
||||
"profile",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="paths",
|
||||
to="character.profile",
|
||||
),
|
||||
),
|
||||
(
|
||||
"race",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="paths",
|
||||
to="character.race",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Character",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=100)),
|
||||
("level", models.PositiveSmallIntegerField()),
|
||||
(
|
||||
"gender",
|
||||
models.CharField(
|
||||
choices=[("M", "Male"), ("F", "Female"), ("O", "Other")],
|
||||
default="O",
|
||||
max_length=1,
|
||||
),
|
||||
),
|
||||
("age", models.PositiveSmallIntegerField()),
|
||||
("height", models.PositiveSmallIntegerField()),
|
||||
("weight", models.PositiveSmallIntegerField()),
|
||||
("value_strength", models.PositiveSmallIntegerField()),
|
||||
("value_dexterity", models.PositiveSmallIntegerField()),
|
||||
("value_constitution", models.PositiveSmallIntegerField()),
|
||||
("value_intelligence", models.PositiveSmallIntegerField()),
|
||||
("value_wisdom", models.PositiveSmallIntegerField()),
|
||||
("value_charisma", models.PositiveSmallIntegerField()),
|
||||
("health_max", models.PositiveSmallIntegerField()),
|
||||
("health_remaining", models.PositiveSmallIntegerField()),
|
||||
("armor", models.PositiveSmallIntegerField()),
|
||||
("shield", models.PositiveSmallIntegerField()),
|
||||
("defense_misc", models.SmallIntegerField()),
|
||||
("equipment", models.TextField()),
|
||||
("luck_points_max", models.PositiveSmallIntegerField()),
|
||||
("luck_points_remaining", models.PositiveSmallIntegerField()),
|
||||
("mana_max", models.PositiveSmallIntegerField()),
|
||||
("mana_remaining", models.PositiveSmallIntegerField()),
|
||||
("notes", models.TextField()),
|
||||
("capabilities", models.ManyToManyField(to="character.capability")),
|
||||
(
|
||||
"player",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="characters",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"profile",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="characters",
|
||||
to="character.profile",
|
||||
),
|
||||
),
|
||||
(
|
||||
"race",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="characters",
|
||||
to="character.race",
|
||||
),
|
||||
),
|
||||
(
|
||||
"racial_capability",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="characters",
|
||||
to="character.racialcapability",
|
||||
),
|
||||
),
|
||||
("weapons", models.ManyToManyField(to="character.weapon")),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="capability",
|
||||
name="path",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="character.path"
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="character",
|
||||
constraint=models.UniqueConstraint(
|
||||
django.db.models.functions.text.Lower("name"),
|
||||
models.F("player"),
|
||||
name="unique_character_player",
|
||||
),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="capability",
|
||||
constraint=models.UniqueConstraint(
|
||||
models.F("path"), models.F("rank"), name="unique_path_rank"
|
||||
),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,46 @@
|
|||
# Generated by Django 4.1.2 on 2022-10-28 21:55
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("character", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="character",
|
||||
name="capabilities",
|
||||
field=models.ManyToManyField(blank=True, to="character.capability"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="character",
|
||||
name="weapons",
|
||||
field=models.ManyToManyField(blank=True, to="character.weapon"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="path",
|
||||
name="profile",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="paths",
|
||||
to="character.profile",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="path",
|
||||
name="race",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="paths",
|
||||
to="character.race",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 4.1.2 on 2022-10-28 21:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
(
|
||||
"character",
|
||||
"0002_alter_character_capabilities_alter_character_weapons_and_more",
|
||||
),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="character",
|
||||
name="equipment",
|
||||
field=models.TextField(blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="character",
|
||||
name="notes",
|
||||
field=models.TextField(blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="path",
|
||||
name="notes",
|
||||
field=models.TextField(blank=True),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,27 @@
|
|||
# Generated by Django 4.1.2 on 2022-10-28 21:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("character", "0003_alter_character_equipment_alter_character_notes_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="character",
|
||||
name="mana_max",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="character",
|
||||
name="mana_remaining",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="character",
|
||||
name="mana_consumed",
|
||||
field=models.PositiveSmallIntegerField(default=0),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
0
src/character/migrations/__init__.py
Normal file
0
src/character/migrations/__init__.py
Normal file
1
src/character/migrations/max_migration.txt
Normal file
1
src/character/migrations/max_migration.txt
Normal file
|
@ -0,0 +1 @@
|
|||
0004_remove_character_mana_max_and_more
|
13
src/character/models/__init__.py
Normal file
13
src/character/models/__init__.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from .capabilities import Capability, Path, RacialCapability
|
||||
from .character import Character, Profile, Race
|
||||
from .equipment import Weapon
|
||||
|
||||
__all__ = [
|
||||
"Capability",
|
||||
"Path",
|
||||
"RacialCapability",
|
||||
"Character",
|
||||
"Profile",
|
||||
"Race",
|
||||
"Weapon",
|
||||
]
|
52
src/character/models/capabilities.py
Normal file
52
src/character/models/capabilities.py
Normal file
|
@ -0,0 +1,52 @@
|
|||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.db import models
|
||||
from django_extensions.db.models import TimeStampedModel
|
||||
|
||||
from common.models import UniquelyNamedModel
|
||||
|
||||
|
||||
class Path(UniquelyNamedModel, TimeStampedModel, models.Model):
|
||||
profile = models.ForeignKey(
|
||||
"character.Profile",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="paths",
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
race = models.ForeignKey(
|
||||
"character.Race",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="paths",
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
class Category(models.TextChoices):
|
||||
PROFILE = "profile", "Profile"
|
||||
RACE = "race", "Race"
|
||||
PRESTIGE = "prestige", "Prestige"
|
||||
|
||||
category = models.CharField(max_length=20, choices=Category.choices)
|
||||
notes = models.TextField(blank=True)
|
||||
|
||||
|
||||
class Capability(UniquelyNamedModel, TimeStampedModel, models.Model):
|
||||
path = models.ForeignKey("character.Path", on_delete=models.CASCADE)
|
||||
rank = models.PositiveSmallIntegerField(
|
||||
validators=[MinValueValidator(1), MaxValueValidator(5)]
|
||||
)
|
||||
limited = models.BooleanField(blank=True, null=False)
|
||||
spell = models.BooleanField(blank=True, null=False)
|
||||
description = models.TextField()
|
||||
|
||||
class Meta:
|
||||
constraints = [models.UniqueConstraint("path", "rank", name="unique_path_rank")]
|
||||
verbose_name_plural = "Capabilities"
|
||||
|
||||
|
||||
class RacialCapability(UniquelyNamedModel, TimeStampedModel, models.Model):
|
||||
race = models.ForeignKey("character.Race", on_delete=models.CASCADE)
|
||||
description = models.TextField()
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "Racial capabilities"
|
176
src/character/models/character.py
Normal file
176
src/character/models/character.py
Normal file
|
@ -0,0 +1,176 @@
|
|||
from django.db import models
|
||||
from django.db.models.functions import Lower
|
||||
from django_extensions.db.models import TimeStampedModel
|
||||
|
||||
from character.models.dice import Dice
|
||||
from common.models import UniquelyNamedModel
|
||||
|
||||
|
||||
class Profile(UniquelyNamedModel, TimeStampedModel, models.Model):
|
||||
class MagicalStrength(models.TextChoices):
|
||||
NONE = "NON", "None"
|
||||
INTELLIGENCE = "INT", "Intelligence"
|
||||
WISDOM = "WIS", "Wisdom"
|
||||
CHARISMA = "CHA", "Charisma"
|
||||
|
||||
magical_strength = models.CharField(
|
||||
max_length=3, choices=MagicalStrength.choices, default=MagicalStrength.NONE
|
||||
)
|
||||
life_dice = models.PositiveSmallIntegerField(choices=Dice.choices)
|
||||
|
||||
|
||||
class Race(UniquelyNamedModel, TimeStampedModel, models.Model):
|
||||
pass
|
||||
|
||||
|
||||
def modifier(value: int) -> int:
|
||||
if 1 < value < 10:
|
||||
value -= 1
|
||||
value -= 10
|
||||
return int(value / 2)
|
||||
|
||||
|
||||
class CharacterManager(models.Manager):
|
||||
def get_by_natural_key(self, name: str, player_id: int):
|
||||
return self.get(name=name, player_id=player_id)
|
||||
|
||||
|
||||
class Character(models.Model):
|
||||
class Gender(models.TextChoices):
|
||||
MALE = "M", "Male"
|
||||
FEMALE = "F", "Female"
|
||||
OTHER = "O", "Other"
|
||||
|
||||
name = models.CharField(max_length=100)
|
||||
player = models.ForeignKey(
|
||||
"common.User", on_delete=models.CASCADE, related_name="characters"
|
||||
)
|
||||
|
||||
race = models.ForeignKey(
|
||||
"character.Race",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="characters",
|
||||
)
|
||||
profile = models.ForeignKey(
|
||||
"character.Profile",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="characters",
|
||||
)
|
||||
level = models.PositiveSmallIntegerField()
|
||||
|
||||
gender = models.CharField(
|
||||
max_length=1, choices=Gender.choices, default=Gender.OTHER
|
||||
)
|
||||
age = models.PositiveSmallIntegerField()
|
||||
height = models.PositiveSmallIntegerField()
|
||||
weight = models.PositiveSmallIntegerField()
|
||||
|
||||
value_strength = models.PositiveSmallIntegerField()
|
||||
value_dexterity = models.PositiveSmallIntegerField()
|
||||
value_constitution = models.PositiveSmallIntegerField()
|
||||
value_intelligence = models.PositiveSmallIntegerField()
|
||||
value_wisdom = models.PositiveSmallIntegerField()
|
||||
value_charisma = models.PositiveSmallIntegerField()
|
||||
|
||||
health_max = models.PositiveSmallIntegerField()
|
||||
health_remaining = models.PositiveSmallIntegerField()
|
||||
|
||||
racial_capability = models.ForeignKey(
|
||||
"character.RacialCapability",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="characters",
|
||||
)
|
||||
|
||||
weapons = models.ManyToManyField("character.Weapon", blank=True)
|
||||
|
||||
armor = models.PositiveSmallIntegerField()
|
||||
shield = models.PositiveSmallIntegerField()
|
||||
defense_misc = models.SmallIntegerField()
|
||||
|
||||
capabilities = models.ManyToManyField("character.Capability", blank=True)
|
||||
|
||||
equipment = models.TextField(blank=True)
|
||||
luck_points_max = models.PositiveSmallIntegerField()
|
||||
luck_points_remaining = models.PositiveSmallIntegerField()
|
||||
|
||||
mana_consumed = models.PositiveSmallIntegerField(default=0)
|
||||
|
||||
notes = models.TextField(blank=True)
|
||||
|
||||
objects = CharacterManager()
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
Lower("name"), "player", name="unique_character_player"
|
||||
)
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def natural_key(self):
|
||||
return (self.name, self.player_id)
|
||||
|
||||
@property
|
||||
def modifier_strength(self) -> int:
|
||||
return modifier(self.value_strength)
|
||||
|
||||
@property
|
||||
def modifier_dexterity(self) -> int:
|
||||
return modifier(self.value_dexterity)
|
||||
|
||||
@property
|
||||
def modifier_constitution(self) -> int:
|
||||
return modifier(self.value_constitution)
|
||||
|
||||
@property
|
||||
def modifier_intelligence(self) -> int:
|
||||
return modifier(self.value_intelligence)
|
||||
|
||||
@property
|
||||
def modifier_wisdom(self) -> int:
|
||||
return modifier(self.value_wisdom)
|
||||
|
||||
@property
|
||||
def modifier_charisma(self) -> int:
|
||||
return modifier(self.value_charisma)
|
||||
|
||||
@property
|
||||
def initiative(self) -> int:
|
||||
return self.value_dexterity
|
||||
|
||||
@property
|
||||
def attack_melee(self) -> int:
|
||||
return self.level + self.modifier_strength
|
||||
|
||||
@property
|
||||
def attack_range(self) -> int:
|
||||
return self.level + self.modifier_dexterity
|
||||
|
||||
@property
|
||||
def attack_magic(self) -> int:
|
||||
modifier_map = {
|
||||
Profile.MagicalStrength.INTELLIGENCE: self.modifier_intelligence,
|
||||
Profile.MagicalStrength.WISDOM: self.modifier_wisdom,
|
||||
Profile.MagicalStrength.CHARISMA: self.modifier_charisma,
|
||||
}
|
||||
|
||||
return self.level + modifier_map.get(
|
||||
Profile.MagicalStrength(self.profile.magical_strength)
|
||||
)
|
||||
|
||||
@property
|
||||
def defense(self) -> int:
|
||||
return (
|
||||
10 + self.armor + self.shield + self.modifier_dexterity + self.defense_misc
|
||||
)
|
||||
|
||||
@property
|
||||
def mana_max(self) -> int:
|
||||
return 2 * self.level + self.modifier_intelligence
|
||||
|
||||
@property
|
||||
def mana_remaining(self) -> int:
|
||||
return self.mana_max - self.mana_consumed
|
10
src/character/models/dice.py
Normal file
10
src/character/models/dice.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
from django.db import models
|
||||
|
||||
|
||||
class Dice(models.IntegerChoices):
|
||||
D4 = 4
|
||||
D6 = 6
|
||||
D8 = 8
|
||||
D10 = 10
|
||||
D12 = 12
|
||||
D20 = 20
|
9
src/character/models/equipment.py
Normal file
9
src/character/models/equipment.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from django.db import models
|
||||
from django_extensions.db.models import TimeStampedModel
|
||||
|
||||
from common.models import UniquelyNamedModel
|
||||
|
||||
|
||||
class Weapon(UniquelyNamedModel, TimeStampedModel, models.Model):
|
||||
damage = models.CharField(max_length=50, blank=True)
|
||||
special = models.TextField(blank=True)
|
0
src/character/tests/__init__.py
Normal file
0
src/character/tests/__init__.py
Normal file
33
src/character/tests/test_modifier.py
Normal file
33
src/character/tests/test_modifier.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
import pytest
|
||||
|
||||
from character.models import modifier
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,expected",
|
||||
[
|
||||
(1, -4),
|
||||
(2, -4),
|
||||
(3, -4),
|
||||
(4, -3),
|
||||
(5, -3),
|
||||
(6, -2),
|
||||
(7, -2),
|
||||
(8, -1),
|
||||
(9, -1),
|
||||
(10, 0),
|
||||
(11, 0),
|
||||
(12, 1),
|
||||
(13, 1),
|
||||
(14, 2),
|
||||
(15, 2),
|
||||
(16, 3),
|
||||
(17, 3),
|
||||
(18, 4),
|
||||
(19, 4),
|
||||
(20, 5),
|
||||
(21, 5),
|
||||
],
|
||||
)
|
||||
def test_modifier_values(value, expected):
|
||||
assert modifier(value) == expected
|
1
src/character/views.py
Normal file
1
src/character/views.py
Normal file
|
@ -0,0 +1 @@
|
|||
# Create your views here.
|
|
@ -60,6 +60,7 @@ if DEBUG_TOOLBAR:
|
|||
CUSTOM_APPS = [
|
||||
"whitenoise.runserver_nostatic", # should be first
|
||||
"common",
|
||||
"character",
|
||||
]
|
||||
|
||||
INSTALLED_APPS = CUSTOM_APPS + DJANGO_APPS + EXTERNAL_APPS
|
||||
|
|
|
@ -1,7 +1,28 @@
|
|||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
"""Default custom user model for My Awesome Project."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UniquelyNamedModelManager(models.Manager):
|
||||
def get_by_natural_key(self, name: str):
|
||||
return self.get(name=name)
|
||||
|
||||
|
||||
class UniquelyNamedModel(models.Model):
|
||||
name = models.CharField(max_length=100, blank=False, null=False, unique=True)
|
||||
objects = UniquelyNamedModelManager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def natural_key(self):
|
||||
return (self.name,)
|
||||
|
|
Loading…
Reference in a new issue