diff --git a/docs/config.rst b/docs/config.rst index c4496f6..ff84568 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -715,6 +715,11 @@ SpawnOverlay this on top of other modes, or on top of ClearBase to create a pure overlay. +SlimeOverlay + Color the map green in chunks where slimes can spawn. Either use + this on top of other modes, or on top of ClearBase to create a + pure overlay. + MineralOverlay Color the map according to what minerals can be found underneath. Either use this on top of other modes, or on top of diff --git a/overviewer.py b/overviewer.py index 1e7b4d1..ab4e4ac 100755 --- a/overviewer.py +++ b/overviewer.py @@ -423,7 +423,7 @@ dir but you forgot to put quotes around the directory, since it contains spaces. render['name'] = render_name # perhaps a hack. This is stored here for the asset manager tileSetOpts = util.dict_subset(render, ["name", "imgformat", "renderchecks", "rerenderprob", "bgcolor", "imgquality", "optimizeimg", "rendermode", "worldname_orig", "title", "dimension", "changelist","showspawn", "overlay","base"]) tileSetOpts.update({"spawn": w.find_true_spawn()}) # TODO find a better way to do this - tset = tileset.TileSet(rset, assetMrg, tex, tileSetOpts, tileset_dir) + tset = tileset.TileSet(w, rset, assetMrg, tex, tileSetOpts, tileset_dir) tilesets.append(tset) # Do tileset preprocessing here, before we start dispatching jobs diff --git a/overviewer_core/rendermodes.py b/overviewer_core/rendermodes.py index 5de04e6..d854d0f 100644 --- a/overviewer_core/rendermodes.py +++ b/overviewer_core/rendermodes.py @@ -195,6 +195,9 @@ class Overlay(RenderPrimitive): class SpawnOverlay(Overlay): name = "overlay-spawn" +class SlimeOverlay(Overlay): + name = "overlay-slime" + class MineralOverlay(Overlay): name = "overlay-mineral" options = { diff --git a/overviewer_core/src/iterate.c b/overviewer_core/src/iterate.c index bbc9446..6411f90 100644 --- a/overviewer_core/src/iterate.c +++ b/overviewer_core/src/iterate.c @@ -432,7 +432,6 @@ generate_pseudo_data(RenderState *state, unsigned char ancilData) { PyObject* chunk_render(PyObject *self, PyObject *args) { RenderState state; - PyObject *regionset; PyObject *modeobj; PyObject *blockmap; @@ -453,7 +452,7 @@ chunk_render(PyObject *self, PyObject *args) { PyObject *t = NULL; - if (!PyArg_ParseTuple(args, "OiiiOiiOO", &state.regionset, &state.chunkx, &state.chunky, &state.chunkz, &state.img, &xoff, &yoff, &modeobj, &state.textures)) + if (!PyArg_ParseTuple(args, "OOiiiOiiOO", &state.world, &state.regionset, &state.chunkx, &state.chunky, &state.chunkz, &state.img, &xoff, &yoff, &modeobj, &state.textures)) return NULL; /* set up the render mode */ diff --git a/overviewer_core/src/overviewer.h b/overviewer_core/src/overviewer.h index 0bca02d..caefdeb 100644 --- a/overviewer_core/src/overviewer.h +++ b/overviewer_core/src/overviewer.h @@ -26,7 +26,7 @@ // increment this value if you've made a change to the c extesion // and want to force users to rebuild -#define OVERVIEWER_EXTENSION_VERSION 35 +#define OVERVIEWER_EXTENSION_VERSION 36 /* Python PIL, and numpy headers */ #include @@ -88,6 +88,7 @@ typedef struct { } ChunkData; typedef struct { /* the regionset object, and chunk coords */ + PyObject *world; PyObject *regionset; int chunkx, chunky, chunkz; diff --git a/overviewer_core/src/primitives/overlay-slime.c b/overviewer_core/src/primitives/overlay-slime.c new file mode 100644 index 0000000..f4abfe8 --- /dev/null +++ b/overviewer_core/src/primitives/overlay-slime.c @@ -0,0 +1,126 @@ +/* + * This file is part of the Minecraft Overviewer. + * + * Minecraft Overviewer is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Minecraft Overviewer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the Overviewer. If not, see . + */ + +#include "overlay.h" +#include + +typedef struct { + /* inherits from overlay */ + RenderPrimitiveOverlay parent; + long seed; +} RenderPrimitiveSlime; + +/* + * random_* are a re-implementation of java's Random() class + * since Minecraft's slime algorithm depends on it + * http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Random.html + */ + +static void random_set_seed(long *seed, long new_seed) { + *seed = (new_seed ^ 0x5deece66dL) & ((1L << 48) - 1); +} + +static int random_next(long *seed, int bits) { + *seed = (*seed * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); + return (int)(*seed >> (48 - bits)); +} + +static int random_next_int(long *seed, int n) { + int bits, val; + + if (n <= 0) { + /* invalid */ + return 0; + } + + if ((n & -n) == n) { + /* n is a power of two */ + return (int)((n * (long)random_next(seed, 31)) >> 31); + } + + do { + bits = random_next(seed, 31); + val = bits % n; + } while (bits - val + (n - 1) < 0); + return val; +} + +static int is_slime(long map_seed, long chunkx, long chunkz) { + /* lots of magic numbers, but they're all correct! I swear! */ + long seed; + random_set_seed(&seed, map_seed + (chunkx * chunkx * 0x4c1906L) + (chunkx * 0x5ac0dbL) + (chunkz * chunkz * 0x4307a7L) + (chunkz * 0x5f24fL) ^ 0x3ad8025fL); + return (random_next_int(&seed, 10) == 0); +} + +static void get_color(void *data, RenderState *state, + unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a) { + RenderPrimitiveSlime *self = (RenderPrimitiveSlime *)data; + + /* set a nice, pretty green color */ + *r = 40; + *g = 230; + *b = 40; + + /* default to no overlay, until told otherwise */ + *a = 0; + + if (is_slime(self->seed, state->chunkx, state->chunkz)) { + /* slimes can spawn! */ + *a = 240; + } +} + +static int +overlay_slime_start(void *data, RenderState *state, PyObject *support) { + RenderPrimitiveSlime *self; + PyObject *pyseed; + + /* first, chain up */ + int ret = primitive_overlay.start(data, state, support); + if (ret != 0) + return ret; + + /* now do custom initializations */ + self = (RenderPrimitiveSlime *)data; + self->parent.get_color = get_color; + + pyseed = PyObject_GetAttrString(state->world, "seed"); + if (!pyseed) + return 1; + self->seed = PyInt_AsLong(pyseed); + Py_DECREF(pyseed); + if (PyErr_Occurred()) + return 1; + + return 0; +} + +static void +overlay_slime_finish(void *data, RenderState *state) { + /* chain up */ + primitive_overlay.finish(data, state); +} + +RenderPrimitiveInterface primitive_overlay_slime = { + "overlay-slime", + sizeof(RenderPrimitiveSlime), + overlay_slime_start, + overlay_slime_finish, + NULL, + NULL, + overlay_draw, +}; diff --git a/overviewer_core/tileset.py b/overviewer_core/tileset.py index e5f10e0..629f59f 100644 --- a/overviewer_core/tileset.py +++ b/overviewer_core/tileset.py @@ -164,12 +164,14 @@ class TileSet(object): """ - def __init__(self, regionsetobj, assetmanagerobj, texturesobj, options, outputdir): + def __init__(self, worldobj, regionsetobj, assetmanagerobj, texturesobj, options, outputdir): """Construct a new TileSet object with the given configuration options dictionary. options is a dictionary of configuration parameters (strings mapping to values) that are interpreted by the rendering engine. + + worldobj is the World object that regionsetobj is from. regionsetobj is the RegionSet object that is used to render the tiles. @@ -269,6 +271,7 @@ class TileSet(object): """ self.options = options + self.world = worldobj self.regionset = regionsetobj self.am = assetmanagerobj self.textures = texturesobj @@ -356,7 +359,7 @@ class TileSet(object): # Only pickle the initial state. Don't pickle anything resulting from the # do_preprocessing step def __getstate__(self): - return self.regionset, self.am, self.textures, self.options, self.outputdir + return self.world, self.regionset, self.am, self.textures, self.options, self.outputdir def __setstate__(self, state): self.__init__(*state) @@ -946,7 +949,7 @@ class TileSet(object): # draw the chunk! try: - c_overviewer.render_loop(self.regionset, chunkx, chunky, + c_overviewer.render_loop(self.world, self.regionset, chunkx, chunky, chunkz, tileimg, xpos, ypos, self.options['rendermode'], self.textures) except nbt.CorruptionError: diff --git a/overviewer_core/world.py b/overviewer_core/world.py index bce578a..af76a49 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -136,7 +136,12 @@ class World(object): except KeyError: # but very old ones might not? so we'll just go with the world dir name if they don't self.name = os.path.basename(os.path.realpath(self.worlddir)) - + + try: + # level.dat also has a RandomSeed attribute + self.seed = data['RandomSeed'] + except KeyError: + self.seed = 0 # oh well # TODO figure out where to handle regionlists