# in mygame/world/mychargen.py import math from evennia.utils.evmenu import EvMenu from evennia import EvForm, EvTable from world import basestats from world import skills as skillmodule # Menu functions def start(caller): text = "" if requiredPropertiesSet(caller): text = \ """ %s %s %s %s """ % (checkmark(caller,"name"), checkmark(caller,"age"), checkmark(caller,"sex"), checkmark(caller, "alignment")) else: text = \ """ |b|[yUnfinished required properties for character:|n %s %s %s %s """ % (checkmark(caller,"name"), checkmark(caller,"age"), checkmark(caller,"sex"), checkmark(caller, "alignment")) options = ({"desc": "Set character name", "goto": "enter_name"}, {"desc": "Set character age", "goto": "enter_age"}, {"desc": "Set attributes", "goto": "set_attribs"}, {"desc": "Set alignement", "goto": "set_align"}, {"desc": "Set your characters gender", "goto": "set_sex"}, {"desc": "Goto skills", "goto": "skill_menu"}, {"desc": "Save and exit", "goto": _save_prefs}) return text, options def set_attribs(caller): text = \ """ Character attributes, abilities and preferences menu. Choose the attribute you want to modify You have |w%i|n attribute points left to spend """ % caller.db.attribpoints options = ({"desc": "Modify strength (|w%i|n - |y%s|n)" % (caller.db.strength, basestats.getStatDescription(caller.db.strength, 'ST')), "goto": ("startMod", {"attr": "ST"})}, {"desc": "Modify dexterity (|w%i|n - |y%s|n)" % (caller.db.dexterity, basestats.getStatDescription(caller.db.dexterity, 'DX')), "goto": ("startMod", {"attr": "DX"})}, {"desc": "Modify intelligence (|w%i|n - |y%s|n)" % (caller.db.intelligence, basestats.getStatDescription(caller.db.intelligence, 'IQ')), "goto": ("startMod", {"attr": "IQ"})}, {"desc": "Modify health (|w%i|n - |y%s|n)" % (caller.db.health, basestats.getStatDescription(caller.db.health, 'HT')), "goto": ("startMod", {"attr": "HT"})}, {"desc": "Help", "goto": "attrib_help"}, {"desc": "Back to main menu", "goto": "start"}) return text, options def attrib_help(caller): text = \ """ |yHow to select basic attributes|n The basic attributes you select, will determine your abilities including your strengths and weaknesses throughout the game. Choose wisely. |yExplenation of the point system|n Each attribute has a numerical point assigned to them. The more points invested in the attribute, the better your characters abilities towards that particular property will be: |c6 or less:|n An attribute with this few points severly constraint your lifestyle. It is crippeling |c7:|n Anyone observing your character will immidiatly notice your limitations in the attribute. It is considered the lowest score you can have and still pass for "able bodied". |c8 or 9:|n This is considered below average. It is limiting, but considered within the human norm. |c10:|n Most humans get by just fine with a score of 10, it's considered average. 10 is the base starting stat of every attribute of player characters. |c11 or 12:|n These scores are superior and considered above average. But also within the norm |c13 or 14:|n We are starting to move into the realm of what is considered exceptional with stats such as these. Characters with these stats will draw attention from their surroundings, and their qualities are apparent. |c15 and above:|n Characters who possess this much points in any given attribute will immidiatly draw attention from anyone, as they are considered amazing. """ options = ({"desc": "Back to attribute editor", "goto": "set_attribs"}) return text, options def set_align(caller): text = \ """ Character attributes, abilities and preferences menu. Choose the moral compass of your character |wCurrent alignment:|n %s """ % getCurrentAlignement(caller) options = ({"desc": "My character is good", "goto": _set_good}, {"desc": "My character is neutral", "goto": _set_neutral}, {"desc": "My character is evil", "goto": _set_evil}, {"desc": "Back to main menu", "goto": "start"}) return text, options def set_sex(caller): text = \ """ Character attributes, abilities and preferences menu. Choose the gender of your character |wCurrent gender:|n %s """ % getCurrentGender(caller) options = ({"desc": "Male sex", "goto": _set_male}, {"desc": "Female sex", "goto": _set_female}, {"desc": "Transgender", "goto": _set_trans}, {"desc": "Back to main menu", "goto": "start"}) return text, options def skill_menu(caller): text = \ """ |wSkills menu|n Here you can choose from a set of available skills for your character to learn """ options = ({"desc": "Add skills", "goto": "add_skills"}, {"desc": "View current skills", "goto": "view_current_skills"}, {"desc": "Back to main menu", "goto": "start"}) return text, options def add_skills(caller, raw_string, **kwargs): text = \ """ |wSkills menu|n Choose which category of skills you want to learn """ options = ({"desc": "Animal skills", "goto": "view_animal_skills"}, {"desc": "Artistic skills", "goto": "view_artistic_skills"}, {"desc": "Athletic skills", "goto": "view_athletic_skills"}, {"desc": "Back to skill menu", "goto": "skill_menu"}) return text, options # Modify skill functions def enter_skill_typename(caller, raw_string, **kwargs): skillId = kwargs.get("skillId") typeName = kwargs.get("typeName") cat = kwargs.get("category") text = \ """ %s Type the name of the |y%s|n or to cancel """ % (skillmodule.getTypeDescription(skillId), skillmodule.getTypenameOfSkill(skillId).lower()) options = { "key": "_default", "goto": (_enter_skill, { "typeName": "Typed", "skillId": skillId, "category": cat }) } return text, options def _enter_skill(caller, raw_string, **kwargs): typeSpecified = True typeName = kwargs.get("typeName") number = kwargs.get("skillId") category = kwargs.get("category") if not typeName: typeSpecified = False cat = kwargs.get("category") if not raw_string: return "add_skills" else: if not typeSpecified: try: number = int(raw_string.strip()) except ValueError: caller.msg("|rYou must enter a numerical value!|n") return category if skillmodule.idInCategory(caller, cat, number): if skillmodule.checkPrerequisite(caller, number): if skillmodule.hasTypes(caller, number) and typeSpecified == False: return "enter_skill_typename", {"skillId": number, "category": cat} else: caller.msg("Adding skill '|y%s|n' to your character" % skillmodule.getSkillName(number)) skillmodule.addSkill(caller, number) if typeSpecified: skillmodule.addSkillTypeName(caller,number, typeName) else: caller.msg("The skill '|y%s|n' depends on having learned '|c%s|n'. Try adding it first" % (skillmodule.getSkillName(number), skillmodule.prerequisiteName(caller, number))) return "add_skills" else: caller.msg("|rNo such skill in the table|n") return category def view_animal_skills(caller, raw_string, **kwargs): text = \ """ Choose the skill you want to add by typing in the numerical (#) number from the table below or to return to the category select menu %s You have |y%i|n points left to use on your skills """ % (skillmodule.skillTable(caller, "Animal Skills"), caller.db.attribpoints) options = { "key": "_default", "goto": (_enter_skill, { "category": "Animal Skills" }) } return text, options def view_artistic_skills(caller, raw_string, **kwargs): text = \ """ Choose the skill you want to add by typing in the numerical (#) number from the table below or to return to the category select menu %s You have |y%i|n points left to use on your skills """ % (skillmodule.skillTable(caller, "Artistic Skills"), caller.db.attribpoints) options = { "key": "_default", "goto": (_enter_skill, { "category": "Artistic Skills" }) } return text, options def view_athletic_skills(caller, raw_string, **kwargs): text = \ """ Choose the skill you want to add by typing in the numerical (#) number from the table below or to return to the category select menu %s You have |y%i|n points left to use on your skills """ % (skillmodule.skillTable(caller, "Athletic Skills"), caller.db.attribpoints) options = { "key": "_default", "goto": (_enter_skill, { "category": "Athletic Skills" }) } return text, options def _remove_skill(caller, raw_string, **kwargs): skillId = kwargs.get("skillId") if skillmodule.removeFromCharacter(caller, skillId): caller.msg("The skill: '|y%s|n' has been removed from your character" % skillmodule.getSkillName(skillId)) else: caller.msg("The skill '|y%s|n' is required by '|c%s|n'. Remove that skill first" % (skillmodule.getSkillName(skillId), skillmodule.requiredByName(caller,skillId))) return "view_current_skills" def _set_level_skill(caller, raw_string, **kwargs): number = kwargs.get("skillId") updown = kwargs.get("updown") if not raw_string: return "view_current_skills" if not updown: updownChoice = raw_string.strip() if updownChoice.lower() not in ("d","down","u","up"): caller.msg(f"|rYou must type either 'd' for down (or 'down') or 'u' for up (or 'up')|n") return "level_skill" elif updownChoice.lower() in ("d", "down") and caller.db.skillevel[number] < 2: caller.msg(f"|rYour %s skill is already at the lowest permissable level|n" % skillmodule.getSkillName(number)) return "level_skill" elif updownChoice.lower() in ("u", "up") and caller.db.skillevel[number] > 9: caller.msg(f"|rYour %s skill is already at the highest permissable level|n") return "level_skill" return "level_skill", {"updown": updownChoice, "skillId": number} else: try: upDownNumber = int(raw_string.strip()) except ValueError: caller.msg(f"|rThe value you want to increase/decrease your level to, has to be a number|n") return "level_skill" oldLevel = caller.db.skillevel[number] if updown.lower() in ("d", "down"): newLevel = oldLevel - upDownNumber if newLevel < 1: caller.msg(f"|rYou cannot decrease your level below 1|n") return "level_skill" costDown = skillmodule.getSkillCost(number, oldLevel) - skillmodule.getSkillCost(number, newLevel) caller.db.skillevel[number] = newLevel caller.db.attribpoints = caller.db.attribpoints + costDown caller.msg(f"You have |gsuccessfully|n changed the level of the skill |y%s|n to |c%i|n, refunding you |g%i|n skillpoints to your attribute point pool" % (skillmodule.getSkillName(number), newLevel, costDown)) else: newLevel = oldLevel + upDownNumber if newLevel > 10: caller.msg(f"|rYou cannot increase your level above 10|n") return "level_skill" costUp = skillmodule.getSkillCost(number, newLevel) - skillmodule.getSkillCost(number, oldLevel) caller.db.skillevel[number] = newLevel caller.db.attribpoints = caller.db.attribpoints - costUp caller.msg(f"You have |gsuccessfully|n changed the level of the skill |y%s|n to |c%i|n, costing you |g%i|n skillpoints from your attribute point pool" % (skillmodule.getSkillName(number), newLevel, costUp)) return "view_current_skills" def level_skill(caller, raw_string, **kwargs): number = kwargs.get("skillId") updown = kwargs.get("updown") text = "" if not updown: text = \ """ Leveling the skill '|y%s|n' with |c%i|n points left to use Choose if you want to level this skill (|wu|n)p or (|wd|n)own """ % (skillmodule.getSkillName(number), caller.db.attribpoints) elif updown.lower() in ('u','up'): text = \ """ Leveling the skill '|y%s|n' with |c%i|n points left to use Type in the number you want to |wincrease|n your level to or to return to the skill overview menu """ % (skillmodule.getSkillName(number), caller.db.attribpoints) else: text = \ """ Leveling the skill '|y%s|n' with |c%i|n points left to use Type in the number you want to |wdecrease|n your level to or to return to the skill overview menu """ % (skillmodule.getSkillName(number), caller.db.attribpoints) options = { "key": "_default", "goto": (_set_level_skill, { "skillId": number, "updown": updown }) } return text, options def edit_skill(caller, raw_string, **kwargs): number = kwargs.get("skillId") text = \ """ Editing skill: '|y%s|n' """ % skillmodule.getSkillName(number) options = ( { "desc": "Remove this skill", "goto": (_remove_skill, { "skillId": number }) }, { "desc": "Level skill up/down", "goto": ("level_skill", { "skillId": number }) }, { "desc": "Return to character skills menu", "goto": "view_current_skills" }, { "desc": "Return to main skill menu", "goto": "skill_menu" } ) return text, options def _edit_skill_check(caller, raw_string, **kwargs): if not raw_string: return "skill_menu" else: try: number = int(raw_string.strip()) except ValueError: caller.msg("|rYou must enter a numerical value!|n") return "view_current_skills" if skillmodule.skillInCharacter(caller, number): return "edit_skill", {"skillId": number} else: caller.msg("|rNo such skill in the table|n") return "view_current_skills" def view_current_skills(caller, raw_string, **kwargs): text = \ """ These are the skills your character currently has. Type in the number of the skill to edit it (edit the level of the skill, other properties or just to remove it alltogether) Press to just cancel and return to the previous menu %s """ % skillmodule.characterSkills(caller) options = { "key": "_default", "goto": (_edit_skill_check, { "category": "Animal Skills" }) } return text, options # Modify attribute functions def _enter_points(caller, raw_string, **kwargs): attr = kwargs.get("attr") calcType = kwargs.get("type") points = raw_string.strip() newVal = 0 cost = 0 pointsGain = 0 try: pointsNumber = int(points) except ValueError: caller.msg(f"You must supply a number") return "set_attribs" if calcType == "add": if validAddition(caller, attr, pointsNumber): if caller.db.attribpoints == 0: caller.msg(f"|wNo more attribute points to spend|n\nYou can remove points from other attributes and\nredistribute them later") return "set_attribs" if attr == "ST": cost = basestats.getStatCost(caller.db.strength, caller.db.strength + pointsNumber) if cost > caller.db.attribpoints: caller.msg(f"|rThe increase in the %s stat costs more than your available point pool \n(%i total cost|n)" % (attr, cost)) return "set_attribs" caller.db.strength = caller.db.strength + pointsNumber newVal = caller.db.strength elif attr == "DX": cost = basestats.getStatCost(caller.db.dexterity, caller.db.dexterity + pointsNumber) if cost > caller.db.attribpoints: caller.msg(f"|rThe increase in the %s stat costs more than your available point pool \n(%i total cost|n)" % (attr, cost)) return "set_attribs" caller.db.dexterity = caller.db.dexterity + pointsNumber newVal = caller.db.dexterity elif attr == "IQ": cost = basestats.getStatCost(caller.db.intelligence, caller.db.intelligence + pointsNumber) if cost > caller.db.attribpoints: caller.msg(f"|rThe increase in the %s stat costs more than your available point pool \n(%i total cost|n))" % (attr, cost)) return "set_attribs" caller.db.intelligence = caller.db.intelligence + pointsNumber newVal = caller.db.intelligence elif attr == "HT": cost = basestats.getStatCost(caller.db.health, caller.db.health + pointsNumber) if cost > caller.db.attribpoints: caller.msg(f"|rThe increase in the %s stat costs more than your available point pool \n(%i total cost|n)" % (attr, cost)) return "set_attribs" caller.db.health = caller.db.health + pointsNumber newVal = caller.db.health if cost >= 0: caller.db.attribpoints = caller.db.attribpoints - cost else: caller.db.attribpoints = caller.db.attribpoints + cost else: caller.msg(f"|rYou cannot increase your %s attribute by that much!|n" % attr) return "set_attribs" else: if validSubtraction(caller, attr, pointsNumber): if attr == "ST": pointsGain = basestats.getStatCost(caller.db.strength, caller.db.strength - pointsNumber) caller.db.strength = caller.db.strength - pointsNumber newVal = caller.db.strength elif attr == "DX": pointsGain = basestats.getStatCost(caller.db.dexterity, caller.db.dexterity - pointsNumber) caller.db.dexterity = caller.db.dexterity - pointsNumber newVal = caller.db.dexterity elif attr == "IQ": pointsGain = basestats.getStatCost(caller.db.intelligence, caller.db.intelligence - pointsNumber) caller.db.intelligence = caller.db.intelligence - pointsNumber newVal = caller.db.intelligence elif attr == "HT": pointsGain = basestats.getStatCost(caller.db.health, caller.db.health - pointsNumber) caller.db.health = caller.db.health - pointsNumber newVal = caller.db.health caller.db.attribpoints = caller.db.attribpoints + pointsGain else: caller.msg(f"|rYou cannot decrease your %s attribute by that much!|n" % attr) return "set_attribs" caller.msg(f"Your |y%s|n stat has been changed to %i" % (attr, newVal)) return "set_attribs" def startMod(caller, **kwargs): attr = kwargs.get("attr") text = "Do you want to |y(s)|nubtract or |y(a)|ndd to the attribute |w%s|n?" % attr options = {"key": "_default", "goto": ("checkType", {"attr": attr})} return text, options def checkType(caller, raw_string, **kwargs): attr = kwargs.get("attr") if raw_string.lower() in ("s", "subtract"): text = "Enter amount of |y%s|n points you want to subtract" % attr options = {"key": "_default", "goto": (_enter_points, {"attr": attr, "type": "subtract"})} return text, options elif raw_string.lower() in ("a", "add"): text = "Enter amount of |y%s|n points you want to add" % attr options = {"key": "_default", "goto": (_enter_points, {"attr": attr, "type": "add"})} return text, options else: text = "I did not understand that reply. Type 'a' or 'add' \nto add or 's' or 'subtract' to subtract" options = {"key": "_default", "goto": ("startMod", {"attr": attr})} return text, options # Modify alignement functions def _set_good(caller): caller.db.alignment = 0 return "set_align" def _set_neutral(caller): caller.db.alignment = 1 return "set_align" def _set_evil(caller): caller.db.alignment = 2 return "set_align" # Modify gender functions def _set_male(caller): caller.db.gender = 0 return "set_sex" def _set_female(caller): caller.db.gender = 1 return "set_sex" def _set_trans(caller): caller.db.gender = 2 return "set_sex" # Modify name functions def _set_name(caller, raw_string, **kwargs): inp = raw_string.strip() prev_entry = kwargs.get("prev_entry") if not inp: # a blank input either means OK or Abort if prev_entry: caller.key = prev_entry caller.msg("Name set to %s." % prev_entry) caller.db.name = prev_entry return "start" else: caller.msg("Aborted. No name set") return "start" else: # re-run old node, but pass in the name given return None, {"prev_entry": inp} def enter_name(caller, raw_string, **kwargs): # check if we already entered a name before prev_entry = kwargs.get("prev_entry") if not prev_entry and caller.db.name != "NO_NAME": prev_entry = caller.db.name if prev_entry: text = "Current name: %s.\nEnter another name or to accept." % prev_entry else: text = "Enter your character's name or to abort." options = {"key": "_default", "goto": (_set_name, {"prev_entry": prev_entry})} return text, options # Modify age functions def _set_age(caller, raw_string): if not raw_string: return "start" try: valueAge = int(raw_string) except ValueError: caller.msg(f"|rYou must type a number|n") return "start" if validAge(caller, valueAge): caller.msg(f"Age set to %i" % valueAge) caller.db.age = valueAge return "start" def enter_age(caller, raw_string): text = "Enter age or press to cancel" options = {"key": "_default", "goto": _set_age} return text,options # Helper functions def validSubtraction(caller, attribute, points): value = 0 if attribute == "ST": value = caller.db.strength - points elif attribute == "IQ": value = caller.db.intelligence - points elif attribute == "DX": value = caller.db.dexterity - points elif attribute == "HT": value = caller.db.health - points if value < 1: return False else: return True def validAddition(caller, attribute, points): value = 0 if attribute == "ST": value = caller.db.strength + points elif attribute == "IQ": value = caller.db.intelligence + points elif attribute == "DX": value = caller.db.dexterity + points elif attribute == "HT": value = caller.db.health + points if value > 20: return False else: return True def getCurrentAlignement(caller): alignment = "" if caller.db.alignment == 0: alignement = "Good" elif caller.db.alignment == 1: alignement = "Neutral" elif caller.db.alignment == 2: alignement = "Evil" else: alignement = "|wUndefined|n" return alignement def getCurrentGender(caller): gender = "" if caller.db.gender == 0: gender = "Male" elif caller.db.gender == 1: gender = "Female" elif caller.db.gender == 2: gender = "Transgender" else: gender = "Undefined" return gender def checkmark(caller, field): if field == "name": if caller.db.name == "NO_NAME": return "|r[ ]|n No name for character set" else: return "|g[X]|n |wName: %s|n" % caller.db.name elif field == "age": if caller.db.age < 1: return "|r[ ]|n Character has no age" else: return "|g[X]|n |wAge: %i|n" % caller.db.age elif field == "sex": if caller.db.gender == 4: return "|r[ ]|n No gender defined" else: return "|g[X]|n |wGender: %s|n" % getCurrentGender(caller) elif field == "alignment": if caller.db.alignment == 4: return "|r[ ]|n Character has no moral alignment" else: return "|g[X]|n |wAlignment: %s|n" % getCurrentAlignement(caller) def validAge(caller, age): if age < 18 or age > 60: caller.msg(f"|rInvalid value for age, it has to be between 18 and 60") return False else: return True def requiredPropertiesSet(caller): if caller.db.name == "NO_NAME": return False if caller.db.age < 1: return False if caller.db.gender == 4: return False if caller.db.alignment == 4: return False return True # Finalizing character creation functions def _save_prefs(caller, raw_string, **kwargs): if not requiredPropertiesSet(caller): caller.msg(f"|rYou have not filled out the required character fields|n") return "start" form = EvForm("world/charsheetform.py") form.map(cells = { 1: caller.db.name, 2: caller.db.account, 3: "A placeholder description for this character", 4: caller.db.strength, 5: caller.db.dexterity, 6: caller.db.intelligence, 7: caller.db.health, 8: caller.db.health + caller.db.dexterity / 4, 9: math.floor(caller.db.health + caller.db.dexterity / 4), 10: "10/08/2021", 11: caller.db.attribpoints, 12: caller.db.strength * 2, 13: caller.db.strength * 4, 14: caller.db.strength * 6, 15: caller.db.strength * 12, 16: caller.db.strength * 20, 17: 0, 18: 0, 19: 0, 20: math.floor(caller.db.health + caller.db.dexterity / 4), 21: 0, 22: 0 }) tableA = EvTable("Item", "Value", border="incols") tableB = EvTable("Skill", "Level", "Comp", border="incols") for key in caller.db.skills: tableB.add_row( skillmodule.getSkillName(key), caller.db.skillevel[key], skillmodule.getSkillDescription(caller.db.skillevel[key]) ) form.map(tables = { "A": tableA, "B": tableB }) caller.msg(form) return "start"