2014-07-07 2 views
0

나는 Conquest라고 불리는 Ruby에서 Text Adventure를 해왔으며, 어디에서 가는지 매우 행복합니다. 그러나 곧 게임을 저장할 수있는 기능을 추가하고 싶습니다. 나는 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해야 알 플레이어를 지키지 않는다면 플레이어의 재산이 아닙니다. 나는 그것을 고치려고하지 않았습니다.

그래서 YAML 직렬화로 이것을 저장하는 것에 대해 어떻게 생각합니까? (못생긴 !ruby/object:Class 물건)

나는 이것에 관해 더 좋은 길을 알고 정말로 싶다. 그러나 나는 아무것도 생각할 수 없다. 나는 ~/.conquest_save에 yaml이나 다른 저장 형식을 넣을 수 있다고 생각했다.

나는 당신의 모든 의견을 고맙게 생각하고있다. 전체 프로젝트는 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의 머드는 전통적으로 실제로 SQL을 사용합니다. 나는 약간의 오류가 있었고 행렬 오류가 발생했을 때 로그를보고 SQL 관련 오류가 큰 놀라움을 겪었습니다.

나는 또한 머드와 함께 나의 행운을 시험했고 실제로 yaml 파일보다 더 복잡해지면 나도 가지 않을 것이라고 말했다.

만약 당신의 시스템이 실제로 더 복잡 해지면, 추상화하고 단지 wrapper를 postgresql이나 mysql과 같이 activerecord와 같이 사용하려고 생각 했습니까?

+0

이것은 내가 사용하고있는 2.1.2의 루비 코어 부분을 말하지만,'로드 이러한 파일 - 마샬링 '나는이 아이디어를 좋아하지만, 감사합니다! – Addison

+0

오, 알았어, 나는 '마음대로'요구할 필요가 없다. – Addison

+0

나는 이것을 내 게임에 추가했고, 그것은 훌륭하게 작동한다! 감사! – Addison

관련 문제