4059 lines
129 KiB
C++
4059 lines
129 KiB
C++
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <queue>
|
|
|
|
//#include "item.h"
|
|
|
|
#include "boe.global.h"
|
|
|
|
#include "classes.h"
|
|
#include "boe.party.h"
|
|
#include "boe.town.h"
|
|
#include "boe.text.h"
|
|
#include "boe.infodlg.h"
|
|
#include "boe.items.h"
|
|
#include "boe.itemdata.h"
|
|
#include "boe.combat.h"
|
|
#include "boe.monster.h"
|
|
#include "boe.locutils.h"
|
|
#include "boe.actions.h"
|
|
#include "soundtool.h"
|
|
#include "boe.townspec.h"
|
|
#include "boe.graphics.h"
|
|
#include "boe.fileio.h"
|
|
#include "boe.specials.h"
|
|
#include "boe.newgraph.h"
|
|
#include "boe.dlgutil.h"
|
|
#include "mathutil.h"
|
|
#include "boe.main.h"
|
|
#include "dlogutil.h"
|
|
#include "fileio.h"
|
|
#include <array>
|
|
|
|
extern sf::RenderWindow mainPtr;
|
|
extern eGameMode overall_mode;
|
|
//extern party_record_type party;
|
|
//extern current_town_type univ.town;
|
|
//extern unsigned char univ.out[96][96],out_e[96][96],sfx[64][64];
|
|
extern short which_combat_type,current_pc,stat_window;
|
|
//extern cOutdoors univ.out.outdoors[2][2];
|
|
extern location center;
|
|
extern bool in_scen_debug,belt_present,processing_fields,monsters_going,suppress_stat_screen,boom_anim_active;
|
|
//extern big_tr_type t_d;
|
|
extern effect_pat_type current_pat;
|
|
//extern town_item_list univ.town;
|
|
extern cOutdoors::cWandering store_wandering_special;
|
|
extern short monst_marked_damage[60];
|
|
extern eSpell spell_being_cast, town_spell;
|
|
extern sf::RenderWindow mini_map;
|
|
extern bool fast_bang,end_scenario;
|
|
//extern short town_size[3];
|
|
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;
|
|
|
|
short boom_gr[8] = {3,0,2,1,1,4,3,3};
|
|
short store_item_spell_level = 10;
|
|
|
|
// global vbalues for when processing special encounters
|
|
short current_pc_picked_in_spec_enc = -1; // pc that's been selected, -1 if none
|
|
extern std::map<eSkill,short> skill_max;
|
|
location store_special_loc;
|
|
bool special_in_progress = false;
|
|
short spec_str_offset[3] = {160,10,0};
|
|
|
|
//// Pretty much all of this is new for BoE
|
|
|
|
// 0 - can't use 1 - combat only 2 - town only 3 - town & combat only 4 - everywhere 5 - outdoor
|
|
// + 10 - mag. inept can use
|
|
std::map<eItemAbil, short> abil_chart = {
|
|
{eItemAbil::POISON_WEAPON,13}, {eItemAbil::BLESS_CURSE,4}, {eItemAbil::AFFECT_POISON,4}, {eItemAbil::HASTE_SLOW,4},
|
|
{eItemAbil::AFFECT_INVULN,3}, {eItemAbil::AFFECT_MAGIC_RES,3}, {eItemAbil::AFFECT_WEB,3}, {eItemAbil::AFFECT_DISEASE,4},
|
|
{eItemAbil::AFFECT_SANCTUARY,3}, {eItemAbil::AFFECT_DUMBFOUND,3}, {eItemAbil::AFFECT_MARTYRS_SHIELD,3}, {eItemAbil::AFFECT_SLEEP,3},
|
|
{eItemAbil::AFFECT_PARALYSIS,3}, {eItemAbil::AFFECT_ACID,3}, {eItemAbil::BLISS,3}, {eItemAbil::AFFECT_EXPERIENCE,4},
|
|
{eItemAbil::AFFECT_SKILL_POINTS,4}, {eItemAbil::AFFECT_HEALTH,4}, {eItemAbil::AFFECT_SPELL_POINTS,4}, {eItemAbil::DOOM,3},
|
|
{eItemAbil::LIGHT,13}, {eItemAbil::STEALTH,3}, {eItemAbil::FIREWALK,3}, {eItemAbil::FLYING,5}, {eItemAbil::MAJOR_HEALING,4},
|
|
{eItemAbil::CALL_SPECIAL,4}, {eItemAbil::FLAME,1}, {eItemAbil::FIREBALL,1}, {eItemAbil::FIRESTORM,1}, {eItemAbil::KILL,1},
|
|
{eItemAbil::ICE_BOLT,1}, {eItemAbil::SLOW,1}, {eItemAbil::SHOCKWAVE,1}, {eItemAbil::DISPEL_UNDEAD,1}, {eItemAbil::DISPEL_SPIRIT,1},
|
|
{eItemAbil::SUMMONING,3}, {eItemAbil::MASS_SUMMONING,3}, {eItemAbil::ACID_SPRAY,1}, {eItemAbil::STINKING_CLOUD,1},
|
|
{eItemAbil::SLEEP_FIELD,1}, {eItemAbil::VENOM,1}, {eItemAbil::SHOCKSTORM,1}, {eItemAbil::PARALYSIS,1}, {eItemAbil::WEB,1},
|
|
{eItemAbil::STRENGTHEN_TARGET,1}, {eItemAbil::QUICKFIRE,3}, {eItemAbil::MASS_CHARM,1}, {eItemAbil::MAGIC_MAP,2},
|
|
{eItemAbil::DISPEL_BARRIER,2}, {eItemAbil::ICE_WALL,1}, {eItemAbil::CHARM_SPELL,1}, {eItemAbil::ANTIMAGIC_CLOUD,1},
|
|
};
|
|
|
|
// TODO: I bet this is completely unused; it looks like it does nothing.
|
|
bool town_specials(short which,short /*t_num*/)
|
|
//short which; // number, 0 - 49, of special
|
|
{
|
|
bool can_enter = true;
|
|
short spec_id;
|
|
location l;
|
|
|
|
|
|
l = univ.town->special_locs[which];
|
|
spec_id = univ.town->spec_id[which];
|
|
if (spec_id < 0)
|
|
return true;
|
|
|
|
// call special
|
|
|
|
erase_specials();
|
|
|
|
return can_enter;
|
|
}
|
|
|
|
bool handle_wandering_specials (short /*which*/,short mode)
|
|
// which is unused
|
|
//short mode; // 0 - pre 1 - end by victory 2 - end by flight
|
|
// wanderin spec 99 -> generic spec
|
|
{
|
|
|
|
// 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,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,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,univ.party.loc_in_sec,&s1,&s2,&s3);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool check_special_terrain(location where_check,eSpecCtx mode,short which_pc,short *spec_num,
|
|
bool *forced)
|
|
// returns true if can enter this space
|
|
// sets forced to true if definitely can enter
|
|
{
|
|
ter_num_t ter;
|
|
short r1,i,door_pc,pic_type = 0,ter_pic = 0;
|
|
eTerSpec ter_special;
|
|
std::string choice;
|
|
ter_flag_t ter_flag1,ter_flag2,ter_flag3;
|
|
eDamageType dam_type = DAMAGE_WEAPON;
|
|
bool can_enter = true;
|
|
location out_where,from_loc,to_loc;
|
|
short s1 = 0,s2 = 0,s3 = 0;
|
|
|
|
*spec_num = -1;
|
|
*forced = false;
|
|
|
|
switch (mode) {
|
|
case eSpecCtx::OUT_MOVE:
|
|
ter = univ.out[where_check.x][where_check.y];
|
|
from_loc = univ.party.p_loc;
|
|
break;
|
|
case eSpecCtx::TOWN_MOVE:
|
|
ter = univ.town->terrain(where_check.x,where_check.y);
|
|
from_loc = univ.town.p_loc;
|
|
break;
|
|
case eSpecCtx::COMBAT_MOVE:
|
|
ter = univ.town->terrain(where_check.x,where_check.y);
|
|
from_loc = univ.party[current_pc].combat_pos;
|
|
break;
|
|
default:
|
|
// No movement happened, so just return false.
|
|
// TODO: Should there be an error message here?
|
|
printf("Note: Improper mode passed to check_special_terrain: %d\n", (int)mode);
|
|
return false;
|
|
}
|
|
ter_special = scenario.ter_types[ter].special;
|
|
ter_flag1 = scenario.ter_types[ter].flag1;
|
|
ter_flag2 = scenario.ter_types[ter].flag2;
|
|
ter_flag3 = scenario.ter_types[ter].flag3;
|
|
ter_pic = scenario.ter_types[ter].picture;
|
|
|
|
// TODO: Why not support conveyors outdoors, too?
|
|
if(mode != eSpecCtx::OUT_MOVE && ter_special == eTerSpec::CONVEYOR) {
|
|
if (
|
|
((ter_flag3.u == DIR_N) && (where_check.y > from_loc.y)) ||
|
|
((ter_flag3.u == DIR_E) && (where_check.x < from_loc.x)) ||
|
|
((ter_flag3.u == DIR_S) && (where_check.y < from_loc.y)) ||
|
|
((ter_flag3.u == DIR_W) && (where_check.x > from_loc.x)) ) {
|
|
ASB("The moving floor prevents you.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(mode == eSpecCtx::OUT_MOVE) {
|
|
out_where = global_to_local(where_check);
|
|
|
|
for (i = 0; i < 18; i++)
|
|
if (out_where == univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].special_locs[i]) {
|
|
*spec_num = univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].special_id[i];
|
|
if ((*spec_num >= 0) &&
|
|
univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].specials[*spec_num].type == eSpecType::SECRET_PASSAGE)
|
|
*forced = true;
|
|
// call special
|
|
run_special(mode,1,univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].special_id[i],out_where,&s1,&s2,&s3);
|
|
if (s1 > 0)
|
|
can_enter = false;
|
|
erase_out_specials();
|
|
put_pc_screen();
|
|
put_item_screen(stat_window,0);
|
|
}
|
|
}
|
|
|
|
if ((is_combat()) && (univ.town.is_spot(where_check.x, where_check.y) ||
|
|
(scenario.ter_types[coord_to_ter(where_check.x, where_check.y)].trim_type == eTrimType::CITY))) {
|
|
ASB("Move: Can't trigger this special in combat.");
|
|
return false; // TODO: Maybe replace eTrimType::CITY check with a blockage == clear/special && is_special() check?
|
|
}
|
|
|
|
if((mode == eSpecCtx::TOWN_MOVE || (mode == eSpecCtx::COMBAT_MOVE && which_combat_type == 1))
|
|
&& (univ.town.is_special(where_check.x,where_check.y))) {
|
|
if (univ.town.is_force_barr(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Magic barrier! ");
|
|
return false;
|
|
}
|
|
if(univ.town.is_force_cage(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Force cage!");
|
|
return false;
|
|
}
|
|
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;
|
|
if(ter_special == eTerSpec::CHANGE_WHEN_STEP_ON) runSpecial = true;
|
|
if(ter_special == eTerSpec::CALL_SPECIAL) runSpecial = true;
|
|
if(!PSD[SDF_NO_BOAT_SPECIALS] && univ.party.in_boat >= 0 && scenario.ter_types[ter].boat_over)
|
|
runSpecial = true;
|
|
if(runSpecial) {
|
|
give_help(54,0);
|
|
run_special(mode,2,univ.town->spec_id[i],where_check,&s1,&s2,&s3);
|
|
if (s1 > 0)
|
|
can_enter = false;
|
|
}
|
|
}
|
|
put_pc_screen();
|
|
put_item_screen(stat_window,0);
|
|
}
|
|
|
|
// TODO: Just verify that yes, it works with this and doesn't work without it.
|
|
if(mode == eSpecCtx::COMBAT_MOVE && town_boat_there(where_check) < 3) {
|
|
add_string_to_buf("Blocked: Can't enter boats in combat");
|
|
can_enter = false;
|
|
}
|
|
|
|
if (can_enter == false)
|
|
return false;
|
|
|
|
if ((!is_out()) && (overall_mode < MODE_TALKING)) {
|
|
check_fields(where_check,mode,which_pc);
|
|
|
|
if (univ.town.is_web(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Webs! ");
|
|
if(mode != eSpecCtx::COMBAT_MOVE) {
|
|
suppress_stat_screen = true;
|
|
for (i = 0; i < 6; i++) {
|
|
r1 = get_ran(1,2,3);
|
|
web_pc(i,r1);
|
|
}
|
|
suppress_stat_screen = true;
|
|
put_pc_screen();
|
|
}
|
|
else web_pc(current_pc,get_ran(1,2,3));
|
|
univ.town.set_web(where_check.x,where_check.y,false);
|
|
}
|
|
if (univ.town.is_force_barr(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Magic barrier! ");
|
|
can_enter = false;
|
|
}
|
|
if (univ.town.is_force_cage(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Force cage! ");
|
|
can_enter = false;
|
|
}
|
|
if (univ.town.is_crate(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" You push the crate.");
|
|
to_loc = push_loc(from_loc,where_check);
|
|
univ.town.set_crate((short) where_check.x,(short) where_check.y,false);
|
|
if (to_loc.x > 0)
|
|
univ.town.set_crate((short) to_loc.x,(short) to_loc.y,true);
|
|
for (i = 0; i < NUM_TOWN_ITEMS; i++)
|
|
if(univ.town.items[i].variety != eItemType::NO_ITEM && univ.town.items[i].item_loc == where_check
|
|
&& (univ.town.items[i].contained))
|
|
univ.town.items[i].item_loc = to_loc;
|
|
}
|
|
if (univ.town.is_barrel(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" You push the barrel.");
|
|
to_loc = push_loc(from_loc,where_check);
|
|
univ.town.set_barrel((short) where_check.x,(short) where_check.y,false);
|
|
if (to_loc.x > 0)
|
|
univ.town.set_barrel((short) to_loc.x,(short) to_loc.y,false);
|
|
for (i = 0; i < NUM_TOWN_ITEMS; i++)
|
|
if(univ.town.items[i].variety != eItemType::NO_ITEM && univ.town.items[i].item_loc == where_check
|
|
&& (univ.town.items[i].contained))
|
|
univ.town.items[i].item_loc = to_loc;
|
|
}
|
|
if(univ.town.is_block(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" You push the stone block.");
|
|
to_loc = push_loc(from_loc,where_check);
|
|
univ.town.set_block(where_check.x,where_check.y,false);
|
|
if(to_loc.x > 0)
|
|
univ.town.set_block(to_loc.x,to_loc.y,false);
|
|
}
|
|
}
|
|
|
|
switch (ter_special) {
|
|
case eTerSpec::CHANGE_WHEN_STEP_ON:
|
|
alter_space(where_check.x,where_check.y,ter_flag1.u);
|
|
if (ter_flag2.u < 200) {
|
|
play_sound(-1 * ter_flag2.u);
|
|
}
|
|
give_help(47,65);
|
|
if(blocksMove(scenario.ter_types[ter].blockage))
|
|
can_enter = false;
|
|
break;
|
|
case eTerSpec::DAMAGING:
|
|
//if the party is flying, in a boat, or entering a boat, they cannot be harmed by terrain
|
|
if(flying() || univ.party.in_boat >= 0)
|
|
break;
|
|
if(mode == eSpecCtx::TOWN_MOVE && town_boat_there(where_check) < 30)
|
|
break;
|
|
if(mode == eSpecCtx::OUT_MOVE && out_boat_there(where_check) < 30)
|
|
break;
|
|
if(ter_flag3.u > 0 && ter_flag3.u < 8)
|
|
dam_type = (eDamageType) ter_flag3.u;
|
|
else dam_type = DAMAGE_WEAPON;
|
|
r1 = get_ran(ter_flag2.u,dam_type,ter_flag1.u);
|
|
switch(dam_type){
|
|
case DAMAGE_FIRE:
|
|
add_string_to_buf(" It's hot!");
|
|
pic_type = 0;
|
|
if (PSD[SDF_PARTY_FIREWALK] > 0) {
|
|
add_string_to_buf(" It doesn't affect you.");
|
|
r1 = -1;
|
|
}
|
|
break;
|
|
case DAMAGE_COLD:
|
|
add_string_to_buf(" You feel cold!");
|
|
pic_type = 4;
|
|
break;
|
|
case DAMAGE_MAGIC:
|
|
case DAMAGE_UNBLOCKABLE:
|
|
add_string_to_buf(" Something shocks you!");
|
|
pic_type = 1;
|
|
break;
|
|
case DAMAGE_WEAPON:
|
|
add_string_to_buf(" You feel pain!");
|
|
pic_type = 3;
|
|
break;
|
|
case DAMAGE_POISON:
|
|
add_string_to_buf(" You suddenly feel very ill for a moment...");
|
|
pic_type = 2;
|
|
break;
|
|
case DAMAGE_UNDEAD:
|
|
case DAMAGE_DEMON:
|
|
add_string_to_buf(" A dark wind blows through you!");
|
|
pic_type = 1; // TODO: Verify that this is correct
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if(r1 < 0) break; // "It doesn't affect you."
|
|
if(mode != eSpecCtx::COMBAT_MOVE)
|
|
hit_party(r1,dam_type);
|
|
fast_bang = 1;
|
|
if(mode == eSpecCtx::COMBAT_MOVE)
|
|
damage_pc(which_pc,r1,dam_type,eRace::UNKNOWN,0);
|
|
else
|
|
boom_space(univ.party.p_loc,overall_mode,pic_type,r1,12);
|
|
fast_bang = 0;
|
|
break;
|
|
case eTerSpec::DANGEROUS:
|
|
//if the party is flying, in a boat, or entering a boat, they cannot be harmed by terrain
|
|
if(flying() || univ.party.in_boat >= 0)
|
|
break;
|
|
if(mode == eSpecCtx::TOWN_MOVE && town_boat_there(where_check) < 30)
|
|
break;
|
|
if(mode == eSpecCtx::OUT_MOVE && out_boat_there(where_check) < 30)
|
|
break;
|
|
//one_sound(17);
|
|
if(mode == eSpecCtx::COMBAT_MOVE) i = which_pc; else i = 0;
|
|
for ( ; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE) {
|
|
if (get_ran(1,1,100) <= ter_flag2.u) {
|
|
switch((eStatus)ter_flag3.u){
|
|
case eStatus::POISONED_WEAPON: // TODO: Do something here
|
|
if(get_ran(1,1,100) > 60) add_string_to_buf("It's eerie here...");
|
|
break;
|
|
case eStatus::BLESS_CURSE: // Should say "You feel awkward." / "You feel blessed."?
|
|
curse_pc(i,ter_flag1.s);
|
|
break;
|
|
case eStatus::POISON:
|
|
poison_pc(i,ter_flag1.u);
|
|
break;
|
|
case eStatus::HASTE_SLOW: // Should say "You feel sluggish." / "You feel speedy."?
|
|
slow_pc(i,ter_flag1.s);
|
|
break;
|
|
case eStatus::INVULNERABLE: // Should say "You feel odd." / "You feel protected."?
|
|
affect_pc(i,eStatus::INVULNERABLE,ter_flag1.u);
|
|
break;
|
|
case eStatus::MAGIC_RESISTANCE: // Should say "You feel odd." / "You feel protected."?
|
|
affect_pc(i,eStatus::MAGIC_RESISTANCE,ter_flag1.u);
|
|
break;
|
|
case eStatus::WEBS: // Should say "You feel sticky." / "Your skin tingles."?
|
|
web_pc(i,ter_flag1.u);
|
|
break;
|
|
case eStatus::DISEASE: // Should say "You feel healthy." / "You feel sick."?
|
|
disease_pc(i,ter_flag1.u);
|
|
break;
|
|
case eStatus::INVISIBLE:
|
|
if(ter_flag1.s < 0) add_string_to_buf("You feel obscure.");
|
|
else add_string_to_buf("You feel exposed.");
|
|
affect_pc(i,eStatus::INVISIBLE,ter_flag1.s);
|
|
break;
|
|
case eStatus::DUMB: // Should say "You feel clearheaded." / "You feel confused."?
|
|
dumbfound_pc(i,ter_flag1.u);
|
|
break;
|
|
case eStatus::MARTYRS_SHIELD: // Should say "You feel dull." / "You start to glow slightly."?
|
|
affect_pc(i,eStatus::MARTYRS_SHIELD,ter_flag1.u);
|
|
break;
|
|
case eStatus::ASLEEP: // Should say "You feel alert." / "You feel very tired."?
|
|
sleep_pc(i,ter_flag1.u,eStatus::ASLEEP,ter_flag1.u / 2);
|
|
break;
|
|
case eStatus::PARALYZED: // Should say "You find it easier to move." / "You feel very stiff."?
|
|
sleep_pc(i,ter_flag1.u,eStatus::PARALYZED,ter_flag1.u / 2);
|
|
break;
|
|
case eStatus::ACID: // Should say "Your skin tingles pleasantly." / "Your skin burns!"?
|
|
acid_pc(i,ter_flag1.u);
|
|
break;
|
|
case eStatus::MAIN: case eStatus::CHARM: // These magic values are illegal in this context
|
|
break;
|
|
}
|
|
if(mode == eSpecCtx::COMBAT_MOVE) break; // only damage once in combat!
|
|
}
|
|
}
|
|
//print_nums(1,which_pc,current_pc);
|
|
break;
|
|
case eTerSpec::CALL_SPECIAL:
|
|
{
|
|
short spec_type = 0;
|
|
if(ter_flag2.u == 3){
|
|
if(mode == eSpecCtx::TOWN_MOVE || (mode == eSpecCtx::COMBAT_MOVE && which_combat_type == 1))
|
|
spec_type = 2;
|
|
else spec_type = 1;
|
|
}else if(ter_flag2.u == 2 && (mode == eSpecCtx::OUT_MOVE || (mode == eSpecCtx::COMBAT_MOVE && which_combat_type == 0)))
|
|
spec_type = 1;
|
|
else if(ter_flag2.u == 1 && (mode == eSpecCtx::TOWN_MOVE || (mode == eSpecCtx::COMBAT_MOVE && which_combat_type == 1)))
|
|
spec_type = 2;
|
|
run_special(mode,spec_type,ter_flag1.u,where_check,&s1,&s2,&s3);
|
|
if (s1 > 0)
|
|
can_enter = false;
|
|
break;
|
|
}
|
|
// Locked doors
|
|
case eTerSpec::UNLOCKABLE:
|
|
if (is_combat()) { // No lockpicking in combat
|
|
add_string_to_buf(" Can't enter: It's locked.");
|
|
break;
|
|
}
|
|
|
|
// See what party wants to do.
|
|
choice = cChoiceDlog("locked-door-action.xml",{"leave","pick","bash"}).show();
|
|
|
|
can_enter = false;
|
|
if (choice == "leave")
|
|
break;
|
|
if ((door_pc = select_pc(1,0)) < 6) {
|
|
if (choice == "pick")
|
|
pick_lock(where_check,door_pc);
|
|
else bash_door(where_check,door_pc);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Action may change terrain, so update what's been seen
|
|
if (is_town())
|
|
update_explored(univ.town.p_loc);
|
|
if (is_combat())
|
|
update_explored(univ.party[current_pc].combat_pos);
|
|
|
|
return can_enter;
|
|
}
|
|
|
|
// This procedure find the effects of fields that would affect a PC who moves into
|
|
// a space or waited in that same space
|
|
void check_fields(location where_check,eSpecCtx mode,short which_pc)
|
|
{
|
|
short r1,i;
|
|
|
|
if(mode != eSpecCtx::COMBAT_MOVE && mode != eSpecCtx::TOWN_MOVE && mode != eSpecCtx::OUT_MOVE) {
|
|
printf("Note: Improper mode passed to check_fields: %d\n", (int)mode);
|
|
return;
|
|
}
|
|
if (is_out())
|
|
return;
|
|
if (is_town())
|
|
fast_bang = 1;
|
|
if (univ.town.is_fire_wall(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Fire wall! ");
|
|
r1 = get_ran(1,1,6) + 1;
|
|
// TODO: Is this commented code important?
|
|
// if (mode < 2)
|
|
// hit_party(r1,1);
|
|
if (mode == eSpecCtx::COMBAT_MOVE)
|
|
damage_pc(which_pc,r1,DAMAGE_FIRE,eRace::UNKNOWN,0);
|
|
if (overall_mode < MODE_COMBAT)
|
|
boom_space(univ.party.p_loc,overall_mode,0,r1,5);
|
|
}
|
|
if (univ.town.is_force_wall(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Force wall! ");
|
|
r1 = get_ran(2,1,6);
|
|
// if (mode < 2)
|
|
// hit_party(r1,3);
|
|
if (mode == eSpecCtx::COMBAT_MOVE)
|
|
damage_pc(which_pc,r1,DAMAGE_MAGIC,eRace::UNKNOWN,0);
|
|
if (overall_mode < MODE_COMBAT)
|
|
boom_space(univ.party.p_loc,overall_mode,1,r1,12);
|
|
}
|
|
if (univ.town.is_ice_wall(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Ice wall! ");
|
|
r1 = get_ran(2,1,6);
|
|
// if (mode < 2)
|
|
// hit_party(r1,5);
|
|
if (mode == eSpecCtx::COMBAT_MOVE)
|
|
damage_pc(which_pc,r1,DAMAGE_COLD,eRace::UNKNOWN,0);
|
|
if (overall_mode < MODE_COMBAT)
|
|
boom_space(univ.party.p_loc,overall_mode,4,r1,7);
|
|
}
|
|
if (univ.town.is_blade_wall(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Blade wall! ");
|
|
r1 = get_ran(4,1,8);
|
|
// if (mode < 2)
|
|
// hit_party(r1,0);
|
|
if (mode == eSpecCtx::COMBAT_MOVE)
|
|
damage_pc(which_pc,r1,DAMAGE_WEAPON,eRace::UNKNOWN,0);
|
|
if (overall_mode < MODE_COMBAT)
|
|
boom_space(univ.party.p_loc,overall_mode,3,r1,2);
|
|
}
|
|
if (univ.town.is_quickfire(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Quickfire! ");
|
|
r1 = get_ran(2,1,8);
|
|
// if (mode < 2)
|
|
// hit_party(r1,1);
|
|
if (mode == eSpecCtx::COMBAT_MOVE)
|
|
damage_pc(which_pc,r1,DAMAGE_FIRE,eRace::UNKNOWN,0);
|
|
if (overall_mode < MODE_COMBAT)
|
|
boom_space(univ.party.p_loc,overall_mode,0,r1,5);
|
|
}
|
|
if (univ.town.is_scloud(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Stinking cloud! ");
|
|
if (mode != eSpecCtx::COMBAT_MOVE) {
|
|
suppress_stat_screen = true;
|
|
for (i = 0; i < 6; i++) {
|
|
r1 = get_ran(1,2,3);
|
|
curse_pc(i,r1);
|
|
}
|
|
suppress_stat_screen = false;
|
|
put_pc_screen();
|
|
}
|
|
else curse_pc(current_pc,get_ran(1,2,3));
|
|
}
|
|
if (univ.town.is_sleep_cloud(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Sleep cloud! ");
|
|
if (mode != eSpecCtx::COMBAT_MOVE) {
|
|
suppress_stat_screen = true;
|
|
for (i = 0; i < 6; i++) {
|
|
sleep_pc(i,3,eStatus::ASLEEP,0);
|
|
}
|
|
suppress_stat_screen = false;
|
|
put_pc_screen();
|
|
}
|
|
else sleep_pc(current_pc,3,eStatus::ASLEEP,0);
|
|
}
|
|
if (univ.town.is_fire_barr(where_check.x,where_check.y)) {
|
|
add_string_to_buf(" Magic barrier! ");
|
|
r1 = get_ran(2,1,10);
|
|
if (mode != eSpecCtx::COMBAT_MOVE)
|
|
hit_party(r1,DAMAGE_MAGIC);
|
|
if (mode == eSpecCtx::COMBAT_MOVE)
|
|
damage_pc(which_pc,r1,DAMAGE_MAGIC,eRace::UNKNOWN,0);
|
|
if (overall_mode < MODE_COMBAT)
|
|
boom_space(univ.party.p_loc,overall_mode,1,r1,12);
|
|
}
|
|
if(univ.town.is_force_cage(where_check.x,where_check.y)) {
|
|
if(univ.party[which_pc].status[eStatus::FORCECAGE] == 0)
|
|
add_string_to_buf(" Trapped in force cage!");
|
|
univ.party[which_pc].status[eStatus::FORCECAGE] = 8;
|
|
} else univ.party[which_pc].status[eStatus::FORCECAGE] = 0;
|
|
fast_bang = 0;
|
|
}
|
|
|
|
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,univ.party.p_loc,&i,&j,&k);
|
|
|
|
}
|
|
|
|
|
|
void use_item(short pc,short item)
|
|
{
|
|
bool take_charge = true,inept_ok = false;
|
|
short 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;
|
|
cCreature *which_m;
|
|
extern effect_pat_type single;
|
|
eItemAbil abil = univ.party[pc].items[item].ability;
|
|
level = univ.party[pc].items[item].item_level;
|
|
|
|
item_use_code = abil_chart[abil];
|
|
if (item_use_code >= 10) {
|
|
item_use_code -= 10;
|
|
inept_ok = true;
|
|
}
|
|
|
|
if (is_out())
|
|
user_loc = univ.party.p_loc;
|
|
if (is_town())
|
|
user_loc = univ.town.p_loc;
|
|
if (is_combat())
|
|
user_loc = univ.party[current_pc].combat_pos;
|
|
|
|
if (item_use_code == 0) {
|
|
add_string_to_buf("Use: Can't use this item. ");
|
|
take_charge = false;
|
|
}
|
|
if (univ.party[pc].traits[eTrait::MAGICALLY_INEPT] && !inept_ok){
|
|
add_string_to_buf("Use: Can't - magically inept. ");
|
|
take_charge = false;
|
|
}
|
|
|
|
// 0 - can't use 1 - combat only 2 - town only 3 - town & combat only 4 - everywhere 5 - outdoor
|
|
if (take_charge) {
|
|
if(overall_mode == MODE_OUTDOORS && item_use_code < 4) {
|
|
add_string_to_buf("Use: Not while outdoors. ");
|
|
take_charge = false;
|
|
}
|
|
// TODO: Almost all of these look wrong!
|
|
if ((overall_mode == MODE_TOWN) && (item_use_code == 1)) {
|
|
add_string_to_buf("Use: Not while in town. ");
|
|
take_charge = false;
|
|
}
|
|
if ((overall_mode == MODE_COMBAT) && (item_use_code == 2)) {
|
|
add_string_to_buf("Use: Not in combat. ");
|
|
take_charge = false;
|
|
}
|
|
if ((overall_mode != MODE_OUTDOORS) && (item_use_code == 5)){
|
|
add_string_to_buf("Use: Only outdoors. ");
|
|
take_charge = false;
|
|
}
|
|
}
|
|
if (take_charge) {
|
|
if (!univ.party[pc].items[item].ident)
|
|
sprintf((char *) to_draw, "Use: %s",univ.party[pc].items[item].name.c_str());
|
|
else sprintf((char *) to_draw, "Use: %s",univ.party[pc].items[item].full_name.c_str());
|
|
add_string_to_buf((char *) to_draw);
|
|
|
|
if(univ.party[pc].items[item].variety == eItemType::POTION)
|
|
play_sound(56);
|
|
|
|
str = univ.party[pc].items[item].ability_strength;
|
|
store_item_spell_level = str * 2 + 1;
|
|
type = univ.party[pc].items[item].magic_use_type;
|
|
|
|
switch (abil) {
|
|
case eItemAbil::POISON_WEAPON: // poison weapon
|
|
take_charge = poison_weapon(pc,str,0);
|
|
break;
|
|
case eItemAbil::BLESS_CURSE:
|
|
play_sound(4);
|
|
which_stat = eStatus::BLESS_CURSE;
|
|
if (type % 2 == 1) {
|
|
ASB(" You feel awkward.");
|
|
str = str * -1;
|
|
}else ASB(" You feel blessed.");
|
|
if (type > 1)
|
|
affect_party(which_stat,str);
|
|
else affect_pc(pc,which_stat,str);
|
|
break;
|
|
case eItemAbil::HASTE_SLOW:
|
|
play_sound(75);
|
|
which_stat = eStatus::HASTE_SLOW;
|
|
if (type % 2 == 1) {
|
|
ASB(" You feel sluggish.");
|
|
str = str * -1;
|
|
}else ASB(" You feel speedy.");
|
|
if (type > 1)
|
|
affect_party(which_stat,str);
|
|
else affect_pc(pc,which_stat,str);
|
|
break;
|
|
case eItemAbil::AFFECT_INVULN:
|
|
play_sound(68);
|
|
which_stat = eStatus::INVULNERABLE;
|
|
if (type % 2 == 1) {
|
|
ASB(" You feel odd.");
|
|
str = str * -1;
|
|
}else ASB(" You feel protected.");
|
|
if (type > 1)
|
|
affect_party(which_stat,str);
|
|
else affect_pc(pc,which_stat,str);
|
|
break;
|
|
case eItemAbil::AFFECT_MAGIC_RES:
|
|
play_sound(51);
|
|
which_stat = eStatus::MAGIC_RESISTANCE;
|
|
if (type % 2 == 1) {
|
|
ASB(" You feel odd.");
|
|
str = str * -1;
|
|
}else ASB(" You feel protected.");
|
|
if (type > 1)
|
|
affect_party(which_stat,str);
|
|
else affect_pc(pc,which_stat,str);
|
|
break;
|
|
case eItemAbil::AFFECT_WEB:
|
|
which_stat = eStatus::WEBS;
|
|
if (type % 2 == 1)
|
|
ASB(" You feel sticky.");
|
|
else {
|
|
ASB(" Your skin tingles.");
|
|
str = str * -1;
|
|
}
|
|
if (type > 1)
|
|
affect_party(which_stat,str);
|
|
else affect_pc(pc,which_stat,str);
|
|
break;
|
|
case eItemAbil::AFFECT_SANCTUARY:
|
|
play_sound(43);
|
|
which_stat = eStatus::INVISIBLE;
|
|
if (type % 2 == 1) {
|
|
ASB(" You feel exposed.");
|
|
str = str * -1;
|
|
}else ASB(" You feel obscure.");
|
|
if (type > 1)
|
|
affect_party(which_stat,str);
|
|
else affect_pc(pc,which_stat,str);
|
|
break;
|
|
case eItemAbil::AFFECT_MARTYRS_SHIELD:
|
|
play_sound(43);
|
|
which_stat = eStatus::MARTYRS_SHIELD;
|
|
if (type % 2 == 1) {
|
|
ASB(" You feel dull.");
|
|
str = str * -1;
|
|
}else ASB(" You start to glow slightly.");
|
|
if (type > 1)
|
|
affect_party(which_stat,str);
|
|
else affect_pc(pc,which_stat,str);
|
|
break;
|
|
case eItemAbil::AFFECT_POISON:
|
|
switch (type) {
|
|
case 0:
|
|
ASB(" You feel better.");
|
|
cure_pc(pc,str);
|
|
break;
|
|
case 1:
|
|
ASB(" You feel ill.");
|
|
poison_pc(pc,str);
|
|
break;
|
|
case 2:
|
|
ASB(" You all feel better.");
|
|
cure_party(str);
|
|
break;
|
|
case 3:
|
|
ASB(" You all feel ill.");
|
|
poison_party(str);
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::AFFECT_DISEASE:
|
|
switch (type) {
|
|
case 0:
|
|
ASB(" You feel healthy.");
|
|
affect_pc(pc,eStatus::DISEASE,-1 * str);
|
|
break;
|
|
case 1:
|
|
ASB(" You feel sick.");
|
|
disease_pc(pc,str);
|
|
break;
|
|
case 2:
|
|
ASB(" You all feel healthy.");
|
|
affect_party(eStatus::DISEASE,-1 * str);
|
|
break;
|
|
case 3:
|
|
ASB(" You all feel sick.");
|
|
for (i = 0; i < 6; i++)
|
|
disease_pc(i,str);
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::AFFECT_DUMBFOUND:
|
|
switch (type) {
|
|
case 0:
|
|
ASB(" You feel clear headed.");
|
|
affect_pc(pc,eStatus::DUMB,-1 * str);
|
|
break;
|
|
case 1:
|
|
ASB(" You feel confused.");
|
|
dumbfound_pc(pc,str);
|
|
break;
|
|
case 2:
|
|
ASB(" You all feel clear headed.");
|
|
affect_party(eStatus::DUMB,-1 * str);
|
|
break;
|
|
case 3:
|
|
ASB(" You all feel confused.");
|
|
for (i = 0; i < 6; i++)
|
|
dumbfound_pc(i,str);
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::AFFECT_SLEEP:
|
|
switch (type) {
|
|
case 0:
|
|
ASB(" You feel alert.");
|
|
affect_pc(pc,eStatus::ASLEEP,-1 * str);
|
|
break;
|
|
case 1:
|
|
ASB(" You feel very tired.");
|
|
sleep_pc(pc,str + 1,eStatus::ASLEEP,200);
|
|
break;
|
|
case 2:
|
|
ASB(" You all feel alert.");
|
|
affect_party(eStatus::ASLEEP,-1 * str);
|
|
break;
|
|
case 3:
|
|
ASB(" You all feel very tired.");
|
|
for (i = 0; i < 6; i++)
|
|
sleep_pc(i,str + 1,eStatus::ASLEEP,200);
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::AFFECT_PARALYSIS:
|
|
switch (type) {
|
|
case 0:
|
|
ASB(" You find it easier to move.");
|
|
affect_pc(pc,eStatus::PARALYZED,-1 * str * 100);
|
|
break;
|
|
case 1:
|
|
ASB(" You feel very stiff.");
|
|
sleep_pc(pc,str * 20 + 10,eStatus::PARALYZED,200);
|
|
break;
|
|
case 2:
|
|
ASB(" You all find it easier to move.");
|
|
affect_party(eStatus::PARALYZED,-1 * str * 100);
|
|
break;
|
|
case 3:
|
|
ASB(" You all feel very stiff.");
|
|
for (i = 0; i < 6; i++)
|
|
sleep_pc(i,str * 20 + 10,eStatus::PARALYZED,200);
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::AFFECT_ACID:
|
|
switch (type) {
|
|
case 0:
|
|
ASB(" Your skin tingles pleasantly.");
|
|
affect_pc(pc,eStatus::ACID,-1 * str);
|
|
break;
|
|
case 1:
|
|
ASB(" Your skin burns!");
|
|
acid_pc(pc,str);
|
|
break;
|
|
case 2:
|
|
ASB(" You all tingle pleasantly.");
|
|
affect_party(eStatus::ACID,-1 * str);
|
|
break;
|
|
case 3:
|
|
ASB(" Everyone's skin burns!");
|
|
for (i = 0; i < 6; i++)
|
|
acid_pc(i,str);
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::BLISS:
|
|
switch (type) {
|
|
case 0: case 1:
|
|
ASB(" You feel wonderful!");
|
|
heal_pc(pc,str * 20);
|
|
affect_pc(pc,eStatus::BLESS_CURSE,str);
|
|
break;
|
|
case 2: case 3:
|
|
ASB(" Everyone feels wonderful!");
|
|
for (i = 0; i < 6; i++) {
|
|
heal_pc(i,str * 20);
|
|
affect_pc(i,eStatus::BLESS_CURSE,str);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::AFFECT_EXPERIENCE:
|
|
switch (type) {
|
|
case 0:
|
|
ASB(" You feel much smarter.");
|
|
award_xp(pc,str * 5);
|
|
break;
|
|
case 1:
|
|
ASB(" You feel forgetful.");
|
|
drain_pc(pc,str * 5);
|
|
break;
|
|
case 2:
|
|
ASB(" You all feel much smarter.");
|
|
award_party_xp(str * 5);
|
|
break;
|
|
case 3:
|
|
ASB(" You all feel forgetful.");
|
|
for (i = 0; i < 6; i++)
|
|
drain_pc(i,str * 5);
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::AFFECT_SKILL_POINTS:
|
|
play_sound(68);
|
|
switch (type) {
|
|
case 0:
|
|
ASB(" You feel much smarter.");
|
|
univ.party[pc].skill_pts += str;
|
|
break;
|
|
case 1:
|
|
ASB(" You feel forgetful.");
|
|
univ.party[pc].skill_pts = max(0,univ.party[pc].skill_pts - str);
|
|
break;
|
|
case 2:
|
|
ASB(" You all feel much smarter.");
|
|
for (i = 0; i < 6; i++)
|
|
univ.party[i].skill_pts += str;
|
|
break;
|
|
case 3:
|
|
ASB(" You all feel forgetful.");
|
|
for (i = 0; i < 6; i++)
|
|
univ.party[i].skill_pts = max(0,univ.party[i].skill_pts - str);
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::AFFECT_HEALTH:
|
|
switch (type) {
|
|
case 0:
|
|
ASB(" You feel better.");
|
|
heal_pc(pc,str * 20);
|
|
break;
|
|
case 1:
|
|
ASB(" You feel sick.");
|
|
damage_pc(pc,20 * str,DAMAGE_UNBLOCKABLE,eRace::HUMAN,0);
|
|
break;
|
|
case 2:
|
|
ASB(" You all feel better.");
|
|
heal_party(str * 20);
|
|
break;
|
|
case 3:
|
|
ASB(" You all feel sick.");
|
|
hit_party(20 * str,DAMAGE_UNBLOCKABLE);
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::AFFECT_SPELL_POINTS:
|
|
switch (type) {
|
|
case 0:
|
|
ASB(" You feel energized.");
|
|
restore_sp_pc(pc,str * 5);
|
|
break;
|
|
case 1:
|
|
ASB(" You feel drained.");
|
|
univ.party[pc].cur_sp = max(0,univ.party[pc].cur_sp - str * 5);
|
|
break;
|
|
case 2:
|
|
ASB(" You all feel energized.");
|
|
restore_sp_party(str * 5);
|
|
break;
|
|
case 3:
|
|
ASB(" You all feel drained.");
|
|
for (i = 0; i < 6; i++)
|
|
univ.party[i].cur_sp = max(0,univ.party[i].cur_sp - str * 5);
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::DOOM:
|
|
switch (type) {
|
|
case 0: case 1:
|
|
ASB(" You feel terrible.");
|
|
drain_pc(pc,str * 5);
|
|
damage_pc(pc,20 * str,DAMAGE_UNBLOCKABLE,eRace::HUMAN,0);
|
|
disease_pc(pc,2 * str);
|
|
dumbfound_pc(pc,2 * str);
|
|
break;
|
|
case 2: case 3:
|
|
ASB(" You all feel terrible.");
|
|
for (i = 0; i < 6; i++) {
|
|
drain_pc(i,str * 5);
|
|
damage_pc(i,20 * str,DAMAGE_UNBLOCKABLE,eRace::HUMAN,0);
|
|
disease_pc(i,2 * str);
|
|
dumbfound_pc(i,2 * str);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::LIGHT:
|
|
ASB(" You have more light.");
|
|
increase_light(50 * str);
|
|
break;
|
|
case eItemAbil::STEALTH:
|
|
ASB(" Your footsteps become quieter.");
|
|
PSD[SDF_PARTY_STEALTHY] += 5 * str;
|
|
break;
|
|
case eItemAbil::FIREWALK:
|
|
ASB(" You feel chilly.");
|
|
PSD[SDF_PARTY_FIREWALK] += 2 * str;
|
|
break;
|
|
case eItemAbil::FLYING:
|
|
if (PSD[SDF_PARTY_FLIGHT] > 0) {
|
|
add_string_to_buf(" Not while already flying. ");
|
|
take_charge = false;
|
|
break;
|
|
}
|
|
if (univ.party.in_boat >= 0) {
|
|
add_string_to_buf(" Leave boat first. ");
|
|
take_charge = false;
|
|
} else if (univ.party.in_horse >= 0) {
|
|
add_string_to_buf(" Leave horse first. ");
|
|
take_charge = false;
|
|
} else {
|
|
ASB(" You rise into the air!");
|
|
PSD[SDF_PARTY_FLIGHT] += str;
|
|
}
|
|
break;
|
|
case eItemAbil::MAJOR_HEALING:
|
|
switch (type) {
|
|
case 0: case 1:
|
|
ASB(" You feel wonderful.");
|
|
heal_pc(pc,200);
|
|
cure_pc(pc,8);
|
|
break;
|
|
case 2: case 3:
|
|
ASB(" You all feel wonderful.");
|
|
heal_party(200);
|
|
cure_party(8);
|
|
break;
|
|
}
|
|
break;
|
|
case eItemAbil::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 eItemAbil::FLAME:
|
|
add_string_to_buf(" It fires a bolt of flame.");
|
|
start_spell_targeting(eSpell::FLAME, true);
|
|
break;
|
|
case eItemAbil::FIREBALL:
|
|
add_string_to_buf(" It shoots a fireball. ");
|
|
start_spell_targeting(eSpell::FIREBALL, true);
|
|
break;
|
|
case eItemAbil::FIRESTORM:
|
|
add_string_to_buf(" It shoots a huge fireball. ");
|
|
start_spell_targeting(eSpell::FIRESTORM, true);
|
|
break;
|
|
case eItemAbil::KILL:
|
|
add_string_to_buf(" It shoots a black ray. ");
|
|
start_spell_targeting(eSpell::KILL, true);
|
|
break;
|
|
case eItemAbil::ICE_BOLT:
|
|
add_string_to_buf(" It fires a ball of ice. ");
|
|
start_spell_targeting(eSpell::ICE_BOLT, true);
|
|
break;
|
|
case eItemAbil::SLOW:
|
|
add_string_to_buf(" It fires a purple ray. ");
|
|
start_spell_targeting(eSpell::SLOW, true);
|
|
break;
|
|
case eItemAbil::SHOCKWAVE:
|
|
add_string_to_buf(" The ground shakes! ");
|
|
do_shockwave(univ.party[current_pc].combat_pos);
|
|
break;
|
|
case eItemAbil::DISPEL_UNDEAD:
|
|
add_string_to_buf(" It shoots a white ray. ");
|
|
start_spell_targeting(eSpell::DISPEL_UNDEAD, true);
|
|
break;
|
|
case eItemAbil::DISPEL_SPIRIT:
|
|
add_string_to_buf(" It shoots a golden ray. ");
|
|
start_spell_targeting(eSpell::RAVAGE_SPIRIT, true);
|
|
break;
|
|
case eItemAbil::SUMMONING:
|
|
if (summon_monster(str,user_loc,50,2) == false)
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
case eItemAbil::MASS_SUMMONING:
|
|
r1 = get_ran(6,1,4);
|
|
for (i = 0; i < get_ran(1,3,5); i++) // TODO: Why recalculate the random number for each loop iteration?
|
|
if (summon_monster(str,user_loc,r1,2) == false)
|
|
add_string_to_buf(" Summon failed.");
|
|
break;
|
|
case eItemAbil::ACID_SPRAY:
|
|
add_string_to_buf(" Acid sprays from the tip! ");
|
|
start_spell_targeting(eSpell::ACID_SPRAY, true);
|
|
break;
|
|
case eItemAbil::STINKING_CLOUD:
|
|
add_string_to_buf(" It creates a cloud of gas. ");
|
|
start_spell_targeting(eSpell::FOUL_VAPOR, true);
|
|
break;
|
|
case eItemAbil::SLEEP_FIELD:
|
|
add_string_to_buf(" It creates a shimmering cloud. ");
|
|
start_spell_targeting(eSpell::CLOUD_SLEEP, true);
|
|
break;
|
|
case eItemAbil::VENOM:
|
|
add_string_to_buf(" A green ray emerges. ");
|
|
start_spell_targeting(eSpell::POISON, true);
|
|
break;
|
|
case eItemAbil::SHOCKSTORM:
|
|
add_string_to_buf(" Sparks fly.");
|
|
start_spell_targeting(eSpell::SHOCKSTORM, true);
|
|
break;
|
|
case eItemAbil::PARALYSIS:
|
|
add_string_to_buf(" It shoots a silvery beam. ");
|
|
start_spell_targeting(eSpell::PARALYZE_BEAM, true);
|
|
break;
|
|
case eItemAbil::WEB:
|
|
add_string_to_buf(" It explodes!");
|
|
start_spell_targeting(eSpell::GOO_BOMB, true);
|
|
break;
|
|
case eItemAbil::STRENGTHEN_TARGET:
|
|
add_string_to_buf(" It shoots a fiery red ray. ");
|
|
start_spell_targeting(eSpell::STRENGTHEN_TARGET, true);
|
|
break;
|
|
case eItemAbil::QUICKFIRE:
|
|
add_string_to_buf("Fire pours out!");
|
|
univ.town.set_quickfire(user_loc.x,user_loc.y,true);
|
|
break;
|
|
case eItemAbil::MASS_CHARM:
|
|
ASB("It throbs, and emits odd rays.");
|
|
for (i = 0; i < univ.town->max_monst(); i++) {
|
|
if ((univ.town.monst[i].active != 0) && (univ.town.monst[i].attitude % 2 == 1)
|
|
&& (dist(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc) <= 8)
|
|
&& (can_see_light(univ.party[current_pc].combat_pos,univ.town.monst[i].cur_loc,sight_obscurity) < 5)) {
|
|
which_m = &univ.town.monst[i];
|
|
charm_monst(which_m,0,eStatus::CHARM,8);
|
|
}
|
|
}
|
|
break;
|
|
case eItemAbil::MAGIC_MAP:
|
|
if (univ.town->defy_scrying || univ.town->defy_mapping) {
|
|
add_string_to_buf(" It doesn't work.");
|
|
break;
|
|
}
|
|
add_string_to_buf(" You have a vision. ");
|
|
for (i = 0; i < univ.town->max_dim(); i++)
|
|
for (j = 0; j < univ.town->max_dim(); j++)
|
|
make_explored(i,j);
|
|
clear_map();
|
|
break;
|
|
case eItemAbil::DISPEL_BARRIER:
|
|
add_string_to_buf(" It fires a blinding ray.");
|
|
add_string_to_buf(" Target spell. ");
|
|
current_pat = single;
|
|
start_town_targeting(eSpell::DISPEL_BARRIER,current_pc, true);
|
|
break;
|
|
case eItemAbil::ICE_WALL:
|
|
add_string_to_buf(" It shoots a blue sphere. ");
|
|
start_spell_targeting(eSpell::WALL_ICE_BALL, true);
|
|
break;
|
|
case eItemAbil::CHARM_SPELL:
|
|
add_string_to_buf(" It fires a lovely, sparkling beam.");
|
|
start_spell_targeting(eSpell::CHARM_FOE, true);
|
|
break;
|
|
case eItemAbil::ANTIMAGIC_CLOUD:
|
|
add_string_to_buf(" Your hair stands on end. ");
|
|
start_spell_targeting(eSpell::ANTIMAGIC, true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
put_pc_screen();
|
|
if ((take_charge == true) && (univ.party[pc].items[item].charges > 0))
|
|
remove_charge(pc,item);
|
|
if (take_charge == false) {
|
|
draw_terrain(0);
|
|
put_item_screen(stat_window,0);
|
|
}
|
|
}
|
|
|
|
// Returns true if an action is actually carried out. This can only be reached in town.
|
|
bool use_space(location where)
|
|
{
|
|
ter_num_t ter;
|
|
short i;
|
|
location from_loc,to_loc;
|
|
|
|
ter = univ.town->terrain(where.x,where.y);
|
|
from_loc = univ.town.p_loc;
|
|
|
|
add_string_to_buf("Use...");
|
|
|
|
if (univ.town.is_web(where.x,where.y)) {
|
|
add_string_to_buf(" You clear the webs.");
|
|
univ.town.set_web(where.x,where.y,false);
|
|
return true;
|
|
}
|
|
if (univ.town.is_crate(where.x,where.y)) {
|
|
to_loc = push_loc(from_loc,where);
|
|
if (from_loc == to_loc) {
|
|
add_string_to_buf(" Can't push crate.");
|
|
return false;
|
|
}
|
|
add_string_to_buf(" You push the crate.");
|
|
univ.town.set_crate((short) where.x,(short) where.y,false);
|
|
univ.town.set_crate((short) to_loc.x,(short) to_loc.y,true);
|
|
for (i = 0; i < NUM_TOWN_ITEMS; i++)
|
|
if(univ.town.items[i].variety != eItemType::NO_ITEM && univ.town.items[i].item_loc == where
|
|
&& (univ.town.items[i].contained))
|
|
univ.town.items[i].item_loc = to_loc;
|
|
}
|
|
if (univ.town.is_barrel(where.x,where.y)) {
|
|
to_loc = push_loc(from_loc,where);
|
|
if (from_loc == to_loc) {
|
|
add_string_to_buf(" Can't push barrel.");
|
|
return false;
|
|
}
|
|
add_string_to_buf(" You push the barrel.");
|
|
univ.town.set_barrel((short) where.x,(short) where.y,false);
|
|
univ.town.set_barrel((short) to_loc.x,(short) to_loc.y,true);
|
|
for (i = 0; i < NUM_TOWN_ITEMS; i++)
|
|
if(univ.town.items[i].variety != eItemType::NO_ITEM && univ.town.items[i].item_loc == where
|
|
&& (univ.town.items[i].contained))
|
|
univ.town.items[i].item_loc = to_loc;
|
|
}
|
|
if (univ.town.is_block(where.x,where.y)) {
|
|
to_loc = push_loc(from_loc,where);
|
|
if (from_loc == to_loc) {
|
|
add_string_to_buf(" Can't push block.");
|
|
return false;
|
|
}
|
|
add_string_to_buf(" You push the block.");
|
|
univ.town.set_block((short) where.x,(short) where.y,false);
|
|
univ.town.set_block((short) to_loc.x,(short) to_loc.y,true);
|
|
}
|
|
|
|
if(scenario.ter_types[ter].special == eTerSpec::CHANGE_WHEN_USED) {
|
|
if (where == from_loc) {
|
|
add_string_to_buf(" Not while on space.");
|
|
return false;
|
|
}
|
|
add_string_to_buf(" OK.");
|
|
alter_space(where.x,where.y,scenario.ter_types[ter].flag1.u);
|
|
play_sound(scenario.ter_types[ter].flag2.u);
|
|
return true;
|
|
} else if(scenario.ter_types[ter].special == eTerSpec::CALL_SPECIAL_WHEN_USED) {
|
|
short spec_type = 0;
|
|
if(scenario.ter_types[ter].flag2.u == 3){
|
|
if((is_town() || (is_combat() && which_combat_type == 1))) spec_type = 2; else spec_type = 1;
|
|
}else if(scenario.ter_types[ter].flag2.u == 1 && (is_town() || (is_combat() && which_combat_type == 1)))
|
|
spec_type = 2;
|
|
else if(scenario.ter_types[ter].flag2.u == 2 && (is_out() || (is_combat() && which_combat_type == 1)))
|
|
spec_type = 1;
|
|
run_special(eSpecCtx::USE_SPACE,spec_type,scenario.ter_types[ter].flag1.u,where,&i,&i,&i);
|
|
return true;
|
|
}
|
|
add_string_to_buf(" Nothing to use.");
|
|
|
|
return false;
|
|
}
|
|
|
|
// Note ... if this is a container, the code must first process any specials. If
|
|
//specials return false, can't get items inside. If true, can get items inside.
|
|
// Can't get items out in combat.
|
|
bool adj_town_look(location where)
|
|
{
|
|
ter_num_t terrain;
|
|
bool can_open = true,item_there = false,got_special = false;
|
|
short i = 0,s1 = 0, s2 = 0, s3 = 0;
|
|
|
|
for (i = 0; i < NUM_TOWN_ITEMS; i++)
|
|
if(univ.town.items[i].variety != eItemType::NO_ITEM && (univ.town.items[i].contained) &&
|
|
(where == univ.town.items[i].item_loc))
|
|
item_there = true;
|
|
|
|
terrain = univ.town->terrain(where.x,where.y);
|
|
if (univ.town.is_special(where.x,where.y)) {// && (get_blockage(terrain) > 0)) {
|
|
if (adjacent(univ.town.p_loc,where) == false)
|
|
add_string_to_buf(" Not close enough to search.");
|
|
else {
|
|
for (i = 0; i < 50; i++)
|
|
if (where == univ.town->special_locs[i]) {
|
|
if (get_blockage(univ.town->terrain(where.x,where.y)) > 0) {
|
|
// tell party you find something, if looking at a space they can't step in
|
|
add_string_to_buf(" Search: You find something! ");
|
|
}
|
|
//call special can_open = town_specials(i,univ.town.town_num);
|
|
|
|
run_special(eSpecCtx::TOWN_LOOK,2,univ.town->spec_id[i],where,&s1,&s2,&s3);
|
|
if (s1 > 0)
|
|
can_open = false;
|
|
got_special = true;
|
|
}
|
|
put_item_screen(stat_window,0);
|
|
}
|
|
}
|
|
if (is_container(where) && item_there && can_open) {
|
|
get_item(where,6,true);
|
|
}else if(scenario.ter_types[terrain].special == eTerSpec::CHANGE_WHEN_USED ||
|
|
scenario.ter_types[terrain].special == eTerSpec::CALL_SPECIAL_WHEN_USED) {
|
|
add_string_to_buf(" (Use this space to do something");
|
|
add_string_to_buf(" with it.)");
|
|
}else{
|
|
if (!got_special)
|
|
add_string_to_buf(" Search: You don't find anything. ");
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// PSOE - place_special_outdoor_encounter
|
|
void PSOE(short which_special,unsigned char *stuff_done_val,short where_put)
|
|
// if always, stuff_done_val is NULL
|
|
{
|
|
short i,j,graphic_num = 0;
|
|
|
|
if (stuff_done_val != NULL) {
|
|
if (*stuff_done_val > 0)
|
|
return;
|
|
else *stuff_done_val = 20;
|
|
}
|
|
for (i = 0; i < 18; i++)
|
|
if (univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].special_id[i] == where_put) {
|
|
for (j = 0; j < 7; j++)
|
|
if (univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].special_enc[which_special].monst[j] > 0) {
|
|
graphic_num = 400 + get_monst_picnum(univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].special_enc[which_special].monst[j]);
|
|
j = 7;
|
|
}
|
|
//display_strings( str1a, str1b, str2a, str2b,
|
|
//// "Encounter!",57, graphic_num, 0);
|
|
draw_terrain(0);
|
|
pause(15);
|
|
//if (univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].special_enc[which_special].spec_code == 0)
|
|
// univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].special_enc[which_special].spec_code = 1;
|
|
//place_outd_wand_monst(univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].special_locs[i],
|
|
// univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].special_enc[which_special]);
|
|
|
|
i = 18;
|
|
}
|
|
draw_terrain(0);
|
|
play_sound(61);
|
|
//play_sound(0);
|
|
}
|
|
|
|
void out_move_party(char x,char y)
|
|
{
|
|
location l;
|
|
|
|
l.x = x;l.y = y;
|
|
l = local_to_global(l);
|
|
univ.party.p_loc = l;
|
|
center = l;
|
|
update_explored(l);
|
|
}
|
|
|
|
void teleport_party(short x,short y,short mode)
|
|
// mode - 0=full teleport flash 1=no teleport flash 2=only fade flash 3=only arrival flash
|
|
{
|
|
short i;
|
|
location l;
|
|
bool fadeIn = false, fadeOut = false;
|
|
if(mode == 0 || mode == 2) fadeOut = true;
|
|
if(mode == 0 || mode == 3) fadeIn = true;
|
|
|
|
if (is_combat())
|
|
mode = 1;
|
|
|
|
l = univ.town.p_loc;
|
|
update_explored(l);
|
|
|
|
if(fadeOut) {
|
|
start_missile_anim();
|
|
for (i = 0; i < 9; i++)
|
|
add_explosion(l,-1,1,1,0,0);
|
|
do_explosion_anim(5,1);
|
|
can_draw_pcs = false;
|
|
do_explosion_anim(5,2);
|
|
end_missile_anim();
|
|
}
|
|
center.x = x; center.y = y;
|
|
if (is_combat()) {
|
|
univ.party[current_pc].combat_pos.x = x;
|
|
univ.party[current_pc].combat_pos.y = y;
|
|
}
|
|
l.x = x; l.y = y;
|
|
univ.town.p_loc.x = x;
|
|
univ.town.p_loc.y = y;
|
|
update_explored(l);
|
|
draw_terrain(0);
|
|
|
|
if(fadeIn) {
|
|
start_missile_anim();
|
|
for (i = 0; i < 14; i++)
|
|
add_explosion(center,-1,1,1,0,0);
|
|
do_explosion_anim(5,1);
|
|
}
|
|
can_draw_pcs = true;
|
|
if(fadeIn) {
|
|
do_explosion_anim(5,2);
|
|
end_missile_anim();
|
|
}
|
|
draw_map(true);
|
|
}
|
|
|
|
|
|
void fade_party()
|
|
{
|
|
short i;
|
|
location l;
|
|
|
|
l = univ.town.p_loc;
|
|
start_missile_anim();
|
|
for (i = 0; i < 14; i++)
|
|
add_explosion(l,-1,1,1,0,0);
|
|
do_explosion_anim(5,1);
|
|
univ.town.p_loc.x = 100;
|
|
univ.town.p_loc.y = 100;
|
|
do_explosion_anim(5,2);
|
|
end_missile_anim();
|
|
}
|
|
|
|
void change_level(short town_num,short x,short y)
|
|
{
|
|
location l(x,y);
|
|
|
|
if ((town_num < 0) || (town_num >= scenario.num_towns)) {
|
|
giveError("The scenario special encounter tried to put you into a town that doesn't exist.");
|
|
return;
|
|
}
|
|
|
|
force_town_enter(town_num,l);
|
|
end_town_mode(1,l);
|
|
start_town_mode(town_num,9);
|
|
}
|
|
|
|
|
|
// Damaging and killing monsters needs to be here because several have specials attached to them.
|
|
bool damage_monst(short which_m, short who_hit, short how_much, short how_much_spec, eDamageType dam_type, short sound_type)
|
|
//short which_m, who_hit, how_much, how_much_spec; // 6 for who_hit means dist. xp evenly 7 for no xp
|
|
//short dam_type; // 0 - weapon 1 - fire 2 - poison 3 - general magic 4 - unblockable 5 - cold
|
|
// 6 - demon 7 - undead
|
|
// 9 - marked damage, from during anim mode
|
|
//+10 = no_print
|
|
// 100s digit - damage sound for boom space
|
|
{
|
|
cCreature *victim;
|
|
short r1,which_spot;
|
|
location where_put;
|
|
|
|
bool do_print = true;
|
|
char resist;
|
|
|
|
//print_num(which_m,(short)univ.town.monst[which_m].m_loc.x,(short)univ.town.monst[which_m].m_loc.y);
|
|
|
|
if (univ.town.monst[which_m].active == 0) return false;
|
|
|
|
//sound_type = dam_type / 100;
|
|
//dam_type = dam_type % 100;
|
|
|
|
if (dam_type >= DAMAGE_MARKED) { // note: MARKED here actually means NO_PRINT
|
|
do_print = false;
|
|
dam_type -= DAMAGE_MARKED;
|
|
}
|
|
|
|
if (sound_type == 0) {
|
|
if ((dam_type == 1) || (dam_type == 4) )
|
|
sound_type = 5;
|
|
if (dam_type == 5)
|
|
sound_type = 7;
|
|
if (dam_type == 3)
|
|
sound_type = 12;
|
|
if (dam_type == 2)
|
|
sound_type = 11;
|
|
}
|
|
|
|
|
|
victim = &univ.town.monst[which_m];
|
|
resist = victim->immunities;
|
|
|
|
if (dam_type == 3) {
|
|
if (resist & 1)
|
|
how_much = how_much / 2;
|
|
if (resist & 2)
|
|
how_much = 0;
|
|
}
|
|
if (dam_type == 1) {
|
|
if (resist & 4)
|
|
how_much = how_much / 2;
|
|
if (resist & 8)
|
|
how_much = 0;
|
|
}
|
|
if (dam_type == 5) {
|
|
if (resist & 16)
|
|
how_much = how_much / 2;
|
|
if (resist & 32)
|
|
how_much = 0;
|
|
}
|
|
if (dam_type == 2) {
|
|
if (resist & 64)
|
|
how_much = how_much / 2;
|
|
if (resist & 128)
|
|
how_much = 0;
|
|
}
|
|
|
|
// Absorb damage?
|
|
if (((dam_type == 1) || (dam_type == 3) || (dam_type == 5))
|
|
&& (victim->spec_skill == 26)) {
|
|
if(32767 - victim->health > how_much)
|
|
victim->health = 32767;
|
|
else victim->health += how_much;
|
|
ASB(" Magic absorbed.");
|
|
return false;
|
|
}
|
|
|
|
// Saving throw
|
|
if (((dam_type == 1) || (dam_type == 5)) && (get_ran(1,0,20) <= victim->level))
|
|
how_much /= 2;
|
|
if ((dam_type == 3) && (get_ran(1,0,24) <= victim->level))
|
|
how_much /= 2;
|
|
|
|
// Invulnerable?
|
|
if (victim->spec_skill == 36)
|
|
how_much = how_much / 10;
|
|
|
|
|
|
r1 = get_ran(1,0,(victim->armor * 5) / 4);
|
|
r1 += victim->level / 4;
|
|
if (dam_type == 0)
|
|
how_much -= r1;
|
|
|
|
if (boom_anim_active == true) {
|
|
if (how_much < 0)
|
|
how_much = 0;
|
|
monst_marked_damage[which_m] += how_much;
|
|
add_explosion(victim->cur_loc,how_much,0,(dam_type > 2) ? 2 : 0,14 * (victim->x_width - 1),18 * (victim->y_width - 1));
|
|
if (how_much == 0)
|
|
return false;
|
|
else return true;
|
|
}
|
|
|
|
if (how_much <= 0) {
|
|
if (is_combat())
|
|
monst_spell_note(victim->number,7);
|
|
if ((how_much <= 0) && ((dam_type == DAMAGE_WEAPON) || (dam_type == DAMAGE_UNDEAD) || (dam_type == DAMAGE_DEMON))) {
|
|
draw_terrain(2);
|
|
play_sound(2);
|
|
}
|
|
// sprintf ((char *) create_line, " No damage. ");
|
|
// add_string_to_buf((char *) create_line);
|
|
return false;
|
|
}
|
|
|
|
if (do_print == true)
|
|
monst_damaged_mes(which_m,how_much,how_much_spec);
|
|
victim->health = victim->health - how_much - how_much_spec;
|
|
|
|
if (in_scen_debug)
|
|
victim->health = -1;
|
|
|
|
// splitting monsters
|
|
if ((victim->spec_skill == 12) && (victim->health > 0)){
|
|
where_put = find_clear_spot(victim->cur_loc,1);
|
|
if (where_put.x > 0)
|
|
if ((which_spot = place_monster(victim->number,where_put)) < 90) {
|
|
univ.town.monst[which_spot].health = victim->health;
|
|
univ.town.monst[which_spot].number = victim->number;
|
|
univ.town.monst[which_spot].start_attitude = victim->start_attitude;
|
|
univ.town.monst[which_spot].start_loc = victim->start_loc;
|
|
univ.town.monst[which_spot].mobility = victim->mobility;
|
|
univ.town.monst[which_spot].time_flag = victim->time_flag;
|
|
univ.town.monst[which_spot].extra1 = victim->extra1;
|
|
univ.town.monst[which_spot].extra2 = victim->extra2;
|
|
univ.town.monst[which_spot].spec1 = victim->spec1;
|
|
univ.town.monst[which_spot].spec2 = victim->spec2;
|
|
univ.town.monst[which_spot].spec_enc_code = victim->spec_enc_code;
|
|
univ.town.monst[which_spot].time_code = victim->time_code;
|
|
univ.town.monst[which_spot].monster_time = victim->monster_time;
|
|
univ.town.monst[which_spot].personality = victim->personality;
|
|
univ.town.monst[which_spot].special_on_kill = victim->special_on_kill;
|
|
univ.town.monst[which_spot].facial_pic = victim->facial_pic;
|
|
monst_spell_note(victim->number,27);
|
|
}
|
|
}
|
|
if (who_hit < 7)
|
|
univ.party.total_dam_done += how_much + how_much_spec;
|
|
|
|
// Monster damages. Make it hostile.
|
|
victim->active = 2;
|
|
|
|
|
|
if (dam_type != 9) { // note special damage only gamed in hand-to-hand, not during animation
|
|
if (party_can_see_monst(which_m) == true) {
|
|
boom_space(victim->cur_loc,100,boom_gr[dam_type],how_much,sound_type);
|
|
if (how_much_spec > 0)
|
|
boom_space(victim->cur_loc,100,51,how_much_spec,5);
|
|
}
|
|
else {
|
|
boom_space(victim->cur_loc,overall_mode, boom_gr[dam_type],how_much,sound_type);
|
|
if (how_much_spec > 0)
|
|
boom_space(victim->cur_loc,overall_mode,51,how_much_spec,5);
|
|
}
|
|
}
|
|
|
|
if (victim->health < 0) {
|
|
monst_killed_mes(which_m);
|
|
kill_monst(victim,who_hit);
|
|
}
|
|
else {
|
|
if (how_much > 0)
|
|
victim->morale = victim->morale - 1;
|
|
if (how_much > 5)
|
|
victim->morale = victim->morale - 1;
|
|
if (how_much > 10)
|
|
victim->morale = victim->morale - 1;
|
|
if (how_much > 20)
|
|
victim->morale = victim->morale - 2;
|
|
}
|
|
|
|
if ((victim->attitude % 2 != 1) && (who_hit < 7) &&
|
|
((processing_fields && !monsters_going) || (processing_fields && !PSD[SDF_HOSTILES_PRESENT]))) {
|
|
add_string_to_buf("Damaged an innocent. ");
|
|
victim->attitude = 1;
|
|
make_town_hostile();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void kill_monst(cCreature *which_m,short who_killed)
|
|
{
|
|
short xp,i,j,s1,s2,s3;
|
|
location l;
|
|
|
|
if(isHumanoid(which_m->m_type)) {
|
|
if (( which_m->number == 38) ||
|
|
( which_m->number == 39))
|
|
i = 4;
|
|
else if ( which_m->number == 45)
|
|
i = 0;
|
|
else i = get_ran(1,0,1);
|
|
play_sound(29 + i);
|
|
} else switch(which_m->m_type) {
|
|
case eRace::GIANT: play_sound(29); break;
|
|
// TODO: Should sliths be considered reptiles too? Check original bladbase.
|
|
// TODO: Should birds be considered beasts? If there are any birds in the bladbase, probably; otherwise, better to have new sound
|
|
case eRace::REPTILE: case eRace::BEAST: case eRace::DEMON: case eRace::UNDEAD: case eRace::STONE:
|
|
i = get_ran(1,0,1); play_sound(31 + i); break;
|
|
default: play_sound(33); break;
|
|
}
|
|
|
|
// Special killing effects
|
|
if (sd_legit(which_m->spec1,which_m->spec2) == true)
|
|
PSD[which_m->spec1][which_m->spec2] = 1;
|
|
|
|
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);
|
|
|
|
if ((!in_scen_debug) && ((which_m->summoned >= 100) || (which_m->summoned == 0))) { // no xp for party-summoned monsters
|
|
xp = which_m->level * 2;
|
|
if (who_killed < 6)
|
|
award_xp(who_killed,xp);
|
|
else if (who_killed == 6)
|
|
award_party_xp(xp / 6 + 1);
|
|
if (who_killed < 7) {
|
|
univ.party.total_m_killed++;
|
|
i = max((xp / 6),1);
|
|
award_party_xp(i);
|
|
}
|
|
l = which_m->cur_loc;
|
|
place_glands(l,which_m->number);
|
|
|
|
}
|
|
if ((!in_scen_debug) && (which_m->summoned == 0))
|
|
place_treasure(which_m->cur_loc, which_m->level / 2, which_m->treasure, 0);
|
|
|
|
i = which_m->cur_loc.x;
|
|
j = which_m->cur_loc.y;
|
|
switch (which_m->m_type) {
|
|
case eRace::DEMON:
|
|
univ.town.set_ash(i,j,true);
|
|
break;
|
|
// TODO: Don't check which_m->number here; find another way to indicate it
|
|
case eRace::UNDEAD:
|
|
if(which_m->number <= 59) univ.town.set_bones(i,j,true);
|
|
break;
|
|
case eRace::SLIME: case eRace::PLANT: case eRace::BUG:
|
|
univ.town.set_sm_slime(i,j,true);
|
|
break;
|
|
case eRace::STONE:
|
|
univ.town.set_rubble(i,j,true);
|
|
break;
|
|
default:
|
|
univ.town.set_sm_blood(i,j,true);
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
if (((is_town()) || (which_combat_type == 1)) && (which_m->summoned == 0)) {
|
|
univ.party.m_killed[univ.town.num]++;
|
|
}
|
|
|
|
univ.party.total_m_killed++;
|
|
|
|
which_m->spec1 = 0; // make sure, if this is a spec. activated monster, it won't come back
|
|
|
|
which_m->active = 0;
|
|
}
|
|
|
|
// Pushes party and monsters around by moving walls and conveyor belts.
|
|
//This is very fragils, and only hands a few cases.
|
|
void push_things()////
|
|
{
|
|
bool redraw = false;
|
|
short i,k;
|
|
ter_num_t ter;
|
|
location l;
|
|
|
|
if (is_out()) // TODO: Make these work outdoors
|
|
return;
|
|
if (belt_present == false)
|
|
return;
|
|
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if (univ.town.monst[i].active > 0) {
|
|
l = univ.town.monst[i].cur_loc;
|
|
ter = univ.town->terrain(l.x,l.y);
|
|
switch (scenario.ter_types[ter].flag1.u) { // TODO: Implement the other 4 possible directions
|
|
case DIR_N: l.y--; break;
|
|
case DIR_E: l.x++; break;
|
|
case DIR_S: l.y++; break;
|
|
case DIR_W: l.x--; break;
|
|
}
|
|
if (l != univ.town.monst[i].cur_loc) {
|
|
univ.town.monst[i].cur_loc = l;
|
|
if ((point_onscreen(center,univ.town.monst[i].cur_loc) == true) ||
|
|
(point_onscreen(center,l) == true))
|
|
redraw = true;
|
|
}
|
|
}
|
|
for (i = 0; i < NUM_TOWN_ITEMS; i++)
|
|
if(univ.town.items[i].variety != eItemType::NO_ITEM) {
|
|
l = univ.town.items[i].item_loc;
|
|
ter = univ.town->terrain(l.x,l.y);
|
|
switch (scenario.ter_types[ter].flag1.u) { // TODO: Implement the other 4 possible directions
|
|
case DIR_N: l.y--; break;
|
|
case DIR_E: l.x++; break;
|
|
case DIR_S: l.y++; break;
|
|
case DIR_W: l.x--; break;
|
|
}
|
|
if (l != univ.town.items[i].item_loc) {
|
|
univ.town.items[i].item_loc = l;
|
|
if ((point_onscreen(center,univ.town.items[i].item_loc) == true) ||
|
|
(point_onscreen(center,l) == true))
|
|
redraw = true;
|
|
}
|
|
}
|
|
|
|
if (is_town()) {
|
|
ter = univ.town->terrain(univ.town.p_loc.x,univ.town.p_loc.y);
|
|
l = univ.town.p_loc;
|
|
switch (scenario.ter_types[ter].flag1.u) { // TODO: Implement the other 4 possible directions
|
|
case DIR_N: l.y--; break;
|
|
case DIR_E: l.x++; break;
|
|
case DIR_S: l.y++; break;
|
|
case DIR_W: l.x--; break;
|
|
}
|
|
if (l != univ.town.p_loc) {
|
|
ASB("You get pushed.");
|
|
if(scenario.ter_types[ter].special == eTerSpec::CONVEYOR)
|
|
draw_terrain(0);
|
|
center = l;
|
|
univ.town.p_loc = l;
|
|
update_explored(l);
|
|
ter = univ.town->terrain(univ.town.p_loc.x,univ.town.p_loc.y);
|
|
draw_map(true);
|
|
if (univ.town.is_barrel(univ.town.p_loc.x,univ.town.p_loc.y)) {
|
|
univ.town.set_barrel(univ.town.p_loc.x,univ.town.p_loc.y,false);
|
|
ASB("You smash the barrel.");
|
|
}
|
|
if (univ.town.is_crate(univ.town.p_loc.x,univ.town.p_loc.y)) {
|
|
univ.town.set_crate(univ.town.p_loc.x,univ.town.p_loc.y,false);
|
|
ASB("You smash the crate.");
|
|
}
|
|
if (univ.town.is_block(univ.town.p_loc.x,univ.town.p_loc.y)) {
|
|
ASB("You crash into the block.");
|
|
hit_party(get_ran(1, 1, 6), DAMAGE_WEAPON);
|
|
}
|
|
for (k = 0; k < NUM_TOWN_ITEMS; k++)
|
|
if(univ.town.items[k].variety != eItemType::NO_ITEM && univ.town.items[k].contained
|
|
&& (univ.town.items[k].item_loc == univ.town.p_loc))
|
|
univ.town.items[k].contained = false;
|
|
redraw = true;
|
|
}
|
|
}
|
|
if (is_combat()) {
|
|
for (i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status == eMainStatus::ALIVE) {
|
|
ter = univ.town->terrain(univ.party[i].combat_pos.x,univ.party[i].combat_pos.y);
|
|
l = univ.party[i].combat_pos;
|
|
switch (scenario.ter_types[ter].flag1.u) { // TODO: Implement the other 4 possible directions
|
|
case DIR_N: l.y--; break;
|
|
case DIR_E: l.x++; break;
|
|
case DIR_S: l.y++; break;
|
|
case DIR_W: l.x--; break;
|
|
}
|
|
if (l != univ.party[i].combat_pos) {
|
|
ASB("Someone gets pushed.");
|
|
ter = univ.town->terrain(l.x,l.y);
|
|
if(scenario.ter_types[ter].special == eTerSpec::CONVEYOR)
|
|
draw_terrain(0);
|
|
univ.party[i].combat_pos = l;
|
|
update_explored(l);
|
|
draw_map(true);
|
|
if (univ.town.is_barrel(univ.party[i].combat_pos.x,univ.party[i].combat_pos.y)) {
|
|
univ.town.set_barrel(univ.party[i].combat_pos.x,univ.party[i].combat_pos.y,false);
|
|
ASB("You smash the barrel.");
|
|
}
|
|
if (univ.town.is_crate(univ.party[i].combat_pos.x,univ.party[i].combat_pos.y)) {
|
|
univ.town.set_crate(univ.party[i].combat_pos.x,univ.party[i].combat_pos.y,false);
|
|
ASB("You smash the crate.");
|
|
}
|
|
if (univ.town.is_block(univ.town.p_loc.x,univ.town.p_loc.y)) {
|
|
ASB("You crash into the block.");
|
|
damage_pc(i,get_ran(1, 1, 6), DAMAGE_WEAPON,eRace::UNKNOWN,0);
|
|
}
|
|
for (k = 0; k < NUM_TOWN_ITEMS; k++)
|
|
if(univ.town.items[k].variety != eItemType::NO_ITEM && univ.town.items[k].contained
|
|
&& (univ.town.items[k].item_loc == univ.party[i].combat_pos))
|
|
univ.town.items[k].contained = false;
|
|
redraw = true;
|
|
}
|
|
}
|
|
}
|
|
if (redraw == true) {
|
|
print_buf();
|
|
draw_terrain(0);
|
|
}
|
|
}
|
|
|
|
void special_increase_age(long length, bool queue)
|
|
{
|
|
unsigned short i;
|
|
short s1,s2,s3;
|
|
bool redraw = false,stat_area = false;
|
|
location null_loc; // TODO: Should we pass the party's location here? It doesn't quite make sense to me though...
|
|
unsigned long age_before = univ.party.age - length;
|
|
unsigned long current_age = univ.party.age;
|
|
|
|
if(is_town() || (is_combat() && which_combat_type == 1)) {
|
|
for(i = 0; i < 8; i++)
|
|
if(univ.town->timer_spec_times[i] > 0) {
|
|
short time = univ.town->timer_spec_times[i];
|
|
for(unsigned long j = age_before; j <= current_age; j++)
|
|
if(j % time == 0) {
|
|
if(queue) {
|
|
univ.party.age = j;
|
|
queue_special(eSpecCtx::TOWN_TIMER, 2, univ.town->timer_specs[i], null_loc);
|
|
} else run_special(eSpecCtx::TOWN_TIMER,2,univ.town->timer_specs[i],null_loc,&s1,&s2,&s3);
|
|
}
|
|
stat_area = true;
|
|
if(s3 > 0)
|
|
redraw = true;
|
|
}
|
|
}
|
|
univ.party.age = current_age;
|
|
for (i = 0; i < 20; i++)
|
|
if(scenario.scenario_timer_times[i] > 0) {
|
|
short time = scenario.scenario_timer_times[i];
|
|
for(unsigned long j = age_before; j <= current_age; j++)
|
|
if(j % time == 0) {
|
|
if(queue) {
|
|
univ.party.age = j;
|
|
queue_special(eSpecCtx::SCEN_TIMER, 0, scenario.scenario_timer_specs[i], null_loc);
|
|
} else run_special(eSpecCtx::SCEN_TIMER,0,scenario.scenario_timer_specs[i],null_loc,&s1,&s2,&s3);
|
|
}
|
|
stat_area = true;
|
|
if (s3 > 0)
|
|
redraw = true;
|
|
}
|
|
univ.party.age = current_age;
|
|
for (i = 0; i < univ.party.party_event_timers.size(); i++) {
|
|
if (univ.party.party_event_timers[i].time <= length) {
|
|
univ.party.age = age_before + univ.party.party_event_timers[i].time;
|
|
short which_type = univ.party.party_event_timers[i].global_or_town == 0 ? 0 : 2;
|
|
if(queue)
|
|
queue_special(eSpecCtx::PARTY_TIMER, which_type, univ.party.party_event_timers[i].node_to_call, null_loc);
|
|
else run_special(eSpecCtx::PARTY_TIMER,which_type,univ.party.party_event_timers[i].node_to_call,null_loc,&s1,&s2,&s3);
|
|
univ.party.party_event_timers[i].time = 0;
|
|
stat_area = true;
|
|
if (s3 > 0)
|
|
redraw = true;
|
|
} else univ.party.party_event_timers[i].time -= length;
|
|
}
|
|
univ.party.age = current_age;
|
|
if (stat_area == true) {
|
|
put_pc_screen();
|
|
put_item_screen(stat_window,0);
|
|
}
|
|
if (redraw == true)
|
|
draw_terrain(0);
|
|
}
|
|
|
|
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;
|
|
special_queue.push(queued_special);
|
|
}
|
|
|
|
void run_special(pending_special_type spec, short* a, short* b, short* redraw) {
|
|
unsigned long store_time = univ.party.age;
|
|
univ.party.age = spec.trigger_time;
|
|
run_special(spec.mode, spec.type, spec.spec, spec.where, a, b, redraw);
|
|
univ.party.age = std::max(univ.party.age, store_time);
|
|
}
|
|
|
|
// This is the big painful one, the main special engine
|
|
// which_mode - says when it was called
|
|
// 0 - out moving (a - 1 if blocked)
|
|
// 1 - town moving (a - 1 if blocked)
|
|
// 2 - combat moving (a - 1 if blocked)
|
|
// 3 - out looking (a - 1 if don't get items inside) TODO: NOT USED!!!
|
|
// 4 - town looking (a - 1 if don't get items inside)
|
|
// 5 - entering town
|
|
// 6 - leaving town
|
|
// 7 - talking (a,b - numbers of strings to respond)
|
|
// 8 - using a special item
|
|
// 9 - town timer
|
|
// 10 - scen timer
|
|
// 11 - party countdown timer
|
|
// 12 - killed a monst
|
|
// 13 - encountering outdoor enc (a - 1 if no fight)
|
|
// 14 - winning outdoor enc
|
|
// 15 - fleeing outdoor enc
|
|
// 16 - target spell on space TODO: Maybe this will become just "target space"?
|
|
// 17 - using space
|
|
// 18 - seeing monster
|
|
// which_type - 0 - scen 1 - out 2 - town
|
|
// start spec - the number of the first spec to call
|
|
// a,b - 2 values that can be returned
|
|
// redraw - 1 if now need redraw
|
|
void run_special(eSpecCtx which_mode,short which_type,short start_spec,location spec_loc,short *a,short *b,short *redraw)
|
|
{
|
|
short cur_spec,cur_spec_type,next_spec,next_spec_type;
|
|
cSpecial cur_node;
|
|
int num_nodes = 0;
|
|
|
|
// 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;
|
|
next_spec = start_spec;
|
|
next_spec_type = which_type;
|
|
current_pc_picked_in_spec_enc = -1;
|
|
store_special_loc = spec_loc;
|
|
if (end_scenario == true) {
|
|
special_in_progress = false;
|
|
return;
|
|
}
|
|
|
|
// 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;
|
|
|
|
while (next_spec >= 0) {
|
|
|
|
cur_spec = next_spec;
|
|
cur_spec_type = next_spec_type;
|
|
next_spec = -1;
|
|
cur_node = get_node(cur_spec,cur_spec_type);
|
|
|
|
|
|
// Convert pointer values to reference values
|
|
// TODO: Might need to make a database of which nodes don't allow pointers in which slots.
|
|
// This is because some nodes now use -2 as a meaningful value. If that's all, then
|
|
// just disallowing single-digit pointers should suffice, but what about arithmetic?
|
|
// (Of course, currently all SDFs are positive, so allowing negative arithmetic is useless.)
|
|
if(cur_node.sd1 <= -10) cur_node.sd1 = univ.party.get_ptr(-cur_node.sd1);
|
|
if(cur_node.sd2 <= -10) cur_node.sd2 = univ.party.get_ptr(-cur_node.sd2);
|
|
if(cur_node.ex1a <= -10) cur_node.ex1a = univ.party.get_ptr(-cur_node.ex1a);
|
|
if(cur_node.ex1b <= -10) cur_node.ex1a = univ.party.get_ptr(-cur_node.ex1b);
|
|
if(cur_node.ex1c <= -10) cur_node.ex1a = univ.party.get_ptr(-cur_node.ex1c);
|
|
if(cur_node.ex2a <= -10) cur_node.ex1a = univ.party.get_ptr(-cur_node.ex2a);
|
|
if(cur_node.ex2b <= -10) cur_node.ex1a = univ.party.get_ptr(-cur_node.ex2b);
|
|
if(cur_node.ex2c <= -10) 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) {
|
|
special_in_progress = false;
|
|
return;
|
|
}
|
|
switch(getNodeCategory(cur_node.type)) {
|
|
case eSpecCat::GENERAL:
|
|
general_spec(which_mode,cur_node,cur_spec_type,&next_spec,&next_spec_type,a,b,redraw);
|
|
break;
|
|
case eSpecCat::ONCE:
|
|
oneshot_spec(which_mode,cur_node,cur_spec_type,&next_spec,&next_spec_type,a,b,redraw);
|
|
break;
|
|
case eSpecCat::AFFECT:
|
|
affect_spec(which_mode,cur_node,cur_spec_type,&next_spec,&next_spec_type,a,b,redraw);
|
|
break;
|
|
case eSpecCat::IF_THEN:
|
|
ifthen_spec(which_mode,cur_node,cur_spec_type,&next_spec,&next_spec_type,a,b,redraw);
|
|
break;
|
|
case eSpecCat::TOWN:
|
|
townmode_spec(which_mode,cur_node,cur_spec_type,&next_spec,&next_spec_type,a,b,redraw);
|
|
break;
|
|
case eSpecCat::RECT:
|
|
rect_spec(which_mode,cur_node,cur_spec_type,&next_spec,&next_spec_type,a,b,redraw);
|
|
break;
|
|
case eSpecCat::OUTDOOR:
|
|
outdoor_spec(which_mode,cur_node,cur_spec_type,&next_spec,&next_spec_type,a,b,redraw);
|
|
break;
|
|
case eSpecCat::INVALID:
|
|
// TODO: Should it print some kind of error message?
|
|
special_in_progress = false;
|
|
return;
|
|
}
|
|
|
|
num_nodes++;
|
|
|
|
if(check_for_interrupt()){
|
|
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;
|
|
}
|
|
}
|
|
if (is_out())
|
|
erase_out_specials();
|
|
else erase_specials();
|
|
special_in_progress = false;
|
|
|
|
if(next_spec == -1 && !special_queue.empty()) {
|
|
pending_special_type pending = special_queue.front();
|
|
special_queue.pop();
|
|
run_special(pending, a, b, redraw);
|
|
}
|
|
}
|
|
|
|
cSpecial get_node(short cur_spec,short cur_spec_type)
|
|
{
|
|
cSpecial dummy_node;
|
|
|
|
dummy_node = scenario.scen_specials[0];
|
|
dummy_node.type = eSpecType::ERROR;
|
|
if (cur_spec_type == 0) {
|
|
if (cur_spec != minmax(0,255,cur_spec)) {
|
|
giveError("The scenario called a scenario special node out of range.");
|
|
return dummy_node;
|
|
}
|
|
return scenario.scen_specials[cur_spec];
|
|
}
|
|
if (cur_spec_type == 1) {
|
|
if (cur_spec != minmax(0,59,cur_spec)) {
|
|
giveError("The scenario called an outdoor special node out of range.");
|
|
return dummy_node;
|
|
}
|
|
return univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].specials[cur_spec];
|
|
}
|
|
if (cur_spec_type == 2) {
|
|
if (cur_spec != minmax(0,99,cur_spec)) {
|
|
giveError("The scenario called a town special node out of range.");
|
|
return dummy_node;
|
|
}
|
|
return univ.town->specials[cur_spec];
|
|
}
|
|
return dummy_node;
|
|
}
|
|
|
|
// TODO: Make cur_spec_type an enum
|
|
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)
|
|
{
|
|
bool check_mess = false;
|
|
std::string str1,str2;
|
|
short store_val = 0,i,j;
|
|
cSpecial spec;
|
|
short mess_adj[3] = {160,10,0};
|
|
|
|
spec = cur_node;
|
|
*next_spec = cur_node.jumpto;
|
|
|
|
switch (cur_node.type) {
|
|
case eSpecType::NONE: break; // null spec
|
|
case eSpecType::SET_SDF:
|
|
check_mess = true; setsd(cur_node.sd1,cur_node.sd2,cur_node.ex1a);
|
|
break;
|
|
case eSpecType::INC_SDF:
|
|
check_mess = true;
|
|
setsd(cur_node.sd1,cur_node.sd2,
|
|
PSD[cur_node.sd1][cur_node.sd2] + ((cur_node.ex1b == 0) ? 1 : -1) * cur_node.ex1a);
|
|
break;
|
|
case eSpecType::DISPLAY_MSG:
|
|
check_mess = true;
|
|
break;
|
|
case eSpecType::DISPLAY_SM_MSG:
|
|
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(), 4);
|
|
if (cur_node.m2 >= 0)
|
|
ASB(str2.c_str(), 4);
|
|
break;
|
|
case eSpecType::FLIP_SDF:
|
|
setsd(cur_node.sd1,cur_node.sd2,
|
|
((PSD[cur_node.sd1][cur_node.sd2] == 0) ? 1 : 0) );
|
|
check_mess = true;break;
|
|
case eSpecType::CANT_ENTER:
|
|
check_mess = true;
|
|
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;
|
|
}
|
|
break;
|
|
case eSpecType::CHANGE_TIME:
|
|
check_mess = true;
|
|
univ.party.age += spec.ex1a;
|
|
break;
|
|
case eSpecType::SCEN_TIMER_START:
|
|
check_mess = true;
|
|
univ.party.start_timer(spec.ex1a, spec.ex1b, 0);
|
|
break;
|
|
case eSpecType::PLAY_SOUND:
|
|
play_sound(spec.ex1a);
|
|
break;
|
|
case eSpecType::CHANGE_HORSE_OWNER:
|
|
check_mess = true;
|
|
if (spec.ex1a != minmax(0,29,spec.ex1a))
|
|
giveError("Horse out of range.");
|
|
else univ.party.horses[spec.ex1a].property = (spec.ex2a == 0) ? 1 : 0;
|
|
break;
|
|
case eSpecType::CHANGE_BOAT_OWNER:
|
|
check_mess = true;
|
|
if (spec.ex1a != minmax(0,29,spec.ex1a))
|
|
giveError("Boat out of range.");
|
|
else univ.party.boats[spec.ex1a].property = (spec.ex2a == 0) ? 1 : 0;
|
|
break;
|
|
case eSpecType::SET_TOWN_VISIBILITY:
|
|
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.ex2a;
|
|
*redraw = true;
|
|
break;
|
|
case eSpecType::MAJOR_EVENT_OCCURRED:
|
|
check_mess = true;
|
|
if (spec.ex1a != minmax(1,10,spec.ex1a))
|
|
giveError("Event code out of range.");
|
|
else if (univ.party.key_times[spec.ex1a] == 30000)
|
|
univ.party.key_times[spec.ex1a] = calc_day();
|
|
break;
|
|
case eSpecType::FORCED_GIVE:
|
|
check_mess = true;
|
|
if(!forced_give(spec.ex1a,eItemAbil::NONE) && spec.ex1b >= 0)
|
|
*next_spec = spec.ex1b;
|
|
break;
|
|
case eSpecType::BUY_ITEMS_OF_TYPE:
|
|
for (i = 0; i < 144; i++)
|
|
if (party_check_class(spec.ex1a,0) == true)
|
|
store_val++;
|
|
if (store_val == 0) {
|
|
if ( spec.ex1b >= 0)
|
|
*next_spec = spec.ex1b;
|
|
}
|
|
else {
|
|
check_mess = true;
|
|
give_gold(store_val * spec.ex2a,true);
|
|
}
|
|
break;
|
|
case eSpecType::CALL_GLOBAL:
|
|
*next_spec_type = 0;
|
|
break;
|
|
case eSpecType::SET_SDF_ROW:
|
|
if (spec.sd1 != minmax(0,299,spec.sd1))
|
|
giveError("Stuff Done flag out of range.");
|
|
else for (i = 0; i < 10; i++) PSD[spec.sd1][i] = spec.ex1a;
|
|
break;
|
|
case eSpecType::COPY_SDF:
|
|
if ((sd_legit(spec.sd1,spec.sd2) == false) || (sd_legit(spec.ex1a,spec.ex1b) == false))
|
|
giveError("Stuff Done flag out of range.");
|
|
else PSD[spec.sd1][spec.sd2] = PSD[spec.ex1a][spec.ex1b];
|
|
break;
|
|
case eSpecType::REST:
|
|
check_mess = true;
|
|
do_rest(spec.ex1a, spec.ex1b, spec.ex1b);
|
|
break;
|
|
case eSpecType::WANDERING_WILL_FIGHT:
|
|
if(which_mode != eSpecCtx::OUTDOOR_ENC)
|
|
break;
|
|
*a = (spec.ex1a == 0) ? 1 : 0;
|
|
break;
|
|
case eSpecType::END_SCENARIO:
|
|
// If party died at some point during the special node, they shouldn't get a victory.
|
|
// (Adapted from Windows version)
|
|
store_val = 6;
|
|
for(i = 0; i < 6; i++)
|
|
if(univ.party[i].main_status != eMainStatus::ALIVE)
|
|
store_val--;
|
|
if(store_val == 0) break;
|
|
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;
|
|
case eSpecType::DISPLAY_PICTURE:
|
|
// TODO: In addition to the large picture, there's a small icon; should that be customizable?
|
|
check_mess = false;
|
|
get_strs(str1, str1, cur_spec_type, spec.m1, -1);
|
|
custom_pic_dialog(str1, spec.ex1a);
|
|
break;
|
|
case eSpecType::SDF_RANDOM:
|
|
check_mess = true;
|
|
|
|
short rand;
|
|
// Automatically fix the range in case some idiot puts it in backwards, or the same (WHY)
|
|
if (cur_node.ex1a == cur_node.ex1b) {
|
|
rand = cur_node.ex1b;
|
|
} else {
|
|
rand = get_ran(1,
|
|
min(cur_node.ex1a,cur_node.ex1b),
|
|
max(cur_node.ex1a,cur_node.ex1b)
|
|
);
|
|
}
|
|
setsd(cur_node.sd1,cur_node.sd2,rand);
|
|
//print_nums(rand, cur_node.ex1a, cur_node.ex1b);
|
|
break;
|
|
// SDF arithmetic! :D
|
|
/*
|
|
SDF1, SDF2 - Output SDF (for division, the quotient)
|
|
Ex1a, Ex1b - Input SDF (left operand) - if ex1b is -1, takes ex1a as a literal value (which must be positive)
|
|
Ex2a, Ex2b - Input SDF (right operand) - if ex2b is -1, takes ex2a as a literal value (which must be positive)
|
|
Ex1c, Ex2c - For division only, output SDF to store the remainder.
|
|
*/
|
|
case eSpecType::SDF_ADD: case eSpecType::SDF_DIFF:
|
|
case eSpecType::SDF_TIMES: case eSpecType::SDF_POWER:
|
|
case eSpecType::SDF_DIVIDE:
|
|
i = spec.ex1b == -1 ? spec.ex1a : PSD[spec.ex1a][spec.ex1b];
|
|
j = spec.ex2b == -1 ? spec.ex2a : PSD[spec.ex2a][spec.ex2b];
|
|
switch(spec.type) {
|
|
case eSpecType::SDF_ADD: setsd(spec.sd1, spec.sd2, i + j); break;
|
|
case eSpecType::SDF_DIFF: setsd(spec.sd1, spec.sd2, i - j); break;
|
|
case eSpecType::SDF_TIMES: setsd(spec.sd1, spec.sd2, i * j); break;
|
|
case eSpecType::SDF_DIVIDE:
|
|
setsd(spec.sd1, spec.sd2, i / j);
|
|
setsd(spec.ex1c, spec.ex2c, i % j);
|
|
break;
|
|
case eSpecType::SDF_POWER:
|
|
if(i == 2) setsd(spec.sd1, spec.sd2, 1 << j);
|
|
else setsd(spec.sd1, spec.sd2, pow(i, j));
|
|
break;
|
|
default: // Unreachable case
|
|
break;
|
|
}
|
|
break;
|
|
case eSpecType::PRINT_NUMS:
|
|
if(!in_scen_debug) break;
|
|
check_mess = false;
|
|
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("debug: " + str1, 7);
|
|
if(cur_node.m2 >= 0)
|
|
ASB("debug: " + str2, 7);
|
|
// TODO: Give more options?
|
|
switch(spec.pic) {
|
|
case 0: // Print SDF contents
|
|
print_nums(spec.sd1, spec.sd2, univ.party.stuff_done[spec.sd1][spec.sd2]);
|
|
break;
|
|
case 1: // Print three literal values (which might be pointers!)
|
|
print_nums(spec.ex1a, spec.ex1b, spec.ex1c);
|
|
break;
|
|
case 2: // Print monster health and spell points
|
|
if(spec.ex1a >= univ.town->max_monst()) break;
|
|
print_nums(spec.ex1a, univ.town.monst[spec.ex1a].health, univ.town.monst[spec.ex1a].mp);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (check_mess == true) {
|
|
handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b);
|
|
}
|
|
}
|
|
|
|
/*
|
|
void general_spec(short which_mode,special_node_type cur_node,short cur_spec_type,
|
|
short *next_spec,short *next_spec_type,short *a,short *b,short *redraw)
|
|
{
|
|
bool check_mess = false;
|
|
char str1[256] = "",str2 = "";
|
|
short store_val = 0,i,j;
|
|
special_node_type spec;
|
|
|
|
spec = cur_node;
|
|
*next_spec = cur_node.jumpto;
|
|
|
|
switch (cur_node.type) {
|
|
case :
|
|
break;
|
|
}
|
|
if (check_mess == true) {
|
|
handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b);
|
|
}
|
|
}
|
|
*/
|
|
|
|
// TODO: What was next_spec_type for? Is it still needed?
|
|
void oneshot_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
|
short *next_spec,short */*next_spec_type*/,short *a,short *b,short *redraw)
|
|
{
|
|
bool check_mess = true,set_sd = true;
|
|
std::array<std::string, 6> strs;
|
|
short i,j;
|
|
std::array<short, 3> buttons = {-1,-1,-1};
|
|
cSpecial spec;
|
|
cItemRec store_i;
|
|
location l;
|
|
std::string choice;
|
|
|
|
spec = cur_node;
|
|
*next_spec = cur_node.jumpto;
|
|
if ((sd_legit(spec.sd1,spec.sd2) == true) && (PSD[spec.sd1][spec.sd2] == 250)) {
|
|
*next_spec = -1;
|
|
return;
|
|
}
|
|
switch (cur_node.type) {
|
|
case eSpecType::ONCE_GIVE_ITEM:
|
|
if(!forced_give(spec.ex1a,eItemAbil::NONE)) {
|
|
set_sd = false;
|
|
if ( spec.ex2b >= 0)
|
|
*next_spec = spec.ex2b;
|
|
}
|
|
else {
|
|
give_gold(spec.ex1b,true);
|
|
give_food(spec.ex2a,true);
|
|
}
|
|
break;
|
|
case eSpecType::ONCE_GIVE_SPEC_ITEM:
|
|
if (spec.ex1a != minmax(0,49,spec.ex1a)) {
|
|
giveError("Special item is out of range.");
|
|
set_sd = false;
|
|
}
|
|
else {
|
|
univ.party.spec_items[spec.ex1a] = (spec.ex1b == 0) ? 1 : 0;
|
|
}
|
|
if (stat_window == 6)
|
|
set_stat_window(6);
|
|
*redraw = 1;
|
|
break;
|
|
case eSpecType::ONCE_NULL:
|
|
set_sd = false;
|
|
check_mess = false;
|
|
break;
|
|
case eSpecType::ONCE_SET_SDF:
|
|
check_mess = false;
|
|
break;
|
|
case eSpecType::ONCE_DIALOG:
|
|
case eSpecType::ONCE_DIALOG_TERRAIN:
|
|
case eSpecType::ONCE_DIALOG_MONSTER:
|
|
check_mess = false;
|
|
if (spec.m1 < 0)
|
|
break;
|
|
for (i = 0; i < 3; i++)
|
|
get_strs(strs[i * 2],strs[i * 2 + 1],cur_spec_type,
|
|
spec.m1 + i * 2 + spec_str_offset[cur_spec_type],spec.m1 + i * 2 + 1 + spec_str_offset[cur_spec_type]);
|
|
if (spec.m2 > 0)
|
|
{buttons[0] = 1; buttons[1] = spec.ex1a; buttons[2] = spec.ex2a;
|
|
if ((spec.ex1a >= 0) || (spec.ex2a >= 0)) buttons[0] = 20; }
|
|
if (spec.m2 <= 0) {buttons[0] = spec.ex1a;buttons[1] = spec.ex2a;}
|
|
if ((buttons[0] < 0) && (buttons[1] < 0)) {
|
|
giveError("Dialog box ended up with no buttons.");
|
|
break;
|
|
}
|
|
switch (cur_node.type) {
|
|
case eSpecType::ONCE_DIALOG:
|
|
if (spec.pic >= 1000) i = custom_choice_dialog(strs,spec.pic % 1000, PIC_CUSTOM_DLOG,buttons) ;
|
|
else i = custom_choice_dialog(strs,spec.pic, PIC_DLOG,buttons) ; break;
|
|
case eSpecType::ONCE_DIALOG_TERRAIN:
|
|
if (spec.pic >= 1000) i = custom_choice_dialog(strs,spec.pic % 1000, PIC_CUSTOM_TER,buttons) ;
|
|
else i = custom_choice_dialog(strs,spec.pic,PIC_TER,buttons) ; break;
|
|
case eSpecType::ONCE_DIALOG_MONSTER:
|
|
if (spec.pic >= 1000) i = custom_choice_dialog(strs,spec.pic % 1000, PIC_CUSTOM_MONST,buttons) ;
|
|
else i = custom_choice_dialog(strs,spec.pic, get_monst_pictype(spec.pic),buttons) ; break;
|
|
}
|
|
if (spec.m2 > 0) {
|
|
if (i == 1) {
|
|
if ((spec.ex1a >= 0) || (spec.ex2a >= 0)) {
|
|
set_sd = false;
|
|
}
|
|
}
|
|
if (i == 2) *next_spec = spec.ex1b;
|
|
if (i == 3) *next_spec = spec.ex2b;
|
|
}
|
|
else {
|
|
if (i == 1) *next_spec = spec.ex1b;
|
|
if (i == 2) *next_spec = spec.ex2b;
|
|
}
|
|
break;
|
|
case eSpecType::ONCE_GIVE_ITEM_DIALOG:
|
|
case eSpecType::ONCE_GIVE_ITEM_TERRAIN:
|
|
case eSpecType::ONCE_GIVE_ITEM_MONSTER:
|
|
check_mess = false;
|
|
if (spec.m1 < 0)
|
|
break;
|
|
for (i = 0; i < 3; i++)
|
|
get_strs(strs[i * 2],strs[i * 2 + 1],cur_spec_type,
|
|
spec.m1 + i * 2 + spec_str_offset[cur_spec_type],spec.m1 + i * 2 + 1 + spec_str_offset[cur_spec_type]);
|
|
buttons[0] = 20; buttons[1] = 19;
|
|
//i = custom_choice_dialog(strs,spec.pic,buttons) ;
|
|
switch (cur_node.type) {
|
|
case eSpecType::ONCE_GIVE_ITEM_DIALOG:
|
|
if (spec.pic >= 1000) i = custom_choice_dialog(strs,spec.pic % 1000,PIC_CUSTOM_DLOG,buttons) ;
|
|
else i = custom_choice_dialog(strs,spec.pic,PIC_DLOG,buttons) ; break;
|
|
case eSpecType::ONCE_GIVE_ITEM_TERRAIN:
|
|
if (spec.pic >= 1000) i = custom_choice_dialog(strs,spec.pic % 1000,PIC_CUSTOM_TER,buttons) ;
|
|
else i = custom_choice_dialog(strs,spec.pic,PIC_TER,buttons) ; break;
|
|
case eSpecType::ONCE_GIVE_ITEM_MONSTER:
|
|
if (spec.pic >= 1000) i = custom_choice_dialog(strs,spec.pic % 1000,PIC_CUSTOM_MONST,buttons) ;
|
|
else i = custom_choice_dialog(strs,spec.pic,get_monst_pictype(spec.pic),buttons) ; break;
|
|
}
|
|
if (i == 1) {set_sd = false; *next_spec = -1;}
|
|
else {
|
|
store_i = get_stored_item(spec.ex1a);
|
|
if ((spec.ex1a >= 0) && (!give_to_party(store_i,true))) {
|
|
set_sd = false; *next_spec = -1;
|
|
}
|
|
else {
|
|
give_gold(spec.ex1b,true);
|
|
give_food(spec.ex2a,true);
|
|
if ((spec.m2 >= 0) && (spec.m2 < 50)) {
|
|
if (univ.party.spec_items[spec.m2] == 0)
|
|
ASB("You get a special item.");
|
|
univ.party.spec_items[spec.m2] = 1;
|
|
*redraw = true;
|
|
if (stat_window == 6)
|
|
set_stat_window(6);
|
|
}
|
|
if (spec.ex2b >= 0) *next_spec = spec.ex2b;
|
|
}
|
|
}
|
|
break;
|
|
case eSpecType::ONCE_OUT_ENCOUNTER:
|
|
if (spec.ex1a != minmax(0,3,spec.ex1a)) {
|
|
giveError("Special outdoor enc. is out of range. Must be 0-3.");
|
|
set_sd = false;
|
|
}
|
|
else {
|
|
l = global_to_local(univ.party.p_loc);
|
|
place_outd_wand_monst(l,
|
|
univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].special_enc[spec.ex1a],true);
|
|
}
|
|
break;
|
|
case eSpecType::ONCE_TOWN_ENCOUNTER:
|
|
activate_monsters(spec.ex1a,0);
|
|
break;
|
|
case eSpecType::ONCE_TRAP:
|
|
check_mess = false;
|
|
if ((spec.m1 >= 0) || (spec.m2 >= 0)) {
|
|
get_strs(strs[0],strs[1],
|
|
cur_spec_type,
|
|
spec.m1 + ((spec.m1 >= 0) ? spec_str_offset[cur_spec_type] : 0),
|
|
spec.m2 + ((spec.m2 >= 0) ? spec_str_offset[cur_spec_type] : 0));
|
|
buttons[0] = 3; buttons[1] = 2;
|
|
// TODO: Why not allow a choice of dialog picture?
|
|
i = custom_choice_dialog(strs,27,PIC_DLOG,buttons);
|
|
// TODO: Make custom_choice_dialog return string?
|
|
}
|
|
else i = cChoiceDlog("basic-trap.xml",{"yes","no"}).show() == "no";
|
|
if (i == 1) {set_sd = false; *next_spec = -1; *a = 1;}
|
|
else {
|
|
if (is_combat() == true)
|
|
j = run_trap(current_pc,(eTrapType)spec.ex1a,spec.ex1b,spec.ex2a);
|
|
else j = run_trap(7,(eTrapType)spec.ex1a,spec.ex1b,spec.ex2a);
|
|
if (j == 0) {
|
|
*a = 1; set_sd = false;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
if (check_mess == true) {
|
|
handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b);
|
|
}
|
|
if ((set_sd == true) && (sd_legit(spec.sd1,spec.sd2) == true))
|
|
PSD[spec.sd1][spec.sd2] = 250;
|
|
|
|
}
|
|
|
|
// TODO: What was next_spec_type for? Is it still needed?
|
|
void affect_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
|
short *next_spec,short */*next_spec_type*/,short *a,short *b,short *redraw)
|
|
{
|
|
bool check_mess = true;
|
|
short i,pc = 6,r1;
|
|
cSpecial spec;
|
|
|
|
spec = cur_node;
|
|
*next_spec = cur_node.jumpto;
|
|
if(univ.party.is_split() && cur_node.type != eSpecType::AFFECT_DEADNESS)
|
|
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) pc = -1;
|
|
|
|
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) {
|
|
i = select_pc(0,0);
|
|
if (i != 6)
|
|
current_pc_picked_in_spec_enc = i;
|
|
}
|
|
else if (spec.ex1a == 0) {
|
|
i = select_pc(1,0);
|
|
if (i != 6)
|
|
current_pc_picked_in_spec_enc = i;
|
|
}
|
|
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:
|
|
{
|
|
r1 = get_ran(spec.ex1a,1,spec.ex1b) + spec.ex2a;
|
|
eDamageType dam_type = (eDamageType) spec.ex2b;
|
|
if (pc < 0) {
|
|
if(spec.pic == 1 && overall_mode == MODE_COMBAT)
|
|
damage_pc(current_pc,r1,dam_type,eRace::UNKNOWN,0); // was HUMAN
|
|
else hit_party(r1,dam_type);
|
|
}
|
|
else damage_pc(pc,r1,dam_type,eRace::UNKNOWN,0);
|
|
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++)
|
|
if ((pc < 0) || (pc == i)) {
|
|
if (spec.ex1b == 0) award_xp(i,spec.ex1a); else drain_pc(i,spec.ex1a);
|
|
}
|
|
break;
|
|
case eSpecType::AFFECT_SKILL_PTS:
|
|
for (i = 0; i < 6; i++)
|
|
if ((pc < 0) || (pc == i))
|
|
univ.party[i].skill_pts = minmax(0, 100,
|
|
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:
|
|
kill_pc(i,eMainStatus::SPLIT_DEAD);
|
|
case 1:
|
|
kill_pc(i,eMainStatus::SPLIT_DUST);
|
|
case 2:
|
|
kill_pc(i,eMainStatus::SPLIT_STONE);
|
|
}
|
|
}
|
|
*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) {
|
|
cure_pc(i,spec.ex1a);
|
|
}
|
|
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) {
|
|
slow_pc(i,-spec.ex1a);
|
|
}
|
|
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++)
|
|
if ((pc < 0) || (pc == i))
|
|
affect_pc(i,eStatus::INVULNERABLE,spec.ex1a * ((spec.ex1b != 0) ? -1: 1));
|
|
break;
|
|
case eSpecType::AFFECT_MAGIC_RES:
|
|
for (i = 0; i < 6; i++)
|
|
if ((pc < 0) || (pc == i))
|
|
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++)
|
|
if ((pc < 0) || (pc == i))
|
|
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) {
|
|
affect_pc(i,eStatus::ASLEEP,-1 * spec.ex1a);
|
|
}
|
|
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) {
|
|
affect_pc(i,eStatus::PARALYZED,-1 * spec.ex1a);
|
|
}
|
|
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,20,spec.ex2a)) {
|
|
giveError("Skill is out of range.");
|
|
break;
|
|
}
|
|
for (i = 0; i < 6; i++)
|
|
if((pc < 0 || pc == i) && get_ran(1,1,100) < spec.pic) {
|
|
eSkill skill = eSkill(spec.ex2a);
|
|
int adj = spec.ex1a * (spec.ex1b != 0 ? -1: 1);
|
|
if(skill == eSkill::MAX_HP)
|
|
univ.party[i].max_health = minmax(6, 250, univ.party[i].max_health + adj);
|
|
else if(skill == eSkill::MAX_SP)
|
|
univ.party[i].max_sp = minmax(0, 150, univ.party[i].max_sp + adj);
|
|
else univ.party[i].skills[skill] = minmax(0, skill_max[skill], univ.party[i].skills[skill] + adj);
|
|
}
|
|
break;
|
|
case eSpecType::AFFECT_MAGE_SPELL:
|
|
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] = spec.ex1b;
|
|
break;
|
|
case eSpecType::AFFECT_PRIEST_SPELL:
|
|
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] = spec.ex1b;
|
|
break;
|
|
case eSpecType::AFFECT_GOLD:
|
|
if (spec.ex1b == 0)
|
|
give_gold(spec.ex1a,true);
|
|
else if (univ.party.gold < spec.ex1a)
|
|
univ.party.gold = 0;
|
|
else take_gold(spec.ex1a,false);
|
|
break;
|
|
case eSpecType::AFFECT_FOOD:
|
|
if (spec.ex1b == 0)
|
|
give_food(spec.ex1a,true);
|
|
else if (univ.party.food < spec.ex1a)
|
|
univ.party.food = 0;
|
|
else take_food(spec.ex1a,false);
|
|
break;
|
|
case eSpecType::AFFECT_ALCHEMY:
|
|
if (spec.ex1a != minmax(0,19,spec.ex1a)) {
|
|
giveError("Alchemy is out of range.");
|
|
break;
|
|
}
|
|
univ.party.alchemy[spec.ex1a] = true;
|
|
break;
|
|
case eSpecType::AFFECT_STEALTH:
|
|
r1 = (short) PSD[SDF_PARTY_STEALTHY];
|
|
r1 = minmax(0,250,r1 + spec.ex1a);
|
|
PSD[SDF_PARTY_STEALTHY] = r1;
|
|
break;
|
|
case eSpecType::AFFECT_FIREWALK:
|
|
r1 = (short) PSD[SDF_PARTY_FIREWALK];
|
|
r1 = minmax(0,250,r1 + spec.ex1a);
|
|
PSD[SDF_PARTY_FIREWALK] = r1;
|
|
break;
|
|
case eSpecType::AFFECT_FLIGHT:
|
|
if (univ.party.in_boat >= 0)
|
|
add_string_to_buf(" Can't fly when on a boat. ");
|
|
else if (univ.party.in_horse >= 0)////
|
|
add_string_to_buf(" Can't fly when on a horse. ");
|
|
else {
|
|
r1 = (short) PSD[SDF_PARTY_FLIGHT];
|
|
r1 = minmax(0,250,r1 + spec.ex1a);
|
|
PSD[SDF_PARTY_FLIGHT] = r1;
|
|
}
|
|
break;
|
|
}
|
|
if (check_mess) {
|
|
handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b);
|
|
}
|
|
}
|
|
|
|
// TODO: What was next_spec_type for? Is it still needed?
|
|
void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
|
short *next_spec,short */*next_spec_type*/,short *a,short *b,short *redraw)
|
|
{
|
|
bool check_mess = false;
|
|
std::string str1, str2, str3;
|
|
short i,j,k;
|
|
cSpecial spec;
|
|
location l;
|
|
|
|
spec = cur_node;
|
|
*next_spec = cur_node.jumpto;
|
|
|
|
switch (cur_node.type) {
|
|
case eSpecType::IF_SDF:
|
|
if (sd_legit(spec.sd1,spec.sd2) == true) {
|
|
if ((spec.ex1a >= 0) && (PSD[spec.sd1][spec.sd2] >= spec.ex1a))
|
|
*next_spec = spec.ex1b;
|
|
else if ((spec.ex2a >= 0) && (PSD[spec.sd1][spec.sd2] < spec.ex2a))
|
|
*next_spec = spec.ex2b;
|
|
}
|
|
break;
|
|
case eSpecType::IF_TOWN_NUM:
|
|
if (((is_town()) || (is_combat())) && (univ.town.num == spec.ex1a))
|
|
*next_spec = spec.ex1b;
|
|
break;
|
|
case eSpecType::IF_RANDOM:
|
|
if (get_ran(1,1,100) < spec.ex1a)
|
|
*next_spec = spec.ex1b;
|
|
break;
|
|
case eSpecType::IF_HAVE_SPECIAL_ITEM:
|
|
if (spec.ex1a != minmax(0,49,spec.ex1a)) {
|
|
giveError("Special item is out of range.");
|
|
}
|
|
else if (univ.party.spec_items[spec.ex1a] > 0)
|
|
*next_spec = spec.ex1b;
|
|
break;
|
|
case eSpecType::IF_SDF_COMPARE:
|
|
if ((sd_legit(spec.sd1,spec.sd2) == true) && (sd_legit(spec.ex1a,spec.ex1b) == true)) {
|
|
if (PSD[spec.ex1a][spec.ex1b] < PSD[spec.sd1][spec.sd2])
|
|
*next_spec = spec.ex2b;
|
|
}
|
|
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:
|
|
l.x = spec.ex1a; l.y = spec.ex1b;
|
|
l = local_to_global(l);
|
|
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)
|
|
*next_spec = spec.ex1b;
|
|
break;
|
|
case eSpecType::IF_HAS_FOOD:
|
|
if (univ.party.food >= spec.ex1a)
|
|
*next_spec = spec.ex1b;
|
|
break;
|
|
case eSpecType::IF_ITEM_CLASS_ON_SPACE:
|
|
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;
|
|
break;
|
|
case eSpecType::IF_HAVE_ITEM_CLASS:
|
|
if (party_check_class(spec.ex1a,1) == true)
|
|
*next_spec = spec.ex1b;
|
|
break;
|
|
case eSpecType::IF_EQUIP_ITEM_CLASS:
|
|
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] == true))
|
|
*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) == true)
|
|
*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] == true)) {
|
|
*next_spec = spec.ex1b;
|
|
*redraw = 1;
|
|
take_item(i,j);
|
|
}
|
|
break;
|
|
case eSpecType::IF_DAY_REACHED:
|
|
if (calc_day() >= spec.ex1a)
|
|
*next_spec = spec.ex1b;
|
|
break;
|
|
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;
|
|
} 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
|
|
// TODO: Allow requiring a minimum and maximum number of objects
|
|
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_SPECIES:
|
|
if(spec.ex1a < 0 || spec.ex1a > 2) {
|
|
giveError("Species out of range (0-human, 1-nephilim, 2-slith)");
|
|
break; // TODO: Should we allow monster races too?
|
|
}
|
|
i = 0;
|
|
j = min(spec.ex2a,party_size(true));
|
|
if (j < 1)
|
|
j = 1;
|
|
i = race_present(eRace(spec.ex1a));
|
|
if(spec.ex2b == -2 && i <= j) *next_spec = spec.ex1b;
|
|
if(spec.ex2b == -1 && i < j) *next_spec = spec.ex1b;
|
|
if(spec.ex2b == 0 && i == j) *next_spec = spec.ex1b;
|
|
if(spec.ex2b == 1 && i > j) *next_spec = spec.ex1b;
|
|
if(spec.ex2b == 2 && i >= j) *next_spec = spec.ex1b;
|
|
break;
|
|
case eSpecType::IF_TRAIT:
|
|
if(spec.ex1a < 0 || spec.ex1a > 15) {
|
|
giveError("Invalid trait (0...15)");
|
|
break;
|
|
}
|
|
j = min(spec.ex2a,party_size(true));
|
|
if (j < 1)
|
|
j = 1;
|
|
i = trait_present((eTrait)spec.ex1a);
|
|
if(spec.ex2b == -2 && i <= j) *next_spec = spec.ex1b;
|
|
if(spec.ex2b == -1 && i < j) *next_spec = spec.ex1b;
|
|
if(spec.ex2b == 0 && i == j) *next_spec = spec.ex1b;
|
|
if(spec.ex2b == 1 && i > j) *next_spec = spec.ex1b;
|
|
if(spec.ex2b == 2 && i >= j) *next_spec = spec.ex1b;
|
|
break;
|
|
case eSpecType::IF_STATISTIC:
|
|
if((spec.ex2a < 0 || spec.ex2a > 20) && (spec.ex2a < 100 || spec.ex2a > 104)) {
|
|
giveError("Attempted to check an invalid statistic (0...20 or 100...104).");
|
|
break;
|
|
}
|
|
if(spec.ex2b < -1 || spec.ex2b > 3) {
|
|
giveError("Invalid statistic-checking mode (-1...3); will fall back to cumulative check.");
|
|
spec.ex2b = 0;
|
|
}
|
|
|
|
if(spec.ex2b == -1) {
|
|
// Check specific PC's stat (uses the active PC from Select PC node)
|
|
short pc = 6;
|
|
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(check_party_stat(eSkill(spec.ex2a), 10 + pc) >= spec.ex1a)
|
|
*next_spec = spec.ex1b;
|
|
break;
|
|
}
|
|
}
|
|
if(check_party_stat(eSkill(spec.ex2a), spec.ex2b) >= spec.ex1a)
|
|
*next_spec = spec.ex1b;
|
|
break;
|
|
case eSpecType::IF_TEXT_RESPONSE:
|
|
check_mess = false;
|
|
get_strs(str1,str1,0,spec.m1,-1);
|
|
str3 = get_text_response(str1);
|
|
j = 1; k = 1;
|
|
spec.pic = minmax(0,50,spec.pic);
|
|
get_strs(str1,str2,0,spec.ex1a,spec.ex2a);
|
|
for (i = 0; i < spec.pic;i++) {
|
|
if ((spec.ex1a < 0) || (str3[i] != str1[i]))
|
|
j = 0;
|
|
if ((spec.ex2a < 0) || (str3[i] != str2[i]))
|
|
k = 0;
|
|
}
|
|
if (j == 1)
|
|
*next_spec = spec.ex1b;
|
|
else if (k == 1)
|
|
*next_spec = spec.ex2b;
|
|
break;
|
|
/* This is a little complicated.
|
|
m1 - points to a prompt string
|
|
m2,m3 - If nonequal, specifies a range of allowed responses.
|
|
pic - Comparison mode: 0 - in range, 1 - not in range, 2 - simple compare
|
|
pictype - Special to jump to if both tests pass
|
|
ex1a, ex1b, ex1c - Test 1 values (see below)
|
|
ex2a, ex2b, ex2c - Test 2 values (see below)
|
|
Test values:
|
|
If pic = 0 or 1:
|
|
ex#a - Lower bound
|
|
ex#b - Upper bound
|
|
Enabled if ex#a < ex#b. If ex#a >= ex#b, this test is ignored.
|
|
If pic = 2:
|
|
ex#a - Value to compare to.
|
|
ex#b - Set to 0 to enable. If -1, this test is ignored.
|
|
ex#c - Special to jump to if test # succeeds but the other test fails.
|
|
jumpto - Special to jump to if both tests fail.
|
|
*/
|
|
case eSpecType::IF_NUM_RESPONSE:
|
|
check_mess = false;
|
|
if(spec.m2 > spec.m3) std::swap(spec.m2,spec.m3);
|
|
get_strs(str1,str1,0,spec.m1,-1);
|
|
i = get_num_response(spec.m2,spec.m3,str1);
|
|
setsd(spec.sd1, spec.sd2, abs(i));
|
|
j = 0;
|
|
spec.pic = minmax(0,2,spec.pic);
|
|
switch(spec.pic) { // Comparison mode
|
|
case 0: // Is in range?
|
|
if(spec.ex1a < spec.ex1b && i == minmax(spec.ex1a,spec.ex1b,i)) j += 1;
|
|
if(spec.ex2a < spec.ex2b && i == minmax(spec.ex2a,spec.ex2b,i)) j += 2;
|
|
break;
|
|
case 1: // Not in range?
|
|
if(spec.ex1a < spec.ex1b && i != minmax(spec.ex1a,spec.ex1b,i)) j += 1;
|
|
if(spec.ex2a < spec.ex2b && i != minmax(spec.ex2a,spec.ex2b,i)) j += 2;
|
|
break;
|
|
case 2: // Simple comparison?
|
|
switch(spec.ex1b) {
|
|
case -2: if(i <= spec.ex1a) j += 1; break;
|
|
case -1: if(i < spec.ex1a) j += 1; break;
|
|
case 0: if(i == spec.ex1a) j += 1; break;
|
|
case 1: if(i > spec.ex1a) j += 1; break;
|
|
case 2: if(i >= spec.ex1a) j += 1; break;
|
|
}
|
|
switch(spec.ex2b) {
|
|
case -2: if(i <= spec.ex2a) j += 1; break;
|
|
case -1: if(i < spec.ex2a) j += 1; break;
|
|
case 0: if(i == spec.ex2a) j += 1; break;
|
|
case 1: if(i > spec.ex2a) j += 1; break;
|
|
case 2: if(i >= spec.ex2a) j += 1; break;
|
|
}
|
|
break;
|
|
}
|
|
if(j == 1) *next_spec = spec.ex1c;
|
|
if(j == 2) *next_spec = spec.ex2c;
|
|
if(j == 3) *next_spec = spec.pictype;
|
|
break;
|
|
case eSpecType::IF_SDF_EQ:
|
|
if (sd_legit(spec.sd1,spec.sd2) == true) {
|
|
if (PSD[spec.sd1][spec.sd2] == spec.ex1a)
|
|
*next_spec = spec.ex1b;
|
|
}
|
|
break;
|
|
case eSpecType::IF_CONTEXT:
|
|
// TODO: Test this. In particular, test that the legacy behaviour is correct.
|
|
j = -1;
|
|
switch(spec.ex1a) {
|
|
case 0: // Out move
|
|
if(is_out()) {
|
|
j = bool(spec.ex1b); // Should block move? 1 = yes, 0 = no
|
|
*next_spec = spec.ex1c;
|
|
if(j && which_mode == eSpecCtx::OUT_MOVE)
|
|
ASB("Can't go here while outdoors.");
|
|
}
|
|
break;
|
|
case 1: // Town move
|
|
if(is_town()) {
|
|
j = bool(spec.ex1b); // Should block move? 1 = yes, 0 = no
|
|
*next_spec = spec.ex1c;
|
|
if(j && which_mode == eSpecCtx::TOWN_MOVE)
|
|
ASB("Can't go here while in town mode.");
|
|
}
|
|
break;
|
|
case 2: // Combat move
|
|
if(is_combat()) {
|
|
j = bool(spec.ex1b); // Should block move? 1 = yes, 0 = no
|
|
*next_spec = spec.ex1c;
|
|
if(j && which_mode == eSpecCtx::COMBAT_MOVE)
|
|
ASB("Can't go here during combat.");
|
|
}
|
|
break;
|
|
case 3: // Out look
|
|
if(is_out()) {
|
|
if(which_mode == eSpecCtx::OUT_LOOK)
|
|
*next_spec = spec.ex1c;
|
|
}
|
|
break;
|
|
case 4: // Town look
|
|
if(is_town()) {
|
|
if(which_mode == eSpecCtx::TOWN_LOOK)
|
|
*next_spec = spec.ex1c;
|
|
}
|
|
break;
|
|
case 5: // Enter town
|
|
if(which_mode == eSpecCtx::ENTER_TOWN)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
case 6: // Leave town
|
|
if(which_mode == eSpecCtx::LEAVE_TOWN)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
case 7: // Talking
|
|
if(which_mode == eSpecCtx::TALK)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
case 8: // Use special item
|
|
if(which_mode == eSpecCtx::USE_SPEC_ITEM)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
case 9: // Town timer
|
|
if(which_mode == eSpecCtx::TOWN_TIMER)
|
|
*next_spec = -1;
|
|
break;
|
|
case 10: // Scenario timer
|
|
if(which_mode == eSpecCtx::SCEN_TIMER)
|
|
*next_spec = spec.ex1c;
|
|
else j = 0;
|
|
break;
|
|
case 11: // Party timer
|
|
if(which_mode == eSpecCtx::PARTY_TIMER)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
case 12: // Kill monster
|
|
if(which_mode == eSpecCtx::KILL_MONST)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
case 13: // Start outdoor encounter
|
|
if(which_mode == eSpecCtx::OUTDOOR_ENC)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
case 14: // Flee outdoor encounter
|
|
if(which_mode == eSpecCtx::FLEE_ENCOUNTER)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
case 15: // Win outdoor encounter
|
|
if(which_mode == eSpecCtx::WIN_ENCOUNTER)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
case 16: // Target spell
|
|
if(which_mode == eSpecCtx::TARGET) {
|
|
// TODO: I'm not quite sure if this covers every way of determining which spell was cast
|
|
if(spec.ex1b == -1 || (is_town() && int(town_spell) == spec.ex1b) || (is_combat() && int(spell_being_cast) == spec.ex1b))
|
|
*next_spec = spec.ex1c;
|
|
}
|
|
break;
|
|
case 17: // Use space
|
|
if(which_mode == eSpecCtx::USE_SPACE)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
case 18: // See monster
|
|
if(which_mode == eSpecCtx::SEE_MONST)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
// Past here are special values that don't have an equivalent in eSpecCtx.
|
|
case 100: // Look (town or out)
|
|
if(which_mode == eSpecCtx::OUT_LOOK || which_mode == eSpecCtx::TOWN_LOOK)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
case 101: // In boat
|
|
if(univ.party.in_boat >= 0)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
case 102: // On horse
|
|
if(univ.party.in_horse >= 0)
|
|
*next_spec = spec.ex1c;
|
|
break;
|
|
}
|
|
if(j >= 0) *a = j;
|
|
break;
|
|
}
|
|
if (check_mess == true) {
|
|
handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b);
|
|
}
|
|
}
|
|
|
|
void set_terrain(location l, ter_num_t terrain_type)
|
|
{
|
|
ter_num_t former = univ.town->terrain(l.x,l.y);
|
|
univ.town->terrain(l.x,l.y) = terrain_type;
|
|
if(scenario.ter_types[terrain_type].special == eTerSpec::CONVEYOR)
|
|
belt_present = true;
|
|
if(scenario.ter_types[former].light_radius != scenario.ter_types[terrain_type].light_radius)
|
|
univ.town->set_up_lights();
|
|
}
|
|
|
|
// TODO: What was next_spec_type for? Is it still needed?
|
|
void townmode_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
|
|
short *next_spec,short */*next_spec_type*/,short *a,short *b,short *redraw)
|
|
{
|
|
static const char*const stairDlogs[8] = {
|
|
"basic-stair-up.xml", "basic-stair-down.xml",
|
|
"basic-slope-up.xml", "basic-slope-down.xml",
|
|
"slimy-stair-up.xml", "slimy-stair-down.xml",
|
|
"dark-slope-up.xml", "dark-slope-down.xml"
|
|
};
|
|
bool check_mess = true;
|
|
std::array<std::string, 6> strs;
|
|
short i,r1;
|
|
std::array<short,3> buttons = {-1,-1,-1};
|
|
cSpecial spec;
|
|
location l;
|
|
ter_num_t ter;
|
|
cItemRec store_i;
|
|
|
|
spec = cur_node;
|
|
*next_spec = cur_node.jumpto;
|
|
|
|
l.x = spec.ex1a; l.y = spec.ex1b;
|
|
|
|
if (is_out())
|
|
return;
|
|
switch (cur_node.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,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.");
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
}
|
|
else { // 1 no
|
|
*a = 1;
|
|
if(which_mode == eSpecCtx::TALK || spec.ex2a == 0)
|
|
teleport_party(spec.ex1a,spec.ex1b,1);
|
|
else teleport_party(spec.ex1a,spec.ex1b,0);
|
|
}
|
|
*redraw = 1;
|
|
break;
|
|
case eSpecType::TOWN_HIT_SPACE:
|
|
if(which_mode == eSpecCtx::TALK)
|
|
break;
|
|
hit_space(l,spec.ex2a,(eDamageType) spec.ex2b,1,1);
|
|
*redraw = 1;
|
|
break;
|
|
case eSpecType::TOWN_EXPLODE_SPACE:
|
|
if(which_mode == eSpecCtx::TALK)
|
|
break;
|
|
radius_damage(l,spec.pic, spec.ex2a, (eDamageType) spec.ex2b);
|
|
*redraw = 1;
|
|
break;
|
|
case eSpecType::TOWN_LOCK_SPACE:
|
|
ter = coord_to_ter(spec.ex1a,spec.ex1b);
|
|
if(scenario.ter_types[ter].special == eTerSpec::LOCKABLE)
|
|
set_terrain(l,scenario.ter_types[ter].flag1.u);
|
|
*redraw = 1;
|
|
break;
|
|
case eSpecType::TOWN_UNLOCK_SPACE:
|
|
ter = coord_to_ter(spec.ex1a,spec.ex1b);
|
|
if(scenario.ter_types[ter].special == eTerSpec::UNLOCKABLE)
|
|
set_terrain(l,scenario.ter_types[ter].flag1.u);
|
|
*redraw = 1;
|
|
break;
|
|
case eSpecType::TOWN_SFX_BURST: // TODO: Add a "random offset" mode
|
|
if(which_mode == eSpecCtx::TALK)
|
|
break;
|
|
run_a_boom(l,spec.ex2a,0,0);
|
|
break;
|
|
case eSpecType::TOWN_CREATE_WANDERING:
|
|
create_wand_monst();
|
|
*redraw = 1;
|
|
break;
|
|
case eSpecType::TOWN_PLACE_MONST:
|
|
if (spec.ex2a > 0)
|
|
forced_place_monster(spec.ex2a,l);
|
|
else place_monster(spec.ex2a,l);
|
|
*redraw = 1;
|
|
break;
|
|
case eSpecType::TOWN_DESTROY_MONST:
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if (univ.town.monst[i].number == spec.ex1a) {
|
|
univ.town.monst[i].active = 0;
|
|
}
|
|
*redraw = 1;
|
|
break;
|
|
case eSpecType::TOWN_NUKE_MONSTS:
|
|
for (i = 0; i < univ.town->max_monst(); i++)
|
|
if ((univ.town.monst[i].active > 0) &&
|
|
(((spec.ex1a == 0) && (1 == 1)) ||
|
|
((spec.ex1a == 1) && (univ.town.monst[i].attitude % 2 == 0)) ||
|
|
((spec.ex1a == 2) && (univ.town.monst[i].attitude % 2 == 1)))){
|
|
univ.town.monst[i].active = 0;
|
|
}
|
|
*redraw = 1;
|
|
break;
|
|
case eSpecType::TOWN_GENERIC_LEVER:
|
|
if(which_mode != eSpecCtx::OUT_MOVE && which_mode != eSpecCtx::TOWN_MOVE && which_mode != eSpecCtx::COMBAT_MOVE
|
|
&& which_mode != eSpecCtx::OUT_LOOK && which_mode != eSpecCtx::TOWN_LOOK) {
|
|
ASB("Can't use lever now.");
|
|
check_mess = false;
|
|
*next_spec = -1;
|
|
}
|
|
else {
|
|
if (handle_lever(store_special_loc) > 0)
|
|
*next_spec = spec.ex1b;
|
|
}
|
|
break;
|
|
case eSpecType::TOWN_GENERIC_PORTAL:
|
|
if (is_combat()) {
|
|
ASB("Not while in combat.");
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
}
|
|
else if(which_mode != eSpecCtx::TOWN_MOVE && which_mode != eSpecCtx::TOWN_LOOK) {
|
|
ASB("Can't teleport now.");
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
}
|
|
else if(cChoiceDlog("basic-portal.xml",{"yes","no"}).show() == "yes") {
|
|
*a = 1;
|
|
if(which_mode == eSpecCtx::TALK)
|
|
teleport_party(spec.ex1a,spec.ex1b,1);
|
|
else teleport_party(spec.ex1a,spec.ex1b,spec.ex2a);
|
|
}
|
|
break;
|
|
case eSpecType::TOWN_GENERIC_BUTTON:
|
|
if(cChoiceDlog("basic-button.xml",{"yes","no"}).show() == "yes")
|
|
*next_spec = spec.ex1b;
|
|
break;
|
|
case eSpecType::TOWN_GENERIC_STAIR:
|
|
if(spec.ex2c != 1 && spec.ex2c != 2 && is_combat()) {
|
|
ASB("Can't change level in combat.");
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
}
|
|
else if (spec.ex2c != 2 && spec.ex2c != 3 && which_mode != eSpecCtx::TOWN_MOVE) {
|
|
ASB("Can't change level now.");
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
}
|
|
else {
|
|
*a = 1;
|
|
if (spec.ex2b < 0) spec.ex2b = 0;
|
|
if ((spec.ex2b >= 8) || (cChoiceDlog(stairDlogs[spec.ex2b],{"climb","leave"}).show() == "climb"))
|
|
change_level(spec.ex2a,l.x,l.y);
|
|
}
|
|
break;
|
|
case eSpecType::TOWN_LEVER:
|
|
check_mess = false;
|
|
if (spec.m1 < 0)
|
|
break;
|
|
if(which_mode != eSpecCtx::OUT_MOVE && which_mode != eSpecCtx::TOWN_MOVE && which_mode != eSpecCtx::COMBAT_MOVE
|
|
&& which_mode != eSpecCtx::OUT_LOOK && which_mode != eSpecCtx::TOWN_LOOK) {
|
|
ASB("Can't use lever now.");
|
|
check_mess = false;
|
|
*next_spec = -1;
|
|
}
|
|
else {
|
|
for (i = 0; i < 3; i++)
|
|
get_strs(strs[i * 2],strs[i * 2 + 1],cur_spec_type,
|
|
spec.m1 + i * 2 + spec_str_offset[cur_spec_type],spec.m1 + i * 2 + 1 + spec_str_offset[cur_spec_type]);
|
|
buttons[0] = 9; buttons[1] = 35;
|
|
// TODO: Handle custom pictures?
|
|
i = custom_choice_dialog(strs,spec.pic,PIC_TER,buttons);
|
|
if (i == 1) {*next_spec = -1;}
|
|
else {
|
|
ter = coord_to_ter(store_special_loc.x,store_special_loc.y);
|
|
set_terrain(store_special_loc,scenario.ter_types[ter].trans_to_what);
|
|
*next_spec = spec.ex1b;
|
|
}
|
|
}
|
|
break;
|
|
case eSpecType::TOWN_PORTAL:
|
|
check_mess = false;
|
|
if (spec.m1 < 0)
|
|
break;
|
|
if (is_combat()) {
|
|
ASB("Not while in combat.");
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
}
|
|
else if(which_mode != eSpecCtx::TOWN_MOVE && which_mode != eSpecCtx::TOWN_LOOK) {
|
|
ASB("Can't teleport now.");
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
}
|
|
else {
|
|
for (i = 0; i < 3; i++)
|
|
get_strs(strs[i * 2],strs[i * 2 + 1]
|
|
,cur_spec_type,spec.m1 + i * 2 + spec_str_offset[cur_spec_type],spec.m1 + i * 2 + 1 + spec_str_offset[cur_spec_type]);
|
|
buttons[0] = 9; buttons[1] = 8;
|
|
// TODO: Wait, wait, aren't you supposed to be able to pick which picture to show?
|
|
i = custom_choice_dialog(strs,22,PIC_DLOG,buttons);
|
|
if (i == 1) {
|
|
*next_spec = -1;
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
}
|
|
else {
|
|
*a = 1;
|
|
if(which_mode == eSpecCtx::TALK)
|
|
teleport_party(spec.ex1a,spec.ex1b,1);
|
|
else teleport_party(spec.ex1a,spec.ex1b,spec.ex2a);
|
|
}
|
|
}
|
|
break;
|
|
case eSpecType::TOWN_STAIR:
|
|
check_mess = false;
|
|
if ((spec.m1 < 0) && (spec.ex2b != 1))
|
|
break;
|
|
if(spec.ex2c != 1 && spec.ex2c != 2 && is_combat()) {
|
|
ASB("Can't change level in combat.");
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
}
|
|
else if(spec.ex2c != 2 && spec.ex2c != 3 && which_mode != eSpecCtx::TOWN_MOVE) {
|
|
ASB("Can't change level now.");
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
}
|
|
else {
|
|
if (spec.m1 >= 0) {
|
|
for (i = 0; i < 3; i++)
|
|
get_strs(strs[i * 2],strs[i * 2 + 1],cur_spec_type,
|
|
spec.m1 + i * 2 + spec_str_offset[cur_spec_type],spec.m1 + i * 2 + 1 + spec_str_offset[cur_spec_type]);
|
|
buttons[0] = 20; buttons[1] = 24;
|
|
}
|
|
if (spec.ex2b == 1)
|
|
i = 2;
|
|
// TODO: Wait, wait, don't you get to choose the picture to show?
|
|
else i = custom_choice_dialog(strs,19,PIC_DLOG,buttons) ;
|
|
*a = 1;
|
|
if (i == 1) {
|
|
*next_spec = -1;
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
} else {
|
|
bool was_in_combat = false;
|
|
short was_active;
|
|
if(overall_mode == MODE_TALKING)
|
|
end_talk_mode();
|
|
else if(is_combat()) {
|
|
was_in_combat = true;
|
|
if(which_combat_type == 0) { // outdoor combat
|
|
ASB("Can't change level in combat.");
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
break;
|
|
} else {
|
|
was_active = current_pc;
|
|
univ.party.direction = end_town_combat();
|
|
}
|
|
}
|
|
*a = 1;
|
|
change_level(spec.ex2a,l.x,l.y);
|
|
if(was_in_combat) {
|
|
start_town_combat(univ.party.direction);
|
|
current_pc = was_active;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case eSpecType::TOWN_RELOCATE:
|
|
position_party(spec.ex1a,spec.ex1b,spec.ex2a,spec.ex2b);
|
|
break;
|
|
case eSpecType::TOWN_PLACE_ITEM:
|
|
store_i = get_stored_item(spec.ex2a);
|
|
place_item(store_i,l,true);
|
|
break;
|
|
case eSpecType::TOWN_SPLIT_PARTY:
|
|
if(which_mode == eSpecCtx::TALK)
|
|
break;
|
|
if (is_combat()) {
|
|
ASB("Not while in combat.");
|
|
if((which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE) < 3)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
break;
|
|
}
|
|
if (univ.party.is_split() > 0) {
|
|
ASB("Party is already split.");
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
break;
|
|
}
|
|
r1 = char_select_pc(1,0,"Which character goes?");
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
if (r1 != 6) {
|
|
current_pc = r1;
|
|
*next_spec = -1;
|
|
ASB(univ.party.start_split(spec.ex1a,spec.ex1b,spec.ex2a,r1));
|
|
update_explored(univ.town.p_loc);
|
|
center = univ.town.p_loc;
|
|
}
|
|
else check_mess = false;
|
|
break;
|
|
case eSpecType::TOWN_REUNITE_PARTY:
|
|
if (is_combat()) {
|
|
ASB("Not while in combat.");
|
|
break;
|
|
}
|
|
if(which_mode == eSpecCtx::OUT_MOVE || which_mode == eSpecCtx::TOWN_MOVE || which_mode == eSpecCtx::COMBAT_MOVE)
|
|
*a = 1;
|
|
*next_spec = -1;
|
|
check_mess = false;
|
|
ASB(univ.party.end_split(spec.ex1a));
|
|
update_explored(univ.town.p_loc);
|
|
center = univ.town.p_loc;
|
|
break;
|
|
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 global town lighting
|
|
if(spec.ex1a >= 0 && spec.ex1a <= 3) {
|
|
univ.town->lighting_type = (eLighting) spec.ex1a;
|
|
draw_terrain();
|
|
}
|
|
// Change party light level
|
|
if (spec.ex2a > 0) {
|
|
if (spec.ex2b == 0)
|
|
increase_light(spec.ex2a);
|
|
else increase_light(-spec.ex2a);
|
|
}
|
|
break;
|
|
case eSpecType::TOWN_SET_ATTITUDE:
|
|
if((spec.ex1a < 0) || (spec.ex1a > 59)){
|
|
giveError("Tried to change the attitude of a nonexistent monster (should be 0...59).");
|
|
break;
|
|
}
|
|
if((spec.ex1b < 0) || (spec.ex1b > 3)){
|
|
giveError("Invalid attitude (0-Friendly Docile, 1-Hostile A, 2-Friendly Will Fight, 3-Hostile B).");
|
|
break;
|
|
}
|
|
univ.town.monst[spec.ex1a].attitude = spec.ex1b;
|
|
break;
|
|
}
|
|
if (check_mess == true) {
|
|
handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b);
|
|
}
|
|
}
|
|
|
|
// TODO: What was next_spec_type for? Is it still needed?
|
|
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){
|
|
bool check_mess = true;
|
|
short i,j,k;
|
|
cSpecial spec;
|
|
location l;
|
|
ter_num_t ter;
|
|
|
|
spec = cur_node;
|
|
*next_spec = cur_node.jumpto;
|
|
|
|
if (is_out())
|
|
return;
|
|
|
|
*redraw = 1;
|
|
for (i = spec.ex1b;i <= spec.ex2b;i++)
|
|
for (j = spec.ex1a; j <= spec.ex2a; j++) {
|
|
l.x = i; l.y = j;
|
|
switch (cur_node.type) {
|
|
case eSpecType::RECT_PLACE_FIRE:
|
|
if (get_ran(1,1,100) <= spec.sd1 )
|
|
univ.town.set_fire_wall(i,j,true);
|
|
break;
|
|
case eSpecType::RECT_PLACE_FORCE:
|
|
if (get_ran(1,1,100) <= spec.sd1 )
|
|
univ.town.set_force_wall(i,j,true);
|
|
break;
|
|
case eSpecType::RECT_PLACE_ICE:
|
|
if (get_ran(1,1,100) <= spec.sd1 )
|
|
univ.town.set_ice_wall(i,j,true);
|
|
break;
|
|
case eSpecType::RECT_PLACE_BLADE:
|
|
if (get_ran(1,1,100) <= spec.sd1 )
|
|
univ.town.set_blade_wall(i,j,true);
|
|
break;
|
|
case eSpecType::RECT_PLACE_SCLOUD:
|
|
if (get_ran(1,1,100) <= spec.sd1 )
|
|
univ.town.set_scloud(i,j,true);
|
|
break;
|
|
case eSpecType::RECT_PLACE_SLEEP:
|
|
if (get_ran(1,1,100) <= spec.sd1 )
|
|
univ.town.set_sleep_cloud(i,j,true);
|
|
break;
|
|
case eSpecType::RECT_PLACE_QUICKFIRE:
|
|
if (get_ran(1,1,100) <= spec.sd1 )
|
|
univ.town.set_quickfire(i,j,true);
|
|
break;
|
|
case eSpecType::RECT_PLACE_FIRE_BARR:
|
|
if (get_ran(1,1,100) <= spec.sd1 )
|
|
univ.town.set_fire_barr(i,j,true);
|
|
break;
|
|
case eSpecType::RECT_PLACE_FORCE_BARR:
|
|
if (get_ran(1,1,100) <= spec.sd1 )
|
|
univ.town.set_force_barr(i,j,true);
|
|
break;
|
|
case eSpecType::RECT_CLEANSE:
|
|
if (spec.sd1 == 0)
|
|
dispel_fields(i,j,1);
|
|
else dispel_fields(i,j,2);
|
|
break;
|
|
case eSpecType::RECT_PLACE_SFX:
|
|
if (get_ran(1,1,100) <= spec.sd1 )
|
|
switch(spec.sd2) {
|
|
case 0: univ.town.set_sm_blood(i,j,true); break;
|
|
case 1: univ.town.set_med_blood(i,j,true); break;
|
|
case 2: univ.town.set_lg_blood(i,j,true); break;
|
|
case 3: univ.town.set_sm_slime(i,j,true); break;
|
|
case 4: univ.town.set_lg_slime(i,j,true); break;
|
|
case 5: univ.town.set_ash(i,j,true); break;
|
|
case 6: univ.town.set_bones(i,j,true); break;
|
|
case 7: univ.town.set_rubble(i,j,true); break;
|
|
default: giveError("Invalid sfx type (0...7)");
|
|
}
|
|
break;
|
|
case eSpecType::RECT_PLACE_OBJECT:
|
|
if (get_ran(1,1,100) <= spec.sd1 ) {
|
|
if (spec.sd2 == 0)
|
|
univ.town.set_web(i,j,true);
|
|
if (spec.sd2 == 1)
|
|
univ.town.set_barrel(i,j,true);
|
|
if (spec.sd2 == 2)
|
|
univ.town.set_crate(i,j,true);
|
|
}
|
|
break;
|
|
case eSpecType::RECT_MOVE_ITEMS:
|
|
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;
|
|
}
|
|
break;
|
|
case eSpecType::RECT_DESTROY_ITEMS:
|
|
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].variety = eItemType::NO_ITEM;
|
|
}
|
|
break;
|
|
case eSpecType::RECT_CHANGE_TER:
|
|
if (get_ran(1,1,100) <= spec.sd2){
|
|
set_terrain(l,spec.sd1);
|
|
*redraw = true;
|
|
draw_map(true);
|
|
}
|
|
break;
|
|
case eSpecType::RECT_SWAP_TER:
|
|
if (coord_to_ter(i,j) == spec.sd1){
|
|
set_terrain(l,spec.sd2);
|
|
*redraw = true;
|
|
draw_map(true);
|
|
}
|
|
else if (coord_to_ter(i,j) == spec.sd2){
|
|
set_terrain(l,spec.sd1);
|
|
*redraw = true;
|
|
draw_map(true);
|
|
}
|
|
break;
|
|
case eSpecType::RECT_TRANS_TER:
|
|
ter = coord_to_ter(i,j);
|
|
set_terrain(l,scenario.ter_types[ter].trans_to_what);
|
|
*redraw = true;
|
|
draw_map(true);
|
|
break;
|
|
case eSpecType::RECT_LOCK:
|
|
ter = coord_to_ter(i,j);
|
|
if(scenario.ter_types[ter].special == eTerSpec::LOCKABLE){
|
|
set_terrain(l,scenario.ter_types[ter].flag1.u);
|
|
*redraw = true;
|
|
draw_map(true);
|
|
}
|
|
break;
|
|
case eSpecType::RECT_UNLOCK:
|
|
ter = coord_to_ter(i,j);
|
|
if (scenario.ter_types[ter].special == eTerSpec::UNLOCKABLE){
|
|
set_terrain(l,scenario.ter_types[ter].flag1.u);
|
|
*redraw = true;
|
|
draw_map(true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (check_mess == true) {
|
|
handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b);
|
|
}
|
|
}
|
|
|
|
// TODO: What was next_spec_type for? Is it still needed?
|
|
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){
|
|
bool check_mess = false;
|
|
std::string str1, str2;
|
|
cSpecial spec;
|
|
location l;
|
|
|
|
spec = cur_node;
|
|
*next_spec = cur_node.jumpto;
|
|
|
|
if (is_out() == false) return;
|
|
|
|
switch (cur_node.type) {
|
|
case eSpecType::OUT_MAKE_WANDER:
|
|
create_wand_monst();
|
|
*redraw = 1;
|
|
break;
|
|
case eSpecType::OUT_CHANGE_TER:
|
|
univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].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.");
|
|
//set_sd = false;
|
|
}
|
|
else {
|
|
l = global_to_local(univ.party.p_loc);
|
|
place_outd_wand_monst(l,
|
|
univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].special_enc[spec.ex1a],true);
|
|
check_mess = true;
|
|
}
|
|
break;
|
|
case eSpecType::OUT_MOVE_PARTY:
|
|
check_mess = true;
|
|
out_move_party(spec.ex1a,spec.ex1b);
|
|
*redraw = 1;
|
|
*a = 1;
|
|
break;
|
|
case eSpecType::OUT_STORE:
|
|
get_strs(str1,str2,1,spec.m1 + 10,-1);
|
|
if (spec.ex2a >= 40)
|
|
spec.ex2a = 39;
|
|
if (spec.ex2a < 1)
|
|
spec.ex2a = 1;
|
|
spec.ex2b = minmax(0,6,spec.ex2b);
|
|
switch (spec.ex1b) {
|
|
case 0: start_shop_mode(0,spec.ex1a,spec.ex1a + spec.ex2a - 1,spec.ex2b,str1.c_str()); break;
|
|
case 1: start_shop_mode(10,spec.ex1a,spec.ex1a + spec.ex2a - 1,spec.ex2b,str1.c_str()); break;
|
|
case 2: start_shop_mode(11,spec.ex1a,spec.ex1a + spec.ex2a - 1 ,spec.ex2b,str1.c_str()); break;
|
|
case 3: start_shop_mode(12,spec.ex1a,spec.ex1a + spec.ex2a - 1,spec.ex2b,str1.c_str()); break;
|
|
case 4: start_shop_mode(3,spec.ex1a,spec.ex1a + spec.ex2a - 1,spec.ex2b,str1.c_str()); break;
|
|
}
|
|
*next_spec = -1;
|
|
break;
|
|
}
|
|
|
|
if (check_mess == true) {
|
|
handle_message(which_mode,cur_spec_type,cur_node.m1,cur_node.m2,a,b);
|
|
}
|
|
}
|
|
|
|
void setsd(short a,short b,short val)
|
|
{
|
|
if (sd_legit(a,b) == false) {
|
|
giveError("The scenario attempted to change an out of range Stuff Done flag.");
|
|
return;
|
|
}
|
|
PSD[a][b] = val;
|
|
}
|
|
|
|
void handle_message(eSpecCtx which_mode,short cur_type,short mess1,short mess2,short *a,short *b)
|
|
{
|
|
eEncNoteType note_type;
|
|
switch(cur_type) {
|
|
case 0:
|
|
note_type = NOTE_SCEN;
|
|
break;
|
|
case 1:
|
|
note_type = NOTE_OUT;
|
|
break;
|
|
case 2:
|
|
note_type = NOTE_TOWN;
|
|
break;
|
|
}
|
|
std::string str1, str2;
|
|
short label1 = -1,label2 = -1,label1b = -1,label2b = -1;
|
|
short mess_adj[3] = {160,10,0};
|
|
|
|
if ((mess1 < 0) && (mess2 < 0))
|
|
return;
|
|
if(which_mode == eSpecCtx::TALK) {
|
|
*a = mess1 + ((mess1 >= 0) ? mess_adj[cur_type] : 0);
|
|
*b = mess2 + ((mess2 >= 0) ? mess_adj[cur_type] : 0);
|
|
return;
|
|
}
|
|
get_strs(str1,str2, cur_type, mess1 + ((mess1 >= 0) ? mess_adj[cur_type] : 0),
|
|
mess2 + ((mess2 >= 0) ? mess_adj[cur_type] : 0)) ;
|
|
if (mess1 >= 0) {
|
|
label1 = mess1 + mess_adj[cur_type];
|
|
label1b = (is_out()) ? (univ.party.outdoor_corner.x + univ.party.i_w_c.x) +
|
|
scenario.out_width * (univ.party.outdoor_corner.y + univ.party.i_w_c.y) : univ.town.num;
|
|
}
|
|
if (mess2 >= 0) {
|
|
label2 = mess2 + mess_adj[cur_type];
|
|
label2b = (is_out()) ? (univ.party.outdoor_corner.x + univ.party.i_w_c.x) +
|
|
scenario.out_width * (univ.party.outdoor_corner.y + univ.party.i_w_c.y) : univ.town.num;
|
|
}
|
|
cStrDlog display_strings(str1.c_str(), str2.c_str(),"",scenario.intro_pic,PIC_SCEN,0);
|
|
display_strings.setSound(57);
|
|
display_strings.setRecordHandler(cStringRecorder()
|
|
.string1(note_type, label1, label1b)
|
|
.string2(note_type, label2, label2b)
|
|
);
|
|
display_strings.show();
|
|
}
|
|
|
|
void get_strs(std::string& str1,std::string& str2,short cur_type,short which_str1,short which_str2)
|
|
{
|
|
short num_strs[3] = {260,108,135};
|
|
|
|
if (((which_str1 >= 0) && (which_str1 != minmax(0,num_strs[cur_type],which_str1))) ||
|
|
((which_str2 >= 0) && (which_str2 != minmax(0,num_strs[cur_type],which_str2)))) {
|
|
giveError("The scenario attempted to access a message out of range.");
|
|
return;
|
|
}
|
|
switch (cur_type) {
|
|
case 0:
|
|
if (which_str1 >= 0)
|
|
str1 = scenario.scen_strs(which_str1);
|
|
if (which_str2 >= 0)
|
|
str2 = scenario.scen_strs(which_str2);
|
|
break;
|
|
case 1:
|
|
if (which_str1 >= 0)
|
|
//load_outdoor_str(loc(univ.party.outdoor_corner.x + univ.party.i_w_c.x,univ.party.outdoor_corner.y + univ.party.i_w_c.y),which_str1,(char *) str1);
|
|
str1 = univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].out_strs(which_str1);
|
|
if (which_str2 >= 0)
|
|
//load_outdoor_str(loc(univ.party.outdoor_corner.x + univ.party.i_w_c.x,univ.party.outdoor_corner.y + univ.party.i_w_c.y),which_str2,(char *) str2);
|
|
str2 = univ.out.outdoors[univ.party.i_w_c.x][univ.party.i_w_c.y].out_strs(which_str2);
|
|
break;
|
|
case 2:
|
|
if (which_str1 >= 0)
|
|
str1 = univ.town->spec_strs[which_str1];
|
|
if (which_str2 >= 0)
|
|
str2 = univ.town->spec_strs[which_str2];
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// 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());
|
|
}
|
|
}
|