From 9017792b3cffe4f04c8bd8d8d60ed818d1d1e659 Mon Sep 17 00:00:00 2001 From: Brandon Cornejo Date: Tue, 10 Nov 2020 21:11:54 -0600 Subject: [PATCH] Single NPC gen, spell list, world map, roll20-hitdice --- acks/npc/models.py | 4 + acks/npc/npc_party.py | 22 +- acks/npc/templates/generate_single_npc.html | 350 ++++++++++++++++++++ acks/npc/templates/spell_list.html | 85 ++++- acks/npc/views.py | 24 +- acks/static/WorldMap.png | Bin 0 -> 4784440 bytes acks/templates/base.html | 18 +- acks/templates/worldmap.html | 15 + acks/views.py | 4 + roll20/hit_dice.js | 117 +++++++ 10 files changed, 625 insertions(+), 14 deletions(-) create mode 100644 acks/npc/templates/generate_single_npc.html create mode 100644 acks/static/WorldMap.png create mode 100644 acks/templates/worldmap.html create mode 100644 roll20/hit_dice.js diff --git a/acks/npc/models.py b/acks/npc/models.py index f059170..5114706 100644 --- a/acks/npc/models.py +++ b/acks/npc/models.py @@ -111,6 +111,10 @@ class Spell(BaseModel): def is_arcane(self): return bool(self.arcane and self.arcane > 0) + @property + def dom_id(self): + return self.name.lower().replace("*", "").replace("'", "").replace(",", "").replace(" ", "_") + @property def roll20_format(self): spell_dict = { diff --git a/acks/npc/npc_party.py b/acks/npc/npc_party.py index 8367747..c805c9d 100644 --- a/acks/npc/npc_party.py +++ b/acks/npc/npc_party.py @@ -127,18 +127,20 @@ def calc_armour(armour_mod, armours): armourval = len(armours) - 1 return armours[armourval] -def generate_npc(base_level, data): +def generate_npc(base_level, data, guild_id=False): npc = CharacterNPC() - npc.guild = npc_class(['Demi-Human']) + if not guild_id: + npc.guild = npc_class(['Demi-Human']) + else: + npc.guild = CharacterClass.query.filter_by(id=guild_id).first() + npc.level = npc_baselevel(base_level) if npc.level > npc.guild.maximum_level: npc.level = npc.guild.maximum_level npc.alignment = npc_alignment() - abilities = npc_abilities() npc.update(npc_abilities()) - npc.hit_points = calc_hp(attribute_mod(npc.constitution), npc.guild.hit_die_size, npc.level) npc.armour = calc_armour(npc.guild.armour_modifier, data['armours']) @@ -194,7 +196,7 @@ def name_party(party): return party -def create_party(base_level): +def load_db_data(): data = { 'armours': EquipmentArmour.query.all(), 'ranged': { @@ -224,6 +226,16 @@ def create_party(base_level): spells.extend(data['spells']['divine'][spell.divine]) data['spells']['divine'][spell.divine] = spells + return data + +def create_npc(base_level, guild_id): + data = load_db_data() + if guild_id: + return name_party([generate_npc(base_level, data, guild_id=guild_id)])[0] + return name_party([generate_npc(base_level, data)])[0] + +def create_party(base_level): + data = load_db_data() return name_party([generate_npc(base_level, data) for x in range(0, number_encountered())]) def print_party(party): diff --git a/acks/npc/templates/generate_single_npc.html b/acks/npc/templates/generate_single_npc.html new file mode 100644 index 0000000..210d8e8 --- /dev/null +++ b/acks/npc/templates/generate_single_npc.html @@ -0,0 +1,350 @@ +{% extends "base.html" %} +{% set active_page = "npcsingle" %} + +{% block title %}Single NPC Generation{% endblock %} +{% block content %} +
+

Adventurer Conqueror King NPC Generator

+
+
+
+ + +
+
+ + +
+
+ +
+
+ +
+ +{% if npc %} +
+

Generated NPC

+ +
+ +
+
+

{{ npc.name }}

+
{{ npc.guild.name }}
+
+
+
Level
+
{{ npc.level }}
+
+
+
HP
+
{{ npc.hp }}
+
+
+
AC
+
{{ npc.armour.ac_mod }}
+
+
+
+
+
{{ npc.str }}
+
Str
+
+
+
{{ npc.int }}
+
Int
+
+
+
{{ npc.wis }}
+
Wis
+
+
+
{{ npc.dex }}
+
Dex
+
+
+
{{ npc.con }}
+
Con
+
+
+
{{ npc.chr }}
+
Chr
+
+
+
+
+
+
{{ npc.save_pp }}
+
P & P
+
+
+
{{ npc.save_pd }}
+
P & D
+
+
+
{{ npc.save_bb }}
+
B & B
+
+
+
{{ npc.save_sw }}
+
S & W
+
+
+
{{ npc.save_sp }}
+
Spells
+
+
+ +
    +
  • + + + + + + + + + + + + {% if npc.ranged %} + + + + + + + {% endif %} + + + + + + + +
    NameWorthThrDmg
    {{ npc.melee.name }}{{ npc.melee.gp_value }}gp{{ npc.attack_throw }}{{ npc.melee.damage_die }}
    {{ npc.ranged.name }}{{ npc.ranged.gp_value }}gp{{ npc.attack_throw }}{{ npc.ranged.damage_die }}
    {{ npc.armour.name }}{{ npc.armour.gp_value }}gp
    +
  • +
  • + + + + + + {% for level in npc.spell_list() %} + {% for spell in npc.spell_list()[level] %} + + + + + + + {% endfor %} + {% endfor %} + +
    NameRangeDurationLevel
    {{ spell.name }}{{ spell.range }}{{ spell.duration }}{{ spell.level_for(npc) }}
    +
  • +
+
+
+ +{% endif %} + +
+
+ +
+

{Spell Title}

+
+
+
    +
  • Range: {Spell Range}
  • +
  • Duration: {Spell Duration}
  • +
  • {Spell School}
  • +
+

{Spell Description}

+
+
+
+ +
+
+ +
+

Roll20 Export

+
+
+
+
Characters to Export:
+
+ {% if npc %} + + {% endif %} +
+
+
+ + +
+
+
+ + +
+ +
+
+ +
+ + + +{% endblock %} diff --git a/acks/npc/templates/spell_list.html b/acks/npc/templates/spell_list.html index 249a3a2..d5728b5 100644 --- a/acks/npc/templates/spell_list.html +++ b/acks/npc/templates/spell_list.html @@ -6,13 +6,29 @@

Adventurer Conqueror King Spell List

+
+
+ + +
+
+
+ + +
+
+ + +
+
+
- - + +
- +
@@ -21,7 +37,7 @@ {% if spells %}
{% for spell in spells %} -
+

{{ spell.name }}

    @@ -39,6 +55,67 @@
{% endif %} + +{% endblock %} diff --git a/acks/views.py b/acks/views.py index e3efbc4..2f8eb57 100644 --- a/acks/views.py +++ b/acks/views.py @@ -11,3 +11,7 @@ def index(): @default_views.route('/handbook') def handbook(): return render_template('handbook.html') + +@default_views.route('/worldmap') +def worldmap(): + return render_template('worldmap.html') diff --git a/roll20/hit_dice.js b/roll20/hit_dice.js new file mode 100644 index 0000000..0777769 --- /dev/null +++ b/roll20/hit_dice.js @@ -0,0 +1,117 @@ +var HitDice = HitDice || (function() { + 'use strict'; + + var tokenIds = [], + configure = function() { + if(!state.HitDice) { + state.HitDice = { + version: 0.1, + config: { + bar: 3, + hitDiceAttribute: 'npc_hitdice', + } + }; + } + }, + handleInput = function(msg) { + if (msg.type === "api" && /^!mhd(\b|$)/i.test(msg.content) && playerIsGM(msg.playerid) ) { + let who = (getObj('player',msg.playerid)||{get:()=>'API'}).get('_displayname'); + let count = 0; + // WUSSALLTHISTHEN + (msg.selected || []) + .map(o=>getObj('graphic',o._id)) + .filter(g=>undefined !== g) + .forEach( o => { + ++count; + tokenIds.push(o.id); + rollHitDice(o); + }) + ; + sendChat('',`/w "${who}" Rolling hit dice for ${count} token(s).`); + } + }, + findRoll = function(txt){ + return txt.match(/\d+d\d+(\+\d+)?/)[0] || 0; + }, + + rollHitDice = function(obj) { + var sets = {}, + bar = 'bar'+state.HitDice.config.bar, + hdAttrib, + hdExpression = 0, + bonus = 0 + ; + + if(_.contains(tokenIds,obj.id)){ + tokenIds=_.without(tokenIds,obj.id); + + if('graphic' === obj.get('type') && + 'token' === obj.get('subtype') && + '' !== obj.get('represents') + ) { + if( obj && '' === obj.get(bar+'_link') ) { + hdAttrib = findObjs({ + type: 'attribute', + characterid: obj.get('represents'), + name: state.HitDice.config.hitDiceAttribute + })[0]; + + if( hdAttrib ) { + //sendChat('', 'HERE WE ARE'); + //log(hdAttrib); + + hdExpression = findRoll(hdAttrib.get('current')); + sendChat('','/r '+hdExpression+'+'+bonus,function(r){ + var hp=0; + _.each(r,function(subr){ + var val=JSON.parse(subr.content); + if(_.has(val,'total')) + { + hp+=val.total; + } + }); + sets[bar+"_value"] = hp||1; + sets[bar+"_max"] = hp||1; + obj.set(sets); + }); + } + } + } + } + }, + + saveTokenId = function(obj){ + tokenIds.push(obj.id); + + setTimeout((function(id){ + return function(){ + var token=getObj('graphic',id); + if(token){ + rollHitDice(token); + } + }; + }(obj.id)),100); + }, + + + registerEventHandlers = function() { + on('chat:message', handleInput); + on('add:graphic', saveTokenId); + on('change:graphic', rollHitDice); + }; + + return { + configure: configure, + RegisterEventHandlers: registerEventHandlers + }; + +}()); + +on('ready',function() { + 'use strict'; + + HitDice.configure(); + HitDice.RegisterEventHandlers(); +}); + +