from django.db import models from django.contrib.auth.models import User # Create your models here. class GameSystem(models.Model): name = models.CharField(max_length=255, unique=True) description = models.TextField(null=True) def __str__(self): return self.name class GM(models.Model): gm = models.ForeignKey(User, on_delete=models.CASCADE, null=True) campaign = models.ForeignKey( "Campaign", null=True, blank=True, on_delete=models.CASCADE, related_name="campaign", ) def __str__(self): return self.campaign.name + " - " + self.gm.get_full_name() class Player(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) status = models.CharField( choices={"I": "Invited", "A": "Accepted", "D": "Declined", "R": "Requested"} ) def __str__(self): return self.user.get_full_name() + " - " + self.get_status_display() class Campaign(models.Model): name = models.CharField(max_length=255, unique=True) description = models.TextField(null=True) game_system = models.ForeignKey( GameSystem, on_delete=models.CASCADE ) gm = models.ManyToManyField("GM", related_name="GM", blank=True) # player = models.ManyToManyField('Player') def __str__(self): return self.name class CampaignPlayer(models.Model): campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE) player = models.ForeignKey( Player, on_delete=models.CASCADE ) # TODO: Check this behaviour status = models.CharField( choices={"I": "Invited", "A": "Accepted", "D": "Declined"} ) # This should be one of invited, accepted, rejected? def __str__(self): return self.campaign.name + " - " + self.player.user.get_full_name() + " - " + self.get_status_display() class CampaignNotes(models.Model): campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE) timestamp = models.DateTimeField(auto_now_add=True) notes = models.TextField(null=True) class GURPSCharacter(models.Model): uuid = models.CharField(max_length=128, unique=True) name = models.CharField(max_length=255, unique=True) player = models.ForeignKey( Player, null=True, on_delete=models.CASCADE ) campaign = models.ForeignKey( Campaign, null=True, on_delete=models.CASCADE ) details = models.JSONField() def __str__(self): return ( str(self.name) + " Player " + str(self.player) + " Campaign " + str(self.campaign) ) def adv_points(self): return sum( [ a["calc"]["points"] for a in self.details.get("advantages", []) if a["calc"]["points"] > 0 ] ) def disadv_points(self): return sum( [ a["calc"]["points"] for a in self.details.get("advantages", []) if a["calc"]["points"] < -1 ] ) def quirks_points(self): return sum( [ a["calc"]["points"] for a in self.details.get("advantages", []) if a["calc"]["points"] == -1 ] ) def attr_points(self): return sum( [a["calc"]["points"] for a in self.details["attributes"]] ) def skills_points(self): return sum( [s["points"] for s in self.details.get("skills", [])] ) def spells_points(self): return sum( [s["points"] for s in self.details.get("spells", [])] ) def race_points(self): # Are we sure? return sum([s["points"] for s in self.details.get("race", [])]) def unspent_points(self): return ( self.details["total_points"] - self.adv_points() - self.disadv_points() - self.attr_points() - self.skills_points() - self.spells_points() - self.race_points() - self.quirks_points() ) def get_primary_attr(self, attr_id): return [ a["calc"] for a in self.details["attributes"] if a["attr_id"] == attr_id ][0] def st(self): return self.get_primary_attr("st") def iq(self): return self.get_primary_attr("iq") def dx(self): return self.get_primary_attr("dx") def ht(self): return self.get_primary_attr("ht") def will(self): return self.get_primary_attr("will") def fright_check(self): return self.get_primary_attr("fright_check") def per(self): return self.get_primary_attr("per") def vision(self): return self.get_primary_attr("vision") def hearing(self): return self.get_primary_attr("hearing") def taste_smell(self): return self.get_primary_attr("taste_smell") def touch(self): return self.get_primary_attr("touch") def basic_move(self): return self.get_primary_attr("basic_move") def basic_speed(self): return self.get_primary_attr("basic_speed") def hp(self): return self.get_primary_attr("hp") def fp(self): return self.get_primary_attr("fp") def weight_carried(self): items = [ i["calc"]["extended_weight"] for i in self.details["equipment"] ] total_weight = 0 for i in items: total_weight += float(i.split()[0]) return total_weight def enc_level(self): if self.weight_carried() <= self.basic_lift(): return 0 elif self.weight_carried() <= self.basic_lift() * 2: return 1 elif self.weight_carried() <= self.basic_lift() * 3: return 2 elif self.weight_carried() <= self.basic_lift() * 6: return 3 else: return 4 def enc_levels(self): enc_level = self.enc_level() dodge = self.details["calc"]["dodge"][0] basic_move = self.basic_move()["value"] return [ { "max_load": round(self.basic_lift()), "move": basic_move, "dodge": dodge, "current": "current" * (enc_level == 0), }, { "max_load": round(self.basic_lift()) * 2, "move": basic_move - 2, "dodge": dodge - 1, "current": "current" * (enc_level == 1), }, { "max_load": round(self.basic_lift()) * 3, "move": basic_move - 3, "dodge": dodge - 2, "current": "current" * (enc_level == 2), }, { "max_load": round(self.basic_lift()) * 6, "move": basic_move - 4, "dodge": dodge - 3, "current": "current" * (enc_level == 3), }, { "max_load": round(self.basic_lift()) * 10, "move": basic_move - 5, "dodge": dodge - 4, "current": "current" * (enc_level == 4), }, ] def basic_lift(self): return float(self.details["calc"]["basic_lift"].split()[0]) def weight_unit(self): return self.details["settings"]["default_weight_units"] def lift_table(self): return [ {"value": round(self.basic_lift()), "label": "Basic Lift"}, { "value": round(self.basic_lift()) * 2, "label": "One-Handed Lift", }, { "value": round(self.basic_lift()) * 8, "label": "Two-Handed Lift", }, { "value": round(self.basic_lift()) * 12, "label": "Shove &ersand; Knock Over", }, { "value": round(self.basic_lift()) * 24, "label": "Running Shove & Knock Over", }, { "value": round(self.basic_lift()) * 15, "label": "Carry on Back", }, { "value": round(self.basic_lift()) * 50, "label": "Shift Slightly", }, ] def reaction_modifiers(self): modifiers = [] for a in self.details.get("advantages", []): if "features" in a: for f in a["features"]: if f["type"] == "reaction_bonus": modifiers.append(f) return modifiers def conditional_modifiers(self): modifiers = [] for a in self.details.get("advantages", []): if "features" in a: for f in a["features"]: if f["type"] == "conditional_modifier": modifiers.append(f) return modifiers def weapons(self): return ( [w for w in self.details["equipment"] if "weapons" in w] + [ a for a in self.details.get("advantages", []) if "weapons" in a ] + [ a for a in self.details.get("traits", []) if "weapons" in a ] + [ s for s in self.details.get("spells", []) if "weapons" in s ] ) def melee_weapons(self): mw = [] for item in self.weapons(): for weapon in item["weapons"]: if weapon["type"] == "melee_weapon": if "description" in item: name = item["description"] else: name = item["name"] mw.append( { "name": name, "usage": weapon["usage"], "skill_level": weapon["calc"].get( "level", 0 ), "parry": weapon["calc"].get("parry", "No"), "block": weapon["calc"].get("block", "No"), "damage": weapon["calc"]["damage"], "reach": weapon["calc"].get("reach", ""), "strength": weapon.get("strength", " "), } ) return mw def ranged_weapons(self): def muscle_range(wpn_range): if wpn_range.startswith("x"): ranges = wpn_range.split("/") new_ranges = [] for wpn_range in ranges: wpn_range = float(wpn_range[1:]) wpn_range = wpn_range * self.st()["value"] wpn_range = str(wpn_range) wpn_range = wpn_range[: wpn_range.index(".")] new_ranges.append(wpn_range) return "/".join(new_ranges) else: return wpn_range rw = [] for item in self.weapons(): for weapon in item["weapons"]: if weapon["type"] == "ranged_weapon": if "description" in item: name = item["description"] else: name = item["name"] rw.append( { "name": name, "bulk": weapon.get("bulk", " "), "usage": weapon.get("usage", " "), "skill_level": weapon["calc"]["level"], "damage": weapon["calc"]["damage"], "strength": weapon.get("strength", " "), "acc": weapon.get("accuracy", 0), "range": muscle_range( weapon.get("range", " ") ), "rof": weapon.get("rate_of_fire", " "), "shots": weapon.get("shots", " "), "recoil": weapon.get("recoil", " "), } ) return rw def traits(self): traits = [] for advantage in self.details.get("advantages", []): cost = advantage["calc"]["points"] name = advantage["name"] if ( "categories" in advantage and "Language" in advantage["categories"] ): levels = [ m for m in advantage["modifiers"] if "disabled" not in m ] notes = "" for level in levels: notes += f"{level['name']}" if "notes" in level: notes += f" ({level['notes']}); " else: notes += "; " notes = notes[:-2] elif "notes" in advantage: notes = advantage["notes"] elif "modifiers" in advantage: notes = [ m for m in advantage["modifiers"] if m["cost"] == cost ][0]["name"] else: notes = "" traits.append( { "name": name, "notes": notes, "points": cost, "reference": advantage["reference"], } ) return traits def spells(self): def get_casting_details(spell_details): level = spell_details["calc"]["level"] if level < 10: descr = ( "Ritual: Need both hands and both feet " "free, and must speak .Time: 2x." ) elif level < 15: descr = ( "Ritual: Must speak a few quiet words " "and make a gesture." ) elif level < 20: descr = ( "Ritual: Must speak a word or two or make " "a small gesture. May move one yard per second " "while concentrating. Cost: -1." ) elif level < 25: descr = "Ritual: None. Time: / 2 (round up). Cost: -2." elif level < 30: descr = "Ritual: None. Time: / 4 (round up). Cost: -3." else: delta = int((level - 25) / 5) power = 2 + delta # divisor = 2**power cost = 3 + delta descr = ( f"Ritual: None. Time: / { power} round up. Cost: " f"-{cost}" ) return descr spells = [] for spell in self.details.get("spells", []): notes = ( f"{get_casting_details(spell)}
Class: { spell['spell_class']}; " f"Cost: {spell['casting_cost']}; Maintain: { spell['maintenance_cost']};" f" Time: {spell['casting_time']}; Duration: { spell['duration']}; " ) spells.append( { "name": spell["name"], "college": ", ".join(spell["college"]), "level": spell["calc"]["level"], "rsl": spell["calc"]["rsl"], "points": spell["points"], "reference": spell["reference"], "notes": notes, } ) return spells def skills(self): skills = [] for skill in self.details.get("skills", []): skills.append( { "name": skill["name"], "level": skill["calc"]["level"], "rsl": skill["calc"]["rsl"], "points": skill["points"], "reference": skill["reference"], } ) return skills def equipment(self): def get_children(parent, level=1): children = [] for item_details in parent["children"]: equipment_details = { "name": item_details["description"], "uses": "", "tech_level": item_details["tech_level"], "lc": "", "value": item_details["value"], "weight": item_details["weight"], "quantity": item_details.get("quantity", 1), "ref": item_details["reference"], "ext_weight": item_details["calc"][ "extended_weight" ], "ext_value": item_details["calc"]["extended_value"], "notes": item_details.get("notes", ""), "equipped": item_details["equipped"], "level": level, } children.append(equipment_details) if "children" in item_details: children += get_children(item_details, level + 1) return children equipment = self.details["equipment"] equipment_list = [] for item in equipment: equipment_list.append( { "name": item["description"], "uses": "", "tech_level": item["tech_level"], "lc": "", "level": 0, "value": item["value"], "weight": item["weight"], "quantity": item.get("quantity", 1), "ref": item.get("reference", ""), "ext_weight": item["calc"]["extended_weight"], "ext_value": item["calc"]["extended_value"], "notes": item.get("notes", ""), "equipped": item["equipped"], } ) if "children" in item: equipment_list += get_children(item) return equipment_list def hit_locations(self): try: # v2 return self.details["settings"]["hit_locations"][ "locations" ] except KeyError: # v4 return self.details["settings"]["body_type"]["locations"]