New editor commands: Resize Outdoors and Import Sector

This includes the following changes (mostly related to the above):
- Move loc_compare functor to location.hpp
- Add reattach() function to rebind a town or outdoor section to a different scenario object. (I don't think this is strictly necessary, as the scenario reference is only used in limited situations in the game itself, not in the editor, but it's better not to keep a reference to freed memory around when importing a town or sector from another scenario, even if the reference is never accessed.)
- Fix imported towns not being officially "loaded" until you explicitly use Load a New Town to reload them
- Fix Choose button in Load New Sector dialog
- More functions in the vector2d implementation
This commit is contained in:
2015-06-25 23:55:55 -04:00
parent 8a4e7a7a72
commit ef0cdebec4
22 changed files with 403 additions and 55 deletions

View File

@@ -0,0 +1,43 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<?xml-stylesheet href="dialog.xsl" type="text/xsl"?>
<dialog skin='dark' defbtn='okay'>
<pict type='dlog' num='16' top='8' left='8'/>
<text size='large' top='8' left='50' width='200' height='17'>Resize/Shift Outdoors</text>
<text top='30' left='100' width='60' height='16'>Extend top</text>
<text name='top' framed='true' top='50' left='100' width='60' height='16'>0</text>
<button name='top-m' type='small' top='72' left='109'>-</button>
<button name='top-p' type='small' top='72' left='135'>+</button>
<text top='120' left='10' width='60' height='16'>Extend left</text>
<text name='left' framed='true' top='140' left='10' width='60' height='16'>0</text>
<button name='left-m' type='small' top='162' left='19'>-</button>
<button name='left-p' type='small' top='162' left='45'>+</button>
<text top='120' left='190' width='70' height='16'>Extend right</text>
<text name='right' framed='true' top='140' left='190' width='60' height='16'>0</text>
<button name='right-m' type='small' top='162' left='199'>-</button>
<button name='right-p' type='small' top='162' left='225'>+</button>
<text top='210' left='100' width='80' height='16'>Extend bottom</text>
<text name='bottom' framed='true' top='230' left='100' width='60' height='16'>0</text>
<button name='bottom-m' type='small' top='252' left='109'>-</button>
<button name='bottom-p' type='small' top='252' left='135'>+</button>
<text framed='true' top='110' left='90' width='80' height='80'/>
<text top='115' left='95' width='80' height='16'>Width:</text>
<text name='w-old' top='130' left='95' width='20' height='16'/>
<text top='130' left='120' width='20' height='16'>--&gt;</text>
<text name='w-new' top='130' left='145' width='20' height='16'/>
<text top='155' left='95' width='80' height='16'>Height:</text>
<text name='h-old' top='170' left='95' width='20' height='16'/>
<text top='170' left='120' width='20' height='16'>--&gt;</text>
<text name='h-new' top='170' left='145' width='20' height='16'/>
<text top='30' left='270' width='200' height='16'>Sections to be deleted:</text>
<text name='delete' framed='true' top='50' left='270' width='200' height='200'/>
<button name='okay' type='regular' top='270' left='407'>OK</button>
<button name='cancel' type='regular' def-key='esc' top='270' left='342'>Cancel</button>
</dialog>

View File

@@ -348,6 +348,14 @@
<reference key="NSOnImage" ref="229763992"/>
<reference key="NSMixedImage" ref="909111550"/>
</object>
<object class="NSMenuItem" id="872790335">
<reference key="NSMenu" ref="399390342"/>
<string key="NSTitle">Resize Outdoors</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="229763992"/>
<reference key="NSMixedImage" ref="909111550"/>
</object>
<object class="NSMenuItem" id="610226442">
<reference key="NSMenu" ref="399390342"/>
<bool key="NSIsDisabled">YES</bool>
@@ -457,6 +465,14 @@
<reference key="NSOnImage" ref="229763992"/>
<reference key="NSMixedImage" ref="909111550"/>
</object>
<object class="NSMenuItem" id="327378958">
<reference key="NSMenu" ref="399390342"/>
<string key="NSTitle"> Import Outdoor Sector</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="229763992"/>
<reference key="NSMixedImage" ref="909111550"/>
</object>
<object class="NSMenuItem" id="580939217">
<reference key="NSMenu" ref="399390342"/>
<string key="NSTitle"> Edit Saved Item Rectangles</string>
@@ -1267,6 +1283,8 @@
<reference ref="963208893"/>
<reference ref="1072664025"/>
<reference ref="368560496"/>
<reference ref="872790335"/>
<reference ref="327378958"/>
</array>
<reference key="parent" ref="741259600"/>
</object>
@@ -1635,6 +1653,16 @@
<reference key="object" ref="1044668105"/>
<reference key="parent" ref="720053764"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">891</int>
<reference key="object" ref="872790335"/>
<reference key="parent" ref="399390342"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">892</int>
<reference key="object" ref="327378958"/>
<reference key="parent" ref="399390342"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
@@ -1744,12 +1772,14 @@
<string key="888.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="889.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="890.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="891.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="892.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">890</int>
<int key="maxID">892</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>

View File

@@ -87,6 +87,7 @@ BEGIN
POPUP "&Scenario"
BEGIN
MENUITEM "&Create New Town", IDM_SCEN_NEW_TOWN
MENUITEM "Resize Outdoors", IDM_SCEN_RESIZE_OUTDOORS
MENUITEM SEPARATOR
MENUITEM "&Scenario Details", IDM_SCEN_DETAILS
MENUITEM "Scenario Intr&o Text", IDM_SCEN_INTRO
@@ -100,6 +101,7 @@ BEGIN
MENUITEM " Edit Scenario &Text", IDM_SCEN_ADV_TEXT
MENUITEM " Edit Journal Entries", IDM_SCEN_ADV_JOURNAL
MENUITEM " &Import Town", IDM_SCEN_ADV_IMPORT_TOWN
MENUITEM " Import Outdoor Sector", IDM_SCEN_ADV_IMPORT_OUT
MENUITEM " Edit Saved Item &Rectangles", IDM_SCEN_ADV_SAVE_RECTS
MENUITEM " Edit &Horses", IDM_SCEN_ADV_HORSES
MENUITEM " Edit &Boats", IDM_SCEN_ADV_BOATS

View File

@@ -69,6 +69,8 @@
#define IDM_SCEN_CUSTOM_SHEETS 164
#define IDM_SCEN_CUSTOM_SNDS 165
#define IDM_FILE_SAVE_AS 166
#define IDM_SCEN_RESIZE_OUTDOORS 167
#define IDM_SCEN_ADV_IMPORT_OUT 168
// Next default values for new objects
//
@@ -77,6 +79,6 @@
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40014
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 167
#define _APS_NEXT_SYMED_VALUE 169
#endif
#endif

View File

@@ -41,6 +41,13 @@ location loc(){
return location();
}
bool loc_compare::operator()(location a, location b) const {
// This is just a lexicographical ordering.
if(a.x != b.x) return a.x < b.x;
if(a.y != b.y) return a.y < b.y;
return false;
}
bool location::in(rectangle r){
if(y >= r.top && y <= r.bottom && x >= r.left && x <= r.right)
return true;

View File

@@ -28,6 +28,10 @@ struct location {
}
};
struct loc_compare {
bool operator()(location a, location b) const;
};
class rectangle_size_delegate {
friend struct rectangle;
rectangle& forRect;

View File

@@ -35,7 +35,7 @@ void cOutdoors::append(legacy::outdoor_record_type& old){
for(i = 0; i < 48; i++)
for(j = 0; j < 48; j++){
terrain[i][j] = old.terrain[i][j];
if(scenario.ter_types[terrain[i][j]].i == 3000) // marker to indicate it used to be a special spot
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;
// Convert roads that crossed grass/hill boundaries
@@ -49,19 +49,19 @@ void cOutdoors::append(legacy::outdoor_record_type& old){
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)
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
terrain[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)
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
terrain[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)
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
terrain[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)
if(connect == 80 || scenario->ter_types[connect].trim_type == eTrimType::CITY)
terrain[i][j] = 38;
}
}
@@ -106,7 +106,7 @@ cOutdoors::cWandering::cWandering() {
std::fill(friendly.begin(), friendly.end(), 0);
}
cOutdoors::cOutdoors(cScenario& scenario) : scenario(scenario) {
cOutdoors::cOutdoors(cScenario& scenario) : scenario(&scenario) {
short i,j;
location locs[4] = {loc(8,8),loc(32,8),loc(8,32),loc(32,32)};
bg_out = bg_fight = bg_town = bg_dungeon = -1;
@@ -192,3 +192,7 @@ bool cOutdoors::cWandering::isNull(){
return false;
return true;
}
void cOutdoors::reattach(cScenario& scen) {
scenario = &scen;
}

View File

@@ -34,7 +34,7 @@ enum eAmbientSound {
};
class cOutdoors {
cScenario& scenario;
cScenario* scenario;
public:
class cWandering { // formerly out_wandering_type
public:
@@ -79,6 +79,7 @@ public:
explicit cOutdoors(cScenario& scenario);
void append(legacy::outdoor_record_type& old);
void reattach(cScenario& to);
};
#endif

View File

@@ -39,12 +39,12 @@ void cTinyTown::append(legacy::tiny_tr_type& old, int town_num){
for(j = 0; j < 32; j++) {
ter[i][j] = old.terrain[i][j];
light[i / 8][j] = old.lighting[i / 8][j];
if(scenario.ter_types[ter[i][j]].i == 3000) { // marker to indicate it used to be a special spot
if(scenario->ter_types[ter[i][j]].i == 3000) { // marker to indicate it used to be a special spot
the_field.loc.x = i;
the_field.loc.y = j;
preset_fields.push_back(the_field);
}
if(scenario.ter_types[ter[i][j]].boat_over) {
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.)
int found_spec = -1;
@@ -108,12 +108,12 @@ void cMedTown::append(legacy::ave_tr_type& old, int town_num){
for(j = 0; j < 48; j++) {
ter[i][j] = old.terrain[i][j];
light[i / 8][j] = old.lighting[i / 8][j];
if(scenario.ter_types[ter[i][j]].i == 3000) { // marker to indicate it used to be a special spot
if(scenario->ter_types[ter[i][j]].i == 3000) { // marker to indicate it used to be a special spot
the_field.loc.x = i;
the_field.loc.y = j;
preset_fields.push_back(the_field);
}
if(scenario.ter_types[ter[i][j]].boat_over) {
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.)
int found_spec = -1;
@@ -177,12 +177,12 @@ void cBigTown::append(legacy::big_tr_type& old, int town_num){
for(j = 0; j < 64; j++) {
ter[i][j] = old.terrain[i][j];
light[i / 8][j] = old.lighting[i / 8][j];
if(scenario.ter_types[ter[i][j]].i == 3000) { // marker to indicate it used to be a special spot
if(scenario->ter_types[ter[i][j]].i == 3000) { // marker to indicate it used to be a special spot
the_field.loc.x = i;
the_field.loc.y = j;
preset_fields.push_back(the_field);
}
if(scenario.ter_types[ter[i][j]].boat_over) {
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.)
int found_spec = -1;

View File

@@ -75,7 +75,7 @@ void cTown::append(legacy::town_record_type& old){
strong_barriers = defy_scrying = defy_mapping = false;
}
cTown::cTown(cScenario& scenario) : scenario(scenario) {
cTown::cTown(cScenario& scenario) : scenario(&scenario) {
short i;
cTown::cWandering d_wan = {0,0,0,0};
@@ -191,7 +191,7 @@ void cTown::set_up_lights() {
for(short j = 0; j < this->max_dim(); j++) {
l.x = i;
l.y = j;
rad = scenario.ter_types[this->terrain(i,j)].light_radius;
rad = scenario->ter_types[this->terrain(i,j)].light_radius;
if(rad > 0) {
for(where.x = std::max(0,i - rad); where.x < min(this->max_dim(),short(i + rad + 1)); where.x++)
for(where.y = std::max(0,j - rad); where.y < min(this->max_dim(),short(j + rad + 1)); where.y++)
@@ -216,7 +216,7 @@ short cTown::light_obscurity(short x,short y) {
what_terrain = this->terrain(x,y);
store = scenario.ter_types[what_terrain].blockage;
store = scenario->ter_types[what_terrain].blockage;
if(store == eTerObstruct::BLOCK_SIGHT || store == eTerObstruct::BLOCK_MOVE_AND_SIGHT)
return 5;
if(store == eTerObstruct::BLOCK_MOVE_AND_SHOOT)
@@ -240,3 +240,7 @@ cTown::cItem::cItem(location loc, short num, ::cItem& item) : cItem() {
if(item.variety == eItemType::GOLD || item.variety == eItemType::FOOD)
charges = get_ran(1,4,6);
}
void cTown::reattach(cScenario& scen) {
scenario = &scen;
}

View File

@@ -34,7 +34,7 @@ class cScenario;
class cTown { // formerly town_record_type
protected:
cScenario& scenario;
cScenario* scenario;
public:
class cWandering { // formerly wandering_type
public:
@@ -106,6 +106,7 @@ public:
explicit cTown(cScenario& scenario);
void append(legacy::town_record_type& old);
void reattach(cScenario& to);
virtual void writeTerrainTo(std::ostream& file) = 0;
virtual void readTerrainFrom(std::istream& file) = 0;
};

View File

@@ -214,7 +214,7 @@ static bool handle_lb_action(location the_point) {
if(!save_check("save-section-confirm"))
break;
}
spot_hit = pick_out(cur_out);
spot_hit = pick_out(cur_out, scenario);
if(spot_hit != cur_out) {
cur_out = spot_hit;
current_terrain = scenario.outdoors[cur_out.x][cur_out.y];

View File

@@ -2965,6 +2965,7 @@ bool build_scenario() {
}
}
if(med > 0) med--;
warriors_grove->reattach(scenario);
}
scenario.shops.push_back(cShop('heal'));

View File

@@ -289,6 +289,12 @@ void handle_menu_choice(eMenu item_hit) {
set_up_main_screen();
change_made = true;
break;
case eMenu::OUT_RESIZE:
if(resize_outdoors())
change_made = true;
if(overall_mode == MODE_MAIN_SCREEN)
set_up_main_screen();
break;
case eMenu::SCEN_DETAILS:
edit_scen_details();
change_made = true;
@@ -327,12 +333,28 @@ void handle_menu_choice(eMenu item_hit) {
break;
case eMenu::TOWN_IMPORT:
if(change_made) {
giveError("You need to save the changes made to your scenario before you can add a new town.");
giveError("You need to save the changes made to your scenario before you can import a town.");
return;
}
if(cTown* town = pick_import_town(0)) {
if(cTown* town = pick_import_town()) {
town->reattach(scenario);
delete scenario.towns[cur_town];
scenario.towns[cur_town] = town;
::town = town;
change_made = true;
redraw_screen();
}
break;
case eMenu::OUT_IMPORT:
if(change_made) {
giveError("You need to save the changes made to your scenario before you can import a sector.");
return;
}
if(cOutdoors* out = pick_import_out()) {
out->reattach(scenario);
delete scenario.outdoors[cur_out.x][cur_out.y];
scenario.outdoors[cur_out.x][cur_out.y] = out;
current_terrain = out;
change_made = true;
redraw_screen();
}

View File

@@ -18,8 +18,8 @@ enum class eMenu {
EDIT_UNDO, EDIT_REDO, EDIT_CUT, EDIT_COPY, EDIT_PASTE, EDIT_DELETE, EDIT_SELECT_ALL,
HELP_TOC, HELP_START, HELP_TEST, HELP_DIST, HELP_CONTEST,
// Scenario menu
TOWN_CREATE, SCEN_DETAILS, SCEN_INTRO, TOWN_START,
SCEN_SPECIALS, SCEN_TEXT, SCEN_JOURNALS, TOWN_IMPORT,
TOWN_CREATE, OUT_RESIZE, SCEN_DETAILS, SCEN_INTRO, TOWN_START,
SCEN_SPECIALS, SCEN_TEXT, SCEN_JOURNALS, TOWN_IMPORT, OUT_IMPORT,
SCEN_SAVE_ITEM_RECTS, SCEN_HORSES, SCEN_BOATS,
TOWN_VARYING, SCEN_TIMERS, SCEN_ITEM_SHORTCUTS, TOWN_DELETE,
SCEN_DATA_DUMP, SCEN_TEXT_DUMP,

View File

@@ -52,8 +52,9 @@ void init_menubar() {
eMenu::EDIT_CUT, eMenu::EDIT_COPY, eMenu::EDIT_PASTE, eMenu::EDIT_DELETE, eMenu::EDIT_SELECT_ALL,
};
static const eMenu scen_choices[] = {
eMenu::TOWN_CREATE, eMenu::NONE, eMenu::SCEN_DETAILS, eMenu::SCEN_INTRO, eMenu::TOWN_START, eMenu::SCEN_SHEETS, eMenu::SCEN_PICS, eMenu::SCEN_SNDS, eMenu::NONE, eMenu::NONE,
eMenu::SCEN_SPECIALS, eMenu::SCEN_TEXT, eMenu::SCEN_JOURNALS, eMenu::TOWN_IMPORT, eMenu::SCEN_SAVE_ITEM_RECTS,
eMenu::TOWN_CREATE, eMenu::OUT_RESIZE, eMenu::NONE,
eMenu::SCEN_DETAILS, eMenu::SCEN_INTRO, eMenu::TOWN_START, eMenu::SCEN_SHEETS, eMenu::SCEN_PICS, eMenu::SCEN_SNDS, eMenu::NONE, eMenu::NONE,
eMenu::SCEN_SPECIALS, eMenu::SCEN_TEXT, eMenu::SCEN_JOURNALS, eMenu::TOWN_IMPORT, eMenu::OUT_IMPORT, eMenu::SCEN_SAVE_ITEM_RECTS,
eMenu::SCEN_HORSES, eMenu::SCEN_BOATS, eMenu::TOWN_VARYING, eMenu::SCEN_TIMERS, eMenu::SCEN_ITEM_SHORTCUTS,
eMenu::TOWN_DELETE, eMenu::SCEN_DATA_DUMP, eMenu::SCEN_TEXT_DUMP,
};

View File

@@ -80,8 +80,9 @@ void init_menubar() {
eMenu::EDIT_CUT, eMenu::EDIT_COPY, eMenu::EDIT_PASTE, eMenu::EDIT_DELETE, eMenu::EDIT_SELECT_ALL,
};
static const eMenu scen_choices[] = {
eMenu::TOWN_CREATE, eMenu::NONE, eMenu::SCEN_DETAILS, eMenu::SCEN_INTRO, eMenu::TOWN_START, eMenu::SCEN_SHEETS, eMenu::SCEN_PICS, eMenu::SCEN_SNDS, eMenu::NONE, eMenu::NONE,
eMenu::SCEN_SPECIALS, eMenu::SCEN_TEXT, eMenu::SCEN_JOURNALS, eMenu::TOWN_IMPORT, eMenu::SCEN_SAVE_ITEM_RECTS,
eMenu::TOWN_CREATE, eMenu::OUT_RESIZE, eMenu::NONE,
eMenu::SCEN_DETAILS, eMenu::SCEN_INTRO, eMenu::TOWN_START, eMenu::SCEN_SHEETS, eMenu::SCEN_PICS, eMenu::SCEN_SNDS, eMenu::NONE, eMenu::NONE,
eMenu::SCEN_SPECIALS, eMenu::SCEN_TEXT, eMenu::SCEN_JOURNALS, eMenu::TOWN_IMPORT, eMenu::OUT_IMPORT, eMenu::SCEN_SAVE_ITEM_RECTS,
eMenu::SCEN_HORSES, eMenu::SCEN_BOATS, eMenu::TOWN_VARYING, eMenu::SCEN_TIMERS, eMenu::SCEN_ITEM_SHORTCUTS,
eMenu::TOWN_DELETE, eMenu::SCEN_DATA_DUMP, eMenu::SCEN_TEXT_DUMP,
};

View File

@@ -1121,7 +1121,7 @@ short edit_talk_node(short which_node) {
return talk_dlg.accepted() ? -1 : talk_edit_stack.top().first;
}
static void put_out_loc_in_dlog(cDialog& me, location cur_loc) {
static void put_out_loc_in_dlog(cDialog& me, location cur_loc, cScenario& scenario) {
std::ostringstream str;
str << "X = " << cur_loc.x;
me["x"].setText(str.str());
@@ -1131,7 +1131,7 @@ static void put_out_loc_in_dlog(cDialog& me, location cur_loc) {
me["title"].setText(scenario.outdoors[cur_loc.x][cur_loc.y]->out_name);
}
static bool pick_out_event_filter(cDialog& me, std::string item_hit, location& cur_loc) {
static bool pick_out_event_filter(cDialog& me, std::string item_hit, location& cur_loc, cScenario& scenario) {
if(item_hit == "xminus") {
if(cur_loc.x == 0) beep();
else cur_loc.x--;
@@ -1145,12 +1145,16 @@ static bool pick_out_event_filter(cDialog& me, std::string item_hit, location& c
if(cur_loc.y >= scenario.outdoors.height() - 1) beep();
else cur_loc.y++;
} else if(item_hit == "choose") {
int i = cur_loc.x * scenario.outdoors.width() + cur_loc.y;
int i = cur_loc.x * scenario.outdoors.height() + cur_loc.y;
if(&scenario != &::scenario)
scenario.outdoors.swap(::scenario.outdoors);
i = choose_text(STRT_SECTOR, i, &me, "Which sector?");
cur_loc.x = i / scenario.outdoors.width();
cur_loc.y = i % scenario.outdoors.width();
if(&scenario != &::scenario)
scenario.outdoors.swap(::scenario.outdoors);
cur_loc.x = i / scenario.outdoors.height();
cur_loc.y = i % scenario.outdoors.height();
}
put_out_loc_in_dlog(me, cur_loc);
put_out_loc_in_dlog(me, cur_loc, scenario);
return true;
}
@@ -1161,25 +1165,28 @@ static bool finish_pick_out(cDialog& me, bool okay, location& cur_loc, location
return true;
}
location pick_out(location default_loc) {
location pick_out(location default_loc,cScenario& scenario) {
using namespace std::placeholders;
location prev_loc = default_loc;
if(default_loc.x < 0)
default_loc.x = 0;
if(default_loc.y < 0)
default_loc.y = 0;
cDialog out_dlg("select-sector");
out_dlg["okay"].attachClickHandler(std::bind(finish_pick_out, _1, true, std::ref(default_loc), prev_loc));
out_dlg["cancel"].attachClickHandler(std::bind(finish_pick_out, _1, false, std::ref(default_loc), prev_loc));
out_dlg.attachClickHandlers(std::bind(pick_out_event_filter, _1, _2, std::ref(default_loc)), {"xplus", "xminus", "yplus", "yminus", "choose"});
out_dlg.attachClickHandlers(std::bind(pick_out_event_filter, _1, _2, std::ref(default_loc), std::ref(scenario)), {"xplus", "xminus", "yplus", "yminus", "choose"});
out_dlg["width"].setTextToNum(scenario.outdoors.width());
out_dlg["height"].setTextToNum(scenario.outdoors.height());
put_out_loc_in_dlog(out_dlg, default_loc);
put_out_loc_in_dlog(out_dlg, default_loc, scenario);
out_dlg.run();
return default_loc;
}
bool new_town(short which_town) {
std::cout << "Town creation currently disabled.\n";
short i,j;
cChoiceDlog new_dlg("new-town", {"okay", "cancel"});
@@ -1222,13 +1229,171 @@ void delete_last_town() {
delete last_town;
}
cTown* pick_import_town(short def) {
cTown* pick_import_town() {
cScenario temp_scenario;
fs::path path = nav_get_scenario();
load_scenario(path, temp_scenario);
short town_num = pick_town_num("select-import-town", def, temp_scenario);
short town_num = pick_town_num("select-import-town", 0, temp_scenario);
if(town_num < 0) return nullptr;
cTown* town = temp_scenario.towns[town_num];
temp_scenario.towns[town_num] = nullptr;
return town;
}
using loc_set = std::set<location, loc_compare>;
static void fill_resize_outdoors(cDialog& me, int top, int left, int right, int bottom, loc_set& del) {
me["top"].setTextToNum(top);
me["left"].setTextToNum(left);
me["right"].setTextToNum(right);
me["bottom"].setTextToNum(bottom);
me["w-new"].setTextToNum(scenario.outdoors.width() + left + right);
me["h-new"].setTextToNum(scenario.outdoors.height() + top + bottom);
if(left >= 0 && right >= 0 && top >= 0 && bottom >= 0)
me["delete"].setText("(none)");
else {
del.clear();
while(left < 0) {
for(int y = 0; y < scenario.outdoors.height(); y++)
del.insert(loc(-left - 1, y));
left++;
}
while(top < 0) {
for(int x = 0; x < scenario.outdoors.width(); x++)
del.insert(loc(x, -top - 1));
top++;
}
while(right < 0) {
for(int y = 0; y < scenario.outdoors.height(); y++)
del.insert(loc(scenario.outdoors.width() + right, y));
right++;
}
while(bottom < 0) {
for(int x = 0; x < scenario.outdoors.width(); x++)
del.insert(loc(x, scenario.outdoors.height() + bottom));
bottom++;
}
std::ostringstream list;
for(location l : del) {
list << '(' << l.x << ", " << l.y << ") " << scenario.outdoors[l.x][l.y]->out_name << '|';
}
me["delete"].setText(list.str());
}
}
static bool resize_outdoors_filter(cDialog& me, std::string btn, rectangle& mod, loc_set& del) {
int dir = btn.back() == 'p' ? 1 : -1;
btn.erase(btn.end() - 2, btn.end());
// Make sure the change won't put the width/height below 1 or over 50
if(btn == "top" || btn == "bottom") {
int h = scenario.outdoors.height() + mod.top + mod.bottom;
if((dir == 1 && h >= 50) || (dir == -1 && h <= 1)) {
beep();
return true;
}
} else if(btn == "left" || btn == "right") {
int w = scenario.outdoors.width() + mod.left + mod.right;
if((dir == 1 && w >= 50) || (dir == -1 && w <= 1)) {
beep();
return true;
}
} else return true; // Should be unreachable
if(btn == "top")
mod.top += dir;
else if(btn == "left")
mod.left += dir;
else if(btn == "right")
mod.right += dir;
else if(btn == "bottom")
mod.bottom += dir;
fill_resize_outdoors(me, mod.top, mod.left, mod.right, mod.bottom, del);
return true;
}
bool resize_outdoors() {
using namespace std::placeholders;
loc_set del;
rectangle mod;
cDialog size_dlg("resize-outdoors");
size_dlg["w-old"].setTextToNum(scenario.outdoors.width());
size_dlg["h-old"].setTextToNum(scenario.outdoors.height());
fill_resize_outdoors(size_dlg, mod.top, mod.left, mod.right, mod.bottom, del);
size_dlg["okay"].attachClickHandler(std::bind(&cDialog::toast, &size_dlg, true));
size_dlg["cancel"].attachClickHandler(std::bind(&cDialog::toast, &size_dlg, false));
size_dlg.attachClickHandlers(std::bind(resize_outdoors_filter, _1, _2, std::ref(mod), std::ref(del)), {
"top-p", "top-m", "left-p", "left-m", "right-p", "right-m", "bottom-p", "bottom-m"
});
size_dlg.run();
if(!size_dlg.accepted()) return false;
for(location l : del) {
delete scenario.outdoors[l.x][l.y];
scenario.outdoors[l.x][l.y] = nullptr;
}
size_t old_w = scenario.outdoors.width(), old_h = scenario.outdoors.height();
size_t new_w = old_w + mod.left + mod.right, new_h = old_h + mod.top + mod.bottom;
if(new_w > old_w)
scenario.outdoors.resize(new_w, scenario.outdoors.height());
if(new_h > old_h)
scenario.outdoors.resize(scenario.outdoors.width(), new_h);
if(mod.top > 0) {
int y = new_h - mod.top;
while(y--) {
scenario.outdoors.row(y + mod.top) = std::move(scenario.outdoors.row(y));
}
} else if(mod.top < 0) {
for(int y = -mod.top; y < old_h; y++) {
scenario.outdoors.row(y + mod.top) = std::move(scenario.outdoors.row(y));
}
}
if(mod.left > 0) {
int x = new_w - mod.left;
while(x--) {
scenario.outdoors.col(x + mod.left) = std::move(scenario.outdoors.col(x));
}
} else if(mod.left < 0) {
for(int x = -mod.left; x < old_w; x++) {
scenario.outdoors.col(x + mod.left) = std::move(scenario.outdoors.col(x));
}
}
scenario.outdoors.resize(new_w, new_h);
for(int y = 0; y < new_h; y++) {
for(int x = 0; x < new_w; x++) {
if(scenario.outdoors[x][y] == nullptr)
scenario.outdoors[x][y] = new cOutdoors(scenario);
}
}
// Update current location - point to same sector if possible
cur_out.x += mod.left;
cur_out.y += mod.top;
if(cur_out.x >= new_w || cur_out.x < 0)
cur_out.x = 0;
if(cur_out.y >= new_h || cur_out.y < 0)
cur_out.y = 0;
current_terrain = scenario.outdoors[cur_out.x][cur_out.y];
return true;
}
cOutdoors* pick_import_out() {
cScenario temp_scenario;
fs::path path = nav_get_scenario();
load_scenario(path, temp_scenario);
location sector = pick_out({-1,-1},temp_scenario);
if(sector.x < 0 && sector.y < 0)
return nullptr;
cOutdoors* out = temp_scenario.outdoors[sector.x][sector.y];
temp_scenario.outdoors[sector.x][sector.y] = nullptr;
return out;
}

View File

@@ -11,9 +11,11 @@ void edit_town_events();
void edit_advanced_town();
void edit_basic_dlog(short which_node);
short edit_talk_node(short which_node);
location pick_out(location default_loc);
cTown* pick_import_town(short def);
location pick_out(location default_loc,cScenario& scenario);
cTown* pick_import_town();
cOutdoors* pick_import_out();
bool new_town(short which_town);
bool resize_outdoors();
void edit_placed_item(short which_i);
void delete_last_town();

View File

@@ -160,10 +160,3 @@ void map_data::writeTo(std::ostream& out) {
out << '\n';
}
}
bool loc_compare::operator()(location a, location b) const {
// This is just a lexicographical ordering.
if(a.x != b.x) return a.x < b.x;
if(a.y != b.y) return a.y < b.y;
return false;
}

View File

@@ -32,10 +32,6 @@ enum class eMapFeature {
FIELD,
};
struct loc_compare {
bool operator()(location a, location b) const;
};
class map_data {
std::vector<std::vector<int>> grid;
using feature_t = std::pair<eMapFeature,int>;

View File

@@ -11,8 +11,6 @@
// Tried using boost::multi_array, but it kept causing weird issues, so I decided to make my own.
// TODO: Fill out missing members (should have equivalents for most of the stuff in std::vector)
// TODO: Add row-wise access using the included row_ref, and support for row/column assignment
// (These would be very useful for implementing a resize outdoors function in the scenario editor.)
#include <vector>
@@ -33,6 +31,26 @@ public:
Type& operator[](size_t x) {
return ref.data[ref.w * y + x];
}
const Type& operator[](size_t x) const {
return ref.data[ref.w * y + x];
}
row_ref operator=(row_ref&& other) {
row_ref& me = *this;
for(int i = 0; i < ref.width(); i++) {
me[i] = std::move(other[i]);
other[i] = Type();
}
return me;
}
row_ref operator=(const row_ref& other) {
row_ref& me = *this;
for(int i = 0; i < ref.width(); i++) {
me[i] = other[i];
}
return me;
}
// Seems like defining a move assignment operator deletes the copy constructor. Don't want that.
row_ref(const row_ref&) = default;
};
class col_ref {
friend class vector2d<Type, Alloc>;
@@ -43,6 +61,26 @@ public:
Type& operator[](size_t y) {
return ref.data[ref.w * y + x];
}
const Type& operator[](size_t y) const {
return ref.data[ref.w * y + x];
}
col_ref operator=(col_ref&& other) {
col_ref& me = *this;
for(int i = 0; i < ref.height(); i++) {
me[i] = std::move(other[i]);
other[i] = Type();
}
return me;
}
col_ref operator=(const col_ref& other) {
col_ref& me = *this;
for(int i = 0; i < ref.height(); i++) {
me[i] = other[i];
}
return me;
}
// Seems like defining a move assignment operator deletes the copy constructor. Don't want that.
col_ref(const col_ref&) = default;
};
class const_row_ref {
friend class vector2d<Type, Alloc>;
@@ -70,6 +108,18 @@ public:
const_col_ref operator[](size_t x) const {
return const_col_ref(*this, x);
}
col_ref col(size_t x) {
return col_ref(*this, x);
}
const_col_ref col(size_t x) const {
return const_col_ref(*this, x);
}
row_ref row(size_t x) {
return row_ref(*this, x);
}
const_row_ref row(size_t x) const {
return const_row_ref(*this, x);
}
size_t width() const {
return w;
}
@@ -86,8 +136,27 @@ public:
return data.size();
}
void resize(size_t width, size_t height) {
if(w > width) {
size_t dx = w - width;
for(int y = 1; y < h; y++) {
std::move(data.begin() + w * y, data.begin() + w * (y + 1) - dx, data.begin() + width * y);
}
}
size_t old_w = w, old_h = h;
w = width; h = height;
data.resize(w * h);
if(old_w < width) {
size_t dx = width - old_w;
for(int y = old_h - 1; y > 0; y--) {
std::move_backward(data.begin() + old_w * y, data.begin() + old_w * (y + 1), data.begin() + w * (y + 1) - dx);
std::fill_n(data.begin() + old_w + w * (y - 1), dx, Type());
}
}
}
void swap(vector2d& other) {
data.swap(other.data);
std::swap(w, other.w);
std::swap(h, other.h);
}
bool empty() const {
return data.empty();