From a51ab021f4d88d17761ff4a9650b9d118b5bb171 Mon Sep 17 00:00:00 2001 From: Celtic Minstrel Date: Wed, 3 Jun 2015 19:50:00 -0400 Subject: [PATCH] Add two new special nodes for cutscenes - Node to change a monster's location (also works on party members) - Node to temporarily place text on the map - Fix the "lift fog" node - Remove the optimization of only redrawing a terrain space if it has changed --- doc/editor/appendix/Specials.html | 37 +++++++++++++++- rsrc/strings/help.txt | 4 +- rsrc/strings/specials-opcodes.txt | 4 +- rsrc/strings/specials-text-town.txt | 32 +++++++++++++ src/boe.actions.cpp | 3 ++ src/boe.graphics.cpp | 24 +++++----- src/boe.graphutil.cpp | 23 ++-------- src/boe.locutils.cpp | 3 +- src/boe.newgraph.cpp | 12 ----- src/boe.specials.cpp | 69 +++++++++++++++++++++++++++++ src/boe.text.cpp | 48 +++++++++++++++++++- src/boe.text.h | 7 +++ src/classes/simpletypes.h | 4 +- src/classes/special.cpp | 23 +++++----- src/scenedit/scen.keydlgs.cpp | 4 ++ src/scenedit/scen.keydlgs.h | 2 +- 16 files changed, 233 insertions(+), 66 deletions(-) diff --git a/doc/editor/appendix/Specials.html b/doc/editor/appendix/Specials.html index 1f28057a..b92fa31d 100644 --- a/doc/editor/appendix/Specials.html +++ b/doc/editor/appendix/Specials.html @@ -1616,7 +1616,9 @@ reset to centre on the active character. of sight become fully visible. The fog is automatically restored at the end of the current turn (after monsters make their move).
-
Extra 1a:
If 1, lift the fog. If 0, restore it.
+
Extra 1a:
If 1, lift the fog. If 0, restore it.
+
Note:
If needed, you can also use this to force the screen to be redrawn (just +specify the current fog setting).
Type 200: Initiate Targeting Mode
This advanced node hooks into the native Blades of Exile targeting engine to allow the player to select one or more targets. The @@ -1681,6 +1683,39 @@ calculate the damage.
similar to what happens when fields are placed and creatures take damage as a result. If 1, all damage will be animated simultaneously using fully-animated explosions, similar to what happens when casting spells such as Fireball or Divine Thud. + +
Type 203: Relocate Creature
This node forces a single creature to relocate to +a different space. Useful for cutscenes. It can handle either absolute or relative +movement. +However, it does not currently check that it is legal for the creature to be on that +space. +This can split the party into its individual members temporarily, similar to combat mode, +but they return to the party's original location once the node sequence ends. +
+
Extra 1a, Extra 1b:
The x and y coordinates or differentials.
+
Extra 2a:
The creature to relocate - 0-5 for party members, 100+x for a +specific monster, -1 for the currently selected target.
+
Extra 2b:
If 0, Extra 1a and 1b are taken as absolute coordinates. If 1, they +are added to the x and y coordinates, respectively. 2 or 3 means the x coordinate is +instead subtracted, while 3 or 4 means the y coordinate is instead subtracted.
+
Extra 2c:
If positive, this specifies a delay in milliseconds after the +creature +is repositioned.
+ +
Type 204:
This node places a text label on the terrain screen, for example to +indicate someone speaking. The label lasts only until the screen is next updated, but you +can optionally specify a delay to ensure there is time to read it. +
+
Mess 1:
The number of the message to display.
+
Extra 1a, Extra 1b:
The X and Y coordinates of the location to place the +message. If Extra 1b is left at -1, Extra 1a instead specifies the number of a creature at +whose location the message should be placed (0-5 for party members, 6 for party location, +100+x for other creatures). If both are left at -1, the current targeted PC from the +Select PC node is used.
+
Extra 2a:
If 1, the text is centred vertically on the space. If 0, it appears +at the top of the space.
+
Extra 2b:
If positive, this specifies a delay in seconds after the label is +placed.

Rectangle Specials:

diff --git a/rsrc/strings/help.txt b/rsrc/strings/help.txt index a91f2e42..894d650f 100644 --- a/rsrc/strings/help.txt +++ b/rsrc/strings/help.txt @@ -65,8 +65,8 @@ Your spell points are what you expend to cast spells. Each spell drains away som Some doors can't be opened no matter what you try. For example, portcullises can almost never be opened by picking locks or bashing. - - +A special node just triggered, and since you previously enabled it, you are now in step-through mode. Take a look at the transcript - it gives you the node that is about to execute, together with the type and number (useful if you need to locate it in the editor). +To execute that node, simply press any key (or click the mouse button). After the node has executed, if it calls another one, you will see it in the transcript. Keep pressing a key to step through the nodes. If you wish to exit step-through mode in the middle of a node sequence, simply press Escape. diff --git a/rsrc/strings/specials-opcodes.txt b/rsrc/strings/specials-opcodes.txt index eee68ae3..ae5c0721 100644 --- a/rsrc/strings/specials-opcodes.txt +++ b/rsrc/strings/specials-opcodes.txt @@ -200,8 +200,8 @@ lift-fog start-target spell-pat-field spell-pat-boom - - +relocate +label diff --git a/rsrc/strings/specials-text-town.txt b/rsrc/strings/specials-text-town.txt index 7d754eb6..02a38f65 100644 --- a/rsrc/strings/specials-text-town.txt +++ b/rsrc/strings/specials-text-town.txt @@ -526,3 +526,35 @@ How many dice? (calculated in d6's) 0 - sequential simple booms, 1 - simultaneous animated booms Special to Jump To -------------------- +Relocate Creature +Unused +Unused +First part of message +Second part of message +Unused +Unused +Unused +X destination or differential +Y destination or differential +Unused +Which creature +Positioning mode +Delay +Special to Jump To +-------------------- +Place Label +Unused +Unused +Label text +Unused +Unused +Unused +Unused +X coordinate, or which creature +Y coordinate, or -1 for creature +Unused +0 - Align top, 1 - align centre +Delay +Unused +Special to Jump To +-------------------- diff --git a/src/boe.actions.cpp b/src/boe.actions.cpp index cd263cac..952f10c4 100644 --- a/src/boe.actions.cpp +++ b/src/boe.actions.cpp @@ -61,6 +61,7 @@ short num_chirps_played = 0; extern rectangle startup_button[6]; extern bool flushingInput; extern bool fog_lifted; +extern bool cartoon_happening; bool ghost_mode; rectangle startup_top; @@ -1446,7 +1447,9 @@ bool handle_action(sf::Event event) { // MARK: Handle non-PC stuff (like monsters) if the party actually did something if(did_something) handle_monster_actions(need_redraw, need_reprint); if(fog_lifted) need_redraw = true; + if(cartoon_happening) need_redraw = true; fog_lifted = false; + cartoon_happening = false; if(need_redraw) draw_terrain(); if(need_reprint || need_redraw) print_buf(); diff --git a/src/boe.graphics.cpp b/src/boe.graphics.cpp index 9f2e853a..80077f6f 100644 --- a/src/boe.graphics.cpp +++ b/src/boe.graphics.cpp @@ -1,6 +1,7 @@ #include #include +#include #include "boe.global.h" @@ -35,6 +36,7 @@ extern eGameMode overall_mode; extern short current_spell_range; extern bool anim_onscreen,play_sounds,frills_on,startup_loaded,party_in_memory; extern bool flushingInput; +extern bool cartoon_happening, fog_lifted; extern short anim_step; extern effect_pat_type current_pat; extern location ul; @@ -67,12 +69,6 @@ extern std::string save_talk_str1, save_talk_str2; rectangle menuBarRect; Region originalGrayRgn, newGrayRgn, underBarRgn; -short terrain_there[9][9]; // this is an optimization variabel. Keeps track of what terrain -// is in the terrain spot, so things don't get redrawn. -// 0 - 299 just terrain graphic in place -// 300 - blackness -// -1 - nothign worth saving - long anim_ticks = 0; // 0 - terrain 1 - buttons 2 - pc stats @@ -214,9 +210,6 @@ void plop_fancy_startup() { // for(i = 0;i < 8; i++) // OffsetRect(&trim_rects[i],61,37); - for(i = 0; i < 9; i++) - for(j = 0; j < 9; j++) - terrain_there[i][j] = -1; win_to_rects[5].offset(TEXT_WIN_UL_X,TEXT_WIN_UL_Y); win_to_rects[2].offset(PC_WIN_UL_X,PC_WIN_UL_Y); @@ -764,6 +757,7 @@ bool is_nature(short x, short y, unsigned short ground_t) { } std::vector forcecage_locs; +extern std::list posted_labels; //mode ... if 1, don't place on screen after redoing // if 2, only redraw over active monst @@ -886,6 +880,8 @@ void draw_terrain(short mode) { } spot_seen[q][r] = can_draw; + if(fog_lifted) can_draw = true; + if((can_draw != 0) && (overall_mode != MODE_RESTING)) { // if can see, not a pit, and not resting if(is_combat()) anim_ticks = 0; @@ -987,6 +983,12 @@ void draw_terrain(short mode) { // TODO: Move into the above loop to eliminate global variable for(location fc_loc : forcecage_locs) Draw_Some_Item(fields_gworld,calc_rect(2,0),terrain_screen_gworld,fc_loc,1,0); + // Draw any posted labels, then clear them out + clip_rect(terrain_screen_gworld, {13, 13, 337, 265}); + for(text_label_t lbl : posted_labels) + draw_text_label(lbl); + undo_clip(terrain_screen_gworld); + posted_labels.clear(); // Now do the light mask thing apply_light_mask(false); @@ -1207,8 +1209,6 @@ void draw_trim(short q,short r,short which_trim,ter_num_t ground_ter) { if(!frills_on) return; - terrain_there[q][r] = -1; - unsigned short pic = univ.scenario.ter_types[ground_ter].picture; if(pic < 960){ from_gworld = &terrain_gworld[pic / 50]; @@ -1327,8 +1327,6 @@ void place_road(short q,short r,location where, bool here) { draw_loc.x = q; draw_loc.y = r; - terrain_there[q][r] = -1; - if(here){ if(where.y > 0) ter = coord_to_ter(where.x,where.y - 1); diff --git a/src/boe.graphutil.cpp b/src/boe.graphutil.cpp index b2711e01..f82b4330 100644 --- a/src/boe.graphutil.cpp +++ b/src/boe.graphutil.cpp @@ -23,6 +23,7 @@ extern sf::RenderWindow mainPtr; extern rectangle windRect; extern short stat_window; extern bool give_delays; +extern bool cartoon_happening; extern eGameMode overall_mode; extern short current_spell_range; extern bool anim_onscreen,play_sounds,frills_on,startup_loaded; @@ -34,7 +35,6 @@ extern short combat_posing_monster , current_working_monster ; // 0-5 PC 100 + x extern sf::RenderTexture terrain_screen_gworld; extern sf::Texture items_gworld,tiny_obj_gworld,pc_gworld,monst_gworld[NUM_MONST_SHEETS]; extern sf::Texture fields_gworld,anim_gworld,vehicle_gworld,terrain_gworld[NUM_TER_SHEETS]; -extern short terrain_there[9][9]; extern std::queue special_queue; extern location ul; @@ -77,20 +77,13 @@ void draw_one_terrain_spot (short i,short j,short terrain_to_draw) { where_draw = calc_rect(i,j); where_draw.offset(13,13); if(terrain_to_draw == -1) { - if(terrain_there[i][j] == 300) { - return; - } - terrain_there[i][j] = 300; fill_rect(terrain_screen_gworld, where_draw, sf::Color::Black); return; } if(terrain_to_draw >= 10000) { // force using a specific graphic terrain_to_draw -= 10000; - if(terrain_there[i][j] == terrain_to_draw) - return; source_gworld = &terrain_gworld[terrain_to_draw / 50]; - terrain_there[i][j] = terrain_to_draw; terrain_to_draw %= 50; source_rect = calc_rect(terrain_to_draw % 10, terrain_to_draw / 10); anim_type = -1; @@ -98,25 +91,19 @@ void draw_one_terrain_spot (short i,short j,short terrain_to_draw) { else if(univ.scenario.ter_types[terrain_to_draw].picture >= 2000) { // custom graf_pos_ref(source_gworld, source_rect) = spec_scen_g.find_graphic(univ.scenario.ter_types[terrain_to_draw].picture - 2000 + (anim_ticks % 4)); anim_type = 0; - terrain_there[i][j] = -1; } else if(univ.scenario.ter_types[terrain_to_draw].picture >= 1000) { // custom graf_pos_ref(source_gworld, source_rect) = spec_scen_g.find_graphic(univ.scenario.ter_types[terrain_to_draw].picture - 1000); - terrain_there[i][j] = -1; } else if(univ.scenario.ter_types[terrain_to_draw].picture >= 960) { // animated source_gworld = &anim_gworld; terrain_to_draw = univ.scenario.ter_types[terrain_to_draw].picture; source_rect = calc_rect(4 * ((terrain_to_draw - 960) / 5) + (anim_ticks % 4),(terrain_to_draw - 960) % 5); - terrain_there[i][j] = -1; anim_type = 0; } else { - if(terrain_there[i][j] == univ.scenario.ter_types[terrain_to_draw].picture) return; - terrain_there[i][j] = univ.scenario.ter_types[terrain_to_draw].picture; terrain_to_draw = univ.scenario.ter_types[terrain_to_draw].picture; source_gworld = &terrain_gworld[terrain_to_draw / 50]; - terrain_there[i][j] = terrain_to_draw; terrain_to_draw %= 50; source_rect = calc_rect(terrain_to_draw % 10, terrain_to_draw / 10); anim_type = -1; @@ -152,7 +139,6 @@ void draw_monsters() { (can_see_light(univ.party.p_loc, univ.party.out_c[i].m_loc,sight_obscurity) < 5)) { where_draw.x = univ.party.out_c[i].m_loc.x - univ.party.p_loc.x + 4; where_draw.y = univ.party.out_c[i].m_loc.y - univ.party.p_loc.y + 4; - terrain_there[where_draw.x][where_draw.y] = -1; for(j = 0; univ.party.out_c[i].what_monst.monst[j] == 0 && j < 7; j++); @@ -250,7 +236,7 @@ void draw_pcs(location center,short mode) { for(i = 0; i < 6; i++) if(univ.party[i].main_status == eMainStatus::ALIVE) if(point_onscreen(center, univ.party[i].combat_pos) && - (/*cartoon_happening ||*/ party_can_see(univ.party[i].combat_pos) < 6)){ + (cartoon_happening || party_can_see(univ.party[i].combat_pos) < 6)){ where_draw.x = univ.party[i].combat_pos.x - center.x + 4; where_draw.y = univ.party[i].combat_pos.y - center.y + 4; sf::Texture* from_gw; @@ -315,18 +301,15 @@ void draw_items(location where){ sf::Texture* src_gw; graf_pos_ref(src_gw, from_rect) = spec_scen_g.find_graphic(univ.town.items[i].graphic_num - 10000, true); to_rect = coord_to_rect(where_draw.x,where_draw.y); - terrain_there[where_draw.x][where_draw.y] = -1; rect_draw_some_item(*src_gw,from_rect,terrain_screen_gworld,to_rect,sf::BlendAlpha); }else if(univ.town.items[i].graphic_num >= 1000){ sf::Texture* src_gw; graf_pos_ref(src_gw, from_rect) = spec_scen_g.find_graphic(univ.town.items[i].graphic_num - 1000); to_rect = coord_to_rect(where_draw.x,where_draw.y); - terrain_there[where_draw.x][where_draw.y] = -1; rect_draw_some_item(*src_gw,from_rect,terrain_screen_gworld,to_rect,sf::BlendAlpha); }else{ from_rect = get_item_template_rect(univ.town.items[i].graphic_num); to_rect = coord_to_rect(where_draw.x,where_draw.y); - terrain_there[where_draw.x][where_draw.y] = -1; if(univ.town.items[i].graphic_num >= 55) { to_rect.inset(5,9); rect_draw_some_item(tiny_obj_gworld, from_rect, terrain_screen_gworld, to_rect,sf::BlendAlpha); @@ -458,7 +441,7 @@ void draw_party_symbol(location center) { return; if((is_town()) && (univ.town.p_loc.x > 70)) return; - if(overall_mode == MODE_LOOK_TOWN) { + if(overall_mode == MODE_LOOK_TOWN || cartoon_happening) { target.x += univ.town.p_loc.x - center.x; target.y += univ.town.p_loc.y - center.y; } diff --git a/src/boe.locutils.cpp b/src/boe.locutils.cpp index 38829d54..804532a2 100644 --- a/src/boe.locutils.cpp +++ b/src/boe.locutils.cpp @@ -16,6 +16,7 @@ extern eGameMode overall_mode; extern eGameMode store_pre_shop_mode, store_pre_talk_mode; extern location center; extern cUniverse univ; +extern bool cartoon_happening; location light_locs[40]; short num_lights = 0; @@ -56,7 +57,7 @@ bool is_out() { } bool is_town() { - if(((overall_mode > MODE_OUTDOORS) && (overall_mode < MODE_COMBAT)) || (overall_mode == MODE_LOOK_TOWN)) + if((overall_mode > MODE_OUTDOORS && overall_mode < MODE_COMBAT) || overall_mode == MODE_LOOK_TOWN || cartoon_happening) return true; else if(overall_mode == MODE_SHOPPING) { std::swap(overall_mode, store_pre_shop_mode); diff --git a/src/boe.newgraph.cpp b/src/boe.newgraph.cpp index 98e21050..809c738f 100644 --- a/src/boe.newgraph.cpp +++ b/src/boe.newgraph.cpp @@ -62,7 +62,6 @@ extern std::shared_ptr done_btn, help_btn; extern location center; extern location store_anim_ul; extern char light_area[13][13]; -extern short terrain_there[9][9]; extern char unexplored_area[13][13]; extern tessel_ref_t bw_pats[6]; extern short combat_posing_monster , current_working_monster ; // 0-5 PC 100 + x - monster x @@ -95,7 +94,6 @@ extern rectangle shop_frame ; extern rectangle shop_done_rect; extern char *heal_types[]; extern short heal_costs[8]; -extern short terrain_there[9][9]; extern short shop_array[30]; // Missile anim vars @@ -146,11 +144,6 @@ void apply_unseen_mask() { to_rect.offset(-28 + i * 28,-36 + 36 * j); to_rect |= big_to; tileImage(terrain_screen_gworld, to_rect, bw_pats[3], sf::BlendAlpha); - for(k = i - 2; k < i + 1; k++) - for(l = j - 2; l < j + 1; l++) - if((k >= 0) && (l >= 0) && (k < 9) && (l < 9) && ((k != i - 1) || (l != j - 1))) - terrain_there[k][l] = -1; - } } @@ -200,11 +193,6 @@ void apply_light_mask(bool onWindow) { light_area[i][j] = 3; } - for(i = 2; i < 11; i++) - for(j = 2; j < 11; j++) { - if(light_area[i][j] == 1) - terrain_there[i - 2][j - 2] = -1; - } for(i = 0; i < 13; i++) for(j = 0; j < 13; j++) if(last_light_mask[i][j] != light_area[i][j]) diff --git a/src/boe.specials.cpp b/src/boe.specials.cpp index 5c9793c4..5cc19a98 100644 --- a/src/boe.specials.cpp +++ b/src/boe.specials.cpp @@ -51,6 +51,7 @@ extern short combat_posing_monster; bool can_draw_pcs = true; bool fog_lifted = false; +bool cartoon_happening = false; std::map boom_gr = { {eDamageType::WEAPON, 3}, @@ -70,6 +71,14 @@ iLiving* current_pc_picked_in_spec_enc = nullptr; extern std::map skill_max; bool special_in_progress = false; +static void start_cartoon() { + if(!cartoon_happening && !is_combat()) { + for(int i = 0; i < 6; i++) + univ.party[i].combat_pos = univ.town.p_loc; + } + cartoon_happening = true; +} + // 0 - can't use 1 - combat only 2 - town only 3 - town & combat only 4 - everywhere 5 - outdoor // + 10 - mag. inept can use std::map abil_chart = { @@ -4116,6 +4125,7 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, case eSpecType::TOWN_SET_CENTER: if(l.x >= 0 && l.y >= 0) center = l; else center = is_combat() ? univ.party[current_pc].combat_pos : univ.town.p_loc; + start_cartoon(); redraw_screen(REFRESH_TERRAIN); break; case eSpecType::TOWN_LIFT_FOG: @@ -4188,6 +4198,65 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, end_missile_anim(); } break; + case eSpecType::TOWN_RELOCATE_CREATURE: + if(spec.ex2b > 4) { + giveError("Invalid positioning mode (0-4)"); + break; + } + if(spec.ex2a < 0) + i = univ.get_target_i(*current_pc_picked_in_spec_enc); + else i = spec.ex2a; + if(spec.ex2b > 1) { + if(spec.ex2b <= 3) l.x *= -1; + if(spec.ex2b >= 3) l.y *= -1; + } + if(i < 6) { + start_cartoon(); + if(spec.ex2b == 0) + univ.party[i].combat_pos = l; + else { + univ.party[i].combat_pos.x += l.x; + univ.party[i].combat_pos.y += l.y; + } + } else if(i >= 100) { + i -= 100; + if(spec.ex2b == 0) + univ.town.monst[i].cur_loc = l; + else { + univ.town.monst[i].cur_loc.x += l.x; + univ.town.monst[i].cur_loc.y += l.y; + } + } else { + giveError("Invalid positioning target!"); + break; + } + redraw_screen(REFRESH_TERRAIN); + if(spec.ex2c > 0) + sf::sleep(sf::milliseconds(spec.ex2c)); + *redraw = 1; + break; + case eSpecType::TOWN_PLACE_LABEL: + check_mess = false; + if(l.y < 0) { + if(l.x < 0) + l.x = univ.get_target_i(*current_pc_picked_in_spec_enc); + if(l.x < 6) + l = (is_combat() || cartoon_happening) ? univ.party[l.x].combat_pos : univ.town.p_loc; + else if(l.x == 6) + l = univ.town.p_loc; + else if(l.x >= 100 && l.x - 100 < univ.town.monst.size()) + l = univ.town.monst[l.x - 100].cur_loc; + else { + giveError("Invalid label target!"); + break; + } + } + get_strs(strs[0], strs[1], cur_spec_type, spec.m1, spec.m1); + place_text_label(strs[0], l, spec.ex2a); + redraw_screen(REFRESH_TERRAIN); + if(spec.ex2b > 0) // TODO: Add preferences setting to increase this delay, for slow readers + sf::sleep(sf::seconds(spec.ex2b)); + break; default: giveError("Special node type \"" + (*cur_node.type).name() + "\" is either miscategorized or unimplemented!"); break; diff --git a/src/boe.text.cpp b/src/boe.text.cpp index 684cac1d..8ed31c71 100644 --- a/src/boe.text.cpp +++ b/src/boe.text.cpp @@ -2,6 +2,7 @@ #define TEXT_BUF_LEN 70 #include +#include #include "boe.global.h" @@ -52,12 +53,13 @@ extern tessel_ref_t bg[]; extern short dest_personalities[40]; extern location source_locs[6]; extern location dest_locs[40] ; +extern location center; extern sf::Texture tiny_obj_gworld,invenbtn_gworld,status_gworld; extern cCustomGraphics spec_scen_g; extern sf::Texture pc_gworld; extern sf::RenderTexture pc_stats_gworld, item_stats_gworld, text_area_gworld; -extern short terrain_there[9][9]; +extern sf::RenderTexture terrain_screen_gworld; // game globals extern location ul; @@ -1117,7 +1119,6 @@ void Draw_Some_Item (sf::Texture& src_gworld, rectangle src_rect, sf::RenderTarg if((supressing_some_spaces) && (target != ok_space[0]) && (target != ok_space[1]) && (target != ok_space[2]) && (target != ok_space[3])) return; - terrain_there[target.x][target.y] = -1; destrec = coord_to_rect(target.x,target.y); if(main_win == 1) destrec.offset(ul.x + 5,ul.y + 5); @@ -1133,6 +1134,49 @@ void Draw_Some_Item (sf::Texture& src_gworld, rectangle src_rect, sf::RenderTarg } } +std::list posted_labels; + +void place_text_label(std::string string, location at, bool centred) { + TextStyle style; + style.font = FONT_PLAIN; + short height = 0; + short width = string_length(string, style, &height); + at.x -= center.x - 4; + at.x *= 28; + at.x += 14; + at.x -= width / 2; + + at.y -= center.y - 4; + at.y *= 36; + if(centred) + at.y += 18; + if(at.y == 0) at.y = 36; + else at.y -= height; + + rectangle text_rect(at, loc(at.x + width, at.y + height)); + text_rect.offset(-min(at.x,0),-min(at.y,0)); // If it's longer, make it off-centre to keep it onscreen. + text_rect.offset(13,13); + posted_labels.push_back({text_rect, string}); +} + +void draw_text_label(const text_label_t& label) { + sf::Color back_clr = {64, 64, 64, 42}; + TextStyle style; + style.font = FONT_PLAIN; + style.colour = sf::Color::White; + rectangle back_rect = label.text_rect, text_rect = label.text_rect; + back_rect.inset(-7,-7); + back_rect.offset(0,-2); + for(int i = 0; i <= 3; i++) { + fill_roundrect(terrain_screen_gworld, back_rect, 7, back_clr); + back_rect.inset(2,2); + back_clr.a *= 1.5; + } + //text_rect.offset(0, -text_rect.height() + 1); + text_rect.offset(0, -5); + win_draw_string(terrain_screen_gworld, text_rect, label.str, eTextMode::LEFT_TOP, style); +} + // TODO: This seems to duplicate logic found in graphtool to get a rect from a picture index rectangle coord_to_rect(short i,short j) { rectangle to_return; diff --git a/src/boe.text.h b/src/boe.text.h index 21901fa5..718b8442 100644 --- a/src/boe.text.h +++ b/src/boe.text.h @@ -37,3 +37,10 @@ bool day_reached(unsigned short which_day, unsigned short which_event); void Draw_Some_Item (sf::Texture& src_gworld, rectangle src_rect, sf::RenderTarget& targ_gworld, location target, char masked, short main_win); rectangle get_stat_effect_rect(int which_effect); +struct text_label_t { + rectangle text_rect; + std::string str; +}; + +void place_text_label(std::string string, location at, bool centred); +void draw_text_label(const text_label_t& label); diff --git a/src/classes/simpletypes.h b/src/classes/simpletypes.h index 5bc6a059..d42d197c 100644 --- a/src/classes/simpletypes.h +++ b/src/classes/simpletypes.h @@ -687,6 +687,8 @@ enum class eSpecType { TOWN_START_TARGETING = 200, TOWN_SPELL_PAT_FIELD = 201, TOWN_SPELL_PAT_BOOM = 202, + TOWN_RELOCATE_CREATURE = 203, + TOWN_PLACE_LABEL = 204, RECT_PLACE_FIELD = 210, RECT_SET_EXPLORED = 211, RECT_MOVE_ITEMS = 212, @@ -717,7 +719,7 @@ inline eSpecCat getNodeCategory(eSpecType node) { return eSpecCat::AFFECT; if(code >= 130 && code <= 160) return eSpecCat::IF_THEN; - if(code >= 170 && code <= 202) + if(code >= 170 && code <= 204) return eSpecCat::TOWN; if(code >= 210 && code <= 218) return eSpecCat::RECT; diff --git a/src/classes/special.cpp b/src/classes/special.cpp index 2e616cdf..651bdd5c 100644 --- a/src/classes/special.cpp +++ b/src/classes/special.cpp @@ -390,6 +390,7 @@ std::istream& operator >> (std::istream& in, eSpecType& e) { // % - Choose button to select shop cost adjustment // { - Choose button to select a spell pattern // } - As above, but allows you to select which version of the rotateable field +// ^ - Choose button to select a positioning mode // e - Choose button to select a status effect // E - Choose button to select a party status effect // w - Choose button to select main party status effect @@ -447,17 +448,17 @@ static const char*const button_dict[7][11] = { "s ss s + s==+s = ", // ex2b " = s ", // ex2c }, { // town nodes - "mmmmmmmmmmmmmmm dddmmmmmmmmmmmm", // msg1 - " ", // msg2 - " ", // msg3 - " 8 ppp ", // pic - " ??? ", // pictype - " c L { ", // ex1a - " s s s s @ ", // ex1b - " }}", // ex1c - "@ 7 ! c T T i FD", // ex2a - " DD / ", // ex2b - " x x : : ", // ex2c + "mmmmmmmmmmmmmmm dddmmmmmmmmmmmmmM", // msg1 + " ", // msg2 + " ", // msg3 + " 8 ppp ", // pic + " ??? ", // pictype + " c L { ", // ex1a + " s s s s @ ", // ex1b + " }} ", // ex1c + "@ 7 ! c T T i FD ", // ex2a + " DD / ^ ", // ex2b + " x x : : ", // ex2c }, { // rectangle nodes "mmmmmmmmm", // msg1 " ", // msg2 diff --git a/src/scenedit/scen.keydlgs.cpp b/src/scenedit/scen.keydlgs.cpp index d3a20766..f2bb7e07 100644 --- a/src/scenedit/scen.keydlgs.cpp +++ b/src/scenedit/scen.keydlgs.cpp @@ -343,6 +343,9 @@ short choose_text(eStrType list, unsigned short cur_choice, cDialog* parent, std "Magic Resistance", "Fire Resistance", "Cold Resistance", "Poison Resistance", }; break; + case STRT_POS_MODE: + strings = {"Absolute Position", "Move Southeast", "Move Southwest", "Move Northwest", "Move Northeast"}; + break; } if(cur_choice < 0 || cur_choice >= strings.size()) cur_choice = -1; @@ -807,6 +810,7 @@ static bool edit_spec_enc_value(cDialog& me, std::string item_hit, node_stack_t& case '&': strt = STRT_SHOP; title = "Which shop?"; break; case '%': strt = STRT_COST_ADJ; title = "What cost adjust?"; break; case '*': strt = STRT_CONTEXT; title = "What context?"; break; + case '^': strt = STRT_POS_MODE; title = "Select positioning mode:"; break; case ':': strt = STRT_STAIR_MODE; title = "Select trigger limitations:"; break; case 'w': strt = STRT_STATUS; title = "Select status:"; str_adj = 1; break; case 'j': strt = STRT_QUEST; title = "Select a quest:"; break; diff --git a/src/scenedit/scen.keydlgs.h b/src/scenedit/scen.keydlgs.h index 2290696b..54b35bad 100644 --- a/src/scenedit/scen.keydlgs.h +++ b/src/scenedit/scen.keydlgs.h @@ -12,7 +12,7 @@ enum eStrType { STRT_SHOP, STRT_COST_ADJ, STRT_STAIR_MODE, STRT_TALK_NODE, STRT_STATUS, STRT_SPELL_PAT, STRT_SUMMON, STRT_TALK, STRT_ENCHANT, STRT_DIR, STRT_QUEST, STRT_QUEST_STATUS, - STRT_HEALING, STRT_TREASURE, STRT_MONST_STAT, + STRT_HEALING, STRT_TREASURE, STRT_MONST_STAT, STRT_POS_MODE, }; bool cre(short val,short min,short max,std::string text1,std::string text2,cDialog* parent) ;