# Development-only shortcut so any screen can be opened directly instead of # playing the game from the start. Creates a player on demand and back-fills # whatever progress the requested screen expects to find. # # ?dev=good|bad|chance|chance_bad picks which answer the back-fill records and # always starts from a fresh player, so the whole run leading up to the screen # is answered that way. Without it an existing session player is kept and only # the gaps are filled, so a jump can be played on from. ?dev_last_save=1 also # answers the last-save question. module DevJump extend ActiveSupport::Concern SCREENS = %w[index facts intro stage stage_result last_save done results].freeze included do prepend_before_action :dev_jump!, only: SCREENS.map(&:to_sym), if: :dev_jump? end private def dev_jump? Rails.env.development? end def dev_jump! set_locale # The start screen is the one place a leftover player is in the way: it # would show the debug panel a first-time visitor never sees. return session.delete(:player_id) if action_name == "index" session.delete(:player_id) if params[:dev] dev_player dev_seed_progress end def dev_player Current.player = Player.find_by(id: session[:player_id]) return Current.player if Current.player Current.player = Player.create(locale: I18n.locale.to_s, country: Player.valid_country_code(params[:country]), device: Player.device_from_user_agent(request.user_agent)) session[:player_id] = Current.player.id Current.player end def dev_seed_progress case action_name when "stage" then dev_fill_stages(params[:id].to_i - 1) when "stage_result" then dev_fill_stages(params[:id].to_i) when "last_save" then dev_fill_stages(n_stages) when "done", "results" dev_fill_stages(n_stages) dev_fill_last_save if params[:dev_last_save] end end # Answers every stage up to `upto` that the player hasn't answered yet, scoring # each one the way the real answer action would. def dev_fill_stages(upto) return if upto < 1 stages.first(upto).each_with_index do |stage_node, i| index = i + 1 next if current_player.answer_id_for(index) answer = dev_answer_for(stage_node) next unless answer current_player.record_answer(index, answer.id) apply_score(score_entry_for(index, answer)) end end def dev_fill_last_save return if current_player.last_save_answer_id node = last_save_node return unless node answer = dev_pick_from(node.children.ordered) return unless answer current_player.record_last_save_answer(answer.id) apply_score(last_save_score_entry_for(answer)) end # A chance answer is only ever recorded through one of its outcomes, so it # resolves one level deeper -- ?dev=chance lands on the good outcome, # ?dev=chance_bad on the bad one. def dev_answer_for(stage_node) answer = dev_pick_from(stage_node.children.ordered) return answer unless answer&.chance? outcomes = answer.children.ordered.to_a wanted = params[:dev] == "chance_bad" ? :bad_answer? : :good_answer? outcomes.find(&wanted) || outcomes.first end # Not every stage offers every kind -- stage 5 has no bad answer, stage 1's # chance cannot go wrong -- so an unavailable pick settles for the first # answer rather than leaving the stage unanswered. def dev_pick_from(nodes) nodes = nodes.to_a case params[:dev] when "bad" then nodes.find(&:bad_answer?) when "chance", "chance_bad" then nodes.find(&:chance?) else nodes.find(&:good_answer?) end || nodes.first end end