def get_tagged_option_to_inject(_tags, _state, _repeat=True): # TODO: Ponder circular import problem from scene import get_scene_description_with_tag injected_scene_desc = get_scene_description_with_tag(_tags, _repeat) if injected_scene_desc: # DON'T evaluate this scene deeply - we only care about the lead-in. evaluated_injected_scene = evaluate_content_blocks(injected_scene_desc.blocks, _state, _deep=False) if evaluated_injected_scene["leadin"] is None: logger.error("Injected scene '{0}' has no lead-in.".format(injected_scene_desc.id)) return None return { "action": goto_action, "params": { "next_scene": injected_scene_desc.id }, "text": evaluated_injected_scene["leadin"] } else: return None
def get_current_scene_data(): """Takes the request and the session and returns a dictionary with all the variables needed to render the current scene.""" if session.new or "game_state_version" not in session: if "nonce" in request.args: logger.info("Nonce in request but no session - probably a bot.") return "You've either set your browser to not accept cookies, or you're a bot. This game won't work for you." logger.info("No nonce in request and no session - restarting.") action = restart_action else: action = request.args.get("action", restart_action) if action == restart_action: restart() else: if not has_compatible_version(): logger.error("Incompatible session version number.") return "We've updated the game and your save game data is no longer compatible. Please restart." fill_in_missing_values() # Get the nonce from the session and the one from the request parameter. session_nonce = session.get("__nonce", generate_nonce()) url_nonce = request.args.get("nonce", session_nonce) # If they are not the same, the player reloaded the page. # EXCEPT WITH A CLEAN URL. So this doesn't work in the first scene. I don't see a way around this. player_reloaded = (session_nonce != url_nonce) and action != restart_action # Did the player reload? if not player_reloaded: # No -> Store the current state, so we can restore it if the player reloads. backup = {} for k, v in session.items(): if k.startswith("__"): continue if isinstance(v, types.ListType): backup[k] = v[:] else: backup[k] = v session["__backup"] = backup else: # Yes -> Restore the state from when this page was originally rendered. # We can only ever get here when there is already something in the session (the nonce). backup = session.get("__backup", None) if backup: session.clear() session.update(backup) session["__backup"] = backup # Nonce will be set at the end, no need to restore it. else: logger.error("Wanted to restore state but couldn't find backup.") # Provide a random number generator that produces the same numbers if the player reloaded the page. session["__rng"] = random.WichmannHill(url_nonce) if action == restart_action: current_scene_id = first_scene_id session["previous_scene"] = "" elif action == goto_action: current_scene_id = request.args.get("next_scene", None) if current_scene_id is None: logger.error("Couldn't find next_scene argument.") # TODO: Find a cleaner way to deal with this. del session["__rng"] return None elif action == respawn_action: session.update(generate_player_character()) wake_up_tags = ["pc_start", session["flesh_act"]] wake_up_scene = get_scene_description_with_tag(wake_up_tags) if not wake_up_scene: logger.error("Couldn't find a valid respawn scene.") # TODO: Find a cleaner way to deal with this. del session["__rng"] return None current_scene_id = wake_up_scene.id session["previous_scene"] = "" else: logger.error("'{0}' is an unknown action type.".format(action)) # TODO: Find a cleaner way to deal with this. del session["__rng"] return None scene_desc = get_scene_description(current_scene_id) if not scene_desc: logger.error("Couldn't find scene description for scene '{0}'.".format(current_scene_id)) # TODO: Find a cleaner way to deal with this. del session["__rng"] return None session["current_scene"] = current_scene_id evaluated_scene = evaluate_content_blocks(scene_desc.blocks, session) for action in evaluated_scene["actions"]: action.execute(session) tags_for_body_classes = list(set(scene_desc.tags + [session["flesh_act"]])) scene_data = { "text": break_text_into_paragraphs(substitute_text_variables(evaluated_scene["text"], session)), "options": [{ "action": option["action"], "text": substitute_text_variables(option["text"], session), "params": option["params"] } for option in evaluated_scene["options"] ], "body_classes": " ".join(tags_for_body_classes) } # Add a nonce so we can deal with page reloads. new_nonce = generate_nonce() for option in scene_data["options"]: option["params"]["nonce"] = new_nonce session["__nonce"] = new_nonce # We don't need this anymore. del session["__rng"] # Set this for next time we evaluate. session["previous_scene"] = current_scene_id # Flask will catch most modifications of the session automatically, but this way we are sure. # See http://flask.pocoo.org/docs/0.10/api/#sessions session.modified = True # Make sure we generated a valid scene. if len(scene_data["options"]) == 0: logger.error("Generated a scene with NO options. Session: {0}.".format(str(session))) return None return scene_data