Make roads an overlay instead of a terrain trim type

Game:
- Roads now show on the automap again!
- Possible fix for undetected issue in displaying outdoor special spots
- Road conversion code for legacy scenarios now applied in towns too

Editor:
- New tool for placing roads, works the same as special dots
- Roads appear at all zoom levels

Other:
- Removed all terrain graphics that used the small road dot, replacing them with blank (white) space
- Make obvious out-of-bounds array accesses an error in the project settings
This commit is contained in:
2015-09-27 22:37:12 -04:00
parent 1222cb57f5
commit c68b01c3a2
28 changed files with 252 additions and 92 deletions

View File

@@ -512,6 +512,7 @@ std::string uAbility::to_string(eMonstAbil key) const {
switch(gen.fld) {
case eFieldType::SPECIAL_EXPLORED:
case eFieldType::SPECIAL_SPOT:
case eFieldType::SPECIAL_ROAD:
break; // These are invalid field types
case eFieldType::CLOUD_SLEEP: sout << "Sleep"; break;
case eFieldType::CLOUD_STINK: sout << "Foul"; break;
@@ -694,6 +695,7 @@ std::string uAbility::to_string(eMonstAbil key) const {
switch(radiate.type) {
case eFieldType::SPECIAL_EXPLORED:
case eFieldType::SPECIAL_SPOT:
case eFieldType::SPECIAL_ROAD:
break; // These are invalid field types
case eFieldType::WALL_BLADES: sout << "blade fields"; break;
case eFieldType::WALL_FIRE: sout << "fire fields"; break;

View File

@@ -38,6 +38,9 @@ void cOutdoors::append(legacy::outdoor_record_type& old){
if(scenario->ter_types[terrain[i][j]].i == 3000) // marker to indicate it used to be a special spot
special_spot[i][j] = true;
else special_spot[i][j] = false;
if(scenario->ter_types[terrain[i][j]].i == 3001) // marker to indicate it used to be a road
roads[i][j] = true;
else roads[i][j] = false;
// Convert roads that crossed grass/hill boundaries
// It won't catch ones that sit exactly at the edge, though; in that case they'd need manual fixing
// For simplicity we assume the non-hill space is either a city or a grass road
@@ -45,7 +48,6 @@ void cOutdoors::append(legacy::outdoor_record_type& old){
// 80 - grass road 81 - hill road
// 38 - hill/grass 40 - hill|grass 42 - grass/hill 44 - grass|hill
// where / means "over" and | means "beside"
// Not going to do it for town since roads in town are uncommon. Maybe later.
if(old.terrain[i][j] == 81 && i > 0 && i < 47 && j > 0 && j < 47) {
if(old.terrain[i+1][j] == 81) {
ter_num_t connect = old.terrain[i-1][j];

View File

@@ -73,6 +73,7 @@ public:
std::string comment;
std::vector<std::string> spec_strs;
bool special_spot[48][48];
bool roads[48][48];
eAmbientSound ambient_sound;
snd_num_t out_sound;
int bg_out, bg_fight, bg_town, bg_dungeon;

View File

@@ -18,11 +18,11 @@
#include "oldstructs.hpp"
#include "fileio.hpp"
void cTinyTown::append(legacy::tiny_tr_type& old, int town_num){
void cTinyTown::append(legacy::tiny_tr_type& old, int){
int i,j;
cField the_field;
(void)town_num;
cField the_field, the_road;
the_field.type = SPECIAL_SPOT;
the_road.type = SPECIAL_ROAD;
// Collect a list of unused special nodes, to be used for fixing specials that could be triggered in a boat.
std::vector<int> unused_special_slots;
for(i = 0; i < 100; i++) {
@@ -44,6 +44,37 @@ void cTinyTown::append(legacy::tiny_tr_type& old, int town_num){
the_field.loc.y = j;
preset_fields.push_back(the_field);
}
if(scenario->ter_types[ter[i][j]].i == 3001) { // marker to indicate it used to be a road
the_road.loc.x = i;
the_road.loc.y = j;
preset_fields.push_back(the_road);
}
// Convert roads that crossed grass/hill boundaries
// It won't catch ones that sit exactly at the edge, though; in that case they'd need manual fixing
// For simplicity we assume the non-hill space is either a city or a grass road
// Terrain types used here:
// 80 - grass road 81 - hill road
// 38 - hill/grass 40 - hill|grass 42 - grass/hill 44 - grass|hill
// where / means "over" and | means "beside"
if(old.terrain[i][j] == 81 && i > 0 && i < 47 && j > 0 && j < 47) {
if(old.terrain[i+1][j] == 81) {
ter_num_t connect = old.terrain[i-1][j];
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
ter[i][j] = 44;
} else if(old.terrain[i-1][j] == 81) {
ter_num_t connect = old.terrain[i+1][j];
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
ter[i][j] = 40;
} else if(old.terrain[i][j+1] == 81) {
ter_num_t connect = old.terrain[i][j-1];
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
ter[i][j] = 42;
} else if(old.terrain[i][j-1] == 81) {
ter_num_t connect = old.terrain[i][j+1];
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
ter[i][j] = 38;
}
}
if(scenario->ter_types[ter[i][j]].boat_over) {
// Try to fix specials that could be triggered while in a boat
// (Boats never triggered specials in the old BoE, so we probably don't want them to trigger.)
@@ -87,11 +118,11 @@ void cTinyTown::append(legacy::tiny_tr_type& old, int town_num){
}
}
void cMedTown::append(legacy::ave_tr_type& old, int town_num){
void cMedTown::append(legacy::ave_tr_type& old, int){
int i,j;
cField the_field;
(void)town_num; // Silencing Warnings until towns have numbers
cField the_field, the_road;
the_field.type = SPECIAL_SPOT;
the_road.type = SPECIAL_ROAD;
// Collect a list of unused special nodes, to be used for fixing specials that could be triggered in a boat.
std::vector<int> unused_special_slots;
for(i = 0; i < 100; i++) {
@@ -113,6 +144,37 @@ void cMedTown::append(legacy::ave_tr_type& old, int town_num){
the_field.loc.y = j;
preset_fields.push_back(the_field);
}
if(scenario->ter_types[ter[i][j]].i == 3001) { // marker to indicate it used to be a road
the_road.loc.x = i;
the_road.loc.y = j;
preset_fields.push_back(the_road);
}
// Convert roads that crossed grass/hill boundaries
// It won't catch ones that sit exactly at the edge, though; in that case they'd need manual fixing
// For simplicity we assume the non-hill space is either a city or a grass road
// Terrain types used here:
// 80 - grass road 81 - hill road
// 38 - hill/grass 40 - hill|grass 42 - grass/hill 44 - grass|hill
// where / means "over" and | means "beside"
if(old.terrain[i][j] == 81 && i > 0 && i < 47 && j > 0 && j < 47) {
if(old.terrain[i+1][j] == 81) {
ter_num_t connect = old.terrain[i-1][j];
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
ter[i][j] = 44;
} else if(old.terrain[i-1][j] == 81) {
ter_num_t connect = old.terrain[i+1][j];
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
ter[i][j] = 40;
} else if(old.terrain[i][j+1] == 81) {
ter_num_t connect = old.terrain[i][j-1];
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
ter[i][j] = 42;
} else if(old.terrain[i][j-1] == 81) {
ter_num_t connect = old.terrain[i][j+1];
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
ter[i][j] = 38;
}
}
if(scenario->ter_types[ter[i][j]].boat_over) {
// Try to fix specials that could be triggered while in a boat
// (Boats never triggered specials in the old BoE, so we probably don't want them to trigger.)
@@ -156,11 +218,11 @@ void cMedTown::append(legacy::ave_tr_type& old, int town_num){
}
}
void cBigTown::append(legacy::big_tr_type& old, int town_num){
void cBigTown::append(legacy::big_tr_type& old, int){
int i,j;
(void)town_num;
cField the_field;
cField the_field, the_road;
the_field.type = SPECIAL_SPOT;
the_road.type = SPECIAL_ROAD;
// Collect a list of unused special nodes, to be used for fixing specials that could be triggered in a boat.
std::vector<int> unused_special_slots;
for(i = 0; i < 100; i++) {
@@ -182,6 +244,37 @@ void cBigTown::append(legacy::big_tr_type& old, int town_num){
the_field.loc.y = j;
preset_fields.push_back(the_field);
}
if(scenario->ter_types[ter[i][j]].i == 3001) { // marker to indicate it used to be a road
the_road.loc.x = i;
the_road.loc.y = j;
preset_fields.push_back(the_road);
}
// Convert roads that crossed grass/hill boundaries
// It won't catch ones that sit exactly at the edge, though; in that case they'd need manual fixing
// For simplicity we assume the non-hill space is either a city or a grass road
// Terrain types used here:
// 80 - grass road 81 - hill road
// 38 - hill/grass 40 - hill|grass 42 - grass/hill 44 - grass|hill
// where / means "over" and | means "beside"
if(old.terrain[i][j] == 81 && i > 0 && i < 47 && j > 0 && j < 47) {
if(old.terrain[i+1][j] == 81) {
ter_num_t connect = old.terrain[i-1][j];
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
ter[i][j] = 44;
} else if(old.terrain[i-1][j] == 81) {
ter_num_t connect = old.terrain[i+1][j];
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
ter[i][j] = 40;
} else if(old.terrain[i][j+1] == 81) {
ter_num_t connect = old.terrain[i][j-1];
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
ter[i][j] = 42;
} else if(old.terrain[i][j-1] == 81) {
ter_num_t connect = old.terrain[i][j+1];
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
ter[i][j] = 38;
}
}
if(scenario->ter_types[ter[i][j]].boat_over) {
// Try to fix specials that could be triggered while in a boat
// (Boats never triggered specials in the old BoE, so we probably don't want them to trigger.)

View File

@@ -301,7 +301,6 @@ enum class eTrimType {
S, SE, E, NE, N, NW, W, SW,
NE_INNER, SE_INNER, SW_INNER, NW_INNER,
FRILLS = 14, // like on lava and underground water; no trim_ter required
ROAD = 15, // the game will treat it like a road space and draw roads; no trim_ter required
WALKWAY = 16, // the game will draw walkway corners; trim_ter is base terrain to draw on
WATERFALL = 17, // special case for waterfalls
CITY = 18, // the game will join roads up to this space but not draw roads on the space
@@ -872,6 +871,7 @@ enum eFieldType {
SFX_BONES = 22,
SFX_RUBBLE = 23,
BARRIER_CAGE = 24,
SPECIAL_ROAD = 25,
// From here on are special values that don't index anything.
// Thus, they start at 32.
FIELD_DISPEL = 32, // Dispel field

View File

@@ -85,7 +85,7 @@ void cTerrain::append(legacy::terrain_type_type& old){
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 18,18,18, 18,18,18,18,18,18,18,18,0, 0,
18,18,15,15,15,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,0, 16,16,
18,18,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16,16,0, 16,16,
16,16,16,16,16,16,0, 0, 0, 0, 0, 0, 18,0, 0, 0, 18,18,18,18,
18,18,18,18,18,18,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
14,14,14,14,14,17,18,0, 0, 0, 0, 0, 0, 0,
@@ -332,6 +332,19 @@ void cTerrain::append(legacy::terrain_type_type& old){
obj_size.x = 2;
obj_size.y = 2;
break;
// Roads
case 202:
picture = 0;
i = 3001;
break;
case 203:
picture = 2;
i = 3001;
break;
case 204:
picture = 32;
i = 3001;
break;
// Special spaces
case 207:
picture = 0;

View File

@@ -104,11 +104,14 @@ void cCurTown::place_preset_fields() {
for(size_t i = 0; i < record()->preset_fields.size(); i++) {
switch(record()->preset_fields[i].type){
case OBJECT_BLOCK:
univ.town.set_block(record()->preset_fields[i].loc.x,record()->preset_fields[i].loc.y,true);
set_block(record()->preset_fields[i].loc.x,record()->preset_fields[i].loc.y,true);
break;
case SPECIAL_SPOT:
set_spot(record()->preset_fields[i].loc.x,record()->preset_fields[i].loc.y,true);
break;
case SPECIAL_ROAD:
set_road(record()->preset_fields[i].loc.x,record()->preset_fields[i].loc.y,true);
break;
case FIELD_WEB:
set_web(record()->preset_fields[i].loc.x,record()->preset_fields[i].loc.y,true);
break;
@@ -236,6 +239,10 @@ bool cCurTown::is_spot(short x, short y) const{
return fields[x][y] & SPECIAL_SPOT;
}
bool cCurTown::is_road(short x, short y) const{
return fields[x][y] & SPECIAL_ROAD;
}
bool cCurTown::is_special(short x, short y) const{
if(x > record()->max_dim() || y > record()->max_dim()) return false;
location check(x,y);
@@ -463,6 +470,13 @@ bool cCurTown::set_spot(short x, short y, bool b){
return true;
}
bool cCurTown::set_road(short x, short y, bool b){
if(x > record()->max_dim() || y > record()->max_dim()) return false;
if(b) fields[x][y] |= SPECIAL_ROAD;
else fields[x][y] &= ~SPECIAL_ROAD;
return true;
}
bool cCurTown::set_web(short x, short y, bool b){
if(x > record()->max_dim() || y > record()->max_dim()) return false;
if(b){ // If certain things are on the space, there's no room for webs
@@ -864,6 +878,24 @@ cOutdoors* cCurOut::operator->() {
return univ.scenario.outdoors[x][y];
}
bool cCurOut::is_spot(int x, int y) {
int sector_x = 0, sector_y = 0;
if(x >= 48) sector_x++, x -= 48;
if(y >= 48) sector_y++, y -= 48;
sector_x += univ.party.outdoor_corner.x;
sector_y += univ.party.outdoor_corner.y;
return univ.scenario.outdoors[sector_x][sector_y]->special_spot[x][y];
}
bool cCurOut::is_road(int x, int y) {
int sector_x = 0, sector_y = 0;
if(x >= 48) sector_x++, x -= 48;
if(y >= 48) sector_y++, y -= 48;
sector_x += univ.party.outdoor_corner.x;
sector_y += univ.party.outdoor_corner.y;
return univ.scenario.outdoors[sector_x][sector_y]->roads[x][y];
}
cUniverse::cUniverse(long party_type) : party(*this, party_type), out(*this), town(*this) {}
void cUniverse::check_monst(cMonster& monst) {

View File

@@ -92,7 +92,7 @@ public:
bool is_bones(short x, short y) const;
bool is_rubble(short x, short y) const;
bool is_force_cage(short x, short y) const;
// bool is_trim(short x, short y, char t) const;
bool is_road(short x, short y) const;
bool set_explored(short x, short y, bool b);
bool set_force_wall(short x, short y, bool b);
bool set_fire_wall(short x, short y, bool b);
@@ -118,7 +118,7 @@ public:
bool set_bones(short x, short y, bool b);
bool set_rubble(short x, short y, bool b);
bool set_force_cage(short x, short y, bool b);
// bool set_trim(short x, short y, char t, bool b);
bool set_road(short x, short y, bool b);
bool is_impassable(short x, short y);
void writeTo(std::ostream& file) const;
void readFrom(std::istream& file);
@@ -133,6 +133,10 @@ public:
ter_num_t out[96][96];
unsigned char out_e[96][96];
// These take global coords (ie 0..95)
bool is_spot(int x, int y);
bool is_road(int x, int y);
void append(legacy::out_info_type& old);
typedef ter_num_t arr_96[96];