diff --git a/app.py b/app.py deleted file mode 100644 index 7822edd..0000000 --- a/app.py +++ /dev/null @@ -1,455 +0,0 @@ -from flask import Flask -from flask_cors import CORS -from flask_restx import Api, Resource - -from models import dice_model, ability_model, hp_model, character_model, encounter_model, ma_model, mutation_model, \ - check_model -from schemas import DiceSchema, CharacterSchema, EncounterSchema, MentalAttackSchema, AbilitySchema, HPSchema, \ - MutationSchema, CheckSchema - -from encounters import EncounterTable -from mentattack import MentalAttackMatrix -from mutations import Mutations -from cybermods import CyberMods - -import random - -app = Flask(__name__) -CORS(app) - -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') -ma = api.namespace('ma', description='Mental Attack operations') -mut = api.namespace('mut', description='Mutation operations') -character = api.namespace('character', description='Character operations') -encounter = api.namespace('encounter', description='Encounter operations') -check = api.namespace('check', description='Check operations') - -check_model = check.model('Check', check_model ) -check_schema = CheckSchema() - -ability_model = ability.model('Ability', ability_model) -ability_schema = AbilitySchema() - -mutation_model = mut.model('Mutation', mutation_model) -mutation_schema = MutationSchema() - -hp_model = hp.model('HP', hp_model) -hp_schema = HPSchema() - -dice_model = dice.model('Dice', dice_model) -dice_schema = DiceSchema() - -ma_model = ma.model('MA', ma_model) -ma_schema = MentalAttackSchema() - -character_model = character.model('Character', character_model) -character_schema = CharacterSchema() - -encounter_model = encounter.model('Encounter', encounter_model) -encounter_schema = EncounterSchema() - - -@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 - errors = ability_schema.validate(data) - if errors: - return errors, 400 - - chartype = data.get('chartype') - attribute = data.get('ability') - - return roll_ability_scores(chartype, attribute), 200 - - -@api.route('/roll/hp', methods=['POST']) -class RollHP(Resource): - @hp.expect(hp_model) - def post(self): - data = api.payload - errors = hp_schema.validate(data) - if errors: - return errors, 400 - 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/encounter', methods=['POST']) -class RollEncounter(Resource): - @encounter.expect(encounter_model) - def post(self): - data = api.payload - errors = encounter_schema.validate(data) - if errors: - return errors, 400 - - terrain = data.get('terrain').lower() - roll = roll_dices(1, 20, False).get('result') - et = EncounterTable() - creature = et.get_encounter(roll, terrain) - if creature is None: - creature = "All clear!" - - return {"encounter": creature}, 200 - - -@api.route('/roll/attack/mental', methods=['POST']) -class RollMentalAttack(Resource): - @ma.expect(ma_model) - def post(self): - data = api.payload - errors = ma_schema.validate(data) - if errors: - return errors, 400 - - ams = data.get('ams') - dms = data.get('dms') - modifier = data.get('modifier') - - result = {} - 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 - - 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, 200 - - -@api.route('/roll/check', methods=['POST']) -class RollCheck(Resource): - @check.expect(check_model) - def post(self): - data = api.payload - errors = check_schema.validate(data) - if errors: - return errors, 400 - - ability_score = data.get('ability_score') - multiplier = data.get('multiplier') - - threshold = ability_score * multiplier - rolled = roll_dices(1, 100, False).get('result') - if rolled < threshold: - return {'threshold': threshold, 'rolled': rolled, 'success': True}, 200 - else: - return {'threshold': threshold, 'rolled': rolled, 'success': False}, 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) - def post(self): - data = api.payload - errors = mutation_schema.validate(data) - if errors: - return errors, 400 - - conscore = data.get('conscore') - intscore = data.get('intscore') - - return roll_mutations(conscore, intscore), 200 - - -@api.route('/character/generate', methods=['POST']) -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') - - character_sheet = {} - if chartype == 'mutant': - character_sheet['animal-stock'] = (roll_mutant_animal()) - ability_scores = roll_ability_scores(chartype) - character_sheet['abilities'] = ability_scores - character_sheet['hp'] = roll_hp(chartype, ability_scores['constitution']) - character_sheet['gold'] = roll_dices(4, 4, False).get('result') * 10 - character_sheet['domar'] = roll_dices(2, 4, False).get('result') * 5 - - if chartype == 'humanoid' or chartype == 'mutant': - character_sheet['mutations'] = ( - roll_mutations(ability_scores['constitution'], ability_scores['intelligence']) - ) - - if chartype == 'cyborg': - character_sheet['cybermods'] = ( - roll_cybermods() - ) - - 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, attribute=None): - if attribute is None or attribute == "all": - ability_list = ['p-strength', 'm-strength', 'constitution', 'dexterity', 'charisma', 'intelligence'] - else: - ability_list = [attribute] - - ability_scores = {} - - for abil in ability_list: - if ((chartype == 'human') and - ((abil == 'intelligence') or - (abil == 'charisma') or - (abil == 'constitution'))): - roll = roll_dices(4, 6, False) # humans are special - if (abil == 'constitution') and roll["result"] > 18: - roll["capped"] = True - roll["result"] = 18 - if (abil == 'intelligence' or abil == 'charisma') and roll["result"] > 21: - roll["capped"] = True - roll["result"] = 21 - ability_scores[abil] = roll["result"] - else: - roll = roll_dices(4, 6, True) # nothing else is special - ability_scores[abil] = roll["result"] - - return ability_scores - - -def roll_hp(chartype, conscore): - if chartype == 'human': - geometry = 8 - else: - geometry = 6 - return roll_dices(conscore, geometry, False).get('result') - - -def roll_cybermods(): - """ - Cybermods are similar to mutations, except that they are artificial changes to the body, - where mutations are 'natural' changes to the body. Fewer cybermods are possible than with - mutations. So, you make one roll of 1d4, and then parse up the score between the two - types of mods, as you see fit. If no emphasis is specified, the score will be split as evenly - as possible. If the score is 1, then a physical mod will be the default. - :return: table of cybermods (in json format) - """ - cybermods_table = {} - - cybermods_count = roll_dices(1, 4, False)["result"] - phys_cnt = split_number(cybermods_count)[1] # This ends up being the higher of the two numbers - ment_cnt = split_number(cybermods_count)[0] - cybermods_table['count'] = {'mental': ment_cnt, 'physical': phys_cnt} - - mental_cybermods_scores = [] - physical_cybermods_scores = [] - - for _ in range(ment_cnt): - mscore = roll_dices(1, 10, False).get('result') - if mscore in mental_cybermods_scores: - mscore = roll_dices(1, 10, False).get('result') - mental_cybermods_scores.append(mscore) - - for _ in range(phys_cnt): - pscore = roll_dices(1, 10, False).get('result') - if pscore in physical_cybermods_scores: - pscore = roll_dices(1, 10, False).get('result') - physical_cybermods_scores.append(pscore) - - cyb = CyberMods() - mcyb = [] - pcyb = [] - - for score in mental_cybermods_scores: - modification = cyb.get_mental_cybermod(score) - mcyb.append(modification) - - if len(mcyb) != 0: - cybermods_table['mental'] = mcyb - - for score in physical_cybermods_scores: - modification = cyb.get_physical_cybermod(score) - pcyb.append(modification) - - if len(pcyb) != 0: - cybermods_table['physical'] = pcyb - - return cybermods_table - - -def roll_mutations(conscore, intscore): - """ - :param conscore: modifier for physical mutation determination - :param intscore: modifier for mental mutation determination - :return: table of mutations (in json form) - """ - mutations_table = {} - - mental_mutations_cnt = roll_dices(1, 4, False).get('result') - physical_mutations_cnt = roll_dices(1, 4, False).get('result') - mutations_table['count'] = {'mental': mental_mutations_cnt, 'physical': physical_mutations_cnt} - - mental_mutations_scores = get_score_list(mental_mutations_cnt, intscore) - mutations_table['mental'] = get_mutations(mental_mutations_scores) - - physical_mutations_scores = get_score_list(physical_mutations_cnt, conscore) - mutations_table['physical'] = get_mutations(physical_mutations_scores) - - return mutations_table - - -def get_mutations(scorelist): - mut_table = Mutations() - mut_list = [] - for score in scorelist: - mutation = mut_table.get_mental_mutation(score) - mut_list.append(mutation) - return mut_list - - -def get_score_list(count, modifier): - tmp_scores = [] - for _ in range(count): - roll = roll_dices(1, 100, False).get('result') + modifier - if roll in tmp_scores: - roll = roll_dices(1, 100, False).get('result') + modifier - if roll > 100: - roll = 100 - tmp_scores.append(roll) - - return tmp_scores - - -def roll_mutant_animal(): - creature = dict() - - animal_stock = ['badger', 'racoon', 'hound', 'wolf', 'big-cat', 'fox', - 'spider', 'lizard', 'ant', 'hornet', 'hawk', 'ostrich', 'emu', 'crocodile', - 'snake', 'rabbit', 'rat', 'bear', 'elephant', 'platypus', 'bull', 'horse', 'goat', 'bat', - 'silverfish', 'cockroach', 'turtle', 'gibbon', 'penguin', 'orangutan', 'chimpanzee', - 'housefly', 'lobster', 'crab', 'prawn', 'pig', 'chicken', 'duck', 'parrot', 'mouse', - 'heron', 'weasel', 'squirrel', 'pigeon', 'crow', 'house-cat', 'shark', 'dolphin', 'narwhal', - 'buffalo'] - - posture = ['upright', 'prone', 'mixed'] - - creature['stock'] = random.choice(animal_stock) - creature['posture'] = random.choice(posture) - - if creature['posture'] == 'upright' or creature['posture'] == 'mixed': - creature['arms'] = random.choice(list(range(2, 4, 2))) # if you're upright 4 is the limit - creature['legs'] = random.choice(list(range(2, 6, 2))) # same with legs - - if creature['posture'] == 'prone': - creature['legs'] = random.choice(list(range(4, 8, 2))) # if you're prone, you only get legs - - return creature - - -def split_number(n): - first_split = n // 2 # this will split the number in two equal parts, if it is even - second_split = n % 2 + first_split # this will add one to one part if the number is odd - return first_split, second_split - - -if __name__ == '__main__': - app.run() diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/app.py b/app/app.py new file mode 100644 index 0000000..04e6689 --- /dev/null +++ b/app/app.py @@ -0,0 +1,205 @@ +import random + +from flask import Flask +from flask_cors import CORS +from flask_restx import Api, Resource + +from app.functions.build_character_sheet import build_character_sheet +from app.functions.roll_ability_scores import roll_ability_scores +from app.functions.roll_ability_check import roll_ability_check +from app.functions.roll_dices import roll_dices +from app.functions.roll_encounter import roll_encounter +from app.functions.roll_mental_attack import roll_mental_attack +from app.functions.roll_mutations import roll_mutations +from app.models.models import dice_model, ability_model, hp_model, character_model, encounter_model, ma_model, \ + mutation_model, \ + check_model +from app.schemas.schemas import DiceSchema, CharacterSchema, EncounterSchema, MentalAttackSchema, AbilitySchema, \ + HPSchema, \ + MutationSchema, CheckSchema + +app = Flask(__name__) +CORS(app) + +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') +ma = api.namespace('ma', description='Mental Attack operations') +mut = api.namespace('mut', description='Mutation operations') +character = api.namespace('character', description='Character operations') +encounter = api.namespace('encounter', description='Encounter operations') +check = api.namespace('check', description='Check operations') + +check_model = check.model('Check', check_model) +check_schema = CheckSchema() + +ability_model = ability.model('Ability', ability_model) +ability_schema = AbilitySchema() + +mutation_model = mut.model('Mutation', mutation_model) +mutation_schema = MutationSchema() + +hp_model = hp.model('HP', hp_model) +hp_schema = HPSchema() + +dice_model = dice.model('Dice', dice_model) +dice_schema = DiceSchema() + +ma_model = ma.model('MA', ma_model) +ma_schema = MentalAttackSchema() + +character_model = character.model('Character', character_model) +character_schema = CharacterSchema() + +encounter_model = encounter.model('Encounter', encounter_model) +encounter_schema = EncounterSchema() + + +@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 + errors = ability_schema.validate(data) + if errors: + return errors, 400 + + chartype = data.get('chartype') + attribute = data.get('ability') + + return roll_ability_scores(chartype, attribute), 200 + + +@api.route('/roll/hp', methods=['POST']) +class RollHP(Resource): + @hp.expect(hp_model) + def post(self): + data = api.payload + errors = hp_schema.validate(data) + if errors: + return errors, 400 + 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/encounter', methods=['POST']) +class RollEncounter(Resource): + @encounter.expect(encounter_model) + def post(self): + data = api.payload + errors = encounter_schema.validate(data) + if errors: + return errors, 400 + + terrain = data.get('terrain').lower() + return roll_encounter(terrain), 200 + + +@api.route('/roll/attack/mental', methods=['POST']) +class RollMentalAttack(Resource): + @ma.expect(ma_model) + def post(self): + data = api.payload + errors = ma_schema.validate(data) + if errors: + return errors, 400 + + ams = data.get('ams') + dms = data.get('dms') + modifier = data.get('modifier') + + return roll_mental_attack(ams, dms, modifier), 200 + + +@api.route('/roll/check', methods=['POST']) +class RollCheck(Resource): + @check.expect(check_model) + def post(self): + data = api.payload + errors = check_schema.validate(data) + if errors: + return errors, 400 + + ability_score = data.get('ability_score') + multiplier = data.get('multiplier') + + 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) + def post(self): + data = api.payload + errors = mutation_schema.validate(data) + if errors: + return errors, 400 + + conscore = data.get('conscore') + intscore = data.get('intscore') + + return roll_mutations(conscore, intscore), 200 + + +@api.route('/character/generate', methods=['POST']) +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') + return build_character_sheet(chartype), 200 + + +if __name__ == '__main__': + app.run() diff --git a/app/functions/__init__.py b/app/functions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/functions/build_character_sheet.py b/app/functions/build_character_sheet.py new file mode 100644 index 0000000..121c1b5 --- /dev/null +++ b/app/functions/build_character_sheet.py @@ -0,0 +1,36 @@ +from app.functions.roll_dices import roll_dices +from app.functions.roll_ability_scores import roll_ability_scores +from app.functions.roll_cybermods import roll_cybermods +from app.functions.roll_mutations import roll_mutations + +from app.functions.roll_hp import roll_hp +from app.functions.generate_profile import generate_profile +from app.functions.roll_mutant_animal import roll_mutant_animal + + +def build_character_sheet(chartype): + character_sheet = dict() + + character_sheet['profile'] = generate_profile(chartype) + + if chartype == 'mutant': + character_sheet['animal-stock'] = (roll_mutant_animal()) + + ability_scores = roll_ability_scores(chartype) + character_sheet['abilities'] = ability_scores + character_sheet['hp'] = roll_hp(chartype, ability_scores['constitution']) + + character_sheet['gold'] = roll_dices(4, 4, False).get('result') * 10 + character_sheet['domar'] = roll_dices(2, 4, False).get('result') * 5 + + if chartype == 'humanoid' or chartype == 'mutant': + character_sheet['mutations'] = ( + roll_mutations(ability_scores['constitution'], ability_scores['intelligence']) + ) + + if chartype == 'cyborg': + character_sheet['cybermods'] = ( + roll_cybermods() + ) + + return character_sheet diff --git a/app/functions/generate_profile.py b/app/functions/generate_profile.py new file mode 100644 index 0000000..ded0957 --- /dev/null +++ b/app/functions/generate_profile.py @@ -0,0 +1,16 @@ +import random +from app.functions.get_character_age import get_age + + +def generate_profile(chartype): + profile = { + 'name': "Anon", + 'sex': random.choice(['male', 'female', 'male', 'female', 'male', 'female']), + 'age': get_age(chartype) + } + if chartype == 'human': + profile['hair'] = random.choice(['black', 'brown', 'auburn', 'blonde', 'copper', 'brass', 'blue', 'bald']) + profile['eyes'] = random.choice(['blue', 'brown', 'green', 'hazel', 'gray']) + profile['skintone'] = random.choice(['dark', 'olive', 'medium', 'light', 'pale']) + + return profile diff --git a/app/functions/get_character_age.py b/app/functions/get_character_age.py new file mode 100644 index 0000000..2db4c48 --- /dev/null +++ b/app/functions/get_character_age.py @@ -0,0 +1,36 @@ +import random + + +def get_age(chartype): + ages = { + "human": { + 'youth': (14, 18), + 'young': (19, 38), + 'midage': (39, 58), + 'veteran': (59, 72) + }, + "humanoid": { + 'youth': (8, 19), + 'young': (20, 48), + 'midage': (49, 68), + 'veteran': (69, 82) + }, + "mutant": { + 'youth': (8, 13), + 'young': (14, 28), + 'midage': (29, 48), + 'veteran': (49, 62) + }, + "cyborg": { + 'youth': (14, 18), + 'young': (19, 38), + 'midage': (39, 58), + 'veteran': (59, 72) + } + } + + char_range = ages[chartype] + age_group = random.choice(list(char_range.keys())) + age_range = char_range[age_group] + + return random.choice(age_range) diff --git a/app/functions/get_mutations.py b/app/functions/get_mutations.py new file mode 100644 index 0000000..80f9a70 --- /dev/null +++ b/app/functions/get_mutations.py @@ -0,0 +1,10 @@ +from app.tables.mutations import Mutations + + +def get_mutations(scorelist): + mut_table = Mutations() + mut_list = [] + for score in scorelist: + mutation = mut_table.get_mental_mutation(score) + mut_list.append(mutation) + return mut_list diff --git a/app/functions/get_score_list.py b/app/functions/get_score_list.py new file mode 100644 index 0000000..7c8c9fa --- /dev/null +++ b/app/functions/get_score_list.py @@ -0,0 +1,14 @@ +from app.functions.roll_dices import roll_dices + + +def get_score_list(count, modifier): + tmp_scores = [] + for _ in range(count): + roll = roll_dices(1, 100, False).get('result') + modifier + if roll in tmp_scores: + roll = roll_dices(1, 100, False).get('result') + modifier + if roll > 100: + roll = 100 + tmp_scores.append(roll) + + return tmp_scores \ No newline at end of file diff --git a/app/functions/roll_ability_check.py b/app/functions/roll_ability_check.py new file mode 100644 index 0000000..fe4595f --- /dev/null +++ b/app/functions/roll_ability_check.py @@ -0,0 +1,10 @@ +from app.functions.roll_dices import roll_dices + + +def roll_ability_check(score, multiplier): + threshold = score * multiplier + rolled = roll_dices(1, 100, False).get('result') + if rolled < threshold: + return {'threshold': threshold, 'rolled': rolled, 'success': True}, 200 + else: + return {'threshold': threshold, 'rolled': rolled, 'success': False}, 200 diff --git a/app/functions/roll_ability_scores.py b/app/functions/roll_ability_scores.py new file mode 100644 index 0000000..19bd53f --- /dev/null +++ b/app/functions/roll_ability_scores.py @@ -0,0 +1,29 @@ +from app.functions.roll_dices import roll_dices + + +def roll_ability_scores(chartype, attribute=None): + if attribute is None or attribute == "all": + ability_list = ['p-strength', 'm-strength', 'constitution', 'dexterity', 'charisma', 'intelligence'] + else: + ability_list = [attribute] + + ability_scores = {} + + for abil in ability_list: + if ((chartype == 'human') and + ((abil == 'intelligence') or + (abil == 'charisma') or + (abil == 'constitution'))): + roll = roll_dices(4, 6, False) # humans are special + if (abil == 'constitution') and roll["result"] > 18: + roll["capped"] = True + roll["result"] = 18 + if (abil == 'intelligence' or abil == 'charisma') and roll["result"] > 21: + roll["capped"] = True + roll["result"] = 21 + ability_scores[abil] = roll["result"] + else: + roll = roll_dices(4, 6, True) # nothing else is special + ability_scores[abil] = roll["result"] + + return ability_scores diff --git a/app/functions/roll_cybermods.py b/app/functions/roll_cybermods.py new file mode 100644 index 0000000..270ce6a --- /dev/null +++ b/app/functions/roll_cybermods.py @@ -0,0 +1,55 @@ +from app.functions.roll_dices import roll_dices +from app.tables.cybermods import CyberMods +from app.functions.split_number import split_number + + +def roll_cybermods(): + """ + Cybermods are similar to mutations, except that they are artificial changes to the body, + where mutations are 'natural' changes to the body. Fewer cybermods are possible than with + mutations. So, you make one roll of 1d4, and then parse up the score between the two + types of mods, as you see fit. If no emphasis is specified, the score will be split as evenly + as possible. If the score is 1, then a physical mod will be the default. + :return: table of cybermods (in json format) + """ + cybermods_table = {} + + cybermods_count = roll_dices(1, 4, False)["result"] + phys_cnt = split_number(cybermods_count)[1] # This ends up being the higher of the two numbers + ment_cnt = split_number(cybermods_count)[0] + cybermods_table['count'] = {'mental': ment_cnt, 'physical': phys_cnt} + + mental_cybermods_scores = [] + physical_cybermods_scores = [] + + for _ in range(ment_cnt): + mscore = roll_dices(1, 10, False).get('result') + if mscore in mental_cybermods_scores: + mscore = roll_dices(1, 10, False).get('result') + mental_cybermods_scores.append(mscore) + + for _ in range(phys_cnt): + pscore = roll_dices(1, 10, False).get('result') + if pscore in physical_cybermods_scores: + pscore = roll_dices(1, 10, False).get('result') + physical_cybermods_scores.append(pscore) + + cyb = CyberMods() + mcyb = [] + pcyb = [] + + for score in mental_cybermods_scores: + modification = cyb.get_mental_cybermod(score) + mcyb.append(modification) + + if len(mcyb) != 0: + cybermods_table['mental'] = mcyb + + for score in physical_cybermods_scores: + modification = cyb.get_physical_cybermod(score) + pcyb.append(modification) + + if len(pcyb) != 0: + cybermods_table['physical'] = pcyb + + return cybermods_table diff --git a/app/functions/roll_dices.py b/app/functions/roll_dices.py new file mode 100644 index 0000000..e769dcf --- /dev/null +++ b/app/functions/roll_dices.py @@ -0,0 +1,32 @@ +import random + + +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 diff --git a/app/functions/roll_encounter.py b/app/functions/roll_encounter.py new file mode 100644 index 0000000..56ba851 --- /dev/null +++ b/app/functions/roll_encounter.py @@ -0,0 +1,9 @@ +from app.functions.roll_dices import roll_dices +from app.tables.encounters import EncounterTable + + +def roll_encounter(terrain): + roll = roll_dices(1, 20, False).get('result') + et = EncounterTable() + creature = et.get_encounter(roll, terrain) + return {"encounter": creature} diff --git a/app/functions/roll_hp.py b/app/functions/roll_hp.py new file mode 100644 index 0000000..5f82a7f --- /dev/null +++ b/app/functions/roll_hp.py @@ -0,0 +1,9 @@ +from app.functions.roll_dices import roll_dices + + +def roll_hp(chartype, conscore): + if chartype == 'human': + geometry = 8 + else: + geometry = 6 + return roll_dices(conscore, geometry, False).get('result') diff --git a/app/functions/roll_mental_attack.py b/app/functions/roll_mental_attack.py new file mode 100644 index 0000000..39c35f2 --- /dev/null +++ b/app/functions/roll_mental_attack.py @@ -0,0 +1,35 @@ +from app.functions.roll_dices import roll_dices +from app.tables.mentattack import MentalAttackMatrix + + +def roll_mental_attack(ams, dms, modifier): + + result = {} + 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 + + 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 diff --git a/app/functions/roll_mutant_animal.py b/app/functions/roll_mutant_animal.py new file mode 100644 index 0000000..fe8acf4 --- /dev/null +++ b/app/functions/roll_mutant_animal.py @@ -0,0 +1,27 @@ +import random + + +def roll_mutant_animal(): + creature = dict() + + animal_stock = ['badger', 'racoon', 'hound', 'wolf', 'big-cat', 'fox', + 'spider', 'lizard', 'ant', 'hornet', 'hawk', 'ostrich', 'emu', 'crocodile', + 'snake', 'rabbit', 'rat', 'bear', 'elephant', 'platypus', 'bull', 'horse', 'goat', 'bat', + 'silverfish', 'cockroach', 'turtle', 'gibbon', 'penguin', 'orangutan', 'chimpanzee', + 'housefly', 'lobster', 'crab', 'prawn', 'pig', 'chicken', 'duck', 'parrot', 'mouse', + 'heron', 'weasel', 'squirrel', 'pigeon', 'crow', 'house-cat', 'shark', 'dolphin', 'narwhal', + 'buffalo'] + + posture = ['upright', 'prone', 'mixed'] + + creature['stock'] = random.choice(animal_stock) + creature['posture'] = random.choice(posture) + + if creature['posture'] == 'upright' or creature['posture'] == 'mixed': + creature['arms'] = random.choice(list(range(2, 4, 2))) # if you're upright 4 is the limit + creature['legs'] = random.choice(list(range(2, 6, 2))) # same with legs + + if creature['posture'] == 'prone': + creature['legs'] = random.choice(list(range(4, 8, 2))) # if you're prone, you only get legs + + return creature diff --git a/app/functions/roll_mutations.py b/app/functions/roll_mutations.py new file mode 100644 index 0000000..c634b6a --- /dev/null +++ b/app/functions/roll_mutations.py @@ -0,0 +1,24 @@ +from app.functions.get_mutations import get_mutations +from app.functions.get_score_list import get_score_list +from app.functions.roll_dices import roll_dices + + +def roll_mutations(conscore, intscore): + """ + :param conscore: modifier for physical mutation determination + :param intscore: modifier for mental mutation determination + :return: table of mutations (in json form) + """ + mutations_table = {} + + mental_mutations_cnt = roll_dices(1, 4, False).get('result') + physical_mutations_cnt = roll_dices(1, 4, False).get('result') + mutations_table['count'] = {'mental': mental_mutations_cnt, 'physical': physical_mutations_cnt} + + mental_mutations_scores = get_score_list(mental_mutations_cnt, intscore) + mutations_table['mental'] = get_mutations(mental_mutations_scores) + + physical_mutations_scores = get_score_list(physical_mutations_cnt, conscore) + mutations_table['physical'] = get_mutations(physical_mutations_scores) + + return mutations_table diff --git a/app/functions/split_number.py b/app/functions/split_number.py new file mode 100644 index 0000000..6c46fd3 --- /dev/null +++ b/app/functions/split_number.py @@ -0,0 +1,4 @@ +def split_number(n): + first_split = n // 2 # this will split the number in two equal parts, if it is even + second_split = n % 2 + first_split # this will add one to one part if the number is odd + return first_split, second_split diff --git a/app/models/__init__.py b/app/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models.py b/app/models/models.py similarity index 82% rename from models.py rename to app/models/models.py index 00ba45b..427384e 100644 --- a/models.py +++ b/app/models/models.py @@ -67,9 +67,26 @@ hp_model = { } ma_model = { - 'ams': fields.Integer(required=True, description='Attacker Mental Strength'), - 'dms': fields.Integer(required=True, description='Defender Mental Strength'), - 'modifier': fields.Integer(required=True, description='Modifier For Mental Attack Roll'), + 'ams': fields.Integer( + required=True, + min=3, + max=18, + default=10, + description='Attacker Mental Strength' + ), + 'dms': fields.Integer( + required=True, + min=3, + max=18, + default=10, + description='Defender Mental Strength' + ), + 'modifier': fields.Integer( + required=False, + min=-100, + max=100, + default=0, + description='Modifier For Mental Attack Roll'), } character_model = { diff --git a/app/schemas/__init__.py b/app/schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/schemas.py b/app/schemas/schemas.py similarity index 100% rename from schemas.py rename to app/schemas/schemas.py diff --git a/app/tables/__init__.py b/app/tables/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cybermods.py b/app/tables/cybermods.py similarity index 100% rename from cybermods.py rename to app/tables/cybermods.py diff --git a/encounters.py b/app/tables/encounters.py similarity index 100% rename from encounters.py rename to app/tables/encounters.py diff --git a/mentattack.py b/app/tables/mentattack.py similarity index 100% rename from mentattack.py rename to app/tables/mentattack.py diff --git a/mutations.py b/app/tables/mutations.py similarity index 100% rename from mutations.py rename to app/tables/mutations.py diff --git a/physattack.py b/app/tables/physattack.py similarity index 100% rename from physattack.py rename to app/tables/physattack.py