diff --git a/osx/boe.actions.cpp b/osx/boe.actions.cpp index 5d50a688..c6689caf 100644 --- a/osx/boe.actions.cpp +++ b/osx/boe.actions.cpp @@ -79,11 +79,6 @@ short priest_need_select[62] = { // 0 - no select 1 - active only 2 - any existing // TODO: The duplication of RECT here shouldn't be necessary... -word_rect_type preset_words[9] = { - {"Look",RECT{366,4,386,54}},{"Name",RECT{366,70,386,130}},{"Job",RECT{366,136,386,186}}, - {"Buy",RECT{389,4,409,54}},{"Sell",RECT{389,70,409,120}},{"Record",RECT{389,121,409,186}}, - {"Done",RECT{389,210,409,270}},{"Go Back",RECT{366,190,386,270}}, - {"Ask About...",RECT{343,4,363,134}}}; cItemRec start_items[6] = {cItemRec('nife'),cItemRec('buck'),cItemRec('bow '),cItemRec('arrw'),cItemRec('pole'),cItemRec('helm')}; bool item_area_button_active[8][6]; bool pc_area_button_active[6][5]; @@ -123,6 +118,8 @@ extern short spec_item_array[60]; extern cScenario scenario; extern cUniverse univ; //extern piles_of_stuff_dumping_type *data_store; +extern std::vector talk_words; +extern bool talk_end_forced; // combat globals @@ -1588,9 +1585,10 @@ bool handle_keystroke(sf::Event& event){ if (chr2 == kb::Space) chr2 = kb::G; for (i = 0; i < 9; i++) - if (chr2 == talk_chars[i]) { - pass_point.x = preset_words[i].word_rect.left + 9 + ul.x; - pass_point.y = preset_words[i].word_rect.top + 9 + ul.y; + if (chr2 == talk_chars[i] && (!talk_end_forced || i == 6 || i == 5)) { + int j = talk_end_forced ? i - 5 : i; + pass_point.x = talk_words[i].rect.left + 9 + ul.x; + pass_point.y = talk_words[i].rect.top + 9 + ul.y; pass_event.mouseButton.x = pass_point.x; pass_event.mouseButton.y = pass_point.y; are_done = handle_action(pass_event); diff --git a/osx/boe.dlgutil.cpp b/osx/boe.dlgutil.cpp index 96ea3d07..b6d6cf3f 100644 --- a/osx/boe.dlgutil.cpp +++ b/osx/boe.dlgutil.cpp @@ -69,13 +69,10 @@ char null_string[256] = ""; short store_tip_page_on = 0; // Talking vars -word_rect_type store_words[50]; eGameMode store_pre_talk_mode; short store_personality,store_personality_graphic,shop_identify_cost; sf::RenderTexture talk_gworld; bool talk_end_forced; -std::string old_str1, old_str2, one_back1, one_back2; -extern word_rect_type preset_words[9]; RECT talk_area_rect = {5,5,420,284}, word_place_rect = {44,7,372,257},talk_help_rect = {5,254,21,272}; std::string title_string; m_num_t store_monst_type; @@ -84,6 +81,8 @@ RECT dummy_rect = {0,0,0,0}; //hold_responses store_resp[83]; short strnum1,strnum2,oldstrnum1,oldstrnum2; short store_talk_face_pic;//,cur_town_talk_loaded = -1; +int current_talk_node; +extern std::vector talk_words; // Shopping vars @@ -161,19 +160,25 @@ void start_shop_mode(short shop_type,short shop_min,short shop_max,short cost_ad give_help(26,27); } +void update_last_talk(int new_node) { + // Store last node in the Go Back button + for(word_rect_t& word : talk_words) { + if(word.word != "Go Back") continue; + word.node = current_talk_node; + current_talk_node = new_node; + break; + } +} + void end_shop_mode() { RECT dummy_rect = {0,0,0,0}; shop_sbar->hide(); if (store_pre_shop_mode == 20) { - old_str1 = "You conclude your business."; - old_str2 = ""; - one_back1 = "You conclude your business."; - one_back2 = ""; - strnum1 = strnum2 = oldstrnum1 = oldstrnum2 = 0; - place_talk_str(old_str1, "", 0, dummy_rect); + place_talk_str("You conclude your business.", "", 0, dummy_rect); + update_last_talk(TALK_BUSINESS); } overall_mode = store_pre_shop_mode; @@ -536,16 +541,13 @@ void start_talk_mode(short m_num,short personality,m_num_t monst_type,short stor overall_mode = MODE_TALKING; talk_end_forced = false; stat_screen_mode = 1; + current_talk_node = TALK_LOOK; // Bring up and place first strings. place_string1 = univ.town.cur_talk().talk_strs[personality % 10 + 10]; strnum1 = personality % 10 + 10; strnum2 = 0; - old_str1 = place_string1; - old_str2 = ""; - one_back1 = place_string1; - one_back2 = ""; place_talk_str(place_string1, "", 0, dummy_rect); put_item_screen(stat_window,0); @@ -572,11 +574,11 @@ void end_talk_mode() void handle_talk_event(location p) { - short i,j,force_special = 0,get_pc,s1 = -1,s2 = -1,s3 = -1; + short i,j,get_pc,s1 = -1,s2 = -1,s3 = -1; char asked[4]; std::string place_string1, place_string2; - short a,b,c,d,ttype,which_talk_entry = -1; + short a,b,c,d,ttype; p.x -= 5; p.y -= 5; @@ -587,151 +589,103 @@ void handle_talk_event(location p) return; } - for (i = 0; i < 9; i++) - if ((p.in(preset_words[i].word_rect)) && ((talk_end_forced == false) || (i == 6) || (i == 5))) { - click_talk_rect(old_str1,old_str2,preset_words[i].word_rect); - switch (i) { - case 0: case 1: case 2: case 7: case 8: - force_special = i + 1; - break; - case 3: case 4: - force_special = i + 1; - - //asked[0] = 'p';asked[1] = 'u';asked[2] = 'r';asked[3] = 'c'; - break; - case 5: // save - if (strnum1 <= 0) { - // TODO: Play an error sound here - return; - } - if(univ.party.has_talk_save(store_personality, univ.town.num, strnum1, strnum2)){ - ASB("This is already saved."); - print_buf(); - return; - } else { - give_help(57,0); - play_sound(0); - bool success = univ.party.save_talk(store_personality,univ.town.num,strnum1,strnum2); - if(success){ - ASB("Noted in journal."); - } else { - ASB("No more room in talking journal."); - } - } - print_buf(); - return; - break; - case 6: // quit - end_talk_mode(); - return; - break; - default: - for (j = 0; j < 4; j++) - asked[j] = preset_words[i].word[j]; - break; - } - i = 100; - } - if (i < 100) { - for (i = 0; i < 50; i++) - if ((p.in(store_words[i].word_rect)) && (talk_end_forced == false)) { - click_talk_rect(old_str1,old_str2,store_words[i].word_rect); - for (j = 0; j < 4; j++) - asked[j] = store_words[i].word[j]; - - i = 100; - } + int which_talk_entry = TALK_DUNNO; + for(word_rect_t& word : talk_words) { + if(word.node == -1) continue; + if(!p.in(word.rect)) continue; + click_talk_rect(word); + which_talk_entry = word.node; + break; } - if (i == 50) // no event + if(which_talk_entry == TALK_DUNNO) return; - if (force_special == 9) { - place_string1 = get_text_response(1017,0); - asked[0] = place_string1[0]; - asked[1] = place_string1[1]; - asked[2] = place_string1[2]; - asked[3] = place_string1[3]; - } - if ((asked[0] == 'n') && (asked[1] == 'a') &&(asked[2] == 'm') &&(asked[3] == 'e')) { - force_special = 2; - } - else if ((asked[0] == 'l') && (asked[1] == 'o') &&(asked[2] == 'o') &&(asked[3] == 'k')) { - force_special = 1; - } - else if (((asked[0] == 'j') && (asked[1] == 'o') &&(asked[2] == 'b')) || - ((asked[0] == 'w') && (asked[1] == 'o') &&(asked[2] == 'r')&&(asked[3] == 'k')) ) { - force_special = 3; - } - else if (((asked[0] == 'b') && (asked[1] == 'u') &&(asked[2] == 'y'))) { - force_special = 3; - } - else if (((asked[0] == 'b') && (asked[1] == 'y') &&(asked[2] == 'e'))) { - end_talk_mode(); - } - - if (force_special > 0) { - switch (force_special) { - case 1: case 2: case 3: - place_string1 = univ.town.cur_talk().talk_strs[store_personality % 10 + 10 * force_special]; - - oldstrnum1 = strnum1; oldstrnum2 = strnum2; - strnum1 = store_personality % 10 + 10 * force_special; - strnum2 = 0; - one_back1 = old_str1; - one_back2 = old_str2; - old_str1 = place_string1; - old_str2 = place_string2; - place_talk_str(place_string1,place_string2,0,dummy_rect); + switch(which_talk_entry) { + case TALK_DUNNO: + SPECIAL_DUNNO: + place_string1 = univ.town.cur_talk().talk_strs[store_personality % 10 + 160]; + if(place_string1.length() < 2) place_string1 = "You get no response."; + place_talk_str(place_string1, "", 0, dummy_rect); + update_last_talk(TALK_DUNNO); + return; + case TALK_BUSINESS: // This one only reachable via "go back". + place_talk_str("You conclude your business.", "", 0, dummy_rect); + update_last_talk(TALK_BUSINESS); + return; + case TALK_LOOK: + SPECIAL_LOOK: + place_string1 = univ.town.cur_talk().talk_strs[store_personality % 10 + 10]; + place_talk_str(place_string1, "", 0, dummy_rect); + update_last_talk(TALK_LOOK); + return; + case TALK_NAME: + SPECIAL_NAME: + place_string1 = univ.town.cur_talk().talk_strs[store_personality % 10 + 20]; + place_talk_str(place_string1, "", 0, dummy_rect); + update_last_talk(TALK_NAME); + return; + case TALK_JOB: + SPECIAL_JOB: + place_string1 = univ.town.cur_talk().talk_strs[store_personality % 10 + 30]; + place_talk_str(place_string1, "", 0, dummy_rect); + update_last_talk(TALK_JOB); + return; + case TALK_BUY: + SPECIAL_BUY: + which_talk_entry = scan_for_response("purc"); + if(which_talk_entry < 0) which_talk_entry = scan_for_response("sale"); + if(which_talk_entry < 0) which_talk_entry = scan_for_response("heal"); + if(which_talk_entry < 0) which_talk_entry = scan_for_response("iden"); + if(which_talk_entry < 0) which_talk_entry = scan_for_response("trai"); + if(which_talk_entry == -1) goto SPECIAL_DUNNO; + break; + case TALK_SELL: + SPECIAL_SELL: + which_talk_entry = scan_for_response("sell"); + if(which_talk_entry == -1) goto SPECIAL_DUNNO; + break; + case TALK_RECORD: + if (strnum1 <= 0) { + // TODO: Play an error sound here return; - break; - case 4: // buy button - asked[0] = 'p';asked[1] = 'u';asked[2] = 'r';asked[3] = 'c'; - if (scan_for_response(asked) >= 0) - break; - asked[0] = 's';asked[1] = 'a';asked[2] = 'l';asked[3] = 'e'; - if (scan_for_response(asked) >= 0) - break; - asked[0] = 'h';asked[1] = 'e';asked[2] = 'a';asked[3] = 'l'; - if (scan_for_response(asked) >= 0) - break; - asked[0] = 'i';asked[1] = 'd';asked[2] = 'e';asked[3] = 'n'; - if (scan_for_response(asked) >= 0) - break; - asked[0] = 't';asked[1] = 'r';asked[2] = 'a';asked[3] = 'i'; - if (scan_for_response(asked) >= 0) - break; - break; - case 5: // sell button - asked[0] = 's';asked[1] = 'e';asked[2] = 'l';asked[3] = 'l'; - if (scan_for_response(asked) >= 0) - break; - break; - case 8: // back 1 - strnum1 = oldstrnum1; strnum2 = oldstrnum2; - place_string1 = one_back1; - place_string2 = one_back2; - one_back1 = old_str1; - one_back2 = old_str2; - old_str1 = place_string1; - old_str2 = place_string2; - place_talk_str(place_string1,place_string2,0,dummy_rect); + } + if(univ.party.has_talk_save(store_personality, univ.town.num, strnum1, strnum2)){ + ASB("This is already saved."); + print_buf(); return; - break; - } - } - - which_talk_entry = scan_for_response(asked); - if ((which_talk_entry < 0) || (which_talk_entry > 59)) { - one_back1 = old_str1; - one_back2 = old_str2; - old_str2 = ""; - old_str1 = univ.town.cur_talk().talk_strs[store_personality % 10 + 160]; - if(old_str1.length() < 2) - old_str1 = "You get no response."; - place_talk_str(old_str1,old_str2,0,dummy_rect); - strnum1 = -1; - return; + } else { + give_help(57,0); + play_sound(0); + bool success = univ.party.save_talk(store_personality,univ.town.num,strnum1,strnum2); + if(success){ + ASB("Noted in journal."); + } else { + ASB("No more room in talking journal."); + } + } + print_buf(); + return; + case TALK_DONE: + SPECIAL_DONE: + end_talk_mode(); + return; + case TALK_BACK: // only if there's nothing to go back to + return; // so, there's nothing to do here + case TALK_ASK: // ask about + place_string1 = get_text_response(1017,0); + strncpy(asked, place_string1.c_str(), 4); + if(strncmp(asked, "name", 4) == 0) goto SPECIAL_NAME; + if(strncmp(asked, "look", 4) == 0) goto SPECIAL_LOOK; + if(strncmp(asked, "job", 3) == 0) goto SPECIAL_JOB; + if(strncmp(asked, "work", 4) == 0) goto SPECIAL_JOB; + if(strncmp(asked, "bye", 3) == 0) goto SPECIAL_DONE; + if(strncmp(asked, "buy", 3) == 0) goto SPECIAL_BUY; + if(strncmp(asked, "sell", 4) == 0) goto SPECIAL_SELL; + which_talk_entry = scan_for_response(asked); + if(which_talk_entry == -1) goto SPECIAL_DUNNO; + break; } + update_last_talk(which_talk_entry); ttype = univ.town.cur_talk().talk_nodes[which_talk_entry].type; a = univ.town.cur_talk().talk_nodes[which_talk_entry].extras[0]; @@ -994,6 +948,7 @@ void handle_talk_event(location p) PSD[univ.town.monst[store_m_num].spec1][univ.town.monst[store_m_num].spec2] = 1; talk_end_forced = true; break; + // TODO: Strings resulting from this don't seem to be recordable; whyever not? case 29: // town special run_special(7,2,a,univ.town.p_loc,&s1,&s2,&s3); // check s1 & s2 to see if we got diff str, and, if so, munch old strs @@ -1004,8 +959,6 @@ void handle_talk_event(location p) place_string2 = ""; } get_strs(place_string1,place_string2,2,s1,s2); - //strnum1 = -1; - //strnum2 = -1; if (s1 >= 0) strnum1 = 2000 + s1; if (s2 >= 0) strnum2 = 2000 + s2; put_pc_screen(); @@ -1021,24 +974,14 @@ void handle_talk_event(location p) place_string2 = ""; } get_strs(place_string1,place_string2,0,s1,s2); - //strnum1 = -1; - //strnum2 = -1; if (s1 >= 0) strnum1 = 3000 + s1; if (s2 >= 0) strnum2 = 3000 + s2; put_pc_screen(); put_item_screen(stat_window,0); break; - - - } - one_back1 = old_str1; - one_back2 = old_str2; - old_str1 = place_string1; - old_str2 = place_string2; - place_talk_str(old_str1,old_str2,0,dummy_rect); - + place_talk_str(place_string1,place_string2,0,dummy_rect); } void store_responses() diff --git a/osx/boe.newgraph.cpp b/osx/boe.newgraph.cpp index aee5ab40..acfe642c 100644 --- a/osx/boe.newgraph.cpp +++ b/osx/boe.newgraph.cpp @@ -1,6 +1,7 @@ #include #include +#include //#include "item.h" @@ -79,17 +80,15 @@ extern cUniverse univ; //extern talking_record_type talking; // Talk vars -extern word_rect_type store_words[50]; extern eGameMode store_pre_talk_mode; extern short store_personality,store_personality_graphic,current_pc; extern sf::RenderTexture talk_gworld; extern bool talk_end_forced; extern std::string old_str1,old_str2,one_back1,one_back2; -extern word_rect_type preset_words[9]; extern RECT talk_area_rect, word_place_rect,talk_help_rect; extern std::string title_string; extern m_num_t store_monst_type; -//extern hold_responses store_resp[83]; +std::vector talk_words; // Shop vars extern short store_shop_items[30],store_shop_costs[30]; @@ -944,15 +943,22 @@ void refresh_shopping() } } -void click_talk_rect(std::string str_to_place,std::string str_to_place2,RECT c_rect) -{ - - place_talk_str(str_to_place,str_to_place2,1,c_rect); +void click_talk_rect(word_rect_t word) { + RECT talkRect(talk_gworld), wordRect(word.rect); + mainPtr.setActive(); + rect_draw_some_item(talk_gworld.getTexture(),talkRect,talk_area_rect,ul); + wordRect.offset(talk_area_rect.topLeft()); + wordRect.offset(ul); + TextStyle style; + style.font = FONT_DUNGEON; + style.pointSize = 18; + style.lineHeight = 18; + style.colour = word.on; + win_draw_string(mainPtr, wordRect, word.word, eTextMode::LEFT_TOP, style); mainPtr.display(); - if (play_sounds == true) - play_sound(37); + if(play_sounds) play_sound(37); else sf::sleep(time_in_ticks(5)); - place_talk_str(str_to_place,str_to_place2,0,c_rect); + rect_draw_some_item(talk_gworld.getTexture(),talkRect,talk_area_rect,ul); } //// @@ -1073,7 +1079,6 @@ void place_talk_str(std::string str_to_place,std::string str_to_place2,short col RECT face_rect = {6,6,38,38}; RECT title_rect = {19,48,42,260}; RECT dest_rect,help_from = {85,36,101,54}; - std::string str; sf::Text str_to_draw; short i,j,str_len,line_height = 17; @@ -1084,8 +1089,8 @@ void place_talk_str(std::string str_to_place,std::string str_to_place2,short col // In the 0..65535 range, these blue components were: 0, 32767, 14535, 26623, 59391 // The green components on the second line were 40959 and 24575 // TODO: The duplication of sf::Color here shouldn't be necessary... - sf::Color c[7] = {sf::Color{0,0,0},sf::Color{0,0,128},sf::Color{0,0,57},sf::Color{0,0,104},sf::Color{0,0,232}, - sf::Color{0,160,0},sf::Color{0,96,0}}; + sf::Color c[8] = {sf::Color{0,0,0},sf::Color{0,0,128},sf::Color{0,0,57},sf::Color{0,0,104},sf::Color{0,0,232}, + sf::Color{0,160,0},sf::Color{0,96,0},sf::Color(160,0,20)}; talk_gworld.setActive(); @@ -1148,169 +1153,81 @@ void place_talk_str(std::string str_to_place,std::string str_to_place2,short col style.colour = c[4]; win_draw_string(talk_gworld,dest_rect,title_string,eTextMode::LEFT_TOP,style); + talk_words.clear(); + static const RECT preset_rects[9] = { + RECT{366,4,386,54}, RECT{366,70,386,130}, RECT{366,136,386,186}, + RECT{389,4,409,54}, RECT{389,70,409,120}, RECT{389,121,409,186}, + RECT{389,210,409,270}, RECT{366,190,386,270}, + RECT{343,4,363,134}, + }; + static const char*const preset_words[9] = { + "Look", "Name", "Job", + "Buy", "Sell", "Record", + "Done", "Go Back", + "Ask About...", + }; + // Place buttons at bottom. if (color == 0) style.colour = c[5]; else style.colour = c[6]; - for (i = 0; i < 9; i++) - if ((talk_end_forced == false) || (i == 6) || (i == 5)) { - preset_words[i].word_rect.offset(0,8); - win_draw_string(talk_gworld,preset_words[i].word_rect,preset_words[i].word,eTextMode::LEFT_TOP,style); - preset_words[i].word_rect.offset(0,-8); + for(i = 0; i < 9; i++) { + if(!talk_end_forced || i == 6 || i == 5) { + word_rect_t preset_word(preset_words[i], preset_rects[i]); + preset_word.on = c[5]; + preset_word.off = c[6]; + switch(i) { + case 0: preset_word.node = TALK_LOOK; break; + case 1: preset_word.node = TALK_NAME; break; + case 2: preset_word.node = TALK_JOB; break; + case 3: preset_word.node = TALK_BUY; break; + case 4: preset_word.node = TALK_SELL; break; + case 5: preset_word.node = TALK_RECORD; break; + case 6: preset_word.node = TALK_DONE; break; + case 7: preset_word.node = TALK_BACK; break; + case 8: preset_word.node = TALK_ASK; break; + } + talk_words.push_back(preset_word); + RECT draw_rect = preset_word.rect; + win_draw_string(talk_gworld,draw_rect,preset_word.word,eTextMode::LEFT_TOP,style); } - // Place bulk of what said. Save words. - //TextSize(14); - if (color == 0) - for (i = 0; i < 50; i++) - store_words[i].word_rect.left = store_words[i].word_rect.right = 0; - - auto text_len = [&str_to_draw](size_t i) -> int { - return str_to_draw.findCharacterPos(i).x; - }; - - str_len = str_to_place.length(); - if (str_len == 0) { - str_to_place = "."; } - str = str_to_place; - str_to_draw.setString(str); dest_rect = word_place_rect; - - current_rect = 0; - - if (color == 0) - style.colour = c[2]; - else style.colour = c[1]; - style.lineHeight = line_height; - style.applyTo(str_to_draw); - // TODO: The entire text flashes when clicking anything - win_draw_string(talk_gworld, dest_rect, str, eTextMode::WRAP, style); - for (i = 0;i < str_len;i++) { - if (((str[i] != 39) && ((str[i] < 65) || (str[i] > 122)) && ((str[i] < 48) || (str[i] > 57))) && (color == 0)) { // New word, so set up a rect - if (((i - store_last_word_break >= 4) || (i >= str_len - 1)) - && (i - last_stored_word_break >= 4) && (talk_end_forced == false)) { - store_words[current_rect].word_rect.left = dest_rect.left + (text_len(store_last_word_break) - text_len(last_line_break)) - 2; - store_words[current_rect].word_rect.right = dest_rect.left + (text_len(i + 1) - text_len(last_line_break)) - 1; - store_words[current_rect].word_rect.top = dest_rect.top + 1 + line_height * on_what_line - 5; - store_words[current_rect].word_rect.bottom = dest_rect.top + 1 + line_height * on_what_line + 13; - - if ((str[store_last_word_break] < 48) || (str[store_last_word_break] == 96) - || (str[store_last_word_break] > 122) - || ((str[store_last_word_break] >= 58) && (str[store_last_word_break] <= 64))) - store_last_word_break++; - - // adjust for if this word will be scrolled down - //if (((text_len[i] - text_len[last_line_break] > (dest_rect.right - dest_rect.left - 6)) - // && (last_word_break > last_line_break)) || (str[i] == '|')) { - // OffsetRect(&store_words[current_rect].word_rect,5 + -1 * store_words[current_rect].word_rect.left,line_height); - //} - - store_words[current_rect].word[0] = str[store_last_word_break]; - store_words[current_rect].word[1] = str[store_last_word_break + 1]; - store_words[current_rect].word[2] = str[store_last_word_break + 2]; - store_words[current_rect].word[3] = str[store_last_word_break + 3]; - store_words[current_rect].word[4] = 0; - for (j = 0; j < 4; j++) - if ((store_words[current_rect].word[j] >= 65) && (store_words[current_rect].word[j] <= 90)) - store_words[current_rect].word[j] += 32; - if (scan_for_response(store_words[current_rect].word) < 0) { - store_words[current_rect].word_rect.left = store_words[current_rect].word_rect.right = 0; - } - else { - start_of_last_kept_word = store_last_word_break; - if (current_rect < 49) - current_rect++; - - // TODO: Debug line, remove - frame_rect(talk_gworld, store_words[current_rect].word_rect, sf::Color::Red); - } - last_stored_word_break = i + 1; + style.colour = c[2]; + // First determine the offsets of clickable words. + // The added spaces ensure that end-of-word boundaries are found + std::string str = str_to_place + " |" + str_to_place2 + " "; + std::vector hilites; + std::vector nodes; + int wordStart = 0, wordEnd = 0; + bool inClickable = false; + for(size_t i = 0; i < str.length(); i++) { + char c = str[i]; + if(isalpha(c) || c == '-' || c == '\'') { + if(wordStart <= wordEnd) wordStart = i; + } else if(wordEnd <= wordStart) { + wordEnd = i; + short node = scan_for_response(str.c_str() + wordStart); + if(node >= 0) { + nodes.push_back(node); + hilites.push_back({wordStart, wordEnd}); } } - if (((text_len(i) - text_len(last_line_break) > (dest_rect.right - dest_rect.left - 6)) - && (last_word_break > last_line_break)) || (str[i] == '|') || (i == str_len - 1)) { - if (str[i] == '|') { - str[i] = ' '; - last_word_break = i + 1; - } - store_last_word_break = last_word_break; - last_line_break = last_word_break; - if ((start_of_last_kept_word >= last_line_break) && (current_rect > 0)) { - // TODO: Should we play an error sound here? - store_words[current_rect - 1].word_rect.offset(5 - store_words[current_rect - 1].word_rect.left,line_height); - } - } - if (str[i] == ' ') { // New word - store_last_word_break = last_word_break = i + 1; - } } - // Now for string 2 - str_len = str_to_place2.length(); - start_of_last_kept_word = -1; + std::vector word_rects = draw_string_hilite(talk_gworld, dest_rect, str, style, hilites, color ? c[1] : c[7]); - if (str_len > 0) { - - str = str_to_place2; - str_to_draw.setString(str); - - last_line_break = store_last_word_break = last_word_break = last_stored_word_break = 0; - win_draw_string(talk_gworld, dest_rect, str, eTextMode::WRAP, style); - for (i = 0;i < str_len;i++) { - if (((str[i] != 39) && ((str[i] < 65) || (str[i] > 122)) && ((str[i] < 48) || (str[i] > 57))) && (color == 0)) { // New word, so set up a rect - if (((i - store_last_word_break >= 4) || (i >= str_len - 1)) - && (i - last_stored_word_break >= 4) && (talk_end_forced == false)) { - store_words[current_rect].word_rect.left = dest_rect.left + (text_len(store_last_word_break) - text_len(last_line_break)) - 2; - store_words[current_rect].word_rect.right = dest_rect.left + (text_len(i + 1) - text_len(last_line_break)) - 1; - store_words[current_rect].word_rect.top = dest_rect.top + 1 + line_height * on_what_line - 5; - store_words[current_rect].word_rect.bottom = dest_rect.top + 1 + line_height * on_what_line + 13; - - if ((str[store_last_word_break] < 48) || (str[store_last_word_break] == 96) - || (str[store_last_word_break] > 122) - || ((str[store_last_word_break] >= 58) && (str[store_last_word_break] <= 64))) - store_last_word_break++; - - // adjust for if this word will be scrolled down - //if (((text_len[i] - text_len[last_line_break] > (dest_rect.right - dest_rect.left - 6)) - // && (last_word_break > last_line_break)) || (str[i] == '|')) { - // OffsetRect(&store_words[current_rect].word_rect,5 + -1 * store_words[current_rect].word_rect.left,line_height); - //} - store_words[current_rect].word[0] = str[store_last_word_break]; - store_words[current_rect].word[1] = str[store_last_word_break + 1]; - store_words[current_rect].word[2] = str[store_last_word_break + 2]; - store_words[current_rect].word[3] = str[store_last_word_break + 3]; - store_words[current_rect].word[4] = 0; - for (j = 0; j < 4; j++) - if ((store_words[current_rect].word[j] >= 65) && (store_words[current_rect].word[j] <= 90)) - store_words[current_rect].word[j] += 32; - if (scan_for_response(store_words[current_rect].word) < 0) - store_words[current_rect].word_rect.left = store_words[current_rect].word_rect.right = 0; - else { - start_of_last_kept_word = store_last_word_break; - if (current_rect < 49) - current_rect++; - - //FrameRect(&store_words[current_rect].word_rect); - } - last_stored_word_break = i + 1; - } - } - if (((text_len(i) - text_len(last_line_break) > (dest_rect.right - dest_rect.left - 6)) - && (last_word_break > last_line_break)) || (str[i] == '|') || (i == str_len - 1)) { - if (str[i] == '|') { - str[i] = ' '; - last_word_break = i + 1; - } - store_last_word_break = last_word_break; - last_line_break = last_word_break; - if ((start_of_last_kept_word >= last_line_break) && (current_rect > 0)) { - store_words[current_rect - 1].word_rect.offset(5 + -1 * store_words[current_rect - 1].word_rect.left,line_height); - } - } - if (str[i] == ' ') { // New word - store_last_word_break = last_word_break = i + 1; - } + if(!talk_end_forced) { + // Now build the list of word rects + for(size_t i = 0; i < hilites.size(); i++) { + word_rect_t thisRect; + thisRect.word = str.substr(hilites[i].first, hilites[i].second - hilites[i].first); + thisRect.rect = word_rects[i]; + thisRect.node = nodes[i]; + thisRect.on = c[1]; + thisRect.off = c[2]; // Note: "off" is never used + talk_words.push_back(thisRect); } } @@ -1321,13 +1238,6 @@ void place_talk_str(std::string str_to_place,std::string str_to_place2,short col // Finally place processed graphics mainPtr.setActive(); rect_draw_some_item(talk_gworld.getTexture(),oldRect,talk_area_rect,ul); - - // Clean up strings - for (i = 0; i < 50; i++) - for (j = 0; j < 4; j++) - if ((store_words[current_rect].word[j] >= 65) && (store_words[current_rect].word[j] <= 90)) - store_words[current_rect].word[j] += 32; - } void refresh_talking() @@ -1336,7 +1246,7 @@ void refresh_talking() rect_draw_some_item(talk_gworld.getTexture(),tempRect,talk_area_rect,ul); } -short scan_for_response(char *str) { +short scan_for_response(const char *str) { cSpeech talk = univ.town.cur_talk(); for(short i = 0; i < 60; i++) { // 60 response in each bunch cSpeech::cNode node = talk.talk_nodes[i]; diff --git a/osx/boe.newgraph.h b/osx/boe.newgraph.h index b4d797b3..84d77d01 100644 --- a/osx/boe.newgraph.h +++ b/osx/boe.newgraph.h @@ -1,13 +1,32 @@ -typedef struct { - char word[15]; - RECT word_rect; - } word_rect_type; -typedef struct { - short per1,per2; - char stra[5],strb[5]; - short ttype,a,b,c,d; - } hold_responses; - + +#include +#include +#include "location.h" +#include "item.h" + +struct word_rect_t { + std::string word; + RECT rect; + int node = -1; + sf::Color on, off; + word_rect_t(std::string w, RECT r) : word(w), rect(r) {} + word_rect_t() {} +}; + +enum { + TALK_DUNNO = -1, + TALK_BUY = -2, + TALK_SELL = -3, + TALK_BUSINESS = -4, + TALK_LOOK = -10, + TALK_NAME = -11, + TALK_JOB = -12, + TALK_RECORD = -13, + TALK_DONE = -14, + TALK_BACK = -15, + TALK_ASK = -16, +}; + void apply_unseen_mask(); void apply_light_mask(bool onWindow); void end_anim(); @@ -43,8 +62,8 @@ cItemRec store_mage_spells(short which_s) ; cItemRec store_priest_spells(short which_s); cItemRec store_alchemy(short which_s); void get_item_interesting_string(cItemRec item,char *message); -void click_talk_rect(std::string str_to_place,std::string str_to_place2,RECT c_rect); +void click_talk_rect(word_rect_t word); void place_talk_str(std::string str_to_place,std::string str_to_place2,short color,RECT c_rect); -short scan_for_response(char *str); +short scan_for_response(const char *str); void refresh_talking(); void draw_dialog_graphic(sf::RenderTarget& target, RECT rect, short which_g, short type_g, bool do_frame); diff --git a/osx/tools/graphtool.cpp b/osx/tools/graphtool.cpp index 36fe2816..f0c39611 100644 --- a/osx/tools/graphtool.cpp +++ b/osx/tools/graphtool.cpp @@ -197,14 +197,20 @@ struct text_params_t { TextStyle style; eTextMode mode; 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} returnType; + std::vector returnRects; }; struct snippet_t { std::string text; location at; + bool hilited; }; -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,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; @@ -214,12 +220,57 @@ void win_draw_string(sf::RenderTarget& dest_window,RECT dest_rect,std::string st win_draw_string(dest_window, dest_rect, str, params); } -void win_draw_string(sf::RenderTarget& dest_window,RECT dest_rect,std::string str,text_params_t options) { +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; +} + +void push_snippets(size_t start, size_t end, text_params_t& options, std::vector& snippets, size_t& iHilite, const std::string& str, location loc) { + std::vector& hilites = options.hilite_ranges; + // 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; + 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) { short line_height = options.style.lineHeight; sf::Text str_to_draw; options.style.applyTo(str_to_draw); short str_len,i; - short last_line_break = 0,last_word_break = 0; + unsigned short last_line_break = 0,last_word_break = 0; short total_width = 0; short adjust_x = 0,adjust_y = 0; @@ -245,9 +296,12 @@ void win_draw_string(sf::RenderTarget& dest_window,RECT dest_rect,std::string st return str_to_draw.findCharacterPos(i).x; }; + // Special stuff + size_t iHilite = 0; + location moveTo; std::vector snippets; - line_height -= 2; + 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); @@ -258,8 +312,7 @@ void win_draw_string(sf::RenderTarget& dest_window,RECT dest_rect,std::string st str[i] = ' '; last_word_break = i + 1; } - size_t amount = last_word_break - last_line_break - 1; - snippets.push_back({str.substr(last_line_break,amount), moveTo}); + push_snippets(last_line_break, last_word_break, options, snippets, iHilite, str, moveTo); moveTo.y += line_height; last_line_break = last_word_break; } @@ -270,7 +323,7 @@ void win_draw_string(sf::RenderTarget& dest_window,RECT dest_rect,std::string st if(i - last_line_break > 1) { std::string snippet = str.substr(last_line_break); if(snippet.size() > 2) - snippets.push_back({snippet, moveTo}); + push_snippets(last_line_break, str.length() + 1, options, snippets, iHilite, str, moveTo); } } else { switch(mode) { @@ -287,12 +340,23 @@ void win_draw_string(sf::RenderTarget& dest_window,RECT dest_rect,std::string st case eTextMode::WRAP: break; // Never happens, but put this here to silence warning } - snippets.push_back({str, moveTo}); + push_snippets(0, str.length() + 1, options, snippets, iHilite, str, moveTo); } for(auto& snippet : 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); + fill_rect(dest_window, bounds, options.hilite_bg); + } else str_to_draw.setColor(options.style.colour); dest_window.draw(str_to_draw); } } diff --git a/osx/tools/graphtool.h b/osx/tools/graphtool.h index 17330bf6..6e53923b 100644 --- a/osx/tools/graphtool.h +++ b/osx/tools/graphtool.h @@ -57,6 +57,7 @@ typedef unsigned short pic_num_t; static const pic_num_t NO_PIC = std::numeric_limits::max(); using graf_pos = std::pair; using graf_pos_ref = std::pair; +using hilite_t = std::pair; struct cCustomGraphics { size_t numSheets; @@ -89,6 +90,8 @@ void rect_draw_some_item(sf::RenderTarget& targ_gworld,RECT targ_rect); void rect_draw_some_item(const sf::Texture& src_gworld,RECT src_rect,sf::RenderTarget& targ_gworld,RECT targ_rect,sf::BlendMode mode = sf::BlendNone); void rect_draw_some_item(const sf::Texture& src_gworld,RECT src_rect,RECT targ_rect,location offset,sf::BlendMode mode = sf::BlendNone); 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); + +std::vector draw_string_hilite(sf::RenderTarget& dest_window,RECT dest_rect,std::string str,TextStyle style,std::vector hilites,sf::Color hiliteClr); void win_draw_string(sf::RenderTarget& dest_window,RECT dest_rect,std::string str,eTextMode mode,TextStyle style, location offset = {0,0}); short string_length(std::string str, TextStyle style); //OSStatus flip_pict(OSType domain, OSType type, short id, void *ptr, UInt32 size, bool isNative, void *refcon);