Skip to content
Permalink
Tree: a8f2d91f27
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
3896 lines (3558 sloc) 129 KB
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <fstream>
#include <vector>
#include <sstream>
#include "overmap.h"
#include "rng.h"
#include "line.h"
#include "game.h"
#include "npc.h"
#include "keypress.h"
#include <cstring>
#include <ostream>
#include "debug.h"
#include "cursesdef.h"
#include "options.h"
#include "catacharset.h"
#include "overmapbuffer.h"
#include "action.h"
#include "input.h"
#include "json.h"
#include <queue>
#define dbg(x) dout((DebugLevel)(x),D_MAP_GEN) << __FILE__ << ":" << __LINE__ << ": "
#ifdef _MSC_VER
// MSVC doesn't have c99-compatible "snprintf", so do what picojson does and use _snprintf_s instead
#define snprintf _snprintf_s
#endif
#define STREETCHANCE 2
#define NUM_FOREST 250
#define TOP_HIWAY_DIST 999
#define MIN_ANT_SIZE 8
#define MAX_ANT_SIZE 20
#define MIN_GOO_SIZE 1
#define MAX_GOO_SIZE 2
#define MIN_RIFT_SIZE 6
#define MAX_RIFT_SIZE 16
#define SETTLE_DICE 2
#define SETTLE_SIDES 2
#define HIVECHANCE 180 //Chance that any given forest will be a hive
#define SWAMPINESS 4 //Affects the size of a swamp
#define SWAMPCHANCE 8500 // Chance that a swamp will spawn instead of forest
enum oter_dir {
oter_dir_north, oter_dir_east, oter_dir_west, oter_dir_south
};
map_extras no_extras(0);
map_extras road_extras(
// %%% HEL MIL SCI STA DRG SUP PRT MIN CRT FUM 1WY ART
50, 40, 50,120,200, 30, 10, 5, 80, 10, 8, 2, 3);
map_extras field_extras(
60, 40, 15, 40, 80, 10, 10, 3, 50, 10, 8, 1, 3);
map_extras subway_extras(
// %%% HEL MIL SCI STA DRG SUP PRT MIN CRT FUM 1WY ART
75, 0, 5, 12, 5, 5, 0, 7, 0, 0, 20, 1, 3);
map_extras build_extras(
90, 0, 5, 12, 0, 10, 0, 5, 5, 60, 8, 1, 3);
std::map<std::string,oter_t> otermap;
std::vector<oter_t> oterlist;
overmap_special overmap_specials[NUM_OMSPECS] = {
// Terrain MIN MAX DISTANCE
{"crater", 0, 10, 0, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::land, mfb(OMS_FLAG_BLOB) | mfb(OMS_FLAG_CLASSIC)},
{"hive", 0, 50, 10, -1, "GROUP_BEE", 20, 60, 2, 4,
&omspec_place::forest, mfb(OMS_FLAG_3X3)},
{"house_north", 0,100, 0, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::by_highway, mfb(OMS_FLAG_ROTATE_ROAD) | mfb(OMS_FLAG_CLASSIC)},
{"s_gas_north", 0,100, 0, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::by_highway, mfb(OMS_FLAG_ROTATE_ROAD) | mfb(OMS_FLAG_CLASSIC)},
{"cabin", 0, 30, 20, -1, "GROUP_NULL", 0, 0, 0, 0, // Woods cabin
&omspec_place::forest, mfb(OMS_FLAG_CLASSIC)},
{"cabin_strange", 1, 1, 20, -1, "GROUP_NULL", 0, 0, 0, 0, // Hidden cabin
&omspec_place::forest, mfb(OMS_FLAG_CLASSIC)},
{"lmoe", 0, 3, 20, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_CLASSIC)},
{"farm", 0, 20, 20, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_3X3_SECOND) |mfb(OMS_FLAG_DIRT_LOT) | mfb(OMS_FLAG_CLASSIC)},
{"temple_stairs", 0, 3, 20, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::forest, 0},
{"lab_stairs", 0, 30, 8, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD)},
{"ice_lab_stairs", 0, 30, 8, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD)},
{"fema_entrance", 2, 5, 8, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::by_highway, mfb(OMS_FLAG_3X3_SECOND) | mfb(OMS_FLAG_CLASSIC)},
// Terrain MIN MAX DISTANCE
{"bunker", 2, 10, 4, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD)},
{"outpost", 0, 10, 4, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, 0},
{"silo", 0, 1, 30, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD)},
{"radio_tower", 1, 5, 0, 20, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::by_highway, mfb(OMS_FLAG_CLASSIC)},
{"mansion_entrance", 0, 8, 0, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::by_highway, mfb(OMS_FLAG_3X3_SECOND) | mfb(OMS_FLAG_CLASSIC)},
{"mansion_entrance", 0, 4, 10, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_3X3_SECOND) | mfb(OMS_FLAG_CLASSIC)},
{"megastore_entrance", 0, 5, 0, 10, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::by_highway, mfb(OMS_FLAG_3X3_SECOND) | mfb(OMS_FLAG_CLASSIC)},
{"hospital_entrance", 1, 5, 3, 15, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::by_highway, mfb(OMS_FLAG_3X3_SECOND) | mfb(OMS_FLAG_CLASSIC)},
{"public_works_entrance", 1, 3, 2, 10, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD) | mfb(OMS_FLAG_CLASSIC) | mfb(OMS_FLAG_2X2_SECOND)},
{"apartments_con_tower_1_entrance", 1, 5, -1, 2, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD) | mfb(OMS_FLAG_CLASSIC) | mfb(OMS_FLAG_2X2_SECOND)},
{"apartments_mod_tower_1_entrance", 1, 4, -1, 2, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD) | mfb(OMS_FLAG_CLASSIC) | mfb(OMS_FLAG_2X2_SECOND)},
{"office_tower_1_entrance", 1, 5, -1, 4, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD) | mfb(OMS_FLAG_CLASSIC) | mfb(OMS_FLAG_2X2_SECOND)},
{"cathedral_1_entrance", 1, 2, -1, 2, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD) | mfb(OMS_FLAG_CLASSIC) | mfb(OMS_FLAG_2X2_SECOND)},
{"school_2", 1, 3, 1, 5, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD) | mfb(OMS_FLAG_CLASSIC) | mfb(OMS_FLAG_3X3_FIXED)},
{"prison_2", 1, 1, 3, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::land, mfb(OMS_FLAG_ROAD) | mfb(OMS_FLAG_CLASSIC) | mfb(OMS_FLAG_3X3_FIXED)},
{"hotel_tower_1_2", 1, 4, -1, 4, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD) | mfb(OMS_FLAG_CLASSIC) | mfb(OMS_FLAG_3X3_FIXED)},
{"sewage_treatment", 1, 5, 10, 20, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_PARKING_LOT) | mfb(OMS_FLAG_CLASSIC)},
{"mine_entrance", 0, 5, 15, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_PARKING_LOT)},
// Terrain MIN MAX DISTANCE
{"anthill", 0, 30, 10, -1, "GROUP_ANT", 1000, 2000, 10, 30,
&omspec_place::wilderness, 0},
{"spider_pit", 0,500, 0, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::forest, 0},
{"slimepit_down", 0, 4, 0, -1, "GROUP_GOO", 100, 200, 2, 10,
&omspec_place::land, 0},
{"fungal_bloom", 0, 3, 5, -1, "GROUP_FUNGI", 600, 1200, 30, 50,
&omspec_place::wilderness, 0},
{"triffid_grove", 0, 4, 0, -1, "GROUP_TRIFFID", 800, 1300, 12, 20,
&omspec_place::forest, 0},
{"river_center", 0, 10, 10, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::always, mfb(OMS_FLAG_BLOB) | mfb(OMS_FLAG_CLASSIC)},
// Terrain MIN MAX DISTANCE
{"shelter", 5, 10, 5, 10, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD) | mfb(OMS_FLAG_CLASSIC)},
{"cave", 0, 30, 0, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, 0},
{"toxic_dump", 0, 5, 15, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_CLASSIC)},
{"s_gas_north", 10, 500, 10, 200, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::by_highway, mfb(OMS_FLAG_CLASSIC) | mfb(OMS_FLAG_ROTATE_ROAD)},
{"haz_sar_entrance", 1, 2, 15, -1, "GROUP_NULL", 0, 0, 0, 0,
&omspec_place::wilderness, mfb(OMS_FLAG_ROAD) | mfb(OMS_FLAG_CLASSIC) | mfb(OMS_FLAG_2X2_SECOND)}
};
double dist(int x1, int y1, int x2, int y2)
{
return sqrt(double((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)));
}
bool is_river(const oter_id &ter)
{
// if the id starts with "river" or "bridge", count as a river, but this
// is done in data init.
// return (ter.compare(0,5,"river",5) == 0 || ter.compare(0,6,"bridge",6) == 0);
return ter.t().is_river;
//oter_t(ter).is_river;
}
bool is_ot_type(const std::string &otype, const oter_id &oter)
{
const size_t compare_size = otype.size();
if (compare_size > oter.size()) {
return false;
} else {
return std::string(oter).compare(0, compare_size, otype ) == 0;
}
}
bool road_allowed(const oter_id &ter)
{
return ter.t().allow_road;
// return oter_t(ter).allow_road;
//otermap[ter].allow_road;
}
// Likelihood to pick a specific overmap terrain.
struct oter_weight {
std::string ot;
int weight;
};
// Local class for picking overmap terrain from a weighted list.
struct oter_weight_list {
oter_weight_list() : total_weight(0) { };
void add_item(std::string id, int weight) {
oter_weight new_weight = { id, weight };
items.push_back(new_weight);
total_weight += weight;
}
// oter_id will effectively be "" if initialized as something that doesn't exist in otermap
std::string pick() {
int picked = rng(0, total_weight);
int accumulated_weight = 0;
int i;
for(i=0; i<items.size(); i++) {
accumulated_weight += items[i].weight;
if(accumulated_weight >= picked) {
break;
}
}
return items[i].ot;
}
private:
int total_weight;
std::vector<oter_weight> items;
};
oter_id shop(int dir)
{
// TODO: adjust weights based on area, maybe using JSON
// (implies we have area types first)
oter_weight_list weightlist;
weightlist.add_item("s_gas", 5);
weightlist.add_item("s_pharm", 3);
weightlist.add_item("s_grocery", 15);
weightlist.add_item("s_hardware", 5);
weightlist.add_item("s_sports", 5);
weightlist.add_item("s_liquor", 5);
weightlist.add_item("s_gun", 5);
weightlist.add_item("s_clothes", 5);
weightlist.add_item("s_library", 4);
weightlist.add_item("s_restaurant", 5);
weightlist.add_item("sub_station", 5);
weightlist.add_item("bank", 3);
weightlist.add_item("bar", 5);
weightlist.add_item("s_electronics", 5);
weightlist.add_item("pawn", 3);
weightlist.add_item("mil_surplus", 2);
weightlist.add_item("s_garage", 5);
weightlist.add_item("station_radio", 5);
weightlist.add_item("office_doctor", 2);
weightlist.add_item("s_restaurant_fast", 3);
weightlist.add_item("s_restaurant_coffee", 3);
weightlist.add_item("church", 2);
weightlist.add_item("office_cubical", 2);
weightlist.add_item("furniture", 2);
weightlist.add_item("abstorefront", 2);
weightlist.add_item("police", 1);
weightlist.add_item("s_lot", 4);
std::string ret = weightlist.pick();
if (ret == "s_lot") { // don't need to rotate
return ret;
}
dir = dir % 4;
if (dir < 0) { dir += 4; }
switch (dir) {
case 0: return ret + "_north";
case 1: return ret + "_east";
case 2: return ret + "_south";
case 3: return ret + "_west";
default:
debugmsg("Bad rotation of shop %s: %d.", ret.c_str(), dir);
return ret;
}
}
oter_id house(int dir)
{
bool base = one_in(2);
if (dir < 0) { dir += 4; }
switch (dir) {
case 0: return base ? "house_base_north" : "house_north";
case 1: return base ? "house_base_east" : "house_east";
case 2: return base ? "house_base_south" : "house_south";
case 3: return base ? "house_base_west" : "house_west";
default: debugmsg("Bad rotation of house: %d.", dir); return "";
}
}
map_extras& get_extras(const std::string &name)
{
if (name == "field") {
return field_extras;
} else if (name == "road") {
return road_extras;
} else if (name == "subway") {
return subway_extras;
} else if (name == "build") {
return build_extras;
} else {
return no_extras;
}
}
// oter_t specific affirmatives to is_road, set at startup (todo; jsonize)
bool isroad(std::string bstr) {
if (bstr=="road" || bstr=="bridge" ||
bstr=="subway" || bstr=="sewer" ||
bstr=="sewage_treatment_hub" ||
bstr=="sewage_treatment_under" ||
bstr == "rift" || bstr == "hellmouth") {
return true;
}
return false;
}
void load_oter(oter_t & oter) {
oter.loadid = oterlist.size();
otermap[oter.id] = oter;
oterlist.push_back(oter);
}
void load_overmap_terrain(JsonObject &jo)
{
oter_t oter;
long syms[4];
bool rotate;
bool line_drawing;
oter.id = jo.get_string("id");
oter.name = _(jo.get_string("name").c_str());
rotate = jo.get_bool("rotate", false);
line_drawing = jo.get_bool("line_drawing", false);
if (line_drawing) {
oter.sym = jo.get_int("sym", (int)'%');
} else if (jo.has_array("sym")) {
JsonArray ja = jo.get_array("sym");
for (int i = 0; i < 4; ++i) {
syms[i] = ja.next_int();
}
oter.sym = syms[0];
} else if (rotate) {
oter.sym = jo.get_int("sym");
for (int i = 0; i < 4; ++i) {
syms[i] = oter.sym;
}
} else {
oter.sym = jo.get_int("sym");
}
oter.color = color_from_string(jo.get_string("color"));
oter.see_cost = jo.get_int("see_cost");
oter.extras = jo.get_string("extras", "none");
oter.known_down = jo.get_bool("known_down", false);
oter.known_up = jo.get_bool("known_up", false);
oter.mondensity = jo.get_int("mondensity", 0);
oter.sidewalk = jo.get_bool("sidewalk", false);
oter.allow_road = jo.get_bool("allow_road", false);
std::string id_base = oter.id;
int start_iid = oterlist.size();
oter.id_base = id_base;
oter.loadid_base = start_iid;
oter.directional_peers.clear();
oter.is_road = isroad(id_base);
oter.is_river = (id_base.compare(0,5,"river",5) == 0 || id_base.compare(0,6,"bridge",6) == 0);
if (line_drawing) {
// add variants for line drawing
oter.line_drawing = true;
for( int i = start_iid; i < start_iid+12; i++ ) {
oter.directional_peers.push_back(i);
}
oter.id = id_base + "_ns";
oter.sym = LINE_XOXO;
load_oter(oter);
oter.id = id_base + "_ew";
oter.sym = LINE_OXOX;
load_oter(oter);
oter.id = id_base + "_ne";
oter.sym = LINE_XXOO;
load_oter(oter);
oter.id = id_base + "_es";
oter.sym = LINE_OXXO;
load_oter(oter);
oter.id = id_base + "_sw";
oter.sym = LINE_OOXX;
load_oter(oter);
oter.id = id_base + "_wn";
oter.sym = LINE_XOOX;
load_oter(oter);
oter.id = id_base + "_nes";
oter.sym = LINE_XXXO;
load_oter(oter);
oter.id = id_base + "_new";
oter.sym = LINE_XXOX;
load_oter(oter);
oter.id = id_base + "_nsw";
oter.sym = LINE_XOXX;
load_oter(oter);
oter.id = id_base + "_esw";
oter.sym = LINE_OXXX;
load_oter(oter);
oter.id = id_base + "_nesw";
oter.sym = LINE_XXXX;
load_oter(oter);
} else if (rotate) {
// add north/east/south/west variants
oter.rotates = true;
for( int i = start_iid; i < start_iid+5; i++ ) {
oter.directional_peers.push_back(i);
}
oter.id = id_base + "_north";
oter.sym = syms[0];
load_oter(oter);
oter.id = id_base + "_east";
oter.sym = syms[1];
load_oter(oter);
oter.id = id_base + "_south";
oter.sym = syms[2];
load_oter(oter);
oter.id = id_base + "_west";
oter.sym = syms[3];
load_oter(oter);
} else {
oter.directional_peers.push_back(start_iid);
load_oter(oter);
}
}
// *** BEGIN overmap FUNCTIONS ***
overmap::overmap()
: loc(999, 999)
, prefix()
, name()
, layer(NULL)
, nullret("")
, nullbool(false)
, nullstr("")
{
// debugmsg("Warning - null overmap!");
}
overmap::overmap(game *g, int x, int y)
: loc(x, y)
, prefix()
, name(g->u.name)
, layer(NULL)
, nullret("")
, nullbool(false)
, nullstr("")
{
if (name.empty()) {
debugmsg("Attempting to load overmap for unknown player! Saving won't work!");
}
if (g->has_gametype()) {
prefix = special_game_name(g->gametype());
}
init_layers();
open(g);
}
overmap::overmap(overmap const& o)
: zg(o.zg)
, radios(o.radios)
, npcs(o.npcs)
, vehicles(o.vehicles)
, cities(o.cities)
, roads_out(o.roads_out)
, loc(o.loc)
, prefix(o.prefix)
, name(o.name)
, layer(NULL)
{
layer = new map_layer[OVERMAP_LAYERS];
for(int z = 0; z < OVERMAP_LAYERS; ++z) {
for(int i = 0; i < OMAPX; ++i) {
for(int j = 0; j < OMAPY; ++j) {
layer[z].terrain[i][j] = o.layer[z].terrain[i][j];
layer[z].visible[i][j] = o.layer[z].visible[i][j];
}
}
layer[z].notes = o.layer[z].notes;
}
}
overmap::~overmap()
{
if (layer) {
delete [] layer;
layer = NULL;
}
}
overmap& overmap::operator=(overmap const& o)
{
zg = o.zg;
radios = o.radios;
npcs = o.npcs;
vehicles = o.vehicles;
cities = o.cities;
roads_out = o.roads_out;
loc = o.loc;
prefix = o.prefix;
name = o.name;
if (layer) {
delete [] layer;
layer = NULL;
}
layer = new map_layer[OVERMAP_LAYERS];
for(int z = 0; z < OVERMAP_LAYERS; ++z) {
for(int i = 0; i < OMAPX; ++i) {
for(int j = 0; j < OMAPY; ++j) {
layer[z].terrain[i][j] = o.layer[z].terrain[i][j];
layer[z].visible[i][j] = o.layer[z].visible[i][j];
}
}
layer[z].notes = o.layer[z].notes;
}
return *this;
}
void overmap::init_layers()
{
layer = new map_layer[OVERMAP_LAYERS];
for(int z = 0; z < OVERMAP_LAYERS; ++z) {
oter_id default_type = (z < OVERMAP_DEPTH) ? "rock" : (z == OVERMAP_DEPTH) ? "field" : "";
// oter_iid default_type = (z < OVERMAP_DEPTH) ? ot_rock : (z == OVERMAP_DEPTH) ? ot_field : ot_null; // todo: regional default_type
for(int i = 0; i < OMAPX; ++i) {
for(int j = 0; j < OMAPY; ++j) {
layer[z].terrain[i][j] = default_type;
layer[z].visible[i][j] = false;
}
}
}
}
oter_id& overmap::ter(const int x, const int y, const int z)
{
if (x < 0 || x >= OMAPX || y < 0 || y >= OMAPY || z < -OVERMAP_DEPTH || z > OVERMAP_HEIGHT) {
return ot_null;
}
return layer[z + OVERMAP_DEPTH].terrain[x][y];
}
bool& overmap::seen(int x, int y, int z)
{
if (x < 0 || x >= OMAPX || y < 0 || y >= OMAPY || z < -OVERMAP_DEPTH || z > OVERMAP_HEIGHT) {
nullbool = false;
return nullbool;
}
return layer[z + OVERMAP_DEPTH].visible[x][y];
}
// this uses om_sub (submap coordinates localized to overmap,
// aka levxy or om_pos * 2)
std::vector<mongroup*> overmap::monsters_at(int x, int y, int z)
{
std::vector<mongroup*> ret;
if (x < 0 || x >= OMAPX*2 || y < 0 || y >= OMAPY*2 || z < -OVERMAP_DEPTH || z > OVERMAP_HEIGHT)
return ret;
for (int i = 0; i < zg.size(); i++) {
if (zg[i].posz != z) { continue; }
if (
( zg[i].diffuse == true ? square_dist(x, y, zg[i].posx, zg[i].posy) : trig_dist(x, y, zg[i].posx, zg[i].posy) )
<= zg[i].radius) {
ret.push_back(&(zg[i]));
}
}
return ret;
}
// this uses om_pos (overmap tiles, aka levxy / 2)
bool overmap::is_safe(int x, int y, int z)
{
std::vector<mongroup*> mons = monsters_at(x*2, y*2, z);
if (mons.empty())
return true;
bool safe = true;
for (int n = 0; n < mons.size() && safe; n++)
safe = mons[n]->is_safe();
return safe;
}
bool overmap::has_note(int const x, int const y, int const z) const
{
if (z < -OVERMAP_DEPTH || z > OVERMAP_HEIGHT) { return false; }
for (int i = 0; i < layer[z + OVERMAP_DEPTH].notes.size(); i++) {
if (layer[z + OVERMAP_DEPTH].notes[i].x == x && layer[z + OVERMAP_DEPTH].notes[i].y == y)
return true;
}
return false;
}
std::string const& overmap::note(int const x, int const y, int const z) const
{
if (z < -OVERMAP_DEPTH || z > OVERMAP_HEIGHT) { return nullstr; }
for (int i = 0; i < layer[z + OVERMAP_DEPTH].notes.size(); i++) {
if (layer[z + OVERMAP_DEPTH].notes[i].x == x && layer[z + OVERMAP_DEPTH].notes[i].y == y)
return layer[z + OVERMAP_DEPTH].notes[i].text;
}
return nullstr;
}
void overmap::add_note(int const x, int const y, int const z, std::string const & message)
{
if (z < -OVERMAP_DEPTH || z > OVERMAP_HEIGHT) {
debugmsg("Attempting to add not to overmap for blank layer %d", z);
return;
}
for (int i = 0; i < layer[z + OVERMAP_DEPTH].notes.size(); i++) {
if (layer[z + OVERMAP_DEPTH].notes[i].x == x && layer[z + OVERMAP_DEPTH].notes[i].y == y) {
if (message.empty())
layer[z + OVERMAP_DEPTH].notes.erase(layer[z + OVERMAP_DEPTH].notes.begin() + i);
else
layer[z + OVERMAP_DEPTH].notes[i].text = message;
return;
}
}
if (message.length() > 0)
layer[z + OVERMAP_DEPTH].notes.push_back(om_note(x, y, layer[z + OVERMAP_DEPTH].notes.size(), message));
}
point overmap::find_note(int const x, int const y, int const z, std::string const& text) const
{
point ret(-1, -1);
if (z < -OVERMAP_DEPTH || z > OVERMAP_HEIGHT) {
debugmsg("Attempting to find note on overmap for blank layer %d", z);
return ret;
}
int closest = 9999;
for (int i = 0; i < layer[z + OVERMAP_DEPTH].notes.size(); i++) {
if (layer[z + OVERMAP_DEPTH].notes[i].text.find(text) != std::string::npos &&
rl_dist(x, y, layer[z + OVERMAP_DEPTH].notes[i].x, layer[z + OVERMAP_DEPTH].notes[i].y) < closest) {
closest = rl_dist(x, y, layer[z + OVERMAP_DEPTH].notes[i].x, layer[z + OVERMAP_DEPTH].notes[i].y);
ret = point(layer[z + OVERMAP_DEPTH].notes[i].x, layer[z + OVERMAP_DEPTH].notes[i].y);
}
}
return ret;
}
//This removes a npc from the overmap. The NPC is supposed to be already dead.
//This function also assumes the npc is not in the list of active npcs anymore.
void overmap::remove_npc(int npc_id)
{
for(int i = 0; i < npcs.size(); i++)
{
if(npcs[i]->getID() == npc_id)
{
//Remove this npc from the list of overmap npcs.
if(!npcs[i]->dead) debugmsg("overmap::remove_npc: NPC (%d) is not dead.",npc_id);
npc * tmp = npcs[i];
npcs.erase(npcs.begin() + i);
delete tmp;
return;
}
}
}
void overmap::remove_vehicle(int id)
{
std::map<int, om_vehicle>::iterator om_veh = vehicles.find(id);
if (om_veh != vehicles.end())
vehicles.erase(om_veh);
}
int overmap::add_vehicle(vehicle *veh)
{
int id = vehicles.size() + 1;
// this *should* be unique but just in case
while ( vehicles.count(id) > 0 )
id++;
om_vehicle tracked_veh;
tracked_veh.x = veh->omap_x()/2;
tracked_veh.y = veh->omap_y()/2;
tracked_veh.name = veh->name;
vehicles[id]=tracked_veh;
return id;
}
point overmap::display_notes(game* g, int const z) const
{
if (z < -OVERMAP_DEPTH || z > OVERMAP_HEIGHT) {
debugmsg("overmap::display_notes: Attempting to display notes on overmap for blank layer %d", z);
return point(-1, -1);
}
std::string title = _("Notes:");
WINDOW* w_notes = newwin(FULL_SCREEN_HEIGHT, FULL_SCREEN_WIDTH,
(TERMY > FULL_SCREEN_HEIGHT) ? (TERMY-FULL_SCREEN_HEIGHT)/2 : 0,
(TERMX > FULL_SCREEN_WIDTH) ? (TERMX-FULL_SCREEN_WIDTH)/2 : 0);
wborder(w_notes, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX,
LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX );
const int maxitems = 20; // Number of items to show at one time.
char ch = '.';
int start = 0, cur_it(0);
mvwprintz(w_notes, 1, 1, c_ltgray, title.c_str());
do{
if (ch == '<' && start > 0) {
for (int i = 1; i < FULL_SCREEN_HEIGHT; i++)
mvwprintz(w_notes, i+1, 1, c_black, " ");
start -= maxitems;
if (start < 0)
start = 0;
mvwprintw(w_notes, maxitems + 2, 1, " ");
}
if (ch == '>' && cur_it < layer[z + OVERMAP_DEPTH].notes.size()) {
start = cur_it;
mvwprintw(w_notes, maxitems + 2, 13, " ");
for (int i = 1; i < FULL_SCREEN_HEIGHT; i++)
mvwprintz(w_notes, i, 0, c_black, " ");
}
int cur_line = 3;
int last_line = -1;
char cur_let = 'a';
for (cur_it = start; cur_it < start + maxitems && cur_line < 23; cur_it++) {
if (cur_it < layer[z + OVERMAP_DEPTH].notes.size()) {
mvwputch (w_notes, cur_line, 1, c_white, cur_let++);
mvwprintz(w_notes, cur_line, 3, c_ltgray, "- %s", layer[z + OVERMAP_DEPTH].notes[cur_it].text.c_str());
} else{
last_line = cur_line - 2;
break;
}
cur_line++;
}
if(last_line == -1)
last_line = 23;
if (start > 0)
mvwprintw(w_notes, maxitems + 4, 1, _("< Go Back"));
if (cur_it < layer[z + OVERMAP_DEPTH].notes.size())
mvwprintw(w_notes, maxitems + 4, 12, _("> More notes"));
if(ch >= 'a' && ch <= 't'){
int chosen_line = (int)(ch % (int)'a');
if(chosen_line < last_line)
return point(layer[z + OVERMAP_DEPTH].notes[start + chosen_line].x, layer[z + OVERMAP_DEPTH].notes[start + chosen_line].y);
}
mvwprintz(w_notes, 1, 40, c_white, _("Press letter to center on note"));
mvwprintz(w_notes, FULL_SCREEN_HEIGHT-2, 40, c_white, _("Spacebar - Return to map "));
wrefresh(w_notes);
ch = getch();
} while(ch != ' ' && ch != '\n' && ch != KEY_ESCAPE);
delwin(w_notes);
return point(-1,-1);
}
bool overmap::has_npc(game *g, int const x, int const y, int const z) const
{
//Check if the target overmap square has an npc in it.
for (int n = 0; n < npcs.size(); n++) {
if(npcs[n]->omz == z && !npcs[n]->marked_for_death)
{
if (npcs[n]->is_active(g))
{ //Active npcs have different coords. Because Cata hates you!
if ((g->levx + (npcs[n]->posx / SEEX))/2 == x &&
(g->levy + (npcs[n]->posy / SEEY))/2 == y)
return true;
} else if ((npcs[n]->mapx)/2 == x && (npcs[n]->mapy)/2== y)
return true;
}
}
return false;
}
bool overmap::has_vehicle(game *g, int const x, int const y, int const z, bool require_pda) const
{
// vehicles only spawn at z level 0 (for now)
if (!z == 0)
return false;
// if the player is not carrying a PDA then he cannot see the vehicle.
if (require_pda && !g->u.has_amount("pda", 1))
return false;
for (std::map<int, om_vehicle>::const_iterator it = vehicles.begin();
it != vehicles.end(); it++)
{
om_vehicle om_veh = it->second;
if ( om_veh.x == x && om_veh.y == y )
return true;
}
return false;
}
// int cursx = (g->levx + int(MAPSIZE / 2)) / 2,
// cursy = (g->levy + int(MAPSIZE / 2)) / 2;
//Helper function for the overmap::draw function.
void overmap::print_npcs(game *g, WINDOW *w, int const x, int const y, int const z)
{
int i = 0, maxnamelength = 0;
//Check the max namelength of the npcs in the target
for (int n = 0; n < npcs.size(); n++)
{
if(npcs[n]->omz == z && !npcs[n]->marked_for_death)
{
if (npcs[n]->is_active(g))
{ //Active npcs have different coords. Because Cata hates you!
if ((g->levx + (npcs[n]->posx / SEEX))/2 == x &&
(g->levy + (npcs[n]->posy / SEEY))/2 == y)
{
if (npcs[n]->name.length() > maxnamelength)
maxnamelength = npcs[n]->name.length();
}
} else if ((npcs[n]->mapx)/2 == x && (npcs[n]->mapy)/2 == y) {
if (npcs[n]->name.length() > maxnamelength)
maxnamelength = npcs[n]->name.length();
}
}
}
//Check if the target has an npc in it.
for (int n = 0; n < npcs.size(); n++)
{
if (npcs[n]->omz == z && !npcs[n]->marked_for_death)
{
if (npcs[n]->is_active(g))
{
if ((g->levx + (npcs[n]->posx / SEEX))/2 == x &&
(g->levy + (npcs[n]->posy / SEEY))/2 == y)
{
mvwprintz(w, i, 0, c_yellow, npcs[n]->name.c_str());
for (int j = npcs[n]->name.length(); j < maxnamelength; j++)
mvwputch(w, i, j, c_black, LINE_XXXX);
i++;
}
} else if ((npcs[n]->mapx)/2 == x && (npcs[n]->mapy)/2 == y)
{
mvwprintz(w, i, 0, c_yellow, npcs[n]->name.c_str());
for (int j = npcs[n]->name.length(); j < maxnamelength; j++)
mvwputch(w, i, j, c_black, LINE_XXXX);
i++;
}
}
}
for (int j = 0; j < i; j++)
mvwputch(w, j, maxnamelength, c_white, LINE_XOXO);
for (int j = 0; j < maxnamelength; j++)
mvwputch(w, i, j, c_white, LINE_OXOX);
mvwputch(w, i, maxnamelength, c_white, LINE_XOOX);
}
void overmap::print_vehicles(game *g, WINDOW *w, int const x, int const y, int const z)
{
if (!z==0) // vehicles only exist on zlevel 0
return;
int i = 0, maxnamelength = 0;
//Check the max namelength of the vehicles in the target
for (std::map<int, om_vehicle>::const_iterator it = vehicles.begin();
it != vehicles.end(); it++)
{
om_vehicle om_veh = it->second;
if ( om_veh.x == x && om_veh.y == y )
{
if (om_veh.name.length() > maxnamelength)
maxnamelength = om_veh.name.length();
}
}
//Check if the target has a vehicle in it.
for (std::map<int, om_vehicle>::const_iterator it = vehicles.begin();
it != vehicles.end(); it++)
{
om_vehicle om_veh = it->second;
if (om_veh.x == x && om_veh.y == y)
{
mvwprintz(w, i, 0, c_cyan, om_veh.name.c_str());
for (int j = om_veh.name.length(); j < maxnamelength; j++)
mvwputch(w, i, j, c_black, LINE_XXXX);
i++;
}
}
for (int j = 0; j < i; j++)
mvwputch(w, j, maxnamelength, c_white, LINE_XOXO);
for (int j = 0; j < maxnamelength; j++)
mvwputch(w, i, j, c_white, LINE_OXOX);
mvwputch(w, i, maxnamelength, c_white, LINE_XOOX);
}
void overmap::generate(game *g, overmap* north, overmap* east, overmap* south,
overmap* west)
{
dbg(D_INFO) << "overmap::generate start...";
erase();
clear();
move(0, 0);
std::vector<city> road_points; // cities and roads_out together
std::vector<point> river_start;// West/North endpoints of rivers
std::vector<point> river_end; // East/South endpoints of rivers
// Determine points where rivers & roads should connect w/ adjacent maps
const oter_id river_center("river_center"); // optimized comparison.
if (north != NULL) {
for (int i = 2; i < OMAPX - 2; i++) {
if (is_river(north->ter(i,OMAPY-1, 0)))
ter(i, 0, 0) = river_center;
if (north->ter(i, OMAPY - 1, 0) == river_center &&
north->ter(i - 1, OMAPY - 1, 0) == river_center &&
north->ter(i + 1, OMAPY - 1, 0) == river_center) {
if (river_start.size() == 0 ||
river_start[river_start.size() - 1].x < i - 6)
river_start.push_back(point(i, 0));
}
}
for (int i = 0; i < north->roads_out.size(); i++) {
if (north->roads_out[i].y == OMAPY - 1)
roads_out.push_back(city(north->roads_out[i].x, 0, 0));
}
}
int rivers_from_north = river_start.size();
if (west != NULL) {
for (int i = 2; i < OMAPY - 2; i++) {
if (is_river(west->ter(OMAPX - 1, i, 0)))
ter(0, i, 0) = river_center;
if (west->ter(OMAPX - 1, i, 0) == river_center &&
west->ter(OMAPX - 1, i - 1, 0) == river_center &&
west->ter(OMAPX - 1, i + 1, 0) == river_center) {
if (river_start.size() == rivers_from_north ||
river_start[river_start.size() - 1].y < i - 6)
river_start.push_back(point(0, i));
}
}
for (int i = 0; i < west->roads_out.size(); i++) {
if (west->roads_out[i].x == OMAPX - 1)
roads_out.push_back(city(0, west->roads_out[i].y, 0));
}
}
if (south != NULL) {
for (int i = 2; i < OMAPX - 2; i++) {
if (is_river(south->ter(i, 0, 0)))
ter(i, OMAPY - 1, 0) = river_center;
if (south->ter(i, 0, 0) == river_center &&
south->ter(i - 1, 0, 0) == river_center &&
south->ter(i + 1, 0, 0) == river_center) {
if (river_end.size() == 0 ||
river_end[river_end.size() - 1].x < i - 6)
river_end.push_back(point(i, OMAPY - 1));
}
if (south->ter(i, 0, 0) == "road_nesw")
roads_out.push_back(city(i, OMAPY - 1, 0));
}
for (int i = 0; i < south->roads_out.size(); i++) {
if (south->roads_out[i].y == 0)
roads_out.push_back(city(south->roads_out[i].x, OMAPY - 1, 0));
}
}
int rivers_to_south = river_end.size();
if (east != NULL) {
for (int i = 2; i < OMAPY - 2; i++) {
if (is_river(east->ter(0, i, 0)))
ter(OMAPX - 1, i, 0) = river_center;
if (east->ter(0, i, 0) == river_center &&
east->ter(0, i - 1, 0) == river_center &&
east->ter(0, i + 1, 0) == river_center) {
if (river_end.size() == rivers_to_south ||
river_end[river_end.size() - 1].y < i - 6)
river_end.push_back(point(OMAPX - 1, i));
}
if (east->ter(0, i, 0) == "road_nesw")
roads_out.push_back(city(OMAPX - 1, i, 0));
}
for (int i = 0; i < east->roads_out.size(); i++) {
if (east->roads_out[i].x == 0)
roads_out.push_back(city(OMAPX - 1, east->roads_out[i].y, 0));
}
}
// Even up the start and end points of rivers. (difference of 1 is acceptable)
// Also ensure there's at least one of each.
std::vector<point> new_rivers;
if (north == NULL || west == NULL) {
while (river_start.empty() || river_start.size() + 1 < river_end.size()) {
new_rivers.clear();
if (north == NULL)
new_rivers.push_back( point(rng(10, OMAPX - 11), 0) );
if (west == NULL)
new_rivers.push_back( point(0, rng(10, OMAPY - 11)) );
river_start.push_back( new_rivers[rng(0, new_rivers.size() - 1)] );
}
}
if (south == NULL || east == NULL) {
while (river_end.empty() || river_end.size() + 1 < river_start.size()) {
new_rivers.clear();
if (south == NULL)
new_rivers.push_back( point(rng(10, OMAPX - 11), OMAPY - 1) );
if (east == NULL)
new_rivers.push_back( point(OMAPX - 1, rng(10, OMAPY - 11)) );
river_end.push_back( new_rivers[rng(0, new_rivers.size() - 1)] );
}
}
// Now actually place those rivers.
if (river_start.size() > river_end.size() && river_end.size() > 0) {
std::vector<point> river_end_copy = river_end;
while (!river_start.empty()) {
int index = rng(0, river_start.size() - 1);
if (!river_end.empty()) {
place_river(river_start[index], river_end[0]);
river_end.erase(river_end.begin());
} else
place_river(river_start[index],
river_end_copy[rng(0, river_end_copy.size() - 1)]);
river_start.erase(river_start.begin() + index);
}
} else if (river_end.size() > river_start.size() && river_start.size() > 0) {
std::vector<point> river_start_copy = river_start;
while (!river_end.empty()) {
int index = rng(0, river_end.size() - 1);
if (!river_start.empty()) {
place_river(river_start[0], river_end[index]);
river_start.erase(river_start.begin());
} else
place_river(river_start_copy[rng(0, river_start_copy.size() - 1)],
river_end[index]);
river_end.erase(river_end.begin() + index);
}
} else if (river_end.size() > 0) {
if (river_start.size() != river_end.size())
river_start.push_back( point(rng(OMAPX * .25, OMAPX * .75),
rng(OMAPY * .25, OMAPY * .75)));
for (int i = 0; i < river_start.size(); i++)
place_river(river_start[i], river_end[i]);
}
// Cities and forests come next.
// These're agnostic of adjacent maps, so it's very simple.
place_cities();
place_forest();
// Ideally we should have at least two exit points for roads, on different sides
if (roads_out.size() < 2) {
std::vector<city> viable_roads;
int tmp;
// Populate viable_roads with one point for each neighborless side.
// Make sure these points don't conflict with rivers.
// TODO: In theory this is a potential infinte loop...
if (north == NULL) {
do
tmp = rng(10, OMAPX - 11);
while (is_river(ter(tmp, 0, 0)) || is_river(ter(tmp - 1, 0, 0)) ||
is_river(ter(tmp + 1, 0, 0)) );
viable_roads.push_back(city(tmp, 0, 0));
}
if (east == NULL) {
do
tmp = rng(10, OMAPY - 11);
while (is_river(ter(OMAPX - 1, tmp, 0)) || is_river(ter(OMAPX - 1, tmp - 1, 0))||
is_river(ter(OMAPX - 1, tmp + 1, 0)));
viable_roads.push_back(city(OMAPX - 1, tmp, 0));
}
if (south == NULL) {
do
tmp = rng(10, OMAPX - 11);
while (is_river(ter(tmp, OMAPY - 1, 0)) || is_river(ter(tmp - 1, OMAPY - 1, 0))||
is_river(ter(tmp + 1, OMAPY - 1, 0)));
viable_roads.push_back(city(tmp, OMAPY - 1, 0));
}
if (west == NULL) {
do
tmp = rng(10, OMAPY - 11);
while (is_river(ter(0, tmp, 0)) || is_river(ter(0, tmp - 1, 0)) ||
is_river(ter(0, tmp + 1, 0)));
viable_roads.push_back(city(0, tmp, 0));
}
while (roads_out.size() < 2 && !viable_roads.empty()) {
tmp = rng(0, viable_roads.size() - 1);
roads_out.push_back(viable_roads[tmp]);
viable_roads.erase(viable_roads.begin() + tmp);
}
}
// Compile our master list of roads; it's less messy if roads_out is first
for (int i = 0; i < roads_out.size(); i++)
road_points.push_back(roads_out[i]);
for (int i = 0; i < cities.size(); i++)
road_points.push_back(cities[i]);
// And finally connect them via "highways"
place_hiways(road_points, 0, "road");
// Place specials
place_specials();
// Clean up our roads and rivers
polish(0);
// TODO: there is no reason we can't generate the sublevels in one pass
// for that matter there is no reason we can't as we add the entrance ways either
// Always need at least one sublevel, but how many more
int z = -1;
bool requires_sub = false;
do {
requires_sub = generate_sub(z);
} while(requires_sub && (--z >= -OVERMAP_DEPTH));
// Place the monsters, now that the terrain is laid out
place_mongroups();
place_radios();
dbg(D_INFO) << "overmap::generate done";
}
bool overmap::generate_sub(int const z)
{
bool requires_sub = false;
std::vector<city> subway_points;
std::vector<city> sewer_points;
std::vector<city> ant_points;
std::vector<city> goo_points;
std::vector<city> lab_points;
std::vector<city> ice_lab_points;
std::vector<point> shaft_points;
std::vector<city> mine_points;
std::vector<point> bunker_points;
std::vector<point> shelter_points;
std::vector<point> lmoe_points;
std::vector<point> cabin_strange_points;
std::vector<point> triffid_points;
std::vector<point> temple_points;
std::vector<point> office_entrance_points;
std::vector<point> office_points;
std::vector<point> prison_points;
std::vector<point> prison_entrance_points;
std::vector<point> haz_sar_entrance_points;
std::vector<point> haz_sar_points;
std::vector<point> cathedral_entrance_points;
std::vector<point> cathedral_points;
std::vector<point> hotel_tower_1_points;
std::vector<point> hotel_tower_2_points;
std::vector<point> hotel_tower_3_points;
// These are so common that it's worth checking first as int.
const oter_id skip_above[5] = {
oter_id("rock"), oter_id("forest"), oter_id("field"),
oter_id("forest_thick"), oter_id("forest_water")
};
for (int i = 0; i < OMAPX; i++) {
for (int j = 0; j < OMAPY; j++) {
oter_id oter_above = ter(i, j, z + 1);
// implicitly skip skip_above oter_ids
bool skipme = false;
for (int si=0; si < 5; si++) {
if (oter_above == skip_above[si]) {
skipme = true;
}
}
if (skipme) {
continue;
}
if (is_ot_type("house_base", oter_above)) {
ter(i, j, z) = "basement";
} else if (is_ot_type("sub_station", oter_above)) {
ter(i, j, z) = "subway_nesw";
subway_points.push_back(city(i, j, 0));
} else if (is_ot_type("prison", oter_above) &&
oter_above != "prison_2") {
prison_points.push_back( point(i, j) );
} else if (oter_above == "prison_2") {
prison_entrance_points.push_back( point(i, j) );
} else if (oter_above == "road_nesw_manhole") {
ter(i, j, z) = "sewer_nesw";
sewer_points.push_back(city(i, j, 0));
} else if (oter_above == "sewage_treatment") {
for (int x = i-1; x <= i+1; x++) {
for (int y = j-1; y <= j+1; y++) {
ter(x, y, z) = "sewage_treatment_under";
}
}
ter(i, j, z) = "sewage_treatment_hub";
sewer_points.push_back(city(i, j, 0));
} else if (oter_above == "spider_pit") {
ter(i, j, z) = "spider_pit_under";
} else if (oter_above == "cave" && z == -1) {
if (one_in(3)) {
ter(i, j, z) = "cave_rat";
requires_sub = true; // rat caves are two level
} else {
ter(i, j, z) = "cave";
}
} else if (oter_above == "cave_rat" && z == -2) {
ter(i, j, z) = "cave_rat";
} else if (oter_above == "anthill") {
int size = rng(MIN_ANT_SIZE, MAX_ANT_SIZE);
ant_points.push_back(city(i, j, size));
zg.push_back(mongroup("GROUP_ANT", i * 2, j * 2, z, size * 1.5, rng(6000, 8000)));
} else if (oter_above == "slimepit_down") {
int size = rng(MIN_GOO_SIZE, MAX_GOO_SIZE);
goo_points.push_back(city(i, j, size));
} else if (oter_above == "forest_water") {
ter(i, j, z) = "cavern";
} else if (oter_above == "triffid_grove" ||
oter_above == "triffid_roots") {
triffid_points.push_back( point(i, j) );
} else if (oter_above == "temple_stairs") {
temple_points.push_back( point(i, j) );
} else if (oter_above == "lab_core" ||
(z == -1 && oter_above == "lab_stairs")) {
lab_points.push_back(city(i, j, rng(1, 5 + z)));
} else if (oter_above == "lab_stairs") {
ter(i, j, z) = "lab";
} else if (oter_above == "ice_lab_core" ||
(z == -1 && oter_above == "ice_lab_stairs")) {
ice_lab_points.push_back(city(i, j, rng(1, 5 + z)));
} else if (oter_above == "ice_lab_stairs") {
ter(i, j, z) = "ice_lab";
} else if (oter_above == "bunker" && z == -1) {
bunker_points.push_back( point(i, j) );
} else if (oter_above == "shelter") {
shelter_points.push_back( point(i, j) );
} else if (oter_above == "lmoe") {
lmoe_points.push_back( point(i, j) );
} else if (oter_above == "cabin_strange") {
cabin_strange_points.push_back( point(i, j) );
} else if (oter_above == "mine_entrance") {
shaft_points.push_back( point(i, j) );
} else if (oter_above == "mine_shaft" ||
oter_above == "mine_down" ) {
ter(i, j, z) = "mine";
mine_points.push_back(city(i, j, rng(6 + z, 10 + z)));
// technically not all finales need a sub level,
// but at this point we don't know
requires_sub = true;
} else if (oter_above == "mine_finale") {
for (int x = i - 1; x <= i + 1; x++) {
for (int y = j - 1; y <= j + 1; y++) {
ter(x, y, z) = "spiral";
}
}
ter(i, j, z) = "spiral_hub";
zg.push_back(mongroup("GROUP_SPIRAL", i * 2, j * 2, z, 2, 200));
} else if (oter_above == "silo") {
if (rng(2, 7) < abs(z) || rng(2, 7) < abs(z)) {
ter(i, j, z) = "silo_finale";
} else {
ter(i, j, z) = "silo";
requires_sub = true;
}
} else if (oter_above == "office_tower_1_entrance") {
office_entrance_points.push_back( point(i, j) );
} else if (oter_above == "office_tower_1") {
office_points.push_back( point(i, j) );
} else if (oter_above == "haz_sar_entrance") {
haz_sar_entrance_points.push_back( point(i, j) );
} else if (oter_above == "haz_sar") {
haz_sar_points.push_back( point(i, j) );
} else if (oter_above == "cathedral_1_entrance") {
cathedral_entrance_points.push_back( point(i, j) );
} else if (oter_above == "cathedral_1") {
cathedral_points.push_back( point(i, j) );
} else if (oter_above == "hotel_tower_1_7") {
hotel_tower_1_points.push_back( point(i, j) );
} else if (oter_above == "hotel_tower_1_8") {
hotel_tower_2_points.push_back( point(i, j) );
} else if (oter_above == "hotel_tower_1_9") {
hotel_tower_3_points.push_back( point(i, j) );
}
}
}
for (int i = 0; i < goo_points.size(); i++) {
requires_sub |= build_slimepit(goo_points[i].x, goo_points[i].y, z, goo_points[i].s);
}
place_hiways(sewer_points, z, "sewer");
polish(z, "sewer");
place_hiways(subway_points, z, "subway");
for (int i = 0; i < subway_points.size(); i++) {
ter(subway_points[i].x, subway_points[i].y, z) = "subway_station";
}
for (int i = 0; i < lab_points.size(); i++) {
bool lab = build_lab(lab_points[i].x, lab_points[i].y, z, lab_points[i].s);
requires_sub |= lab;
if (!lab && ter(lab_points[i].x, lab_points[i].y, z) == "lab_core") {
ter(lab_points[i].x, lab_points[i].y, z) = "lab";
}
}
for (int i = 0; i < ice_lab_points.size(); i++) {
bool ice_lab = build_ice_lab(ice_lab_points[i].x, ice_lab_points[i].y, z, ice_lab_points[i].s);
requires_sub |= ice_lab;
if (!ice_lab && ter(ice_lab_points[i].x, ice_lab_points[i].y, z) == "ice_lab_core") {
ter(ice_lab_points[i].x, ice_lab_points[i].y, z) = "ice_lab";
}
}
for (int i = 0; i < ant_points.size(); i++) {
build_anthill(ant_points[i].x, ant_points[i].y, z, ant_points[i].s);
}
polish(z, "subway");
polish(z, "ants");
for (int i = 0; i < cities.size(); i++) {
if (one_in(3)) {
zg.push_back(mongroup("GROUP_CHUD", cities[i].x * 2,
cities[i].y * 2, z, cities[i].s,
cities[i].s * 20));
}
if (!one_in(8)) {
zg.push_back(mongroup("GROUP_SEWER",
cities[i].x * 2, cities[i].y * 2, z,
cities[i].s * 3.5, cities[i].s * 70));
}
}
place_rifts(z);
for (int i = 0; i < mine_points.size(); i++) {
build_mine(mine_points[i].x, mine_points[i].y, z, mine_points[i].s);
}
for (int i = 0; i < shaft_points.size(); i++) {
ter(shaft_points[i].x, shaft_points[i].y, z) = "mine_shaft";
requires_sub = true;
}
for (int i = 0; i < bunker_points.size(); i++) {
ter(bunker_points[i].x, bunker_points[i].y, z) = "bunker";
}
for (int i = 0; i < shelter_points.size(); i++) {
ter(shelter_points[i].x, shelter_points[i].y, z) = "shelter_under";
}
for (int i = 0; i < lmoe_points.size(); i++) {
ter(lmoe_points[i].x, lmoe_points[i].y, z) = "lmoe_under";
}
for (int i = 0; i < cabin_strange_points.size(); i++) {
ter(cabin_strange_points[i].x, cabin_strange_points[i].y, z) = "cabin_strange_b";
}
for (int i = 0; i < triffid_points.size(); i++) {
if (z == -1) {
ter(triffid_points[i].x, triffid_points[i].y, z) = "triffid_roots";
requires_sub = true;
} else {
ter(triffid_points[i].x, triffid_points[i].y, z) = "triffid_finale";
}
}
for (int i = 0; i < temple_points.size(); i++) {
if (z == -5) {
ter(temple_points[i].x, temple_points[i].y, z) = "temple_finale";
} else {
ter(temple_points[i].x, temple_points[i].y, z) = "temple_stairs";
requires_sub = true;
}
}
for (int i = 0; i < office_entrance_points.size(); i++) {
ter(office_entrance_points[i].x, office_entrance_points[i].y, z) = "office_tower_b_entrance";
}
for (int i = 0; i < office_points.size(); i++) {
ter(office_points[i].x, office_points[i].y, z) = "office_tower_b";
}
for (int i = 0; i < prison_points.size(); i++) {
ter(prison_points[i].x, prison_points[i].y, z) = "prison_b";
}
for (int i = 0; i < prison_entrance_points.size(); i++) {
ter(prison_entrance_points[i].x, prison_entrance_points[i].y, z) = "prison_b_entrance";
}
for (int i = 0; i < haz_sar_entrance_points.size(); i++) {
ter(haz_sar_entrance_points[i].x, haz_sar_entrance_points[i].y, z-1) = "haz_sar_entrance_b1";
}
for (int i = 0; i < haz_sar_points.size(); i++) {
ter(haz_sar_points[i].x, haz_sar_points[i].y, z-1) = "haz_sar_b1";
}
for (int i = 0; i < cathedral_entrance_points.size(); i++) {
ter(cathedral_entrance_points[i].x, cathedral_entrance_points[i].y, z) = "cathedral_b_entrance";
}
for (int i = 0; i < cathedral_points.size(); i++) {
ter(cathedral_points[i].x, cathedral_points[i].y, z) = "cathedral_b";
}
for (int i = 0; i < hotel_tower_1_points.size(); i++) {
ter(hotel_tower_1_points[i].x, hotel_tower_1_points[i].y, z) = "hotel_tower_b_1";
}
for (int i = 0; i < hotel_tower_2_points.size(); i++) {
ter(hotel_tower_2_points[i].x, hotel_tower_2_points[i].y, z) = "hotel_tower_b_2";
}
for (int i = 0; i < hotel_tower_3_points.size(); i++) {
ter(hotel_tower_3_points[i].x, hotel_tower_3_points[i].y, z) = "hotel_tower_b_3";
}
return requires_sub;
}
void overmap::make_tutorial()
{
for (int i = 0; i < OMAPX; i++) {
for (int j = 0; j < OMAPY; j++) {
ter(i, j, -1) = "rock";
}
}
ter(50, 50, 0) = "tutorial";
ter(50, 50, -1) = "tutorial";
zg.clear();
}
point overmap::find_closest(point origin, const oter_id &type,
int &dist, bool must_be_seen)
{
//does origin qualify?
if (check_ot_type(type, origin.x, origin.y, 0)) {
if (!must_be_seen || seen(origin.x, origin.y, 0)) {
return point(origin.x, origin.y);
}
}
int max = (dist == 0 ? OMAPX : dist);
// expanding box
for (dist = 0; dist <= max; dist++) {
// each edge length is 2*dist-2, because corners belong to one edge
// south is +y, north is -y
for (int i = 0; i < dist*2-1; i++) {
//start at northwest, scan north edge
int x = origin.x - dist + i;
int y = origin.y - dist;
if (check_ot_type(type, x, y, 0)) {
if (!must_be_seen || seen(x, y, 0)) {
return point(x, y);
}
}
//start at southeast, scan south
x = origin.x + dist - i;
y = origin.y + dist;
if (check_ot_type(type, x, y, 0)) {
if (!must_be_seen || seen(x, y, 0)) {
return point(x, y);
}
}
//start at southwest, scan west
x = origin.x - dist;
y = origin.y + dist - i;
if (check_ot_type(type, x, y, 0)) {
if (!must_be_seen || seen(x, y, 0)) {
return point(x, y);
}
}
//start at northeast, scan east
x = origin.x + dist;
y = origin.y - dist + i;
if (check_ot_type(type, x, y, 0)) {
if (!must_be_seen || seen(x, y, 0)) {
return point(x, y);
}
}
}
}
dist = -1;
return point(-1, -1);
}
std::vector<point> overmap::find_all(tripoint origin, const std::string &type,
int &dist, bool must_be_seen)
{
std::vector<point> res;
int max = (dist == 0 ? OMAPX / 2 : dist);
for (dist = 0; dist <= max; dist++) {
for (int x = origin.x - dist; x <= origin.x + dist; x++) {
for (int y = origin.y - dist; y <= origin.y + dist; y++) {
if (check_ot_type(type, x, y, origin.z)
&& (!must_be_seen || seen(x, y, origin.z))) {
res.push_back(point(x, y));
}
}
}
}
return res;
}
std::vector<point> overmap::find_terrain(std::string term, int cursx, int cursy, int zlevel)
{
std::vector<point> found;
for (int x = 0; x < OMAPX; x++) {
for (int y = 0; y < OMAPY; y++) {
if (seen(x, y, zlevel) && otermap[ter(x, y, zlevel)].name.find(term) != std::string::npos) {
found.push_back( point(x, y) );
}
}
}
return found;
}
int overmap::closest_city(point p)
{
int distance = 999, ret = -1;
for (int i = 0; i < cities.size(); i++) {
int dist = rl_dist(p.x, p.y, cities[i].x, cities[i].y);
if (dist < distance || (dist == distance && cities[i].s < cities[ret].s)) {
ret = i;
distance = dist;
}
}
return ret;
}
point overmap::random_house_in_city(int city_id)
{
if (city_id < 0 || city_id >= cities.size()) {
debugmsg("overmap::random_house_in_city(%d) (max %d)", city_id,
cities.size() - 1);
return point(-1, -1);
}
std::vector<point> valid;
int startx = cities[city_id].x - cities[city_id].s;
int endx = cities[city_id].x + cities[city_id].s;
int starty = cities[city_id].y - cities[city_id].s;
int endy = cities[city_id].y + cities[city_id].s;
for (int x = startx; x <= endx; x++) {
for (int y = starty; y <= endy; y++) {
if (check_ot_type("house", x, y, 0)) {
valid.push_back( point(x, y) );
}
}
}
if (valid.empty()) {
return point(-1, -1);
}
return valid[ rng(0, valid.size() - 1) ];
}
int overmap::dist_from_city(point p)
{
int distance = 999;
for (int i = 0; i < cities.size(); i++) {
int dist = rl_dist(p.x, p.y, cities[i].x, cities[i].y);
dist -= cities[i].s;
if (dist < distance)
distance = dist;
}
return distance;
}
void overmap::draw(WINDOW *w, game *g, int z, int &cursx, int &cursy,
int &origx, int &origy, signed char &ch, bool blink,
overmap &hori, overmap &vert, overmap &diag, input_context* inp_ctxt)
{
bool note_here = false, npc_here = false, veh_here = false;
std::string note_text;
int om_map_width = TERMX-28;
int om_map_height = TERMY;
int omx, omy;
point target(-1, -1);
if (g->u.active_mission >= 0 &&
g->u.active_mission < g->u.active_missions.size())
target = g->find_mission(g->u.active_missions[g->u.active_mission])->target;
bool see;
oter_id cur_ter = ot_null;
nc_color ter_color;
long ter_sym;
/* First, determine if we're close enough to the edge to need an
* adjacent overmap, and record the offsets. */
int offx = 0;
int offy = 0;
if (cursx < om_map_width / 2)
{
offx = -1;
}
else if (cursx > OMAPX - 2 - (om_map_width / 2))
{
offx = 1;
}
if (cursy < (om_map_height / 2))
{
offy = -1;
}
else if (cursy > OMAPY - 2 - (om_map_height / 2))
{
offy = 1;
}
// If the offsets don't match the previously loaded ones, load the new adjacent overmaps.
if( offx && loc.x + offx != hori.loc.x )
{
hori = overmap_buffer.get( g, loc.x + offx, loc.y );
}
if( offy && loc.y + offy != vert.loc.y )
{
vert = overmap_buffer.get( g, loc.x, loc.y + offy );
}
if( offx && offy && (loc.x + offx != diag.loc.x || loc.y + offy != diag.loc.y ) )
{
diag = overmap_buffer.get( g, loc.x + offx, loc.y + offy );
}
// Now actually draw the map
bool csee = false;
oter_id ccur_ter = "";
for (int i = -(om_map_width / 2); i < (om_map_width / 2); i++) {
for (int j = -(om_map_height / 2);
j <= (om_map_height / 2) + (ch == 'j' ? 1 : 0); j++) {
omx = cursx + i;
omy = cursy + j;
see = false;
npc_here = false;
veh_here = false;
if (omx >= 0 && omx < OMAPX && omy >= 0 && omy < OMAPY) { // It's in-bounds
cur_ter = ter(omx, omy, z);
see = seen(omx, omy, z);
note_here = has_note(omx, omy, z);
if (note_here) {
note_text = note(omx, omy, z);
}
//Check if there is an npc.
npc_here = has_npc(g,omx,omy,z);
// and a vehicle
veh_here = has_vehicle(g,omx,omy,z);
// <Out of bounds placement>
} else if (omx < 0) {
omx += OMAPX;
if (omy < 0 || omy >= OMAPY) {
omy += (omy < 0 ? OMAPY : 0 - OMAPY);
cur_ter = diag.ter(omx, omy, z);
see = diag.seen(omx, omy, z);
veh_here = diag.has_vehicle(g, omx, omy, z);
note_here = diag.has_note(omx, omy, z);
if (note_here) {
note_text = diag.note(omx, omy, z);
}
} else {
cur_ter = hori.ter(omx, omy, z);
see = hori.seen(omx, omy, z);
veh_here = hori.has_vehicle(g, omx, omy, z);
note_here = hori.has_note(omx, omy, z);
if (note_here) {
note_text = hori.note(omx, omy, z);
}
}
} else if (omx >= OMAPX) {
omx -= OMAPX;
if (omy < 0 || omy >= OMAPY) {
omy += (omy < 0 ? OMAPY : 0 - OMAPY);
cur_ter = diag.ter(omx, omy, z);
see = diag.seen(omx, omy, z);
veh_here = diag.has_vehicle(g, omx, omy, z);
note_here = diag.has_note(omx, omy, z);
if (note_here) {
note_text = diag.note(omx, omy, z);
}
} else {
cur_ter = hori.ter(omx, omy, z);
see = hori.seen(omx, omy, z);
veh_here = hori.has_vehicle(g, omx, omy, z);
note_here = hori.has_note(omx, omy, z);
if (note_here) {
note_text = hori.note(omx, omy, z);
}
}
} else if (omy < 0) {
omy += OMAPY;
cur_ter = vert.ter(omx, omy, z);
see = vert.seen(omx, omy, z);
veh_here = vert.has_vehicle(g, omx, omy, z);
note_here = vert.has_note(omx, omy, z);
if (note_here) {
note_text = vert.note(omx, omy, z);
}
} else if (omy >= OMAPY) {
omy -= OMAPY;
cur_ter = vert.ter(omx, omy, z);
see = vert.seen(omx, omy, z);
veh_here = vert.has_vehicle(g, omx, omy, z);
note_here = vert.has_note(omx, omy, z);
if (note_here) {
note_text = vert.note(omx, omy, z);
}
} else {
debugmsg("No data loaded! omx: %d omy: %d", omx, omy);
}
// </Out of bounds replacement>
if (see) {
if (note_here && blink) {
ter_color = c_yellow;
if (note_text[1] == ':') {
ter_sym = note_text[0];
} else {
ter_sym = 'N';
}
} else if (omx == origx && omy == origy && blink) {
ter_color = g->u.color();
ter_sym = '@';
} else if (npc_here && blink) {
ter_color = c_pink;
ter_sym = '@';
} else if (veh_here && blink) {
ter_color = c_cyan;
ter_sym = 'c';
} else if (omx == target.x && omy == target.y && blink) {
ter_color = c_red;
ter_sym = '*';
} else {
if (otermap.find(cur_ter) == otermap.end()) {
debugmsg("Bad ter %s (%d, %d)", cur_ter.c_str(), omx, omy);
}
ter_color = otermap[cur_ter].color;
ter_sym = otermap[cur_ter].sym;
}
} else { // We haven't explored this tile yet
ter_color = c_dkgray;
ter_sym = '#';
}
if (j == 0 && i == 0) {
mvwputch_hi (w, om_map_height / 2, om_map_width / 2,
ter_color, ter_sym);
csee = see;
ccur_ter = cur_ter;
} else {
mvwputch(w, (om_map_height / 2) + j, (om_map_width / 2) + i,
ter_color, ter_sym);
}
}
}
if (target.x != -1 && target.y != -1 && blink &&
(target.x < cursx - om_map_height / 2 ||
target.x > cursx + om_map_height / 2 ||
target.y < cursy - om_map_width / 2 ||
target.y > cursy + om_map_width / 2 )) {
switch (direction_from(cursx, cursy, target.x, target.y)) {
case NORTH: mvwputch(w, 0, (om_map_width / 2), c_red, '^'); break;
case NORTHEAST: mvwputch(w, 0, om_map_width - 1, c_red, LINE_OOXX); break;
case EAST: mvwputch(w, (om_map_height / 2),
om_map_width - 1, c_red, '>'); break;
case SOUTHEAST: mvwputch(w, om_map_height,
om_map_width - 1, c_red, LINE_XOOX); break;
case SOUTH: mvwputch(w, om_map_height,
om_map_height / 2, c_red, 'v'); break;
case SOUTHWEST: mvwputch(w, om_map_height, 0, c_red, LINE_XXOO); break;
case WEST: mvwputch(w, om_map_height / 2, 0, c_red, '<'); break;
case NORTHWEST: mvwputch(w, 0, 0, c_red, LINE_OXXO); break;
}
}
if (has_note(cursx, cursy, z)) {
note_text = note(cursx, cursy, z);
if (note_text[1] == ':')
note_text = note_text.substr(2, note_text.size());
for (int i = 0; i < note_text.length(); i++)
mvwputch(w, 1, i, c_white, LINE_OXOX);
mvwputch(w, 1, note_text.length(), c_white, LINE_XOOX);
mvwputch(w, 0, note_text.length(), c_white, LINE_XOXO);
mvwprintz(w, 0, 0, c_yellow, note_text.c_str());
} else if (has_npc(g, cursx, cursy, z))
{
print_npcs(g, w, cursx, cursy, z);
} else if (has_vehicle(g, cursx, cursy, z))
{
print_vehicles(g, w, cursx, cursy, z);
}
cur_ter = ter(cursx, cursy, z);
// Draw the vertical line
for (int j = 0; j < om_map_height; j++)
mvwputch(w, j, om_map_width, c_white, LINE_XOXO);
// Clear the legend
for (int i = om_map_width + 1; i < om_map_width + 55; i++) {
for (int j = 0; j < om_map_height; j++)
mvwputch(w, j, i, c_black, ' ');
}
real_coords rc;
rc.fromomap( g->cur_om->pos().x, g->cur_om->pos().y, cursx, cursy );
if (csee) {
mvwputch(w, 1, om_map_width + 1, otermap[ccur_ter].color, otermap[ccur_ter].sym);
std::vector<std::string> name = foldstring(otermap[ccur_ter].name,25);
for (int i = 1; (i - 1) < name.size(); i++)
{
mvwprintz(w, i, om_map_width + 3, otermap[ccur_ter].color, "%s",
name[i-1].c_str());
}
} else
mvwprintz(w, 1, om_map_width + 1, c_dkgray, _("# Unexplored"));
if (target.x != -1 && target.y != -1) {
int distance = rl_dist(origx, origy, target.x, target.y);
mvwprintz(w, 3, om_map_width + 1, c_white, _("Distance to target: %d"), distance);
}
mvwprintz(w, 15, om_map_width + 1, c_magenta, _("Use movement keys to pan. "));
mvwprintz(w, 16, om_map_width + 1, c_magenta, (inp_ctxt->get_desc("CENTER") +
_(" - Center map on character")).c_str());
mvwprintz(w, 17, om_map_width + 1, c_magenta, (inp_ctxt->get_desc("SEARCH") +
_(" - Search ")).c_str());
mvwprintz(w, 18, om_map_width + 1, c_magenta, (inp_ctxt->get_desc("CREATE_NOTE") +
_(" - Add/Edit a note ")).c_str());
mvwprintz(w, 19, om_map_width + 1, c_magenta, (inp_ctxt->get_desc("DELETE_NOTE") +
_(" - Delete a note ")).c_str());
mvwprintz(w, 20, om_map_width + 1, c_magenta, (inp_ctxt->get_desc("LIST_NOTES") +
_(" - List notes ")).c_str());
fold_and_print(w, 21, om_map_width + 1, 27, c_magenta, (inp_ctxt->get_desc("QUIT") +
_(" - Return to game ")).c_str());
mvwprintz(w, getmaxy(w)-1, om_map_width + 1, c_red, string_format(_("LEVEL %i"),z).c_str());
mvwprintz( w, getmaxy(w) - 1, om_map_width + 1, c_red, "%s, %d'%d, %d'%d",
string_format(_("LEVEL %i"),z).c_str(), rc.abs_om.x, rc.om_pos.x,
rc.abs_om.y, rc.om_pos.y );
// Done with all drawing!
wrefresh(w);
}
//Start drawing the overmap on the screen using the (m)ap command.
point overmap::draw_overmap(game *g, int zlevel)
{
WINDOW* w_map = newwin(TERMY, TERMX, 0, 0);
WINDOW* w_search = newwin(13, 27, 3, TERMX-27);
timeout(BLINK_SPEED); // Enable blinking!
bool blink = true;
int cursx = (g->levx + int(MAPSIZE / 2)) / 2,
cursy = (g->levy + int(MAPSIZE / 2)) / 2;
int origx = cursx, origy = cursy, origz = zlevel;
signed char ch = 0;
point ret(-1, -1);
overmap hori, vert, diag; // Adjacent maps
// Configure input context for navigating the map.
input_context ictxt("OVERMAP");
ictxt.register_action("ANY_INPUT");
ictxt.register_directions();
ictxt.register_action("CONFIRM");
ictxt.register_action("LEVEL_UP");
ictxt.register_action("LEVEL_DOWN");
ictxt.register_action("HELP_KEYBINDINGS");
// Actions whose keys we want to display.
ictxt.register_action("CENTER");
ictxt.register_action("CREATE_NOTE");
ictxt.register_action("DELETE_NOTE");
ictxt.register_action("SEARCH");
ictxt.register_action("LIST_NOTES");
ictxt.register_action("QUIT");
std::string action;
do {
draw(w_map, g, zlevel, cursx, cursy, origx, origy, ch, blink, hori, vert, diag, &ictxt);
action = ictxt.handle_input();
timeout(BLINK_SPEED); // Enable blinking!
int dirx, diry;
if (action != "ANY_INPUT") {
blink = true; // If any input is detected, make the blinkies on
}
ictxt.get_direction(dirx, diry, action);
if (dirx != -2 && diry != -2) {
cursx += dirx;
cursy += diry;
} else if (action == "CENTER") {
cursx = origx;
cursy = origy;
zlevel = origz;
} else if (action == "LEVEL_DOWN" && zlevel > -OVERMAP_DEPTH) {
zlevel -= 1;
} else if (action == "LEVEL_UP" && zlevel < OVERMAP_HEIGHT) {
zlevel += 1;
}
else if (action == "CONFIRM")
ret = point(cursx, cursy);
else if (action == "QUIT")
ret = point(-1, -1);
else if (action == "CREATE_NOTE") {
timeout(-1);
add_note(cursx, cursy, zlevel, string_input_popup(_("Note (X:TEXT for custom symbol):"),
45, note(cursx, cursy, zlevel))); // 45 char max
timeout(BLINK_SPEED);
} else if(action == "DELETE_NOTE"){
timeout(-1);
if (has_note(cursx, cursy, zlevel)){
bool res = query_yn(_("Really delete note?"));
if (res == true)
delete_note(cursx, cursy, zlevel);
}
timeout(BLINK_SPEED);
} else if (action == "LIST_NOTES"){
timeout(-1);
point p = display_notes(g, zlevel);
if (p.x != -1){
cursx = p.x;
cursy = p.y;
}
timeout(BLINK_SPEED);
wrefresh(w_map);
} else if (action == "SEARCH") {
int tmpx = cursx, tmpy = cursy;
timeout(-1);
std::string term = string_input_popup(_("Search term:"));
timeout(BLINK_SPEED);
draw(w_map, g, zlevel, cursx, cursy, origx, origy, ch, blink, hori, vert, diag, &ictxt);
point found = find_note(cursx, cursy, zlevel, term);
if (found.x == -1) { // Didn't find a note
std::vector<point> terlist;
terlist = find_terrain(term, origx, origy, zlevel);
if (terlist.size() != 0){
int i = 0;
//Navigate through results
do {
//Draw search box
wborder(w_search, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX,
LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX );
mvwprintz(w_search, 1, 1, c_red, _("Find place:"));
mvwprintz(w_search, 2, 1, c_ltblue, " ");
mvwprintz(w_search, 2, 1, c_ltblue, "%s", term.c_str());
mvwprintz(w_search, 4, 1, c_white,
_("'<' '>' Cycle targets."));
mvwprintz(w_search, 10, 1, c_white, _("Enter/Spacebar to select."));
mvwprintz(w_search, 11, 1, c_white, _("q to return."));
ch = input();
if (ch == ERR)
blink = !blink;
else if (ch == '<') {
i++;
if(i > terlist.size() - 1)
i = 0;
} else if(ch == '>'){
i--;
if(i < 0)
i = terlist.size() - 1;
}
cursx = terlist[i].x;
cursy = terlist[i].y;
draw(w_map, g, zlevel, cursx, cursy, origx, origy, ch, blink, hori, vert, diag, &ictxt);
wrefresh(w_search);
timeout(BLINK_SPEED);
} while(ch != '\n' && ch != ' ' && ch != 'q');
//If q is hit, return to the last position
if(ch == 'q'){
cursx = tmpx;
cursy = tmpy;
}
ch = '.';
}
}
if (found.x != -1) {
cursx = found.x;
cursy = found.y;
}
}
else if (action == "ANY_INPUT") { // Hit timeout on input, so make characters blink
blink = !blink;
}
} while (action != "QUIT" && action != "CONFIRM");
timeout(-1);
werase(w_map);
wrefresh(w_map);
delwin(w_map);
werase(w_search);
wrefresh(w_search);
delwin(w_search);
erase();
g->refresh_all();
return ret;
}
void overmap::first_house(int &x, int &y)
{
std::vector<point> valid;
for (int i = 0; i < OMAPX; i++) {
for (int j = 0; j < OMAPY; j++) {
if (ter(i, j, 0) == "shelter") {
valid.push_back( point(i, j) );
}
}
}
if (valid.size() == 0) {
debugmsg("Couldn't find a shelter!");
x = 1;
y = 1;
return;
}
int index = rng(0, valid.size() - 1);
x = valid[index].x;
y = valid[index].y;
}
void overmap::process_mongroups()
{
for (int i = 0; i < zg.size(); i++) {
if (zg[i].dying) {
zg[i].population *= .8;
zg[i].radius *= .9;
}
}
}
void grow_forest_oter_id(oter_id & oid, bool swampy) {
if (swampy && ( oid == ot_field || oid == ot_forest ) ) {
oid = ot_forest_water;
} else if ( oid == ot_forest ) {
oid = ot_forest_thick;
} else if ( oid == ot_field ) {
oid = ot_forest;
}
}
void overmap::place_forest()
{
for (int i = 0; i < NUM_FOREST; i++) {
// forx and fory determine the epicenter of the forest
int forx = rng(0, OMAPX - 1);
int fory = rng(0, OMAPY - 1);
// fors determinds its basic size
int fors = rng(15, 40);
int outer_tries = 1000;
int inner_tries = 1000;
for (int j = 0; j < cities.size(); j++) {
inner_tries = 1000;
while (dist(forx,fory,cities[j].x,cities[j].y) - fors / 2 < cities[j].s ) {
// Set forx and fory far enough from cities
forx = rng(0, OMAPX - 1);
fory = rng(0, OMAPY - 1);
// Set fors to determine the size of the forest; usually won't overlap w/ cities
fors = rng(15, 40);
j = 0;
if( 0 == --inner_tries ) { break; }
}
if( 0 == --outer_tries || 0 == inner_tries ) {
break;
}
}
if( 0 == outer_tries || 0 == inner_tries ) {
break;
}
int swamps = SWAMPINESS; // How big the swamp may be...
int x = forx;
int y = fory;
// Depending on the size on the forest...
for (int j = 0; j < fors; j++) {
int swamp_chance = 0;
for (int k = -2; k <= 2; k++) {
for (int l = -2; l <= 2; l++) {
if (ter(x + k, y + l, 0) == "forest_water" ||
check_ot_type("river", x+k, y+l, 0)) {
swamp_chance += 5;
}
}
}
bool swampy = false;
if (swamps > 0 && swamp_chance > 0 && !one_in(swamp_chance) &&
(ter(x, y, 0) == "forest" || ter(x, y, 0) == "forest_thick" ||
ter(x, y, 0) == "field" || one_in(SWAMPCHANCE))) {
// ...and make a swamp.
ter(x, y, 0) = "forest_water";
swampy = true;
swamps--;
} else if (swamp_chance == 0) {
swamps = SWAMPINESS;
}
// Place or embiggen forest
for ( int mx = -1; mx < 2; mx++ ) {
for ( int my = -1; my < 2; my++ ) {
grow_forest_oter_id( ter(x+mx, y+my, 0), ( mx == 0 && my == 0 ? false : swampy ) );
}
}
// Random walk our forest
x += rng(-2, 2);
if (x < 0 ) { x = 0; }
if (x > OMAPX) { x = OMAPX; }
y += rng(-2, 2);
if (y < 0 ) { y = 0; }
if (y > OMAPY) { y = OMAPY; }
}
}
}
void overmap::place_river(point pa, point pb)
{
int x = pa.x, y = pa.y;
do {
x += rng(-1, 1);
y += rng(-1, 1);
if (x < 0) x = 0;
if (x > OMAPX - 1) x = OMAPX - 2;
if (y < 0) y = 0;
if (y > OMAPY - 1) y = OMAPY - 1;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (y+i >= 0 && y+i < OMAPY && x+j >= 0 && x+j < OMAPX)
ter(x+j, y+i, 0) = "river_center";
}
}
if (pb.x > x && (rng(0, int(OMAPX * 1.2) - 1) < pb.x - x ||
(rng(0, int(OMAPX * .2) - 1) > pb.x - x &&
rng(0, int(OMAPY * .2) - 1) > abs(pb.y - y))))
x++;
if (pb.x < x && (rng(0, int(OMAPX * 1.2) - 1) < x - pb.x ||
(rng(0, int(OMAPX * .2) - 1) > x - pb.x &&
rng(0, int(OMAPY * .2) - 1) > abs(pb.y - y))))
x--;
if (pb.y > y && (rng(0, int(OMAPY * 1.2) - 1) < pb.y - y ||
(rng(0, int(OMAPY * .2) - 1) > pb.y - y &&
rng(0, int(OMAPX * .2) - 1) > abs(x - pb.x))))
y++;
if (pb.y < y && (rng(0, int(OMAPY * 1.2) - 1) < y - pb.y ||
(rng(0, int(OMAPY * .2) - 1) > y - pb.y &&
rng(0, int(OMAPX * .2) - 1) > abs(x - pb.x))))
y--;
x += rng(-1, 1);
y += rng(-1, 1);
if (x < 0) x = 0;
if (x > OMAPX - 1) x = OMAPX - 2;
if (y < 0) y = 0;
if (y > OMAPY - 1) y = OMAPY - 1;
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
// We don't want our riverbanks touching the edge of the map for many reasons
if ((y+i >= 1 && y+i < OMAPY - 1 && x+j >= 1 && x+j < OMAPX - 1) ||
// UNLESS, of course, that's where the river is headed!
(abs(pb.y - (y+i)) < 4 && abs(pb.x - (x+j)) < 4))
ter(x+j, y+i, 0) = "river_center";
}
}
} while (pb.x != x || pb.y != y);
}
/*: the root is overmap::place_cities()
20:50 <kevingranade>: which is at overmap.cpp:1355 or so
20:51 <kevingranade>: the key is cs = rng(4, 17), setting the "size" of the city
20:51 <kevingranade>: which is roughly it's radius in overmap tiles
20:52 <kevingranade>: then later overmap::place_mongroups() is called
20:52 <kevingranade>: which creates a mongroup with radius city_size * 2.5 and population city_size * 80
20:53 <kevingranade>: tadaa
spawns happen at... <cue Clue music>
20:56 <kevingranade>: game:pawn_mon() in game.cpp:7380*/
void overmap::place_cities()
{
int NUM_CITIES = dice(4, 4);
int start_dir;
int op_city_size = int(ACTIVE_WORLD_OPTIONS["CITY_SIZE"]);
int city_min = op_city_size - 1;
int city_max = op_city_size + 1;
// Limit number of cities based on how big they are.
NUM_CITIES = std::min(NUM_CITIES, int(256 / op_city_size * op_city_size));
while (cities.size() < NUM_CITIES) {
int cx = rng(12, OMAPX - 12);
int cy = rng(12, OMAPY - 12);
int size = dice(city_min, city_max) ;
if (ter(cx, cy, 0) == "field") {
ter(cx, cy, 0) = "road_nesw";
city tmp; tmp.x = cx; tmp.y = cy; tmp.s = size;
cities.push_back(tmp);
start_dir = rng(0, 3);
for (int j = 0; j < 4; j++)
make_road(cx, cy, size, (start_dir + j) % 4, tmp);
}
}
}
void overmap::put_buildings(int x, int y, int dir, city town)
{
int ychange = dir % 2, xchange = (dir + 1) % 2;
for (int i = -1; i <= 1; i += 2) {
if ((ter(x+i*xchange, y+i*ychange, 0) == "field") && !one_in(STREETCHANCE)) {
if (rng(0, 99) > 80 * dist(x,y,town.x,town.y) / town.s)
ter(x+i*xchange, y+i*ychange, 0) = shop(((dir%2)-i)%4);
else {
if (rng(0, 99) > 130 * dist(x, y, town.x, town.y) / town.s)
ter(x+i*xchange, y+i*ychange, 0) = (one_in(5)?"pool":"park");
else
ter(x+i*xchange, y+i*ychange, 0) = house(((dir%2)-i)%4);
}
}
}
}
void overmap::make_road(int cx, int cy, int cs, int dir, city town)
{
int x = cx, y = cy;
int c = cs, croad = cs;
switch (dir) {
case 0:
while (c > 0 && y > 0 && (ter(x, y-1, 0) == "field" || c == cs)) {
y--;
c--;
ter(x, y, 0) = "road_ns";
for (int i = -1; i <= 0; i++) {
for (int j = -1; j <= 1; j++) {
if (abs(j) != abs(i) && (ter(x+j, y+i, 0) == "road_ew" ||
ter(x+j, y+i, 0) == "road_ns")) {
ter(x, y, 0) = "road_null";
c = -1;
}
}
}
put_buildings(x, y, dir, town);
if (c < croad - 1 && c >= 2 && ter(x - 1, y, 0) == "field" &&
ter(x + 1, y, 0) == "field") {
croad = c;
make_road(x, y, cs - rng(1, 3), 1, town);
make_road(x, y, cs - rng(1, 3), 3, town);
}
}
if (is_road(x, y-2, 0)) {
ter(x, y-1, 0) = "road_ns";
}
break;
case 1:
while (c > 0 && x < OMAPX-1 && (ter(x+1, y, 0) == "field" || c == cs)) {
x++;
c--;
ter(x, y, 0) = "road_ew";
for (int i = -1; i <= 1; i++) {
for (int j = 0; j <= 1; j++) {
if (abs(j) != abs(i) && (ter(x+j, y+i, 0) == "road_ew" ||
ter(x+j, y+i, 0) == "road_ns")) {
ter(x, y, 0) = "road_null";
c = -1;
}
}
}
put_buildings(x, y, dir, town);
if (c < croad-2 && c >= 3 && ter(x, y-1, 0) == "field" &&
ter(x, y+1, 0) == "field") {
croad = c;
make_road(x, y, cs - rng(1, 3), 0, town);
make_road(x, y, cs - rng(1, 3), 2, town);
}
}
if (is_road(x-2, y, 0)) {
ter(x-1, y, 0) = "road_ew";
}
break;
case 2:
while (c > 0 && y < OMAPY-1 && (ter(x, y+1, 0) == "field" || c == cs)) {
y++;
c--;
ter(x, y, 0) = "road_ns";
for (int i = 0; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (abs(j) != abs(i) && (ter(x+j, y+i, 0) == "road_ew" ||
ter(x+j, y+i, 0) == "road_ns")) {
ter(x, y, 0) = "road_null";
c = -1;
}
}
}
put_buildings(x, y, dir, town);
if (c < croad-2 && ter(x-1, y, 0) == "field" && ter(x+1, y, 0) == "field") {
croad = c;
make_road(x, y, cs - rng(1, 3), 1, town);
make_road(x, y, cs - rng(1, 3), 3, town);
}
}
if (is_road(x, y+2, 0)) {
ter(x, y+1, 0) = "road_ns";
}
break;
case 3:
while (c > 0 && x > 0 && (ter(x-1, y, 0) == "field" || c == cs)) {
x--;
c--;
ter(x, y, 0) = "road_ew";
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 0; j++) {
if (abs(j) != abs(i) && (ter(x+j, y+i, 0) == "road_ew" ||
ter(x+j, y+i, 0) == "road_ns")) {
ter(x, y, 0) = "road_null";
c = -1;
}
}
}
put_buildings(x, y, dir, town);
if (c < croad - 2 && c >= 3 && ter(x, y-1, 0) == "field" &&
ter(x, y+1, 0) == "field") {
croad = c;
make_road(x, y, cs - rng(1, 3), 0, town);
make_road(x, y, cs - rng(1, 3), 2, town);
}
}
if (is_road(x+2, y, 0)) {
ter(x+1, y, 0) = "road_ew";
}
break;
}
cs -= rng(1, 3);
if (cs >= 2 && c == 0) {
int dir2;
if (dir % 2 == 0) {
dir2 = rng(0, 1) * 2 + 1;
} else {
dir2 = rng(0, 1) * 2;
}
make_road(x, y, cs, dir2, town);
if (one_in(5)) {
make_road(x, y, cs, (dir2 + 2) % 4, town);
}
}
}
bool overmap::build_lab(int x, int y, int z, int s)
{
std::vector<point> generated_lab;
ter(x, y, z) = "lab";
for (int n = 0; n <= 1; n++) { // Do it in two passes to allow diagonals
for (int i = 1; i <= s; i++) {
for (int lx = x - i; lx <= x + i; lx++) {
for (int ly = y - i; ly <= y + i; ly++) {
if ((ter(lx - 1, ly, z) == "lab" ||
ter(lx + 1, ly, z) == "lab" ||
ter(lx, ly - 1, z) == "lab" ||
ter(lx, ly + 1, z) == "lab") && one_in(i)) {
ter(lx, ly, z) = "lab";
generated_lab.push_back(point(lx,ly));
}
}
}
}
}
bool generate_stairs = true;
for (std::vector<point>::iterator it=generated_lab.begin();
it != generated_lab.end(); it++) {
if (ter(it->x, it->y, z+1) == "lab_stairs") {
generate_stairs = false;
}
}
if (generate_stairs && generated_lab.size() > 0) {
int v = rng(0,generated_lab.size()-1);
point p = generated_lab[v];
ter(p.x, p.y, z+1) = "lab_stairs";
}
ter(x, y, z) = "lab_core";
int numstairs = 0;
if (s > 0) { // Build stairs going down
while (!one_in(6)) {
int stairx, stairy;
int tries = 0;
do {
stairx = rng(x - s, x + s);
stairy = rng(y - s, y + s);
tries++;
} while (ter(stairx, stairy, z) != "lab" && tries < 15);
if (tries < 15) {
ter(stairx, stairy, z) = "lab_stairs";
numstairs++;
}
}
}
if (numstairs == 0) { // This is the bottom of the lab; We need a finale
int finalex, finaley;
int tries = 0;
do {
finalex = rng(x - s, x + s);
finaley = rng(y - s, y + s);
tries++;
} while (tries < 15 && ter(finalex, finaley, z) != "lab"
&& ter(finalex, finaley, z) != "lab_core");
ter(finalex, finaley, z) = "lab_finale";
}
zg.push_back(mongroup("GROUP_LAB", (x * 2), (y * 2), z, s, 400));
return numstairs > 0;
}
bool overmap::build_ice_lab(int x, int y, int z, int s)
{
std::vector<point> generated_ice_lab;
ter(x, y, z) = "ice_lab";
for (int n = 0; n <= 1; n++) { // Do it in two passes to allow diagonals
for (int i = 1; i <= s; i++) {
for (int lx = x - i; lx <= x + i; lx++) {
for (int ly = y - i; ly <= y + i; ly++) {
if ((ter(lx - 1, ly, z) == "ice_lab" ||
ter(lx + 1, ly, z) == "ice_lab" ||
ter(lx, ly - 1, z) == "ice_lab" ||
ter(lx, ly + 1, z) == "ice_lab") && one_in(i)) {
ter(lx, ly, z) = "ice_lab";
generated_ice_lab.push_back(point(lx,ly));
}
}
}
}
}
bool generate_stairs = true;
for (std::vector<point>::iterator it = generated_ice_lab.begin();
it != generated_ice_lab.end(); ++it) {
if (ter(it->x, it->y, z + 1) == "ice_lab_stairs") {
generate_stairs = false;
}
}
if (generate_stairs && generated_ice_lab.size() > 0) {
int v = rng(0,generated_ice_lab.size() - 1);
point p = generated_ice_lab[v];
ter(p.x, p.y, z + 1) = "ice_lab_stairs";
}
ter(x, y, z) = "ice_lab_core";
int numstairs = 0;
if (s > 0) { // Build stairs going down
while (!one_in(6)) {
int stairx, stairy;
int tries = 0;
do {
stairx = rng(x - s, x + s);
stairy = rng(y - s, y + s);
tries++;
} while (ter(stairx, stairy, z) != "ice_lab" && tries < 15);
if (tries < 15) {
ter(stairx, stairy, z) = "ice_lab_stairs";
numstairs++;
}
}
}
if (numstairs == 0) { // This is the bottom of the ice_lab; We need a finale
int finalex, finaley;
int tries = 0;
do {
finalex = rng(x - s, x + s);
finaley = rng(y - s, y + s);
tries++;
} while (tries < 15 && ter(finalex, finaley, z) != "ice_lab"
&& ter(finalex, finaley, z) != "ice_lab_core");
ter(finalex, finaley, z) = "ice_lab_finale";
}
zg.push_back(mongroup("GROUP_ICE_LAP", (x * 2), (y * 2), z, s, 400));
return numstairs > 0;
}
void overmap::build_anthill(int x, int y, int z, int s)
{
build_tunnel(x, y, z, s - rng(0, 3), 0);
build_tunnel(x, y, z, s - rng(0, 3), 1);
build_tunnel(x, y, z, s - rng(0, 3), 2);
build_tunnel(x, y, z, s - rng(0, 3), 3);
std::vector<point> queenpoints;
for (int i = x - s; i <= x + s; i++) {
for (int j = y - s; j <= y + s; j++) {
if (check_ot_type("ants", i, j, z)) {
queenpoints.push_back(point(i, j));
}
}
}
int index = rng(0, queenpoints.size() - 1);
ter(queenpoints[index].x, queenpoints[index].y, z) = "ants_queen";
}
void overmap::build_tunnel(int x, int y, int z, int s, int dir)
{
if (s <= 0) {
return;
}
if (!check_ot_type("ants", x, y, z)) {
ter(x, y, z) = "ants_ns";
}
point next;
switch (dir) {
case 0: next = point(x , y - 1);
case 1: next = point(x + 1, y );
case 2: next = point(x , y + 1);
case 3: next = point(x - 1, y );
}
if (s == 1) {
next = point(-1, -1);
}
std::vector<point> valid;
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y + 1; j++) {
if (!check_ot_type("ants", i, j, z) && abs(i - x) + abs(j - y) == 1) {
valid.push_back(point(i, j));
}
}
}
for (int i = 0; i < valid.size(); i++) {
if (valid[i].x != next.x || valid[i].y != next.y) {
if (one_in(s * 2)) {
if (one_in(2)) {
ter(valid[i].x, valid[i].y, z) = "ants_food";
} else {
ter(valid[i].x, valid[i].y, z) = "ants_larvae";
}
} else if (one_in(5)) {
int dir2;
if (valid[i].y == y - 1) { dir2 = 0; }
if (valid[i].x == x + 1) { dir2 = 1; }
if (valid[i].y == y + 1) { dir2 = 2; }
if (valid[i].x == x - 1) { dir2 = 3; }
build_tunnel(valid[i].x, valid[i].y, z, s - rng(0, 3), dir2);
}
}
}
build_tunnel(next.x, next.y, z, s - 1, dir);
}
bool overmap::build_slimepit(int x, int y, int z, int s)
{
bool requires_sub = false;
for (int n = 1; n <= s; n++)
{
for (int i = x - n; i <= x + n; i++)
{
for (int j = y - n; j <= y + n; j++)
{
if (rng(1, s * 2) >= n)
{
if (one_in(8) && z > -OVERMAP_DEPTH)
{
ter(i, j, z) = "slimepit_down";
requires_sub = true;
} else {
ter(i, j, z) = "slimepit";
}
}
}
}
}
return requires_sub;
}
void overmap::build_mine(int x, int y, int z, int s)
{
bool finale = (s <= rng(1, 3));
int built = 0;
if (s < 2) {
s = 2;
}
while (built < s) {
ter(x, y, z) = "mine";
std::vector<point> next;
for (int i = -1; i <= 1; i += 2) {
if (ter(x, y + i, z) == "rock") {
next.push_back( point(x, y + i) );
}
if (ter(x + i, y, z) == "rock") {
next.push_back( point(x + i, y) );
}
}
if (next.empty()) { // Dead end! Go down!
ter(x, y, z) = (finale ? "mine_finale" : "mine_down");
return;
}
point p = next[ rng(0, next.size() - 1) ];
x = p.x;
y = p.y;
built++;
}
ter(x, y, z) = (finale ? "mine_finale" : "mine_down");
}
void overmap::place_rifts(int const z)
{
int num_rifts = rng(0, 2) * rng(0, 2);
std::vector<point> riftline;
if (!one_in(4))
num_rifts++;
for (int n = 0; n < num_rifts; n++) {
int x = rng(MAX_RIFT_SIZE, OMAPX - MAX_RIFT_SIZE);
int y = rng(MAX_RIFT_SIZE, OMAPY - MAX_RIFT_SIZE);
int xdist = rng(MIN_RIFT_SIZE, MAX_RIFT_SIZE),
ydist = rng(MIN_RIFT_SIZE, MAX_RIFT_SIZE);
// We use rng(0, 10) as the t-value for this Bresenham Line, because by
// repeating this twice, we can get a thick line, and a more interesting rift.
for (int o = 0; o < 3; o++) {
if (xdist > ydist)
riftline = line_to(x - xdist, y - ydist+o, x + xdist, y + ydist, rng(0,10));
else
riftline = line_to(x - xdist+o, y - ydist, x + xdist, y + ydist, rng(0,10));
for (int i = 0; i < riftline.size(); i++) {
if (i == riftline.size() / 2 && !one_in(3))
ter(riftline[i].x, riftline[i].y, z) = "hellmouth";
else
ter(riftline[i].x, riftline[i].y, z) = "rift";
}
}
}
}
void overmap::make_hiway(int x1, int y1, int x2, int y2, int z, const std::string &base)
{
if (x1 == x2 && y1 == y2) {
return;
}
std::priority_queue<node> nodes[2];
bool closed[OMAPX][OMAPY] = {{false}};
int open[OMAPX][OMAPY] = {{0}};
int dirs[OMAPX][OMAPY] = {{0}};
int dx[4]={1, 0, -1, 0};
int dy[4]={0, 1, 0, -1};
int i = 0;
int disp = (base == "road") ? 5 : 2;
nodes[i].push(node(x1, y1, 5, 1000));
open[x1][y1] = 1000;
// use A* to find the shortest path from (x1,y1) to (x2,y2)
while (!nodes[i].empty()) {
// get the best-looking node
node mn = nodes[i].top();
nodes[i].pop();
// make sure it's in bounds
if (mn.x >= OMAPX || mn.x < 0 || mn.y >= OMAPY || mn.y < 0) {
continue;
}
// mark it visited
closed[mn.x][mn.y] = true;
// if we've reached the end, draw the path and return
if (mn.x == x2 && mn.y == y2) {
int x = mn.x;
int y = mn.y;
while (x != x1 || y != y1) {
int d = dirs[x][y];
x += dx[d];
y += dy[d];
if (road_allowed(ter(x, y, z))) {
if (is_river(ter(x, y, z))) {
if (d == 1 || d == 3) {
ter(x, y, z) = "bridge_ns";
} else {
ter(x, y, z) = "bridge_ew";
}
} else {
ter(x, y, z) = base + "_nesw";
}
}
}
return;
}
// otherwise, expand to
for(int d = 0; d < 4; d++) {
int x = mn.x + dx[d];
int y = mn.y + dy[d];
// don't allow:
// * out of bounds
// * already traversed tiles
// * tiles that don't allow roads to cross them (e.g. buildings)
// * corners on rivers
if (x < 1 || x > OMAPX - 2 || y < 1 || y > OMAPY - 2 ||
closed[x][y] || !road_allowed(ter(x, y, z)) ||
(is_river(ter(mn.x, mn.y, z)) && mn.d != d) ||
(is_river(ter(x, y, z)) && mn.d != d) ) {
continue;
}
node cn = node(x, y, d, 0);
// distance to target
cn.p += ((abs(x2 - x) + abs(y2 - y)) / disp);
// prefer existing roads.
cn.p += check_ot_type(base, x, y, z) ? 0 : 3;
// and flat land over bridges
cn.p += !is_river(ter(x, y, z)) ? 0 : 2;
// try not to turn too much
//cn.p += (mn.d == d) ? 0 : 1;
// record direction to shortest path
if (open[x][y] == 0) {
dirs[x][y] = (d + 2) % 4;
open[x][y] = cn.p;
nodes[i].push(cn);
} else if (open[x][y] > cn.p) {
dirs[x][y] = (d + 2) % 4;
open[x][y] = cn.p;
// wizardry
while (nodes[i].top().x != x || nodes[i].top().y != y) {
nodes[1 - i].push(nodes[i].top());
nodes[i].pop();
}
nodes[i].pop();
if (nodes[i].size() > nodes[1-i].size()) {
i = 1 - i;
}
while (!nodes[i].empty()) {
nodes[1 - i].push(nodes[i].top());
nodes[i].pop();
}
i = 1 - i;
nodes[i].push(cn);
} else {
// a shorter path has already been found
}
}
}
}
void overmap::building_on_hiway(int x, int y, int dir)
{
int xdif = dir * (1 - 2 * rng(0,1));
int ydif = (1 - dir) * (1 - 2 * rng(0,1));
int rot = 0;
if (ydif == 1) {
rot = 0;
} else if (xdif == -1) {
rot = 1;
} else if (ydif == -1) {
rot = 2;
} else if (xdif == 1) {
rot = 3;
}
switch (rng(1, 4)) {
case 1:
if (!is_river(ter(x + xdif, y + ydif, 0))) {
ter(x + xdif, y + ydif, 0) = "lab_stairs";
}
break;
case 2:
if (!is_river(ter(x + xdif, y + ydif, 0))) {
ter(x + xdif, y + ydif, 0) = "ice_lab_stairs";
}
break;
case 3:
if (!is_river(ter(x + xdif, y + ydif, 0))) {
ter(x + xdif, y + ydif, 0) = house(rot);
}
break;
case 4:
if (!is_river(ter(x + xdif, y + ydif, 0))) {
ter(x + xdif, y + ydif, 0) = "radio_tower";
}
break;
}
}
void overmap::place_hiways(std::vector<city> cities, int z, const std::string &base)
{
if (cities.size() == 1) {
return;
}
city best;
for (int i = 0; i < cities.size(); i++) {
int closest = -1;
for (int j = i + 1; j < cities.size(); j++) {
int distance = (int)dist(cities[i].x, cities[i].y, cities[j].x, cities[j].y);
if (distance < closest || closest < 0) {
closest = distance;
best = cities[j];
}
}
if( closest > 0 ) {
make_hiway(cities[i].x, cities[i].y, best.x, best.y, z, base);
}
}
}
// Polish does both good_roads and good_rivers (and any future polishing) in
// a single loop; much more efficient
void overmap::polish(const int z, const std::string &terrain_type)
{
const bool check_all = (terrain_type == "all");
// Main loop--checks roads and rivers that aren't on the borders of the map
for (int x = 0; x < OMAPX; x++) {
for (int y = 0; y < OMAPY; y++) {
if (check_all || check_ot_type(terrain_type, x, y, z)) {
if (check_ot_type("road", x, y, z)) {
good_road("road", x, y, z);
} else if (check_ot_type("bridge", x, y, z) &&
check_ot_type("bridge", x - 1, y, z) &&
check_ot_type("bridge", x + 1, y, z) &&
check_ot_type("bridge", x, y - 1, z) &&
check_ot_type("bridge", x, y + 1, z)) {
ter(x, y, z) = "road_nesw";
} else if (check_ot_type("subway", x, y, z)) {
good_road("subway", x, y, z);
} else if (check_ot_type("sewer", x, y, z)) {
good_road("sewer", x, y, z);
} else if (check_ot_type("ants", x, y, z)) {
good_road("ants", x, y, z);
} else if (check_ot_type("river", x, y, z)) {
good_river(x, y, z);
// Sometimes a bridge will start at the edge of a river,
// and this looks ugly.
// So, fix it by making that square normal road;
// bit of a kludge but it works.
} else if (ter(x, y, z) == "bridge_ns" &&
(!is_river(ter(x - 1, y, z)) ||
!is_river(ter(x + 1, y, z)))) {
ter(x, y, z) = "road_ns";
} else if (ter(x, y, z) == "bridge_ew" &&
(!is_river(ter(x, y - 1, z)) ||
!is_river(ter(x, y + 1, z)))) {
ter(x, y, z) = "road_ew";
}
}
}
}
// Fixes stretches of parallel roads--turns them into two-lane highways
// Note that this fixes 2x2 areas...
// a "tail" of 1x2 parallel roads may be left.
// This can actually be a good thing; it ensures nice connections
// Also, this leaves, say, 3x3 areas of road.
// TODO: fix this? courtyards etc?
for (int y = 0; y < OMAPY - 1; y++) {
for (int x = 0; x < OMAPX - 1; x++) {
if (check_ot_type(terrain_type, x, y, z)) {
if (ter(x, y, z) == "road_nes"
&& ter(x+1, y, z) == "road_nsw"
&& ter(x, y+1, z) == "road_nes"
&& ter(x+1, y+1, z) == "road_nsw") {
ter(x, y, z) = "hiway_ns";
ter(x+1, y, z) = "hiway_ns";
ter(x, y+1, z) = "hiway_ns";
ter(x+1, y+1, z) = "hiway_ns";
} else if (ter(x, y, z) == "road_esw"
&& ter(x+1, y, z) == "road_esw"
&& ter(x, y+1, z) == "road_new"
&& ter(x+1, y+1, z) == "road_new") {
ter(x, y, z) = "hiway_ew";
ter(x+1, y, z) = "hiway_ew";
ter(x, y+1, z) = "hiway_ew";
ter(x+1, y+1, z) = "hiway_ew";
}
}
}
}
}
bool overmap::check_ot_type(const std::string &otype, int x, int y, int z)
{
const oter_id oter = ter(x, y, z);
return is_ot_type(otype, oter);
}
bool overmap::is_road(int x, int y, int z)
{
if (x < 0 || x >= OMAPX || y < 0 || y >= OMAPY) {
for (int i = 0; i < roads_out.size(); i++) {
if (abs(roads_out[i].x - x) + abs(roads_out[i].y - y) <= 1) {
return true;
}
}
}
return ter(x, y, z).t().is_road;
//oter_t(ter(x, y, z)).is_road;
}
bool overmap::is_road_or_highway(int x, int y, int z)
{
if (is_road(x,y,z) || check_ot_type("hiway",x,y,z)) {
return true;
}
return false;
}
void overmap::good_road(const std::string &base, int x, int y, int z)
{
if (check_ot_type(base, x, y-1, z)) {
if (check_ot_type(base, x+1, y, z)) {
if (check_ot_type(base, x, y+1, z)) {
if (check_ot_type(base, x-1, y, z)) {
ter(x, y, z) = base + "_nesw";
} else {
ter(x, y, z) = base + "_nes";
}
} else {
if (check_ot_type(base, x-1, y, z)) {
ter(x, y, z) = base + "_new";
} else {
ter(x, y, z) = base + "_ne";
}
}
} else {
if (check_ot_type(base, x, y+1, z)) {
if (check_ot_type(base, x-1, y, z)) {
ter(x, y, z) = base + "_nsw";
} else {
ter(x, y, z) = base + "_ns";
}
} else {
if (check_ot_type(base, x-1, y, z)) {
ter(x, y, z) = base + "_wn";
} else {
ter(x, y, z) = base + "_ns";
}
}
}
} else {
if (check_ot_type(base, x+1, y, z)) {
if (check_ot_type(base, x, y+1, z)) {
if (check_ot_type(base, x-1, y, z)) {
ter(x, y, z) = base + "_esw";
} else {
ter(x, y, z) = base + "_es";
}
} else {
ter(x, y, z) = base + "_ew";
}
} else {
if (check_ot_type(base, x, y+1, z)) {
if (check_ot_type(base, x-1, y, z)) {
ter(x, y, z) = base + "_sw";
} else {
ter(x, y, z) = base + "_ns";
}
} else {
if (check_ot_type(base, x-1, y, z)) {
ter(x, y, z) = base + "_ew";
} else {
// No adjoining roads/etc.
// Happens occasionally, esp. with sewers.
ter(x, y, z) = base + "_nesw";
}
}
}
}
if (ter(x, y, z) == "road_nesw" && one_in(4)) {
ter(x, y, z) = "road_nesw_manhole";
}
}
void overmap::good_river(int x, int y, int z)
{
if (is_river(ter(x - 1, y, z))) {
if (is_river(ter(x, y - 1, z))) {
if (is_river(ter(x, y + 1, z))) {
if (is_river(ter(x + 1, y, z))) {
// River on N, S, E, W;
// but we might need to take a "bite" out of the corner
if (!is_river(ter(x - 1, y - 1, z))) {
ter(x, y, z) = "river_c_not_nw";
} else if (!is_river(ter(x + 1, y - 1, z))) {
ter(x, y, z) = "river_c_not_ne";
} else if (!is_river(ter(x - 1, y + 1, z))) {
ter(x, y, z) = "river_c_not_sw";
} else if (!is_river(ter(x + 1, y + 1, z))) {
ter(x, y, z) = "river_c_not_se";
} else {
ter(x, y, z) = "river_center";
}
} else {
ter(x, y, z) = "river_east";
}
} else {
if (is_river(ter(x + 1, y, z))) {
ter(x, y, z) = "river_south";
} else {
ter(x, y, z) = "river_se";
}
}
} else {
if (is_river(ter(x, y + 1, z))) {
if (is_river(ter(x + 1, y, z))) {
ter(x, y, z) = "river_north";
} else {
ter(x, y, z) = "river_ne";
}
} else {
if (is_river(ter(x + 1, y, z))) { // Means it's swampy
ter(x, y, z) = "forest_water";
}
}
}
} else {
if (is_river(ter(x, y - 1, z))) {
if (is_river(ter(x, y + 1, z))) {
if (is_river(ter(x + 1, y, z))) {
ter(x, y, z) = "river_west";
} else { // Should never happen
ter(x, y, z) = "forest_water";
}
} else {
if (is_river(ter(x + 1, y, z))) {
ter(x, y, z) = "river_sw";
} else { // Should never happen
ter(x, y, z) = "forest_water";
}
}
} else {
if (is_river(ter(x, y + 1, z))) {
if (is_river(ter(x + 1, y, z))) {
ter(x, y, z) = "river_nw";
} else { // Should never happen
ter(x, y, z) = "forest_water";
}
} else { // Should never happen
ter(x, y, z) = "forest_water";
}
}
}
}
void overmap::place_specials()
{
int placed[NUM_OMSPECS];
for (int i = 0; i < NUM_OMSPECS; i++)
placed[i] = 0;
std::vector<point> sectors;
for (int x = 0; x < OMAPX; x += OMSPEC_FREQ) {
for (int y = 0; y < OMAPY; y += OMSPEC_FREQ)
sectors.push_back(point(x, y));
}
while (!sectors.empty()) {
int sector_pick = rng(0, sectors.size() - 1);
int x = sectors[sector_pick].x, y = sectors[sector_pick].y;
sectors.erase(sectors.begin() + sector_pick);
std::vector<omspec_id> valid;
int tries = 0;
tripoint p;
do {
p = tripoint(rng(x, x + OMSPEC_FREQ - 1), rng(y, y + OMSPEC_FREQ - 1), 0);
if (p.x >= OMAPX - 1)
p.x = OMAPX - 2;
if (p.y >= OMAPY - 1)
p.y = OMAPY - 2;
if (p.x == 0)
p.x = 1;
if (p.y == 0)
p.y = 1;
for (int i = 0; i < NUM_OMSPECS; i++) {
omspec_place place;
overmap_special special = overmap_specials[i];
int min = special.min_dist_from_city, max = special.max_dist_from_city;
point pt(p.x, p.y);
// Skip non-classic specials if we're in classic mode
if (ACTIVE_WORLD_OPTIONS["CLASSIC_ZOMBIES"] && !(special.flags & mfb(OMS_FLAG_CLASSIC))) continue;
if ((placed[ omspec_id(i) ] < special.max_appearances || special.max_appearances <= 0) &&
(min == -1 || dist_from_city(pt) >= min) &&
(max == -1 || dist_from_city(pt) <= max) &&
(place.*special.able)(this, special.flags, p))
valid.push_back( omspec_id(i) );
}
tries++;
} while (valid.empty() && tries < 20); // Done looking for valid spot
if (!valid.empty()) { // We found a valid spot!
// Place the MUST HAVE ones first, to try and guarantee that they appear
std::vector<omspec_id> must_place;
for (int i = 0; i < valid.size(); i++) {
if (placed[ valid[i] ] < overmap_specials[ valid[i] ].min_appearances)
must_place.push_back(valid[i]);
}
if (must_place.empty()) {
int selection = rng(0, valid.size() - 1);
overmap_special special = overmap_specials[ valid[selection] ];
placed[ valid[selection] ]++;
place_special(special, p);
} else {
int selection = rng(0, must_place.size() - 1);
overmap_special special = overmap_specials[ must_place[selection] ];
placed[ must_place[selection] ]++;
place_special(special, p);
}
} // Done with <Found a valid spot>
} // Done picking sectors...
}
// find the id for a specified rotation of a rotatable oter_t
oter_id overmap::rotate(const oter_id &oter, int dir)
{
const oter_t & otert = oter;
if (! otert.rotates ) {
debugmsg("%s does not rotate.", oter.c_str());
return oter;
}
if (dir < 0) {
dir += 4;
} else if (dir > 3) {
debugmsg("Bad rotation for %s: %d.", oter.c_str(), dir);
return oter;
}
return otert.directional_peers[dir];
}
void overmap::place_special(overmap_special special, tripoint p)
{
bool rotated = false;
int city = -1;
// First, place terrain...
ter(p.x, p.y, p.z) = special.ter;
// Next, obey any special effects the flags might have
if (special.flags & mfb(OMS_FLAG_ROTATE_ROAD)) {
if (is_road_or_highway(p.x, p.y - 1, p.z)) {
ter(p.x, p.y, p.z) = rotate(special.ter, 0);
rotated = true;
} else if (is_road_or_highway(p.x + 1, p.y, p.z)) {
ter(p.x, p.y, p.z) = rotate(special.ter, 1);
rotated = true;
} else if (is_road_or_highway(p.x, p.y + 1, p.z)) {
ter(p.x, p.y, p.z) = rotate(special.ter, 2);
rotated = true;
} else if (is_road_or_highway(p.x - 1, p.y, p.z)) {
ter(p.x, p.y, p.z) = rotate(special.ter, 3);
rotated = true;
}
}
if (!rotated && special.flags & mfb(OMS_FLAG_ROTATE_RANDOM)) {
ter(p.x, p.y, p.z) = rotate(special.ter, rng(0, 3));
}
if (special.flags & mfb(OMS_FLAG_ROAD)) {
int closest = -1, distance = 999;
for (int i = 0; i < cities.size(); i++) {
int dist = rl_dist(p.x, p.y, cities[i].x, cities[i].y);
if (dist < distance) {
closest = i;
distance = dist;
}
}
if (special.flags & (mfb(OMS_FLAG_2X2_SECOND) | mfb(OMS_FLAG_3X3_FIXED))) {
city = closest;
} else {
make_hiway(p.x, p.y, cities[closest].x, cities[closest].y, p.z, "road");
}
}
if (special.flags & mfb(OMS_FLAG_3X3)) {
for (int x = p.x; x < p.x + 3; x++) {
for (int y = p.y; y < p.y + 3; y++) {
if (x == p.x && y == p.y) {
y++; // Already handled
}
ter(x, y, p.z) = special.ter;
}
}
}
if (special.flags & mfb(OMS_FLAG_3X3_SECOND)) {
size_t e_pos = std::string(special.ter).find("_entrance",0,9);
std::string ter_base;
if (e_pos != std::string::npos) {
// strip "_entrance" to get the base oter_id
ter_base = std::string(special.ter).substr(0, e_pos);
} else if (special.ter == "farm") {
ter_base = std::string(special.ter) + "_field";
} else {
ter_base = std::string(special.ter);
}
for (int x = p.x; x < p.x + 3; x++) {
for (int y = p.y; y < p.y + 3; y++) {
ter(x, y, p.z) = ter_base;
}
}
if (is_road(p.x + 3, p.y + 1, p.z)) { // Road to east
ter(p.x + 2, p.y + 1, p.z) = special.ter;
} else if (is_road(p.x + 1, p.y + 3, p.z)) { // Road to south
ter(p.x + 1, p.y + 2, p.z) = special.ter;
} else if (is_road(p.x - 1, p.y + 1, p.z)) { // Road to west
ter(p.x, p.y + 1, p.z) = special.ter;
} else { // Road to north, or no roads
ter(p.x + 1, p.y, p.z) = special.ter;
}
}
if (special.flags & mfb(OMS_FLAG_BLOB)) {
for (int x = -2; x <= 2; x++) {
for (int y = -2; y <= 2; y++) {
if (x == 0 && y == 0)
y++; // Already handled
omspec_place place;
tripoint np(p.x + x, p.y + y, p.z);
if (one_in(1 + abs(x) + abs(y)) && (place.*special.able)(this, special.flags, np))
ter(p.x + x, p.y + y, p.z) = special.ter;
}
}
}
if (special.flags & mfb(OMS_FLAG_BIG)) {
for (int x = -3; x <= 3; x++) {
for (int y = -3; y <= 3; y++) {
if (x == 0 && y == 0)
y++; // Already handled
omspec_place place;
tripoint np(p.x + x, p.y + y, p.z);
if ((place.*special.able)(this, special.flags, np))
ter(p.x + x, p.y + y, p.z) = special.ter;
ter(p.x + x, p.y + y, p.z) = special.ter;
}
}
}
if (special.flags & mfb(OMS_FLAG_3X3_FIXED)) {
// road comes out of "_2" variant, rotations:
// |
// 321 963 789 147
// 654 852- 456 -258
// 987 741 123 369
// |
// reference point is top-left
int dir = 0;
if (is_road(p.x + 1, p.y - 1, p.z)) { // Road to north
dir = 0;
} else if (is_road(p.x + 3, p.y + 1, p.z)) { // Road to east
dir = 1;
} else if (is_road(p.x + 1, p.y + 3, p.z)) { // Road to south
dir = 2;
} else if (is_road(p.x - 1, p.y + 1, p.z)) { // Road to west
dir = 3;
} else {
dir = rng(0, 3); // Random direction;
}
//fixme
// usually will be called with the _2 entrance variant,
// for example "school_2"
size_t suffix_pos = std::string(special.ter).rfind("_2", std::string::npos, 2);
std::string ter_base = std::string(special.ter).substr(0, suffix_pos);
const char* suffix[] = {
"_1", "_2", "_3", "_4", "_5", "_6","_7", "_8", "_9"
};
if (dir == 0) {
for (int i = 0, y = p.y; y <= p.y + 2; y++) {
for (int x = p.x + 2; x >= p.x; x--, i++) {
ter(x, y, p.z) = ter_base + suffix[i];
}
}
if (ter_base == "school") { // wat. fixme.
make_hiway(p.x, p.y - 1, p.x + 1, p.y - 1, p.z, "road");
}
} else if (dir == 1) {
for (int i = 0, x = p.x + 2; x >= p.x; x--) {
for (int y = p.y + 2; y >= p.y; y--, i++) {
ter(x, y, p.z) = ter_base + suffix[i];
}
}
if (ter_base == "school") {
make_hiway(p.x + 3, p.y, p.x + 3, p.y + 1, p.z, "road");
}
} else if (dir == 2) {
for (int i = 0, y = p.y + 2; y >= p.y; y--) {
for (int x = p.x; x <= p.x + 2; x++, i++) {
ter(x, y, p.z) = ter_base + suffix[i];
}
}
if (ter_base == "school") {
make_hiway(p.x + 2, p.y + 3, p.x + 1, p.y + 3, p.z, "road");
}
} else if (dir == 3) {
for (int i = 0, x = p.x; x <= p.x + 2; x++) {
for (int y = p.y; y <= p.y + 2; y++, i++) {
ter(x, y, p.z) = ter_base + suffix[i];
}
}
if (ter_base == "school") {
make_hiway(p.x - 1, p.y + 2, p.x - 1, p.y + 1, p.z, "road");
}
}
if (special.flags & mfb(OMS_FLAG_ROAD)) {
if (dir == 0) {
make_hiway(p.x + 1, p.y - 1, cities[city].x,
cities[city].y, p.z, "road");
} else if (dir == 1) {
make_hiway(p.x + 3, p.y + 1, cities[city].x,
cities[city].y, p.z, "road");
} else if (dir == 2) {
make_hiway(p.x + 1, p.y + 3, cities[city].x,
cities[city].y, p.z, "road");
} else if (dir == 3) {
make_hiway(p.x - 1, p.y + 1, cities[city].x,
cities[city].y, p.z, "road");
}
}
}
// Buildings should be designed with the entrance at the southwest corner
// and open to the street on the south.
if (special.flags & mfb(OMS_FLAG_2X2_SECOND)) {
size_t e_pos = std::string(special.ter).find("_entrance",0,9);
std::string ter_base;
if (e_pos != std::string::npos) {
// strip "_entrance" to get the base oter_id
ter_base = std::string(special.ter).substr(0, e_pos);
} else {
ter_base = std::string(special.ter);
}
for (int x = p.x; x < p.x + 2; x++) {
for (int y = p.y; y < p.y + 2; y++) {
ter(x, y, p.z) = ter_base;
}
}
int dir = 0;
if (is_road(p.x + 1, p.y - 1, p.z)) { // Road to north
dir = 0;
} else if (is_road(p.x + 2, p.y + 1, p.z)) { // Road to east
dir = 1;
} else if (is_road(p.x, p.y + 2, p.z)) { // Road to south
dir = 2;
} else if (is_road(p.x - 1, p.y, p.z)) { // Road to west
dir = 3;
} else {
dir = rng(0, 3); // Random direction;
}
if (dir == 0) {
ter(p.x + 1, p.y, p.z) = special.ter;
} else if (dir == 1) {
ter(p.x + 1, p.y + 1, p.z) = special.ter;
} else if (dir == 2) {
ter(p.x, p.y + 1, p.z) = special.ter;
} else if (dir == 3) {
ter(p.x, p.y, p.z) = special.ter;
}
if (special.flags & mfb(OMS_FLAG_ROAD)) {
if (dir == 0) {
make_hiway(p.x + 1, p.y - 1, cities[city].x,
cities[city].y, p.z, "road");
} else if (dir == 1) {
make_hiway(p.x + 2, p.y + 1, cities[city].x,
cities[city].y, p.z, "road");
} else if (dir == 2) {
make_hiway(p.x, p.y + 2, cities[city].x,
cities[city].y, p.z, "road");
} else if (dir == 3) {
make_hiway(p.x - 1, p.y, cities[city].x,
cities[city].y, p.z, "road");
}
}
}
if (special.flags & mfb(OMS_FLAG_PARKING_LOT)) {
int closest = -1, distance = 999;
for (int i = 0; i < cities.size(); i++) {
int dist = rl_dist(p.x, p.y, cities[i].x, cities[i].y);
if (dist < distance) {
closest = i;
distance = dist;
}
}
if (special.flags & (mfb(OMS_FLAG_3X3) | mfb(OMS_FLAG_3X3_FIXED) | mfb(OMS_FLAG_3X3_SECOND))) {
ter(p.x + 1, p.y - 1, p.z) = "s_lot";
make_hiway(p.x + 1, p.y - 1, cities[closest].x, cities[closest].y, p.z, "road");
} else {
ter(p.x, p.y - 1, p.z) = "s_lot";
make_hiway(p.x, p.y - 1, cities[closest].x, cities[closest].y, p.z, "road");
}
}
if (special.flags & mfb(OMS_FLAG_DIRT_LOT)) {
int closest = -1, distance = 999;
for (int i = 0; i < cities.size(); i++) {
int dist = rl_dist(p.x, p.y, cities[i].x, cities[i].y);
if (dist < distance) {
closest = i;
distance = dist;
}
}
if (special.flags & (mfb(OMS_FLAG_3X3) | mfb(OMS_FLAG_3X3_FIXED) | mfb(OMS_FLAG_3X3_SECOND))) {
ter(p.x + 1, p.y - 1, p.z) = "dirtlot";
make_hiway(p.x + 1, p.y - 1, cities[closest].x, cities[closest].y, p.z, "road");
} else {
ter(p.x, p.y - 1, p.z) = "dirtlot";
make_hiway(p.x, p.y - 1, cities[closest].x, cities[closest].y, p.z, "road");
}
}
// Finally, place monsters if applicable
if (special.monsters != "GROUP_NULL") {
if (special.monster_pop_min == 0 || special.monster_pop_max == 0 ||
special.monster_rad_min == 0 || special.monster_rad_max == 0 ) {
debugmsg("Overmap special %s has bad spawn: pop(%d, %d) rad(%d, %d)",
otermap[special.ter].name.c_str(), special.monster_pop_min,
special.monster_pop_max, special.monster_rad_min,
special.monster_rad_max);
return;
}
int population = rng(special.monster_pop_min, special.monster_pop_max);
int radius = rng(special.monster_rad_min, special.monster_rad_max);
zg.push_back(
mongroup(special.monsters, p.x * 2, p.y * 2, p.z, radius, population));
}
}
void overmap::place_mongroups()
{
if (!ACTIVE_WORLD_OPTIONS["STATIC_SPAWN"]) {
// Cities are full of zombies
for (unsigned int i = 0; i < cities.size(); i++) {
if (!one_in(16) || cities[i].s > 5)
zg.push_back (mongroup("GROUP_ZOMBIE", (cities[i].x * 2), (cities[i].y * 2), 0,
int(cities[i].s * 2.5), cities[i].s * 80));
}
}
if (!ACTIVE_WORLD_OPTIONS["CLASSIC_ZOMBIES"]) {
// Figure out where swamps are, and place swamp monsters
for (int x = 3; x < OMAPX - 3; x += 7) {
for (int y = 3; y < OMAPY - 3; y += 7) {
int swamp_count = 0;
for (int sx = x - 3; sx <= x + 3; sx++) {
for (int sy = y - 3; sy <= y + 3; sy++) {
if (ter(sx, sy, 0) == "forest_water")
swamp_count += 2;
}
}
if (swamp_count >= 25)
zg.push_back(mongroup("GROUP_SWAMP", x * 2, y * 2, 0, 3,
rng(swamp_count * 8, swamp_count * 25)));
}
}
}
if (!ACTIVE_WORLD_OPTIONS["CLASSIC_ZOMBIES"]) {
// Figure out where rivers are, and place swamp monsters
for (int x = 3; x < OMAPX - 3; x += 7) {
for (int y = 3; y < OMAPY - 3; y += 7) {
int river_count = 0;
for (int sx = x - 3; sx <= x + 3; sx++) {
for (int sy = y - 3; sy <= y + 3; sy++) {
if (is_river(ter(sx, sy, 0)))
river_count++;
}
}
if (river_count >= 25)
zg.push_back(mongroup("GROUP_RIVER", x * 2, y * 2, 0, 3,
rng(river_count * 8, river_count * 25)));
}
}
}
if (!ACTIVE_WORLD_OPTIONS["CLASSIC_ZOMBIES"]) {
// Place the "put me anywhere" groups
int numgroups = rng(0, 3);
for (int i = 0; i < numgroups; i++) {
zg.push_back(
mongroup("GROUP_WORM", rng(0, OMAPX * 2 - 1), rng(0, OMAPY * 2 - 1), 0,
rng(20, 40), rng(30, 50)));
}
}
// Forest groups cover the entire map
zg.push_back( mongroup("GROUP_FOREST", OMAPX / 2, OMAPY / 2, 0,
OMAPY, rng(2000, 12000)));
zg.back().diffuse = true;
zg.push_back( mongroup("GROUP_FOREST", OMAPX / 2, (OMAPY * 3) / 2, 0,
OMAPY, rng(2000, 12000)));
zg.back().diffuse = true;
zg.push_back( mongroup("GROUP_FOREST", (OMAPX * 3) / 2, OMAPY / 2, 0,
OMAPX, rng(2000, 12000)));
zg.back().diffuse = true;
zg.push_back( mongroup("GROUP_FOREST", (OMAPX * 3) / 2, (OMAPY * 3) / 2, 0,
OMAPX, rng(2000, 12000)));
zg.back().diffuse = true;
}
void overmap::place_radios()
{
char message[200];
for (int i = 0; i < OMAPX; i++) {
for (int j = 0; j < OMAPY; j++) {
if (ter(i, j, 0) == "radio_tower") {
int choice = rng(0, 2);
switch(choice)
{
case 0:
snprintf( message, sizeof(message), _("This is emergency broadcast station %d%d.\
Please proceed quickly and calmly to your designated evacuation point."), i, j);
radios.push_back(radio_tower(i*2, j*2, rng(RADIO_MIN_STRENGTH, RADIO_MAX_STRENGTH), message));
break;
case 1:
radios.push_back(radio_tower(i*2, j*2, rng(RADIO_MIN_STRENGTH, RADIO_MAX_STRENGTH),
_("Head West. All survivors, head West. Help is waiting.")));
break;
case 2:
radios.push_back(radio_tower(i*2, j*2, rng(RADIO_MIN_STRENGTH, RADIO_MAX_STRENGTH), "", WEATHER_RADIO));
break;
}
} else if (ter(i, j, 0) == "lmoe") {
snprintf( message, sizeof(message), _("This is automated emergency shelter beacon %d%d.\
Supplies, amenities and shelter are stocked."), i, j);
radios.push_back(radio_tower(i*2, j*2, rng(RADIO_MIN_STRENGTH, RADIO_MAX_STRENGTH) / 2, message));
} else if (ter(i, j, 0) == "fema_entrance") {
snprintf( message, sizeof(message), _("This is FEMA camp %d%d.\
Supplies are limited, please bring supplemental food, water, and bedding.\
This is FEMA camp %d%d. A designated long-term emergency shelter."), i, j, i, j);
radios.push_back(radio_tower(i*2, j*2, rng(RADIO_MIN_STRENGTH, RADIO_MAX_STRENGTH), message));
}
}
}
}
void overmap::open(game *g)
{
std::string const plrfilename = player_filename(loc.x, loc.y);
std::string const terfilename = terrain_filename(loc.x, loc.y);
std::ifstream fin;
// Set position IDs
fin.open(terfilename.c_str());
if (fin.is_open()) {
unserialize(g, fin, plrfilename, terfilename);
fin.close();
} else { // No map exists! Prepare neighbors, and generate one.
std::vector<overmap*> pointers;
// Fetch north and south
for (int i = -1; i <= 1; i+=2) {
std::string const tmpfilename = terrain_filename(loc.x, loc.y + i);
fin.open(tmpfilename.c_str());
if (fin.is_open()) {
fin.close();
pointers.push_back(new overmap(g, loc.x, loc.y + i));
} else
pointers.push_back(NULL);
}
// Fetch east and west
for (int i = -1; i <= 1; i+=2) {
std::string const tmpfilename = terrain_filename(loc.x + i, loc.y);
fin.open(tmpfilename.c_str());
if (fin.is_open()) {
fin.close();
pointers.push_back(new overmap(g, loc.x + i, loc.y));
} else
pointers.push_back(NULL);
}
// pointers looks like (north, south, west, east)
generate(g, pointers[0], pointers[3], pointers[1], pointers[2]);
for (int i = 0; i < 4; i++)
delete pointers[i];
save();
}
}
std::string overmap::terrain_filename(int const x, int const y) const
{
std::stringstream filename;
filename << world_generator->active_world->world_path << "/";
if (!prefix.empty()) {
filename << prefix << ".";
}
filename << "o." << x << "." << y;
return filename.str();
}
std::string overmap::player_filename(int const x, int const y) const
{
std::stringstream filename;
filename << world_generator->active_world->world_path <<"/" << base64_encode(name) << ".seen." << x << "." << y;
return filename.str();
}
// Overmap special placement functions
bool omspec_place::water(overmap *om, unsigned long f, tripoint p)
{
int size = 1;
if (f & (mfb(OMS_FLAG_2X2) | mfb(OMS_FLAG_2X2_SECOND)))
size = 2;
else if (f & (mfb(OMS_FLAG_3X3) | mfb(OMS_FLAG_3X3_FIXED) | mfb(OMS_FLAG_3X3_SECOND)))
size = 3;
for (int x = p.x; x < p.x + size; x++) {
for (int y = p.y; y < p.y + size; y++) {
oter_id oter = om->ter(x, y, p.z);
if (!is_ot_type("river", oter)) {
return false;
}
}
}
return true;
}
bool omspec_place::land(overmap *om, unsigned long f, tripoint p)
{
int size = 1;
if (f & (mfb(OMS_FLAG_2X2) | mfb(OMS_FLAG_2X2_SECOND)))
size = 2;
else if (f & (mfb(OMS_FLAG_3X3) | mfb(OMS_FLAG_3X3_FIXED) | mfb(OMS_FLAG_3X3_SECOND)))
size = 3;
for (int x = p.x; x < p.x + size; x++) {
for (int y = p.y; y < p.y + size; y++) {
oter_id oter = om->ter(x, y, p.z);
if (is_ot_type("river", oter)) {
return false;
}
}
}
return true;
}
bool omspec_place::forest(overmap *om, unsigned long f, tripoint p)
{
int size = 1;
if (f & (mfb(OMS_FLAG_2X2) | mfb(OMS_FLAG_2X2_SECOND)))
size = 2;
else if (f & (mfb(OMS_FLAG_3X3) | mfb(OMS_FLAG_3X3_FIXED) | mfb(OMS_FLAG_3X3_SECOND)))
size = 3;
for (int x = p.x; x < p.x + size; x++) {
for (int y = p.y; y < p.y + size; y++) {
oter_id oter = om->ter(x, y, p.z);
if (!is_ot_type("forest", oter)) {
return false;
}
}
}
return true;
}
bool omspec_place::wilderness(overmap *om, unsigned long f, tripoint p)
{
int size = 1;
if (f & (mfb(OMS_FLAG_2X2) | mfb(OMS_FLAG_2X2_SECOND)))
size = 2;
else if (f & (mfb(OMS_FLAG_3X3) | mfb(OMS_FLAG_3X3_FIXED) | mfb(OMS_FLAG_3X3_SECOND)))
size = 3;
for (int x = p.x; x < p.x + size; x++) {
for (int y = p.y; y < p.y + size; y++) {
oter_id oter = om->ter(x, y, p.z);
if (!is_ot_type("forest", oter) && !is_ot_type("field", oter)) {
return false;
}
}
}
return true;
}
bool omspec_place::by_highway(overmap *om, unsigned long f, tripoint p)
{
int size = 1;
if (f & (mfb(OMS_FLAG_2X2) | mfb(OMS_FLAG_2X2_SECOND)))
size = 2;
else if (f & (mfb(OMS_FLAG_3X3) | mfb(OMS_FLAG_3X3_FIXED) | mfb(OMS_FLAG_3X3_SECOND)))
size = 3;
for (int x = p.x; x < p.x + size; x++) {
for (int y = p.y; y < p.y + size; y++) {
oter_id oter = om->ter(x, y, p.z);
if (!is_ot_type("forest", oter) && !is_ot_type("field", oter)) {
return false;
}
}
}
if (size == 3 &&
!om->is_road_or_highway(p.x + 1, p.y - 1, p.z) &&
!om->is_road_or_highway(p.x + 3, p.y + 1, p.z) &&
!om->is_road_or_highway(p.x + 1, p.y + 3, p.z) &&
!om->is_road_or_highway(p.x - 1, p.y + 1, p.z))
return false;
else if (size == 2 &&
!om->is_road_or_highway(p.x + 1, p.y - 1, p.z) &&
!om->is_road_or_highway(p.x + 2, p.y + 1, p.z) &&
!om->is_road_or_highway(p.x, p.y + 2, p.z) &&
!om->is_road_or_highway(p.x - 1, p.y, p.z))
return false;
else if (size == 1 &&
!om->is_road_or_highway(p.x, p.y - 1, p.z) &&
!om->is_road_or_highway(p.x, p.y + 1, p.z) &&
!om->is_road_or_highway(p.x - 1, p.y, p.z) &&
!om->is_road_or_highway(p.x + 1, p.y, p.z))
return false;
return true;
}
#include "omdata.h"
////////////////
oter_iid ot_null,
ot_crater,
ot_field,
ot_forest,
ot_forest_thick,
ot_forest_water,
ot_river_center;
oter_iid oterfind(const std::string id) {
if( otermap.find(id) == otermap.end() ) {
debugmsg("Can't find %s",id.c_str());
return 0;
}
return otermap[id].loadid;
};
void set_oter_ids() {
ot_null = oterfind("");
// NOT required.
ot_crater = oterfind("crater");
ot_field = oterfind("field");
ot_forest = oterfind("forest");
ot_forest_thick = oterfind("forest_thick");
ot_forest_water = oterfind("forest_water");
ot_river_center = oterfind("river_center");
};
//////////////////////////
//// sneaky
// ter(...) = 0;
const int& oter_id::operator=(const int& i) {
_val = i;
return _val;
}
// ter(...) = "rock"
oter_id::operator std::string() const {
if ( _val < 0 || _val > oterlist.size() ) {
debugmsg("oterlist[%d] > %d",_val,oterlist.size()); // remove me after testing (?)
return 0;
}
return std::string(oterlist[_val].id);
}
// int index = ter(...);
oter_id::operator int() const {
return _val;
}
// ter(...) != "foobar"
bool oter_id::operator!=(const char * v) const {
return oterlist[_val].id.compare(v) != 0;
/* hellaciously slow string allocation frenzy -v
std::map<std::string, oter_t>::const_iterator it=otermap.find(v);
return ( it == otermap.end() || it->second.loadid != _val);
*/
}
// ter(...) == "foobar"
bool oter_id::operator==(const char * v) const {
return oterlist[_val].id.compare(v) == 0;
}
bool oter_id::operator<=(const char * v) const {
std::map<std::string, oter_t>::const_iterator it=otermap.find(v);
return ( it == otermap.end() || it->second.loadid <= _val);
}
bool oter_id::operator>=(const char * v) const {
std::map<std::string, oter_t>::const_iterator it=otermap.find(v);
return ( it != otermap.end() && it->second.loadid >= _val);
}
// o_id1 != o_id2
bool oter_id::operator!=(const oter_id & v) const {
return ( _val != v._val );
}
bool oter_id::operator==(const oter_id & v) const {
return ( _val == v._val );
}
// oter_t( ter(...) ).name // WARNING
oter_id::operator oter_t() const {
return oterlist[_val];
}
const oter_t & oter_id::t() const {
return oterlist[_val];
}
// ter(...).size()
int oter_id::size() const {
return oterlist[_val].id.size();
}
// ter(...).find("foo");
int oter_id::find(const std::string &v, const int start, const int end) const {
return oterlist[_val].id.find(v);//, start, end);
}
// ter(...).compare(0, 3, "foo");
int oter_id::compare(size_t pos, size_t len, const char* s, size_t n) const {
if ( n != 0 ) {
return oterlist[_val].id.compare(pos, len, s, n);
} else {
return oterlist[_val].id.compare(pos, len, s);
}
}
// std::string("river_ne"); oter_id van_location(down_by);
oter_id::oter_id(const std::string& v) {
std::map<std::string, oter_t>::const_iterator it=otermap.find(v);
if ( it == otermap.end() ) {
debugmsg("not found: %s",v.c_str());
} else {
_val = it->second.loadid;
}
}
// oter_id b("house_north");
oter_id::oter_id(const char * v) {
std::map<std::string, oter_t>::const_iterator it=otermap.find(v);
if ( it == otermap.end() ) {
debugmsg("not found: %s",v);
} else {
_val = it->second.loadid;
}
}
// wprint("%s",ter(...).c_str() );
const char * oter_id::c_str() const {
return std::string(oterlist[_val].id).c_str();
}
You can’t perform that action at this time.