/* * graphtool.cpp * BoE * * Created by Celtic Minstrel on 16/04/09. * */ #define GRAPHTOOL_CPP #include "graphtool.h" #ifdef __APPLE__ #include #else #include #endif #include #include #include #include #include "restypes.hpp" #include "mathutil.h" using boost::math::constants::pi; RECT bg_rects[21]; RECT map_pat_rects[30]; RECT bw_rects[6]; tessel_ref_t bg[21]; tessel_ref_t map_pat[30]; tessel_ref_t bw_pat[6]; sf::Texture bg_gworld; bool use_win_graphics = false; sf::Shader maskShader; extern fs::path progDir; // TODO: Shouldn't need this extern sf::RenderWindow mainPtr; static void register_main_patterns(); void init_graph_tool(){ fs::path shaderPath = progDir/"data"/"shaders"; fs::path fragPath = shaderPath/"mask.frag", vertPath = shaderPath/"mask.vert"; std::ifstream fin; fin.open(fragPath.c_str()); if(!fin.good()) perror("Error loading fragment shader"); fin.seekg(0, std::ios::end); int size = fin.tellg(); fin.seekg(0); char* fbuf = new char[size + 1]; fbuf[size] = 0; fin.read(fbuf, size); fin.close(); fin.open(vertPath.c_str()); if(!fin.good()) perror("Error loading vertex shader"); fin.seekg(0, std::ios::end); size = fin.tellg(); fin.seekg(0); char* vbuf = new char[size + 1]; vbuf[size] = 0; fin.read(vbuf, size); if(!maskShader.loadFromMemory(vbuf, fbuf)) { fprintf(stderr,"Error: Failed to load shaders from %s\nVertex:\n%s\nFragment:\n%s",shaderPath.c_str(),vbuf,fbuf); } delete[] fbuf; delete[] vbuf; int i; // TODO: The duplication of location here shouldn't be necessary static const location pat_offs[17] = { location{0,3}, location{1,1}, location{2,1}, location{2,0}, location{3,0}, location{3,1}, location{1,3}, location{0,0}, location{0,2}, location{1,2}, location{0,1}, location{2,2}, location{2,3}, location{3,2}, location{1,0}, location{4,0}, location{3,3} }; static const int pat_i[17] = { 2, 3, 4, 5, 6, 8, 9, 10, 11,12,13,14,15,16,17,19,20 }; static const int map_i[26] = { 0, 1, 2, 3, 4, 5, 6, 9, 10,11,12,13,14,15,16,17, 18,19,20,21,22,24,25,27, 28,29 }; for(i = 0; i < 17; i++){ bg_rects[pat_i[i]] = {0,0,64,64}; bg_rects[pat_i[i]].offset(64 * pat_offs[i].x,64 * pat_offs[i].y); } RECT tmp_rect = bg_rects[19]; tmp_rect.offset(0, 64); bg_rects[0] = bg_rects[1] = bg_rects[18] = map_pat_rects[7] = tmp_rect; bg_rects[0].right -= 32; bg_rects[0].bottom -= 32; bg_rects[1].left -= 32; bg_rects[1].bottom -= 32; bg_rects[18].right -= 32; bg_rects[18].top -= 32; map_pat_rects[7].left -= 32; map_pat_rects[7].top -= 32; tmp_rect.offset(0, 64); map_pat_rects[8] = map_pat_rects[23] = map_pat_rects[26] = tmp_rect; map_pat_rects[8].right -= 32; map_pat_rects[8].bottom -= 32; map_pat_rects[23].left -= 32; map_pat_rects[23].right -= 16; map_pat_rects[23].bottom -= 32; map_pat_rects[26].left -= 32 + 16; map_pat_rects[26].bottom -= 32; tmp_rect.offset(0, 64); bg_rects[7] = tmp_rect; bg_rects[7].bottom = bg_rects[7].top + 16; tmp_rect.offset(0, -32); tmp_rect.right = tmp_rect.left + 8; tmp_rect.bottom = tmp_rect.top + 8; for(i = 0; i < 26; i++){ map_pat_rects[map_i[i]] = tmp_rect; map_pat_rects[map_i[i]].offset(8 * (i % 8),8 * (i / 8)); // Note: 8 * (i / 8) != i, despite appearances, due to integer rounding } tmp_rect = map_pat_rects[29]; for(i = 0; i < 6; i++) { tmp_rect.offset(8, 0); bw_rects[i] = tmp_rect; } bg_gworld.loadFromImage(*ResMgr::get("pixpats")); register_main_patterns(); } static void rect_draw_some_item(const sf::Texture& src_gworld,RECT src_rect,sf::RenderTarget& targ_gworld,RECT targ_rect,sf::RenderStates mode); void rect_draw_some_item(sf::RenderTarget& targ_gworld,RECT targ_rect) { fill_rect(targ_gworld, targ_rect, sf::Color::Black); } void rect_draw_some_item(const sf::Texture& src_gworld,RECT src_rect,sf::RenderTarget& targ_gworld,RECT targ_rect,sf::BlendMode mode){ rect_draw_some_item(src_gworld, src_rect, targ_gworld, targ_rect, sf::RenderStates(mode)); } void rect_draw_some_item(const sf::Texture& src_gworld,RECT src_rect,sf::RenderTarget& targ_gworld,RECT targ_rect,sf::RenderStates mode) { setActiveRenderTarget(targ_gworld); sf::Sprite tile(src_gworld, src_rect); tile.setPosition(targ_rect.left, targ_rect.top); double xScale = targ_rect.width(), yScale = targ_rect.height(); xScale /= src_rect.width(); yScale /= src_rect.height(); tile.setScale(xScale, yScale); targ_gworld.draw(tile, mode); } void rect_draw_some_item(const sf::Texture& src_gworld,RECT src_rect,RECT targ_rect,location offset, sf::BlendMode mode) { targ_rect.offset(offset); rect_draw_some_item(src_gworld,src_rect,mainPtr,targ_rect,mode); } void rect_draw_some_item(const sf::Texture& src_gworld,RECT src_rect,const sf::Texture& mask_gworld,RECT mask_rect,sf::RenderTarget& targ_gworld,RECT targ_rect) { sf::RenderTexture src; src.create(src_rect.width(), src_rect.height()); RECT dest_rect = src_rect; dest_rect.offset(-dest_rect.left,-dest_rect.top); rect_draw_some_item(src_gworld, src_rect, src, dest_rect); src.display(); sf::RenderTexture mask; mask.create(mask_rect.width(), mask_rect.height()); dest_rect = mask_rect; dest_rect.offset(-dest_rect.left,-dest_rect.top); rect_draw_some_item(mask_gworld, mask_rect, mask, dest_rect); mask.display(); maskShader.setParameter("texture", sf::Shader::CurrentTexture); maskShader.setParameter("mask", mask.getTexture()); rect_draw_some_item(src.getTexture(), dest_rect, targ_gworld, targ_rect, &maskShader); } void TextStyle::applyTo(sf::Text& text) { switch(font) { case FONT_PLAIN: text.setFont(*ResMgr::get("plain")); break; case FONT_BOLD: text.setFont(*ResMgr::get("bold")); break; case FONT_DUNGEON: text.setFont(*ResMgr::get("dungeon")); break; case FONT_MAIDWORD: text.setFont(*ResMgr::get("maidenword")); break; } text.setCharacterSize(pointSize); int style = sf::Text::Regular; if(italic) style |= sf::Text::Italic; if(underline) style |= sf::Text::Underlined; text.setStyle(style); text.setColor(colour); } struct text_params_t { TextStyle style; eTextMode mode; bool showBreaks = false; location offset = {0,0}; // Hilite ranges are, like the STL, of the form [first, last). std::vector hilite_ranges; sf::Color hilite_fg, hilite_bg = sf::Color::Transparent; enum {RECTS, SNIPPETS} returnType; std::vector returnRects; std::vector snippets; }; void win_draw_string(sf::RenderTarget& dest_window,RECT dest_rect,std::string str,text_params_t& options); void win_draw_string(sf::RenderTarget& dest_window,RECT dest_rect,std::string str,eTextMode mode,TextStyle style, location offset) { text_params_t params; params.mode = mode; params.style = style; params.offset = offset; win_draw_string(dest_window, dest_rect, str, params); } std::vector draw_string_hilite(sf::RenderTarget& dest_window,RECT dest_rect,std::string str,TextStyle style,std::vector hilites,sf::Color hiliteClr) { text_params_t params; params.mode = eTextMode::WRAP; params.hilite_ranges = hilites; params.style = style; params.hilite_fg = hiliteClr; params.returnType = text_params_t::RECTS; win_draw_string(dest_window, dest_rect, str, params); return params.returnRects; } std::vector draw_string_sel(sf::RenderTarget& dest_window,RECT dest_rect,std::string str,TextStyle style,std::vector hilites,sf::Color hiliteClr) { text_params_t params; params.mode = eTextMode::WRAP; params.showBreaks = true; params.hilite_ranges = hilites; params.style = style; params.hilite_fg = style.colour; params.hilite_bg = hiliteClr; params.returnType = text_params_t::RECTS; win_draw_string(dest_window, dest_rect, str, params); return params.snippets; } static void push_snippets(size_t start, size_t end, text_params_t& options, size_t& iHilite, const std::string& str, location loc) { std::vector& hilites = options.hilite_ranges; std::vector& snippets = options.snippets; // Check if we have any hilites on this line. // We assume the list of hilites is sorted, so we just need to // check the current entry. size_t upper_bound = end; do { bool hilited = false; // Skip any hilite rules that have start = end while(iHilite < hilites.size() && hilites[iHilite].first == hilites[iHilite].second) iHilite++; if(iHilite < hilites.size()) { // Possibilities: (vertical bars indicate line boundaries, parentheses for hilite boundaries, dots are data) // 1. |...|...(...) --> no hilite on this line // 2a. |...(...|...) --> hilite starts on this line and continues past it // 2b. |...(...)...| --> hilite starts and ends on this line // 3a. (...|...)...| --> hilite starts (or continues) at the start of the line and ends on this line // 3b. (...|......)| --> entire line hilited // Case 1 needs no special handling; check case 3, then case 2. if(hilites[iHilite].first <= start) { hilited = true; if(hilites[iHilite].second < end) // 3a end = hilites[iHilite].second; } else if(hilites[iHilite].first < end) end = hilites[iHilite].first; // 2b will reduce to 3a after shifting start over } size_t amount = end - start; snippets.push_back({str.substr(start,amount), loc, hilited}); loc.x += string_length(snippets[snippets.size()-1].text, options.style); start = end; end = upper_bound; if(iHilite < hilites.size() && start >= hilites[iHilite].second) iHilite++; } while(start < upper_bound); } void win_draw_string(sf::RenderTarget& dest_window,RECT dest_rect,std::string str,text_params_t& options) { if(str.empty()) return; // Nothing to do! short line_height = options.style.lineHeight; sf::Text str_to_draw; options.style.applyTo(str_to_draw); short str_len,i; unsigned short last_line_break = 0,last_word_break = 0; short total_width = 0; short adjust_x = 0,adjust_y = 0; adjust_x = options.offset.x; adjust_y = options.offset.y; str_to_draw.setString("fj"); // Something that has both an ascender and a descender adjust_y -= str_to_draw.getLocalBounds().height; str_to_draw.setString(str); str_len = str.length(); if (str_len == 0) { return; } eTextMode mode = options.mode; total_width = str_to_draw.getLocalBounds().width; if(mode == eTextMode::WRAP && total_width < dest_rect.width()) mode = eTextMode::LEFT_TOP; if(mode == eTextMode::LEFT_TOP && str.find('|') != std::string::npos) mode = eTextMode::WRAP; auto text_len = [&str_to_draw](size_t i) -> int { return str_to_draw.findCharacterPos(i).x; }; // Special stuff size_t iHilite = 0; location moveTo; line_height -= 2; // TODO: ...why are we arbitrarily reducing the line height from the requested value? if(mode == eTextMode::WRAP) { moveTo = location(dest_rect.left + 1 + adjust_x, dest_rect.top + 1 + adjust_y + 9); for(i = 0; text_len(i) != text_len(i + 1) && i < str_len;i++) { if(((text_len(i) - text_len(last_line_break) > (dest_rect.width() - 6)) && (last_word_break > last_line_break)) || (str[i] == '|')) { if(str[i] == '|') { if(!options.showBreaks) str[i] = ' '; last_word_break = i + 1; } push_snippets(last_line_break, last_word_break, options, iHilite, str, moveTo); moveTo.y += line_height; last_line_break = last_word_break; } if(str[i] == ' ') last_word_break = i + 1; } if(i - last_line_break > 0) { std::string snippet = str.substr(last_line_break); if(!snippet.empty()) push_snippets(last_line_break, str.length() + 1, options, iHilite, str, moveTo); } } else { switch(mode) { case eTextMode::CENTRE: moveTo = location((dest_rect.right + dest_rect.left) / 2 - (4 * total_width) / 9 + adjust_x, (dest_rect.bottom + dest_rect.top - line_height) / 2 + 9 + adjust_y); break; case eTextMode::LEFT_TOP: moveTo = location(dest_rect.left + 1 + adjust_x, dest_rect.top + 1 + adjust_y + 9); break; case eTextMode::LEFT_BOTTOM: moveTo = location(dest_rect.left + 1 + adjust_x, dest_rect.top + 1 + adjust_y + 9 + dest_rect.height() / 6); break; case eTextMode::WRAP: break; // Never happens, but put this here to silence warning } push_snippets(0, str.length() + 1, options, iHilite, str, moveTo); } for(auto& snippet : options.snippets) { str_to_draw.setString(snippet.text); str_to_draw.setPosition(snippet.at); if(snippet.hilited) { RECT bounds = str_to_draw.getGlobalBounds(); // Adjust so that drawing the same text to // the same rect is positioned exactly right bounds.left = snippet.at.x - 1; bounds.top = snippet.at.y + 5; if(options.returnType == text_params_t::RECTS) options.returnRects.push_back(bounds); str_to_draw.setColor(options.hilite_fg); bounds.inset(0,-4); fill_rect(dest_window, bounds, options.hilite_bg); } else str_to_draw.setColor(options.style.colour); dest_window.draw(str_to_draw); } } short string_length(std::string str, TextStyle style){ short total_width = 0; sf::Text text; style.applyTo(text); text.setString(str); total_width = text.getLocalBounds().width; return total_width; } /* void draw_terrain(){ short q,r,x,y,i,small_i; location which_pt,where_draw; RECT draw_rect,clipping_rect = {8,8,332,260}; unsigned char t_to_draw; RECT source_rect,tiny_to,tiny_to_base = {37,29,44,36},tiny_from,from_rect,to_rect; RECT boat_rect[4] = {{0,0,36,28}, {0,28,36,56},{0,56,36,84},{0,84,36,112}}; if (overall_mode >= 60) return; OffsetRect(&boat_rect[0],61,0); if (cur_viewing_mode == 0) { SetPort( ter_draw_gworld); FillCRect(&terrain_rect,bg_rects[6]); FrameRect(&terrain_rect); SetPortWindowPort(mainPtr); for (q = 0; q < 9; q++) for (r = 0; r < 9; r++) { where_draw.x = q; where_draw.y = r; if (editing_town == true) { t_to_draw = t_d.terrain[cen_x + q - 4][cen_y + r - 4]; } else { if (cen_x + q - 4 == -1) t_to_draw = borders[3][cen_y + r - 4]; else if (cen_x + q - 4 == 48) t_to_draw = borders[1][cen_y + r - 4]; else if (cen_y + r - 4 == -1) t_to_draw = borders[0][cen_x + q - 4]; else if (cen_y + r - 4 == 48) t_to_draw = borders[2][cen_x + q - 4]; else t_to_draw = current_terrain.terrain[cen_x + q - 4][cen_y + r - 4]; } draw_one_terrain_spot(q,r,t_to_draw); which_pt.x = cen_x + q - 4; which_pt.y =cen_y + r - 4; tiny_to = tiny_to_base; OffsetRect(&tiny_to,28 * q, 36 * r); // draw start icon, if starting point if ((editing_town == true) && (cur_town == scenario.which_town_start) && (scenario.where_start.x == cen_x + q - 4) && (scenario.where_start.y == cen_y + r - 4)) { from_rect = start_button_from; to_rect = tiny_to; to_rect.left -= 14; rect_draw_some_item(editor_mixed,from_rect,ter_draw_gworld,to_rect,0,0); OffsetRect(&tiny_to,0,-7); } if ((editing_town == false) && (scenario.out_sec_start.x == cur_out.x) && (scenario.out_sec_start.y == cur_out.y) && (scenario.out_start.x == cen_x + q - 4) && (scenario.out_start.y == cen_y + r - 4)) { from_rect = start_button_from; to_rect = tiny_to; to_rect.left -= 14; rect_draw_some_item(editor_mixed,from_rect,ter_draw_gworld,to_rect,0,0); OffsetRect(&tiny_to,0,-7); } small_i = small_icons[scenario.ter_types[t_to_draw].special]; if ((small_i == 30) && (scenario.ter_types[t_to_draw].flag2 >= 5)) small_i = 31; if ((small_i == 31) && (scenario.ter_types[t_to_draw].flag2 == 10)) small_i = 32; tiny_from = base_small_button_from; OffsetRect(&tiny_from,7 * (small_i % 10),7 * (small_i / 10)); if (small_i > 0) { rect_draw_some_item(editor_mixed,tiny_from,ter_draw_gworld,tiny_to,0,0); OffsetRect(&tiny_to,0,-7); } if (is_special(cen_x + q - 4,cen_y + r - 4) == true) { tiny_from = base_small_button_from; OffsetRect(&tiny_from,7 * (7),7 * (0)); rect_draw_some_item(editor_mixed,tiny_from,ter_draw_gworld,tiny_to,0,0); OffsetRect(&tiny_to,0,-7); } if ((t_to_draw == 7) || (t_to_draw == 10) || (t_to_draw == 13) || (t_to_draw == 16)) { tiny_from = base_small_button_from; OffsetRect(&tiny_from,7 * (3),7 * (2)); rect_draw_some_item(editor_mixed,tiny_from,ter_draw_gworld,tiny_to,0,0); OffsetRect(&tiny_to,0,-7); } //if (is_s_d(cen_x + q - 4,cen_y + r - 4) == true) { // } if (editing_town == false) { for (i = 0; i < 4; i++) if ((cen_x + q - 4 == current_terrain.wandering_locs[i].x) && (cen_y + r - 4 == current_terrain.wandering_locs[i].y)) { tiny_from = base_small_button_from; OffsetRect(&tiny_from,7 * (2),7 * (1)); rect_draw_some_item(editor_mixed,tiny_from,ter_draw_gworld,tiny_to,0,0); OffsetRect(&tiny_to,0,-7); i = 4; } } if (editing_town == true) { for (i = 0; i < 30; i++) { if ((scenario.scen_boats[i].which_town == cur_town) && (scenario.scen_boats[i].boat_loc.x == cen_x + q - 4) && (scenario.scen_boats[i].boat_loc.y == cen_y + r - 4)) Draw_Some_Item(mixed_gworld,boat_rect[0],ter_draw_gworld,where_draw,1,0); } for (i = 0; i < 30; i++) { source_rect = boat_rect[0]; OffsetRect(&source_rect,0,74); OffsetRect(&source_rect,56,36); if ((scenario.scen_horses[i].which_town == cur_town) && (scenario.scen_horses[i].horse_loc.x == cen_x + q - 4) && (scenario.scen_horses[i].horse_loc.y == cen_y + r - 4)) Draw_Some_Item(mixed_gworld,source_rect,ter_draw_gworld,where_draw,1,0); } for (i = 0; i < 4; i++) if ((cen_x + q - 4 == town.start_locs[i].x) && (cen_y + r - 4 == town.start_locs[i].y)) { tiny_from = base_small_button_from; OffsetRect(&tiny_from,7 * (6 + i),7 * (1)); rect_draw_some_item(editor_mixed,tiny_from,ter_draw_gworld,tiny_to,0,0); OffsetRect(&tiny_to,0,-7); } for (i = 0; i < 4; i++) if ((cen_x + q - 4 == town.wandering_locs[i].x) && (cen_y + r - 4 == town.wandering_locs[i].y)) { tiny_from = base_small_button_from; OffsetRect(&tiny_from,7 * (2),7 * (1)); rect_draw_some_item(editor_mixed,tiny_from,ter_draw_gworld,tiny_to,0,0); OffsetRect(&tiny_to,0,-7); i = 4; } if (is_web(cen_x + q - 4,cen_y + r - 4) == true) { from_rect = calc_rect(5,0); Draw_Some_Item(field_gworld,from_rect,ter_draw_gworld,where_draw,1,0); } if (is_crate(cen_x + q - 4,cen_y + r - 4) == true) { from_rect = calc_rect(6,0); Draw_Some_Item(field_gworld,from_rect,ter_draw_gworld,where_draw,1,0); } if (is_barrel(cen_x + q - 4,cen_y + r - 4) == true) { from_rect = calc_rect(7,0); Draw_Some_Item(field_gworld,from_rect,ter_draw_gworld,where_draw,1,0); } if (is_fire_barrier(cen_x + q - 4,cen_y + r - 4) == true) { from_rect = calc_rect(0,2); Draw_Some_Item(field_gworld,from_rect,ter_draw_gworld,where_draw,1,0); } if (is_quickfire(cen_x + q - 4,cen_y + r - 4) == true) { from_rect = calc_rect(7,1); Draw_Some_Item(field_gworld,from_rect,ter_draw_gworld,where_draw,1,0); } if (is_force_barrier(cen_x + q - 4,cen_y + r - 4) == true) { from_rect = calc_rect(2,2); Draw_Some_Item(field_gworld,from_rect,ter_draw_gworld,where_draw,1,0); } for (i = 0; i < 8; i++) if (is_sfx(cen_x + q - 4,cen_y + r - 4,i)) { from_rect = calc_rect(i,3); Draw_Some_Item(field_gworld,from_rect,ter_draw_gworld,where_draw,1,0); } for (x = 0; x < 64; x++) if ((cen_x + q - 4 == town.preset_items[x].item_loc.x) && (cen_y + r - 4 == town.preset_items[x].item_loc.y) && (town.preset_items[x].item_code >= 0)) { } for (x = 0; x < 60; x++) if ((cen_x + q - 4 == t_d.creatures[x].start_loc.x) && (cen_y + r - 4 == t_d.creatures[x].start_loc.y) && (t_d.creatures[x].number != 0)) { } } } if (editing_town == true) { draw_monsts(); draw_items(); } SetPort( ter_draw_gworld); ClipRect(&clipping_rect); if (editing_town == true) { // draw info rects for (i = 0; i < 16; i++) if (t_d.room_rect[i].left > 0) { draw_rect.left = 22 + 28 * (t_d.room_rect[i].left - cen_x + 4); draw_rect.right = 22 + 28 * (t_d.room_rect[i].right - cen_x + 4); draw_rect.top = 24 + 36 * (t_d.room_rect[i].top - cen_y + 4); draw_rect.bottom = 24 + 36 * (t_d.room_rect[i].bottom - cen_y + 4); ForeColor(redColor); FrameRect(&draw_rect); ForeColor(blackColor); } // draw border rect draw_rect.left = 21 + 28 * (town.in_town_rect.left - cen_x + 4); draw_rect.right = 21 + 28 * (town.in_town_rect.right - cen_x + 4); draw_rect.top = 25 + 36 * (town.in_town_rect.top - cen_y + 4); draw_rect.bottom = 25 + 36 * (town.in_town_rect.bottom - cen_y + 4); ForeColor(whiteColor); FrameRect(&draw_rect); ForeColor(blackColor); } if (editing_town == false) { // draw info rects for (i = 0; i < 8; i++) if (current_terrain.info_rect[i].left > 0) { draw_rect.left = 22 + 28 * (current_terrain.info_rect[i].left - cen_x + 4); draw_rect.right = 22 + 28 * (current_terrain.info_rect[i].right - cen_x + 4); draw_rect.top = 24 + 36 * (current_terrain.info_rect[i].top - cen_y + 4); draw_rect.bottom = 24 + 36 * (current_terrain.info_rect[i].bottom - cen_y + 4); ForeColor(redColor); FrameRect(&draw_rect); ForeColor(blackColor); } } ClipRect(&terrain_rect); SetPortWindowPort(mainPtr); small_any_drawn = false; //if (cur_viewing_mode == 0) // draw_frames(); } if (cur_viewing_mode == 1) { SetPort( ter_draw_gworld); if (small_any_drawn == false) { FillCRect(&terrain_rect,bg_rects[6]); FrameRect(&terrain_rect); } for (q = 0; q < ((editing_town == true) ? max_dim[town_type] : 48); q++) for (r = 0; r < ((editing_town == true) ? max_dim[town_type] : 48); r++) { t_to_draw = (editing_town == true) ? t_d.terrain[q][r] : current_terrain.terrain[q][r]; if ((small_what_drawn[q][r] != t_to_draw) || (small_any_drawn == false)) { draw_one_tiny_terrain_spot(q,r,t_to_draw); small_what_drawn[q][r] = t_to_draw; } } SetPortWindowPort(mainPtr); small_any_drawn = true; } //draw_cur_string(); place_location(); //to_rect = world_screen; //OffsetRect(&to_rect,TER_RECT_UL_X,TER_RECT_UL_Y); rect_draw_some_item(ter_draw_gworld,terrain_rect,ter_draw_gworld,world_screen,0,1); }*/ RECT calc_rect(short i, short j){ RECT base_rect = {0,0,36,28}; base_rect.offset(i * 28, j * 36); return base_rect; } graf_pos cCustomGraphics::find_graphic(pic_num_t which_rect, bool party) { short sheet = which_rect / 100; if(is_old) sheet = 0; else which_rect %= 100; RECT store_rect = {0,0,36,28}; store_rect.offset(28 * (which_rect % 10),36 * (which_rect / 10)); return std::make_pair(party ? this->party : &sheets[sheet],store_rect); } size_t cCustomGraphics::count() { if(sheets == NULL) return 0; else if(is_old) { RECT bounds(sheets[0]); if(bounds.width() < 280) return bounds.width() / 28; return bounds.height() / 36; } else { size_t count = 100 * (numSheets - 1); RECT bounds(sheets[numSheets - 1]); if(bounds.width() < 280) count += bounds.width() / 28; else count += bounds.height() / 36; return count; } } // TODO: This doesn't belong in this file std::string get_str(std::string list, short j){ if(j == 0) return list; StringRsrc& strings = *ResMgr::get(list); return strings[j - 1]; } m_pic_index_t m_pic_index[] = { {1, 1, 1}, {2, 1, 1}, {3, 1, 1}, {4, 1, 1}, {5, 1, 1}, {6, 1, 1}, {7, 1, 1}, {8, 1, 1}, {9, 1, 1}, {10, 1, 1}, //10 {11, 1, 1}, {12, 1, 1}, {13, 1, 1}, {14, 1, 1}, {15, 1, 1}, {16, 1, 1}, {17, 1, 1}, {18, 1, 1}, {19, 1, 1}, {20, 1, 1}, //20 {21, 1, 1}, {22, 1, 1}, {23, 1, 1}, {24, 1, 1}, {25, 1, 1}, {26, 1, 1}, {27, 1, 1}, {28, 1, 1}, {29, 1, 1}, {30, 1, 1}, //30 {31, 1, 1}, {32, 1, 1}, {33, 1, 1}, {34, 1, 1}, {35, 1, 1}, {36, 1, 1}, {37, 1, 1}, {38, 1, 1}, {39, 1, 1}, {40, 1, 1}, //40 {41, 1, 1}, {42, 1, 1}, {43, 1, 1}, {44, 1, 1}, {46, 1, 1}, {47, 1, 1}, {48, 1, 1}, {49, 1, 1}, {50, 1, 1}, {51, 1, 2}, //50 {53, 1, 2}, {55, 1, 2}, {57, 1, 2}, {59, 1, 1}, {60, 1, 1}, {61, 1, 1}, {62, 1, 1}, {63, 1, 1}, {64, 1, 1}, {65, 1, 1}, //60 {66, 1, 1}, {67, 1, 1}, {68, 1, 1}, {69, 1, 1}, {70, 1, 1}, {71, 1, 1}, {72, 1, 1}, {73, 1, 1}, {74, 1, 1}, {75, 1, 1}, //70 {76, 1, 1}, {77, 1, 1}, {78, 1, 1}, {79, 2, 1}, {81, 1, 1}, {82, 1, 1}, {83, 1, 2}, {85, 1, 1}, {86, 1, 1}, {87, 1, 1}, //80 {88, 1, 1}, {89, 1, 1}, {90, 1, 1}, {91, 1, 1}, {92, 1, 1}, {93, 1, 1}, {94, 1, 1}, {95, 1, 1}, {96, 1, 1}, {97, 1, 1}, //90 {98, 1, 1}, {99, 1, 1}, {100, 1, 1}, {101, 1, 1}, {102, 1, 1}, {103, 1, 1}, {104, 1, 1}, {105, 1, 1}, {106, 1, 1}, {107, 1, 1}, //100 {108, 1, 1}, {109, 2, 1}, {111, 1, 1}, {112, 1, 1}, {113, 1, 1}, {114, 2, 1}, {116, 1, 1}, {117, 1, 1}, {118, 1, 1}, {119, 1, 1}, //110 {120, 2, 1}, {122, 1, 1}, {123, 1, 2}, {125, 1, 2}, {127, 1, 1}, {128, 1, 1}, {129, 1, 1}, {130, 1, 1}, {131, 2, 2}, {135, 1, 1}, //120 {136, 1, 1}, {137, 2, 1}, {139, 1, 1}, {140, 1, 1}, {141, 1, 1}, {142, 1, 1}, {143, 1, 1}, {144, 1, 1}, {145, 1, 1}, {146, 1, 1}, //130 {147, 1, 1}, {148, 1, 1}, {149, 1, 1}, {150, 1, 1}, {151, 1, 1}, {152, 1, 1}, {153, 1, 1}, {154, 1, 1}, {155, 2, 2}, {159, 1, 1}, //140 {160, 2, 2}, {164, 2, 1}, {166, 2, 1}, {168, 1, 2}, {170, 1, 1}, {171, 1, 1}, {172, 1, 1}, {173, 1, 1}, {174, 1, 1}, {175, 1, 1}, //150 {176, 1, 1}, {177, 1, 1}, {178, 1, 1}, {179, 1, 1}, {180, 1, 1}, {181, 1, 1}, {182, 1, 1}, {183, 1, 1}, {184, 1, 1}, {185, 1, 1}, //160 {186, 1, 1}, {187, 1, 1}, {188, 1, 1}, {189, 1, 1}, {190, 1, 1}, {191, 1, 1}, {192, 1, 1}, {193, 1, 1}, {194, 1, 1}, {195, 1, 1}, //170 {196, 1, 1}, {197, 1, 1}, {198, 1, 1}, {0, 1, 1}, {45, 1, 1}, {199, 1, 1}, {200, 1, 1}, {201, 1, 1}, {202, 1, 1}, {203, 1, 1}, //180 {204, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, //190 {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, //200 }; // TODO: Put these classes in a header? class Ellipse : public sf::Shape { float divSz; int points; float a, b; public: explicit Ellipse(sf::Vector2f size, unsigned int points = 30) : points(points) { a = size.x / 2.0f; b = size.y / 2.0f; divSz = 2 * pi() / points; update(); } unsigned int getPointCount() const override { return points; } sf::Vector2f getPoint(unsigned int i) const override { float t = i * divSz; return sf::Vector2f(a + a*sin(t), b + b*cos(t)); } // TODO: Additional functions? }; class RoundRect : public sf::Shape { float divSz; int points; float w,h,r; public: RoundRect(sf::Vector2f size, float cornerRadius, unsigned int points = 32) : points(points / 4) { w = size.x; h = size.y; r = cornerRadius; divSz = 2 * pi() / points; update(); } unsigned int getPointCount() const override { return points * 4; } sf::Vector2f getPoint(unsigned int i) const override { const float pi = ::pi(); const float half_pi = 0.5 * pi; float t = i * divSz; switch(i / points) { case 0: // top left corner return {r + r*sin(t + pi), r + r*cos(t + pi)}; case 1: // bottom left corner return {r + r*cos(t + half_pi), h - r + r*sin(t - half_pi)}; case 2: // bottom right corner return {w - r + r*cos(t + half_pi), h - r - r*sin(t + half_pi)}; case 3: // top right corner return {w - r - r*cos(t - half_pi), r + r*sin(t - half_pi)}; } // Unreachable printf("Whoops, rounded rectangle had bad point!"); return {0,0}; } // TODO: Additional functions? }; void draw_line(sf::RenderTarget& target, location from, location to, int thickness, sf::Color colour, sf::BlendMode mode) { sf::VertexArray line(sf::LinesStrip, 2); line[0].position = from; line[0].color = colour; line[1].position = to; line[1].color = colour; setActiveRenderTarget(target); float saveThickness; glGetFloatv(GL_LINE_WIDTH, &saveThickness); glLineWidth(thickness); target.draw(line, mode); glLineWidth(saveThickness); } static void fill_shape(sf::RenderTarget& target, sf::Shape& shape, int x, int y, sf::Color colour) { shape.setPosition(x, y); shape.setFillColor(colour); setActiveRenderTarget(target); target.draw(shape); } static void frame_shape(sf::RenderTarget& target, sf::Shape& shape, int x, int y, sf::Color colour) { shape.setPosition(x, y); shape.setOutlineColor(colour); shape.setFillColor(sf::Color::Transparent); // TODO: Determine the correct outline value; should be one pixel, not sure if it should be negative shape.setOutlineThickness(1.0); target.draw(shape); } void fill_rect(sf::RenderTarget& target, RECT rect, sf::Color colour) { sf::RectangleShape fill(sf::Vector2f(rect.width(), rect.height())); fill_shape(target, fill, rect.left, rect.top, colour); } void frame_rect(sf::RenderTarget& target, RECT rect, sf::Color colour) { sf::RectangleShape frame(sf::Vector2f(rect.width(), rect.height())); frame_shape(target, frame, rect.left, rect.top, colour); } void fill_roundrect(sf::RenderTarget& target, RECT rect, int rad, sf::Color colour) { RoundRect fill(sf::Vector2f(rect.width(), rect.height()), rad); fill_shape(target, fill, rect.left, rect.top, colour); } void frame_roundrect(sf::RenderTarget& target, RECT rect, int rad, sf::Color colour) { RoundRect frame(sf::Vector2f(rect.width(), rect.height()), rad); frame_shape(target, frame, rect.left, rect.top, colour); } void fill_circle(sf::RenderTarget& target, RECT rect, sf::Color colour) { Ellipse fill(sf::Vector2f(rect.width(), rect.height())); fill_shape(target, fill, rect.left, rect.top, colour); } void frame_circle(sf::RenderTarget& target, RECT rect, sf::Color colour) { Ellipse frame(sf::Vector2f(rect.width(), rect.height())); frame_shape(target, frame, rect.left, rect.top, colour); } void fill_region(sf::RenderWindow& target, Region& region, sf::Color colour) { clip_region(target, region); fill_rect(target, RECT(target), colour); undo_clip(target); } void frame_region(sf::RenderWindow& target, Region& region, sf::Color colour) { // TODO: Uh, actually, this won't do what it says. Eh, I'll fix it if I ever use it. clip_region(target, region); frame_rect(target, RECT(target), colour); undo_clip(target); } void Region::addEllipse(RECT frame) { Ellipse* ellipse = new Ellipse(sf::Vector2f(frame.width(), frame.height())); ellipse->setFillColor(sf::Color::Black); shapes.push_back(std::shared_ptr(ellipse)); } void Region::addRect(RECT rect){ sf::RectangleShape* frame = new sf::RectangleShape(sf::Vector2f(rect.width(), rect.height())); frame->setPosition(rect.left, rect.top); frame->setFillColor(sf::Color::Black); shapes.push_back(std::shared_ptr(frame)); } void Region::clear() { shapes.clear(); } void Region::offset(int x, int y) { for(auto shape : shapes) { shape->move(x,y); } } void Region::offset(location off) { offset(off.x, off.y); } RECT Region::getEnclosingRect() { if(shapes.empty()) return RECT(); RECT bounds = shapes[0]->getGlobalBounds(); for(size_t i = 0; i < shapes.size(); i++) { RECT shapeRect = shapes[i]->getGlobalBounds(); if(shapeRect.top < bounds.top) bounds.top = shapeRect.top; if(shapeRect.left < bounds.left) bounds.top = shapeRect.top; if(shapeRect.bottom > bounds.bottom) bounds.top = shapeRect.top; if(shapeRect.right > bounds.right) bounds.top = shapeRect.top; } return bounds; } // We can only use stencil buffer in the main window // Could request it in dialogs, but currently don't // SFML does not appear to allow requesting it for render textures void Region::setStencil(sf::RenderWindow& where) { where.setActive(); glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); glEnable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS, 1, 1); for(auto shape : shapes) { // Save the colour in case we need to reuse this region sf::Color colour = shape->getFillColor(); if(colour == sf::Color::Black) glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); else glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); // Make transparent so we don't overwrite important stuff shape->setFillColor(sf::Color::Transparent); where.draw(*shape); shape->setFillColor(colour); } glStencilFunc(GL_EQUAL, 1, 1); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); } void clip_rect(sf::RenderTarget& where, RECT rect) { rect |= RECT(where); // Make sure we don't draw out of bounds // TODO: Make sure this works for the scissor test... setActiveRenderTarget(where); glEnable(GL_SCISSOR_TEST); glScissor(rect.left, RECT(where).height() - rect.bottom, rect.width(), rect.height()); } void clip_region(sf::RenderWindow& where, Region& region) { region.setStencil(where); } void undo_clip(sf::RenderTarget& where) { setActiveRenderTarget(where); glDisable(GL_SCISSOR_TEST); glDisable(GL_STENCIL_TEST); } void setActiveRenderTarget(sf::RenderTarget& where) { const std::type_info& type = typeid(where); if(type == typeid(sf::RenderWindow&)) dynamic_cast(where).setActive(); else if(type == typeid(sf::RenderTexture&)) dynamic_cast(where).setActive(); else throw std::bad_cast(); } Region& Region::operator-=(Region& other) { for(auto shape : other.shapes) { // TODO: Shouldn't this be done to a copy of the shape instead? if(shape->getFillColor() == sf::Color::Black) shape->setFillColor(sf::Color::White); else shape->setFillColor(sf::Color::Black); shapes.push_back(shape); } return *this; } struct tessel_t { sf::RenderTexture* tessel; sf::Texture* img; RECT srcRect; }; bool operator==(const tessel_ref_t& a, const tessel_ref_t& b) { return a.key == b.key; } template<> struct std::hash { size_t operator()(tessel_ref_t key) const { return key.key; } }; std::unordered_map tiling_reservoir; static int tessel_index = 0; tessel_ref_t prepareForTiling(sf::Texture& srcImg, RECT srcRect) { tessel_ref_t ref = {tessel_index++}; tiling_reservoir[ref].img = &srcImg; tiling_reservoir[ref].srcRect = srcRect; tiling_reservoir[ref].tessel = new sf::RenderTexture; tiling_reservoir[ref].tessel->create(srcRect.width(), srcRect.height()); RECT tesselRect(*tiling_reservoir[ref].tessel); rect_draw_some_item(srcImg, srcRect, *tiling_reservoir[ref].tessel, tesselRect); tiling_reservoir[ref].tessel->display(); tiling_reservoir[ref].tessel->setRepeated(true); return ref; } void flushTessels(sf::Texture& alteredImg) { erase_if(tiling_reservoir, [&alteredImg](std::pair& kv) -> bool { if(kv.second.img == &alteredImg) { delete kv.second.tessel; return true; } return false; }); if(&alteredImg == &bg_gworld) register_main_patterns(); } static void register_main_patterns() { for(int i = 0; i < 30; i++) { if(i < 21) { if(i < 6) { bw_pat[i] = prepareForTiling(bg_gworld, bw_rects[i]); } bg[i] = prepareForTiling(bg_gworld, bg_rects[i]); } map_pat[i] = prepareForTiling(bg_gworld, map_pat_rects[i]); } } void tileImage(sf::RenderTarget& target, RECT area, tessel_ref_t tessel, sf::BlendMode mode) { // First, set up a dictionary of all textures ever tiled. // The key type is a pair. // The value type is a Texture. tessel_t& tesselInfo = tiling_reservoir[tessel]; RECT clipArea = area; area.left -= area.left % tesselInfo.srcRect.width(); area.top -= area.top % tesselInfo.srcRect.height(); area |= RECT(target); // Make sure we don't draw out of bounds sf::RectangleShape tesselShape(sf::Vector2f(area.width(),area.height())); tesselShape.setTexture(&tesselInfo.tessel->getTexture()); tesselShape.setTextureRect(area); tesselShape.setPosition(area.left, area.top); sf::RenderStates renderMode(mode); setActiveRenderTarget(target); clip_rect(target, clipArea); target.draw(tesselShape, renderMode); undo_clip(target); } short can_see(location p1,location p2,std::function get_obscurity) { short storage = 0; if(p1.y == p2.y) { if(p1.x > p2.x) { for(short count = p2.x + 1; count < p1.x; count++) storage += get_obscurity(count, p1.y); } else { for(short count = p1.x + 1; count < p2.x; count++) storage += get_obscurity(count, p1.y); } } else if(p1.x == p2.x) { if(p1.y > p2.y) { for(short count = p1.y - 1; count > p2.y; count--) storage += get_obscurity(p1.x, count); } else { for(short count = p1.y + 1; count < p2.y; count++) storage += get_obscurity(p1.x, count); } } else { short dx = p2.x - p1.x; short dy = p2.y - p1.y; if(abs(dy) > abs(dx)) { if(p2.y > p1.y) { for(short count = 1; count < dy; count++) storage += get_obscurity(p1.x + (count * dx) / dy, p1.y + count); } else { for(short count = -1; count > dy; count--) storage += get_obscurity(p1.x + (count * dx) / dy, p1.y + count); } } if(abs(dy) <= abs(dx)) { if(p2.x > p1.x) { for(short count = 1; count < dx; count++) storage += get_obscurity(p1.x + count, p1.y + (count * dy) / dx); } else { for(short count = -1; count > dx; count--) storage += get_obscurity(p1.x + count, p1.y + (count * dy) / dx); } } } return storage; }