refactor custom graphics dialog into a class

This commit is contained in:
2025-06-30 22:17:44 -05:00
parent 8587af17ef
commit 27b8fba4dc

View File

@@ -3715,115 +3715,71 @@ void edit_custom_pics_types() {
extern fs::path tempDir; extern fs::path tempDir;
extern std::string scenario_temp_dir_name; extern std::string scenario_temp_dir_name;
// Keep a single RenderTexture for splicing together custom graphics on a sheet class cCustomGraphicsDialog {
bool canvas_created = false; private:
static sf::RenderTexture& canvas() { cDialog dlg;
static sf::RenderTexture instance; size_t cur = 0;
if(!canvas_created){ std::unordered_map<size_t, sf::Image> sheets;
instance.create(280, 360);
canvas_created = true;
}
return instance;
}
bool scratch_created = false;
static sf::RenderTexture& scratch() {
static sf::RenderTexture instance;
if(!scratch_created){
instance.create(28, 36);
scratch_created = true;
}
return instance;
}
static void set_up_canvas(size_t sheet){
fs::path pic_dir = tempDir/scenario_temp_dir_name/"graphics";
fs::path fromPath = pic_dir/("sheet" + std::to_string(sheet) + ".png");
sf::Texture texture;
texture.loadFromFile(fromPath.string());
sf::Sprite s(texture);
canvas().clear(sf::Color(0,0,0,0));
canvas().draw(s);
}
static void set_dlg_custom_sheet(cDialog& me, size_t sheet) {
me["num"].setTextToNum(sheet);
dynamic_cast<cPict&>(me["sheet"]).setPict(sheet, PIC_FULL);
}
// Faster -- could this be used for threshold setting without sqrt?
static int color_diff2(sf::Color c1, sf::Color c2) {
return pow(c1.r - c2.r, 2) + pow(c1.g - c2.g, 2) + pow(c1.b - c2.b, 2);
}
static float color_diff(sf::Color c1, sf::Color c2) {
return sqrt(color_diff2(c1, c2));
}
void edit_custom_sheets() {
// Everything you do in this dialog can be undone when you hit cancel, leaving no trace in the main history. // Everything you do in this dialog can be undone when you hit cancel, leaving no trace in the main history.
std::vector<action_ptr> deferred_actions; std::vector<action_ptr> deferred_actions;
int max_pic = -1; int max_pic = -1;
std::vector<int> all_pics; std::vector<int> all_pics;
fs::path pic_dir = tempDir/scenario_temp_dir_name/"graphics"; fs::path pic_dir;
if(!scenario.scen_file.has_extension()) // It's an unpacked scenario
pic_dir = scenario.scen_file/"graphics"; // Icon selection info
if(!fs::exists(pic_dir)) fs::create_directories(pic_dir); int icon_start = 0;
for(fs::directory_iterator iter(pic_dir); iter != fs::directory_iterator(); iter++) { int icon_end = 0;
std::string fname = iter->path().filename().string().c_str(); int icon_min = 0;
int dot = fname.find_last_of('.'); int icon_max = 0;
if(fname.substr(0,5) == "sheet" && fname.substr(dot) == ".png" && std::all_of(fname.begin()+5, fname.begin()+dot, isdigit)) { // Buttons for working with icons
int this_pic = boost::lexical_cast<int>(fname.substr(5,dot-5)); std::vector<std::string> icon_buttons = {"icon-terr", "strip-terr", "add-terr"};
max_pic = max(max_pic, this_pic);
all_pics.push_back(this_pic); // Keep a single RenderTexture for splicing together custom graphics on a sheet
static sf::RenderTexture& canvas() {
static bool canvas_created = false;
static sf::RenderTexture instance;
if(!canvas_created){
instance.create(280, 360);
canvas_created = true;
} }
} return instance;
// Iterating through the graphics folder is not deterministic
std::sort(all_pics.begin(), all_pics.end());
// First, make sure we even have custom graphics! Also make sure they're not legacy format.
bool must_init_spec_g = false;
if(spec_scen_g.is_old) {
if(cChoiceDlog("convert-pics-now", {"cancel", "convert"}).show() == "cancel")
return;
spec_scen_g.convert_sheets();
all_pics.resize(spec_scen_g.numSheets);
std::iota(all_pics.begin(), all_pics.end(), 0);
} else if(max_pic < 0) {
if(cChoiceDlog("have-no-pics", {"cancel", "new"}).show() == "cancel")
return;
must_init_spec_g = true;
} else if(max_pic >= 0 && spec_scen_g.numSheets < 1) {
if(cChoiceDlog("have-only-full-pics", {"cancel", "new"}).show() == "new")
must_init_spec_g = true;
} }
if(must_init_spec_g) { // Keep a 1-tile RenderTexture for rendering new tiles
spec_scen_g.clear(); static sf::RenderTexture& scratch() {
spec_scen_g.sheets.resize(1); static bool scratch_created = false;
spec_scen_g.numSheets = 1; static sf::RenderTexture instance;
spec_scen_g.init_sheet(0); if(!scratch_created){
spec_scen_g.sheets[0]->copyToImage().saveToFile((pic_dir/"sheet0.png").string().c_str()); instance.create(28, 36);
all_pics.insert(all_pics.begin(), 0); scratch_created = true;
ResMgr::graphics.pushPath(pic_dir); }
return instance;
// We'll update the edit menu after this dialog closes
deferred_actions.push_back(action_ptr(new aCreateGraphicsSheet(0)));
} }
set_cursor(watch_curs); // Set up the canvas texture for modifying a given graphics sheet
void set_up_canvas(size_t sheet){
fs::path fromPath = pic_dir/("sheet" + std::to_string(sheet) + ".png");
// Get image data from the sheets in memory sf::Texture texture;
size_t cur = 0; texture.loadFromFile(fromPath.string());
std::unordered_map<size_t, sf::Image> sheets; sf::Sprite s(texture);
for(size_t i = 0; i < spec_scen_g.numSheets; i++) { canvas().clear(sf::Color(0,0,0,0));
sheets[i] = spec_scen_g.sheets[i]->copyToImage(); canvas().draw(s);
} }
cDialog pic_dlg(*ResMgr::dialogs.get("graphic-sheets")); // Set which custom sheet is viewed
void set_dlg_custom_sheet(size_t sheet) {
dlg["num"].setTextToNum(sheet);
dynamic_cast<cPict&>(dlg["sheet"]).setPict(sheet, PIC_FULL);
}
auto replace_from_file = [&pic_dlg,&sheets,&cur,&all_pics,&pic_dir,&deferred_actions](std::string action_name, fs::path fpath) -> bool { static int color_diff2(sf::Color c1, sf::Color c2) {
return pow(c1.r - c2.r, 2) + pow(c1.g - c2.g, 2) + pow(c1.b - c2.b, 2);
}
// Replace the current sheet with the image in a file
bool replace_from_file(std::string action_name, fs::path fpath) {
sf::Image img; sf::Image img;
if(!img.loadFromFile(fpath.string().c_str())) { if(!img.loadFromFile(fpath.string().c_str())) {
beep(); beep();
@@ -3844,18 +3800,19 @@ void edit_custom_sheets() {
img.saveToFile(toPath.string().c_str()); img.saveToFile(toPath.string().c_str());
ResMgr::graphics.free(resName); ResMgr::graphics.free(resName);
set_dlg_custom_sheet(pic_dlg, all_pics[cur]); set_dlg_custom_sheet(all_pics[cur]);
return true; return true;
} }
deferred_actions.push_back(action_ptr(new aReplaceGraphicsSheet(action_name, all_pics[cur], sheets[cur], img))); deferred_actions.push_back(action_ptr(new aReplaceGraphicsSheet(action_name, all_pics[cur], sheets[cur], img)));
sheets[cur] = img; sheets[cur] = img;
spec_scen_g.replace_sheet(cur, img); spec_scen_g.replace_sheet(cur, img);
set_dlg_custom_sheet(pic_dlg, all_pics[cur]); set_dlg_custom_sheet(all_pics[cur]);
return true; return true;
}; }
auto replace_from_image = [&cur, &all_pics, pic_dir, &deferred_actions, &sheets, &pic_dlg](std::string action_name, sf::Image& image) -> bool { // Replace the current sheet with an sf::Image
bool replace_from_image(std::string action_name, sf::Image& image) {
if(cur >= spec_scen_g.numSheets) { if(cur >= spec_scen_g.numSheets) {
std::string resName = "sheet" + std::to_string(all_pics[cur]); std::string resName = "sheet" + std::to_string(all_pics[cur]);
fs::path toPath = pic_dir/(resName + ".png"); fs::path toPath = pic_dir/(resName + ".png");
@@ -3866,21 +3823,19 @@ void edit_custom_sheets() {
image.saveToFile(toPath.string().c_str()); image.saveToFile(toPath.string().c_str());
ResMgr::graphics.free(resName); ResMgr::graphics.free(resName);
set_dlg_custom_sheet(pic_dlg, all_pics[cur]); set_dlg_custom_sheet(all_pics[cur]);
return true; return true;
} }
deferred_actions.push_back(action_ptr(new aReplaceGraphicsSheet(action_name, cur, sheets[cur], image))); deferred_actions.push_back(action_ptr(new aReplaceGraphicsSheet(action_name, cur, sheets[cur], image)));
sheets[cur] = image; sheets[cur] = image;
spec_scen_g.replace_sheet(cur, image); spec_scen_g.replace_sheet(cur, image);
set_dlg_custom_sheet(pic_dlg, all_pics[cur]); set_dlg_custom_sheet(all_pics[cur]);
return true; return true;
}; }
using namespace std::placeholders; // Copy the current graphic sheet to system clipboard
pic_dlg["cancel"].attachClickHandler(std::bind(&cDialog::toast, _1, false)); bool copy_sheet() {
pic_dlg["okay"].attachClickHandler(std::bind(&cDialog::toast, _1, true));
pic_dlg["copy"].attachClickHandler([&sheets,&cur,&all_pics,&pic_dir](cDialog&, std::string, eKeyMod) -> bool {
if(cur >= spec_scen_g.numSheets) { if(cur >= spec_scen_g.numSheets) {
fs::path fromPath = pic_dir/("sheet" + std::to_string(all_pics[cur]) + ".png"); fs::path fromPath = pic_dir/("sheet" + std::to_string(all_pics[cur]) + ".png");
sf::Image img; sf::Image img;
@@ -3890,16 +3845,19 @@ void edit_custom_sheets() {
} }
set_clipboard_img(sheets[cur]); set_clipboard_img(sheets[cur]);
return true; return true;
}); }
pic_dlg["paste"].attachClickHandler([&sheets,&cur,&all_pics,&pic_dir,&deferred_actions,replace_from_image](cDialog& me, std::string, eKeyMod) -> bool {
// Paste image from system clipboard over current sheet
bool paste_sheet() {
auto img = get_clipboard_img(); auto img = get_clipboard_img();
if(img == nullptr) { if(img == nullptr) {
beep(); beep();
return true; return true;
} }
return replace_from_image("Paste Graphics Sheet", *img); return replace_from_image("Paste Graphics Sheet", *img);
}); }
pic_dlg["edit"].attachClickHandler([&sheets,&cur,&all_pics,&pic_dir](cDialog&, std::string, eKeyMod) -> bool {
bool edit_sheet_external() {
fs::path image_editor = get_string_pref("ImageEditor"); fs::path image_editor = get_string_pref("ImageEditor");
if(image_editor.empty()){ if(image_editor.empty()){
showWarning("Choose an external image editor in the preferences window first."); showWarning("Choose an external image editor in the preferences window first.");
@@ -3928,20 +3886,25 @@ void edit_custom_sheets() {
showError("An unknown error occurred running your image editor!"); showError("An unknown error occurred running your image editor!");
} }
return true; return true;
}); }
pic_dlg["reload"].attachClickHandler([replace_from_file, &all_pics, &cur, &pic_dir](cDialog&, std::string, eKeyMod) -> bool { // Reload external changes to current graphics sheet
bool reload_sheet() {
std::string resName = "sheet" + std::to_string(all_pics[cur]); std::string resName = "sheet" + std::to_string(all_pics[cur]);
fs::path sheetPath = pic_dir/(resName + ".png"); fs::path sheetPath = pic_dir/(resName + ".png");
return replace_from_file("Reload Graphics Sheet", sheetPath); return replace_from_file("Reload Graphics Sheet", sheetPath);
}); }
pic_dlg["open"].attachClickHandler([replace_from_file](cDialog& me, std::string, eKeyMod) -> bool {
// Import image as graphics sheet
bool import_sheet() {
fs::path fpath = nav_get_rsrc({"png", "bmp", "jpg", "jpeg", "gif", "psd"}); fs::path fpath = nav_get_rsrc({"png", "bmp", "jpg", "jpeg", "gif", "psd"});
if(fpath.empty()) return true; if(fpath.empty()) return true;
replace_from_file("Import Graphics Sheet", fpath); replace_from_file("Import Graphics Sheet", fpath);
return true; return true;
}); }
pic_dlg["save"].attachClickHandler([&sheets,&cur,&all_pics,&pic_dir](cDialog&, std::string, eKeyMod) -> bool {
// Export current sheet to image file
bool export_sheet() {
fs::path fpath = nav_put_rsrc({"png", "bmp", "jpg", "jpeg"}); fs::path fpath = nav_put_rsrc({"png", "bmp", "jpg", "jpeg"});
if(fpath.empty()) return true; if(fpath.empty()) return true;
if(cur >= spec_scen_g.numSheets) { if(cur >= spec_scen_g.numSheets) {
@@ -3953,9 +3916,11 @@ void edit_custom_sheets() {
} }
sheets[cur].saveToFile(fpath.string().c_str()); sheets[cur].saveToFile(fpath.string().c_str());
return true; return true;
}); }
pic_dlg["new"].attachClickHandler([&sheets,&cur,&all_pics,&pic_dir,&deferred_actions](cDialog& me, std::string, eKeyMod) -> bool {
cChoiceDlog pickNum("add-new-sheet", {"cancel", "new"}, &me); // Create new sheet
bool new_sheet() {
cChoiceDlog pickNum("add-new-sheet", {"cancel", "new"}, &dlg);
pickNum->getControl("num").setTextToNum(spec_scen_g.numSheets); pickNum->getControl("num").setTextToNum(spec_scen_g.numSheets);
if(pickNum.show() == "cancel") return true; if(pickNum.show() == "cancel") return true;
int newSheet = pickNum->getControl("num").getTextAsNum(); int newSheet = pickNum->getControl("num").getTextAsNum();
@@ -3970,7 +3935,7 @@ void edit_custom_sheets() {
} else { } else {
auto iter = std::lower_bound(all_pics.begin(), all_pics.end(), newSheet); auto iter = std::lower_bound(all_pics.begin(), all_pics.end(), newSheet);
if(*iter == newSheet) { if(*iter == newSheet) {
showError("Sorry, but that sheet already exists! Try creating a sheet with a different number.", "Sheet number: " + std::to_string(newSheet), &me); showError("Sorry, but that sheet already exists! Try creating a sheet with a different number.", "Sheet number: " + std::to_string(newSheet), &dlg);
return true; return true;
} }
iter = all_pics.insert(iter, newSheet); iter = all_pics.insert(iter, newSheet);
@@ -3979,15 +3944,17 @@ void edit_custom_sheets() {
img.create(280, 360); img.create(280, 360);
img.saveToFile(sheetPath.string().c_str()); img.saveToFile(sheetPath.string().c_str());
} }
me["left"].show(); dlg["left"].show();
me["right"].show(); dlg["right"].show();
set_dlg_custom_sheet(me, all_pics[cur]); set_dlg_custom_sheet(all_pics[cur]);
deferred_actions.push_back(action_ptr(new aCreateGraphicsSheet(newSheet))); deferred_actions.push_back(action_ptr(new aCreateGraphicsSheet(newSheet)));
return true; return true;
}); }
pic_dlg["del"].attachClickHandler([&sheets,&cur,&all_pics,&pic_dir,&deferred_actions](cDialog& me, std::string, eKeyMod) -> bool {
// Delete the current sheet
bool delete_sheet() {
int which_pic = all_pics[cur]; int which_pic = all_pics[cur];
fs::path fpath = pic_dir/("sheet" + std::to_string(which_pic) + ".png"); fs::path fpath = pic_dir/("sheet" + std::to_string(which_pic) + ".png");
@@ -3998,7 +3965,7 @@ void edit_custom_sheets() {
if(which_pic < spec_scen_g.numSheets) { if(which_pic < spec_scen_g.numSheets) {
std::string choice = "del"; std::string choice = "del";
if(which_pic < spec_scen_g.numSheets - 1) if(which_pic < spec_scen_g.numSheets - 1)
choice = cChoiceDlog("must-delete-in-order", {"cancel", "del", "move"}, &me).show(); choice = cChoiceDlog("must-delete-in-order", {"cancel", "del", "move"}, &dlg).show();
if(choice == "cancel") return true; if(choice == "cancel") return true;
if(choice == "move") { if(choice == "move") {
moved = true; moved = true;
@@ -4033,48 +4000,37 @@ void edit_custom_sheets() {
deferred_actions.push_back(action_ptr(new aDeleteGraphicsSheet(which_pic, moved, image_for_undo))); deferred_actions.push_back(action_ptr(new aDeleteGraphicsSheet(which_pic, moved, image_for_undo)));
if(all_pics.size() == 1) { if(all_pics.size() == 1) {
me["left"].hide(); dlg["left"].hide();
me["right"].hide(); dlg["right"].hide();
} else if(all_pics.empty()) { } else if(all_pics.empty()) {
cStrDlog("You've just deleted the last custom graphics sheet, so this dialog will now close. If you want to add more sheets, you can of course reopen the dialog.", "", "Last Sheet Deleted", 16, PIC_DLOG).show(); cStrDlog("You've just deleted the last custom graphics sheet, so this dialog will now close. If you want to add more sheets, you can of course reopen the dialog.", "", "Last Sheet Deleted", 16, PIC_DLOG).show();
me.toast(true); dlg.toast(true);
return true; return true;
} }
if(cur > 0) cur--; if(cur > 0) cur--;
set_dlg_custom_sheet(me, all_pics[cur]); set_dlg_custom_sheet(all_pics[cur]);
return true; return true;
});
if(all_pics.size() == 1) {
pic_dlg["left"].hide();
pic_dlg["right"].hide();
} }
int icon_start = 0; // Show which icons are selected
int icon_end = 0; void show_icon_selection() {
int icon_min = 0; dlg["icon-min"].setTextToNum(icon_min);
int icon_max = 0; dlg["icon-max"].setTextToNum(icon_max);
std::vector<std::string> icon_buttons = {"icon-terr", "strip-terr", "add-terr"};
auto show_icon_selection = [&icon_min, &icon_max, &pic_dlg, icon_buttons]() {
pic_dlg["icon-min"].setTextToNum(icon_min);
pic_dlg["icon-max"].setTextToNum(icon_max);
// TODO highlight the selected icons visually // TODO highlight the selected icons visually
if(icon_min != 0){ if(icon_min != 0){
for(auto name : icon_buttons){ for(auto name : icon_buttons){
pic_dlg[name].show(); dlg[name].show();
} }
pic_dlg["diff-threshold"].show(); dlg["diff-threshold"].show();
}else{ }else{
for(auto name : icon_buttons){ for(auto name : icon_buttons){
pic_dlg[name].hide(); dlg[name].hide();
} }
pic_dlg["diff-threshold"].hide(); dlg["diff-threshold"].hide();
} }
}; };
show_icon_selection(); bool arrow_button_filter(std::string dir) {
pic_dlg.attachClickHandlers([&sheets,&cur,&all_pics,show_icon_selection,&icon_start,&icon_end,&icon_min,&icon_max](cDialog& me, std::string dir, eKeyMod) -> bool {
size_t old_cur = cur; size_t old_cur = cur;
if(dir == "left") { if(dir == "left") {
if(cur == 0) if(cur == 0)
@@ -4090,16 +4046,16 @@ void edit_custom_sheets() {
show_icon_selection(); show_icon_selection();
} }
set_dlg_custom_sheet(me, all_pics[cur]); set_dlg_custom_sheet(all_pics[cur]);
return true; return true;
}, {"left", "right"}); }
set_dlg_custom_sheet(pic_dlg, all_pics[cur]); bool click_on_sheet() {
eKeyMod mod = current_key_mod();
pic_dlg["sheet"].attachClickHandler([&all_pics, &cur, &icon_start, &icon_end, &icon_min, &icon_max, show_icon_selection](cDialog& me, std::string, eKeyMod mod) -> bool { location where_curs = sf::Mouse::getPosition(dlg.getWindow());
location where_curs = sf::Mouse::getPosition(me.getWindow()); where_curs = dlg.getWindow().mapPixelToCoords(where_curs);
where_curs = me.getWindow().mapPixelToCoords(where_curs); rectangle sheet_bounds = dlg["sheet"].getBounds();
rectangle sheet_bounds = me["sheet"].getBounds();
where_curs.x -= sheet_bounds.left; where_curs.x -= sheet_bounds.left;
where_curs.y -= sheet_bounds.top; where_curs.y -= sheet_bounds.top;
where_curs.x /= 28; where_curs.x /= 28;
@@ -4117,128 +4073,221 @@ void edit_custom_sheets() {
show_icon_selection(); show_icon_selection();
return true; return true;
}); }
static sf::BlendMode blend_replace(sf::BlendMode::Factor::One, sf::BlendMode::Factor::Zero, sf::BlendMode::Equation::Add); bool import_ter_icon() {
pic_dlg.attachClickHandlers([&icon_max, &icon_min, &all_pics, &cur, replace_from_image](cDialog& me, std::string item_hit, eKeyMod) -> bool {
int sheet_start = 1000 + 100 * all_pics[cur]; int sheet_start = 1000 + 100 * all_pics[cur];
if(item_hit == "icon-terr"){ set_up_canvas(all_pics[cur]);
set_up_canvas(all_pics[cur]); pic_num_t icon = choose_graphic(0, PIC_TER, &dlg);
pic_num_t icon = choose_graphic(0, PIC_TER, &me); for(int i = 0; i < (icon_max - icon_min) + 1; ++i){
for(int i = 0; i < (icon_max - icon_min) + 1; ++i){ int sheet_icon = icon_min + i - sheet_start;
int sheet_icon = icon_min + i - sheet_start; int x = sheet_icon % 10;
int x = sheet_icon % 10; int y = sheet_icon / 10;
int y = sheet_icon / 10; rectangle dest {y * 36, x * 28, y * 36 + 36, x * 28 + 28};
rectangle dest {y * 36, x * 28, y * 36 + 36, x * 28 + 28}; cPict::drawAt(mainPtr(),canvas(),dest,icon+i,PIC_TER,false);
cPict::drawAt(mainPtr(),canvas(),dest,icon+i,PIC_TER,false); }
} canvas().display();
canvas().display(); sf::Image img = canvas().getTexture().copyToImage();
sf::Image img = canvas().getTexture().copyToImage(); return replace_from_image("Import Terrain Icon", img);
return replace_from_image("Import Terrain Icon", img); }
}else if(item_hit == "strip-terr"){
pic_num_t icon = choose_graphic(0, PIC_TER, &me);
scratch().clear(sf::Color(0,0,0,0));
scratch().display();
sf::Image without_floor = scratch().getTexture().copyToImage();
cPict::drawAt(mainPtr(),scratch(),{0,0,36,28},icon,PIC_TER,false);
scratch().display();
sf::Image floor_to_strip = scratch().getTexture().copyToImage();
set_up_canvas(all_pics[cur]);
sf::Image canvas_image = canvas().getTexture().copyToImage();
for(int i = 0; i < (icon_max - icon_min) + 1; ++i){
sf::Image copy = without_floor;
int sheet_icon = icon_min + i - sheet_start;
int x = sheet_icon % 10;
int y = sheet_icon / 10;
rectangle dest {y * 36, x * 28, y * 36 + 36, x * 28 + 28};
for(int y = 0; y < 36; ++y){
for(int x = 0; x < 28; ++x){
sf::Color source = canvas_image.getPixel(dest.left + x, dest.top + y);
sf::Color floor_source = floor_to_strip.getPixel(x, y);
float epsilon = me["diff-threshold"].getTextAsNum(); bool strip_ter_floor() {
if(color_diff(source, floor_source) > epsilon){ int sheet_start = 1000 + 100 * all_pics[cur];
copy.setPixel(x, y, source); static sf::BlendMode blend_replace(sf::BlendMode::Factor::One, sf::BlendMode::Factor::Zero, sf::BlendMode::Equation::Add);
} pic_num_t icon = choose_graphic(0, PIC_TER, &dlg);
scratch().clear(sf::Color(0,0,0,0));
scratch().display();
sf::Image without_floor = scratch().getTexture().copyToImage();
cPict::drawAt(mainPtr(),scratch(),{0,0,36,28},icon,PIC_TER,false);
scratch().display();
sf::Image floor_to_strip = scratch().getTexture().copyToImage();
set_up_canvas(all_pics[cur]);
sf::Image canvas_image = canvas().getTexture().copyToImage();
for(int i = 0; i < (icon_max - icon_min) + 1; ++i){
sf::Image copy = without_floor;
int sheet_icon = icon_min + i - sheet_start;
int x = sheet_icon % 10;
int y = sheet_icon / 10;
rectangle dest {y * 36, x * 28, y * 36 + 36, x * 28 + 28};
for(int y = 0; y < 36; ++y){
for(int x = 0; x < 28; ++x){
sf::Color source = canvas_image.getPixel(dest.left + x, dest.top + y);
sf::Color floor_source = floor_to_strip.getPixel(x, y);
float epsilon = dlg["diff-threshold"].getTextAsNum();
if(color_diff2(source, floor_source) > pow(epsilon, 2)){
copy.setPixel(x, y, source);
} }
} }
sf::Texture t;
t.loadFromImage(copy);
sf::Sprite s(t);
s.setPosition(dest.left, dest.top);
canvas().draw(s, blend_replace);
} }
canvas().display(); sf::Texture t;
sf::Image img = canvas().getTexture().copyToImage(); t.loadFromImage(copy);
return replace_from_image("Strip Terrain Background", img); sf::Sprite s(t);
}else if(item_hit == "add-terr"){ s.setPosition(dest.left, dest.top);
pic_num_t icon = choose_graphic(0, PIC_TER, &me);
set_up_canvas(all_pics[cur]); canvas().draw(s, blend_replace);
}
canvas().display();
sf::Image img = canvas().getTexture().copyToImage();
return replace_from_image("Strip Terrain Background", img);
}
bool add_ter_floor() {
int sheet_start = 1000 + 100 * all_pics[cur];
static sf::BlendMode blend_replace(sf::BlendMode::Factor::One, sf::BlendMode::Factor::Zero, sf::BlendMode::Equation::Add);
pic_num_t icon = choose_graphic(0, PIC_TER, &dlg);
set_up_canvas(all_pics[cur]);
scratch().clear(sf::Color(0,0,0,0));
cPict::drawAt(mainPtr(),scratch(),{0,0,36,28},icon,PIC_TER,false);
scratch().display();
sf::Image floor_to_add = scratch().getTexture().copyToImage();
sf::Texture floor_t;
floor_t.loadFromImage(floor_to_add);
sf::Sprite floor_s(floor_t);
sf::Texture sheet_t = canvas().getTexture();
for(int i = 0; i < (icon_max - icon_min) + 1; ++i){
int sheet_icon = icon_min + i - sheet_start;
int x = sheet_icon % 10;
int y = sheet_icon / 10;
rectangle dest {y * 36, x * 28, y * 36 + 36, x * 28 + 28};
scratch().clear(sf::Color(0,0,0,0)); scratch().clear(sf::Color(0,0,0,0));
cPict::drawAt(mainPtr(),scratch(),{0,0,36,28},icon,PIC_TER,false); scratch().draw(floor_s);
sf::Sprite ter_s(sheet_t, dest);
scratch().draw(ter_s, sf::BlendAlpha);
scratch().display(); scratch().display();
sf::Sprite combined_s(scratch().getTexture());
combined_s.setPosition(dest.left, dest.top);
canvas().draw(combined_s, blend_replace);
}
canvas().display();
sf::Image img = canvas().getTexture().copyToImage();
return replace_from_image("Add Terrain Background", img);
}
sf::Image floor_to_add = scratch().getTexture().copyToImage(); public:
sf::Texture floor_t; cCustomGraphicsDialog() : dlg(*ResMgr::dialogs.get("graphic-sheets")) {
floor_t.loadFromImage(floor_to_add); pic_dir = tempDir/scenario_temp_dir_name/"graphics";
sf::Sprite floor_s(floor_t); if(!scenario.scen_file.has_extension()) // It's an unpacked scenario
pic_dir = scenario.scen_file/"graphics";
sf::Texture sheet_t = canvas().getTexture(); if(!fs::exists(pic_dir)) fs::create_directories(pic_dir);
for(int i = 0; i < (icon_max - icon_min) + 1; ++i){ }
int sheet_icon = icon_min + i - sheet_start; void run() {
int x = sheet_icon % 10; for(fs::directory_iterator iter(pic_dir); iter != fs::directory_iterator(); iter++) {
int y = sheet_icon / 10; std::string fname = iter->path().filename().string().c_str();
rectangle dest {y * 36, x * 28, y * 36 + 36, x * 28 + 28}; int dot = fname.find_last_of('.');
if(fname.substr(0,5) == "sheet" && fname.substr(dot) == ".png" && std::all_of(fname.begin()+5, fname.begin()+dot, isdigit)) {
scratch().clear(sf::Color(0,0,0,0)); int this_pic = boost::lexical_cast<int>(fname.substr(5,dot-5));
scratch().draw(floor_s); max_pic = max(max_pic, this_pic);
sf::Sprite ter_s(sheet_t, dest); all_pics.push_back(this_pic);
scratch().draw(ter_s, sf::BlendAlpha);
scratch().display();
sf::Sprite combined_s(scratch().getTexture());
combined_s.setPosition(dest.left, dest.top);
canvas().draw(combined_s, blend_replace);
} }
canvas().display();
sf::Image img = canvas().getTexture().copyToImage();
return replace_from_image("Add Terrain Background", img);
} }
return true; // Iterating through the graphics folder is not deterministic
},icon_buttons); std::sort(all_pics.begin(), all_pics.end());
shut_down_menus(5); // So that cmd+O, cmd+N, cmd+S can work // First, make sure we even have custom graphics! Also make sure they're not legacy format.
pic_dlg.run(); bool must_init_spec_g = false;
if(spec_scen_g.is_old) {
// Commit undo actions for everything that was done if(cChoiceDlog("convert-pics-now", {"cancel", "convert"}).show() == "cancel")
if(pic_dlg.accepted()){ return;
for(action_ptr action : deferred_actions){ spec_scen_g.convert_sheets();
undo_list.add(action); all_pics.resize(spec_scen_g.numSheets);
} std::iota(all_pics.begin(), all_pics.end(), 0);
} } else if(max_pic < 0) {
// On cancel, UWIND the stack of deferred actions, reversing creations and deletions too. if(cChoiceDlog("have-no-pics", {"cancel", "new"}).show() == "cancel")
else{ return;
while(!deferred_actions.empty()){ must_init_spec_g = true;
action_ptr previous = deferred_actions.back(); } else if(max_pic >= 0 && spec_scen_g.numSheets < 1) {
deferred_actions.pop_back(); if(cChoiceDlog("have-only-full-pics", {"cancel", "new"}).show() == "new")
previous->undo(); must_init_spec_g = true;
} }
if(must_init_spec_g) {
spec_scen_g.clear();
spec_scen_g.sheets.resize(1);
spec_scen_g.numSheets = 1;
spec_scen_g.init_sheet(0);
spec_scen_g.sheets[0]->copyToImage().saveToFile((pic_dir/"sheet0.png").string().c_str());
all_pics.insert(all_pics.begin(), 0);
ResMgr::graphics.pushPath(pic_dir);
// We'll update the edit menu after this dialog closes
deferred_actions.push_back(action_ptr(new aCreateGraphicsSheet(0)));
}
set_cursor(watch_curs);
// Get image data from the sheets in memory
for(size_t i = 0; i < spec_scen_g.numSheets; i++) {
sheets[i] = spec_scen_g.sheets[i]->copyToImage();
}
using namespace std::placeholders;
dlg["cancel"].attachClickHandler(std::bind(&cDialog::toast, _1, false));
dlg["okay"].attachClickHandler(std::bind(&cDialog::toast, _1, true));
dlg["copy"].attachClickHandler(std::bind(&cCustomGraphicsDialog::copy_sheet,this));
dlg["paste"].attachClickHandler(std::bind(&cCustomGraphicsDialog::paste_sheet,this));
dlg["edit"].attachClickHandler(std::bind(&cCustomGraphicsDialog::edit_sheet_external,this));
dlg["reload"].attachClickHandler(std::bind(&cCustomGraphicsDialog::reload_sheet,this));
dlg["open"].attachClickHandler(std::bind(&cCustomGraphicsDialog::import_sheet,this));
dlg["save"].attachClickHandler(std::bind(&cCustomGraphicsDialog::export_sheet,this));
dlg["new"].attachClickHandler(std::bind(&cCustomGraphicsDialog::new_sheet,this));
dlg["del"].attachClickHandler(std::bind(&cCustomGraphicsDialog::delete_sheet,this));
if(all_pics.size() == 1) {
dlg["left"].hide();
dlg["right"].hide();
}
show_icon_selection();
dlg["left"].attachClickHandler(std::bind(&cCustomGraphicsDialog::arrow_button_filter, this, "left"));
dlg["right"].attachClickHandler(std::bind(&cCustomGraphicsDialog::arrow_button_filter, this, "right"));
set_dlg_custom_sheet(all_pics[cur]);
dlg["sheet"].attachClickHandler(std::bind(&cCustomGraphicsDialog::click_on_sheet, this));
dlg["icon-terr"].attachClickHandler(std::bind(&cCustomGraphicsDialog::import_ter_icon,this));
dlg["strip-terr"].attachClickHandler(std::bind(&cCustomGraphicsDialog::strip_ter_floor,this));
dlg["add-terr"].attachClickHandler(std::bind(&cCustomGraphicsDialog::add_ter_floor,this));
shut_down_menus(5); // So that cmd+O, cmd+N, cmd+S can work
dlg.run();
// Commit undo actions for everything that was done
if(dlg.accepted()){
for(action_ptr action : deferred_actions){
undo_list.add(action);
}
}
// On cancel, UWIND the stack of deferred actions, reversing creations and deletions too.
else{
while(!deferred_actions.empty()){
action_ptr previous = deferred_actions.back();
deferred_actions.pop_back();
previous->undo();
}
}
// Restore menus
shut_down_menus(4);
if(overall_mode <= MODE_MAIN_SCREEN)
shut_down_menus(editing_town ? 2 : 1);
else shut_down_menus(3);
update_edit_menu();
} }
};
// Restore menus void edit_custom_sheets() {
shut_down_menus(4); cCustomGraphicsDialog().run();
if(overall_mode <= MODE_MAIN_SCREEN)
shut_down_menus(editing_town ? 2 : 1);
else shut_down_menus(3);
update_edit_menu();
} }
fs::path get_snd_path(size_t index) { fs::path get_snd_path(size_t index) {
extern fs::path tempDir;
extern std::string scenario_temp_dir_name;
fs::path sndpath = tempDir/scenario_temp_dir_name/"sounds"; fs::path sndpath = tempDir/scenario_temp_dir_name/"sounds";
std::string sndbasenm = "SND" + std::to_string(index); std::string sndbasenm = "SND" + std::to_string(index);
fs::path sndfile = sndpath/(sndbasenm + ".wav"); fs::path sndfile = sndpath/(sndbasenm + ".wav");