Split monster immunities bitfield into more comprehensible chunks.

- Monsters now properly decide whether they can enter damaging terrains
- The possibility of being unusually vulnerable to a damage type is introduce - such monsters would take double damage from that type
This commit is contained in:
2015-01-13 12:33:04 -05:00
parent 12c87a85af
commit 2a41b68129
7 changed files with 197 additions and 93 deletions

View File

@@ -21,14 +21,14 @@
<text name='priest' framed='true' top='137' left='188' width='25' height='17'/>
<text name='poison' framed='true' top='137' left='280' width='25' height='17'/>
<text name='abil1' framed='true' top='165' left='67' width='238' height='16'/>
<led name='immune1' state='off' top='227' left='118'/>
<led name='immune2' state='off' top='242' left='118'/>
<led name='immune3' state='off' top='257' left='118'/>
<led name='immune4' state='off' top='272' left='118'/>
<led name='immune5' state='off' top='227' left='241'/>
<led name='immune6' state='off' top='242' left='241'/>
<led name='immune7' state='off' top='257' left='241'/>
<led name='immune8' state='off' top='272' left='241'/>
<led name='magic-res' state='off' top='227' left='118'/>
<led name='magic-imm' state='off' top='242' left='118'/>
<led name='fire-res' state='off' top='257' left='118'/>
<led name='fire-imm' state='off' top='272' left='118'/>
<led name='cold-res' state='off' top='227' left='241'/>
<led name='cold-imm' state='off' top='242' left='241'/>
<led name='poison-res' state='off' top='257' left='241'/>
<led name='poison-imm' state='off' top='272' left='241'/>
<button name='left' type='left' def-key='left' top='319' left='11'/>
<button name='right' type='right' def-key='right' top='319' left='74'/>
<text top='288' left='10' width='305' height='28'>(To make monsters appear in roster menu, cast Scry Monster on them.)</text>

View File

@@ -410,17 +410,15 @@ static void put_monst_info(cDialog& me, const cCreature& store_m) {
me["mage"].setTextToNum(store_m.mu);
me["priest"].setTextToNum(store_m.cl);
me["poison"].setTextToNum(store_m.poison);
// 2140 - lit 2141 - dark
// immunities
for(i = 0; i < 8; i++) {
std::string id = "immune" + boost::lexical_cast<std::string>(i + 1);
cLed& led = dynamic_cast<cLed&>(me[id]);
// TODO: What's this s_pow nonsense? Isn't this just 1 << i? Also, why not make it a C++ bitfield?
if(store_m.immunities & (char)(s_pow(2,i)))
led.setState(led_red);
else led.setState(led_off);
}
// 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);
dynamic_cast<cLed&>(me["fire-res"]).setState(store_m.fire_res == RESIST_HALF ? led_red : led_off);
dynamic_cast<cLed&>(me["fire-imm"]).setState(store_m.fire_res == RESIST_ALL ? led_red : led_off);
dynamic_cast<cLed&>(me["cold-res"]).setState(store_m.cold_res == RESIST_HALF ? led_red : led_off);
dynamic_cast<cLed&>(me["cold-imm"]).setState(store_m.cold_res == RESIST_ALL ? led_red : led_off);
dynamic_cast<cLed&>(me["poison-res"]).setState(store_m.poison_res == RESIST_HALF ? led_red : led_off);
dynamic_cast<cLed&>(me["poison-imm"]).setState(store_m.poison_res == RESIST_ALL ? led_red : led_off);
}

View File

@@ -280,20 +280,50 @@ bool monst_hate_spot(short which_m,location *good_loc) {
location prospect,loc;
loc = univ.town.monst[which_m].cur_loc;
if(univ.town.is_fire_barr(loc.x,loc.y) || univ.town.is_force_barr(loc.x,loc.y) || univ.town.is_quickfire(loc.x,loc.y)
|| univ.town.is_blade_wall(loc.x,loc.y) // hate regular fields
|| (univ.town.is_ice_wall(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 2)
&& ((univ.town.monst[which_m].immunities & 32) == 0)) // hate ice wall?
|| (univ.town.is_fire_wall(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 1)
&& ((univ.town.monst[which_m].immunities & 8) == 0)) // hate fire wall?
|| (univ.town.is_scloud(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 6)
&& ((univ.town.monst[which_m].immunities & 3) == 0)) // hate stink cloud?
|| (univ.town.is_sleep_cloud(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 5)
&& ((univ.town.monst[which_m].immunities & 3) == 0)) // hate sleep cloud?
|| (univ.town.is_force_wall(loc.x,loc.y) && (univ.town.monst[which_m].radiate_1 != 3)
&& ((univ.town.monst[which_m].immunities & 3) == 0)) // hate shock cloud?
|| (((univ.town.monst[which_m].mu > 0) || (univ.town.monst[which_m].cl > 0))
&& univ.town.is_antimagic(loc.x,loc.y))) { // hate antimagic
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;
// Hate regular fields
else if(univ.town.is_quickfire(loc.x,loc.y) || univ.town.is_blade_wall(loc.x,loc.y)) hate_spot = true;
// 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 == 2) 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 == 1) 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 == 3) 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 == 6) 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 == 5) 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 antimagic?
else if(univ.town.is_antimagic(loc.x,loc.y)) {
if(univ.town.monst[which_m].mu > 0 || univ.town.monst[which_m].cl > 0)
hate_spot = true;
}
if(hate_spot) {
prospect = find_clear_spot(loc,1);
if(prospect.x > 0) {
*good_loc = prospect;
@@ -1006,10 +1036,18 @@ bool monst_check_special_terrain(location where_check,short mode,short which_mon
break;
case eTerSpec::DAMAGING: // TODO: Update this to check other cases
if(eDamageType(ter_flag) == eDamageType::FIRE && univ.town.monst[which_monst].immunities & 8)
return true;
else return false;
break;
switch(eDamageType(ter_flag)) {
case eDamageType::FIRE:
return univ.town.monst[which_monst].fire_res == RESIST_ALL;
case eDamageType::COLD:
return univ.town.monst[which_monst].cold_res == RESIST_ALL;
case eDamageType::MAGIC:
return univ.town.monst[which_monst].magic_res == RESIST_ALL;
case eDamageType::POISON:
return univ.town.monst[which_monst].poison_res == RESIST_ALL;
default:
return univ.town.monst[which_monst].spec_skill == MONSTER_INVULNERABILITY;
}
// TODO: Should it check any other terrain specials?
}
@@ -1051,18 +1089,30 @@ void magic_adjust(cCreature *which_m,short *how_much) {
which_m->health = 32767;
else which_m->health += 3;
}
if(which_m->immunities & 1)
*how_much = *how_much / 2;
if(which_m->immunities & 2)
*how_much = 0;
switch(which_m->magic_res) {
case RESIST_HALF:
*how_much /= 2;
break;
case RESIST_ALL:
*how_much = 0;
break;
case RESIST_DOUBLE:
*how_much *= 2;
break;
}
}
void poison_monst(cCreature *which_m,short how_much) {
if(which_m->immunities & 64)
how_much = how_much / 2;
if(which_m->immunities & 128) {
monst_spell_note(which_m->number,10);
return;
switch(which_m->poison_res) {
case RESIST_HALF:
how_much /= 2;
break;
case RESIST_ALL:
monst_spell_note(which_m->number,10);
return;
case RESIST_DOUBLE:
how_much *= 2;
break;
}
which_m->status[eStatus::POISON] = min(8, which_m->status[eStatus::POISON] + how_much);
if(how_much >= 0)
@@ -1147,10 +1197,17 @@ void charm_monst(cCreature *which_m,short penalty,eStatus which_status,short amo
which_m->m_type == eRace::STONE || which_m->m_type == eRace::PLANT))
return;
r1 = get_ran(1,1,100);
if(which_m->immunities & 1)
r1 = r1 * 2;
if(which_m->immunities & 2)
r1 = 200;
switch(which_m->magic_res) {
case RESIST_HALF:
r1 *= 2;
break;
case RESIST_ALL:
r1 = 200;
break;
case RESIST_DOUBLE:
r1 /= 2;
break;
}
r1 += penalty;
if(which_status == eStatus::ASLEEP)
r1 -= 25;

View File

@@ -1391,8 +1391,6 @@ bool damage_monst(short which_m, short who_hit, short how_much, short how_much_s
short r1,which_spot;
location where_put;
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;
@@ -1408,33 +1406,59 @@ bool damage_monst(short which_m, short who_hit, short how_much, short how_much_s
sound_type = 11;
}
victim = &univ.town.monst[which_m];
resist = victim->immunities;
if(dam_type == eDamageType::MAGIC) {
if(resist & 1)
how_much = how_much / 2;
if(resist & 2)
how_much = 0;
switch(victim->magic_res) {
case RESIST_HALF:
how_much /= 2;
break;
case RESIST_ALL:
how_much = 0;
break;
case RESIST_DOUBLE:
how_much *= 2;
break;
}
}
if(dam_type == eDamageType::FIRE) {
if(resist & 4)
how_much = how_much / 2;
if(resist & 8)
how_much = 0;
switch(victim->fire_res) {
case RESIST_HALF:
how_much /= 2;
break;
case RESIST_ALL:
how_much = 0;
break;
case RESIST_DOUBLE:
how_much *= 2;
break;
}
}
if(dam_type == eDamageType::COLD) {
if(resist & 16)
how_much = how_much / 2;
if(resist & 32)
how_much = 0;
switch(victim->cold_res) {
case RESIST_HALF:
how_much /= 2;
break;
case RESIST_ALL:
how_much = 0;
break;
case RESIST_DOUBLE:
how_much *= 2;
break;
}
}
if(dam_type == eDamageType::POISON) {
if(resist & 64)
how_much = how_much / 2;
if(resist & 128)
how_much = 0;
switch(victim->poison_res) {
case RESIST_HALF:
how_much /= 2;
break;
case RESIST_ALL:
how_much = 0;
break;
case RESIST_DOUBLE:
how_much *= 2;
break;
}
}
// Absorb damage?
@@ -1576,7 +1600,7 @@ void petrify_monst(cCreature* m_target, short strength) {
r1 += m_target->status[eStatus::BLESS_CURSE];
r1 -= strength;
if(r1 > 14 || m_target->immunities & 2)
if(r1 > 14 || m_target->magic_res == RESIST_ALL)
monst_spell_note(m_target->number,10);
else {
monst_spell_note(m_target->number,8);

View File

@@ -39,7 +39,22 @@ void cMonster::append(legacy::monster_record_type& old){
poison = old.poison;
corpse_item = old.corpse_item;
corpse_item_chance = old.corpse_item_chance;
immunities = old.immunities;
if(old.immunities & 2)
magic_res = RESIST_ALL;
else if(old.immunities & 1)
magic_res = RESIST_HALF;
if(old.immunities & 8)
fire_res = RESIST_ALL;
else if(old.immunities & 4)
fire_res = RESIST_HALF;
if(old.immunities & 32)
cold_res = RESIST_ALL;
else if(old.immunities & 16)
cold_res = RESIST_HALF;
if(old.immunities & 128)
poison_res = RESIST_ALL;
else if(old.immunities & 64)
poison_res = RESIST_HALF;
x_width = old.x_width;
y_width = old.y_width;
radiate_1 = old.radiate_1;
@@ -488,7 +503,7 @@ void cMonster::writeTo(std::ostream& file) const {
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 " << int(immunities) << '\n';
file << "IMMUNE " << magic_res << '\t' << fire_res << '\t' << cold_res << '\t' << poison_res << '\n';
file << "SIZE " << int(x_width) << ' ' << int(y_width) << '\n';
file << "ATTITUDE " << int(default_attitude) << '\n';
file << "SUMMON " << int(summon_type) << '\n';
@@ -530,10 +545,17 @@ void cMonster::readFrom(std::istream& file) {
spec_skill = temp1;
else if(which == 2)
radiate_1 = temp1, radiate_2 = temp2;
} else if(cur == "SIZE") {
} else if(cur == "SIZE") {
line >> temp1 >> temp2;
x_width = temp1;
y_width = temp2;
} else if(cur == "IMMUNE") {
line >> temp1 >> temp2;
magic_res = temp1;
fire_res = temp2;
line >> temp1 >> temp2;
cold_res = temp1;
poison_res = temp2;
} else if(cur == "RACE")
line >> m_type;
else if(cur == "CORPSEITEM")
@@ -564,8 +586,6 @@ void cMonster::readFrom(std::istream& file) {
treasure = temp1;
else if(cur == "POISON")
poison = temp1;
else if(cur == "IMMUNITIES")
immunities = temp1;
else if(cur == "ATTITUDE")
default_attitude = temp1;
else if(cur == "SUMMON")

View File

@@ -113,6 +113,8 @@ inline eDirection& operator++ (eDirection& me, int) {
else return me = (eDirection)(1 + (int)me);
}
enum eResistType {RESIST_NONE, RESIST_HALF, RESIST_ALL, RESIST_DOUBLE};
class cMonster {
public:
struct cAttack{
@@ -143,7 +145,10 @@ public:
unsigned char poison;
item_num_t corpse_item;
short corpse_item_chance;
unsigned char immunities;
int magic_res : 2;
int fire_res : 2;
int cold_res : 2;
int poison_res : 2;
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

View File

@@ -573,11 +573,6 @@ short edit_monst_type(short which_monst) {
return 0;
}
static const std::string resist_field_names[8] = {
"magic-res", "fire-res", "cold-res", "poison-res",
"magic-imm", "fire-imm", "cold-imm", "poison-imm",
};
static void put_monst_abils_in_dlog(cDialog& me, cMonster& store_monst, short which_monst) {
me["num"].setTextToNum(which_monst);
@@ -609,12 +604,14 @@ static void put_monst_abils_in_dlog(cDialog& me, cMonster& store_monst, short wh
dynamic_cast<cLedGroup&>(me["summon"]).setSelected("s" + boost::lexical_cast<std::string,short>(store_monst.summon_type));
for(short i = 0; i < 8; i++) {
cLed& resistLed = dynamic_cast<cLed&>(me[resist_field_names[i]]);
if(store_monst.immunities & (1 << i))
resistLed.setState(led_red);
else resistLed.setState(led_off);
}
dynamic_cast<cLed&>(me["magic-res"]).setState(store_monst.magic_res == RESIST_HALF ? led_red : led_off);
dynamic_cast<cLed&>(me["magic-imm"]).setState(store_monst.magic_res == RESIST_ALL ? led_red : led_off);
dynamic_cast<cLed&>(me["fire-res"]).setState(store_monst.fire_res == RESIST_HALF ? led_red : led_off);
dynamic_cast<cLed&>(me["fire-imm"]).setState(store_monst.fire_res == RESIST_ALL ? led_red : led_off);
dynamic_cast<cLed&>(me["cold-res"]).setState(store_monst.cold_res == RESIST_HALF ? led_red : led_off);
dynamic_cast<cLed&>(me["cold-imm"]).setState(store_monst.cold_res == RESIST_ALL ? led_red : led_off);
dynamic_cast<cLed&>(me["poison-res"]).setState(store_monst.poison_res == RESIST_HALF ? led_red : led_off);
dynamic_cast<cLed&>(me["poison-imm"]).setState(store_monst.poison_res == RESIST_ALL ? led_red : led_off);
}
static bool save_monst_abils(cDialog& me, cMonster& store_monst) {
@@ -634,11 +631,14 @@ static bool save_monst_abils(cDialog& me, cMonster& store_monst) {
store_monst.corpse_item = me["loot-item"].getTextAsNum();
store_monst.corpse_item_chance = me["loot-chance"].getTextAsNum();
store_monst.immunities = 0;
for(short i = 0; i < 8; i++) {
if(dynamic_cast<cLed&>(me[resist_field_names[i]]).getState() != led_off)
store_monst.immunities |= 1 << i;
}
if(dynamic_cast<cLed&>(me["magic-res"]).getState() != led_off) store_monst.magic_res = RESIST_HALF;
if(dynamic_cast<cLed&>(me["magic-imm"]).getState() != led_off) store_monst.magic_res = RESIST_ALL;
if(dynamic_cast<cLed&>(me["fire-res"]).getState() != led_off) store_monst.fire_res = RESIST_HALF;
if(dynamic_cast<cLed&>(me["fire-imm"]).getState() != led_off) store_monst.fire_res = RESIST_ALL;
if(dynamic_cast<cLed&>(me["cold-res"]).getState() != led_off) store_monst.cold_res = RESIST_HALF;
if(dynamic_cast<cLed&>(me["cold-imm"]).getState() != led_off) store_monst.cold_res = RESIST_ALL;
if(dynamic_cast<cLed&>(me["poison-res"]).getState() != led_off) store_monst.poison_res = RESIST_HALF;
if(dynamic_cast<cLed&>(me["poison-imm"]).getState() != led_off) store_monst.poison_res = RESIST_ALL;
return true;
}