Flesh out acid as a real damage type
This commit is contained in:
@@ -21,10 +21,9 @@ enum class eDamageType {
|
||||
COLD = 5,
|
||||
UNDEAD = 6,
|
||||
DEMON = 7,
|
||||
// Acid is treated as magic damage but needs a value to prevent it using a zap explosion
|
||||
ACID = -1,
|
||||
ACID = 8,
|
||||
// Keep these two last
|
||||
SPECIAL = 8, // Completely unblockable damage from assassination skill
|
||||
SPECIAL = 9, // Completely unblockable damage from assassination skill
|
||||
MARKED = 10,
|
||||
};
|
||||
|
||||
|
@@ -371,7 +371,7 @@ std::istream& operator >> (std::istream& in, eFieldType& e) {
|
||||
// MARK: eDamageType
|
||||
|
||||
cEnumLookup dmg_names = {
|
||||
"weap", "fire", "poison", "magic", "weird", "cold", "undead", "demon", "spec",
|
||||
"weap", "fire", "poison", "magic", "weird", "cold", "undead", "demon", "acid", "spec",
|
||||
};
|
||||
|
||||
std::ostream& operator << (std::ostream& out, eDamageType e) {
|
||||
|
@@ -118,7 +118,9 @@ std::map<std::string,std::vector<std::string>> feature_flags = {
|
||||
{"talk-go-back", {"StackV1"}},
|
||||
// Bugs required for several VoDT test replays to run faithfully
|
||||
{"empty-wandering-monster-bug", {"fixed"}},
|
||||
{"too-many-extra-wandering-monsters-bug", {"fixed"}}
|
||||
{"too-many-extra-wandering-monsters-bug", {"fixed"}},
|
||||
// Game balance
|
||||
{"magic-resistance", {"fixed"}} // Resist Magic used to not help with magic damage!
|
||||
};
|
||||
|
||||
struct cParseEntrance {
|
||||
|
@@ -2363,12 +2363,9 @@ short damage_pc(cPlayer& which_pc,short how_much,eDamageType damage_type,eRace t
|
||||
|
||||
int boom_type = boom_gr[damage_type];
|
||||
|
||||
// Acid doesn't actually have its own damage type in classic BoE
|
||||
if(damage_type == eDamageType::ACID)
|
||||
damage_type = eDamageType::MAGIC;
|
||||
|
||||
// armor
|
||||
if(damage_type == eDamageType::WEAPON || damage_type == eDamageType::UNDEAD || damage_type == eDamageType::DEMON) {
|
||||
static std::set<eDamageType> armor_resist_damage = { eDamageType::WEAPON, eDamageType::UNDEAD, eDamageType::DEMON };
|
||||
if(armor_resist_damage.count(damage_type)) {
|
||||
how_much -= minmax(-5,5,which_pc.status[eStatus::BLESS_CURSE]);
|
||||
for(short i = 0; i < cPlayer::INVENTORY_SIZE; i++) {
|
||||
const cItem& item = which_pc.items[i];
|
||||
@@ -2416,6 +2413,10 @@ short damage_pc(cPlayer& which_pc,short how_much,eDamageType damage_type,eRace t
|
||||
}
|
||||
|
||||
short prot_from_dmg = which_pc.get_prot_level(eItemAbil::DAMAGE_PROTECTION,int(damage_type));
|
||||
// Acid damage used to be magic damage, so magic protection counts as acid protection:
|
||||
if(damage_type == eDamageType::ACID){
|
||||
prot_from_dmg += which_pc.get_prot_level(eItemAbil::DAMAGE_PROTECTION,int(eDamageType::MAGIC));
|
||||
}
|
||||
if(prot_from_dmg > 0) {
|
||||
// TODO: Why does this not depend on the ability strength if it's not weapon damage?
|
||||
if(damage_type == eDamageType::WEAPON) how_much -= prot_from_dmg;
|
||||
@@ -2447,8 +2448,13 @@ short damage_pc(cPlayer& which_pc,short how_much,eDamageType damage_type,eRace t
|
||||
how_much = 0;
|
||||
|
||||
// Mag. res helps w. fire and cold
|
||||
// TODO: Why doesn't this help with magic damage!?
|
||||
if(damage_type == eDamageType::FIRE || damage_type == eDamageType::COLD) {
|
||||
static std::set<eDamageType> magic_resist_damage = { eDamageType::FIRE, eDamageType::COLD };
|
||||
// Now it also helps with MAGIC:
|
||||
if(has_feature_flag("magic-resistance", "fixed")){
|
||||
magic_resist_damage.insert(eDamageType::MAGIC);
|
||||
magic_resist_damage.insert(eDamageType::ACID);
|
||||
}
|
||||
if(magic_resist_damage.count(damage_type)) {
|
||||
int magic_res = which_pc.status[eStatus::MAGIC_RESISTANCE];
|
||||
if(magic_res > 0)
|
||||
how_much /= 2;
|
||||
@@ -2458,7 +2464,8 @@ short damage_pc(cPlayer& which_pc,short how_much,eDamageType damage_type,eRace t
|
||||
|
||||
// major resistance
|
||||
short full_prot = which_pc.get_prot_level(eItemAbil::FULL_PROTECTION);
|
||||
if((damage_type == eDamageType::FIRE || damage_type == eDamageType::POISON || damage_type == eDamageType::MAGIC || damage_type == eDamageType::COLD)
|
||||
std::set<eDamageType> major_resist_damage = { eDamageType::FIRE, eDamageType::POISON, eDamageType::MAGIC, eDamageType::ACID, eDamageType::COLD};
|
||||
if(major_resist_damage.count(damage_type)
|
||||
&& (full_prot > 0))
|
||||
how_much = how_much / ((full_prot >= 7) ? 4 : 2);
|
||||
|
||||
|
@@ -338,7 +338,7 @@ bool check_special_terrain(location where_check,eSpecCtx mode,cPlayer& which_pc,
|
||||
break;
|
||||
if(mode == eSpecCtx::OUT_MOVE && out_boat_there(where_check))
|
||||
break;
|
||||
if(ter_flag3 > 0 && ter_flag3 < 8)
|
||||
if(ter_flag3 > 0 && ter_flag3 < int(eDamageType::SPECIAL))
|
||||
dam_type = (eDamageType) ter_flag3;
|
||||
else dam_type = eDamageType::WEAPON;
|
||||
r1 = get_ran(ter_flag2,1,ter_flag1);
|
||||
@@ -356,6 +356,10 @@ bool check_special_terrain(location where_check,eSpecCtx mode,cPlayer& which_pc,
|
||||
add_string_to_buf(" You feel cold!");
|
||||
pic_type = 4;
|
||||
break;
|
||||
case eDamageType::ACID:
|
||||
add_string_to_buf(" It burns!");
|
||||
pic_type = 6;
|
||||
break;
|
||||
case eDamageType::SPECIAL:
|
||||
dam_type = eDamageType::UNBLOCKABLE;
|
||||
BOOST_FALLTHROUGH;
|
||||
@@ -1472,16 +1476,13 @@ short damage_monst(cCreature& victim, short who_hit, short how_much, eDamageType
|
||||
|
||||
int boom_type = boom_gr[dam_type];
|
||||
|
||||
// Acid doesn't actually have its own damage type in classic BoE
|
||||
if(dam_type == eDamageType::ACID)
|
||||
dam_type = eDamageType::MAGIC;
|
||||
|
||||
if(dam_type < eDamageType::SPECIAL) {
|
||||
how_much = percent(how_much, victim.resist[dam_type]);
|
||||
}
|
||||
|
||||
// Absorb damage?
|
||||
if((dam_type == eDamageType::FIRE || dam_type == eDamageType::MAGIC || dam_type == eDamageType::COLD)
|
||||
std::set<eDamageType> absorbable_damage = { eDamageType::FIRE, eDamageType::MAGIC, eDamageType::COLD, eDamageType::ACID };
|
||||
if(absorbable_damage.count(dam_type)
|
||||
&& victim.abil[eMonstAbil::ABSORB_SPELLS].active && get_ran(1,1,1000) <= victim.abil[eMonstAbil::ABSORB_SPELLS].special.extra1) {
|
||||
add_check_overflow(victim.health, how_much);
|
||||
ASB(" Magic absorbed.");
|
||||
@@ -1491,7 +1492,7 @@ short damage_monst(cCreature& victim, short who_hit, short how_much, eDamageType
|
||||
// Saving throw
|
||||
if((dam_type == eDamageType::FIRE || dam_type == eDamageType::COLD) && get_ran(1,0,20) <= victim.level)
|
||||
how_much /= 2;
|
||||
if(dam_type == eDamageType::MAGIC && (get_ran(1,0,24) <= victim.level))
|
||||
if((dam_type == eDamageType::MAGIC || dam_type == eDamageType::ACID) && (get_ran(1,0,24) <= victim.level))
|
||||
how_much /= 2;
|
||||
|
||||
// Invulnerable?
|
||||
@@ -1501,8 +1502,13 @@ short damage_monst(cCreature& victim, short who_hit, short how_much, eDamageType
|
||||
how_much /= 10;
|
||||
|
||||
// Mag. res helps w. fire and cold
|
||||
// TODO: Why doesn't this help with magic damage!?
|
||||
if(dam_type == eDamageType::FIRE || dam_type == eDamageType::COLD) {
|
||||
static std::set<eDamageType> magic_resist_damage = { eDamageType::FIRE, eDamageType::COLD };
|
||||
// Now it also helps with MAGIC:
|
||||
if(has_feature_flag("magic-resistance", "fixed")){
|
||||
magic_resist_damage.insert(eDamageType::MAGIC);
|
||||
magic_resist_damage.insert(eDamageType::ACID);
|
||||
}
|
||||
if(magic_resist_damage.count(dam_type)) {
|
||||
int magic_res = victim.status[eStatus::MAGIC_RESISTANCE];
|
||||
if(magic_res > 0)
|
||||
how_much /= 2;
|
||||
|
@@ -1037,6 +1037,7 @@ std::string cItem::getAbilName() const {
|
||||
case eDamageType::WEAPON: sout << "Enhanced"; break;
|
||||
case eDamageType::UNDEAD: sout << "Necrotic"; break;
|
||||
case eDamageType::DEMON: sout << "Unholy"; break;
|
||||
case eDamageType::ACID: sout << "Acid"; break;
|
||||
case eDamageType::SPECIAL:
|
||||
case eDamageType::UNBLOCKABLE: sout << "Dark"; break;
|
||||
case eDamageType::MARKED: break; // Invalid
|
||||
@@ -1079,6 +1080,7 @@ std::string cItem::getAbilName() const {
|
||||
case eDamageType::MAGIC: sout << "in sparks"; break;
|
||||
case eDamageType::POISON: sout << "into slime"; break;
|
||||
case eDamageType::WEAPON: sout << "in shrapnel"; break;
|
||||
case eDamageType::ACID: sout << "with acid"; break;
|
||||
case eDamageType::SPECIAL:
|
||||
case eDamageType::UNBLOCKABLE: sout << "in darkness"; break;
|
||||
case eDamageType::UNDEAD: sout.str("Implodes"); break;
|
||||
@@ -1117,6 +1119,7 @@ std::string cItem::getAbilName() const {
|
||||
case eDamageType::DEMON: sout << "Demon"; break;
|
||||
case eDamageType::UNDEAD: sout << "Undead"; break;
|
||||
case eDamageType::POISON: sout << "Poison"; break;
|
||||
case eDamageType::ACID: sout << "Acid"; break;
|
||||
case eDamageType::SPECIAL:
|
||||
case eDamageType::UNBLOCKABLE: sout << "Darkness"; break;
|
||||
case eDamageType::MARKED: break; // Invalid
|
||||
|
@@ -395,7 +395,7 @@ std::map<eMonstAbil,uAbility>::iterator cMonster::addAbil(eMonstAbilTemplate wha
|
||||
}
|
||||
|
||||
cMonster::cMonster(){
|
||||
for(int i = 0; i <= 8; i++) {
|
||||
for(int i = 0; i <= int(eDamageType::SPECIAL); i++) {
|
||||
eDamageType dmg = eDamageType(i);
|
||||
resist[dmg] = 100;
|
||||
}
|
||||
@@ -560,6 +560,7 @@ std::string uAbility::to_string(eMonstAbil key) const {
|
||||
case eDamageType::FIRE: sout << "Fiery"; break;
|
||||
case eDamageType::COLD: sout << "Icy"; break;
|
||||
case eDamageType::MAGIC: sout << "Shock"; break;
|
||||
case eDamageType::ACID: sout << "Acid"; break;
|
||||
case eDamageType::SPECIAL:
|
||||
case eDamageType::UNBLOCKABLE: sout << "Wounding"; break;
|
||||
case eDamageType::POISON: sout << "Pain"; break;
|
||||
|
@@ -595,7 +595,7 @@ void writeMonstersToXml(ticpp::Printer&& data, cScenario& scenario) {
|
||||
data.CloseElement("attacks");
|
||||
|
||||
data.OpenElement("immunity");
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for(int i = 0; i < int(eDamageType::SPECIAL); i++) {
|
||||
eDamageType dmg = eDamageType(i);
|
||||
if(monst.resist[dmg] != 100)
|
||||
data.PushElement(boost::lexical_cast<std::string>(dmg), monst.resist[dmg]);
|
||||
|
@@ -88,6 +88,7 @@ extern rectangle terrain_rects[256];
|
||||
unsigned char small_what_drawn[64][64];
|
||||
extern bool small_any_drawn;
|
||||
|
||||
// These are at the bottom of edbuttons.png:
|
||||
static short get_small_icon(ter_num_t ter){
|
||||
short icon = -1;
|
||||
switch(scenario.ter_types[ter].special){
|
||||
@@ -108,6 +109,9 @@ static short get_small_icon(ter_num_t ter){
|
||||
case eDamageType::POISON:
|
||||
icon = 17;
|
||||
break;
|
||||
case eDamageType::ACID:
|
||||
icon = 24; // green with black spots, doesn't seem to be used elsewhere
|
||||
break;
|
||||
case eDamageType::MAGIC:
|
||||
icon = 20;
|
||||
break;
|
||||
|
@@ -221,6 +221,7 @@ static void set_pattern(cTilemap& map, const effect_pat_type& pat) {
|
||||
case eDamageType::WEAPON: clr = Colours::MAROON; break;
|
||||
case eDamageType::FIRE: clr = Colours::RED; break;
|
||||
case eDamageType::POISON: clr = Colours::GREEN; break;
|
||||
case eDamageType::ACID: clr = Colours::LIGHT_GREEN; break; // Distinct enough from green?
|
||||
case eDamageType::MAGIC: clr = Colours::PURPLE; break;
|
||||
case eDamageType::UNBLOCKABLE: clr = Colours::LIGHT_BLUE; break;
|
||||
case eDamageType::COLD: clr = Colours::BLUE; break;
|
||||
@@ -975,8 +976,8 @@ short choose_field_type(short cur, cDialog* parent, bool includeSpec) {
|
||||
}
|
||||
|
||||
pic_num_t choose_damage_type(short cur, cDialog* parent, bool allow_spec) {
|
||||
static const char*const damageNames[] = {"Weapon", "Fire", "Poison", "Magic", "Weird", "Cold", "Undead", "Demon", "Unblockable"};
|
||||
static const std::vector<pic_num_t> pics = {3,0,2,1,5,4,3,3,1};
|
||||
static const char*const damageNames[] = {"Weapon", "Fire", "Poison", "Magic", "Weird", "Cold", "Undead", "Demon", "Acid", "Unblockable"};
|
||||
static const std::vector<pic_num_t> pics = {3,0,2,1,5,4,3,3,6,1};
|
||||
short prev = cur;
|
||||
if(cur < 0 || cur >= pics.size()) cur = 0;
|
||||
cPictChoice pic_dlg(pics.begin(), pics.end() - !allow_spec, PIC_BOOM, parent);
|
||||
|
@@ -68,7 +68,7 @@ TEST_CASE("Initialization sanity test for monster") {
|
||||
CHECK(monst.abil.empty());
|
||||
CHECK(monst.corpse_item == 0);
|
||||
CHECK(monst.corpse_item_chance == 0);
|
||||
CHECK(monst.resist.size() == 10);
|
||||
CHECK(monst.resist.size() == 11);
|
||||
CHECK(monst.resist[eDamageType::WEAPON] == 100);
|
||||
CHECK(monst.resist[eDamageType::FIRE] == 100);
|
||||
CHECK(monst.resist[eDamageType::POISON] == 100);
|
||||
@@ -77,6 +77,7 @@ TEST_CASE("Initialization sanity test for monster") {
|
||||
CHECK(monst.resist[eDamageType::UNBLOCKABLE] == 100);
|
||||
CHECK(monst.resist[eDamageType::UNDEAD] == 100);
|
||||
CHECK(monst.resist[eDamageType::DEMON] == 100);
|
||||
CHECK(monst.resist[eDamageType::ACID] == 100);
|
||||
CHECK(monst.resist[eDamageType::MARKED] == 100);
|
||||
CHECK_FALSE(monst.mindless);
|
||||
CHECK_FALSE(monst.invuln);
|
||||
|
@@ -132,7 +132,8 @@ TEST_CASE("Saving monster types") {
|
||||
scen.scen_monsters[1].resist[eDamageType::COLD] = 30;
|
||||
scen.scen_monsters[1].resist[eDamageType::UNDEAD] = 35;
|
||||
scen.scen_monsters[1].resist[eDamageType::DEMON] = 40;
|
||||
scen.scen_monsters[1].resist[eDamageType::SPECIAL] = 45;
|
||||
scen.scen_monsters[1].resist[eDamageType::ACID] = 45;
|
||||
scen.scen_monsters[1].resist[eDamageType::SPECIAL] = 50;
|
||||
in_and_out("resistance", scen);
|
||||
CHECK(scen.scen_monsters[1].resist[eDamageType::WEAPON] == 5);
|
||||
CHECK(scen.scen_monsters[1].resist[eDamageType::FIRE] == 10);
|
||||
@@ -142,6 +143,7 @@ TEST_CASE("Saving monster types") {
|
||||
CHECK(scen.scen_monsters[1].resist[eDamageType::COLD] == 30);
|
||||
CHECK(scen.scen_monsters[1].resist[eDamageType::UNDEAD] == 35);
|
||||
CHECK(scen.scen_monsters[1].resist[eDamageType::DEMON] == 40);
|
||||
CHECK(scen.scen_monsters[1].resist[eDamageType::ACID] == 45);
|
||||
// This one should not be saved, so we expect it to revert to default
|
||||
CHECK(scen.scen_monsters[1].resist[eDamageType::SPECIAL] == 100);
|
||||
}
|
||||
|
Reference in New Issue
Block a user