Files
oboe/src/scenedit/scen.actions.cpp

2887 lines
94 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <cmath>
#include <cstdio>
#include <cstring>
#include <array>
#include <string>
#include <stack>
#include <vector>
#include <boost/lexical_cast.hpp>
#include <boost/variant.hpp>
#include "scen.global.hpp"
#include "scenario/scenario.hpp"
#include "gfx/render_shapes.hpp"
#include "scen.graphics.hpp"
#include "scen.actions.hpp"
#include "sounds.hpp"
#include "scen.core.hpp"
#include "scen.fileio.hpp"
#include "scen.keydlgs.hpp"
#include "scen.townout.hpp"
#include "scen.menus.hpp"
#include "mathutil.hpp"
#include "fileio/fileio.hpp"
#include "tools/keymods.hpp"
#include "tools/winutil.hpp"
#include "tools/cursors.hpp"
#include "dialogxml/widgets/scrollbar.hpp"
#include "dialogxml/dialogs/strdlog.hpp"
#include "dialogxml/dialogs/strchoice.hpp"
#include "dialogxml/dialogs/choicedlog.hpp"
#ifndef MSBUILD_GITREV
#include "tools/gitrev.hpp"
#endif
#include "scen.btnmg.hpp"
extern std::string current_string[2];
extern short mini_map_scales[3];
extern rectangle terrain_rect;
extern eDrawMode draw_mode;
// border rects order: top, left, bottom, right //
rectangle border_rect[4];
short current_block_edited = 0;
short current_terrain_type = 0;
short safety = 0;
location spot_hit,last_spot_hit(-1,-1),mouse_spot(-1,-1);
cUndoList undo_list;
short flood_count = 0;
rectangle terrain_rects[256],terrain_rect_base = {0,0,16,16},command_rects[21];
extern rectangle terrain_buttons_rect;
extern short cen_x, cen_y, cur_town;
extern eScenMode overall_mode;
extern bool mouse_button_held,editing_town;
extern short cur_viewing_mode;
extern cTown* town;
extern short mode_count,to_create;
extern ter_num_t template_terrain[64][64];
extern cScenario scenario;
extern std::shared_ptr<cScrollbar> right_sbar, pal_sbar;
extern cOutdoors* current_terrain;
extern location cur_out;
bool small_any_drawn = false;
extern bool change_made;
rectangle left_buttons[NLS][2]; // 0 - whole, 1 - blue button
std::array<lb_t,NLS> left_button_status;
std::vector<rb_t> right_button_status;
rectangle right_buttons[NRSONPAGE];
rectangle palette_buttons[10][6];
short current_rs_top = 0;
ePalBtn out_buttons[6][10] = {
{PAL_PENCIL, PAL_BRUSH_LG, PAL_BRUSH_SM, PAL_SPRAY_LG, PAL_SPRAY_SM, PAL_ERASER, PAL_RECT_HOLLOW, PAL_RECT_FILLED, PAL_BUCKET, PAL_DROPPER},
{PAL_EDIT_TOWN, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_EDIT_SIGN, PAL_TEXT_AREA, PAL_WANDER, PAL_START, PAL_ZOOM},
{PAL_SPEC, PAL_COPY_SPEC, PAL_ERASE_SPEC, PAL_EDIT_SPEC, PAL_SPEC_SPOT, PAL_BOAT, PAL_HORSE, PAL_COPY_TER, PAL_CHANGE, PAL_PASTE},
{PAL_ROAD, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK},
{PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK},
{PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK, PAL_BLANK},
};
ePalBtn town_buttons[6][10] = {
{PAL_PENCIL, PAL_BRUSH_LG, PAL_BRUSH_SM, PAL_SPRAY_LG, PAL_SPRAY_SM, PAL_ERASER, PAL_RECT_HOLLOW, PAL_RECT_FILLED, PAL_BUCKET, PAL_DROPPER},
{PAL_ENTER_N, PAL_ENTER_W, PAL_ENTER_S, PAL_ENTER_E, PAL_TOWN_BORDER, PAL_EDIT_SIGN, PAL_TEXT_AREA, PAL_WANDER, PAL_START, PAL_ZOOM},
{PAL_SPEC, PAL_COPY_SPEC, PAL_ERASE_SPEC, PAL_EDIT_SPEC, PAL_SPEC_SPOT, PAL_BOAT, PAL_HORSE, PAL_COPY_TER, PAL_CHANGE, PAL_TERRAIN},
{PAL_ROAD, PAL_WEB, PAL_CRATE, PAL_BARREL, PAL_BLOCK, PAL_EDIT_STORAGE, PAL_EDIT_ITEM, PAL_COPY_ITEM, PAL_ERASE_ITEM, PAL_ITEM},
{PAL_FIRE_BARR, PAL_FORCE_BARR, PAL_QUICKFIRE, PAL_FORCECAGE, PAL_BLANK, PAL_PASTE, PAL_EDIT_MONST, PAL_COPY_MONST, PAL_ERASE_MONST, PAL_MONST},
{PAL_SFX_SB, PAL_SFX_MB, PAL_SFX_LB, PAL_SFX_SS, PAL_SFX_LS, PAL_SFX_ASH, PAL_SFX_BONE, PAL_SFX_ROCK, PAL_ERASE_FIELD, PAL_BLANK},
};
rectangle working_rect;
location last_space_hit;
bool erasing_mode;
ter_num_t current_ground = 0;
location last_placement{-1,-1};
boost::variant<
boost::none_t,
std::pair<long /* special */, bool /* town */>,
cTownperson /* monst */,
cTown::cItem /* item */,
vector2d<ter_num_t> /* terrain */
> clipboard = boost::none;
bool monst_on_space(location loc,short m_num);
static bool terrain_matches(unsigned char x, unsigned char y, ter_num_t ter);
bool check_for_interrupt(std::string); // to suppress "missing prototype" warning
bool check_for_interrupt(std::string) { return false; }
void init_screen_locs() {
for(short i = 0; i < 4; i++)
border_rect[i] = terrain_rect;
border_rect[0].bottom = border_rect[0].top + 8;
border_rect[1].right = border_rect[1].left + 8;
border_rect[2].top = border_rect[2].bottom - 8;
border_rect[3].left = border_rect[3].right - 8;
for(short i = 0; i < 256; i++) {
terrain_rects[i] = terrain_rect_base;
terrain_rects[i].offset(3 + (i % 16) * (terrain_rect_base.right + 1),
3 + (i / 16) * (terrain_rect_base.bottom + 1));
}
}
static cursor_type get_edit_cursor() {
switch(overall_mode) {
case MODE_INTRO_SCREEN: case MODE_MAIN_SCREEN: case MODE_EDIT_TYPES:
case MODE_PLACE_CREATURE: case MODE_PLACE_ITEM: case MODE_PLACE_SPECIAL:
case MODE_PLACE_WEB: case MODE_PLACE_CRATE: case MODE_PLACE_BARREL:
case MODE_PLACE_STONE_BLOCK: case MODE_PLACE_FIRE_BARRIER:
case MODE_PLACE_FORCE_BARRIER: case MODE_PLACE_QUICKFIRE:
case MODE_PLACE_FORCECAGE: case MODE_PLACE_SFX:
case MODE_PLACE_HORSE: case MODE_PLACE_BOAT:
case MODE_TOGGLE_SPECIAL_DOT: case MODE_TOGGLE_ROAD:
case MODE_DRAWING:
return wand_curs;
case MODE_LARGE_PAINTBRUSH: case MODE_SMALL_PAINTBRUSH:
return brush_curs;
case MODE_LARGE_SPRAYCAN: case MODE_SMALL_SPRAYCAN:
return spray_curs;
case MODE_FLOOD_FILL:
return bucket_curs;
case MODE_EYEDROPPER:
return eyedropper_curs;
case MODE_ROOM_RECT: case MODE_SET_TOWN_RECT:
case MODE_HOLLOW_RECT: case MODE_FILLED_RECT:
case MODE_STORAGE_RECT: case MODE_COPY_TERRAIN:
return mode_count == 2 ? topleft_curs : bottomright_curs;
case MODE_ERASE_CREATURE: case MODE_ERASE_ITEM:
case MODE_ERASE_SPECIAL:
case MODE_ERASER: case MODE_CLEAR_FIELDS:
return eraser_curs;
case MODE_EDIT_CREATURE: case MODE_EDIT_ITEM:
case MODE_EDIT_SPECIAL: case MODE_EDIT_TOWN_ENTRANCE:
case MODE_EDIT_SIGN:
case MODE_COPY_SPECIAL: case MODE_COPY_ITEM:
case MODE_COPY_CREATURE: case MODE_PASTE:
case MODE_PLACE_EAST_ENTRANCE: case MODE_PLACE_NORTH_ENTRANCE:
case MODE_PLACE_SOUTH_ENTRANCE: case MODE_PLACE_WEST_ENTRANCE:
case MODE_SET_OUT_START: case MODE_SET_TOWN_START:
case MODE_SET_WANDER_POINTS:
return hand_curs;
}
return wand_curs;
}
void update_mouse_spot(location the_point) {
rectangle terrain_rect = ::terrain_rect;
terrain_rect.inset(8,8);
terrain_rect.right -= 4;
if(overall_mode >= MODE_MAIN_SCREEN)
set_cursor(sword_curs);
else if(terrain_rect.contains(the_point)) {
set_cursor(get_edit_cursor());
if(cur_viewing_mode == 0) {
mouse_spot.x = (the_point.x - TER_RECT_UL_X - 8) / 28;
mouse_spot.y = (the_point.y - TER_RECT_UL_Y - 8) / 36;
} else {
short scale = mini_map_scales[cur_viewing_mode - 1];
mouse_spot.x = (the_point.x - TER_RECT_UL_X - 8) / scale;
mouse_spot.y = (the_point.y - TER_RECT_UL_Y - 8) / scale;
}
} else {
mouse_spot = {-1,-1};
the_point.x -= RIGHT_AREA_UL_X;
the_point.y -= RIGHT_AREA_UL_Y;
rectangle terpal_rect = terrain_rects[0];
terpal_rect.right = terrain_rects[255].right;
terpal_rect.bottom = terrain_rects[255].bottom;
if(terpal_rect.contains(the_point))
set_cursor(eyedropper_curs);
else set_cursor(wand_curs);
}
if(overall_mode < MODE_MAIN_SCREEN) place_location();
}
static bool handle_lb_action(location the_point) {
fs::path file_to_load;
int x;
for(int i = 0; i < NLS; i++)
if(!mouse_button_held && the_point.in(left_buttons[i][0])
&& (left_button_status[i].action != LB_NO_ACTION)) {
draw_lb_slot(i,1);
play_sound(37);
mainPtr().display();
// TODO: Proper button handling
sf::sleep(time_in_ticks(10));
draw_lb_slot(i,0);
mainPtr().display();
if(overall_mode == MODE_INTRO_SCREEN || overall_mode == MODE_MAIN_SCREEN || overall_mode == MODE_EDIT_TYPES) {
switch(left_button_status[i].action) {
case LB_NO_ACTION:
break;
case LB_RETURN: // Handled separately, below
break;
case LB_NEW_SCEN:
if(build_scenario()){
overall_mode = MODE_MAIN_SCREEN;
set_up_main_screen();
}
break;
case LB_LOAD_SCEN:
file_to_load = nav_get_scenario();
if(!file_to_load.empty() && load_scenario(file_to_load, scenario)) {
restore_editor_state();
} else if(!file_to_load.empty())
// If we tried to load but failed, the scenario record is messed up, so boot to start screen.
set_up_start_screen();
break;
case LB_EDIT_TER:
start_terrain_editing();
break;
case LB_EDIT_MONST:
start_monster_editing(0);
break;
case LB_EDIT_ITEM:
start_item_editing(0);
break;
case LB_NEW_TOWN:
if(scenario.towns.size() >= 200) {
showError("You have reached the limit of 200 towns you can have in one scenario.");
return true;
}
if(new_town())
set_up_main_screen();
break;
case LB_EDIT_TEXT:
right_sbar->setPosition(0);
start_string_editing(STRS_SCEN,0);
break;
case LB_EDIT_SPECITEM:
start_special_item_editing(false);
break;
case LB_EDIT_QUEST:
start_quest_editing(false);
break;
case LB_EDIT_SHOPS:
start_shops_editing(false);
break;
case LB_LOAD_OUT:
spot_hit = pick_out(cur_out, scenario);
if(spot_hit != cur_out) {
set_current_out(spot_hit);
}
break;
case LB_EDIT_OUT:
start_out_edit();
mouse_button_held = false;
break;
case LB_LOAD_TOWN:
x = pick_town_num("select-town-edit",cur_town,scenario);
if(x >= 0){
cur_town = x;
town = scenario.towns[cur_town];
set_up_main_screen();
}
break;
case LB_EDIT_TOWN:
start_town_edit();
mouse_button_held = false;
break;
case LB_EDIT_TALK:
start_dialogue_editing(0);
break;
}
}
if((overall_mode < MODE_MAIN_SCREEN) && left_button_status[i].action == LB_RETURN) {
set_up_main_screen();
}
mouse_button_held = false;
update_edit_menu();
return true;
}
return false;
}
static bool handle_rb_action(location the_point, bool option_hit) {
long right_top = right_sbar->getPosition();
for(int i = 0; i < NRSONPAGE && i + right_top < NRS; i++)
if(!mouse_button_held && (the_point.in(right_buttons[i]) )
&& (right_button_status[i + right_top].action != RB_CLEAR)) {
int j = right_button_status[i + right_top].i;
//flash_rect(left_buttons[i][0]);
draw_rb_slot(i + right_top,1);
mainPtr().display();
// TODO: Proper button handling
play_sound(37);
sf::sleep(time_in_ticks(10));
draw_rb_slot(i + right_top,0);
mainPtr().display();
change_made = true;
size_t size_before;
size_t pos_before = right_sbar->getPosition();
switch(right_button_status[i + right_top].action) {
case RB_CLEAR:
break;
case RB_MONST:
size_before = scenario.scen_monsters.size();
if(option_hit) {
if(j == size_before - 1 && !scenario.is_monst_used(j))
scenario.scen_monsters.pop_back();
else if(j == size_before) {
scenario.scen_monsters.resize(size_before + 8);
for(; j < scenario.scen_monsters.size(); j++)
scenario.scen_monsters[j].m_name = "New Monster";
} else {
scenario.scen_monsters[j] = cMonster();
scenario.scen_monsters[j].m_name = "Unused Monster";
}
} else {
if(j == size_before) {
scenario.scen_monsters.emplace_back();
scenario.scen_monsters.back().m_name = "New Monster";
}
if(!edit_monst_type(j) && j == size_before && scenario.scen_monsters.back().m_name == "New Monster")
scenario.scen_monsters.pop_back();
}
start_monster_editing(size_before == scenario.scen_monsters.size());
if(size_before > scenario.scen_monsters.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_ITEM:
size_before = scenario.scen_items.size();
if(option_hit) {
if(j == size_before - 1 && !scenario.is_item_used(j))
scenario.scen_items.pop_back();
else if(j == size_before) {
scenario.scen_items.resize(size_before + 8);
for(; j < scenario.scen_items.size(); j++) {
scenario.scen_items[j].full_name = "New Item";
scenario.scen_items[j].name = "Item";
}
} else {
scenario.scen_items[j] = cItem();
scenario.scen_items[j].full_name = "Unused Item";
scenario.scen_items[j].name = "Item";
}
} else {
if(j == size_before) {
scenario.scen_items.emplace_back();
scenario.scen_items.back().full_name = "New Item";
scenario.scen_items.back().name = "Item";
}
if(!edit_item_type(j) && j == size_before && scenario.scen_items.back().name == "New Item")
scenario.scen_items.pop_back();
}
start_item_editing(size_before == scenario.scen_items.size());
if(size_before > scenario.scen_items.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_SCEN_SPEC:
size_before = scenario.scen_specials.size();
if(option_hit) {
if(j == size_before - 1)
scenario.scen_specials.pop_back();
else if(j == size_before)
break;
else scenario.scen_specials[j] = cSpecial();
} else {
if(j == size_before)
scenario.scen_specials.emplace_back();
edit_spec_enc(j,0,nullptr);
}
start_special_editing(0,size_before == scenario.scen_specials.size());
if(size_before > scenario.scen_specials.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_OUT_SPEC:
size_before = current_terrain->specials.size();
if(option_hit) {
if(j == size_before - 1)
current_terrain->specials.pop_back();
else if(j == size_before)
break;
else current_terrain->specials[j] = cSpecial();
} else {
if(j == size_before)
current_terrain->specials.emplace_back();
edit_spec_enc(j,1,nullptr);
}
start_special_editing(1,size_before == current_terrain->specials.size());
if(size_before > current_terrain->specials.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_TOWN_SPEC:
size_before = town->specials.size();
if(option_hit) {
if(j == size_before - 1)
town->specials.pop_back();
else if(j == size_before)
break;
else town->specials[j] = cSpecial();
} else {
if(j == size_before)
town->specials.emplace_back();
edit_spec_enc(j,2,nullptr);
}
start_special_editing(2,size_before == town->specials.size());
if(size_before > town->specials.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_SCEN_STR:
size_before = scenario.spec_strs.size();
if(option_hit) {
if(j == size_before - 1)
scenario.spec_strs.pop_back();
else if(j == size_before)
scenario.spec_strs.resize(size_before + 8, "*");
else scenario.spec_strs[j] = "*";
} else {
if(j == size_before)
scenario.spec_strs.emplace_back("*");
if(!edit_text_str(j,STRS_SCEN) && j == size_before && scenario.spec_strs[j] == "*")
scenario.spec_strs.pop_back();
}
start_string_editing(STRS_SCEN,size_before == scenario.spec_strs.size());
if(size_before > scenario.spec_strs.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_OUT_STR:
size_before = current_terrain->spec_strs.size();
if(option_hit) {
if(j == size_before - 1)
current_terrain->spec_strs.pop_back();
else if(j == size_before)
current_terrain->spec_strs.resize(size_before + 8, "*");
else current_terrain->spec_strs[j] = "*";
} else {
if(j == size_before)
current_terrain->spec_strs.emplace_back("*");
if(!edit_text_str(j,STRS_OUT) && j == size_before && current_terrain->spec_strs[j] == "*")
current_terrain->spec_strs.pop_back();
}
start_string_editing(STRS_OUT,size_before == current_terrain->spec_strs.size());
if(size_before > current_terrain->spec_strs.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_TOWN_STR:
size_before = town->spec_strs.size();
if(option_hit) {
if(j == size_before - 1)
town->spec_strs.pop_back();
else if(j == size_before)
town->spec_strs.resize(size_before + 8, "*");
else town->spec_strs[j] = "*";
} else {
if(j == size_before)
town->spec_strs.emplace_back("*");
if(!edit_text_str(j,STRS_TOWN) && j == size_before && town->spec_strs[j] == "*")
town->spec_strs.pop_back();
}
start_string_editing(STRS_TOWN,size_before == town->spec_strs.size());
if(size_before > town->spec_strs.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_SPEC_ITEM:
size_before = scenario.special_items.size();
if(option_hit) {
if(j == size_before - 1)
scenario.special_items.pop_back();
else if(j == size_before)
break;
else {
scenario.special_items[j] = cSpecItem();
scenario.special_items[j].name = "Unused Special Item";
}
} else {
if(j == size_before) {
scenario.special_items.emplace_back();
scenario.special_items.back().name = "New Special Item";
}
if(!edit_spec_item(j) && j == size_before)
scenario.special_items.pop_back();
}
start_special_item_editing(size_before == scenario.special_items.size());
if(size_before > scenario.special_items.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_JOURNAL:
size_before = scenario.journal_strs.size();
if(option_hit) {
if(j == size_before - 1)
scenario.journal_strs.pop_back();
else if(j == size_before)
scenario.journal_strs.resize(size_before + 8, "*");
else scenario.journal_strs[j] = "*";
} else {
if(j == size_before)
scenario.journal_strs.emplace_back("*");
if(!edit_text_str(j,STRS_JOURNAL) && j == size_before && scenario.journal_strs[j] == "*")
scenario.journal_strs.pop_back();
}
start_string_editing(STRS_JOURNAL,size_before == scenario.journal_strs.size());
if(size_before > scenario.journal_strs.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_DIALOGUE:
size_before = town->talking.talk_nodes.size();
if(option_hit) {
if(j == size_before)
break;
town->talking.talk_nodes.erase(town->talking.talk_nodes.begin() + j);
} else {
if(j == size_before)
town->talking.talk_nodes.emplace_back();
if((j = edit_talk_node(j)) >= 0 && town->talking.talk_nodes[j].personality == -1)
town->talking.talk_nodes.erase(town->talking.talk_nodes.begin() + j);
}
start_dialogue_editing(size_before == town->talking.talk_nodes.size());
if(size_before > town->talking.talk_nodes.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_PERSONALITY:
edit_basic_dlog(j);
start_dialogue_editing(1);
break;
case RB_OUT_SIGN:
size_before = current_terrain->sign_locs.size();
if(option_hit) {
if(j == size_before - 1)
current_terrain->sign_locs.pop_back();
else if(j == size_before)
break;
else current_terrain->sign_locs[j] = {-1, -1, "*"};
} else {
if(j == size_before)
current_terrain->sign_locs.emplace_back(-1,-1,"*");
if(!edit_text_str(j,STRS_OUT_SIGN) && j == size_before && current_terrain->sign_locs[j].text == "*")
current_terrain->sign_locs.pop_back();
}
start_string_editing(STRS_OUT_SIGN,size_before == current_terrain->sign_locs.size());
if(size_before > current_terrain->sign_locs.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_TOWN_SIGN:
size_before = town->sign_locs.size();
if(option_hit) {
if(j == size_before - 1)
town->sign_locs.pop_back();
else if(j == size_before)
break;
else town->sign_locs[j] = {-1, -1, "*"};
} else {
if(j == size_before)
town->sign_locs.emplace_back(-1,-1,"*");
if(!edit_text_str(j,STRS_TOWN_SIGN) && j == size_before && town->sign_locs[j].text == "*")
town->sign_locs.pop_back();
}
start_string_editing(STRS_TOWN_SIGN,size_before == town->sign_locs.size());
if(size_before > town->sign_locs.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_OUT_RECT:
size_before = current_terrain->area_desc.size();
if(option_hit) {
if(j == size_before - 1)
current_terrain->area_desc.pop_back();
else if(j == size_before)
break;
else current_terrain->area_desc[j] = {0, 0, 0, 0, "*"};
} else {
if(j == size_before)
current_terrain->area_desc.emplace_back(0,0,0,0,"*");
if(!edit_text_str(j,STRS_OUT_RECT) && j == size_before && current_terrain->area_desc[j].descr == "*")
current_terrain->area_desc.pop_back();
}
start_string_editing(STRS_OUT_RECT,size_before == current_terrain->area_desc.size());
if(size_before > current_terrain->area_desc.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_TOWN_RECT:
size_before = town->area_desc.size();
if(option_hit) {
if(j == size_before - 1)
town->area_desc.pop_back();
else if(j == size_before)
break;
else town->area_desc[j] = {0, 0, 0, 0, "*"};
} else {
if(j == size_before)
town->area_desc.emplace_back(0,0,0,0,"*");
if(!edit_text_str(j,STRS_TOWN_RECT) && j == size_before && town->area_desc[j].descr == "*")
town->area_desc.pop_back();
}
start_string_editing(STRS_TOWN_RECT,size_before == town->area_desc.size());
if(size_before > town->area_desc.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_QUEST:
size_before = scenario.quests.size();
if(option_hit) {
if(j == scenario.quests.size() - 1)
scenario.quests.pop_back();
else {
scenario.quests[j] = cQuest();
scenario.quests[j].name = "Unused Quest";
}
} else {
if(!edit_quest(j) && j == size_before && scenario.quests[j].name == "New Quest")
scenario.quests.pop_back();
}
start_quest_editing(size_before == scenario.quests.size());
if(size_before > scenario.quests.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
case RB_SHOP:
size_before = scenario.shops.size();
if(option_hit) {
if(j == scenario.shops.size() - 1)
scenario.shops.pop_back();
else scenario.shops[j] = cShop("Unused Shop");
} else {
if(!edit_shop(j) && j == size_before && scenario.shops[j].getName() == "New Shop")
scenario.shops.pop_back();
}
start_shops_editing(size_before == scenario.shops.size());
if(size_before > scenario.shops.size())
pos_before--;
right_sbar->setPosition(pos_before);
break;
}
change_made = true;
mouse_button_held = false;
return true;
}
return false;
}
static bool handle_terrain_action(location the_point, bool ctrl_hit) {
cArea* cur_area = get_current_area();
if(mouse_spot.x >= 0 && mouse_spot.y >= 0) {
if(cur_viewing_mode == 0) {
spot_hit.x = cen_x + mouse_spot.x - 4;
spot_hit.y = cen_y + mouse_spot.y - 4;
if((mouse_spot.x < 0) || (mouse_spot.x > 8) || (mouse_spot.y < 0) || (mouse_spot.y > 8))
spot_hit.x = -1;
}
else {
short scale = mini_map_scales[cur_viewing_mode - 1];
if(scale > 4) {
if(cen_x + 5 > 256 / scale)
spot_hit.x = cen_x + 5 - 256/scale + mouse_spot.x;
else spot_hit.x = mouse_spot.x;
if(cen_y + 5 > 324 / scale)
spot_hit.y = cen_y + 5 - 324/scale + mouse_spot.y;
else spot_hit.y = mouse_spot.y;
} else {
spot_hit.x = mouse_spot.x;
spot_hit.y = mouse_spot.y;
}
}
if((mouse_button_held) && (spot_hit.x == last_spot_hit.x) &&
(spot_hit.y == last_spot_hit.y))
return true;
else last_spot_hit = spot_hit;
if(!mouse_button_held)
last_spot_hit = spot_hit;
eScenMode old_mode = overall_mode;
change_made = true;
if(!cur_area->is_on_map(spot_hit)) ;
else switch(overall_mode) {
case MODE_DRAWING:
if(!mouse_button_held) {
erasing_mode = terrain_matches(spot_hit.x, spot_hit.y, current_terrain_type);
mouse_button_held = true;
}
if(!editing_town) {
// Implicitly erase town entrances when a space is set from a town terrain to a non-town terrain
const cTerrain& paint_ter = scenario.ter_types[current_terrain_type];
const cTerrain& erase_ter = scenario.ter_types[current_ground];
const cTerrain& cur_ter = scenario.ter_types[cur_area->terrain(spot_hit.x, spot_hit.y)];
if(cur_ter.special == eTerSpec::TOWN_ENTRANCE && (erasing_mode ? erase_ter : paint_ter).special != eTerSpec::TOWN_ENTRANCE)
for(short i = current_terrain->city_locs.size() - 1; i >= 0; i--) {
if(current_terrain->city_locs[i] == spot_hit)
current_terrain->city_locs.erase(current_terrain->city_locs.begin() + i);
}
}
if(erasing_mode) set_terrain(spot_hit,current_ground);
else set_terrain(spot_hit,current_terrain_type);
break;
case MODE_ROOM_RECT: case MODE_SET_TOWN_RECT: case MODE_HOLLOW_RECT: case MODE_FILLED_RECT:
case MODE_STORAGE_RECT: case MODE_COPY_TERRAIN:
if(mouse_button_held)
break;
if(mode_count == 2) {
working_rect.left = spot_hit.x;
working_rect.top = spot_hit.y;
mode_count = 1;
set_string("Now select lower right corner","");
break;
}
working_rect.right = spot_hit.x;
working_rect.bottom = spot_hit.y;
if((overall_mode == 1) || (overall_mode == MODE_FILLED_RECT)) {
change_rect_terrain(working_rect,current_terrain_type,20,0);
change_made = true;
}
else if(overall_mode == MODE_HOLLOW_RECT) {
change_rect_terrain(working_rect,current_terrain_type,20,1);
change_made = true;
}
else if(overall_mode == MODE_SET_TOWN_RECT) {
town->in_town_rect = working_rect;
change_made = true;
}
else if(overall_mode == MODE_ROOM_RECT) {
auto& area_descs = cur_area->area_desc;
auto iter = std::find_if(area_descs.begin(), area_descs.end(), [](const info_rect_t& r) {
return r.right == 0;
});
if(iter != area_descs.end()) {
static_cast<rectangle&>(*iter) = working_rect;
iter->descr = "";
if(!edit_area_rect_str(*iter))
iter->right = 0;
} else {
area_descs.emplace_back(working_rect);
if(!edit_area_rect_str(area_descs.back()))
area_descs.pop_back();
}
change_made = true;
}
else if(overall_mode == MODE_STORAGE_RECT) {
scenario.store_item_rects[cur_town] = working_rect;
change_made = true;
}
else if(overall_mode == MODE_COPY_TERRAIN) {
vector2d<ter_num_t> copied;
copied.resize(working_rect.width() + 1, working_rect.height() + 1);
for(int i = 0; i <= working_rect.width(); i++) {
for(int j = 0; j <= working_rect.height(); j++) {
if(editing_town) copied[i][j] = town->terrain(i + working_rect.left, j + working_rect.top);
else copied[i][j] = current_terrain->terrain(i + working_rect.left, j + working_rect.top);
}
}
clipboard = copied;
}
overall_mode = MODE_DRAWING;
break;
case MODE_SET_WANDER_POINTS:
if(mouse_button_held)
break;
if(editing_town) {
town->wandering_locs[mode_count - 1].x = spot_hit.x;
town->wandering_locs[mode_count - 1].y = spot_hit.y;
} else {
current_terrain->wandering_locs[mode_count - 1].x = spot_hit.x;
current_terrain->wandering_locs[mode_count - 1].y = spot_hit.y;
}
mode_count--;
switch(mode_count) {
case 3:
set_string("Place second wandering monster arrival point","");
break;
case 2:
set_string("Place third wandering monster arrival point","");
break;
case 1:
set_string("Place fourth wandering monster arrival point","");
break;
case 0:
overall_mode = MODE_DRAWING;
set_string("Drawing mode",scenario.ter_types[current_terrain_type].name);
break;
}
break;
case MODE_LARGE_PAINTBRUSH:
mouse_button_held = true;
change_circle_terrain(spot_hit,4,current_terrain_type,20);
break;
case MODE_SMALL_PAINTBRUSH:
mouse_button_held = true;
change_circle_terrain(spot_hit,1,current_terrain_type,20);
break;
case MODE_LARGE_SPRAYCAN:
mouse_button_held = true;
change_circle_terrain(spot_hit,4,current_terrain_type,1);
break;
case MODE_SMALL_SPRAYCAN:
mouse_button_held = true;
change_circle_terrain(spot_hit,2,current_terrain_type,1);
break;
case MODE_ERASER: // erase
change_circle_terrain(spot_hit,2,current_ground,20);
mouse_button_held = true;
break;
case MODE_FLOOD_FILL:
if(mouse_button_held) break;
flood_fill_terrain(spot_hit, current_terrain_type);
mouse_button_held = true;
break;
case MODE_PLACE_ITEM:
// If we just placed this item there, forget it
if(!mouse_button_held || last_placement != spot_hit) {
mouse_button_held = true;
auto iter = std::find_if(town->preset_items.begin(), town->preset_items.end(), [](const cTown::cItem& item) {
return item.code < 0;
});
if(iter != town->preset_items.end()) {
*iter = {spot_hit, mode_count, scenario.scen_items[mode_count]};
if(container_there(spot_hit)) iter->contained = true;
} else {
town->preset_items.push_back({spot_hit, mode_count, scenario.scen_items[mode_count]});
if(container_there(spot_hit)) town->preset_items.back().contained = true;
}
last_placement = spot_hit;
}
break;
case MODE_EDIT_ITEM:
for(short x = 0; x < town->preset_items.size(); x++)
if((spot_hit.x == town->preset_items[x].loc.x) &&
(spot_hit.y == town->preset_items[x].loc.y) && (town->preset_items[x].code >= 0)) {
edit_placed_item(x);
}
overall_mode = MODE_DRAWING;
break;
case MODE_PASTE:
if(auto spec = boost::get<std::pair<long,bool>>(&clipboard)) {
if(!editing_town && (spot_hit.x == 0 || spot_hit.x == 47 || spot_hit.y == 0 || spot_hit.y == 47)) {
cChoiceDlog("not-at-edge").show();
break;
} else {
auto& specials = cur_area->special_locs;
for(short x = 0; x <= specials.size(); x++) {
if(x == specials.size())
specials.emplace_back(-1,-1,-1);
if(specials[x].spec < 0) {
specials[x] = spot_hit;
specials[x].spec = spec->first;
break;
}
}
}
} else if(auto monst = boost::get<cTownperson>(&clipboard)) {
if(!editing_town) {
set_string("Paste monster","Not while outdoors.");
break;
}
auto iter = std::find_if(town->creatures.begin(), town->creatures.end(), [](const cTownperson& who) {
return who.number == 0;
});
if(iter != town->creatures.end()) {
*iter = *monst;
iter->start_loc = spot_hit;
} else { // Placement failed
town->creatures.push_back(*monst);
town->creatures.back().start_loc = spot_hit;
}
} else if(auto item = boost::get<cTown::cItem>(&clipboard)) {
if(!editing_town) {
set_string("Paste item","Not while outdoors.");
break;
}
auto iter = std::find_if(town->preset_items.begin(), town->preset_items.end(), [](const cTown::cItem& item) {
return item.code < 0;
});
if(iter != town->preset_items.end()) {
*iter = *item;
iter->loc = spot_hit;
iter->contained = container_there(spot_hit);
} else {
town->preset_items.push_back(*item);
town->preset_items.back().loc = spot_hit;
town->preset_items.back().contained = container_there(spot_hit);
}
} else if(auto patch = boost::get<vector2d<ter_num_t>>(&clipboard)) {
for(int x = 0; x < patch->width(); x++)
for(int y = 0; y < patch->height(); y++)
cur_area->terrain(spot_hit.x + x, spot_hit.y + y) = (*patch)[x][y];
} else {
showError("Nothing to paste. Try copying something first.");
}
overall_mode = MODE_DRAWING;
break;
case MODE_PLACE_CREATURE:
// If we just placed this same creature here, forget it
if(!mouse_button_held || last_placement != spot_hit) {
mouse_button_held = true;
auto iter = std::find_if(town->creatures.begin(), town->creatures.end(), [](const cTownperson& who) {
return who.number == 0;
});
if(iter != town->creatures.end()) {
*iter = {spot_hit, static_cast<mon_num_t>(mode_count), scenario.scen_monsters[mode_count]};
} else { // Placement failed
town->creatures.push_back({spot_hit, static_cast<mon_num_t>(mode_count), scenario.scen_monsters[mode_count]});
}
last_placement = spot_hit;
}
break;
case MODE_PLACE_NORTH_ENTRANCE: case MODE_PLACE_EAST_ENTRANCE:
case MODE_PLACE_SOUTH_ENTRANCE: case MODE_PLACE_WEST_ENTRANCE:
town->start_locs[overall_mode - 10].x = spot_hit.x;
town->start_locs[overall_mode - 10].y = spot_hit.y;
overall_mode = MODE_DRAWING;
break;
case MODE_PLACE_WEB:
make_field_type(spot_hit.x,spot_hit.y,FIELD_WEB);
mouse_button_held = true;
break;
case MODE_PLACE_CRATE:
make_field_type(spot_hit.x,spot_hit.y,OBJECT_CRATE);
mouse_button_held = true;
break;
case MODE_PLACE_BARREL:
make_field_type(spot_hit.x,spot_hit.y,OBJECT_BARREL);
mouse_button_held = true;
break;
case MODE_PLACE_FIRE_BARRIER:
make_field_type(spot_hit.x,spot_hit.y,BARRIER_FIRE);
mouse_button_held = true;
break;
case MODE_PLACE_FORCE_BARRIER:
make_field_type(spot_hit.x,spot_hit.y,BARRIER_FORCE);
mouse_button_held = true;
break;
case MODE_PLACE_QUICKFIRE:
make_field_type(spot_hit.x,spot_hit.y,FIELD_QUICKFIRE);
mouse_button_held = true;
break;
case MODE_PLACE_STONE_BLOCK:
make_field_type(spot_hit.x,spot_hit.y,OBJECT_BLOCK);
mouse_button_held = true;
break;
case MODE_PLACE_FORCECAGE:
make_field_type(spot_hit.x,spot_hit.y,BARRIER_CAGE);
mouse_button_held = true;
break;
case MODE_TOGGLE_SPECIAL_DOT:
if(editing_town){
make_field_type(spot_hit.x, spot_hit.y, SPECIAL_SPOT);
mouse_button_held = true;
} else {
if(!mouse_button_held)
mode_count = !current_terrain->special_spot[spot_hit.x][spot_hit.y];
current_terrain->special_spot[spot_hit.x][spot_hit.y] = mode_count;
mouse_button_held = true;
}
break;
case MODE_TOGGLE_ROAD:
if(editing_town){
make_field_type(spot_hit.x, spot_hit.y, SPECIAL_ROAD);
mouse_button_held = true;
} else {
if(!mouse_button_held)
mode_count = !current_terrain->roads[spot_hit.x][spot_hit.y];
current_terrain->roads[spot_hit.x][spot_hit.y] = mode_count;
mouse_button_held = true;
}
break;
case MODE_CLEAR_FIELDS:
for(int i = 8; i <= SPECIAL_ROAD; i++)
take_field_type(spot_hit.x,spot_hit.y, eFieldType(i));
mouse_button_held = true;
break;
case MODE_PLACE_SFX:
make_field_type(spot_hit.x,spot_hit.y,eFieldType(SFX_SMALL_BLOOD + mode_count));
mouse_button_held = true;
break;
case MODE_EYEDROPPER:
set_new_terrain(cur_area->terrain(spot_hit.x,spot_hit.y));
overall_mode = MODE_DRAWING;
break;
case MODE_EDIT_SIGN: //edit sign
{
auto& signs = cur_area->sign_locs;
auto iter = std::find(signs.begin(), signs.end(), spot_hit);
short picture = scenario.ter_types[cur_area->terrain(spot_hit.x,spot_hit.y)].picture;
if(iter != signs.end()) {
edit_sign(*iter, iter - signs.begin(), picture);
} else {
signs.emplace_back(spot_hit);
edit_sign(signs.back(), signs.size() - 1, picture);
}
overall_mode = MODE_DRAWING;
break;
}
case MODE_EDIT_CREATURE: //edit monst
for(short x = 0; x < town->creatures.size(); x++)
if(monst_on_space(spot_hit,x)) {
edit_placed_monst(x);
}
overall_mode = MODE_DRAWING;
break;
case MODE_EDIT_SPECIAL: //make special
place_edit_special(spot_hit);
overall_mode = MODE_DRAWING;
break;
case MODE_COPY_SPECIAL: //copy special
{
auto& specials = cur_area->special_locs;
auto iter = std::find_if(town->special_locs.begin(), town->special_locs.end(), [](const spec_loc_t& loc) {
return loc == spot_hit && loc.spec >= 0;
});
if(iter != specials.end())
clipboard = std::pair<long,bool>{iter->spec, editing_town};
else showError("There wasn't a special on that spot.");
overall_mode = MODE_DRAWING;
break;
}
case MODE_ERASE_SPECIAL: //erase special
{
auto& specials = cur_area->special_locs;
for(short x = 0; x < specials.size(); x++)
if(specials[x] == spot_hit && specials[x].spec >= 0) {
specials[x] = {-1,-1};
specials[x].spec = -1;
if(x == specials.size() - 1) {
// Delete not only the last entry but any other empty entries at the end of the list
do {
specials.pop_back();
} while(!specials.empty() && specials.back().spec < 0);
}
break;
}
overall_mode = MODE_DRAWING;
break;
}
case MODE_PLACE_SPECIAL: //edit special
set_special(spot_hit);
overall_mode = MODE_DRAWING;
break;
case MODE_EDIT_TOWN_ENTRANCE: //edit town entry
town_entry(spot_hit);
overall_mode = MODE_DRAWING;
break;
case MODE_SET_OUT_START:
if((spot_hit.x != minmax(4,43,spot_hit.x)) || (spot_hit.y != minmax(4,43,spot_hit.y))) {
showError("You can't put the starting location this close to the edge of an outdoor section. It has to be at least 4 spaces away.");
break;
}
scenario.out_sec_start.x = cur_out.x;
scenario.out_sec_start.y = cur_out.y;
scenario.out_start = spot_hit;
overall_mode = MODE_DRAWING;
change_made = true;
break;
case MODE_ERASE_CREATURE: //delete monst
for(short x = 0; x < town->creatures.size(); x++)
if(monst_on_space(spot_hit,x)) {
town->creatures[x].number = 0;
break;
}
while(!town->creatures.empty() && town->creatures.back().number == 0)
town->creatures.pop_back();
overall_mode = MODE_DRAWING;
break;
case MODE_ERASE_ITEM: // delete item
for(short x = 0; x < town->preset_items.size(); x++)
if((spot_hit.x == town->preset_items[x].loc.x) &&
(spot_hit.y == town->preset_items[x].loc.y) && (town->preset_items[x].code >= 0)) {
town->preset_items[x].code = -1;
break;
}
while(!town->preset_items.empty() && town->preset_items.back().code == -1)
town->preset_items.pop_back();
overall_mode = MODE_DRAWING;
break;
case MODE_SET_TOWN_START:
if(!town->in_town_rect.contains(spot_hit)) {
showError("You can't put the starting location outside the town boundaries.");
break;
}
scenario.which_town_start = cur_town;
scenario.where_start = spot_hit;
overall_mode = MODE_DRAWING;
change_made = true;
break;
case MODE_PLACE_BOAT: case MODE_PLACE_HORSE: {
auto& all = overall_mode == MODE_PLACE_BOAT ? scenario.boats : scenario.horses;
auto iter = std::find_if(all.begin(), all.end(), [](const cVehicle& what) {
if(editing_town && cur_town != what.which_town) return false;
else if(!editing_town && what.which_town != 200) return false;
return what.loc == spot_hit;
});
if(iter == all.end()) {
iter = std::find_if(all.begin(), all.end(), [](const cVehicle& what) {
return what.which_town < 0;
});
if(iter == all.end()) {
all.emplace_back();
iter = all.end() - 1;
}
iter->loc = spot_hit;
iter->which_town = editing_town ? cur_town : 200;
iter->property = false;
iter->exists = false;
if(!editing_town) iter->sector = cur_out;
}
if(!edit_vehicle(*iter, iter - all.begin(), overall_mode == MODE_PLACE_BOAT))
all.erase(iter);
overall_mode = MODE_DRAWING;
break;
}
case MODE_INTRO_SCREEN:
case MODE_EDIT_TYPES:
case MODE_MAIN_SCREEN:
break; // Nothing to do here, of course.
case MODE_COPY_CREATURE:
for(short x = 0; x < town->creatures.size(); x++)
if(monst_on_space(spot_hit,x)) {
clipboard = town->creatures[x];
break;
}
overall_mode = MODE_DRAWING;
break;
case MODE_COPY_ITEM:
for(short x = 0; x < town->preset_items.size(); x++)
if((spot_hit.x == town->preset_items[x].loc.x) && (spot_hit.y == town->preset_items[x].loc.y) && (town->preset_items[x].code >= 0)) {
clipboard = town->preset_items[x];
break;
}
overall_mode = MODE_DRAWING;
break;
}
if((overall_mode == MODE_DRAWING) && (old_mode != MODE_DRAWING))
set_string("Drawing mode",scenario.ter_types[current_terrain_type].name);
draw_terrain();
return true;
}
bool need_redraw = false;
if((the_point.in(border_rect[0]))) {
if(ctrl_hit)
cen_y = ((editing_town) ? 4 : 3);
else
handle_editor_screen_shift(0, -1);
need_redraw = true;
mouse_button_held = true;
}
if((the_point.in(border_rect[1]))) {
if(ctrl_hit)
cen_x = ((editing_town) ? 4 : 3);
else
handle_editor_screen_shift(-1, 0);
need_redraw = true;
mouse_button_held = true;
}
auto max_dim = cur_area->max_dim - 5;
// This allows you to see a strip of terrain from the adjacent sector when editing outdoors
if(!editing_town) max_dim++;
if((the_point.in(border_rect[2]))) {
if(ctrl_hit)
cen_y = max_dim;
else
handle_editor_screen_shift(0, 1);
need_redraw = true;
mouse_button_held = true;
}
if((the_point.in(border_rect[3]))) {
if(ctrl_hit)
cen_x = max_dim;
else
handle_editor_screen_shift(1, 0);
need_redraw = true;
mouse_button_held = true;
}
if(need_redraw) {
draw_terrain();
place_location();
return true;
}
return false;
}
static bool handle_terpal_action(location cur_point, bool option_hit) {
for(int i = 0; i < 256; i++)
if(cur_point.in(terrain_rects[i])) {
int k = pal_sbar->getPosition() * 16 + i;
rectangle temp_rect = terrain_rects[i];
temp_rect.offset(RIGHT_AREA_UL_X, RIGHT_AREA_UL_Y );
flash_rect(temp_rect);
if(overall_mode < MODE_MAIN_SCREEN) {
switch(draw_mode) {
case DRAW_TERRAIN:
set_new_terrain(k);
break;
case DRAW_ITEM:
if(k >= scenario.scen_items.size())
break;
if(scenario.scen_items[k].variety == eItemType::NO_ITEM) {
showError("This item has its Variety set to No Item. You can only place items with a Variety set to an actual item type.");
break;
}
overall_mode = MODE_PLACE_ITEM;
mode_count = k;
set_string("Place the item:",scenario.scen_items[mode_count].full_name);
break;
case DRAW_MONST:
if(k + 1 >= scenario.scen_monsters.size())
break;
overall_mode = MODE_PLACE_CREATURE;
mode_count = k + 1;
set_string("Place the monster:",scenario.scen_monsters[mode_count].m_name);
break;
}
}
else {
short size_before = scenario.ter_types.size(), pos_before = pal_sbar->getPosition();
i += pos_before * 16;
if(i > size_before) return true;
if(option_hit) {
if(i == size_before - 1 && !scenario.is_ter_used(i))
scenario.ter_types.pop_back();
else if(i == size_before) {
scenario.ter_types.resize(size_before + 16);
for(; i < scenario.ter_types.size(); i++)
scenario.ter_types[i].name = "New Terrain";
} else {
scenario.ter_types[i] = cTerrain();
scenario.ter_types[i].name = "Unused Terrain";
}
} else {
if(i == size_before) {
scenario.ter_types.emplace_back();
scenario.ter_types.back().name = "New Terrain";
}
if(!edit_ter_type(i) && i == size_before && scenario.ter_types.back().name == "New Terrain")
scenario.ter_types.pop_back();
}
if(size_before / 16 > scenario.ter_types.size() / 16)
pos_before--;
pal_sbar->setPosition(pos_before);
set_up_terrain_buttons(false);
change_made = true;
}
place_location();
return true;
}
return false;
}
static bool handle_toolpal_action(location cur_point2) {
for(int i = 0; i < 10; i++)
for(int j = 0; j < 6; j++) {
auto cur_palette_buttons = editing_town ? town_buttons : out_buttons;
if(cur_palette_buttons[j][i] != PAL_BLANK && !mouse_button_held && cur_point2.in(palette_buttons[i][j])
&& /*((j < 3) || (editing_town)) &&*/ (overall_mode < MODE_MAIN_SCREEN)) {
rectangle temp_rect = palette_buttons[i][j];
temp_rect.offset(RIGHT_AREA_UL_X + 5, RIGHT_AREA_UL_Y + terrain_rects[255].bottom + 5);
flash_rect(temp_rect);
switch(cur_palette_buttons[j][i]) {
case PAL_ARROW_UP: case PAL_ARROW_DOWN: // These two might never be used.
case PAL_BLANK: break;
case PAL_PENCIL:
set_string("Drawing mode",scenario.ter_types[current_terrain_type].name);
overall_mode = MODE_DRAWING;
break;
case PAL_BRUSH_LG:
set_string("Paintbrush (large)",scenario.ter_types[current_terrain_type].name);
overall_mode = MODE_LARGE_PAINTBRUSH;
break;
case PAL_BRUSH_SM:
set_string("Paintbrush (small)",scenario.ter_types[current_terrain_type].name);
overall_mode = MODE_SMALL_PAINTBRUSH;
break;
case PAL_SPRAY_LG:
set_string("Spraycan (large)",scenario.ter_types[current_terrain_type].name);
overall_mode = MODE_LARGE_SPRAYCAN;
break;
case PAL_SPRAY_SM:
set_string("Spraycan (small)",scenario.ter_types[current_terrain_type].name);
overall_mode = MODE_SMALL_SPRAYCAN;
break;
case PAL_DROPPER:
set_string("Eyedropper","Select terrain to draw");
overall_mode = MODE_EYEDROPPER;
break;
case PAL_RECT_HOLLOW:
overall_mode = MODE_HOLLOW_RECT;
set_string("Fill rectangle (hollow)","Select upper left corner");
if(false) // Skip next statement
case PAL_RECT_FILLED:
overall_mode = MODE_FILLED_RECT,
set_string("Fill rectangle (solid)","Select upper left corner");
mode_count = 2;
break;
case PAL_BUCKET:
overall_mode = MODE_FLOOD_FILL;
set_string("Flood fill", scenario.ter_types[current_terrain_type].name);
break;
case PAL_ZOOM: // switch view
cur_viewing_mode = (cur_viewing_mode + 1) % 4;
draw_main_screen();
draw_terrain();
break;
case PAL_ERASER:
set_string("Erase space","Select space to clear");
overall_mode = MODE_ERASER;
break;
case PAL_EDIT_SIGN:
set_string("Edit sign","Select sign to edit");
overall_mode = MODE_EDIT_SIGN;
break;
case PAL_TEXT_AREA:
overall_mode = MODE_ROOM_RECT;
mode_count = 2;
set_string("Create room rectangle","Select upper left corner");
break;
case PAL_EDIT_STORAGE:
overall_mode = MODE_STORAGE_RECT;
mode_count = 2;
set_string("Create saved item rect","Select upper left corner");
break;
case PAL_WANDER:
overall_mode = MODE_SET_WANDER_POINTS;
mode_count = 4;
set_string("Place first wandering monster arrival point","");
break;
case PAL_CHANGE: // replace terrain
swap_terrain();
draw_main_screen();
draw_terrain();
mouse_button_held = false;
break;
case PAL_EDIT_TOWN:
if(editing_town) {
set_string("Can only set town entrances outdoors","");
break;
}
set_string("Set town entrance","Select town to edit");
overall_mode = MODE_EDIT_TOWN_ENTRANCE;
break;
case PAL_EDIT_ITEM:
if(!editing_town) {
set_string("Edit placed item","Not while outdoors.");
break;
}
set_string("Edit placed item","Select item to edit");
overall_mode = MODE_EDIT_ITEM;
break;
case PAL_COPY_ITEM:
if(!editing_town) {
set_string("Copy item","Not while outdoors.");
break;
}
set_string("Copy item","Select item");
overall_mode = MODE_COPY_ITEM;
break;
case PAL_ERASE_ITEM:
set_string("Delete an item","Select item");
overall_mode = MODE_ERASE_ITEM;
break;
case PAL_SPEC:
set_string("Create/Edit special","Select special location");
overall_mode = MODE_EDIT_SPECIAL;
break;
case PAL_COPY_SPEC:
set_string("Copy special","Select special to copy");
overall_mode = MODE_COPY_SPECIAL;
break;
case PAL_PASTE:
if(auto spec = boost::get<std::pair<long,bool>>(&clipboard))
if(editing_town == spec->second) set_string("Paste special","Select location to paste");
else {
if(editing_town) set_string("Paste special","Not while in town");
else set_string("Paste special","Not while outdoors");
break;
}
else if(boost::get<cTownperson>(&clipboard))
if(editing_town) set_string("Paste monster","Select location to paste");
else {
set_string("Paste monster","Not while outdoors.");
break;
}
else if(boost::get<cTown::cItem>(&clipboard))
if(editing_town) set_string("Paste item","Select location to paste");
else {
set_string("Paste item","Not while outdoors.");
break;
}
else if(boost::get<vector2d<ter_num_t>>(&clipboard))
set_string("Paste terrain","Select location to paste");
else set_string("Can't paste","Nothing to paste");
overall_mode = MODE_PASTE;
break;
case PAL_ERASE_SPEC:
set_string("Erase special","Select special to erase");
overall_mode = MODE_ERASE_SPECIAL;
break;
case PAL_EDIT_SPEC:
set_string("Set/place special","Select special location");
overall_mode = MODE_PLACE_SPECIAL;
break;
case PAL_EDIT_MONST:
set_string("Edit creature","Select creature to edit");
overall_mode = MODE_EDIT_CREATURE;
break;
case PAL_COPY_MONST:
set_string("Copy creature","Select creature");
overall_mode = MODE_COPY_CREATURE;
break;
case PAL_ERASE_MONST:
set_string("Delete a creature","Select creature");
overall_mode = MODE_ERASE_CREATURE;
break;
case PAL_ENTER_N:
set_string("Place north entrace","Select entrance location");
overall_mode = MODE_PLACE_NORTH_ENTRANCE;
break;
case PAL_ENTER_W:
set_string("Place west entrace","Select entrance location");
overall_mode = MODE_PLACE_WEST_ENTRANCE;
break;
case PAL_ENTER_S:
set_string("Place south entrace","Select entrance location");
overall_mode = MODE_PLACE_SOUTH_ENTRANCE;
break;
case PAL_ENTER_E:
set_string("Place east entrace","Select entrance location");
overall_mode = MODE_PLACE_EAST_ENTRANCE;
break;
case PAL_WEB:
set_string("Place web","Select location");
overall_mode = MODE_PLACE_WEB;
break;
case PAL_CRATE:
set_string("Place crate","Select location");
overall_mode = MODE_PLACE_CRATE;
break;
case PAL_BARREL:
set_string("Place barrel","Select location");
overall_mode = MODE_PLACE_BARREL;
break;
case PAL_BLOCK:
set_string("Place stone block","Select location");
overall_mode = MODE_PLACE_STONE_BLOCK;
break;
case PAL_FIRE_BARR:
set_string("Place fire barrier","Select location");
overall_mode = MODE_PLACE_FIRE_BARRIER;
break;
case PAL_FORCE_BARR:
set_string("Place force barrier","Select location");
overall_mode = MODE_PLACE_FORCE_BARRIER;
break;
case PAL_QUICKFIRE:
set_string("Place quickfire","Select location");
overall_mode = MODE_PLACE_QUICKFIRE;
break;
case PAL_SPEC_SPOT:
set_string(editing_town ? "Place special spot" : "Toggle special spot","Select location");
overall_mode = MODE_TOGGLE_SPECIAL_DOT;
break;
case PAL_ROAD:
set_string(editing_town ? "Place roads" : "Toggle roads", "Select location");
overall_mode = MODE_TOGGLE_ROAD;
break;
case PAL_FORCECAGE:
set_string("Place forcecage","Select location");
overall_mode = MODE_PLACE_FORCECAGE;
break;
case PAL_ERASE_FIELD:
set_string("Clear space","Select space to clear");
overall_mode = MODE_CLEAR_FIELDS;
break;
case PAL_SFX_SB:
set_string("Place small blood stain","Select stain location");
overall_mode = MODE_PLACE_SFX;
mode_count = 0;
break;
case PAL_SFX_MB:
set_string("Place ave. blood stain","Select stain location");
overall_mode = MODE_PLACE_SFX;
mode_count = 1;
break;
case PAL_SFX_LB:
set_string("Place large blood stain","Select stain location");
overall_mode = MODE_PLACE_SFX;
mode_count = 2;
break;
case PAL_SFX_SS:
set_string("Place small slime pool","Select slime location");
overall_mode = MODE_PLACE_SFX;
mode_count = 3;
break;
case PAL_SFX_LS:
set_string("Place large slime pool","Select slime location");
overall_mode = MODE_PLACE_SFX;
mode_count = 4;
break;
case PAL_SFX_ASH:
set_string("Place ash","Select ash location");
overall_mode = MODE_PLACE_SFX;
mode_count = 5;
break;
case PAL_SFX_BONE:
set_string("Place bones","Select bones location");
overall_mode = MODE_PLACE_SFX;
mode_count = 6;
break;
case PAL_SFX_ROCK:
set_string("Place rocks","Select rocks location");
overall_mode = MODE_PLACE_SFX;
mode_count = 7;
break;
case PAL_TERRAIN: // Terrain palette
draw_mode = DRAW_TERRAIN;
set_up_terrain_buttons(true);
break;
case PAL_ITEM: // Item palette
draw_mode = DRAW_ITEM;
set_up_terrain_buttons(true);
break;
case PAL_MONST: // Monster palette
draw_mode = DRAW_MONST;
set_up_terrain_buttons(true);
break;
case PAL_BOAT:
set_string("Place/edit boat","Select boat location");
overall_mode = MODE_PLACE_BOAT;
break;
case PAL_HORSE:
set_string("Place/edit horse","Select horse location");
overall_mode = MODE_PLACE_HORSE;
break;
case PAL_TOWN_BORDER:
if(!editing_town) {
set_string("Place boundaries","Not while outdoors.");
break;
}
overall_mode = MODE_SET_TOWN_RECT;
mode_count = 2;
set_string("Set town boundary","Select upper left corner");
break;
case PAL_START:
if(editing_town) {
overall_mode = MODE_SET_TOWN_START;
set_string("Select party starting location.","");
} else {
overall_mode = MODE_SET_OUT_START;
set_string("Select party starting location.","");
}
break;
case PAL_COPY_TER:
set_string("Copy terrain", "Select upper left corner");
overall_mode = MODE_COPY_TERRAIN;
mode_count = 2;
break;
}
return true;
}
}
return false;
}
void handle_action(location the_point,sf::Event /*event*/) {
std::string s2;
bool option_hit = false,ctrl_hit = false;
location spot_hit;
location cur_point,cur_point2;
rectangle temp_rect;
if(kb.isAltPressed())
option_hit = true;
if(kb.isCtrlPressed())
ctrl_hit = true;
if(handle_lb_action(the_point))
return;
if(overall_mode == MODE_MAIN_SCREEN && handle_rb_action(the_point, option_hit))
return;
update_mouse_spot(the_point);
if(overall_mode < MODE_MAIN_SCREEN && handle_terrain_action(the_point, ctrl_hit))
return;
if(!mouse_button_held && ((overall_mode < MODE_MAIN_SCREEN) || (overall_mode == MODE_EDIT_TYPES))) {
cur_point = the_point;
cur_point.x -= RIGHT_AREA_UL_X;
cur_point.y -= RIGHT_AREA_UL_Y;
if(handle_terpal_action(cur_point, option_hit))
return;
cur_point2 = the_point;
cur_point2.x -= 5;
cur_point2.y -= terrain_rects[255].bottom + 5;
if(handle_toolpal_action(cur_point2))
return;
}
return;
}
void flash_rect(rectangle /*to_flash*/) {
// TODO: Determine a good way to do this
// InvertRect (&to_flash);
play_sound(37);
sf::sleep(time_in_ticks(10));
}
void swap_terrain() {
short a,b,c;
ter_num_t ter;
if(!change_ter(a,b,c)) return;
cArea* cur_area = get_current_area();
for(short i = 0; i < cur_area->max_dim; i++)
for(short j = 0; j < cur_area->max_dim; j++) {
ter = cur_area->terrain(i,j);
if((ter == a) && (get_ran(1,1,100) <= c)) {
cur_area->terrain(i,j) = b;
}
}
}
void set_new_terrain(ter_num_t selected_terrain) {
if(selected_terrain >= scenario.ter_types.size()) return;
current_terrain_type = selected_terrain;
current_ground = scenario.get_ground_from_ter(selected_terrain);
cTerrain& ter = scenario.ter_types[current_terrain_type];
cTerrain& gter = scenario.ter_types[current_ground];
if(gter.blockage >= eTerObstruct::BLOCK_MOVE || ter.trim_type == eTrimType::WALKWAY || /*current_ground == current_terrain_type ||*/
(ter.trim_type >= eTrimType::S && ter.trim_type <= eTrimType::NW_INNER)) {
long trim = scenario.ter_types[current_ground].trim_ter;
if(trim < 0) current_ground = 0;
else current_ground = scenario.get_ter_from_ground(trim);
}
set_string(current_string[0],scenario.ter_types[current_terrain_type].name);
}
void handle_keystroke(sf::Event event) {
using kb = sf::Keyboard;
using Key = sf::Keyboard::Key;
Key keypad[10] = {kb::Numpad0,kb::Numpad1,kb::Numpad2,kb::Numpad3,kb::Numpad4,kb::Numpad5,kb::Numpad6,kb::Numpad7,kb::Numpad8,kb::Numpad9};
Key arrows[4] = {kb::Down, kb::Left, kb::Right, kb::Up};
// TODO: The repetition of location shouldn't be needed here!
location terrain_click[10] = {location{0,0}, // 0
location{6,356}, location{140,356}, location{270,356},
location{6,190}, location{140,190}, location{270,190},
location{6,24}, location{140,20}, location{270,24},
};
location pass_point;
Key chr2 = event.key.code;
char chr;
short store_ter;
obscureCursor();
if(overall_mode >= MODE_MAIN_SCREEN)
return;
// Shortcuts while terrain is visible:
for(short i = 0; i < 10; i++)
if(chr2 == keypad[i] || (i % 2 == 0 && i > 0 && chr2 == arrows[i / 2 - 1])) {
if(i == 0) {
chr = 'z';
(void) chr; // TODO: This seems like it was intended to make Numpad0 and Z equivalent.
}
else {
pass_point = terrain_click[i];
handle_action(pass_point,event);
draw_terrain();
mouse_button_held = false;
return;
}
}
store_ter = current_terrain_type;
chr = keyToChar(chr2, event.key.shift);
switch(chr) {
/*
case 'g':
set_new_terrain(current_ground);
break;
case 'm':
case 's':
set_string("Mark special.");
overall_mode = 6;
break;
case 'u':
modify_lists();
break;
case 'y':
overall_mode = 2;
mode_count = 4;
set_string("Place wandering.");
break;
case 'z':
overall_mode = 46;
set_string("Place same item.");
break;
case 'F':
overall_mode = 1;
mode_count = 2;
set_string("Fill rect.");
break;
case 'i':
working_rect.right = spot_hit.x;
working_rect.bottom = spot_hit.y;
overall_mode = 3;
mode_count = 2;
set_string("Create info rect.");
break;*/
/*case 'q':
if(overall_mode != 7) {
set_string("Place monster.");
overall_mode = 7;
}
else {
set_string("Place same monster.");
overall_mode = 8;
}
break;
break;*/
case 'D':
pass_point.x = RIGHT_AREA_UL_X + 6 + palette_buttons[0][0].left;
pass_point.y = RIGHT_AREA_UL_Y + 6 + terrain_rects[255].bottom + palette_buttons[0][0].top;
handle_action(pass_point,event);
break;
case 'R':
pass_point.x = RIGHT_AREA_UL_X + 6 + palette_buttons[7][0].left;
pass_point.y = RIGHT_AREA_UL_Y + 6 + terrain_rects[255].bottom + palette_buttons[7][0].top;
handle_action(pass_point,event);
break;
case '1': case '2': case '3': case '4': case '5': case '6':
pass_point.x = RIGHT_AREA_UL_X + 6 + palette_buttons[chr - 49][4].left;
pass_point.y = RIGHT_AREA_UL_Y + 6 + terrain_rects[255].bottom + palette_buttons[chr - 48][4].top;
handle_action(pass_point,event);
break;
case '0':
pass_point.x = RIGHT_AREA_UL_X + 6 + palette_buttons[7][4].left;
pass_point.y = RIGHT_AREA_UL_Y + 6 + terrain_rects[255].bottom + palette_buttons[7][4].top;
handle_action(pass_point,event);
break;
case 'I':
for(short i = 0; i < town->preset_items.size(); i++) {
if((town->preset_items[i].loc.x < 0) ||
(town->preset_items[i].loc.y < 0))
town->preset_items[i].code = -1;
if(town->preset_items[i].code >= 0) {
edit_placed_item(i);
}
}
break;
case '.':
set_string("Pick item to edit.","");
overall_mode = MODE_EDIT_ITEM;
break;
case ',':
set_string("Edit creature","Select creature to edit");
overall_mode = MODE_EDIT_CREATURE;
break;
case '+': case '=': // accept + with or without shift held for symmetry with -
cur_viewing_mode = std::max(cur_viewing_mode - 1, 0);
// Skip first line of fallthrough
if(false)
case '-':
cur_viewing_mode = std::min(cur_viewing_mode + 1, 3);
draw_main_screen();
draw_terrain();
break;
default:
if(chr >= 'a' && chr <= 'z') {
for(short i = 0; i < scenario.ter_types.size(); i++) {
int j = current_terrain_type + i + 1;
j %= scenario.ter_types.size();
if(scenario.ter_types[j].shortcut_key == chr) {
set_new_terrain(j);
place_location();
break;
}
}
}
break;
}
if(store_ter != current_terrain_type)
draw_terrain();
mouse_button_held = false;
}
static bool handle_outdoor_sec_shift(int dx, int dy){
if(editing_town) return false;
int new_x = cur_out.x + dx;
int new_y = cur_out.y + dy;
if(new_x < 0) return true;
if(new_x >= scenario.outdoors.width()) return true;
if(new_y < 0) return true;
if(new_y >= scenario.outdoors.height()) return true;
cChoiceDlog shift_prompt("shift-outdoor-section", {"yes", "no"});
location new_out_sec = { new_x, new_y };
shift_prompt->getControl("out-sec").setText(boost::lexical_cast<std::string>(new_out_sec));
if(shift_prompt.show() == "yes"){
int last_cen_x = cen_x;
int last_cen_y = cen_y;
set_current_out(new_out_sec);
// match the terrain view to where we were
start_out_edit();
if(dx < 0) {
cen_x = get_current_area()->max_dim - 4;
}else if(dx > 0){
cen_x = 3;
}else{
cen_x = last_cen_x;
}
if(dy < 0){
cen_y = get_current_area()->max_dim - 4;
}else if(dy > 0){
cen_y = 3;
}else{
cen_y = last_cen_y;
}
redraw_screen();
}
return true;
}
void handle_editor_screen_shift(int dx, int dy) {
int min = (editing_town ? 4 : 3);
int max = get_current_area()->max_dim - 5;
if(!editing_town) max++;
bool out_of_bounds = false;
if(cen_x + dx < min){
// In outdoors, prompt whether to swap to the next section west
if(handle_outdoor_sec_shift(-1, 0)) return;
out_of_bounds = true;
}else if(cen_x + dx > max){
// In outdoors, prompt whether to swap to the next section east
if(handle_outdoor_sec_shift(1, 0)) return;
out_of_bounds = true;
}else if(cen_y + dy < min){
// In outdoors, prompt whether to swap to the next section north
if(handle_outdoor_sec_shift(0, -1)) return;
out_of_bounds = true;
}else if(cen_y + dy > max){
// In outdoors, prompt whether to swap to the next section south
if(handle_outdoor_sec_shift(0, 1)) return;
out_of_bounds = true;
}
if(out_of_bounds){
// In town, prompt whether to go back to outdoor entrance location
std::vector<town_entrance_t> town_entrances = scenario.find_town_entrances(cur_town);
if(town_entrances.size() == 1){
town_entrance_t only_entrance = town_entrances[0];
cChoiceDlog shift_prompt("shift-town-entrance", {"yes", "no"});
shift_prompt->getControl("out-sec").setText(boost::lexical_cast<std::string>(only_entrance.out_sec));
if(shift_prompt.show() == "yes"){
set_current_out(only_entrance.out_sec);
start_out_edit();
cen_x = only_entrance.loc.x;
cen_y = only_entrance.loc.y;
redraw_screen();
return;
}
}else if(town_entrances.size() > 1){
std::vector<std::string> entrance_strings;
for(town_entrance_t entrance : town_entrances){
std::ostringstream sstr;
sstr << "Entrance in section " << entrance.out_sec << " at " << entrance.loc;
entrance_strings.push_back(sstr.str());
}
cStringChoice dlog(entrance_strings, "Shift to one of this town's entrances in the outdoors?");
size_t choice = dlog.show(-1);
if(choice >= 0 && choice < town_entrances.size()){
town_entrance_t entrance = town_entrances[choice];
set_current_out(entrance.out_sec);
start_out_edit();
cen_x = entrance.loc.x;
cen_y = entrance.loc.y;
redraw_screen();
return;
}
}
}
cen_x = minmax(min, max, cen_x + dx);
cen_y = minmax(min, max, cen_y + dy);
}
void handle_scroll(const sf::Event& event) {
location pos { translate_mouse_coordinates({event.mouseMove.x,event.mouseMove.y}) };
int amount = event.mouseWheel.delta;
if(overall_mode < MODE_MAIN_SCREEN && pos.in(terrain_rect)) {
if(kb.isCtrlPressed())
handle_editor_screen_shift(-amount, 0);
else handle_editor_screen_shift(0, -amount);
draw_terrain();
place_location();
}
}
void change_circle_terrain(location center,short radius,ter_num_t terrain_type,short probability) {
// prob is 0 - 20, 0 no, 20 always
location l;
cArea* cur_area = get_current_area();
for(short i = 0; i < cur_area->max_dim; i++)
for(short j = 0; j < cur_area->max_dim; j++) {
l.x = i;
l.y = j;
if((dist(center,l) <= radius) && (get_ran(1,1,20) <= probability))
set_terrain(l,terrain_type);
}
}
void change_rect_terrain(rectangle r,ter_num_t terrain_type,short probability,bool hollow) {
// prob is 0 - 20, 0 no, 20 always
location l;
cArea* cur_area = get_current_area();
for(short i = 0; i < cur_area->max_dim; i++)
for(short j = 0; j < cur_area->max_dim; j++) {
l.x = i;
l.y = j;
if((i >= r.left) && (i <= r.right) && (j >= r.top) && (j <= r.bottom)
&& (!hollow || (i == r.left) || (i == r.right) || (j == r.top) || (j == r.bottom))
&& ((hollow) || (get_ran(1,1,20) <= probability)))
set_terrain(l,terrain_type);
}
}
void flood_fill_terrain(location start, ter_num_t terrain_type) {
static const int dx[4] = {0, 1, 0, -1};
static const int dy[4] = {-1, 0, 1, 0};
cArea* cur_area = get_current_area();
ter_num_t to_replace = cur_area->terrain(start.x, start.y);
std::stack<location> to_visit;
std::set<location, loc_compare> visited;
to_visit.push(start);
while(!to_visit.empty()) {
location this_loc = to_visit.top();
to_visit.pop();
visited.insert(this_loc);
for(int i = 0; i < 4; i++) {
location adj_loc = this_loc;
adj_loc.x += dx[i];
adj_loc.y += dy[i];
if(!cur_area->is_on_map(adj_loc))
continue;
ter_num_t check = cur_area->terrain(adj_loc.x, adj_loc.y);
if(check == to_replace && !visited.count(adj_loc))
to_visit.push(adj_loc);
}
cur_area->terrain(this_loc.x, this_loc.y) = terrain_type;
}
}
void frill_up_terrain() {
ter_num_t terrain_type;
cArea* cur_area = get_current_area();
for(short i = 0; i < cur_area->max_dim; i++)
for(short j = 0; j < cur_area->max_dim; j++) {
terrain_type = cur_area->terrain(i,j);
for(size_t k = 0; k < scenario.ter_types.size(); k++) {
if(terrain_type == k) continue;
cTerrain& ter = scenario.ter_types[k];
if(terrain_type == ter.frill_for && get_ran(1,1,100) < ter.frill_chance)
terrain_type = k;
}
cur_area->terrain(i,j) = terrain_type;
}
draw_terrain();
}
void unfrill_terrain() {
ter_num_t terrain_type;
cArea* cur_area = get_current_area();
for(short i = 0; i < cur_area->max_dim; i++)
for(short j = 0; j < cur_area->max_dim; j++) {
terrain_type = cur_area->terrain(i,j);
cTerrain& ter = scenario.ter_types[terrain_type];
if(ter.frill_for >= 0)
terrain_type = ter.frill_for;
cur_area->terrain(i,j) = terrain_type;
}
draw_terrain();
}
static ter_num_t find_object_part(unsigned char num, short x, short y, ter_num_t fallback){
for(int i = 0; i < scenario.ter_types.size(); i++){
if(scenario.ter_types[i].obj_num == num &&
scenario.ter_types[i].obj_pos.x == x &&
scenario.ter_types[i].obj_pos.y == y)
return i;
}
return fallback;
}
bool terrain_matches(unsigned char x, unsigned char y, ter_num_t ter) {
ter_num_t ter2;
ter2 = get_current_area()->terrain(x,y);
if(ter2 == ter) return true;
if(scenario.ter_types[ter2].ground_type != scenario.ter_types[ter].ground_type)
return false;
if(scenario.ter_types[ter].trim_type == eTrimType::NONE &&
scenario.ter_types[ter2].trim_type >= eTrimType::S &&
scenario.ter_types[ter2].trim_type <= eTrimType::NW_INNER)
return ter == scenario.get_ground_from_ter(ter);
if(scenario.ter_types[ter2].trim_type == eTrimType::NONE &&
scenario.ter_types[ter].trim_type >= eTrimType::S &&
scenario.ter_types[ter].trim_type <= eTrimType::NW_INNER)
return ter2 == scenario.get_ground_from_ter(ter2);
if(scenario.ter_types[ter2].trim_type >= eTrimType::S &&
scenario.ter_types[ter2].trim_type <= eTrimType::NW_INNER &&
scenario.ter_types[ter].trim_type >= eTrimType::S &&
scenario.ter_types[ter].trim_type <= eTrimType::NW_INNER)
return scenario.ter_types[ter].trim_type != scenario.ter_types[ter2].trim_type;
return false;
}
static const std::array<location,5> trim_diffs = {{
loc(0,0), loc(-1,0), loc(1,0), loc(0,-1), loc(0,1)
}};
void set_terrain(location l,ter_num_t terrain_type) {
location l2;
cArea* cur_area = get_current_area();
if(!cur_area->is_on_map(l)) return;
cur_area->terrain(l.x,l.y) = terrain_type;
l2 = l;
// Large objects (eg rubble)
if(scenario.ter_types[terrain_type].obj_num > 0){
int q = scenario.ter_types[terrain_type].obj_num;
location obj_loc = scenario.ter_types[terrain_type].obj_pos;
location obj_dim = scenario.ter_types[terrain_type].obj_size;
while(obj_loc.x > 0) l2.x-- , obj_loc.x--;
while(obj_loc.y > 0) l2.y-- , obj_loc.y--;
for(short i = 0; i < obj_dim.x; i++)
for(short j = 0; j < obj_dim.y; j++){
cur_area->terrain(l2.x + i,l2.y + j) = find_object_part(q,i,j,terrain_type);
}
}
// First make sure surrounding spaces have the correct ground types.
// This should handle the case of placing hills around mountains.
unsigned int main_ground = scenario.ter_types[terrain_type].ground_type;
long trim_ground = scenario.ter_types[terrain_type].trim_ter;
for(int x = -1; x <= 1; x++) {
for(int y = -1; y <= 1; y++) {
location l3(l.x+x,l.y+y);
ter_num_t ter_there = cur_area->terrain(l3.x,l3.y);
unsigned int ground_there = scenario.ter_types[ter_there].ground_type;
if(ground_there != main_ground && ground_there != trim_ground) {
ter_num_t new_ter = scenario.get_ter_from_ground(trim_ground);
if(new_ter > scenario.ter_types.size()) continue;
cTerrain& ter_type = scenario.ter_types[new_ter];
// We need to be very cautious here.
// Only make the change if the terrain already there is the archetype for the ground type
// that is the trim terrain of the terrain we're trying to place.
// Otherwise it might overwrite important things, like buildings or forests.
if(ter_there != scenario.get_ter_from_ground(ter_type.trim_ter))
continue;
cur_area->terrain(l3.x,l3.y) = new_ter;
}
}
}
// Adjusting terrains with trim
for(location d : trim_diffs) {
location l3(l.x+d.x, l.y+d.y);
adjust_space(l3);
}
if(scenario.ter_types[terrain_type].special == eTerSpec::IS_A_SIGN) {
if(!editing_town && (l.x == 0 || l.x == 47 || l.y == 0 || l.y == 47)) {
cChoiceDlog("not-at-edge").show();
mouse_button_held = false;
return;
}
auto& signs = cur_area->sign_locs;
auto iter = std::find(signs.begin(), signs.end(), l);
if(iter == signs.end()) {
iter = std::find_if(signs.begin(), signs.end(), [cur_area](const sign_loc_t& sign) {
if(sign.x == 100) return true;
ter_num_t ter = cur_area->terrain(sign.x,sign.y);
return scenario.ter_types[ter].special != eTerSpec::IS_A_SIGN;
});
if(iter == signs.end()) {
signs.emplace_back();
iter = signs.end() - 1;
}
}
static_cast<location&>(*iter) = l;
ter_num_t terrain_type = cur_area->terrain(iter->x,iter->y);
edit_sign(*iter, iter - signs.begin(), scenario.ter_types[terrain_type].picture);
mouse_button_held = false;
}
}
void adjust_space(location l) {
bool needed_change = false;
location l2;
cArea* cur_area = get_current_area();
if(!cur_area->is_on_map(l)) return;
size_t size = cur_area->max_dim;
ter_num_t off_map = -1;
ter_num_t store_ter[3][3];
long store_ter2[3][3];
unsigned int store_ground[3][3];
eTrimType store_trim[3][3];
for(int dx = -1; dx <= 1; dx++) {
for(int dy = -1; dy <= 1; dy++) {
int x = l.x + dx, y = l.y + dy;
if(x < 0 || x >= size || y < 0 || y >= size) {
store_ter[dx+1][dy+1] = off_map;
continue;
}
store_ter[dx+1][dy+1] = cur_area->terrain(x,y);
cTerrain& ter_type = scenario.ter_types[store_ter[dx+1][dy+1]];
store_ter2[dx+1][dy+1] = ter_type.trim_ter;
store_ground[dx+1][dy+1] = ter_type.ground_type;
store_trim[dx+1][dy+1] = ter_type.trim_type;
}
}
// Correctable spaces are recognizable by having a trim terrain...
if(store_ter2[1][1] < 0) return;
// ...as well as a particular subset of trim types.
if(store_trim[1][1] >= eTrimType::FRILLS || store_trim[1][1] == eTrimType::WALL)
return;
bool have_wall = scenario.ter_types[store_ter[1][1]].blockage >= eTerObstruct::BLOCK_MOVE;
unsigned int main_ground = store_ground[1][1];
long trim_ground = store_ter2[1][1];
auto is_same_ter = [&](int x,int y) -> bool {
if(store_ground[x][y] == main_ground)
return true;
if(store_ter2[x][y] == main_ground)
return true;
if(store_ter[x][y] == off_map)
return true;
if(!have_wall)
return false;
if(store_trim[x][y] == eTrimType::WATERFALL)
return true;
if(store_trim[x][y] == eTrimType::WALL)
return true;
return false;
};
// Then go through and figure out what the trim type of the centre should be.
eTrimType need_trim = eTrimType::NONE;
if(!is_same_ter(0,1)) {
if(!is_same_ter(1,0)) {
need_trim = eTrimType::NW;
} else if(!is_same_ter(1,2)) {
need_trim = eTrimType::SW;
} else need_trim = eTrimType::W;
} else if(!is_same_ter(2,1)) {
if(!is_same_ter(1,0)) {
need_trim = eTrimType::NE;
} else if(!is_same_ter(1,2)) {
need_trim = eTrimType::SE;
} else need_trim = eTrimType::E;
} else if(!is_same_ter(1,0))
need_trim = eTrimType::N;
else if(!is_same_ter(1,2))
need_trim = eTrimType::S;
else if(!is_same_ter(0,0))
need_trim = eTrimType::SE_INNER;
else if(!is_same_ter(0,2))
need_trim = eTrimType::NE_INNER;
else if(!is_same_ter(2,0))
need_trim = eTrimType::SW_INNER;
else if(!is_same_ter(2,2))
need_trim = eTrimType::NW_INNER;
if(store_trim[1][1] != need_trim) {
ter_num_t replace = scenario.get_trim_terrain(main_ground, trim_ground, need_trim);
if(replace != 90) { // If we got 90 back, the required trim doesn't exist.
needed_change = true;
cur_area->terrain(l.x,l.y) = replace;
}
}
if(needed_change) {
for(location d : trim_diffs) {
if(d.x == 0 && d.y == 0) continue;
location l2(l.x+d.x, l.y+d.y);
adjust_space(l2);
}
}
}
bool place_item(location spot_hit,short which_item,bool property,bool always,short odds) {
// odds 0 - 100, with 100 always
if((which_item < 0) || (which_item >= scenario.scen_items.size()))
return true;
if(scenario.scen_items[which_item].variety == eItemType::NO_ITEM)
return true;
if(get_ran(1,1,100) > odds)
return false;
for(short x = 0; x < town->preset_items.size(); x++)
if(town->preset_items[x].code < 0) {
town->preset_items[x] = {spot_hit, which_item, scenario.scen_items[which_item]};
town->preset_items[x].contained = container_there(spot_hit);
town->preset_items[x].property = property;
town->preset_items[x].always_there = always;
return true;
}
town->preset_items.push_back({spot_hit, which_item, scenario.scen_items[which_item]});
town->preset_items.back().contained = container_there(spot_hit);
town->preset_items.back().property = property;
town->preset_items.back().always_there = always;
return true;
}
void place_items_in_town() {
location l;
for(short i = 0; i < town->max_dim; i++)
for(short j = 0; j < town->max_dim; j++) {
l.x = i;
l.y = j;
for(short k = 0; k < 10; k++)
if(town->terrain(i,j) == scenario.storage_shortcuts[k].ter_type) {
for(short x = 0; x < 10; x++)
place_item(l,scenario.storage_shortcuts[k].item_num[x],
scenario.storage_shortcuts[k].property,false,
scenario.storage_shortcuts[k].item_odds[x]);
}
}
draw_terrain();
}
void place_edit_special(location loc) {
if(!editing_town && (loc.x == 0 || loc.x == 47 || loc.y == 0 || loc.y == 47)) {
cChoiceDlog("not-at-edge").show();
return;
}
auto& specials = get_current_area()->special_locs;
for(short i = 0; i < specials.size(); i++)
if(specials[i] == loc && specials[i].spec >= 0) {
edit_spec_enc(specials[i].spec, editing_town ? 2 : 1, nullptr);
return;
}
// new special
short spec = get_fresh_spec(editing_town ? 2 : 1);
for(short i = 0; i <= specials.size(); i++) {
if(i == specials.size())
specials.emplace_back(-1,-1,-1);
if(specials[i].spec < 0) {
if(edit_spec_enc(spec, editing_town ? 2: 1, nullptr)) {
specials[i] = loc;
specials[i].spec = spec;
}
break;
}
}
}
void set_special(location spot_hit) {
if(!editing_town && (spot_hit.x == 0 || spot_hit.x == 47 || spot_hit.y == 0 || spot_hit.y == 47)) {
cChoiceDlog("not-at-edge").show();
return;
}
auto& specials = get_current_area()->special_locs;
for(short x = 0; x < specials.size(); x++)
if(specials[x] == spot_hit && specials[x].spec >= 0) {
int spec = edit_special_num(editing_town ? 2 : 1,specials[x].spec);
if(spec >= 0) specials[x].spec = spec;
return;
}
for(short x = 0; x <= specials.size(); x++) {
if(x == specials.size())
specials.emplace_back(-1,-1,-1);
if(specials[x].spec < 0) {
int spec = edit_special_num(editing_town ? 2 : 1, 0);
if(spec >= 0) {
specials[x] = spot_hit;
specials[x].spec = spec;
}
break;
}
}
}
void town_entry(location spot_hit) {
ter_num_t ter = current_terrain->terrain[spot_hit.x][spot_hit.y];
if(scenario.ter_types[ter].special != eTerSpec::TOWN_ENTRANCE) {
showError("This space isn't a town entrance. Town entrances are marked by a small brown castle icon.");
return;
}
// clean up old town entries
for(short x = 0; x < current_terrain->city_locs.size(); x++)
if(current_terrain->city_locs[x].spec >= 0) {
ter = current_terrain->terrain[current_terrain->city_locs[x].x][current_terrain->city_locs[x].y];
if(scenario.ter_types[ter].special != eTerSpec::TOWN_ENTRANCE)
current_terrain->city_locs[x].spec = -1;
}
auto iter = std::find(current_terrain->city_locs.begin(), current_terrain->city_locs.end(), spot_hit);
if(iter != current_terrain->city_locs.end()) {
int town = pick_town_num("select-town-enter",iter->spec,scenario);
if(town >= 0) iter->spec = town;
} else {
iter = std::find_if(current_terrain->city_locs.begin(), current_terrain->city_locs.end(), [](const spec_loc_t& loc) {
return loc.spec < 0;
});
if(iter != current_terrain->city_locs.end()) {
int town = pick_town_num("select-town-enter",0,scenario);
if(town >= 0) {
*iter = spot_hit;
iter->spec = town;
}
} else {
int town = pick_town_num("select-town-enter",0,scenario);
if(town >= 0) {
current_terrain->city_locs.emplace_back(spot_hit);
current_terrain->city_locs.back().spec = town;
}
}
}
}
static std::string version() {
static std::string version;
if(version.empty()) {
std::ostringstream sout;
sout << "Version " << oboeVersionString();
#if defined(GIT_REVISION) && defined(GIT_TAG_REVISION)
if(strcmp(GIT_REVISION, GIT_TAG_REVISION) != 0) {
sout << " [" << GIT_REVISION << "]";
}
#endif
version = sout.str();
}
return version;
}
// is slot >= 0, force that slot
// if -1, use 1st free slot
void set_up_start_screen() {
reset_lb();
reset_rb();
set_lb(0,LB_TITLE,LB_NO_ACTION,"Blades of Exile");
set_lb(1,LB_TITLE,LB_NO_ACTION,"Scenario Editor");
set_lb(3,LB_TEXT,LB_NEW_SCEN,"Make New Scenario");
set_lb(4,LB_TEXT,LB_LOAD_SCEN,"Load Scenario");
set_lb(7,LB_TEXT,LB_NO_ACTION,"To find out how to use the");
set_lb(8,LB_TEXT,LB_NO_ACTION,"editor, select Getting Started ");
set_lb(9,LB_TEXT,LB_NO_ACTION,"from the Help menu.");
set_lb(NLS - 2,LB_TEXT,LB_NO_ACTION,"Created 1997, Free Open Source");
set_lb(NLS - 1,LB_TEXT,LB_NO_ACTION,version());
change_made = false;
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
}
void set_up_main_screen() {
std::ostringstream strb;
reset_lb();
reset_rb();
set_lb(-1,LB_TITLE,LB_NO_ACTION,"Blades of Exile");
set_lb(-1,LB_TEXT,LB_NO_ACTION,"Scenario Options");
set_lb(-1,LB_TEXT,LB_EDIT_TER,"Edit Terrain Types");
set_lb(-1,LB_TEXT,LB_EDIT_MONST,"Edit Monsters");
set_lb(-1,LB_TEXT,LB_EDIT_ITEM,"Edit Items");
set_lb(-1,LB_TEXT,LB_NEW_TOWN,"Create New Town");
set_lb(-1,LB_TEXT,LB_EDIT_TEXT,"Edit Scenario Text");
set_lb(-1,LB_TEXT,LB_EDIT_SPECITEM,"Edit Special Items");
set_lb(-1,LB_TEXT,LB_EDIT_QUEST,"Edit Quests");
set_lb(-1,LB_TEXT,LB_EDIT_SHOPS,"Edit Shops");
set_lb(-1,LB_TEXT,LB_NO_ACTION,"");
set_lb(-1,LB_TEXT,LB_NO_ACTION,"Outdoors Options");
strb << " Section x = " << cur_out.x << ", y = " << cur_out.y;
set_lb(-1,LB_TEXT,LB_NO_ACTION, strb.str());
set_lb(-1,LB_TEXT,LB_LOAD_OUT,"Load New Section");
set_lb(-1,LB_TEXT,LB_EDIT_OUT,"Edit Outdoor Terrain");
set_lb(-1,LB_TEXT,LB_NO_ACTION,"",0);
set_lb(-1,LB_TEXT,LB_NO_ACTION,"Town/Dungeon Options");
clear_sstr(strb);
strb << " Town " << cur_town << ": " << town->name;
set_lb(-1,LB_TEXT,LB_NO_ACTION, strb.str());
set_lb(-1,LB_TEXT,LB_LOAD_TOWN,"Load Another Town");
set_lb(-1,LB_TEXT,LB_EDIT_TOWN,"Edit Town Terrain");
set_lb(-1,LB_TEXT,LB_EDIT_TALK,"Edit Town Dialogue");
set_lb(NLS - 2,LB_TEXT,LB_NO_ACTION,"Created 1997, Free Open Source");
set_lb(NLS - 1,LB_TEXT,LB_NO_ACTION,version());
overall_mode = MODE_MAIN_SCREEN;
right_sbar->show();
pal_sbar->hide();
shut_down_menus(4);
shut_down_menus(3);
redraw_screen();
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
}
void start_town_edit() {
std::ostringstream strb;
small_any_drawn = false;
cen_x = town->max_dim / 2;
cen_y = town->max_dim / 2;
reset_lb();
strb << "Editing Town " << cur_town;
set_lb(0,LB_TITLE,LB_NO_ACTION,strb.str());
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,town->name);
set_lb(NLS - 2,LB_TEXT,LB_RETURN,"Back to Main Menu");
set_lb(NLS - 1,LB_TEXT,LB_NO_ACTION,"(Click border to scroll view.)");
overall_mode = MODE_DRAWING;
editing_town = true;
set_up_terrain_buttons(true);
shut_down_menus(4);
shut_down_menus(2);
right_sbar->hide();
pal_sbar->show();
set_string("Drawing mode",scenario.ter_types[current_terrain_type].name);
place_location();
for(short i = 0; i < town->max_dim; i++)
for(short j = 0; j < town->max_dim; j++)
if(town->terrain(i,j) == 0)
current_ground = 0;
else if(town->terrain(i,j) == 2)
current_ground = 2;
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
}
void start_out_edit() {
std::ostringstream strb;
small_any_drawn = false;
cen_x = 24;
cen_y = 24;
reset_lb();
strb << "Editing outdoors (" << cur_out.x << ',' << cur_out.y << ")";
set_lb(0,LB_TITLE,LB_NO_ACTION,strb.str());
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,current_terrain->name);
set_lb(NLS - 2,LB_TEXT,LB_RETURN,"Back to Main Menu");
set_lb(NLS - 1,LB_TEXT,LB_NO_ACTION,"(Click border to scroll view.)");
overall_mode = MODE_DRAWING;
draw_mode = DRAW_TERRAIN;
editing_town = false;
set_up_terrain_buttons(true);
right_sbar->hide();
pal_sbar->show();
shut_down_menus(4);
shut_down_menus(1);
set_string("Drawing mode",scenario.ter_types[current_terrain_type].name);
place_location();
for(short i = 0; i < 48; i++)
for(short j = 0; j < 48; j++)
if(current_terrain->terrain[i][j] == 0)
current_ground = 0;
else if(current_terrain->terrain[i][j] == 2)
current_ground = 2;
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
redraw_screen();
}
void start_terrain_editing() {
right_sbar->hide();
pal_sbar->show();
overall_mode = MODE_EDIT_TYPES;
draw_mode = DRAW_TERRAIN;
set_up_terrain_buttons(true);
place_location();
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,"Alt-click to delete/clear",true);
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
}
void start_monster_editing(bool just_redo_text) {
int num_options = scenario.scen_monsters.size() + 1;
if(!just_redo_text) {
overall_mode = MODE_MAIN_SCREEN;
right_sbar->show();
pal_sbar->hide();
right_sbar->setPosition(0);
reset_rb();
right_sbar->setMaximum(num_options - 1 - NRSONPAGE);
}
for(short i = 1; i < num_options; i++) {
std::string title;
if(i == scenario.scen_monsters.size())
title = "Create New Monster";
else title = scenario.scen_monsters[i].m_name;
title = std::to_string(i) + " - " + title;
set_rb(i - 1,RB_MONST, i, title);
}
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,"Alt-click to delete",true);
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
redraw_screen();
}
void start_item_editing(bool just_redo_text) {
int num_options = scenario.scen_items.size() + 1;
if(!just_redo_text) {
if(overall_mode < MODE_MAIN_SCREEN)
set_up_main_screen();
overall_mode = MODE_MAIN_SCREEN;
right_sbar->show();
pal_sbar->hide();
right_sbar->setPosition(0);
reset_rb();
right_sbar->setMaximum(num_options - NRSONPAGE);
}
for(short i = 0; i < num_options; i++) {
std::string title;
if(i == scenario.scen_items.size())
title = "Create New Item";
else title = scenario.scen_items[i].full_name;
title = std::to_string(i) + " - " + title;
set_rb(i,RB_ITEM, i, title);
}
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,"Alt-click to delete",true);
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
redraw_screen();
}
void start_special_item_editing(bool just_redo_text) {
int num_options = scenario.special_items.size() + 1;
if(!just_redo_text) {
if(overall_mode < MODE_MAIN_SCREEN)
set_up_main_screen();
overall_mode = MODE_MAIN_SCREEN;
right_sbar->show();
pal_sbar->hide();
right_sbar->setPosition(0);
reset_rb();
right_sbar->setMaximum(num_options - NRSONPAGE);
}
for(short i = 0; i < num_options; i++) {
std::string title;
if(i == scenario.special_items.size())
title = "Create New Special Item";
else title = scenario.special_items[i].name;
title = std::to_string(i) + " - " + title;
set_rb(i,RB_SPEC_ITEM, i, title);
}
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,"Alt-click to delete",true);
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
redraw_screen();
}
void start_quest_editing(bool just_redo_text) {
int num_options = scenario.quests.size() + 1;
if(!just_redo_text) {
if(overall_mode < MODE_MAIN_SCREEN)
set_up_main_screen();
overall_mode = MODE_MAIN_SCREEN;
right_sbar->show();
pal_sbar->hide();
right_sbar->setPosition(0);
reset_rb();
right_sbar->setMaximum(num_options - NRSONPAGE);
}
for(int i = 0; i < num_options; i++) {
std::string title;
if(i == scenario.quests.size())
title = "Create New Quest";
else title = scenario.quests[i].name;
title = std::to_string(i) + " - " + title;
set_rb(i, RB_QUEST, i, title);
}
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,"Alt-click to delete",true);
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
redraw_screen();
}
void start_shops_editing(bool just_redo_text) {
int num_options = scenario.shops.size() + 1;
if(!just_redo_text) {
if(overall_mode < MODE_MAIN_SCREEN)
set_up_main_screen();
overall_mode = MODE_MAIN_SCREEN;
right_sbar->show();
pal_sbar->hide();
right_sbar->setPosition(0);
reset_rb();
right_sbar->setMaximum(num_options - NRSONPAGE);
}
for(int i = 0; i < num_options; i++) {
std::string title;
if(i == scenario.shops.size())
title = "Create New Shop";
else title = scenario.shops[i].getName();
title = std::to_string(i) + " - " + title;
set_rb(i, RB_SHOP, i, title);
}
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,"Alt-click to delete",true);
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
redraw_screen();
}
extern size_t num_strs(short mode); // defined in scen.keydlgs.cpp
// mode 0 - scen 1 - out 2 - town 3 - journal
// if just_redo_text not 0, simply need to update text portions
void start_string_editing(eStrMode mode,short just_redo_text) {
if(just_redo_text == 0) {
if(overall_mode < MODE_MAIN_SCREEN)
set_up_main_screen();
overall_mode = MODE_MAIN_SCREEN;
right_sbar->show();
pal_sbar->hide();
reset_rb();
right_sbar->setMaximum(num_strs(mode) + 1 - NRSONPAGE);
}
size_t num_strs = ::num_strs(mode);
for(size_t i = 0; i < num_strs; i++) {
std::ostringstream str;
switch(mode) {
case 0:
str << i << " - " << scenario.spec_strs[i].substr(0,30);
set_rb(i,RB_SCEN_STR, i,str.str());
break;
case 1:
str << i << " - " << current_terrain->spec_strs[i].substr(0,30);
set_rb(i,RB_OUT_STR, i,str.str());
break;
case 2:
str << i << " - " << town->spec_strs[i].substr(0,30);
set_rb(i,RB_TOWN_STR, i,str.str());
break;
case 3:
str << i << " - " << scenario.journal_strs[i].substr(0,30);
set_rb(i,RB_JOURNAL, i,str.str());
break;
case 4:
str << i << " - " << current_terrain->sign_locs[i].text.substr(0,30);
set_rb(i,RB_OUT_SIGN, i,str.str());
break;
case 5:
str << i << " - " << town->sign_locs[i].text.substr(0,30);
set_rb(i,RB_TOWN_SIGN, i,str.str());
break;
case 6:
str << i << " - " << current_terrain->area_desc[i].descr.substr(0,30);
set_rb(i,RB_OUT_RECT, i,str.str());
break;
case 7:
str << i << " - " << town->area_desc[i].descr.substr(0,30);
set_rb(i,RB_TOWN_RECT, i,str.str());
break;
}
}
if(mode <= STRS_JOURNAL) {
// Signs and area rects don't get a Create New option you create a new one on the map.
std::string make_new = std::to_string(num_strs) + " - Create New String";
switch(mode) {
case 0: set_rb(num_strs, RB_SCEN_STR, num_strs, make_new); break;
case 1: set_rb(num_strs, RB_OUT_STR, num_strs, make_new); break;
case 2: set_rb(num_strs, RB_TOWN_STR, num_strs, make_new); break;
case 3: set_rb(num_strs, RB_JOURNAL, num_strs, make_new); break;
case 4: set_rb(num_strs, RB_OUT_SIGN, num_strs, make_new); break;
case 5: set_rb(num_strs, RB_TOWN_SIGN, num_strs, make_new); break;
case 6: set_rb(num_strs, RB_OUT_RECT, num_strs, make_new); break;
case 7: set_rb(num_strs, RB_TOWN_RECT, num_strs, make_new); break;
}
}
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,"Alt-click to delete",true);
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
redraw_screen();
}
// mode 0 - scen 1 - out 2 - town
// if just_redo_text not 0, simply need to update text portions
void start_special_editing(short mode,short just_redo_text) {
size_t num_specs;
switch(mode) {
case 0: num_specs = scenario.scen_specials.size(); break;
case 1: num_specs = current_terrain->specials.size(); break;
case 2: num_specs = town->specials.size(); break;
}
if(just_redo_text == 0) {
if(overall_mode < MODE_MAIN_SCREEN)
set_up_main_screen();
overall_mode = MODE_MAIN_SCREEN;
right_sbar->show();
pal_sbar->hide();
reset_rb();
right_sbar->setMaximum(num_specs + 1 - NRSONPAGE);
}
for(size_t i = 0; i < num_specs; i++) {
std::ostringstream strb;
switch(mode) {
case 0:
strb << i << " - " << (*scenario.scen_specials[i].type).name();
set_rb(i,RB_SCEN_SPEC, i, strb.str());
break;
case 1:
strb << i << " - " << (*current_terrain->specials[i].type).name();
set_rb(i,RB_OUT_SPEC, i, strb.str());
break;
case 2:
strb << i << " - " << (*town->specials[i].type).name();
set_rb(i,RB_TOWN_SPEC, i, strb.str());
break;
}
}
std::string make_new = std::to_string(num_specs) + " - Create New Special";
switch(mode) {
case 0: set_rb(num_specs, RB_SCEN_SPEC, num_specs, make_new); break;
case 1: set_rb(num_specs, RB_OUT_SPEC, num_specs, make_new); break;
case 2: set_rb(num_specs, RB_TOWN_SPEC, num_specs, make_new); break;
}
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,"Alt-click to delete",true);
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
redraw_screen();
}
// if restoring is 1, this is just a redraw, so don't move scroll bar position
void start_dialogue_editing(short restoring) {
char s[15] = " , ";
if(overall_mode < MODE_MAIN_SCREEN)
set_up_main_screen();
overall_mode = MODE_MAIN_SCREEN;
right_sbar->show();
pal_sbar->hide();
if(restoring == 0) {
right_sbar->setPosition(0);
reset_rb();
}
for(short i = 0; i < 10; i++) {
std::ostringstream strb;
strb << "Personality " << (i + cur_town * 10) << " - " << town->talking.people[i].title;
set_rb(i,RB_PERSONALITY, i, strb.str());
}
size_t n_nodes = town->talking.talk_nodes.size();
for(short i = 0; i < n_nodes; i++) {
for(short j = 0; j < 4; j++) {
s[j] = town->talking.talk_nodes[i].link1[j];
s[j + 6] = town->talking.talk_nodes[i].link2[j];
}
std::ostringstream strb;
strb << "Node " << i << " - Per. " << town->talking.talk_nodes[i].personality << ", " << s;
set_rb(10 + i,RB_DIALOGUE, i, strb.str());
}
set_rb(10 + n_nodes, RB_DIALOGUE, n_nodes, "Create New Node");
right_sbar->setMaximum((11 + n_nodes) - NRSONPAGE);
set_lb(NLS - 3,LB_TEXT,LB_NO_ACTION,"Alt-click node to delete",true);
update_mouse_spot(translate_mouse_coordinates(sf::Mouse::getPosition(mainPtr())));
redraw_screen();
}
bool save_check(std::string which_dlog, bool allow_no) {
std::string choice;
if(!change_made)
return true;
cChoiceDlog dlog(which_dlog, {"save","revert","cancel"});
if(!allow_no){
dlog->getControl("revert").hide();
}
choice = dlog.show();
if(choice == "revert")
return true;
else if(choice == "cancel")
return false;
change_made = false;
town->set_up_lights();
save_scenario();
return true;
}
bool is_lava(short x,short y) {
if((coord_to_ter(x,y) == 75) || (coord_to_ter(x,y) == 76))
return true;
else return false;
}
ter_num_t coord_to_ter(short x,short y) {
if(!get_current_area()->is_on_map(loc(x,y))) return 0;
return get_current_area()->terrain(x,y);
}
bool monst_on_space(location loc,short m_num) {
if(!editing_town)
return false;
if(m_num >= town->creatures.size())
return false;
if(town->creatures[m_num].number == 0)
return false;
if((loc.x - town->creatures[m_num].start_loc.x >= 0) &&
(loc.x - town->creatures[m_num].start_loc.x <= scenario.scen_monsters[town->creatures[m_num].number].x_width - 1) &&
(loc.y - town->creatures[m_num].start_loc.y >= 0) &&
(loc.y - town->creatures[m_num].start_loc.y <= scenario.scen_monsters[town->creatures[m_num].number].y_width - 1))
return true;
return false;
}
void restore_editor_state() {
set_current_town(scenario.last_town_edited);
set_current_out(scenario.last_out_edited);
}