summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--randtilegen.rb219
-rw-r--r--src/randtiles.cpp65
2 files changed, 263 insertions, 21 deletions
diff --git a/randtilegen.rb b/randtilegen.rb
index f92db5d..21a9f58 100644
--- a/randtilegen.rb
+++ b/randtilegen.rb
@@ -1,12 +1,14 @@
class RandTileGenerator
+ Types = [:none, :horz, :vert, :both]
+ Specials = [nil, :vdouble_top, :vdouble_bottom]
+
def initialize
@sections = {}
end
- def section(name)
- @sections[name] ||= {entries: []}
- @current_section = @sections[name]
-
+ def section(*namelist)
+ raise "what" unless namelist.all?{|x| x.is_a?(String)}
+ @current_section = (@sections[namelist] ||= {entries: []})
yield
end
@@ -43,6 +45,13 @@ class RandTileGenerator
end
end
+ # assign an offset to each section name list
+ namelist_offsets = {}
+ @sections.each_key do |namelist|
+ namelist_offsets[namelist] = current_offset
+ current_offset += 4 + (4 * namelist.size)
+ end
+
# assign an offset to each piece of entry data
data_offsets = {}
all_entry_data.uniq!
@@ -54,17 +63,19 @@ class RandTileGenerator
# assign an offset to each section name
name_offsets = {}
- @sections.keys.each do |name|
- name_offsets[name] = current_offset
- current_offset += name.size + 1
+ @sections.each_key do |namelist|
+ namelist.each do |name|
+ name_offsets[name] = current_offset
+ current_offset += name.size + 1
+ end
end
# now pack it all together
header = ['NwRT', @sections.count].pack('a4 N')
offsets = @sections.each_value.map{|s| s[:offset]}.pack('N*')
- section_data = @sections.each_pair.map do |name, section|
- name_offset = name_offsets[name] - section[:offset]
+ section_data = @sections.each_pair.map do |namelist, section|
+ namelist_offset = namelist_offsets[namelist] - section[:offset]
entry_count = section[:entries].count
entry_data = section[:entries].map do |entry|
@@ -72,20 +83,34 @@ class RandTileGenerator
upper_bound = entry[:range].max
count = entry[:numbers].count
- type = [:none, :horz, :vert, :both].index(entry[:type])
+
+ type_sym, special_sym = entry[:type].to_s.split('_', 2).map(&:to_sym)
+ type_id = Types.index(type_sym)
+ special_id = Specials.index(special_sym)
+ type = type_id | (special_id << 2)
num_offset = data_offsets[entry[:numbers]] - entry[:offset]
[lower_bound, upper_bound, count, type, num_offset].pack('CCCC N')
end
- [name_offset, entry_count].pack('NN') + entry_data.join
+ [namelist_offset, entry_count].pack('NN') + entry_data.join
+ end
+
+ namelist_data = @sections.each_key.map do |namelist|
+ puts "Writing list: #{namelist.inspect}"
+ count = namelist.size
+ c_offsets = namelist.map{|n| name_offsets[n] - namelist_offsets[namelist]}
+ puts "Offsets: #{c_offsets.inspect}"
+
+ [count].pack('N') + c_offsets.pack('N*')
end
output = [header, offsets]
output += section_data
+ output += namelist_data
output += all_entry_data.map{|data| data.pack('C*')}
- output << @sections.keys.join("\0")
+ output << @sections.keys.flatten.join("\0")
output << "\0"
output.join
end
@@ -126,11 +151,179 @@ g.section('TestTileset') do
g.random(250..255, :vert, 0..5)
end
-g.section('Pa1_nohara') do
+regular_ts1 = %w(chika setsugen shiro suichu daishizen sabaku_chika)
+regular_ts1 += %w(nohara2 kurayami_chika shiro_yogan koopa_out shiro_koopa gake_yougan)
+regular_ts2 = %w(doukutu doukutu2 doukutu3 doukutu4 doukutu5 doukutu6 doukutu8)
+newer = %w(ghostrocks Pa1_daishizenkuri Pa1_darkmtmush Pa1_e3setsugen Pa2_darkcave)
+newer += %w(Pa3_autumnbg Pa2_volcanobg Pa1_mushcastle Pa1_pumpkin)
+
+regular_ts1.map!{ |x| "Pa1_#{x}" }
+regular_ts2.map!{ |x| "Pa2_#{x}" }
+g.section(*regular_ts1, *regular_ts2, *newer) do
+ g.regular_terrain
+end
+
+nohara_clones = %w(nohara newnohara cracks springwater mtmush darkmtmush space)
+g.section(*nohara_clones.map{ |x| "Pa1_#{x}"}) do
+ g.regular_terrain
+ g.sub_terrain
+end
+
+g.section('Pa1_gake', 'Pa1_gake_nocoll', 'Pa1_Mountains') do
g.regular_terrain
g.sub_terrain
+ g.random([0xAA, 0xBA, 0xCA, 0xDA], :vert)
+ g.random([0xAB, 0xBB, 0xCB, 0xDB], :vert)
+ g.random([0x8E, 0x9E, 0xAE, 0xBE], :vert)
+ g.random([0x8F, 0x9F, 0xAF, 0xBF], :vert)
+ g.random(0xC4..0xC9, :horz)
+ g.random(0xD4..0xD9, :horz)
+ g.random(0xE4..0xE9, :horz)
+end
+
+g.section('Pa1_kaigan', 'Pa1_kaiganplus') do
+ g.regular_terrain
+ g.random(0x18..0x1B)
+ g.random(0x28..0x2A)
+ g.random(0x3A..0x3D)
+end
+
+g.section('Pa1_obake_soto') do
+ g.random(0xA..0xF, :horz)
+ g.random(0x1A..0x1F)
+ g.random(0x2A..0x2F, :horz)
+ g.random([0x4E, 0x5E, 0x6E, 0x7E], :vert)
+ g.random([0x4F, 0x5F, 0x6F, 0x7F], :vert)
end
+g.section('Pa1_korichika') do
+ g.regular_terrain
+ g.random(0x6A..0x6F, :horz)
+ g.random(0x7A..0x7F)
+ g.random(0x8A..0x8F, :horz)
+ g.random([0x1E, 0x2E, 0x3E, 0x4E], :vert)
+ g.random([0x1F, 0x2F, 0x3F, 0x4F], :vert)
+ g.random(0x6A..0x6F, :horz)
+ g.random(0x7A..0x7F)
+ g.random(0x8A..0x8F, :horz)
+ g.random([0xB8, 0xC8, 0xD8, 0xE8], :vert)
+ g.random([0xB9, 0xC9, 0xD9, 0xE9], :vert)
+end
+
+g.section('Pa1_shiro_boss1') do
+ g.regular_terrain
+ g.random(0x60..0x65, :horz)
+end
+
+g.section('Pa2_kori', 'Pa2_e3kori') do
+ g.regular_terrain
+ g.random(0xA0..0xA5, :horz)
+ g.random(0xB0..0xB5, :horz)
+end
+
+
+g.section('Cloudscape', 'pa2_clouds') do
+ g.random(0x20..0x23, :horz)
+ g.random(0x30..0x33)
+ g.random(0x24..0x27, :horz)
+ g.random([0x8, 0x18, 0x28, 0x38], :vert)
+ g.random([0x9, 0x19, 0x29, 0x39], :vert)
+end
+
+g.section('Pa1_autumncastle', 'Pa3_autumncastle', 'Pa1_snowfort', 'Pa2_ghostcastle', 'Pa2_crystalcastle') do
+ g.random([0xB0, 0xC0, 0xD0, 0xE0], :vert)
+ g.random([0xB1, 0xC1, 0xD1, 0xE1], :vert)
+ g.random(0xB2..0xB7, :horz)
+ g.random(0xC2..0xC7)
+ g.random(0xD2..0xD7, :horz)
+end
+
+g.section('CrackedMega') do
+ g.random([0x20, 0x30, 0x40], :vert)
+ g.random([0x21, 0x31, 0x41], :vert)
+ g.random(0x22..0x27, :horz)
+ g.random(0x2A..0x2F, :horz)
+ g.random([0x5E, 0x6E, 0x7E], :vert)
+ g.random([0x5F, 0x6F, 0x7F], :vert)
+ g.random(0xB0..0xDF)
+ g.random(0x2..0x7, :horz_vdouble_top)
+ g.random(0x12..0x17, :horz_vdouble_bottom)
+ g.random(0xA..0xF, :horz_vdouble_top)
+ g.random(0x1A..0x1F, :horz_vdouble_bottom)
+end
+
+g.section('Pa1_dessert') do
+ g.regular_terrain
+ g.random(0xC..0xE, :horz)
+ g.random(0x1C..0x1E, :horz)
+end
+
+g.section('Pa1_freezeflame') do
+ g.random(0x1A..0x1F, :horz)
+ g.random(0x2A..0x2F, :horz)
+ g.random([0x10, 0x20, 0x30, 0x40], :vert)
+ g.random([0x11, 0x21, 0x31, 0x41], :vert)
+ g.random([0x18, 0x28, 0x38, 0x48], :vert)
+ g.random([0x19, 0x29, 0x39, 0x49], :vert)
+ g.random(0x2..0x7, :horz)
+ g.random(0x12..0x17)
+ g.random(0x22..0x27, :horz)
+ g.random(0x6A..0x6F)
+ g.random(0x52..0x57, :horz)
+ g.random([0x96, 0xA6, 0xB6, 0xC6], :vert)
+ g.random([0x97, 0xA7, 0xB7, 0xC7], :vert)
+ g.random([0xBE, 0xCE, 0xDE, 0xEE], :vert)
+ g.random([0xBF, 0xCF, 0xDF, 0xEF], :vert)
+ g.random(0xB8..0xBD, :horz)
+ g.random(0xC8..0xCD)
+ g.random(0xD8..0xDD, :horz)
+ g.random(0xE8..0xED)
+ g.random(0xF8..0xFD, :horz)
+end
+
+g.section(*%w(Pa1_magicsnow Pa2_magicsnow Pa3_magicsnow)) do
+ g.random(0xB4..0xB9, :horz)
+end
+
+g.section('Pa1_graveyard', 'Pa3_graveyars', 'Pa3_graveyard') do
+ g.regular_terrain
+ g.random(0x19..0x1E, :horz)
+end
+
+g.section('Pa3_ghostship') do
+ g.random(0xA..0xF, :horz)
+ g.random(0x1A..0x1F)
+ g.random(0x2A..0x2F, :horz)
+ g.random([0x4E, 0x5E, 0x6E, 0x7E], :vert)
+ g.random([0x4F, 0x5F, 0x6F, 0x7F], :vert)
+ g.random(0xA0..0xA3, :horz)
+end
+
+g.section('Pa1_lavaglow') do
+ g.random([0x20, 0x30, 0x40, 0x50], :vert)
+ g.random([0x21, 0x31, 0x41, 0x51], :vert)
+ g.random(0x2..0x7, :horz_vdouble_top)
+ g.random(0x12..0x17, :horz_vdouble_bottom)
+ g.random(0x22..0x27)
+ g.random(0x32..0x37, :horz)
+ g.random(0x28..0x2D, :horz)
+end
+
+g.section('Pa1_StarRoad') do
+ g.random(0x11, :both, [0x11, 0x4C, 0x4D, 0x4E, 0x4F, 0x5C, 0x5D, 0x5E, 0x5F])
+end
+
+g.section('Pa2_forestobake') do
+ g.random(0..5, :horz)
+ g.random(0x10..0x15)
+ g.random(0x20..0x25, :horz)
+ g.random([0x40, 0x50, 0x60, 0x70], :vert)
+ g.random([0x41, 0x51, 0x61, 0x71], :vert)
+end
+
+
+
+
File.open('/home/me/Games/Newer/DolphinPatch/NewerRes/RandTiles.bin', 'wb') do |f|
f.write g.pack
end
diff --git a/src/randtiles.cpp b/src/randtiles.cpp
index 92e31ae..34e2daf 100644
--- a/src/randtiles.cpp
+++ b/src/randtiles.cpp
@@ -9,6 +9,34 @@ public:
CHECK_BOTH = 3
};
+ enum Special {
+ SP_NONE = 0,
+ SP_VDOUBLE_TOP = 1,
+ SP_VDOUBLE_BOTTOM = 2
+ };
+
+ class NameList {
+ public:
+ u32 count;
+ u32 offsets[1]; // variable size
+
+ const char *getName(int index) {
+ return ((char*)this) + offsets[index];
+ }
+
+ bool contains(const char *name) {
+ OSReport("NameList[%p] with %d names being compared against %s\n", this, count, name);
+ for (int i = 0; i < count; i++) {
+ OSReport("Checking %s\n", getName(i));
+ if (strcmp(name, getName(i)) == 0)
+ return true;
+ OSReport("Did not pass\n");
+ }
+
+ return false;
+ }
+ };
+
class Entry {
public:
u8 lowerBound, upperBound;
@@ -22,12 +50,12 @@ public:
class Section {
public:
- u32 nameOffset;
+ u32 nameListOffset;
u32 entryCount;
Entry entries[1]; // variable size
- const char *getName() {
- return ((char*)this) + nameOffset;
+ NameList *getNameList() {
+ return (NameList*)(((u32)this) + nameListOffset);
}
};
@@ -54,9 +82,8 @@ RandomTileData::Section *RandomTileData::getSection(const char *name) {
for (int i = 0; i < sectionCount; i++) {
RandomTileData::Section *sect = getSection(i);
- if (strcmp(name, sect->getName()) == 0) {
+ if (sect->getNameList()->contains(name))
return sect;
- }
}
return 0;
@@ -76,7 +103,7 @@ extern "C" bool RandTileLoadHook() {
OSReport("Failed.\n");
return false;
} else {
- OSReport("Successfully loaded RandTiles.bin.\n");
+ OSReport("Successfully loaded RandTiles.bin [%p].\n", buf);
RandomTileData::instance = (RandomTileData*)buf;
return true;
}
@@ -90,6 +117,7 @@ extern "C" void IdentifyTilesets(RTilemapClass *self) {
const char *tilesetName = BGDatClass::instance->getTilesetName(self->areaID, i);
self->sections[i] = RandomTileData::instance->getSection(tilesetName);
+ OSReport("[%d] Chose %p for %s\n", i, self->sections[i], tilesetName);
}
}
@@ -108,24 +136,38 @@ extern "C" void TryAndRandomise(RTilemapClass *self, BGRender *bgr) {
if (tile >= entry->lowerBound && tile <= entry->upperBound) {
// Found it!!
// Try to make one until we meet the conditions
+ u8 type = entry->type & 3;
+ u8 special = entry->type >> 2;
+
u8 *tileNums = entry->getTileNums();
u16 chosen = 0xFF;
+ // If it's the top special, then ignore this tile, we'll place that one
+ // once we choose the bottom one
+ if (special == RandomTileData::SP_VDOUBLE_TOP)
+ break;
+
u16 *top = 0, *left = 0, *right = 0, *bottom = 0;
- if (entry->type == RandomTileData::CHECK_HORZ || entry->type == RandomTileData::CHECK_BOTH) {
+ if (type == RandomTileData::CHECK_HORZ || type == RandomTileData::CHECK_BOTH) {
left = self->getPointerToTile(bgr->curX - 1, bgr->curY);
right = self->getPointerToTile(bgr->curX + 1, bgr->curY);
}
- if (entry->type == RandomTileData::CHECK_VERT || entry->type == RandomTileData::CHECK_BOTH) {
+ if (type == RandomTileData::CHECK_VERT || type == RandomTileData::CHECK_BOTH) {
top = self->getPointerToTile(bgr->curX, bgr->curY - 1);
bottom = self->getPointerToTile(bgr->curX, bgr->curY + 1);
}
+ int attempts = 0;
while (true) {
// is there even a point to using that special random function?
chosen = (tileset << 8) | tileNums[MakeRandomNumberForTiles(entry->count)];
+ // avoid infinite loops
+ attempts++;
+ if (attempts > 5)
+ break;
+
if (top != 0 && *top == chosen)
continue;
if (bottom != 0 && *bottom == chosen)
@@ -139,6 +181,13 @@ extern "C" void TryAndRandomise(RTilemapClass *self, BGRender *bgr) {
bgr->tileToPlace = chosen;
+ if (special == RandomTileData::SP_VDOUBLE_BOTTOM) {
+ if (top == 0)
+ top = self->getPointerToTile(bgr->curX, bgr->curY - 1);
+
+ *top = (chosen - 0x10);
+ }
+
return;
}
}