diff --git a/firebase-rules.json b/firebase-rules.json
deleted file mode 100644
index 9836a25f..00000000
--- a/firebase-rules.json
+++ /dev/null
@@ -1,1873 +0,0 @@
-{
- "rules": {
- "campaigns": {
- "$uid": {
- ".write": "$uid === auth.uid || root.child('users').child(auth.uid).child('admin').exists()",
- ".indexOn": "private",
- "$campaignId": {
- ".validate": "newData.hasChildren(['name'])",
- "timestamp": {
- ".validate": "newData.isNumber()"
- },
- "harmless_key": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 200"
- },
- "advancement": {
- ".validate": true
- },
- "private": {
- ".validate": "newData.isBoolean()"
- },
- "background": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "hk_background": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "inventory": {
- "currency": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 4294967295"
- },
- "items": { ".validate": true }
- },
- "temporary_background": {
- "image": { ".validate": "newData.isString() && newData.val().length <= 2000" },
- "youtube": { ".validate": "newData.isString() && newData.val().length <= 2000" },
- "video": { ".validate": "newData.isString() && newData.val().length <= 2000" }
- },
- "sharing": {
- "image": { ".validate": "newData.isString() && newData.val().length <= 2000" },
- "youtube": { ".validate": "newData.isString() && newData.val().length <= 2000" },
- "message": { ".validate": "newData.isString() && newData.val().length <= 9999" }
- },
- "weather": {
- "$idx": {
- ".validate": "$idx.matches(/^(ash|fog|hail|lightning|quake|rain|sand|smoke|snow)$/)"
- }
- },
- "companions": {
- "$companionId": {
- "curHp": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- },
- "tempHp": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- },
- "ac_bonus": {
- ".validate": "newData.isNumber() && newData.val() >= -999 && newData.val() <= 999"
- },
- "maxHpMod": {
- ".validate": "newData.isNumber() && newData.val() >= -9999 && newData.val() <= 9999"
- },
- "saves": {
- "$idx": {
- ".validate": "newData.isString() && newData.val().matches(/^(succes|fail)$/)"
- }
- },
- "dead": { ".validate": "newData.isBoolean()" },
- "stable": { ".validate": "newData.isBoolean()" },
- "meters": {
- "damage": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "damageTaken": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "overkill": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "overkillTaken": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "healing": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "healingTaken": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "overhealing": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "overhealingTaken": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- }
- },
- "transformed": {
- "curHp": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- },
- "maxHp": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- },
- "ac": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- }
- },
- "$other": { ".validate": false }
- }
- },
- "players": {
- "$playerId": {
- "curHp": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- },
- "tempHp": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- },
- "ac_bonus": {
- ".validate": "newData.isNumber() && newData.val() >= -999 && newData.val() <= 999"
- },
- "maxHpMod": {
- ".validate": "newData.isNumber() && newData.val() >= -9999 && newData.val() <= 9999"
- },
- "saves": {
- "$idx": {
- ".validate": "newData.isString() && newData.val().matches(/^(succes|fail)$/)"
- }
- },
- "dead": { ".validate": "newData.isBoolean()" },
- "stable": { ".validate": "newData.isBoolean()" },
- "meters": {
- "damage": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "damageTaken": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "overkill": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "overkillTaken": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "healing": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "healingTaken": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "overhealing": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "overhealingTaken": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- }
- },
- "transformed": {
- "curHp": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- },
- "maxHp": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- },
- "ac": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- }
- },
- "$other": { ".validate": false }
- }
- },
- "player_count": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "shares": { ".validate": true },
- "$other": { ".validate": false }
- }
- },
- ".read": true
- },
- "search_campaigns": {
- ".read": true,
- "$uid": {
- ".write": "$uid === auth.uid",
- "results": {
- ".indexOn": ["name"]
- }
- }
- },
- "campaign_notes": {
- "$uid": {
- "$campaignId": {
- "$noteId": {
- "title": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "description": {
- ".validate": "newData.isString() && newData.val().length <= 1000"
- },
- "created": {
- ".validate": "newData.isNumber()"
- }
- },
- ".indexOn": ["created"]
- },
- ".read": "$uid === auth.uid",
- ".write": "$uid === auth.uid"
- }
- },
- "users": {
- "$uid": {
- "username": {
- ".write": "$uid === auth.uid || root.child('users').child(auth.uid).child('admin').exists()"
- },
- "email": {
- ".write": "$uid === auth.uid || root.child('users').child(auth.uid).child('admin').exists()"
- },
- "active_campaign": {
- ".write": "$uid === auth.uid || root.child('users').child(auth.uid).child('admin').exists()"
- },
- "admin": {
- ".write": false
- },
- "contribute": {
- ".write": "root.child('users').child(auth.uid).child('admin').exists()"
- },
- "follow": {
- ".write": "$uid === auth.uid"
- },
- "voucher": {
- ".write": "$uid === auth.uid || root.child('users').child(auth.uid).child('admin').exists()"
- },
- "patreon_email": {
- ".write": "root.child('users').child(auth.uid).child('admin').exists()"
- }
- },
- ".read": true,
- ".indexOn": ["username", "email", "admin", "contribute"]
- },
- "search_users": {
- ".read": true,
- "$uid": {
- ".write": "$uid === auth.uid"
- },
- ".indexOn": ["username", "email"]
- },
- "status": {
- ".write": true,
- ".read": true,
- ".indexOn": "state"
- },
- "posters": {
- ".write": true,
- ".read": true
- },
- "patrons": {
- ".write": true,
- ".read": true,
- ".indexOn": "email"
- },
- "new_patrons": {
- ".write": true,
- ".read": true,
- ".indexOn": "email"
- },
- "patreon_data": {
- ".write": true,
- ".read": true,
- ".indexOn": "timestamp"
- },
- "tiers": {
- ".write": true,
- ".read": true
- },
- "settings": {
- "$uid": {
- ".write": "$uid === auth.uid",
- ".read": true
- }
- },
- "players": {
- "$uid": {
- "$playerid": {
- ".write": "$uid === auth.uid || root.child('character_control').child(auth.uid).child($playerid).child('user').val() === $uid",
- ".validate": "newData.hasChildren(['player_name', 'character_name', 'maxHp', 'ac'])",
- "sync_character": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "created": {
- ".validate": "newData.isNumber()"
- },
- "updated": {
- ".validate": "newData.isNumber()"
- },
- "control": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "campaign_id": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "player_name": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "character_name": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "avatar": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "storage_avatar": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "experience": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 355000"
- },
- "level": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "maxHp": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 999"
- },
- "ac": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "spell_save_dc": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "speed": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "initiative": {
- ".validate": "newData.isNumber() && newData.val() >= -10 && newData.val() <= 99"
- },
- "strength": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "dexterity": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "constitution": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "intelligence": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "wisdom": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "charisma": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "strength-save-profficient": {
- ".validate": "newData.isBoolean()"
- },
- "dexterity-save-profficient": {
- ".validate": "newData.isBoolean()"
- },
- "constitution-save-profficient": {
- ".validate": "newData.isBoolean()"
- },
- "intelligence-save-profficient": {
- ".validate": "newData.isBoolean()"
- },
- "wisdom-save-profficient": {
- ".validate": "newData.isBoolean()"
- },
- "charisma-save-profficient": {
- ".validate": "newData.isBoolean()"
- },
- "passive_insight": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "passive_investigation": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "passive_perception": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "damage_vulnerabilities": {
- "$vulnerabilityIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- }
- },
- "damage_resistances": {
- "$resistanceIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- }
- },
- "damage_immunities": {
- "$immunityIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- }
- },
- "condition_immunities": {
- "$conditionImmunityIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(blinded|charmed|deafened|exhaustion|frightened|grappled|incapacitated|invisible|paralyzed|petrified|poisoned|prone|restrained|stunned|unconscious)$/)"
- }
- },
- "skills": {
- "$skillIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(acrobatics|animal Handling|arcana|athletics|deception|history|insight|intimidation|investigation|medicine|nature|perception|performance|persuasion|religion|sleight of Hand|stealth|survival)$/)"
- }
- },
- "skills_expertise": {
- "$skillIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(acrobatics|animal Handling|arcana|athletics|deception|history|insight|intimidation|investigation|medicine|nature|perception|performance|persuasion|religion|sleight of Hand|stealth|survival)$/)"
- }
- },
- "skills_jack_of_all_trades": {
- ".validate": "newData.isBoolean()"
- },
- "companions": {
- "$npcId": {
- ".validate": "newData.isBoolean()"
- }
- },
- "$other": { ".validate": false }
- },
- ".read": true
- },
- ".read": "root.child('users').child(auth.uid).child('admin').exists()"
- },
- "search_players": {
- ".read": true,
- "$uid": {
- ".write": "$uid === auth.uid",
- "results": {
- "$playerid": {
- ".write": "$uid === auth.uid || root.child('character_control').child(auth.uid).child($playerid).child('user').val() === $uid"
- },
- ".indexOn": ["character_name"]
- }
- }
- },
- "characters": {
- "$uid": {
- "$characterId": {
- ".write": "$uid === auth.uid || root.child('character_control').child(auth.uid).child($characterId).child('user').val() === $uid"
- },
- ".read": true
- }
- },
- "search_characters": {
- ".read": true,
- "$uid": {
- ".write": "$uid === auth.uid",
- "results": {
- ".indexOn": ["character_name"]
- }
- }
- },
- "character_control": {
- "$uid": {
- "$playerid": {
- ".write": "data.exists() || newData.child('user').val() === auth.uid || data.child('user').val() === auth.uid"
- },
- ".read": true
- }
- },
- "npcs": {
- "$uid": {
- "$npcId": {
- ".write": "$uid === auth.uid || root.child('character_control').child(auth.uid).child( data.child('player_id').val() ).child('user').val() === $uid",
- ".validate": "newData.hasChildren(['name', 'size', 'type', 'challenge_rating', 'armor_class', 'hit_points', 'strength', 'dexterity', 'constitution', 'intelligence', 'wisdom', 'charisma'])",
- "created": {
- ".validate": "newData.isNumber()"
- },
- "updated": {
- ".validate": "newData.isNumber()"
- },
- "harmless_key": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "source": {
- ".validate": "newData.isString() && newData.val().length <= 20"
- },
- "avatar": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "storage_avatar": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "size": {
- ".validate": "newData.isString() && newData.val().matches(/^(Tiny|Small|Medium|Large|Huge|Gargantuan)$/)"
- },
- "type": {
- ".validate": "newData.isString() && newData.val().matches(/^(Aberration|Beast|Celestial|Construct|Dragon|Elemental|Fey|Fiend|Giant|Humanoid|Monstrosity|Ooze|Plant|Swarm of tiny beasts|Undead)$/)"
- },
- "subtype": {
- ".validate": "newData.isString() && newData.val().length <= 25"
- },
- "alignment": {
- ".validate": "newData.isString() && newData.val().matches(/^(Any|Any alignment|Unaligned|Lawful Good|Neutral Good|Chaotic Good|Lawful neutral|Neutral|Chaotic neutral|Lawful evil|Neutral evil|Chaotic evil)$/i)"
- },
- "walk_speed": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "swim_speed": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "fly_speed": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "burrow_speed": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "climb_speed": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "languages": {
- "$langugeIndex": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- }
- },
- "challenge_rating": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 30"
- },
- "proficiency": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 10"
- },
- "friendly": {
- ".validate": "newData.isBoolean()"
- },
- "player_id": {
- ".validate": "newData.isString() && newData.val().length <= 50"
- },
- "armor_class": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "hit_points": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 9999"
- },
- "hit_dice": {
- ".validate": "newData.isString() && newData.val().length <= 10 && (newData.val().length === 0 ||newData.val().matches(/^[0-9]+d[0-9]+$/))"
- },
-
- "senses": {
- "blindsight": {
- "blindsight": {
- ".validate": "newData.isBoolean()"
- },
- "range": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "comments": {
- ".validate": "newData.isString() && newData.val().length <= 999"
- },
- "$other": { ".validate": false }
- },
- "darkvision": {
- "darkvision": {
- ".validate": "newData.isBoolean()"
- },
- "range": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "comments": {
- ".validate": "newData.isString() && newData.val().length <= 999"
- },
- "$other": { ".validate": false }
- },
- "tremorsense": {
- "tremorsense": {
- ".validate": "newData.isBoolean()"
- },
- "range": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "comments": {
- ".validate": "newData.isString() && newData.val().length <= 999"
- },
- "$other": { ".validate": false }
- },
- "truesight": {
- "truesight": {
- ".validate": "newData.isBoolean()"
- },
- "range": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "comments": {
- ".validate": "newData.isString() && newData.val().length <= 999"
- },
- "$other": { ".validate": false }
- }
- },
-
- "strength": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
- },
- "dexterity": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
- },
- "constitution": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
- },
- "intelligence": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
- },
- "wisdom": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
- },
- "charisma": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
- },
-
- "saving_throws": {
- "$savingIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(strength|dexterity|constitution|intelligence|wisdom|charisma)$/)"
- }
- },
-
- "skills": {
- "$skillIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(acrobatics|animal Handling|arcana|athletics|deception|history|insight|intimidation|investigation|medicine|nature|perception|performance|persuasion|religion|sleight of Hand|stealth|survival)$/)"
- }
- },
- "skills_expertise": {
- "$skillIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(acrobatics|animal Handling|arcana|athletics|deception|history|insight|intimidation|investigation|medicine|nature|perception|performance|persuasion|religion|sleight of Hand|stealth|survival)$/)"
- }
- },
- "skill_modifiers": {
- "acrobatics": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "animal Handling": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "arcana": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "athletics": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "deception": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "history": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "insight": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "intimidation": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "investigation": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "medicine": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "nature": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "perception": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "performance": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "persuasion": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "religion": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "sleight of Hand": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "stealth": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "survival": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "$other": {
- ".validate": false
- }
- },
-
- "damage_vulnerabilities": {
- "$vulnerabilityIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- }
- },
- "damage_resistances": {
- "$resistanceIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- }
- },
- "damage_immunities": {
- "$immunityIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- }
- },
- "condition_immunities": {
- "$conditionImmunityIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(blinded|charmed|deafened|exhaustion|frightened|grappled|incapacitated|invisible|paralyzed|petrified|poisoned|prone|restrained|stunned|unconscious)$/)"
- }
- },
-
- "caster_ability": {
- ".validate": "newData.isString() && newData.val().matches(/^(strength|dexterity|constitution|intelligence|wisdom|charisma)$/)"
- },
- "caster_level": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "caster_save_dc": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "caster_spell_attack": {
- ".validate": "newData.isNumber() && newData.val() >= -10 && newData.val() <= 99"
- },
- "caster_spell_slots": {
- "$slotIndex": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 9"
- }
- },
- "caster_spells": {
- "$spellId": {
- "level": {
- ".validate": "newData.isNumber() && newData.val() >= -1 && newData.val() <= 9"
- },
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 50"
- },
- "custom": {
- ".validate": "newData.isBoolean()"
- },
- "$other": { ".validate": false }
- }
- },
-
- "innate_ability": {
- ".validate": "newData.isString() && newData.val().matches(/^(strength|dexterity|constitution|intelligence|wisdom|charisma)$/)"
- },
- "innate_save_dc": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "innate_spell_attack": {
- ".validate": "newData.isNumber() && newData.val() >= -10 && newData.val() <= 99"
- },
- "innate_spells": {
- "$spellId": {
- "limit": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 10"
- },
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 50"
- },
- "custom": {
- ".validate": "newData.isBoolean()"
- },
-
- "$other": { ".validate": false }
- }
- },
-
- "special_abilities": {
- "$abilityIndex": {
- ".validate": "newData.hasChildren(['name'])",
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 50"
- },
- "recharge": {
- ".validate": "newData.isString() && (newData.val().length === 0 || newData.val().matches(/^[0-9]+(-[0-9]+)*$/) || newData.val().matches(/^(rest)$/))"
- },
- "limit": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 9"
- },
- "limit_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(day|turn)$/)"
- },
- "desc": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "reach": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "range": {
- ".validate": "newData.isString() && (newData.val().length === 0 || newData.val().matches(/^[0-9]+(\\/[0-9]+)*$/))"
- },
- "aoe_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(cone|cube|cylinder|line|sphere|square|square feet)$/)"
- },
- "aoe_size": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "versatile": {
- ".validate": "newData.isBoolean()"
- },
- "versatile_one": {
- ".validate": "newData.isString() && newData.val().length <= 25"
- },
- "versatile_two": {
- ".validate": "newData.isString() && newData.val().length <= 25"
- },
- "options": {
- "$optionIndex": {
- ".validate": "newData.isString() && newData.val().length <= 30"
- }
- },
- "action_list": {
- "$listIndex": {
- "type": {
- ".validate": "newData.isString() && newData.val().matches(/^(melee_weapon|ranged_weapon|spell_attack|save|damage|healing|other)$/)"
- },
- "attack_bonus": {
- ".validate": "newData.isNumber() && newData.val() >= -10 && newData.val() <= 99"
- },
- "save_ability": {
- ".validate": "newData.isString() && newData.val().matches(/^(strength|dexterity|constitution|intelligence|wisdom|charisma)$/)"
- },
- "save_dc": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "rolls": {
- "$rollIndex": {
- "damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "magical": {
- ".validate": "newData.isBoolean()"
- },
- "dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "versatile_damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "versatile_magical": {
- ".validate": "newData.isBoolean()"
- },
- "versatile_dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "versatile_dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "versatile_fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "options": {
- "$optionKey": {
- "damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "magical": {
- ".validate": "newData.isBoolean()"
- },
- "dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "ignore": {
- ".validate": "newData.isBoolean()"
- }
- }
- },
- "projectile_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "miss_mod": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 2"
- },
- "save_fail_mod": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 2"
- },
- "special": {
- "$specialIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(siphon_full|siphon_half|drain)$/)"
- }
- },
- "$other": { ".validate": false }
- }
- },
- "$other": { ".validate": false }
- }
- },
- "$other": { ".validate": false }
- }
- },
-
- "actions": {
- "$actionIndex": {
- ".validate": "newData.hasChildren(['name'])",
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 50"
- },
- "recharge": {
- ".validate": "newData.isString() && (newData.val().length === 0 || newData.val().matches(/^[0-9]+(-[0-9]+)*$/) || newData.val().matches(/^(rest)$/))"
- },
- "limit": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 9"
- },
- "limit_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(day|turn)$/)"
- },
- "desc": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "reach": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "range": {
- ".validate": "newData.isString() && (newData.val().length === 0 || newData.val().matches(/^[0-9]+(\\/[0-9]+)*$/))"
- },
- "aoe_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(cone|cube|cylinder|line|sphere|square|square feet)$/)"
- },
- "aoe_size": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "versatile": {
- ".validate": "newData.isBoolean()"
- },
- "versatile_one": {
- ".validate": "newData.isString() && newData.val().length <= 25"
- },
- "versatile_two": {
- ".validate": "newData.isString() && newData.val().length <= 25"
- },
- "options": {
- "$optionIndex": {
- ".validate": "newData.isString() && newData.val().length <= 30"
- }
- },
- "action_list": {
- "$listIndex": {
- "type": {
- ".validate": "newData.isString() && newData.val().matches(/^(melee_weapon|ranged_weapon|spell_attack|save|damage|healing|other)$/)"
- },
- "attack_bonus": {
- ".validate": "newData.isNumber() && newData.val() >= -10 && newData.val() <= 99"
- },
- "save_ability": {
- ".validate": "newData.isString() && newData.val().matches(/^(strength|dexterity|constitution|intelligence|wisdom|charisma)$/)"
- },
- "save_dc": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "rolls": {
- "$rollIndex": {
- "damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "magical": {
- ".validate": "newData.isBoolean()"
- },
- "dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "versatile_damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "versatile_magical": {
- ".validate": "newData.isBoolean()"
- },
- "versatile_dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "versatile_dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "versatile_fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "options": {
- "$optionKey": {
- "damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "magical": {
- ".validate": "newData.isBoolean()"
- },
- "dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "ignore": {
- ".validate": "newData.isBoolean()"
- }
- }
- },
- "projectile_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "miss_mod": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 2"
- },
- "save_fail_mod": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 2"
- },
- "special": {
- "$specialIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(siphon_full|siphon_half|drain)$/)"
- }
- },
- "$other": { ".validate": false }
- }
- },
- "$other": { ".validate": false }
- }
- },
- "$other": { ".validate": false }
- }
- },
-
- "legendary_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 9"
- },
- "legendary_actions": {
- "$legendaryIndex": {
- ".validate": "newData.hasChildren(['name', 'legendary_cost'])",
- "legendary_cost": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9"
- },
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 50"
- },
- "recharge": {
- ".validate": "newData.isString() && (newData.val().length === 0 || newData.val().matches(/^[0-9]+(-[0-9]+)*$/) || newData.val().matches(/^(rest)$/))"
- },
- "limit": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 9"
- },
- "limit_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(day|turn)$/)"
- },
- "desc": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "reach": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "range": {
- ".validate": "newData.isString() && (newData.val().length === 0 || newData.val().matches(/^[0-9]+(\\/[0-9]+)*$/))"
- },
- "aoe_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(cone|cube|cylinder|line|sphere|square|square feet)$/)"
- },
- "aoe_size": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "versatile": {
- ".validate": "newData.isBoolean()"
- },
- "versatile_one": {
- ".validate": "newData.isString() && newData.val().length <= 25"
- },
- "versatile_two": {
- ".validate": "newData.isString() && newData.val().length <= 25"
- },
- "options": {
- "$optionIndex": {
- ".validate": "newData.isString() && newData.val().length <= 30"
- }
- },
- "action_list": {
- "$listIndex": {
- "type": {
- ".validate": "newData.isString() && newData.val().matches(/^(melee_weapon|ranged_weapon|spell_attack|save|damage|healing|other)$/)"
- },
- "attack_bonus": {
- ".validate": "newData.isNumber() && newData.val() >= -10 && newData.val() <= 99"
- },
- "save_ability": {
- ".validate": "newData.isString() && newData.val().matches(/^(strength|dexterity|constitution|intelligence|wisdom|charisma)$/)"
- },
- "save_dc": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "rolls": {
- "$rollIndex": {
- "damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "magical": {
- ".validate": "newData.isBoolean()"
- },
- "dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "versatile_damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "versatile_magical": {
- ".validate": "newData.isBoolean()"
- },
- "versatile_dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "versatile_dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "versatile_fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "options": {
- "$optionKey": {
- "damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "magical": {
- ".validate": "newData.isBoolean()"
- },
- "dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "ignore": {
- ".validate": "newData.isBoolean()"
- }
- }
- },
- "projectile_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "miss_mod": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 2"
- },
- "save_fail_mod": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 2"
- },
- "special": {
- "$specialIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(siphon_full|siphon_half|drain)$/)"
- }
- },
- "$other": { ".validate": false }
- }
- },
- "$other": { ".validate": false }
- }
- },
- "$other": { ".validate": false }
- }
- },
-
- "reactions": {
- "$reactionIndex": {
- ".validate": "newData.hasChildren(['name'])",
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 50"
- },
- "recharge": {
- ".validate": "newData.isString() && (newData.val().length === 0 || newData.val().matches(/^[0-9]+(-[0-9]+)*$/) || newData.val().matches(/^(rest)$/))"
- },
- "limit": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 9"
- },
- "limit_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(day|turn)$/)"
- },
- "desc": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "reach": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "range": {
- ".validate": "newData.isString() && (newData.val().length === 0 || newData.val().matches(/^[0-9]+(\\/[0-9]+)*$/))"
- },
- "aoe_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(cone|cube|cylinder|line|sphere|square|square feet)$/)"
- },
- "aoe_size": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "versatile": {
- ".validate": "newData.isBoolean()"
- },
- "versatile_one": {
- ".validate": "newData.isString() && newData.val().length <= 25"
- },
- "versatile_two": {
- ".validate": "newData.isString() && newData.val().length <= 25"
- },
- "options": {
- "$optionIndex": {
- ".validate": "newData.isString() && newData.val().length <= 30"
- }
- },
- "action_list": {
- "$listIndex": {
- "type": {
- ".validate": "newData.isString() && newData.val().matches(/^(melee_weapon|ranged_weapon|spell_attack|save|damage|healing|other)$/)"
- },
- "attack_bonus": {
- ".validate": "newData.isNumber() && newData.val() >= -10 && newData.val() <= 99"
- },
- "save_ability": {
- ".validate": "newData.isString() && newData.val().matches(/^(strength|dexterity|constitution|intelligence|wisdom|charisma)$/)"
- },
- "save_dc": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "rolls": {
- "$rollIndex": {
- "damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "magical": {
- ".validate": "newData.isBoolean()"
- },
- "dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "versatile_damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "versatile_magical": {
- ".validate": "newData.isBoolean()"
- },
- "versatile_dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "versatile_dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "versatile_fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "options": {
- "$optionKey": {
- "damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "magical": {
- ".validate": "newData.isBoolean()"
- },
- "dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "ignore": {
- ".validate": "newData.isBoolean()"
- }
- }
- },
- "projectile_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "miss_mod": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 2"
- },
- "save_fail_mod": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 2"
- },
- "special": {
- "$specialIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(siphon_full|siphon_half|drain)$/)"
- }
- },
- "$other": { ".validate": false }
- }
- },
- "$other": { ".validate": false }
- }
- },
- "$other": { ".validate": false }
- }
- },
- "$other": { ".validate": false }
- },
- ".write": "root.child('users').child(auth.uid).child('admin').exists()",
- ".read": true,
- ".indexOn": ["name", "harmless_key"]
- },
- ".read": "root.child('users').child(auth.uid).child('admin').exists()"
- },
- "search_npcs": {
- ".read": true,
- "$uid": {
- ".write": "$uid === auth.uid",
- "results": {
- ".indexOn": ["name", "challenge_rating", "type"]
- }
- }
- },
- "custom_items": {
- "$uid": {
- ".write": "$uid === auth.uid",
- ".read": true
- },
- ".read": "root.child('users').child(auth.uid).child('admin').exists()"
- },
- "search_custom_items": {
- ".read": true,
- "$uid": {
- ".write": "$uid === auth.uid",
- "results": {
- ".indexOn": ["name"]
- }
- }
- },
- "encounters": {
- "$uid": {
- ".write": "$uid === auth.uid || root.child('users').child(auth.uid).child('admin').exists()",
- ".read": true,
-
- "$campaignId": {
- ".indexOn": "entities/id",
- "$encounterId": {
- "timestamp": {
- ".validate": "newData.isNumber()"
- },
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 200"
- },
- "encounter": {
- ".validate": "newData.isString() && newData.val().length <= 200"
- },
- "turn": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "round": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999"
- },
- "finished": {
- ".validate": "newData.isBoolean()"
- },
- "audio": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "background": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "hk_background": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "currency": {
- "$coin": {
- ".validate": "$coin.matches(/^(cp|sp|ep|gp|pp)$/)"
- }
- },
- "loot": {
- "$itemId": {
- "linked_item": {
- "custom": {
- ".validate": "newData.isBoolean()"
- },
- "key": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- }
- },
- "public_name": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "public_description": {
- ".validate": "newData.isString() && newData.val().length <= 9999"
- }
- }
- },
- "xp": {
- "calculated": {
- ".validate": "newData.isNumber() && newData.val() >= 0"
- },
- "overwrite": {
- ".validate": "newData.isString() || newData.isNumber()"
- }
- },
- "weather": {
- "$idx": {
- ".validate": "$idx.matches(/^(ash|fog|hail|lightning|quake|rain|sand|smoke|snow)$/)"
- }
- },
- "entities": {
- "$entityId": {
- "id": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "key": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "entityType": {
- ".validate": "newData.isString() && newData.val().matches(/^(player|npc|companion)$/)"
- },
- "npc": {
- ".validate": "newData.isString() && newData.val().matches(/^(srd|custom|api)$/)"
- },
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 200"
- },
- "active": {
- ".validate": "newData.isBoolean()"
- },
- "hidden": {
- ".validate": "newData.isBoolean()"
- },
- "initiative": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- },
- "ac": {
- ".validate": "(newData.isNumber() && newData.val() >= 1 && newData.val() <= 9999) || (newData.isString() && newData.val().length <= 10)"
- },
- "ac_bonus": {
- ".validate": "newData.isNumber() && newData.val() >= -999 && newData.val() <= 9999"
- },
- "curHp": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- },
- "maxHp": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 9999"
- },
- "maxHpMod": {
- ".validate": "newData.isNumber() && newData.val() >= -9999 && newData.val() <= 9999"
- },
- "tempHp": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999"
- },
- "color_label": {
- ".validate": "newData.isString() && newData.val().length <= 10"
- },
- "conditions": {
- "exhaustion": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 6"
- },
- "$condition": {
- ".validate": "newData.isBoolean() && $condition.matches(/^(blinded|charmed|deafened|exhaustion|frightened|grappled|incapacitated|invisible|paralyzed|petrified|poisoned|prone|restrained|stunned|unconscious)$/)"
- }
- },
- "reminders": {
- "$remindId": {
- "action": {
- ".validate": "newData.isString() && newData.val().matches(/^(remove|notify)$/)"
- },
- "color": {
- ".validate": "newData.isString() && newData.val().matches(/^(green-light|yellow-light|orange-light|red-light|purple|blue-light)$/)"
- },
- "trigger": {
- ".validate": "newData.isString() && newData.val().matches(/^(endTurn|startTurn|damage|timed)$/)"
- },
- "rounds": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
- },
- "title": {
- ".validate": "newData.isString() && newData.val().length <= 999"
- },
- "notify": {
- ".validate": "newData.isString() && newData.val().length <= 999"
- },
- "variables": {
- "$varName": {
- "$vi": {
- ".validate": "newData.isString() && newData.val().length <= 5"
- }
- }
- }
- }
- },
- "settings": {
- "ac": {
- ".validate": "newData.isBoolean()"
- },
- "health": {
- ".validate": "newData.isBoolean() || newData.val().matches(/^(obscured)$/)"
- },
- "name": {
- ".validate": "newData.isBoolean()"
- }
- },
- "meters": {
- "damage": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "damageTaken": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "overkill": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "overkillTaken": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "healing": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "healingTaken": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "overhealing": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- },
- "overhealingTaken": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 999999"
- }
- },
- "limited_uses": {
- "caster": {
- "$lvl": {
- ".validate": "newData.isNumber() && newData.val() <= 100"
- }
- },
- "innate": {
- "$spellId": {
- ".validate": "newData.isNumber() && newData.val() <= 100"
- }
- },
- "actions": {
- "$actionId": {
- ".validate": "newData.isNumber() && newData.val() <= 100"
- }
- },
- "legendary_actions": {
- "legendaries_used": {
- ".validate": "newData.isNumber() && newData.val() <= 100"
- }
- }
- }
- }
- },
- "requests": {
- ".write": true
- },
- "$other": { ".validate": false }
- }
- }
- },
- ".read": "root.child('users').child(auth.uid).child('admin').exists()"
- },
- "search_encounters": {
- ".read": true,
- "$uid": {
- ".write": "$uid === auth.uid",
- "results": {
- "$campaignId": {
- ".indexOn": ["name", "finished"]
- }
- }
- }
- },
- "meters": {
- "$uid": {
- ".write": "$uid === auth.uid",
- ".read": true
- }
- },
- "track": {
- "$uid": {
- ".write": true
- },
- ".read": true
- },
- "broadcast": {
- "$uid": {
- ".write": "$uid === auth.uid"
- },
- ".read": true
- },
- "conditions": {
- ".write": false,
- ".read": true
- },
- "items": {
- ".write": false,
- ".read": true
- },
- "abilities": {
- ".write": false,
- ".read": true
- },
- "spells": {
- "$uid": {
- "$spellId": {
- ".validate": "newData.hasChildren(['name', 'level', 'school', 'cast_time', 'cast_time_type', 'range_type', 'duration_type', 'aoe_type', 'scaling'])",
- "harmless_key": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "created": {
- ".validate": "newData.isNumber()"
- },
- "updated": {
- ".validate": "newData.isNumber()"
- },
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "source": {
- ".validate": "newData.isString() && newData.val().length <= 30"
- },
- "level": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9"
- },
- "school": {
- ".validate": "newData.isString() && newData.val().matches(/^(abjuration|conjuration|divination|enchantment|evocation|illusion|necromancy|transmutation)$/i)"
- },
- "cast_time": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 999"
- },
- "cast_time_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(action|bonus_action|reaction|minute|hour|no_action|special)$/i)"
- },
- "cast_time_react_desc": {
- ".validate": "newData.isString() && newData.val().length <= 200"
- },
- "components": {
- "$componentIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(verbal|somatic|material)$/i)"
- }
- },
- "material_description": {
- ".validate": "newData.isString() && newData.val().length <= 500"
- },
- "range_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(self|touch|ranged|sight|unlimited|special)$/i)"
- },
- "range": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 9999999"
- },
- "classes": {
- "$classIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(bard|barbarian|cleric|druid|fighter|monk|paladin|ranger|rogue|sorcerer|warlock|wizard)$/i)"
- }
- },
- "duration_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(concentration|instantaneous|special|time|until_dispelled|until_dispelled_or_triggered)$/i)"
- },
- "duration": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 999"
- },
- "duration_scale": {
- ".validate": "newData.isString() && newData.val().matches(/^(round|minute|hour|day)$/i)"
- },
- "aoe_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(none|cone|cube|cylinder|line|radius|sphere|square|square feet)$/i)"
- },
- "aoe_size": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99999"
- },
- "ritual": {
- ".validate": "newData.isBoolean()"
- },
- "scaling": {
- ".validate": "newData.isString() && newData.val().matches(/^(none|character_level|spell_scale|spell_level)$/i)"
- },
- "description": {
- ".validate": "newData.isString() && newData.val().length <= 5000"
- },
- "higher_level": {
- ".validate": "newData.isString() && newData.val().length <= 1000"
- },
- "projectiles": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "projectile_scaling": {
- "$scalingIndex": {
- ".validate": "newData.hasChildren(['level'])",
- "level": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "projectile_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 25"
- },
- "$other": { ".validate": false }
- }
- },
- "options": {
- "$optionIndex": {
- ".validate": "newData.isString() && newData.val().length <= 30"
- }
- },
- "actions": {
- "$actionIndex": {
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 100"
- },
- "type": {
- ".validate": "newData.isString() && newData.val().matches(/^(melee_weapon|ranged_weapon|spell_attack|save|damage|healing|other)$/)"
- },
- "save_ability": {
- ".validate": "newData.isString() && newData.val().matches(/^(strength|dexterity|constitution|intelligence|wisdom|charisma)$/)"
- },
- "optional": {
- ".validate": "newData.isBoolean()"
- },
- "rolls": {
- "$rollIndex": {
- "damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "magical": {
- ".validate": "newData.isBoolean()"
- },
- "dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "options": {
- "$optionKey": {
- "damage_type": {
- ".validate": "newData.isString() && newData.val().matches(/^(acid|bludgeoning|cold|fire|force|lightning|necrotic|piercing|poison|psychic|radiant|slashing|thunder|non_magical_bludgeoning|non_magical_piercing|non_magical_slashing)$/)"
- },
- "magical": {
- ".validate": "newData.isBoolean()"
- },
- "dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "dice_type": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "ignore": {
- ".validate": "newData.isBoolean()"
- }
- }
- },
- "primary": {
- ".validate": "newData.isBoolean()"
- },
- "miss_mod": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 2"
- },
- "save_fail_mod": {
- ".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 2"
- },
- "special": {
- "$specialIndex": {
- ".validate": "newData.isString() && newData.val().matches(/^(siphon_full|siphon_half|drain)$/)"
- }
- },
- "scaling": {
- "$scalingIndex": {
- ".validate": "newData.hasChildren(['level'])",
- "level": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 20"
- },
- "dice_count": {
- ".validate": "newData.isNumber() && newData.val() >= 1 && newData.val() <= 99"
- },
- "fixed_val": {
- ".validate": "newData.isNumber() && newData.val() >= -99 && newData.val() <= 99"
- },
- "$other": { ".validate": false }
- }
- },
- "$other": { ".validate": false }
- }
- },
- "$other": { ".validate": false }
- }
- },
- "$other": { ".validate": false },
- ".write": "$uid === auth.uid"
- },
- ".write": "root.child('users').child(auth.uid).child('admin').exists()",
- ".read": true,
- ".indexOn": ["metadata/tagged", "metadata/finished"]
- }
- },
- "search_spells": {
- ".read": true,
- "$uid": {
- ".write": "$uid === auth.uid",
- "results": {
- ".indexOn": ["name"]
- }
- }
- },
- "reminders": {
- "$uid": {
- ".write": "$uid === auth.uid",
- ".read": true
- },
- ".read": "root.child('users').child(auth.uid).child('admin').exists()"
- },
- "search_reminders": {
- ".read": true,
- "$uid": {
- ".write": "$uid === auth.uid",
- "results": {
- ".indexOn": ["name"]
- }
- }
- },
- "vouchers": {
- "$voucherId": {
- "times_used": {
- ".write": true
- }
- },
- ".read": true,
- ".write": "root.child('users').child(auth.uid).child('admin').exists()"
- },
- "voucher_history": {
- "$uid": {
- ".read": "$uid === auth.uid",
- ".write": "$uid === auth.uid"
- }
- },
- "soundboard": {
- "$uid": {
- "$soundboardId": {
- ".validate": "newData.hasChildren(['type', 'name', 'url'])",
- "type": {
- ".validate": "newData.isString() && newData.val().matches(/^(music|ambience)$/)"
- },
- "name": {
- ".validate": "newData.isString() && newData.val().length <= 20"
- },
- "url": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "image": {
- ".validate": "newData.isString() && newData.val().length <= 2000"
- },
- "$other": { ".validate": false }
- },
- ".indexOn": ["type"],
- ".read": "$uid === auth.uid",
- ".write": "$uid === auth.uid"
- }
- }
- }
-}
diff --git a/src/components/campaign/Players.vue b/src/components/campaign/Players.vue
index ce5b97e0..aa3c4d2b 100644
--- a/src/components/campaign/Players.vue
+++ b/src/components/campaign/Players.vue
@@ -77,7 +77,7 @@
Edit Group Health