""" The WyldWood Character Creation """ import inflect from typeclasses.characters import Character # from evennia.prototypes.spawner import spawn # from evennia.utils.evtable import EvTable from utils.word_list import paragraph _INFLECT = inflect.engine() ######################################################### # Utilities ######################################################### # def create_objects(character): # """do the actual object spawning""" # # since our example chargen saves the starting prototype to an attribute, we retrieve that here # proto = dict(character.db.starter_weapon) # # set the location to our character, so they actually have it # proto["location"] = character # # create the object # spawn(proto) ######################################################### # Welcome Page ######################################################### def menunode_welcome(caller): """Starting page.""" char = caller.new_char # another decision, so save the resume point char.db.chargen_step = "menunode_welcome" text = paragraph("""\ |wHoward's Character Creator, v1|n This game takes place in the |cDomain of the WyldWood|n, and your character may be a faery denizen of this strange wonderland, or perhaps someone more typical, but who stepped into a faery circle or other portal and landed in this strange, magical place. To create this fantasy character, I will ask a series of questions, and you will type your answer. Type |ghelp|n at any of these prompts, for additional information. Type |gquit|n to stop the process (recover your session using |gcharcreate|n). """) + """ 1. Short Description 2. Set Gender 3. Character's "Pose" 4. Long Description 5. Character's Name """ + "\nType |cReturn|n to begin creating your character." help = paragraph(""" Type |gnext|n or |gskip|n (or even a blank answer), to keep any previously entered data for the step and go to the next prompt. Type |gback|n to return to a previous step, well, except for this first message. Type |gquit|n instead. :-D """) options = {"key": "_default", "goto": _check_welcome} return (text, help), options def _check_welcome(caller, raw_string, **kwargs): """Check and fix the welcome message.""" return "menunode_sdesc" ######################################################### # Short Description ######################################################### def menunode_sdesc(caller, raw_string, **kwargs): """Starting page.""" char = caller.new_char # another decision, so save the resume point char.db.chargen_step = "menunode_sdesc" # check if an error message was passed to the node. if so, you'll # want to include it into your "name prompt" at the end of the # node text. if error := kwargs.get("error"): prompt_text = f"{error}. Enter a different short description:" elif char.db._sdesc: prompt_text = f"You previously wrote: |w{char.db._sdesc}|n" else: prompt_text = paragraph("""\ Let's begin with a two or three word description, e.g. "big-eared hobbit", "beardless dwarf" or "frumpy, old woman": """) text = paragraph(f"""\ |wShort Description|n Since this game emphasizes |wrole playing|n, you will pick your character's name at the end (since no one will know, or should know, your |wreal|n name). When you and another player characters uses the |glook|n command, you will see a description of the area, including a list of all the other characters, but using a short description instead of the character's name. What should your |wshort description|n identify your character? {prompt_text}""") help = paragraph("""\ In fact, you will even use this description to refer to them, for instance, to get a better description of the character, you might type |glook hobbit|n, or if there are two hobbits in the area, they would need to type more of the description: |glook big-eared hobbit|n |wNote:|n Type, |gnext|n to keep your value and skip to the next step.""") # Free-formed text: options = {"key": "_default", "goto": _check_sdesc} return (text, help), options def _check_sdesc(caller, raw_string, **kwargs): """Check and confirm short description""" # strip any extraneous whitespace from the raw text sdesc = raw_string.strip() if sdesc == 'back': return "menunode_welcome" if sdesc in ('next', 'skip') or not sdesc: return "menunode_gender" # aside from validation, the built-in normalization function from # the caller's Account does some useful cleanup on the input, just # in case they try something sneaky: sdesc = caller.account.normalize_username(sdesc) # Make sure it isn't too long. if len(sdesc) > 30: return ( "menunode_sdesc", {"error": f"|w{sdesc}|n is too long. Try less than two dozen letters."}, ) caller.new_char.db._sdesc = sdesc return "menunode_gender" ######################################################### # Character Gender ######################################################### def menunode_gender(caller, raw_string, **kwargs): """Choosing a gender for the character.""" char = caller.new_char # another decision, so save the resume point char.db.chargen_step = "menunode_gender" text = paragraph(f"""\ |wCharacter Gender|n Specifying the gender allows the system to use the correct pronouns for your character. You can change this in the game using the |ggender|n command. Choose one of the following:""") help = paragraph("""\ This feature only works from the system, not from other players. A character would need to tell others of their preferred pronouns. """) options = ( {"key": "m", "desc": "male with pronouns, he/him/his", "goto": (_check_gender, {"gender": "male"}), }, {"key": "f", "desc": "female with pronouns, she/her/hers", "goto": (_check_gender, {"gender": "female"}), }, {"key": "n", "desc": "neutral with pronouns, it/its", "goto": (_check_gender, {"gender": "neutral"}), }, {"key": "a", "desc": "ambiguous with pronouns, they/them/their", "goto": (_check_gender, {"gender": "ambiguous"}), }, {"key": "b", "desc": "go back and change the short description", "goto": (menunode_sdesc), }, ) return (text, help), options def _check_gender(caller, rawstring, **kwargs): """Check and confirm pose""" caller.new_char.db.gender = kwargs['gender'] return "menunode_pose" ######################################################### # Posing the Character ######################################################### def menunode_pose(caller, raw_string, **kwargs): """Posing a character.""" char = caller.new_char # another decision, so save the resume point char.db.chargen_step = "menunode_pose" if char.db.pose: prompt_text = f"You previously wrote: |w{char.db.pose}|n" else: prompt_text = '' text = paragraph(f"""\ |wPosing a Character|n Along with the short description given in the previous step, we can |wpose|n our character. For instance, entering: |wsmoking a pipe|n Might show our character as: a |mbig-eared hobbit|w smoking a pipe|n As the game plays, you can change this at any time using the |gpose|n command. This is just sets up a default pose, which can return to with |gpose reset|n. Even this isn't permanent, as you can change the default with something like: |gpose default sitting on the ground|n {prompt_text} Enter an initial pose to describe, |m{char.db._sdesc}|n:""") help = paragraph("""\ Don't worry much about this pose, since you can (and will) change it as you play the game. Having an initial pose makes the game (and your character) look better. Enter an initial pose to describe your character, or type |gback|n to change your short description.""") # Free-formed text: options = {"key": "_default", "goto": _check_pose} return (text, help), options def _check_pose(caller, raw_string, **kwargs): """Check and confirm pose""" # strip any extraneous whitespace from the raw text pose = raw_string.strip() if pose == 'back': return "menunode_gender" if pose in ('next', 'skip') or not pose: return "menunode_desc" # First, allow the character to be posed: caller.new_char.pose = True caller.new_char.db.pose = pose caller.new_char.db.pose_default = pose return "menunode_desc" ######################################################### # Full Description ######################################################### def menunode_desc(caller, raw_string, **kwargs): """Posing a character.""" char = caller.new_char # another decision, so save the resume point char.db.chargen_step = "menunode_desc" if char.db.desc and char.db.desc != "This is a character.": prompt_text = f"You previously wrote:\n\n |w{char.db.desc}|n" else: prompt_text = '' text = paragraph(f""" |wCharacter's Full Description|n Unlike the short description given in the previous step (see only when glancing around the room), a player will see this description when they type: |glook {char.db._sdesc}|n This can be as long as you would like. Keep in mind this description is what a player |wsees|n. so, no backstory here. Well, unless you can cleverly weave in backstory hints from that scar across your cheek. You can change this at any time using the |gdesc|n command. {prompt_text} Enter your character's full description:""") help = paragraph("""\ What makes a good character description? You want someone to be able to picture your character in their mind, so hair, skin and eye color, as well as what they are wearing. To make your character truly memorable, mention the unique features, so think of your character's backstory, and describe the small hammer on your necklace, so you can tell others who inquire about your father's last wish... Enter your character's full description or type |gback|n to change your pose.""") # Free-formed text: options = {"key": "_default", "goto": _check_desc} return (text, help), options def _check_desc(caller, raw_string, **kwargs): """Check and confirm desc""" # strip any extraneous whitespace from the raw text desc = raw_string.strip() if desc == 'back': return "menunode_pose" if desc in ('next', 'skip') or not desc: return "menunode_choose_name" # First, allow the character to be descd: caller.new_char.db.desc = desc return "menunode_choose_name" ######################################################### # Choosing a Name ######################################################### def menunode_choose_name(caller, raw_string, **kwargs): """Name selection""" char = caller.new_char # another decision, so save the resume point char.db.chargen_step = "menunode_choose_name" # check if an error message was passed to the node. if so, you'll # want to include it into your "name prompt" at the end of the # node text. if error := kwargs.get("error"): prompt_text = f"{error}. Enter a different name." else: # there was no error, so just ask them to enter a name. prompt_text = "Enter a name here to check if it's available." # this will print every time the player is prompted to choose a name, # including the prompt text defined above text = paragraph(f"""\ |wChoosing a Name|n Now, let's give your character a name. Unless you tell others, no one will know (or need to know) your this name, however, you will use this name, when you type |gic Name|n to choose this character to play. {prompt_text}""") help = paragraph(""" Since you may have to type the entire name, you may want this to be a single word for the first name. Your background could include your full family name. """) # since this is a free-text field, we just have the one options = {"key": "_default", "goto": _check_charname} return (text, help), options def _check_charname(caller, raw_string, **kwargs): """Check and confirm name choice""" # strip any extraneous whitespace from the raw text # if you want to do any other validation on the name, e.g. no punctuation allowed, this # is the place! charname = raw_string.strip() if charname == 'back': return "menunode_desc" if charname in ('next', 'skip') or not charname: return ( "menunode_choose_name", {"error": f"Can't skip this step. Please enter a name."}, ) # aside from validation, the built-in normalization function from the caller's Account does # some useful cleanup on the input, just in case they try something sneaky charname = caller.account.normalize_username(charname) # check to make sure that the name doesn't already exist candidates = Character.objects.filter_family(db_key__iexact=charname) if len(candidates): # the name is already taken - report back with the error return ( "menunode_choose_name", {"error": f"|w{charname}|n is unavailable. Enter a different name."}, ) else: # it's free! set the character's key to the name to reserve it caller.new_char.key = charname # continue on to the confirmation node return "menunode_confirm_name" def menunode_confirm_name(caller, raw_string, **kwargs): """Confirm the name choice""" char = caller.new_char text = paragraph(f"""\ |wCreate Character?|n Shall I create your character, |c{char.key}|n: |w{char.db._sdesc} {char.db.pose}|n Description: {char.db.desc}""") options = [ {"key": ("Yes", "y"), "goto": "menunode_end"}, {"key": ("No", "n"), "goto": "menunode_choose_name"}, ] return text, options ######################################################### # The End ######################################################### def menunode_end(caller, raw_string): """End-of-chargen cleanup.""" char = caller.new_char # clear in-progress status char.attributes.remove("chargen_step") char.db.tutorstate = 0 char.db.visited = False text = paragraph(f""" You have created a character. I hope you enjoy playing this game. To stop playing with this character, type: |gooc|n You can create a new character with: |gcharcreate|n To play with this one, type: |gic {caller.new_char.key}|n As always, type |ghelp|n (especially |ghelp start|n), to learn more about how to play this game. Alrighty then, let's log in and start playing the game. Imagine that you are now in a strange land on the edge of Forever. Welcome to the cozy forest section in the |wDomain of the WyldWood|n. """) return text, None