2014-07-07 39 views
0

我一直在Ruby的文本冒险工作叫做征服,我非常高兴它的去向,但我想添加能够很快保存你的游戏。我曾经只是用YAML做这样的事情,但我会想象这是更好的方式。添加一个保存系统到我的游戏

这个游戏比我做的其他游戏还要复杂一点,所以我想要一个非常简单的方法来管理保存系统。

我想过在我的游戏做这样的事情与YAML我的自定义类的每一个实例:

--- !ruby/object:Player 
items: ... 

但我想这可能是一个糟糕的方式去了解它。我会告诉你一些我的文件,我很想知道你认为这是最好的方法。并且记住它正在进行中,所以诸如任务之类的东西不能完全工作,但你会明白这一点。

的lib/player.rb:

class Player 

    attr_accessor :items 

    def initialize 
     @items = {} 
     @quests = QuestList.quests 
    end 

    def pickup(key, item) 
     @items[key.to_sym] = item 
     if key == "scroll" 
      @quests[:mordor].start 
     end 
    end 

    def inventory 
     @items.values.each { |item| 
      a_or_an = %w[a e i o u].include?(item.name[0]) \ 
       ? "an " : "a " 
      a_or_an = "" if item.name[-1] == "s" 
      puts "#{a_or_an}#{item.name.downcase}" 
     } 
    end 

end 

的lib/item.rb的:

class Item 

    attr_reader :name, :description, :hidden, :can_pickup 

    def initialize(name, description, options = {}) 
     @name = name 
     @description = description 
     @options = options 
     @hidden = options[:hidden] || false 
     @can_pickup = options[:hidden] || true 
     add_info 
    end 

    def add_info 
    end 

end 

class Prop < Item 
    def add_info 
     @hidden = true 
     @can_pickup = false 
    end 
end 

# sends you to a new room, usally something that 
# has more functionality than just a room 
class Transporter < Prop 

    attr_accessor :goto 

    def add_info 
     @hidden = true 
     @can_pickup = false 
     @goto = options[:goto] 
    end 
end 

# SUBCLASSES BELOW: (only subclass when you have a good reason) 

# can be eaten, 
# use item.is_a? Food 
class Food < Item 
end 

class Tree < Prop 

    def climb 
     if @options[:can_climb] 
      puts @description 
     else 
      puts "You start climbing the tree, but you don't get far before you fall down." 
     end 
    end 
end 

的lib/quest_list.rb(这可能会由节能系统代替):

module QuestList 

    QUESTS = { 
     # this need a better name ⬇️ 
     main: Quest.new("The main mission", []), 
     mordor: Quest.new("Onward to Mordor", []) 
    } 

    def self.quests 
     QUESTS 
    end 

end 

lib/room_list.rb(这可能也会被保存系统取代):

module RoomList 

    ROOMS = { 
     castle_main: 
      Room.new("Main room", "This is the main room of the castle. It needs a better description\nand name. Theres a hallway south.", 
       paths: { s: :hallway} 
       ), 
     hallway: 
      Room.new("Hallway", "This castle has a long hallway. There is a door to the west and\na large room north.", 
       paths: { n: :castle_main, s: :castle, w: :dinning_hall } 
       ), 
      dinning_hall: 
       Room.new("Dinning hall", "The dinning hall. There is a door to the east.", 
        paths: { e: :hallway } 
        ), 
     castle: 
      Room.new("Castle", "You are in the castle. There's a long hallway to the north, and\nthe courtyard is to the south.", 
       paths: { n: :hallway, s: :courtyard } 
       ), 
     courtyard: 
      Room.new("Castle courtyard", "You are at the castle courtyard. There's a nice fountain in the center.\nThe castle entrance is north. There is a forest south.", 
       paths: { n: :castle, s: :forest }, 
       items: { 
        # this peach is useless, it'll confuse people 
        # a peach: 
        peach: Food.new("Peach", "A delicious peach") 
        }), 
     forest: 
      Room.new("Large forest", "This forest is very dense. There is a nice courtyard north.\nThe forest continues west and south.", 
       paths: { n: :courtyard, s: :forest_1, w: :forest__1 } 
       ), 
    forest__1: 
     Room.new("Large forest", "This forest is very nice. You can go north, east and west into\nsome more forest.", 
      paths: { n: :forest__2, e: :forest, w: :sticks } 
      ), 
sticks: 
    Room.new("Large forest", "This forest is getting boring, but hey, who knows what you'll find here!\nYou can go east.", 
     paths: { e: :forest__1 }, 
     items: { 
      sticks: Item.new("Sticks", "Just a couple of sticks. They like they are cedar wood.") 
      }), 
    forest__2: 
     Room.new("Large forest", "You are in a large forest. There looks like theres a grand building over\neast, but you can't quite get to it from here. You can go south.", 
      paths: { s: :forest__1 } 
      ), 
     forest_1: 
      Room.new("Large forest", "There is a large, magnificent tree east. The forest continues\nnorth and south.", 
       paths: { n: :forest, e: :banyan_tree, s: :forest_2 } 
       ), 
      banyan_tree: 
       # http://en.wikipedia.org/wiki/Banyan 
       Room.new("Large banyan tree", "There is a large banyan tree, with many twists and roots going up the tree.\nYou can go west.", 
        paths: { w: :forest_1 }, 
        items: { 
         tree: Tree.new("Banyan", "You climb up the top of the tree, and see lots of trees and a\ncastle somewhere around north. It looks like there is a small\nvillage some where south east. You climb back down.", { # 
          can_climb: true 
          })}), 
     forest_2: 
      Room.new("Large forest", "Just some more forest. The forest continues north and south.", 
       paths: { n: :forest_1, s: :forest_3 } 
       ), 
     forest_3: 
      Room.new("Large forest", "Dang, how many trees are in this forest? You can go north, south, and west.", 
       paths: { n: :forest_2, s: :forest_4, w: :more_trees } 
       ), 
    more_trees: 
     Room.new("Large forest", "You can go east and west.", 
      paths: { e: :forest_3, w: :more_trees_1 } 
      ), 
more_trees_1: 
    Room.new("Large forest", "You can go east and south.", 
     paths: { e: :more_trees, s: :more_trees_2 } 
     ), 
more_trees_2: 
    Room.new("Large forest", "You can go north and south.", 
     paths: { n: :more_trees_1, s: :more_trees_3 } 
     ), 
more_trees_3: 
    Room.new("Large forest", "You can go north and east", 
     paths: { n: :more_trees_2, e: :path_to_village } 
     ), 
     path_to_village: 
      Room.new("Large forest", "Its hard to see because of all these trees, but you think you see a small\nhut to the east. You can also go back west", 
       paths: { e: :village, w: :more_trees_3 } 
       ), 
      village: 
       # add an item or 2 here 
       Room.new("Abandon village", "There are a bunch of huts here, some people must have lived here before.\nThere is some more forest down south. You can go back west into the forest.", 
        paths: { w: :path_to_village, s: :forest_by_village }, 
        items: { 
         pickaxe: Item.new("Pickaxe", "Be careful, it looks sharp.") 
         }), 
      forest_by_village: 
       Room.new("Large forest", "Geez more forest. The village is north, and there is a valley east", 
        paths: { n: :village, e: :valley } 
        ), 
       valley: 
        Room.new("Valley", "It's a beautiful valley, with some giganic mountains east, with some\nsnow of the tops. There is a forest to the west", 
         paths: { e: :mountains, w: :forest_by_village } 
         ), 
        mountains: 
         Room.new("Mountains", "There are many tall mountains with snow on the tops. You can go back west.", 
          paths: { u: :mountain, w: :valley }, 
          has_mountain: true 
          ), 
         mountain: 
          Room.new("Tall mountain", "This mountain is very steep. You can continue climbing or go back down", 
           paths: { d: :mountains, u: :mountain_1 }, 

           # the scroll and Randy should be moved to mountain_3 once it exists 
           items: { 
            scroll: Item.new("Scroll", "Its some kind of elvish... You can't read it.") }, 
           people: { 
            # Randy will read elvish in the future 
            randy: Person.new("Randy", "He's just an elf", 
             race: "Elf", 
             talk: "I can read elvish. Go figure." 
             )}), 
         mountain_1: 
          Room.new("Tall mountain", "Climbing this mountain is very tiring. You can continue climbing\nor go back down", 
           paths: { d: :mountain } 
           ), 
     forest_4: 
      Room.new("Large forest", "There is a lot of trees here. It's very shady in this area.\nThe forest continues north.", 
       paths: { n: :forest_3 } 
       ) 
    } 

    def self.room_list 
     ROOMS 
    end 

end 

的lib/delegate.rb:

class Delegate 

    attr_accessor :current_room 

    def initialize 
     @rooms = RoomList.room_list 
     @player = Player.new 
     @current_room = @rooms[:courtyard] 
     @help = 0 
    end 

    def parse(input) 
     directions = "up|down|north|east|south|west|u|d|n|e|s|w" 
     # input will always be converted to lower case before getting here 
     case input 
     when /^(?<direction>(#{directions}))$/ 
      direction = $~[:direction] 
      walk(direction) 
     when /^(go|walk)((?<direction>#{directions}|to mordor))?$/ 
      direction = $~[:direction] 
      if direction 
       walk(direction) 
      else 
       puts "#{input.capitalize} where?" 
      end 
     when /^(get|take|pickup|pick up)((?<item>[a-z ]+))?$/ 
      item = $~[:item] 
      if item 
       pickup(item) 
      else 
       puts "Please supply an object to #{input}." 
      end 
     when /^look((?<item>[a-z]+))?$/ 
      item = $~[:item] 
      item.nil? ? look : inspect(item) 
     when /^inspect((?<item>[a-z]+))?$/ 
      item = $~[:item] 
      if item 
       inspect(item) 
      else 
       puts "Please supply an object to inspect." 
      end 
     when /^rub sticks(together)?$/ 
      rub_sticks 
     when /^quests?$/ 
      # this is probably going to be a for statement. You understand thos more than i do so have at it. 
      # this should loop through the list of quests in quests.yml and return the ones that are true 

      # correction: it should call .each, for statments are bad practice in ruby 
     when /^(i|inv|inventory)$/ 
      inventory 
     when /^climb((?<tree_name>[a-z]+))?(tree)?$/ 
      # this regex needs to be cleaned up, just the tree part really 
      # nvm, the whole regex sucks 
      = $~[:tree_name] 
      climb() 
      # doesn't have to be a tree... 
     when /^(help|h)$/ 
      @smart_aleck ||= ["Why?","No.","Stop asking plz.","seriously, shut up.","...","...","...","Ok, seriously.","Do u not understand the meaning of \"be quiet\"?","ug"].to_enum 
      begin 
       puts @smart_aleck.next 
      rescue StopIteration 
       @smart_aleck.rewind 
       puts @smart_aleck.next 
      end 
     when /^(quit|exit)$/ 
      quit 
     when /^\s?$/ 
     else 
      = ["I don't speak jibberish.","Speak up. Ur not making any sense.","R u trying to confuse me? Cuz dats not gonna work","What the heck is that supposed to mean?"] 
      puts .sample 
     end 
    end 

    def walk(direction) 
     if direction != "to mordor" 
      if new_room = @rooms[@current_room[direction]] 
       @current_room = new_room.enter 
      else 
       puts "You can't go that way." 
      end 
     else 
      #TODO: add quest system. We should have a main quest and other side quests like going to mordor. 
      puts "One does not simply walk to Mordor... You need to find the eagles. They will take you to Mordor." 
     end 
    end 

    def pickup(item) 
     if _item = @current_room.items[item.to_sym] 
      if _item.can_pickup 
       _item = @current_room.remove_item(item) 
       @player.pickup(item, _item) 
      else 
       puts "You can't pick that up." 
      end 
     else 
      puts "That item isn't in here." 
     end 
    end 

    def inventory 
     @player.inventory 
    end 

    def look 
     @current_room.look 
    end 

    def inspect(item) 
     # this could be refactored 
     if the_item = @player.items[item.to_sym] 
      puts the_item.description 
     elsif the_item = @current_room.items[item.to_sym] 
      puts the_item.description 
     else 
      puts "This item is not here or your inventory." 
     end 
    end 

    def rub_sticks 
     if @player.items[:sticks] 
      # do something involving fire 
      puts "I need to implement this." 
     end 
    end 

    def climb(thing_name) 
     if = @current_room.items[:tree] 
      name = .name.downcase 
      if thing_name.nil? || thing_name == "tree" || thing_name == name 
       .climb 
      else 
       puts "You can't climb that." 
      end 

     # I don't like how this works :(
     elsif @current_room.options[:has_mountain] 
      if ["up", "mountain", nil].include? thing_name 
       walk("u") 
      end 
     else 
      puts "You can't climb that." 
     end 
    end 

    def quit 
     exit 
    end 

end 

的lib/quest.rb:

class Quest 

    attr_accessor :steps 

    def initialize(name, steps, options = {}) 
     @name = name 

     # steps (the argument) should be a hash like this: 
     # [:found_ring, :melted_ring] 
     @steps = steps.inject({}) { |hash, step| hash[step] = false; hash } 
     # then @step will be this: 
     # { found_ring: false, melted_ring: false } 

     @started = false 
     @options = options 
    end 

    def start 
     @started = true 
     puts "#{'Quest started!'.cyan} - #{name}" 
    end 

end 

的lib/room.rb:

class Room 

    attr_reader :items, :options, :people 

    def initialize(name, description, options = {}) 
     @name = name 
     @description = description 
     @paths = options[:paths] || {} 
     @items = options[:items] || {} 
     @people = options[:people] || {} 
     @options = options 
     @visited = false 
    end 

    def [](direction) 
     @paths[direction.to_sym] 
    end 

    def enter 
     puts @name.cyan 
     unless @visited 
      puts @description 
      list_items 
     end 
     @visited = true # can't hurt to set it every time, right? 
     self 
    end 

    def remove_item(item) 
     @items.delete(item.to_sym) 
    end 

    def look 
     puts @name.cyan 
     puts @description 
     list_items 
    end 

    def list_items 
     visible_items = @items.values.select { |i| (!i.hidden) && i.can_pickup } 
     unless visible_items.empty? 

      puts "Items that are here:".magenta 
      visible_items.map do |item| 
       a_or_an = %w[a e i o u].include?(item.name[0]) \ 
        ? "an " : "a " 
       a_or_an = "" if item.name[-1] == "s" 
       puts "#{a_or_an}#{item.name.downcase}" 
      end 
     end 

     visible_people = @people.values.select { |i| (!i.hidden) && i.can_pickup } 
     unless visible_people.empty? 

      puts "People that are here:".magenta 
      visible_people.map do |people| 
       puts "#{people.name}" 
      end 
     end 

    end 

end 

我知道Delegate.current_room应如果我要保存它,那么成为Player not Delegate的一个属性,我只是没有得到解决这个问题。

那么你们怎么看待用YAML序列化来保存这个? (丑陋的!ruby/object:Class东西)

我真的想知道更好的方式去做这件事,但我想不出任何东西。我想我可以把yaml或其他保存格式在〜/ .conquest_save

我很想在这里所有的输入,谢谢!完整的项目在Github here上。

+0

为什么不是db和'player_gateway'? – dax

+0

你可以将sqlite3数据库放进去,因为它只是一个文件。这将是想到的最优雅和可读的方式。 – Cereal

+0

我曾考虑过这个问题,但我不知道您是否可以或如何保存像我的玩家类这样的红宝石物体。 – Addison

回答

0

你可以尝试使用元帅。

使用

http://ruby-doc.org/core-2.1.2/Marshal.html

两个主要方法将是这些:

data = Marshal.dump(o) 
obj = Marshal.load(data) 

LPC传统的MUD使用SQL,其实。我发现当出现一些错误并且出现了矩阵故障时,然后查看日志,我发现SQL相关的错误令我大吃一惊。

我也用MUD试过我的运气,我实际上对自己说,如果它比yaml文件变得更复杂,那么这是一种我不会去的方式。

我认为如果你的系统真的会变得更加复杂,你有没有试图抽象出来,只是像postgresql或MySQL,像activerecord使用包装?

+0

这说明它是我在使用的2.1.2中的Ruby核心的一部分,但它说'加载这样的文件 - 编组'我喜欢这个想法,谢谢! – Addison

+0

哦,我明白了,我不需要'不需要'它不介意 – Addison

+0

我将它添加到我的游戏中,它效果很棒!谢谢! – Addison