Merge remote branch 'source/master' into remote-tiles
Conflicts: overviewer.py
This commit is contained in:
@@ -552,6 +552,18 @@ values. The valid configuration keys are listed below.
|
||||
|
||||
**Default:** ``[]`` (an empty list)
|
||||
|
||||
.. _option_overlay:
|
||||
|
||||
``overlay``
|
||||
This specifies which renders that this render will be displayed on top of.
|
||||
It should be a list of renders.
|
||||
|
||||
.. warning::
|
||||
|
||||
At this time, this feature is not fully implemented.
|
||||
|
||||
**Default:** ``[]`` (an empty list)
|
||||
|
||||
``showspawn``
|
||||
This is a boolean, and defaults to ``True``. If set to ``False``, then the spawn
|
||||
icon will not be displayed on the rendered map.
|
||||
@@ -636,6 +648,15 @@ Cave
|
||||
only_lit
|
||||
Only render lit caves. Default: False
|
||||
|
||||
Hide
|
||||
Hide blocks based on blockid. Blocks hidden in this way will be
|
||||
treated exactly the same as air.
|
||||
|
||||
**Options**
|
||||
|
||||
minerals
|
||||
A list of block ids, or (blockid, data) tuples to hide.
|
||||
|
||||
DepthTinting
|
||||
Tint blocks a color according to their depth (height) from bedrock. Useful
|
||||
mainly for cave renders.
|
||||
|
||||
@@ -20,51 +20,97 @@ Filter Functions
|
||||
----------------
|
||||
|
||||
A filter function is a python function that is used to figure out if a given POI
|
||||
should be part of a markerSet of not. The function should accept one argument
|
||||
(a dictionary, also know as an associative array), and return a boolean::
|
||||
should be part of a markerSet of not, and to control how it is displayed.
|
||||
The function should accept one argument (a dictionary, also know as an associative
|
||||
array), and return a string representing the text to be displayed. For example::
|
||||
|
||||
def signFilter(poi):
|
||||
"All signs"
|
||||
return poi['id'] == 'Sign'
|
||||
if poi['id'] == 'Sign':
|
||||
return "\n".join([poi['Text1'], poi['Text2'], poi['Text3'], poi['Text4']])
|
||||
|
||||
If a POI doesn't match, the filter can return None (which is the default if a python
|
||||
functions runs off the end without an explicit 'return').
|
||||
|
||||
The single argument will either a TileEntity, or an Entity taken directly from
|
||||
the chunk file. In this example, this function returns true only if the type
|
||||
of entity is a sign. For more information of TileEntities and Entities, see
|
||||
the chunk file. It could also be a special entity representing a player's location
|
||||
or a player's spawn. See below for more details.
|
||||
|
||||
In this example, this function returns all 4 lines from the sign
|
||||
if the entity is a sign.
|
||||
For more information of TileEntities and Entities, see
|
||||
the `Chunk Format <http://www.minecraftwiki.net/wiki/Chunk_format>`_ page on
|
||||
the Minecraft Wiki.
|
||||
|
||||
.. note::
|
||||
The doc string ("All signs" in this example) is important. It is the label
|
||||
that appears in your rendered map
|
||||
A more complicated filter function can construct a more customized display text::
|
||||
|
||||
A more advanced filter may also look at other entity fields, such as the sign text::
|
||||
def chestFilter(poi):
|
||||
if poi['id'] == "Chest":
|
||||
return "Chest with %d items" % len(poi['Items'])
|
||||
|
||||
def goldFilter(poi):
|
||||
"Gold"
|
||||
return poi['id'] == 'Sign' and (\
|
||||
'gold' in poi['Text1'] or
|
||||
'gold' in poi['Text2'])
|
||||
|
||||
This looks for the word 'gold' in either the first or second line of the signtext.
|
||||
|
||||
Since writing these filters can be a little tedious, a set of predefined filters
|
||||
functions are provided. See the :ref:`predefined_filter_functions` section for
|
||||
details.
|
||||
|
||||
|
||||
Special POIs
|
||||
------------
|
||||
|
||||
There are currently two special types of POIs. They each have a special id:
|
||||
|
||||
PlayerSpawn
|
||||
Used to indicate the spawn location of a player. The player's name is set
|
||||
in the ``EntityId`` key, and the location is in the x,y,z keys
|
||||
|
||||
Player
|
||||
Used to indicate the last known location of a player. The player's name is set
|
||||
in the ``EntityId`` key, and the location is in the x,y,z keys.
|
||||
|
||||
.. note::
|
||||
The player location is taken from level.dat (in the case of a single-player world)
|
||||
or the player.dat files (in the case of a multi-player server). The locations are
|
||||
only written to these files when the world is saved, so this won't give you real-time
|
||||
player location information.
|
||||
|
||||
Here's an example that displays icons for each player::
|
||||
|
||||
def playerIcons(poi):
|
||||
if poi['id'] == 'Player':
|
||||
poi['icon'] = "http://overviewer.org/avatar/%s" % poi['EntityId']
|
||||
return "Last known location for %s" % poi['EntityId']
|
||||
|
||||
Note how each POI can get a different icon by setting ``poi['icon']``
|
||||
|
||||
Render Dictionary Key
|
||||
---------------------
|
||||
|
||||
Each render can specify a list of zero or more filter functions. Each of these
|
||||
filter functions become a selectable item in the 'Signs' drop-down menu in the
|
||||
rendered map. For example::
|
||||
rendered map. Previously, this used to be a list of functions. Now it is a list
|
||||
of dictionaries. For example::
|
||||
|
||||
renders['myrender'] = {
|
||||
'world': 'myworld',
|
||||
'title': "Example",
|
||||
'markers': [allFilter, anotherFilter],
|
||||
'markers': [dict(name="All signs", filterFunction=signFilter),
|
||||
dict(name="Chests", filterFunction=chestFilter, icon="chest.png")]
|
||||
}
|
||||
|
||||
|
||||
The following keys are accepted in the marker dictionary:
|
||||
|
||||
``name``
|
||||
This is the text that is displayed in the 'Signs' dropdown.
|
||||
|
||||
``filterFunction``
|
||||
This is the filter function. It must accept at least 1 argument (the POI to filter),
|
||||
and it must return either None or a string.
|
||||
|
||||
``icon``
|
||||
Optional. Specifies the icon to use for POIs in this group. If omitted, it defaults
|
||||
to a signpost icon. Note that each POI can have different icon by setting the key 'icon'
|
||||
on the POI itself (this can be done by modifying the POI in the filter function. See the
|
||||
example above)
|
||||
|
||||
|
||||
Generating the POI Markers
|
||||
|
||||
95
genPOI.py
95
genPOI.py
@@ -17,6 +17,7 @@ markers.js holds a list of which markerSets are attached to each tileSet
|
||||
import os
|
||||
import logging
|
||||
import json
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
|
||||
from overviewer_core import logger
|
||||
@@ -25,7 +26,8 @@ from overviewer_core import configParser, world
|
||||
|
||||
|
||||
def handleSigns(rset, outputdir, render, rname):
|
||||
|
||||
|
||||
# if we're already handled the POIs for this region regionset, do nothing
|
||||
if hasattr(rset, "_pois"):
|
||||
return
|
||||
|
||||
@@ -39,10 +41,64 @@ def handleSigns(rset, outputdir, render, rname):
|
||||
rset._pois['TileEntities'] += data['TileEntities']
|
||||
rset._pois['Entities'] += data['Entities']
|
||||
|
||||
logging.info("Done.")
|
||||
|
||||
def handlePlayers(rset, render, worldpath):
|
||||
if not hasattr(rset, "_pois"):
|
||||
rset._pois = dict(TileEntities=[], Entities=[])
|
||||
|
||||
# only handle this region set once
|
||||
if 'Players' in rset._pois:
|
||||
return
|
||||
dimension = {'overworld': 0,
|
||||
'nether': -1,
|
||||
'end': 1,
|
||||
'default': 0}[render['dimension']]
|
||||
playerdir = os.path.join(worldpath, "players")
|
||||
if os.path.isdir(playerdir):
|
||||
playerfiles = os.listdir(playerdir)
|
||||
isSinglePlayer = False
|
||||
else:
|
||||
playerfiles = [os.path.join(worldpath, "level.dat")]
|
||||
isSinglePlayer = True
|
||||
|
||||
rset._pois['Players'] = []
|
||||
for playerfile in playerfiles:
|
||||
try:
|
||||
data = nbt.load(os.path.join(playerdir, playerfile))[1]
|
||||
if isSinglePlayer:
|
||||
data = data['Data']['Player']
|
||||
except IOError:
|
||||
logging.warning("Skipping bad player dat file %r", playerfile)
|
||||
continue
|
||||
playername = playerfile.split(".")[0]
|
||||
if isSinglePlayer:
|
||||
playername = 'Player'
|
||||
if data['Dimension'] == dimension:
|
||||
# Position at last logout
|
||||
data['id'] = "Player"
|
||||
data['EntityId'] = playername
|
||||
data['x'] = int(data['Pos'][0])
|
||||
data['y'] = int(data['Pos'][1])
|
||||
data['z'] = int(data['Pos'][2])
|
||||
rset._pois['Players'].append(data)
|
||||
if "SpawnX" in data and dimension == 0:
|
||||
# Spawn position (bed or main spawn)
|
||||
spawn = {"id": "PlayerSpawn",
|
||||
"EntityId": playername,
|
||||
"x": data['SpawnX'],
|
||||
"y": data['SpawnY'],
|
||||
"z": data['SpawnZ']}
|
||||
rset._pois['Players'].append(spawn)
|
||||
|
||||
def main():
|
||||
helptext = """genPOI
|
||||
%prog --config=<config file>"""
|
||||
|
||||
if os.path.basename(sys.argv[0]) == """genPOI.py""":
|
||||
helptext = """genPOI.py
|
||||
%prog --config=<config file> [--quiet]"""
|
||||
else:
|
||||
helptext = """genPOI
|
||||
%prog --genpoi --config=<config file> [--quiet]"""
|
||||
|
||||
logger.configure()
|
||||
|
||||
@@ -97,26 +153,43 @@ def main():
|
||||
return 1
|
||||
|
||||
for f in render['markers']:
|
||||
markersets.add((f, rset))
|
||||
name = f.__name__ + hex(hash(f))[-4:] + "_" + hex(hash(rset))[-4:]
|
||||
d = dict(icon="signpost_icon.png")
|
||||
d.update(f)
|
||||
markersets.add(((d['name'], d['filterFunction']), rset))
|
||||
name = f['name'].replace(" ","_") + hex(hash(f['filterFunction']))[-4:] + "_" + hex(hash(rset))[-4:]
|
||||
try:
|
||||
l = markers[rname]
|
||||
l.append(dict(groupName=name, displayName = f.__doc__))
|
||||
l.append(dict(groupName=name, displayName = f['name'], icon=d['icon']))
|
||||
except KeyError:
|
||||
markers[rname] = [dict(groupName=name, displayName=f.__doc__),]
|
||||
markers[rname] = [dict(groupName=name, displayName=f['name'], icon=d['icon']),]
|
||||
|
||||
handleSigns(rset, os.path.join(destdir, rname), render, rname)
|
||||
handlePlayers(rset, render, worldpath)
|
||||
|
||||
logging.info("Done scanning regions")
|
||||
logging.info("Writing out javascript files")
|
||||
markerSetDict = dict()
|
||||
for (flter, rset) in markersets:
|
||||
# generate a unique name for this markerset. it will not be user visible
|
||||
name = flter.__name__ + hex(hash(flter))[-4:] + "_" + hex(hash(rset))[-4:]
|
||||
markerSetDict[name] = dict(created=False, raw=[])
|
||||
filter_name = flter[0]
|
||||
filter_function = flter[1]
|
||||
|
||||
name = filter_name.replace(" ","_") + hex(hash(filter_function))[-4:] + "_" + hex(hash(rset))[-4:]
|
||||
markerSetDict[name] = dict(created=False, raw=[], name=filter_name)
|
||||
for poi in rset._pois['TileEntities']:
|
||||
if flter(poi):
|
||||
markerSetDict[name]['raw'].append(poi)
|
||||
result = filter_function(poi)
|
||||
if result:
|
||||
d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result, createInfoWindow=True)
|
||||
if "icon" in poi:
|
||||
d.update({"icon": poi['icon']})
|
||||
markerSetDict[name]['raw'].append(d)
|
||||
for poi in rset._pois['Players']:
|
||||
result = filter_function(poi)
|
||||
if result:
|
||||
d = dict(x=poi['x'], y=poi['y'], z=poi['z'], text=result, createInfoWindow=True)
|
||||
if "icon" in poi:
|
||||
d.update({"icon": poi['icon']})
|
||||
markerSetDict[name]['raw'].append(d)
|
||||
#print markerSetDict
|
||||
|
||||
with open(os.path.join(destdir, "markersDB.js"), "w") as output:
|
||||
|
||||
@@ -89,6 +89,8 @@ def main():
|
||||
help="Print less output. You can specify this option multiple times.")
|
||||
parser.add_option("-v", "--verbose", dest="verbose", action="count", default=0,
|
||||
help="Print more output. You can specify this option multiple times.")
|
||||
parser.add_option("--simple-output", dest="simple", action="store_true", default=False,
|
||||
help="Use a simple output format, with no colors or progress bars")
|
||||
|
||||
# create a group for "plugin exes" (the concept of a plugin exe is only loosly defined at this point)
|
||||
exegroup = OptionGroup(parser, "Other Scripts",
|
||||
@@ -114,7 +116,8 @@ def main():
|
||||
|
||||
# re-configure the logger now that we've processed the command line options
|
||||
logger.configure(logging.INFO + 10*options.quiet - 10*options.verbose,
|
||||
options.verbose > 0)
|
||||
verbose=options.verbose > 0,
|
||||
simple=options.simple)
|
||||
|
||||
##########################################################################
|
||||
# This section of main() runs in response to any one-time options we have,
|
||||
@@ -233,7 +236,6 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
||||
return 1
|
||||
|
||||
# Parse the config file
|
||||
mw_parser = configParser.MultiWorldParser()
|
||||
mw_parser.parse(options.config)
|
||||
|
||||
# Add in the command options here, perhaps overriding values specified in
|
||||
@@ -244,8 +246,12 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
||||
# Now parse and return the validated config
|
||||
try:
|
||||
config = mw_parser.get_validated_config()
|
||||
except Exception:
|
||||
logging.exception("An error was encountered with your configuration. See the info below.")
|
||||
except Exception as ex:
|
||||
if options.verbose:
|
||||
logging.exception("An error was encountered with your configuration. See the info below.")
|
||||
else: # no need to print scary traceback! just
|
||||
logging.error("An error was encountered with your configuration.")
|
||||
logging.error(str(ex))
|
||||
return 1
|
||||
|
||||
|
||||
@@ -302,6 +308,20 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
||||
if render.get('forcerender', False):
|
||||
render['renderchecks'] = 2
|
||||
|
||||
# check if overlays are set, if so, make sure that those renders exist
|
||||
if render.get('overlay', []) != []:
|
||||
for x in render.get('overlay'):
|
||||
if x != rname:
|
||||
try:
|
||||
renderLink = config['renders'][x]
|
||||
except KeyError:
|
||||
logging.error("Render %s's overlay is '%s', but I could not find a corresponding entry in the renders dictionary.",
|
||||
rname, x)
|
||||
return 1
|
||||
else:
|
||||
logging.error("Render %s's overlay contains itself.", rname)
|
||||
return 1
|
||||
|
||||
destdir = config['outputdir']
|
||||
if not destdir:
|
||||
logging.error("You must specify the output directory in your config file.")
|
||||
@@ -401,7 +421,7 @@ dir but you forgot to put quotes around the directory, since it contains spaces.
|
||||
|
||||
# only pass to the TileSet the options it really cares about
|
||||
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","base"])
|
||||
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)
|
||||
tilesets.append(tset)
|
||||
|
||||
@@ -87,6 +87,7 @@ directory.
|
||||
dump['CONST']['image'] = {
|
||||
'defaultMarker': 'signpost.png',
|
||||
'signMarker': 'signpost_icon.png',
|
||||
'bedMarker': 'bed.png',
|
||||
'compass': 'compass_upper-left.png',
|
||||
'spawnMarker': 'http://google-maps-icons.googlecode.com/files/home.png',
|
||||
'queryMarker': 'http://google-maps-icons.googlecode.com/files/regroup.png'
|
||||
|
||||
@@ -63,6 +63,8 @@ overviewer.util = {
|
||||
signs.registerEvents(signs);
|
||||
}
|
||||
|
||||
var overlayControl = new overviewer.views.OverlayControlView();
|
||||
|
||||
var spawnmarker = new overviewer.views.SpawnIconView();
|
||||
|
||||
// Update coords on mousemove
|
||||
@@ -81,6 +83,9 @@ overviewer.util = {
|
||||
compass.render();
|
||||
spawnmarker.render();
|
||||
|
||||
// update list of spawn overlays
|
||||
overlayControl.render();
|
||||
|
||||
// re-center on the last viewport
|
||||
var currentWorldView = overviewer.mapModel.get("currentWorldView");
|
||||
if (currentWorldView.options.lastViewport) {
|
||||
@@ -113,8 +118,6 @@ overviewer.util = {
|
||||
|
||||
});
|
||||
|
||||
var worldSelector = new overviewer.views.WorldSelectorView({tagName:'DIV'});
|
||||
overviewer.collections.worlds.bind("add", worldSelector.render, worldSelector);
|
||||
|
||||
// hook up some events
|
||||
|
||||
@@ -125,6 +128,11 @@ overviewer.util = {
|
||||
// Jump to the hash if given
|
||||
overviewer.util.initHash();
|
||||
|
||||
// create this control after initHash so it can correctly select the current world
|
||||
var worldSelector = new overviewer.views.WorldSelectorView({tagName:'DIV'});
|
||||
overviewer.collections.worlds.bind("add", worldSelector.render, worldSelector);
|
||||
|
||||
|
||||
overviewer.util.initializeMarkers();
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,8 +4,19 @@ overviewer.views= {}
|
||||
overviewer.views.WorldView = Backbone.View.extend({
|
||||
initialize: function(opts) {
|
||||
this.options.mapTypes = [];
|
||||
this.options.overlayMapTypes = [];
|
||||
this.options.mapTypeIds = [];
|
||||
this.options.overlayMapTypeIds = [];
|
||||
|
||||
var curTileSet = this.model.get("tileSets").at(0);
|
||||
var spawn = curTileSet.get("spawn");
|
||||
if (spawn == "false") {
|
||||
var spawn = [0,64,0];
|
||||
}
|
||||
this.options.lastViewport = [spawn[0],spawn[1],spawn[2],curTileSet.get("defaultZoom")];
|
||||
|
||||
this.model.get("tileSets").each(function(tset, index, list) {
|
||||
// ignore overlays:
|
||||
var ops = {
|
||||
getTileUrl: overviewer.gmap.getTileUrlGenerator(tset.get("path"), tset.get("base"), tset.get("imgextension")),
|
||||
'tileSize': new google.maps.Size(
|
||||
@@ -20,11 +31,23 @@ overviewer.views.WorldView = Backbone.View.extend({
|
||||
newMapType.shortname = tset.get("name");
|
||||
newMapType.alt = "Minecraft " + tset.get("name") + " Map";
|
||||
newMapType.projection = new overviewer.classes.MapProjection();
|
||||
|
||||
this.options.mapTypes.push(newMapType);
|
||||
this.options.mapTypeIds.push(overviewerConfig.CONST.mapDivId + this.model.get("name") + tset.get("name"));
|
||||
|
||||
if (tset.get("isOverlay")) {
|
||||
newMapType.tiles = tset.get("tilesets");
|
||||
this.options.overlayMapTypes.push(newMapType);
|
||||
this.options.overlayMapTypeIds.push(overviewerConfig.CONST.mapDivId + this.model.get("name") + tset.get("name"));
|
||||
} else {
|
||||
this.options.mapTypes.push(newMapType);
|
||||
this.options.mapTypeIds.push(overviewerConfig.CONST.mapDivId + this.model.get("name") + tset.get("name"));
|
||||
}
|
||||
|
||||
}, this);
|
||||
|
||||
this.model.get("tileSets").each(function(tset, index, list) {
|
||||
// ignore non-overlays:
|
||||
if (!tset.get("isOverlay")) { return; };
|
||||
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -33,6 +56,8 @@ overviewer.views.WorldView = Backbone.View.extend({
|
||||
overviewer.views.WorldSelectorView = Backbone.View.extend({
|
||||
initialize: function() {
|
||||
if(overviewer.collections.worldViews.length > 1) {
|
||||
$(this.el).addClass("customControl");
|
||||
|
||||
// a div will have already been created for us, we just
|
||||
// need to register it with the google maps control
|
||||
var selectBox = document.createElement('select');
|
||||
@@ -40,6 +65,9 @@ overviewer.views.WorldSelectorView = Backbone.View.extend({
|
||||
var o = document.createElement("option");
|
||||
o.value = elem.model.get("name");
|
||||
o.innerHTML = elem.model.get("name");
|
||||
if (elem.model == overviewer.mapModel.get("currentWorldView").model) {
|
||||
o.selected=true;
|
||||
}
|
||||
$(o).data("viewObj", elem);
|
||||
selectBox.appendChild(o);
|
||||
|
||||
@@ -119,17 +147,16 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
|
||||
var curWorld = this.model.get("currentWorldView").model;
|
||||
|
||||
var curTset = curWorld.get("tileSets").at(0);
|
||||
var spawn = curTset.get("spawn");
|
||||
if (spawn == "false") {
|
||||
var spawn = [0,64,0];
|
||||
}
|
||||
var mapcenter = overviewer.util.fromWorldToLatLng(
|
||||
spawn[0],
|
||||
spawn[1],
|
||||
spawn[2],
|
||||
curTset);
|
||||
|
||||
/*
|
||||
var defaultCenter = overviewer.util.fromWorldToLatLng(
|
||||
overviewerConfig.map.center[0],
|
||||
overviewerConfig.map.center[1],
|
||||
overviewerConfig.map.center[2],
|
||||
curTset.get("defaultZoom"));
|
||||
*/
|
||||
var lat = 0.62939453125;// TODO defaultCenter.lat();
|
||||
var lng = 0.38525390625; // TODO defaultCenter.lng();
|
||||
var mapcenter = new google.maps.LatLng(lat, lng);
|
||||
|
||||
this.options.mapTypes=[];
|
||||
this.options.mapTypeIds=[];
|
||||
@@ -200,6 +227,114 @@ overviewer.views.GoogleMapView = Backbone.View.extend({
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* OverlayControlView
|
||||
*/
|
||||
overviewer.views.OverlayControlView = Backbone.View.extend({
|
||||
/** OverlayControlVIew::initialize
|
||||
*/
|
||||
initialize: function(opts) {
|
||||
$(this.el).addClass("customControl");
|
||||
overviewer.map.controls[google.maps.ControlPosition.TOP_RIGHT].push(this.el);
|
||||
},
|
||||
registerEvents: function(me) {
|
||||
overviewer.mapModel.bind("change:currentWorldView", me.render, me);
|
||||
},
|
||||
|
||||
/**
|
||||
* OverlayControlView::render
|
||||
*/
|
||||
render: function() {
|
||||
this.el.innerHTML="";
|
||||
|
||||
// hide all visible overlays:
|
||||
overviewer.map.overlayMapTypes.clear()
|
||||
|
||||
// if this world has no overlays, don't create this control
|
||||
var mapTypes = overviewer.mapModel.get('currentWorldView').options.overlayMapTypes;
|
||||
if (mapTypes.length == 0) { return; }
|
||||
|
||||
var controlText = document.createElement('DIV');
|
||||
controlText.innerHTML = "Overlays";
|
||||
|
||||
var controlBorder = document.createElement('DIV');
|
||||
$(controlBorder).addClass('top');
|
||||
this.el.appendChild(controlBorder);
|
||||
controlBorder.appendChild(controlText);
|
||||
|
||||
var dropdownDiv = document.createElement('DIV');
|
||||
$(dropdownDiv).addClass('dropDown');
|
||||
this.el.appendChild(dropdownDiv);
|
||||
dropdownDiv.innerHTML='';
|
||||
|
||||
$(controlText).click(function() {
|
||||
$(controlBorder).toggleClass('top-active');
|
||||
$(dropdownDiv).toggle();
|
||||
});
|
||||
|
||||
var currentTileSetPath = overviewer.mapView.options.currentTileSet.get('path');
|
||||
|
||||
for (i in mapTypes) {
|
||||
var mt = mapTypes[i];
|
||||
// if this overlay specifies a list of valid tilesets, then skip over any invalid tilesets
|
||||
if ((mt.tiles.length > 0) && (mt.tiles.indexOf(currentTileSetPath) ==-1)) {
|
||||
continue;
|
||||
}
|
||||
this.addItem({label: mt.name,
|
||||
name: mt.name,
|
||||
mt: mt,
|
||||
|
||||
action: function(this_item, checked) {
|
||||
if (checked) {
|
||||
overviewer.map.overlayMapTypes.push(this_item.mt);
|
||||
} else {
|
||||
var idx_to_delete = -1;
|
||||
overviewer.map.overlayMapTypes.forEach(function(e, j) {
|
||||
if (e == this_item.mt) {
|
||||
idx_to_delete = j;
|
||||
}
|
||||
});
|
||||
if (idx_to_delete >= 0) {
|
||||
overviewer.map.overlayMapTypes.removeAt(idx_to_delete);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
addItem: function(item) {
|
||||
var itemDiv = document.createElement('div');
|
||||
var itemInput = document.createElement('input');
|
||||
itemInput.type='checkbox';
|
||||
|
||||
// if this overlay is already visible, set the checkbox
|
||||
// to checked
|
||||
overviewer.map.overlayMapTypes.forEach(function(e, j) {
|
||||
if (e == item.mt) {
|
||||
itemInput.checked=true;
|
||||
}
|
||||
});
|
||||
|
||||
// give it a name
|
||||
$(itemInput).attr("_mc_overlayname", item.name);
|
||||
jQuery(itemInput).click((function(local_item) {
|
||||
return function(e) {
|
||||
item.action(local_item, e.target.checked);
|
||||
};
|
||||
})(item));
|
||||
|
||||
this.$(".dropDown")[0].appendChild(itemDiv);
|
||||
itemDiv.appendChild(itemInput);
|
||||
var textNode = document.createElement('text');
|
||||
textNode.innerHTML = item.label + '<br/>';
|
||||
|
||||
itemDiv.appendChild(textNode);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
@@ -265,7 +400,7 @@ overviewer.views.SignControlView = Backbone.View.extend({
|
||||
//var dataRoot = overviewer.collections.markerInfo[curMarkerSet];
|
||||
var dataRoot = markers[curMarkerSet];
|
||||
|
||||
this.el.innerHTML=""
|
||||
this.el.innerHTML="";
|
||||
|
||||
// if we have no markerSets for this tileset, do nothing:
|
||||
if (!dataRoot) { return; }
|
||||
@@ -302,7 +437,6 @@ overviewer.views.SignControlView = Backbone.View.extend({
|
||||
}});
|
||||
}
|
||||
|
||||
iconURL = overviewerConfig.CONST.image.signMarker;
|
||||
//dataRoot['markers'] = [];
|
||||
//
|
||||
for (i in dataRoot) {
|
||||
@@ -310,15 +444,20 @@ overviewer.views.SignControlView = Backbone.View.extend({
|
||||
if (!markersDB[groupName].created) {
|
||||
for (j in markersDB[groupName].raw) {
|
||||
var entity = markersDB[groupName].raw[j];
|
||||
if (entity['icon']) {
|
||||
iconURL = entity['icon'];
|
||||
} else {
|
||||
iconURL = dataRoot[i].icon;
|
||||
}
|
||||
var marker = new google.maps.Marker({
|
||||
'position': overviewer.util.fromWorldToLatLng(entity.x,
|
||||
entity.y, entity.z, overviewer.mapView.options.currentTileSet),
|
||||
'map': overviewer.map,
|
||||
'title': jQuery.trim(entity.Text1 + "\n" + entity.Text2 + "\n" + entity.Text3 + "\n" + entity.Text4),
|
||||
'title': jQuery.trim(entity.text),
|
||||
'icon': iconURL,
|
||||
'visible': false
|
||||
});
|
||||
if (entity['id'] == 'Sign') {
|
||||
if (entity.createInfoWindow) {
|
||||
overviewer.util.createMarkerInfoWindow(marker);
|
||||
}
|
||||
jQuery.extend(entity, {markerObj: marker});
|
||||
|
||||
BIN
overviewer_core/data/web_assets/bed.png
Normal file
BIN
overviewer_core/data/web_assets/bed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 631 B |
@@ -40,6 +40,17 @@ body {
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.customControl > select {
|
||||
font-size: 12px;
|
||||
line-height: 160%;
|
||||
text-align: center;
|
||||
|
||||
border: 1px solid #A9BBDF;
|
||||
border-radius: 2px 2px;
|
||||
box-shadow: rgba(0, 0, 0, 0.347656) 2px 2px 3px;
|
||||
|
||||
}
|
||||
|
||||
.customControl > div.top {
|
||||
font-size: 12px;
|
||||
line-height: 160%;
|
||||
|
||||
@@ -254,7 +254,7 @@ class ANSIColorFormatter(HighlightingFormatter):
|
||||
# No coloring if it's not to be highlighted or colored
|
||||
return logging.Formatter.format(self, record)
|
||||
|
||||
def configure(loglevel=logging.INFO, verbose=False):
|
||||
def configure(loglevel=logging.INFO, verbose=False, simple=False):
|
||||
"""Configures the root logger to our liking
|
||||
|
||||
For a non-standard loglevel, pass in the level with which to configure the handler.
|
||||
@@ -267,15 +267,17 @@ def configure(loglevel=logging.INFO, verbose=False):
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
outstream = sys.stderr
|
||||
outstream = sys.stdout
|
||||
if simple:
|
||||
formatter = DumbFormatter(verbose)
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
elif platform.system() == 'Windows':
|
||||
# Our custom output stream processor knows how to deal with select ANSI
|
||||
# color escape sequences
|
||||
outstream = WindowsOutputStream()
|
||||
outstream = WindowsOutputStream(outstream)
|
||||
formatter = ANSIColorFormatter(verbose)
|
||||
|
||||
elif sys.stderr.isatty():
|
||||
elif outstream.isatty():
|
||||
# terminal logging with ANSI color
|
||||
formatter = ANSIColorFormatter(verbose)
|
||||
|
||||
|
||||
@@ -188,6 +188,12 @@ class MineralOverlay(Overlay):
|
||||
'minerals' : ('a list of (blockid, (r, g, b)) tuples for coloring minerals', None),
|
||||
}
|
||||
|
||||
class Hide(RenderPrimitive):
|
||||
name = "hide"
|
||||
options = {
|
||||
'blocks' : ('a list of blockids or (blockid, data) tuples of blocks to hide', []),
|
||||
}
|
||||
|
||||
# Built-in rendermodes for your convenience!
|
||||
normal = [Base(), EdgeLines()]
|
||||
lighting = [Base(), EdgeLines(), Lighting()]
|
||||
|
||||
@@ -79,6 +79,7 @@ renders = Setting(required=True, default=util.OrderedDict(),
|
||||
"crop": Setting(required=False, validator=validateCrop, default=None),
|
||||
"changelist": Setting(required=False, validator=validateStr, default=None),
|
||||
"markers": Setting(required=False, validator=validateMarkers, default=[]),
|
||||
"overlay": Setting(required=False, validator=validateOverlays, default=[]),
|
||||
"showspawn": Setting(required=False, validator=validateBool, default=True),
|
||||
"base": Setting(required=False, validator=validateStr, default=None),
|
||||
|
||||
@@ -99,9 +100,10 @@ processes = Setting(required=True, validator=int, default=-1)
|
||||
# ends up adding overhead and isn't worth it.
|
||||
memcached_host = Setting(required=False, validator=str, default=None)
|
||||
|
||||
if platform.system() == 'Windows' or not sys.stderr.isatty():
|
||||
# TODO clean up this ugly in sys.argv hack
|
||||
if platform.system() == 'Windows' or not sys.stdout.isatty() or "--simple" in sys.argv:
|
||||
obs = LoggingObserver()
|
||||
else:
|
||||
obs = ProgressBarObserver()
|
||||
obs = ProgressBarObserver(fd=sys.stdout)
|
||||
|
||||
observer = Setting(required=True, validator=validateObserver, default=obs)
|
||||
|
||||
@@ -45,12 +45,24 @@ def checkBadEscape(s):
|
||||
|
||||
def validateMarkers(filterlist):
|
||||
if type(filterlist) != list:
|
||||
raise ValidationException("Markers must specify a list of filters")
|
||||
raise ValidationException("Markers must specify a list of filters. This has recently changed, so check the docs.")
|
||||
for x in filterlist:
|
||||
if not callable(x):
|
||||
raise ValidationException("%r must be a function"% x)
|
||||
if "name" not in x:
|
||||
raise ValidationException("Must define a name")
|
||||
if "filterFunction" not in x:
|
||||
raise ValidationException("Must define a filter function")
|
||||
if not callable(x['filterFunction']):
|
||||
raise ValidationException("%r must be a function"% x['filterFunction'])
|
||||
return filterlist
|
||||
|
||||
def validateOverlays(renderlist):
|
||||
if type(renderlist) != list:
|
||||
raise ValidationException("Overlay must specify a list of renders")
|
||||
for x in renderlist:
|
||||
if validateStr(x) == '':
|
||||
raise ValidationException("%r must be a string"% x)
|
||||
return renderlist
|
||||
|
||||
def validateWorldPath(worldpath):
|
||||
_, worldpath = checkBadEscape(worldpath)
|
||||
abs_path = os.path.abspath(os.path.expanduser(worldpath))
|
||||
|
||||
@@ -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 30
|
||||
#define OVERVIEWER_EXTENSION_VERSION 31
|
||||
|
||||
/* Python PIL, and numpy headers */
|
||||
#include <Python.h>
|
||||
|
||||
@@ -206,6 +206,7 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec
|
||||
};
|
||||
|
||||
if (color_table) {
|
||||
unsigned char biome;
|
||||
int dx, dz;
|
||||
unsigned char tablex, tabley;
|
||||
float temp = 0.0, rain = 0.0;
|
||||
@@ -215,7 +216,7 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec
|
||||
/* average over all neighbors */
|
||||
for (dx = -1; dx <= 1; dx++) {
|
||||
for (dz = -1; dz <= 1; dz++) {
|
||||
unsigned char biome = get_data(state, BIOMES, state->x + dx, state->y, state->z + dz);
|
||||
biome = get_data(state, BIOMES, state->x + dx, state->y, state->z + dz);
|
||||
if (biome >= NUM_BIOMES) {
|
||||
/* note -- biome 255 shows up on map borders.
|
||||
who knows what it is? certainly not I.
|
||||
@@ -257,6 +258,18 @@ base_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, PyObjec
|
||||
r = PyInt_AsLong(PyTuple_GET_ITEM(color, 0));
|
||||
g = PyInt_AsLong(PyTuple_GET_ITEM(color, 1));
|
||||
b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2));
|
||||
|
||||
/* swamp hack
|
||||
All values are guessed. They are probably somewhat odd or
|
||||
completely wrong, but this looks okay for me, and I'm male,
|
||||
so I can only distinct about 10 different colors anyways.
|
||||
Blame my Y-Chromosone. */
|
||||
if(biome == 6) {
|
||||
r = PyInt_AsLong(PyTuple_GET_ITEM(color, 0)) * 0.8;
|
||||
g = PyInt_AsLong(PyTuple_GET_ITEM(color, 1)) / 2.0;
|
||||
b = PyInt_AsLong(PyTuple_GET_ITEM(color, 2)) * 1.0;
|
||||
}
|
||||
|
||||
Py_DECREF(color);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ edge_lines_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, P
|
||||
Imaging img_i = imaging_python_to_c(state->img);
|
||||
unsigned char ink[] = {0, 0, 0, 255 * self->opacity};
|
||||
unsigned short side_block;
|
||||
int x = state->x, y = state->y, z = state->z;
|
||||
|
||||
int increment=0;
|
||||
if (state->block == 44 && ((state->block_data & 0x8) == 0 )) // half-step BUT no upsidown half-step
|
||||
@@ -46,15 +47,15 @@ edge_lines_draw(void *data, RenderState *state, PyObject *src, PyObject *mask, P
|
||||
increment=9;
|
||||
|
||||
/* +X side */
|
||||
side_block = get_data(state, BLOCKS, state->x+1, state->y, state->z);
|
||||
if (side_block != state->block && is_transparent(side_block)) {
|
||||
side_block = get_data(state, BLOCKS, x+1, y, z);
|
||||
if (side_block != state->block && (is_transparent(side_block) || render_mode_hidden(state->rendermode, x+1, y, z))) {
|
||||
ImagingDrawLine(img_i, state->imgx+12, state->imgy+1+increment, state->imgx+22+1, state->imgy+5+1+increment, &ink, 1);
|
||||
ImagingDrawLine(img_i, state->imgx+12, state->imgy+increment, state->imgx+22+1, state->imgy+5+increment, &ink, 1);
|
||||
}
|
||||
|
||||
/* -Z side */
|
||||
side_block = get_data(state, BLOCKS, state->x, state->y, state->z-1);
|
||||
if (side_block != state->block && is_transparent(side_block)) {
|
||||
side_block = get_data(state, BLOCKS, x, y, z-1);
|
||||
if (side_block != state->block && (is_transparent(side_block) || render_mode_hidden(state->rendermode, x, y, z-1))) {
|
||||
ImagingDrawLine(img_i, state->imgx, state->imgy+6+1+increment, state->imgx+12+1, state->imgy+1+increment, &ink, 1);
|
||||
ImagingDrawLine(img_i, state->imgx, state->imgy+6+increment, state->imgx+12+1, state->imgy+increment, &ink, 1);
|
||||
}
|
||||
|
||||
117
overviewer_core/src/primitives/hide.c
Normal file
117
overviewer_core/src/primitives/hide.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../overviewer.h"
|
||||
|
||||
struct HideRule {
|
||||
unsigned short blockid;
|
||||
unsigned char has_data;
|
||||
unsigned char data;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct HideRule* rules;
|
||||
} RenderPrimitiveHide;
|
||||
|
||||
static int
|
||||
hide_start(void *data, RenderState *state, PyObject *support) {
|
||||
PyObject *opt;
|
||||
RenderPrimitiveHide* self = (RenderPrimitiveHide *)data;
|
||||
self->rules = NULL;
|
||||
|
||||
if (!render_mode_parse_option(support, "blocks", "O", &(opt)))
|
||||
return 1;
|
||||
if (opt && opt != Py_None) {
|
||||
Py_ssize_t blocks_size = 0, i;
|
||||
|
||||
if (!PyList_Check(opt)) {
|
||||
PyErr_SetString(PyExc_TypeError, "'blocks' must be a list");
|
||||
return 1;
|
||||
}
|
||||
|
||||
blocks_size = PyList_GET_SIZE(opt);
|
||||
self->rules = calloc(blocks_size + 1, sizeof(struct HideRule));
|
||||
if (self->rules == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < blocks_size; i++) {
|
||||
PyObject *block = PyList_GET_ITEM(opt, i);
|
||||
|
||||
if (PyInt_Check(block)) {
|
||||
/* format 1: just a block id */
|
||||
self->rules[i].blockid = PyInt_AsLong(block);
|
||||
self->rules[i].has_data = 0;
|
||||
} else if (PyArg_ParseTuple(block, "Hb", &(self->rules[i].blockid), &(self->rules[i].data))) {
|
||||
/* format 2: (blockid, data) */
|
||||
self->rules[i].has_data = 1;
|
||||
} else {
|
||||
/* format not recognized */
|
||||
free(self->rules);
|
||||
self->rules = NULL;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
hide_finish(void *data, RenderState *state) {
|
||||
RenderPrimitiveHide *self = (RenderPrimitiveHide *)data;
|
||||
|
||||
if (self->rules) {
|
||||
free(self->rules);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
hide_hidden(void *data, RenderState *state, int x, int y, int z) {
|
||||
RenderPrimitiveHide *self = (RenderPrimitiveHide *)data;
|
||||
unsigned int i;
|
||||
unsigned short block;
|
||||
|
||||
if (self->rules == NULL)
|
||||
return 0;
|
||||
|
||||
block = get_data(state, BLOCKS, x, y, z);
|
||||
for (i = 0; self->rules[i].blockid != 0; i++) {
|
||||
if (block == self->rules[i].blockid) {
|
||||
unsigned char data;
|
||||
|
||||
if (!(self->rules[i].has_data))
|
||||
return 1;
|
||||
|
||||
data = get_data(state, DATA, x, y, z);
|
||||
if (data == self->rules[i].data)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
RenderPrimitiveInterface primitive_hide = {
|
||||
"hide",
|
||||
sizeof(RenderPrimitiveHide),
|
||||
hide_start,
|
||||
hide_finish,
|
||||
NULL,
|
||||
hide_hidden,
|
||||
NULL,
|
||||
};
|
||||
@@ -90,6 +90,7 @@ overlay_mineral_start(void *data, RenderState *state, PyObject *support) {
|
||||
/* now do custom initializations */
|
||||
self = (RenderPrimitiveMineral *)data;
|
||||
|
||||
// opt is a borrowed reference. do not deref
|
||||
if (!render_mode_parse_option(support, "minerals", "O", &(opt)))
|
||||
return 1;
|
||||
if (opt && opt != Py_None) {
|
||||
@@ -119,7 +120,6 @@ overlay_mineral_start(void *data, RenderState *state, PyObject *support) {
|
||||
} else {
|
||||
self->minerals = default_minerals;
|
||||
}
|
||||
Py_XDECREF(opt);
|
||||
|
||||
/* setup custom color */
|
||||
self->parent.get_color = get_color;
|
||||
|
||||
@@ -32,6 +32,7 @@ from .util import roundrobin
|
||||
from . import nbt
|
||||
from .files import FileReplacer
|
||||
from .optimizeimages import optimize_image
|
||||
import rendermodes
|
||||
import c_overviewer
|
||||
|
||||
"""
|
||||
@@ -507,6 +508,8 @@ class TileSet(object):
|
||||
"""
|
||||
def bgcolorformat(color):
|
||||
return "#%02x%02x%02x" % color[0:3]
|
||||
isOverlay = not any(isinstance(x, rendermodes.Base) for x in self.options.get("rendermode"))
|
||||
|
||||
d = dict(name = self.options.get('title'),
|
||||
zoomLevels = self.treedepth,
|
||||
minZoom = 0,
|
||||
@@ -519,7 +522,11 @@ class TileSet(object):
|
||||
(" - " + self.options.get('dimension') if self.options.get('dimension') != 'default' else ''),
|
||||
last_rendertime = self.max_chunk_mtime,
|
||||
imgextension = self.imgextension,
|
||||
isOverlay = isOverlay
|
||||
)
|
||||
if isOverlay:
|
||||
d.update({"tilesets": self.options.get("overlay")})
|
||||
|
||||
if (self.regionset.get_type() == "overworld" and self.options.get("showspawn", True)):
|
||||
d.update({"spawn": self.options.get("spawn")})
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user