mirror of
https://github.com/Crocmagnon/charasheet.git
synced 2024-11-05 14:23:53 +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$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="jdk" jdkName="Python 3.10 (charasheet)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PyDocumentationSettings">
|
<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 = [
|
CUSTOM_APPS = [
|
||||||
"whitenoise.runserver_nostatic", # should be first
|
"whitenoise.runserver_nostatic", # should be first
|
||||||
"common",
|
"common",
|
||||||
|
"character",
|
||||||
]
|
]
|
||||||
|
|
||||||
INSTALLED_APPS = CUSTOM_APPS + DJANGO_APPS + EXTERNAL_APPS
|
INSTALLED_APPS = CUSTOM_APPS + DJANGO_APPS + EXTERNAL_APPS
|
||||||
|
|
|
@ -1,7 +1,28 @@
|
||||||
from django.contrib.auth.models import AbstractUser
|
from django.contrib.auth.models import AbstractUser
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class User(AbstractUser):
|
class User(AbstractUser):
|
||||||
"""Default custom user model for My Awesome Project."""
|
"""Default custom user model for My Awesome Project."""
|
||||||
|
|
||||||
pass
|
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