From 5450ae1caf04a4c4c11761483a34993102684fb3 Mon Sep 17 00:00:00 2001 From: Celtic Minstrel Date: Mon, 19 Jan 2015 12:01:28 -0500 Subject: [PATCH] Implement editing dialogs for individual monster abilities Changes to game: - Permanent martyr's shield is now customizable - extra1 is per-mille chance to activate each time damage is taken, and extra2 is percent of damage taken to apply to attacker - Absort spells is now customizable - extra1 is per-mille chance to activate each time damage is taken or a magic effect is applied, and extra2 is how much hp to gain from non-damaging magic effects - Splits ability is now customizable - extra1 is per-mille chance of splitting each time damage is taken - Special node ability is now called before the monster tries its normal attack, and multiple monsters can use them per round (though each can only use theirs once) - Fix issue with breath weapons and icy touch abilities conflicting (legacy import issue) - Add two variations of the summoning ability - summon according to summoning level (like the spells), or summon a random creature of a particular species - Fix touch abilities using the range as chance to use Changes to dialog engine: - Fix tab order handling to exclude fields that are currently hidden - Add method to retrieve the parameter passed to toast(), as a quick way to distinguish between the user clicking OK or Cancel after run() has exited - Fix hidden fields being drawn anyway - Fix setting stack page sometimes crashing if the current page was invalid due to earlier reducing the page count to 0 --- rsrc/dialogs/edit-mabil-general.xml | 42 +++ rsrc/dialogs/edit-mabil-missile.xml | 40 +++ rsrc/dialogs/edit-mabil-radiate.xml | 23 ++ rsrc/dialogs/edit-mabil-special.xml | 28 ++ rsrc/dialogs/edit-mabil-summon.xml | 34 +++ rsrc/dialogs/edit-monster-abils.xml | 3 +- rsrc/dialogs/get-mabil-num.xml | 8 + rsrc/strings/monster-abilities.txt | 227 ++++++++++------ src/boe.combat.cpp | 77 ++++-- src/boe.monster.cpp | 7 +- src/boe.specials.cpp | 4 +- src/classes/monster.cpp | 222 ++++++++------- src/classes/monster.h | 10 +- src/classes/simpletypes.h | 1 + src/classes/universe.cpp | 8 +- src/dialogxml/dialog.cpp | 16 +- src/dialogxml/dialog.hpp | 5 +- src/dialogxml/field.cpp | 1 + src/dialogxml/stack.cpp | 1 + src/scenedit/scen.core.cpp | 406 +++++++++++++++++++++++++++- src/scenedit/scen.core.h | 2 +- src/scenedit/scen.keydlgs.cpp | 11 +- src/scenedit/scen.keydlgs.h | 5 +- 23 files changed, 936 insertions(+), 245 deletions(-) create mode 100644 rsrc/dialogs/edit-mabil-general.xml create mode 100644 rsrc/dialogs/edit-mabil-missile.xml create mode 100644 rsrc/dialogs/edit-mabil-radiate.xml create mode 100644 rsrc/dialogs/edit-mabil-special.xml create mode 100644 rsrc/dialogs/edit-mabil-summon.xml create mode 100644 rsrc/dialogs/get-mabil-num.xml diff --git a/rsrc/dialogs/edit-mabil-general.xml b/rsrc/dialogs/edit-mabil-general.xml new file mode 100644 index 00000000..0541f3f6 --- /dev/null +++ b/rsrc/dialogs/edit-mabil-general.xml @@ -0,0 +1,42 @@ + + + + + Edit Monster Ability: + For monster: + + Display name: + + Ability Type: + + Action Point Cost: + + + Ability Subtype: + + + + Missile: + + None + + + + Ability Range: + + Touch + Chance of Using: + + + Ability Strength: + + + + Extra: + + + + + + + \ No newline at end of file diff --git a/rsrc/dialogs/edit-mabil-missile.xml b/rsrc/dialogs/edit-mabil-missile.xml new file mode 100644 index 00000000..29507a7b --- /dev/null +++ b/rsrc/dialogs/edit-mabil-missile.xml @@ -0,0 +1,40 @@ + + + + + Edit Monster Ability: + For monster: + + Display name: + + Ability Type: + + Action Point Cost: + + + Ability Subtype: + + + + Missile: + + + + + Number of Dice: + + Sides per Die: + + + Ability Range: + + Chance of Using: + + + Skill Level: + + + + + + \ No newline at end of file diff --git a/rsrc/dialogs/edit-mabil-radiate.xml b/rsrc/dialogs/edit-mabil-radiate.xml new file mode 100644 index 00000000..5a64c817 --- /dev/null +++ b/rsrc/dialogs/edit-mabil-radiate.xml @@ -0,0 +1,23 @@ + + + + + Edit Monster Ability: + For monster: + + Display name: + + Ability Type: + + + Field Type: + + + + Radiate Chance: + + + + + + \ No newline at end of file diff --git a/rsrc/dialogs/edit-mabil-special.xml b/rsrc/dialogs/edit-mabil-special.xml new file mode 100644 index 00000000..8ec6f673 --- /dev/null +++ b/rsrc/dialogs/edit-mabil-special.xml @@ -0,0 +1,28 @@ + + + + + Edit Monster Ability: + For monster: + + Display name: + + Ability Type: + + Action Point Cost: + + + Extra 1 + + + + Extra 2 + + + Extra 3 + + + + + + \ No newline at end of file diff --git a/rsrc/dialogs/edit-mabil-summon.xml b/rsrc/dialogs/edit-mabil-summon.xml new file mode 100644 index 00000000..2427e146 --- /dev/null +++ b/rsrc/dialogs/edit-mabil-summon.xml @@ -0,0 +1,34 @@ + + + + + Edit Monster Ability: + For monster: + + Display name: + + Ability Type: + + + Summoning Type: + + + + Which Monster? + + + + Minimum number: + + Maximum number: + + + Summon duration: + + Chance of Using: + + + + + + \ No newline at end of file diff --git a/rsrc/dialogs/edit-monster-abils.xml b/rsrc/dialogs/edit-monster-abils.xml index 73b7edd3..3f87acc7 100644 --- a/rsrc/dialogs/edit-monster-abils.xml +++ b/rsrc/dialogs/edit-monster-abils.xml @@ -1,7 +1,6 @@ - - + diff --git a/rsrc/dialogs/get-mabil-num.xml b/rsrc/dialogs/get-mabil-num.xml new file mode 100644 index 00000000..28dcd021 --- /dev/null +++ b/rsrc/dialogs/get-mabil-num.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/rsrc/strings/monster-abilities.txt b/rsrc/strings/monster-abilities.txt index 7d9734cc..41447bf4 100644 --- a/rsrc/strings/monster-abilities.txt +++ b/rsrc/strings/monster-abilities.txt @@ -1,108 +1,73 @@ -No special ability -Throws darts (dam 1-6) -Shoots arrows (dam 2-12) -Throws spears (dam 3-18) -Throws rocks (dam 4-24) -Throws rocks (dam 5-30) -Throws rocks (dam 6-36) -Throws razordisks (4-24) +Throws darts +Shoots arrows +Throws spears +Throws stones +Throws rocks +Throws boulders +Throws razordisks +Throws knives +Good archer +Shoots spines +Crossbowman +Slinger (small stones) Petrification ray Spell point drain ray Heat ray -Invisible -Splits when hit -Mindless (resists fear) -Breathes stinking clouds -Icy touch -Experience draining touch -Icy and draining touch -Slowing touch -Shoots webs -Good archer (dam 7-42) -Steals food when hits -Permanent martyr's shield Paralysis ray -Dumbfounding touch +Breathes fire +Breathes frost +Breathes electricity +Breathes darkness +Breathes stinking clouds +Breathes sleep clouds +Acid spit +Shoots webs +Poisoned weapon +Acid touch Disease touch -Absorb spells Web touch Sleep touch +Dumbfounding touch Paralysis touch Petrification touch -Acid touch -Breathe sleep clouds -Acid spit -Shoot spines (dam 7-42) Death touch (use with care) -Invulnerable (use with care) -Guard - - - - - - - - - - - - -No ability +Experience draining touch +Icy touch +Icy and draining touch +Stunning touch +Steals food when hits +Steals gold when hits +Splits when hit +Permanent martyr's shield +Absorb spells +Summon (%5 chance) +Summon (%20 chance) +Summon (%50 chance) +Run global special during turn +Death triggers global special Radiate fire fields Radiate ice fields Radiate shock fields Radiate antimagic fields Radiate sleep fields Radiate stink clouds -Unused -Unused -Unused -Summon (%5 chance) -Summon (%20 chance) -Summon (%50 chance) -Unused -Unused -Death triggers global special - - - - - - - - - - - - - - -Unused -Percentage chance (1-100%) -Percentage chance (1-100%) -Percentage chance (1-100%) -Percentage chance (1-100%) -Percentage chance (1-100%) -Percentage chance (1-100%) -Unused -Unused -Unused -Number of creature to summon -Number of creature to summon -Number of creature to summon -Unused -Unused -Number of special to call - - - - - - - - - +Radiate blade fields +Radiate webs +Custom missile ability (advanced) +Custom damage ability (advanced) +Custom status effect (advanced) +Custom field ability (advanced) +Custom petrification (advanced) +Custom SP drain (advanced) +Custom XP drain (advanced) +Custom death ability (advanced) +Custom steal food (advanced) +Custom steal gold (advanced) +Custom undead stat (advanced) +Custom weapon stat (advanced) +Custom radiate fields (advanced) +Custom summoning (advanced) +Custom long damage (advanced) @@ -112,6 +77,28 @@ Number of special to call +Missile +Damaging +Status effect +Field creation +Petrification +Drain spell points +Drain experience +Kill +Steal food +Steal gold +Stun (status negated by life-saving) +Damaging (full attack - 4 ap) +Status effect (first attack only) +Splits when hit +Martyr's shield +Absorb spells +Web missile +Heat ray (costs 1 action point) +Call special node +Death triggers special +Radiate fields +Summon aid @@ -120,8 +107,21 @@ Number of special to call +Darts +Bow/Arrows +Spears +Rocks +Razordisks +Spines +Knives +Crossbow/Bolts +Ray +Touch +Gaze +Breath +Spit @@ -138,3 +138,52 @@ Burns Harms Stabs + + + + + + + + + +Summon specific creature +Summon creature of summon type +Summon random creature of species + + + + + + + +Chance of Activating + + + + + +Chance of Activating +HP gained from non-damaging effects + +Ability Range +Chance of using + +Ability Range +Chance of Using +Ability Strength +Special to Call +Action Points +Chance of using +Special to Call + + + + + + + + + + + diff --git a/src/boe.combat.cpp b/src/boe.combat.cpp index b1b5c212..d7db44b2 100644 --- a/src/boe.combat.cpp +++ b/src/boe.combat.cpp @@ -590,10 +590,17 @@ void pc_attack(short who_att,short target) { move_to_zero(univ.party[who_att].status[eStatus::POISONED_WEAPON]); take_ap(4); - if((univ.town.monst[target].status[eStatus::MARTYRS_SHIELD] > 0 || univ.town.monst[target].abil[eMonstAbil::MARTYRS_SHIELD].active) - && (store_hp - univ.town.monst[target].health > 0)) { - add_string_to_buf(" Shares damage! "); - damage_pc(who_att, store_hp - univ.town.monst[target].health, eDamageType::MAGIC,eRace::UNKNOWN,0); + if(store_hp - univ.town.monst[target].health > 0) { + cCreature& who = univ.town.monst[target]; + if(who.status[eStatus::MARTYRS_SHIELD] > 0 || (who.abil[eMonstAbil::MARTYRS_SHIELD].active && get_ran(1,1,1000) <= who.abil[eMonstAbil::MARTYRS_SHIELD].special.extra1)) { + int how_much = store_hp - who.health; + if(who.abil[eMonstAbil::MARTYRS_SHIELD].active) { + how_much *= who.abil[eMonstAbil::MARTYRS_SHIELD].special.extra2; + how_much /= 100; + } + add_string_to_buf(" Shares damage! "); + damage_pc(who_att, how_much, eDamageType::MAGIC,eRace::UNKNOWN,0); + } } combat_posing_monster = current_working_monster = -1; } @@ -1899,7 +1906,7 @@ void do_monster_turn() { return; futzing = 0; // assume monster is fresh - + special_called = false; cur_monst = &univ.town.monst[i]; @@ -2005,10 +2012,10 @@ void do_monster_turn() { // add_string_to_buf((char *)create_line); // Basic breath weapons - if(cur_monst->abil[eMonstAbil::DAMAGE].active && cur_monst->abil[eMonstAbil::DAMAGE].gen.type == eMonstGen::BREATH - && !acted_yet && get_ran(1,1,1000) < cur_monst->abil[eMonstAbil::DAMAGE].gen.odds) { - if(target != 6 && dist(cur_monst->cur_loc,targ_space) <= cur_monst->abil[eMonstAbil::DAMAGE].gen.range) { - acted_yet = monst_breathe(cur_monst,targ_space,cur_monst->abil[eMonstAbil::DAMAGE]); + if(cur_monst->abil[eMonstAbil::DAMAGE2].active && cur_monst->abil[eMonstAbil::DAMAGE2].gen.type == eMonstGen::BREATH + && !acted_yet && get_ran(1,1,1000) < cur_monst->abil[eMonstAbil::DAMAGE2].gen.odds) { + if(target != 6 && dist(cur_monst->cur_loc,targ_space) <= cur_monst->abil[eMonstAbil::DAMAGE2].gen.range) { + acted_yet = monst_breathe(cur_monst,targ_space,cur_monst->abil[eMonstAbil::DAMAGE2]); had_monst = true; acted_yet = true; take_m_ap(4,cur_monst); @@ -2054,7 +2061,7 @@ void do_monster_turn() { break; case eMonstAbil::DAMAGE: case eMonstAbil::STATUS: case eMonstAbil::STATUS2: case eMonstAbil::FIELD: case eMonstAbil::PETRIFY: case eMonstAbil::DRAIN_SP: case eMonstAbil::DRAIN_XP: case eMonstAbil::KILL: - case eMonstAbil::STEAL_FOOD: case eMonstAbil::STEAL_GOLD: case eMonstAbil::STUN: + case eMonstAbil::STEAL_FOOD: case eMonstAbil::STEAL_GOLD: case eMonstAbil::STUN: case eMonstAbil::DAMAGE2: if(abil.second.gen.type == eMonstGen::TOUCH) break; // We're looking for ranged attacks if(dist(cur_monst->cur_loc, targ_space) > abil.second.gen.range) @@ -2094,10 +2101,22 @@ void do_monster_turn() { else take_m_ap(2,cur_monst); } else if(pick_abil.first == eMonstAbil::RAY_HEAT) take_m_ap(1,cur_monst); + else if(pick_abil.first == eMonstAbil::DAMAGE2) + take_m_ap(4,cur_monst); else take_m_ap(3,cur_monst); had_monst = true; acted_yet = true; } + + // Unusual ability - don't use multiple times per round + if(cur_monst->abil[eMonstAbil::SPECIAL].active && !special_called && party_can_see_monst(i) && get_ran(1,1,1000) <= cur_monst->abil[eMonstAbil::SPECIAL].special.extra3) { + uAbility abil = cur_monst->abil[eMonstAbil::SPECIAL]; + short s1, s2, s3; + special_called = true; + // TODO: Is it good to allow only one monster to use a special ability per combat round? + run_special(eSpecCtx::MONST_SPEC_ABIL,0,abil.special.extra1,cur_monst->cur_loc,&s1,&s2,&s3); + take_m_ap(abil.special.extra2,cur_monst); + } } // Special attacks // Attack pc @@ -2189,24 +2208,32 @@ void do_monster_turn() { place_spell_pattern(square, cur_monst->cur_loc, cur_monst->abil[eMonstAbil::RADIATE].radiate.type, 7); 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]; - r1 = get_ran(1, abil.summon.min, abil.summon.max); - if(r1 && summon_monster(abil.summon.type, cur_monst->cur_loc,abil.summon.len,cur_monst->attitude)) { + mon_num_t what_summon = 0; + switch(abil.summon.type) { + case eMonstSummon::TYPE: what_summon = abil.summon.what; break; + case eMonstSummon::LEVEL: what_summon = get_summon_monster(minmax(0, 4, abil.summon.what)); break; + case eMonstSummon::SPECIES: + for(k = 0; k < 200; k++) { + j = get_ran(1,0,255); + if(univ.scenario.scen_monsters[j].m_type == eRace(abil.summon.what)) { + what_summon = j; + break; + } + } + if(!what_summon) ASB(" Summon failed."); + break; + } + if(what_summon) r1 = get_ran(1, abil.summon.min, abil.summon.max); + else r1 = 0; + if(r1 && summon_monster(what_summon, cur_monst->cur_loc,abil.summon.len,cur_monst->attitude)) { monst_spell_note(cur_monst->number,33); play_sound(61); bool failed = false; while(--r1 && !failed) { - failed = summon_monster(abil.summon.type, cur_monst->cur_loc,abil.summon.len,cur_monst->attitude); + failed = summon_monster(what_summon, cur_monst->cur_loc,abil.summon.len,cur_monst->attitude); } } } - if(cur_monst->abil[eMonstAbil::SPECIAL].active && !special_called && party_can_see_monst(i)) { - uAbility abil = cur_monst->abil[eMonstAbil::SPECIAL]; - short s1, s2, s3; - special_called = true; - // TODO: Is it good to allow only one monster to use a special ability per combat round? - run_special(eSpecCtx::MONST_SPEC_ABIL,0,abil.special.extra1,cur_monst->cur_loc,&s1,&s2,&s3); - take_m_ap(abil.special.extra2,cur_monst); - } } combat_posing_monster = current_working_monster = -1; @@ -2391,7 +2418,7 @@ void monster_attack_pc(short who_att,short target) { continue; if(abil.second.gen.type != eMonstGen::TOUCH) continue; - if(abil.second.gen.range > 0 && get_ran(1,1,1000) <= abil.second.gen.range) + if(abil.second.gen.odds > 0 && get_ran(1,1,1000) <= abil.second.gen.odds) continue; // Print message and possibly choose sound snd_num_t snd = 0; @@ -2404,7 +2431,7 @@ void monster_attack_pc(short who_att,short target) { case eMonstAbil::STEAL_FOOD: add_string_to_buf(" Steals food!"); snd = 26; break; case eMonstAbil::STEAL_GOLD: add_string_to_buf(" Steals gold!"); break; // TODO: Pick a sound case eMonstAbil::FIELD: break; // TODO: Invent messages? - case eMonstAbil::DAMAGE: + case eMonstAbil::DAMAGE: case eMonstAbil::DAMAGE2: switch(abil.second.gen.dmg) { case eDamageType::FIRE: add_string_to_buf(" Burning touch!"); break; case eDamageType::COLD: add_string_to_buf(" Freezing touch!"); break; @@ -2528,7 +2555,7 @@ void monster_attack_monster(short who_att,short attackee) { case eMonstAbil::DRAIN_SP: add_string_to_buf(" Drains magic!"); break; case eMonstAbil::KILL: add_string_to_buf(" Killing touch!"); break; case eMonstAbil::FIELD: break; // TODO: Invent messages? - case eMonstAbil::DAMAGE: + case eMonstAbil::DAMAGE: case eMonstAbil::DAMAGE2: switch(abil.second.gen.dmg) { case eDamageType::FIRE: add_string_to_buf(" Burning touch!"); break; case eDamageType::COLD: add_string_to_buf(" Freezing touch!"); break; @@ -2747,7 +2774,7 @@ void monst_basic_abil(short m_num, std::pair abil, short ta return; } switch(abil.first) { - case eMonstAbil::DAMAGE: + case eMonstAbil::DAMAGE: case eMonstAbil::DAMAGE2: // Determine die size i = 6; if(abil.second.gen.type == eMonstGen::BREATH) diff --git a/src/boe.monster.cpp b/src/boe.monster.cpp index 8dccd6cb..8320f27a 100644 --- a/src/boe.monster.cpp +++ b/src/boe.monster.cpp @@ -1100,11 +1100,12 @@ void forced_place_monster(mon_num_t which,location where) { void magic_adjust(cCreature *which_m,short *how_much) { if(*how_much <= 0) return; - if(which_m->abil[eMonstAbil::ABSORB_SPELLS].active) { + if(which_m->abil[eMonstAbil::ABSORB_SPELLS].active && get_ran(1,1,1000) <= which_m->abil[eMonstAbil::ABSORB_SPELLS].special.extra1) { + int gain = which_m->abil[eMonstAbil::ABSORB_SPELLS].special.extra2; *how_much = 0; - if(32767 - which_m->health > 3) + if(32767 - which_m->health > gain) which_m->health = 32767; - else which_m->health += 3; + else which_m->health += gain; } *how_much *= which_m->magic_res; *how_much /= 100; diff --git a/src/boe.specials.cpp b/src/boe.specials.cpp index 2a6cf926..172d0a8e 100644 --- a/src/boe.specials.cpp +++ b/src/boe.specials.cpp @@ -1431,7 +1431,7 @@ bool damage_monst(short which_m, short who_hit, short how_much, short how_much_s // Absorb damage? if((dam_type == eDamageType::FIRE || dam_type == eDamageType::MAGIC || dam_type == eDamageType::COLD) - && victim->abil[eMonstAbil::ABSORB_SPELLS].active) { + && victim->abil[eMonstAbil::ABSORB_SPELLS].active && get_ran(1,1,1000) <= victim->abil[eMonstAbil::ABSORB_SPELLS].special.extra1) { if(32767 - victim->health > how_much) victim->health = 32767; else victim->health += how_much; @@ -1491,7 +1491,7 @@ bool damage_monst(short which_m, short who_hit, short how_much, short how_much_s victim->health = -1; // splitting monsters - if(victim->abil[eMonstAbil::SPLITS].active && victim->health > 0){ + if(victim->abil[eMonstAbil::SPLITS].active && victim->health > 0 && get_ran(1,1,1000) < victim->abil[eMonstAbil::SPLITS].special.extra1){ where_put = find_clear_spot(victim->cur_loc,1); if(where_put.x > 0) if((which_spot = place_monster(victim->number,where_put)) < 90) { diff --git a/src/classes/monster.cpp b/src/classes/monster.cpp index e37b7cfd..df6a9997 100644 --- a/src/classes/monster.cpp +++ b/src/classes/monster.cpp @@ -135,235 +135,242 @@ void cMonster::append(legacy::monster_record_type& old){ ambient_sound = 0; } -void cMonster::addAbil(eMonstAbilTemplate what, int param) { +std::map::iterator cMonster::addAbil(eMonstAbilTemplate what, int param) { switch(what) { // Missiles: {true, type, missile pic, dice, sides, skill, range, odds} case eMonstAbilTemplate::THROWS_DARTS: abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::DART, 1, 1, 7, 2, 6, 500}; - break; + return abil.find(eMonstAbil::MISSILE); case eMonstAbilTemplate::SHOOTS_ARROWS: abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ARROW, 3, 2, 7, 4, 8, 750}; - break; + return abil.find(eMonstAbil::MISSILE); case eMonstAbilTemplate::THROWS_SPEARS: abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::SPEAR, 5, 3, 7, 6, 8, 625}; - break; + return abil.find(eMonstAbil::MISSILE); case eMonstAbilTemplate::THROWS_ROCKS1: abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ROCK, 12, 4, 7, 8, 10, 625}; - break; + return abil.find(eMonstAbil::MISSILE); case eMonstAbilTemplate::THROWS_ROCKS2: abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ROCK, 12, 6, 7, 12, 10, 500}; - break; + return abil.find(eMonstAbil::MISSILE); case eMonstAbilTemplate::THROWS_ROCKS3: abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ROCK, 12, 8, 7, 16, 10, 500}; - break; + return abil.find(eMonstAbil::MISSILE); case eMonstAbilTemplate::THROWS_RAZORDISKS: abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::RAZORDISK, 7, 7, 7, 14, 8, 625}; - break; + return abil.find(eMonstAbil::MISSILE); case eMonstAbilTemplate::THROWS_KNIVES: abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::KNIFE, 10, 2, 7, 2, 6, 500}; - break; + return abil.find(eMonstAbil::MISSILE); case eMonstAbilTemplate::GOOD_ARCHER: abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ARROW, 3, 8, 7, 16, 10, 875}; - break; + return abil.find(eMonstAbil::MISSILE); case eMonstAbilTemplate::SHOOTS_SPINES: abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::SPINE, 5, 6, 7, 12, 9, 625}; - break; + return abil.find(eMonstAbil::MISSILE); case eMonstAbilTemplate::CROSSBOWMAN: abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::BOLT, 3, 10, 7, 16, 10, 875}; - break; + return abil.find(eMonstAbil::MISSILE); case eMonstAbilTemplate::SLINGER: abil[eMonstAbil::MISSILE].missile = {true, eMonstMissile::ROCK, 12, 2, 7, 3, 8, 750}; - break; + return abil.find(eMonstAbil::MISSILE); // Magical missiles: {true, type, missile pic, strength, range, odds} case eMonstAbilTemplate::RAY_PETRIFY: abil[eMonstAbil::PETRIFY].gen = {true, eMonstGen::GAZE, -1, 25, 6, 625}; - break; + return abil.find(eMonstAbil::PETRIFY); case eMonstAbilTemplate::RAY_SP_DRAIN: abil[eMonstAbil::DRAIN_SP].gen = {true, eMonstGen::GAZE, 8, 50, 8, 625}; - break; + return abil.find(eMonstAbil::DRAIN_SP); case eMonstAbilTemplate::RAY_HEAT: abil[eMonstAbil::RAY_HEAT].special = {true, 6, 625, 7}; - break; + return abil.find(eMonstAbil::RAY_HEAT); case eMonstAbilTemplate::RAY_PARALYSIS: abil[eMonstAbil::STATUS].gen = {true, eMonstGen::RAY, -1, 100, 6, 750}; abil[eMonstAbil::STATUS].gen.stat = eStatus::PARALYZED; - break; + return abil.find(eMonstAbil::STATUS); case eMonstAbilTemplate::BREATH_FIRE: - abil[eMonstAbil::DAMAGE].gen = {true, eMonstGen::BREATH, 13, param, 8, 375}; - abil[eMonstAbil::DAMAGE].gen.dmg = eDamageType::FIRE; - break; + abil[eMonstAbil::DAMAGE2].gen = {true, eMonstGen::BREATH, 13, param, 8, 375}; + abil[eMonstAbil::DAMAGE2].gen.dmg = eDamageType::FIRE; + return abil.find(eMonstAbil::DAMAGE2); case eMonstAbilTemplate::BREATH_FROST: - abil[eMonstAbil::DAMAGE].gen = {true, eMonstGen::BREATH, 6, param, 8, 375}; - abil[eMonstAbil::DAMAGE].gen.dmg = eDamageType::COLD; - break; + abil[eMonstAbil::DAMAGE2].gen = {true, eMonstGen::BREATH, 6, param, 8, 375}; + abil[eMonstAbil::DAMAGE2].gen.dmg = eDamageType::COLD; + return abil.find(eMonstAbil::DAMAGE2); case eMonstAbilTemplate::BREATH_ELECTRICITY: - abil[eMonstAbil::DAMAGE].gen = {true, eMonstGen::BREATH, 8, param, 8, 375}; - abil[eMonstAbil::DAMAGE].gen.dmg = eDamageType::MAGIC; - break; + abil[eMonstAbil::DAMAGE2].gen = {true, eMonstGen::BREATH, 8, param, 8, 375}; + abil[eMonstAbil::DAMAGE2].gen.dmg = eDamageType::MAGIC; + return abil.find(eMonstAbil::DAMAGE2); case eMonstAbilTemplate::BREATH_DARKNESS: - abil[eMonstAbil::DAMAGE].gen = {true, eMonstGen::BREATH, 8, param, 8, 375}; - abil[eMonstAbil::DAMAGE].gen.dmg = eDamageType::UNBLOCKABLE; - break; + abil[eMonstAbil::DAMAGE2].gen = {true, eMonstGen::BREATH, 8, param, 8, 375}; + abil[eMonstAbil::DAMAGE2].gen.dmg = eDamageType::UNBLOCKABLE; + return abil.find(eMonstAbil::DAMAGE2); case eMonstAbilTemplate::BREATH_FOUL: abil[eMonstAbil::FIELD].gen = {true, eMonstGen::BREATH, 12, PAT_SINGLE, 6, 375}; abil[eMonstAbil::FIELD].gen.fld = eFieldType::CLOUD_STINK; - break; + return abil.find(eMonstAbil::FIELD); case eMonstAbilTemplate::BREATH_SLEEP: abil[eMonstAbil::FIELD].gen = {true, eMonstGen::BREATH, 0, PAT_RAD2, 8, 750}; abil[eMonstAbil::FIELD].gen.fld = eFieldType::CLOUD_SLEEP; - break; + return abil.find(eMonstAbil::FIELD); case eMonstAbilTemplate::SPIT_ACID: abil[eMonstAbil::STATUS].gen = {true, eMonstGen::SPIT, 0, 6, 6, 500}; abil[eMonstAbil::STATUS].gen.stat = eStatus::ACID; - break; + return abil.find(eMonstAbil::STATUS); case eMonstAbilTemplate::SHOOTS_WEB: abil[eMonstAbil::MISSILE_WEB].special = {true, 4, 375}; - break; + return abil.find(eMonstAbil::MISSILE_WEB); // Touch abilities case eMonstAbilTemplate::TOUCH_POISON: - abil[eMonstAbil::STATUS2].gen = {true, eMonstGen::TOUCH, -1, param}; + abil[eMonstAbil::STATUS2].gen = {true, eMonstGen::TOUCH, -1, param, 0, 1000}; abil[eMonstAbil::STATUS2].gen.stat = eStatus::POISON; - break; + return abil.find(eMonstAbil::STATUS2); case eMonstAbilTemplate::TOUCH_ACID: - abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, level > 20 ? 4 : 2}; + abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, level > 20 ? 4 : 2, 0, 1000}; abil[eMonstAbil::STATUS].gen.stat = eStatus::ACID; - break; + return abil.find(eMonstAbil::STATUS); case eMonstAbilTemplate::TOUCH_DISEASE: - abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 6, 667}; + abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 6, 0, 667}; abil[eMonstAbil::STATUS].gen.stat = eStatus::DISEASE; - break; + return abil.find(eMonstAbil::STATUS); case eMonstAbilTemplate::TOUCH_WEB: - abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 5}; + abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 5, 0, 1000}; abil[eMonstAbil::STATUS].gen.stat = eStatus::WEBS; - break; + return abil.find(eMonstAbil::STATUS); case eMonstAbilTemplate::TOUCH_SLEEP: - abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 6}; + abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 6, 0, 1000}; abil[eMonstAbil::STATUS].gen.stat = eStatus::ASLEEP; - break; + return abil.find(eMonstAbil::STATUS); case eMonstAbilTemplate::TOUCH_DUMB: - abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 2}; + abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 2, 0, 1000}; abil[eMonstAbil::STATUS].gen.stat = eStatus::DUMB; - break; + return abil.find(eMonstAbil::STATUS); case eMonstAbilTemplate::TOUCH_PARALYSIS: - abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 500}; + abil[eMonstAbil::STATUS].gen = {true, eMonstGen::TOUCH, -1, 0, 500}; abil[eMonstAbil::STATUS].gen.stat = eStatus::PARALYZED; - break; + return abil.find(eMonstAbil::STATUS); case eMonstAbilTemplate::TOUCH_PETRIFY: - abil[eMonstAbil::PETRIFY].gen = {true, eMonstGen::TOUCH, -1, 25}; - break; + abil[eMonstAbil::PETRIFY].gen = {true, eMonstGen::TOUCH, -1, 0, 25}; + return abil.find(eMonstAbil::PETRIFY); case eMonstAbilTemplate::TOUCH_DEATH: - abil[eMonstAbil::KILL].gen = {true, eMonstGen::TOUCH, -1, 2, 667}; - break; + abil[eMonstAbil::KILL].gen = {true, eMonstGen::TOUCH, -1, 2, 0, 667}; + return abil.find(eMonstAbil::KILL); case eMonstAbilTemplate::TOUCH_ICY: case eMonstAbilTemplate::TOUCH_ICY_DRAINING: - abil[eMonstAbil::DAMAGE].gen = {true, eMonstGen::TOUCH, -1, 3, 667}; + abil[eMonstAbil::DAMAGE].gen = {true, eMonstGen::TOUCH, -1, 3, 0, 667}; abil[eMonstAbil::DAMAGE].gen.dmg = eDamageType::COLD; - if(what == eMonstAbilTemplate::TOUCH_ICY) break; + if(what == eMonstAbilTemplate::TOUCH_ICY) + return abil.find(eMonstAbil::DAMAGE); case eMonstAbilTemplate::TOUCH_XP_DRAIN: - abil[eMonstAbil::DRAIN_XP].gen = {true, eMonstGen::TOUCH, -1, 150}; - break; + abil[eMonstAbil::DRAIN_XP].gen = {true, eMonstGen::TOUCH, -1, 0, 150}; + return abil.find(eMonstAbil::DRAIN_XP); case eMonstAbilTemplate::TOUCH_STUN: - abil[eMonstAbil::STUN].gen = {true, eMonstGen::TOUCH, -1, 2, 667}; + abil[eMonstAbil::STUN].gen = {true, eMonstGen::TOUCH, -1, 2, 0, 667}; abil[eMonstAbil::STUN].gen.stat = eStatus::HASTE_SLOW; - break; + return abil.find(eMonstAbil::STUN); case eMonstAbilTemplate::TOUCH_STEAL_FOOD: - abil[eMonstAbil::STEAL_FOOD].gen = {true, eMonstGen::TOUCH, -1, 10, 667}; - break; + abil[eMonstAbil::STEAL_FOOD].gen = {true, eMonstGen::TOUCH, -1, 10, 0, 667}; + return abil.find(eMonstAbil::STEAL_FOOD); case eMonstAbilTemplate::TOUCH_STEAL_GOLD: - abil[eMonstAbil::STEAL_GOLD].gen = {true, eMonstGen::TOUCH, -1, 10, 667}; - break; + abil[eMonstAbil::STEAL_GOLD].gen = {true, eMonstGen::TOUCH, -1, 10, 0, 667}; + return abil.find(eMonstAbil::STEAL_GOLD); // Misc abilities case eMonstAbilTemplate::SPLITS: - abil[eMonstAbil::SPLITS].special = {true, 100, 0}; - break; + abil[eMonstAbil::SPLITS].special = {true, 1000, 0, 0}; + return abil.find(eMonstAbil::SPLITS); case eMonstAbilTemplate::MARTYRS_SHIELD: - abil[eMonstAbil::MARTYRS_SHIELD].special = {true, 0, 0}; - break; + abil[eMonstAbil::MARTYRS_SHIELD].special = {true, 1000, 100, 0}; + return abil.find(eMonstAbil::MARTYRS_SHIELD); case eMonstAbilTemplate::ABSORB_SPELLS: - abil[eMonstAbil::ABSORB_SPELLS].special = {true, 100, 0}; - break; + abil[eMonstAbil::ABSORB_SPELLS].special = {true, 1000, 3, 0}; + return abil.find(eMonstAbil::ABSORB_SPELLS); case eMonstAbilTemplate::SUMMON_5: - abil[eMonstAbil::SUMMON].summon = {true, static_cast(param), 1, 1, 130, 5}; - break; + abil[eMonstAbil::SUMMON].summon = {true, eMonstSummon::TYPE, static_cast(param), 1, 1, 130, 5}; + return abil.find(eMonstAbil::SUMMON); case eMonstAbilTemplate::SUMMON_20: - abil[eMonstAbil::SUMMON].summon = {true, static_cast(param), 1, 1, 130, 20}; - break; + abil[eMonstAbil::SUMMON].summon = {true, eMonstSummon::TYPE, static_cast(param), 1, 1, 130, 20}; + return abil.find(eMonstAbil::SUMMON); case eMonstAbilTemplate::SUMMON_50: - abil[eMonstAbil::SUMMON].summon = {true, static_cast(param), 1, 1, 130, 50}; - break; + abil[eMonstAbil::SUMMON].summon = {true, eMonstSummon::TYPE, static_cast(param), 1, 1, 130, 50}; + return abil.find(eMonstAbil::SUMMON); case eMonstAbilTemplate::SPECIAL: abil[eMonstAbil::SPECIAL].special = {true, param, 1}; - break; + return abil.find(eMonstAbil::SPECIAL); case eMonstAbilTemplate::DEATH_TRIGGERS: abil[eMonstAbil::DEATH_TRIGGER].special = {true, param, 0}; - break; + return abil.find(eMonstAbil::SPECIAL); // Radiate abilities case eMonstAbilTemplate::RADIATE_FIRE: abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_FIRE, param}; - break; + return abil.find(eMonstAbil::RADIATE); case eMonstAbilTemplate::RADIATE_ICE: abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_ICE, param}; - break; + return abil.find(eMonstAbil::RADIATE); case eMonstAbilTemplate::RADIATE_SHOCK: abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_FORCE, param}; - break; + return abil.find(eMonstAbil::RADIATE); case eMonstAbilTemplate::RADIATE_ANTIMAGIC: abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::FIELD_ANTIMAGIC, param}; - break; + return abil.find(eMonstAbil::RADIATE); case eMonstAbilTemplate::RADIATE_SLEEP: abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::CLOUD_SLEEP, param}; - break; + return abil.find(eMonstAbil::RADIATE); case eMonstAbilTemplate::RADIATE_STINK: abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::CLOUD_STINK, param}; - break; + return abil.find(eMonstAbil::RADIATE); case eMonstAbilTemplate::RADIATE_BLADE: abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::WALL_BLADES, param}; - break; + return abil.find(eMonstAbil::RADIATE); case eMonstAbilTemplate::RADIATE_WEB: abil[eMonstAbil::RADIATE].radiate = {true, eFieldType::FIELD_WEB, param}; - break; + return abil.find(eMonstAbil::RADIATE); // Advanced abilities case eMonstAbilTemplate::CUSTOM_MISSILE: abil[eMonstAbil::MISSILE].active = true; - break; + return abil.find(eMonstAbil::MISSILE); case eMonstAbilTemplate::CUSTOM_DAMAGE: abil[eMonstAbil::DAMAGE].active = true; - break; + return abil.find(eMonstAbil::DAMAGE); + case eMonstAbilTemplate::CUSTOM_DAMAGE2: + abil[eMonstAbil::DAMAGE2].active = true; + return abil.find(eMonstAbil::DAMAGE2); case eMonstAbilTemplate::CUSTOM_STATUS: abil[eMonstAbil::STATUS].active = true; - break; + return abil.find(eMonstAbil::STATUS); case eMonstAbilTemplate::CUSTOM_FIELD: abil[eMonstAbil::FIELD].active = true; - break; + return abil.find(eMonstAbil::FIELD); case eMonstAbilTemplate::CUSTOM_PETRIFY: abil[eMonstAbil::PETRIFY].active = true; - break; + return abil.find(eMonstAbil::PETRIFY); case eMonstAbilTemplate::CUSTOM_SP_DRAIN: abil[eMonstAbil::DRAIN_SP].active = true; - break; + return abil.find(eMonstAbil::DRAIN_SP); case eMonstAbilTemplate::CUSTOM_XP_DRAIN: abil[eMonstAbil::DRAIN_XP].active = true; - break; + return abil.find(eMonstAbil::DRAIN_XP); case eMonstAbilTemplate::CUSTOM_KILL: abil[eMonstAbil::KILL].active = true; - break; + return abil.find(eMonstAbil::KILL); case eMonstAbilTemplate::CUSTOM_STEAL_FOOD: abil[eMonstAbil::STEAL_FOOD].active = true; - break; + return abil.find(eMonstAbil::STEAL_FOOD); case eMonstAbilTemplate::CUSTOM_STEAL_GOLD: abil[eMonstAbil::STEAL_GOLD].active = true; - break; + return abil.find(eMonstAbil::STEAL_GOLD); case eMonstAbilTemplate::CUSTOM_STUN: abil[eMonstAbil::STUN].active = true; - break; + return abil.find(eMonstAbil::STUN); case eMonstAbilTemplate::CUSTOM_STATUS2: abil[eMonstAbil::STATUS2].active = true; + return abil.find(eMonstAbil::STATUS2); case eMonstAbilTemplate::CUSTOM_RADIATE: abil[eMonstAbil::RADIATE].active = true; + return abil.find(eMonstAbil::RADIATE); case eMonstAbilTemplate::CUSTOM_SUMMON: abil[eMonstAbil::SUMMON].active = true; - break; + return abil.find(eMonstAbil::SUMMON); } + return abil.end(); } cMonster::cMonster(){ @@ -561,6 +568,19 @@ std::istream& operator >> (std::istream& in, eMonstMissile& e) { return in; } +std::ostream& operator << (std::ostream& out, eMonstSummon e) { + return out << (int)e; +} + +std::istream& operator >> (std::istream& in, eMonstSummon& e) { + int i; + in >> i; + if(i >= 0 && i <= int(eMonstSummon::SPECIES)) + e = (eMonstSummon)i; + else e = eMonstSummon::TYPE; + return in; +} + std::string uAbility::to_string(eMonstAbil key) const { std::ostringstream sout; short i = 0; @@ -616,7 +636,7 @@ std::string uAbility::to_string(eMonstAbil key) const { case eFieldType::FIELD_QUICKFIRE: sout << "Incendiary"; break; } break; - case eMonstAbil::DAMAGE: + case eMonstAbil::DAMAGE: case eMonstAbil::DAMAGE2: switch(gen.dmg) { case eDamageType::FIRE: sout << "Fiery"; break; case eDamageType::COLD: sout << "Icy"; break; @@ -649,7 +669,7 @@ std::string uAbility::to_string(eMonstAbil key) const { case eMonstGen::BREATH: sout << " breath"; break; case eMonstGen::SPIT: sout << " spit"; break; } - if(key == eMonstAbil::DAMAGE) { + if(key == eMonstAbil::DAMAGE || key == eMonstAbil::DAMAGE2) { sout << " (" << gen.strength << ")"; } else if(key == eMonstAbil::STATUS || key == eMonstAbil::STATUS2 || key == eMonstAbil::STUN) { sout << " (" << gen.strength; @@ -763,7 +783,7 @@ void cMonster::writeTo(std::ostream& file) const { file << "DAMAGE " << abil.second.gen.strength << '\n'; file << "RANGE " << abil.second.gen.range << '\n'; file << "CHANCE " << abil.second.gen.odds << '\n'; - if(abil.first == eMonstAbil::DAMAGE) + if(abil.first == eMonstAbil::DAMAGE || abil.first == eMonstAbil::DAMAGE2) file << "EXTRA " << abil.second.gen.dmg << '\n'; else if(abil.first == eMonstAbil::FIELD) file << "EXTRA " << abil.second.gen.fld << '\n'; @@ -775,7 +795,7 @@ void cMonster::writeTo(std::ostream& file) const { file << "CHANCE " << abil.second.radiate.chance << '\n'; break; case eMonstAbilCat::SUMMON: - file << "TYPE " << abil.second.summon.type << '\n'; + file << "TYPE " << abil.second.summon.type << '\t' << abil.second.summon.what << '\n'; file << "HOWMANY " << abil.second.summon.min << ' ' << abil.second.summon.max << '\n'; file << "CHANCE " << abil.second.summon.chance << '\n'; break; @@ -870,7 +890,7 @@ void cMonster::readFrom(std::istream& file) { else if(cat == eMonstAbilCat::RADIATE) line >> abil.radiate.type; else if(cat == eMonstAbilCat::SUMMON) - line >> abil.summon.type; + line >> abil.summon.type >> abil.summon.what; } else if(cur == "DAMAGE") { if(cat == eMonstAbilCat::MISSILE) line >> abil.missile.dice >> abil.missile.sides; @@ -894,7 +914,7 @@ void cMonster::readFrom(std::istream& file) { else if(cat == eMonstAbilCat::SUMMON) line >> abil.summon.chance; } else if(cur == "EXTRA") { - if(key == eMonstAbil::DAMAGE) + if(key == eMonstAbil::DAMAGE || key == eMonstAbil::DAMAGE2) line >> abil.gen.dmg; else if(key == eMonstAbil::FIELD) line >> abil.gen.fld; diff --git a/src/classes/monster.h b/src/classes/monster.h index 7e67f25c..a94a9246 100644 --- a/src/classes/monster.h +++ b/src/classes/monster.h @@ -44,6 +44,7 @@ enum class eMonstAbilTemplate { // Advanced abilities CUSTOM_MISSILE, CUSTOM_DAMAGE, CUSTOM_STATUS, CUSTOM_FIELD, CUSTOM_PETRIFY, CUSTOM_SP_DRAIN, CUSTOM_XP_DRAIN, CUSTOM_KILL, CUSTOM_STEAL_FOOD, CUSTOM_STEAL_GOLD, CUSTOM_STUN, CUSTOM_STATUS2, CUSTOM_RADIATE, CUSTOM_SUMMON, + CUSTOM_DAMAGE2, }; enum class eMonstMelee {SWING, CLAW, BITE, SLIME, PUNCH, STING, CLUB, BURN, HARM, STAB}; @@ -52,6 +53,8 @@ enum class eMonstMissile {DART, ARROW, SPEAR, ROCK, RAZORDISK, SPINE, KNIFE, BOL enum class eMonstGen {RAY, TOUCH, GAZE, BREATH, SPIT}; +enum class eMonstSummon {TYPE, LEVEL, SPECIES}; + // Directions! enum eDirection { DIR_N = 0, @@ -91,7 +94,8 @@ union uAbility { } gen; struct { bool active; - mon_num_t type; + eMonstSummon type; + mon_num_t what; int min, max, len, chance; } summon; struct { @@ -144,7 +148,7 @@ public: spec_num_t see_spec; public: - void addAbil(eMonstAbilTemplate what, int param = 0); + std::map::iterator addAbil(eMonstAbilTemplate what, int param = 0); void append(legacy::monster_record_type& old); cMonster(); void writeTo(std::ostream& file) const; @@ -193,6 +197,8 @@ std::ostream& operator << (std::ostream& out, eMonstAbil e); std::istream& operator >> (std::istream& in, eMonstAbil& e); std::ostream& operator << (std::ostream& out, eMonstMissile e); std::istream& operator >> (std::istream& in, eMonstMissile& e); +std::ostream& operator << (std::ostream& out, eMonstSummon e); +std::istream& operator >> (std::istream& in, eMonstSummon& e); std::ostream& operator << (std::ostream& out, eMonstMelee e); std::istream& operator >> (std::istream& in, eMonstMelee& e); std::ostream& operator << (std::ostream& out, eMonstGen e); diff --git a/src/classes/simpletypes.h b/src/classes/simpletypes.h index 0016b9fb..690fcce9 100644 --- a/src/classes/simpletypes.h +++ b/src/classes/simpletypes.h @@ -153,6 +153,7 @@ enum class eMonstAbil { STEAL_FOOD, STEAL_GOLD, STUN, + DAMAGE2, STATUS2, SPLITS, diff --git a/src/classes/universe.cpp b/src/classes/universe.cpp index be0607ec..739ae7f7 100644 --- a/src/classes/universe.cpp +++ b/src/classes/universe.cpp @@ -889,7 +889,8 @@ void cUniverse::check_monst(cMonster& monst) { } break; case eMonstAbilCat::SUMMON: - check_monst(scenario.scen_monsters[abil.second.summon.type]); + if(abil.second.summon.type == eMonstSummon::TYPE) + check_monst(scenario.scen_monsters[abil.second.summon.what]); break; case eMonstAbilCat::RADIATE: case eMonstAbilCat::SPECIAL: @@ -1065,8 +1066,9 @@ void cUniverse::exportSummons() { while(!last_check.empty()) { mon_num_t monst = last_check.top(); last_check.pop(); - if(scenario.scen_monsters[monst].abil[eMonstAbil::SUMMON].active) { - mon_num_t summon = scenario.scen_monsters[monst].abil[eMonstAbil::SUMMON].summon.type; + cMonster& what = scenario.scen_monsters[monst]; + if(what.abil[eMonstAbil::SUMMON].active && what.abil[eMonstAbil::SUMMON].summon.type == eMonstSummon::TYPE) { + mon_num_t summon = what.abil[eMonstAbil::SUMMON].summon.what; if(summon >= 10000) used_monsters.insert(summon - 10000); else if(!need_monsters.count(summon)) { diff --git a/src/dialogxml/dialog.cpp b/src/dialogxml/dialog.cpp index b7a2d062..f20f7aea 100644 --- a/src/dialogxml/dialog.cpp +++ b/src/dialogxml/dialog.cpp @@ -1016,8 +1016,13 @@ void cDialog::run(){ dialogNotToast = true; // Focus the first text field, if there is one if(!tabOrder.empty()) { - tabOrder[0].second->triggerFocusHandler(*this, tabOrder[0].first, false); - currentFocus = tabOrder[0].first; + auto iter = std::find_if(tabOrder.begin(), tabOrder.end(), [](std::pair ctrl){ + return ctrl.second->isVisible(); + }); + if(iter != tabOrder.end()) { + iter->second->triggerFocusHandler(*this, tabOrder[0].first, false); + currentFocus = iter->first; + } } // Sometimes it seems like the Cocoa menu handling clobbers the active rendering context. // For whatever reason, delaying 100 milliseconds appears to fix this. @@ -1197,7 +1202,7 @@ template void cDialog::handleTabOrder(string& itemHit, Iter begin while(iter != cur){ // If tab order is explicitly specified for all fields, gaps are possible if(iter->second == nullptr) continue; - if(iter->second->getType() == CTRL_FIELD){ + if(iter->second->getType() == CTRL_FIELD && iter->second->isVisible()){ if(iter->second->triggerFocusHandler(*this,iter->first,false)){ currentFocus = iter->first; } @@ -1235,6 +1240,7 @@ bool cDialog::toast(bool triggerFocus){ if(!this->getControl(currentFocus).triggerFocusHandler(*this, currentFocus, true)) return false; } dialogNotToast = false; + didAccept = triggerFocus; return true; } @@ -1242,6 +1248,10 @@ void cDialog::untoast() { dialogNotToast = true; } +bool cDialog::accepted() { + return didAccept; +} + bool cDialog::setFocus(cTextField* newFocus, bool force) { // TODO: Should check that there IS a currently focused field (which might not be the case if there are no fields at all). if(!force) { diff --git a/src/dialogxml/dialog.hpp b/src/dialogxml/dialog.hpp index a6e44378..9ae9a38e 100644 --- a/src/dialogxml/dialog.hpp +++ b/src/dialogxml/dialog.hpp @@ -150,6 +150,9 @@ public: /// This is meant to be called from within an event handler to reverse a previous call to toast(); /// if you're already out of the dialog's event loop, you should instead call run() to reopen it. void untoast(); + /// Determine how the dialog exited. + /// @return the argument passed to toast() when the dialog was closed + bool accepted(); /// Get a reference to a control. /// @param id The unique key of the control. /// @throw std::invalid_argument if the control does not exist. @@ -190,7 +193,7 @@ private: void draw(); std::string process_keystroke(cKey keyHit); std::string process_click(location where); - bool dialogNotToast; + bool dialogNotToast, didAccept; rectangle winRect; std::string defaultButton; boost::any result; diff --git a/src/dialogxml/field.cpp b/src/dialogxml/field.cpp index 8760963b..46d6a823 100644 --- a/src/dialogxml/field.cpp +++ b/src/dialogxml/field.cpp @@ -175,6 +175,7 @@ cTextField::cTextField(cDialog* parent) : cTextField::~cTextField(){} void cTextField::draw(){ + if(!visible) return; static const sf::Color hiliteClr = {127,127,127}, ipClr = {92, 92, 92}; inWindow->setActive(); rectangle outline = frame; diff --git a/src/dialogxml/stack.cpp b/src/dialogxml/stack.cpp index 861be464..c5448de6 100644 --- a/src/dialogxml/stack.cpp +++ b/src/dialogxml/stack.cpp @@ -98,6 +98,7 @@ size_t cStack::getPage() { void cStack::setPageCount(size_t n) { if(curPage >= n && n > 0) setPage(nPages - 1); + if(n == 0) curPage = 0; nPages = n; storage.resize(nPages); } diff --git a/src/scenedit/scen.core.cpp b/src/scenedit/scen.core.cpp index 0bf48ddd..4b551be3 100644 --- a/src/scenedit/scen.core.cpp +++ b/src/scenedit/scen.core.cpp @@ -506,7 +506,7 @@ static bool edit_monst_type_event_filter(cDialog& me,std::string item_hit,cMonst } } else if(item_hit == "abils") { if(!save_monst_info(me,store_monst)) return false; - temp_monst = edit_monst_abil(store_monst,which_monst); + temp_monst = edit_monst_abil(store_monst,which_monst,me); if(temp_monst.level < 255) store_monst = temp_monst; put_monst_info_in_dlog(me,store_monst,which_monst); @@ -596,8 +596,7 @@ short edit_monst_type(short which_monst) { return 0; } -static void put_monst_abils_in_dlog(cDialog& me, cMonster& store_monst, short which_monst) { - me["num"].setTextToNum(which_monst); +static void put_monst_abils_in_dlog(cDialog& me, cMonster& store_monst) { me["loot-item"].setTextToNum(store_monst.corpse_item); me["loot-chance"].setTextToNum(store_monst.corpse_item_chance); @@ -626,12 +625,26 @@ static void put_monst_abils_in_dlog(cDialog& me, cMonster& store_monst, short wh for(auto& abil : store_monst.abil) { if(!abil.second.active) continue; std::string id = std::to_string((i % 4) + 1); - if(i % 4 == 0) abils.setPage(i / 4); - else if(i % 4 == 3) abils.addPage(); + if(i % 4 == 0) { + abils.setPage(i / 4); + for(int j = 0; j < 4; j++) { + std::string id = std::to_string(j + 1); + me["abil-name" + id].setText(""); + me["abil-edit" + id].setText("Add"); + } + } else if(i % 4 == 3) abils.addPage(); me["abil-name" + id].setText(abil.second.to_string(abil.first)); me["abil-edit" + id].setText("Edit"); i++; } + if(i % 4 == 0) { + abils.setPage(i / 4); + for(int j = 0; j < 4; j++) { + std::string id = std::to_string(j + 1); + me["abil-name" + id].setText(""); + me["abil-edit" + id].setText("Add"); + } + } abils.setPage(0); if(abils.getPageCount() <= 1) { @@ -699,16 +712,393 @@ static bool edit_monst_abil_event_filter(cDialog& me,std::string item_hit,cMonst return true; } -cMonster edit_monst_abil(cMonster starting_record,short which_monst) { +static short get_monst_abil_num(std::string prompt, int min, int max, cDialog& parent) { + cDialog numPanel("get-mabil-num", &parent); + numPanel["okay"].attachClickHandler(std::bind(&cDialog::toast, &numPanel, false)); + numPanel["prompt"].setText(prompt + " (" + std::to_string(min) + "-" + std::to_string(max) + ") "); + numPanel["number"].setTextToNum(min); + numPanel.run(); + + int result = numPanel["number"].getTextAsNum(); + if(result < min) return min; + if(result > max) return max; + return result; +} + +static void fill_monst_abil_detail(cDialog& me, cMonster& monst, eMonstAbil abil, uAbility detail) { + eMonstAbilCat cat = getMonstAbilCategory(abil); + me["monst"].setText(monst.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) + me["ap"].setTextToNum(3); + else me["ap"].setTextToNum(2); + } else if(cat == eMonstAbilCat::GENERAL) { + if(detail.gen.type == eMonstGen::TOUCH) + me["ap"].setText("0 (part of standard attack)"); + else if(abil == eMonstAbil::DAMAGE2) + me["ap"].setTextToNum(4); + else me["ap"].setTextToNum(3); + } else if(abil == eMonstAbil::RAY_HEAT) + me["ap"].setTextToNum(1); + else if(abil == eMonstAbil::MISSILE_WEB) + me["ap"].setTextToNum(3); + else if(abil == eMonstAbil::SPECIAL) + me["ap"].setTextToNum(detail.special.extra2); + else if(cat == eMonstAbilCat::SPECIAL) + me["ap"].setText("0 (passive ability)"); + // Subtype + if(cat == eMonstAbilCat::MISSILE) + me["subtype"].setText(get_str("monster-abilities", 110 + int(detail.missile.type))); + else if(cat == eMonstAbilCat::GENERAL) + me["subtype"].setText(get_str("monster-abilities", 120 + int(detail.gen.type))); + else if(cat == eMonstAbilCat::SUMMON) + me["subtype"].setText(get_str("monster-abilities", 150 + int(detail.summon.type))); + // Missile and range + if(cat == eMonstAbilCat::MISSILE || cat == eMonstAbilCat::GENERAL) { + if(cat == eMonstAbilCat::GENERAL && detail.gen.type == eMonstGen::TOUCH) { + me["missile"].hide(); + me["pick-missile"].hide(); + me["missile-pic"].hide(); + me["missile-touch"].show(); + me["range"].hide(); + me["range-touch"].show(); + } else { + me["missile"].show(); + me["pick-missile"].show(); + me["missile-pic"].show(); + me["missile-touch"].hide(); + me["range"].show(); + me["range-touch"].hide(); + } + miss_num_t missile; + int range; + if(cat == eMonstAbilCat::GENERAL) { + missile = detail.gen.pic; + range = detail.gen.range; + } else { + missile = detail.missile.pic; + range = detail.missile.range; + } + me["missile"].setTextToNum(missile); + dynamic_cast(me["missile-pic"]).setPict(missile, PIC_MISSILE); + me["range"].setTextToNum(range); + } + // Chance + if(cat != eMonstAbilCat::SPECIAL) { + int odds = 0; + if(cat == eMonstAbilCat::MISSILE) odds = detail.missile.odds; + else if(cat == eMonstAbilCat::GENERAL) odds = detail.gen.odds; + else if(cat == eMonstAbilCat::SUMMON) odds = detail.summon.chance; + else if(cat == eMonstAbilCat::RADIATE) odds = detail.radiate.chance; + me["odds"].setTextToNum(odds); + } + // Field type + if(cat == eMonstAbilCat::RADIATE || abil == eMonstAbil::FIELD) { + if(abil == eMonstAbil::FIELD) + me["extra"].setTextToNum(int(detail.gen.fld)); + else me["field"].setTextToNum(int(detail.radiate.type)); + } + // Other type-specific fields + if(cat == eMonstAbilCat::MISSILE) { + me["dice"].setTextToNum(detail.missile.dice); + me["sides"].setTextToNum(detail.missile.sides); + me["skill"].setTextToNum(detail.missile.skill); + } else if(cat == eMonstAbilCat::GENERAL) { + me["strength"].setTextToNum(detail.gen.strength); + bool haveExtra = false; + cControl& title = me["extra-title"]; + cControl& field = me["extra"]; + cControl& pick = me["pick-extra"]; + if(abil == eMonstAbil::FIELD) { + haveExtra = true; + title.setText("Field Type:"); + field.setTextToNum(int(detail.gen.fld)); + } else if(abil == eMonstAbil::DAMAGE || abil == eMonstAbil::DAMAGE2) { + haveExtra = true; + title.setText("Damage Type:"); + field.setTextToNum(int(detail.gen.dmg)); + } else if(abil == eMonstAbil::STATUS || abil == eMonstAbil::STATUS2 || abil == eMonstAbil::STUN) { + haveExtra = true; + title.setText("Status Effect:"); + field.setTextToNum(int(detail.gen.stat)); + } + if(haveExtra) + title.show(), field.show(), pick.show(); + else title.hide(), field.hide(), pick.hide(); + } else if(cat == eMonstAbilCat::SUMMON) { + switch(detail.summon.type) { + case eMonstSummon::TYPE: me["summon"].setText(scenario.scen_monsters[detail.summon.what].m_name); break; + case eMonstSummon::LEVEL: me["summon"].setTextToNum(detail.summon.what); break; + case eMonstSummon::SPECIES: me["summon"].setText(get_str("traits", detail.summon.what * 2 + 33)); break; + } + me["max"].setTextToNum(detail.summon.max); + me["min"].setTextToNum(detail.summon.min); + me["len"].setTextToNum(detail.summon.len); + } else if(cat == eMonstAbilCat::SPECIAL) { + me["extra1"].setTextToNum(detail.special.extra1); + me["extra2"].setTextToNum(detail.special.extra2); + me["extra3"].setTextToNum(detail.special.extra3); + } +} + +static void save_monst_abil_detail(cDialog& me, eMonstAbil abil, uAbility& detail) { + eMonstAbilCat cat = getMonstAbilCategory(abil); + if(cat == eMonstAbilCat::MISSILE) { + detail.missile.pic = me["missile"].getTextAsNum(); + detail.missile.dice = me["dice"].getTextAsNum(); + detail.missile.sides = me["sides"].getTextAsNum(); + detail.missile.range = me["range"].getTextAsNum(); + detail.missile.odds = me["odds"].getTextAsNum(); + detail.missile.skill = me["skill"].getTextAsNum(); + } else if(cat == eMonstAbilCat::GENERAL) { + detail.gen.pic = me["missile"].getTextAsNum(); + detail.gen.strength = me["strength"].getTextAsNum(); + detail.gen.range = me["range"].getTextAsNum(); + detail.gen.odds = me["odds"].getTextAsNum(); + } else if(cat == eMonstAbilCat::SUMMON) { + detail.summon.max = me["max"].getTextAsNum(); + detail.summon.min = me["min"].getTextAsNum(); + detail.summon.len = me["len"].getTextAsNum(); + detail.summon.chance = me["odds"].getTextAsNum(); + } else if(cat == eMonstAbilCat::RADIATE) { + detail.radiate.chance = me["odds"].getTextAsNum(); + } else if(cat == eMonstAbilCat::SPECIAL) { + detail.special.extra1 = me["extra1"].getTextAsNum(); + detail.special.extra2 = me["extra2"].getTextAsNum(); + detail.special.extra3 = me["extra3"].getTextAsNum(); + } +} + +static bool edit_monst_abil_detail(cDialog& me, std::string hit, cMonster& monst) { + eMonstAbil abil; + uAbility abil_params; + std::map::iterator iter; + if(me[hit].getText() == "Add") { + int i = choose_text_res("monster-abilities", 1, 70, 0, &me, "Select an ability to add."); + eMonstAbilTemplate tmpl = eMonstAbilTemplate(i); + int param = 0; + switch(tmpl) { + case eMonstAbilTemplate::RADIATE_FIRE: + case eMonstAbilTemplate::RADIATE_ICE: + case eMonstAbilTemplate::RADIATE_SHOCK: + case eMonstAbilTemplate::RADIATE_ANTIMAGIC: + case eMonstAbilTemplate::RADIATE_SLEEP: + case eMonstAbilTemplate::RADIATE_STINK: + case eMonstAbilTemplate::RADIATE_BLADE: + case eMonstAbilTemplate::RADIATE_WEB: + param = get_monst_abil_num("Percentage chance of radiating:", 0, 100, me); + break; + case eMonstAbilTemplate::SUMMON_5: + case eMonstAbilTemplate::SUMMON_20: + case eMonstAbilTemplate::SUMMON_50: + param = choose_text(STRT_MONST, 0, &me, "Summon which monster?"); + break; + case eMonstAbilTemplate::SPECIAL: + case eMonstAbilTemplate::DEATH_TRIGGERS: + param = get_fresh_spec(0); + if(param < 0) { + giveError("You can't create a new scenario special encounter because there are no more free special nodes.", + "To free a special node, set its type to No Special and set its Jump To special to -1.", &me); + return true; + } + if(!edit_spec_enc(param,0,&me)) + return true; + break; + case eMonstAbilTemplate::TOUCH_POISON: + param = get_monst_abil_num("Poison strength:", 0, 8, me); + break; + case eMonstAbilTemplate::BREATH_FIRE: + case eMonstAbilTemplate::BREATH_FROST: + case eMonstAbilTemplate::BREATH_ELECTRICITY: + case eMonstAbilTemplate::BREATH_DARKNESS: + param = get_monst_abil_num("Breath weapon strength:", 0, 4, me); + break; + } + // Protect from overwriting an existing ability. + auto save_abils = monst.abil; + iter = monst.addAbil(tmpl, param); + // Normally it'll never return end(), but if a new template was added and not implemented, it would. + if(iter == monst.abil.end()) { + giveError("Failed to add the new ability. When reporting this, mention which ability you tried to add.", &me); + return true; + } + if(save_abils.find(iter->first) != save_abils.end() && save_abils[iter->first].active) { + // TODO: Warn about overwriting an ability and give a choce between keeping the old or the new + bool overwrite = true; + if(!overwrite) { + monst.abil = save_abils; + return true; + } + } + size_t iShow = std::distance(monst.abil.begin(), iter); + dynamic_cast(me["abils"]).setPage(iShow / 4); + if(tmpl < eMonstAbilTemplate::CUSTOM_MISSILE && tmpl != eMonstAbilTemplate::SPECIAL) { + put_monst_abils_in_dlog(me, monst); + return true; + } + } else { + size_t which = 4 * dynamic_cast(me["abils"]).getPage() + (hit[9] - '1'); + iter = monst.abil.begin(); + std::advance(iter, which); + } + abil = iter->first; + abil_params = iter->second; + std::string which_dlg = "special"; + eMonstAbilCat cat = getMonstAbilCategory(abil); + switch(cat) { + case eMonstAbilCat::MISSILE: which_dlg = "missile"; break; + case eMonstAbilCat::GENERAL: which_dlg = "general"; break; + case eMonstAbilCat::SUMMON: which_dlg = "summon"; break; + case eMonstAbilCat::RADIATE: which_dlg = "radiate"; break; + case eMonstAbilCat::INVALID: return true; + case eMonstAbilCat::SPECIAL: break; + } + using namespace std::placeholders; + cDialog abil_dlg("edit-mabil-" + which_dlg, &me); + abil_dlg["okay"].attachClickHandler(std::bind(&cDialog::toast, _1, true)); + abil_dlg["cancel"].attachClickHandler(std::bind(&cDialog::toast, _1, false)); + abil_dlg["delete"].attachClickHandler([&monst,iter](cDialog& me,std::string,eKeyMod){ + // TODO: Show confirmation first? + return me.toast(false), monst.abil.erase(iter), true; + }); + auto onfocus = [&](cDialog& me,std::string,bool losing) -> bool { + if(!losing) return true; + save_monst_abil_detail(me, abil, abil_params); + fill_monst_abil_detail(me, monst, abil, abil_params); + return true; + }; + abil_dlg.forEach([&onfocus](std::string, cControl& ctrl){ + if(ctrl.getType() == CTRL_FIELD) + ctrl.attachFocusHandler(onfocus); + }); + + if(cat == eMonstAbilCat::MISSILE || cat == eMonstAbilCat::GENERAL || cat == eMonstAbilCat::SUMMON) { + int first, last; + if(cat == eMonstAbilCat::MISSILE) first = 110, last = 117; + else if(cat == eMonstAbilCat::GENERAL) first = 120, last = 124; + else if(cat == eMonstAbilCat::SUMMON) first = 150, last = 152; + abil_dlg["pick-subtype"].attachClickHandler([&,cat](cDialog& me,std::string,eKeyMod) -> bool { + save_monst_abil_detail(me, abil, abil_params); + int i = 0; + if(cat == eMonstAbilCat::MISSILE) i = int(abil_params.missile.type); + else if(cat == eMonstAbilCat::GENERAL) i = int(abil_params.gen.type); + else if(cat == eMonstAbilCat::SUMMON) i = int(abil_params.summon.type); + i = choose_text_res("monster-abilities", first, last, i + first, &me, "Select ability subtype:"); + if(cat == eMonstAbilCat::MISSILE) abil_params.missile.type = eMonstMissile(i); + else if(cat == eMonstAbilCat::GENERAL) abil_params.gen.type = eMonstGen(i); + else if(cat == eMonstAbilCat::SUMMON) abil_params.summon.type = eMonstSummon(i); + fill_monst_abil_detail(me, monst, abil, abil_params); + return true; + }); + if(cat != eMonstAbilCat::SUMMON) + abil_dlg["pick-missile"].attachClickHandler(std::bind(pick_picture, PIC_MISSILE, _1, "missile", "missile-pic")); + } + if(cat == eMonstAbilCat::GENERAL) { + abil_dlg["pick-extra"].attachClickHandler([&](cDialog& me,std::string,eKeyMod) -> bool { + save_monst_abil_detail(me, abil, abil_params); + int i = -1; + if(abil == eMonstAbil::FIELD) { + i = int(abil_params.gen.fld); + i = choose_field_type(i, &me, false); + abil_params.gen.fld = eFieldType(i); + } else if(abil == eMonstAbil::DAMAGE || abil == eMonstAbil::DAMAGE2) { + i = int(abil_params.gen.dmg); + i = choose_damage_type(i, &me); + abil_params.gen.dmg = eDamageType(i); + } else if(abil == eMonstAbil::STATUS || abil == eMonstAbil::STATUS2 || abil == eMonstAbil::STUN) { + i = int(abil_params.gen.stat); + i = choose_status_effect(i, false, &me); + abil_params.gen.stat = eStatus(i); + } + fill_monst_abil_detail(me, monst, abil, abil_params); + return true; + }); + if(abil == eMonstAbil::FIELD) + abil_dlg["pick-strength"].attachClickHandler([&](cDialog& me,std::string,eKeyMod) -> bool { + save_monst_abil_detail(me, abil, abil_params); + int i = abil_params.gen.strength; + i = choose_text(STRT_SPELL_PAT, i, &me, "Which spell pattern?"); + abil_params.gen.strength = i; + fill_monst_abil_detail(me, monst, abil, abil_params); + return true; + }); + else abil_dlg["pick-strength"].hide(); + } else if(cat == eMonstAbilCat::RADIATE) { + abil_dlg["pick-field"].attachClickHandler([&](cDialog& me,std::string,eKeyMod) -> bool { + save_monst_abil_detail(me, abil, abil_params); + int i = abil_params.radiate.type; + i = choose_field_type(i, &me, false); + abil_params.radiate.type = eFieldType(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::LEVEL: type = STRT_SUMMON; break; + case eMonstSummon::SPECIES: type = STRT_RACE; break; + } + i = choose_text(type, i, &me, "Summon what?"); + abil_params.summon.what = i; + fill_monst_abil_detail(me, monst, abil, abil_params); + return true; + }); + } else if(cat == eMonstAbilCat::SPECIAL) { + if(abil == eMonstAbil::SPECIAL || abil == eMonstAbil::DEATH_TRIGGER) + abil_dlg["pick-extra1"].attachClickHandler([&](cDialog& me,std::string,eKeyMod) -> bool { + short spec = me["extra1"].getTextAsNum(); + if(spec < 0 || spec > 255) { + spec = get_fresh_spec(0); + if(spec < 0) { + giveError("You can't create a new scenario special encounter because there are no more free special nodes.", + "To free a special node, set its type to No Special and set its Jump To special to -1.", &me); + return true; + } + } + if(edit_spec_enc(spec,0,&me)) + me["extra1"].setTextToNum(spec); + return true; + }); + else abil_dlg["pick-extra1"].hide(); + int iStr = int(abil) - int(eMonstAbil::SPLITS); + for(int i = 0; i < 3; i++) { + std::string id = "extra" + std::to_string(i + 1); + std::string title = get_str("monster-abilities", 160 + iStr * 3 + i); + if(title.empty()) { + abil_dlg[id].hide(); + abil_dlg[id + "-title"].hide(); + } else abil_dlg[id + "-title"].setText(title + ":"); + } + } + fill_monst_abil_detail(abil_dlg, monst, abil, abil_params); + abil_dlg.run(); + save_monst_abil_detail(abil_dlg, abil, abil_params); + if(abil_dlg.accepted()) + iter->second = abil_params; + put_monst_abils_in_dlog(me, monst); + return true; +} + +cMonster edit_monst_abil(cMonster starting_record,short which_monst,cDialog& parent) { using namespace std::placeholders; cMonster store_monst = starting_record; - cDialog monst_dlg("edit-monster-abils"); + cDialog monst_dlg("edit-monster-abils",&parent); monst_dlg["loot-item"].attachFocusHandler(std::bind(check_range_msg, _1, _2, _3, -1, 399, "Item To Drop", "-1 for no item")); monst_dlg["loot-chance"].attachFocusHandler(std::bind(check_range_msg, _1, _2, _3, -1, 100, "Dropping Chance", "-1 for no item")); + monst_dlg.attachClickHandlers(std::bind(edit_monst_abil_detail, _1, _2, std::ref(store_monst)), {"abil-edit1", "abil-edit2", "abil-edit3", "abil-edit4"}); monst_dlg.attachClickHandlers(std::bind(edit_monst_abil_event_filter, _1, _2, std::ref(store_monst)), {"okay", "cancel", "abil-up", "abil-down", "edit-see", "pick-snd"}); - put_monst_abils_in_dlog(monst_dlg, store_monst, which_monst); + monst_dlg["num"].setTextToNum(which_monst); + put_monst_abils_in_dlog(monst_dlg, store_monst); monst_dlg.run(); return store_monst; diff --git a/src/scenedit/scen.core.h b/src/scenedit/scen.core.h index 02bd97d5..66c1468c 100644 --- a/src/scenedit/scen.core.h +++ b/src/scenedit/scen.core.h @@ -4,7 +4,7 @@ class cDialog; void edit_custom_pics_types(); short edit_ter_type(ter_num_t which_ter); short edit_monst_type(short which_monst); -cMonster edit_monst_abil(cMonster starting_record,short parent_num); +cMonster edit_monst_abil(cMonster starting_record,short which_monst,cDialog& parent); short edit_item_type(short which_item); cItem edit_item_abil(cItem starting_record,short parent_num); void edit_spec_item(short which_item); diff --git a/src/scenedit/scen.keydlgs.cpp b/src/scenedit/scen.keydlgs.cpp index 461ffd62..f876a86f 100644 --- a/src/scenedit/scen.keydlgs.cpp +++ b/src/scenedit/scen.keydlgs.cpp @@ -265,6 +265,9 @@ short choose_text(eStrType list, unsigned short cur_choice, cDialog* parent, con case STRT_SPELL_PAT: strings = {"Single Space", "3x3 Square", "2x2 Square", "3x3 Open Square", "Radius 2 Circle", "Radius 3 Circle", "Cross", "Rotateable 2x8 Wall"}; break; + case STRT_SUMMON: + strings = {"0 - no summon (weak)", "1 - weak summoning", "2 - summoning", "3 - major summoning", "4 - no summon (unique/powerful"}; + break; } if(cur_choice < 0 || cur_choice >= strings.size()) cur_choice = 0; @@ -529,7 +532,7 @@ static bool edit_spec_enc_type(cDialog& me, std::string item_hit, node_stack_t& return true; } -static short choose_field_type(short cur, cDialog* parent, bool includeSpec) { +short choose_field_type(short cur, cDialog* parent, bool includeSpec) { static const char*const fieldNames[] = { "Wall of Force", "Wall of Fire", "Antimagic Field", "Stinking Cloud", "Wall of Ice", "Wall of Blades", "Sleep Cloud", "Stone Block", "Webs", "Crate", "Barrel", "Fire Barrier", "Force Barrier", "Quickfire", @@ -570,7 +573,7 @@ static short choose_field_type(short cur, cDialog* parent, bool includeSpec) { return made_choice ? item_hit : prev; } -static pic_num_t choose_damage_type(short cur, cDialog* parent) { +pic_num_t choose_damage_type(short cur, cDialog* parent) { static const char*const damageNames[] = {"Weapon", "Fire", "Poison", "Magic", "Unblockable", "Cold", "Undead", "Demon"}; static const std::vector pics = {3,0,2,1,1,4,3,3}; short prev = cur; @@ -602,14 +605,14 @@ static pic_num_t choose_boom_type(short cur, cDialog* parent) { return made_choice ? item_hit : prev; } -static pic_num_t choose_status_effect(short cur, bool party, cDialog* parent) { +pic_num_t choose_status_effect(short cur, bool party, cDialog* parent) { static const char*const status[] = { "Poisoned Weapon", "Bless/Curse", "Poison", "Haste/Slow", "Invulnerable", "Magic Resist/Amplify", "Webs", "Disease", "Sanctuary", "Dumbfounding/Enlightening", "Martyr's Shield", "Sleep/Hyperactivity", "Paralysis", "Acid" }; static const char*const pstatus[] = {"Stealth", "Flight", "Detect Life", "Firewalk"}; - static const std::vector status_pics = {4,0,2,6,5,9,10,11,13,14,15,16,17,18}; + static const std::vector status_pics = {4,2,0,6,5,9,10,11,12,13,14,15,16,17}; static const std::vector pstatus_pics = {25,23,24,26}; short prev = cur; if(cur < 0 || cur >= (party ? pstatus_pics : status_pics).size()) cur = 0; diff --git a/src/scenedit/scen.keydlgs.h b/src/scenedit/scen.keydlgs.h index 8882a1f2..6a83966c 100644 --- a/src/scenedit/scen.keydlgs.h +++ b/src/scenedit/scen.keydlgs.h @@ -10,7 +10,7 @@ enum eStrType { STRT_PICT, STRT_SND, STRT_CMP, STRT_ACCUM, STRT_TRAP, STRT_ATTITUDE, STRT_STAIR, STRT_LIGHT, STRT_CONTEXT, STRT_SHOP, STRT_COST_ADJ, STRT_STAIR_MODE, STRT_TALK_NODE, - STRT_STATUS, STRT_SPELL_PAT, + STRT_STATUS, STRT_SPELL_PAT, STRT_SUMMON, }; bool cre(short val,short min,short max,std::string text1,std::string text2,cDialog* parent) ; @@ -33,3 +33,6 @@ void make_cursor_sword() ; void edit_dialog_text(short mode,short *str1,cDialog* parent); size_t num_strs(short str_mode); +pic_num_t choose_damage_type(short cur, cDialog* parent); +short choose_field_type(short cur, cDialog* parent, bool includeSpec); +pic_num_t choose_status_effect(short cur, bool party, cDialog* parent);