Add a lot of stuff scraped from *i's version of the code, plus a couple of additional bits.
Adapted from *i: - Show a confirm dialog when interrupting a special node sequence - New monster special ability: call global special node (as an action, not on death) - New item special ability: call global special node - Check there's a monster death special before calling it (wasn't necessary before, might be now with the special queue changes) - Queue specials that are triggered while another special is in progress, instead of ignoring them; they will be run after the current special in progress finishes. - *i's version of petrification touch is currently active only for monster-on-monster combat; need to merge with my version for monster-on-pc combat. - Pass party location to special in use special item context - Fix set town visibility node (was checking wrong field and thus could not hide towns) Special nodes: - Town Hostile: change to Set Town Attitude - Select PC node: option to select random PC - Affect special nodes can now affect monsters - Fix affect death node reviving non-existent PCs - Affect Spells: Can remove spells, and can affect level 1-3 spells - If Objects: Merged from If Barrels and If Crates - If Species: Replaces If Cave Lore - If Trait: Replaces If Woodsman - If Statistic: Replaces If Enough Mage Lore - Change Lighting: Can affect town's global lighting setting, player's light level, or both at once. - Pointers! Actually, I'd already implemented the callbacks for setting and getting them, but they're now actually used, and the implementation has been tweaked a little. - Campaign flags! Again, I'd already implemented them sorta, but I tweaked things and they ended up sort of halfway between the two implementations. Plus there's now a special node to set them. Additional bits: - Special queue now uses an std::queue instead of a basic array. - Enum for town lighting levels - Disease touch ability is now honoured for monster-on-monster combat - See monster special context now passes the monster's location as the trigger location; also, removed the double-trigger from one circumstance. - Along with the set town attitude change, there's now the possibility for making the town hostile to trigger a special node, which can cause the party to be slain. - Select PC special node: option to select specific PC - Spell IDs for use in shops and Affect Spell nodes have changed so that 0 is now the first level 1 spell, and so forth. - add_string_to_buf can now auto-split the string over multiple lines, and the special node that uses it takes advantage of this - Special node parser warns if a node type is missing a corresponding opcode - Reserved "pointers" to access the special node's trigger location (this was *i's idea, but he never implemented it)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
|
||||
#include <cmath>
|
||||
#include <queue>
|
||||
|
||||
//#include "item.h"
|
||||
|
||||
@@ -151,7 +152,7 @@ short monst_place_count = 0; // 1 - standard place 2 - place last
|
||||
// 0 - whole area, 1 - active area 2 - graphic 3 - item name
|
||||
// 4 - item cost 5 - item extra str 6 - item help button
|
||||
RECT shopping_rects[8][7];
|
||||
pending_special_type special_queue[20];
|
||||
std::queue<pending_special_type> special_queue;
|
||||
bool end_scenario = false;
|
||||
|
||||
void init_screen_locs() ////
|
||||
@@ -325,8 +326,10 @@ bool handle_action(sf::Event event)
|
||||
the_point = location(event.mouseButton.x, event.mouseButton.y);
|
||||
the_point.x -= ul.x;
|
||||
the_point.y -= ul.y;
|
||||
for (i = 0; i < 20; i++) // TODO: Does this cause problems by leaving some specials uncalled?
|
||||
special_queue[i].spec = -1;
|
||||
if(!special_queue.empty())
|
||||
printf("Note: %ld queued specials have been flushed without running!", special_queue.size());
|
||||
while(!special_queue.empty()) // TODO: Does this cause problems by leaving some specials uncalled?
|
||||
special_queue.pop();
|
||||
end_scenario = false;
|
||||
|
||||
// Now split off the extra stuff, like talking and shopping.
|
||||
@@ -1291,17 +1294,11 @@ bool handle_action(sf::Event event)
|
||||
}
|
||||
|
||||
// MARK: At this point, see if any specials have been queued up, and deal with them
|
||||
// TODO: Use an std::queue for this.
|
||||
for (i = 0; i < 20; i++)
|
||||
if (special_queue[i].spec >= 0) {
|
||||
long long store_time = univ.party.age;
|
||||
univ.party.age = special_queue[i].trigger_time;
|
||||
// Note: We just check once here instead of looping because run_special also pulls from the queue.
|
||||
if(!special_queue.empty()) {
|
||||
s3 = 0;
|
||||
run_special(special_queue[i].mode,special_queue[i].type,special_queue[i].spec,
|
||||
special_queue[i].where,&s1,&s2,&s3);
|
||||
special_queue[i].spec = -1;
|
||||
long long change_time = univ.party.age - special_queue[i].trigger_time;
|
||||
univ.party.age = store_time + change_time;
|
||||
run_special(special_queue.front(), &s1, &s2, &s3);
|
||||
special_queue.pop();
|
||||
if (s3 > 0)
|
||||
draw_terrain();
|
||||
}
|
||||
@@ -2220,9 +2217,9 @@ void increase_age()////
|
||||
|
||||
move_to_zero(PSD[SDF_PARTY_FLIGHT]);
|
||||
|
||||
if ((overall_mode > MODE_OUTDOORS) && (univ.town->lighting_type == 2)) {
|
||||
univ.party.light_level = max (0,univ.party.light_level - 9);
|
||||
if (univ.town->lighting_type == 3) {
|
||||
if(overall_mode > MODE_OUTDOORS && univ.town->lighting_type >= LIGHT_DRAINS) {
|
||||
increase_light(-9);
|
||||
if(univ.town->lighting_type == LIGHT_NONE) {
|
||||
if (univ.party.light_level > 0)
|
||||
ASB("Your light is drained.");
|
||||
univ.party.light_level = 0;
|
||||
@@ -2658,7 +2655,7 @@ static void run_waterfalls(short mode){ // mode 0 - town, 1 - outdoors
|
||||
}
|
||||
draw_terrain();
|
||||
print_buf();
|
||||
if ((cave_lore_present() > 0) && (get_ran(1,0,1) == 0))
|
||||
if ((wilderness_lore_present() > 0) && (get_ran(1,0,1) == 0))
|
||||
add_string_to_buf(" (No supplies lost.)");
|
||||
else if (univ.party.food > 1800){
|
||||
add_string_to_buf(" (Many supplies lost.)");
|
||||
@@ -3088,11 +3085,17 @@ bool is_sign(ter_num_t ter)
|
||||
|
||||
bool check_for_interrupt(){
|
||||
using kb = sf::Keyboard;
|
||||
bool interrupt = false;
|
||||
#ifdef __APPLE__
|
||||
if((kb::isKeyPressed(kb::LSystem) || kb::isKeyPressed(kb::RSystem)) && kb::isKeyPressed(kb::Period))
|
||||
return true;
|
||||
interrupt = true;
|
||||
#endif
|
||||
if((kb::isKeyPressed(kb::LControl) || kb::isKeyPressed(kb::RControl)) && kb::isKeyPressed(kb::C))
|
||||
return true;
|
||||
interrupt = true;
|
||||
if(interrupt) {
|
||||
// TODO: A customized dialog with a more appropriate message
|
||||
cChoiceDlog confirm("quit-confirm-nosave.xml", {"quit","cancel"});
|
||||
if(confirm.show() == "quit") return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1787,6 +1787,7 @@ void do_monster_turn()
|
||||
{
|
||||
bool acted_yet, had_monst = false,printed_poison = false,printed_disease = false,printed_acid = false;
|
||||
bool redraw_not_yet_done = true;
|
||||
bool special_called = false;
|
||||
location targ_space,move_targ,l;
|
||||
short i,j,k,num_monst, target,r1,move_target;
|
||||
cCreature *cur_monst;
|
||||
@@ -2169,6 +2170,12 @@ void do_monster_turn()
|
||||
cur_monst->cur_loc,130,cur_monst->attitude) == true)
|
||||
{monst_spell_note(cur_monst->number,33); play_sound(61);}
|
||||
}
|
||||
if ((cur_monst->radiate_1 == 14) && !special_called && party_can_see_monst(i)) {
|
||||
short s1, s2, s3;
|
||||
special_called = true;
|
||||
take_m_ap(1,cur_monst);
|
||||
run_special(eSpecCtx::MONST_SPEC_ABIL,0,cur_monst->radiate_2,cur_monst->cur_loc,&s1,&s2,&s3);
|
||||
}
|
||||
}
|
||||
|
||||
combat_posing_monster = current_working_monster = -1;
|
||||
@@ -2369,7 +2376,7 @@ void monster_attack_pc(short who_att,short target)
|
||||
&& (get_ran(1,0,2) < 2)) {
|
||||
add_string_to_buf(" Causes disease! ");
|
||||
print_buf();
|
||||
disease_pc(target,(attacker->spec_skill == 25) ? 6 : 2);
|
||||
disease_pc(target,6);
|
||||
}
|
||||
|
||||
// Petrification touch
|
||||
@@ -2381,6 +2388,29 @@ void monster_attack_pc(short who_att,short target)
|
||||
print_buf();
|
||||
kill_pc(target,eMainStatus::STONE); // petrified, duh!
|
||||
}
|
||||
#if 0 // TODO: This is *i's version of the petrification touch ability.
|
||||
// It seems better in some ways, like printing a message when you resist,
|
||||
// but its calculation is very different, so I'm not sure what to with it.
|
||||
// Note, his version has also been incorporated into monster_attack_monster.
|
||||
|
||||
// Petrify target
|
||||
if (attacker->spec_skill == 30) {
|
||||
add_string_to_buf(" Petrification touch! ");
|
||||
r1 = max(0,(get_ran(1,0,100) - univ.party[target].level + 0.5*attacker->level));
|
||||
// Equip petrify protection?
|
||||
if (pc_has_abil_equip(target,49) < 24)
|
||||
r1 = 0;
|
||||
// Check if petrified.
|
||||
if (r1 > 60) {
|
||||
kill_pc(target,eMainStatus::STONE);
|
||||
add_string_to_buf(" Turned to stone! ");
|
||||
play_sound(43);
|
||||
}
|
||||
else {
|
||||
add_string_to_buf(" Resists! ");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Undead xp drain
|
||||
if (((attacker->spec_skill == 16) || (attacker->spec_skill == 17))
|
||||
@@ -2543,11 +2573,34 @@ void monster_attack_monster(short who_att,short attackee)
|
||||
add_string_to_buf(" Dumbfounds! ");
|
||||
dumbfound_monst(target,2);
|
||||
}
|
||||
// Disease target
|
||||
if (((attacker->spec_skill == 25))
|
||||
&& (get_ran(1,0,2) < 2)) {
|
||||
add_string_to_buf(" Causes disease! ");
|
||||
print_buf();
|
||||
disease_monst(target,6);
|
||||
}
|
||||
// Paralyze target
|
||||
if (attacker->spec_skill == 29) {
|
||||
add_string_to_buf(" Paralysis touch! ");
|
||||
charm_monst(target,-5,eStatus::PARALYZED,500);
|
||||
}
|
||||
// Petrify target
|
||||
if (attacker->spec_skill == 30) {
|
||||
add_string_to_buf(" Petrification touch! ");
|
||||
r1 = max(0,(get_ran(1,0,100) - target->level + 0.5*attacker->level));
|
||||
// Check if petrified.
|
||||
if ((r1 < 60) || (target->immunities & 2)) {
|
||||
add_string_to_buf(" Resists! ");
|
||||
}
|
||||
else {
|
||||
kill_monst(target,7);
|
||||
add_string_to_buf(" Turned to stone! ");
|
||||
play_sound(43);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Acid touch
|
||||
if (attacker->spec_skill == 31) {
|
||||
add_string_to_buf(" Acid touch! ");
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#define SFX_RUBBLE 128
|
||||
|
||||
/* stuff done flags */
|
||||
#define SDF_SPEC_LOC_X 301][0 // For special nodes to access the trigger location
|
||||
#define SDF_SPEC_LOC_Y 301][1
|
||||
//#define SDF_IS_PARTY_SPLIT 304][0
|
||||
//#define SDF_PARTY_SPLIT_X 304][1
|
||||
//#define SDF_PARTY_SPLIT_Y 304][2
|
||||
|
||||
@@ -486,7 +486,7 @@ void set_up_shop_array()
|
||||
if (i == minmax(0,31,i)) {
|
||||
store_i = store_mage_spells(i);
|
||||
store_shop_costs[shop_pos] = store_i.value;
|
||||
store_shop_items[shop_pos] = 800 + i + 30;
|
||||
store_shop_items[shop_pos] = 800 + i;
|
||||
shop_pos++;
|
||||
}
|
||||
break;
|
||||
@@ -495,7 +495,7 @@ void set_up_shop_array()
|
||||
if (i == minmax(0,31,i)) {
|
||||
store_i = store_priest_spells(i);
|
||||
store_shop_costs[shop_pos] = store_i.value;
|
||||
store_shop_items[shop_pos] = 900 + i + 30;
|
||||
store_shop_items[shop_pos] = 900 + i;
|
||||
shop_pos++;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <SFML/Graphics.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <queue>
|
||||
|
||||
//#include "item.h"
|
||||
|
||||
@@ -53,7 +54,7 @@ extern sf::Texture fields_gworld,anim_gworld,vehicle_gworld,terrain_gworld[NUM_T
|
||||
//extern short wish_list[STORED_GRAPHICS];
|
||||
//extern short storage_status[STORED_GRAPHICS]; // 0 - empty 1 - in use 2 - there, not in use
|
||||
extern short terrain_there[9][9];
|
||||
extern pending_special_type special_queue[20];
|
||||
extern std::queue<pending_special_type> special_queue;
|
||||
|
||||
extern location ul;
|
||||
extern location pc_pos[6],center;
|
||||
@@ -214,12 +215,10 @@ void draw_monsters() ////
|
||||
for (i = 0; i < univ.town->max_monst(); i++)
|
||||
if ((univ.town.monst[i].active != 0) && (univ.town.monst[i].spec_skill != 11))
|
||||
if (party_can_see_monst(i)) {
|
||||
check_if_monst_seen(univ.town.monst[i].number);
|
||||
check_if_monst_seen(univ.town.monst[i].number, univ.town.monst[i].cur_loc);
|
||||
where_draw.x = univ.town.monst[i].cur_loc.x - center.x + 4;
|
||||
where_draw.y = univ.town.monst[i].cur_loc.y - center.y + 4;
|
||||
get_monst_dims(univ.town.monst[i].number,&width,&height);
|
||||
if (point_onscreen(center,univ.town.monst[i].cur_loc) == true)
|
||||
play_see_monster_str(univ.town.monst[i].number); // TODO: This also gets called by check_if_monst_seen!
|
||||
|
||||
for (k = 0; k < width * height; k++) {
|
||||
store_loc = where_draw;
|
||||
@@ -261,7 +260,7 @@ void draw_monsters() ////
|
||||
for (i = 0; i < univ.town->max_monst(); i++)
|
||||
if ((univ.town.monst[i].active != 0) && (univ.town.monst[i].spec_skill != 11))
|
||||
if (point_onscreen(center,univ.town.monst[i].cur_loc) || party_can_see_monst(i)) {
|
||||
check_if_monst_seen(univ.town.monst[i].number);
|
||||
check_if_monst_seen(univ.town.monst[i].number,univ.town.monst[i].cur_loc);
|
||||
where_draw.x = univ.town.monst[i].cur_loc.x - center.x + 4;
|
||||
where_draw.y = univ.town.monst[i].cur_loc.y - center.y + 4;
|
||||
get_monst_dims(univ.town.monst[i].number,&width,&height);
|
||||
@@ -304,7 +303,7 @@ void draw_monsters() ////
|
||||
}
|
||||
}
|
||||
|
||||
void play_see_monster_str(unsigned short m){
|
||||
void play_see_monster_str(unsigned short m, location monst_loc) {
|
||||
short str1, str2, pic, snd, spec;
|
||||
ePicType type;
|
||||
str1 = scenario.scen_monsters[m].see_str1;
|
||||
@@ -325,16 +324,7 @@ void play_see_monster_str(unsigned short m){
|
||||
}
|
||||
// Then run the special, if any
|
||||
if(spec > -1){
|
||||
for(int i = 2; i < 20; i++){
|
||||
if(special_queue[i].spec == -1){
|
||||
special_queue[i].spec = spec;
|
||||
special_queue[i].mode = eSpecCtx::SEE_MONST;
|
||||
special_queue[i].type = 0;
|
||||
special_queue[i].trigger_time = univ.party.age;
|
||||
special_queue[i].where = loc(); // TODO: Maybe a different location should be passed?
|
||||
break;
|
||||
}
|
||||
}
|
||||
queue_special(eSpecCtx::SEE_MONST, 0, spec, monst_loc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,11 +762,11 @@ char get_fluid_trim(location where,ter_num_t ter_type)
|
||||
}
|
||||
|
||||
// Sees if party has seen a monster of this sort, gives special messages as necessary
|
||||
void check_if_monst_seen(unsigned short m_num) {
|
||||
void check_if_monst_seen(unsigned short m_num, location at) {
|
||||
// Give special messages if necessary
|
||||
if (!univ.party.m_seen[m_num]) {
|
||||
univ.party.m_seen[m_num] = true;
|
||||
play_see_monster_str(m_num);
|
||||
play_see_monster_str(m_num, at);
|
||||
}
|
||||
// Make the monster vocalize if applicable
|
||||
snd_num_t sound = scenario.scen_monsters[m_num].ambient_sound;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
void draw_one_terrain_spot (short i,short j,short terrain_to_draw);
|
||||
void draw_monsters();
|
||||
void play_see_monster_str(unsigned short m);
|
||||
void play_see_monster_str(unsigned short m, location monst_loc);
|
||||
void draw_pcs(location center,short mode);
|
||||
void draw_outd_boats(location center);
|
||||
void draw_town_boat(location center) ;
|
||||
@@ -21,7 +21,7 @@ bool is_shore(ter_num_t ter_type);
|
||||
bool is_wall(ter_num_t ter_type);
|
||||
bool is_ground(ter_num_t ter_type);
|
||||
char get_fluid_trim(location where,ter_num_t ter_type);
|
||||
void check_if_monst_seen(unsigned short m_num);
|
||||
void check_if_monst_seen(unsigned short m_num, location monst_loc);
|
||||
void play_ambient_sound();
|
||||
|
||||
void draw_items(location where);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "boe.graphics.h"
|
||||
#include "boe.text.h"
|
||||
#include "boe.items.h"
|
||||
#include "boe.specials.h"
|
||||
#include "boe.party.h"
|
||||
#include "boe.fields.h"
|
||||
#include "boe.locutils.h"
|
||||
@@ -835,40 +836,57 @@ short get_item(location place,short pc_num,bool check_container)
|
||||
|
||||
void make_town_hostile()
|
||||
{
|
||||
set_town_attitude(0, -1, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set Attitude node adapted from *i, meant to replace make_town_hostile node
|
||||
// att is any valid monster attitude (so, 0..3)
|
||||
void set_town_attitude(short lo,short hi,short att) {
|
||||
short i,num;
|
||||
bool fry_party = false;
|
||||
short a[3] = {}; // Dummy values to pass to run_special.
|
||||
|
||||
if (which_combat_type == 0)
|
||||
return;
|
||||
give_help(53,0);
|
||||
univ.town.monst.friendly = 1;
|
||||
////
|
||||
for (i = 0; i < univ.town->max_monst(); i++)
|
||||
|
||||
// Nice smart indexing, like Python :D
|
||||
if(lo <= -univ.town->max_monst())
|
||||
lo = 0;
|
||||
if(lo < 0)
|
||||
lo = univ.town->max_monst() + lo;
|
||||
if(hi <= -univ.town->max_monst())
|
||||
hi = 0;
|
||||
if(hi < 0)
|
||||
hi = univ.town->max_monst() + hi;
|
||||
if(hi < lo)
|
||||
std::swap(lo, hi);
|
||||
|
||||
for (i = lo; i <= hi; i++) {
|
||||
if ((univ.town.monst[i].active > 0) && (univ.town.monst[i].summoned == 0)){
|
||||
univ.town.monst[i].attitude = 1;
|
||||
univ.town.monst[i].attitude = att;
|
||||
num = univ.town.monst[i].number;
|
||||
// If made hostile, make mobile
|
||||
if (att == 1 || att == 3) {
|
||||
|
||||
univ.town.monst[i].mobility = 1;
|
||||
// If a "guard", give a power boost
|
||||
if (scenario.scen_monsters[num].spec_skill == 37) {
|
||||
univ.town.monst[i].active = 2;
|
||||
|
||||
// If a town, give pwoer boost
|
||||
univ.town.monst[i].health *= 3;
|
||||
univ.town.monst[i].status[eStatus::HASTE_SLOW] = 8;
|
||||
univ.town.monst[i].status[eStatus::BLESS_CURSE] = 8;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In some towns, doin' this'll getcha' killed.
|
||||
//// wedge in special
|
||||
// TODO: Resupport this!
|
||||
|
||||
if (fry_party == true) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if(univ.party[i].main_status > eMainStatus::ABSENT)
|
||||
univ.party[i].main_status = eMainStatus::ABSENT;
|
||||
stat_window = 6;
|
||||
boom_anim_active = false;
|
||||
}
|
||||
// (Or something else! Killing the party would be the responsibility of whatever special node is called.)
|
||||
if((att == 1 || att == 3) && univ.town->spec_on_hostile >= 0)
|
||||
run_special(eSpecCtx::TOWN_HOSTILE, 2, univ.town->spec_on_hostile, univ.party.p_loc, &a[0], &a[1], &a[2]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ short get_item(location place,short pc_num,bool check_container);
|
||||
short get_prot_level(short pc_num,short abil);
|
||||
|
||||
void make_town_hostile();
|
||||
void set_town_attitude(short lo,short hi,short att);
|
||||
bool display_item(location from_loc,short pc_num,short mode, bool check_container);
|
||||
short custom_choice_dialog(std::array<std::string, 6>& strs,short pic_num,ePicType pic_type,std::array<short, 3>& buttons) ;
|
||||
//short fancy_choice_dialog(short which_dlog,short parent);
|
||||
|
||||
@@ -846,6 +846,7 @@ void pause(short length)
|
||||
sf::sleep(time_in_ticks(len));
|
||||
}
|
||||
|
||||
// TODO: I think this should be in a better place, maybe in cParty?
|
||||
// stuff done legit, i.e. flags are within proper ranges for stuff done flag
|
||||
bool sd_legit(short a, short b)
|
||||
{
|
||||
|
||||
@@ -1153,50 +1153,71 @@ void poison_monst(cCreature *which_m,short how_much)
|
||||
return;
|
||||
}
|
||||
which_m->status[eStatus::POISON] = min(8, which_m->status[eStatus::POISON] + how_much);
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 4);
|
||||
if (how_much >= 0)
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 4);
|
||||
else
|
||||
monst_spell_note(which_m->number,34);
|
||||
|
||||
}
|
||||
void acid_monst(cCreature *which_m,short how_much)
|
||||
{
|
||||
magic_adjust(which_m,&how_much);
|
||||
which_m->status[eStatus::ACID] = minmax(-8,8, which_m->status[eStatus::ACID] + how_much);
|
||||
monst_spell_note(which_m->number,31);
|
||||
|
||||
if(how_much >= 0)
|
||||
monst_spell_note(which_m->number,31);
|
||||
else
|
||||
monst_spell_note(which_m->number,48);
|
||||
}
|
||||
|
||||
void slow_monst(cCreature *which_m,short how_much)
|
||||
{
|
||||
magic_adjust(which_m,&how_much);
|
||||
which_m->status[eStatus::HASTE_SLOW] = minmax(-8,8, which_m->status[eStatus::HASTE_SLOW] - how_much);
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 2);
|
||||
if (how_much >= 0)
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 2);
|
||||
else
|
||||
monst_spell_note(which_m->number,35);
|
||||
|
||||
}
|
||||
void curse_monst(cCreature *which_m,short how_much)
|
||||
{
|
||||
magic_adjust(which_m,&how_much);
|
||||
which_m->status[eStatus::BLESS_CURSE] = minmax(-8,8, which_m->status[eStatus::BLESS_CURSE] - how_much);
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 5);
|
||||
if (how_much >= 0)
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 5);
|
||||
else
|
||||
monst_spell_note(which_m->number,36);
|
||||
|
||||
}
|
||||
void web_monst(cCreature *which_m,short how_much)
|
||||
{
|
||||
magic_adjust(which_m,&how_much);
|
||||
which_m->status[eStatus::WEBS] = minmax(-8,8, which_m->status[eStatus::WEBS] + how_much);
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 19);
|
||||
if (how_much >= 0)
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 19);
|
||||
else
|
||||
monst_spell_note(which_m->number,37);
|
||||
|
||||
}
|
||||
void scare_monst(cCreature *which_m,short how_much)
|
||||
{
|
||||
magic_adjust(which_m,&how_much);
|
||||
which_m->morale = which_m->morale - how_much;
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 1);
|
||||
// TODO: I don't think there's currently any way to increase monster morale at the moment - add one!
|
||||
if(how_much >= 0)
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 1);
|
||||
else
|
||||
monst_spell_note(which_m->number,47);
|
||||
|
||||
}
|
||||
void disease_monst(cCreature *which_m,short how_much)
|
||||
{
|
||||
magic_adjust(which_m,&how_much);
|
||||
which_m->status[eStatus::DISEASE] = minmax(-8,8, which_m->status[eStatus::DISEASE] + how_much);
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 25);
|
||||
if (how_much >= 0)
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 25);
|
||||
else
|
||||
monst_spell_note(which_m->number,38);
|
||||
|
||||
}
|
||||
|
||||
@@ -1204,7 +1225,10 @@ void dumbfound_monst(cCreature *which_m,short how_much)
|
||||
{
|
||||
magic_adjust(which_m,&how_much);
|
||||
which_m->status[eStatus::DUMB] = minmax(-8,8, which_m->status[eStatus::DUMB] + how_much);
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 22);
|
||||
if (how_much >= 0)
|
||||
monst_spell_note(which_m->number,(how_much == 0) ? 10 : 22);
|
||||
else
|
||||
monst_spell_note(which_m->number,39);
|
||||
|
||||
}
|
||||
|
||||
@@ -1242,10 +1266,12 @@ void charm_monst(cCreature *which_m,short penalty,eStatus which_status,short amo
|
||||
}
|
||||
else {
|
||||
which_m->status[which_status] = amount;
|
||||
if (which_status == eStatus::ASLEEP)
|
||||
if (which_status == eStatus::ASLEEP && (amount >= 0))
|
||||
monst_spell_note(which_m->number,28);
|
||||
if (which_status == eStatus::PARALYZED)
|
||||
if (which_status == eStatus::PARALYZED && (amount >= 0))
|
||||
monst_spell_note(which_m->number,30);
|
||||
if (amount < 0)
|
||||
monst_spell_note(which_m->number,40);
|
||||
}
|
||||
//one_sound(53);
|
||||
}
|
||||
|
||||
@@ -177,8 +177,10 @@ void put_pick_spell_graphics();
|
||||
|
||||
|
||||
//mode; // 0 - prefab 1 - regular 2 - debug
|
||||
// Note: mode 1 is never used
|
||||
void init_party(short mode)
|
||||
{
|
||||
// TODO: Remove in favour of cParty constructor.
|
||||
short i,j,k,l;
|
||||
|
||||
cVehicle null_boat;// = {{0,0},{0,0},{0,0},200,false};
|
||||
@@ -252,6 +254,10 @@ void init_party(short mode)
|
||||
for (j = 0; j < 8; j++)
|
||||
univ.party.item_taken[i][j] = 0;
|
||||
|
||||
// Zero out campaign flags and pointers
|
||||
univ.party.campaign_flags.clear();
|
||||
univ.party.pointers.clear();
|
||||
|
||||
|
||||
refresh_store_items();
|
||||
|
||||
@@ -814,6 +820,8 @@ void increase_light(short amt)
|
||||
location where;
|
||||
|
||||
univ.party.light_level += amt;
|
||||
if(univ.party.light_level < 0)
|
||||
univ.party.light_level = 0;
|
||||
if (is_combat()) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if(univ.party[i].main_status == eMainStatus::ALIVE) {
|
||||
@@ -941,13 +949,23 @@ void drain_pc(short which_pc,short how_much)
|
||||
}
|
||||
}
|
||||
|
||||
short mage_lore_total()
|
||||
{
|
||||
short total = 0,i;
|
||||
// mode: 0 = total, 1 = mean, 2 = min, 3 = max
|
||||
short check_party_stat(short which_stat, short mode) {
|
||||
short total = mode == 2 ? std::numeric_limits<short>::max() : 0, num_pcs = 0;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
if(univ.party[i].main_status == eMainStatus::ALIVE)
|
||||
total += univ.party[i].skills[11];
|
||||
for(short i = 0; i < 6; i++)
|
||||
if(univ.party[i].main_status == eMainStatus::ALIVE) {
|
||||
num_pcs++;
|
||||
if(mode < 2)
|
||||
total += univ.party[i].skills[which_stat];
|
||||
else if(mode == 2)
|
||||
total = max(univ.party[i].skills[which_stat], total);
|
||||
else if(mode == 3)
|
||||
total = min(univ.party[i].skills[which_stat], total);
|
||||
}
|
||||
|
||||
if(mode == 1 && num_pcs > 0)
|
||||
total /= num_pcs;
|
||||
|
||||
return total;
|
||||
}
|
||||
@@ -3122,20 +3140,38 @@ void take_ap(short num)
|
||||
univ.party[current_pc].ap = max(0,univ.party[current_pc].ap - num);
|
||||
}
|
||||
|
||||
short cave_lore_present()
|
||||
{
|
||||
// TODO: Enumify
|
||||
// TODO: Use this to check cave lore and woodsman for the purpose of gaining food
|
||||
// (It replaces cave_lore_present() and woodsman_present(), but the latter was never used,
|
||||
// and the former was only used to help you lose less food when going over a waterfall.
|
||||
short trait_present(short which_trait) {
|
||||
short i,ret = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
if(univ.party[i].main_status == eMainStatus::ALIVE && univ.party[i].traits[4] > 0)
|
||||
if(univ.party[i].main_status == eMainStatus::ALIVE && univ.party[i].traits[which_trait] > 0)
|
||||
ret += 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
short woodsman_present()
|
||||
{
|
||||
short i,ret = 0;
|
||||
for (i = 0; i < 6; i++)
|
||||
if(univ.party[i].main_status == eMainStatus::ALIVE && univ.party[i].traits[5] > 0)
|
||||
ret += 1;
|
||||
return ret;
|
||||
short wilderness_lore_present() {
|
||||
// TODO: Add contional statement to choose between these
|
||||
// (Probably requires something added to terrain types to specify that it's cave/surface wilderness.)
|
||||
return trait_present(4); // Cave Lore
|
||||
return trait_present(5); // Woodsman
|
||||
}
|
||||
|
||||
short party_size(bool only_living) {
|
||||
short num_pcs = 0;
|
||||
for (short i = 0; i < 6; i++) {
|
||||
if (!only_living) {
|
||||
if (univ.party[i].main_status != eMainStatus::ABSENT)
|
||||
num_pcs++;
|
||||
}
|
||||
else {
|
||||
if (univ.party[i].main_status == eMainStatus::ALIVE)
|
||||
num_pcs++;
|
||||
}
|
||||
}
|
||||
|
||||
return num_pcs;
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ void restore_sp_party(short amt);
|
||||
void award_party_xp(short amt);
|
||||
void award_xp(short pc_num,short amt);
|
||||
void drain_pc(short which_pc,short how_much);
|
||||
short mage_lore_total();
|
||||
short check_party_stat(short which_stat, short mode);
|
||||
bool poison_weapon( short pc_num, short how_much,short safe);
|
||||
bool is_weapon(short pc_num,short item);
|
||||
void cast_spell(short type);
|
||||
@@ -55,10 +55,11 @@ bool damage_pc(short which_pc,short how_much,eDamageType damage_type,eRace type_
|
||||
void kill_pc(short which_pc,eMainStatus type);
|
||||
void set_pc_moves();
|
||||
void take_ap(short num);
|
||||
short cave_lore_present();
|
||||
short woodsman_present();
|
||||
short trait_present(short which_trait);
|
||||
short wilderness_lore_present();
|
||||
void print_spell_cast(short spell_num,short which);
|
||||
void put_party_in_scen(std::string scen_name);
|
||||
short party_size(bool only_living);
|
||||
|
||||
// This is defined in pc.editors.cpp since it is also used by the character editor
|
||||
bool spend_xp(short pc_num, short mode, cDialog* parent);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <queue>
|
||||
|
||||
//#include "item.h"
|
||||
|
||||
@@ -54,6 +55,7 @@ extern bool fast_bang,end_scenario;
|
||||
extern short town_type;
|
||||
extern cScenario scenario;
|
||||
extern cUniverse univ;
|
||||
extern std::queue<pending_special_type> special_queue;
|
||||
//extern piles_of_stuff_dumping_type *data_store;
|
||||
|
||||
bool can_draw_pcs = true;
|
||||
@@ -124,20 +126,22 @@ bool handle_wandering_specials (short /*which*/,short mode)
|
||||
// wanderin spec 99 -> generic spec
|
||||
{
|
||||
|
||||
// TODO: Should a better location be passed to these specials?
|
||||
// TODO: Is loc_in_sec the correct location to pass here?
|
||||
// (I'm pretty sure it is, but I should verify it somehow.)
|
||||
// (It's either that or univ.party.p_loc.)
|
||||
short s1 = 0,s2 = 0,s3 = 0;
|
||||
|
||||
if ((mode == 0) && (store_wandering_special.spec_on_meet >= 0)) { // When encountering
|
||||
run_special(eSpecCtx::OUTDOOR_ENC,1,store_wandering_special.spec_on_meet,loc(),&s1,&s2,&s3);
|
||||
run_special(eSpecCtx::OUTDOOR_ENC,1,store_wandering_special.spec_on_meet,univ.party.loc_in_sec,&s1,&s2,&s3);
|
||||
if (s1 > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((mode == 1) && (store_wandering_special.spec_on_win >= 0)) {// After defeating
|
||||
run_special(eSpecCtx::WIN_ENCOUNTER,1,store_wandering_special.spec_on_win,loc(),&s1,&s2,&s3);
|
||||
run_special(eSpecCtx::WIN_ENCOUNTER,1,store_wandering_special.spec_on_win,univ.party.loc_in_sec,&s1,&s2,&s3);
|
||||
}
|
||||
if ((mode == 2) && (store_wandering_special.spec_on_flee >= 0)) {// After fleeing like a buncha girly men
|
||||
run_special(eSpecCtx::FLEE_ENCOUNTER,1,store_wandering_special.spec_on_flee,loc(),&s1,&s2,&s3);
|
||||
run_special(eSpecCtx::FLEE_ENCOUNTER,1,store_wandering_special.spec_on_flee,univ.party.loc_in_sec,&s1,&s2,&s3);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -595,7 +599,7 @@ void use_spec_item(short item)
|
||||
short i,j,k;
|
||||
location null_loc;
|
||||
|
||||
run_special(eSpecCtx::USE_SPEC_ITEM,0,scenario.special_items[item].special,loc(),&i,&j,&k);
|
||||
run_special(eSpecCtx::USE_SPEC_ITEM,0,scenario.special_items[item].special,univ.party.p_loc,&i,&j,&k);
|
||||
|
||||
}
|
||||
|
||||
@@ -604,6 +608,7 @@ void use_item(short pc,short item)
|
||||
{
|
||||
bool take_charge = true,inept_ok = false;
|
||||
short abil,level,i,j,item_use_code,str,type,r1;
|
||||
short sp[3] = {}; // Dummy values to pass to run_special; not actually used
|
||||
eStatus which_stat;
|
||||
char to_draw[60];
|
||||
location user_loc;
|
||||
@@ -1045,6 +1050,11 @@ void use_item(short pc,short item)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ITEM_CALL_SPECIAL:
|
||||
// TODO: Should this have its own separate eSpecCtx?
|
||||
run_special(eSpecCtx::USE_SPEC_ITEM,0,str,user_loc,&sp[0],&sp[1],&sp[2]);
|
||||
break;
|
||||
|
||||
|
||||
// spell effects
|
||||
case ITEM_SPELL_FLAME:
|
||||
@@ -1671,7 +1681,8 @@ void kill_monst(cCreature *which_m,short who_killed)
|
||||
if (sd_legit(which_m->spec1,which_m->spec2) == true)
|
||||
PSD[which_m->spec1][which_m->spec2] = 1;
|
||||
|
||||
run_special(eSpecCtx::KILL_MONST,2,which_m->special_on_kill,which_m->cur_loc,&s1,&s2,&s3);
|
||||
if (which_m->special_on_kill >= 0)
|
||||
run_special(eSpecCtx::KILL_MONST,2,which_m->special_on_kill,which_m->cur_loc,&s1,&s2,&s3);
|
||||
if (which_m->radiate_1 == 15)
|
||||
run_special(eSpecCtx::KILL_MONST,0,which_m->radiate_2,which_m->cur_loc,&s1,&s2,&s3);
|
||||
|
||||
@@ -1863,7 +1874,7 @@ void special_increase_age()
|
||||
unsigned short i;
|
||||
short s1,s2,s3;
|
||||
bool redraw = false,stat_area = false;
|
||||
location null_loc;
|
||||
location null_loc; // TODO: Should we pass the party's location here? It doesn't quite make sense to me though...
|
||||
|
||||
if(is_town()) {
|
||||
for(i = 0; i < 8; i++)
|
||||
@@ -1903,6 +1914,21 @@ void special_increase_age()
|
||||
|
||||
}
|
||||
|
||||
void queue_special(eSpecCtx mode, short which_type, short spec, location spec_loc) {
|
||||
if(spec < 0) return;
|
||||
pending_special_type queued_special;
|
||||
queued_special.spec = spec;
|
||||
queued_special.where = spec_loc;
|
||||
queued_special.type = which_type;
|
||||
queued_special.mode = mode;
|
||||
// queued_special.trigger_time = univ.party.age; // Don't think this is needed after all.
|
||||
special_queue.push(queued_special);
|
||||
}
|
||||
|
||||
void run_special(pending_special_type spec, short* a, short* b, short* redraw) {
|
||||
run_special(spec.mode, spec.type, spec.spec, spec.where, a, b, redraw);
|
||||
}
|
||||
|
||||
// This is the big painful one, the main special engine
|
||||
// which_mode - says when it was called
|
||||
// 0 - out moving (a - 1 if blocked)
|
||||
@@ -1932,10 +1958,11 @@ void run_special(eSpecCtx which_mode,short which_type,short start_spec,location
|
||||
{
|
||||
short cur_spec,cur_spec_type,next_spec,next_spec_type;
|
||||
cSpecial cur_node;
|
||||
short num_nodes = 0;
|
||||
int num_nodes = 0;
|
||||
|
||||
if (special_in_progress == true) {
|
||||
giveError("The scenario called a special node while processing another special encounter. The second special will be ignored.");
|
||||
// Modify this to put a value in the special node queue instead of raising an error
|
||||
if(special_in_progress && start_spec >= 0) {
|
||||
queue_special(which_mode, which_type, start_spec, spec_loc);
|
||||
return;
|
||||
}
|
||||
special_in_progress = true;
|
||||
@@ -1955,6 +1982,25 @@ void run_special(eSpecCtx which_mode,short which_type,short start_spec,location
|
||||
next_spec = -1;
|
||||
cur_node = get_node(cur_spec,cur_spec_type);
|
||||
|
||||
// Store the special's location in reserved pointers
|
||||
univ.party.force_ptr(10, 301, 0);
|
||||
univ.party.force_ptr(11, 301, 1);
|
||||
// And put the location there
|
||||
PSD[SDF_SPEC_LOC_X] = spec_loc.x;
|
||||
PSD[SDF_SPEC_LOC_Y] = spec_loc.y;
|
||||
// (We do this here instead of before the loop, in case a queued special has a different location.)
|
||||
|
||||
// Convert pointer values to reference values
|
||||
if(cur_node.sd1 < -1) cur_node.sd1 = univ.party.get_ptr(-cur_node.sd1);
|
||||
if(cur_node.sd2 < -1) cur_node.sd2 = univ.party.get_ptr(-cur_node.sd2);
|
||||
if(cur_node.ex1a < -1) cur_node.ex1a = univ.party.get_ptr(-cur_node.ex1a);
|
||||
if(cur_node.ex1b < -1) cur_node.ex1a = univ.party.get_ptr(-cur_node.ex1b);
|
||||
if(cur_node.ex1c < -1) cur_node.ex1a = univ.party.get_ptr(-cur_node.ex1c);
|
||||
if(cur_node.ex2a < -1) cur_node.ex1a = univ.party.get_ptr(-cur_node.ex2a);
|
||||
if(cur_node.ex2b < -1) cur_node.ex1a = univ.party.get_ptr(-cur_node.ex2b);
|
||||
if(cur_node.ex2c < -1) cur_node.ex1a = univ.party.get_ptr(-cur_node.ex2c);
|
||||
// TODO: Should pointers be allowed in message, pict, or jumpto as well?
|
||||
|
||||
//print_nums(1111,cur_spec_type,cur_node.type);
|
||||
|
||||
if(cur_node.type == eSpecType::ERROR) {
|
||||
@@ -1991,8 +2037,17 @@ void run_special(eSpecCtx which_mode,short which_type,short start_spec,location
|
||||
|
||||
num_nodes++;
|
||||
|
||||
if(next_spec == -1 && !special_queue.empty()) {
|
||||
pending_special_type pending = special_queue.front();
|
||||
which_mode = pending.mode;
|
||||
which_type = pending.type;
|
||||
next_spec = pending.spec;
|
||||
spec_loc = pending.where;
|
||||
special_queue.pop();
|
||||
}
|
||||
|
||||
if(check_for_interrupt()){
|
||||
giveError("The special encounter was interrupted. The scenario may be in an unexpected state; it is recommended that you reload from a saved game.");
|
||||
add_string_to_buf("The special encounter was interrupted. The scenario may be in an unexpected state; it is recommended that you reload from a saved game.", 3);
|
||||
next_spec = -1;
|
||||
}
|
||||
}
|
||||
@@ -2062,9 +2117,9 @@ void general_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
get_strs(str1,str2, cur_spec_type,cur_node.m1 + mess_adj[cur_spec_type],
|
||||
cur_node.m2 + mess_adj[cur_spec_type]);
|
||||
if (cur_node.m1 >= 0)
|
||||
ASB(str1.c_str());
|
||||
ASB(str1.c_str(), 4);
|
||||
if (cur_node.m2 >= 0)
|
||||
ASB(str2.c_str());
|
||||
ASB(str2.c_str(), 4);
|
||||
break;
|
||||
case eSpecType::FLIP_SDF:
|
||||
setsd(cur_node.sd1,cur_node.sd2,
|
||||
@@ -2105,7 +2160,7 @@ void general_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
check_mess = true;
|
||||
if (spec.ex1a != minmax(0,scenario.num_towns - 1,spec.ex1a))
|
||||
giveError("Town out of range.");
|
||||
else univ.party.can_find_town[spec.ex1a] = (spec.ex1b == 0) ? 0 : 1;
|
||||
else univ.party.can_find_town[spec.ex1a] = spec.ex2a;
|
||||
*redraw = true;
|
||||
break;
|
||||
case eSpecType::MAJOR_EVENT_OCCURRED:
|
||||
@@ -2160,6 +2215,24 @@ void general_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
case eSpecType::END_SCENARIO:
|
||||
end_scenario = true;
|
||||
break;
|
||||
case eSpecType::SET_POINTER:
|
||||
if(spec.ex1a < 0)
|
||||
giveError("Attempted to assign a pointer out of range (100..199)");
|
||||
else try {
|
||||
if(spec.sd1 < 0 && spec.sd2 < 0)
|
||||
univ.party.clear_ptr(spec.ex1a);
|
||||
else univ.party.set_ptr(spec.sd1,spec.sd2,spec.ex1a);
|
||||
} catch(std::range_error x) {
|
||||
giveError(x.what());
|
||||
}
|
||||
break;
|
||||
case eSpecType::SET_CAMP_FLAG:
|
||||
if(!sd_legit(spec.sd1,spec.sd2))
|
||||
giveError("Stuff Done flag out of range (x - 0..299, y - 0..49).");
|
||||
else {
|
||||
set_campaign_flag(spec.sd1,spec.sd2,spec.ex1a,spec.ex1b,spec.m1,spec.ex2a);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (check_mess == true) {
|
||||
handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b);
|
||||
@@ -2388,6 +2461,11 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
switch (cur_node.type) {
|
||||
case eSpecType::SELECT_PC:
|
||||
check_mess = false;
|
||||
// If this <= 0, pick PC normally
|
||||
// TODO: I think this is for compatibility with old scenarios? If so, remove it and just convert data on load.
|
||||
// (Actually, I think the only compatibility thing is that it's <= instead of ==)
|
||||
if (spec.ex2a <= 0) {
|
||||
|
||||
if (spec.ex1a == 2)
|
||||
current_pc_picked_in_spec_enc = -1;
|
||||
else if (spec.ex1a == 1) {
|
||||
@@ -2402,6 +2480,32 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
}
|
||||
if (i == 6)// && (spec.ex1b >= 0))
|
||||
*next_spec = spec.ex1b;
|
||||
|
||||
}
|
||||
else if(spec.ex2a > 10 || spec.ex2a <= 16) {
|
||||
// Select a specific PC
|
||||
short pc = spec.ex2a - 11;
|
||||
// Honour the request for alive PCs only.
|
||||
if(spec.ex1a == 1 || univ.party[pc].main_status == eMainStatus::ALIVE)
|
||||
current_pc_picked_in_spec_enc = pc;
|
||||
} else {
|
||||
// Pick random PC (from *i)
|
||||
// TODO: What if spec.ex1a == 2?
|
||||
|
||||
if (spec.ex1a == 0) {
|
||||
short pc_alive = 0;
|
||||
while (pc_alive == 0) {
|
||||
i = get_ran(1,0,5);
|
||||
if (univ.party[i].main_status == eMainStatus::ALIVE)
|
||||
pc_alive = 1;
|
||||
}
|
||||
current_pc_picked_in_spec_enc = i;
|
||||
}
|
||||
else {
|
||||
i = get_ran(1,0,5);
|
||||
current_pc_picked_in_spec_enc = i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case eSpecType::DAMAGE:
|
||||
{
|
||||
@@ -2416,16 +2520,36 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
break;
|
||||
}
|
||||
case eSpecType::AFFECT_HP:
|
||||
if (spec.ex2a < 0) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i))
|
||||
univ.party[i].cur_health = minmax(0,univ.party[i].max_health,
|
||||
univ.party[i].cur_health + spec.ex1a * (spec.ex1b ? -1: 1));
|
||||
}
|
||||
else {
|
||||
univ.town.monst[spec.ex2a].health = minmax(0, univ.town.monst[spec.ex2a].m_health,
|
||||
univ.town.monst[spec.ex2a].health + spec.ex1a * ((spec.ex1b != 0) ? -1: 1));
|
||||
if (spec.ex1b == 0)
|
||||
monst_spell_note(univ.town.monst[spec.ex2a].number,41);
|
||||
else
|
||||
monst_spell_note(univ.town.monst[spec.ex2a].number,42);
|
||||
}
|
||||
break;
|
||||
case eSpecType::AFFECT_SP:
|
||||
if (spec.ex2a < 0) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i))
|
||||
univ.party[i].cur_sp = minmax(0, univ.party[i].max_sp,
|
||||
univ.party[i].cur_sp + spec.ex1a * ((spec.ex1b != 0) ? -1: 1));
|
||||
}
|
||||
else {
|
||||
univ.town.monst[spec.ex2a].mp = minmax(0, univ.town.monst[spec.ex2a].max_mp,
|
||||
univ.town.monst[spec.ex2a].mp + spec.ex1a * ((spec.ex1b != 0) ? -1: 1));
|
||||
if (spec.ex1b == 0)
|
||||
monst_spell_note(univ.town.monst[spec.ex2a].number,43);
|
||||
else
|
||||
monst_spell_note(univ.town.monst[spec.ex2a].number,44);
|
||||
}
|
||||
break;
|
||||
case eSpecType::AFFECT_XP:
|
||||
for (i = 0; i < 6; i++)
|
||||
@@ -2440,12 +2564,14 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
univ.party[i].skill_pts + spec.ex1a * ((spec.ex1b != 0) ? -1: 1));
|
||||
break;
|
||||
case eSpecType::AFFECT_DEADNESS:
|
||||
if (spec.ex2a < 0) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i)) {
|
||||
if (spec.ex1b == 0) {
|
||||
if ((univ.party[i].main_status > eMainStatus::ABSENT) && (univ.party[i].main_status < eMainStatus::SPLIT))
|
||||
univ.party[i].main_status = eMainStatus::ALIVE;
|
||||
}
|
||||
else if (univ.party[i].main_status == eMainStatus::ABSENT);
|
||||
else switch(spec.ex1a){
|
||||
// When passed to kill_pc, the SPLIT party status actually means "no saving throw".
|
||||
case 0:
|
||||
@@ -2457,8 +2583,26 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
}
|
||||
}
|
||||
*redraw = 1;
|
||||
}
|
||||
else {
|
||||
// Kill monster
|
||||
if ((univ.town.monst[spec.ex2a].active > 0) && (spec.ex1b > 0)) {
|
||||
// If dead/dust actually kill, if stone just erase
|
||||
if (spec.ex1a < 2) {
|
||||
kill_monst(&univ.town.monst[spec.ex2a],7);
|
||||
monst_spell_note(univ.town.monst[spec.ex2a].number,46);
|
||||
}
|
||||
univ.town.monst[spec.ex2a].active = 0;
|
||||
}
|
||||
// Bring back to life
|
||||
if ((univ.town.monst[spec.ex2a].active == 0) && (spec.ex1b == 0)) {
|
||||
univ.town.monst[spec.ex2a].active = 1;
|
||||
monst_spell_note(univ.town.monst[spec.ex2a].number,45);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case eSpecType::AFFECT_POISON:
|
||||
if (spec.ex2a < 0) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i)) {
|
||||
if (spec.ex1b == 0) {
|
||||
@@ -2466,8 +2610,18 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
}
|
||||
else poison_pc(i,spec.ex1a);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (univ.town.monst[spec.ex2a].active > 0) {
|
||||
short alvl = spec.ex1a;
|
||||
if (spec.ex1b == 0)
|
||||
alvl = -1*alvl;
|
||||
poison_monst(&univ.town.monst[spec.ex2a],alvl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case eSpecType::AFFECT_SPEED:
|
||||
if (spec.ex2a < 0) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i)) {
|
||||
if (spec.ex1b == 0) {
|
||||
@@ -2475,6 +2629,15 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
}
|
||||
else slow_pc(i,spec.ex1a);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (univ.town.monst[spec.ex2a].active > 0) {
|
||||
short alvl = spec.ex1a;
|
||||
if (spec.ex1b == 0)
|
||||
alvl = -1*alvl;
|
||||
slow_monst(&univ.town.monst[spec.ex2a],alvl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case eSpecType::AFFECT_INVULN:
|
||||
for (i = 0; i < 6; i++)
|
||||
@@ -2487,14 +2650,34 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
affect_pc(i,eStatus::MAGIC_RESISTANCE,spec.ex1a * ((spec.ex1b != 0) ? -1: 1));
|
||||
break;
|
||||
case eSpecType::AFFECT_WEBS:
|
||||
if (spec.ex2a < 0) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i))
|
||||
affect_pc(i,eStatus::WEBS,spec.ex1a * ((spec.ex1b != 0) ? -1: 1));
|
||||
}
|
||||
else {
|
||||
if (univ.town.monst[spec.ex2a].active > 0) {
|
||||
short alvl = spec.ex1a;
|
||||
if (spec.ex1b == 0)
|
||||
alvl = -1*alvl;
|
||||
web_monst(&univ.town.monst[spec.ex2a],alvl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case eSpecType::AFFECT_DISEASE:
|
||||
if (spec.ex2a < 0) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i))
|
||||
affect_pc(i,eStatus::DISEASE,spec.ex1a * ((spec.ex1b != 0) ? 1: -1));
|
||||
}
|
||||
else {
|
||||
if (univ.town.monst[spec.ex2a].active > 0) {
|
||||
short alvl = spec.ex1a;
|
||||
if (spec.ex1b == 0)
|
||||
alvl = -1*alvl;
|
||||
disease_monst(&univ.town.monst[spec.ex2a],alvl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case eSpecType::AFFECT_SANCTUARY:
|
||||
for (i = 0; i < 6; i++)
|
||||
@@ -2502,16 +2685,37 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
affect_pc(i,eStatus::INVISIBLE,spec.ex1a * ((spec.ex1b != 0) ? -1: 1));
|
||||
break;
|
||||
case eSpecType::AFFECT_CURSE_BLESS:
|
||||
if (spec.ex2a < 0) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i))
|
||||
affect_pc(i,eStatus::BLESS_CURSE,spec.ex1a * ((spec.ex1b != 0) ? -1: 1));
|
||||
}
|
||||
else {
|
||||
if (univ.town.monst[spec.ex2a].active > 0) {
|
||||
short alvl = spec.ex1a;
|
||||
if (spec.ex1b == 0)
|
||||
alvl = -1*alvl;
|
||||
curse_monst(&univ.town.monst[spec.ex2a],alvl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case eSpecType::AFFECT_DUMBFOUND:
|
||||
if (spec.ex2a < 0) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i))
|
||||
affect_pc(i,eStatus::DUMB,spec.ex1a * ((spec.ex1b == 0) ? -1: 1));
|
||||
}
|
||||
else {
|
||||
if (univ.town.monst[spec.ex2a].active > 0) {
|
||||
short alvl = spec.ex1a;
|
||||
if (spec.ex1b == 0)
|
||||
alvl = -1*alvl;
|
||||
dumbfound_monst(&univ.town.monst[spec.ex2a],alvl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case eSpecType::AFFECT_SLEEP:
|
||||
if (spec.ex2a < 0) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i)) {
|
||||
if (spec.ex1b == 0) {
|
||||
@@ -2519,8 +2723,18 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
}
|
||||
else sleep_pc(i,spec.ex1a,eStatus::ASLEEP,10);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (univ.town.monst[spec.ex2a].active > 0) {
|
||||
short alvl = spec.ex1a;
|
||||
if (spec.ex1b == 0)
|
||||
alvl = -1*alvl;
|
||||
charm_monst(&univ.town.monst[spec.ex2a],0,eStatus::ASLEEP,alvl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case eSpecType::AFFECT_PARALYSIS:
|
||||
if (spec.ex2a < 0) {
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i)) {
|
||||
if (spec.ex1b == 0) {
|
||||
@@ -2528,6 +2742,15 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
}
|
||||
else sleep_pc(i,spec.ex1a,eStatus::PARALYZED,10);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (univ.town.monst[spec.ex2a].active > 0) {
|
||||
short alvl = spec.ex1a;
|
||||
if (spec.ex1b == 0)
|
||||
alvl = -1*alvl;
|
||||
charm_monst(&univ.town.monst[spec.ex2a],0,eStatus::PARALYZED,alvl);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case eSpecType::AFFECT_STAT:
|
||||
if (spec.ex2a != minmax(0,18,spec.ex2a)) {
|
||||
@@ -2540,22 +2763,22 @@ void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
univ.party[i].skills[spec.ex2a] + spec.ex1a * ((spec.ex1b != 0) ? -1: 1));
|
||||
break;
|
||||
case eSpecType::AFFECT_MAGE_SPELL:
|
||||
if (spec.ex1a != minmax(0,31,spec.ex1a)) {
|
||||
giveError("Mage spell is out of range (0 - 31). See docs.");
|
||||
if (spec.ex1a != minmax(0,61,spec.ex1a)) {
|
||||
giveError("Mage spell is out of range (0 - 61). See docs.");
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i))
|
||||
univ.party[i].mage_spells[spec.ex1a + 30] = true;
|
||||
univ.party[i].mage_spells[spec.ex1a] = spec.ex1b;
|
||||
break;
|
||||
case eSpecType::AFFECT_PRIEST_SPELL:
|
||||
if (spec.ex1a != minmax(0,31,spec.ex1a)) {
|
||||
giveError("Priest spell is out of range (0 - 31). See docs.");
|
||||
if (spec.ex1a != minmax(0,61,spec.ex1a)) {
|
||||
giveError("Priest spell is out of range (0 - 61). See docs.");
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < 6; i++)
|
||||
if ((pc < 0) || (pc == i))
|
||||
univ.party[i].priest_spells[spec.ex1a + 30] = true;
|
||||
univ.party[i].priest_spells[spec.ex1a] = spec.ex1b;
|
||||
break;
|
||||
case eSpecType::AFFECT_GOLD:
|
||||
if (spec.ex1b == 0)
|
||||
@@ -2731,34 +2954,74 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
if (calc_day() >= spec.ex1a)
|
||||
*next_spec = spec.ex1b;
|
||||
break;
|
||||
case eSpecType::IF_BARRELS:
|
||||
case eSpecType::IF_OBJECTS:
|
||||
if(spec.ex1a == 0) {
|
||||
for (j = 0; j < univ.town->max_dim(); j++)
|
||||
for (k = 0; k < univ.town->max_dim(); k++)
|
||||
if (univ.town.is_barrel(j,k))
|
||||
*next_spec = spec.ex1b;
|
||||
break;
|
||||
case eSpecType::IF_CRATES:
|
||||
} else if(spec.ex1a == 1) {
|
||||
for (j = 0; j < univ.town->max_dim(); j++)
|
||||
for (k = 0; k < univ.town->max_dim(); k++)
|
||||
if (univ.town.is_crate(j,k))
|
||||
*next_spec = spec.ex1b;
|
||||
}
|
||||
// TODO: Are there other object types to account for?
|
||||
// TODO: Allow restricting to a specific rect
|
||||
break;
|
||||
case eSpecType::IF_PARTY_SIZE:
|
||||
if (spec.ex2a < 1) {
|
||||
if (party_size(spec.ex2b) == spec.ex1a)
|
||||
*next_spec = spec.ex1b;
|
||||
}
|
||||
else {
|
||||
if (party_size(spec.ex2b) >= spec.ex1a)
|
||||
*next_spec = spec.ex1b;
|
||||
}
|
||||
break;
|
||||
case eSpecType::IF_EVENT_OCCURRED:
|
||||
if (day_reached(spec.ex1a,spec.ex1b) == true)
|
||||
*next_spec = spec.ex2b;
|
||||
break;
|
||||
case eSpecType::IF_HAS_CAVE_LORE:
|
||||
for (i = 0; i < 6; i++)
|
||||
if(univ.party[i].main_status == eMainStatus::ALIVE && univ.party[i].traits[4] > 0)
|
||||
*next_spec = spec.ex1b;
|
||||
case eSpecType::IF_SPECIES:
|
||||
if(spec.ex1a < 0 || spec.ex1a > 2) break; // TODO: Should we allow monster races too?
|
||||
i = 0;
|
||||
j = min(spec.ex2a,party_size(0));
|
||||
if (j < 1)
|
||||
j = 1;
|
||||
for (i = 0; i < 6; i++) {
|
||||
if ((univ.party[i].main_status == eMainStatus::ALIVE) && (univ.party[i].race == eRace(spec.ex1a)))
|
||||
i++;
|
||||
}
|
||||
if (i >= j)
|
||||
*next_spec = spec.ex1b;
|
||||
break;
|
||||
case eSpecType::IF_HAS_WOODSMAN:
|
||||
for (i = 0; i < 6; i++)
|
||||
if(univ.party[i].main_status == eMainStatus::ALIVE && univ.party[i].traits[5] > 0)
|
||||
*next_spec = spec.ex1b;
|
||||
case eSpecType::IF_TRAIT:
|
||||
j = min(spec.ex2a,party_size(0));
|
||||
if (j < 1)
|
||||
j = 1;
|
||||
for (i = 0; i < 6; i++) {
|
||||
if(univ.party[i].main_status == eMainStatus::ALIVE && univ.party[i].traits[spec.ex1a] > 0)
|
||||
i++;
|
||||
}
|
||||
if (trait_present(spec.ex1a) >= j)
|
||||
*next_spec = spec.ex1b;
|
||||
break;
|
||||
case eSpecType::IF_ENOUGH_MAGE_LORE:
|
||||
if (mage_lore_total() >= spec.ex1a)
|
||||
case eSpecType::IF_STATISTIC:
|
||||
if(spec.ex2b == -1) {
|
||||
// Check specific PC's stat (uses the active PC from Select PC node)
|
||||
short pc;
|
||||
if(univ.party.is_split())
|
||||
pc = univ.party.pc_present();
|
||||
if(pc == 6 && univ.party.pc_present(current_pc_picked_in_spec_enc))
|
||||
pc = current_pc_picked_in_spec_enc;
|
||||
if(pc != 6) {
|
||||
if(univ.party[pc].skills[spec.ex2a] >= spec.ex1a)
|
||||
*next_spec = spec.ex1b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(check_party_stat(spec.ex2a, spec.ex2b) >= spec.ex1a)
|
||||
*next_spec = spec.ex1b;
|
||||
break;
|
||||
case eSpecType::IF_TEXT_RESPONSE:
|
||||
@@ -2925,7 +3188,7 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
return;
|
||||
switch (cur_node.type) {
|
||||
case eSpecType::MAKE_TOWN_HOSTILE:
|
||||
make_town_hostile();
|
||||
set_town_attitude(spec.ex1a,spec.ex1b,spec.ex2a);
|
||||
break;
|
||||
case eSpecType::TOWN_CHANGE_TER:
|
||||
set_terrain(l,spec.ex2a);
|
||||
@@ -3242,7 +3505,19 @@ void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
case eSpecType::TOWN_TIMER_START:
|
||||
univ.party.start_timer(spec.ex1a, spec.ex1b, 1);
|
||||
break;
|
||||
}
|
||||
// OBoE: Change town lighting
|
||||
case eSpecType::TOWN_CHANGE_LIGHTING:
|
||||
// Change bulk town lighting
|
||||
if ((spec.ex1a >= 0) && (spec.ex1a <= 3))
|
||||
univ.town->lighting_type = (eLighting) spec.ex1a;
|
||||
// Change party light level
|
||||
if (spec.ex2a > 0) {
|
||||
if (spec.ex2b == 0)
|
||||
increase_light(spec.ex2a);
|
||||
else increase_light(-spec.ex2a);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (check_mess == true) {
|
||||
handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b);
|
||||
}
|
||||
@@ -3552,3 +3827,25 @@ void get_strs(std::string& str1,std::string& str2,short cur_type,short which_str
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This function sets/retrieves values to/from campaign flags
|
||||
void set_campaign_flag(short sdf_a, short sdf_b, short cpf_a, short cpf_b, short str, bool get_send) {
|
||||
// get_send = false: Send value in SDF to Campaign Flag
|
||||
// get_send = true: Retrieve value from Campaign Flag and put in SDF
|
||||
try {
|
||||
if(str >= 0) {
|
||||
std::string cp_id = scenario.scen_strs(str);
|
||||
if(get_send)
|
||||
univ.party.stuff_done[sdf_a][sdf_b] = univ.party.cpn_flag(cpf_a, cpf_b, cp_id);
|
||||
else
|
||||
univ.party.cpn_flag(cpf_a, cpf_b, cp_id) = univ.party.stuff_done[sdf_a][sdf_b];
|
||||
} else {
|
||||
if(get_send)
|
||||
univ.party.stuff_done[sdf_a][sdf_b] = univ.party.cpn_flag(cpf_a, cpf_b);
|
||||
else
|
||||
univ.party.cpn_flag(cpf_a, cpf_b) = univ.party.stuff_done[sdf_a][sdf_b];
|
||||
}
|
||||
} catch(std::range_error x) {
|
||||
giveError(x.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ void fade_party();
|
||||
void change_level(short town_num,short x,short y);
|
||||
void push_things();
|
||||
void special_increase_age();
|
||||
void queue_special(eSpecCtx mode, short which_type, short spec, location spec_loc);
|
||||
void run_special(eSpecCtx which_mode,short which_type,short start_spec,location spec_loc,short *a,short *b,short *redraw);
|
||||
void run_special(pending_special_type spec, short* a, short* b, short* redraw);
|
||||
cSpecial get_node(short cur_spec,short cur_spec_type);
|
||||
void general_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
short *next_spec,short *next_spec_type,short *a,short *b,short *redraw);
|
||||
@@ -37,3 +39,5 @@ void rect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
short *next_spec,short *next_spec_type,short *a,short *b,short *redraw);
|
||||
void outdoor_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
||||
short *next_spec,short *next_spec_type,short *a,short *b,short *redraw);
|
||||
|
||||
void set_campaign_flag(short sdf_a, short sdf_b, short cpf_a, short cpf_b, short str, bool get_send);
|
||||
|
||||
@@ -1182,6 +1182,51 @@ void monst_spell_note(m_num_t number,short which_mess)
|
||||
case 33:
|
||||
msg = " " + msg + " summons aid. ";
|
||||
break;
|
||||
case 34:
|
||||
msg = " " + msg + " is cured.";
|
||||
break;
|
||||
case 35:
|
||||
msg = " " + msg + " is hasted.";
|
||||
break;
|
||||
case 36:
|
||||
msg = " " + msg + " is blessed.";
|
||||
break;
|
||||
case 37:
|
||||
msg = " " + msg + " cleans webs.";
|
||||
break;
|
||||
case 38:
|
||||
msg = " " + msg + " feels better.";
|
||||
break;
|
||||
case 39:
|
||||
msg = " " + msg + " mind cleared.";
|
||||
break;
|
||||
case 40:
|
||||
msg = " " + msg + " feels alert.";
|
||||
break;
|
||||
case 41:
|
||||
msg = " " + msg + " is healed.";
|
||||
break;
|
||||
case 42:
|
||||
msg = " " + msg + " drained of health.";
|
||||
break;
|
||||
case 43:
|
||||
msg = " " + msg + " magic recharged.";
|
||||
break;
|
||||
case 44:
|
||||
msg = " " + msg + " drained of magic.";
|
||||
break;
|
||||
case 45:
|
||||
msg = " " + msg + " returns to life!";
|
||||
break;
|
||||
case 46:
|
||||
msg = " " + msg + " dies.";
|
||||
break;
|
||||
case 47:
|
||||
msg = " " + msg + " rallies its courage.";
|
||||
break;
|
||||
case 48:
|
||||
msg = " " + msg + " cleans off acid.";
|
||||
break;
|
||||
}
|
||||
|
||||
if (which_mess > 0)
|
||||
@@ -1255,13 +1300,28 @@ short print_terrain(location space)
|
||||
}
|
||||
|
||||
|
||||
void add_string_to_buf(std::string str)
|
||||
void add_string_to_buf(std::string str, unsigned short indent)
|
||||
{
|
||||
if(overall_mode == MODE_STARTUP)
|
||||
return;
|
||||
|
||||
if(str == "") return;
|
||||
|
||||
if(indent && str.find_last_not_of(' ') > 48) {
|
||||
if(indent > 20) indent = 20;
|
||||
size_t split = str.find_last_of(' ', 49);
|
||||
add_string_to_buf(str.substr(0,split));
|
||||
str = str.substr(split);
|
||||
while(str.find_last_not_of(' ') > 48 - indent) {
|
||||
std::string wrap(indent, ' ');
|
||||
split = str.find_last_of(' ', 49 - indent);
|
||||
wrap += str.substr(0,split);
|
||||
str = str.substr(split);
|
||||
add_string_to_buf(wrap);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
text_sbar->setPosition(58); // TODO: This seems oddly specific
|
||||
if (buf_pointer == mark_where_printing_long) {
|
||||
printing_long = true;
|
||||
|
||||
@@ -33,7 +33,7 @@ void monst_damaged_mes(short which_m,short how_much,short how_much_spec);
|
||||
void monst_killed_mes(short which_m);
|
||||
void print_nums(short a,short b,short c);
|
||||
short print_terrain(location space);
|
||||
void add_string_to_buf(std::string str);
|
||||
void add_string_to_buf(std::string str, unsigned short indent = 0); // Set second paramater to nonzero to auto-split the line if it's too long
|
||||
void init_buf();
|
||||
void print_buf () ;
|
||||
void restart_printing();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <queue>
|
||||
|
||||
//#include "item.h"
|
||||
|
||||
@@ -45,7 +46,7 @@ extern short store_current_pc,current_ground;
|
||||
//extern pascal bool cd_event_filter();
|
||||
extern eGameMode store_pre_shop_mode,store_pre_talk_mode;
|
||||
//extern location monster_targs[60];
|
||||
extern pending_special_type special_queue[20];
|
||||
extern std::queue<pending_special_type> special_queue;
|
||||
|
||||
extern bool map_visible,diff_depth_ok,belt_present;
|
||||
extern sf::RenderWindow mini_map;
|
||||
@@ -715,29 +716,12 @@ location end_town_mode(short switching_level,location destination) // returns n
|
||||
return to_return;
|
||||
}
|
||||
|
||||
// actually, entry_dir is non zero is town is dead - kludge!
|
||||
void handle_town_specials(short /*town_number*/, short entry_dir,location /*start_loc*/) {
|
||||
|
||||
//if (entry_dir > 0)
|
||||
// run_special(5,2,univ.town.town.spec_on_entry_if_dead,start_loc,&s1,&s2,&s3);
|
||||
//else run_special(5,2,univ.town.town.spec_on_entry,start_loc,&s1,&s2,&s3);
|
||||
if (entry_dir > 0)
|
||||
special_queue[0].spec = univ.town->spec_on_entry_if_dead;
|
||||
else special_queue[0].spec = univ.town->spec_on_entry;
|
||||
special_queue[0].where = univ.town.p_loc;
|
||||
special_queue[0].type = 2;
|
||||
special_queue[0].mode = eSpecCtx::ENTER_TOWN;
|
||||
special_queue[0].trigger_time = univ.party.age; // TODO: Simply pushing into slot 0 seems like a bad idea
|
||||
void handle_town_specials(short /*town_number*/, bool town_dead,location /*start_loc*/) {
|
||||
queue_special(eSpecCtx::ENTER_TOWN, 2, town_dead ? univ.town->spec_on_entry_if_dead : univ.town->spec_on_entry, univ.town.p_loc);
|
||||
}
|
||||
|
||||
void handle_leave_town_specials(short /*town_number*/, short which_spec,location /*start_loc*/) {
|
||||
|
||||
//run_special(6,2,which_spec,start_loc,&s1,&s2,&s3);
|
||||
special_queue[1].spec = which_spec;
|
||||
special_queue[1].where = univ.party.p_loc;
|
||||
special_queue[1].type = 2;
|
||||
special_queue[1].mode = eSpecCtx::LEAVE_TOWN;
|
||||
special_queue[1].trigger_time = univ.party.age; // TODO: Simply pushing into slot 1 seems like a bad idea
|
||||
queue_special(eSpecCtx::LEAVE_TOWN, 2, which_spec, univ.party.p_loc);
|
||||
}
|
||||
|
||||
bool abil_exists(short abil) // use when univ.out.outdoors
|
||||
|
||||
@@ -6,7 +6,7 @@ void start_town_mode(short which_town, short entry_dir);
|
||||
void terrain_under_rentar();
|
||||
location end_town_mode(short switching_level,location destination); // returns new party location
|
||||
void handle_leave_town_specials(short town_number, short which_spec,location start_loc) ;
|
||||
void handle_town_specials(short town_number, short entry_dir,location start_loc) ;
|
||||
void handle_town_specials(short town_number, bool town_dead,location start_loc) ;
|
||||
bool abil_exists(short abil) ;
|
||||
|
||||
void start_town_combat(short direction);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "dlogutil.h"
|
||||
#include "classes.h"
|
||||
#include "oldstructs.h"
|
||||
#include "fileio.h"
|
||||
@@ -208,6 +209,7 @@ bool cParty::start_timer(short time, short node, short type){
|
||||
}
|
||||
|
||||
void cParty::writeTo(std::ostream& file){
|
||||
file << "CREATEVERSION" << OBOE_CURRENT_VERSION << '\n';
|
||||
file << "AGE " << age << '\n';
|
||||
file << "GOLD " << gold << '\n';
|
||||
file << "FOOD " << food << '\n';
|
||||
@@ -215,7 +217,7 @@ void cParty::writeTo(std::ostream& file){
|
||||
for(int j = 0; j < 50; j++)
|
||||
if(stuff_done[i][j] > 0)
|
||||
file << "SDF " << i << ' ' << j << ' ' << unsigned(stuff_done[i][j]) << '\n';
|
||||
for(ptrIter iter = pointers.begin(); iter != pointers.end(); iter++)
|
||||
for(auto iter = pointers.begin(); iter != pointers.end(); iter++)
|
||||
file << "POINTER " << iter->first << ' ' << iter->second.first << ' ' << iter->second.second << '\n';
|
||||
for(int i = 0; i < 200; i++)
|
||||
if(item_taken[i][0] > 0 || item_taken[i][1] > 0 || item_taken[i][2] > 0 || item_taken[i][3] > 0 ||
|
||||
@@ -270,10 +272,43 @@ void cParty::writeTo(std::ostream& file){
|
||||
for(unsigned int i = 0; i < 250; i++)
|
||||
if(graphicUsed[i])
|
||||
file << "GRAPHIC " << i << '\n';
|
||||
for(campIter iter = campaign_flags.begin(); iter != campaign_flags.end(); iter++){
|
||||
for(unsigned int i = 0; i < iter->second.size(); i++)
|
||||
if(iter->second[i] > 0)
|
||||
file << "CAMPAIGN \"" << iter->first << "\" " << i << ' ' << iter->second[i] << '\n';
|
||||
for(auto iter = campaign_flags.begin(); iter != campaign_flags.end(); iter++){
|
||||
std::string campaign_id = iter->first;
|
||||
if(campaign_id.find_first_of(' ') != std::string::npos || campaign_id[0] == '"' || campaign_id[0] == '\'') {
|
||||
// The string contains spaces or starts with a quote, so quote it.
|
||||
// We may have to escape quotes or backslashes.
|
||||
int apos = 0, quot = 0, bslash = 0;
|
||||
std::for_each(campaign_id.begin(), campaign_id.end(), [&apos,",&bslash](char c) {
|
||||
if(c == '\'') apos++;
|
||||
if(c == '"') quot++;
|
||||
if(c == '\\') bslash++;
|
||||
});
|
||||
char quote_c;
|
||||
// Surround it in whichever quote character appears fewer times.
|
||||
if(quot < apos) quote_c = '"';
|
||||
else quote_c = '\'';
|
||||
// Let's create this string to initially have the required size.
|
||||
std::string temp;
|
||||
size_t quoted_len = campaign_id.length() + std::min(quot,apos) + bslash + 2;
|
||||
temp.reserve(quoted_len);
|
||||
temp += quote_c;
|
||||
for(size_t i = 0; i < campaign_id.length(); i++) {
|
||||
if(campaign_id[i] == quote_c) {
|
||||
temp += '\\';
|
||||
temp += quote_c;
|
||||
} else if(campaign_id[i] == '\\')
|
||||
temp += "\\\\";
|
||||
else temp += campaign_id[i];
|
||||
}
|
||||
temp += quote_c;
|
||||
campaign_id.swap(temp);
|
||||
}
|
||||
// Okay, we have the campaign ID in a state such that reading it back in will restore the original ID.
|
||||
// Now output any flags that are set for this campaign.
|
||||
for(unsigned int i = 0; i < 25; i++)
|
||||
for(unsigned int j = 0; j < 20; j++)
|
||||
if(iter->second.idx[i][j] > 0)
|
||||
file << "CAMPAIGN " << campaign_id << ' ' << i << ' ' << j << ' ' << unsigned(iter->second.idx[i][j]) << '\n';
|
||||
}
|
||||
file << '\f';
|
||||
for(int i = 0; i < 30; i++){
|
||||
@@ -393,6 +428,12 @@ void cParty::readFrom(std::istream& file){
|
||||
unsigned int n;
|
||||
sin >> i >> j >> n;
|
||||
stuff_done[i][j] = n;
|
||||
} else if(cur == "CREATEVERSION") {
|
||||
unsigned long long version;
|
||||
sin >> version;
|
||||
if(version > OBOE_CURRENT_VERSION) {
|
||||
giveError("Warning: this game appears to have been created with a newer version of Blades of Exile than you are running. Exile will do its best to load the saved game anyway, but there may be loss of information.");
|
||||
}
|
||||
} else if(cur == "POINTER") {
|
||||
int i,j,k;
|
||||
sin >> i >> j >> k;
|
||||
@@ -517,13 +558,12 @@ void cParty::readFrom(std::istream& file){
|
||||
out_c[i].what_monst.readFrom(bin);
|
||||
out_c[i].exists = true;
|
||||
}else if(cur == "CAMPAIGN") {
|
||||
unsigned int i;
|
||||
int j;
|
||||
unsigned int i, j;
|
||||
int val;
|
||||
cur = read_maybe_quoted_string(bin);
|
||||
bin >> i >> j;
|
||||
// TODO: value_type of campaign_flags is a vector, but maybe a map would be better?
|
||||
while(campaign_flags[cur].size() < i) campaign_flags[cur].push_back(0);
|
||||
campaign_flags[cur][i] = j;
|
||||
bin >> i >> j >> val;
|
||||
if(i < 25 && j < 25)
|
||||
campaign_flags[cur].idx[i][j] = val;
|
||||
} else if(cur == "TIMER") {
|
||||
int i;
|
||||
bin >> i;
|
||||
@@ -583,25 +623,40 @@ cPlayer& cParty::operator[](unsigned short n){
|
||||
return adven[n];
|
||||
}
|
||||
|
||||
void cParty::set_ptr(short p, unsigned short sdfx, unsigned short sdfy){ // This function is not used for setting the reserved pointers
|
||||
if(p >= -199 && p <= -100){ // must be a mutable pointer
|
||||
// Note that the pointer functions take the pointer with its negative sign stripped off!
|
||||
void cParty::set_ptr(unsigned short p, unsigned short sdfx, unsigned short sdfy){
|
||||
// This function is not used for setting the reserved pointers
|
||||
if(p >= 100 && p <= 199){ // must be a mutable pointer
|
||||
if(sdfx >= 300) throw std::range_error("SDF x-coordinate out of range (0..299)");
|
||||
if(sdfy >= 50) throw std::range_error("SDF y-coordinate out of range (0..49)");
|
||||
pointers[p] = std::make_pair(sdfx,sdfy);
|
||||
}
|
||||
else throw std::range_error("Pointer out of range (-199 to -100)");
|
||||
else throw std::range_error("Attempted to assign a pointer out of range (100..199)");
|
||||
}
|
||||
|
||||
void cParty::force_ptr(short p, unsigned short sdfx, unsigned short sdfy){
|
||||
void cParty::clear_ptr(unsigned short p) {
|
||||
if(p >= 100 && p <= 199) {
|
||||
pointers[p] = std::make_pair(-1,-1);
|
||||
} else throw std::range_error("Attempted to assign a pointer out of range (100 to 199)");
|
||||
}
|
||||
|
||||
void cParty::force_ptr(unsigned short p, unsigned short sdfx, unsigned short sdfy){
|
||||
pointers[p] = std::make_pair(sdfx,sdfy);
|
||||
}
|
||||
|
||||
unsigned char cParty::get_ptr(short p){
|
||||
ptrIter iter = pointers.find(p);
|
||||
unsigned char cParty::get_ptr(unsigned short p){
|
||||
auto iter = pointers.find(p);
|
||||
if(iter == pointers.end()) return 0;
|
||||
return stuff_done[iter->second.first][iter->second.second];
|
||||
}
|
||||
|
||||
unsigned char& cParty::cpn_flag(unsigned int x, unsigned int y, std::string id) {
|
||||
if(id.empty()) id = scenario.campaign_id;
|
||||
if(id.empty()) id = scenario.scen_name;
|
||||
if(x >= 25 || y >= 25) throw std::range_error("Attempted to access a campaign flag out of range (0..25)");
|
||||
return campaign_flags[id].idx[x][y];
|
||||
}
|
||||
|
||||
bool cParty::is_split(){
|
||||
bool ret = false;
|
||||
for(int i = 0; i < 6; i++)
|
||||
|
||||
@@ -30,6 +30,10 @@ namespace legacy {
|
||||
struct setup_save_type;
|
||||
};
|
||||
|
||||
struct campaign_flag_type{
|
||||
unsigned char idx[25][25];
|
||||
};
|
||||
|
||||
class cParty {
|
||||
public:
|
||||
class cConvers { // conversation; formerly talk_save_type
|
||||
@@ -68,6 +72,7 @@ public:
|
||||
short light_level;
|
||||
location outdoor_corner;
|
||||
location i_w_c;
|
||||
// TODO: Does this duplicate cCurTown::p_loc? If not, why not?
|
||||
location p_loc;
|
||||
location loc_in_sec;
|
||||
cVehicle boats[30];
|
||||
@@ -106,12 +111,17 @@ public:
|
||||
std::vector<cMonster> summons; // an array of monsters which can be summoned by the party's items yet don't originate from this scenario
|
||||
bool graphicUsed[250]; // whether each custom graphics slot on the party's sheet is actually used; needed to place new custom graphics on the sheet.
|
||||
unsigned short scen_won, scen_played; // numbers of scenarios won and played respectively by this party
|
||||
std::map<std::string,std::vector<signed short> > campaign_flags;
|
||||
std::map<short,std::pair<unsigned short,unsigned char> > pointers;
|
||||
private:
|
||||
std::map<std::string,campaign_flag_type> campaign_flags;
|
||||
std::map<unsigned short,std::pair<unsigned short,unsigned char>> pointers;
|
||||
public:
|
||||
|
||||
void set_ptr(short p, unsigned short sdfx, unsigned short sdfy);
|
||||
void force_ptr(short p, unsigned short sdfx, unsigned short sdfy);
|
||||
unsigned char get_ptr(short p);
|
||||
void set_ptr(unsigned short p, unsigned short sdfx, unsigned short sdfy);
|
||||
void force_ptr(unsigned short p, unsigned short sdfx, unsigned short sdfy);
|
||||
void clear_ptr(unsigned short p);
|
||||
unsigned char get_ptr(unsigned short p);
|
||||
|
||||
unsigned char& cpn_flag(unsigned int x, unsigned int y, std::string id = "");
|
||||
|
||||
cParty& operator = (legacy::party_record_type& old);
|
||||
void append(legacy::big_tr_type& old);
|
||||
@@ -142,8 +152,8 @@ public:
|
||||
typedef std::vector<cJournal>::iterator journalIter;
|
||||
typedef std::vector<cConvers>::iterator talkIter;
|
||||
typedef std::vector<cTimer>::iterator timerIter;
|
||||
typedef std::map<std::string,std::vector<signed short> >::iterator campIter;
|
||||
typedef std::map<short,std::pair<unsigned short,unsigned char> >::iterator ptrIter;
|
||||
// TODO: Remove this in favour of cParty constructor
|
||||
friend void init_party(short);
|
||||
};
|
||||
|
||||
bool operator==(const cParty::cConvers& one, const cParty::cConvers& two);
|
||||
|
||||
@@ -81,6 +81,7 @@ public:
|
||||
location last_out_edited;
|
||||
short last_town_edited;
|
||||
scenario_header_flags format;
|
||||
std::string campaign_id; // A hopefully unique identifier to specify the campaign this scenario is a part of.
|
||||
// scen_item_data_type scen_item_list {
|
||||
cItemRec scen_items[400];
|
||||
//char monst_names[256][20];
|
||||
@@ -107,4 +108,7 @@ public:
|
||||
void writeTo(std::ostream& file);
|
||||
};
|
||||
|
||||
// OBoE Current Version
|
||||
const unsigned long long OBOE_CURRENT_VERSION = 0x010000; // MMmmff; M - major, m - minor, f - bugfix
|
||||
|
||||
#endif
|
||||
@@ -373,6 +373,7 @@ enum eItemAbil {
|
||||
ITEM_FIREWALK = 92,
|
||||
ITEM_FLYING = 93,
|
||||
ITEM_MAJOR_HEALING = 94,
|
||||
ITEM_CALL_SPECIAL = 95,
|
||||
// Spell Usable
|
||||
ITEM_SPELL_FLAME = 110,
|
||||
ITEM_SPELL_FIREBALL = 111,
|
||||
@@ -539,6 +540,8 @@ enum class eSpecCtx {
|
||||
TARGET = 16,
|
||||
USE_SPACE = 17,
|
||||
SEE_MONST = 18,
|
||||
MONST_SPEC_ABIL = 19,
|
||||
TOWN_HOSTILE = 20,
|
||||
};
|
||||
|
||||
enum class eSpecType {
|
||||
@@ -571,6 +574,8 @@ enum class eSpecType {
|
||||
REST = 25,
|
||||
WANDERING_WILL_FIGHT = 26,
|
||||
END_SCENARIO = 27,
|
||||
SET_POINTER = 28,
|
||||
SET_CAMP_FLAG = 29,
|
||||
ONCE_GIVE_ITEM = 50,
|
||||
ONCE_GIVE_SPEC_ITEM = 51,
|
||||
ONCE_NULL = 52,
|
||||
@@ -630,12 +635,12 @@ enum class eSpecType {
|
||||
IF_HAVE_ITEM_CLASS_AND_TAKE = 145,
|
||||
IF_EQUIP_ITEM_CLASS_AND_TAKE = 146,
|
||||
IF_DAY_REACHED = 147,
|
||||
IF_BARRELS = 148,
|
||||
IF_CRATES = 149,
|
||||
IF_OBJECTS = 148,
|
||||
IF_PARTY_SIZE = 149,
|
||||
IF_EVENT_OCCURRED = 150,
|
||||
IF_HAS_CAVE_LORE = 151,
|
||||
IF_HAS_WOODSMAN = 152,
|
||||
IF_ENOUGH_MAGE_LORE = 153,
|
||||
IF_SPECIES = 151,
|
||||
IF_TRAIT = 152,
|
||||
IF_STATISTIC = 153,
|
||||
IF_TEXT_RESPONSE = 154,
|
||||
IF_SDF_EQ = 155,
|
||||
IF_CONTEXT = 156,
|
||||
@@ -665,6 +670,7 @@ enum class eSpecType {
|
||||
TOWN_SPLIT_PARTY = 193,
|
||||
TOWN_REUNITE_PARTY = 194,
|
||||
TOWN_TIMER_START = 195,
|
||||
TOWN_CHANGE_LIGHTING = 196,
|
||||
RECT_PLACE_FIRE = 200,
|
||||
RECT_PLACE_FORCE = 201,
|
||||
RECT_PLACE_ICE = 202,
|
||||
@@ -698,7 +704,7 @@ enum class eSpecCat {
|
||||
|
||||
inline eSpecCat getNodeCategory(eSpecType node) {
|
||||
int code = (int) node;
|
||||
if(code >= 0 && code <= 27)
|
||||
if(code >= 0 && code <= 29)
|
||||
return eSpecCat::GENERAL;
|
||||
if(code >= 50 && code <= 63)
|
||||
return eSpecCat::ONCE;
|
||||
@@ -706,7 +712,7 @@ inline eSpecCat getNodeCategory(eSpecType node) {
|
||||
return eSpecCat::AFFECT;
|
||||
if(code >= 130 && code <= 156)
|
||||
return eSpecCat::IF_THEN;
|
||||
if(code >= 170 && code <= 195)
|
||||
if(code >= 170 && code <= 196)
|
||||
return eSpecCat::TOWN;
|
||||
if(code >= 200 && code <= 218)
|
||||
return eSpecCat::RECT;
|
||||
|
||||
@@ -66,6 +66,27 @@ cSpecial& cSpecial::operator = (legacy::special_node_type& old){
|
||||
ex1a = (int) eSpecCtx::TARGET;
|
||||
ex1b = 108; // Spell ID for ritual of sanctification, as seen in cast_town_spell()
|
||||
break;
|
||||
case 99: case 100: // Add mage/priest spell TODO: Merge these by adding 100 if it's a priest spell
|
||||
ex1a += 30;
|
||||
ex1b = 1; // Meaning give spell, not take
|
||||
break;
|
||||
case 148: case 149: // if barrels or crates
|
||||
type = eSpecType::IF_OBJECTS;
|
||||
ex1a = old.type - 148;
|
||||
break;
|
||||
case 151: case 152: // if has cave lore or woodsman
|
||||
type = eSpecType::IF_TRAIT;
|
||||
ex1a = old.type - 147;
|
||||
break;
|
||||
case 153: // if enough mage lore
|
||||
type = eSpecType::IF_STATISTIC;
|
||||
ex2a = 11;
|
||||
ex2b = 0;
|
||||
break;
|
||||
case 229: // Outdoor store - fix spell IDs
|
||||
if(ex1b == 1 || ex1b == 2)
|
||||
ex1a += 30;
|
||||
break;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -208,12 +229,11 @@ const std::map<eSpecType, node_properties_t> allNodeProps = {
|
||||
{eSpecType::IF_HAVE_ITEM_CLASS_AND_TAKE, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_EQUIP_ITEM_CLASS_AND_TAKE, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_DAY_REACHED, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_BARRELS, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_CRATES, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_OBJECTS, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_EVENT_OCCURRED, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_HAS_CAVE_LORE, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_HAS_WOODSMAN, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_ENOUGH_MAGE_LORE, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_SPECIES, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_TRAIT, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_STATISTIC, {ex1b_ch = true,jmp_lbl = 3}},
|
||||
{eSpecType::IF_TEXT_RESPONSE, {ex1b_ch = true,ex2b_ch = true,pic_lbl = 5,jmp_lbl = 3}},
|
||||
{eSpecType::IF_SDF_EQ, {ex1b_ch = true,sdf_lbl = 1,jmp_lbl = 3}},
|
||||
{eSpecType::IF_CONTEXT, {}},
|
||||
|
||||
@@ -43,7 +43,6 @@ struct pending_special_type {
|
||||
eSpecCtx mode;
|
||||
unsigned char type; // 0 - scen, 1 - out, 2 - town
|
||||
location where;
|
||||
long long trigger_time;
|
||||
};
|
||||
|
||||
struct node_properties_t {
|
||||
|
||||
@@ -26,6 +26,12 @@ cSpeech& cSpeech::operator = (legacy::talking_record_type& old){
|
||||
talk_nodes[i].link2[j] = old.talk_nodes[i].link2[j];
|
||||
talk_nodes[i].extras[j] = old.talk_nodes[i].extras[j];
|
||||
}
|
||||
// Now, convert data if necessary
|
||||
switch(old.talk_nodes[i].type) {
|
||||
case 9: case 10: // Spell shops TODO: Merge these by adding 100 if it's priest spells
|
||||
talk_nodes[i].extras[1] += 30;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ cTown& cTown::operator = (legacy::town_record_type& old){
|
||||
sign_locs[i].x = old.sign_locs[i].x;
|
||||
sign_locs[i].y = old.sign_locs[i].y;
|
||||
}
|
||||
lighting_type = old.lighting;
|
||||
lighting_type = (eLighting) old.lighting;
|
||||
in_town_rect.top = old.in_town_rect.top;
|
||||
in_town_rect.left = old.in_town_rect.left;
|
||||
in_town_rect.bottom = old.in_town_rect.bottom;
|
||||
@@ -98,7 +98,7 @@ cTown::cTown(short){
|
||||
special_locs[i] = d_loc;
|
||||
spec_id[i] = 0;
|
||||
}
|
||||
lighting_type = 0;
|
||||
lighting_type = LIGHT_NORMAL;
|
||||
for (i = 0; i < 4; i++) {
|
||||
start_locs[i] = d_loc;
|
||||
exit_specs[i] = -1;
|
||||
|
||||
@@ -28,6 +28,13 @@ namespace legacy {
|
||||
struct preset_field_type;
|
||||
};
|
||||
|
||||
enum eLighting {
|
||||
LIGHT_NORMAL = 0,
|
||||
LIGHT_DARK = 1,
|
||||
LIGHT_DRAINS = 2,
|
||||
LIGHT_NONE = 3,
|
||||
};
|
||||
|
||||
class cTown { // formerly town_record_type
|
||||
public:
|
||||
// class cCreature { // formerly creature_start_type
|
||||
@@ -73,7 +80,7 @@ public:
|
||||
location special_locs[50];
|
||||
unsigned short spec_id[50];
|
||||
location sign_locs[15];
|
||||
short lighting_type;
|
||||
eLighting lighting_type;
|
||||
location start_locs[4];
|
||||
location exit_locs[4];
|
||||
short exit_specs[4];
|
||||
@@ -82,6 +89,7 @@ public:
|
||||
short max_num_monst;
|
||||
std::vector<cField> preset_fields;
|
||||
short spec_on_entry,spec_on_entry_if_dead;
|
||||
short spec_on_hostile;
|
||||
short timer_spec_times[8];
|
||||
short timer_specs[8];
|
||||
unsigned char strlens[180];
|
||||
|
||||
@@ -1360,7 +1360,7 @@ bool save_party(fs::path dest_file)
|
||||
static_cast<short>(in_town ? 1342 : 5790), // is the party in town?
|
||||
static_cast<short>(in_scen ? 100 : 200), // is the party in a scenario?
|
||||
static_cast<short>(save_maps ? 5567 : 3422), // is the save maps feature enabled?
|
||||
0x0100, // current version number, major and minor revisions only
|
||||
OBOE_CURRENT_VERSION >> 8, // current version number, major and minor revisions only
|
||||
// Version 1 indicates a beta format that may not be supported in the final release
|
||||
};
|
||||
if(!mac_is_intel) // must flip all the flags to little-endian
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <boost/phoenix/bind.hpp>
|
||||
|
||||
#include "special.h"
|
||||
@@ -126,12 +127,12 @@ struct initer {
|
||||
("if-item-class", eSpecType::IF_HAVE_ITEM_CLASS_AND_TAKE)
|
||||
("if-item-class-equip", eSpecType::IF_EQUIP_ITEM_CLASS_AND_TAKE)
|
||||
("if-day", eSpecType::IF_DAY_REACHED)
|
||||
("if-field", eSpecType::IF_BARRELS)
|
||||
("if-object", eSpecType::IF_CRATES)
|
||||
// ("if-field", eSpecType::IF_BARRELS)
|
||||
("if-object", eSpecType::IF_OBJECTS)
|
||||
("if-event", eSpecType::IF_EVENT_OCCURRED)
|
||||
("if-cave-lore", eSpecType::IF_HAS_CAVE_LORE)
|
||||
("if-woodsman", eSpecType::IF_HAS_WOODSMAN)
|
||||
("if-mage-lore", eSpecType::IF_ENOUGH_MAGE_LORE)
|
||||
("if-trait", eSpecType::IF_TRAIT)
|
||||
("if-species", eSpecType::IF_SPECIES)
|
||||
("if-statistic", eSpecType::IF_STATISTIC)
|
||||
("if-response", eSpecType::IF_TEXT_RESPONSE)
|
||||
("if-sdf-eq", eSpecType::IF_SDF_EQ)
|
||||
("town-attitude", eSpecType::MAKE_TOWN_HOSTILE)
|
||||
@@ -174,6 +175,24 @@ struct initer {
|
||||
("make-out-monst", eSpecType::OUT_PLACE_ENCOUNTER)
|
||||
("start-shop", eSpecType::OUT_STORE)
|
||||
;
|
||||
// A check for missing types.
|
||||
using underlying = std::underlying_type<eSpecType>::type;
|
||||
struct node_less : std::binary_function<eSpecType, eSpecType, bool> {
|
||||
bool operator()(const eSpecType& x, const eSpecType& y) const {return underlying(x) < underlying(y);}
|
||||
};
|
||||
std::set<eSpecType, node_less> allNodes;
|
||||
for(underlying i = 0; i < std::numeric_limits<underlying>::max(); i++) {
|
||||
eSpecType check = (eSpecType) i;
|
||||
eSpecCat category = getNodeCategory(check);
|
||||
if(category == eSpecCat::INVALID) continue;
|
||||
allNodes.insert(check);
|
||||
}
|
||||
opcode.for_each([&allNodes](const std::string&, eSpecType node) {
|
||||
allNodes.erase(node);
|
||||
});
|
||||
std::for_each(allNodes.begin(), allNodes.end(), [](eSpecType node){
|
||||
printf("Warning: Missing opcode definition for special node type with ID %d\n", (int)node);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user