Rewrite the implementation of monster abilities
- Abilities are much more generic, but also much more complex; a set of "templates" is provided to try to make it more accessible - There is now a possibility for monsters to charm each other
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -25,8 +25,9 @@ void combat_run_monst();
|
||||
void do_monster_turn();
|
||||
void monster_attack_pc(short who_att,short target);
|
||||
void monster_attack_monster(short who_att,short attackee);
|
||||
void monst_fire_missile(short m_num,short bless,short level,location source,short target);
|
||||
bool monst_breathe(cCreature *caster,location targ_space,short dam_type);
|
||||
void monst_fire_missile(short m_num,short bless,std::pair<eMonstAbil,uAbility> abil,location source,short target);
|
||||
void monst_basic_abil(short m_num, std::pair<eMonstAbil,uAbility> abil, short target);
|
||||
bool monst_breathe(cCreature *caster,location targ_space,uAbility dam_type);
|
||||
bool monst_cast_mage(cCreature *caster,short targ);
|
||||
bool monst_cast_priest(cCreature *caster,short targ);
|
||||
void damage_target(short target,short dam,eDamageType type);
|
||||
|
||||
@@ -190,7 +190,7 @@ void draw_monsters() {
|
||||
}
|
||||
if(is_town() || is_combat()) {
|
||||
for(i = 0; i < univ.town->max_monst(); i++)
|
||||
if(univ.town.monst[i].active != 0 && univ.town.monst[i].spec_skill != MONSTER_INVISIBLE)
|
||||
if(univ.town.monst[i].active != 0 && !univ.town.monst[i].invisible)
|
||||
if(point_onscreen(center,univ.town.monst[i].cur_loc) && party_can_see_monst(i)) {
|
||||
check_if_monst_seen(univ.town.monst[i].number, univ.town.monst[i].cur_loc);
|
||||
where_draw.x = univ.town.monst[i].cur_loc.x - center.x + 4;
|
||||
|
||||
@@ -357,7 +357,7 @@ static void put_monst_info(cDialog& me, const cCreature& store_m) {
|
||||
short abil,i;
|
||||
|
||||
cPict& pic = dynamic_cast<cPict&>(me["pic"]);
|
||||
if(store_m.spec_skill == MONSTER_INVISIBLE)
|
||||
if(store_m.invisible)
|
||||
pic.setPict(400,PIC_MONST);// TODO: should probably be PICT_BLANK?
|
||||
else if(store_m.picture_num < 1000)
|
||||
pic.setPict(store_m.picture_num,PIC_MONST);
|
||||
@@ -382,12 +382,13 @@ static void put_monst_info(cDialog& me, const cCreature& store_m) {
|
||||
store_text = get_m_name(store_m.number);
|
||||
me["name"].setText(store_text);
|
||||
|
||||
// TODO: More descriptive ability descriptions, taking into account potential variation
|
||||
abil = store_m.spec_skill;
|
||||
str = get_str("monster-abilities",abil + 1);
|
||||
me["abil1"].setText(str);
|
||||
str = get_str("monster-abilities",store_m.radiate_1 + 50);
|
||||
me["abil2"].setText(str);
|
||||
i = 1;
|
||||
for(auto& abil : store_m.abil) {
|
||||
if(i > 2) break; // TODO: Support showing more than just the first two abilities
|
||||
std::string id = "abil" + std::to_string(i);
|
||||
me[id].setText(abil.second.to_string(abil.first));
|
||||
i++;
|
||||
}
|
||||
|
||||
for(i = 0; i < 3; i++) {
|
||||
if(store_m.a[i] > 0) {
|
||||
@@ -409,7 +410,6 @@ static void put_monst_info(cDialog& me, const cCreature& store_m) {
|
||||
me["ap"].setTextToNum(store_m.speed);
|
||||
me["mage"].setTextToNum(store_m.mu);
|
||||
me["priest"].setTextToNum(store_m.cl);
|
||||
me["poison"].setTextToNum(store_m.poison);
|
||||
// Immunities
|
||||
dynamic_cast<cLed&>(me["magic-res"]).setState(store_m.magic_res == RESIST_HALF ? led_red : led_off);
|
||||
dynamic_cast<cLed&>(me["magic-imm"]).setState(store_m.magic_res == RESIST_ALL ? led_red : led_off);
|
||||
|
||||
@@ -833,7 +833,7 @@ void set_town_attitude(short lo,short hi,short att) {
|
||||
|
||||
univ.town.monst[i].mobility = 1;
|
||||
// If a "guard", give a power boost
|
||||
if(univ.scenario.scen_monsters[num].spec_skill == MONSTER_GUARD) {
|
||||
if(univ.scenario.scen_monsters[num].guard) {
|
||||
univ.town.monst[i].active = 2;
|
||||
univ.town.monst[i].health *= 3;
|
||||
univ.town.monst[i].status[eStatus::HASTE_SLOW] = 8;
|
||||
|
||||
@@ -224,7 +224,7 @@ void do_monsters() {
|
||||
l1 = univ.town.monst[i].cur_loc;
|
||||
l2 = (univ.town.monst[i].target <= 6) ? univ.town.p_loc : univ.town.monst[target - 100].cur_loc;
|
||||
|
||||
if(univ.town.monst[i].morale < 0 && univ.town.monst[i].spec_skill != MONSTER_MINDLESS
|
||||
if(univ.town.monst[i].morale < 0 && !univ.town.monst[i].mindless
|
||||
&& univ.town.monst[i].m_type != eRace::UNDEAD) {
|
||||
acted_yet = flee_party(i,l1,l2);
|
||||
if(get_ran(1,0,10) < 6)
|
||||
@@ -280,6 +280,8 @@ bool monst_hate_spot(short which_m,location *good_loc) {
|
||||
location prospect,loc;
|
||||
|
||||
loc = univ.town.monst[which_m].cur_loc;
|
||||
bool have_radiate = univ.town.monst[which_m].abil[eMonstAbil::RADIATE].active;
|
||||
eFieldType which_radiate = univ.town.monst[which_m].abil[eMonstAbil::RADIATE].radiate.type;
|
||||
bool hate_spot = false;
|
||||
// Hate barriers
|
||||
if(univ.town.is_fire_barr(loc.x,loc.y) || univ.town.is_force_barr(loc.x,loc.y)) hate_spot = true;
|
||||
@@ -288,39 +290,39 @@ bool monst_hate_spot(short which_m,location *good_loc) {
|
||||
// Hate blade wall?
|
||||
else if(univ.town.is_blade_wall(loc.x,loc.y)) {
|
||||
hate_spot = true;
|
||||
if(univ.town.monst[which_m].radiate_1 == MONSTER_RADIATE_BLADE_FIELDS) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].spec_skill == MONSTER_INVULNERABILITY) hate_spot = false;
|
||||
if(have_radiate && which_radiate == eFieldType::WALL_BLADES) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].invuln) hate_spot = false;
|
||||
}
|
||||
// Hate ice wall?
|
||||
else if(univ.town.is_ice_wall(loc.x,loc.y)) {
|
||||
hate_spot = true;
|
||||
if(univ.town.monst[which_m].radiate_1 == MONSTER_RADIATE_ICE_FIELDS) hate_spot = false;
|
||||
if(have_radiate && which_radiate == eFieldType::WALL_ICE) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].cold_res == RESIST_ALL) hate_spot = false;
|
||||
}
|
||||
// Hate fire wall?
|
||||
else if(univ.town.is_fire_wall(loc.x,loc.y)) {
|
||||
hate_spot = true;
|
||||
if(univ.town.monst[which_m].radiate_1 == MONSTER_RADIATE_FIRE_FIELDS) hate_spot = false;
|
||||
if(have_radiate && which_radiate == eFieldType::WALL_FIRE) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].fire_res == RESIST_ALL) hate_spot = false;
|
||||
}
|
||||
// Note: Monsters used to enter shock walls even if they were merely resistant to magic
|
||||
// Hate shock wall?
|
||||
else if(univ.town.is_force_wall(loc.x,loc.y)) {
|
||||
hate_spot = true;
|
||||
if(univ.town.monst[which_m].radiate_1 == MONSTER_RADIATE_SHOCK_FIELDS) hate_spot = false;
|
||||
if(have_radiate && which_radiate == eFieldType::WALL_FORCE) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].magic_res == RESIST_ALL) hate_spot = false;
|
||||
}
|
||||
// Hate stink cloud?
|
||||
else if(univ.town.is_scloud(loc.x,loc.y)) {
|
||||
hate_spot = true;
|
||||
if(univ.town.monst[which_m].radiate_1 == MONSTER_RADIATE_STINKING_CLOUDS) hate_spot = false;
|
||||
if(have_radiate && which_radiate == eFieldType::CLOUD_STINK) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].magic_res == RESIST_ALL) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].magic_res == RESIST_HALF) hate_spot = false;
|
||||
}
|
||||
// Hate sleep cloud?
|
||||
else if(univ.town.is_sleep_cloud(loc.x,loc.y)) {
|
||||
hate_spot = true;
|
||||
if(univ.town.monst[which_m].radiate_1 == MONSTER_RADIATE_SLEEP_FIELDS) hate_spot = false;
|
||||
if(have_radiate && which_radiate == eFieldType::CLOUD_SLEEP) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].magic_res == RESIST_ALL) hate_spot = false;
|
||||
else if(univ.town.monst[which_m].magic_res == RESIST_HALF) hate_spot = false;
|
||||
}
|
||||
@@ -815,11 +817,14 @@ void monst_inflict_fields(short which_monst) {
|
||||
return;
|
||||
|
||||
which_m = &univ.town.monst[which_monst];
|
||||
bool have_radiate = which_m->abil[eMonstAbil::RADIATE].active;
|
||||
eFieldType which_radiate = which_m->abil[eMonstAbil::RADIATE].radiate.type;
|
||||
for(i = 0; i < univ.town.monst[which_monst].x_width; i++)
|
||||
for(j = 0; j < univ.town.monst[which_monst].y_width; j++)
|
||||
if(univ.town.monst[which_monst].active > 0) {
|
||||
where_check.x = univ.town.monst[which_monst].cur_loc.x + i;
|
||||
where_check.y = univ.town.monst[which_monst].cur_loc.y + j;
|
||||
// TODO: If the goal is to damage the monster by any fields it's on, why all the break statements?
|
||||
if(univ.town.is_quickfire(where_check.x,where_check.y)) {
|
||||
r1 = get_ran(2,1,8);
|
||||
damage_monst(which_monst,7,r1,0,eDamageType::FIRE,0);
|
||||
@@ -827,30 +832,30 @@ void monst_inflict_fields(short which_monst) {
|
||||
}
|
||||
if(univ.town.is_blade_wall(where_check.x,where_check.y)) {
|
||||
r1 = get_ran(6,1,8);
|
||||
if(univ.town.monst[which_monst].radiate_1 != MONSTER_RADIATE_BLADE_FIELDS)
|
||||
if(have_radiate && which_radiate != eFieldType::WALL_BLADES)
|
||||
damage_monst(which_monst,7,r1,0,eDamageType::WEAPON,0);
|
||||
break;
|
||||
}
|
||||
if(univ.town.is_force_wall(where_check.x,where_check.y)) {
|
||||
r1 = get_ran(3,1,6);
|
||||
if(univ.town.monst[which_monst].radiate_1 != MONSTER_RADIATE_SHOCK_FIELDS)
|
||||
if(have_radiate && which_radiate != eFieldType::WALL_FORCE)
|
||||
damage_monst(which_monst,7,r1,0,eDamageType::MAGIC,0);
|
||||
break;
|
||||
}
|
||||
if(univ.town.is_sleep_cloud(where_check.x,where_check.y)) {
|
||||
if(univ.town.monst[which_monst].radiate_1 != MONSTER_RADIATE_SLEEP_FIELDS)
|
||||
if(have_radiate && which_radiate != eFieldType::CLOUD_SLEEP)
|
||||
charm_monst(which_m,0,eStatus::ASLEEP,3);
|
||||
break;
|
||||
}
|
||||
if(univ.town.is_ice_wall(where_check.x,where_check.y)) {
|
||||
r1 = get_ran(3,1,6);
|
||||
if(univ.town.monst[which_monst].radiate_1 != MONSTER_RADIATE_ICE_FIELDS)
|
||||
if(have_radiate && which_radiate != eFieldType::WALL_ICE)
|
||||
damage_monst(which_monst,7,r1,0,eDamageType::COLD,0);
|
||||
break;
|
||||
}
|
||||
if(univ.town.is_scloud(where_check.x,where_check.y)) {
|
||||
r1 = get_ran(1,2,3);
|
||||
if(univ.town.monst[which_monst].radiate_1 != MONSTER_RADIATE_STINKING_CLOUDS)
|
||||
if(have_radiate && which_radiate != eFieldType::CLOUD_STINK)
|
||||
curse_monst(which_m,r1);
|
||||
break;
|
||||
}
|
||||
@@ -863,7 +868,7 @@ void monst_inflict_fields(short which_monst) {
|
||||
}
|
||||
if(univ.town.is_fire_wall(where_check.x,where_check.y)) {
|
||||
r1 = get_ran(2,1,6);
|
||||
if(univ.town.monst[which_monst].radiate_1 != MONSTER_RADIATE_FIRE_FIELDS)
|
||||
if(have_radiate && which_radiate != eFieldType::WALL_FIRE)
|
||||
damage_monst(which_monst,7,r1,0,eDamageType::FIRE,0);
|
||||
break;
|
||||
}
|
||||
@@ -926,7 +931,7 @@ bool monst_check_special_terrain(location where_check,short mode,short which_mon
|
||||
// nasty barriers
|
||||
if((which_m->mu > 0) || (which_m->cl > 0))
|
||||
mage = true;
|
||||
if(which_m->spec_skill == MONSTER_MINDLESS)
|
||||
if(which_m->mindless)
|
||||
guts = 20;
|
||||
else guts = get_ran(1,1,(which_m->level / 2));
|
||||
guts += which_m->health / 20;
|
||||
@@ -937,28 +942,31 @@ bool monst_check_special_terrain(location where_check,short mode,short which_mon
|
||||
|
||||
if((univ.town.is_antimagic(where_check.x,where_check.y)) && (mage))
|
||||
return false;
|
||||
if(univ.town.is_fire_wall(where_check.x,where_check.y) && which_m->radiate_1 != MONSTER_RADIATE_FIRE_FIELDS) {
|
||||
bool have_radiate = which_m->abil[eMonstAbil::RADIATE].active;
|
||||
eFieldType which_radiate = which_m->abil[eMonstAbil::RADIATE].radiate.type;
|
||||
if(univ.town.is_fire_wall(where_check.x,where_check.y) && !(have_radiate && which_radiate == eFieldType::WALL_FIRE)) {
|
||||
if(guts < 3) return false;
|
||||
}
|
||||
if(univ.town.is_force_wall(where_check.x,where_check.y) && which_m->radiate_1 != MONSTER_RADIATE_SHOCK_FIELDS) {
|
||||
if(univ.town.is_force_wall(where_check.x,where_check.y) && !(have_radiate && which_radiate == eFieldType::WALL_FORCE)) {
|
||||
if(guts < 4) return false;
|
||||
}
|
||||
if(univ.town.is_ice_wall(where_check.x,where_check.y) && which_m->radiate_1 != MONSTER_RADIATE_ICE_FIELDS) {
|
||||
if(univ.town.is_ice_wall(where_check.x,where_check.y) && !(have_radiate && which_radiate == eFieldType::WALL_ICE)) {
|
||||
if(guts < 5) return false;
|
||||
}
|
||||
if(univ.town.is_sleep_cloud(where_check.x,where_check.y) && which_m->radiate_1 != MONSTER_RADIATE_SLEEP_FIELDS) {
|
||||
if(univ.town.is_sleep_cloud(where_check.x,where_check.y) && !(have_radiate && which_radiate == eFieldType::CLOUD_SLEEP)) {
|
||||
if(guts < 8) return false;
|
||||
}
|
||||
if(univ.town.is_blade_wall(where_check.x,where_check.y) && which_m->radiate_1 != MONSTER_RADIATE_BLADE_FIELDS) {
|
||||
if(univ.town.is_blade_wall(where_check.x,where_check.y) && !(have_radiate && which_radiate == eFieldType::WALL_BLADES)) {
|
||||
if(guts < 8) return false;
|
||||
}
|
||||
if(univ.town.is_quickfire(where_check.x,where_check.y)) {
|
||||
if(guts < 8) return false;
|
||||
}
|
||||
if(univ.town.is_scloud(where_check.x,where_check.y) && which_m->radiate_1 != MONSTER_RADIATE_STINKING_CLOUDS) {
|
||||
if(univ.town.is_scloud(where_check.x,where_check.y) && !(have_radiate && which_radiate == eFieldType::CLOUD_STINK)) {
|
||||
if(guts < 4) return false;
|
||||
}
|
||||
if(univ.town.is_web(where_check.x,where_check.y) && which_m->m_type != eRace::BUG) {
|
||||
if(univ.town.is_web(where_check.x,where_check.y) && which_m->m_type != eRace::BUG
|
||||
&& !(have_radiate && which_radiate == eFieldType::FIELD_WEB)) {
|
||||
if(guts < 3) return false;
|
||||
}
|
||||
if(univ.town.is_fire_barr(where_check.x,where_check.y)) {
|
||||
@@ -1056,7 +1064,7 @@ bool monst_check_special_terrain(location where_check,short mode,short which_mon
|
||||
case eDamageType::POISON:
|
||||
return univ.town.monst[which_monst].poison_res == RESIST_ALL;
|
||||
default:
|
||||
return univ.town.monst[which_monst].spec_skill == MONSTER_INVULNERABILITY;
|
||||
return univ.town.monst[which_monst].invuln;
|
||||
}
|
||||
// TODO: Should it check any other terrain specials?
|
||||
}
|
||||
@@ -1093,7 +1101,7 @@ void forced_place_monster(mon_num_t which,location where) {
|
||||
}
|
||||
|
||||
void magic_adjust(cCreature *which_m,short *how_much) {
|
||||
if(which_m->spec_skill == MONSTER_ABSORB_SPELLS) {
|
||||
if(which_m->abil[eMonstAbil::ABSORB_SPELLS].active) {
|
||||
*how_much = 0;
|
||||
if(32767 - which_m->health > 3)
|
||||
which_m->health = 32767;
|
||||
@@ -1197,7 +1205,8 @@ void dumbfound_monst(cCreature *which_m,short how_much) {
|
||||
|
||||
}
|
||||
|
||||
// Also used for sleep and paralyze, which_statys is 0 means charm
|
||||
// Also used for sleep and paralyze.
|
||||
// For charm, amount is the resulting attitude of the charmed monster; if 0, attitude is 2.
|
||||
void charm_monst(cCreature *which_m,short penalty,eStatus which_status,short amount) {
|
||||
short r1;
|
||||
|
||||
@@ -1223,7 +1232,7 @@ void charm_monst(cCreature *which_m,short penalty,eStatus which_status,short amo
|
||||
r1 -= 25;
|
||||
if(which_status == eStatus::PARALYZED)
|
||||
r1 -= 15;
|
||||
if(which_status == eStatus::ASLEEP && which_m->spec_skill == MONSTER_BREATHES_SLEEP_CLOUDS)
|
||||
if(which_status == eStatus::ASLEEP && which_m->abil[eMonstAbil::FIELD].active && which_m->abil[eMonstAbil::FIELD].gen.fld == eFieldType::CLOUD_SLEEP)
|
||||
return;
|
||||
|
||||
if(r1 > charm_odds[which_m->level / 2]) {
|
||||
@@ -1232,7 +1241,8 @@ void charm_monst(cCreature *which_m,short penalty,eStatus which_status,short amo
|
||||
}
|
||||
else {
|
||||
if(which_status == eStatus::CHARM) {
|
||||
which_m->attitude = 2;
|
||||
if(amount == 0 || amount > 3) amount = 2;
|
||||
which_m->attitude = amount;
|
||||
monst_spell_note(which_m->number,23);
|
||||
} else if(which_status == eStatus::FORCECAGE) {
|
||||
which_m->status[eStatus::FORCECAGE] = 8;
|
||||
@@ -1260,7 +1270,7 @@ void record_monst(cCreature *which_m) {
|
||||
ASB("Capture Soul: Monster is too big.");
|
||||
}
|
||||
// TODO: Are these two sounds right?
|
||||
else if(r1 > charm_odds[which_m->level / 2] || which_m->spec_skill == MONSTER_SPLITS
|
||||
else if(r1 > charm_odds[which_m->level / 2] || which_m->abil[eMonstAbil::SPLITS].active
|
||||
|| which_m->m_type == eRace::IMPORTANT) {
|
||||
monst_spell_note(which_m->number,10);
|
||||
play_sound(68);
|
||||
|
||||
@@ -1463,7 +1463,7 @@ bool damage_monst(short which_m, short who_hit, short how_much, short how_much_s
|
||||
|
||||
// Absorb damage?
|
||||
if((dam_type == eDamageType::FIRE || dam_type == eDamageType::MAGIC || dam_type == eDamageType::COLD)
|
||||
&& victim->spec_skill == MONSTER_ABSORB_SPELLS) {
|
||||
&& victim->abil[eMonstAbil::ABSORB_SPELLS].active) {
|
||||
if(32767 - victim->health > how_much)
|
||||
victim->health = 32767;
|
||||
else victim->health += how_much;
|
||||
@@ -1478,7 +1478,7 @@ bool damage_monst(short which_m, short who_hit, short how_much, short how_much_s
|
||||
how_much /= 2;
|
||||
|
||||
// Invulnerable?
|
||||
if(victim->spec_skill == MONSTER_INVULNERABILITY)
|
||||
if(victim->invuln)
|
||||
how_much = how_much / 10;
|
||||
|
||||
|
||||
@@ -1523,7 +1523,7 @@ bool damage_monst(short which_m, short who_hit, short how_much, short how_much_s
|
||||
victim->health = -1;
|
||||
|
||||
// splitting monsters
|
||||
if(victim->spec_skill == MONSTER_SPLITS && victim->health > 0){
|
||||
if(victim->abil[eMonstAbil::SPLITS].active && 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) {
|
||||
@@ -1636,8 +1636,8 @@ void kill_monst(cCreature *which_m,short who_killed,eMainStatus type) {
|
||||
|
||||
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 == MONSTER_DEATH_TRIGGERS)
|
||||
run_special(eSpecCtx::KILL_MONST,0,which_m->radiate_2,which_m->cur_loc,&s1,&s2,&s3);
|
||||
if(which_m->abil[eMonstAbil::DEATH_TRIGGER].active)
|
||||
run_special(eSpecCtx::KILL_MONST,0,which_m->abil[eMonstAbil::DEATH_TRIGGER].special.extra1,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;
|
||||
|
||||
@@ -1118,6 +1118,12 @@ void monst_spell_note(mon_num_t number,short which_mess) {
|
||||
case 52:
|
||||
msg = " " + msg + " is trapped!";
|
||||
break;
|
||||
case 53:
|
||||
msg = " Throws dart at " + msg + '.';
|
||||
break;
|
||||
case 54:
|
||||
msg = " Throws knife at " + msg + '.';
|
||||
break;
|
||||
}
|
||||
|
||||
if(which_mess > 0)
|
||||
|
||||
@@ -361,7 +361,7 @@ void start_town_mode(short which_town, short entry_dir) {
|
||||
|
||||
// Flush excess doomguards and viscous goos
|
||||
for(i = 0; i < univ.town->max_monst(); i++)
|
||||
if((univ.town.monst[i].spec_skill == MONSTER_SPLITS) &&
|
||||
if((univ.town.monst[i].abil[eMonstAbil::SPLITS].active) &&
|
||||
(univ.town.monst[i].number != univ.town->creatures(i).number))
|
||||
univ.town.monst[i].active = 0;
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ void cPopulation::assign(size_t n, const cCreature& other, const cMonster& base,
|
||||
static_cast<cMonster&>(dudes[n]) = base;
|
||||
// Now set up extra stuff
|
||||
dudes[n].active = 1; // TODO: Is this right?
|
||||
if(dudes[n].spec_skill == MONSTER_INVISIBLE) dudes[n].picture_num = 0;
|
||||
if(dudes[n].invisible) dudes[n].picture_num = 0;
|
||||
dudes[n].m_health /= easy ? 2 : 1;
|
||||
dudes[n].m_health *= difficulty_adjust;
|
||||
dudes[n].health = dudes[n].m_health;
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
#include "classes.h"
|
||||
#include "oldstructs.h"
|
||||
#include "fileio.hpp"
|
||||
#include "spell.hpp"
|
||||
|
||||
static uAbility test;
|
||||
static_assert(&test.active == &test.missile.active, "uAbility union has incorrect layout");
|
||||
|
||||
void cMonster::append(legacy::monster_record_type& old){
|
||||
level = old.level;
|
||||
@@ -32,11 +36,69 @@ void cMonster::append(legacy::monster_record_type& old){
|
||||
speed = old.speed;
|
||||
mu = old.mu;
|
||||
cl = old.cl;
|
||||
breath = old.breath;
|
||||
breath_type = old.breath_type;
|
||||
treasure = old.treasure;
|
||||
spec_skill = old.spec_skill;
|
||||
poison = old.poison;
|
||||
invisible = mindless = invuln = guard = false;
|
||||
abil.clear();
|
||||
switch(old.spec_skill) {
|
||||
case 1: addAbil(eMonstAbilTemplate::THROWS_DARTS); break;
|
||||
case 2: addAbil(eMonstAbilTemplate::SHOOTS_ARROWS); break;
|
||||
case 3: addAbil(eMonstAbilTemplate::THROWS_SPEARS); break;
|
||||
case 4: addAbil(eMonstAbilTemplate::THROWS_ROCKS1); break;
|
||||
case 5: addAbil(eMonstAbilTemplate::THROWS_ROCKS1); break;
|
||||
case 6: addAbil(eMonstAbilTemplate::THROWS_ROCKS1); break;
|
||||
case 7: addAbil(eMonstAbilTemplate::THROWS_RAZORDISKS); break;
|
||||
case 8: addAbil(eMonstAbilTemplate::RAY_PETRIFY); break;
|
||||
case 9: addAbil(eMonstAbilTemplate::RAY_SP_DRAIN); break;
|
||||
case 10: addAbil(eMonstAbilTemplate::RAY_HEAT); break;
|
||||
case 11: invisible = true; break;
|
||||
case 12: addAbil(eMonstAbilTemplate::SPLITS); break;
|
||||
case 13: mindless = true; break;
|
||||
case 14: addAbil(eMonstAbilTemplate::BREATH_FOUL); break;
|
||||
case 15: addAbil(eMonstAbilTemplate::TOUCH_ICY); break;
|
||||
case 17: addAbil(eMonstAbilTemplate::TOUCH_ICY_DRAINING); break;
|
||||
case 16: addAbil(eMonstAbilTemplate::TOUCH_XP_DRAIN); break;
|
||||
case 18: addAbil(eMonstAbilTemplate::TOUCH_STUN); break;
|
||||
case 19: addAbil(eMonstAbilTemplate::SHOOTS_WEB); break;
|
||||
case 20: addAbil(eMonstAbilTemplate::GOOD_ARCHER); break;
|
||||
case 21: addAbil(eMonstAbilTemplate::TOUCH_STEAL_FOOD); break;
|
||||
case 22: addAbil(eMonstAbilTemplate::MARTYRS_SHIELD); break;
|
||||
case 23: addAbil(eMonstAbilTemplate::RAY_PARALYSIS); break;
|
||||
case 24: addAbil(eMonstAbilTemplate::TOUCH_DUMB); break;
|
||||
case 25: addAbil(eMonstAbilTemplate::TOUCH_DISEASE); break;
|
||||
case 26: addAbil(eMonstAbilTemplate::ABSORB_SPELLS); break;
|
||||
case 27: addAbil(eMonstAbilTemplate::TOUCH_WEB); break;
|
||||
case 28: addAbil(eMonstAbilTemplate::TOUCH_SLEEP); break;
|
||||
case 29: addAbil(eMonstAbilTemplate::TOUCH_PARALYSIS); break;
|
||||
case 30: addAbil(eMonstAbilTemplate::TOUCH_PETRIFY); break;
|
||||
case 31: addAbil(eMonstAbilTemplate::TOUCH_ACID); break;
|
||||
case 32: addAbil(eMonstAbilTemplate::BREATH_SLEEP); break;
|
||||
case 33: addAbil(eMonstAbilTemplate::SPIT_ACID); break;
|
||||
case 34: addAbil(eMonstAbilTemplate::SHOOTS_SPINES); break;
|
||||
case 35: addAbil(eMonstAbilTemplate::TOUCH_DEATH); break;
|
||||
case 36: invuln = true; break;
|
||||
case 37: guard = true; break;
|
||||
}
|
||||
switch(old.radiate_1) {
|
||||
case 1: addAbil(eMonstAbilTemplate::RADIATE_FIRE, old.radiate_2); break;
|
||||
case 2: addAbil(eMonstAbilTemplate::RADIATE_ICE, old.radiate_2); break;
|
||||
case 3: addAbil(eMonstAbilTemplate::RADIATE_SHOCK, old.radiate_2); break;
|
||||
case 4: addAbil(eMonstAbilTemplate::RADIATE_ANTIMAGIC, old.radiate_2); break;
|
||||
case 5: addAbil(eMonstAbilTemplate::RADIATE_SLEEP, old.radiate_2); break;
|
||||
case 6: addAbil(eMonstAbilTemplate::RADIATE_STINK, old.radiate_2); break;
|
||||
case 10: addAbil(eMonstAbilTemplate::SUMMON_5, old.radiate_2); break;
|
||||
case 11: addAbil(eMonstAbilTemplate::SUMMON_20, old.radiate_2); break;
|
||||
case 12: addAbil(eMonstAbilTemplate::SUMMON_50, old.radiate_2); break;
|
||||
case 15: addAbil(eMonstAbilTemplate::DEATH_TRIGGERS, old.radiate_2); break;
|
||||
}
|
||||
if(old.poison > 0) addAbil(eMonstAbilTemplate::TOUCH_POISON, old.poison);
|
||||
if(old.breath > 0) {
|
||||
switch(old.breath_type) {
|
||||
case 0: addAbil(eMonstAbilTemplate::BREATH_FIRE, old.breath); break;
|
||||
case 1: addAbil(eMonstAbilTemplate::BREATH_FROST, old.breath); break;
|
||||
case 2: addAbil(eMonstAbilTemplate::BREATH_ELECTRICITY, old.breath); break;
|
||||
case 3: addAbil(eMonstAbilTemplate::BREATH_DARKNESS, old.breath); break;
|
||||
}
|
||||
}
|
||||
corpse_item = old.corpse_item;
|
||||
corpse_item_chance = old.corpse_item_chance;
|
||||
if(old.immunities & 2)
|
||||
@@ -57,14 +119,248 @@ void cMonster::append(legacy::monster_record_type& old){
|
||||
poison_res = RESIST_HALF;
|
||||
x_width = old.x_width;
|
||||
y_width = old.y_width;
|
||||
radiate_1 = old.radiate_1;
|
||||
radiate_2 = old.radiate_2;
|
||||
default_attitude = old.default_attitude;
|
||||
summon_type = old.summon_type;
|
||||
default_facial_pic = old.default_facial_pic;
|
||||
picture_num = old.picture_num;
|
||||
if(picture_num == 122) picture_num = 119;
|
||||
see_spec = -1;
|
||||
see_str1 = see_str2 = -1;
|
||||
see_sound = 0;
|
||||
ambient_sound = 0;
|
||||
}
|
||||
|
||||
void cMonster::addAbil(eMonstAbilTemplate what, int param) {
|
||||
switch(what) {
|
||||
// Missiles: {true, type, missile pic, dice, sides, skill, range, odds}
|
||||
case eMonstAbilTemplate::THROWS_DARTS:
|
||||
abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::DART, 1, 1, 7, 2, 6, 500};
|
||||
break;
|
||||
case eMonstAbilTemplate::SHOOTS_ARROWS:
|
||||
abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ARROW, 3, 2, 7, 4, 8, 750};
|
||||
break;
|
||||
case eMonstAbilTemplate::THROWS_SPEARS:
|
||||
abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ARROW, 5, 3, 7, 6, 8, 625};
|
||||
break;
|
||||
case eMonstAbilTemplate::THROWS_ROCKS1:
|
||||
abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ROCK, 12, 4, 7, 8, 10, 625};
|
||||
break;
|
||||
case eMonstAbilTemplate::THROWS_ROCKS2:
|
||||
abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ROCK, 12, 6, 7, 12, 10, 500};
|
||||
break;
|
||||
case eMonstAbilTemplate::THROWS_ROCKS3:
|
||||
abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ROCK, 12, 8, 7, 16, 10, 500};
|
||||
break;
|
||||
case eMonstAbilTemplate::THROWS_RAZORDISKS:
|
||||
abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::RAZORDISK, 7, 7, 7, 14, 8, 625};
|
||||
break;
|
||||
case eMonstAbilTemplate::THROWS_KNIVES:
|
||||
abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::KNIFE, 10, 2, 7, 2, 6, 500};
|
||||
break;
|
||||
case eMonstAbilTemplate::GOOD_ARCHER:
|
||||
abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ARROW, 3, 8, 7, 16, 10, 875};
|
||||
break;
|
||||
case eMonstAbilTemplate::SHOOTS_SPINES:
|
||||
abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::SPINE, 5, 6, 7, 12, 9, 625};
|
||||
break;
|
||||
case eMonstAbilTemplate::CROSSBOWMAN:
|
||||
abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::BOLT, 3, 10, 7, 16, 10, 875};
|
||||
break;
|
||||
case eMonstAbilTemplate::SLINGER:
|
||||
abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ROCK, 12, 2, 7, 3, 8, 750};
|
||||
break;
|
||||
// Magical missiles: {true, type, missile pic, strength, range, odds}
|
||||
case eMonstAbilTemplate::RAY_PETRIFY:
|
||||
abil[eMonstAbil::PETRIFY].gen = {true, eMonstGen::GAZE, -1, 25, 6, 625};
|
||||
break;
|
||||
case eMonstAbilTemplate::RAY_SP_DRAIN:
|
||||
abil[eMonstAbil::DRAIN_SP].gen = {true, eMonstGen::GAZE, 8, 50, 8, 625};
|
||||
break;
|
||||
case eMonstAbilTemplate::RAY_HEAT:
|
||||
abil[eMonstAbil::DAMAGE].gen = {true, eMonstGen::RAY, 13, 7, 6, 625};
|
||||
abil[eMonstAbil::DAMAGE].gen.dmg = eDamageType::FIRE;
|
||||
break;
|
||||
case eMonstAbilTemplate::RAY_PARALYSIS:
|
||||
abil[eMonstAbil::STATUS].gen = {true, eMonstGen::RAY, -1, 100, 6, 750};
|
||||
abil[eMonstAbil::STATUS].gen.stat = eStatus::PARALYZED;
|
||||
break;
|
||||
case eMonstAbilTemplate::BREATH_FIRE:
|
||||
abil[eMonstAbil::DAMAGE].gen = {true, eMonstGen::BREATH, 13, param, 8, 375};
|
||||
abil[eMonstAbil::DAMAGE].gen.dmg = eDamageType::UNBLOCKABLE;
|
||||
break;
|
||||
case eMonstAbilTemplate::BREATH_FROST:
|
||||
abil[eMonstAbil::DAMAGE].gen = {true, eMonstGen::BREATH, 6, param, 8, 375};
|
||||
abil[eMonstAbil::DAMAGE].gen.dmg = eDamageType::UNBLOCKABLE;
|
||||
break;
|
||||
case eMonstAbilTemplate::BREATH_ELECTRICITY:
|
||||
abil[eMonstAbil::DAMAGE].gen = {true, eMonstGen::BREATH, 8, param, 8, 375};
|
||||
abil[eMonstAbil::DAMAGE].gen.dmg = eDamageType::UNBLOCKABLE;
|
||||
break;
|
||||
case eMonstAbilTemplate::BREATH_DARKNESS:
|
||||
abil[eMonstAbil::DAMAGE].gen = {true, eMonstGen::BREATH, 8, param, 8, 375};
|
||||
abil[eMonstAbil::DAMAGE].gen.dmg = eDamageType::UNBLOCKABLE;
|
||||
break;
|
||||
case eMonstAbilTemplate::BREATH_FOUL:
|
||||
abil[eMonstAbil::FIELD].gen = {true, eMonstGen::BREATH, 12, PAT_SINGLE, 6, 375};
|
||||
abil[eMonstAbil::FIELD].gen.fld = eFieldType::CLOUD_STINK;
|
||||
break;
|
||||
case eMonstAbilTemplate::BREATH_SLEEP:
|
||||
abil[eMonstAbil::FIELD].gen = {true, eMonstGen::BREATH, 0, PAT_RAD2, 8, 750};
|
||||
abil[eMonstAbil::FIELD].gen.fld = eFieldType::CLOUD_SLEEP;
|
||||
break;
|
||||
case eMonstAbilTemplate::SPIT_ACID:
|
||||
abil[eMonstAbil::STATUS].gen = {true, eMonstGen::SPIT, 0, 6, 6, 500};
|
||||
abil[eMonstAbil::STATUS].gen.stat = eStatus::ACID;
|
||||
break;
|
||||
case eMonstAbilTemplate::SHOOTS_WEB:
|
||||
abil[eMonstAbil::FIELD].gen = {true, eMonstGen::SPIT, 8, PAT_SINGLE, 4, 375};
|
||||
abil[eMonstAbil::FIELD].gen.fld = eFieldType::FIELD_WEB;
|
||||
break;
|
||||
// Touch abilities
|
||||
case eMonstAbilTemplate::TOUCH_POISON:
|
||||
abil[eMonstAbil::STATUS2].gen = {true, eMonstGen::TOUCH, -1, param};
|
||||
abil[eMonstAbil::STATUS2].gen.stat = eStatus::POISON;
|
||||
break;
|
||||
case eMonstAbilTemplate::TOUCH_ACID:
|
||||
abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, level > 20 ? 4 : 2};
|
||||
abil[eMonstAbil::STATUS].gen.stat = eStatus::ACID;
|
||||
break;
|
||||
case eMonstAbilTemplate::TOUCH_DISEASE:
|
||||
abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 6, 667};
|
||||
abil[eMonstAbil::STATUS].gen.stat = eStatus::DISEASE;
|
||||
break;
|
||||
case eMonstAbilTemplate::TOUCH_WEB:
|
||||
abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 5};
|
||||
abil[eMonstAbil::STATUS].gen.stat = eStatus::WEBS;
|
||||
break;
|
||||
case eMonstAbilTemplate::TOUCH_SLEEP:
|
||||
abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 6};
|
||||
abil[eMonstAbil::STATUS].gen.stat = eStatus::ASLEEP;
|
||||
break;
|
||||
case eMonstAbilTemplate::TOUCH_DUMB:
|
||||
abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 2};
|
||||
abil[eMonstAbil::STATUS].gen.stat = eStatus::DUMB;
|
||||
break;
|
||||
case eMonstAbilTemplate::TOUCH_PARALYSIS:
|
||||
abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 500};
|
||||
abil[eMonstAbil::STATUS].gen.stat = eStatus::PARALYZED;
|
||||
break;
|
||||
case eMonstAbilTemplate::TOUCH_PETRIFY:
|
||||
abil[eMonstAbil::PETRIFY].gen = {true, eMonstGen::TOUCH, -1, 25};
|
||||
break;
|
||||
case eMonstAbilTemplate::TOUCH_DEATH:
|
||||
abil[eMonstAbil::KILL].gen = {true, eMonstGen::TOUCH, -1, 2, 667};
|
||||
break;
|
||||
case eMonstAbilTemplate::TOUCH_ICY:
|
||||
case eMonstAbilTemplate::TOUCH_ICY_DRAINING:
|
||||
abil[eMonstAbil::DAMAGE].gen = {true, eMonstGen::TOUCH, -1, 3, 667};
|
||||
abil[eMonstAbil::DAMAGE].gen.dmg = eDamageType::COLD;
|
||||
if(what == eMonstAbilTemplate::TOUCH_ICY) break;
|
||||
case eMonstAbilTemplate::TOUCH_XP_DRAIN:
|
||||
abil[eMonstAbil::DRAIN_XP].gen = {true, eMonstGen::TOUCH, -1, 150};
|
||||
break;
|
||||
case eMonstAbilTemplate::TOUCH_STUN:
|
||||
abil[eMonstAbil::STUN].gen = {true, eMonstGen::TOUCH, -1, 2, 667};
|
||||
abil[eMonstAbil::STUN].gen.stat = eStatus::HASTE_SLOW;
|
||||
break;
|
||||
case eMonstAbilTemplate::TOUCH_STEAL_FOOD:
|
||||
abil[eMonstAbil::STEAL_FOOD].gen = {true, eMonstGen::TOUCH, -1, 10, 667};
|
||||
break;
|
||||
case eMonstAbilTemplate::TOUCH_STEAL_GOLD:
|
||||
abil[eMonstAbil::STEAL_GOLD].gen = {true, eMonstGen::TOUCH, -1, 10, 667};
|
||||
break;
|
||||
// Misc abilities
|
||||
case eMonstAbilTemplate::SPLITS:
|
||||
abil[eMonstAbil::SPLITS].special = {true, 100, 0};
|
||||
break;
|
||||
case eMonstAbilTemplate::MARTYRS_SHIELD:
|
||||
abil[eMonstAbil::MARTYRS_SHIELD].special = {true, 0, 0};
|
||||
break;
|
||||
case eMonstAbilTemplate::ABSORB_SPELLS:
|
||||
abil[eMonstAbil::ABSORB_SPELLS].special = {true, 100, 0};
|
||||
break;
|
||||
case eMonstAbilTemplate::SUMMON_5:
|
||||
abil[eMonstAbil::SUMMON].summon = {true, static_cast<mon_num_t>(param), 1, 1, 130, 5};
|
||||
break;
|
||||
case eMonstAbilTemplate::SUMMON_20:
|
||||
abil[eMonstAbil::SUMMON].summon = {true, static_cast<mon_num_t>(param), 1, 1, 130, 20};
|
||||
break;
|
||||
case eMonstAbilTemplate::SUMMON_50:
|
||||
abil[eMonstAbil::SUMMON].summon = {true, static_cast<mon_num_t>(param), 1, 1, 130, 50};
|
||||
break;
|
||||
case eMonstAbilTemplate::SPECIAL:
|
||||
abil[eMonstAbil::SPECIAL].special = {true, param, 1};
|
||||
break;
|
||||
case eMonstAbilTemplate::DEATH_TRIGGERS:
|
||||
abil[eMonstAbil::DEATH_TRIGGER].special = {true, param, 0};
|
||||
break;
|
||||
// Radiate abilities
|
||||
case eMonstAbilTemplate::RADIATE_FIRE:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_FIRE, param};
|
||||
break;
|
||||
case eMonstAbilTemplate::RADIATE_ICE:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_ICE, param};
|
||||
break;
|
||||
case eMonstAbilTemplate::RADIATE_SHOCK:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_FORCE, param};
|
||||
break;
|
||||
case eMonstAbilTemplate::RADIATE_ANTIMAGIC:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::FIELD_ANTIMAGIC, param};
|
||||
break;
|
||||
case eMonstAbilTemplate::RADIATE_SLEEP:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::CLOUD_SLEEP, param};
|
||||
break;
|
||||
case eMonstAbilTemplate::RADIATE_STINK:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::CLOUD_STINK, param};
|
||||
break;
|
||||
case eMonstAbilTemplate::RADIATE_BLADE:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_BLADES, param};
|
||||
break;
|
||||
case eMonstAbilTemplate::RADIATE_WEB:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::FIELD_WEB, param};
|
||||
break;
|
||||
// Advanced abilities
|
||||
case eMonstAbilTemplate::CUSTOM_MISSILE:
|
||||
abil[eMonstAbil::MISSILE].active = true;
|
||||
break;
|
||||
case eMonstAbilTemplate::CUSTOM_DAMAGE:
|
||||
abil[eMonstAbil::DAMAGE].active = true;
|
||||
break;
|
||||
case eMonstAbilTemplate::CUSTOM_STATUS:
|
||||
abil[eMonstAbil::STATUS].active = true;
|
||||
break;
|
||||
case eMonstAbilTemplate::CUSTOM_FIELD:
|
||||
abil[eMonstAbil::FIELD].active = true;
|
||||
break;
|
||||
case eMonstAbilTemplate::CUSTOM_PETRIFY:
|
||||
abil[eMonstAbil::PETRIFY].active = true;
|
||||
break;
|
||||
case eMonstAbilTemplate::CUSTOM_SP_DRAIN:
|
||||
abil[eMonstAbil::DRAIN_SP].active = true;
|
||||
break;
|
||||
case eMonstAbilTemplate::CUSTOM_XP_DRAIN:
|
||||
abil[eMonstAbil::DRAIN_XP].active = true;
|
||||
break;
|
||||
case eMonstAbilTemplate::CUSTOM_KILL:
|
||||
abil[eMonstAbil::KILL].active = true;
|
||||
break;
|
||||
case eMonstAbilTemplate::CUSTOM_STEAL_FOOD:
|
||||
abil[eMonstAbil::STEAL_FOOD].active = true;
|
||||
break;
|
||||
case eMonstAbilTemplate::CUSTOM_STEAL_GOLD:
|
||||
abil[eMonstAbil::STEAL_GOLD].active = true;
|
||||
break;
|
||||
case eMonstAbilTemplate::CUSTOM_STUN:
|
||||
abil[eMonstAbil::STUN].active = true;
|
||||
break;
|
||||
case eMonstAbilTemplate::CUSTOM_STATUS2:
|
||||
abil[eMonstAbil::STATUS2].active = true;
|
||||
case eMonstAbilTemplate::CUSTOM_RADIATE:
|
||||
abil[eMonstAbil::RADIATE].active = true;
|
||||
case eMonstAbilTemplate::CUSTOM_SUMMON:
|
||||
abil[eMonstAbil::SUMMON].active = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cMonster::cMonster(){
|
||||
@@ -198,293 +494,233 @@ std::istream& operator >> (std::istream& in, eDirection& e) {
|
||||
return in;
|
||||
}
|
||||
|
||||
cMonster::cAbility::operator std::string(){
|
||||
std::ostream& operator << (std::ostream& out, eFieldType e) {
|
||||
return out << (int)e;
|
||||
}
|
||||
|
||||
std::istream& operator >> (std::istream& in, eFieldType& e) {
|
||||
int i;
|
||||
in >> i;
|
||||
if(i >= 0 && i <= 24)
|
||||
e = (eFieldType)i;
|
||||
else if(i == 33) e = eFieldType::FIELD_SMASH;
|
||||
else e = eFieldType::FIELD_DISPEL;
|
||||
return in;
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream& out, eDamageType e) {
|
||||
return out << (int)e;
|
||||
}
|
||||
|
||||
std::istream& operator >> (std::istream& in, eDamageType& e) {
|
||||
int i;
|
||||
in >> i;
|
||||
if(i >= 0 && i < 8)
|
||||
e = (eDamageType)i;
|
||||
else e = eDamageType::MARKED;
|
||||
return in;
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream& out, eMonstAbil e) {
|
||||
return out << (int)e;
|
||||
}
|
||||
|
||||
std::istream& operator >> (std::istream& in, eMonstAbil& e) {
|
||||
int i;
|
||||
in >> i;
|
||||
if(i > 0 && i <= int(eMonstAbil::SUMMON))
|
||||
e = (eMonstAbil)i;
|
||||
else e = eMonstAbil::NO_ABIL;
|
||||
return in;
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream& out, eMonstGen e) {
|
||||
return out << (int)e;
|
||||
}
|
||||
|
||||
std::istream& operator >> (std::istream& in, eMonstGen& e) {
|
||||
int i;
|
||||
in >> i;
|
||||
if(i >= 0 && i <= int(eMonstGen::SPIT))
|
||||
e = (eMonstGen)i;
|
||||
else e = eMonstGen::TOUCH;
|
||||
return in;
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream& out, eMonstMissile e) {
|
||||
return out << (int)e;
|
||||
}
|
||||
|
||||
std::istream& operator >> (std::istream& in, eMonstMissile& e) {
|
||||
int i;
|
||||
in >> i;
|
||||
if(i >= 0 && i <= int(eMonstMissile::BOLT))
|
||||
e = (eMonstMissile)i;
|
||||
else e = eMonstMissile::ARROW;
|
||||
return in;
|
||||
}
|
||||
|
||||
std::string uAbility::to_string(eMonstAbil key) const {
|
||||
std::ostringstream sout;
|
||||
short i = 0;
|
||||
switch(abil){
|
||||
case MONST_NO_ABIL:
|
||||
break;
|
||||
case MONST_THROWS_DARTS:
|
||||
sout << "Throws darts (" << extra1 << 'd' << extra2 << ')';
|
||||
break;
|
||||
case MONST_SHOOTS_ARROWS:
|
||||
sout << "Shoots arrows (" << extra1 << 'd' << extra2 << ')';
|
||||
break;
|
||||
case MONST_THROWS_SPEARS:
|
||||
sout << "Throws spears (" << extra1 << 'd' << extra2 << ')';
|
||||
break;
|
||||
case MONST_THROWS_ROCKS:
|
||||
sout << "Throws rocks (" << extra1 << 'd' << extra2 << ')';
|
||||
break;
|
||||
case MONST_THROWS_RAZORDISKS:
|
||||
sout << "Throws razordisks (" << extra1 << 'd' << extra2 << ')';
|
||||
break;
|
||||
case MONST_GOOD_ARCHER:
|
||||
sout << "Good archer (" << extra1 << 'd' << extra2 << ')';
|
||||
break;
|
||||
case MONST_SHOOTS_SPINES:
|
||||
sout << "Shoots spines (" << extra1 << 'd' << extra2 << ')';
|
||||
break;
|
||||
case MONST_THROWS_KNIVES:
|
||||
sout << "Throws knives (" << extra1 << 'd' << extra2 << ')';
|
||||
break;
|
||||
case MONST_DAMAGE_RAY:
|
||||
case MONST_DRAIN_XP_DAMAGE_RAY:
|
||||
case MONST_DAMAGE_TOUCH:
|
||||
case MONST_DRAIN_XP_DAMAGE_TOUCH:
|
||||
switch(eDamageType(extra1)) {
|
||||
case eDamageType::WEAPON:
|
||||
sout << "Health drain";
|
||||
switch(getMonstAbilCategory(key)){
|
||||
case eMonstAbilCat::INVALID: break;
|
||||
case eMonstAbilCat::MISSILE:
|
||||
switch(missile.type) {
|
||||
case eMonstMissile::DART:
|
||||
sout << "Throws darts (" << missile.dice << 'd' << missile.sides << ')';
|
||||
break;
|
||||
case eDamageType::FIRE:
|
||||
sout << "Heat";
|
||||
case eMonstMissile::ARROW:
|
||||
sout << "Shoots arrows (" << missile.dice << 'd' << missile.sides << ')';
|
||||
break;
|
||||
case eDamageType::POISON:
|
||||
sout << "Pain";
|
||||
case eMonstMissile::BOLT:
|
||||
sout << "Shoots bolts (" << missile.dice << 'd' << missile.sides << ')';
|
||||
break;
|
||||
case eDamageType::MAGIC:
|
||||
sout << "Shock";
|
||||
case eMonstMissile::SPEAR:
|
||||
sout << "Throws spears (" << missile.dice << 'd' << missile.sides << ')';
|
||||
break;
|
||||
case eDamageType::UNBLOCKABLE:
|
||||
sout << "Wounding";
|
||||
case eMonstMissile::ROCK:
|
||||
sout << "Throws rocks (" << missile.dice << 'd' << missile.sides << ')';
|
||||
break;
|
||||
case eDamageType::COLD:
|
||||
sout << "Icy";
|
||||
case eMonstMissile::RAZORDISK:
|
||||
sout << "Throws razordisks (" << missile.dice << 'd' << missile.sides << ')';
|
||||
break;
|
||||
case eDamageType::UNDEAD:
|
||||
case eDamageType::DEMON:
|
||||
sout << "Unholy";
|
||||
case eMonstMissile::SPINE:
|
||||
sout << "Shoots spines (" << missile.dice << 'd' << missile.sides << ')';
|
||||
break;
|
||||
default:
|
||||
sout << "*ERROR INVALID DAMAGE TYPE*";
|
||||
}
|
||||
if(abil == MONST_DRAIN_XP_DAMAGE_RAY || abil == MONST_DRAIN_XP_DAMAGE_TOUCH)
|
||||
sout << ", draining";
|
||||
if(abil == MONST_DAMAGE_RAY || abil == MONST_DRAIN_XP_DAMAGE_RAY)
|
||||
sout << " ray";
|
||||
else sout << " touch";
|
||||
break;
|
||||
case MONST_STATUS_RAY:
|
||||
case MONST_STATUS_TOUCH:
|
||||
switch((eStatus)extra1){
|
||||
case eStatus::BLESS_CURSE:
|
||||
sout << "Curse";
|
||||
break;
|
||||
case eStatus::POISON:
|
||||
sout << "Poison";
|
||||
i = 1;
|
||||
break;
|
||||
case eStatus::HASTE_SLOW:
|
||||
sout << "Slowing";
|
||||
break;
|
||||
case eStatus::WEBS:
|
||||
sout << "Glue";
|
||||
i = 1;
|
||||
break;
|
||||
case eStatus::DISEASE:
|
||||
sout << "Infectious";
|
||||
i = 1;
|
||||
break;
|
||||
case eStatus::DUMB:
|
||||
sout << "Dumbfounding";
|
||||
break;
|
||||
case eStatus::ASLEEP:
|
||||
sout << "Sleep";
|
||||
break;
|
||||
case eStatus::PARALYZED:
|
||||
sout << "Paralysis";
|
||||
break;
|
||||
case eStatus::ACID:
|
||||
sout << "Acid";
|
||||
i = 1;
|
||||
break;
|
||||
default: // Poisoned weapon, invulnerable, magic resistance, sanctuary, martyr's shield, or invalid
|
||||
sout << "*ERROR BAD OR INVALID STATUS TYPE*";
|
||||
}
|
||||
if(abil == MONST_STATUS_RAY)
|
||||
if(i == 1) sout << " spit";
|
||||
else sout << " ray";
|
||||
else sout << " touch";
|
||||
break;
|
||||
case MONST_PETRIFY_RAY:
|
||||
sout << "Petrification ray";
|
||||
break;
|
||||
case MONST_DRAIN_SP_RAY:
|
||||
sout << "Spell point drain ray";
|
||||
break;
|
||||
case MONST_DRAIN_XP_RAY:
|
||||
sout << "Experience draining ray";
|
||||
break;
|
||||
case MONST_KILL_RAY:
|
||||
sout << "Death ray";
|
||||
break;
|
||||
case MONST_STEAL_FOOD_RAY:
|
||||
sout << "Steals food from afar";
|
||||
break;
|
||||
case MONST_STEAL_GOLD_RAY:
|
||||
sout << "Steals gold from afar";
|
||||
break;
|
||||
break;
|
||||
case MONST_PETRIFY_TOUCH:
|
||||
sout << "Petrification touch";
|
||||
break;
|
||||
case MONST_DRAIN_SP_TOUCH:
|
||||
sout << "Spell point draining touch";
|
||||
break;
|
||||
case MONST_DRAIN_XP_TOUCH:
|
||||
sout << "Experience draining touch";
|
||||
break;
|
||||
break;
|
||||
case MONST_KILL_TOUCH:
|
||||
sout << "Death touch";
|
||||
break;
|
||||
case MONST_STEAL_FOOD_TOUCH:
|
||||
sout << "Steals food when hits";
|
||||
break;
|
||||
case MONST_STEAL_GOLD_TOUCH:
|
||||
sout << "Steals gold when hits";
|
||||
break;
|
||||
case MONST_SUMMON_ONE:
|
||||
// TODO: Store the name of the summoned monster in the class so it can be used here.
|
||||
// sout << "Summons " << univ.scenario.scen_monsters[extra1].m_name << "s (" << extra2 <<"% chance)";
|
||||
break;
|
||||
case MONST_SUMMON_TYPE:
|
||||
sout << "Summons ";
|
||||
switch(extra1){
|
||||
case 0:
|
||||
sout << "wildlife";
|
||||
break;
|
||||
case 1:
|
||||
sout << "weak aid";
|
||||
break;
|
||||
case 2:
|
||||
sout << "strong aid";
|
||||
break;
|
||||
case 3:
|
||||
sout << "powerful aid";
|
||||
break;
|
||||
case 4:
|
||||
sout << "friends";
|
||||
case eMonstMissile::KNIFE:
|
||||
sout << "Throws knives (" << missile.dice << 'd' << missile.sides << ')';
|
||||
break;
|
||||
}
|
||||
sout << " (" << extra2 << "% chance)";
|
||||
break;
|
||||
case MONST_SUMMON_SPECIES:
|
||||
sout << "Summons ";
|
||||
switch((eRace)extra1){
|
||||
case eRace::HUMAN:
|
||||
sout << "Humans";
|
||||
break;
|
||||
case eRace::NEPHIL:
|
||||
sout << "Nephilim";
|
||||
break;
|
||||
case eRace::SLITH:
|
||||
sout << "Slithzerikai";
|
||||
break;
|
||||
case eRace::VAHNATAI:
|
||||
sout << "Vahnatai";
|
||||
break;
|
||||
case eRace::REPTILE:
|
||||
sout << "reptiles";
|
||||
break;
|
||||
case eRace::BEAST:
|
||||
sout << "beasts";
|
||||
break;
|
||||
case eRace::HUMANOID:
|
||||
sout << "humanoids";
|
||||
break;
|
||||
case eRace::DEMON:
|
||||
sout << "demons";
|
||||
break;
|
||||
case eRace::UNDEAD:
|
||||
sout << "undead";
|
||||
break;
|
||||
case eRace::GIANT:
|
||||
sout << "giants";
|
||||
break;
|
||||
case eRace::SLIME:
|
||||
sout << "slimes";
|
||||
break;
|
||||
case eRace::STONE:
|
||||
sout << "golems";
|
||||
break;
|
||||
case eRace::BUG:
|
||||
sout << "bugs";
|
||||
break;
|
||||
case eRace::DRAGON:
|
||||
sout << "Dragons";
|
||||
break;
|
||||
case eRace::MAGICAL:
|
||||
sout << "magical creatures";
|
||||
break;
|
||||
case eRace::PLANT:
|
||||
sout << "plants";
|
||||
break;
|
||||
case eRace::BIRD:
|
||||
sout << "birds";
|
||||
break;
|
||||
default: // Important, Mage, Priest, or invalid
|
||||
sout << "*ERROR INVALID RACE*";
|
||||
case eMonstAbilCat::GENERAL:
|
||||
if(key == eMonstAbil::FIELD && gen.type == eMonstGen::SPIT && gen.fld == eFieldType::FIELD_WEB) {
|
||||
sout << "Throws webs";
|
||||
} else {
|
||||
switch(key) {
|
||||
case eMonstAbil::STUN: sout << "Stunning"; break;
|
||||
case eMonstAbil::PETRIFY: sout << "Petrifying"; break;
|
||||
case eMonstAbil::DRAIN_SP: sout << "Spell point drain"; break;
|
||||
case eMonstAbil::DRAIN_XP: sout << "Experience drain"; break;
|
||||
case eMonstAbil::KILL: sout << "Death"; break;
|
||||
case eMonstAbil::STEAL_FOOD: sout << "Steals food"; break;
|
||||
case eMonstAbil::STEAL_GOLD: sout << "Steals gold!"; break;
|
||||
case eMonstAbil::FIELD:
|
||||
switch(gen.fld) {
|
||||
case eFieldType::CLOUD_SLEEP: sout << "Sleep"; break;
|
||||
case eFieldType::CLOUD_STINK: sout << "Foul"; break;
|
||||
case eFieldType::WALL_FIRE: sout << "Fiery"; break;
|
||||
case eFieldType::WALL_FORCE: sout << "Charged"; break;
|
||||
case eFieldType::WALL_ICE: sout << "Frosted"; break;
|
||||
case eFieldType::WALL_BLADES: sout << "Thorny"; break;
|
||||
case eFieldType::FIELD_ANTIMAGIC: sout << "Null"; break;
|
||||
case eFieldType::FIELD_WEB: sout << "Web"; break;
|
||||
case eFieldType::FIELD_QUICKFIRE: sout << "Incendiary"; break;
|
||||
}
|
||||
break;
|
||||
case eMonstAbil::DAMAGE:
|
||||
switch(gen.dmg) {
|
||||
case eDamageType::FIRE: sout << "Heat"; break;
|
||||
case eDamageType::COLD: sout << "Icy"; break;
|
||||
case eDamageType::MAGIC: sout << "Shock"; break;
|
||||
case eDamageType::UNBLOCKABLE: sout << "Wounding"; break;
|
||||
case eDamageType::POISON: sout << "Pain"; break;
|
||||
case eDamageType::WEAPON: sout << "Stamina drain"; break;
|
||||
case eDamageType::DEMON: sout << "Unholy"; break;
|
||||
case eDamageType::UNDEAD: sout << "Necrotic"; break;
|
||||
}
|
||||
break;
|
||||
case eMonstAbil::STATUS: case eMonstAbil::STATUS2:
|
||||
switch(gen.stat) {
|
||||
case eStatus::POISON: sout << "Poison"; break;
|
||||
case eStatus::DISEASE: sout << "Infectious"; break;
|
||||
case eStatus::DUMB: sout << "Dumbfounding"; break;
|
||||
case eStatus::WEBS: sout << "Glue"; break;
|
||||
case eStatus::ASLEEP: sout << "Sleep"; break;
|
||||
case eStatus::PARALYZED: sout << "Paralysis"; break;
|
||||
case eStatus::ACID: sout << "Acid"; break;
|
||||
case eStatus::HASTE_SLOW: sout << "Slowing"; break;
|
||||
case eStatus::BLESS_CURSE: sout << "Curse"; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch(gen.type) {
|
||||
case eMonstGen::RAY: sout << " ray"; break;
|
||||
case eMonstGen::TOUCH: sout << " touch"; break;
|
||||
case eMonstGen::GAZE: sout << " gaze"; break;
|
||||
case eMonstGen::BREATH: sout << " breath"; break;
|
||||
case eMonstGen::SPIT: sout << " spit"; break;
|
||||
}
|
||||
}
|
||||
if(key == eMonstAbil::DAMAGE) {
|
||||
sout << " (" << gen.strength << ")";
|
||||
} else if(key == eMonstAbil::STATUS || key == eMonstAbil::STATUS2 || key == eMonstAbil::STUN) {
|
||||
sout << " (" << gen.strength;
|
||||
switch(gen.type) {
|
||||
case eMonstGen::RAY: sout << "d6"; break;
|
||||
case eMonstGen::TOUCH: sout << "d10"; break;
|
||||
case eMonstGen::GAZE: sout << "d6"; break;
|
||||
case eMonstGen::BREATH: sout << "d8"; break;
|
||||
case eMonstGen::SPIT: sout << "d10"; break;
|
||||
}
|
||||
sout << ")";
|
||||
} else if(key == eMonstAbil::FIELD && gen.strength != PAT_SINGLE) {
|
||||
sout << " (";
|
||||
switch(eSpellPat(gen.strength)) {
|
||||
case PAT_SINGLE: break;
|
||||
case PAT_SMSQ: sout << "small ";
|
||||
case PAT_SQ: sout << "square"; break;
|
||||
case PAT_OPENSQ: sout << "open square"; break;
|
||||
case PAT_PLUS: sout << "plus"; break;
|
||||
case PAT_RAD2: sout << "small circle"; break;
|
||||
case PAT_RAD3: sout << "big circle"; break;
|
||||
case PAT_WALL: sout << "line"; break;
|
||||
}
|
||||
sout << ")";
|
||||
} else if(key == eMonstAbil::KILL) {
|
||||
sout << " (" << gen.strength * 20 << "d10)";
|
||||
} else if(key == eMonstAbil::STEAL_FOOD || key == eMonstAbil::STEAL_GOLD) {
|
||||
sout << " (" << gen.strength << '-' << gen.strength * 2 << ")";
|
||||
}
|
||||
sout << " (" << extra2 << "% chance)";
|
||||
break;
|
||||
case MONST_SUMMON_RANDOM:
|
||||
sout << "Summons aid" << " (" << extra2 << "% chance)";
|
||||
case eMonstAbilCat::SPECIAL:
|
||||
switch(key) {
|
||||
case eMonstAbil::SPLITS:
|
||||
sout << "Splits when hit (" << special.extra1 << "% chance)";
|
||||
break;
|
||||
case eMonstAbil::MARTYRS_SHIELD:
|
||||
sout << "Permanent martyr's shield";
|
||||
break;
|
||||
case eMonstAbil::ABSORB_SPELLS:
|
||||
sout << "Absorbs spells";
|
||||
break;
|
||||
case eMonstAbil::SPECIAL:
|
||||
case eMonstAbil::DEATH_TRIGGER:
|
||||
sout << "Unusual ability";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MONST_MASS_SUMMON:
|
||||
sout << "Summons aid" << " (" << extra2 << "% chance)";
|
||||
case eMonstAbilCat::SUMMON:
|
||||
// TODO: Somehow look up the name of the monster to be summoned
|
||||
sout << "Summons " << summon.min << '-' << summon.max << ' ' << "monst-name" << "s (" << summon.chance << "% chance)";
|
||||
break;
|
||||
case MONST_SPLITS:
|
||||
sout << "Splits when hit" << " (" << extra2 << "% chance)";
|
||||
break;
|
||||
case MONST_FIELD_MISSILE:
|
||||
// TODO: Fill these in
|
||||
sout << "MONST_FIELD_MISSILE";
|
||||
break;
|
||||
case MONST_MARTYRS_SHIELD:
|
||||
sout << "Permanent Martyr's shield";
|
||||
break;
|
||||
case MONST_ABSORB_SPELLS:
|
||||
sout << "Absorbs spells";
|
||||
break;
|
||||
case MONST_INVULNERABLE:
|
||||
sout << "Invulnerable";
|
||||
break;
|
||||
case MONST_RADIATE:
|
||||
case eMonstAbilCat::RADIATE:
|
||||
sout << "Radiates ";
|
||||
switch(extra1){ // TODO: Fill these in
|
||||
switch(radiate.type) {
|
||||
case eFieldType::WALL_BLADES: sout << "blade fields"; break;
|
||||
case eFieldType::WALL_FIRE: sout << "fire fields"; break;
|
||||
case eFieldType::WALL_FORCE: sout << "shock fields"; break;
|
||||
case eFieldType::WALL_ICE: sout << "ice fields"; break;
|
||||
case eFieldType::CLOUD_STINK: sout << "stinking clouds"; break;
|
||||
case eFieldType::CLOUD_SLEEP: sout << "sleep fields"; break;
|
||||
case eFieldType::FIELD_ANTIMAGIC: sout << "antimagic fields"; break;
|
||||
case eFieldType::FIELD_WEB: sout << "webs"; break;
|
||||
}
|
||||
sout << " (" << extra2 << "% chance)";
|
||||
break;
|
||||
case MONST_CALL_LOCAL_SPECIAL:
|
||||
case MONST_CALL_GLOBAL_SPECIAL:
|
||||
sout << "Unusual ability";
|
||||
break;
|
||||
}
|
||||
return sout.str();
|
||||
}
|
||||
|
||||
std::string cMonster::getAbil1Name() {
|
||||
return (std::string) abil1;
|
||||
}
|
||||
|
||||
std::string cMonster::getAbil2Name() {
|
||||
return (std::string) abil2;
|
||||
}
|
||||
|
||||
bool cMonster::hasAbil(eMonstAbil what, unsigned short& x1, unsigned short& x2){
|
||||
if(abil1.abil == what){
|
||||
x1 = abil1.extra1;
|
||||
x2 = abil1.extra2;
|
||||
return true;
|
||||
}else if(abil2.abil == what){
|
||||
x1 = abil2.extra1;
|
||||
x2 = abil2.extra2;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cMonster::writeTo(std::ostream& file) const {
|
||||
file << "MONSTER " << maybe_quote_string(m_name) << '\n';
|
||||
file << "LEVEL " << int(level) << '\n';
|
||||
@@ -497,11 +733,7 @@ void cMonster::writeTo(std::ostream& file) const {
|
||||
file << "MAGE " << int(mu) << '\n';
|
||||
file << "PRIEST " << int(cl) << '\n';
|
||||
file << "RACE " << m_type << '\n';
|
||||
file << "BREATH " << int(breath_type) << ' ' << int(breath) << '\n';
|
||||
file << "TREASURE" << int(treasure) << '\n';
|
||||
file << "ABIL 1 " << int(spec_skill) << " 0 0\n";
|
||||
file << "ABIL 2 " << int(radiate_1) << ' ' << int(radiate_2) << " 0\n";
|
||||
file << "POISON " << int(poison) << '\n';
|
||||
file << "CORPSEITEM " << corpse_item << ' ' << corpse_item_chance << '\n';
|
||||
file << "IMMUNE " << magic_res << '\t' << fire_res << '\t' << cold_res << '\t' << poison_res << '\n';
|
||||
file << "SIZE " << int(x_width) << ' ' << int(y_width) << '\n';
|
||||
@@ -510,6 +742,46 @@ void cMonster::writeTo(std::ostream& file) const {
|
||||
file << "PORTRAIT " << default_facial_pic << '\n';
|
||||
file << "PICTURE " << picture_num << '\n';
|
||||
file << "SOUND " << ambient_sound << '\n';
|
||||
file << '\f';
|
||||
for(auto& abil : abil) {
|
||||
if(!abil.second.active || abil.first == eMonstAbil::NO_ABIL) continue;
|
||||
file << "ABIL " << abil.first << '\n';
|
||||
switch(getMonstAbilCategory(abil.first)) {
|
||||
case eMonstAbilCat::INVALID: continue;
|
||||
case eMonstAbilCat::MISSILE:
|
||||
file << "TYPE " << abil.second.missile.type << ' ' << abil.second.missile.pic << '\n';
|
||||
file << "DAMAGE " << abil.second.missile.dice << ' ' << abil.second.missile.sides << '\n';
|
||||
file << "SKILL " << abil.second.missile.skill << '\n';
|
||||
file << "RANGE " << abil.second.missile.range << '\n';
|
||||
file << "CHANCE " << abil.second.missile.odds << '\n';
|
||||
break;
|
||||
case eMonstAbilCat::GENERAL:
|
||||
file << "TYPE " << abil.second.gen.type << ' ' << abil.second.gen.pic << '\n';
|
||||
file << "DAMAGE " << abil.second.gen.strength << '\n';
|
||||
file << "RANGE " << abil.second.gen.range << '\n';
|
||||
file << "CHANCE " << abil.second.gen.odds << '\n';
|
||||
if(abil.first == eMonstAbil::DAMAGE)
|
||||
file << "EXTRA " << abil.second.gen.dmg << '\n';
|
||||
else if(abil.first == eMonstAbil::FIELD)
|
||||
file << "EXTRA " << abil.second.gen.fld << '\n';
|
||||
else if(abil.first == eMonstAbil::STATUS || abil.first == eMonstAbil::STATUS2 || abil.first == eMonstAbil::STUN)
|
||||
file << "EXTRA " << abil.second.gen.stat;
|
||||
break;
|
||||
case eMonstAbilCat::RADIATE:
|
||||
file << "TYPE " << abil.second.radiate.type << '\n';
|
||||
file << "CHANCE " << abil.second.radiate.chance << '\n';
|
||||
break;
|
||||
case eMonstAbilCat::SUMMON:
|
||||
file << "TYPE " << abil.second.summon.type << '\n';
|
||||
file << "HOWMANY " << abil.second.summon.min << ' ' << abil.second.summon.max << '\n';
|
||||
file << "CHANCE " << abil.second.summon.chance << '\n';
|
||||
break;
|
||||
case eMonstAbilCat::SPECIAL:
|
||||
file << "EXTRA " << abil.second.special.extra1 << ' ' << abil.second.special.extra2 << '\n';
|
||||
break;
|
||||
}
|
||||
file << '\f';
|
||||
}
|
||||
}
|
||||
|
||||
void cMonster::readFrom(std::istream& file) {
|
||||
@@ -518,10 +790,13 @@ void cMonster::readFrom(std::istream& file) {
|
||||
see_str1 = -1;
|
||||
see_str2 = -1;
|
||||
see_spec = -1;
|
||||
while(file) {
|
||||
std::string cur;
|
||||
std::istringstream bin;
|
||||
std::string cur;
|
||||
getline(file, cur, '\f');
|
||||
bin.str(cur);
|
||||
while(bin) {
|
||||
int temp1, temp2, temp3;
|
||||
getline(file, cur);
|
||||
getline(bin, cur);
|
||||
std::istringstream line(cur);
|
||||
line >> cur;
|
||||
if(cur == "MONSTER") {
|
||||
@@ -534,17 +809,6 @@ void cMonster::readFrom(std::istream& file) {
|
||||
a[which].dice = temp1;
|
||||
a[which].sides = temp2;
|
||||
a[which].type = temp3;
|
||||
} else if(cur == "BREATH") {
|
||||
line >> temp1 >> temp2;
|
||||
breath_type = temp1;
|
||||
breath = temp2;
|
||||
} else if(cur == "ABIL") {
|
||||
int which;
|
||||
line >> which >> temp1 >> temp2 >> temp3;
|
||||
if(which == 1)
|
||||
spec_skill = temp1;
|
||||
else if(which == 2)
|
||||
radiate_1 = temp1, radiate_2 = temp2;
|
||||
} else if(cur == "SIZE") {
|
||||
line >> temp1 >> temp2;
|
||||
x_width = temp1;
|
||||
@@ -584,14 +848,75 @@ void cMonster::readFrom(std::istream& file) {
|
||||
cl = temp1;
|
||||
else if(cur == "TREASURE")
|
||||
treasure = temp1;
|
||||
else if(cur == "POISON")
|
||||
poison = temp1;
|
||||
else if(cur == "ATTITUDE")
|
||||
default_attitude = temp1;
|
||||
else if(cur == "SUMMON")
|
||||
summon_type = temp1;
|
||||
}
|
||||
}
|
||||
while(file) {
|
||||
getline(file, cur, '\f');
|
||||
bin.str(cur);
|
||||
getline(bin, cur);
|
||||
std::istringstream line(cur);
|
||||
line >> cur;
|
||||
if(cur == "ABIL") {
|
||||
eMonstAbil key;
|
||||
uAbility abil;
|
||||
line >> key;
|
||||
eMonstAbilCat cat = getMonstAbilCategory(key);
|
||||
if(cat == eMonstAbilCat::INVALID) continue;
|
||||
while(bin) {
|
||||
getline(bin, cur);
|
||||
line.str(cur);
|
||||
line >> cur;
|
||||
if(cur == "TYPE") {
|
||||
if(cat == eMonstAbilCat::MISSILE)
|
||||
line >> abil.missile.type >> abil.missile.pic;
|
||||
else if(cat == eMonstAbilCat::GENERAL)
|
||||
line >> abil.gen.type >> abil.gen.pic;
|
||||
else if(cat == eMonstAbilCat::RADIATE)
|
||||
line >> abil.radiate.type;
|
||||
else if(cat == eMonstAbilCat::SUMMON)
|
||||
line >> abil.summon.type;
|
||||
} else if(cur == "DAMAGE") {
|
||||
if(cat == eMonstAbilCat::MISSILE)
|
||||
line >> abil.missile.dice >> abil.missile.sides;
|
||||
else if(cat == eMonstAbilCat::GENERAL)
|
||||
line >> abil.gen.strength;
|
||||
} else if(cur == "SKILL") {
|
||||
if(cat == eMonstAbilCat::MISSILE)
|
||||
line >> abil.missile.skill;
|
||||
} else if(cur == "RANGE") {
|
||||
if(cat == eMonstAbilCat::MISSILE)
|
||||
line >> abil.missile.range;
|
||||
else if(cat == eMonstAbilCat::GENERAL)
|
||||
line >> abil.gen.range;
|
||||
} else if(cur == "CHANCE") {
|
||||
if(cat == eMonstAbilCat::MISSILE)
|
||||
line >> abil.missile.odds;
|
||||
else if(cat == eMonstAbilCat::GENERAL)
|
||||
line >> abil.gen.odds;
|
||||
else if(cat == eMonstAbilCat::RADIATE)
|
||||
line >> abil.radiate.chance;
|
||||
else if(cat == eMonstAbilCat::SUMMON)
|
||||
line >> abil.summon.chance;
|
||||
} else if(cur == "EXTRA") {
|
||||
if(key == eMonstAbil::DAMAGE)
|
||||
line >> abil.gen.dmg;
|
||||
else if(key == eMonstAbil::FIELD)
|
||||
line >> abil.gen.fld;
|
||||
else if(key == eMonstAbil::STATUS || key == eMonstAbil::STATUS2 || key == eMonstAbil::STUN)
|
||||
line >> abil.gen.stat;
|
||||
else if(cat == eMonstAbilCat::SPECIAL)
|
||||
line >> abil.special.extra1 >> abil.special.extra2;
|
||||
} else if(cur == "HOWMANY") {
|
||||
if(cat == eMonstAbilCat::SUMMON)
|
||||
line >> abil.summon.min >> abil.summon.max;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cCreature::writeTo(std::ostream& file) const {
|
||||
|
||||
@@ -26,76 +26,31 @@ namespace legacy {
|
||||
class cScenario;
|
||||
class cUniverse;
|
||||
|
||||
/* Attack Types */
|
||||
enum class eMonstAbilTemplate {
|
||||
// Non-magical missiles
|
||||
THROWS_DARTS, SHOOTS_ARROWS, THROWS_SPEARS, THROWS_ROCKS1, THROWS_ROCKS2, THROWS_ROCKS3,
|
||||
THROWS_RAZORDISKS, THROWS_KNIVES, GOOD_ARCHER, SHOOTS_SPINES, CROSSBOWMAN, SLINGER,
|
||||
// Magical missiles
|
||||
RAY_PETRIFY, RAY_SP_DRAIN, RAY_HEAT, RAY_PARALYSIS,
|
||||
BREATH_FIRE, BREATH_FROST, BREATH_ELECTRICITY, BREATH_DARKNESS, BREATH_FOUL, BREATH_SLEEP,
|
||||
SPIT_ACID, SHOOTS_WEB,
|
||||
// Touch abilities
|
||||
TOUCH_POISON, TOUCH_ACID, TOUCH_DISEASE, TOUCH_WEB, TOUCH_SLEEP, TOUCH_DUMB, TOUCH_PARALYSIS,
|
||||
TOUCH_PETRIFY, TOUCH_DEATH, TOUCH_XP_DRAIN, TOUCH_ICY, TOUCH_ICY_DRAINING, TOUCH_STUN, TOUCH_STEAL_FOOD, TOUCH_STEAL_GOLD,
|
||||
// Misc abilities
|
||||
SPLITS, MARTYRS_SHIELD, ABSORB_SPELLS, SUMMON_5, SUMMON_20, SUMMON_50, SPECIAL, DEATH_TRIGGERS,
|
||||
// Radiate abilities
|
||||
RADIATE_FIRE, RADIATE_ICE, RADIATE_SHOCK, RADIATE_ANTIMAGIC, RADIATE_SLEEP, RADIATE_STINK, RADIATE_BLADE, RADIATE_WEB,
|
||||
// Advanced abilities
|
||||
CUSTOM_MISSILE, CUSTOM_DAMAGE, CUSTOM_STATUS, CUSTOM_FIELD, CUSTOM_PETRIFY, CUSTOM_SP_DRAIN, CUSTOM_XP_DRAIN,
|
||||
CUSTOM_KILL, CUSTOM_STEAL_FOOD, CUSTOM_STEAL_GOLD, CUSTOM_STUN, CUSTOM_STATUS2, CUSTOM_RADIATE, CUSTOM_SUMMON,
|
||||
};
|
||||
|
||||
#define MONSTER_ATTACK_SWINGS 0
|
||||
#define MONSTER_ATTACK_CLAWS 1
|
||||
#define MONSTER_ATTACK_BITES 2
|
||||
#define MONSTER_ATTACK_SLIMES 3
|
||||
#define MONSTER_ATTACK_PUNCHES 4
|
||||
#define MONSTER_ATTACK_STINGS 5
|
||||
#define MONSTER_ATTACK_CLUBS 6
|
||||
#define MONSTER_ATTACK_BURNS 7
|
||||
#define MONSTER_ATTACK_HARMS 8
|
||||
#define MONSTER_ATTACK_STABS 9
|
||||
enum class eMonstMelee {SWING, CLAW, BITE, SLIME, PUNCH, STING, CLUB, BURN, HARM, STAB};
|
||||
|
||||
enum class eMonstMissile {DART, ARROW, SPEAR, ROCK, RAZORDISK, SPINE, KNIFE, BOLT};
|
||||
|
||||
#define MONSTER_NO_SPECIAL_ABILITY 0
|
||||
#define MONSTER_THROWS_DARTS 1 //1-6
|
||||
#define MONSTER_SHOOTS_ARROWS 2 //2-12
|
||||
#define MONSTER_THROWS_SPEARS 3 //3-18
|
||||
#define MONSTER_THROWS_ROCKS1 4 //4-24 damages
|
||||
#define MONSTER_THROWS_ROCKS2 5 //5-30 damages
|
||||
#define MONSTER_THROWS_ROCKS3 6 //6-36 damages
|
||||
#define MONSTER_THROWS_RAZORDISKS 7 //4-24
|
||||
#define MONSTER_PETRIFICATION_RAY 8
|
||||
#define MONSTER_SP_DRAIN_RAY 9 //spell points drain ray
|
||||
#define MONSTER_HEAT_RAY 10
|
||||
#define MONSTER_INVISIBLE 11
|
||||
#define MONSTER_SPLITS 12
|
||||
#define MONSTER_MINDLESS 13
|
||||
#define MONSTER_BREATHES_STINKING_CLOUDS 14
|
||||
#define MONSTER_ICY_TOUCH 15
|
||||
#define MONSTER_XP_DRAINING_TOUCH 16
|
||||
#define MONSTER_ICY_AND_DRAINING_TOUCH 17
|
||||
#define MONSTER_SLOWING_TOUCH 18
|
||||
#define MONSTER_SHOOTS_WEB 19
|
||||
#define MONSTER_GOOD_ARCHER 20 //7-42
|
||||
#define MONSTER_STEALS_FOOD 21
|
||||
#define MONSTER_PERMANENT_MARTYRS_SHIELD 22
|
||||
#define MONSTER_PARALYSIS_RAY 23
|
||||
#define MONSTER_DUMBFOUNDING_TOUCH 24
|
||||
#define MONSTER_DISEASE_TOUCH 25
|
||||
#define MONSTER_ABSORB_SPELLS 26
|
||||
#define MONSTER_WEB_TOUCH 27
|
||||
#define MONSTER_SLEEP_TOUCH 28
|
||||
#define MONSTER_PARALYSIS_TOUCH 29
|
||||
#define MONSTER_PETRIFICATION_TOUCH 30
|
||||
#define MONSTER_ACID_TOUCH 31
|
||||
#define MONSTER_BREATHES_SLEEP_CLOUDS 32
|
||||
#define MONSTER_ACID_SPIT 33
|
||||
#define MONSTER_SHOOTS_SPINES 34 //7-42
|
||||
#define MONSTER_DEATH_TOUCH 35
|
||||
#define MONSTER_INVULNERABILITY 36
|
||||
#define MONSTER_GUARD 37
|
||||
|
||||
/* Create Monsters/Fields */
|
||||
|
||||
#define MONSTER_NO_RADIATE 0
|
||||
#define MONSTER_RADIATE_FIRE_FIELDS 1
|
||||
#define MONSTER_RADIATE_ICE_FIELDS 2
|
||||
#define MONSTER_RADIATE_SHOCK_FIELDS 3
|
||||
#define MONSTER_RADIATE_ANTIMAGIC_FIELDS 4
|
||||
#define MONSTER_RADIATE_SLEEP_FIELDS 5
|
||||
#define MONSTER_RADIATE_STINKING_CLOUDS 6
|
||||
#define MONSTER_RADIATE_BLADE_FIELDS 7
|
||||
|
||||
#define MONSTER_SUMMON_5_PERCENT 10 //5 percent chance
|
||||
#define MONSTER_SUMMON_20_PERCENT 11 //20 percent chance
|
||||
#define MONSTER_SUMMON_50_PERCENT 12 //50 percent chance
|
||||
|
||||
#define MONSTER_SPECIAL_ACTION 14
|
||||
#define MONSTER_DEATH_TRIGGERS 15 //death triggers global special
|
||||
enum class eMonstGen {RAY, TOUCH, GAZE, BREATH, SPIT};
|
||||
|
||||
// Directions!
|
||||
enum eDirection {
|
||||
@@ -117,6 +72,42 @@ inline eDirection& operator++ (eDirection& me, int) {
|
||||
|
||||
enum eResistType {RESIST_NONE, RESIST_HALF, RESIST_ALL, RESIST_DOUBLE};
|
||||
|
||||
union uAbility {
|
||||
bool active;
|
||||
struct {
|
||||
bool active;
|
||||
eMonstMissile type;
|
||||
miss_num_t pic;
|
||||
int dice, sides, skill, range, odds;
|
||||
} missile;
|
||||
struct {
|
||||
bool active;
|
||||
eMonstGen type;
|
||||
miss_num_t pic;
|
||||
int strength, range, odds;
|
||||
union {
|
||||
eDamageType dmg;
|
||||
eStatus stat;
|
||||
eFieldType fld;
|
||||
};
|
||||
} gen;
|
||||
struct {
|
||||
bool active;
|
||||
mon_num_t type;
|
||||
int min, max, len, chance;
|
||||
} summon;
|
||||
struct {
|
||||
bool active;
|
||||
eFieldType type;
|
||||
int chance;
|
||||
} radiate;
|
||||
struct {
|
||||
bool active;
|
||||
int extra1, extra2;
|
||||
} special;
|
||||
std::string to_string(eMonstAbil myKey) const;
|
||||
};
|
||||
|
||||
class cMonster {
|
||||
public:
|
||||
struct cAttack{
|
||||
@@ -125,11 +116,6 @@ public:
|
||||
operator int() const;
|
||||
cAttack& operator=(int n);
|
||||
};
|
||||
struct cAbility{
|
||||
eMonstAbil abil;
|
||||
unsigned short extra1, extra2;
|
||||
operator std::string();
|
||||
};
|
||||
unsigned char level;
|
||||
std::string m_name;
|
||||
short m_health;
|
||||
@@ -140,20 +126,20 @@ public:
|
||||
unsigned char speed;
|
||||
unsigned char mu;
|
||||
unsigned char cl;
|
||||
unsigned char breath;
|
||||
unsigned char breath_type;
|
||||
unsigned char treasure;
|
||||
unsigned char spec_skill; // TODO: Delete in favour of cAbility
|
||||
unsigned char poison;
|
||||
std::map<eMonstAbil, uAbility> abil;
|
||||
item_num_t corpse_item;
|
||||
short corpse_item_chance;
|
||||
int magic_res : 2;
|
||||
int fire_res : 2;
|
||||
int cold_res : 2;
|
||||
int poison_res : 2;
|
||||
bool mindless : 1;
|
||||
bool invuln : 1;
|
||||
bool invisible : 1;
|
||||
bool guard : 1;
|
||||
char : 4;
|
||||
unsigned char x_width,y_width;
|
||||
unsigned char radiate_1; // TODO: Delete in favour of cAbility
|
||||
unsigned short radiate_2; // I THINK this is the extra field for the second ability TODO: Delete in favour of cAbility
|
||||
unsigned char default_attitude;
|
||||
unsigned char summon_type;
|
||||
pic_num_t default_facial_pic;
|
||||
@@ -161,13 +147,9 @@ public:
|
||||
str_num_t see_str1, see_str2;
|
||||
snd_num_t see_sound, ambient_sound; // ambient_sound has a chance of being played every move
|
||||
spec_num_t see_spec;
|
||||
private:
|
||||
cAbility abil1, abil2;
|
||||
public:
|
||||
|
||||
std::string getAbil1Name();
|
||||
std::string getAbil2Name();
|
||||
bool hasAbil(eMonstAbil what, unsigned short& x1, unsigned short& x2);
|
||||
void addAbil(eMonstAbilTemplate what, int param = 0);
|
||||
void append(legacy::monster_record_type& old);
|
||||
cMonster();
|
||||
void writeTo(std::ostream& file) const;
|
||||
@@ -214,7 +196,15 @@ std::ostream& operator << (std::ostream& out, eRace e);
|
||||
std::istream& operator >> (std::istream& in, eRace& e);
|
||||
std::ostream& operator << (std::ostream& out, eMonstAbil e);
|
||||
std::istream& operator >> (std::istream& in, eMonstAbil& e);
|
||||
std::ostream& operator << (std::ostream& out, eMonstMissile e);
|
||||
std::istream& operator >> (std::istream& in, eMonstMissile& e);
|
||||
std::ostream& operator << (std::ostream& out, eMonstGen e);
|
||||
std::istream& operator >> (std::istream& in, eMonstGen& e);
|
||||
std::ostream& operator << (std::ostream& out, eDirection e);
|
||||
std::istream& operator >> (std::istream& in, eDirection& e);
|
||||
std::ostream& operator << (std::ostream& out, eDamageType e);
|
||||
std::istream& operator >> (std::istream& in, eDamageType& e);
|
||||
std::ostream& operator << (std::ostream& out, eFieldType e);
|
||||
std::istream& operator >> (std::istream& in, eFieldType& e);
|
||||
std::ostream& operator<<(std::ostream& out, const cMonster::cAttack& att);
|
||||
#endif
|
||||
|
||||
@@ -134,54 +134,51 @@ inline bool isStatusNegative(eStatus stat) {
|
||||
|
||||
/* Special Ability a.k.a spec_skill */
|
||||
|
||||
enum eMonstAbil {
|
||||
MONST_NO_ABIL = 0,
|
||||
// Missile abilities (extra1 = number of sided dice; extra2 = number of sides)
|
||||
MONST_THROWS_DARTS = 10,
|
||||
MONST_SHOOTS_ARROWS,
|
||||
MONST_THROWS_SPEARS,
|
||||
MONST_THROWS_ROCKS,
|
||||
MONST_THROWS_RAZORDISKS,
|
||||
MONST_GOOD_ARCHER,
|
||||
MONST_SHOOTS_SPINES,
|
||||
MONST_THROWS_KNIVES,
|
||||
// Ray abilities (extra1 = type of damage / status where applicable)
|
||||
MONST_DAMAGE_RAY = 20,
|
||||
MONST_STATUS_RAY,
|
||||
MONST_PETRIFY_RAY,
|
||||
MONST_DRAIN_SP_RAY,
|
||||
MONST_DRAIN_XP_RAY,
|
||||
MONST_DRAIN_XP_DAMAGE_RAY,
|
||||
MONST_KILL_RAY,
|
||||
MONST_STEAL_FOOD_RAY,
|
||||
MONST_STEAL_GOLD_RAY,
|
||||
// Touch abilities (extra1 = type of damage / status where applicable)
|
||||
MONST_DAMAGE_TOUCH = 30,
|
||||
MONST_STATUS_TOUCH,
|
||||
MONST_PETRIFY_TOUCH,
|
||||
MONST_DRAIN_SP_TOUCH,
|
||||
MONST_DRAIN_XP_TOUCH,
|
||||
MONST_DRAIN_XP_DAMAGE_TOUCH,
|
||||
MONST_KILL_TOUCH,
|
||||
MONST_STEAL_FOOD_TOUCH,
|
||||
MONST_STEAL_GOLD_TOUCH,
|
||||
// Summon abilities (extra1 = which monster / type / species; extra2 = % chance)
|
||||
MONST_SUMMON_ONE = 40,
|
||||
MONST_SUMMON_TYPE,
|
||||
MONST_SUMMON_SPECIES,
|
||||
MONST_SUMMON_RANDOM,
|
||||
MONST_MASS_SUMMON,
|
||||
// Misc abilities (extra1 = field / special #; extra2 = % chance for radiate only)
|
||||
MONST_SPLITS = 50,
|
||||
MONST_FIELD_MISSILE,
|
||||
MONST_MARTYRS_SHIELD,
|
||||
MONST_ABSORB_SPELLS,
|
||||
MONST_INVULNERABLE,
|
||||
MONST_RADIATE,
|
||||
MONST_CALL_LOCAL_SPECIAL,
|
||||
MONST_CALL_GLOBAL_SPECIAL,
|
||||
enum class eMonstAbil {
|
||||
NO_ABIL,
|
||||
MISSILE,
|
||||
|
||||
DAMAGE,
|
||||
STATUS,
|
||||
FIELD,
|
||||
PETRIFY,
|
||||
DRAIN_SP,
|
||||
DRAIN_XP,
|
||||
KILL,
|
||||
STEAL_FOOD,
|
||||
STEAL_GOLD,
|
||||
STUN,
|
||||
STATUS2,
|
||||
|
||||
SPLITS,
|
||||
MARTYRS_SHIELD,
|
||||
ABSORB_SPELLS,
|
||||
SPECIAL,
|
||||
DEATH_TRIGGER,
|
||||
|
||||
RADIATE,
|
||||
SUMMON,
|
||||
};
|
||||
|
||||
enum class eMonstAbilCat {
|
||||
INVALID, MISSILE, GENERAL, SUMMON, RADIATE, SPECIAL
|
||||
};
|
||||
|
||||
inline eMonstAbilCat getMonstAbilCategory(eMonstAbil what) {
|
||||
if(what == eMonstAbil::NO_ABIL)
|
||||
return eMonstAbilCat::SPECIAL;
|
||||
if(what == eMonstAbil::MISSILE)
|
||||
return eMonstAbilCat::MISSILE;
|
||||
if(what >= eMonstAbil::DAMAGE && what <= eMonstAbil::STATUS2)
|
||||
return eMonstAbilCat::GENERAL;
|
||||
if(what >= eMonstAbil::SPLITS && what <= eMonstAbil::DEATH_TRIGGER)
|
||||
return eMonstAbilCat::SPECIAL;
|
||||
if(what == eMonstAbil::RADIATE)
|
||||
return eMonstAbilCat::RADIATE;
|
||||
if(what == eMonstAbil::SUMMON)
|
||||
return eMonstAbilCat::SUMMON;
|
||||
return eMonstAbilCat::INVALID;
|
||||
}
|
||||
|
||||
/* Terrains Special Properties : scenario.ter_types[i].special */ //complete
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ enum eSpellSelect {SELECT_NO, SELECT_ACTIVE, SELECT_ANY};
|
||||
// This one is meant for indexing a bit field
|
||||
enum eSpellWhen {WHEN_COMBAT = 1, WHEN_TOWN = 2, WHEN_OUTDOORS = 4};
|
||||
|
||||
enum eSpellPat {PAT_SINGLE, PAT_SQ, PAT_SMSQ, PAT_OPENSQ, PAT_RAD2, PAT_RAD3, PAT_PLUS, PAT_WALL};
|
||||
|
||||
class cSpell {
|
||||
static std::map<eSpell,cSpell> dictionary;
|
||||
friend const cSpell& operator*(eSpell spell_num);
|
||||
|
||||
Reference in New Issue
Block a user