gammatools/app.py

217 lines
6.6 KiB
Python

from flask import Flask
from flask_restx import Api, Resource
from marshmallow import Schema, fields, validate
from models import dice_model, ability_model, hp_model, character_model
import random
app = Flask(__name__)
app.config.SWAGGER_UI_JSONEDITOR = True
app.config['SWAGGER_UI_JSONEDITOR'] = True
api = Api(app, version='1.0', title='Gamma World Dice', description='Rolled Dice As A Service')
dice = api.namespace('dice', description='Dice operations')
ability = api.namespace('ability', description='Ability operations')
hp = api.namespace('hp', description='HP operations')
character = api.namespace('character', description='Character operations')
dice_model = dice.model('Dice', dice_model)
ability_model = ability.model('Ability', ability_model)
hp_model = hp.model('HP', hp_model)
character_model = character.model('Character', character_model)
class DiceSchema(Schema):
quantity = fields.Int(required=True, validate=validate.Range(min=1), description='The number of dice to roll')
geometry = fields.Int(required=True, validate=validate.Range(min=2), description='The number of sides on each die')
discard_lowest = fields.Bool(default=False, description='Drop the lowest score')
dice_schema = DiceSchema()
class CharacterSchema(Schema):
chartype = fields.String(
required=True,
validate=validate.OneOf(["human", "mutant", "android", "robot"]),
description='The characters type of being'
)
emphasis = fields.String(
required=True,
validate=validate.OneOf(["physical", "mental", "random"]),
description='Valid inputs: physical, mental, random'
)
character_schema = CharacterSchema()
@api.route('/roll/dice', methods=['POST'])
class RollDice(Resource):
@dice.expect(dice_model)
def post(self):
data = api.payload
errors = dice_schema.validate(data)
if errors:
return errors, 400
quantity = data.get('quantity')
geometry = data.get('geometry')
discard_lowest = data.get('discard_lowest')
if quantity is None or geometry is None:
return {"message": "Required dice data not provided"}, 400
return roll_dices(quantity, geometry, discard_lowest), 200
@api.route('/roll/ability', methods=['POST'])
class RollAbility(Resource):
@ability.expect(ability_model)
def post(self):
data = api.payload
chartype = data.get('chartype')
if chartype == 'human':
geometry = 8
else:
geometry = 6
return roll_dices(4, geometry, True), 200
@api.route('/roll/hp', methods=['POST'])
class RollHP(Resource):
@hp.expect(hp_model)
def post(self):
data = api.payload
chartype = data.get('chartype')
conscore = data.get('conscore')
if conscore is None:
return {"message": "A constitution score is required"}, 400
if chartype == 'human':
geometry = 8
else:
geometry = 6
return roll_dices(conscore, geometry, False), 200
@api.route('/roll/tohit', methods=['GET'])
class RollToHit(Resource):
def get(self):
return roll_dices(1, 20, False), 200
@api.route('/roll/chance', methods=['GET'])
class RollChance(Resource):
def get(self):
return roll_dices(1, 100, False), 200
@api.route('/coinflip', methods=['GET'])
class RollCoinflip(Resource):
def get(self):
return random.choice(['Heads', 'Tails']), 200
@api.route('/character/generate')
class GenerateCharacter(Resource):
@character.expect(character_model)
def post(self):
data = api.payload
errors = character_schema.validate(data)
if errors:
return errors, 400
chartype = data.get('chartype')
char_emphasis = data.get('emphasis')
character_sheet = {}
if chartype == 'human':
geometry = 8
else:
geometry = 6
scores = roll_ability_scores(chartype)
assigned_abilities = assign_ability_scores(scores, char_emphasis)
character_sheet['abilities'] = assigned_abilities
character_sheet['hp'] = roll_hp(chartype, assigned_abilities['constitution'])
character_sheet['gold'] = roll_dices(4, 4, False).get('result')
character_sheet['domar'] = roll_dices(2, 4, False).get('result')
return character_sheet, 200
def roll_dices(dice_count, dice_sides, discard_lowest):
roll_results = []
discarded = []
for _ in range(dice_count):
roll_results.append(random.randint(1, dice_sides))
if discard_lowest and len(roll_results) > 0:
discarded.append(min(roll_results))
roll_results.remove(min(roll_results))
if discarded:
net_dice_count = dice_count - 1
mnemonic = str(dice_count) + "(-1)d" + str(dice_sides)
else:
mnemonic = str(dice_count) + 'd' + str(dice_sides)
net_dice_count = dice_count
result = {
'dice-set': {
'mnemonic': mnemonic,
'min-roll': net_dice_count,
'max-roll': dice_sides * net_dice_count
},
'rolls': roll_results,
'result': sum(roll_results)
}
if discarded:
result['discarded'] = discarded
return result
def roll_ability_scores(chartype):
if chartype == 'human':
geometry = 8
else:
geometry = 6
scores = []
for _ in range(6):
scores.append(roll_dices(4, geometry, True)['result'])
return scores
def assign_ability_scores(scores, char_emphasis):
ability_scores = {}
if char_emphasis == 'physical':
ability_list = ['p-strength', 'constitution', 'dexterity',
'charisma', 'intelligence', 'm-strength']
for ab in ability_list:
max_score = max(scores)
ability_scores[ab] = max_score
scores.remove(max_score)
elif char_emphasis == 'mental':
ability_list = ['m-strength', 'intelligence', 'charisma',
'constitution', 'dexterity', 'p-strength']
for ab in ability_list:
max_score = max(scores)
ability_scores[ab] = max_score
scores.remove(max_score)
else:
ability_list = ['p-strength', 'constitution', 'dexterity',
'charisma', 'intelligence', 'm-strength']
random.shuffle(scores)
for ab, score in zip(ability_list, scores):
ability_scores[ab] = score
return ability_scores
def roll_hp(chartype, conscore):
if chartype == 'human':
geometry = 8
else:
geometry = 6
return roll_dices(conscore, geometry, False).get('result')
if __name__ == '__main__':
app.run()