Factor out the game toolbars into a class.

This includes the following related or incidental changes:
* Remove the win_from_rects global variable. With one minor exception, they were all equal to the relevant render texture's size anyway.
* Split out time advancement from the handle_action function into a separate function
* Split out each individual button action into its own function
* Thanks to the above two, button actions triggered from the keyboard (and menu spells) no longer pass thru handle_action
* Side-effect: keyboard shortcuts and menu spells no longer trigger the button press animation
* Button presses now behave like proper buttons
* Button clicks are now grouped by effect in the handling code, rather than by index
* Removed a variable that mysteriously caused dialogue to become blank
This commit is contained in:
2020-01-30 00:00:40 -05:00
parent 1d61d48ec3
commit 4859fa70bc
12 changed files with 466 additions and 296 deletions

View File

@@ -117,6 +117,7 @@
<ClInclude Include="..\..\..\src\game\boe.text.hpp" />
<ClInclude Include="..\..\..\src\game\boe.town.hpp" />
<ClInclude Include="..\..\..\src\game\boe.townspec.hpp" />
<ClInclude Include="..\..\..\src\game\boe.ui.hpp" />
<ClInclude Include="..\..\..\rsrc\menus\boeresource.h" />
</ItemGroup>
<ItemGroup>
@@ -142,6 +143,7 @@
<ClCompile Include="..\..\..\src\game\boe.text.cpp" />
<ClCompile Include="..\..\..\src\game\boe.town.cpp" />
<ClCompile Include="..\..\..\src\game\boe.townspec.cpp" />
<ClCompile Include="..\..\..\src\game\boe.ui.cpp" />
<ClCompile Include="..\..\..\src\pcedit\pc.editors.cpp" />
</ItemGroup>
<ItemGroup>

View File

@@ -81,6 +81,9 @@
<ClInclude Include="..\..\..\src\game\boe.actions.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\game\boe.ui.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\game\boe.actions.cpp">
@@ -140,6 +143,9 @@
<ClCompile Include="..\..\..\src\game\boe.townspec.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\game\boe.ui.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\pcedit\pc.editors.cpp">
<Filter>Source Files</Filter>
</ClCompile>

View File

@@ -299,6 +299,7 @@
91FCC8D818FE28CC007026CE /* pcedit.xib in Resources */ = {isa = PBXBuildFile; fileRef = 91FCC8D718FE28CC007026CE /* pcedit.xib */; };
91FCC8DC18FE2CE8007026CE /* pc.menus.mac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 91FCC8DB18FE2CE8007026CE /* pc.menus.mac.mm */; };
91FCC8F418FF0866007026CE /* pc.appleevents.mm in Sources */ = {isa = PBXBuildFile; fileRef = 91FCC8F318FF069A007026CE /* pc.appleevents.mm */; };
91FD417423DFC9D4003D5B8A /* boe.ui.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91FD417223DFC5C1003D5B8A /* boe.ui.cpp */; };
DCCA42001A8C467000E6A9A5 /* SFML.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 91F6F8E218F87F3700E3EA15 /* SFML.framework */; };
DCCA42021A8C467800E6A9A5 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DCCA42011A8C467800E6A9A5 /* libz.dylib */; };
DCCA42031A8C469400E6A9A5 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DCCA42011A8C467800E6A9A5 /* libz.dylib */; };
@@ -814,6 +815,8 @@
91FCC8DA18FE2CCA007026CE /* pc.menus.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = pc.menus.hpp; sourceTree = "<group>"; };
91FCC8DB18FE2CE8007026CE /* pc.menus.mac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = pc.menus.mac.mm; sourceTree = "<group>"; };
91FCC8F318FF069A007026CE /* pc.appleevents.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = pc.appleevents.mm; sourceTree = "<group>"; };
91FD417223DFC5C1003D5B8A /* boe.ui.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = boe.ui.cpp; sourceTree = "<group>"; };
91FD417323DFC5C1003D5B8A /* boe.ui.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = boe.ui.hpp; sourceTree = "<group>"; };
91FDB5771A4E71A900DE5983 /* shop.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = shop.hpp; sourceTree = "<group>"; };
91FDB5791A4E774E00DE5983 /* shop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = shop.cpp; sourceTree = "<group>"; };
DCCA42011A8C467800E6A9A5 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
@@ -1458,6 +1461,7 @@
2BF04B070BF51924006C0831 /* boe.text.cpp */,
2BF04B090BF51924006C0831 /* boe.town.cpp */,
2BF04AD50BF51923006C0831 /* boe.townspec.cpp */,
91FD417223DFC5C1003D5B8A /* boe.ui.cpp */,
);
name = src;
sourceTree = "<group>";
@@ -1485,6 +1489,7 @@
2BF04B080BF51924006C0831 /* boe.text.hpp */,
2BF04B0A0BF51924006C0831 /* boe.town.hpp */,
2BF04AD60BF51923006C0831 /* boe.townspec.hpp */,
91FD417323DFC5C1003D5B8A /* boe.ui.hpp */,
);
name = headers;
sourceTree = "<group>";
@@ -1849,6 +1854,7 @@
919145FC18E3AB1B005CF3A4 /* boe.appleevents.mm in Sources */,
915325171A2E1DF0000A9A1C /* oldstructs.cpp in Sources */,
91B0D5D11E3442FE002BE4DA /* view_dialogs.cpp in Sources */,
91FD417423DFC9D4003D5B8A /* boe.ui.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -23,6 +23,7 @@
#include "sounds.hpp"
#include "boe.infodlg.hpp"
#include "boe.main.hpp"
#include "boe.ui.hpp"
#include "mathutil.hpp"
#include "fileio.hpp"
#include "choicedlog.hpp"
@@ -37,7 +38,6 @@
#include "render_shapes.hpp"
#include "enum_map.hpp"
rectangle bottom_buttons[14];
rectangle item_screen_button_rects[9] = {
{125,10,141,28},{125,40,141,58},{125,68,141,86},{125,98,141,116},{125,126,141,144},{125,156,141,174},
{126,176,141,211},
@@ -123,6 +123,8 @@ std::queue<pending_special_type> special_queue;
bool end_scenario = false;
bool current_bash_is_bash = false;
static void advance_time(bool did_something, bool need_redraw, bool need_reprint);
// This is defined in pc.editors.cpp since the PC editor also uses it
extern void edit_stuff_done();
@@ -294,6 +296,51 @@ static void handle_spellcast(eSkill which_type, bool& did_something, bool& need_
put_item_screen(stat_window);
}
static void handle_begin_look(bool& need_redraw) {
if(overall_mode == MODE_OUTDOORS) overall_mode = MODE_LOOK_OUTDOORS;
if(overall_mode == MODE_TOWN) overall_mode = MODE_LOOK_TOWN;
if(overall_mode == MODE_COMBAT) overall_mode = MODE_LOOK_COMBAT;
add_string_to_buf("Look: Select a space. You can also right click to look.", 2);
need_redraw = true;
}
static void handle_begin_talk(bool& need_reprint) {
overall_mode = MODE_TALK_TOWN;
add_string_to_buf("Talk: Select someone.");
need_reprint = true;
}
static void handle_stand_ready(bool& need_redraw, bool& need_reprint) {
need_reprint = true;
need_redraw = true;
//draw_terrain();
//pause(2);
univ.cur_pc++;
combat_next_step();
set_stat_window_for_pc(univ.cur_pc);
put_pc_screen();
}
static void handle_parry(bool& did_something, bool& need_redraw, bool& need_reprint) {
add_string_to_buf("Parry.");
char_parry();
did_something = true;
need_reprint = true;
need_redraw = true;
}
static void handle_toggle_active(bool& need_reprint) {
if(combat_active_pc == 6) {
add_string_to_buf("This PC now active.");
combat_active_pc = univ.cur_pc;
} else {
add_string_to_buf("All PC's now active.");
univ.cur_pc = combat_active_pc;
combat_active_pc = 6;
}
need_reprint = true;
}
static void handle_rest(bool& need_redraw, bool& need_reprint) {
sf::Event dummy_evt;
int i = 0;
@@ -969,56 +1016,27 @@ static void handle_party_death() {
}
bool handle_action(const sf::Event& event) {
short s1,s2,s3;
long item_hit;
bool are_done = false;
bool need_redraw = false, did_something = false, need_reprint = false;
bool pc_delayed = false;
location cur_loc,loc_in_sec,cur_direction;
short button_hit = 12;
location loc_in_sec,cur_direction;
bool right_button = event.mouseButton.button == sf::Mouse::Right;
eGameMode previous_mode;
rectangle world_screen = win_to_rects[WINRECT_TERVIEW];
world_screen.inset(13, 13);
std::ostringstream str;
location the_point,point_in_area;
location point_in_area;
the_point = location(event.mouseButton.x, event.mouseButton.y);
location the_point(event.mouseButton.x, event.mouseButton.y);
the_point = mainPtr.mapPixelToCoords(the_point, mainView);
end_scenario = false;
// MARK: First, figure out where party is
switch(overall_mode) {
case MODE_OUTDOORS: case MODE_LOOK_OUTDOORS:
cur_loc = univ.party.out_loc;
for(int i = 0; i < 14; i++)
if(the_point.in(bottom_buttons[i])) {
button_hit = i;
if(!spell_forced)
main_button_click(i);
}
break;
case MODE_TOWN: case MODE_TALK_TOWN: case MODE_TOWN_TARGET: case MODE_USE_TOWN: case MODE_LOOK_TOWN:
case MODE_DROP_TOWN: case MODE_BASH_TOWN:
case MODE_COMBAT: case MODE_SPELL_TARGET: case MODE_FIRING: case MODE_THROWING:
case MODE_FANCY_TARGET: case MODE_DROP_COMBAT: case MODE_LOOK_COMBAT:
cur_loc = center;
for(int i = 0; i < 14; i++)
if(the_point.in(bottom_buttons[i])) {
button_hit = i;
if(!spell_forced)
main_button_click(i);
}
break;
case MODE_TALKING: case MODE_SHOPPING: break;
case MODE_STARTUP: case MODE_RESTING: case MODE_CUTSCENE:
// If we get here during these modes, something is probably not right, so bail out
add_string_to_buf("Unexpected game state!");
return are_done;
if(overall_mode == MODE_STARTUP || overall_mode == MODE_RESTING || overall_mode == MODE_CUTSCENE) {
// If we get here during these modes, something is probably not right, so bail out
add_string_to_buf("Unexpected game state!");
return are_done;
}
// Now split off the extra stuff, like talking and shopping.
@@ -1033,52 +1051,60 @@ bool handle_action(const sf::Event& event) {
return false;
}
// Otherwise they're in a terrain view mode
location cur_loc = is_out() ? univ.party.out_loc : center;
auto button_hit = UI::toolbar.button_hit(mainPtr, the_point);
// MARK: Then, handle a button being hit.
if(button_hit != 12)
switch(button_hit) {
case 0: case 1:
handle_spellcast(button_hit == 0 ? eSkill::MAGE_SPELLS : eSkill::PRIEST_SPELLS, did_something, need_redraw, need_reprint);
case TOOLBAR_NONE: break;
case TOOLBAR_MAGE: case TOOLBAR_PRIEST:
handle_spellcast(button_hit == TOOLBAR_MAGE ? eSkill::MAGE_SPELLS : eSkill::PRIEST_SPELLS, did_something, need_redraw, need_reprint);
break;
case 2:
if(overall_mode == MODE_OUTDOORS) overall_mode = MODE_LOOK_OUTDOORS;
if(overall_mode == MODE_TOWN) overall_mode = MODE_LOOK_TOWN;
if(overall_mode == MODE_COMBAT) overall_mode = MODE_LOOK_COMBAT;
add_string_to_buf("Look: Select a space. You can also right click to look.", 2);
need_redraw = true;
case TOOLBAR_LOOK:
handle_begin_look(need_redraw);
break;
case 3:
if(overall_mode == MODE_COMBAT) {
add_string_to_buf("Parry.");
char_parry();
did_something = true;
need_reprint = true;
need_redraw = true;
} else if(overall_mode == MODE_TOWN) {
overall_mode = MODE_TALK_TOWN;
add_string_to_buf("Talk: Select someone.");
need_reprint = true;
} else if(overall_mode == MODE_OUTDOORS)
case TOOLBAR_SHIELD:
if(overall_mode == MODE_COMBAT)
handle_parry(did_something, need_redraw, need_reprint);
break;
case TOOLBAR_TALK:
if(overall_mode == MODE_TOWN)
handle_begin_talk(need_reprint);
break;
case TOOLBAR_CAMP:
if(overall_mode == MODE_OUTDOORS)
handle_rest(need_redraw, need_reprint);
break;
case 4:
if(overall_mode == MODE_OUTDOORS) {
case TOOLBAR_SCROLL: case TOOLBAR_MAP:
if(overall_mode == MODE_OUTDOORS || overall_mode == MODE_TOWN) {
give_help(62,0);
display_map();
set_cursor(sword_curs);
} else if(overall_mode == MODE_TOWN || overall_mode == MODE_COMBAT)
}
break;
case TOOLBAR_BAG: case TOOLBAR_HAND:
if(overall_mode == MODE_TOWN || overall_mode == MODE_COMBAT)
handle_get_items(did_something, need_redraw, need_reprint);
break;
case 5:
case TOOLBAR_SAVE:
if(overall_mode == MODE_OUTDOORS) {
save_party(univ.file, univ);
need_redraw = true;
current_switch = 6;
break;
} else if(overall_mode == MODE_TOWN) {
}
break;
case TOOLBAR_USE:
if(overall_mode == MODE_TOWN) {
add_string_to_buf("Use: Select a space or item.");
add_string_to_buf(" (Hit button again to cancel.)");
need_reprint = true;
@@ -1087,39 +1113,32 @@ bool handle_action(const sf::Event& event) {
overall_mode = MODE_TOWN;
need_reprint = true;
add_string_to_buf(" Cancelled.");
} else if(overall_mode == MODE_COMBAT) {
need_reprint = true;
need_redraw = true;
pc_delayed = true;
}
break;
case 6:
case TOOLBAR_WAIT:
if(overall_mode == MODE_COMBAT) {
handle_stand_ready(need_redraw, need_reprint);
}
break;
case TOOLBAR_LOAD:
if(overall_mode == MODE_OUTDOORS)
do_load();
else if(overall_mode == MODE_TOWN) {
give_help(62,0);
display_map();
set_cursor(sword_curs);
} else handle_missile(need_redraw, need_reprint);
break;
case 7:
case TOOLBAR_SHOOT:
if(overall_mode == MODE_COMBAT || overall_mode == MODE_FIRING || overall_mode == MODE_THROWING)
handle_missile(need_redraw, need_reprint);
break;
case TOOLBAR_SWORD: case TOOLBAR_END:
handle_combat_switch(did_something, need_redraw, need_reprint);
break;
case 8:
case TOOLBAR_ACT:
if(overall_mode == MODE_COMBAT) {
if(combat_active_pc == 6) {
add_string_to_buf("This PC now active.");
combat_active_pc = univ.cur_pc;
} else {
add_string_to_buf("All PC's now active.");
univ.cur_pc = combat_active_pc;
combat_active_pc = 6;
}
need_reprint = true;
handle_toggle_active(need_reprint);
}
break;
}
@@ -1389,20 +1408,17 @@ bool handle_action(const sf::Event& event) {
need_reprint = true;
}
// MARK: If in combat and pc delayed, jump forward a step
if(pc_delayed) {
draw_terrain();
//pause(2);
univ.cur_pc++;
combat_next_step();
set_stat_window_for_pc(univ.cur_pc);
put_pc_screen();
}
advance_time(did_something, need_redraw, need_reprint);
are_done = All_Done;
return are_done;
}
static void advance_time(bool did_something, bool need_redraw, bool need_reprint) {
// MARK: At this point, see if any specials have been queued up, and deal with them
// Note: We just check once here instead of looping because run_special also pulls from the queue.
if(!special_queue.empty()) {
s3 = 0;
short s1 = 0, s2 = 0, s3 = 0;
pending_special_type pending = special_queue.front();
special_queue.pop();
run_special(pending, &s1, &s2, &s3);
@@ -1428,9 +1444,6 @@ bool handle_action(const sf::Event& event) {
handle_party_death();
else if(end_scenario)
handle_victory();
are_done = All_Done;
return are_done;
}
void handle_monster_actions(bool& need_redraw, bool& need_reprint) {
@@ -1522,26 +1535,10 @@ void handle_menu_spell(eSpell spell_picked) {
if((store_spell_target = char_select_pc((*spell_picked).need_select == SELECT_ANY ? 1 : 0,"Cast spell on who?")) == 6)
return;
}
/* if((is_combat()) && (((spell_type == 0) && (refer_mage[spell_picked] > 0)) ||
((spell_type == 1) && (refer_priest[spell_picked] > 0)))){
if((spell_type == 0) && (mage_need_select[spell_picked] > 0))
store_spell_target = char_select_pc(2 - mage_need_select[spell_picked],0,"Cast spell on who?");
else if((spell_type == 1) && (priest_need_select[spell_picked] > 0))
store_spell_target = char_select_pc(2 - priest_need_select[spell_picked],0,"Cast spell on who?");
}
else {
} */
if(spell_type == eSkill::MAGE_SPELLS) {
pass_point.x = bottom_buttons[0].left + 5;
pass_point.y = bottom_buttons[0].top + 5;
} else {
pass_point.x = bottom_buttons[1].left + 5;
pass_point.y = bottom_buttons[1].top + 5;
}
pass_point = mainPtr.mapCoordsToPixel(pass_point, mainView);
event.mouseButton.x = pass_point.x;
event.mouseButton.y = pass_point.y;
handle_action(event);
bool did_something = false, need_redraw = false, need_reprint = false;
handle_spellcast(spell_type, did_something, need_redraw, need_reprint);
advance_time(did_something, need_redraw, need_reprint);
}
void initiate_outdoor_combat(short i) {
@@ -1687,6 +1684,8 @@ bool handle_keystroke(const sf::Event& event){
}
}
bool did_something = false, need_redraw = false, need_reprint = false;
char chr = keyToChar(chr2, event.key.shift);
// F1 should bring up help.
// TODO: So should the help key, if it exists (but SFML doesn't support the help key)
@@ -2074,80 +2073,99 @@ bool handle_keystroke(const sf::Event& event){
}
break;
case 's': case 'x': case 'e':
case 'm': case 'p': case 'l': case 'r': case 'w': case 't': case 'd': case 'g': case 'f':
case 'M': case 'P':
int btn = 50;
if(overall_mode == MODE_SPELL_TARGET || overall_mode == MODE_FANCY_TARGET || overall_mode == MODE_TOWN_TARGET) { // cancel spell
if(chr == 'm') btn = 0;
else if(chr == 'p') btn = 1;
// Spells (cast/cancel)
case 'M': spell_forced = true;
case 'm':
if(overall_mode == MODE_SPELL_TARGET || overall_mode == MODE_FANCY_TARGET || overall_mode == MODE_TOWN_TARGET || overall_mode == MODE_OUTDOORS || overall_mode == MODE_TOWN || overall_mode == MODE_COMBAT) {
handle_spellcast(eSkill::MAGE_SPELLS, did_something, need_redraw, need_reprint);
advance_time(did_something, need_redraw, need_reprint);
}
break;
case 'P': spell_forced = true;
case 'p':
if(overall_mode == MODE_SPELL_TARGET || overall_mode == MODE_FANCY_TARGET || overall_mode == MODE_TOWN_TARGET || overall_mode == MODE_OUTDOORS || overall_mode == MODE_TOWN || overall_mode == MODE_COMBAT) {
handle_spellcast(eSkill::PRIEST_SPELLS, did_something, need_redraw, need_reprint);
advance_time(did_something, need_redraw, need_reprint);
}
break;
case 'l': // Look
if((overall_mode == MODE_OUTDOORS) || (overall_mode == MODE_TOWN) || (overall_mode == MODE_COMBAT)) {
switch(chr) {
// Spells
case 'M': spell_forced = true; btn = 0; break;
case 'm': btn = 0; break;
case 'P': spell_forced = true; btn = 1; break;
case 'p': btn = 1; break;
// Look
case 'l': btn = 2; break;
case 'r': // Rest
if(overall_mode != MODE_OUTDOORS) return false;
btn = 3;
break;
case 't': // Talk
if(overall_mode != MODE_TOWN) return false;
btn = 3;
break;
case 'w': // Wait (town), delay action (combat)
if(overall_mode == MODE_COMBAT)
btn = 5;
else if(overall_mode == MODE_TOWN) {
pass_point = mainPtr.mapCoordsToPixel({1001, 0}, mainView);
pass_event.mouseButton.x = pass_point.x;
pass_event.mouseButton.y = pass_point.y;
are_done = handle_action(pass_event);
}
else {
add_string_to_buf("Wait: In town only.");
print_buf();
return false;
}
break;
case 'd': // Parry
if(overall_mode != MODE_COMBAT) return false;
btn = 3;
break;
case 'g': // Get
if(overall_mode == MODE_OUTDOORS) return false;
btn = 4;
break;
case 's': // Shoot
if(overall_mode != MODE_COMBAT) return false;
btn = 6;
break;
case 'x': // Toggle active
if(overall_mode != MODE_COMBAT) return false;
btn = 8;
break;
case 'e': // End combat
if(overall_mode != MODE_COMBAT) return false;
btn = 7;
break;
case 'f': // Fight (toggle combat)
if(overall_mode != MODE_TOWN && overall_mode != MODE_COMBAT) return false;
btn = 7;
break;
}
} else if(chr == 's' && (overall_mode == MODE_FIRING || overall_mode == MODE_THROWING))
btn = 6;
if(btn < 50) {
pass_point.x = bottom_buttons[btn].left + 5;
pass_point.y = bottom_buttons[btn].top + 5;
pass_point = mainPtr.mapCoordsToPixel(pass_point, mainView);
handle_begin_look(need_redraw);
advance_time(did_something, need_redraw, need_reprint);
}
break;
case 'r': // Rest (outdoors)
if(overall_mode == MODE_OUTDOORS) {
handle_rest(need_redraw, need_reprint);
advance_time(did_something, need_redraw, need_reprint);
}
break;
case 't': // Talk
if(overall_mode == MODE_TOWN) {
handle_begin_talk(need_reprint);
advance_time(did_something, need_redraw, need_reprint);
}
break;
case 'w': // Wait / delay
if(overall_mode == MODE_TOWN) {
pass_point = mainPtr.mapCoordsToPixel({1001, 0}, mainView);
pass_event.mouseButton.x = pass_point.x;
pass_event.mouseButton.y = pass_point.y;
are_done = handle_action(pass_event);
} else if(overall_mode == MODE_COMBAT) {
handle_stand_ready(need_redraw, need_reprint);
advance_time(did_something, need_redraw, need_reprint);
} else if(overall_mode == MODE_OUTDOORS) {
add_string_to_buf("Wait: In town only.");
print_buf();
return false;
}
break;
case 'd': // Parry
if(overall_mode == MODE_COMBAT) {
handle_parry(did_something, need_redraw, need_reprint);
advance_time(did_something, need_redraw, need_reprint);
}
break;
case 'g': // Get items
if(overall_mode == MODE_TOWN || overall_mode == MODE_COMBAT) {
handle_get_items(did_something, need_redraw, need_reprint);
advance_time(did_something, need_redraw, need_reprint);
}
break;
case 's': // Shoot
if(overall_mode == MODE_COMBAT || overall_mode == MODE_FIRING || overall_mode == MODE_THROWING) {
handle_missile(need_redraw, need_reprint);
advance_time(did_something, need_redraw, need_reprint);
}
break;
case 'x': // Toggle active
if(overall_mode == MODE_COMBAT) {
handle_toggle_active(need_reprint);
advance_time(did_something, need_redraw, need_reprint);
}
break;
case 'e': // End combat
if(overall_mode == MODE_COMBAT) {
handle_combat_switch(did_something, need_redraw, need_reprint);
advance_time(did_something, need_redraw, need_reprint);
}
break;
case 'f': // Toggle combat
if(overall_mode == MODE_TOWN || overall_mode == MODE_COMBAT) {
handle_combat_switch(did_something, need_redraw, need_reprint);
advance_time(did_something, need_redraw, need_reprint);
}
break;
}
@@ -2214,7 +2232,7 @@ void post_load() {
set_stat_window(ITEM_WIN_PC1);
put_pc_screen();
draw_terrain();
draw_buttons(0);
UI::toolbar.draw(mainPtr);
draw_text_bar();
print_buf();

View File

@@ -18,6 +18,7 @@
#include "boe.specials.hpp"
#include "boe.graphutil.hpp"
#include "boe.main.hpp"
#include "boe.ui.hpp"
#include "mathutil.hpp"
#include "choicedlog.hpp"
#include "boe.menus.hpp"
@@ -360,7 +361,7 @@ void start_outdoor_combat(cOutdoors::cCreature encounter,location where,short nu
set_pc_moves();
pick_next_pc();
center = univ.current_pc().combat_pos;
draw_buttons(0);
UI::toolbar.draw(mainPtr);
put_pc_screen();
set_stat_window_for_pc(univ.cur_pc);
@@ -4656,7 +4657,7 @@ void end_combat() {
if(univ.current_pc().main_status != eMainStatus::ALIVE)
univ.cur_pc = first_active_pc();
put_item_screen(stat_window);
draw_buttons(0);
UI::toolbar.draw(mainPtr);
}

View File

@@ -544,7 +544,6 @@ void set_up_shop_array() {
void start_talk_mode(short m_num,short personality,mon_num_t monst_type,short store_face_pic) {
rectangle area_rect;
std::string place_string1;
store_personality = personality;
@@ -570,10 +569,11 @@ void start_talk_mode(short m_num,short personality,mon_num_t monst_type,short st
current_talk_node = TALK_LOOK;
// Bring up and place first strings.
place_string1 = univ.town.cur_talk().people[personality % 10].look;
save_talk_str1 = univ.town.cur_talk().people[personality % 10].look;
save_talk_str2 = "";
can_save_talk = true;
place_talk_str(place_string1, "", 0, dummy_rect);
place_talk_str(save_talk_str1, "", 0, dummy_rect);
put_item_screen(stat_window);
give_help(5,6);

View File

@@ -25,6 +25,7 @@
#include "boe.items.hpp"
#include "boe.dlgutil.hpp"
#include "boe.infodlg.hpp"
#include "boe.ui.hpp"
#include "scrollbar.hpp"
@@ -74,10 +75,7 @@ sf::View mainView;
long anim_ticks = 0;
// 0 - terrain 1 - buttons 2 - pc stats
// 3 - item stats 4 - text bar 5 - text area (not right)
enum_map(eGuiArea, rectangle) win_from_rects = {{0,0,350,278},{0,0,37,258},{0,0,115,288},{0,0,143,288},{0,0,21,279},{0,0,0,288}};
enum_map(eGuiArea, rectangle) win_to_rects = {{7,19,358,298},{385,19,423,285},{7,305,123,576},{132,305,276,576},{360,19,381,298},{285,305,423,561}};
extern enum_map(eGuiArea, rectangle) win_to_rects;
// 0 - title 1 - button 2 - credits 3 - base button
rectangle startup_from[4] = {{0,0,274,602},{274,0,322,301},{0,301,67,579},{274,301,314,341}};
@@ -446,17 +444,6 @@ void draw_start_button(eStartButton which_position,short which_button) {
win_draw_string(mainPtr,to_rect,button_labels[which_position],eTextMode::CENTRE,style);
}
void main_button_click(int which_button) {
mainPtr.setActive();
// TODO: Mini-event loop so that the click doesn't happen until releasing the mouse button
draw_buttons(which_button);
mainPtr.display();
play_sound(37, time_in_ticks(5));
draw_buttons(-1);
undo_clip(mainPtr);
}
void arrow_button_click(rectangle button_rect) {
mainPtr.setActive();
clip_rect(mainPtr, button_rect);
@@ -539,7 +526,7 @@ void redraw_screen(int refresh) {
if(refresh & REFRESH_BAR)
draw_text_bar();
refresh_text_bar();
draw_buttons(-1);
UI::toolbar.draw(mainPtr);
break;
}
if(overall_mode == MODE_COMBAT)
@@ -595,81 +582,6 @@ void put_background() {
tileImage(mainPtr, rectangle(mainPtr), bg_pict);
}
// mode; -1 - all buttons, normal; otherwise draw this button pressed
void draw_buttons(short mode) {
rectangle lg_rect = {0,0,38,38}, sm_rect[2] = {{0,38,19,76}, {19,38,38,76}}, dest_rec;
static const int MAX_TOOLBAR_BUTTONS = 14;
static const location null_loc(-1,-1);
static const location out_buttons[MAX_TOOLBAR_BUTTONS] = {
{0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {5,0}, {5,1}, null_loc, null_loc, null_loc, null_loc, null_loc, null_loc, null_loc
};
static const location town_buttons[MAX_TOOLBAR_BUTTONS] = {
{0,0}, {1,0}, {2,0}, {2,1}, {3,1}, {4,2}, {5,2}, {4,1}, null_loc, null_loc, null_loc, null_loc, null_loc, null_loc
};
static const location combat_buttons[MAX_TOOLBAR_BUTTONS] = {
{0,0}, {1,0}, {2,0}, {0,1}, {1,1}, {0,2}, {2,2}, {1,2}, {3,2}, null_loc, null_loc, null_loc, null_loc, null_loc
};
extern rectangle bottom_buttons[MAX_TOOLBAR_BUTTONS];
const location* toolbar = is_combat() ? combat_buttons : (is_town() ? town_buttons : out_buttons);
static bool inited = false;
static sf::RenderTexture button_gw;
if(!inited) {
inited = true;
button_gw.create(266,38);
}
sf::Texture& buttons_gworld = *ResMgr::graphics.get("buttons");
dest_rec = lg_rect;
bool bottom_half = false;
std::fill_n(bottom_buttons, MAX_TOOLBAR_BUTTONS, rectangle());
for(int i = 0; i < MAX_TOOLBAR_BUTTONS && toolbar[i] != null_loc; i++) {
rectangle source_rect = {0, 0, 32, 32};
rectangle to_rect = dest_rec, btn_rect, icon_rect;
source_rect.offset(32 * toolbar[i].x, 38 + 32 * toolbar[i].y);
if(toolbar[i].y == 2) {
// Small button
btn_rect = sm_rect[bottom_half];
source_rect.height() = 13;
to_rect.height() = 19;
if(bottom_half) {
to_rect.offset(0,19);
bottom_half = false;
} else bottom_half = true;
icon_rect = {3,3,13,13};
} else {
// Large button
btn_rect = lg_rect;
if(bottom_half) {
dest_rec.offset(38,0);
bottom_half = false;
}
icon_rect = {3,3,32,32};
}
if(mode == -1) {
rect_draw_some_item(buttons_gworld, btn_rect, button_gw, to_rect);
to_rect.inset(3,3);
rect_draw_some_item(buttons_gworld, source_rect, button_gw, to_rect, sf::BlendAlpha);
to_rect.inset(-3,-3);
}
to_rect.offset(win_to_rects[WINRECT_ACTBTNS].topLeft());
if(i == mode)
fill_rect(mainPtr, to_rect, sf::Color::Blue);
else fill_rect(mainPtr, to_rect, sf::Color::Black);
bottom_buttons[i] = to_rect;
if(toolbar[i].y != 2 || !bottom_half) {
dest_rec.offset(38,0);
bottom_half = false;
}
}
button_gw.display();
dest_rec = win_to_rects[WINRECT_ACTBTNS];
rect_draw_some_item(button_gw.getTexture(), rectangle(button_gw), mainPtr, dest_rec, sf::BlendAdd);
}
void draw_text_bar() {
location loc;
@@ -713,7 +625,8 @@ void draw_text_bar() {
void put_text_bar(std::string str) {
text_bar_gworld.setActive();
rect_draw_some_item(*ResMgr::graphics.get("textbar"), win_from_rects[WINRECT_STATUS], text_bar_gworld, win_from_rects[WINRECT_STATUS]);
auto& bar_gw = *ResMgr::graphics.get("textbar");
rect_draw_some_item(bar_gw, rectangle(bar_gw), text_bar_gworld, rectangle(bar_gw));
TextStyle style;
style.colour = sf::Color::White;
style.font = FONT_BOLD;
@@ -753,7 +666,7 @@ void put_text_bar(std::string str) {
void refresh_text_bar() {
mainPtr.setActive();
rect_draw_some_item(text_bar_gworld.getTexture(), win_from_rects[WINRECT_STATUS], mainPtr, win_to_rects[WINRECT_STATUS]);
rect_draw_some_item(text_bar_gworld.getTexture(), rectangle(text_bar_gworld), mainPtr, win_to_rects[WINRECT_STATUS]);
}
// this is used for determinign whether to round off walkway corners
@@ -1555,10 +1468,9 @@ void draw_pointing_arrows() {
}
void redraw_terrain() {
rectangle to_rect;
to_rect = win_to_rects[WINRECT_TERVIEW];
rect_draw_some_item(terrain_screen_gworld.getTexture(), win_from_rects[WINRECT_TERVIEW], mainPtr, to_rect);
rectangle to_rect = win_to_rects[WINRECT_TERVIEW], from_rect(terrain_screen_gworld);
from_rect.bottom -= 10; // Clip off the little arrows TODO: Maybe move them to another sheet?
rect_draw_some_item(terrain_screen_gworld.getTexture(), from_rect, mainPtr, to_rect);
apply_light_mask(true);

View File

@@ -24,13 +24,11 @@ void draw_startup(short but_type);
void draw_anim();
void place_anim();
void draw_start_button(eStartButton which_position,short which_button);
void main_button_click(int which_button);
void arrow_button_click(rectangle button_rect);
void end_startup();
void load_main_screen();
void redraw_screen(int refresh);
void put_background();
void draw_buttons(short mode);
void draw_text_bar();
void refresh_text_bar();
void put_text_bar(std::string str);

View File

@@ -20,6 +20,7 @@
#include "boe.locutils.hpp"
#include "boe.specials.hpp"
#include "boe.infodlg.hpp"
#include "boe.ui.hpp"
#include "mathutil.hpp"
#include "render_image.hpp"
#include "render_shapes.hpp"
@@ -703,7 +704,7 @@ void start_town_combat(eDirection direction) {
set_pc_moves();
pick_next_pc();
center = univ.current_pc().combat_pos;
draw_buttons(0);
UI::toolbar.draw(mainPtr);
put_pc_screen();
set_stat_window_for_pc(univ.cur_pc);
give_help(48,49);

176
src/game/boe.ui.cpp Normal file
View File

@@ -0,0 +1,176 @@
//
// boe.ui.cpp
// BoE
//
// Created by Celtic Minstrel on 20-01-27.
//
//
#include "boe.ui.hpp"
#include <numeric>
#include "enum_map.hpp"
#include "boe.consts.hpp"
#include "boe.locutils.hpp"
#include "boe.graphics.hpp"
#include "render_shapes.hpp"
#include "render_image.hpp"
#include "res_image.hpp"
#include "mathutil.hpp"
#include "sounds.hpp"
namespace UI {
cToolbar toolbar;
}
// The location of each UI area
enum_map(eGuiArea, rectangle) win_to_rects = {{7,19,358,298},{385,19,423,285},{7,305,123,576},{132,305,276,576},{360,19,381,298},{285,305,423,561}};
// Get the location of the button icon in buttons.png.
static location btn_pos(eToolbarButton icon) {
location pos;
pos.x = icon % 6;
pos.y = icon / 6;
return pos;
}
// The list of buttons for each mode
std::vector<eToolbarButton> cToolbar::out_buttons = {TOOLBAR_MAGE, TOOLBAR_PRIEST, TOOLBAR_LOOK, TOOLBAR_CAMP, TOOLBAR_SCROLL, TOOLBAR_SAVE, TOOLBAR_LOAD};
std::vector<eToolbarButton> cToolbar::town_buttons = {TOOLBAR_MAGE, TOOLBAR_PRIEST, TOOLBAR_LOOK, TOOLBAR_TALK, TOOLBAR_HAND, TOOLBAR_USE, TOOLBAR_MAP, TOOLBAR_SWORD};
std::vector<eToolbarButton> cToolbar::fight_buttons = {TOOLBAR_MAGE, TOOLBAR_PRIEST, TOOLBAR_LOOK, TOOLBAR_SHIELD, TOOLBAR_BAG, TOOLBAR_WAIT, TOOLBAR_SHOOT, TOOLBAR_END, TOOLBAR_ACT};
// Source rects for each button type in buttons.png.
static rectangle btn_src_rects[] = {{0,0,38,38}, {0,38,19,76}, {19,38,38,76}};
eToolbarButton cToolbar::button_hit(sf::RenderWindow& win, location click) {
rectangle dest_rect = win_to_rects[WINRECT_ACTBTNS];
click.x -= dest_rect.left;
click.y -= dest_rect.top;
if(click.in(total_rect)) {
for(int i = 0; i < toolbar.size(); i++) {
if(click.in(toolbar[i].bounds)) {
sf::Event e;
bool done = false, clicked = false;
win.setActive();
active = i;
while(!done){
redraw_screen(REFRESH_NONE);
while(win.pollEvent(e)) {
if(e.type == sf::Event::MouseButtonReleased){
done = true;
location clickPos(e.mouseButton.x, e.mouseButton.y);
clickPos = win.mapPixelToCoords(clickPos);
clickPos.x -= dest_rect.left;
clickPos.y -= dest_rect.top;
clicked = toolbar[i].bounds.contains(clickPos);
active = -1;
} else if(e.type == sf::Event::MouseMoved){
location toPos(e.mouseMove.x, e.mouseMove.y);
toPos = win.mapPixelToCoords(toPos);
toPos.x -= dest_rect.left;
toPos.y -= dest_rect.top;
active = toolbar[i].bounds.contains(toPos) ? i : -1;
}
}
}
play_sound(37, time_in_ticks(5));
redraw_screen(REFRESH_NONE);
if(clicked) return toolbar[i].btn;
}
}
}
return TOOLBAR_NONE;
}
cToolbar::eMode cToolbar::get_mode() {
if(is_combat()) return COMBAT;
if(is_town()) return TOWN;
return OUTDOORS;
}
void cToolbar::init() {
eMode mode = get_mode();
if(mode == cur_mode) return;
cur_mode = mode;
// Step 1: Select the correct toolbar
std::vector<eToolbarButton>* src;
switch(mode) {
case UNKNOWN: case OUTDOORS:
src = &out_buttons;
break;
case TOWN:
src = &town_buttons;
break;
case COMBAT:
src = &fight_buttons;
break;
}
// Step 2: calculate the positions of the toolbar buttons
toolbar.clear();
int offset = 0;
bool bottom_half = false;
for(eToolbarButton btn : *src) {
location slot = btn_pos(btn);
cButton btn_info;
btn_info.btn = btn;
if(slot.y == 2) {
// Small button (3rd row)
btn_info.type = bottom_half ? BTN_SM_LO : BTN_SM_HI;
btn_info.bounds = {0,0,19,38};
if(bottom_half) {
btn_info.bounds.offset(0,19);
bottom_half = false;
} else bottom_half = true;
} else {
// Large button (1st & 2nd rows)
btn_info.type = BTN_LG;
btn_info.bounds = {0,0,38,38};
}
btn_info.bounds.offset(offset, 0);
if(btn_info.type != BTN_SM_HI) offset += 38;
toolbar.push_back(btn_info);
}
// Step 3: draw the buttons into the cache
static bool inited = false;
if(!inited) {
inited = true;
cache.create(266,38);
cache.clear(sf::Color::Black);
}
sf::Texture& buttons_gworld = *ResMgr::graphics.get("buttons");
for(const auto& btn : toolbar) {
rectangle icon_rect = {0, 0, 32, 32}, to_rect = btn.bounds;
location slot = btn_pos(btn.btn);
if(btn.type != BTN_LG) icon_rect.bottom = 16;
icon_rect.offset(32 * slot.x, 38 + 32 * slot.y);
rect_draw_some_item(buttons_gworld, btn_src_rects[btn.type], cache, to_rect);
to_rect.inset(3,3);
if(btn.type != BTN_LG) to_rect.bottom += 3;
rect_draw_some_item(buttons_gworld, icon_rect, cache, to_rect, sf::BlendAlpha);
}
cache.display();
// Step 4: Calculate total bounds
if(toolbar.empty()) return;
total_rect = std::accumulate(toolbar.begin() + 1, toolbar.end(), toolbar[0].bounds, [](const rectangle& accum, const cButton& next) {
return rectunion(accum, next.bounds);
});
}
void cToolbar::draw(sf::RenderTarget& targ) {
init();
// Determine actual rect
rectangle to = win_to_rects[WINRECT_ACTBTNS];
to.width() = total_rect.width();
// Prepare background for additive blending
fill_rect(targ, to, sf::Color::Black);
if(active >= 0 && active < toolbar.size()) {
rectangle press_rect = toolbar[active].bounds;
press_rect.offset(win_to_rects[WINRECT_ACTBTNS].topLeft());
fill_rect(targ, press_rect, sf::Color::Blue);
}
// Add the cached toolbar over the background
rect_draw_some_item(cache.getTexture(), rectangle(cache), targ, to, sf::BlendAdd);
}

50
src/game/boe.ui.hpp Normal file
View File

@@ -0,0 +1,50 @@
//
// boe.ui.hpp
// BoE
//
// Created by Celtic Minstrel on 20-01-27.
//
//
#ifndef BOE_GAME_UI_H
#define BOE_GAME_UI_H
#include <vector>
#include "location.hpp"
// These correspond to the icons in buttons.png
enum eToolbarButton {
TOOLBAR_MAGE, TOOLBAR_PRIEST, TOOLBAR_LOOK, TOOLBAR_CAMP, TOOLBAR_SCROLL, TOOLBAR_SAVE,
TOOLBAR_SHIELD, TOOLBAR_BAG, TOOLBAR_TALK, TOOLBAR_HAND, TOOLBAR_SWORD, TOOLBAR_LOAD,
TOOLBAR_WAIT, TOOLBAR_END, TOOLBAR_SHOOT, TOOLBAR_ACT, TOOLBAR_USE, TOOLBAR_MAP,
TOOLBAR_NONE = -1 // Keep this last
};
class cToolbar {
// These correspond to the button frames in buttons.png (top row)
enum eButtonType {BTN_LG, BTN_SM_HI, BTN_SM_LO};
static std::vector<eToolbarButton> out_buttons, town_buttons, fight_buttons;
struct cButton {
eToolbarButton btn;
eButtonType type;
rectangle bounds;
};
sf::RenderTexture cache;
std::vector<cButton> toolbar;
rectangle total_rect;
enum eMode {UNKNOWN, OUTDOORS, TOWN, COMBAT};
eMode cur_mode = UNKNOWN, get_mode();
int active = -1;
void init();
public:
void draw(sf::RenderTarget& targ);
eToolbarButton button_hit(sf::RenderWindow& win, location click);
};
namespace UI {
extern cToolbar toolbar;
}
#endif /* defined(BOE_GAME_UI_H) */