diff --git a/binding/binding-mri.cpp b/binding/binding-mri.cpp index 28b821c8..9077f41a 100644 --- a/binding/binding-mri.cpp +++ b/binding/binding-mri.cpp @@ -61,6 +61,8 @@ extern const char module_rpg1[]; extern const char module_rpg2[]; extern const char module_rpg3[]; +#include "EssentialsTilemapHack.rb.xxd" + static void mriBindingExecute(); static void mriBindingTerminate(); static void mriBindingReset(); @@ -473,6 +475,14 @@ struct BacktraceData { BoostHash scriptNames; }; +bool evalScript(VALUE string, const char *filename) +{ + int state; + evalString(string, rb_str_new_cstr(filename), &state); + if (state) return false; + return true; +} + #ifndef MARIN #define SCRIPT_SECTION_FMT (rgssVer >= 3 ? "{%04ld}" : "Section%03ld") @@ -570,6 +580,9 @@ static void runRMXPScripts(BacktraceData &btData) { return; #endif + // Used to try and fix Essentials garbage later if it's detected + int minimonsters = 0; + while (true) { #if RAPI_FULL < 200 && defined(NO_CONSOLE) VALUE iostr = rb_str_new2(NULL_IO); @@ -596,6 +609,20 @@ static void runRMXPScripts(BacktraceData &btData) { fname = newStringUTF8(buf, len); btData.scriptNames.insert(buf, scriptName); + // There is 0 reason for anything other than Essentials to have this class + if (rb_const_defined(rb_cObject, rb_intern("PokemonMapMetadata")) && minimonsters == 0) + minimonsters = 1; + + // Before checking to see if the next script should be skipped, + // make sure to check whether it's the last one or not and run + // any extra stuff before the end (primarily stupid Essentials stuff) + // Will be placed within a build option later + #define SCRIPT(name) rb_str_new((const char*)&___scripts_##name##_rb, ___scripts_##name##_rb_len), #name " (Internal)" + if (minimonsters > 0 && i + 2 == scriptCount && !RTEST(rb_gv_get("Z_NOPOKEFIX"))){ + evalScript(SCRIPT(EssentialsTilemapHack)); + minimonsters = -1; + } + // if the script name starts with |s|, only execute // it if "s" is the same first letter as the platform // we're running on diff --git a/meson.build b/meson.build index 4d1784de..bcff14e2 100644 --- a/meson.build +++ b/meson.build @@ -140,6 +140,7 @@ subdir('src') subdir('binding') subdir('shader') subdir('assets') +subdir('scripts') global_include_dirs += include_directories('src', 'binding') diff --git a/scripts/EssentialsTilemapHack.rb b/scripts/EssentialsTilemapHack.rb new file mode 100644 index 00000000..6de1d946 --- /dev/null +++ b/scripts/EssentialsTilemapHack.rb @@ -0,0 +1,109 @@ +# ====================================================================== +# TILEMAP VERTICAL WRAPPER +# +# This is a little fix for Pokemon Essentials' custom tilemap code +# that works around MKXP's GPU texture size limit that would normally +# stop you from playing a lot of games. +# +# The concept is simple enough: If your tileset is too big, a new +# bitmap will be constructed with all the excess pixels sent to the +# image's right side. This basically means that you now have a limit +# far higher than you should ever actually need. +# +# Because of the extra math the game will have to do to find the right +# pixels, this will probably cause a performance hit while on these +# maps which would normally be megasurfaces. +# +# It can probably be improved by changing CustomTilemap.getRegularTile +# to just find the correct pixels on its own, without having to +# translate the coordinates afterwards, but this will satisfy me just +# for the moment. +# +# Really, it'd be far better to cut up the image on the C++ end and +# use a custom shader to get the image right or something like that, +# but that's work for another day. +# +# For now, I'm just happy I can finally test whatever game I like. +# +$GL_TEX_CAP = 16384 # << This should be automatically set at some point. +# +# ~Zoro +#======================================================================= + +module VWrap + + def self.clamp(val, min, max) + val = max if val > max + val = min if val < min + return val + end + + def self.makeVWrappedTileset(originalbmp) + width = originalbmp.width + height = originalbmp.height + if width == 256 && height > $GL_TEX_CAP + columns = (height / $GL_TEX_CAP.to_f).ceil + + return nil if columns * 256 > $GL_TEX_CAP + bmp = Bitmap.new(256*columns, $GL_TEX_CAP) + remainder = height % $GL_TEX_CAP + + columns.times{|col| + srcrect = Rect.new(0, col * $GL_TEX_CAP, width, (col + 1 == columns) ? remainder : $GL_TEX_CAP) + bmp.blt(col*256, 0, originalbmp, srcrect) + } + return bmp + end + + return originalbmp + end + + def self.blitVWrappedPixels(destX, destY, dest, src, srcrect) + merge = (srcrect.y % $GL_TEX_CAP) > ((srcrect.y + srcrect.height) % $GL_TEX_CAP) + + srcrect.x = clamp(srcrect.x, 0,256) + srcrect.width = clamp(srcrect.width, 0, 256 - srcrect.x) + col = (srcrect.y / $GL_TEX_CAP.to_f).floor + srcX = col * 256 + srcrect.x + srcY = srcrect.y % $GL_TEX_CAP + + if !merge + dest.blt(destX, destY, src, Rect.new(srcX, srcY, srcrect.width, srcrect.height)) + else + #FIXME won't work on heights longer than two columns, but nobody should need + # more than 32k pixels high at once anyway + side = {:a => $GL_TEX_CAP - srcY, :b => srcrect.height - ($GL_TEX_CAP - srcY)} + dest.blt(destX, destY, src, Rect.new(srcX, srcY, srcrect.width, side[:a])) + dest.blt(destX, destY + side[:a], src, Rect.new(srcX + 256, 0, srcrect.width, side[:b])) + end + end +end + +# ------------------------- +if $MKXP == true + class CustomTilemap + def tileset=(value) + if value.height > $GL_TEX_CAP || value.width > $GL_TEX_CAP + @tileset = VWrap::makeVWrappedTileset(value) + else + @tileset = value + end + @tilesetchanged = true + end + + alias old_getRegularTile getRegularTile + def getRegularTile(sprite, id) + return old_getRegularTile(sprite, id) if @tileset.width <= 256 + + bitmap = @regularTileInfo[id] + if !bitmap + bitmap = Bitmap.new(@tileWidth, @tileHeight) + rect=Rect.new(((id - 384)&7)*@tileSrcWidth,((id - 384)>>3)*@tileSrcHeight, + @tileSrcWidth,@tileSrcHeight) + VWrap::blitVWrappedPixels(0,0, bitmap, @tileset, rect) + @regularTileInfo[id]=bitmap + end + sprite.bitmap = bitmap if sprite.bitmap != bitmap + end + end +end diff --git a/scripts/meson.build b/scripts/meson.build new file mode 100644 index 00000000..88b25a69 --- /dev/null +++ b/scripts/meson.build @@ -0,0 +1,20 @@ + +embedded_scripts = [ + 'EssentialsTilemapHack.rb' +] + +embedded_scripts_f = files(embedded_scripts) + +count = 0 +foreach file : embedded_scripts_f + global_sources += custom_target(embedded_scripts[count], + input: file, + output: '@0@.xxd'.format(embedded_scripts[count]), + command: [ + xxd, '-i', '@INPUT@' + ], + capture: true, + depend_files: embedded_scripts_f[count] + ) + count += 1 +endforeach