Add two new monster races to account for previously-hardcoded special cases

- Skeletal undead are distinguished from normal undead in that they leave sfx bones when they die; normal undead leave no sfx. In all other respects, they are the same.
- Goblins are distinguished from other humanoids in their death sound. In all other respects, they are the same.
- The sleep() function for players now checks for racial immunity - eg undead, plants, etc
- Protect form humanoids now also protects from sliths, nephilim, vahnatai, and goblins, as it should (this is legacy behaviour)
- Protect from undead still also protects from skeletal undead
- The special case for the ogre death sound is removed; that might mean that ogres can now have a femal death sound? I'm not quite sure what the purpose of the exception was.
- The if species node can now check for non-standard races in the party
- When loading a legacy scenario, it attempts to detect skeletal undead and goblins by the presence of the words "Skeleton" and "Goblin" in the name, respectively
This commit is contained in:
2015-01-28 17:04:01 -05:00
parent a23682306c
commit a4350ca8b2
9 changed files with 60 additions and 21 deletions

View File

@@ -69,3 +69,7 @@ Magical
Plant
Bird
Skeletal Undead
Goblin

View File

@@ -842,8 +842,10 @@ short calc_spec_dam(eItemAbil abil,short abil_str,short abil_dat,iLiving& monst,
else if(cPlayer* who = dynamic_cast<cPlayer*>(&monst))
race = who->race;
if(race == eRace::UNKNOWN) return 0;
// Slith, nephilim, and vahnatai are affected by humanoid-bane weapons as well as their individual banes
if(abil_dat == int(eRace::HUMANOID) && (race == eRace::SLITH || race == eRace::NEPHIL || race == eRace::VAHNATAI));
// Slith, nephilim, goblins, and vahnatai are affected by humanoid-bane weapons as well as their individual banes
if(abil_dat == int(eRace::HUMANOID) && isHumanoid(race) && !isHuman(race));
// Skeletal undead are also affected by normal undead-bane weapons
else if(abil_dat == int(eRace::UNDEAD) && race == eRace::SKELETAL);
else if(race != eRace(abil_dat))
return 0;
store += abil_str;
@@ -860,6 +862,7 @@ short calc_spec_dam(eItemAbil abil,short abil_str,short abil_dat,iLiving& monst,
store *= 8;
break;
case eRace::UNDEAD:
case eRace::SKELETAL:
store *= 6;
break;
case eRace::REPTILE:
@@ -881,6 +884,7 @@ short calc_spec_dam(eItemAbil abil,short abil_str,short abil_dat,iLiving& monst,
case eRace::NEPHIL:
case eRace::SLITH:
case eRace::VAHNATAI:
case eRace::GOBLIN:
case eRace::HUMANOID:
store *= 3;
break;
@@ -1400,7 +1404,7 @@ void do_combat_cast(location target) {
break;
case eSpell::TURN_UNDEAD: case eSpell::DISPEL_UNDEAD:
if(cur_monst->m_type != eRace::UNDEAD) {
if(cur_monst->m_type != eRace::UNDEAD && cur_monst->m_type != eRace::SKELETAL) {
add_string_to_buf(" Not undead.");
store_m_type = -1;
break;
@@ -2220,7 +2224,7 @@ void do_monster_turn() {
// flee
if((univ.town.monst[i].target != 6) && (((cur_monst->morale <= 0)
&& !cur_monst->mindless && cur_monst->m_type != eRace::UNDEAD)
&& !cur_monst->mindless && cur_monst->m_type != eRace::UNDEAD && cur_monst->m_type != eRace::SKELETAL)
|| (current_monst_tactic == 1))) {
if(cur_monst->morale < 0)
cur_monst->morale++;
@@ -2656,7 +2660,7 @@ void monster_attack(short who_att,iLiving* target) {
draw_terrain(2);
// Check if hit, and do effects
if(r1 <= hit_chance[(attacker->skill + 4) / 2]) {
if(attacker->m_type == eRace::UNDEAD)
if(attacker->m_type == eRace::UNDEAD || attacker->m_type == eRace::SKELETAL)
dam_type = eDamageType::UNDEAD;
if(attacker->m_type == eRace::DEMON)
dam_type = eDamageType::DEMON;

View File

@@ -219,7 +219,7 @@ void do_monsters() {
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].mindless
&& univ.town.monst[i].m_type != eRace::UNDEAD) {
&& univ.town.monst[i].m_type != eRace::UNDEAD && univ.town.monst[i].m_type != eRace::SKELETAL) {
acted_yet = flee_party(i,l1,l2);
if(get_ran(1,0,10) < 6)
univ.town.monst[i].morale++;
@@ -1159,7 +1159,7 @@ void cCreature::sleep(eStatus which_status,int amount,int penalty) {
}
if((which_status == eStatus::ASLEEP) &&
(m_type == eRace::UNDEAD || m_type == eRace::SLIME ||
(m_type == eRace::UNDEAD || m_type == eRace::SKELETAL || m_type == eRace::SLIME ||
m_type == eRace::STONE || m_type == eRace::PLANT))
return;
short r1 = get_ran(1,1,100);

View File

@@ -425,6 +425,10 @@ void cPlayer::sleep(eStatus what_type,int how_much,int adjust) {
if(!is_alive()) return;
if(how_much == 0) return;
if((what_type == eStatus::ASLEEP) &&
(race == eRace::UNDEAD || race == eRace::SKELETAL || race == eRace::SLIME ||
race == eRace::STONE || race == eRace::PLANT))
return;
if(what_type == eStatus::ASLEEP || what_type == eStatus::PARALYZED) {
how_much -= get_prot_level(eItemAbil::WILL) / 2;
level = get_prot_level(eItemAbil::FREE_ACTION);
@@ -2605,6 +2609,17 @@ bool damage_pc(short which_pc,short how_much,eDamageType damage_type,eRace type_
// TODO: Do these perhaps depend on the ability strength less than they should?
if((level = univ.party[which_pc].get_prot_level(eItemAbil::PROTECT_FROM_SPECIES,int(type_of_attacker))) > 0)
how_much = how_much / ((level >= 7) ? 4 : 2);
if(isHumanoid(type_of_attacker) && !isHuman(type_of_attacker) && type_of_attacker != eRace::HUMANOID) {
// If it's a slith, nephil, vahnatai, or goblin, Protection from Humanoids also helps
// Humanoid is explicitly excluded here because otherwise it would help twice.
if((level = univ.party[which_pc].get_prot_level(eItemAbil::PROTECT_FROM_SPECIES,int(eRace::HUMANOID))) > 0)
how_much = how_much / ((level >= 7) ? 4 : 2);
}
if(type_of_attacker == eRace::SKELETAL) {
// Protection from Undead helps with both types of undead
if((level = univ.party[which_pc].get_prot_level(eItemAbil::PROTECT_FROM_SPECIES,int(eRace::UNDEAD))) > 0)
how_much = how_much / ((level >= 7) ? 4 : 2);
}
// invuln
if(damage_type != eDamageType::SPECIAL && univ.party[which_pc].status[eStatus::INVULNERABLE] > 0)

View File

@@ -1603,19 +1603,16 @@ void kill_monst(cCreature *which_m,short who_killed,eMainStatus type) {
location l;
if(isHumanoid(which_m->m_type)) {
// TODO: Uh, don't hardcode these!
if(( which_m->number == 38) ||
( which_m->number == 39))
if(which_m->m_type == eRace::GOBLIN)
i = 4;
else if( which_m->number == 45)
i = 0;
else i = get_ran(1,0,1);
play_sound(29 + i);
} else switch(which_m->m_type) {
case eRace::GIANT: play_sound(29); break;
// TODO: Should birds be considered beasts? If there are any birds in the bladbase, probably; otherwise, better to have new sound
case eRace::REPTILE: case eRace::BEAST: case eRace::DEMON: case eRace::UNDEAD: case eRace::STONE:
case eRace::REPTILE: case eRace::BEAST: case eRace::DEMON: case eRace::UNDEAD: case eRace::SKELETAL: case eRace::STONE:
i = get_ran(1,0,1); play_sound(31 + i); break;
// TODO: I feel like dragons should have a different sound.
default: play_sound(33); break;
}
@@ -1655,9 +1652,10 @@ void kill_monst(cCreature *which_m,short who_killed,eMainStatus type) {
case eRace::DEMON:
univ.town.set_ash(i,j,true);
break;
// TODO: Don't check which_m->number here; find another way to indicate it
case eRace::UNDEAD:
if(which_m->number <= 59) univ.town.set_bones(i,j,true);
break;
case eRace::SKELETAL:
univ.town.set_bones(i,j,true);
break;
case eRace::SLIME: case eRace::PLANT: case eRace::BUG:
univ.town.set_sm_slime(i,j,true);
@@ -3212,9 +3210,9 @@ void ifthen_spec(eSpecCtx which_mode,cSpecial cur_node,short cur_spec_type,
*next_spec = spec.ex2b;
break;
case eSpecType::IF_SPECIES:
if(spec.ex1a < 0 || spec.ex1a > 2) {
giveError("Species out of range (0-human, 1-nephilim, 2-slith)");
break; // TODO: Should we allow monster races too?
if(spec.ex1a < 0 || spec.ex1a > 21) {
giveError("Species out of range (0-21)");
break;
}
i = 0;
j = min(spec.ex2a,party_size(true));

View File

@@ -478,6 +478,8 @@ std::ostream& operator << (std::ostream& out, eRace e){
case eRace::SLIME: out << "slime"; break;
case eRace::STONE: out << "stone"; break;
case eRace::UNDEAD: out << "undead"; break;
case eRace::SKELETAL: out << "skeletal"; break;
case eRace::GOBLIN: out << "goblin"; break;
case eRace::UNKNOWN: out << "humanoid"; break;
}
return out;
@@ -532,6 +534,10 @@ std::istream& operator >> (std::istream& in, eRace& e){
e = eRace::STONE;
else if(key == "undead")
e = eRace::UNDEAD;
else if(key == "skeletal")
e = eRace::SKELETAL;
else if(key == "goblin")
e = eRace::GOBLIN;
}
return in;
}

View File

@@ -166,8 +166,13 @@ void cScenario::append(legacy::scen_item_data_type& old){
short i;
for(i = 0; i < 400; i++)
scen_items[i].append(old.scen_items[i]);
for(i = 0; i < 256; i++)
for(i = 0; i < 256; i++) {
scen_monsters[i].m_name = old.monst_names[i];
if(scen_monsters[i].m_type == eRace::UNDEAD && scen_monsters[i].m_name.find("Skeleton") != std::string::npos)
scen_monsters[i].m_type = eRace::SKELETAL;
if(scen_monsters[i].m_type == eRace::HUMANOID && scen_monsters[i].m_name.find("Goblin") != std::string::npos)
scen_monsters[i].m_type = eRace::GOBLIN;
}
for(i = 0; i < 256; i++)
ter_types[i].name = old.ter_names[i];
}

View File

@@ -91,13 +91,20 @@ enum class eRace {
MAGICAL = 17, // 14
PLANT = 18,
BIRD = 19,
SKELETAL = 20,
GOBLIN = 21,
};
// Types IMPORTANT, MAGE, and PRIEST are implicitly human
inline bool isHuman(eRace race) {
int code = (int) race;
return code == 0 || (code >= 6 && code <= 8);
}
// Types NEPHIL, SLITH, and VAHNATAI are implicitly humanoid
inline bool isHumanoid(eRace race) {
int code = (int) race;
return (code >= 0 && code <= 3) || (code >= 6 && code <= 9);
return (code >= 0 && code <= 3) || (code >= 6 && code <= 9) || code == 21;
}
/* adven[i].status*/ //complete - assign a positive value for a help pc effect, a negative for harm pc

View File

@@ -246,7 +246,7 @@ short choose_text(eStrType list, unsigned short cur_choice, cDialog* parent, con
}
break;
case STRT_RACE:
for(int i = 0; i < 20; i++) {
for(int i = 0; i < 22; i++) {
strings.push_back(get_str("traits", i * 2 + 33));
}
break;