class Integer def gri Vnum.get_room_index self end end # Example: sect_city or sect_forest class Sector attr_accessor :name, :symbol, :symbolw, :path_options, :wall_options @@sector_hash = {} # for fast lookups using symbols. Was considerably more efficient. @@map_tiles = {} # for fast lookup of map tile. def initialize n, sym, wall_op, path_op @name = n @symbol = sym @symbolw = (sym.to_s + "w").to_sym @wall_options = wall_op @path_options = path_op @@sector_hash[sym] = self # add this to the list. end def to_s "#{@name} (#{@wall_options} #{@path_options})" end # lookup a sector given a name or a symbol def self.lookup name if name.is_a? Symbol #log :debug, "Sector.sector_lookup(#{name}) returns #{@@sector_hash[name].to_str}" found = @@sector_hash[name] if found == nil temp = @@sector_hash.values temp = temp.select {|v| v.symbolw == name} found = temp[0] end return found end name.downcase! # select from list temp = @@sector_hash.values # array of values temp = temp.select {|v| v.name.start_with?(name) } return temp[0] end end # Room sector definitions. # NAME SYMBOL [[Color], north/south, east/west], # Sector.new("name", :sect_symbol, [[], wall tiles], [[], path tiles. begin log :info, "Loading sector definitions." Sector.new("forest", :sect_forest, [['g', 'G'], ['|'], '-'], [['w'], ['.']]) Sector.new("fancy", :sect_fancy, [['P'], ['##'], '##'], [['C'], ['.']]) Sector.new("city", :sect_city, [['W'], ['##'], '##'], [['C'], ['.']]) Sector.new("inside", :sect_inside, [['n'], ['##'], '##'], [['D'], ['.']]) Sector.new("fountain", :sect_fount, [['W'], ['##']], [['B'], ['0']]) Sector.new("alley", :sect_alley, [['y', 'Y'], ['|'], '-'], [['w'], ['.']]) Sector.new("water", :sect_water, [['b','B'], ['##'], '##'], [['b', 'B'], ['~', '-']]) Sector.new("beach", :sect_beach, [['Y'], ['##'], '##'], [['y', 'Y'], ['.']]) Sector.new("hills", :sect_hills, [['g', 'G'], ['~'], '~'], [['y'], ['.']]) Sector.new("dock", :sect_dock, [['y'], ['=']], [['D'], ['.']]) Sector.new("void", :sect_void, [['n'], [' '], ' '], [['x'], [' ']]) rescue Exception=>e log :error, "Sector table failed to load properly." log_exception e end # just used for rooms because of the nature of needing a way to look them up very quickly. # It's also legacy. class Vnum @count = 1 @rooms = {} # find a free vnum. # Static variable is used since we really don't care what the number is # This way multiple searches won't require rehashing ground. def self.gen_vnum # then we don't care where. while self.get_room_index(@count) @count += 1 end return @count end def self.get_room_index xnum @rooms[xnum] end # Adds a room to this area. The room has to be within the vnum range. def self.inject_room r @rooms[r.vnum] = r end def self.rooms @rooms end end class Exit attr_accessor :towards_room, :from_room, :direction attr_accessor :flags_state, :flags def initialize(xnum=nil,xnum2=nil,dir=nil) return if xnum == nil @direction = dir @towards_room = xnum.gri || xnum @from_room = xnum2.gri || xnum2 @from_room.exit_list[dir] = self if @from_room.is_a?(Room) end def open @flags_state = {} if !@flags_state @flags_state.toggle(:closed) oe = @towards_room.gri.exit_list[@direction.exit_code_rev] if oe && oe.towards_room.gri == @from_room.gri oe.flags_state = {} if !oe.flags_state oe.flags_state.toggle(:closed) end end def close @flags_state = {} if !@flags_state @flags_state.toggle(:closed) oe = @towards_room.gri.exit_list[@direction.exit_code_rev] if oe && oe.towards_room.gri == @from_room.gri @flags_state = {} if !oe.flags_state oe.flags_state.toggle(:closed) end end # remove the exit. def do_delete 4.times do |x| ex = @from_room.exit_list[x] next if !ex if ex == self @from_room.exit_list[x] = nil return end end end def to_s "#{@towards_room} #{@from_room} #{@directon}" end def enter xplayer xplayer.from_room if @from_room.is_a?(Integer) @from_room = @from_room.gri end if @towards_room.is_a?(Integer) @towards_room = @towards_room.gri end @from_room.gri.text_to_room ("#{xplayer.short_desc.capitalize} leaves #{@direction.exit_code_to_s}." + ENDL) @towards_room.gri.text_to_room ("#{xplayer.short_desc.capitalize} has arrived." + ENDL) @towards_room.accept_player(xplayer) xplayer.execute_command("look"); end end module RoomDSL include ScriptEnvironment end # a single room. class Room attr_accessor :vnum, :exit_list, :people, :name, :sector, :desc def to_configure_properties ['@name', '@vtag', '@vnum', '@sector', '@exit_list'] end include CoralMUD::FileIO # standard saving mechanisms. include CoralMUD::VirtualTags # vtags and indexing include CoralMUD::HasStuff # can contain things. include RoomDSL # called by the create command if this function exists. def self.create ch r = self.new(Vnum.gen_vnum) r.namespace = ch.in_room.namespace # have to set it so gen_generic_tag will work correctly. r.assign_tag Tag.gen_generic_tag(r), ch.in_room.namespace return r end def flags (@flags ||= {}) end def initialize xnum=nil # Instance variables @name = DEFAULT_STRING @sector = :sect_city @desc = "There is little unique to be seen here." @people = nil @vnum = xnum @exit_list = [nil, nil, nil, nil, nil, nil] $room_list << self associate_with_area if @vnum != nil end # unlink the room completely. def do_delete moved = [] # evacuate the room. if @people moved = @people.dup moved.each do |person| # send them to a safe vnum...we're going to use vnum 1 person.go_anywhere end end 4.times do |i| ex = self.exit_list[i] next if ex == nil log :debug, "ex #{ex.direction} deleted" if ex.towards_room.gri.exit_list[ex.direction.exit_code_rev] if ex.towards_room.gri.exit_list[ex.direction.exit_code_rev].towards_room.gri == self # if it's the same room as being deleted we delete ex.towards_room.gri.exit_list[ex.direction.exit_code_rev].do_delete end end ex.do_delete end moved.each do |p| p.execute_command("look") end Tag.clear_tag(self) @vtag = nil a = @vnum / 1000 Vnum.rooms[@vnum % 1000] = nil # unlinked from main list. end def associate_with_area Vnum.inject_room self end def to_s mxptag("send 'edit #{Tag.full_tag(self)}'")+ "[Room #{@vtag}]" + mxptag("/send") end def self.load_rooms log :debug, "Load_rooms: Loading all rooms." rfiles = File.join("data/rooms", "*.yml") Dir.glob(rfiles).each do |a_file| room = Room.new() room.load_from_file(a_file) # loads each room file. room.associate_with_area # Must be done after we have the vnum end end def data_transform_on_load version, map # transform :exit_list from an array of ar = map["@exit_list"] exits = [nil, nil, nil, nil, nil, nil] # all nil while !ar.empty? each_arr = ar.shift dir, toward_room, flags = *each_arr exits[dir] = Exit.new() exits[dir].towards_room = toward_room exits[dir].from_room = self exits[dir].direction = dir if flags && !flags.empty? exits[dir].flags = flags exits[dir].flags_state = flags.dup # set the flags state end end map["@exit_list"] = exits # transform the tag using the namespace vtag = map['@vtag'] if vtag assign_tag vtag, map['@namespace'] vtag = @vtag map['@vtag'] = vtag end return map end def data_transform_on_save map ex = map["@exit_list"] # transform the exit_list into something we can save more reliably. arr = [] ex.each do |e| next if e == nil arr << [e.direction, e.towards_room.gri.vnum, e.flags] end map["@exit_list"] = arr vtag = map['@vtag'] if vtag map['@vtag'] = vtag.to_s end return map end # returns self def gri self end # does a circular exit check. def circular_check a return false if (self.exit_list[a[0]] == nil || self.exit_list[a[1]] == nil) return false if (self.exit_list[a[0]].towards_room.gri.exit_list[a[1]] == nil || self.exit_list[a[1]].towards_room.gri.exit_list[a[0]] == nil) return false if ((r = self.exit_list[a[0]].towards_room.gri.exit_list[a[1]].towards_room.gri) != self.exit_list[a[1]].towards_room.gri.exit_list[a[0]].towards_room.gri) return r end # for each direction in a room that exists. def each_dir self.exit_list.each do |e| yield e if e end end # do a dfs search with a code block def each_bfs (option_hash={:full_traverse=>true}) def adjust_context context, dir con = context.dup case dir when 0 then con[:y] += 1 when 1 then con[:x] += 1 when 2 then con[:y] -= 1 when 3 then con[:x] -= 1 end return con end color_list, white_list = [], [] full_traverse = option_hash[:full_traverse] yield_x_range, yield_y_range = option_hash[:yield_range] # white list contains data to yield and process each pass. white_list << [self, {:x=>0, :y=>0}] color_list << self # while we still have something to act upon process it. # And yield the result to our block of code. while !white_list.empty? r, context = white_list.shift() yield(r, context) if yield_x_range === context[:x] && yield_y_range === context[:y] r.each_dir do |e| next if full_traverse == false && (e.flags_state.is_set(:soft_door) || e.flags_state.is_set(:closed)) if e.towards_room.gri == nil e.do_delete next end if !color_list.include?(e.towards_room.gri) white_list << [e.towards_room.gri, adjust_context(context, e.direction)] color_list << e.towards_room.gri end end end return nil end # do a dfs search with a code block def each_dfs color_list, white_list = [], [] white_list << self color_list << self while !white_list.empty? r = white_list.pop() # do some code for each room found in the BFS. yield r r.each_dir do |e| if !color_list.include?(e.towards_room.gri) white_list << r.exit_list[i].towards_room.gri color_list << r.exit_list[i].towards_room.gri end end end return nil end # Create new rooms. Connect them. Nil is valid # By default direction is unset. If no direction both must exist. # Also, input should havbeen checked by now. def Room.dig_rooms(rvnum1, rvnum2, direction) if direction >= 6 || direction < 0 return false #failed end # lookup 2 rooms. r1, r2 = rvnum1.gri, rvnum2.gri # if either is nil now set it up. r1 = Room.new(rvnum1) if !r1 r2 = Room.new(rvnum2) if !r2 # creates exits with hook back Exit.new(rvnum2, rvnum1, direction) Exit.new(rvnum1, rvnum2, direction.exit_code_rev) return r2 end def accept_player(player) @people = [] if !@people @people << player player.in_room = self end def remove_player(player) @people.delete player if @people @people = nil if @people && @people.empty? player.in_room = nil end # display a type of message to the room. def display type, actor, blacklist, template, *arg type = [type].flatten template = ERB.new(template, 3) @people.each do |other| all_true = true type.each do |condition| break if not all_true all_true = case condition when String then eval(condition) when Symbol case condition when :visual then !other.is_blind? when :sound then !other.is_deaf? when :physical then !other.is_dumb? else false end else false end end if all_true and !blacklist.include?(other) other.view(template.result(binding) + ENDL) end end end #method to send to every character in the room. def text_to_room txt if @people @people.each do |p| p.text_to_player(txt) end end end end def save_all_rooms $room_list.each do |r| r.save_room end end def area_lookup num t = num / 1000 return $area_list[t] end