Rewrote the random dungeon generator code

This commit is contained in:
Maruno17
2022-10-01 18:06:15 +01:00
parent 8c31ad994d
commit 1ccbafb499
15 changed files with 1618 additions and 559 deletions

View File

@@ -0,0 +1,209 @@
module GameData
class DungeonTileset
attr_reader :id
attr_reader :tile_type_ids
attr_reader :snap_to_large_grid # "large" means 2x2 tiles
attr_reader :large_void_tiles # "large" means 2x2 tiles
attr_reader :large_wall_tiles # "large" means 1x2 or 2x1 tiles depending on side
attr_reader :large_floor_tiles # "large" means 2x2 tiles
attr_reader :double_walls
attr_reader :floor_patch_under_walls
attr_reader :thin_north_wall_offset
attr_reader :flags
DATA = {}
DATA_FILENAME = "dungeon_tilesets.dat"
SCHEMA = {
"Autotile" => [:autotile, "us"],
"Tile" => [:tile, "us"],
"SnapToLargeGrid" => [:snap_to_large_grid, "b"],
"LargeVoidTiles" => [:large_void_tiles, "b"],
"LargeWallTiles" => [:large_wall_tiles, "b"],
"LargeFloorTiles" => [:large_floor_tiles, "b"],
"DoubleWalls" => [:double_walls, "b"],
"FloorPatchUnderWalls" => [:floor_patch_under_walls, "b"],
"ThinNorthWallOffset" => [:thin_north_wall_offset, "i"],
"Flags" => [:flags, "*s"]
}
extend ClassMethodsIDNumbers
include InstanceMethods
# @param other [self, Integer]
# @return [self]
def self.try_get(other)
validate other => [Integer, self]
return other if other.is_a?(self)
return (self::DATA.has_key?(other)) ? self::DATA[other] : self.get(self::DATA.keys.first)
end
def initialize(hash)
@id = hash[:id]
@snap_to_large_grid = hash[:snap_to_large_grid] || false
@large_void_tiles = hash[:large_void_tiles] || false
@large_wall_tiles = hash[:large_wall_tiles] || false
@large_floor_tiles = hash[:large_floor_tiles] || false
@double_walls = hash[:double_walls] || false
@floor_patch_under_walls = hash[:floor_patch_under_walls] || false
@thin_north_wall_offset = hash[:thin_north_wall_offset] || 0
@flags = hash[:flags] || []
@tile_type_ids = {}
set_tile_type_ids(hash)
end
def set_tile_type_ids(hash)
[hash[:autotile], hash[:tile]].each_with_index do |array, i|
array.each do |tile_info|
next if !tile_info
tile_type = tile_info[1].downcase.to_sym
if tile_type == :walls
if @double_walls
if @large_wall_tiles
push_tile(:wall_1, 384 + tile_info[0] + 33)
push_tile(:wall_2, 384 + tile_info[0] + 34)
push_tile(:wall_3, 384 + tile_info[0] + 36)
push_tile(:wall_4, 384 + tile_info[0] + 17)
push_tile(:wall_6, 384 + tile_info[0] + 20)
push_tile(:wall_7, 384 + tile_info[0] + 9)
push_tile(:wall_8, 384 + tile_info[0] + 10)
push_tile(:wall_9, 384 + tile_info[0] + 12)
push_tile(:wall_in_1, 384 + tile_info[0] + 23)
push_tile(:wall_in_3, 384 + tile_info[0] + 22)
push_tile(:wall_in_7, 384 + tile_info[0] + 31)
push_tile(:wall_in_9, 384 + tile_info[0] + 30)
push_tile(:upper_wall_1, 384 + tile_info[0] + 40)
push_tile(:upper_wall_2, 384 + tile_info[0] + 42)
push_tile(:upper_wall_3, 384 + tile_info[0] + 45)
push_tile(:upper_wall_4, 384 + tile_info[0] + 16)
push_tile(:upper_wall_6, 384 + tile_info[0] + 21)
push_tile(:upper_wall_7, 384 + tile_info[0] + 0)
push_tile(:upper_wall_8, 384 + tile_info[0] + 2)
push_tile(:upper_wall_9, 384 + tile_info[0] + 5)
push_tile(:upper_wall_in_1, 384 + tile_info[0] + 7)
push_tile(:upper_wall_in_3, 384 + tile_info[0] + 6)
push_tile(:upper_wall_in_7, 384 + tile_info[0] + 15)
push_tile(:upper_wall_in_9, 384 + tile_info[0] + 14)
else
push_tile(:wall_1, 384 + tile_info[0] + 25)
push_tile(:wall_2, 384 + tile_info[0] + 26)
push_tile(:wall_3, 384 + tile_info[0] + 27)
push_tile(:wall_4, 384 + tile_info[0] + 17)
push_tile(:wall_6, 384 + tile_info[0] + 19)
push_tile(:wall_7, 384 + tile_info[0] + 9)
push_tile(:wall_8, 384 + tile_info[0] + 10)
push_tile(:wall_9, 384 + tile_info[0] + 11)
push_tile(:wall_in_1, 384 + tile_info[0] + 22)
push_tile(:wall_in_3, 384 + tile_info[0] + 21)
push_tile(:wall_in_7, 384 + tile_info[0] + 30)
push_tile(:wall_in_9, 384 + tile_info[0] + 29)
push_tile(:upper_wall_1, 384 + tile_info[0] + 32)
push_tile(:upper_wall_2, 384 + tile_info[0] + 34)
push_tile(:upper_wall_3, 384 + tile_info[0] + 36)
push_tile(:upper_wall_4, 384 + tile_info[0] + 16)
push_tile(:upper_wall_6, 384 + tile_info[0] + 20)
push_tile(:upper_wall_7, 384 + tile_info[0] + 0)
push_tile(:upper_wall_8, 384 + tile_info[0] + 2)
push_tile(:upper_wall_9, 384 + tile_info[0] + 4)
push_tile(:upper_wall_in_1, 384 + tile_info[0] + 6)
push_tile(:upper_wall_in_3, 384 + tile_info[0] + 5)
push_tile(:upper_wall_in_7, 384 + tile_info[0] + 14)
push_tile(:upper_wall_in_9, 384 + tile_info[0] + 13)
end
elsif @large_wall_tiles
push_tile(:wall_1, 384 + tile_info[0] + 24)
push_tile(:wall_2, 384 + tile_info[0] + 25)
push_tile(:wall_3, 384 + tile_info[0] + 27)
push_tile(:wall_4, 384 + tile_info[0] + 8)
push_tile(:wall_6, 384 + tile_info[0] + 11)
push_tile(:wall_7, 384 + tile_info[0] + 0)
push_tile(:wall_8, 384 + tile_info[0] + 1)
push_tile(:wall_9, 384 + tile_info[0] + 3)
push_tile(:wall_in_1, 384 + tile_info[0] + 5)
push_tile(:wall_in_3, 384 + tile_info[0] + 4)
push_tile(:wall_in_7, 384 + tile_info[0] + 13)
push_tile(:wall_in_9, 384 + tile_info[0] + 12)
else
push_tile(:wall_1, 384 + tile_info[0] + 16)
push_tile(:wall_2, 384 + tile_info[0] + 17)
push_tile(:wall_3, 384 + tile_info[0] + 18)
push_tile(:wall_4, 384 + tile_info[0] + 8)
push_tile(:wall_6, 384 + tile_info[0] + 10)
push_tile(:wall_7, 384 + tile_info[0] + 0)
push_tile(:wall_8, 384 + tile_info[0] + 1)
push_tile(:wall_9, 384 + tile_info[0] + 2)
push_tile(:wall_in_1, 384 + tile_info[0] + 4)
push_tile(:wall_in_3, 384 + tile_info[0] + 3)
push_tile(:wall_in_7, 384 + tile_info[0] + 12)
push_tile(:wall_in_9, 384 + tile_info[0] + 11)
end
end
id = (i == 0) ? tile_info[0] * 48 : 384 + tile_info[0]
push_tile(tile_type, id, false)
end
end
end
def push_tile(tile_type, id, auto = true)
@tile_type_ids[tile_type] ||= []
@tile_type_ids[tile_type].push([id, auto])
end
def has_flag?(flag)
return @flags.any? { |f| f.downcase == flag.downcase }
end
def has_decoration?(deco)
return @tile_type_ids.include?(deco) && @tile_type_ids[deco].length > 0
end
def get_random_tile_of_type(tile_type, dungeon, x, y, layer)
tiles = @tile_type_ids[tile_type]
return 0 if !tiles || tiles.empty?
ret = tiles.sample[0]
if ret < 384 # Autotile
nb = TileDrawingHelper.tableNeighbors(dungeon, x, y, layer)
variant = TileDrawingHelper::NEIGHBORS_TO_AUTOTILE_INDEX[nb]
ret += variant
else
case tile_type
when :void
if @large_void_tiles
ret += 1 if x.odd?
ret += 8 if y.odd?
end
when :floor
if large_floor_tiles
ret += 1 if x.odd?
ret += 8 if y.odd?
end
when :wall_2, :wall_8, :wall_top
ret += 1 if @large_wall_tiles && x.odd?
when :wall_4, :wall_6
ret += 8 if @large_wall_tiles && y.odd?
end
# Different wall tiles for northern walls if there's another wall directly
# north of them (i.e. tree tiles that shouldn't have shaded grass because
# there isn't a tree-enclosed area there)
if @thin_north_wall_offset != 0 && [:wall_7, :wall_8, :wall_9].include?(tile_type)
ret += @thin_north_wall_offset if dungeon.tile_is_wall?(dungeon[x, y - 1, 1])
end
end
return ret
end
def property_from_string(str)
case str
when "SnapToLargeGrid" then return @snap_to_large_grid
when "LargeVoidTiles" then return @large_void_tiles
when "LargeWallTiles" then return @large_wall_tiles
when "LargeFloorTiles" then return @large_floor_tiles
when "DoubleWalls" then return @double_walls
when "FloorPatchUnderWalls" then return @floor_patch_under_walls
when "ThinNorthWallOffset" then return @thin_north_wall_offset
when "Flags" then return @flags
end
return nil
end
end
end

View File

@@ -0,0 +1,138 @@
# TODO: Add tileset number in here?
module GameData
class DungeonParameters
attr_reader :id, :area, :version
attr_reader :cell_count_x, :cell_count_y
attr_reader :cell_width, :cell_height
attr_reader :room_min_width, :room_min_height
attr_reader :room_max_width, :room_max_height
attr_reader :corridor_width, :random_corridor_shift
# Layouts:
# :full - every node in the map
# :no_corners - every node except for one in each corner
# :ring - every node around the edge of the map
# :antiring - every node except one that touches an edge of the map
# :plus - every node in a plus (+) shape
# :diagonal_up - every node in a line from bottom left to top right (/)
# :diagonal_down - every node in a line from top left to bottom right (\)
# :cross - every node in a cross (x) shape
# :quadrants - every node except the middles of each edge (i.e. each corner bulges out)
attr_reader :node_layout, :room_layout
attr_reader :room_chance # Percentage of active roomable nodes that will become rooms
attr_reader :extra_connections_count
attr_reader :floor_patch_radius, :floor_patch_chance, :floor_patch_smooth_rate
attr_reader :floor_decoration_density, :floor_decoration_large_density
attr_reader :void_decoration_density, :void_decoration_large_density
attr_reader :rng_seed
attr_reader :flags
DATA = {}
DATA_FILENAME = "dungeon_parameters.dat"
SCHEMA = {
"DungeonSize" => [:dungeon_size, "vv"],
"CellSize" => [:cell_size, "vv"],
"MinRoomSize" => [:min_room_size, "vv"],
"MaxRoomSize" => [:max_room_size, "vv"],
"CorridorWidth" => [:corridor_width, "v"],
"ShiftCorridors" => [:shift_corridors, "b"],
"NodeLayout" => [:node_layout, "s"],
"RoomLayout" => [:room_layout, "s"],
"RoomChance" => [:room_chance, "v"],
"ExtraConnections" => [:extra_connections_count, "u"],
"FloorPatches" => [:floor_patches, "vvu"],
"FloorDecorations" => [:floor_decorations, "uu"],
"VoidDecorations" => [:void_decorations, "uu"],
"RNGSeed" => [:rng_seed, "u"],
"Flags" => [:flags, "*s"]
}
extend ClassMethodsSymbols
include InstanceMethods
# @param other [Symbol, String, self]
# @param version [Integer]
# @return [self]
def self.try_get(area, version = 0)
validate area => [Symbol, self, String]
validate version => Integer
area = area.id if area.is_a?(self)
area = area.to_sym if area.is_a?(String)
trial = sprintf("%s_%d", area, version).to_sym
area_version = (DATA[trial].nil?) ? area : trial
return (DATA.has_key?(area_version)) ? DATA[area_version] : self.new({})
end
def initialize(hash)
@id = hash[:id]
@area = hash[:area]
@version = hash[:version] || 0
@cell_count_x = (hash[:dungeon_size]) ? hash[:dungeon_size][0] : 5
@cell_count_y = (hash[:dungeon_size]) ? hash[:dungeon_size][1] : 5
@cell_width = (hash[:cell_size]) ? hash[:cell_size][0] : 10
@cell_height = (hash[:cell_size]) ? hash[:cell_size][1] : 10
@room_min_width = (hash[:min_room_size]) ? hash[:min_room_size][0] : 5
@room_min_height = (hash[:min_room_size]) ? hash[:min_room_size][1] : 5
@room_max_width = (hash[:max_room_size]) ? hash[:max_room_size][0] : @cell_width - 1
@room_max_height = (hash[:max_room_size]) ? hash[:max_room_size][1] : @cell_height - 1
@corridor_width = hash[:corridor_width] || 2
@random_corridor_shift = hash[:shift_corridors]
@node_layout = hash[:node_layout]&.downcase&.to_sym || :full
@room_layout = hash[:room_layout]&.downcase&.to_sym || :full
@room_chance = hash[:room_chance] || 70
@extra_connections_count = hash[:extra_connections_count] || 2
@floor_patch_radius = (hash[:floor_patches]) ? hash[:floor_patches][0] : 3
@floor_patch_chance = (hash[:floor_patches]) ? hash[:floor_patches][1] : 75
@floor_patch_smooth_rate = (hash[:floor_patches]) ? hash[:floor_patches][2] : 25
@floor_decoration_density = (hash[:floor_decorations]) ? hash[:floor_decorations][0] : 50
@floor_decoration_large_density = (hash[:floor_decorations]) ? hash[:floor_decorations][1] : 200
@void_decoration_density = (hash[:void_decorations]) ? hash[:void_decorations][0] : 50
@void_decoration_large_density = (hash[:void_decorations]) ? hash[:void_decorations][1] : 200
@rng_seed = hash[:rng_seed]
@flags = hash[:flags] || []
end
def has_flag?(flag)
return @flags.any? { |f| f.downcase == flag.downcase }
end
def rand_cell_center
x = (@cell_width / 2) + rand(-2..2)
y = (@cell_height / 2) + rand(-2..2)
return x, y
end
def rand_room_size
width = @room_min_width
if @room_max_width > @room_min_width
width = rand(@room_min_width..@room_max_width)
end
height = @room_min_height
if @room_max_height > @room_min_height
height = rand(@room_min_height..@room_max_height)
end
return width, height
end
def property_from_string(str)
case str
when "DungeonSize" then return [@cell_count_x, @cell_count_y]
when "CellSize" then return [@cell_width, @cell_height]
when "MinRoomSize" then return [@room_min_width, @room_min_height]
when "MaxRoomSize" then return [@room_max_width, @room_max_height]
when "CorridorWidth" then return @corridor_width
when "ShiftCorridors" then return @random_corridor_shift
when "NodeLayout" then return @node_layout
when "RoomLayout" then return @room_layout
when "RoomChance" then return @room_chance
when "ExtraConnections" then return @extra_connections_count
when "FloorPatches" then return [@floor_patch_radius, @floor_patch_chance, @floor_patch_smooth_rate]
when "FloorDecorations" then return [@floor_decoration_density, @floor_decoration_large_density]
when "VoidDecorations" then return [@void_decoration_density, @void_decoration_large_density]
when "RNGSeed" then return @rng_seed
when "Flags" then return @flags
end
return nil
end
end
end