From aaa3cde16b6294812cec28bc688feab10e799f55 Mon Sep 17 00:00:00 2001 From: Celtic Minstrel Date: Thu, 15 Jan 2015 23:30:07 -0500 Subject: [PATCH] Lots of special node changes - Moved change/swap/transform terrain to the General section - Merge change outdoor terrain into general change terrain - Merge if town/outdoor terrain nodes - Merge the if+take nodes with the equivalent base nodes - Merge secret passage node into the can't enter node - Move outdoor shop to the General section since it's not restricted to outdoors - New story dialog node displays a sequence of strings one at a time, like the Exile 2 intro dialog - New town nodes for animations: run missile, animate monster attack, draw simple boom with optional damage number - If fields node expanded - now checks if the count of the desired field type existing within a specified rectangle falls within a given range - Place items and move items nodes can now set the items as contained, provided there's a container on the destination space - All rectangle nodes can now be restricted to just the boundary, as per the documentation --- rsrc/strings/specials-opcodes.txt | 32 ++-- rsrc/strings/specials-text-general.txt | 96 ++++++++-- rsrc/strings/specials-text-ifthen.txt | 80 ++++----- rsrc/strings/specials-text-outdoor.txt | 17 +- rsrc/strings/specials-text-rect.txt | 18 +- rsrc/strings/specials-text-town.txt | 56 +++--- src/boe.graphics.cpp | 9 +- src/boe.items.cpp | 33 +++- src/boe.items.h | 3 +- src/boe.specials.cpp | 239 +++++++++++++------------ src/classes/scenario.h | 4 +- src/classes/simpletypes.h | 42 ++--- src/classes/special.cpp | 81 +++++++-- 13 files changed, 422 insertions(+), 288 deletions(-) diff --git a/rsrc/strings/specials-opcodes.txt b/rsrc/strings/specials-opcodes.txt index 39a5ec2c..048a3f51 100644 --- a/rsrc/strings/specials-opcodes.txt +++ b/rsrc/strings/specials-opcodes.txt @@ -1,13 +1,13 @@ set-sdf inc-sdf disp-msg -secret-pass +start-shop disp-sm-msg flip-sdf sdf-rand sdf-add sdf-diff - +story-dlog block-move change-time start-timer-scen @@ -31,9 +31,9 @@ print-nums sdf-times sdf-divide sdf-power - - - +change-ter +swap-ter +trans-ter @@ -58,8 +58,8 @@ once-dlog once-give-dlog -once-encounter - +once-out-encounter +once-town-encounter once-trap @@ -134,16 +134,16 @@ if-spec-item if-sdf-compare if-ter - - - - - if-gold if-food if-item-class-at if-item-class if-item-class-equip + + + + + if-day if-field if-party-size @@ -168,9 +168,9 @@ if-num-response town-attitude -change-ter -swap-ter -trans-ter +anim-missile +anim-attack +anim-splat move-party hit-space explode-space @@ -225,5 +225,3 @@ rect-unlock make-out-monst - -start-shop \ No newline at end of file diff --git a/rsrc/strings/specials-text-general.txt b/rsrc/strings/specials-text-general.txt index a824aff5..b1336740 100644 --- a/rsrc/strings/specials-text-general.txt +++ b/rsrc/strings/specials-text-general.txt @@ -62,19 +62,19 @@ Unused Unused Special to Jump To -------------------- -Secret Passage -Unused -Unused -First part of message -Second part of message -Unused +Start Shopping Unused Unused +Name of Store Unused Unused Unused Unused +Number of first item in store +Store type (see docs. for list) Unused +Number of items in store (1 .. 40) +Cost adjust (0 .. 6, lower = cheaper) Unused Special to Jump To -------------------- @@ -158,21 +158,21 @@ Second SDF part B, or -1 for literal number Unused Special to Jump To -------------------- -Unused Node -Unused -Unused -Unused -Unused -Unused -Unused -Unused -Unused +Story Dialog +Unused +Unused +Dialog title string +First string in dialog +Last string in dialog +Picture number +Picture type Unused Unused Unused Unused Unused Unused +Special to Jump To -------------------- Can't Enter Unused @@ -185,7 +185,7 @@ Unused 0 - can enter, 1 - no enter Unused Unused -Unused +0 - don't force, 1 - force if blocked Unused Unused Special to Jump To @@ -542,3 +542,67 @@ Second SDF part B, or -1 for literal number Unused Special to Jump To -------------------- +Change Terrain +Unused +Unused +First part of message +Second part of message +Unused +Unused +Unused +X coordinate of space +Y coordinate of space +Unused +Terrain to change to +Unused +Unused +Special to Jump To +-------------------- +Swap Terrain +Unused +Unused +First part of message +Second part of message +Unused +Unused +Unused +X coordinate of space +Y coordinate of space +Unused +Swap this terrain ... +With this terrain ... +Unused +Special to Jump To +-------------------- +Transform Terrain +Unused +Unused +First part of message +Second part of message +Unused +Unused +Unused +X coordinate of space +Y coordinate of space +Unused +Unused +Unused +Unused +Special to Jump To +-------------------- +Special Name +sdf1 +sdf2 +message1 +message2 +message3 +pic +pictype +extra 1a +extra 1b +extra 1c +extra 2a +extra 2b +extra 2c +Special to Jump To +-------------------- diff --git a/rsrc/strings/specials-text-ifthen.txt b/rsrc/strings/specials-text-ifthen.txt index 35d0d5b1..d95d93d6 100644 --- a/rsrc/strings/specials-text-ifthen.txt +++ b/rsrc/strings/specials-text-ifthen.txt @@ -121,7 +121,7 @@ Unused If party has this much gold ... Call this special ... Unused -Unused +If 1, take gold Unused Unused Otherwise call this special @@ -137,7 +137,7 @@ Unused If party has this much food ... Call this special ... Unused -Unused +If 1, take food Unused Unused Otherwise call this special @@ -155,7 +155,7 @@ Y coordinate of space Unused If item of this class on space ... Call this special ... -Unused +If 1, take item Otherwise call this special -------------------- Have Item With Class? @@ -169,7 +169,7 @@ Unused If has item of this special class ... Call this special ... Unused -Unused +If 1, take item Unused Unused Otherwise call this special @@ -185,28 +185,12 @@ Unused If has equipped item of this special ... Call this special ... Unused -Unused -Unused -Unused -Otherwise call this special --------------------- -Has Gold? (+ take) -Unused -Unused -Unused -Unused -Unused -Unused -Unused -If party has this much gold ... -Call this special ... -Unused -Unused +If 1, take item Unused Unused Otherwise call this special -------------------- -Has Food? (+ take) +Unused Node Unused Unused Unused @@ -214,15 +198,15 @@ Unused Unused Unused Unused -If party has this much food ... -Call this special ... Unused Unused Unused Unused -Otherwise call this special +Unused +Unused +Special to Jump To -------------------- -Item Class on Space? (+ take) +Unused Node Unused Unused Unused @@ -230,15 +214,15 @@ Unused Unused Unused Unused -X coordinate of space -Y coordinate of space Unused -If item of this class on space ... -Call this special ... Unused -Otherwise call this special +Unused +Unused +Unused +Unused +Special to Jump To -------------------- -Have Item W. Class? (+ take) +Unused Node Unused Unused Unused @@ -246,15 +230,15 @@ Unused Unused Unused Unused -If has item of this special class ... -Call this special ... Unused Unused Unused Unused -Otherwise call this special +Unused +Unused +Special to Jump To -------------------- -Equip Item W. Class? (+ take) +Unused Node Unused Unused Unused @@ -262,13 +246,29 @@ Unused Unused Unused Unused -If has equipped item of this class ... -Call this special ... Unused Unused -eUnused Unused -Otherwise call this special +Unused +Unused +Unused +Special to Jump To +-------------------- +Unused Node +Unused +Unused +Unused +Unused +Unused +Unused +Unused +Unused +Unused +Unused +Unused +Unused +Unused +Special to Jump To -------------------- Day Reached? Unused diff --git a/rsrc/strings/specials-text-outdoor.txt b/rsrc/strings/specials-text-outdoor.txt index 0bdeae62..33577f93 100644 --- a/rsrc/strings/specials-text-outdoor.txt +++ b/rsrc/strings/specials-text-outdoor.txt @@ -62,19 +62,4 @@ Unused Unused Special to Jump To -------------------- -Outdoor Store -Unused -Unused -Name of Store -Unused -Unused -Unused -Unused -Number of first item in store -Store type (see docs. for list) -Unused -Number of items in store (1 .. 40) -Cost adjust (0 .. 6, lower = cheaper) -Unused -Special to Jump To --------------------- + diff --git a/rsrc/strings/specials-text-rect.txt b/rsrc/strings/specials-text-rect.txt index 8a026c4d..a3eb708d 100644 --- a/rsrc/strings/specials-text-rect.txt +++ b/rsrc/strings/specials-text-rect.txt @@ -4,7 +4,7 @@ Which field type First part of message Second part of message Unused -Unused +0 - entire rectangle, 1 - just borders Unused Top of rectangle Left of rectangle @@ -195,8 +195,8 @@ X of space to move to Y of space to move to First part of message Second part of message -Unused -Unused +Set to 1 to place in container, if present +0 - entire rectangle, 1 - just borders Unused Top of rectangle Left of rectangle @@ -212,7 +212,7 @@ Unused First part of message Second part of message Unused -Unused +0 - entire rectangle, 1 - just borders Unused Top of rectangle Left of rectangle @@ -228,7 +228,7 @@ Chance of changing (0 - 100) First part of message Second part of message Unused -Unused +0 - entire rectangle, 1 - just borders Unused Top of rectangle Left of rectangle @@ -244,7 +244,7 @@ with this ter. type First part of message Second part of message Unused -Unused +0 - entire rectangle, 1 - just borders Unused Top of rectangle Left of rectangle @@ -260,7 +260,7 @@ Unused First part of message Second part of message Unused -Unused +0 - entire rectangle, 1 - just borders Unused Top of rectangle Left of rectangle @@ -276,7 +276,7 @@ Unused First part of message Second part of message Unused -Unused +0 - entire rectangle, 1 - just borders Unused Top of rectangle Left of rectangle @@ -292,7 +292,7 @@ Unused First part of message Second part of message Unused -Unused +0 - entire rectangle, 1 - just borders Unused Top of rectangle Left of rectangle diff --git a/rsrc/strings/specials-text-town.txt b/rsrc/strings/specials-text-town.txt index 3a589e79..242fd35d 100644 --- a/rsrc/strings/specials-text-town.txt +++ b/rsrc/strings/specials-text-town.txt @@ -14,7 +14,23 @@ Unused Unused Special to Jump To -------------------- -Change Terrain +Do Missile Animation +Unused +Unused +First part of message +Second part of message +Unused +Which missile animation? +Unused +X coordinate of start space +Y coordinate of start space +Path type +X coordinate of end space +Y coordinate of end space +Sound to play +Special to Jump To +-------------------- +Animate Monster Attack Unused Unused First part of message @@ -22,44 +38,28 @@ Second part of message Unused Unused Unused -X coordinate of space -Y coordinate of space +X coordinate of monster (or monster ID) +Y coordinate of monster (or -1 to use X as ID) +Unused Unused -Terrain to change to Unused Unused Special to Jump To -------------------- -Swap Terrain +Animate Fake Damage Unused Unused First part of message Second part of message Unused -Unused -Unused +pic +pictype X coordinate of space Y coordinate of space -Unused -Swap this terrain ... -With this terrain ... -Unused -Special to Jump To --------------------- -Transform Terrain -Unused -Unused -First part of message -Second part of message -Unused -Unused -Unused -X coordinate of space -Y coordinate of space -Unused -Unused -Unused -Unused +extra 1c +Boom type +Number to print (0 means print no number) +Sound to play Special to Jump To -------------------- Move Party @@ -362,7 +362,7 @@ X coordinate to place at Y coordinate to place at Unused Item to place -Unused +Set to 1 to place in container, if present Unused Special to Jump To -------------------- diff --git a/src/boe.graphics.cpp b/src/boe.graphics.cpp index dc25e45c..ecd4a05f 100644 --- a/src/boe.graphics.cpp +++ b/src/boe.graphics.cpp @@ -1472,9 +1472,10 @@ void boom_space(location where,short mode,short type,short damage,short sound) { rectangle source_rect = {0,0,36,28},text_rect,dest_rect = {13,13,49,41},big_to = {13,13,337,265},store_rect; short del_len; short x_adj = 0,y_adj = 0,which_m; - short sound_to_play[20] = { + short sound_lookup[20] = { 97,69,70,71,72, 73,55,75,42,86, 87,88,89,98,0, 0,0,0,0,0}; + short sound_to_play = sound < 0 ? -sound : sound_lookup[sound]; //sound_key = type / 10; //type = type % 10; @@ -1495,7 +1496,7 @@ void boom_space(location where,short mode,short type,short damage,short sound) { // Redraw terrain in proper position if(((!point_onscreen(center,where) && (overall_mode >= MODE_COMBAT)) || (overall_mode == MODE_OUTDOORS)) ) { - play_sound(sound_to_play[sound]); + play_sound(sound_to_play); return; } @@ -1532,7 +1533,7 @@ void boom_space(location where,short mode,short type,short damage,short sound) { source_rect.offset(-store_rect.left + 28 * type,-store_rect.top); rect_draw_some_item(boom_gworld,source_rect,dest_rect,ul,sf::BlendAlpha); - if((dest_rect.right - dest_rect.left >= 28) && (dest_rect.bottom - dest_rect.top >= 36)) { + if(damage > 0 && dest_rect.right - dest_rect.left >= 28 && dest_rect.bottom - dest_rect.top >= 36) { TextStyle style; style.lineHeight = 10; //text_rect = coord_to_rect(where_draw.x,where_draw.y); @@ -1544,7 +1545,7 @@ void boom_space(location where,short mode,short type,short damage,short sound) { text_rect.offset(-4,-5); win_draw_string(mainPtr,text_rect,std::to_string(damage),eTextMode::CENTRE,style,ul); } - play_sound((skip_boom_delay?-1:1)*sound_to_play[sound]); + play_sound((skip_boom_delay?-1:1)*sound_to_play); mainPtr.display(); if((sound == 6) && (fast_bang == 0) && (!skip_boom_delay)) sf::sleep(time_in_ticks(12)); diff --git a/src/boe.items.cpp b/src/boe.items.cpp index 58a4ac49..11f124f8 100644 --- a/src/boe.items.cpp +++ b/src/boe.items.cpp @@ -603,13 +603,17 @@ void drop_item(short pc_num,short item_num,location where_drop) { } } -bool place_item(cItem item,location where,bool forced) { +bool place_item(cItem item,location where,bool forced,bool contained) { short i; + if(contained && !is_container(where)) + contained = false; + for(i = 0; i < NUM_TOWN_ITEMS; i++) if(univ.town.items[i].variety == eItemType::NO_ITEM) { univ.town.items[i] = item; univ.town.items[i].item_loc = where; + univ.town.items[i].contained = contained; reset_item_max(); return true; } @@ -620,6 +624,7 @@ bool place_item(cItem item,location where,bool forced) { if(univ.town.items[i].variety == eItemType::NO_ITEM) { univ.town.items[i] = item; univ.town.items[i].item_loc = where; + univ.town.items[i].contained = contained; reset_item_max(); return true; } @@ -1120,6 +1125,32 @@ void custom_pic_dialog(std::string title, pic_num_t bigpic) { pic_dlg.run(); } +void story_dialog(std::string title, str_num_t first, str_num_t last, int which_str_type, pic_num_t pic, ePicType pt) { + cDialog story_dlg("many-str"); + dynamic_cast(story_dlg["pict"]).setPict(pic, pt); + str_num_t cur = first; + story_dlg.attachClickHandlers([&cur,first,last,which_str_type](cDialog& me, std::string clicked, eKeyMod) -> bool { + if(clicked == "left") { + if(cur > first) cur--; + } else if(clicked == "done" || cur == last) { + me.toast(false); + return true; + } else if(clicked == "right") { + cur++; + } + if(which_str_type == 0) + me["str"].setText(univ.scenario.spec_strs[cur]); + else if(which_str_type == 1) + me["str"].setText(univ.out->spec_strs[cur]); + else if(which_str_type == 2) + me["str"].setText(univ.town->spec_strs[cur]); + return true; + }, {"left", "right", "done"}); + story_dlg["left"].triggerClickHandler(story_dlg, "left", eKeyMod()); + story_dlg["title"].setText(title); + story_dlg.run(); +} + static bool get_num_of_items_event_filter(cDialog& me, std::string, eKeyMod) { if(me.toast(true)) me.setResult(me["number"].getTextAsNum()); diff --git a/src/boe.items.h b/src/boe.items.h index 1e27b23a..4631f381 100644 --- a/src/boe.items.h +++ b/src/boe.items.h @@ -25,7 +25,7 @@ void remove_charge(short pc_num,short which_item); void enchant_weapon(short pc_num,short item_hit,short enchant_type,short new_val); void equip_item(short pc_num,short item_num); void drop_item(short pc_num,short item_num,location where_drop); -bool place_item(cItem item,location where,bool forced); +bool place_item(cItem item,location where,bool forced,bool contained = false); void destroy_an_item(); void give_thing(short pc_num, short item_num); void combine_things(short pc_num); @@ -40,6 +40,7 @@ bool show_get_items(std::string titleText, std::vector& itemRefs, short bool display_item(location from_loc,short pc_num,short mode, bool check_container); short custom_choice_dialog(std::array& strs,short pic_num,ePicType pic_type,std::array& buttons) ; void custom_pic_dialog(std::string title, pic_num_t bigpic); +void story_dialog(std::string title, str_num_t first, str_num_t last, int which_str_type, pic_num_t pic, ePicType pt); short get_num_of_items(short max_num); void init_mini_map(); void put_pc_effects_on_dialog(cDialog& dialog,short item); diff --git a/src/boe.specials.cpp b/src/boe.specials.cpp index f5b63078..e55c2854 100644 --- a/src/boe.specials.cpp +++ b/src/boe.specials.cpp @@ -44,6 +44,7 @@ extern short fast_bang; extern bool end_scenario; extern cUniverse univ; extern std::queue special_queue; +extern short combat_posing_monster; bool can_draw_pcs = true; @@ -180,13 +181,12 @@ bool check_special_terrain(location where_check,eSpecCtx mode,short which_pc,sho for(i = 0; i < 18; i++) if(out_where == univ.out->special_locs[i]) { *spec_num = univ.out->special_id[i]; - if((*spec_num >= 0) && - univ.out->specials[*spec_num].type == eSpecType::SECRET_PASSAGE) - *forced = true; // call special run_special(mode,1,univ.out->special_id[i],out_where,&s1,&s2,&s3); if(s1 > 0) can_enter = false; + else if(s2 > 0) + *forced = true; erase_out_specials(); put_pc_screen(); put_item_screen(stat_window,0); @@ -211,9 +211,6 @@ bool check_special_terrain(location where_check,eSpecCtx mode,short which_pc,sho } for(i = 0; i < 50; i++) if(where_check == univ.town->special_locs[i]) { - if(univ.town->specials[univ.town->spec_id[i]].type == eSpecType::SECRET_PASSAGE) { - *forced = true; - } *spec_num = univ.town->spec_id[i]; bool runSpecial = false; if(!is_blocked(where_check)) runSpecial = true; @@ -226,6 +223,8 @@ bool check_special_terrain(location where_check,eSpecCtx mode,short which_pc,sho run_special(mode,2,univ.town->spec_id[i],where_check,&s1,&s2,&s3); if(s1 > 0) can_enter = false; + else if(s2 > 0) + *forced = true; } } put_pc_screen(); @@ -2102,7 +2101,10 @@ void general_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE) { if(spec.ex1a != 0) *a = 1; - else *a = 0; + else { + *a = 0; + if(spec.ex2a != 0) *b = 1; + } } break; case eSpecType::CHANGE_TIME: @@ -2285,6 +2287,43 @@ void general_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, break; } break; + case eSpecType::CHANGE_TER: + set_terrain(loc(spec.ex1a,spec.ex1b),spec.ex2a); + *redraw = true; + draw_map(true); + check_mess = true; + break; + case eSpecType::SWAP_TER: + if(coord_to_ter(spec.ex1a,spec.ex1b) == spec.ex2a){ + set_terrain(loc(spec.ex1a,spec.ex1b),spec.ex2b); + } + else if(coord_to_ter(spec.ex1a,spec.ex1b) == spec.ex2b){ + set_terrain(loc(spec.ex1a,spec.ex1b),spec.ex2a); + } + *redraw = 1; + draw_map(true); + check_mess = true; + break; + case eSpecType::TRANS_TER: + set_terrain(loc(spec.ex1a,spec.ex1b),univ.scenario.ter_types[coord_to_ter(spec.ex1a,spec.ex1b)].trans_to_what); + *redraw = 1; + draw_map(true); + check_mess = true; + break; + case eSpecType::ENTER_SHOP: + get_strs(str1,str2,1,spec.m1,-1); + if(spec.ex2a >= 40) + spec.ex2a = 39; + if(spec.ex2a < 1) + spec.ex2a = 1; + spec.ex2b = minmax(0,6,spec.ex2b); + start_shop_mode(eShopType(spec.ex1b), spec.ex1a, spec.ex1a + spec.ex2a - 1, spec.ex2b, str1); + *next_spec = -1; + break; + case eSpecType::STORY_DIALOG: + get_strs(str1,str2,cur_spec_type,spec.m1,-1); + story_dialog(str1, spec.m2, spec.m3, cur_spec_type, spec.pic, ePicType(spec.pictype)); + break; } if(check_mess) { handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b); @@ -2934,23 +2973,25 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, } else giveError("A Stuff Done flag is out of range."); break; - case eSpecType::IF_TOWN_TER_TYPE: - if(((is_town()) || (is_combat())) && (univ.town->terrain(spec.ex1a,spec.ex1b) == spec.ex2a)) - *next_spec = spec.ex2b; - break; - case eSpecType::IF_OUT_TER_TYPE: + case eSpecType::IF_TER_TYPE: l.x = spec.ex1a; l.y = spec.ex1b; l = local_to_global(l); - if((is_out()) && (univ.out[l.x][l.y] == spec.ex2a)) + if((is_town() || is_combat()) && univ.town->terrain(spec.ex1a,spec.ex1b) == spec.ex2a) + *next_spec = spec.ex2b; + else if(is_out() && univ.out[l.x][l.y] == spec.ex2a) *next_spec = spec.ex2b; break; case eSpecType::IF_HAS_GOLD: - if(univ.party.gold >= spec.ex1a) + if(univ.party.gold >= spec.ex1a) { + if(spec.ex2a) take_gold(spec.ex1a,true); *next_spec = spec.ex1b; + } break; case eSpecType::IF_HAS_FOOD: - if(univ.party.food >= spec.ex1a) + if(univ.party.food >= spec.ex1a) { + if(spec.ex2a) take_food(spec.ex1a,true); *next_spec = spec.ex1b; + } break; case eSpecType::IF_ITEM_CLASS_ON_SPACE: if(is_out()) @@ -2958,11 +2999,16 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, l.x = spec.ex1a; l.y = spec.ex1b; for(i = 0; i < NUM_TOWN_ITEMS; i++) if(univ.town.items[i].variety != eItemType::NO_ITEM && univ.town.items[i].special_class == (unsigned)spec.ex2a - && (l == univ.town.items[i].item_loc)) + && l == univ.town.items[i].item_loc) { *next_spec = spec.ex2b; + if(spec.ex2c) { + *redraw = 1; + univ.town.items[i].variety = eItemType::NO_ITEM; + } + } break; case eSpecType::IF_HAVE_ITEM_CLASS: - if(party_check_class(spec.ex1a,1)) + if(party_check_class(spec.ex1a,!spec.ex2a)) *next_spec = spec.ex1b; break; case eSpecType::IF_EQUIP_ITEM_CLASS: @@ -2970,46 +3016,12 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, if(univ.party[i].main_status == eMainStatus::ALIVE) for(j = 0; j < 24; j++) if(univ.party[i].items[j].variety != eItemType::NO_ITEM && univ.party[i].items[j].special_class == (unsigned)spec.ex1a - && (univ.party[i].equip[j])) + && univ.party[i].equip[j]) { *next_spec = spec.ex1b; - break; - case eSpecType::IF_HAS_GOLD_AND_TAKE: - if(univ.party.gold >= spec.ex1a) { - take_gold(spec.ex1a,true); - *next_spec = spec.ex1b; - } - break; - case eSpecType::IF_HAS_FOOD_AND_TAKE: - if(univ.party.food >= spec.ex1a) { - take_food(spec.ex1a,true); - *next_spec = spec.ex1b; - } - break; - case eSpecType::IF_ITEM_CLASS_ON_SPACE_AND_TAKE: - if(is_out()) - break; - l.x = spec.ex1a; l.y = spec.ex1b; - for(i = 0; i < NUM_TOWN_ITEMS; i++) - if(univ.town.items[i].variety != eItemType::NO_ITEM && univ.town.items[i].special_class == (unsigned)spec.ex2a - && (l == univ.town.items[i].item_loc)) { - *next_spec = spec.ex2b; - *redraw = 1; - univ.town.items[i].variety = eItemType::NO_ITEM; - } - break; - case eSpecType::IF_HAVE_ITEM_CLASS_AND_TAKE: - if(party_check_class(spec.ex1a,0)) - *next_spec = spec.ex1b; - break; - case eSpecType::IF_EQUIP_ITEM_CLASS_AND_TAKE: - for(i = 0; i < 6; i++) - if(univ.party[i].main_status == eMainStatus::ALIVE) - for(j = 0; j < 24; j++) - if(univ.party[i].items[j].variety != eItemType::NO_ITEM && univ.party[i].items[j].special_class == (unsigned)spec.ex1a - && (univ.party[i].equip[j])) { - *next_spec = spec.ex1b; - *redraw = 1; - take_item(i,j); + if(spec.ex2c) { + *redraw = 1; + take_item(i,j); + } } break; case eSpecType::IF_DAY_REACHED: @@ -3017,14 +3029,14 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, *next_spec = spec.ex1b; break; case eSpecType::IF_FIELDS: - if(!isValidField(spec.ex1a, false)) { + if(!isValidField(spec.m1, false)) { giveError("Scenario tried to check for invalid field type (1...24)"); break; } i = 0; - for(j = 0; j < univ.town->max_dim(); j++) - for(k = 0; k < univ.town->max_dim(); k++) { - switch(eFieldType(spec.ex1a)) { + for(j = spec.ex1b; j < std::min(spec.ex2b, univ.town->max_dim()); j++) + for(k = spec.ex1a; k < std::min(spec.ex2a, univ.town->max_dim()); k++) { + switch(eFieldType(spec.m1)) { // These values are not allowed case SPECIAL_EXPLORED: case SPECIAL_SPOT: case FIELD_DISPEL: case FIELD_SMASH: break; // Walls @@ -3057,11 +3069,8 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, case SFX_RUBBLE: i += univ.town.is_rubble(i,j); break; } } - if(i > 0) - *next_spec = spec.ex1b; - // TODO: Are there other object types to account for? - // TODO: Allow restricting to a specific rect - // TODO: Allow requiring a minimum and maximum number of objects + if(i >= spec.sd1 && i <= spec.sd2) + *next_spec = spec.m2; break; case eSpecType::IF_PARTY_SIZE: if(spec.ex2a < 1) { @@ -3337,14 +3346,19 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, } void set_terrain(location l, ter_num_t terrain_type) { - // TODO: Use dynamic size() instead of hard-coded limit - if(terrain_type >= 256) return; - ter_num_t former = univ.town->terrain(l.x,l.y); - univ.town->terrain(l.x,l.y) = terrain_type; - if(univ.scenario.ter_types[terrain_type].special == eTerSpec::CONVEYOR) - belt_present = true; - if(univ.scenario.ter_types[former].light_radius != univ.scenario.ter_types[terrain_type].light_radius) - univ.town->set_up_lights(); + if(terrain_type >= univ.scenario.ter_types.size()) return; + if(is_out()) { + univ.out->terrain[l.x][l.y] = terrain_type; + l = local_to_global(l); + univ.out[l.x][l.y] = terrain_type; + } else { + ter_num_t former = univ.town->terrain(l.x,l.y); + univ.town->terrain(l.x,l.y) = terrain_type; + if(univ.scenario.ter_types[terrain_type].special == eTerSpec::CONVEYOR) + belt_present = true; + if(univ.scenario.ter_types[former].light_radius != univ.scenario.ter_types[terrain_type].light_radius) + univ.town->set_up_lights(); + } } // TODO: What was next_spec_type for? Is it still needed? @@ -3376,27 +3390,6 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, case eSpecType::MAKE_TOWN_HOSTILE: set_town_attitude(spec.ex1a,spec.ex1b,spec.ex2a); break; - case eSpecType::TOWN_CHANGE_TER: - set_terrain(l,spec.ex2a); - *redraw = true; - draw_map(true); - break; - case eSpecType::TOWN_SWAP_TER: - if(coord_to_ter(spec.ex1a,spec.ex1b) == spec.ex2a){ - set_terrain(l,spec.ex2b); - } - else if(coord_to_ter(spec.ex1a,spec.ex1b) == spec.ex2b){ - set_terrain(l,spec.ex2a); - } - *redraw = 1; - draw_map(true); - break; - case eSpecType::TOWN_TRANS_TER: - ter = coord_to_ter(spec.ex1a,spec.ex1b); - set_terrain(l,univ.scenario.ter_types[ter].trans_to_what); - *redraw = 1; - draw_map(true); - break; case eSpecType::TOWN_MOVE_PARTY: if(is_combat()) { ASB("Not while in combat."); @@ -3654,7 +3647,7 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, break; case eSpecType::TOWN_PLACE_ITEM: store_i = get_stored_item(spec.ex2a); - place_item(store_i,l,true); + place_item(store_i,l,true,spec.ex2b); break; case eSpecType::TOWN_SPLIT_PARTY: if(which_mode == eSpecCtx::TALK) @@ -3728,6 +3721,36 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, } univ.town.monst[spec.ex1a].attitude = spec.ex1b; break; + case eSpecType::TOWN_RUN_MISSILE: + if(which_mode == eSpecCtx::TALK) + break; + if((i = monst_there(loc(spec.ex2a, spec.ex2b))) < 90) { + cCreature& who = univ.town.monst[i]; + i = 14 * who.x_width - 1; + r1 = 18 * who.y_width - 1; + } else i = r1 = 0; + run_a_missile(l, loc(spec.ex2a, spec.ex2b), spec.pic, spec.ex1c, spec.ex2c, i, r1, 100); + break; + case eSpecType::TOWN_BOOM_SPACE: + // TODO: This should work, but does it need a bit of extra logic? + if(which_mode == eSpecCtx::TALK) + break; + boom_space(l, 100, spec.ex2a, spec.ex2b, -spec.ex2c); + break; + case eSpecType::TOWN_MONST_ATTACK: + // TODO: I'm not certain if this will work. + if(which_mode == eSpecCtx::TALK) + break; + i = combat_posing_monster; + if(l.y >= 0) combat_posing_monster = monst_there(l); + else combat_posing_monster = spec.ex1a; + if(combat_posing_monster < 0 || combat_posing_monster >= univ.town->max_monst()) { + combat_posing_monster = i; + break; + } + redraw_screen(REFRESH_TERRAIN); + combat_posing_monster = i; + break; } if(check_mess) { handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b); @@ -3753,6 +3776,9 @@ void rect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, for(i = spec.ex1b;i <= spec.ex2b;i++) for(j = spec.ex1a; j <= spec.ex2a; j++) { l.x = i; l.y = j; + // If pict non-zero, exclude rectangle interior + if(spec.pic > 0 && i > spec.ex1b && i < spec.ex2b && j > spec.ex1a && j < spec.ex2a) + continue; switch(cur_node.type) { case eSpecType::RECT_PLACE_FIELD: if(!isValidField(spec.sd2, true)) { @@ -3802,10 +3828,13 @@ void rect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, } break; case eSpecType::RECT_MOVE_ITEMS: + i = is_container(loc(spec.sd1,spec.sd2)); for(k = 0; k < NUM_TOWN_ITEMS; k++) if(univ.town.items[k].variety != eItemType::NO_ITEM && univ.town.items[k].item_loc == l) { univ.town.items[k].item_loc.x = spec.sd1; univ.town.items[k].item_loc.y = spec.sd2; + if(i && spec.m3) + univ.town.items[k].contained = true; } break; case eSpecType::RECT_DESTROY_ITEMS: @@ -3881,16 +3910,6 @@ void outdoor_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, create_wand_monst(); *redraw = 1; break; - case eSpecType::OUT_CHANGE_TER: - if(spec.ex2a < 0) break; - univ.out->terrain[spec.ex1a][spec.ex1b] = spec.ex2a; - l.x = spec.ex1a; - l.y = spec.ex1b; - l = local_to_global(l); - univ.out[l.x][l.y] = spec.ex2a; - *redraw = 1; - check_mess = true; - break; case eSpecType::OUT_PLACE_ENCOUNTER: if(spec.ex1a != minmax(0,3,spec.ex1a)) { giveError("Special outdoor enc. is out of range. Must be 0-3."); @@ -3908,16 +3927,6 @@ void outdoor_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type, *redraw = 1; *a = 1; break; - case eSpecType::OUT_STORE: - get_strs(str1,str2,1,spec.m1,-1); - if(spec.ex2a >= 40) - spec.ex2a = 39; - if(spec.ex2a < 1) - spec.ex2a = 1; - spec.ex2b = minmax(0,6,spec.ex2b); - start_shop_mode(eShopType(spec.ex1b), spec.ex1a, spec.ex1a + spec.ex2a - 1, spec.ex2b, str1); - *next_spec = -1; - break; } if(check_mess) { diff --git a/src/classes/scenario.h b/src/classes/scenario.h index 221915b3..bcc8aead 100644 --- a/src/classes/scenario.h +++ b/src/classes/scenario.h @@ -58,10 +58,10 @@ public: short store_item_towns[3]; cSpecItem special_items[50]; short rating,uses_custom_graphics; - cMonster scen_monsters[256]; + std::array scen_monsters; cVehicle boats[30]; cVehicle horses[30]; - cTerrain ter_types[256]; + std::array ter_types; short scenario_timer_times[20]; short scenario_timer_specs[20]; std::array scen_specials; diff --git a/src/classes/simpletypes.h b/src/classes/simpletypes.h index d832ccaa..ccd23bc4 100644 --- a/src/classes/simpletypes.h +++ b/src/classes/simpletypes.h @@ -498,13 +498,13 @@ enum class eSpecType { SET_SDF = 1, INC_SDF = 2, DISPLAY_MSG = 3, - SECRET_PASSAGE = 4, + ENTER_SHOP = 4, DISPLAY_SM_MSG = 5, FLIP_SDF = 6, - SDF_RANDOM = 7, // formerly OUT_BLOCK - SDF_ADD = 8, // formerly TOWN_BLOCK - SDF_DIFF = 9, // formerly FIGHT_BLOCK - UNUSED1 = 10, // formerly LOOK_BLOCK + SDF_RANDOM = 7, + SDF_ADD = 8, + SDF_DIFF = 9, + STORY_DIALOG = 10, CANT_ENTER = 11, CHANGE_TIME = 12, SCEN_TIMER_START = 13, @@ -518,7 +518,7 @@ enum class eSpecType { CALL_GLOBAL = 21, SET_SDF_ROW = 22, COPY_SDF = 23, - DISPLAY_PICTURE = 24, // formerly SANCTIFY + DISPLAY_PICTURE = 24, REST = 25, WANDERING_WILL_FIGHT = 26, END_SCENARIO = 27, @@ -528,6 +528,9 @@ enum class eSpecType { SDF_TIMES = 31, SDF_DIVIDE = 32, // Computes both quotient and remainder SDF_POWER = 33, + CHANGE_TER = 34, + SWAP_TER = 35, + TRANS_TER = 36, ONCE_GIVE_ITEM = 50, ONCE_GIVE_SPEC_ITEM = 51, ONCE_NULL = 52, @@ -574,18 +577,18 @@ enum class eSpecType { IF_RANDOM = 132, IF_HAVE_SPECIAL_ITEM = 133, IF_SDF_COMPARE = 134, - IF_TOWN_TER_TYPE = 135, - IF_OUT_TER_TYPE = 136, + IF_TER_TYPE = 135, + UNUSED21 = 136, IF_HAS_GOLD = 137, IF_HAS_FOOD = 138, IF_ITEM_CLASS_ON_SPACE = 139, IF_HAVE_ITEM_CLASS = 140, IF_EQUIP_ITEM_CLASS = 141, - IF_HAS_GOLD_AND_TAKE = 142, - IF_HAS_FOOD_AND_TAKE = 143, - IF_ITEM_CLASS_ON_SPACE_AND_TAKE = 144, - IF_HAVE_ITEM_CLASS_AND_TAKE = 145, - IF_EQUIP_ITEM_CLASS_AND_TAKE = 146, + UNUSED22 = 142, + UNUSED23 = 143, + UNUSED24 = 144, + UNUSED25 = 145, + UNUSED26 = 146, IF_DAY_REACHED = 147, IF_FIELDS = 148, IF_PARTY_SIZE = 149, @@ -598,9 +601,9 @@ enum class eSpecType { IF_CONTEXT = 156, IF_NUM_RESPONSE = 157, MAKE_TOWN_HOSTILE = 170, - TOWN_CHANGE_TER = 171, - TOWN_SWAP_TER = 172, - TOWN_TRANS_TER = 173, + TOWN_RUN_MISSILE = 171, + TOWN_MONST_ATTACK = 172, + TOWN_BOOM_SPACE = 173, TOWN_MOVE_PARTY = 174, TOWN_HIT_SPACE = 175, TOWN_EXPLODE_SPACE = 176, @@ -645,10 +648,9 @@ enum class eSpecType { RECT_LOCK = 217, RECT_UNLOCK = 218, OUT_MAKE_WANDER = 225, - OUT_CHANGE_TER = 226, + UNUSED20 = 226, OUT_PLACE_ENCOUNTER = 227, OUT_MOVE_PARTY = 228, - OUT_STORE = 229, }; enum class eSpecCat { @@ -658,7 +660,7 @@ enum class eSpecCat { inline eSpecCat getNodeCategory(eSpecType node) { int code = (int) node; - if(code >= 0 && code <= 33) + if(code >= 0 && code <= 36) return eSpecCat::GENERAL; if(code >= 50 && code <= 63) return eSpecCat::ONCE; @@ -670,7 +672,7 @@ inline eSpecCat getNodeCategory(eSpecType node) { return eSpecCat::TOWN; if(code >= 200 && code <= 218) return eSpecCat::RECT; - if(code >= 225 && code <= 229) + if(code >= 225 && code <= 228) return eSpecCat::OUTDOOR; return eSpecCat::INVALID; } diff --git a/src/classes/special.cpp b/src/classes/special.cpp index 4a79ce66..161ca2af 100644 --- a/src/classes/special.cpp +++ b/src/classes/special.cpp @@ -107,7 +107,12 @@ void cSpecial::append(legacy::special_node_type& old){ break; case 148: case 149: // if barrels or crates type = eSpecType::IF_FIELDS; - ex1a = old.type == 148 ? OBJECT_BARREL : OBJECT_CRATE; + m1 = old.type == 148 ? OBJECT_BARREL : OBJECT_CRATE; + m2 = ex1b; + ex1a = ex1b = 0; + ex2a = ex2b = 64; + sd1 = 1; + sd2 = std::numeric_limits::max(); break; case 151: case 152: // if has cave lore or woodsman type = eSpecType::IF_TRAIT; @@ -132,9 +137,47 @@ void cSpecial::append(legacy::special_node_type& old){ ex1a -= 160; break; case 229: // Outdoor store - fix spell IDs + type = eSpecType::ENTER_SHOP; if(ex1b == 1 || ex1b == 2) ex1a += 30; break; + case 4: // Secret passage + type = eSpecType::CANT_ENTER; + ex1a = 0; + ex2a = 1; + break; + case 171: case 226: // Change terrain (town/outdoor) + type = eSpecType::CHANGE_TER; + break; + case 172: // Swap terrain + type = eSpecType::SWAP_TER; + break; + case 173: // Transform terrain + type = eSpecType::TRANS_TER; + break; + case 135: case 136: // If terrain + type = eSpecType::IF_TER_TYPE; + break; + case 137: case 142: // If has gold (and maybe take) + type = eSpecType::IF_HAS_GOLD; + ex2a = (old.type - 137) / 5; + break; + case 138: case 143: // If has food (and maybe take) + type = eSpecType::IF_HAS_FOOD; + ex2a = (old.type - 138) / 5; + break; + case 139: case 144: // If item on space (and maybe take) + type = eSpecType::IF_ITEM_CLASS_ON_SPACE; + ex2c = (old.type - 139) / 5; + break; + case 140: case 145: // If have item class (and maybe take) + type = eSpecType::IF_HAVE_ITEM_CLASS; + ex2a = (old.type - 140) / 5; + break; + case 141: case 146: // If equip item class (and maybe take) + type = eSpecType::IF_EQUIP_ITEM_CLASS; + ex2a = (old.type - 131) / 5; + break; // Place fields (twelve individual node types were collapsed into one) case 200: type = eSpecType::RECT_PLACE_FIELD; @@ -264,17 +307,17 @@ std::istream& operator >> (std::istream& in, eSpecType& e) { // % - Choose button to select shop cost adjustment static const char*const button_dict[7][11] = { { // general nodes - " mmmmmmmmm mmm mmmmmm Mmm $ mmm", // msg1 - " ", // msg2 - " ", // msg3 - " ", // pic - " ", // pictype - " x T i ", // ex1a - " S ss ", // ex1b - " ", // ex1c - " ", // ex2a - " ", // ex2b - " ", // ex2c + " mmmMMmmmm mmm mmmmmm Mmm $ mmmmmm", // msg1 + " ", // msg2 + " ", // msg3 + " ", // pic + " ", // pictype + " # x T i ", // ex1a + " & S ss ", // ex1b + " ", // ex1c + " tt ", // ex2a + " % t ", // ex2b + " ", // ex2c }, { // one-shot nodes "mm mddddddmmm", // msg1 " ", // msg2 @@ -306,23 +349,23 @@ static const char*const button_dict[7][11] = { " ", // pic " ", // pictype " f Qq $ * ", // ex1a - "ssss ss ssss sssss sssss =", // ex1b + "ssss ss ss sss sssss =", // ex1b " ss", // ex1c " K$ ", // ex2a - "s sss s s s==+s =", // ex2b + "s ss s s==+s =", // ex2b " s", // ex2c }, { // town nodes "mmmmmmmmmmmmmmm dddmmmmmmm", // msg1 " ", // msg2 " ", // msg3 - " ppp ", // pic - " ??? ", // pictype + " p ppp ", // pic + " ? ??? ", // pictype " c L ", // ex1a " s s s s @", // ex1b " ", // ex1c - "@tt ! c T T i ", // ex2a - " t DD / ", // ex2b - " : : ", // ex2c + "@ D ! c T T i ", // ex2a + " DD / ", // ex2b + " x x : : ", // ex2c }, { // rectangle nodes "m mmmmmmm", // msg1 " ", // msg2