{"size": 22187, "ext": "js", "max_stars_repo_path": "locations/inc_geo.js", "max_stars_repo_name": "ElevenGiants/eleven-gsjs", "max_stars_repo_stars_event_min_datetime": "2015-01-21T20:06:38.000Z", "max_stars_repo_stars_event_max_datetime": "2019-11-12T05:40:45.000Z", "max_issues_repo_path": "locations/inc_geo.js", "max_issues_repo_name": "ElevenGiants/eleven-gsjs", "max_issues_repo_issues_event_min_datetime": "2015-08-18T15:07:28.000Z", "max_issues_repo_issues_event_max_datetime": "2017-03-15T23:02:54.000Z", "max_forks_repo_path": "locations/inc_geo.js", "max_forks_repo_name": "ElevenGiants/eleven-gsjs", "max_forks_repo_forks_event_min_datetime": "2015-01-24T05:56:38.000Z", "max_forks_repo_forks_event_max_datetime": "2019-04-16T09:26:55.000Z"}
{"max_stars_count": 17, "max_issues_count": 3, "max_forks_count": 12, "avg_line_length": 23.087408949, "max_line_length": 165, "alphanum_fraction": 0.6806688601}

function geo_add_door(details){
	return this.geo_add_item('door', details);
}

function geo_add_signpost(details){
	return this.geo_add_item('signpost', details);
}

function geo_add_item(name, details){

	if (!this.geometry){
		log.info('******************* no geometry while going to create '+name+" in "+this.tsid);
	}

	var m = this.geometry.layers.middleground;
	if (!m[name+'s']){
		m[name+'s'] = {};
	}

	var key = this.geo_get_next_key(m[name+'s'], name);

	m[name+'s'][key] = details;

	return key;
}

function geo_get_next_key(collection, prefix){

	var i = 1;
	while (collection[prefix+'_'+i]){
		i++;
	}
	return prefix+'_'+i;
}

function geo_get_next_index(collection){

	var i = 0;
	while (collection[i]){
		i++;
	}
	return i;
}


//
// add a destination to an existing signpost/door, pointing at an (x,y)
//

function geo_signpost_add_dest_pos(signpost_id, dest, x, y, skip_source){

	var signpost = this.geometry.layers.middleground.signposts[signpost_id];

	if (!signpost.connects) signpost.connects = {};

	var connect_key = this.geo_get_next_index(signpost.connects);
	var info = dest.geo_get_info();

	signpost.connects[connect_key] = {
		target	: dest,
		mote_id	: info.mote_id,
		hub_id	: info.hub_id,
		x	: x,
		y	: y
	};

	if (!skip_source && !this.isInstance()){
		dest.geo_links_add_source(this.tsid);

		this.jobs_modify_connections();
		dest.jobs_modify_connections();
	}
}

function geo_door_set_dest_pos(door_id, dest, x, y, skip_source){

	var door = this.geometry.layers.middleground.doors[door_id];
	var info = dest.geo_get_info();

	door.connect = {
		target	: dest,
		mote_id	: info.mote_id,
		hub_id	: info.hub_id,
		x	: x,
		y	: y
	};

	if (!skip_source && !this.isInstance()){
		dest.geo_links_add_source(this.tsid);

		this.jobs_modify_connections();
		dest.jobs_modify_connections();
	}
}


function geo_signpost_set_instance_exit(signpost_id){

	if (!this.geometry || !this.geometry.layers || !this.geometry.layers.middleground || !this.geometry.layers.middleground.signposts) { 
		return;
	}
	
	var signpost = this.geometry.layers.middleground.signposts[signpost_id];

	if (!signpost) return;

	signpost.instance_exit = true;
}

//
// utility function to avoid remote access when adding connections (above)
//

function geo_get_info(){
	return {
		mote_id		: this.moteid,
		hub_id		: this.hubid
	};
}


// return the ID of the first signpost we come across
function geo_find_signpost_id(){

	if (!this.geo || !this.geo.signposts) return null;

	for (var i in this.geo.signposts){
		return i;
	}

	return null;
}

// return the ID of the first door we come across
function geo_find_door_id(){

	if (!this.geo || !this.geo.doors) return null;

	for (var i in this.geo.doors){
		return i;
	}

	return null;
}

function geo_get_door_info(door_id){

	return this.geo.doors[door_id];
}

function geo_get_signpost_info(signpost_id){

	return this.geo.signposts[signpost_id];
}

function geo_clear_doors(){

	this.geometry.layers.middleground.doors = {};
}

function geo_clear_signposts(){

	this.geometry.layers.middleground.signposts = {};
}

function geo_count_signposts(){
	if (!this.geo || !this.geo.signposts) return 0;

	return utils.lengthOfObj(this.geo.signposts);
}

function geo_door_set_deco(door_id, sprite, w, h){

	var door = this.geometry.layers.middleground.doors[door_id];

	door.w = w;
	door.h = h;
	door.deco.w = w;
	door.deco.h = h;
	door.deco.sprite_class = sprite;
}

function geo_lock_signpost(signpost_id){
	var signpost = this.geometry.layers.middleground.signposts[signpost_id];
	if (!signpost) return;

	log.info(this+" setting signpost "+signpost_id+" to hidden");
	signpost.hidden = true;

	this.geo_signpost_updated(signpost_id);
}

function geo_unlock_signpost(signpost_id){
	var signpost = this.geometry.layers.middleground.signposts[signpost_id];
	if (!signpost) return;

	log.info(this+" setting signpost "+signpost_id+" to UNhidden");
	delete signpost.hidden;

	this.geo_signpost_updated(signpost_id);
}

function geo_lock_signpost_connection(signpost_id, connect_id){

	var connect = this.geometry.layers.middleground.signposts[signpost_id].connects[connect_id];
	if (!connect) return;
	if (connect.hidden) return;

	log.info(this+" setting connection "+connect_id+" on signpost "+signpost_id+" to hidden");
	connect.hidden = true;

	this.geo_signpost_updated(signpost_id);
}

function geo_unlock_signpost_connection(signpost_id, connect_id){

	var connect = this.geometry.layers.middleground.signposts[signpost_id].connects[connect_id];
	if (!connect) return;
	if (!connect.hidden) return;

	log.info(this+" setting connection "+connect_id+" on signpost "+signpost_id+" to UNhidden");
	connect.hidden = false;

	this.geo_signpost_updated(signpost_id);
}

function geo_signpost_updated(signpost_id){

	var geo = this.prep_geometry(); // We need the same signpost struct we normally send the client
	var sign = geo.layers.middleground.signposts[signpost_id];
	sign.id = signpost_id;
	var rsp = {
		type: 'signpost_change',
		location_tsid: this.tsid,
		signpost: sign
	};

	for (var i in this.activePlayers){
		try{
			this.activePlayers[i].apiSendMsg(rsp);
		}
		catch(e){
			log.error("Cannot send signpost update to "+i);
		}
	}
	
	apiAdminCall('clearMapCache', {hub_id: this.hubid});
	this.updateMap();
	apiReloadDataForGlobalPathFinding();
}

function geo_lock_door_connection(door_id){

	var connect = this.geo.doors[door_id].connect;
	if (!connect) return;
	if (connect.hidden) return;

	log.info(this+" setting connection on door "+door_id+" to hidden");
	connect.hidden = true;

	this.geo_door_updated(door_id);
}

function geo_unlock_door_connection(door_id){

	var connect = this.geo.doors[door_id].connect;
	if (!connect) return;
	if (!connect.hidden) return;

	log.info(this+" setting connection on door "+door_id+" to UNhidden");
	connect.hidden = false;

	this.geo_door_updated(door_id);
}

function geo_door_updated(door_id){

	var geo = this.prep_geometry(); // We need the same door struct we normally send the client
	var door = geo.layers.middleground.doors[door_id];
	door.id = door_id;
	var rsp = {
		type: 'door_change',
		location_tsid: this.tsid,
		door: door
	};

	for (var i in this.activePlayers){
		try{
			this.activePlayers[i].apiSendMsg(rsp);
		}
		catch(e){
			log.error("Cannot send door update to "+i);
		}
	}
	
	apiAdminCall('clearMapCache', {hub_id: this.hubid});
	this.updateMap();
	apiReloadDataForGlobalPathFinding();
}


//
// we use marker_teleport objects to show where you'll land in a location.
// used in POL and quarter templates
//

function geo_get_teleport_point(destroy){

	var point = {
		found: false,
		x: 0,
		y: 0
	};

	this.items.apiIterate('marker_teleport', function(marker){
		if (!point.found){
			point.found = true;
			point.x = marker.x;
			point.y = marker.y;
		}

		if (destroy){
			marker.apiDelete();
		}
	});

	return point;
}

function geo_add_hitbox(id, x, y, width, height){
	for (var i = 1; ; i++){
		if (!this.geometry.layers.middleground.boxes['box_'+i]){
			this.geometry.layers.middleground.boxes['box_'+i] = {id:id, x:x, y:y, w:width, h:height};
			break;
		}
	}
	
	this.apiGeometryUpdated();
}

function geo_delete_hitbox(id){
	for (var i in this.geometry.layers.middleground.boxes){
		var b = this.geometry.layers.middleground.boxes[i];
		if (b.id == id){
			delete this.geometry.layers.middleground.boxes[i];
			this.apiGeometryUpdated();
			return true;
			break;
		}
	}
	
	return false;
}


function find_hitbox_by_id(id){
	var box = null;
	for (var i in this.geometry.layers.middleground.boxes){
		var b = this.geometry.layers.middleground.boxes[i];
		if (b.id == id){
			box = b;
			break;
		}
	}
	
	return box;
}

function find_hitboxes_by_id_prefix(prefix){
	var boxes = [];
	for (var i in this.geometry.layers.middleground.boxes){
		var b = this.geometry.layers.middleground.boxes[i];
		if (b.id.indexOf(prefix) == 0){
			boxes.push(b);
		}
	}
	
	return boxes;
}

////////////////////////////////////////////////////////////////////////////////

//
// Deco manipulation
//

function geo_deco_remove(name, reload_players){
	var d = this.geo_find_deco_by_name(name);

	if (d){
		delete d;
		if (reload_players) this.upgrades_move_players('upgrade');

		return true;
	}

	return false;
}

function geo_deco_replace(name, data, reload_players){
	var d = this.geo_find_deco_by_name(name);

	if (d){
		if (!data.name) data.name = name;
		d = data;
		if (reload_players) this.upgrades_move_players('upgrade');

		return true;
	}

	return false;
}

function geo_find_deco_by_name(name){
	var layers = this.geometry.layers;

	for (var l in layers){
		if (!layers[l].decos) continue;

		for (var d in layers[l].decos){
			var deco = layers[l].decos[d];
			if (deco.name == name) return deco;
		}
	}

	return null;
}

// IMPORTANT NOTE: This will only work for decos that are marked as animated in locodeco, or those that have been assigned sign_txt. It will fail silently otherwise.
function geo_deco_toggle_visibility(name, is_visible, fade){
	this.apiSendMsg({
		type: 'deco_visibility',
		visible: is_visible,
		deco_name: name, // this is the name property on the deco in the location geo, it can be set via locodeco
		fade_ms: fade ? fade : 400 // by default the fade is 400 ms. If you specify any other number it will be what you specify (0 for immediate, with no fade)
	});
}

function geo_deco_change_txt(name, txt, css_class){
	this.apiSendMsg({
		type: 'deco_sign_txt',
		deco_name: name, // this is the name property on the deco in the location geo, it can be set via locodeco
		txt: txt,
		css_class: css_class ? css_class : '' // totally optional param to change the css_class used by the sign
	});
}


////////////////////////////////////////////////////////////////////////////////

//
// http://wiki.tinyspeck.com/wiki/PlacementPlatforms
//

function geo_placement_get(pl_id){
	var layers = this.geometry.layers;
	if (!layers) return {};

	var middleground = layers.middleground;
	if (!middleground) return {};

	var platform_lines = middleground.platform_lines;
	if (!platform_lines) return {};

	var plat = platform_lines[pl_id];
	if (!plat) return {};

	return {
		is_for_placement: plat.is_for_placement ? true : false,
		invoking_set: plat.placement_invoking_set ? plat.placement_invoking_set : '',
		userdeco_set: plat.placement_userdeco_set ? plat.placement_userdeco_set : ''
	};
}

function geo_placement_set(pl_id){
	var geo = this.geometry;
	var layers = geo.layers;
	if (!layers) geo.layers = {};

	var middleground = layers.middleground;
	if (!middleground) geo.layers.middleground = {};

	var platform_lines = middleground.platform_lines;
	if (!platform_lines) geo.layers.middleground.platform_lines = {};

	var plat = platform_lines[pl_id];
	if (!plat) return {};
}

function geo_placement_delete(pl_id){
	var layers = this.geometry.layers;
	if (!layers) return false;

	var middleground = layers.middleground;
	if (!middleground) return false;

	var platform_lines = middleground.platform_lines;
	if (!platform_lines) return false;

	var plat = platform_lines[pl_id];
	if (!plat) return false;

	delete plat;
	return true;
}

function geo_placement_can_invoke(pl_id, class_tsid){
	var sets = this.geo_placement_get_invoking_sets();
	var placement = this.geo_placement_get(pl_id);
	if (!placement.is_for_placement) return false;

	var can_invoke = sets[placement.invoking_set];
	if (!can_invoke) return false;

	return can_invoke[class_tsid] ? true : false;
}

////

function geo_placement_get_invoking_sets(){
	var catalog = apiFindItemPrototype('catalog_invoking_sets');
	if (!catalog) return {};
	
	return catalog.sets;
}

function geo_placement_get_userdeco_sets(){
	var catalog = apiFindItemPrototype('catalog_userdeco_sets');
	if (!catalog) return {};
	
	return sets;
}


////////////////////////////////////////////////////////////////////////////////

//
// http://wiki.tinyspeck.com/wiki/FurnitureMessaging
//
// Functions for adding/removing/changing plats and walls in a location. Mostly for decorable houses.
//

function geo_get_platform_lines_ref(){
	var geo = this.geometry;
	var layers = geo.layers;
	if (!layers) geo.layers = {};

	var middleground = layers.middleground;
	if (!middleground) geo.layers.middleground = {};

	var platform_lines = middleground.platform_lines;
	if (!platform_lines) geo.layers.middleground.platform_lines = {};

	return platform_lines;
}

function geo_get_walls_ref(){
	var geo = this.geometry;
	var layers = geo.layers;
	if (!layers) geo.layers = {};

	var middleground = layers.middleground;
	if (!middleground) geo.layers.middleground = {};

	var walls = middleground.walls;
	if (!walls) geo.layers.middleground.walls = {};

	return walls;
}

function geo_remove_plats(plats, walls, no_nudge){
	var loc_plats = this.geo_get_platform_lines_ref();

	var to_nudge = [];
	var pl_ids = [];
	var loc = this;
	for (var i in plats){
		if (!no_nudge){
			loc.items.apiIterate(function(item){
				//log.info("Checking item: "+item);
				if (item.hasTag('furniture') && !item.hasTag('furniture_placed_normally')) return;

				var pl = loc.apiGetClosestPlatformLineBelow(item.x, item.y-1);
				if (!pl) return;
				
				//log.info(item+': '+pl+' vs '+plats[i]);
				if (item && pl.x1 == plats[i].start.x && pl.y1 == plats[i].start.y && pl.x2 == plats[i].end.x && pl.y2 == plats[i].end.y){
					to_nudge.push(item);
				}
			});
		}

		pl_ids.push(i);
		delete loc_plats[i];
	}

	var loc_walls = this.geo_get_walls_ref();
	var wall_ids = [];
	for (var i in walls){
		wall_ids.push(i);
		delete loc_walls[i];
	}
	
	if (pl_ids.length || wall_ids.length) {
		this.apiGeometryUpdated();
	
		if (!no_nudge){
			for (var i in to_nudge){
				var it = to_nudge[i];
				if (!it) continue;
	
				var pt = this.apiGetPointOnTheClosestPlatformLineBelow(it.x, it.y-1);
				//log.info(it+' new pt: '+pt);
				if (pt) it.apiSetXY(pt.x, pt.y);
			}
		}
		return this.apiSendMsgAsIs({type: 'geo_remove', platform_lines: pl_ids, walls: wall_ids});
	}

	
}

function geo_add_plats(plats, walls, source){
	var loc_plats = this.geo_get_platform_lines_ref();

	var plats_to_add;
	for (var i in plats){
		if (!plats_to_add) plats_to_add = {};
		var pl = plats[i];
		pl.source = source;

		var pl_id = source+'_'+i+'_'+time();
		loc_plats[pl_id] = pl;
		plats_to_add[pl_id] = pl;
	}

	var loc_walls = this.geo_get_walls_ref();

	var walls_to_add;
	for (var i in walls){
		if (!walls_to_add) walls_to_add = {};
		var w = walls[i];
		w.source = source;

		var w_id = source+'_'+i+'_'+time();
		loc_walls[w_id] = w;
		walls_to_add[w_id] = w;
	}
	
	if (plats_to_add || walls_to_add) {
		this.apiGeometryUpdated();
		return this.geo_update_players_plats('geo_add', plats_to_add, walls_to_add);
	}
}

function geo_modify_plats(plats, walls){
	var loc_plats = this.geo_get_platform_lines_ref();

	var modified_plats = false;
	for (var i in plats){
		modified_plats = true;

		var pl = plats[i];
		loc_plats[i] = pl;
	}

	var loc_walls = this.geo_get_walls_ref();

	var modified_walls = false;
	for (var i in walls){
		modified_walls = true;
		
		var w = walls[i];
		loc_walls[i] = w;
	}
	
	if (modified_plats || modified_walls) {
		this.apiGeometryUpdated();
		return this.geo_update_players_plats('geo_update', plats, walls);
	}
}

function geo_find_plats_by_source(source){
	var loc_plats = this.geo_get_platform_lines_ref();

	var ret = {
		plats: {},
		walls: {}
	};
	if (!source) return ret;

	for (var i in loc_plats){
		var pl = loc_plats[i];
		if (pl.source == source){
			ret.plats[i] = pl;
		}
	}

	var loc_walls = this.geo_get_walls_ref();
	for (var i in loc_walls){
		var w = loc_walls[i];
		if (w.source == source){
			ret.walls[i] = w;
		}
	}

	return ret;
}

function geo_find_door_by_source(source){
        var geo = this.geometry;
        var layers = geo.layers;
        if (!layers) geo.layers = {};

        var middleground = layers.middleground;
        if (!middleground) geo.layers.middleground = {};

        var doors = middleground.doors;
        if (!doors) geo.layers.middleground.doors = {};

        for (var i in middleground.doors){
                var door = middleground.doors[i];
                if (!door) continue;

                if (door.itemstack_tsid == source.tsid) return door;
        }
}

function geo_find_door_by_target_source(source){
        var geo = this.geometry;
        var layers = geo.layers;
        if (!layers) geo.layers = {};

        var middleground = layers.middleground;
        if (!middleground) geo.layers.middleground = {};

        var doors = middleground.doors;
        if (!doors) geo.layers.middleground.doors = {};

        for (var i in middleground.doors){
                var door = middleground.doors[i];
                if (!door) continue;

                if (door.target_tsid == source.tsid) return door;
        }
}

function geo_find_plats_by_id(tsids){

	var ret = {
		plats: {},
		walls: {}
	};
	
	if (!tsids) return ret;

	var loc_plats = this.geo_get_platform_lines_ref();

	var plat_ids = tsids.split(',');
	var plat_tsid = '';
	for (var i in plat_ids){
		plat_tsid = utils.trim(plat_ids[i]);	
		if (loc_plats[plat_tsid]){
			ret.plats[plat_tsid] = loc_plats[plat_tsid];
		}
	}

	var loc_walls = this.geo_get_walls_ref();
	for (var i in plat_ids){
		plat_tsid = utils.trim(plat_ids[i]);
		if (loc_walls[plat_tsid]){
			ret.walls[plat_tsid] = loc_walls[plat_tsid];
		}
	}
	
	return ret;
}

function geo_remove_plats_by_source(source, no_nudge){
	var ret = this.geo_find_plats_by_source(source);

	return this.geo_remove_plats(ret.plats, ret.walls, no_nudge);
}

function geo_update_players_plats(type, plats, walls){
	return this.apiSendMsgAsIs({type: type, platform_lines: plats, walls: walls});
}

// "Disables" all plats in the box by making them soft for players in both directions
function geo_disable_plats(x1, y1, x2, y2, partials_only){
	this.geo_modify_plat_pc_perms(x1, y1, x2, y2, partials_only, false, 0);
}

// "Enables" all plats in the box by making them hard for players in both directions
function geo_enable_plats(x1, y1, x2, y2, partials_only){
	this.geo_modify_plat_pc_perms(x1, y1, x2, y2, partials_only, false, null);
}

// Sets all plats in the box by setting them to their initial perms
function geo_set_plats_to_initial_perms(x1, y1, x2, y2, partials_only){
	this.geo_modify_plat_pc_perms(x1, y1, x2, y2, partials_only, true, null);
}

// Will set the inital_perms for all of the plats
function geo_set_initial_perms(){
	var plats = this.geo_find_plats(null, null, null, null, true);

	for (var i in plats){
		plats[i].inital_perm = plats[i].platform_pc_perm;
	}

	var walls = this.geo_find_walls(null, null, null, null, true);

	for (var i in walls){
		walls[i].inital_perm = walls[i].platform_pc_perm;
	}
}

// Applies "perm" to all plats within the box for pcs
// See http://wiki.tinyspeck.com/wiki/Location_Geometry_XML for perm values
function geo_modify_plat_pc_perms(x1, y1, x2, y2, partials_only, use_inital_perm, perm){
	var plats = this.geo_find_plats(x1, y1, x2, y2, partials_only);

	for (var i in plats){
		if (use_inital_perm){
			plats[i].platform_pc_perm = plats[i].inital_perm;
		}else{
			plats[i].platform_pc_perm = perm;
		}
	}

	var walls = this.geo_find_walls(x1, y1, x2, y2, partials_only);

	for (var i in walls){
		if (use_inital_perm){
			walls[i].pc_perm = walls[i].inital_perm;
		}else{
			walls[i].pc_perm = perm;
		}
	}

	this.geo_modify_plats(plats, walls);
}

// x1,y1 is the top-left
// x2,y2 is the bottom-right
// passing null for any parameter ignores it
// includes any plats that are anywhere inside the box, unless full_only is true
function geo_find_plats(x1, y1, x2, y2, full_only){
	var loc_plats = this.geo_get_platform_lines_ref();

	var ret = {};

	for (var i in loc_plats){
		var pl = loc_plats[i];

		//log.info("Checking: "+i+" ", x1, y1, x2, y2, " against ", pl.start.x, pl.start.y, pl.end.x, pl.end.y);

		if (x1 !== null){
			//log.info("Checking x1");
			if (intval(pl.end.x) < intval(x1)) continue;
			if (full_only && intval(pl.start.x) < intval(x1)) continue;
		}

		if (y1 !== null){
			//log.info("Checking y1");
			if (intval(pl.end.y) < intval(y1)) continue;
			if (full_only && intval(pl.start.y) < intval(y1)) continue;
		}

		if (x2 !== null){
			//log.info("Checking x2");
			if (intval(pl.start.x) > intval(x2)) continue;
			if (full_only && intval(pl.end.x) > intval(x2)) continue;
		}

		if (y2 !== null){
			//log.info("Checking y2");
			if (intval(pl.start.y) > intval(y2)) continue;
			if (full_only && intval(pl.end.y) > intval(y2)) continue;
		}

		//log.info('PASS');

		ret[i] = pl;
	}

	return ret;
}

function geo_find_walls(x1, y1, x2, y2, full_only){
	var loc_walls = this.geo_get_walls_ref();
	log.info(loc_walls);

	var ret = {};

	for (var i in loc_walls){
		var wall = loc_walls[i];
		log.info(wall);

		//log.info("Checking: "+wall.tsid+" ", x1, y1, x2, y2, " against ", wall.x, wall.y, wall.h);

		if (x1 !== null){
			//log.info("Checking x1");
			if (intval(wall.x) < intval(x1)) continue;
		}

		if (y1 !== null){
			//log.info("Checking y1");
			if (intval(wall.y+wall.h) < intval(y1)) continue;
			if (full_only && intval(wall.y) < intval(y1)) continue;
		}

		if (x2 !== null){
			//log.info("Checking x2");
			if (intval(wall.x) > intval(x2)) continue;
		}

		if (y2 !== null){
			//log.info("Checking y2");
			if (intval(wall.y) > intval(y2)) continue;
			if (full_only && intval(wall.y+wall.h) > intval(y2)) continue;
		}

		//log.info('PASS');

		ret[i] = wall;
	}

	return ret;
}

function geo_add_plats_from_item(item){
	if (!item.has_parent('furniture_base')) return false;

	// Add plats contained in the item and let players know
	var p_ret = item.getPlatsAndWalls();
	for (var i in p_ret.plats){
		p_ret.plats[i].start.x+=item.x;
		p_ret.plats[i].end.x+=item.x;

		p_ret.plats[i].start.y+=item.y;
		p_ret.plats[i].end.y+=item.y;
	}

	for (var i in p_ret.walls){
		p_ret.walls[i].x+=item.x;
		p_ret.walls[i].y+=item.y;
	}

	return this.geo_add_plats(p_ret.plats, p_ret.walls, item.tsid);
}

function geo_set_instance_exit_doors(){
	for (var i in this.geometry.layers.middleground.doors){
		this.geometry.layers.middleground.doors[i].instance_exit = true;
	}

	this.apiGeometryUpdated();
}