refactor custom graphics dialog into a class
This commit is contained in:
@@ -3715,29 +3715,50 @@ 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;
|
||||||
|
size_t cur = 0;
|
||||||
|
std::unordered_map<size_t, sf::Image> sheets;
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
int max_pic = -1;
|
||||||
|
std::vector<int> all_pics;
|
||||||
|
fs::path pic_dir;
|
||||||
|
|
||||||
|
// Icon selection info
|
||||||
|
int icon_start = 0;
|
||||||
|
int icon_end = 0;
|
||||||
|
int icon_min = 0;
|
||||||
|
int icon_max = 0;
|
||||||
|
// Buttons for working with icons
|
||||||
|
std::vector<std::string> icon_buttons = {"icon-terr", "strip-terr", "add-terr"};
|
||||||
|
|
||||||
|
// 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;
|
static sf::RenderTexture instance;
|
||||||
if(!canvas_created){
|
if(!canvas_created){
|
||||||
instance.create(280, 360);
|
instance.create(280, 360);
|
||||||
canvas_created = true;
|
canvas_created = true;
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool scratch_created = false;
|
// Keep a 1-tile RenderTexture for rendering new tiles
|
||||||
static sf::RenderTexture& scratch() {
|
static sf::RenderTexture& scratch() {
|
||||||
|
static bool scratch_created = false;
|
||||||
static sf::RenderTexture instance;
|
static sf::RenderTexture instance;
|
||||||
if(!scratch_created){
|
if(!scratch_created){
|
||||||
instance.create(28, 36);
|
instance.create(28, 36);
|
||||||
scratch_created = true;
|
scratch_created = true;
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_up_canvas(size_t sheet){
|
// Set up the canvas texture for modifying a given graphics sheet
|
||||||
fs::path pic_dir = tempDir/scenario_temp_dir_name/"graphics";
|
void set_up_canvas(size_t sheet){
|
||||||
fs::path fromPath = pic_dir/("sheet" + std::to_string(sheet) + ".png");
|
fs::path fromPath = pic_dir/("sheet" + std::to_string(sheet) + ".png");
|
||||||
|
|
||||||
sf::Texture texture;
|
sf::Texture texture;
|
||||||
@@ -3745,31 +3766,416 @@ static void set_up_canvas(size_t sheet){
|
|||||||
sf::Sprite s(texture);
|
sf::Sprite s(texture);
|
||||||
canvas().clear(sf::Color(0,0,0,0));
|
canvas().clear(sf::Color(0,0,0,0));
|
||||||
canvas().draw(s);
|
canvas().draw(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_dlg_custom_sheet(cDialog& me, size_t sheet) {
|
// Set which custom sheet is viewed
|
||||||
me["num"].setTextToNum(sheet);
|
void set_dlg_custom_sheet(size_t sheet) {
|
||||||
dynamic_cast<cPict&>(me["sheet"]).setPict(sheet, PIC_FULL);
|
dlg["num"].setTextToNum(sheet);
|
||||||
}
|
dynamic_cast<cPict&>(dlg["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) {
|
||||||
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);
|
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() {
|
// Replace the current sheet with the image in a file
|
||||||
// Everything you do in this dialog can be undone when you hit cancel, leaving no trace in the main history.
|
bool replace_from_file(std::string action_name, fs::path fpath) {
|
||||||
std::vector<action_ptr> deferred_actions;
|
sf::Image img;
|
||||||
|
if(!img.loadFromFile(fpath.string().c_str())) {
|
||||||
|
beep();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(cur >= spec_scen_g.numSheets) {
|
||||||
|
std::string resName = "sheet" + std::to_string(all_pics[cur]);
|
||||||
|
fs::path toPath = pic_dir/(resName + ".png");
|
||||||
|
|
||||||
int max_pic = -1;
|
sf::Image image_for_undo;
|
||||||
std::vector<int> all_pics;
|
// TODO for reload, old image and new image will be the same! This could possibly be addressed by loading the image when edit is clicked?
|
||||||
fs::path pic_dir = tempDir/scenario_temp_dir_name/"graphics";
|
// But there is no guarantee Edit is clicked first--the designer could externally edit the full-sheet image whenever they want.
|
||||||
|
|
||||||
|
// Also comparing image blobs for equality is probably expensive, but we should have a check to prevent adding undo actions if the same file was imported
|
||||||
|
// or reload is clicked when the file isn't changed.
|
||||||
|
image_for_undo.loadFromFile(toPath.string());
|
||||||
|
deferred_actions.push_back(action_ptr(new aReplaceGraphicsSheet(action_name, all_pics[cur], image_for_undo, img)));
|
||||||
|
|
||||||
|
img.saveToFile(toPath.string().c_str());
|
||||||
|
ResMgr::graphics.free(resName);
|
||||||
|
set_dlg_custom_sheet(all_pics[cur]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
deferred_actions.push_back(action_ptr(new aReplaceGraphicsSheet(action_name, all_pics[cur], sheets[cur], img)));
|
||||||
|
|
||||||
|
sheets[cur] = img;
|
||||||
|
spec_scen_g.replace_sheet(cur, img);
|
||||||
|
set_dlg_custom_sheet(all_pics[cur]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
std::string resName = "sheet" + std::to_string(all_pics[cur]);
|
||||||
|
fs::path toPath = pic_dir/(resName + ".png");
|
||||||
|
|
||||||
|
sf::Image image_for_undo;
|
||||||
|
image_for_undo.loadFromFile(toPath.string());
|
||||||
|
deferred_actions.push_back(action_ptr(new aReplaceGraphicsSheet(action_name, all_pics[cur], image_for_undo, image)));
|
||||||
|
|
||||||
|
image.saveToFile(toPath.string().c_str());
|
||||||
|
ResMgr::graphics.free(resName);
|
||||||
|
set_dlg_custom_sheet(all_pics[cur]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
deferred_actions.push_back(action_ptr(new aReplaceGraphicsSheet(action_name, cur, sheets[cur], image)));
|
||||||
|
|
||||||
|
sheets[cur] = image;
|
||||||
|
spec_scen_g.replace_sheet(cur, image);
|
||||||
|
set_dlg_custom_sheet(all_pics[cur]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the current graphic sheet to system clipboard
|
||||||
|
bool copy_sheet() {
|
||||||
|
if(cur >= spec_scen_g.numSheets) {
|
||||||
|
fs::path fromPath = pic_dir/("sheet" + std::to_string(all_pics[cur]) + ".png");
|
||||||
|
sf::Image img;
|
||||||
|
img.loadFromFile(fromPath.string().c_str());
|
||||||
|
set_clipboard_img(img);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
set_clipboard_img(sheets[cur]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paste image from system clipboard over current sheet
|
||||||
|
bool paste_sheet() {
|
||||||
|
auto img = get_clipboard_img();
|
||||||
|
if(img == nullptr) {
|
||||||
|
beep();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return replace_from_image("Paste Graphics Sheet", *img);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool edit_sheet_external() {
|
||||||
|
fs::path image_editor = get_string_pref("ImageEditor");
|
||||||
|
if(image_editor.empty()){
|
||||||
|
showWarning("Choose an external image editor in the preferences window first.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#ifdef SFML_SYSTEM_MACOS
|
||||||
|
// Find actual binary inside the .app bundle:
|
||||||
|
|
||||||
|
image_editor = image_editor / "Contents" / "MacOS" / image_editor.stem();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string resName = "sheet" + std::to_string(all_pics[cur]);
|
||||||
|
fs::path toPath = pic_dir/(resName + ".png");
|
||||||
|
|
||||||
|
std::vector<std::string> args = {toPath.string()};
|
||||||
|
try{
|
||||||
|
bp::child ch(image_editor, args, bp::std_out > stdout);
|
||||||
|
ch.detach();
|
||||||
|
}
|
||||||
|
catch(std::exception& x) {
|
||||||
|
showError(std::string{"Error running your image editor: "} + x.what());
|
||||||
|
} catch(std::string& x) {
|
||||||
|
showError("Error running your image editor: " + x);
|
||||||
|
} catch(...) {
|
||||||
|
showError("An unknown error occurred running your image editor!");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload external changes to current graphics sheet
|
||||||
|
bool reload_sheet() {
|
||||||
|
std::string resName = "sheet" + std::to_string(all_pics[cur]);
|
||||||
|
fs::path sheetPath = pic_dir/(resName + ".png");
|
||||||
|
return replace_from_file("Reload Graphics Sheet", sheetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Import image as graphics sheet
|
||||||
|
bool import_sheet() {
|
||||||
|
fs::path fpath = nav_get_rsrc({"png", "bmp", "jpg", "jpeg", "gif", "psd"});
|
||||||
|
if(fpath.empty()) return true;
|
||||||
|
replace_from_file("Import Graphics Sheet", fpath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export current sheet to image file
|
||||||
|
bool export_sheet() {
|
||||||
|
fs::path fpath = nav_put_rsrc({"png", "bmp", "jpg", "jpeg"});
|
||||||
|
if(fpath.empty()) return true;
|
||||||
|
if(cur >= spec_scen_g.numSheets) {
|
||||||
|
fs::path fromPath = pic_dir/("sheet" + std::to_string(all_pics[cur]) + ".png");
|
||||||
|
sf::Image img;
|
||||||
|
img.loadFromFile(fromPath.string().c_str());
|
||||||
|
img.saveToFile(fpath.string().c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
sheets[cur].saveToFile(fpath.string().c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new sheet
|
||||||
|
bool new_sheet() {
|
||||||
|
cChoiceDlog pickNum("add-new-sheet", {"cancel", "new"}, &dlg);
|
||||||
|
pickNum->getControl("num").setTextToNum(spec_scen_g.numSheets);
|
||||||
|
if(pickNum.show() == "cancel") return true;
|
||||||
|
int newSheet = pickNum->getControl("num").getTextAsNum();
|
||||||
|
fs::path sheetPath = pic_dir/("sheet" + std::to_string(newSheet) + ".png");
|
||||||
|
if(newSheet == spec_scen_g.numSheets) {
|
||||||
|
spec_scen_g.sheets.emplace_back();
|
||||||
|
spec_scen_g.init_sheet(newSheet);
|
||||||
|
spec_scen_g.sheets[newSheet]->copyToImage().saveToFile(sheetPath.string().c_str());
|
||||||
|
spec_scen_g.numSheets++;
|
||||||
|
auto iter = all_pics.insert(std::upper_bound(all_pics.begin(), all_pics.end(), newSheet), newSheet);
|
||||||
|
cur = iter - all_pics.begin();
|
||||||
|
} else {
|
||||||
|
auto iter = std::lower_bound(all_pics.begin(), all_pics.end(), 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), &dlg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
iter = all_pics.insert(iter, newSheet);
|
||||||
|
cur = iter - all_pics.begin();
|
||||||
|
sf::Image img;
|
||||||
|
img.create(280, 360);
|
||||||
|
img.saveToFile(sheetPath.string().c_str());
|
||||||
|
}
|
||||||
|
dlg["left"].show();
|
||||||
|
dlg["right"].show();
|
||||||
|
set_dlg_custom_sheet(all_pics[cur]);
|
||||||
|
|
||||||
|
|
||||||
|
deferred_actions.push_back(action_ptr(new aCreateGraphicsSheet(newSheet)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the current sheet
|
||||||
|
bool delete_sheet() {
|
||||||
|
int which_pic = all_pics[cur];
|
||||||
|
|
||||||
|
fs::path fpath = pic_dir/("sheet" + std::to_string(which_pic) + ".png");
|
||||||
|
bool moved = false;
|
||||||
|
sf::Image image_for_undo;
|
||||||
|
image_for_undo.loadFromFile(fpath.string());
|
||||||
|
|
||||||
|
if(which_pic < spec_scen_g.numSheets) {
|
||||||
|
std::string choice = "del";
|
||||||
|
if(which_pic < spec_scen_g.numSheets - 1)
|
||||||
|
choice = cChoiceDlog("must-delete-in-order", {"cancel", "del", "move"}, &dlg).show();
|
||||||
|
if(choice == "cancel") return true;
|
||||||
|
if(choice == "move") {
|
||||||
|
moved = true;
|
||||||
|
spec_scen_g.sheets.erase(spec_scen_g.sheets.begin() + which_pic);
|
||||||
|
spec_scen_g.numSheets--;
|
||||||
|
for(; which_pic < spec_scen_g.numSheets; which_pic++) {
|
||||||
|
fs::path from = pic_dir/("sheet" + std::to_string(which_pic + 1) + ".png");
|
||||||
|
fs::path to = pic_dir/("sheet" + std::to_string(which_pic) + ".png");
|
||||||
|
if(!fs::exists(from)) continue; // Just in case
|
||||||
|
fs::remove(to);
|
||||||
|
fs::rename(from, to);
|
||||||
|
ResMgr::graphics.free("sheet" + std::to_string(which_pic));
|
||||||
|
}
|
||||||
|
auto end = std::find(all_pics.begin() + cur, all_pics.end(), which_pic - 1);
|
||||||
|
if(end != all_pics.end())
|
||||||
|
all_pics.erase(end);
|
||||||
|
else {
|
||||||
|
// This shouldn't be reached
|
||||||
|
std::cerr << "Whoops! Somehow failed to remove the index of the deleted sheet!" << std::endl;
|
||||||
|
}
|
||||||
|
} else if(choice == "del") {
|
||||||
|
all_pics.erase(all_pics.begin() + cur);
|
||||||
|
spec_scen_g.numSheets = which_pic;
|
||||||
|
spec_scen_g.sheets.resize(which_pic);
|
||||||
|
ResMgr::graphics.free("sheet" + std::to_string(which_pic));
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
all_pics.erase(all_pics.begin() + cur);
|
||||||
|
}
|
||||||
|
if(fs::exists(fpath)) fs::remove(fpath);
|
||||||
|
|
||||||
|
deferred_actions.push_back(action_ptr(new aDeleteGraphicsSheet(which_pic, moved, image_for_undo)));
|
||||||
|
|
||||||
|
if(all_pics.size() == 1) {
|
||||||
|
dlg["left"].hide();
|
||||||
|
dlg["right"].hide();
|
||||||
|
} 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();
|
||||||
|
dlg.toast(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(cur > 0) cur--;
|
||||||
|
set_dlg_custom_sheet(all_pics[cur]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show which icons are selected
|
||||||
|
void show_icon_selection() {
|
||||||
|
dlg["icon-min"].setTextToNum(icon_min);
|
||||||
|
dlg["icon-max"].setTextToNum(icon_max);
|
||||||
|
// TODO highlight the selected icons visually
|
||||||
|
if(icon_min != 0){
|
||||||
|
for(auto name : icon_buttons){
|
||||||
|
dlg[name].show();
|
||||||
|
}
|
||||||
|
dlg["diff-threshold"].show();
|
||||||
|
}else{
|
||||||
|
for(auto name : icon_buttons){
|
||||||
|
dlg[name].hide();
|
||||||
|
}
|
||||||
|
dlg["diff-threshold"].hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool arrow_button_filter(std::string dir) {
|
||||||
|
size_t old_cur = cur;
|
||||||
|
if(dir == "left") {
|
||||||
|
if(cur == 0)
|
||||||
|
cur = all_pics.size() - 1;
|
||||||
|
else cur--;
|
||||||
|
} else if(dir == "right") {
|
||||||
|
cur++;
|
||||||
|
if(cur >= all_pics.size())
|
||||||
|
cur = 0;
|
||||||
|
} else return true;
|
||||||
|
if(cur != old_cur){
|
||||||
|
icon_start = icon_end = icon_min = icon_max = 0;
|
||||||
|
show_icon_selection();
|
||||||
|
}
|
||||||
|
|
||||||
|
set_dlg_custom_sheet(all_pics[cur]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool click_on_sheet() {
|
||||||
|
eKeyMod mod = current_key_mod();
|
||||||
|
|
||||||
|
location where_curs = sf::Mouse::getPosition(dlg.getWindow());
|
||||||
|
where_curs = dlg.getWindow().mapPixelToCoords(where_curs);
|
||||||
|
rectangle sheet_bounds = dlg["sheet"].getBounds();
|
||||||
|
where_curs.x -= sheet_bounds.left;
|
||||||
|
where_curs.y -= sheet_bounds.top;
|
||||||
|
where_curs.x /= 28;
|
||||||
|
where_curs.y /= 36;
|
||||||
|
|
||||||
|
int icon_hit = 1000 + 100 * all_pics[cur] + 10 * where_curs.y + where_curs.x;
|
||||||
|
|
||||||
|
if(mod_contains(mod, mod_shift)){
|
||||||
|
icon_end = icon_hit;
|
||||||
|
}else{
|
||||||
|
icon_start = icon_end = icon_hit;
|
||||||
|
}
|
||||||
|
icon_min = min(icon_start, icon_end);
|
||||||
|
icon_max = max(icon_start, icon_end);
|
||||||
|
show_icon_selection();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool import_ter_icon() {
|
||||||
|
int sheet_start = 1000 + 100 * all_pics[cur];
|
||||||
|
set_up_canvas(all_pics[cur]);
|
||||||
|
pic_num_t icon = choose_graphic(0, PIC_TER, &dlg);
|
||||||
|
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};
|
||||||
|
cPict::drawAt(mainPtr(),canvas(),dest,icon+i,PIC_TER,false);
|
||||||
|
}
|
||||||
|
canvas().display();
|
||||||
|
sf::Image img = canvas().getTexture().copyToImage();
|
||||||
|
return replace_from_image("Import Terrain Icon", img);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool strip_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);
|
||||||
|
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::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().draw(floor_s);
|
||||||
|
sf::Sprite ter_s(sheet_t, dest);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
cCustomGraphicsDialog() : dlg(*ResMgr::dialogs.get("graphic-sheets")) {
|
||||||
|
pic_dir = tempDir/scenario_temp_dir_name/"graphics";
|
||||||
if(!scenario.scen_file.has_extension()) // It's an unpacked scenario
|
if(!scenario.scen_file.has_extension()) // It's an unpacked scenario
|
||||||
pic_dir = scenario.scen_file/"graphics";
|
pic_dir = scenario.scen_file/"graphics";
|
||||||
if(!fs::exists(pic_dir)) fs::create_directories(pic_dir);
|
if(!fs::exists(pic_dir)) fs::create_directories(pic_dir);
|
||||||
|
}
|
||||||
|
void run() {
|
||||||
for(fs::directory_iterator iter(pic_dir); iter != fs::directory_iterator(); iter++) {
|
for(fs::directory_iterator iter(pic_dir); iter != fs::directory_iterator(); iter++) {
|
||||||
std::string fname = iter->path().filename().string().c_str();
|
std::string fname = iter->path().filename().string().c_str();
|
||||||
int dot = fname.find_last_of('.');
|
int dot = fname.find_last_of('.');
|
||||||
@@ -3815,405 +4221,45 @@ void edit_custom_sheets() {
|
|||||||
set_cursor(watch_curs);
|
set_cursor(watch_curs);
|
||||||
|
|
||||||
// Get image data from the sheets in memory
|
// Get image data from the sheets in memory
|
||||||
size_t cur = 0;
|
|
||||||
std::unordered_map<size_t, sf::Image> sheets;
|
|
||||||
for(size_t i = 0; i < spec_scen_g.numSheets; i++) {
|
for(size_t i = 0; i < spec_scen_g.numSheets; i++) {
|
||||||
sheets[i] = spec_scen_g.sheets[i]->copyToImage();
|
sheets[i] = spec_scen_g.sheets[i]->copyToImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
cDialog pic_dlg(*ResMgr::dialogs.get("graphic-sheets"));
|
|
||||||
|
|
||||||
auto replace_from_file = [&pic_dlg,&sheets,&cur,&all_pics,&pic_dir,&deferred_actions](std::string action_name, fs::path fpath) -> bool {
|
|
||||||
sf::Image img;
|
|
||||||
if(!img.loadFromFile(fpath.string().c_str())) {
|
|
||||||
beep();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if(cur >= spec_scen_g.numSheets) {
|
|
||||||
std::string resName = "sheet" + std::to_string(all_pics[cur]);
|
|
||||||
fs::path toPath = pic_dir/(resName + ".png");
|
|
||||||
|
|
||||||
sf::Image image_for_undo;
|
|
||||||
// TODO for reload, old image and new image will be the same! This could possibly be addressed by loading the image when edit is clicked?
|
|
||||||
// But there is no guarantee Edit is clicked first--the designer could externally edit the full-sheet image whenever they want.
|
|
||||||
|
|
||||||
// Also comparing image blobs for equality is probably expensive, but we should have a check to prevent adding undo actions if the same file was imported
|
|
||||||
// or reload is clicked when the file isn't changed.
|
|
||||||
image_for_undo.loadFromFile(toPath.string());
|
|
||||||
deferred_actions.push_back(action_ptr(new aReplaceGraphicsSheet(action_name, all_pics[cur], image_for_undo, img)));
|
|
||||||
|
|
||||||
img.saveToFile(toPath.string().c_str());
|
|
||||||
ResMgr::graphics.free(resName);
|
|
||||||
set_dlg_custom_sheet(pic_dlg, all_pics[cur]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
deferred_actions.push_back(action_ptr(new aReplaceGraphicsSheet(action_name, all_pics[cur], sheets[cur], img)));
|
|
||||||
|
|
||||||
sheets[cur] = img;
|
|
||||||
spec_scen_g.replace_sheet(cur, img);
|
|
||||||
set_dlg_custom_sheet(pic_dlg, all_pics[cur]);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto replace_from_image = [&cur, &all_pics, pic_dir, &deferred_actions, &sheets, &pic_dlg](std::string action_name, sf::Image& image) -> bool {
|
|
||||||
if(cur >= spec_scen_g.numSheets) {
|
|
||||||
std::string resName = "sheet" + std::to_string(all_pics[cur]);
|
|
||||||
fs::path toPath = pic_dir/(resName + ".png");
|
|
||||||
|
|
||||||
sf::Image image_for_undo;
|
|
||||||
image_for_undo.loadFromFile(toPath.string());
|
|
||||||
deferred_actions.push_back(action_ptr(new aReplaceGraphicsSheet(action_name, all_pics[cur], image_for_undo, image)));
|
|
||||||
|
|
||||||
image.saveToFile(toPath.string().c_str());
|
|
||||||
ResMgr::graphics.free(resName);
|
|
||||||
set_dlg_custom_sheet(pic_dlg, all_pics[cur]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
deferred_actions.push_back(action_ptr(new aReplaceGraphicsSheet(action_name, cur, sheets[cur], image)));
|
|
||||||
|
|
||||||
sheets[cur] = image;
|
|
||||||
spec_scen_g.replace_sheet(cur, image);
|
|
||||||
set_dlg_custom_sheet(pic_dlg, all_pics[cur]);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
pic_dlg["cancel"].attachClickHandler(std::bind(&cDialog::toast, _1, false));
|
dlg["cancel"].attachClickHandler(std::bind(&cDialog::toast, _1, false));
|
||||||
pic_dlg["okay"].attachClickHandler(std::bind(&cDialog::toast, _1, true));
|
dlg["okay"].attachClickHandler(std::bind(&cDialog::toast, _1, true));
|
||||||
pic_dlg["copy"].attachClickHandler([&sheets,&cur,&all_pics,&pic_dir](cDialog&, std::string, eKeyMod) -> bool {
|
dlg["copy"].attachClickHandler(std::bind(&cCustomGraphicsDialog::copy_sheet,this));
|
||||||
if(cur >= spec_scen_g.numSheets) {
|
dlg["paste"].attachClickHandler(std::bind(&cCustomGraphicsDialog::paste_sheet,this));
|
||||||
fs::path fromPath = pic_dir/("sheet" + std::to_string(all_pics[cur]) + ".png");
|
dlg["edit"].attachClickHandler(std::bind(&cCustomGraphicsDialog::edit_sheet_external,this));
|
||||||
sf::Image img;
|
dlg["reload"].attachClickHandler(std::bind(&cCustomGraphicsDialog::reload_sheet,this));
|
||||||
img.loadFromFile(fromPath.string().c_str());
|
dlg["open"].attachClickHandler(std::bind(&cCustomGraphicsDialog::import_sheet,this));
|
||||||
set_clipboard_img(img);
|
dlg["save"].attachClickHandler(std::bind(&cCustomGraphicsDialog::export_sheet,this));
|
||||||
return true;
|
dlg["new"].attachClickHandler(std::bind(&cCustomGraphicsDialog::new_sheet,this));
|
||||||
}
|
dlg["del"].attachClickHandler(std::bind(&cCustomGraphicsDialog::delete_sheet,this));
|
||||||
set_clipboard_img(sheets[cur]);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
pic_dlg["paste"].attachClickHandler([&sheets,&cur,&all_pics,&pic_dir,&deferred_actions,replace_from_image](cDialog& me, std::string, eKeyMod) -> bool {
|
|
||||||
auto img = get_clipboard_img();
|
|
||||||
if(img == nullptr) {
|
|
||||||
beep();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return replace_from_image("Paste Graphics Sheet", *img);
|
|
||||||
});
|
|
||||||
pic_dlg["edit"].attachClickHandler([&sheets,&cur,&all_pics,&pic_dir](cDialog&, std::string, eKeyMod) -> bool {
|
|
||||||
fs::path image_editor = get_string_pref("ImageEditor");
|
|
||||||
if(image_editor.empty()){
|
|
||||||
showWarning("Choose an external image editor in the preferences window first.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#ifdef SFML_SYSTEM_MACOS
|
|
||||||
// Find actual binary inside the .app bundle:
|
|
||||||
|
|
||||||
image_editor = image_editor / "Contents" / "MacOS" / image_editor.stem();
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::string resName = "sheet" + std::to_string(all_pics[cur]);
|
|
||||||
fs::path toPath = pic_dir/(resName + ".png");
|
|
||||||
|
|
||||||
std::vector<std::string> args = {toPath.string()};
|
|
||||||
try{
|
|
||||||
bp::child ch(image_editor, args, bp::std_out > stdout);
|
|
||||||
ch.detach();
|
|
||||||
}
|
|
||||||
catch(std::exception& x) {
|
|
||||||
showError(std::string{"Error running your image editor: "} + x.what());
|
|
||||||
} catch(std::string& x) {
|
|
||||||
showError("Error running your image editor: " + x);
|
|
||||||
} catch(...) {
|
|
||||||
showError("An unknown error occurred running your image editor!");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
pic_dlg["reload"].attachClickHandler([replace_from_file, &all_pics, &cur, &pic_dir](cDialog&, std::string, eKeyMod) -> bool {
|
|
||||||
std::string resName = "sheet" + std::to_string(all_pics[cur]);
|
|
||||||
fs::path sheetPath = pic_dir/(resName + ".png");
|
|
||||||
return replace_from_file("Reload Graphics Sheet", sheetPath);
|
|
||||||
});
|
|
||||||
pic_dlg["open"].attachClickHandler([replace_from_file](cDialog& me, std::string, eKeyMod) -> bool {
|
|
||||||
fs::path fpath = nav_get_rsrc({"png", "bmp", "jpg", "jpeg", "gif", "psd"});
|
|
||||||
if(fpath.empty()) return true;
|
|
||||||
replace_from_file("Import Graphics Sheet", fpath);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
pic_dlg["save"].attachClickHandler([&sheets,&cur,&all_pics,&pic_dir](cDialog&, std::string, eKeyMod) -> bool {
|
|
||||||
fs::path fpath = nav_put_rsrc({"png", "bmp", "jpg", "jpeg"});
|
|
||||||
if(fpath.empty()) return true;
|
|
||||||
if(cur >= spec_scen_g.numSheets) {
|
|
||||||
fs::path fromPath = pic_dir/("sheet" + std::to_string(all_pics[cur]) + ".png");
|
|
||||||
sf::Image img;
|
|
||||||
img.loadFromFile(fromPath.string().c_str());
|
|
||||||
img.saveToFile(fpath.string().c_str());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
sheets[cur].saveToFile(fpath.string().c_str());
|
|
||||||
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);
|
|
||||||
pickNum->getControl("num").setTextToNum(spec_scen_g.numSheets);
|
|
||||||
if(pickNum.show() == "cancel") return true;
|
|
||||||
int newSheet = pickNum->getControl("num").getTextAsNum();
|
|
||||||
fs::path sheetPath = pic_dir/("sheet" + std::to_string(newSheet) + ".png");
|
|
||||||
if(newSheet == spec_scen_g.numSheets) {
|
|
||||||
spec_scen_g.sheets.emplace_back();
|
|
||||||
spec_scen_g.init_sheet(newSheet);
|
|
||||||
spec_scen_g.sheets[newSheet]->copyToImage().saveToFile(sheetPath.string().c_str());
|
|
||||||
spec_scen_g.numSheets++;
|
|
||||||
auto iter = all_pics.insert(std::upper_bound(all_pics.begin(), all_pics.end(), newSheet), newSheet);
|
|
||||||
cur = iter - all_pics.begin();
|
|
||||||
} else {
|
|
||||||
auto iter = std::lower_bound(all_pics.begin(), all_pics.end(), 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);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
iter = all_pics.insert(iter, newSheet);
|
|
||||||
cur = iter - all_pics.begin();
|
|
||||||
sf::Image img;
|
|
||||||
img.create(280, 360);
|
|
||||||
img.saveToFile(sheetPath.string().c_str());
|
|
||||||
}
|
|
||||||
me["left"].show();
|
|
||||||
me["right"].show();
|
|
||||||
set_dlg_custom_sheet(me, all_pics[cur]);
|
|
||||||
|
|
||||||
|
|
||||||
deferred_actions.push_back(action_ptr(new aCreateGraphicsSheet(newSheet)));
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
pic_dlg["del"].attachClickHandler([&sheets,&cur,&all_pics,&pic_dir,&deferred_actions](cDialog& me, std::string, eKeyMod) -> bool {
|
|
||||||
int which_pic = all_pics[cur];
|
|
||||||
|
|
||||||
fs::path fpath = pic_dir/("sheet" + std::to_string(which_pic) + ".png");
|
|
||||||
bool moved = false;
|
|
||||||
sf::Image image_for_undo;
|
|
||||||
image_for_undo.loadFromFile(fpath.string());
|
|
||||||
|
|
||||||
if(which_pic < spec_scen_g.numSheets) {
|
|
||||||
std::string choice = "del";
|
|
||||||
if(which_pic < spec_scen_g.numSheets - 1)
|
|
||||||
choice = cChoiceDlog("must-delete-in-order", {"cancel", "del", "move"}, &me).show();
|
|
||||||
if(choice == "cancel") return true;
|
|
||||||
if(choice == "move") {
|
|
||||||
moved = true;
|
|
||||||
spec_scen_g.sheets.erase(spec_scen_g.sheets.begin() + which_pic);
|
|
||||||
spec_scen_g.numSheets--;
|
|
||||||
for(; which_pic < spec_scen_g.numSheets; which_pic++) {
|
|
||||||
fs::path from = pic_dir/("sheet" + std::to_string(which_pic + 1) + ".png");
|
|
||||||
fs::path to = pic_dir/("sheet" + std::to_string(which_pic) + ".png");
|
|
||||||
if(!fs::exists(from)) continue; // Just in case
|
|
||||||
fs::remove(to);
|
|
||||||
fs::rename(from, to);
|
|
||||||
ResMgr::graphics.free("sheet" + std::to_string(which_pic));
|
|
||||||
}
|
|
||||||
auto end = std::find(all_pics.begin() + cur, all_pics.end(), which_pic - 1);
|
|
||||||
if(end != all_pics.end())
|
|
||||||
all_pics.erase(end);
|
|
||||||
else {
|
|
||||||
// This shouldn't be reached
|
|
||||||
std::cerr << "Whoops! Somehow failed to remove the index of the deleted sheet!" << std::endl;
|
|
||||||
}
|
|
||||||
} else if(choice == "del") {
|
|
||||||
all_pics.erase(all_pics.begin() + cur);
|
|
||||||
spec_scen_g.numSheets = which_pic;
|
|
||||||
spec_scen_g.sheets.resize(which_pic);
|
|
||||||
ResMgr::graphics.free("sheet" + std::to_string(which_pic));
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
all_pics.erase(all_pics.begin() + cur);
|
|
||||||
}
|
|
||||||
if(fs::exists(fpath)) fs::remove(fpath);
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
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);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if(cur > 0) cur--;
|
|
||||||
set_dlg_custom_sheet(me, all_pics[cur]);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if(all_pics.size() == 1) {
|
|
||||||
pic_dlg["left"].hide();
|
|
||||||
pic_dlg["right"].hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
int icon_start = 0;
|
|
||||||
int icon_end = 0;
|
|
||||||
int icon_min = 0;
|
|
||||||
int icon_max = 0;
|
|
||||||
|
|
||||||
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
|
|
||||||
if(icon_min != 0){
|
|
||||||
for(auto name : icon_buttons){
|
|
||||||
pic_dlg[name].show();
|
|
||||||
}
|
|
||||||
pic_dlg["diff-threshold"].show();
|
|
||||||
}else{
|
|
||||||
for(auto name : icon_buttons){
|
|
||||||
pic_dlg[name].hide();
|
|
||||||
}
|
|
||||||
pic_dlg["diff-threshold"].hide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
show_icon_selection();
|
show_icon_selection();
|
||||||
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;
|
|
||||||
if(dir == "left") {
|
|
||||||
if(cur == 0)
|
|
||||||
cur = all_pics.size() - 1;
|
|
||||||
else cur--;
|
|
||||||
} else if(dir == "right") {
|
|
||||||
cur++;
|
|
||||||
if(cur >= all_pics.size())
|
|
||||||
cur = 0;
|
|
||||||
} else return true;
|
|
||||||
if(cur != old_cur){
|
|
||||||
icon_start = icon_end = icon_min = icon_max = 0;
|
|
||||||
show_icon_selection();
|
|
||||||
}
|
|
||||||
|
|
||||||
set_dlg_custom_sheet(me, all_pics[cur]);
|
dlg["left"].attachClickHandler(std::bind(&cCustomGraphicsDialog::arrow_button_filter, this, "left"));
|
||||||
return true;
|
dlg["right"].attachClickHandler(std::bind(&cCustomGraphicsDialog::arrow_button_filter, this, "right"));
|
||||||
}, {"left", "right"});
|
|
||||||
|
|
||||||
set_dlg_custom_sheet(pic_dlg, all_pics[cur]);
|
set_dlg_custom_sheet(all_pics[cur]);
|
||||||
|
|
||||||
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 {
|
dlg["sheet"].attachClickHandler(std::bind(&cCustomGraphicsDialog::click_on_sheet, this));
|
||||||
location where_curs = sf::Mouse::getPosition(me.getWindow());
|
|
||||||
where_curs = me.getWindow().mapPixelToCoords(where_curs);
|
|
||||||
rectangle sheet_bounds = me["sheet"].getBounds();
|
|
||||||
where_curs.x -= sheet_bounds.left;
|
|
||||||
where_curs.y -= sheet_bounds.top;
|
|
||||||
where_curs.x /= 28;
|
|
||||||
where_curs.y /= 36;
|
|
||||||
|
|
||||||
int icon_hit = 1000 + 100 * all_pics[cur] + 10 * where_curs.y + where_curs.x;
|
dlg["icon-terr"].attachClickHandler(std::bind(&cCustomGraphicsDialog::import_ter_icon,this));
|
||||||
|
dlg["strip-terr"].attachClickHandler(std::bind(&cCustomGraphicsDialog::strip_ter_floor,this));
|
||||||
if(mod_contains(mod, mod_shift)){
|
dlg["add-terr"].attachClickHandler(std::bind(&cCustomGraphicsDialog::add_ter_floor,this));
|
||||||
icon_end = icon_hit;
|
|
||||||
}else{
|
|
||||||
icon_start = icon_end = icon_hit;
|
|
||||||
}
|
|
||||||
icon_min = min(icon_start, icon_end);
|
|
||||||
icon_max = max(icon_start, icon_end);
|
|
||||||
show_icon_selection();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
static sf::BlendMode blend_replace(sf::BlendMode::Factor::One, sf::BlendMode::Factor::Zero, sf::BlendMode::Equation::Add);
|
|
||||||
|
|
||||||
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];
|
|
||||||
if(item_hit == "icon-terr"){
|
|
||||||
set_up_canvas(all_pics[cur]);
|
|
||||||
pic_num_t icon = choose_graphic(0, PIC_TER, &me);
|
|
||||||
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};
|
|
||||||
cPict::drawAt(mainPtr(),canvas(),dest,icon+i,PIC_TER,false);
|
|
||||||
}
|
|
||||||
canvas().display();
|
|
||||||
sf::Image img = canvas().getTexture().copyToImage();
|
|
||||||
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();
|
|
||||||
if(color_diff(source, floor_source) > epsilon){
|
|
||||||
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::Image img = canvas().getTexture().copyToImage();
|
|
||||||
return replace_from_image("Strip Terrain Background", img);
|
|
||||||
}else if(item_hit == "add-terr"){
|
|
||||||
pic_num_t icon = choose_graphic(0, PIC_TER, &me);
|
|
||||||
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().draw(floor_s);
|
|
||||||
sf::Sprite ter_s(sheet_t, dest);
|
|
||||||
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;
|
|
||||||
},icon_buttons);
|
|
||||||
|
|
||||||
shut_down_menus(5); // So that cmd+O, cmd+N, cmd+S can work
|
shut_down_menus(5); // So that cmd+O, cmd+N, cmd+S can work
|
||||||
pic_dlg.run();
|
dlg.run();
|
||||||
|
|
||||||
// Commit undo actions for everything that was done
|
// Commit undo actions for everything that was done
|
||||||
if(pic_dlg.accepted()){
|
if(dlg.accepted()){
|
||||||
for(action_ptr action : deferred_actions){
|
for(action_ptr action : deferred_actions){
|
||||||
undo_list.add(action);
|
undo_list.add(action);
|
||||||
}
|
}
|
||||||
@@ -4234,11 +4280,14 @@ void edit_custom_sheets() {
|
|||||||
else shut_down_menus(3);
|
else shut_down_menus(3);
|
||||||
|
|
||||||
update_edit_menu();
|
update_edit_menu();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void edit_custom_sheets() {
|
||||||
|
cCustomGraphicsDialog().run();
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
||||||
|
|||||||
Reference in New Issue
Block a user