OutlandsMud/world/cgmenu.py

808 lines
28 KiB
Python

# 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 <enter> 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 <enter> 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 <enter> 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 <enter> 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
<enter> 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
<enter> 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 <enter> 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 <return> to accept." % prev_entry
else:
text = "Enter your character's name or <return> 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 <return> 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"