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:
@@ -69,3 +69,7 @@ Magical
|
||||
Plant
|
||||
|
||||
Bird
|
||||
|
||||
Skeletal Undead
|
||||
|
||||
Goblin
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
|
@@ -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));
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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];
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user