Lots of monster stuff, particularly documenting monster abilities
- Monster radiate abilities can now specify a spell pattern to use - Fix some abilities being incorrectly shown in the editor's ability details dialog (for example, radiate abilities were shown as "Summon aid") - Add the recently-added monster missile types to the options offered in the editor - Fix chance of activating not being editable for the martyr's shield ability - Show the monster summoned for summoning abilities (finally!) - Show the subcategory for unusual abilities (one of active, passive death) - Fix the displayed percentage chance for summon abilities (the permille was shown instead) - Fix incorrect action point display in the editor for the two recently-added monster missiles - Fix crash when editing a missile ability - Fix abilities page not showing the newly-added or recently-edited ability after exiting the detail/option dialog - Fix pick monster button for summoning abilities being off by one
This commit is contained in:
@@ -2545,8 +2545,35 @@ void do_monster_turn() {
|
||||
|
||||
// Place fields for monsters that create them. Only done when monst sees foe
|
||||
if(target != 6 && can_see_light(cur_monst->cur_loc,targ_space,sight_obscurity) < 5) {
|
||||
if(cur_monst->abil[eMonstAbil::RADIATE].active && get_ran(1,1,100) < cur_monst->abil[eMonstAbil::RADIATE].radiate.chance)
|
||||
place_spell_pattern(square, cur_monst->cur_loc, cur_monst->abil[eMonstAbil::RADIATE].radiate.type, 7);
|
||||
if(cur_monst->abil[eMonstAbil::RADIATE].active && get_ran(1,1,100) < cur_monst->abil[eMonstAbil::RADIATE].radiate.chance) {
|
||||
switch(cur_monst->abil[eMonstAbil::RADIATE].radiate.pat) {
|
||||
case PAT_SINGLE:
|
||||
place_spell_pattern(single, cur_monst->cur_loc, cur_monst->abil[eMonstAbil::RADIATE].radiate.type, 7);
|
||||
break;
|
||||
case PAT_SMSQ:
|
||||
place_spell_pattern(small_square, cur_monst->cur_loc, cur_monst->abil[eMonstAbil::RADIATE].radiate.type, 7);
|
||||
break;
|
||||
case PAT_SQ:
|
||||
place_spell_pattern(square, cur_monst->cur_loc, cur_monst->abil[eMonstAbil::RADIATE].radiate.type, 7);
|
||||
break;
|
||||
case PAT_OPENSQ:
|
||||
place_spell_pattern(open_square, cur_monst->cur_loc, cur_monst->abil[eMonstAbil::RADIATE].radiate.type, 7);
|
||||
break;
|
||||
case PAT_RAD2:
|
||||
place_spell_pattern(radius2, cur_monst->cur_loc, cur_monst->abil[eMonstAbil::RADIATE].radiate.type, 7);
|
||||
break;
|
||||
case PAT_RAD3:
|
||||
place_spell_pattern(radius3, cur_monst->cur_loc, cur_monst->abil[eMonstAbil::RADIATE].radiate.type, 7);
|
||||
break;
|
||||
case PAT_PLUS:
|
||||
place_spell_pattern(t, cur_monst->cur_loc, cur_monst->abil[eMonstAbil::RADIATE].radiate.type, 7);
|
||||
break;
|
||||
case PAT_WALL:
|
||||
int dir = (cur_monst->direction + 6) % 8;
|
||||
place_spell_pattern(field[dir], cur_monst->cur_loc, cur_monst->abil[eMonstAbil::RADIATE].radiate.type, 7);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(cur_monst->abil[eMonstAbil::SUMMON].active && get_ran(1,1,100) < cur_monst->abil[eMonstAbil::SUMMON].summon.chance) {
|
||||
uAbility abil = cur_monst->abil[eMonstAbil::SUMMON];
|
||||
mon_num_t what_summon = 0;
|
||||
|
@@ -356,7 +356,10 @@ static void put_monst_info(cDialog& me, const cCreature& store_m) {
|
||||
if(i > 4) break; // TODO: Support showing more than just the first four abilities
|
||||
if(!abil.second.active) continue;
|
||||
std::string id = "abil" + std::to_string(i);
|
||||
me[id].setText(abil.second.to_string(abil.first));
|
||||
std::string name = abil.second.to_string(abil.first);
|
||||
if(abil.first == eMonstAbil::SUMMON && abil.second.summon.type == eMonstSummon::TYPE)
|
||||
name.replace(name.find("%s"), 2, univ.scenario.scen_monsters[abil.second.summon.what].m_name);
|
||||
me[id].setText(name);
|
||||
i++;
|
||||
}
|
||||
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "oldstructs.h"
|
||||
@@ -305,28 +306,28 @@ std::map<eMonstAbil,uAbility>::iterator cMonster::addAbil(eMonstAbilTemplate wha
|
||||
return abil.find(eMonstAbil::DEATH_TRIGGER);
|
||||
// Radiate abilities
|
||||
case eMonstAbilTemplate::RADIATE_FIRE:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_FIRE, param};
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_FIRE, param, PAT_SQ};
|
||||
return abil.find(eMonstAbil::RADIATE);
|
||||
case eMonstAbilTemplate::RADIATE_ICE:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_ICE, param};
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_ICE, param, PAT_SQ};
|
||||
return abil.find(eMonstAbil::RADIATE);
|
||||
case eMonstAbilTemplate::RADIATE_SHOCK:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_FORCE, param};
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_FORCE, param, PAT_SQ};
|
||||
return abil.find(eMonstAbil::RADIATE);
|
||||
case eMonstAbilTemplate::RADIATE_ANTIMAGIC:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::FIELD_ANTIMAGIC, param};
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::FIELD_ANTIMAGIC, param, PAT_SQ};
|
||||
return abil.find(eMonstAbil::RADIATE);
|
||||
case eMonstAbilTemplate::RADIATE_SLEEP:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::CLOUD_SLEEP, param};
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::CLOUD_SLEEP, param, PAT_SQ};
|
||||
return abil.find(eMonstAbil::RADIATE);
|
||||
case eMonstAbilTemplate::RADIATE_STINK:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::CLOUD_STINK, param};
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::CLOUD_STINK, param, PAT_SQ};
|
||||
return abil.find(eMonstAbil::RADIATE);
|
||||
case eMonstAbilTemplate::RADIATE_BLADE:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_BLADES, param};
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_BLADES, param, PAT_SQ};
|
||||
return abil.find(eMonstAbil::RADIATE);
|
||||
case eMonstAbilTemplate::RADIATE_WEB:
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::FIELD_WEB, param};
|
||||
abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::FIELD_WEB, param, PAT_SQ};
|
||||
return abil.find(eMonstAbil::RADIATE);
|
||||
// Advanced abilities
|
||||
case eMonstAbilTemplate::CUSTOM_MISSILE:
|
||||
@@ -772,7 +773,7 @@ std::string uAbility::to_string(eMonstAbil key) const {
|
||||
case eMonstAbil::DRAIN_XP: sout << "Draining"; 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::STEAL_GOLD: sout << "Steals gold"; break;
|
||||
case eMonstAbil::FIELD:
|
||||
switch(gen.fld) {
|
||||
case eFieldType::SPECIAL_EXPLORED:
|
||||
@@ -878,7 +879,7 @@ std::string uAbility::to_string(eMonstAbil key) const {
|
||||
case eMonstAbilCat::SPECIAL:
|
||||
switch(key) {
|
||||
case eMonstAbil::SPLITS:
|
||||
sout << "Splits when hit (" << special.extra1 << "% chance)";
|
||||
sout << "Splits when hit (" << std::fixed << std::setprecision(1) << double(special.extra1) / 10 << "% chance)";
|
||||
break;
|
||||
case eMonstAbil::MARTYRS_SHIELD:
|
||||
sout << "Permanent martyr's shield";
|
||||
@@ -893,9 +894,13 @@ std::string uAbility::to_string(eMonstAbil key) const {
|
||||
sout << "Heat ray (" << special.extra3 << "d6)";
|
||||
break;
|
||||
case eMonstAbil::SPECIAL:
|
||||
sout << "Unusual ability (active)";
|
||||
break;
|
||||
case eMonstAbil::DEATH_TRIGGER:
|
||||
sout << "Unusual ability (death)";
|
||||
break;
|
||||
case eMonstAbil::HIT_TRIGGER:
|
||||
sout << "Unusual ability";
|
||||
sout << "Unusual ability (passive)";
|
||||
break;
|
||||
// Non-special abilities
|
||||
case eMonstAbil::STUN: case eMonstAbil::NO_ABIL: case eMonstAbil::RADIATE: case eMonstAbil::SUMMON:
|
||||
@@ -906,8 +911,49 @@ std::string uAbility::to_string(eMonstAbil key) const {
|
||||
}
|
||||
break;
|
||||
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)";
|
||||
sout << "Summons " << summon.min << '-' << summon.max << ' ';
|
||||
switch(summon.type) {
|
||||
case eMonstSummon::TYPE:
|
||||
sout << "%s";
|
||||
break;
|
||||
case eMonstSummon::SPECIES:
|
||||
switch(eRace(summon.what)) {
|
||||
case eRace::BEAST: sout << "beasts"; break;
|
||||
case eRace::BIRD: sout << "birds"; break;
|
||||
case eRace::BUG: sout << "bugs"; break;
|
||||
case eRace::DEMON: sout << "demons"; break;
|
||||
case eRace::DRAGON: sout << "dragons"; break;
|
||||
case eRace::GIANT: sout << "giants"; break;
|
||||
case eRace::GOBLIN: sout << "goblins"; break;
|
||||
case eRace::HUMAN: sout << "humans"; break;
|
||||
case eRace::HUMANOID: sout << "humanoids"; break;
|
||||
case eRace::IMPORTANT: sout << "VIPs"; break;
|
||||
case eRace::MAGE: sout << "mages"; break;
|
||||
case eRace::MAGICAL: sout << "magical beings"; break;
|
||||
case eRace::NEPHIL: sout << "nephilim"; break;
|
||||
case eRace::PLANT: sout << "plants"; break;
|
||||
case eRace::PRIEST: sout << "priests"; break;
|
||||
case eRace::REPTILE: sout << "reptiles"; break;
|
||||
case eRace::SKELETAL: sout << "skeletal undead"; break;
|
||||
case eRace::SLIME: sout << "slimes"; break;
|
||||
case eRace::SLITH: sout << "sliths"; break;
|
||||
case eRace::STONE: sout << "mineral beings"; break;
|
||||
case eRace::UNDEAD: sout << "undead"; break;
|
||||
case eRace::UNKNOWN: sout << "monsters"; break;
|
||||
case eRace::VAHNATAI: sout << "vahnatai"; break;
|
||||
}
|
||||
break;
|
||||
case eMonstSummon::LEVEL:
|
||||
switch(summon.what) {
|
||||
case 0: sout << "cannon fodder"; break;
|
||||
case 1: sout << "minor allies"; break;
|
||||
case 2: sout << "allies"; break;
|
||||
case 3: sout << "major allies"; break;
|
||||
case 4: sout << "protectors"; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
sout << " (" << summon.chance << "% chance)";
|
||||
break;
|
||||
case eMonstAbilCat::RADIATE:
|
||||
sout << "Radiates ";
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include "simpletypes.h"
|
||||
#include "graphtool.hpp"
|
||||
#include "living.hpp"
|
||||
#include "spell.hpp"
|
||||
|
||||
namespace legacy {
|
||||
struct monster_record_type;
|
||||
@@ -90,6 +91,7 @@ union uAbility {
|
||||
bool active;
|
||||
eFieldType type;
|
||||
int chance;
|
||||
eSpellPat pat;
|
||||
} radiate;
|
||||
struct {
|
||||
bool active;
|
||||
|
@@ -874,7 +874,10 @@ static void put_monst_abils_in_dlog(cDialog& me, cMonster& monst) {
|
||||
me["abil-edit" + id].setText("Add");
|
||||
}
|
||||
} else if(i % 4 == 3) abils.addPage();
|
||||
me["abil-name" + id].setText(abil.second.to_string(abil.first));
|
||||
std::string name = abil.second.to_string(abil.first);
|
||||
if(abil.first == eMonstAbil::SUMMON && abil.second.summon.type == eMonstSummon::TYPE)
|
||||
name.replace(name.find("%s"), 2, scenario.scen_monsters[abil.second.summon.what].m_name);
|
||||
me["abil-name" + id].setText(name);
|
||||
me["abil-edit" + id].setText("Edit");
|
||||
i++;
|
||||
}
|
||||
@@ -963,12 +966,15 @@ static short get_monst_abil_num(std::string prompt, int min, int max, cDialog& p
|
||||
static void fill_monst_abil_detail(cDialog& me, cMonster& monst, eMonstAbil abil, uAbility detail) {
|
||||
eMonstAbilCat cat = getMonstAbilCategory(abil);
|
||||
me["monst"].setText(monst.m_name);
|
||||
std::string name = detail.to_string(abil);
|
||||
if(abil == eMonstAbil::SUMMON && detail.summon.type == eMonstSummon::TYPE)
|
||||
name.replace(name.find("%s"), 2, scenario.scen_monsters[detail.summon.what].m_name);
|
||||
me["name"].setText(detail.to_string(abil));
|
||||
// These names start at line 80 in the strings file, but the first valid ability is ID 1, so add 79.
|
||||
me["type"].setText(get_str("monster-abilities", 79 + int(abil)));
|
||||
// Action points
|
||||
if(cat == eMonstAbilCat::MISSILE) {
|
||||
if(detail.missile.type == eMonstMissile::ARROW || detail.missile.type == eMonstMissile::BOLT || detail.missile.type == eMonstMissile::SPINE || detail.missile.type == eMonstMissile::RAPID_ARROW)
|
||||
if(detail.missile.type == eMonstMissile::ARROW || detail.missile.type == eMonstMissile::BOLT || detail.missile.type == eMonstMissile::SPINE || detail.missile.type == eMonstMissile::BOULDER)
|
||||
me["ap"].setTextToNum(3);
|
||||
else me["ap"].setTextToNum(2);
|
||||
} else if(cat == eMonstAbilCat::GENERAL) {
|
||||
@@ -1005,9 +1011,11 @@ static void fill_monst_abil_detail(cDialog& me, cMonster& monst, eMonstAbil abil
|
||||
me["missile"].show();
|
||||
me["pick-missile"].show();
|
||||
me["missile-pic"].show();
|
||||
me["missile-touch"].hide();
|
||||
if(cat != eMonstAbilCat::MISSILE)
|
||||
me["missile-touch"].hide();
|
||||
me["range"].show();
|
||||
me["range-touch"].hide();
|
||||
if(cat != eMonstAbilCat::MISSILE)
|
||||
me["range-touch"].hide();
|
||||
}
|
||||
miss_num_t missile;
|
||||
int range;
|
||||
@@ -1036,6 +1044,8 @@ static void fill_monst_abil_detail(cDialog& me, cMonster& monst, eMonstAbil abil
|
||||
if(abil == eMonstAbil::FIELD)
|
||||
me["extra"].setTextToNum(int(detail.gen.fld));
|
||||
else me["field"].setTextToNum(int(detail.radiate.type));
|
||||
if(cat == eMonstAbilCat::RADIATE)
|
||||
me["pat"].setTextToNum(int(detail.radiate.pat));
|
||||
}
|
||||
// Other type-specific fields
|
||||
if(cat == eMonstAbilCat::MISSILE) {
|
||||
@@ -1101,6 +1111,7 @@ static void save_monst_abil_detail(cDialog& me, eMonstAbil abil, uAbility& detai
|
||||
detail.summon.chance = me["odds"].getTextAsNum();
|
||||
} else if(cat == eMonstAbilCat::RADIATE) {
|
||||
detail.radiate.chance = me["odds"].getTextAsNum();
|
||||
detail.radiate.pat = eSpellPat(me["pat"].getTextAsNum());
|
||||
} else if(cat == eMonstAbilCat::SPECIAL) {
|
||||
detail.special.extra1 = me["extra1"].getTextAsNum();
|
||||
detail.special.extra2 = me["extra2"].getTextAsNum();
|
||||
@@ -1167,10 +1178,10 @@ static bool edit_monst_abil_detail(cDialog& me, std::string hit, cMonster& monst
|
||||
return true;
|
||||
}
|
||||
}
|
||||
size_t iShow = std::distance(monst.abil.begin(), iter);
|
||||
dynamic_cast<cStack&>(me["abils"]).setPage(iShow / 4);
|
||||
if(tmpl < eMonstAbilTemplate::CUSTOM_MISSILE && tmpl != eMonstAbilTemplate::SPECIAL) {
|
||||
put_monst_abils_in_dlog(me, monst);
|
||||
size_t iShow = std::distance(monst.abil.begin(), iter);
|
||||
dynamic_cast<cStack&>(me["abils"]).setPage(iShow / 4);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@@ -1211,7 +1222,7 @@ static bool edit_monst_abil_detail(cDialog& me, std::string hit, cMonster& monst
|
||||
|
||||
if(cat == eMonstAbilCat::MISSILE || cat == eMonstAbilCat::GENERAL || cat == eMonstAbilCat::SUMMON) {
|
||||
int first, last;
|
||||
if(cat == eMonstAbilCat::MISSILE) first = 110, last = 117;
|
||||
if(cat == eMonstAbilCat::MISSILE) first = 110, last = 119;
|
||||
else if(cat == eMonstAbilCat::GENERAL) first = 120, last = 124;
|
||||
else if(cat == eMonstAbilCat::SUMMON) first = 150, last = 152;
|
||||
abil_dlg["pick-subtype"].attachClickHandler([&,cat,first,last](cDialog& me,std::string,eKeyMod) -> bool {
|
||||
@@ -1270,17 +1281,26 @@ static bool edit_monst_abil_detail(cDialog& me, std::string hit, cMonster& monst
|
||||
fill_monst_abil_detail(me, monst, abil, abil_params);
|
||||
return true;
|
||||
});
|
||||
abil_dlg["pick-pat"].attachClickHandler([&](cDialog& me,std::string,eKeyMod) -> bool {
|
||||
save_monst_abil_detail(me, abil, abil_params);
|
||||
int i = abil_params.radiate.pat;
|
||||
i = choose_text(STRT_SPELL_PAT, i, &me, "Which spell pattern?");
|
||||
abil_params.radiate.pat = eSpellPat(i);
|
||||
fill_monst_abil_detail(me, monst, abil, abil_params);
|
||||
return true;
|
||||
});
|
||||
} else if(cat == eMonstAbilCat::SUMMON) {
|
||||
abil_dlg["pick-summon"].attachClickHandler([&](cDialog& me,std::string,eKeyMod) -> bool {
|
||||
save_monst_abil_detail(me, abil, abil_params);
|
||||
int i = abil_params.summon.what;
|
||||
eStrType type;
|
||||
switch(abil_params.summon.type) {
|
||||
case eMonstSummon::TYPE: type = STRT_MONST; break;
|
||||
case eMonstSummon::TYPE: type = STRT_MONST; i--; break;
|
||||
case eMonstSummon::LEVEL: type = STRT_SUMMON; break;
|
||||
case eMonstSummon::SPECIES: type = STRT_RACE; break;
|
||||
}
|
||||
i = choose_text(type, i, &me, "Summon what?");
|
||||
if(type == STRT_MONST) i++;
|
||||
abil_params.summon.what = i;
|
||||
fill_monst_abil_detail(me, monst, abil, abil_params);
|
||||
return true;
|
||||
@@ -1312,6 +1332,8 @@ static bool edit_monst_abil_detail(cDialog& me, std::string hit, cMonster& monst
|
||||
if(abil_dlg.accepted())
|
||||
iter->second = abil_params;
|
||||
put_monst_abils_in_dlog(me, monst);
|
||||
size_t iShow = std::distance(monst.abil.begin(), iter);
|
||||
dynamic_cast<cStack&>(me["abils"]).setPage(iShow / 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user