我一直在使用Ruby中的文本冒险游戏Conquest,我对它的发展方向感到非常满意,但我想尽快添加保存游戏的功能。我以前只用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 :(得分:0)
您可以尝试使用Marshal。
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?