add both types of attack matrices
This commit is contained in:
parent
9246cb5900
commit
f06b426d49
63
app/app.py
63
app/app.py
@ -4,6 +4,7 @@ from flask import Flask
|
||||
from flask_cors import CORS
|
||||
from flask_restx import Api, Resource
|
||||
|
||||
from app.functions.role_physical_attack import roll_physical_attack
|
||||
from .functions.build_character_sheet import build_character_sheet
|
||||
from .functions.roll_ability_scores import roll_ability_scores
|
||||
from .functions.roll_ability_check import roll_ability_check
|
||||
@ -12,10 +13,9 @@ from .functions.roll_encounter import roll_encounter
|
||||
from .functions.roll_mental_attack import roll_mental_attack
|
||||
from .functions.roll_mutations import roll_mutations
|
||||
from .models.models import dice_model, ability_model, hp_model, character_model, encounter_model, ma_model, \
|
||||
mutation_model, \
|
||||
check_model
|
||||
mutation_model, check_model, pa_model
|
||||
from .schemas.schemas import DiceSchema, CharacterSchema, EncounterSchema, MentalAttackSchema, AbilitySchema, \
|
||||
HPSchema, MutationSchema, CheckSchema
|
||||
HPSchema, MutationSchema, CheckSchema, PhysicalAttackSchema
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
@ -28,6 +28,7 @@ dice = api.namespace('dice', description='Dice operations')
|
||||
ability = api.namespace('ability', description='Ability operations')
|
||||
hp = api.namespace('hp', description='HP operations')
|
||||
ma = api.namespace('ma', description='Mental Attack operations')
|
||||
pa = api.namespace('pa', description='Physical Attack operations')
|
||||
mut = api.namespace('mut', description='Mutation operations')
|
||||
character = api.namespace('character', description='Character operations')
|
||||
encounter = api.namespace('encounter', description='Encounter operations')
|
||||
@ -51,6 +52,9 @@ dice_schema = DiceSchema()
|
||||
ma_model = ma.model('MA', ma_model)
|
||||
ma_schema = MentalAttackSchema()
|
||||
|
||||
pa_model = pa.model('PA', pa_model)
|
||||
pa_schema = PhysicalAttackSchema()
|
||||
|
||||
character_model = character.model('Character', character_model)
|
||||
character_schema = CharacterSchema()
|
||||
|
||||
@ -58,6 +62,20 @@ encounter_model = encounter.model('Encounter', encounter_model)
|
||||
encounter_schema = EncounterSchema()
|
||||
|
||||
|
||||
@api.route('/coinflip', methods=['GET'])
|
||||
class RollCoinflip(Resource):
|
||||
@staticmethod
|
||||
def get():
|
||||
return random.choice(['Heads', 'Tails']), 200
|
||||
|
||||
|
||||
@api.route('/roll/chance', methods=['GET'])
|
||||
class RollChance(Resource):
|
||||
@staticmethod
|
||||
def get():
|
||||
return roll_dices(1, 100, False), 200
|
||||
|
||||
|
||||
@api.route('/roll/dice', methods=['POST'])
|
||||
class RollDice(Resource):
|
||||
@dice.expect(dice_model)
|
||||
@ -137,6 +155,24 @@ class RollMentalAttack(Resource):
|
||||
return roll_mental_attack(ams, dms, modifier), 200
|
||||
|
||||
|
||||
@api.route('/roll/attack/physical', methods=['POST'])
|
||||
class RollPhysicalAttack(Resource):
|
||||
@pa.expect(pa_model)
|
||||
def post(self):
|
||||
data = api.payload
|
||||
errors = pa_schema.validate(data)
|
||||
if errors:
|
||||
return errors, 400
|
||||
|
||||
weapon_attack = data.get('weapon_attack') # to pick the attack table
|
||||
dac = data.get('dac') # needed for both attacks
|
||||
awc = data.get('awc') # only needed for weapon attacks
|
||||
ahd = data.get('ahd') # only needed for non-weapon attacks
|
||||
modifier = data.get('modifier')
|
||||
|
||||
return roll_physical_attack(weapon_attack, dac, modifier, awc, ahd), 200
|
||||
|
||||
|
||||
@api.route('/roll/check', methods=['POST'])
|
||||
class RollCheck(Resource):
|
||||
@check.expect(check_model)
|
||||
@ -152,27 +188,6 @@ class RollCheck(Resource):
|
||||
return roll_ability_check(ability_score, multiplier), 200
|
||||
|
||||
|
||||
@api.route('/roll/tohit', methods=['GET'])
|
||||
class RollToHit(Resource):
|
||||
@staticmethod
|
||||
def get():
|
||||
return roll_dices(1, 20, False), 200
|
||||
|
||||
|
||||
@api.route('/roll/chance', methods=['GET'])
|
||||
class RollChance(Resource):
|
||||
@staticmethod
|
||||
def get():
|
||||
return roll_dices(1, 100, False), 200
|
||||
|
||||
|
||||
@api.route('/coinflip', methods=['GET'])
|
||||
class RollCoinflip(Resource):
|
||||
@staticmethod
|
||||
def get():
|
||||
return random.choice(['Heads', 'Tails']), 200
|
||||
|
||||
|
||||
@api.route('/roll/mutations', methods=['POST'])
|
||||
class RollMutations(Resource):
|
||||
@mut.expect(mutation_model)
|
||||
|
26
app/functions/get_attack_roll_outcome.py
Normal file
26
app/functions/get_attack_roll_outcome.py
Normal file
@ -0,0 +1,26 @@
|
||||
from app.functions.roll_dices import roll_dices
|
||||
|
||||
|
||||
def get_attack_roll_outcome(result, modifier=0):
|
||||
outcome = 'Unknown'
|
||||
raw_roll = roll_dices(1, 20, False).get('result')
|
||||
needed = result['needed']
|
||||
|
||||
if modifier != 0:
|
||||
result["original-roll"] = raw_roll
|
||||
result["modifier"] = modifier
|
||||
rolled = raw_roll + modifier # negative modifiers will subtract themselves naturally
|
||||
result["adjusted-roll"] = rolled
|
||||
else:
|
||||
rolled = raw_roll
|
||||
result["original-roll"] = rolled
|
||||
|
||||
if (rolled >= 20) and (needed <= 16):
|
||||
outcome = "Devastating Success!"
|
||||
elif needed <= rolled:
|
||||
outcome = "Attack Successful!"
|
||||
elif needed > rolled:
|
||||
outcome = "Attack Failed!"
|
||||
|
||||
result['outcome'] = outcome
|
||||
return result
|
12
app/functions/get_attack_values.py
Normal file
12
app/functions/get_attack_values.py
Normal file
@ -0,0 +1,12 @@
|
||||
from app.tables.physattack import AttackerWeaponClassMatrix, AttackerHitDiceMatrix
|
||||
|
||||
|
||||
def get_weapon_class_threshold(awc, dac):
|
||||
awc_table = AttackerWeaponClassMatrix()
|
||||
return awc_table.get_attack_score(awc, dac)
|
||||
|
||||
|
||||
def get_hit_dice_threshold(ahd, dac):
|
||||
ahd_table = AttackerHitDiceMatrix()
|
||||
return ahd_table.get_attack_score(ahd, dac)
|
||||
|
32
app/functions/role_physical_attack.py
Normal file
32
app/functions/role_physical_attack.py
Normal file
@ -0,0 +1,32 @@
|
||||
from app.functions.get_attack_roll_outcome import get_attack_roll_outcome
|
||||
from app.tables.physattack import AttackerHitDiceMatrix, AttackerWeaponClassMatrix
|
||||
|
||||
|
||||
def roll_physical_attack(weapon_attack, dac, modifier, awc=0, ahd=0):
|
||||
"""
|
||||
:param weapon_attack: boolean. required. Determines which attack matrix to use.
|
||||
:param dac: integer. required. defender armour class. used in both matrices.
|
||||
:param modifier: integer. required. any pluses or minuses to be applied to roll
|
||||
:param awc: integer. optional(*). Attacker weapon class. This is required, if weapon attack == True.
|
||||
:param ahd: integer. optional(*). Attacker hit dice. This is required, if weapon attack == False.
|
||||
:return:
|
||||
"""
|
||||
result = {}
|
||||
if weapon_attack:
|
||||
if awc == 0:
|
||||
print("Attacker Weapon Class is required for Weapon Attacks!")
|
||||
result["outcome"] = "Attacker Weapon Class is required for Weapon Attacks!"
|
||||
return result
|
||||
else:
|
||||
hit_table = AttackerWeaponClassMatrix()
|
||||
result["needed"] = hit_table.get_attack_score(awc, dac)
|
||||
else:
|
||||
if ahd == 0:
|
||||
print("Attacker Hit Dice is required for Non-Weapon Attacks!")
|
||||
result["outcome"] = "Attacker Hit Dice is required for Non-Weapon Attacks!"
|
||||
return result
|
||||
else:
|
||||
hit_table = AttackerHitDiceMatrix()
|
||||
result["needed"] = hit_table.get_attack_score(ahd, dac)
|
||||
|
||||
return get_attack_roll_outcome(result, modifier)
|
@ -1,4 +1,4 @@
|
||||
from app.functions.roll_dices import roll_dices
|
||||
from app.functions.get_attack_roll_outcome import get_attack_roll_outcome
|
||||
from app.tables.mentattack import MentalAttackMatrix
|
||||
|
||||
|
||||
@ -8,28 +8,5 @@ def roll_mental_attack(ams, dms, modifier):
|
||||
mam = MentalAttackMatrix()
|
||||
needed = mam.get_attack_score(ams, dms)
|
||||
result["needed"] = needed
|
||||
outcome = None
|
||||
if needed < 2:
|
||||
outcome = "Automatic Success!"
|
||||
elif needed > 20:
|
||||
outcome = "Absolutely No Chance!"
|
||||
else:
|
||||
raw_roll = roll_dices(1, 20, False).get('result')
|
||||
if modifier != 0:
|
||||
result["original-roll"] = raw_roll
|
||||
result["modifier"] = modifier
|
||||
rolled = raw_roll + modifier # negative modifiers will subtract themselves naturally
|
||||
result["adjusted-roll"] = rolled
|
||||
else:
|
||||
rolled = raw_roll
|
||||
result["original-roll"] = rolled
|
||||
return get_attack_roll_outcome(result, modifier)
|
||||
|
||||
if (rolled >= 20) and (needed <= 16):
|
||||
outcome = "Devastating Success!"
|
||||
elif needed <= rolled:
|
||||
outcome = "Attack Successful!"
|
||||
elif needed > rolled:
|
||||
outcome = "Attack Failed!"
|
||||
|
||||
result["outcome"] = outcome
|
||||
return result
|
||||
|
@ -66,6 +66,44 @@ hp_model = {
|
||||
'conscore': conscore_field
|
||||
}
|
||||
|
||||
|
||||
pa_model = {
|
||||
'weapon_attack': fields.Boolean(
|
||||
required=True,
|
||||
default=True,
|
||||
description='Is the attacker using a weapon? If so, To-Hit is based on weapon class.'
|
||||
),
|
||||
'dac': fields.Integer(
|
||||
required=True,
|
||||
min=1,
|
||||
max=10,
|
||||
default=1,
|
||||
description='The defenders armour class. This is needed for both weapon attacks and non-weapon attacks'
|
||||
),
|
||||
'awc': fields.Integer(
|
||||
required=False,
|
||||
min=1,
|
||||
max=16,
|
||||
default=1,
|
||||
description='The attackers weapon class. This is needed for weapon attacks only.'
|
||||
),
|
||||
'ahd': fields.Integer(
|
||||
required=False,
|
||||
min=1,
|
||||
max=16,
|
||||
default=1,
|
||||
description='The attackers hit dice count. This is needed for non-weapon attacks only.'
|
||||
),
|
||||
'modifier': fields.Integer(
|
||||
required=False,
|
||||
min=-100,
|
||||
max=100,
|
||||
default=0,
|
||||
description='The roll modifier to be applied to the hit roll.'
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
ma_model = {
|
||||
'ams': fields.Integer(
|
||||
required=True,
|
||||
|
@ -101,3 +101,39 @@ class MentalAttackSchema(Schema):
|
||||
validate=validate.Range(min=-100, max=100),
|
||||
description='Roll modifier for mental attack'
|
||||
)
|
||||
|
||||
|
||||
class PhysicalAttackSchema(Schema):
|
||||
weapon_attack = fields.Boolean(
|
||||
required=True,
|
||||
default=True,
|
||||
description="Is the attacker using a weapon? If so, To-Hit is based on weapon class."
|
||||
)
|
||||
dac = fields.Integer(
|
||||
required=True,
|
||||
min=1,
|
||||
max=10,
|
||||
default=1,
|
||||
description='The defenders armour class. This is needed for both weapon attacks and non-weapon attacks'
|
||||
)
|
||||
awc = fields.Integer(
|
||||
required=False,
|
||||
min=1,
|
||||
max=16,
|
||||
default=1,
|
||||
description='The attackers weapon class. This is needed for weapon attacks only.'
|
||||
)
|
||||
ahd = fields.Integer(
|
||||
required=False,
|
||||
min=1,
|
||||
max=16,
|
||||
default=1,
|
||||
description='The attackers hit dice count. This is needed for non-weapon attacks only.'
|
||||
)
|
||||
modifier = fields.Integer(
|
||||
required=False,
|
||||
min=-100,
|
||||
max=100,
|
||||
default=0,
|
||||
description='The roll modifier to be applied to the hit roll.'
|
||||
)
|
||||
|
@ -1,12 +1,77 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from math import floor
|
||||
|
||||
|
||||
class WeaponClassAttackMatrix:
|
||||
class AttackerWeaponClassMatrix:
|
||||
def __init__(self):
|
||||
data = {}
|
||||
# Define the range for X-axis (column) and Y-axis (row).
|
||||
x_range = list(range(1, 17)) # attacker weapon class
|
||||
y_range = list(range(1, 11)) # defender armour class
|
||||
|
||||
# Provide your static values here as per the intersection
|
||||
values = [
|
||||
# Attacker Weapon class
|
||||
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
||||
[19, 19, 18, 15, 13, 16, 14, 18, 18, 16, 16, 16, 12, 14, 14, 12], # DAC 1
|
||||
[17, 18, 17, 14, 12, 15, 13, 17, 16, 15, 15, 15, 11, 13, 13, 11], # DAC 2
|
||||
[16, 16, 16, 12, 10, 15, 12, 16, 15, 14, 15, 15, 8, 12, 13, 11], # DAC 3
|
||||
[15, 14, 15, 12, 10, 15, 11, 15, 14, 13, 15, 15, 8, 11, 13, 18], # DAC 4
|
||||
[14, 13, 14, 12, 10, 15, 10, 14, 13, 12, 14, 15, 8, 11, 13, 11], # DAC 5
|
||||
[13, 12, 13, 12, 10, 15, 9, 13, 12, 11, 11, 15, 8, 10, 13, 11], # DAC 6
|
||||
[12, 11, 12, 12, 10, 13, 8, 12, 11, 10, 10, 11, 8, 10, 13, 11], # DAC 7
|
||||
[11, 10, 11, 12, 10, 13, 7, 11, 10, 9, 9, 9, 8, 9, 13, 11], # DAC 8
|
||||
[10, 9, 10, 12, 10, 7, 6, 10, 9, 8, 7, 6, 8, 8, 8, 11], # DAC 9
|
||||
[9, 8, 9, 11, 9, 6, 5, 9, 8, 7, 6, 5, 8, 8, 8, 10] # DAC 10
|
||||
]
|
||||
|
||||
# Create the DataFrame.
|
||||
self.table = pd.DataFrame(values, columns=x_range, index=y_range)
|
||||
|
||||
def get_attack_score(self, awc, dac):
|
||||
# pandas uses a 'column-major' order
|
||||
# So, (X,Y) method arguments become (Y,X)
|
||||
# for pandas locators.
|
||||
return int(self.table.loc[dac, awc])
|
||||
|
||||
def get_matrix(self):
|
||||
return self.table
|
||||
|
||||
def dump_matrix(self):
|
||||
print(self.table)
|
||||
|
||||
|
||||
class HitDiceAttackMatrix:
|
||||
class AttackerHitDiceMatrix:
|
||||
def __init__(self):
|
||||
data = {}
|
||||
# Define the range for X-axis (column) and Y-axis (row).
|
||||
x_range = list(range(1, 17)) # attacker weapon class
|
||||
y_range = list(range(1, 11)) # defender armour class
|
||||
|
||||
values = [
|
||||
# Attacker Hit Dice
|
||||
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
||||
[20, 19, 19, 18, 18, 17, 17, 17, 16, 16, 15, 15, 15, 15, 14, 14], # DAC 1
|
||||
[19, 18, 18, 17, 17, 16, 16, 16, 15, 15, 14, 14, 14, 14, 13, 13], # DAC 2
|
||||
[18, 17, 17, 16, 16, 15, 15, 15, 14, 14, 13, 13, 13, 13, 12, 12], # DAC 3
|
||||
[17, 16, 16, 15, 15, 14, 14, 14, 13, 13, 12, 12, 12, 12, 11, 11], # DAC 4
|
||||
[16, 15, 15, 14, 14, 13, 13, 13, 12, 12, 11, 11, 11, 11, 10, 10], # DAC 5
|
||||
[14, 13, 13, 12, 12, 11, 11, 11, 10, 10, 9, 9, 9, 9, 8, 8], # DAC 6
|
||||
[13, 12, 12, 11, 11, 10, 10, 10, 9, 9, 8, 8, 8, 8, 7, 7], # DAC 7
|
||||
[12, 11, 11, 10, 10, 9, 9, 9, 8, 8, 7, 7, 7, 7, 6, 6], # DAC 8
|
||||
[11, 10, 10, 9, 9, 8, 8, 8, 7, 7, 6, 6, 6, 6, 5, 5], # DAC 9
|
||||
[10, 9, 9, 8, 8, 7, 7, 7, 6, 6, 5, 5, 5, 5, 4, 4] # DAC 10
|
||||
]
|
||||
|
||||
# Create the DataFrame.
|
||||
self.table = pd.DataFrame(values, columns=x_range, index=y_range)
|
||||
|
||||
def get_attack_score(self, ahd, dac):
|
||||
# pandas uses a 'column-major' order
|
||||
# So, (X,Y) method arguments become (Y,X)
|
||||
# for pandas locators.
|
||||
return int(self.table.loc[dac, ahd])
|
||||
|
||||
def get_matrix(self):
|
||||
return self.table
|
||||
|
||||
def dump_matrix(self):
|
||||
print(self.table)
|
||||
|
Loading…
Reference in New Issue
Block a user