django-exalted/app/models.py

1012 lines
35 KiB
Python
Raw Permalink Normal View History

from django.db import models
2020-04-13 00:31:12 +01:00
from polymorphic.models import PolymorphicModel
2020-04-11 14:01:52 +01:00
import multiselectfield
2020-04-11 14:55:45 +01:00
from random import randint
from math import ceil
#==============================================================================#
#-------------------------------- OPTION LISTS --------------------------------#
#==============================================================================#
2020-04-11 13:50:56 +01:00
ATTRIBUTES = [
(
2020-04-11 13:56:12 +01:00
"Physical", (
2020-04-13 00:31:12 +01:00
("Strength", "Strength"),
("Dexterity", "Dexterity"),
("Stamina", "Stamina"),
2020-04-11 13:50:56 +01:00
),
),
(
2020-04-11 13:56:12 +01:00
"Social", (
2020-04-13 00:31:12 +01:00
("Charisma", "Charisma"),
("Manipulation", "Manipulation"),
("Appearance", "Appearance"),
2020-04-11 13:50:56 +01:00
),
),
(
2020-04-11 13:56:12 +01:00
"Mental", (
2020-04-13 00:31:12 +01:00
("Perception", "Perception"),
("Intelligence", "Intelligence"),
("Wits", "Wits"),
2020-04-11 13:50:56 +01:00
),
),
]
ABILITIES = [
(
2020-04-11 13:56:12 +01:00
"War", (
2020-04-13 00:31:12 +01:00
("Archery", "Archery"),
("Athletics", "Athletics"),
("Awareness", "Awareness"),
("Brawl", "Brawl"),
("Dodge", "Dodge"),
("Integrity", "Integrity"),
("Melee", "Melee"),
("Resistance", "Resistance"),
("Thrown", "Thrown"),
("War", "War"),
2020-04-11 13:50:56 +01:00
),
),
(
2020-04-11 13:56:12 +01:00
"Life", (
2020-04-13 00:31:12 +01:00
("Craft", "Craft"),
("Larceny", "Larceny"),
("Linguistics", "Linguistics"),
("Performance", "Performance"),
("Presence", "Presence"),
("Ride", "Ride"),
("Sail", "Sail"),
("Socialise", "Socialise"),
("Stealth", "Stealth"),
("Survival", "Survival"),
2020-04-11 13:50:56 +01:00
),
),
(
2020-04-11 13:56:12 +01:00
"Wisdom", (
2020-04-13 00:31:12 +01:00
("Bureaucracy", "Bureaucracy"),
("Investigation", "Investigation"),
("Lore", "Lore"),
("Medicine", "Medicine"),
("Occult", "Occult"),
2020-04-11 13:50:56 +01:00
),
),
]
ATTRIBUTE_TYPES = [
("Physical", "Physical"),
("Social", "Social"),
("Mental", "Mental"),
]
ABILITY_TYPES = [
("War", "War"),
("Life", "Life"),
("Wisdom", "Wisdom"),
("Additional", "Additional")
]
2020-04-11 13:50:56 +01:00
STATICS = [
2020-04-13 00:31:12 +01:00
("Natural Soak", "Natural Soak"),
("Armored Soak", "Armored Soak"),
("Total Soak", "Total Soak"),
("Hardness", "Hardness"),
("Parry", "Parry"),
("Evasion", "Evasion"),
("Resolve", "Resolve"),
("Guile", "Guile"),
("Rush", "Rush"),
("Disengage", "Disengage"),
("Join Battle", "Join Battle"),
2020-04-11 13:50:56 +01:00
]
CATEGORIES = [
2020-04-13 00:31:12 +01:00
("Light", "Light"),
("Medium", "Medium"),
("Heavy", "Heavy"),
2020-04-11 13:50:56 +01:00
]
2020-04-11 14:01:52 +01:00
TAGS_WEAPONS = [
2020-04-11 13:50:56 +01:00
(
2020-04-11 13:56:12 +01:00
"General", (
2020-04-13 00:31:12 +01:00
("One Handed", "One Handed"),
("Two Handed", "Two Handed"),
("Bashing", "Bashing"),
("Concealable", "Concealable"),
("Lethal", "Lethal"),
("Mounted", "Mounted"),
("Piercing", "Piercing"),
("Special", "Special"),
2020-04-11 13:50:56 +01:00
),
),
(
2020-04-11 13:56:12 +01:00
"Melee", (
2020-04-13 00:31:12 +01:00
("Melee", "Melee"),
("Balanced", "Balanced"),
("Brawl", "Brawl"),
("Chopping", "Chopping"),
("Disarming", "Disarming"),
("Flexible", "Flexible"),
("Improvised", "Improvised"),
("Grappling", "Grappling"),
("Martial Arts", "Martial Arts"),
("Natural", "Natural"),
("Reaching", "Reaching"),
("Shield", "Shield"),
("Smashing", "Smashing"),
("Worn", "Worn"),
2020-04-11 13:50:56 +01:00
),
),
(
2020-04-11 13:56:12 +01:00
"Thrown", (
2020-04-16 00:07:58 +01:00
("Thrown", "Thrown"),
2020-04-13 00:31:12 +01:00
("Occult", "Occult"),
("Cutting", "Cutting"),
("Poisonable", "Poisonable"),
("Subtle", "Subtle"),
2020-04-11 13:50:56 +01:00
),
),
(
2020-04-11 13:56:12 +01:00
"Archery", (
2020-04-13 00:31:12 +01:00
("Archery", "Archery"),
("Crossbow", "Crossbow"),
("Flame", "Flame"),
("Powerful", "Powerful"),
("Slow", "Slow"),
2020-04-11 13:50:56 +01:00
),
),
]
TAGS_ARMOR = [
2020-04-13 00:31:12 +01:00
("Buoyant", "Buoyant"),
("Concealable", "Concealable"),
("Silent", "Silent"),
2020-04-11 13:50:56 +01:00
]
INTENSITIES = [
2020-04-13 00:31:12 +01:00
("Minor", "Minor"),
("Major", "Major"),
("Defining", "Defining"),
]
2020-04-11 14:55:45 +01:00
DIE_TYPES = [
2020-04-13 00:31:12 +01:00
("None", "None"),
("Success", "Success"),
("Double", "Double"),
("Exploding / Disappearing", "Exploding / Disappearing"),
("Subtracting", "Subtracting")
]
CHARM_TYPES = [
("Permanent", "Permanent"),
("Simple", "Simple"),
("Reflexive", "Reflexive"),
("Suplemental", "Suplemental"),
]
CHARM_DURATIONS = [
("", ""),
("One Round", "One Round"),
("One Scene", "One Scene"),
("Indefinite", "Indefinite"),
]
CHARM_KEYWORDS = [
(
"Charms", (
("Advantage", "Advantage"),
("Attack-Action", "Attack-Action"),
("Counterattack", "Counterattack"),
("Form", "Form"),
("Group", "Group"),
("Mute", "Mute"),
("Perilous", "Perilous"),
("Once Per Scene", "Once Per Scene"),
("Once Per Day", "Once Per Day"),
("Once Per Story", "Once Per Story"),
("Once Per Season", "Once Per Season"),
("Pilot", "Pilot"),
("Post-Roll", "Post-Roll"),
("Psyche", "Psyche"),
("Quickshot", "Quickshot"),
)
),
(
"Evocations", (
("Dissonant", "Dissonant"),
("Resonant", "Resonant"),
)
),
]
CASTES_SOLAR = [
("Dawn", "Dawn"),
("Zenith", "Zenith"),
("Twilight", "Twilight"),
("Night", "Night"),
("Eclipse", "Eclipse"),
]
CASTES_LUNAR = [
("Full Moon", "Full Moon"),
("Changing Moon", "Changing Moon"),
("No Moon", "No Moon"),
("Castless", "Castless")
2020-04-11 14:55:45 +01:00
]
2020-04-16 00:07:58 +01:00
SHAPE_SIZES = [
("Normal", "Normal"),
]
SHAPE_TYPES = [
("Human", "Human"),
("Animal", "Animal"),
]
#==============================================================================#
#------------------------------- CUSTOM MODELS --------------------------------#
#==============================================================================#
class NameField(models.CharField):
def __init__(self, *args, **kwargs):
kwargs['verbose_name'] = "Name"
kwargs['blank'] = False
kwargs['max_length'] = 100
super().__init__(*args, **kwargs)
class DescriptionField(models.TextField):
def __init__(self, *args, **kwargs):
kwargs['verbose_name'] = "Description"
kwargs['blank'] = True
kwargs['max_length'] = 1000
super().__init__(*args, **kwargs)
class DotField(models.IntegerField):
def __init__(self, verbose_name, *args, **kwargs):
kwargs['verbose_name'] = verbose_name
kwargs['blank'] = False
kwargs['default'] = 0
super().__init__(*args, **kwargs)
class SingleChoiceField(models.CharField):
def __init__(self, verbose_name, choices, *args, **kwargs):
kwargs['verbose_name'] = verbose_name
kwargs['choices'] = choices
kwargs['blank'] = True
kwargs['max_length'] = 100
super().__init__(*args, **kwargs)
2020-04-11 14:01:52 +01:00
class MultiChoiceField(multiselectfield.MultiSelectField):
def __init__(self, verbose_name, choices, *args, **kwargs):
kwargs['verbose_name'] = verbose_name
kwargs['choices'] = choices
kwargs['blank'] = True
kwargs['max_length'] = 100
super().__init__(*args, **kwargs)
class NamedIntegerField(models.IntegerField):
def __init__(self, verbose_name, desc=None, *args, **kwargs):
kwargs['verbose_name'] = verbose_name
kwargs['help_text'] = desc
kwargs['blank'] = False
kwargs['default'] = 0
super().__init__(*args, **kwargs)
class NamedCharField(models.CharField):
def __init__(self, verbose_name, *args, **kwargs):
kwargs['verbose_name'] = verbose_name
kwargs['blank'] = False
kwargs['max_length'] = 100
super().__init__(*args, **kwargs)
2020-04-11 15:15:05 +01:00
class DieField(multiselectfield.MultiSelectField):
2020-04-11 14:55:45 +01:00
def __init__(self, verbose_name, number, default, *args, **kwargs):
self.number = number
kwargs['verbose_name'] = verbose_name
kwargs['default'] = default
kwargs['choices'] = DIE_TYPES
kwargs['blank'] = False
kwargs['max_length'] = 100
super().__init__(*args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
kwargs['number'] = self.number
return name, path, args, kwargs
class NamedBooleanField(models.BooleanField):
def __init__(self, verbose_name, default=False, *args, **kwargs):
kwargs['verbose_name'] = verbose_name
kwargs['default'] = default
kwargs['blank'] = False
super().__init__(*args, **kwargs)
class NamedForeignKeyField(models.ForeignKey):
2020-04-11 15:29:21 +01:00
def __init__(self, verbose_name, *args, **kwargs):
kwargs['verbose_name'] = verbose_name
kwargs['on_delete'] = models.CASCADE
kwargs['blank'] = True
kwargs['null'] = True
super().__init__(*args, **kwargs)
class NamedManyToManyField(models.ManyToManyField):
def __init__(self, verbose_name, *args, **kwargs):
kwargs['verbose_name'] = verbose_name
kwargs['blank'] = True
super().__init__(*args, **kwargs)
class NamedOneToOneField(models.OneToOneField):
def __init__(self, verbose_name, *args, **kwargs):
kwargs['verbose_name'] = verbose_name
kwargs['on_delete'] = models.CASCADE
kwargs['blank'] = True
kwargs['null'] = True
super().__init__(*args, **kwargs)
2020-04-11 14:55:45 +01:00
#==============================================================================#
#-------------------------------- DICE ROLLING --------------------------------#
#==============================================================================#
2020-04-16 20:12:02 +01:00
class RollConfiguration(PolymorphicModel):
def __str__(self):
return self.name
name = NameField()
successesAuto = NamedIntegerField("Auto-Successes")
2020-04-11 15:15:05 +01:00
r01 = DieField("1s", 1, ["NONE"])
r02 = DieField("2s", 2, ["NONE"])
r03 = DieField("3s", 3, ["NONE"])
r04 = DieField("4s", 4, ["NONE"])
r05 = DieField("5s", 5, ["NONE"])
r06 = DieField("6s", 6, ["NONE"])
r07 = DieField("7s", 7, ["SUCCESS"])
r08 = DieField("8s", 8, ["SUCCESS"])
r09 = DieField("9s", 9, ["SUCCESS"])
r10 = DieField("10s", 10, ["SUCCESS", "DOUBLE"])
2020-04-11 14:55:45 +01:00
def roll(self, pool=1, successesStunt=0):
2020-04-11 14:55:45 +01:00
listDice = [
self.r01,
self.r02,
self.r03,
self.r04,
self.r05,
self.r06,
self.r07,
self.r08,
self.r09,
self.r10,
]
2020-04-11 15:18:32 +01:00
listSuccess, listDouble, listExplodingDisappearing, listSubtracting = [], [], [], []
2020-04-11 14:55:45 +01:00
for die in listDice:
2020-04-11 15:15:05 +01:00
if "SUCCESS" in die:
2020-04-11 14:55:45 +01:00
listSuccess.append(die.number)
2020-04-11 15:15:05 +01:00
if "DOUBLE" in die:
2020-04-11 14:55:45 +01:00
listDouble.append(die.number)
2020-04-11 15:15:05 +01:00
if "EXPLODING_DISAPPEARING" in die:
listExplodingDisappearing.append(die.number)
if "SUBTRACTING" in die:
2020-04-11 14:55:45 +01:00
listSubtracting.append(die.number)
successes = self.successesAuto + successesStunt
2020-04-11 14:55:45 +01:00
listExploded, listDisappeared = [], []
listRoll = [0 for die in range(pool)]
while 0 in listRoll:
for die in listRoll:
if die == 0:
die = randint(1, 10)
2020-04-11 15:15:05 +01:00
if die in listExplodingDisappearing:
if die in listSuccess:
listExploded.append(die)
else:
listDisappeared.append(die)
2020-04-11 14:55:45 +01:00
die = 0
for die in listRoll + listExploded:
if die in listSuccess:
successes += 1
2020-04-11 15:15:05 +01:00
if die in listDouble:
successes += 1
if die in listSubtracting:
2020-04-11 14:55:45 +01:00
successes -= 1
if successes < 0:
successes = 0
botch = (successes == 0) and (1 in listRoll)
return successes, botch, listRoll, listExploded, listDisappeared
#==============================================================================#
#--------------------------------- PROPERTIES ---------------------------------#
#==============================================================================#
class PropertyBase(PolymorphicModel):
name = NameField()
description = DescriptionField()
class PropertyAttribute(PropertyBase):
propertyType = SingleChoiceField("Type", ATTRIBUTE_TYPES)
core = NamedBooleanField("Core?")
class PropertyAbility(PropertyBase):
propertyType = SingleChoiceField("Type", ABILITY_TYPES)
class PropertyStatic(PropertyBase):
propertyType = None
#==============================================================================#
#--------------------------------- MODIFIERS ----------------------------------#
#==============================================================================#
2020-04-16 20:12:02 +01:00
class ModifierBase(PolymorphicModel):
value = NamedIntegerField("Modifier Value")
2020-04-16 20:12:02 +01:00
class ModifierAttribute(ModifierBase):
def __str__(self):
2020-04-13 00:31:12 +01:00
return "{} [{}]".format(self.keyword, self.value)
2020-04-13 00:31:12 +01:00
keyword = SingleChoiceField("Attribute", ATTRIBUTES)
2020-04-16 20:12:02 +01:00
class ModifierAbility(ModifierBase):
def __str__(self):
2020-04-13 00:31:12 +01:00
return "{} [{}]".format(self.keyword, self.value)
2020-04-13 00:31:12 +01:00
keyword = SingleChoiceField("Ability", ABILITIES)
2020-04-16 20:12:02 +01:00
class ModifierStatic(ModifierBase):
def __str__(self):
2020-04-13 00:31:12 +01:00
return "{} [{}]".format(self.keyword, self.value)
2020-04-13 00:31:12 +01:00
keyword = SingleChoiceField("Static", STATICS)
#==============================================================================#
#--------------------------------- CHARACTERS ---------------------------------#
#==============================================================================#
2020-04-16 20:12:02 +01:00
class CharacterBase(PolymorphicModel):
def __str__(self):
return self.name
#======== MODIFIER METHODS ========#
def effectModifier(self, PropertyBase):
modifier = 0
try:
2020-04-15 20:35:45 +01:00
ownerships = [ownership for ownership in ownershipBase.objects.all() if (ownership.owner == self) and (ownership.active)]
2020-04-13 00:31:12 +01:00
for ownership in ownerships:
try:
modifier += ownership.target.modifier(keyword)
except:
pass
except:
pass
return modifier
#============ GENERAL =============#
name = NameField()
2020-04-13 00:31:12 +01:00
player = models.CharField(verbose_name="Player", max_length=100, blank=True)
concept = models.TextField(blank=True)
#=========== ATTRIBUTES ===========#
# Reverse Reference
def attributeSet(self):
try:
return [ownership.target for ownership in self.OwnershipPropertyAttribute_set.all()]
except:
return None
#=========== ABILITIES ============#
# Reverse Reference
def abilitySet(self):
2020-04-13 00:31:12 +01:00
output = []
try:
ownerships = self.ownershipPropertyAbility_set.order_by("target__name")
output.append(("War", [ownership for ownership in ownerships if ownership.target.propertyType=="War"]))
output.append(("Life", [ownership for ownership in ownerships if ownership.target.propertyType=="Life"]))
output.append(("Wisdom", [ownership for ownership in ownerships if ownership.target.propertyType=="Wisdom"]))
output.append(("Additional", [ownership for ownership in ownerships if ownership.target.propertyType=="Additional"]))
except:
output.append(("War", []))
output.append(("Life", []))
output.append(("Wisdom", []))
output.append(("Additional", []))
2020-04-13 00:31:12 +01:00
return output
#============= MERITS =============#
# Reverse relation
2020-04-13 00:31:12 +01:00
def meritSet(self):
output = []
try:
ownerships = self.ownershipMerit_set.all()
for ownership in ownerships:
output.append(ownership.target)
except:
pass
return output
#========== SPECIALITIES ==========#
# Reverse relation
def specialitySet(self):
output = []
try:
ownerships = self.ownershipSpeciality_set.all()
for ownership in ownerships:
output.append(ownership.target)
except:
pass
return output
#=========== INTIMACIES ===========#
# Reverse relation
2020-04-15 20:35:45 +01:00
# character.intimactBase_set.all()
# character.intimacyTie_set.object.all()
# character.intimacyPrincipal_set.all()
#=========== WILLPOWER ============#
willpowerMax = NamedIntegerField("Maximum Willpower")
willpower = NamedIntegerField("Current Willpower")
def dotsTriWillpower(self):
output = [0 for i in range(10)]
for i in range(10):
if i < self.willpowerMax and i >= self.willpower:
output[i] = 1
elif i < self.willpower:
output[i] = 2
return output
#=========== EXPERIENCE ===========#
experienceTotal = NamedIntegerField("Total Experience")
experience = NamedIntegerField("Current Experience")
#============ WEAPONS =============#
# Reverse relation
2020-04-13 00:31:12 +01:00
def itemWeaponSet(self):
output = []
try:
ownerships = self.ownershipItemWeapon_set.all()
for ownership in ownerships:
output.append(ownership.target)
except:
pass
return output
#============= ARMOR ==============#
# Reverse relation
2020-04-13 00:31:12 +01:00
def itemArmorSet(self):
output = []
try:
ownerships = self.ownershipItemArmor_set.all()
for ownership in ownerships:
output.append(ownership.target)
except:
pass
return output
def armorSoak(self):
armor = None
modifier = 0
try:
2020-04-13 00:31:12 +01:00
armor = self.itemArmorSet().filter(equipped=True)
except:
pass
if armor:
for item in armor:
modifier += item.soak
return modifier
def armorHardness(self):
armor = None
modifier = 0
try:
2020-04-13 00:31:12 +01:00
armor = self.itemArmorSet().filter(equipped=True)
except:
pass
if armor:
for item in armor:
modifier += item.hardness
return modifier
def armorMobilityPenalty(self):
armor = None
modifier = 0
try:
2020-04-13 00:31:12 +01:00
armor = self.itemArmorSet().filter(equipped=True)
except:
pass
if armor:
for item in armor:
modifier += item.mobilityPenalty
return modifier
#============= ITEMS ==============#
# Reverse relation
2020-04-13 00:31:12 +01:00
def itemSet(self):
output = []
try:
ownerships = self.ownershipItem_set.all()
for ownership in ownerships:
output.append(ownership.target)
except:
pass
return output
#============ ESSENCE =============#
essence = NamedIntegerField("Essence")
def dotsEssence(self):
output = []
for i in range(self.essence):
output.append(True)
for i in range(5 - self.essence):
output.append(False)
return output
#============= HEALTH =============#
health0 = NamedIntegerField("'-0' Health Levels")
health1 = NamedIntegerField("'-1' Health Levels")
health2 = NamedIntegerField("'-2' Health Levels")
healthIndex = NamedIntegerField("Health Track Index")
def healthTrack(self):
return ["Healthy"] + ["-0" for i in range(self.health0)] + ["-1" for i in range(self.health1)] + ["-1" for i in range(self.health1)] + ["-4", "i"]
def healthLevel(self):
return self.healthTrack()[self.healthIndex]
2020-04-16 00:07:58 +01:00
def healthDots(self):
output = []
for i in range(len(self.healthTrack()) - 1):
output.append((self.healthTrack()[i+1], i < self.healthIndex))
return output
#============ STATICS =============#
# def resolve(self, speciality=None, mod=0):
# return mod + ceil((self.attributeWits() + self.abilityIntegrity()) / 2) + self.effectModifier("RESOLVE")
#
# def guile(self, speciality=None, mod=0):
# return mod + ceil((self.attributeManipulation() + self.abilitySocialize()) / 2) + self.effectModifier("GUILE")
#
# def soakNatural(self, mod=0):
# return mod + self.attributeStamina() + self.effectModifier("SOAK NATURAL")
#
# def soakArmored(self, mod=0):
# return mod + self.armorSoak()
#
# def soakTotal(self, mod=0):
# return mod + self.soakNatural() + self.soakArmored()
#
# def hardness(self, mod=0):
# return mod + self.armorHardness() + self.effectModifier("HARDNESS")
#
# def joinBattle(self, mod=0):
# return mod + self.attributeWits() + self.abilityAwareness() + 3 + self.effectModifier("JOIN BATTLE")
#
# def evasion(self, mod=0):
# return mod + ceil((self.attributeDexterity() + self.abilityDodge()) / 2) - self.armorMobilityPenalty() + self.effectModifier("EVASION")
#
# def rush(self, mod=0):
# return mod + self.attributeDexterity() + self.abilityAthletics() + self.effectModifier("RUSH")
#
# def disengage(self, mod=0):
# return mod + self.attributeDexterity() + self.abilityDodge() + self.effectModifier("DISENGAGE")
2020-04-16 20:12:02 +01:00
class CharacterMortal(CharacterBase):
def type(self):
return "Mortal"
class CharacterExaltBase(CharacterBase):
2020-04-13 00:31:12 +01:00
anima = models.CharField(verbose_name="Anima", max_length=100)
#============= MOTES ==============#
motesPersonalMax = NamedIntegerField("Maximum Personal Motes")
motesPersonal = NamedIntegerField("Current Personal Motes")
motesPeripheralMax = NamedIntegerField("Maximum Peripheral Motes")
motesPeripheral = NamedIntegerField("Current Peripheral Motes")
motesCommitted = NamedIntegerField("Committed Motes")
#============= LIMIT ==============#
limitTrigger = models.TextField(verbose_name="Limit Trigger", blank="False", max_length=1000)
limitBreak = NamedIntegerField("Limit Break")
def dotsLimit(self):
output = [False for i in range(10)]
for i in range(10):
output[i] = i < self.limitBreak
return output
#======= EXALTED EXPERIENCE =======#
2020-04-13 00:31:12 +01:00
experienceExaltedTotal = NamedIntegerField("Total Exalted Experience")
experienceExalted = NamedIntegerField("Current Exalted Experience")
#========== MARTIAL ARTS ==========#
# Reverse relation
def martialArtSet(self):
output = []
try:
ownerships = self.ownershipCharmMartialArt_set.all()
for ownership in ownerships:
output.append(ownership.target)
except:
pass
return output
#=========== EVOCATIONS ===========#
# Reverse relation
def evocationSet(self):
output = []
try:
ownerships = self.ownershipCharmEvocation_set.all()
for ownership in ownerships:
output.append(ownership.target)
except:
pass
return output
2020-04-16 20:12:02 +01:00
class CharacterExaltSolar(CharacterExaltBase):
2020-04-13 00:31:12 +01:00
def type(self):
return "Solar Exalt"
caste = SingleChoiceField("Solar Caste", CASTES_SOLAR)
#============= CHARMS =============#
# Reverse relation
def charmSet(self):
output = []
try:
ownerships = self.ownershipCharmSolar_set.all()
for ownership in ownerships:
output.append(ownership.target)
except:
pass
return output
#======= SUPERNAL & FAVORED =======#
abilitySupernal = SingleChoiceField("Supernal Ability", ABILITIES)
abilityFavored = MultiChoiceField("Favoured Abilities", ABILITIES)
2020-04-16 20:12:02 +01:00
class CharacterExaltLunar(CharacterExaltBase):
2020-04-13 00:31:12 +01:00
def type(self):
return "Lunar Exalt"
2020-04-13 00:31:12 +01:00
caste = SingleChoiceField("Lunar Caste", CASTES_LUNAR)
#========= SHAPESHIFTING ==========#
2020-04-13 00:31:12 +01:00
spiritShape = models.CharField(verbose_name="Spirit Shape", max_length=100)
# Reverse relation
2020-04-16 00:07:58 +01:00
def lunarShapeSet(self):
2020-04-13 00:31:12 +01:00
output = []
try:
ownerships = self.ownershipCharmLunarShape_set.all()
for ownership in ownerships:
output.append(ownership.target)
except:
pass
return output
#============= CHARMS =============#
# Reverse relation
2020-04-13 00:31:12 +01:00
def charmSet(self):
output = []
try:
ownerships = self.ownershipCharmLunar_set.all()
for ownership in ownerships:
output.append(ownership.target)
except:
pass
return output
#============ FAVORED =============#
attributeFavored = MultiChoiceField("Favoured Attributes", ATTRIBUTES)
#==============================================================================#
#----------------------------------- ITEMS ------------------------------------#
#==============================================================================#
2020-04-16 20:12:02 +01:00
class ItemBase(PolymorphicModel):
def __str__(self):
return self.name
name = NameField()
description = DescriptionField()
2020-04-16 20:12:02 +01:00
class Item(ItemBase):
pass
#==============================================================================#
#---------------------------------- WEAPONS -----------------------------------#
#==============================================================================#
2020-04-16 20:12:02 +01:00
class ItemWeaponBase(ItemBase):
2020-04-11 14:01:52 +01:00
category = SingleChoiceField("Category", CATEGORIES)
tags = MultiChoiceField("Tags", TAGS_WEAPONS)
accuracy = NamedIntegerField("Accuracy")
damage = NamedIntegerField("Damage")
defense = NamedIntegerField("Defense")
overwhelming = NamedIntegerField("Overwhelming")
attunement = NamedIntegerField("Attunement")
2020-04-16 20:12:02 +01:00
class ItemWeaponMelee(ItemWeaponBase):
def attack(self, ability, mod=0, withering=True):
if withering:
return mod + ability + self.dexterity + weapon.accuracy
else:
return mod + ability + self.dexterity
def parry(self, ability, mod=0):
if self.character.charmsActive():
mod += sum([])
return mod + ceil((self.dexterity + ability) / 2) + weapon.defense
2020-04-16 20:12:02 +01:00
class ItemWeaponRanged(ItemWeaponBase):
rangeClose = NamedIntegerField("Close Range")
rangeShort = NamedIntegerField("Short Range")
rangeMedium = NamedIntegerField("Medium Range")
rangeLong = NamedIntegerField("Long Range")
rangeExtreme = NamedIntegerField("Extreme Range")
def attack(self, ability, rangeBand, mod=0, withering=True):
rangeBand = rangeBand.lower()
if rangeBand == "close" or rangeBand == "c":
rangeModifier = self.rangeClose
elif rangeBand == "short" or rangeBand == "s":
rangeModifier = self.rangeShort
elif rangeBand == "medium" or rangeBand == "m":
rangeModifier = self.rangeMedium
elif rangeBand == "long" or rangeBand == "l":
rangeModifier = self.rangeLong
elif rangeBand == "extreme" or rangeBand == "e":
rangeModifier = self.rangeExtreme
else:
rangeModifier = 0
if withering:
return mod + rangeModifier + ability + self.dexterity + weapon.accuracy
else:
return mod + rangeModifier + ability + self.dexterity
#==============================================================================#
#----------------------------------- ARMOR ------------------------------------#
#==============================================================================#
2020-04-16 20:12:02 +01:00
class ItemArmor(ItemBase):
2020-04-11 14:03:04 +01:00
category = SingleChoiceField("Category", CATEGORIES)
tags = MultiChoiceField("Tags", TAGS_ARMOR)
soak = NamedIntegerField("Soak")
hardness = NamedIntegerField("Hardness")
mobilityPenalty = NamedIntegerField("Mobility Penalty")
attunement = NamedIntegerField("Attunement")
#==============================================================================#
2020-04-13 00:31:12 +01:00
#---------------------------------- EFFECTS -----------------------------------#
#==============================================================================#
2020-04-16 20:12:02 +01:00
class EffectBase(PolymorphicModel):
def __str__(self):
return self.name
name = NameField()
description = DescriptionField()
2020-04-16 20:12:02 +01:00
rollConfiguration = NamedManyToManyField("Roll Configurations", RollConfiguration)
modifiers = NamedManyToManyField("Modifiers", ModifierBase)
def modifier(self, keyword):
output = 0
2020-04-13 00:31:12 +01:00
for modifier in self.modifiers.all():
if keyword == modifier.keyword:
output += modifier.value
return output
2020-04-13 00:31:12 +01:00
#==============================================================================#
#----------------------------------- CHARMS -----------------------------------#
#==============================================================================#
2020-04-16 20:12:02 +01:00
class CharmBase(EffectBase):
2020-04-13 00:31:12 +01:00
levelEssence = NamedIntegerField("Essence Level")
charmType = SingleChoiceField("Charm Type", CHARM_TYPES)
duration = SingleChoiceField("Charm Duration", CHARM_DURATIONS)
keywords = MultiChoiceField("Charm Keywords", CHARM_KEYWORDS)
2020-04-16 20:12:02 +01:00
class CharmMartialArt(CharmBase):
2020-04-16 00:07:58 +01:00
def type(self):
return "Martial Art"
2020-04-13 00:31:12 +01:00
levelKey = NamedIntegerField("Martial Arts Level")
key = None
2020-04-16 20:12:02 +01:00
class CharmEvocation(CharmBase):
2020-04-16 00:07:58 +01:00
def type(self):
return "Evocation"
2020-04-13 00:31:12 +01:00
levelKey = 0
2020-04-16 20:12:02 +01:00
key = NamedForeignKeyField("Artifact", ItemBase)
2020-04-13 00:31:12 +01:00
2020-04-16 20:12:02 +01:00
class CharmSolar(CharmBase):
2020-04-16 00:07:58 +01:00
def type(self):
return "Solar"
2020-04-13 00:31:12 +01:00
levelKey = NamedIntegerField("Ability Level")
key = SingleChoiceField("Key Ability", ABILITIES)
2020-04-16 20:12:02 +01:00
class CharmLunar(CharmBase):
2020-04-16 00:07:58 +01:00
def type(self):
return "Lunar"
2020-04-13 00:31:12 +01:00
levelKey = NamedIntegerField("Attribute Level")
key = SingleChoiceField("Key Attribute", ATTRIBUTES)
2020-04-16 20:12:02 +01:00
class CharmLunarShape(CharmBase):
2020-04-13 00:31:12 +01:00
levelKey = 0
2020-04-16 00:07:58 +01:00
def type(self):
return "Lunar Shape"
2020-04-13 00:31:12 +01:00
key = None
2020-04-16 00:07:58 +01:00
size = SingleChoiceField("Size", SHAPE_SIZES)
shapeType = SingleChoiceField("Shape Type", SHAPE_TYPES)
#==============================================================================#
#----------------------------------- MERITS -----------------------------------#
#==============================================================================#
2020-04-16 20:12:02 +01:00
class Merit(EffectBase):
dots = DotField("Dots")
2020-04-16 00:07:58 +01:00
def dotsDisplay(self):
output = []
for i in range(self.dots):
output.append(True)
for i in range(5 - self.dots):
output.append(False)
return output
#==============================================================================#
#-------------------------------- SPECIALITIES --------------------------------#
#==============================================================================#
2020-04-16 20:12:02 +01:00
class Speciality(EffectBase):
2020-04-13 00:31:12 +01:00
pass
#==============================================================================#
#--------------------------------- INTIMACIES ---------------------------------#
#==============================================================================#
2020-04-16 20:12:02 +01:00
class IntimacyBase(PolymorphicModel):
def __str__(self):
return "[{}] {}".format(self.description, self.intensity)
description = DescriptionField()
intensity = SingleChoiceField("Intensity", INTENSITIES)
2020-04-16 20:12:02 +01:00
character = NamedForeignKeyField("Character", CharacterBase, related_name="intimacy_set")
2020-04-16 20:12:02 +01:00
class IntimacyTie(IntimacyBase):
target = NamedCharField("Target")
2020-04-16 20:12:02 +01:00
class IntimacyPrincipal(IntimacyBase):
pass
2020-04-13 00:31:12 +01:00
#==============================================================================#
#--------------------------------- OWNERSHIP ----------------------------------#
#==============================================================================#
2020-04-16 20:12:02 +01:00
class OwnershipBase(PolymorphicModel):
2020-04-13 00:31:12 +01:00
notes = models.TextField(verbose_name="Notes", blank=True)
active = NamedBooleanField("Active/Equipped?")
2020-04-16 20:12:02 +01:00
class OwnershipItem(OwnershipBase):
target = NamedForeignKeyField("Item", Item, related_name="ownershipItemTarget_set")
owner = NamedForeignKeyField("Owner", CharacterBase, related_name="ownershipItem_set")
class OwnershipItemWeapon(OwnershipBase):
target = NamedForeignKeyField("Weapon", ItemWeaponBase, related_name="ownershipItemWeaponTarget_set")
owner = NamedForeignKeyField("Owner", CharacterBase, related_name="ownershipItemWeapon_set")
class OwnershipItemArmor(OwnershipBase):
target = NamedForeignKeyField("Armor", ItemArmor, related_name="ownershipItemArmorTarget_set")
owner = NamedForeignKeyField("Owner", CharacterBase, related_name="ownershipItemArmor_set")
class OwnershipPropertyAttribute(OwnershipBase):
target = NamedForeignKeyField("Attribute", PropertyAttribute, related_name="ownershipPropertyAttributeTarget_set")
character = NamedForeignKeyField("Owner", CharacterBase, related_name="ownershipPropertyAttribute_set")
value = NamedIntegerField("Dots")
def dots(self):
output = []
for i in range(self.value):
output.append(True)
for i in range(5 - self.value):
output.append(False)
return output
class OwnershipPropertyAbility(OwnershipBase):
target = NamedForeignKeyField("Ability", PropertyAbility, related_name="ownershipPropertyAbilityTarget_set")
character = NamedForeignKeyField("Owner", CharacterBase, related_name="ownershipPropertyAbility_set")
value = NamedIntegerField("Dots")
def dots(self):
output = []
for i in range(self.value):
output.append(True)
for i in range(5 - self.value):
output.append(False)
return output
class OwnershipPropertyStatic(OwnershipBase):
target = NamedForeignKeyField("Static", PropertyStatic, related_name="ownershipPropertyStaticTarget_set")
character = NamedForeignKeyField("Owner", CharacterBase, related_name="ownershipPropertyStaticTarget_set")
2020-04-16 20:12:02 +01:00
class OwnershipCharmMartialArt(OwnershipBase):
target = NamedForeignKeyField("Martial Arts Charm", CharmMartialArt, related_name="ownershipCharmMartialArtTarget_set")
owner = NamedForeignKeyField("Exalted Owner", CharacterExaltBase, related_name="ownershipCharmMartialArt_set")
class OwnershipCharmEvocation(OwnershipBase):
target = NamedForeignKeyField("Evocation", CharmEvocation, related_name="ownershipCharmEvocationTarget_set")
owner = NamedForeignKeyField("Exalted Owner", CharacterExaltBase, related_name="ownershipCharmEvocation_set")
class OwnershipCharmSolar(OwnershipBase):
target = NamedForeignKeyField("Solar Charm", CharmSolar, related_name="ownershipCharmSolarTarget_set")
owner = NamedForeignKeyField("Solar Exalted Owner", CharacterExaltSolar, related_name="ownershipCharmSolar_set")
class OwnershipCharmLunar(OwnershipBase):
target = NamedForeignKeyField("Lunar Charm", CharmLunar, related_name="ownershipCharmLunarTarget_set")
owner = NamedForeignKeyField("Lunar Exalted Owner", CharacterExaltLunar, related_name="ownershipCharmLunar_set")
class OwnershipCharmLunarShape(OwnershipBase):
target = NamedForeignKeyField("Lunar Shape", CharmLunarShape, related_name="ownershipCharmLunarShapeTarget_set")
owner = NamedForeignKeyField("Lunar Exalted Owner", CharacterExaltLunar, related_name="ownershipCharmLunarShape_set")
class OwnershipMerit(OwnershipBase):
target = NamedForeignKeyField("Merit", Merit, related_name="ownershipMeritTarget_set")
owner = NamedForeignKeyField("Owner", CharacterBase, related_name="ownershipMerit_set")
class OwnershipSpeciality(OwnershipBase):
target = NamedForeignKeyField("Speciality", Speciality, related_name="ownershipSpecialityTarget_set")
owner = NamedForeignKeyField("Owner", CharacterBase, related_name="ownershipSpeciality_set")