diff --git a/scripts/bigshot.lic b/scripts/bigshot.lic index 385e4396c..85c15f8d9 100644 --- a/scripts/bigshot.lic +++ b/scripts/bigshot.lic @@ -8,7 +8,7 @@ contributors: SpiffyJr, Tillmen, Kalros, Hazado, Tysong, Athias, Falicor, Deysh, Nisugi game: Gemstone tags: hunting, bigshot, combat - version: 5.10.0 + version: 5.11.0 required: Lich >= 5.12.6 Setup Instructions: https://gswiki.play.net/Script_Bigshot @@ -17,6 +17,19 @@ Version Control: Major_change.feature_addition.bugfix + v5.11.0 (2025-11-29) + - add support for boon creatures + - add toggle to stop for dead group members + - refactor find_routine, ma_looter, check_mind, ready_to_hunt?, and ready_to_rest? + - removed gather_ammo (no longer used since archery update) + - reworked debug process + - refactor attack loop for followers + - only call group open call if grouped + - refactor escape from oozes, crawlers, and roa'ters + - removed voln_favor method for Lich::Resources.voln_favor + - update default fried to be 100 and removed value check + - refactor sort_npcs so if valid targets is left blank it targets everything + - removed target check from check_required_values v5.10.0 (2025-10-31) - update BSAreaRooms - fix MA so leader has group open @@ -449,7 +462,7 @@ class Bigshot def start_logging # Startup Message - log("Starting up!") + log("Starting up! Character: #{Char.name} | Version: #{get_script_version}") @worker_thread = Thread.new { UpstreamHook.add("debug_upstream_hook", proc { |data| @@ -518,7 +531,7 @@ class Bigshot @@RECOGNIZED = [:HUNTING_PREP_COMMANDS, :HUNTING_SCRIPTS_START, :CAST_SIGNS, :ATTACK, :PREP_REST, :HUNTING_SCRIPTS_STOP, :RESTING_SCRIPTS_START, :RESTING_PREP_COMMANDS, :DISPLAY_WATCH, :START_WATCH, :STOP_WATCH, :SINGLE_STOP, :FOLLOWER_OVERKILL, :STAY_QUIET, :FOLLOW_NOW, :LOOT, :CUSTOM_PUT, :CUSTOM_CMD, :CUSTOM_DO_CLIENT, - :PUBLIC_SEND, :GO2_WAYPOINTS, :GO2_RESTING_ROOM, :GO2_RALLY_ROOM, :GO2_HUNTING_ROOM, :CHECK_MIND, :JOIN_LEADER, :FOG_RETURN, :CHECK_SNEAKY, + :PUBLIC_SEND, :GO2_WAYPOINTS, :GO2_RESTING_ROOM, :GO2_RALLY_ROOM, :GO2_HUNTING_ROOM, :CHECK_MIND, :FOG_RETURN, :CHECK_SNEAKY, :LEAVE_GROUP, :HUNT_MONITOR_START, :HUNT_MONITOR_STOP] def initialize(type, time_stamp, room_id, c_in = nil) @@ -656,6 +669,10 @@ class Bigshot @leader.name end + def leader_target + @leader.leader_target? + end + def add_member(member) @members[member.name] = member end @@ -912,7 +929,7 @@ class Bigshot fog_optional: { default: false }, custom_fog: { default: '' }, fog_rift: { default: false }, - fried: { default: '' }, + fried: { default: 100 }, overkill: { default: '' }, lte_boost: { default: '' }, oom: { default: '' }, @@ -984,6 +1001,15 @@ class Bigshot targets: { default: '' }, quickhunt_targets: { default: '' }, + # Boon Creatures + boons_all: { default: [] }, + boons_ignore: { default: [] }, + boons_flee: { default: [] }, + immunity: { default: [] }, + misc: { default: [] }, + offensive: { default: [] }, + defensive: { default: [] }, + # Misc tier3: { default: '' }, aim: { default: '' }, @@ -1003,6 +1029,7 @@ class Bigshot wand_if_oom: { default: false }, independent_travel: { default: false }, independent_return: { default: false }, + group_deader: { default: false }, ma_looter: { default: '' }, never_loot: { default: '' }, random_loot: { default: false }, @@ -1036,6 +1063,16 @@ class Bigshot super() @settings = settings @silent_exit = false + @updating = false + + @groups = { + :immunity => ["crit_death_immune", "damage_padding", "elemental_negation", "magic_resistance", "physical_negation", "stun_immune"], + :misc => ["boosted_hp", "boosted_mana", "diseased", "extra_elem", "extra_spirit", "extra_other", "ethereal", "jack", "regen", "soul", "terrifying", "weaken"], + :offensive => ["boosted_offense", "cheat_death", "counter_attack", "crit_weighting", "dmg_weighting", "dispelling", "elem_flares", "frenzy", "mind_blast", "parting_shot", "poisonous"], + :defensive => ["blink", "bolt_shield", "boosted_defense", "confuse", "crit_padding"] + } + + @all_modes = %w[common ignore flee] # set default values if they don't exist @@categories.each do |_, data| @@ -1076,20 +1113,20 @@ class Bigshot -50150110100110-100100110 1000110-50150110-50150110 -50150110-1001001101000.1010 - -50150110FalseBigshot Setup1120750TrueFalsevertical5TrueTrueTrueTrueTrueTrueinTrueFalseTrueFalseverticalTrueFalse100TrueFalse555542TrueFalse1020True400True10 + -50150110FalseBigshot Setup1080800TrueFalsevertical5TrueTrueTrueTrueTrueTrueinTrueFalseTrueFalseverticalTrueFalse100TrueFalse555542TrueFalse1020TrueFalse 00Load Profile30TrueTrueTruestartcenter3019 - 10TrueFalsestart1810Current Profile:012 - 400TrueTruestart1810False02TrueFalse1010 + 10TrueFalsestart1010Current Profile:012 + 400TrueTruestart1010False02TrueFalse1010 2023Update Profile30TrueTrueTrueSaves setting changes to the current profile.startcenter3015 12TrueFalse10label03 - TrueFalseLoad ProfileFalseTrue0TrueFalse5100TrueFalsestart5555Save Current Settings as...30TrueTrueTruestartcenter15105 + TrueFalseLoad ProfileFalseFalse0TrueFalse5100TrueFalsestart5555Save Current Settings as...30TrueTrueTruestartcenter15105 00400TrueTruestart1515012 TrueFalse151010label10 Save Profile30TrueTrueTruestartcenter15152013 20Cancel30TrueTrueTruestartcenter102013 - 30TrueFalseSave ProfileFalseTrue1 - 600150TrueFalse5100TrueTrueTrueTrue555word - TrueFalseNotesFalseTrue2TrueFalsestart10TrueTruestart1010Bigshot is designed to handle combat routines, including moving to and from hunting areas, defining creatures to hunt and accompanying attack routines + 30TrueFalseSave ProfileFalseFalse1 + 150TrueFalse5100TrueTrueTrueTrue555word + TrueFalseNotesFalseFalse2TrueFalsestart10TrueTruestart1010Bigshot is designed to handle combat routines, including moving to and from hunting areas, defining creatures to hunt and accompanying attack routines Afk scripting on any server but Shattered is in violation of game policy. Additional details available on the Gemstone Wiki: <a href="https://gswiki.play.net/Lich:Script_Bigshot" title="Bigshot Wiki">https://gswiki.play.net/Lich:Script_Bigshot</a>True @@ -1107,7 +1144,7 @@ class Bigshot 13TrueTruestart1010encumbered_adjustment14 TrueTruestart101010creeping_dread_adjustment30TrueTruestart1010100crushing_dread_adjustment 31Box in hand after lootingTrueTrueFalsestart45True - 24TrueFalseShould Rest?10TrueFalse0TrueFalse200TrueFalsestart100TrueNoneSpirit Guide (130)Voln Symbol of ReturnTravelers Song (1020)GoS Sigil of EscapeFamiliar Gate (930)Custom + 242TrueFalseShould Rest?10TrueFalse0TrueFalse200TrueFalsestart100TrueNoneSpirit Guide (130)Voln Symbol of ReturnTravelers Song (1020)GoS Sigil of EscapeFamiliar Gate (930)Custom False14TrueFalseendFog Options04 Fog ONLY if wounded or encumberedTrueTrueFalsestart10105True 17 Fog twice if returning from the riftTrueTrueFalsestart1010True @@ -1137,47 +1174,45 @@ class Bigshot 3122Check favor before casting symbolsTrueTrueFalsestart30True 2132TrueFalse100TrueFalse55TrueFalseend305when percentmind <= 00TrueFalseendand percentmana >=01TrueFalseend5and checkspirit >= - 02TrueTruestart50rest_till_exp_adjustment10 + 02TrueTrue1050rest_till_exp_adjustment10 TrueTruestart550rest_till_mana_adjustment11 TrueTruestart50rest_till_spirit_adjustment12TrueTruestart50rest_till_stamina 13TrueFalseend5and percentstamina >=03 - TrueFalseShould Hunt?077TrueFalse100TrueFalse5530TrueTruestart10105 - 1030TrueTruestart10511 - 30TrueTruestart10512TrueFalseend5Starting Room ID: - 00TrueFalseend5Rallypoint Room IDs:01TrueFalseend355Boundary Room IDs: - 02TrueFalseend101010Bigshot will not enter boundary rooms. + TrueFalseShould Hunt?0727TrueFalse100TrueFalse55TrueTrue10105 + 012TrueTrue10105032 + TrueTrue10105052TrueFalsestart105Starting Room ID: + 00TrueFalsestart105Rallypoint Room IDs:02 + TrueFalsestart105Boundary Room IDs:04TrueFalsestart10101010Bigshot will not enter boundary rooms. The goal is to pin yourself into a hunting area. - 032TrueFalseHunting Map - 0027TrueFalseend105Wander Stance: - 21TrueFalsestartcenter101055TrueOffensiveAdvanceForwardNeutralGuardedDefensive - True30TrueFalsestartcenter101055TrueOffensiveAdvanceForwardNeutralGuardedDefensive - True31TrueTruestart100wracking_spirit_adjustment + 062TrueFalseHunting Map0027 + TrueFalseend105Wander Stance:21TrueFalsestartcenter101055TrueOffensiveAdvanceForwardNeutralGuardedDefensive + False30TrueFalsestartcenter101055TrueOffensiveAdvanceForwardNeutralGuardedDefensive + False31TrueTruestart100wracking_spirit_adjustment 382TrueFalsestartcenter101055TrueOffensiveAdvanceForwardNeutralGuardedDefensive - True32TrueFalseend105Stand-up Stance: + False32TrueFalseend105Stand-up Stance: 22 - TrueFalseFalseTrue0 - 2TrueFalseHunting2FalseTrueTrueinTrueFalseTrueFalseverticalTrueFalse100noneTrueFalse5555400TrueTrue10 + TrueFalseFalseTrue02 + TrueFalseHunting2FalseTrueTrueinTrueFalseTrueFalseverticalTrueFalse100noneTrueFalse5555400TrueTrue10 10400TrueTrue101011TrueFalseend10Ambush aiming locations (head, etc): 00TrueFalseend10Archery aiming locations (head, etc):01 TrueFalseend1010Flee if enemy count is >02TrueFalsestart10010Note: Use nouns or name for flee info (case sensative). - Mouse over some hunting commands field for more info. + Mouse over some hunting command fields for more info. 1112400TrueTrue101013 400TrueTrue101014400TrueTrue1010 15TrueFalseend1010...but don\'t count these:03 TrueFalseend1010...and always flee from04TrueFalseend1010Flee from environmental message: 05TrueFalseend1010Wait before wandering to another room06 - Flee from Boon/Boss/Glamour creaturesTrueTrueFalsestart102020True - 07Approach Lone Targets OnlyTrueTrueFalsestart1020True - 18Activate Weapon ReactionsTrueTrueFalseWill initiate reactive strikes when the opportunity presents itself.start1020True + Approach Lone Targets OnlyTrueTrueFalsestart1020True18 + Activate Weapon ReactionsTrueTrueFalseWill initiate reactive strikes when the opportunity presents itself.start1020True 19Bless Weapon?TrueTrueFalsestart102020True - 17Flee from CloudsTrueTrueFalsestart1010True - 08Flee from VinesTrueTrueFalsestart1010True - 09Flee from WebsTrueTrueFalsestart1010True - 010Flee from VoidsTrueTrueFalsestart10True - 011TrueTruestart10100flee_count_adjustment12 + 17Flee from CloudsTrueTrueFalsestart101020True + 07Flee from VinesTrueTrueFalsestart1010True + 08Flee from WebsTrueTrueFalsestart1010True + 09Flee from VoidsTrueTrueFalsestart10True + 010TrueTruestart10100flee_count_adjustment12 TrueTruestart10100.0wander_wait_adjustment116 - TrueFalseFalseTrue03TrueFalseAttacking - 3FalseTrueTrueinTrueFalseTrueFalseverticalTrueFalse100noneTrueFalse5555350TrueTruestart1010 + TrueFalseFalseTrue03 + TrueFalseAttacking3FalseTrueTrueinTrueFalseTrueFalseverticalTrueFalse100noneTrueFalse5555350TrueTruestart1010 10350TrueTruestart101011 350TrueTruestart101012TrueFalseend1010Hunting Commands (a): 00TrueFalseend10Hunting Commands (b):01TrueFalseend1010Hunting Commands (c): @@ -1198,7 +1233,151 @@ class Bigshot 32TrueFalseend6510Quickhunt Targets:22 TrueFalse31TrueFalse34 TrueFalseFalseTrue0 - 4TrueFalseCommands4FalseTrueTrueinTrueFalseTrueFalseverticalTrueFalseTrueFalse30200TrueFalse5530TrueTruestart101015 + 4TrueFalseCommands4FalseTrueTrueinTrueFalseTrueFalseTrueTrueFalsecenter1010200TrueFalseTrue50TrueFalse5Common (?) + 0050TrueFalse5Ignore (?)10 + 50TrueFalse5Flee (?)20TrueFalse + 013TrueTrueFalsecentercenter55True + 02TrueTrueFalsecentercenter55True + 12TrueTrueFalsecentercenter55TrueTrue + 22TrueFalseAll Boon Creatures003TrueFalsestart50100TrueFalsestart510Boss Creatures or the Boss Creature System upgrades newly created creatures into bosses, minibosses, or packs. + Upgraded creatures have their level and basic stats boosted, and are granted a number of special abilities called boons + which are denoted by a special adjective. + Players have alternatively called this "glam critters," "gifted creatures," or "boosted critters." + + Additional details available on the Gemstone Wiki: <a href="https://gswiki.play.net/Category:Boss_creatures" title="Category:Boss_creatures">https://gswiki.play.net/Category:Boss_creatures</a>True + TrueFalse1033TrueTruestart105TrueFalsestart10100TrueFalse100TrueFalse5Boon + 0050TrueFalseCommon1050TrueFalseIgnore + 2050TrueFalseFlee30TrueFalse + 409250TrueFalseNotes50TrueFalse + 016TrueFalsestart555Crit Death Immune + 03TrueFalseSimilar to constructs or golems53TrueFalsestart555Damage Padding + 04TrueFalseAbsorbs magic attacks54TrueFalsestart555Elem Negation + 05TrueFalseAbsorbs elemental magic attacks55TrueFalsestart555Magic Resistance + 06TrueFalseReduces magic damage by 50%56TrueFalsestart555Physical Negation + 07TrueFalsePhysical damage resist shield57TrueFalsestart555Stun Immune + 08TrueFalse57TrueFalsestart555Entire Group170 + 02TrueTrueFalsecentercenterTrue12 + TrueTrueFalsecentercenterTrue22TrueTrueFalsecentercenterTrue + 32TrueTrueFalsecentercenterTrue13 + TrueTrueFalsecentercenterTrue23TrueTrueFalsecentercenterTrue + 33TrueTrueFalsecentercenterTrue14 + TrueTrueFalsecentercenterTrue24TrueTrueFalsecentercenterTrue + 34TrueTrueFalsecentercenterTrue15 + TrueTrueFalsecentercenterTrue25TrueTrueFalsecentercenterTrue + 35TrueTrueFalsecentercenterTrue16 + TrueTrueFalsecentercenterTrue26TrueTrueFalsecentercenterTrue + 36TrueTrueFalsecentercenterTrue17 + TrueTrueFalsecentercenterTrue27TrueTrueFalsecentercenterTrue + 37TrueTrueFalsecentercenterTrue18 + TrueTrueFalsecentercenterTrue28TrueTrueFalsecentercenterTrue + 38TrueFalseTrueFalseImmunity + 032TrueTruestart105TrueFalse15100TrueFalse100TrueFalse5Boon + 0050TrueFalseCommon1050TrueFalseIgnore + 2050TrueFalseFlee30TrueFalse + 4015250TrueFalseNotes50TrueFalse + 016TrueFalsestart555Boosted HP03 + TrueFalsestart555Boosted Mana04TrueFalsestart555Diseased + 05TrueFalsestart555Extra Spells - Elem06 + TrueFalsestart555Extra Spells - Spirit07TrueFalsestart555Extra Spells - Other + 08TrueFalsestart555Ethereal09 + TrueFalseNoncorporeal undead. No Sheer Fear.59TrueFalsestart555Jack of all Trades + 010TrueFalse59TrueFalsestart555Regeneration + 011TrueFalseSimilar to Troll\'s Blood (1125)511TrueFalsestart555Soul Stealing + 012TrueFalseMay possess 1204, 1601, and 1712512TrueFalsestart555Terrifying + 013TrueFalsePre-attack SMR can force player into def513TrueFalsestart555Weaken + 014TrueFalseWeaker creature than normal514TrueFalsestart555Entire Group0 + 02TrueTrueFalsecentercenterTrue12 + TrueTrueFalsecentercenterTrue22TrueTrueFalsecentercenterTrue + 32TrueTrueFalsecentercenterTrue13 + TrueTrueFalsecentercenterTrue23TrueTrueFalsecentercenterTrue + 33TrueTrueFalsecentercenterTrue14 + TrueTrueFalsecentercenterTrue24TrueTrueFalsecentercenterTrue + 34TrueTrueFalsecentercenterTrue15 + TrueTrueFalsecentercenterTrue25TrueTrueFalsecentercenterTrue + 35TrueTrueFalsecentercenterTrue16 + TrueTrueFalsecentercenterTrue26TrueTrueFalsecentercenterTrue + 36TrueTrueFalsecentercenterTrue17 + TrueTrueFalsecentercenterTrue27TrueTrueFalsecentercenterTrue + 37TrueTrueFalsecentercenterTrue18 + TrueTrueFalsecentercenterTrue28TrueTrueFalsecentercenterTrue + 38TrueTrueFalsecentercenterTrue19 + TrueTrueFalsecentercenterTrue29TrueTrueFalsecentercenterTrue + 39TrueTrueFalsecentercenterTrue110 + TrueTrueFalsecentercenterTrue210TrueTrueFalsecentercenterTrue + 310TrueTrueFalsecentercenterTrue111 + TrueTrueFalsecentercenterTrue211TrueTrueFalsecentercenterTrue + 311TrueTrueFalsecentercenterTrue112 + TrueTrueFalsecentercenterTrue212TrueTrueFalsecentercenterTrue + 312TrueTrueFalsecentercenterTrue113 + TrueTrueFalsecentercenterTrue213TrueTrueFalsecentercenterTrue + 313TrueTrueFalsecentercenterTrue114 + TrueTrueFalsecentercenterTrue214TrueTrueFalsecentercenterTrue + 314TrueFalse + TrueFalseStats / Misc042TrueTruestart105TrueFalse100TrueFalse100TrueFalse5Boon + 0050TrueFalseCommon1050TrueFalseIgnore + 2050TrueFalseFlee30TrueFalse + 4014250TrueFalseNotes50TrueFalse + 016TrueFalsestart555Boosted Offense03 + TrueFalsestart555Cheat Death04TrueFalseRaises self after death + 54TrueFalsestart555Counter Attack05 + TrueFalseSMR counter-attack55TrueFalsestart555Crit Weighting + 06TrueFalsestart555Dmg Weighting07 + TrueFalsestart555Dispelling08TrueFalseSpirit Dispel (119) and Elem Dispel (417) + 58TrueFalsestart555Elemental Flares09 + TrueFalsestart555Frenzy010TrueFalsestart555Parting Shot + 012TrueFalseDeals damage one last time upon death512TrueFalsestart555Mind Blast + 011TrueFalsestart555Poisonous013 + TrueFalseSMR poison gas cloud513TrueFalsestart555Entire Group180 + 02TrueTrueFalsecentercenterTrue12 + TrueTrueFalsecentercenterTrue22TrueTrueFalsecentercenterTrue + 32TrueTrueFalsecentercenterTrue13 + TrueTrueFalsecentercenterTrue23TrueTrueFalsecentercenterTrue + 33TrueTrueFalsecentercenterTrue14 + TrueTrueFalsecentercenterTrue24TrueTrueFalsecentercenterTrue + 34TrueTrueFalsecentercenterTrue15 + TrueTrueFalsecentercenterTrue25TrueTrueFalsecentercenterTrue + 35TrueTrueFalsecentercenterTrue16 + TrueTrueFalsecentercenterTrue26TrueTrueFalsecentercenterTrue + 36TrueTrueFalsecentercenterTrue17 + TrueTrueFalsecentercenterTrue27TrueTrueFalsecentercenterTrue + 37TrueTrueFalsecentercenterTrue18 + TrueTrueFalsecentercenterTrue28TrueTrueFalsecentercenterTrue + 38TrueTrueFalsecentercenterTrue19 + TrueTrueFalsecentercenterTrue29TrueTrueFalsecentercenterTrue + 39TrueTrueFalsecentercenterTrue110 + TrueTrueFalsecentercenterTrue210TrueTrueFalsecentercenterTrue + 310TrueTrueFalsecentercenterTrue111 + TrueTrueFalsecentercenterTrue211TrueTrueFalsecentercenterTrue + 311TrueTrueFalsecentercenterTrue112 + TrueTrueFalsecentercenterTrue212TrueTrueFalsecentercenterTrue + 312TrueTrueFalsecentercenterTrue113 + TrueTrueFalsecentercenterTrue213TrueTrueFalsecentercenterTrue + 313TrueFalseTrueFalsestartOffensive + 052TrueTruestart10TrueFalse15100TrueFalse100TrueFalse5Boon + 0050TrueFalseCommon1050TrueFalseIgnore + 2050TrueFalseFlee30TrueFalse + 408250TrueFalseNotes50TrueFalse + 016TrueFalsestart555Blink03 + TrueFalseTemporary phase ability53TrueFalsestart555Bolt Shield + 04TrueFalsestart555Boosted Defense05 + TrueFalseHigh damage padding (+12 CER)55TrueFalsestart555Confuse + 06TrueFalseMay give hard RT for casting at them56TrueFalsestart555Crit Padding + 07TrueFalse56TrueFalsestart555Entire Group170 + 02TrueTrueFalsecentercenterTrue12 + TrueTrueFalsecentercenterTrue22TrueTrueFalsecentercenterTrue + 32TrueTrueFalsecentercenterTrue13 + TrueTrueFalsecentercenterTrue23TrueTrueFalsecentercenterTrue + 33TrueTrueFalsecentercenterTrue14 + TrueTrueFalsecentercenterTrue24TrueTrueFalsecentercenterTrue + 34TrueTrueFalsecentercenterTrue15 + TrueTrueFalsecentercenterTrue25TrueTrueFalsecentercenterTrue + 35TrueTrueFalsecentercenterTrue16 + TrueTrueFalsecentercenterTrue26TrueTrueFalsecentercenterTrue + 36TrueTrueFalsecentercenterTrue17 + TrueTrueFalsecentercenterTrue27TrueTrueFalsecentercenterTrue + 37TrueFalseTrueFalseDefensive + 0625 + TrueFalseBoon Creatures5FalseTrueTrueinTrueFalseTrueFalseverticalTrueFalseTrueFalse30200TrueFalse5530TrueTruestart101015 10TrueFalseend15Tier 3 Attack:0030TrueTruestart1010 11TrueFalseend1110Aim at location (head, etc):01 Use Voln SMITE?TrueTrueFalsestart1015True12 @@ -1219,24 +1398,25 @@ class Bigshot 04Hide to pick up ammoTrueTrueFalsestart1015True 15Use wands when out of manaTrueTrueFalsestart101010True 16TrueFalseAmmo/Wands0427 - TrueFalsestart3015200TrueFalse55342TrueTrue1010optional: Defaults to group leader - 14TrueFalsestart105List of Characters that should never loot052 - 280TrueTrue10104Character names separated by commas: Character1, Character2...062 - TrueFalseend10Primary Looter04Loot randomly based on lowest encumbranceTrueTrueFalsestart1010True - 022Travel TO hunting grounds independentlyTrueTrueFalsestart1010True + TrueFalsestart3015200TrueFalse55342TrueTrue1010optional: Defaults to group leader + 15TrueFalsestart105List of Characters that should never loot062 + 280TrueTrue1010410Character names separated by commas: Character1, Character2...072 + TrueFalseend10Primary Looter05Loot randomly based on lowest encumbranceTrueTrueFalsestart1010True + 032Travel TO hunting grounds independentlyTrueTrueFalsestart1010True 002Travel FROM hunting grounds independentlyTrueTrueFalsestart1010True 012Leader performs a final loot before leaving the roomTrueTrueFalsestart101010True - 032TrueFalseMA Grouping242 + 042Stop for dead group membersTrueTrueFalsestart1010True + 022TrueFalseMA Grouping242 FalseTrue1 - 5TrueFalseMisc5FalseTrueTrueinTrueFalseTrueFalseverticalTrueFalse100noneTrueFalse5555Monitor InteractionsTrueTrueFalsestart50True + 6TrueFalseMisc6FalseTrueTrueinTrueFalseTrueFalseverticalTrueFalse100noneTrueFalse5555Monitor InteractionsTrueTrueFalsestart50True 03600150TrueFalse50100TrueTrueTrueTrueword TrueFalseWatch for strings (or regexes) that contain05600150TrueFalse50100TrueTrueTrueTrueword TrueFalse....except if they also contain06Engage dead man\'s switch (Shattered Only)TrueTrueFalsestart50True 00Depart/rerun if deadTrueTrueFalsestart50True 01Quiet FollowersTrueTrueFalsestart50True 02Ignore disks without obvious hidersTrueTrueFalsestart50True - 04TrueFalseFalseTrue06 - TrueFalseMonitoring6FalseFalseTrue0TrueFalseTrueFalseend20510To save properly, exit with the close button and not the X window. --> + 04TrueFalseFalseTrue07 + TrueFalseMonitoring7FalseFalseTrue0TrueFalseTrueFalseend20510To save properly, exit with the close button and not the X window. --> FalseTrue0Close80TrueTrueTrueend10510True FalseTrue1FalseTrueend1' end @@ -1537,9 +1717,14 @@ class Bigshot # Quick Hunting Commands self['quick_commands'].tooltip_text = '' # Valid Targets - self['targets'].tooltip_text = '' + self['targets'].tooltip_text = 'Leave blank to target all creatures.' # Quickhunt Targets - self['quickhunt_targets'].tooltip_text = "Leave blank to target all critters." + self['quickhunt_targets'].tooltip_text = "Leave blank to target all creatures." + + # Boon Creatures + self['all:common_label'].tooltip_text = "Uses hunting commands to kill.\nStill requires proper valid target matching to engage.\nAlso default behavior if no selections made." + self['all:ignore_label'].tooltip_text = "Will ignore creatures in this category." + self['all:flee_label'].tooltip_text = "Will flee from creatures in this category." # Misc Tab @@ -1607,7 +1792,35 @@ class Bigshot self['monitor_safe_strings'].tooltip_text = '' end + def refresh_boon_settings + # Reset all settings + (@groups.keys + [:boons_all, :boons_ignore, :boons_flee]).each { |k| @settings[k] = [] } + + @all_modes.each do |mode| + # Check the top-level "All Boons" + @settings[:boons_all] << mode if self["boons_all:#{mode}"].active? + + @groups.each do |group_key, items| + # Check the group header + header_key = "#{group_key}:#{mode}" + @settings[group_key] << mode if self[header_key].active? + + items.each do |item| + full_key = "#{group_key}:#{item}_#{mode}" + next unless self[full_key].active? + + # Individual item is active + @settings[group_key] << "#{item}_#{mode}" + @settings[:boons_ignore] << item if mode == "ignore" + @settings[:boons_flee] << item if mode == "flee" + end + end + end + end + def pre_save + refresh_boon_settings + @settings.delete(:profile_name) @settings.delete(:profile_button) @settings.delete(:save_current) @@ -1616,6 +1829,7 @@ class Bigshot @settings.delete(:profile_cancel) @settings.delete(:msg_label) @settings.delete(:no_current_profile) + @settings.delete(:boon_flee_from) @settings = @settings.transform_keys(&:to_s) end @@ -1745,32 +1959,121 @@ class Bigshot end # checkboxes for array storage with id's : - # this is primarily used by the loot types + # this is primarily used by the boon types objects.each do |obj| next unless obj.methods.include?(:builder_name) - next unless obj.builder_name =~ /^([^:]+):(.*)$/i next unless obj.class == Gtk::CheckButton + next unless obj.builder_name =~ /^([^:]+):(.*)$/i key = Regexp.last_match(1).to_sym value = Regexp.last_match(2).to_s - # echo "key: #{key} value: #{value}" + next if Setup.get_setting(key).nil? + # Handle legacy boon_flee_from toggle + if @settings[:boon_flee_from] && value =~ /flee/ + self["all:flee"].active = true + @settings[key] << value + end + # echo @settings[key].include?(value) obj.active = @settings[key].include?(value) # add in hook obj.signal_connect('toggled') do - @settings[key].delete(value) - if obj.active? - @settings[key].push(value) - @settings[key].uniq!.sort! - end + update_boon(obj, key, value) end if connect end end end + def check_group(key, value) + return unless @all_modes.include?(value) + + if key == :boons_all + # if everything else is active and boon_all is toggled off, turn everyting off + if @groups.all? { |group_key, items| items.all? { |item| self["#{group_key}:#{item}_#{value}"].active? } } + @groups.each do |sub_key, sections| + self["#{sub_key}:#{value}"].active = false + sections.each do |item| + self["#{sub_key}:#{item}_#{value}"].active = false + end + end + end + else + if @groups[key].all? { |s| self["#{key}:#{s}_#{value}"].active? } + self["boons_all:#{value}"].active = false + @groups[key].each do |section| + self["#{key}:#{section}_#{value}"].active = false + end + end + end + end + + def update_boon(obj, key, value) + return if @updating + + unless obj.active? + @updating = true + check_group(key, value) + @updating = false + + return + end + + result = value.scan(/\A([a-z0-9_]+)_(common|ignore|flee)\z/i).first + + if result.nil? + # Change all the checkboxes + other_modes = @all_modes - [value] + other_modes.each { |m| self["#{key}:#{m}"].active = false } + + if key == :boons_all + @groups.each do |group_key, _section| + self["#{group_key}:#{value}"].active = true + end + else + @groups[key].each do |section| + self["#{key}:#{section}_#{value}"].active = true + other_modes.each { |m| self["#{key}:#{section}_#{m}"].active = false } + end + + unless self["boons_all:#{value}"].active? + @all_modes.each { |m| self["boons_all:#{m}"].active = false } + end + end + else + return unless @groups.key?(key) + + # Change the individual checkbox + section = result[0] + mode = result[1] + other_modes = @all_modes - [result[1]] + + other_modes.each do |m| + self["#{key}:#{section}_#{m}"].active = false + end + + # If the mode doesn't match the section turn off the section + unless self["#{key}:#{mode}"].active? + @all_modes.each do |m| + self["#{key}:#{m}"].active = false + self["boons_all:#{m}"].active = false + end + end + + # Toggles Entire Group option if the section is all the same + if @groups[key].all? { |s| self["#{key}:#{s}_#{mode}"].active? } + self["#{key}:#{mode}"].active = true + end + + # Toggles All Boon Creatures option if every section is the same + if @groups.all? { |group_key, items| items.all? { |item| self["#{group_key}:#{item}_#{mode}"].active? } } + self["boons_all:#{mode}"].active = true + end + end + end + def save_profile self['test_label'].hide @filename = '' @@ -1922,19 +2225,17 @@ class Bigshot :MONITOR_INTERACTION, :FLEE_CLOUDS, :FLEE_VINES, :FLEE_WEBS, :FLEE_VOIDS, :WRACKING_SPIRIT, :REST_TILL_SPIRIT, :REST_TILL_PERCENTSTAMINA, :BOUNTY_MODE, :AMBUSH, :ARCHERY_AIM, :event_stack, :inbounds, :followers, :group, :leader, :BLESS, :AIM, :TIER3, :QUIET_FOLLOWERS, :IGNORE_DISKS, - :MSTRIKE_COOLDOWN, :MSTRIKE_STAMINA_COOLDOWN, :MSTRIKE_MOB, + :MSTRIKE_COOLDOWN, :MSTRIKE_STAMINA_COOLDOWN, :MSTRIKE_MOB, :BOON_ABILITIES, :BOONS_IGNORE, :BOONS_FLEE, :BOON_CACHE, :MSTRIKE_QUICKSTRIKE, :MSTRIKE_STAMINA_QUICKSTRIKE, :UAC_MSTRIKE, :WANDER_WAIT, :QUICK_COMMANDS, :PRIORITY, :TROUBADOURS_RALLY, :SNEAKY_SNEAKY, :QUICKHUNT_TARGETS, :UAC_SMITE, :FOG_RETURN, :FOG_OPTIONAL, :LOOT_STANCE, :DELAY_LOOT, :PULL, :OVERKILL, :LTE_BOOST, :HELP_GROUP_KILL, :WEAPON_REACTION, :DEADER, :CORRECT_PERCENT_MIND, :MA_LOOTER, - :NEVER_LOOT, :RANDOM_LOOT, :BANDIT_HUNTING, :INDEPENDENT_TRAVEL, :INDEPENDENT_RETURN, :DESIGNATED_LOOTER, :CONTAINERS, + :GROUP_DEADER, :NEVER_LOOT, :RANDOM_LOOT, :BANDIT_HUNTING, :INDEPENDENT_TRAVEL, :INDEPENDENT_RETURN, :DESIGNATED_LOOTER, :CONTAINERS, :REMAINING_SKINS, :SKIN, :BUNDLE_SKIN, :GEM, :GEM_NUMBER, :TRACKING_CREATURE, :BOUNTY_EVAL, :CHECK_FAVOR, - :DEBUG_COMBAT, :DEBUG_COMMANDS, :DEBUG_STATUS, :DEBUG_SYSTEM, :DEBUG_FILE, :debug_logger, :LAST_CALLED, :REST_PREP, :FINAL_LOOT + :DEBUG_COMBAT, :DEBUG_COMMANDS, :DEBUG_STATUS, :DEBUG_SYSTEM, :DEBUG_FILE, :debug_logger, :LAST_CALLED, :REST_PREP, :FINAL_LOOT, + :COMMAND_AMOUNT_CHECKS, :COMMAND_BUFF_CHECKS, :COMMAND_MODIFIER_REGEX, :COMMAND_AMOUNT_REGEX, :COMMAND_BUFF_REGEX, :DAGGER_REGEX, :BLUNT_REGEX PRONE ||= /sleeping|webbed|stunned|kneeling|sitting|^lying|prone|frozen|held in place|entangled/ - # Changed to gameobj-data.xml detection 4.12.2 update - # No longer needed - # BOON_LIST_ADJECTIVES = /^(?:adroit |afflicted |apt |barbed |belligerent |blurry |canny |combative |dazzling |deft |diseased |drab |dreary |ethereal |flashy |flexile |flickering |flinty |frenzied |ghastly |ghostly |gleaming |glittering |glorious |glowing |grotesque |hardy |illustrious |indistinct |keen |lanky |luminous |lustrous |muculent |nebulous |oozing |pestilent |radiant |raging |ready |resolute |robust |rune-covered |shadowy |shielded |shifting |shimmering |shining |sickly green |sinuous |slimy |sparkling |spindly |spiny |stalwart |steadfast |stout |tattooed |tenebrous |tough |twinkling |unflinching |unyielding |wavering |wispy )/ def hunt_monitor(cur_action) bigshot_monitor = proc { |server_string| @@ -2036,19 +2337,19 @@ class Bigshot before_dying { DownstreamHook.remove("#{$current_script_name}_monitor") } if cur_action == "start" - debug_msg(@DEBUG_SYSTEM, "starting bigshot_monitor | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "starting bigshot_monitor") DownstreamHook.add("#{$current_script_name}_monitor", bigshot_monitor) elsif cur_action == "stop" - debug_msg(@DEBUG_SYSTEM, "removing bigshot_monitor | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "removing bigshot_monitor") DownstreamHook.remove("#{$current_script_name}_monitor") # ensure monitor is removed else - debug_msg(@DEBUG_SYSTEM, "removing bigshot_monitor | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "removing bigshot_monitor") DownstreamHook.remove("#{$current_script_name}_monitor") # ensure monitor is removed end end def add_event(type, time_stamp, room_id, c_in = nil) - debug_msg(@DEBUG_SYSTEM, "add_event | type: #{type} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "add_event | type: #{type}") unless (@event_stack.size > 5 && type == :ATTACK) if (type == :FOLLOWER_OVERKILL) add_overkill() @@ -2060,12 +2361,12 @@ class Bigshot end def clear_events() - debug_msg(@DEBUG_SYSTEM, "clear_events | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "clear_events") @event_stack.clear end def grab_event() - debug_msg(@DEBUG_SYSTEM, "grab_event | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "grab_event") @event_stack.shift() end @@ -2180,17 +2481,114 @@ class Bigshot end end - def debug_msg(type, msg) - return unless $bigshot_debug - return unless type + def caller_method(depth = 1) + c = caller(depth..depth).first - if @DEBUG_FILE - @debug_logger.log(msg) - else - echo msg + if c =~ /(.*):(\d+):in `(.*)'$/ + line_number = $2 + method_name = $3.sub(/^block in /, '') + "#{method_name}(#{line_number})" end end + def debug_msg(type, msg) + return unless $bigshot_debug && type + + # Capture the caller of the method that called debug_msg (two frames up) + caller_of_caller = caller(2..2).first + + full_msg = "#{msg} | called by #{caller_of_caller}" + @DEBUG_FILE ? @debug_logger.log(full_msg) : echo(full_msg) + end + + def initialize_boon_data + boon_type = { + "blink" => ["flickering", "wavering"], + "bolt_shield" => ["shielded"], + "boosted_hp" => ["robust", "stalwart"], + "boosted_mana" => ["luminous", "lustrous"], + "boosted_defense" => ["sinuous", "flexile"], + "boosted_offense" => ["combative", "belligerent"], + "cheat_death" => ["glorious", "illustrious"], + "confuse" => ["blurry", "shifting"], + "counter_attack" => ["apt", "ready"], + "crit_death_immune" => ["resolute", "unflinching"], + "crit_padding" => ["stout", "hardy"], + "crit_weighting" => ["shimmering", "gleaming"], + "damage_padding" => ["flinty", "tough"], + "dmg_weighting" => ["barbed", "spiny"], + "diseased" => ["pestilent", "afflicted", "diseased"], + "dispelling" => ["dazzling", "flashy"], + "elem_flares" => ["glittering"], + "elemental_negation" => ["sparkling", "shining"], + "extra_elem" => ["glowing"], + "extra_spirit" => ["radiant"], + "extra_other" => ["twinkling"], + "ethereal" => ["ethereal", "wispy", "ghostly"], + "frenzy" => ["raging", "frenzied"], + "jack" => ["adroit", "deft"], + "magic_resistance" => ["rune-covered", "tattooed"], + "mind_blast" => ["canny", "keen"], + "parting_shot" => ["dreary", "drab"], + "physical_negation" => ["indistinct", "nebulous"], + "poisonous" => ["sickly green", "oozing"], + "regen" => ["slimy", "muculent"], + "soul" => ["tenebrous", "shadowy"], + "stun_immune" => ["steadfast", "unyielding"], + "terrifying" => ["ghastly", "grotesque"], + "weaken" => ["spindly", "lanky"] + } + + # Build a reverse lookup: adjective (downcased) => ability name + @BOON_ABILITIES = boon_type.each_with_object({}) do |(ability, adjs), h| + adjs.each { |adj| h[adj.downcase] = ability } + end + end + + def initialize_command_data + @COMMAND_MODIFIER_REGEX = /\((.*?(?:!?506|!?ancient|!?animate|!?barrage|!?bearhug|buff|!?burst|!?celerity|censer|!?coupdegrace|!?disease|!?e|!?frozen|!?flurry|!?flying|!?fury|!?garrote|!?h|!?hidden|!?holler|!?justice|!?k|!?m|!?mob|!?momentum|!?noncorporeal|once|!?outside|!?pcs|!?poison|!?prone|!?pummel|!?rapid|!?rebuke|!?reflex|room|!?rooted|!?s|!?scourge|!?shout|!?surge|!?tailwind|!?tier|!?tier1|!?tier2|!?tier3|!?thrash|!?undead|!?v|!?valid|!?vigor|!?voidweaver|!?yowlp).*?)\)$/i.freeze + + @COMMAND_AMOUNT_REGEX = /((?:!?e|!?h|!?k|!?m|!?mob|!?s|!?tier|!?v|!?valid))(\d+)/i.freeze + + @COMMAND_BUFF_REGEX = /((?:buff))(\d+)/i.freeze + + @COMMAND_AMOUNT_CHECKS = { + 'e' => ->(amount) { Char.percent_encumbrance < amount }, + '!e' => ->(amount) { Char.percent_encumbrance >= amount }, + 'h' => ->(amount) { Char.percent_health < amount }, + '!h' => ->(amount) { Char.percent_health >= amount }, + 'k' => ->(_amount) { !checkkneeling }, + '!k' => ->(_amount) { checkkneeling }, + 'm' => ->(amount) { Char.mana < amount }, + '!m' => ->(amount) { Char.mana >= amount }, + 'mob' => ->(amount) { gameobj_npc_check() < amount }, + '!mob' => ->(amount) { gameobj_npc_check() > amount }, + 's' => ->(amount) { Char.stamina < amount }, + '!s' => ->(amount) { Char.stamina >= amount }, + 'tier' => ->(amount) { $bigshot_unarmed_tier < amount }, + '!tier' => ->(amount) { $bigshot_unarmed_tier > amount }, + 'v' => ->(amount) { Char.spirit < amount }, + '!v' => ->(amount) { Char.spirit >= amount }, + 'valid' => ->(amount) { sort_npcs.count { |s| valid_target?(s) } < amount }, + '!valid' => ->(amount) { sort_npcs.count { |s| valid_target?(s) } > amount } + }.freeze + + @COMMAND_BUFF_CHECKS = { + 'barrage' => "Enh. Dexterity (+10)", + 'bearhug' => "Enh. Strength (+10)", + 'coupdegrace' => /Empowered \(\+\d+\)/, + 'flurry' => "Slashing Strikes", + 'fury' => "Enh. Constitution (+10)", + 'garrote' => "Enh. Agility (+10)", + 'kweed' => "Tangleweed Vigor", + 'pummel' => "Concussive Blows", + 'shout' => "Empowered (+20)", + 'thrash' => "Forceful Blows", + 'weed' => "Tangleweed Vigor", + 'yowlp' => "Yertie's Yowlp" + }.freeze + end + def initialize(options = nil, group = nil) $bigshot = self @@ -2222,7 +2620,7 @@ class Bigshot echo "debug active for bigshot version #{$bigshot_version}" $bigshot_debug = true end - debug_msg(@DEBUG_SYSTEM, "initialize | options: #{options} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "initialize | options: #{options}") @followers = nil @event_stack = [] @@ -2232,6 +2630,12 @@ class Bigshot set_value(key, clean, default) end + initialize_command_data + initialize_boon_data + @BOONS_IGNORE = UserVars.op["boons_ignore"] || [] + @BOONS_FLEE = UserVars.op["boons_flee"] || [] + @BOON_CACHE = {} + convert_from_uid unless $bigshot_quick @@ -2247,6 +2651,27 @@ class Bigshot @TRACKING_CREATURE = options[0].gsub(/(solo|bounty|quick|single|head|tail)\s*/i, '').strip @BANDIT_NOUN_REGEX = /bandit|brigand|robber|thug|thief|rogue|outlaw|mugger|marauder|highwayman/i + @DAGGER_REGEX = /alfange|basilard|bodkin|cinquedea|dagger|dirk|knife|kozuka|ice pick|misericord|parazonium|pavade|poignard|pugio|scramasax|sgian achlais|spike|stiletto|tanto|sidearm-of-Onar/i + @BLUNT_REGEX = Regexp.union( + # Blunt Weapons + /\b(?:whip|bull whip|cat o' nine tails|signal whip|single-tail whip|training whip)\b/, # whip - crush + # /\b(?:crowbill|hakapik|skull-piercer)\b/, # crowbill - puncture/crush + /\b(?:cudgel|aklys|baculus|club|jo stick|lisan|periperiu|shillelagh|tambara|truncheon|waihaka|war club)\b/, # cudgel - crush + /\b(?:mace|bulawa|dhara|flanged mace|knee-breaker|massuelle|mattina|nifa otti|ox mace|pernat|quadrelle|ridgemace|studded mace)\b/, # mace - crush + /\b(?:ball and chain|binnol|goupillon|mace and chain)\b/, # ball and chain - crush + # /\b(?:war hammer|fang|hammerbeak|hoolurge|horseman's hammer|skull-crusher|taavish)\b/, # war hammer - puncture/crush + /\b(?:morning star|spiked mace|holy water sprinkler|spikestar)\b/, # morning star - crush/puncture + # Brawling Weapons + /\b(?:cestus)\b/, # cestus - crush + /\b(?:knuckle-duster|knuckle guard|\w+ knuckles)\b/, # knuckle-duster - crush + /\b(?:blackjack|bludgeon|sap)\b/, # blackjack - crush + # Two-handed Weapons + /\b(?:runestaff|asaya|crook|crosier|pastoral staff|rune staff|scepter|staff|staff-of-Lumnis|walking stick)\b/, # runestaff - crush + /\b(?:quaterstaff|bo stick|toyak|walking staff|warstaff|yoribo)\b/, # quarterstaff - crush + /\b(?:war mattock|mattock|oncin|pickaxe|sabar)\b/, # war mattock - crush + /\b(?:maul|battle hammer|footman's hammer|sledgehammer|tetsubo)\b/, # maul - crush + ) + if options.any? { |var| var =~ /bounty/i } @BOUNTY_MODE = true if checkbounty.include?("bandit") @@ -2429,6 +2854,7 @@ class Bigshot # Misc Tab - MA Grouping 'independent_travel' => ['', false], # travel TO hunting grounds not as a group 'independent_return' => ['', false], # travel FROM hunting grounds not as a group + 'group_deader' => ['', false], # stop for dead followers 'ma_looter' => ['', nil], # character name of looter 'never_loot' => ['split_xx', Array.new], # no-looting list 'random_loot' => ['', false], # random looting based on encumbrance @@ -2477,6 +2903,7 @@ class Bigshot elsif (clean =~ /targets|qtargets/) targets = Hash.new tokens = value.split(/,/) + tokens.each do |i| if (i =~ /(.*)\(([a|b|c|d|e|f|g|h|i|j|A|B|C|D|E|F|G|H|I|J])\)/) targets[$1.downcase.strip] = $2.downcase.strip @@ -2508,27 +2935,27 @@ class Bigshot @RETURN_WAYPOINT_IDS.map! { |place| uid_match(place.to_s).to_s } - debug_msg(@DEBUG_SYSTEM, "convert_from_uid | @RETURN_WAYPOINT_IDS: #{@RETURN_WAYPOINT_IDS} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "convert_from_uid | @RETURN_WAYPOINT_IDS: #{@RETURN_WAYPOINT_IDS}") # Hunting Room @HUNTING_ROOM_ID = uid_match(@HUNTING_ROOM_ID.to_s) - debug_msg(@DEBUG_SYSTEM, "convert_from_uid | @HUNTING_ROOM_ID: #{@HUNTING_ROOM_ID} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "convert_from_uid | @HUNTING_ROOM_ID: #{@HUNTING_ROOM_ID}") # Resting Room @RESTING_ROOM_ID = uid_match(@RESTING_ROOM_ID.to_s) - debug_msg(@DEBUG_SYSTEM, "convert_from_uid | @RESTING_ROOM_ID: #{@RESTING_ROOM_ID} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "convert_from_uid | @RESTING_ROOM_ID: #{@RESTING_ROOM_ID}") # Rally Room(s) @RALLYPOINT_ROOM_IDS.map! { |place| uid_match(place.to_s).to_s } - debug_msg(@DEBUG_SYSTEM, "convert_from_uid | @RALLYPOINT_ROOM_IDS: #{@RALLYPOINT_ROOM_IDS} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "convert_from_uid | @RALLYPOINT_ROOM_IDS: #{@RALLYPOINT_ROOM_IDS}") # Boundary Rooms @HUNTING_BOUNDARIES.map! { |place| uid_match(place.to_s).to_s } - debug_msg(@DEBUG_SYSTEM, "convert_from_uid | @HUNTING_BOUNDARIES: #{@HUNTING_BOUNDARIES} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "convert_from_uid | @HUNTING_BOUNDARIES: #{@HUNTING_BOUNDARIES}") end def uid_match(room) @@ -2561,10 +2988,10 @@ class Bigshot conditions = [ # resting tab UserVars.op["resting_room_id"].to_s.empty?, - !UserVars.op["fried"].to_i.positive?, # hunting tab UserVars.op["hunting_room_id"].to_s.empty?, + UserVars.op["hunting_boundaries"].to_s.empty?, UserVars.op["rest_till_exp"].to_i.negative?, !UserVars.op["rest_till_spirit"].to_i.positive?, @@ -2573,7 +3000,6 @@ class Bigshot # commands tab UserVars.op["hunting_commands"].to_s.empty?, - UserVars.op["targets"].to_s.empty? ] return if conditions.none?(&:itself) @@ -2592,13 +3018,12 @@ class Bigshot # Define the conditions and messages for easier maintenance conditions = [ { condition: UserVars.op["resting_room_id"].to_s.empty?, message: " Resting room id cannot be empty", tab: "resting" }, - { condition: !UserVars.op["fried"].to_i.positive?, message: " when percentmind >= needs to be a positive number", tab: "resting" }, { condition: UserVars.op["hunting_room_id"].to_s.empty?, message: " starting room id cannot be empty", tab: "hunting" }, + { condition: UserVars.op["hunting_boundaries"].to_s.empty?, message: " hunting boundary cannot be empty", tab: "hunting" }, { condition: UserVars.op["rest_till_exp"].to_i.negative?, message: " when percent mind <= cannot be negative", tab: "hunting" }, { condition: !UserVars.op["rest_till_spirit"].to_i.positive?, message: " and Char.spirit >= needs to be a positive number", tab: "hunting" }, { condition: !UserVars.op["flee_count"].to_i.positive?, message: " Flee if enemy count is > needs to be a positive number", tab: "attacking" }, { condition: UserVars.op["hunting_commands"].to_s.empty?, message: " Hunting Commands (a) cannot be empty", tab: "commands" }, - { condition: UserVars.op["targets"].to_s.empty?, message: " Valid Targets cannot be empty", tab: "commands" }, { condition: UserVars.op["quick_commands"].to_s.empty?, message: " Quick Hunting Commands cannot be empty", bs_quick: true, tab: "commands" } ] @@ -2714,8 +3139,8 @@ class Bigshot regex = Regexp.union(regex, /(?:Roundtime:|\.\.\.wait) (\d+) [Ss]ec(?:onds)?\./) loop do - lines = Lich::Util.issue_command(command, regex, usexml: true, silent: nil, quiet: true) - debug_msg(@DEBUG_SYSTEM, "get_lines | lines: #{lines} | called by #{caller[0]}") + lines = Lich::Util.issue_command(command, regex, usexml: true, silent: true, quiet: true) + debug_msg(@DEBUG_SYSTEM, "get_lines | lines: #{lines}") break unless lines.grep(/(?:Roundtime:|\.\.\.wait) (\d+) [Ss]ec(?:onds)?\./i).any? wait_rt @@ -2730,7 +3155,7 @@ class Bigshot result = nil loop do result = dothistimeout(command, 2, regex) - debug_msg(@DEBUG_SYSTEM, "get_res | result: #{result} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "get_res | result: #{result}") break unless result =~ rt_regex wait_rt end @@ -2739,22 +3164,43 @@ class Bigshot end def check_for_deaders_prone - debug_msg(@DEBUG_COMMANDS, "check_for_deaders_prone | called by #{caller[0]}") - return unless @PULL + debug_msg(@DEBUG_COMMANDS, "check_for_deaders_prone") return if GameObj.pcs.nil? - GameObj.pcs.each { |s| if s.status =~ /sitting|^lying|prone/ && s.status !~ /dead/; fput "pull #{s.noun}"; end; } if GameObj.targets.any? { |s| s.type =~ /aggressive npc/ } - if GameObj.pcs.any? { |s| s.status =~ /dead/ } && @DEADER + if @PULL && GameObj.targets.any? { |s| s.type =~ /aggressive npc/ } + GameObj.pcs.each do |s| + if s.status =~ /sitting|^lying|prone/ && s.status !~ /dead/ + fput "pull #{s.noun}" + end + end + end + + # Always pull group members + Lich::Gemstone::Group.members.each do |s| + if s.status =~ /sitting|^lying|prone/ && s.status !~ /dead/ + fput "pull #{s.noun}" + end + end + + if @DEADER && leading? && GameObj.pcs.any? { |s| s.status =~ /dead/ } puts "#{monsterbold_start} Found a deader! #{monsterbold_end}" echo "PAUSING SCRIPT" echo ";u bigshot" echo "TO CONTINUE" pause_script end + + if @GROUP_DEADER && !solo? && leading? && Lich::Gemstone::Group.members.any? { |s| s.status =~ /dead/ } + 5.times { message("yellow", "ALERT: Group member is dead!") } + echo "PAUSING SCRIPT" + echo ";u bigshot" + echo "TO CONTINUE" + pause_script + end end def cmd(command, npc = nil, stance_dance = true) - debug_msg(@DEBUG_COMMANDS, "cmd | command: #{command} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd | command: #{command}") check_for_deaders_prone @@ -2769,7 +3215,7 @@ class Bigshot command.each do |i| break if npc.status =~ /dead|gone/ || !GameObj.targets.any? { |s| s.id == npc.id } - debug_msg(@DEBUG_COMMANDS, "cmd | command array[i]: #{i} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd | command array[i]: #{i}") cmd(i, npc, stance_dance) end return @@ -2953,7 +3399,7 @@ class Bigshot else return if $ambusher_here - debug_msg(@DEBUG_COMMANDS, "inside command | command: #{command} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "inside command | command: #{command}") bs_put command end @@ -2961,7 +3407,7 @@ class Bigshot end def once_commands_register(npc, command) - debug_msg(@DEBUG_COMMANDS, "once_commands_register | npc.id: #{npc.id} | command: #{command} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "once_commands_register | npc.id: #{npc.id} | command: #{command}") if @COMMANDS_REGISTRY[npc.id].nil? @COMMANDS_REGISTRY[npc.id] = [command] @@ -2971,7 +3417,7 @@ class Bigshot end def cmd_eachtarget(command, npc) - debug_msg(@DEBUG_COMMANDS, "cmd_eachtarget | npc: #{npc} | command: #{command} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_eachtarget | npc: #{npc} | command: #{command}") current_target = npc GameObj.targets.each { |target| @@ -2983,179 +3429,166 @@ class Bigshot end def command_check(command, npc) - debug_msg(@DEBUG_COMMANDS, "command_check | npc: #{npc} | command: #{command} | called by #{caller[0]}") - - # should_return = false - # check mana/stamina/health(percentage)/encumbrance/unarmed tiering/mobs in room/target not prone/target undead - # ! means the inverse/opposite effect original_command = command - if (command =~ /(.*)\((.*?(?:!?506|!?ancient|!?animate|!?barrage|!?bearhug|buff|!?burst|!?celerity|censer|!?coupdegrace|!?disease|!?e|!?frozen|!?flurry|!?flying|!?fury|!?garrote|!?h|!?hidden|!?holler|!?justice|!?k|!?m|!?mob|!?momentum|!?noncorporeal|once|!?outside|!?pcs|!?poison|!?prone|!?pummel|!?rapid|!?rebuke|!?reflex|room|!?rooted|!?s|!?scourge|!?shout|!?surge|!?tailwind|!?tier|!?tier1|!?tier2|!?tier3|!?thrash|!?undead|!?v|!?valid|!?vigor|!?voidweaver|!?yowlp).*?)\)$/i) - command = $1.strip - - $2.split(" ").each { |s| - if s =~ /((?:!?e|!?h|!?k|!?m|!?mob|!?s|!?tier|!?v|!?valid))(\d+)/i - amount = $2.to_i - split_item = $1.strip - - split_check = { - 'e' => lambda { !(Char.percent_encumbrance >= amount) }, - '!e' => lambda { Char.percent_encumbrance >= amount }, - 'h' => lambda { !(Char.percent_health >= amount) }, - '!h' => lambda { Char.percent_health >= amount }, - 'k' => lambda { !checkkneeling }, - '!k' => lambda { checkkneeling }, - 'm' => lambda { !(Char.mana >= amount) }, - '!m' => lambda { Char.mana >= amount }, - 'mob' => lambda { gameobj_npc_check() < amount }, - '!mob' => lambda { gameobj_npc_check() > amount }, - 's' => lambda { !(Char.stamina >= amount) }, - '!s' => lambda { Char.stamina >= amount }, - 'tier' => lambda { $bigshot_unarmed_tier < amount }, - '!tier' => lambda { $bigshot_unarmed_tier > amount }, - 'v' => lambda { !(Char.spirit >= amount) }, - '!v' => lambda { Char.spirit >= amount }, - 'valid' => lambda { sort_npcs.count { |s| valid_target?(s) } < amount }, - '!valid' => lambda { sort_npcs.count { |s| valid_target?(s) } > amount }, - } - - debug_msg(@DEBUG_COMMANDS, "command_check section 1 | s: #{s} | split_item: #{split_item} | split_check[split_item]: #{result = split_check[split_item].call} | called by #{caller[0]}") - return result if result + match = command.match(@COMMAND_MODIFIER_REGEX) + return false unless match + + command = match[1].strip + modifiers = command.split(" ") + + # Process each modifier + modifiers.each do |modifier| + # Check amount-based conditions (e.g., m50, h80, tier2) + if (amount_match = modifier.match(@COMMAND_AMOUNT_REGEX)) + check_type = amount_match[1].downcase + amount = amount_match[2].to_i + + checker = @COMMAND_AMOUNT_CHECKS[check_type] + if checker && instance_exec(amount, &checker) + debug_msg(@DEBUG_COMMANDS, "command_check amount | modifier: #{modifier} | check: #{check_type} | amount: #{amount}") + return true end + end - if s =~ /((?:buff))(\d+)/i - amount = $2.to_i - buff_check = { - 'barrage' => lambda { !(Effects::Buffs.time_left("Enh. Dexterity (+10)") <= (amount / 60.to_f)) }, - 'bearhug' => lambda { !(Effects::Buffs.time_left("Enh. Strength (+10)") <= (amount / 60.to_f)) }, - 'coupdegrace' => lambda { !(Effects::Buffs.time_left(/Empowered \(\+\d+\)/) <= (amount / 60.to_f)) }, - 'flurry' => lambda { !(Effects::Buffs.time_left("Slashing Strikes") <= (amount / 60.to_f)) }, - 'fury' => lambda { !(Effects::Buffs.time_left("Enh. Constitution (+10)") <= (amount / 60.to_f)) }, - 'garrote' => lambda { !(Effects::Buffs.time_left("Enh. Agility (+10)") <= (amount / 60.to_f)) }, - 'kweed' => lambda { !(Effects::Buffs.time_left("Tangleweed Vigor") <= (amount / 60.to_f)) }, - 'pummel' => lambda { !(Effects::Buffs.time_left("Concussive Blows") <= (amount / 60.to_f)) }, - 'shout' => lambda { !(Effects::Buffs.time_left("Empowered (+20)") <= (amount / 60.to_f)) }, - 'thrash' => lambda { !(Effects::Buffs.time_left("Forceful Blows") <= (amount / 60.to_f)) }, - 'weed' => lambda { !(Effects::Buffs.time_left("Tangleweed Vigor") <= (amount / 60.to_f)) }, - 'yowlp' => lambda { !(Effects::Buffs.time_left("Yertie's Yowlp") <= (amount / 60.to_f)) }, - } - - debug_msg(@DEBUG_COMMANDS, "command_check section 2 | s: #{s} | command: #{command} | buff_check[command]: #{result = buff_check[command].call} | called by #{caller[0]}") - return result if result - end + # Check buff time conditions (e.g., buff60 with command 'barrage') + if (buff_match = modifier.match(@COMMAND_BUFF_REGEX)) + amount = buff_match[2].to_i + buff_name = @COMMAND_BUFF_CHECKS[command] - if s =~ /((?:!?506|!?ancient|!?animate|!?barrage|!?bearhug|!?burst|!?celerity|censer|!?coupdegrace|!?disease|!?flurry|!?flying|!?frozen|!?fury|!?garrote|!?hidden|!?holler|!?justice|!?momentum|!?noncorporeal|once|!?outside|!?pcs|!?poison|!?prone|!?pummel|!?rapid|!?rebuke|!?reflex|room|!?rooted|!?scourge|!?shout|!?surge|!?tailwind|!?thrash|!?tier1|!?tier2|!?tier3|!?undead|!?vigor|!?voidweaver|!?yowlp))/i - item = $1.strip - other_checks = { - '506' => lambda { (!Spell[506].active?) }, - '!506' => lambda { (Spell[506].active? && Spell[506].timeleft <= 0.05) }, - 'ancient' => lambda { npc.name !~ /^(?:grizzled|ancient) / || npc.name == 'ancient ghoul master' }, - '!ancient' => lambda { npc.name =~ /^(?:grizzled|ancient) / && npc.name != 'ancient ghoul master' }, - 'animate' => lambda { !Effects::Spells.active?("Animate Dead") }, - '!animate' => lambda { Effects::Spells.active?("Animate Dead") }, - 'barrage' => lambda { !Effects::Buffs.active?("Enh. Dexterity (+10)") }, - '!barrage' => lambda { Effects::Buffs.active?("Enh. Dexterity (+10)") }, - 'bearhug' => lambda { (!Effects::Buffs.active?("Enh. Strength (+10)") && !Effects::Buffs.active?("Enh. Strength (+20)")) }, - '!bearhug' => lambda { (Effects::Buffs.active?("Enh. Strength (+10)") || Effects::Buffs.active?("Enh. Strength (+20)")) }, - 'burst' => lambda { !Effects::Buffs.to_h.keys.grep(/Enh. Dexterity/).any? }, - '!burst' => lambda { Effects::Cooldowns.active?("Burst of Swiftness") }, - 'celerity' => lambda { (!Spell[506].active?) }, - '!celerity' => lambda { (Spell[506].active? && Spell[506].timeleft <= 0.05) }, - 'coupdegrace' => lambda { !Effects::Buffs.active?(/Empowered \(\+\d+\)/) }, - '!coupdegrace' => lambda { Effects::Buffs.active?(/Empowered \(\+\d+\)/) }, - 'disease' => lambda { !checkdisease }, - '!disease' => lambda { checkdisease }, - 'flurry' => lambda { !Effects::Buffs.active?("Slashing Strikes") }, - '!flurry' => lambda { Effects::Buffs.active?("Slashing Strikes") }, - 'flying' => lambda { !npc.status.include?("flying") }, - '!flying' => lambda { npc.status.include?("flying") }, - 'frozen' => lambda { npc.status =~ /frozen/i }, - '!frozen' => lambda { npc.status !~ /frozen/i }, - 'fury' => lambda { !Effects::Buffs.active?("Enh. Constitution (+10)") }, - '!fury' => lambda { Effects::Buffs.active?("Enh. Constitution (+10)") }, - 'garrote' => lambda { !Effects::Buffs.active?("Enh. Agility (+10)") }, - '!garrote' => lambda { Effects::Buffs.active?("Enh. Agility (+10)") }, - 'hidden' => lambda { !hiding? }, - '!hidden' => lambda { hiding? }, - 'holler' => lambda { !Effects::Buffs.active?('Enh. Health (+20)') }, - '!holler' => lambda { Effects::Buffs.active?('Enh. Health (+20)') }, - 'justice' => lambda { $bigshot_swift_justice == 0 }, - '!justice' => lambda { $bigshot_swift_justice >= 1 }, - 'momentum' => lambda { !Effects::Buffs.active?("Glorious Momentum") }, - '!momentum' => lambda { Effects::Buffs.active?("Glorious Momentum") }, - 'noncorporeal' => lambda { !npc.type.split(',').any? { |a| a == "noncorporeal" } }, - '!noncorporeal' => lambda { npc.type.split(',').any? { |a| a == "noncorporeal" } }, - 'once' => lambda { @COMMANDS_REGISTRY[npc.id].include?(original_command) }, - 'outside' => lambda { !outside? }, - '!outside' => lambda { outside? }, - 'pcs' => lambda { !((checkpcs - Lich::Gemstone::Group.members.map(&:noun)).count > 0) }, - '!pcs' => lambda { ((checkpcs - Lich::Gemstone::Group.members.map(&:noun)).count > 0) }, - 'poison' => lambda { !checkpoison }, - '!poison' => lambda { checkpoison }, - 'prone' => lambda { npc.status =~ PRONE }, - '!prone' => lambda { npc.status !~ PRONE }, - 'pummel' => lambda { !Effects::Buffs.active?("Concussive Blows") }, - '!pummel' => lambda { Effects::Buffs.active?("Concussive Blows") }, - 'rapid' => lambda { !Effects::Buffs.active?("Rapid Fire") }, - '!rapid' => lambda { Effects::Buffs.active?("Rapid Fire") }, - 'rebuke' => lambda { !Effects::Buffs.active?("Righteous Rebuke") }, - '!rebuke' => lambda { Effects::Buffs.active?("Righteous Rebuke") }, - 'reflex' => lambda { !$bigshot_arcane_reflex }, - '!reflex' => lambda { $bigshot_arcane_reflex }, - 'room' => lambda { @COMMANDS_REGISTRY.any? { |_k, v| v.include?(original_command) } }, - 'rooted' => lambda { npc.status =~ /rooted/i }, - '!rooted' => lambda { npc.status !~ /rooted/i }, - 'scourge' => lambda { !Effects::Buffs.active?("Ardor of the Scourge") }, - '!scourge' => lambda { Effects::Buffs.active?("Ardor of the Scourge") }, - 'shout' => lambda { !Effects::Buffs.active?('Empowered (+20)') }, - '!shout' => lambda { Effects::Buffs.active?('Empowered (+20)') }, - 'surge' => lambda { !Effects::Buffs.to_h.keys.grep(/Enh\. Strength/).any? }, - '!surge' => lambda { Effects::Cooldowns.active?("Surge of Strength") }, - 'tailwind' => lambda { !Effects::Buffs.active?("Breeze Archery Tailwind") }, - '!tailwind' => lambda { Effects::Buffs.active?("Breeze Archery Tailwind") }, - 'thrash' => lambda { !Effects::Buffs.active?("Forceful Blows") }, - '!thrash' => lambda { Effects::Buffs.active?("Forceful Blows") }, - 'tier1' => lambda { $bigshot_unarmed_tier != 1 }, - '!tier1' => lambda { $bigshot_unarmed_tier == 1 }, - 'tier2' => lambda { $bigshot_unarmed_tier != 2 }, - '!tier2' => lambda { $bigshot_unarmed_tier == 2 }, - 'tier3' => lambda { $bigshot_unarmed_tier != 3 }, - '!tier3' => lambda { $bigshot_unarmed_tier == 3 }, - 'undead' => lambda { !npc.type.split(',').any? { |a| a == "undead" } }, - '!undead' => lambda { npc.type.split(',').any? { |a| a == "undead" } }, - 'vigor' => lambda { !Effects::Buffs.active?('Tangleweed Vigor') }, - '!vigor' => lambda { Effects::Buffs.active?('Tangleweed Vigor') }, - 'voidweaver' => lambda { Effects::Buffs.to_h.keys.grep(/Voidweaver/).any? }, - '!voidweaver' => lambda { !Effects::Buffs.to_h.keys.grep(/Voidweaver/).any? }, - 'yowlp' => lambda { !Effects::Buffs.active?("Yertie's Yowlp") }, - '!yowlp' => lambda { Effects::Buffs.active?("Yertie's Yowlp") }, - } - - if (item == 'censer') - if (command =~ /^(incant)?\s?(\d+)\s?((?:open|closed)?\s?(?:cast|channel|evoke)?\s?(?:cast|channel|evoke)?\s?(?:open|closed)?\s?(?:air|earth|fire|lightning|water|cold)?)?.*$/i) - id = $2.to_i - spell_cost = Spell[id].cost.to_i + Spell[320].cost.to_i - else - spell_cost = Spell[320].cost.to_i - end + if buff_name && Effects::Buffs.time_left(buff_name) <= (amount / 60.0) + debug_msg(@DEBUG_COMMANDS, "command_check buff | command: #{command} | amount: #{amount} | result: true") + return true + end + end - if !Effects::Cooldowns.active?("Ethereal Censer") && Spell[320].known? - Spell[320].cast if (Char.mana >= spell_cost) - end - else - debug_msg(@DEBUG_COMMANDS, "command_check section 3 | s: #{s} | item: #{item} | other_checks[item]: #{result = other_checks[item].call} | called by #{caller[0]}") - return result if result - end + # Check state-based conditions (e.g., prone, undead, flying) + result = check_state_condition(modifier, command, npc, original_command) + return true if result + end - end - } + return false + end + def check_state_condition(modifier, command, npc, original_command) + case modifier.downcase + # Active spell/effect checks + when '506' then !Spell[506].active? + when '!506' then Spell[506].active? && Spell[506].timeleft <= 0.05 + when 'animate' then !Effects::Spells.active?("Animate Dead") + when '!animate' then Effects::Spells.active?("Animate Dead") + when 'barrage' then !Effects::Buffs.active?("Enh. Dexterity (+10)") + when '!barrage' then Effects::Buffs.active?("Enh. Dexterity (+10)") + when 'bearhug' then !Effects::Buffs.active?("Enh. Strength (+10)") && !Effects::Buffs.active?("Enh. Strength (+20)") + when '!bearhug' then Effects::Buffs.active?("Enh. Strength (+10)") || Effects::Buffs.active?("Enh. Strength (+20)") + when 'burst' then !Effects::Buffs.to_h.keys.grep(/Enh. Dexterity/).any? + when '!burst' then Effects::Cooldowns.active?("Burst of Swiftness") + when 'celerity' then !Spell[506].active? + when '!celerity' then Spell[506].active? && Spell[506].timeleft <= 0.05 + when 'coupdegrace' then !Effects::Buffs.active?(/Empowered \(\+\d+\)/) + when '!coupdegrace' then Effects::Buffs.active?(/Empowered \(\+\d+\)/) + when 'flurry' then !Effects::Buffs.active?("Slashing Strikes") + when '!flurry' then Effects::Buffs.active?("Slashing Strikes") + when 'fury' then !Effects::Buffs.active?("Enh. Constitution (+10)") + when '!fury' then Effects::Buffs.active?("Enh. Constitution (+10)") + when 'garrote' then !Effects::Buffs.active?("Enh. Agility (+10)") + when '!garrote' then Effects::Buffs.active?("Enh. Agility (+10)") + when 'holler' then !Effects::Buffs.active?('Enh. Health (+20)') + when '!holler' then Effects::Buffs.active?('Enh. Health (+20)') + when 'momentum' then !Effects::Buffs.active?("Glorious Momentum") + when '!momentum' then Effects::Buffs.active?("Glorious Momentum") + when 'pummel' then !Effects::Buffs.active?("Concussive Blows") + when '!pummel' then Effects::Buffs.active?("Concussive Blows") + when 'rapid' then !Effects::Buffs.active?("Rapid Fire") + when '!rapid' then Effects::Buffs.active?("Rapid Fire") + when 'rebuke' then !Effects::Buffs.active?("Righteous Rebuke") + when '!rebuke' then Effects::Buffs.active?("Righteous Rebuke") + when 'scourge' then !Effects::Buffs.active?("Ardor of the Scourge") + when '!scourge' then Effects::Buffs.active?("Ardor of the Scourge") + when 'shout' then !Effects::Buffs.active?('Empowered (+20)') + when '!shout' then Effects::Buffs.active?('Empowered (+20)') + when 'surge' then !Effects::Buffs.to_h.keys.grep(/Enh\. Strength/).any? + when '!surge' then Effects::Cooldowns.active?("Surge of Strength") + when 'tailwind' then !Effects::Buffs.active?("Breeze Archery Tailwind") + when '!tailwind' then Effects::Buffs.active?("Breeze Archery Tailwind") + when 'thrash' then !Effects::Buffs.active?("Forceful Blows") + when '!thrash' then Effects::Buffs.active?("Forceful Blows") + when 'vigor' then !Effects::Buffs.active?('Tangleweed Vigor') + when '!vigor' then Effects::Buffs.active?('Tangleweed Vigor') + when 'voidweaver' then Effects::Buffs.to_h.keys.grep(/Voidweaver/).any? + when '!voidweaver' then !Effects::Buffs.to_h.keys.grep(/Voidweaver/).any? + when 'yowlp' then !Effects::Buffs.active?("Yertie's Yowlp") + when '!yowlp' then Effects::Buffs.active?("Yertie's Yowlp") + + # Character state checks + when 'disease' then !checkdisease + when '!disease' then checkdisease + when 'hidden' then !hiding? + when '!hidden' then hiding? + when 'outside' then !outside? + when '!outside' then outside? + when 'poison' then !checkpoison + when '!poison' then checkpoison + + # NPC-specific checks + when 'ancient' then npc.name !~ /^(?:grizzled|ancient) / || npc.name == 'ancient ghoul master' + when '!ancient' then npc.name =~ /^(?:grizzled|ancient) / && npc.name != 'ancient ghoul master' + when 'flying' then !npc.status.include?("flying") + when '!flying' then npc.status.include?("flying") + when 'frozen' then npc.status =~ /frozen/i + when '!frozen' then npc.status !~ /frozen/i + when 'noncorporeal' then !npc.type.split(',').any? { |a| a == "noncorporeal" } + when '!noncorporeal' then npc.type.split(',').any? { |a| a == "noncorporeal" } + when 'prone' then npc.status =~ PRONE + when '!prone' then npc.status !~ PRONE + when 'rooted' then npc.status =~ /rooted/i + when '!rooted' then npc.status !~ /rooted/i + when 'undead' then !npc.type.split(',').any? { |a| a == "undead" } + when '!undead' then npc.type.split(',').any? { |a| a == "undead" } + + # Tier checks + when 'tier1' then $bigshot_unarmed_tier != 1 + when '!tier1' then $bigshot_unarmed_tier == 1 + when 'tier2' then $bigshot_unarmed_tier != 2 + when '!tier2' then $bigshot_unarmed_tier == 2 + when 'tier3' then $bigshot_unarmed_tier != 3 + when '!tier3' then $bigshot_unarmed_tier == 3 + + # Room/registry checks + when 'once' then @COMMANDS_REGISTRY[npc.id].include?(original_command) + when 'room' then @COMMANDS_REGISTRY.any? { |_k, v| v.include?(original_command) } + + # PC checks + when 'pcs' then !((checkpcs - Lich::Gemstone::Group.members.map(&:noun)).count > 0) + when '!pcs' then ((checkpcs - Lich::Gemstone::Group.members.map(&:noun)).count > 0) + + # Special handling + when 'justice' then $bigshot_swift_justice == 0 + when '!justice' then $bigshot_swift_justice >= 1 + when 'reflex' then !$bigshot_arcane_reflex + when '!reflex' then $bigshot_arcane_reflex + + when 'censer' then handle_censer(command) + + else false + end + end + + def handle_censer(command) + spell_cost = if command =~ /^(incant)?\s?(\d+)\s?(?:(?:open|closed)?\s?(?:cast|channel|evoke)?\s?){0,2}(?:air|earth|fire|lightning|water|cold)?/i + Spell[$2.to_i].cost.to_i + Spell[320].cost.to_i + else + Spell[320].cost.to_i + end + + if !Effects::Cooldowns.active?("Ethereal Censer") && Spell[320].known? + Spell[320].cast if Char.mana >= spell_cost end return false end def cmd_wield(noun, hand) - debug_msg(@DEBUG_COMMANDS, "cmd_wield | noun: #{noun} | hand: #{hand} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_wield | noun: #{noun} | hand: #{hand}") return if (hand.empty? || hand == 'right') && GameObj.right_hand.noun == "#{noun}" return if hand == 'left' && GameObj.left_hand.noun == "#{noun}" @@ -3172,7 +3605,7 @@ class Bigshot end def cmd_store(hand = 'both') - debug_msg(@DEBUG_COMMANDS, "cmd_store | hand: #{hand} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_store | hand: #{hand}") return if GameObj.right_hand.id.nil? && hand == 'right' return if GameObj.left_hand.id.nil? && hand == 'left' @@ -3188,7 +3621,7 @@ class Bigshot end def cmd_assault(npc, cmd) - debug_msg(@DEBUG_COMMANDS, "cmd_assault | npc: #{npc} | cmd: #{cmd} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_assault | npc: #{npc} | cmd: #{cmd}") complete_regex = Regexp.union( /Distracted, you hesitate/i, @@ -3213,6 +3646,7 @@ class Bigshot /\.\.\.wait/i, /(?:Barrage|Flurry|Fury|Guardant Thrusts|Pummel|Thrash) is still in cooldown\./, /Your mind clouds with confusion and you glance around uncertainly\./i, + /You can't reach/, ) result_regex = Regexp.union(complete_regex, error_regex) @@ -3265,7 +3699,7 @@ class Bigshot end def cmd_curse(npc, type) - debug_msg(@DEBUG_COMMANDS, "cmd_curse | npc: #{npc} | type: #{type} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_curse | npc: #{npc} | type: #{type}") return if npc.status =~ /dead|gone/ return if type == 'star' && Effects::Spells.time_left("Curse of the Star (bonus)") > 0.5 @@ -3287,7 +3721,7 @@ class Bigshot end def cmd_weapons(npc, cmd) - debug_msg(@DEBUG_COMMANDS, "cmd_weapons | npc: #{npc} | cmd: #{cmd} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_weapons | npc: #{npc} | cmd: #{cmd}") complete_regex = Regexp.union( /You rush forward/i, # charge @@ -3353,7 +3787,7 @@ class Bigshot end def cmd_shields(npc, cmd) - debug_msg(@DEBUG_COMMANDS, "cmd_shields | npc: #{npc} | cmd: #{cmd} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_shields | npc: #{npc} | cmd: #{cmd}") complete_regex = Regexp.union( /awkward proposition/i, @@ -3421,7 +3855,7 @@ class Bigshot end def cmd_caststop(npc, spell, extra) - debug_msg(@DEBUG_COMMANDS, "cmd_caststop | npc: #{npc} | spell: #{spell} | extra: #{extra} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_caststop | npc: #{npc} | spell: #{spell} | extra: #{extra}") return if npc.status.match?(/dead|gone/) return unless GameObj.targets.any? { |s| s.id == npc.id } return unless Spell[spell].known? and Spell[spell].affordable? @@ -3432,7 +3866,7 @@ class Bigshot end def cmd_depress(npc, original_command) - debug_msg(@DEBUG_COMMANDS, "cmd_depress | npc: #{npc} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_depress | npc: #{npc}") return if npc.status.match?(/dead|gone/) return unless GameObj.targets.any? { |s| s.id == npc.id } @@ -3456,7 +3890,7 @@ class Bigshot end def cmd_phase(npc) - debug_msg(@DEBUG_COMMANDS, "cmd_phase | npc: #{npc} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_phase | npc: #{npc}") return if npc.status.match?(/dead|gone/) return unless GameObj.targets.any? { |s| s.id == npc.id } @@ -3468,7 +3902,7 @@ class Bigshot end def cmd_unravel(cmd, npc) - debug_msg(@DEBUG_COMMANDS, "cmd_unravel | npc: #{npc} | cmd: #{cmd} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_unravel | npc: #{npc} | cmd: #{cmd}") return if npc.status.match?(/dead|gone/) return unless GameObj.targets.any? { |s| s.id == npc.id } @@ -3511,7 +3945,7 @@ class Bigshot end def cmd_cmans(npc, cmd) - debug_msg(@DEBUG_COMMANDS, "cmd_cmans | npc: #{npc} | cmd: #{cmd} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_cmans | npc: #{npc} | cmd: #{cmd}") complete_regex = Regexp.union( /awkward proposition|You can't reach|little bit late|still stunned|too injured|what?|You cannot|Could not find|seconds/i, @@ -3625,7 +4059,7 @@ class Bigshot end def cmd_feats(npc, cmd) - debug_msg(@DEBUG_COMMANDS, "cmd_feats | npc: #{npc} | cmd: #{cmd} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_feats | npc: #{npc} | cmd: #{cmd}") complete_regex = Regexp.union( /awkward proposition|You can't reach|little bit late|still stunned|too injured|what?|You cannot|Could not find|seconds/i, @@ -3666,7 +4100,7 @@ class Bigshot end def cmd_jewel(mnemonic) - debug_msg(@DEBUG_COMMANDS, "cmd_jewel | called by #{caller[0]} | mnemonic #{mnemonic}") + debug_msg(@DEBUG_COMMANDS, "cmd_jewel") complete_regex = Regexp.union( # General jewel messaging: @@ -3730,7 +4164,7 @@ class Bigshot waitcastrt? break_out = Time.now() + 2 - loop { + loop do result = dothistimeout("gemstone activate #{mnemonic}", 2, complete_regex) if result =~ /\.\.\.wait/i waitrt? @@ -3739,11 +4173,11 @@ class Bigshot break end sleep(0.25) - } + end end def cmd_bearhug(npc, cmd) - debug_msg(@DEBUG_COMMANDS, "cmd_bearhug | npc: #{npc} | cmd: #{cmd} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_bearhug | npc: #{npc} | cmd: #{cmd}") complete_regex = Regexp.union( /You release your grip/i, @@ -3794,7 +4228,7 @@ class Bigshot end def cmd_rogue_cmans(npc, cmd) - debug_msg(@DEBUG_COMMANDS, "cmd_rogue_cmans | npc: #{npc} | cmd: #{cmd} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_rogue_cmans | npc: #{npc} | cmd: #{cmd}") complete_regex = Regexp.union( /awkward proposition|You can't reach|little bit late|still stunned|too injured|what?|You cannot|Could not find|seconds/i, @@ -3870,7 +4304,7 @@ class Bigshot end def cmd_warrior_shouts(npc, cmd) - debug_msg(@DEBUG_COMMANDS, "cmd_warrior_shouts | npc: #{npc} | cmd: #{cmd} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_warrior_shouts | npc: #{npc} | cmd: #{cmd}") cmd = cmd.downcase @@ -3916,7 +4350,7 @@ class Bigshot end def cmd_volnsmite(npc) - debug_msg(@DEBUG_COMMANDS, "cmd_volnsmite | npc: #{npc} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_volnsmite | npc: #{npc}") while !$bigshot_smite_list.any? { |a| a.to_i == npc.id.to_i } && npc.status !~ /dead|gone/ && GameObj.targets.any? { |s| s.id == npc.id } && !should_flee? && (npc.type.split(',').any? { |a| a == "undead" } || npc.type.split(',').any? { |a| a == "noncorporeal" }) res = dothistimeout "smite ##{npc.id}", 1, /^Roundtime|^What were you referring to\?$|^It looks like somebody already did the job for you\.$/ @@ -3943,7 +4377,7 @@ class Bigshot end def cmd_unarmed(command, npc, manualaim, stance_dance) - debug_msg(@DEBUG_COMMANDS, "cmd_unarmed | npc: #{npc} | command: #{command} | manualaim: #{manualaim} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_unarmed | npc: #{npc} | command: #{command} | manualaim: #{manualaim}") return if npc.status =~ /dead|gone/ || !GameObj.targets.any? { |s| s.id == npc.id } @@ -4041,7 +4475,7 @@ class Bigshot end def cmd_bless() - debug_msg(@DEBUG_COMMANDS, "cmd_bless | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_bless") while $bigshot_bless.count > 0 if Spell[1604].known? && Spell[1604].affordable? waitrt? @@ -4068,7 +4502,7 @@ class Bigshot end def cmd_assume(aspect, extra) - debug_msg(@DEBUG_COMMANDS, "cmd_assume | aspect: #{aspect} | extra: #{extra} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_assume | aspect: #{aspect} | extra: #{extra}") return unless Spell[650].known? return if Spell["Aspect of the #{aspect.capitalize()} Cooldown"].active? && Spell["Aspect of the #{extra.capitalize()} Cooldown"].active? @@ -4126,7 +4560,7 @@ class Bigshot end def cmd_briar(weapon) - debug_msg(@DEBUG_COMMANDS, "cmd_briar | weapon: #{weapon.inspect} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_briar | weapon: #{weapon.inspect}") return if (weapon.nil? || Spell[9105].active?) briar_weapons = [] @@ -4151,7 +4585,7 @@ class Bigshot end def cmd_throw(npc) - debug_msg(@DEBUG_COMMANDS, "cmd_throw | npc: #{npc} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_throw | npc: #{npc}") unless npc.status == 'lying down' empty_hands @@ -4162,7 +4596,7 @@ class Bigshot end def cmd_force(force_this, goal, npc, stance_dance) - debug_msg(@DEBUG_COMMANDS, "cmd_force | force_this: #{force_this} | npc: #{npc} | goal: #{goal} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_force | force_this: #{force_this} | npc: #{npc} | goal: #{goal}") result_regex = Regexp.union( /== \+(\d+)/, @@ -4209,7 +4643,7 @@ class Bigshot end def cmd_weed(command, target) - debug_msg(@DEBUG_COMMANDS, "cmd_weed | command: #{command} | target: #{target} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_weed | command: #{command} | target: #{target}") return if target.status =~ /dead|gone/ || !GameObj.targets.any? { |s| s.id == target.id } return if GameObj.loot.find { |loot| loot.name =~ /\b(?:vine|bramble|widgeonweed|vathor club|swallowwort|smilax|creeper|briar|ivy|tumbleweed)\b/ } @@ -4270,7 +4704,7 @@ class Bigshot end def cmd_spell(incant: nil, id: nil, extra: nil, target: nil) - debug_msg(@DEBUG_COMMANDS, "cmd_spell | incant: #{incant} | id: #{id} | extra: #{extra} | target: #{target} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_spell | incant: #{incant} | id: #{id} | extra: #{extra} | target: #{target}") selfcast = spell_is_selfcast?(id) @@ -4341,7 +4775,7 @@ class Bigshot end def cmd_wand(target) - debug_msg(@DEBUG_COMMANDS, "cmd_wand | target: #{target} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_wand | target: #{target}") if (@FRESH_WAND_CONTAINER) until ((GameObj.right_hand.name.to_s + GameObj.left_hand.name.to_s) =~ /#{@WAND[$bigshot_wand].split(' ').join('.*?')}/i) @@ -4381,7 +4815,7 @@ class Bigshot end def cmd_wandolier(target, stance) - debug_msg(@DEBUG_COMMANDS, "cmd_wandolier | target: #{target} | stance: #{stance} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_wandolier | target: #{target} | stance: #{stance}") if stance.empty? || stance.nil? stance = 'offensive' end @@ -4412,7 +4846,7 @@ class Bigshot end def cmd_stomp() - debug_msg(@DEBUG_COMMANDS, "cmd_stomp | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_stomp") return if !Spell[909].known? @@ -4428,7 +4862,7 @@ class Bigshot end def cmd_leech() - debug_msg(@DEBUG_COMMANDS, "cmd_leech | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_leech") return if !Spell[516].known? @@ -4440,7 +4874,7 @@ class Bigshot end def cmd_rapid(ignore = "") - debug_msg(@DEBUG_COMMANDS, "cmd_rapid | ignore: #{ignore} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_rapid | ignore: #{ignore}") return unless Spell[515].known? return unless Spell[515].affordable? @@ -4453,7 +4887,7 @@ class Bigshot end def cmd_efury(npc, extra) - debug_msg(@DEBUG_COMMANDS, "cmd_efury | npc: #{npc} | extra: #{extra} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_efury | npc: #{npc} | extra: #{extra}") return if npc.status =~ /dead|gone/ return unless GameObj.targets.any? { |s| s.id == npc.id } @@ -4495,7 +4929,7 @@ class Bigshot end def cmd_hide(attempts) - debug_msg(@DEBUG_COMMANDS, "cmd_hide | attempts: #{attempts} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_hide | attempts: #{attempts}") tries = 0 attempts = 3 if attempts == 0 until (hiding?) @@ -4508,7 +4942,7 @@ class Bigshot end def mstrike_spell_check() - debug_msg(@DEBUG_COMMANDS, "mstrike_spell_check | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "mstrike_spell_check") # Rejuvenation if (Spell[1607].known? && !Spell[1607].active? && Spell[1607].affordable? && (Char.stamina < (@MSTRIKE_STAMINA_COOLDOWN || @MSTRIKE_STAMINA_QUICKSTRIKE))) @@ -4536,7 +4970,7 @@ class Bigshot end def cmd_mstrike(command, target) - debug_msg(@DEBUG_COMMANDS, "cmd_mstrike | target: #{target} | command: #{command} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_mstrike | target: #{target} | command: #{command}") mstrike_spell_check() if Stats.prof =~ /Paladin|Empath/i # Mstrike checks @@ -4574,7 +5008,7 @@ class Bigshot end def check_target_vitals(command, target, ranged = false) - debug_msg(@DEBUG_COMBAT, "check_target_vitals | target: #{target} | command: #{command} | ranged: #{ranged} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "check_target_vitals | target: #{target} | command: #{command} | ranged: #{ranged}") regex = /(?:he|she|it)<\/a> has (.*)/i woundinfo = nil @@ -4607,7 +5041,7 @@ class Bigshot end def cmd_1040(target) - debug_msg(@DEBUG_COMMANDS, "cmd_1040 | target: #{target} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_1040 | target: #{target}") return if !Spell[1040].known? @@ -4626,7 +5060,7 @@ class Bigshot end def cmd_dhurl(target, command) - debug_msg(@DEBUG_COMMANDS, "cmd_dhurl | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_dhurl") if target.status =~ /dead|gone/ || !GameObj.targets.any? { |s| s.id == target.id } $bigshot_ambush = 0 return @@ -4670,7 +5104,7 @@ class Bigshot end def cmd_recover(weapon_lost = true) - debug_msg(@DEBUG_COMMANDS, "cmd_recover | weapon_lost: #{weapon_lost} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_recover | weapon_lost: #{weapon_lost}") until weapon_lost.eql?(false) break if $bigshot_bond_return.eql?(true) @@ -4698,7 +5132,7 @@ class Bigshot end def cmd_ranged(npc) - debug_msg(@DEBUG_COMMANDS, "cmd_ranged | npc: #{npc} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_ranged | npc: #{npc}") if npc.status =~ /dead|gone/ || !GameObj.targets.any? { |s| s.id == npc.id } $bigshot_archery_aim = 0 @@ -4726,9 +5160,7 @@ class Bigshot waitcastrt? result = dothistimeout("fire ##{npc.id}", 2, /round(time)?|You cannot|Could not find|seconds|Get what?/i) - if (result =~ /^Could not find/) - # gather_ammo()#Not used since archery updates. - elsif (result =~ /You cannot fire/) + if (result =~ /You cannot fire/) unless GameObj.right_hand.id.nil? line = dothistimeout "stow ##{GameObj.right_hand.id}", 3, /put|closed/ if line =~ /closed/ @@ -4750,7 +5182,7 @@ class Bigshot end def cmd_dislodge(npc, location) - debug_msg(@DEBUG_COMMANDS, "cmd_dislodge | npc: #{npc} | location: #{location} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_dislodge | npc: #{npc} | location: #{location}") return if !CMan.available?("Dislodge") return if npc.id != $bigshot_dislodge_target @@ -4781,7 +5213,7 @@ class Bigshot end def cmd_burst() - debug_msg(@DEBUG_COMMANDS, "cmd_burst | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_burst") return unless CMan.known?("Burst of Swiftness") return if Effects::Buffs.to_h.keys.grep(/Enh. Dexterity/).any? @@ -4801,7 +5233,7 @@ class Bigshot end def cmd_surge() - debug_msg(@DEBUG_COMMANDS, "cmd_surge | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_surge") return unless CMan.known?("Surge of Strength") return if Effects::Buffs.to_h.keys.grep(/Enh\. Strength/).any? @@ -4821,7 +5253,7 @@ class Bigshot end def cmd_berserk() - debug_msg(@DEBUG_COMMANDS, "cmd_berserk | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_berserk") if (Char.stamina >= 20) change_stance(@WANDER_STANCE) @@ -4834,7 +5266,7 @@ class Bigshot end def cmd_run_script(name, args) - debug_msg(@DEBUG_SYSTEM, "cmd_run_script | name: #{name} | args: #{args} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "cmd_run_script | name: #{name} | args: #{args}") if (args == nil || args =~ /^\s*$/) run_script(name, true) @@ -4844,7 +5276,7 @@ class Bigshot end def cmd_sleep(time, nostance, npc) - debug_msg(@DEBUG_COMMANDS, "cmd_sleep | npc: #{npc} | time: #{time} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_sleep | npc: #{npc} | time: #{time}") change_stance(@WANDER_STANCE) unless nostance time.to_i.times do @@ -4855,7 +5287,7 @@ class Bigshot end def cmd_ambush(command, target) - debug_msg(@DEBUG_COMMANDS, "cmd_ambush | target: #{target} | command: #{command} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_ambush | target: #{target} | command: #{command}") if target.status =~ /dead|gone/ || !GameObj.targets.any? { |s| s.id == target.id } $bigshot_ambush = 0 @@ -4884,7 +5316,7 @@ class Bigshot end def cmd_nudge_weapons() - debug_msg(@DEBUG_COMMANDS, "cmd_nudge_weapons | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_nudge_weapons") return if checkpaths.size == 0 @@ -4914,7 +5346,7 @@ class Bigshot end def cmd_tether(npc, recast_on_transfer = false) - debug_msg(@DEBUG_COMMANDS, "cmd_tether | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cmd_tether") return if npc.status =~ /dead|gone/ return unless GameObj.targets.any? { |s| s.id == npc.id } @@ -4974,7 +5406,7 @@ class Bigshot end def group_status_ailments() - debug_msg(@DEBUG_STATUS, "group_status_ailments | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "group_status_ailments") if @TROUBADOURS_RALLY && Spell[1040].known? if webbed? || sleeping? || stunned? || frozen? @@ -5022,7 +5454,7 @@ class Bigshot end def dead_man_switch() - debug_msg(@DEBUG_SYSTEM, "dead_man_switch | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "dead_man_switch") if @DEAD_MAN_SWITCH && XMLData.game =~ /GSF/ Thread.new { @@ -5059,7 +5491,7 @@ class Bigshot end def monitor_interaction() - debug_msg(@DEBUG_SYSTEM, "monitor_interaction | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "monitor_interaction") if @MONITOR_INTERACTION start_exec_script(<<-eos @@ -5096,7 +5528,7 @@ class Bigshot npcs.delete_if { |npc| CharSettings['untargetable'].include?(npc.name) } npcs.delete_if { |npc| npc.noun =~ /^(?:arm|appendage|claw|limb|pincer|tentacle)s?$|^(?:palpus|palpi)$/i } - debug_msg(@DEBUG_COMBAT, "gameobj_npc_check | npcs.size: #{npcs.size.to_i} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "gameobj_npc_check | npcs.size: #{npcs.size.to_i}") return npcs.size.to_i end @@ -5110,7 +5542,7 @@ class Bigshot end def wrack() - debug_msg(@DEBUG_COMMANDS, "wrack | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "wrack") if Spell[9918].known? && !Spell[9012].active? && Char.spirit >= @WRACKING_SPIRIT && Char.spirit >= (6 + [9912, 9913, 9914, 9916, 9916, 9916].count { |num| Spell[num].active? }) Spell[9918].cast @@ -5131,7 +5563,7 @@ class Bigshot end def change_stance(new_stance, force = true) - debug_msg(@DEBUG_COMMANDS, "change_stance | new_stance: #{new_stance} | force: #{force} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "change_stance | new_stance: #{new_stance} | force: #{force}") return if Spell[216].active? || dead? return unless checkroom('Ooze, Innards').nil? new_stance = new_stance.downcase @@ -5161,7 +5593,7 @@ class Bigshot end def wait_for_swing(seconds, target = nil) - debug_msg(@DEBUG_COMBAT, "wait_for_swing | target: #{target} | seconds: #{seconds} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "wait_for_swing | target: #{target} | seconds: #{seconds}") start = Time.now # swung = false @@ -5195,14 +5627,14 @@ class Bigshot end def croak(message) - debug_msg(@DEBUG_SYSTEM, "croak | message: #{message} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "croak | message: #{message}") message("yellow", message); croak_scripts(["#{$current_script_name}"]) end def run_script(name, pause_bigshot = false, args = nil) - debug_msg(@DEBUG_SYSTEM, "run_script | name: #{name} | pause_bigshot: #{pause_bigshot} | args: #{args} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "run_script | name: #{name} | pause_bigshot: #{pause_bigshot} | args: #{args}") if Script.running.find { |s| s.name.downcase == name.downcase }.paused || Script.running?(name) Script.kill(name) @@ -5222,7 +5654,7 @@ class Bigshot end def run_scripts(scripts, pause_bigshot = false) - debug_msg(@DEBUG_SYSTEM, "run_scripts | scripts: #{scripts} | pause_bigshot: #{pause_bigshot} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "run_scripts | scripts: #{scripts} | pause_bigshot: #{pause_bigshot}") scripts.each do |i| tokens = i.split(/\s+/) @@ -5235,7 +5667,7 @@ class Bigshot end def croak_script(name) - debug_msg(@DEBUG_SYSTEM, "croak_script | script: #{name} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "croak_script | script: #{name}") # check for script name string with args name = name.split(/\s+/).first @@ -5243,7 +5675,7 @@ class Bigshot end def croak_scripts(scripts) - debug_msg(@DEBUG_SYSTEM, "croak_script | scripts: #{scripts} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "croak_script | scripts: #{scripts}") scripts.each { |i| croak_script(i) } end @@ -5268,7 +5700,7 @@ class Bigshot end def stand(stand_command = nil) - debug_msg(@DEBUG_COMMANDS, "stand | stand_command: #{stand_command} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "stand | stand_command: #{stand_command}") return if Array(stand_command).any? { |cmd| cmd =~ /^(?:fire|kneel|hide)|^(?:incant )?608\b/i } && kneeling? && checkleft =~ /^(?:arbalest|kut'ziko|crossbow|kut'zikokra)$/ @@ -5284,55 +5716,49 @@ class Bigshot def groupcheck() Lich::Gemstone::Group.check unless Lich::Gemstone::Group.checked? && !Lich::Gemstone::Group.broken? - debug_msg(@DEBUG_STATUS, "groupcheck | Group.members.map(&:noun): #{Lich::Gemstone::Group.members.map(&:noun)} | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "groupcheck | Group.members.map(&:noun): #{Lich::Gemstone::Group.members.map(&:noun)}") end def bigclaim?(check_disks: true) - debug_msg(@DEBUG_STATUS, "bigclaim? | $bigshot_quick: #{$bigshot_quick} | Claim.mine?: #{Lich::Gemstone::Claim.mine?} | Disks: #{(Lich::Gemstone::Disk.all - Lich::Gemstone::Group.disks)} | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "bigclaim? | $bigshot_quick: #{$bigshot_quick} | Claim.mine?: #{Lich::Gemstone::Claim.mine?} | Disks: #{(Lich::Gemstone::Disk.all - Lich::Gemstone::Group.disks)}") + return true unless Char.name == @leader return true if $bigshot_quick - return @group.leader_claim?(check_disks: check_disks) if Char.name != @leader return false unless Lich::Gemstone::Claim.mine? return false unless @IGNORE_DISKS || (!check_disks || (Lich::Gemstone::Disk.all - Lich::Gemstone::Group.disks).empty?) return true end def ma_looter - looter = nil - - if solo? - looter = Char.name - elsif @RANDOM_LOOT - encumbrance_hash = @followers.group_encumbrance - encumbrance_hash[Char.name] = (@ENCUMBERED - Char.percent_encumbrance) # Add the leader - encumbrance_hash.reject! { |key, _value| @NEVER_LOOT.include?(key) } # Remove any character in the no-loot list + # Solo: the player is always the looter + return Char.name if solo? - # Remove or handle nil values - encumbrance_hash.compact! + # Designated looter: pick by name if set + unless @MA_LOOTER.to_s.empty? + looter = @followers.get_names.find { |name| name =~ /#{@MA_LOOTER}/i } + return looter if looter + end - # Find the max - max_value = encumbrance_hash.values.max + # Random loot mode + if @RANDOM_LOOT + encumbrance = @followers.group_encumbrance + encumbrance[Char.name] = (@ENCUMBERED - Char.percent_encumbrance) + encumbrance.reject! { |name, _| @NEVER_LOOT.include?(name) } + encumbrance.compact! - # Find all party members with the same amount - all_max_values = encumbrance_hash.select { |_key, value| value == max_value }.keys + max_value = encumbrance.values.max + candidates = encumbrance.select { |_name, v| v == max_value }.keys - if all_max_values.include?(@MA_LOOTER) # favor the designated looter - looter = @MA_LOOTER - else - looter = all_max_values.sample # otherwise just pick one at random - end - elsif !@MA_LOOTER.to_s.empty? - looter = @followers.get_names.find { |name| name =~ /#{@MA_LOOTER}/i } - else - looter_list = @followers.get_names.reject { |element| @NEVER_LOOT.include?(element) } - if looter_list.include?(@followers.leader_name) - looter = @followers.leader_name - else - looter = looter_list.sample - end + # Favor designated looter if among max, else pick random + looter = candidates.include?(@MA_LOOTER) ? @MA_LOOTER : candidates.sample + return looter end - debug_msg(@DEBUG_STATUS, "ma_looter | looter: #{looter} | called by #{caller[0]}") + # Fallback: leader if eligible, otherwise random eligible follower + eligible = @followers.get_names.reject { |name| @NEVER_LOOT.include?(name) } + looter = eligible.include?(@followers.leader_name) ? @followers.leader_name : eligible.sample + + debug_msg(@DEBUG_STATUS, "ma_looter | looter: #{looter}") return looter end @@ -5341,7 +5767,7 @@ class Bigshot end def lead(my_group = nil) - debug_msg(@DEBUG_STATUS, "lead | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "lead") monitor_interaction() npc_room_check() @@ -5354,42 +5780,39 @@ class Bigshot end def find_routine(target) - debug_msg(@DEBUG_COMBAT, "find_routine | target: #{target} | @DISABLE_COMMANDS.size: #{@DISABLE_COMMANDS.size} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "find_routine | target: #{target} | @DISABLE_COMMANDS.size: #{@DISABLE_COMMANDS.size}") + # DISABLE override always wins if (!solo? && fried? && @DISABLE_COMMANDS.size > 0) return @DISABLE_COMMANDS - else - key = @TARGETS.keys.find { |k| target.name =~ /^#{k}$/i or target.noun =~ /^#{k}$/i } - if key.nil? - routine_letter = 'a' - else - routine_letter = @TARGETS[key] - end + end - if routine_letter == 'quick' || ($bigshot_quick && key.nil?) - return @QUICK_COMMANDS unless @QUICK_COMMANDS.size == 0 - elsif routine_letter == 'j' - return @HUNTING_COMMANDS_J unless @HUNTING_COMMANDS_J.size == 0 - elsif routine_letter == 'i' - return @HUNTING_COMMANDS_I unless @HUNTING_COMMANDS_I.size == 0 - elsif routine_letter == 'h' - return @HUNTING_COMMANDS_H unless @HUNTING_COMMANDS_H.size == 0 - elsif routine_letter == 'g' - return @HUNTING_COMMANDS_G unless @HUNTING_COMMANDS_G.size == 0 - elsif routine_letter == 'f' - return @HUNTING_COMMANDS_F unless @HUNTING_COMMANDS_F.size == 0 - elsif routine_letter == 'e' - return @HUNTING_COMMANDS_E unless @HUNTING_COMMANDS_E.size == 0 - elsif routine_letter == 'd' - return @HUNTING_COMMANDS_D unless @HUNTING_COMMANDS_D.size == 0 - elsif routine_letter == 'c' - return @HUNTING_COMMANDS_C unless @HUNTING_COMMANDS_C.size == 0 - elsif routine_letter == 'b' - return @HUNTING_COMMANDS_B unless @HUNTING_COMMANDS_B.size == 0 - end + key = @TARGETS.keys.find { |k| target.name =~ /^#{k}$/i || target.noun =~ /^#{k}$/i } + routine_letter = key.nil? ? 'a' : @TARGETS[key] - return @HUNTING_COMMANDS + # Quick checks first + if routine_letter == 'quick' || ($bigshot_quick && key.nil?) + return @QUICK_COMMANDS unless @QUICK_COMMANDS.empty? end + + # Lookup table for letters + lookup = { + 'b' => @HUNTING_COMMANDS_B, + 'c' => @HUNTING_COMMANDS_C, + 'd' => @HUNTING_COMMANDS_D, + 'e' => @HUNTING_COMMANDS_E, + 'f' => @HUNTING_COMMANDS_F, + 'g' => @HUNTING_COMMANDS_G, + 'h' => @HUNTING_COMMANDS_H, + 'i' => @HUNTING_COMMANDS_I, + 'j' => @HUNTING_COMMANDS_J + } + + commands = lookup[routine_letter] + return commands unless commands.nil? || commands.empty? + + # fallback to default + return @HUNTING_COMMANDS end def solo? @@ -5406,7 +5829,7 @@ class Bigshot end def no_players_hunt() - debug_msg(@DEBUG_COMBAT, "no_players_hunt | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "no_players_hunt") return true if $bigshot_quick return false if $ambusher_here @@ -5414,7 +5837,7 @@ class Bigshot end def pre_hunt(manually_walking = false) - debug_msg(@DEBUG_COMBAT, "pre_hunt | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "pre_hunt") if (!solo? && leading?) @followers.group_assist(true) @@ -5426,7 +5849,6 @@ class Bigshot if !$bigshot_quick && !manually_walking unless @INDEPENDENT_TRAVEL @followers.add_event(:FOLLOW_NOW) # trigger rubber band - @followers.add_event(:JOIN_LEADER) message("yellow", "Waiting for followers....") if !@followers.all_present? sleep(0.5) while (!@followers.all_present?) end @@ -5435,8 +5857,9 @@ class Bigshot if @INDEPENDENT_TRAVEL Lich::Util.quiet_command_xml("disband group", /You have no group to disband|You disband your group/) - until Lich::Gemstone::Group.members.map(&:noun).empty? + until Lich::Gemstone::Group.empty? groupcheck() + sleep(0.25) end @followers.add_event(:GO2_RALLY_ROOM) @RALLYPOINT_ROOM_IDS.each { |room| go2(room.to_i) } @@ -5452,7 +5875,7 @@ class Bigshot end end - Lich::Util.quiet_command_xml("group open", /Your group status/) + Lich::Util.quiet_command_xml("group open", /Your group status/) unless solo? @followers.add_event(:FOLLOW_NOW) # trigger rubber band message("yellow", "Waiting for followers....") if !@followers.all_present? sleep 0.5 while (!@followers.all_present?) @@ -5495,7 +5918,7 @@ class Bigshot check_for_deaders_prone - @followers.add_event(:JOIN_LEADER) + @followers.add_event(:FOLLOW_NOW) @followers.add_event(:CAST_SIGNS) # make sure we're hiding if we're sneaking @@ -5516,12 +5939,10 @@ class Bigshot if @SIGNS.any? { |s| ['902', '411'].include?(s) } check_902_411() end - - cast_signs() end def do_hunt() # Finds target and calls attack block - debug_msg(@DEBUG_COMBAT, "do_hunt | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "do_hunt") start_watch() message("yellow", 'Bigshot hunting') @@ -5543,7 +5964,7 @@ class Bigshot while ((target = find_target(target, just_arrived)) && !should_rest? && no_players_hunt && !should_flee?) debug_msg(@DEBUG_COMBAT, "inside do_hunt loop | target: #{target}") - if @PRIORITY && !priority(target) + if !priority(target) target = find_target(nil) end @@ -5570,6 +5991,15 @@ class Bigshot sleep 0.5 end + # ensure everyone is grouped before continuing + if (!solo? && leading?) && (!@followers.all_present? || Lich::Gemstone::Group.broken?) + Lich::Util.quiet_command_xml("group open", /Your group status/) + @followers.add_event(:FOLLOW_NOW) # trigger rubber band + message("yellow", "Waiting for followers....") + sleep(0.5) while (!@followers.all_present?) + groupcheck + end + # return or exit if needed return if should_rest? single_stop if $bigshot_quick @@ -5582,7 +6012,7 @@ class Bigshot # this is a leader method def hunt() - debug_msg(@DEBUG_COMBAT, "hunt | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "hunt") pre_hunt() do_hunt() rest() @@ -5590,18 +6020,18 @@ class Bigshot # this is a leader method def rest() - debug_msg(@DEBUG_COMBAT, "rest | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "rest") $bigshot_group_status[Char.name] = $rest_reason sorted_hash = $bigshot_group_status.sort_by { |key, _| [key == Char.name ? -1 : 0, key.to_s] }.to_h any_wounded = $bigshot_group_status.values.include?("wounded.") - _respond - sorted_hash.each { |name, reason| + respond + sorted_hash.each do |name, reason| next if reason.nil? message("yellow", " #{name} resting: #{reason}") - } - _respond + end + respond # Stop bigshot hunting monitor @followers.add_event(:HUNT_MONITOR_STOP) @@ -5611,8 +6041,8 @@ class Bigshot msg = " Exiting: Bounty mode with child rescue." message("yellow", msg) @BOUNTY_EVAL = '' - single_stop() @followers.add_event(:SINGLE_STOP) + single_stop() end stop_watch() @@ -5627,12 +6057,11 @@ class Bigshot $bigshot_should_rest = false $bigshot_overkill_counter = 0 $bigshot_lte_boost_counter = 0 + @BOON_CACHE = {} # Wait for follower to finish looting if needed - while !@followers.looting_done - $looting_inactive = @followers.looting_done - sleep 0.5 - end + wait_while { !@followers.looting_done } + $looting_inactive = true wait_while { @followers.roundtime? } prepare_for_movement @@ -5652,6 +6081,7 @@ class Bigshot Lich::Util.quiet_command_xml("disband group", /You have no group to disband|You disband your group/) until Lich::Gemstone::Group.empty? groupcheck() + sleep(0.25) end end @@ -5688,7 +6118,7 @@ class Bigshot escape_rooms if (@QUIET_FOLLOWERS) && !any_wounded - Lich::Util.quiet_command_xml("group open", /Your group status/) + Lich::Util.quiet_command_xml("group open", /Your group status/) unless solo? @followers.add_event(:FOLLOW_NOW) message("yellow", "Waiting for followers....") if !@followers.all_present? while (!@followers.all_present?) @@ -5711,9 +6141,9 @@ class Bigshot end # join everyone back to the leader - Lich::Util.quiet_command_xml("group open", /Your group status/) - @followers.add_event(:JOIN_LEADER) - message("yellow", "Waiting for followers....") if (!@followers.all_present? || @followers.roundtime? || !@followers.rest_prep_complete?) + Lich::Util.quiet_command_xml("group open", /Your group status/) unless solo? + @followers.add_event(:FOLLOW_NOW) + message("yellow", "Waiting for followers to finish....") if (!@followers.all_present? || @followers.roundtime? || !@followers.rest_prep_complete?) wait_while { !@followers.all_present? } wait_while { @followers.roundtime? } wait_while { !@followers.rest_prep_complete? } @@ -5735,19 +6165,19 @@ class Bigshot until (should_hunt?) @followers.add_event(:DISPLAY_WATCH) - fput 'exp' - sleep 0.2 - display_watch() + display_watch + + respond message("yellow", "Bigshot last rested because: #{$rest_reason}") unless $rest_reason.nil? $bigshot_group_status[Char.name] = $not_hunting_reason sorted_hash = $bigshot_group_status.sort_by { |key, _| [key == Char.name ? -1 : 0, key.to_s] }.to_h - sorted_hash.each { |name, reason| + sorted_hash.each do |name, reason| next if reason.nil? || reason == "ready" message("yellow", "#{name} isn't hunting because: #{reason}") - } - _respond + end + respond sleep($bigshot_rest_interval) end @@ -5759,7 +6189,7 @@ class Bigshot end def mana_pulse(spell_id) - debug_msg(@DEBUG_COMMANDS, "mana_pulse | spell_id: #{spell_id} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "mana_pulse | spell_id: #{spell_id}") return unless Spell[spell_id].known? && !Spell[spell_id].affordable? @@ -5775,7 +6205,7 @@ class Bigshot end def fog_return_spirit(from_voln = false) - debug_msg(@DEBUG_COMBAT, "fog_return_spirit | from_voln: #{from_voln} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "fog_return_spirit | from_voln: #{from_voln}") current_room = Room.current.id mana_pulse(130) @@ -5799,7 +6229,7 @@ class Bigshot end def fog_return_voln(from_130 = false) - debug_msg(@DEBUG_COMBAT, "fog_return_voln | from_130: #{from_130} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "fog_return_voln | from_130: #{from_130}") current_room = Room.current.id if Spell[9825].known? @@ -5819,7 +6249,7 @@ class Bigshot end def fog_return - debug_msg(@DEBUG_COMBAT, "fog_return | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "fog_return") return if @FOG_RETURN.to_i.zero? return if @FOG_OPTIONAL && $rest_reason !~ /wounded|encumbered/ @@ -5869,28 +6299,28 @@ class Bigshot def attack_break(target) break_conditions = { - "No Claim" => !bigclaim?(check_disks: false), - "Invalid target" => !valid_target?(target), - "Should Rest" => $bigshot_should_rest, - "Ambusher present" => $ambusher_here, - "Should rest" => should_rest?, - "Priority target available" => !$bigshot_bandits && @PRIORITY && !priority(target), - "Event stack has PREP_REST" => @event_stack.any? { |a| a.type == :PREP_REST } + "No Claim" => -> { !bigclaim?(check_disks: false) }, + "Invalid target" => -> { !valid_target?(target) }, + "Should Rest" => -> { $bigshot_should_rest }, + "Ambusher present" => -> { $ambusher_here }, + "Should rest" => -> { should_rest? }, + "Priority target available" => -> { !$bigshot_bandits && @PRIORITY && !priority(target) }, + "Event stack has PREP_REST" => -> { @event_stack.any? { |a| a.type == :PREP_REST } } } - break_conditions.each do |condition, result| - if result + break_conditions.each do |condition, check| + if check.call debug_msg(@DEBUG_COMBAT, "attack_break| target: #{target} | returning true from attack_break | reason: #{condition}") return true end end - return false + false end def attack(target) # Starts the process when a target is found commands = find_routine(target) - debug_msg(@DEBUG_COMBAT, "attack | commands #{commands} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "attack | commands #{commands}") if XMLData.current_target_id != target.id # Rest variables and retarget if target changed @@ -5904,6 +6334,17 @@ class Bigshot commands.each do |i| break if attack_break(target) + if (!solo? && leading?) && (!@followers.all_present? || Lich::Gemstone::Group.broken?) + @followers.add_event(:FOLLOW_NOW) # trigger rubber band + Lich::Util.quiet_command_xml("group open", /Your group status/) + message("yellow", "A follower is missing. Calling them back") + end + + if @leader != Char.name && !checkpcs.include?(@leader) + group_all_followers + break + end + group_status_ailments() if @TROUBADOURS_RALLY && Spell[1040].known? stand(i) if !standing? cast_signs() @@ -5941,7 +6382,7 @@ class Bigshot # determine the looter @DESIGNATED_LOOTER = ma_looter - debug_msg(@DEBUG_COMBAT, "need_to_loot | final_loot: #{final_loot} | @DESIGNATED_LOOTER: #{@DESIGNATED_LOOTER} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "need_to_loot | final_loot: #{final_loot} | @DESIGNATED_LOOTER: #{@DESIGNATED_LOOTER}") $looting_inactive = false if Char.name == @DESIGNATED_LOOTER @@ -5963,7 +6404,7 @@ class Bigshot end def loot() - debug_msg(@DEBUG_COMBAT, "loot | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "loot") # get a list of dead npcs dead_npcs = GameObj.npcs.find_all { |i| i.status == 'dead' && i.type !~ /escort/i } @@ -6015,7 +6456,7 @@ class Bigshot end def go2(place) - debug_msg(@DEBUG_COMMANDS, "go2 | place: #{place} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "go2 | place: #{place}") fput('unhide') if (hidden? || invisible?) return if Room.current.id == place.to_i || Room.current.tags.include?(place) @@ -6023,7 +6464,7 @@ class Bigshot end def goto(id, cast_signs_moving = true) - debug_msg(@DEBUG_COMMANDS, "goto | id: #{id} | cast_signs_moving: #{cast_signs_moving} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "goto | id: #{id} | cast_signs_moving: #{cast_signs_moving}") prepare_for_movement(cast_signs_moving) @@ -6033,14 +6474,14 @@ class Bigshot end def start_watch() - debug_msg(@DEBUG_SYSTEM, "start_watch | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "start_watch") @START_TIME = Time.now.to_i @followers.add_event(:START_WATCH) end def stop_watch() - debug_msg(@DEBUG_SYSTEM, "stop_watch | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "stop_watch") if (@START_TIME > 100) @STORED_TIMES.push(Time.now.to_i - @START_TIME) @@ -6050,7 +6491,10 @@ class Bigshot end def display_watch() - debug_msg(@DEBUG_SYSTEM, "display_watch | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "display_watch") + + fput 'exp' + sleep 0.2 # current if (@STORED_TIMES.size > 0) @@ -6075,7 +6519,7 @@ class Bigshot end def display_items_for_blessing() - debug_msg(@DEBUG_STATUS, "display_items_for_blessing | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "display_items_for_blessing") $bigshot_bless = $bigshot_bless - ["", nil] bless_bundles = false @@ -6095,7 +6539,7 @@ class Bigshot end def single_stop() - debug_msg(@DEBUG_SYSTEM, "single_stop | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "single_stop") Script.self.kill sleep 0.5 @@ -6110,7 +6554,7 @@ class Bigshot end def perform_reaction() - debug_msg(@DEBUG_COMBAT, "performing weapon reaction | $bigshot_reaction: #{$bigshot_reaction} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "performing weapon reaction | $bigshot_reaction: #{$bigshot_reaction}") original_stance = Char.stance change_stance(@HUNTING_STANCE) fput "weapon #{$bigshot_reaction}" @@ -6119,13 +6563,85 @@ class Bigshot end def get_grouplist() - debug_msg(@DEBUG_STATUS, "get_grouplist | Group.members.map(&:noun): #{Lich::Gemstone::Group.members.map(&:noun)} | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "get_grouplist | Group.members.map(&:noun): #{Lich::Gemstone::Group.members.map(&:noun)}") return Lich::Gemstone::Group.members.map(&:noun) end + def check_boons(creature) + debug_msg(@DEBUG_COMBAT, "check_boons | creature: #{creature.inspect}") + + return nil unless creature.type.include?("boon") + + # Return cached result if it exists + return @BOON_CACHE[creature.id] if @BOON_CACHE.key?(creature.id) + + # Otherwise, do the assessment + lines = Lich::Util.quiet_command_xml( + "assess ##{creature.id}", + /The .*<\/a>|You do not currently have a target./ + ) + + text = lines.find { |l| l.include?("appears to be") } + unless text + @BOON_CACHE[creature.id] = nil + return nil + end + + # Clean out XML tags + cleaned = text.gsub(/<[^>]+>/, '') + + # Capture descriptors after "appears to be" + match = cleaned.match(/appears to be (.+?)(?:\.|$)/i) + unless match + @BOON_CACHE[creature.id] = nil + return nil + end + + phrase = match[1].downcase.strip + + # Split by commas or "and" (preserve multi-word adjectives) + boons = phrase.split(/\s*(?:,|and)\s*/i).map(&:strip) + + abilities = boons.map { |b| @BOON_ABILITIES[b] }.compact.uniq + + # Cache the result + @BOON_CACHE[creature.id] = abilities.empty? ? nil : abilities + end + + def invalid_target_with_boons(creature) + debug_msg(@DEBUG_COMBAT, "invalid_target_with_boons | creature: #{creature.inspect}") + + return false if @BOONS_IGNORE.empty? + return false unless creature.type.include?("boon") + + abilities = check_boons(creature) + return false unless abilities + + return true if (abilities & @BOONS_IGNORE).any? + end + + def should_flee_from_boons? + debug_msg(@DEBUG_COMBAT, "should_flee_from_boons?") + + return false if @BOONS_FLEE.empty? + + GameObj.targets.each do |creature| + next unless creature.type.include?("boon") + + # Convert adjectives → trait names + abilities = check_boons(creature) + next unless abilities + + # If any ability appears in your flee list → flee + return true if (abilities & @BOONS_FLEE).any? + end + + return false + end + def should_flee?(just_entered = false) - debug_msg(@DEBUG_COMBAT, "should_flee | just_entered: #{just_entered} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "should_flee | just_entered: #{just_entered}") return false if $bigshot_quick return true if $bigshot_flee @@ -6140,19 +6656,22 @@ class Bigshot return true if GameObj.npcs.any? { |i| @ALWAYS_FLEE_FROM.include?(i.noun) or @ALWAYS_FLEE_FROM.include?(i.name) } return true if checkpcs.any? { |i| @ALWAYS_FLEE_FROM.include?(i) } - return false if $bigshot_bandits - return true if GameObj.targets.any? { |i| i.type =~ /boon/ } && @BOON_FLEE_FROM + return false if $bigshot_bandits + return true if @BOONS_FLEE.any? && GameObj.targets.any? { |c| c.type.include?("boon") } && should_flee_from_boons? return true if !leading? && checkpcs.empty? - npcs = GameObj.targets.find_all { |i| i.status !~ /dead|gone/ } - npcs.delete_if { |npc| (@INVALID_TARGETS.include?(npc.name) or @INVALID_TARGETS.include?(npc.noun)) } - npcs.delete_if { |npc| CharSettings['untargetable'].include?(npc.name) } - npcs.delete_if { |npc| npc.noun =~ /^(?:arm|appendage|claw|limb|pincer|tentacle)s?$|^(?:palpus|palpi)$/i } - npcs.delete_if { |npc| npc.noun =~ /^(?:grik|grik'trak|grik'mlar|grik'pwal|grik'tval|verlok|verlok'asha|verlok'cina|verlok'ar|imp|abyran|abyran'a|abyran'sa|grantris|igaesha|haze|rouk|brume|haar|murk|nyle|mist|smoke|vapor|fog|aishan|shien|darkling|shadowling|arashan)$/i } - npcs.delete_if { |npc| ['quickly growing troll king', 'severed troll arm', 'severed troll leg'].include?(npc.name) } - npcs.delete_if { |npc| npc.type =~ /companion|familiar/i && npc.type !~ /aggressive npc/i } + npcs = GameObj.targets.reject! do |npc| + npc.status =~ /dead|gone/ || + @INVALID_TARGETS.include?(npc.name) || + @INVALID_TARGETS.include?(npc.noun) || + CharSettings['untargetable'].include?(npc.name) || + npc.noun =~ /^(?:arm|appendage|claw|limb|pincer|tentacle)s?$|^(?:palpus|palpi)$/i || + npc.noun =~ /^(?:grik|grik'trak|grik'mlar|grik'pwal|grik'tval|verlok|verlok'asha|verlok'cina|verlok'ar|imp|abyran|abyran'a|abyran'sa|grantris|igaesha|haze|rouk|brume|haar|murk|nyle|mist|smoke|vapor|fog|aishan|shien|darkling|shadowling|arashan)$/i || + ['quickly growing troll king', 'severed troll arm', 'severed troll leg'].include?(npc.name) || + (npc.type =~ /companion|familiar/i && npc.type !~ /aggressive npc/i) + end || GameObj.targets flee_count = (just_entered && @LONE_TARGETS_ONLY) ? 1 : @FLEE_COUNT @@ -6160,7 +6679,7 @@ class Bigshot end def valid_target?(target, just_entered = false) - debug_msg(@DEBUG_COMBAT, "valid_target? | target: #{target} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "valid_target? | target: #{target}") # Initial set of invalid conditions invalid_conditions = { @@ -6169,6 +6688,7 @@ class Bigshot 'target.status =~ /dead|gone/' => target.status =~ /dead|gone/, 'target gone from GameObj.targets collection' => GameObj.targets.none? { |n| n.id == target.id }, 'target.name contains animated but not an animated slush/' => target.name =~ /animated/ && target.name !~ /animated slush/, + 'targets with boons are being ignored' => (target.type.include?("boon") && invalid_target_with_boons(target)) } invalid_conditions.each do |condition, result| @@ -6193,88 +6713,70 @@ class Bigshot end if CharSettings['untargetable'].include?(target.name) - debug_msg(@DEBUG_COMBAT, "valid_target? | returning false - untargetable | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "valid_target? | returning false - untargetable") return false end - if (@TARGETS.nil? or @TARGETS.keys.any? { |i| target.name =~ /#{i}/i or target.noun =~ /#{i}/i }) - debug_msg(@DEBUG_COMBAT, "valid_target? | returning true for #{target.name} | called by #{caller[0]}") + if (@TARGETS.nil? || @TARGETS.keys.any? { |i| target.name =~ /#{i}/i || target.noun =~ /#{i}/i }) + debug_msg(@DEBUG_COMBAT, "valid_target? | returning true for #{target.name}") return true else - debug_msg(@DEBUG_COMBAT, "valid_target? | not sure what this is. returning false for #{target.name} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "valid_target? | not sure what this is. returning false for #{target.name}") return false end end def sort_npcs() - # If statement just figures out the target hash if $bigshot_quick || $bigshot_bandits - targets = Hash.new - - # Initialize @QUICKHUNT_TARGETS only if it's nil - @QUICKHUNT_TARGETS ||= Hash.new + # Initialize hashs if needed + @QUICKHUNT_TARGETS ||= {} + @TARGETS ||= {} - # Collect non-dead, non-gone NPCs - npcs = GameObj.targets.reject { |i| i.status =~ /dead|gone/ } + if @QUICKHUNT_TARGETS.empty? + GameObj.targets.each do |npc| + next if CharSettings['untargetable'].include?(npc.name) + next if $bigshot_bandits && npc.noun !~ @BANDIT_NOUN_REGEX - # Remove untargetable NPCs - npcs.reject! { |npc| CharSettings['untargetable'].include?(npc.name) } - - # Further filter for bandits, if required - if $bigshot_bandits - npcs.reject! { |npc| npc.noun !~ @BANDIT_NOUN_REGEX } + @QUICKHUNT_TARGETS[npc.name] = "quick" + end end - # Collect unique NPC names - tokens = npcs.map(&:name).uniq - - # Assign "quick" to each token if @QUICKHUNT_TARGETS is empty - tokens.each { |i| targets[i] = "quick" } if @QUICKHUNT_TARGETS.empty? - - # Initialize and merge targets into @TARGETS - @TARGETS ||= Hash.new - @TARGETS.replace(@QUICKHUNT_TARGETS).merge!(targets) - - targets = @TARGETS + # Set @TARGETS with @QUICKHUNT_TARGETS + @TARGETS = @QUICKHUNT_TARGETS else - targets = @TARGETS + @TARGETS ||= { ".+"=>"a" } end - debug_msg(@DEBUG_COMBAT, "sort_npcs | npcs: #{targets.keys.flat_map { |target| GameObj.targets.select { |npc| npc.name =~ /^#{target}$/i || npc.noun =~ /^#{target}$/i } }} | called by #{caller[0]}") - return targets.keys.flat_map { |target| GameObj.targets.select { |npc| npc.name =~ /^#{target}$/i || npc.noun =~ /^#{target}$/i } } + targets = @TARGETS.keys.flat_map { |target| GameObj.targets.select { |npc| npc.name =~ /^#{target}$/i || npc.noun =~ /^#{target}$/i } } + + debug_msg(@DEBUG_COMBAT, "sort_npcs | targets: #{targets}") + return targets end def priority(target) + return false if target.nil? + return true if $bigshot_bandits + return true if !@PRIORITY return true if (!$current_room_npcs.zip($room_npcs_last_check).map { |x, y| x.id == y.id }.any? { |s| s == false }) $room_npcs_last_check = GameObj.npcs priority = false - stoppriority = false - npcs = GameObj.targets.find_all { |i| i.status !~ /dead|gone/ } - npcs.delete_if { |npc| CharSettings['untargetable'].include?(npc.name) } - @TARGETS.keys.each do |t| - break if stoppriority == true + npcs = GameObj.targets.reject { |npc| CharSettings['untargetable'].include?(npc.name) } - npcs.each do |s| - break if stoppriority == true - - if (s.name =~ /#{t}/i or s.noun =~ /#{t}/i) - if s.name == target.name - stoppriority = true - priority = true - else - stoppriority = true - end - end + @TARGETS.keys.each do |name| + name_regex = /#{name}/i + if (npc = npcs.find { |s| s.name =~ name_regex || s.noun =~ name_regex }) + priority = (npc.name == target.name) + break end end - debug_msg(@DEBUG_COMBAT, "priority check | target: #{target} | priority: #{priority} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "priority check | target: #{target} | priority: #{priority}") return priority end def find_target(target, just_entered = false) - debug_msg(@DEBUG_COMBAT, "find_target | target: #{target} | just_entered: #{just_entered} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "find_target | target: #{target} | just_entered: #{just_entered}") tmp_valid = valid_target?(target, just_entered) # Exists solely for debug printout if tmp_valid @@ -6282,16 +6784,15 @@ class Bigshot return target end - if @PRIORITY && !$bigshot_bandits - debug_msg(@DEBUG_COMBAT, "find_target | @PRIORITY: #{@PRIORITY} && !$bigshot_bandits") - sort_npcs.each { |i| return i if valid_target?(i, just_entered) && priority(i) } - else - sort_npcs.each { |i| return i if valid_target?(i, just_entered) } - end - debug_msg(@DEBUG_COMBAT, "find_target | returning nil | called by #{caller[0]}") + sort_npcs.each { |i| return i if valid_target?(i, just_entered) && priority(i) } + return nil end + def leader_target? + GameObj.target + end + def rt?() return checkrt() end @@ -6301,14 +6802,14 @@ class Bigshot end def fried?() - debug_msg(@DEBUG_STATUS, "fried? | @CORRECT_PERCENT_MIND: #{@CORRECT_PERCENT_MIND} | @FRIED: #{@FRIED} | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "fried? | @CORRECT_PERCENT_MIND: #{@CORRECT_PERCENT_MIND} | @FRIED: #{@FRIED}") return false if $bigshot_quick || @FRIED > 100 @CORRECT_PERCENT_MIND >= @FRIED end def oom?() - debug_msg(@DEBUG_STATUS, "oom? | Char.percent_mana: #{Char.percent_mana} | @OOM: #{@OOM} | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "oom? | Char.percent_mana: #{Char.percent_mana} | @OOM: #{@OOM}") return false if @OOM.negative? Char.percent_mana < @OOM @@ -6323,32 +6824,32 @@ class Bigshot end def encumbrance? - debug_msg(@DEBUG_STATUS, "encumbrance? | [Char.percent_encumbrance, @ENCUMBERED]: #{[Char.percent_encumbrance, @ENCUMBERED]} | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "encumbrance? | [Char.percent_encumbrance, @ENCUMBERED]: #{[Char.percent_encumbrance, @ENCUMBERED]}") return [Char.percent_encumbrance, @ENCUMBERED] end def saturated?() - debug_msg(@DEBUG_STATUS, "saturated? | checkmind =~ /saturated/: #{checkmind =~ /saturated/} | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "saturated? | checkmind =~ /saturated/: #{checkmind =~ /saturated/}") checkmind =~ /saturated/ end def overkill?() - debug_msg(@DEBUG_STATUS, "overkill? | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "overkill?") $bigshot_overkill_counter >= @OVERKILL && lte_boost? end def set_help_group(keep_attacking) - debug_msg(@DEBUG_COMBAT, "set_help_group | keep_attacking: #{keep_attacking} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "set_help_group | keep_attacking: #{keep_attacking}") @HELP_GROUP_KILL = keep_attacking end def lte_boost?() - debug_msg(@DEBUG_STATUS, "lte_boost? | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "lte_boost?") $bigshot_lte_boost_counter >= @LTE_BOOST end def use_lte_boost() - debug_msg(@DEBUG_COMMANDS, "use_lte_boost | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "use_lte_boost") if (fried? && !lte_boost?) # Need the check because of the race condition caused if you are a follower in RT when called boost_attempt = dothistimeout "boost longterm", 3, /You do not have any Long-Term Experience Boosts to redeem.|You have deducted 500 experience points from your field experience/ @@ -6366,91 +6867,16 @@ class Bigshot def check_mind @followers.add_event(:CHECK_MIND) - if percentmind() < 100 - @CORRECT_PERCENT_MIND = percentmind() - debug_msg(@DEBUG_STATUS, "check_mind | percentmind(): #{percentmind()} | called by #{caller[0]}") - else - lines = Lich::Util.quiet_command_xml("experience", //) - if lines.any? { |l| l =~ /Field Exp\:\s*([0-9,]+)\/([0-9,]+)/i } - in_bucket = $1.delete(",").to_f - max_bucket = $2.delete(",").to_f - end - - actual_percent = (in_bucket / max_bucket) * 100 - if actual_percent.nil? - @CORRECT_PERCENT_MIND = percentmind() - else - @CORRECT_PERCENT_MIND = actual_percent - end - debug_msg(@DEBUG_STATUS, "check_mind | in_bucket: #{in_bucket} | max_bucket: #{max_bucket} | actual_percent: #{actual_percent} | called by #{caller[0]}") - end - - return @CORRECT_PERCENT_MIND - end + @CORRECT_PERCENT_MIND = percentmind + return @CORRECT_PERCENT_MIND unless @CORRECT_PERCENT_MIND >= @FRIED - def ammo_on_ground(ammo) # Not used - debug_msg(@DEBUG_COMBAT, "ammo_on_ground | ammo: #{ammo} | called by #{caller[0]}") - return GameObj.loot.find { |i| i.name =~ /\b#{ammo}s?\b/i or i.noun =~ /^#{ammo}s?$/i } - end - - def gather_ammo() # Not used - debug_msg(@DEBUG_COMMANDS, "gather_ammo | called by #{caller[0]}") - - ammo = @AMMO - container = GameObj.inv.find { |obj| obj.name =~ /#{@AMMO_CONTAINER}/ } - hide = @HIDE_FOR_AMMO - - return if wounded? - - if ammo.nil? or ammo.empty? - return - end - - if ammo =~ /\b(arrow|bolt|dart)\b/i - ammo_noun = $1 - else - echo "failed to gather: invalid ammo type specified (use the full name)" - end - - if container.nil? - echo "failed to gather: you must specify an ammo container to use this feature" - return - end - - while (ammo_on_ground(ammo_noun)) - change_stance(@WANDER_STANCE) - - unless GameObj.right_hand.id.nil? - line = dothistimeout "stow ##{GameObj.right_hand.id}", 3, /put|closed/ - if line =~ /closed/ - fput "open my ##{container.id}" - fput "put ##{GameObj.right_hand.id} in my ##{container.id}" - end - end - - result = dothistimeout("gather #{ammo_noun}", 2, /^You gather|^You pick up|^I could not|^What were you|^You may only|reach|on the ground/) - - if (result =~ /on the ground/) - result = dothistimeout "get #{ammo_noun}s", 3, /^You gather|^You pick up|^I could not|^What were you|^You may only|reach|on the ground/ - end - - if (result =~ /^You gather|^You pick up/) - dothistimeout("put my #{ammo_noun} in my #{ammo_noun}s in my #{container.noun}", 2, /^I could not find|^You can't seem to|^You add|^You bundle|You cannot bundle/) - bs_put "put my #{ammo_noun} in my ##{container.id}" if righthand? - elsif (result =~ /You may only/) - return - elsif (result == false || result =~ /^I could not find|^What were you/) - return - elsif (result =~ /reach/) - bs_put 'hide' if hide and not hidden? - sleep(4) - end - end + Lich::Util.quiet_command_xml("experience", //) + @CORRECT_PERCENT_MIND = Lich::Gemstone::Experience.percent_fxp end def reset_variables(moved = true) - debug_msg(@DEBUG_SYSTEM, "reset_variables | moved: #{moved} | called by #{caller[0]}") + debug_msg(@DEBUG_SYSTEM, "reset_variables | moved: #{moved}") if moved $ambusher_here = false @@ -6470,7 +6896,7 @@ class Bigshot end def wounded?() - debug_msg(@DEBUG_STATUS, "wounded? | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "wounded?") return false if $bigshot_quick return eval(@WOUNDED_EVAL) if @WOUNDED_EVAL @@ -6478,26 +6904,34 @@ class Bigshot end def creeping_dread? - debug_msg(@DEBUG_STATUS, "creeping_dread? | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "creeping_dread?") + + return false unless @CREEPING_DREAD.positive? - Effects::Debuffs.to_h.keys.map(&:to_s).find { |k| k.include? "Creeping Dread" }.match(/\((\d+)\)/)[1].to_i - counter = $1.to_i - return counter >= @CREEPING_DREAD && @CREEPING_DREAD > 0 + key = Effects::Debuffs.to_h.keys.find { |k| k.to_s.include?("Creeping Dread") } + return false unless key + + key.to_s[/\((\d+)\)/, 1].to_i >= @CREEPING_DREAD end def crushing_dread? - debug_msg(@DEBUG_STATUS, "crushing_dread? | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "crushing_dread?") + + return false unless @CRUSHING_DREAD.positive? - Effects::Debuffs.to_h.keys.map(&:to_s).find { |k| k.include? "Crushing Dread" }.match(/\((\d+)\)/)[1].to_i - counter = $1.to_i - return counter >= @CRUSHING_DREAD && @CRUSHING_DREAD > 0 + key = Effects::Debuffs.to_h.keys.find { |k| k.to_s.include?("Crushing Dread") } + return false unless key + + key.to_s[/\((\d+)\)/, 1].to_i >= @CRUSHING_DREAD end def wot_poison? - debug_msg(@DEBUG_STATUS, "wot_poison? | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "wot_poison?") + + return false unless @WOT_POISON poisoned = Effects::Debuffs.to_h.keys.any? { |key| key.to_s =~ /Wall of Thorns Poison/ } - return poisoned && @WOT_POISON == true + return poisoned end def bounty_check?() @@ -6511,30 +6945,31 @@ class Bigshot result = eval(@BOUNTY_EVAL) - debug_msg(@DEBUG_STATUS, "bounty_check? | result: #{result} | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "bounty_check? | result: #{result}") return result end def ready_to_hunt? - debug_msg(@DEBUG_STATUS, "ready_to_hunt? | called by #{caller[0]}") - + debug_msg(@DEBUG_STATUS, "ready_to_hunt?") $not_hunting_reason = nil - not_hunting = { - 'wounded.' => lambda { wounded? }, - 'encumbered.' => lambda { Char.percent_encumbrance >= @ENCUMBERED }, - 'creeping dread active.' => lambda { creeping_dread? }, - 'crushing dread active.' => lambda { crushing_dread? }, - 'confusion debuff active.' => lambda { Effects::Debuffs.active?("Confused") && @CONFUSION == true }, - 'wall of thorns poison active.' => lambda { wot_poison? }, - 'resting scripts are still running.' => lambda { @RESTING_SCRIPTS.any? { |i| running?(i) } }, - 'mind still above threshold.' => lambda { @CORRECT_PERCENT_MIND > @REST_TILL_EXP }, - 'mana still below threshold.' => lambda { Char.percent_mana < @REST_TILL_MANA }, - 'spirit still below threshold.' => lambda { Char.spirit < @REST_TILL_SPIRIT }, - 'stamina still below threshold.' => lambda { Char.percent_stamina < @REST_TILL_PERCENTSTAMINA } - } - not_hunting.each do |reason, condition| - if condition.call + # Array of [condition, reason] pairs + not_hunting_checks = [ + [wounded?, 'wounded.'], + [Char.percent_encumbrance >= @ENCUMBERED, 'encumbered.'], + [creeping_dread?, 'creeping dread active.'], + [crushing_dread?, 'crushing dread active.'], + [Effects::Debuffs.active?("Confused") && @CONFUSION, 'confusion debuff active.'], + [wot_poison?, 'wall of thorns poison active.'], + [@RESTING_SCRIPTS.any? { |i| running?(i) }, 'resting scripts are still running.'], + [@CORRECT_PERCENT_MIND > @REST_TILL_EXP, 'mind still above threshold.'], + [Char.percent_mana < @REST_TILL_MANA, 'mana still below threshold.'], + [Char.spirit < @REST_TILL_SPIRIT, 'spirit still below threshold.'], + [Char.percent_stamina < @REST_TILL_PERCENTSTAMINA, 'stamina still below threshold.'] + ] + + not_hunting_checks.each do |condition, reason| + if condition $not_hunting_reason = reason return reason end @@ -6544,7 +6979,7 @@ class Bigshot end def should_hunt?() - debug_msg(@DEBUG_STATUS, "should_hunt? | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "should_hunt?") if @BOUNTY_MODE && (bounty_check?) message("yellow", " Bounty mode. Killing self. Reason: Bounty available or complete.") @@ -6569,51 +7004,43 @@ class Bigshot end def ready_to_rest? - debug_msg(@DEBUG_STATUS, "ready_to_rest? | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "ready_to_rest?") return false if $bigshot_quick - # Define rest_conditions with lambda functions representing conditions - rest_conditions = { - bounty_mode: lambda { @BOUNTY_MODE && $bigshot_should_rest }, - should_rest: lambda { $bigshot_should_rest }, - wounded: lambda { wounded? }, - percentencumbrance: lambda { Char.percent_encumbrance >= @ENCUMBERED }, - creeping_dread: lambda { creeping_dread? }, - crushing_dread: lambda { crushing_dread? }, - wot_poison: lambda { wot_poison? }, - confusion_debuff: lambda { @CONFUSION == true && Effects::Debuffs.active?("Confused") }, - fried_overkill_and_lte_boost: lambda { fried? && overkill? && lte_boost? }, - oom: lambda { oom? } - } + rest_checks = [ + [-> { @BOUNTY_MODE && $bigshot_should_rest }, 'bounty complete.'], + [-> { $bigshot_should_rest }, '$bigshot_should_rest was set to true.'], + [-> { wounded? }, 'wounded.'], + [-> { fried? && overkill? && lte_boost? }, 'fried.'], + [-> { Char.percent_encumbrance >= @ENCUMBERED }, 'encumbered.'], + [-> { creeping_dread? }, 'creeping dread limit.'], + [-> { crushing_dread? }, 'crushing dread limit.'], + [-> { wot_poison? }, 'wall of thorns poison.'], + [-> { @CONFUSION && Effects::Debuffs.active?("Confused") }, 'confusion debuff.'], + ] - # Define rest_actions with lambda functions representing actions - rest_actions = { - bounty_mode: lambda { $rest_reason = 'bounty complete.' }, - should_rest: lambda { $rest_reason ? $rest_reason : $rest_reason = '$bigshot_should_rest was set to true.' }, - wounded: lambda { $rest_reason = 'wounded.' }, - percentencumbrance: lambda { $rest_reason = 'encumbered.' }, - creeping_dread: lambda { $rest_reason = 'creeping dread limit.' }, - crushing_dread: lambda { $rest_reason = 'crushing dread limit.' }, - wot_poison: lambda { $rest_reason = 'wall of thorns poison.' }, - confusion_debuff: lambda { $rest_reason = 'confusion debuff.' }, - fried_overkill_and_lte_boost: lambda { $rest_reason = 'fried.' }, - oom: lambda { wrack() if @USE_WRACKING; return false unless oom?; $rest_reason = 'out of mana.' } - } + rest_checks.each do |check, reason| + if check.call + $rest_reason = reason + $bigshot_status = :resting + return $rest_reason + end + end - # Iterate through rest_conditions and execute corresponding actions from rest_actions - rest_conditions.each do |condition, lambda_function| - if lambda_function.call - if rest_actions[condition].call - $bigshot_status = :resting - return $rest_reason - end + if oom? + wrack if @USE_WRACKING + if oom? + $rest_reason = 'out of mana.' + $bigshot_status = :resting + return $rest_reason end end + return false end def should_rest?() - debug_msg(@DEBUG_STATUS, "should_rest? | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "should_rest?") group_rest = @followers.group_should_rest? leader_rest = ready_to_rest? @@ -6646,7 +7073,7 @@ class Bigshot end def add_overkill() - debug_msg(@DEBUG_COMBAT, "add_overkill | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "add_overkill") if (fried? && lte_boost?) $bigshot_overkill_counter += 1 @@ -6669,24 +7096,10 @@ class Bigshot $bard_renewal_check = Time.now end - debug_msg(@DEBUG_STATUS, "bard_renewal | $bard_renewal_cost: #{$bard_renewal_cost} | called by #{caller[0]}") + debug_msg(@DEBUG_STATUS, "bard_renewal | $bard_renewal_cost: #{$bard_renewal_cost}") return $bard_renewal_cost end - def voln_favor() - current_favor = 0 - favor_pattern = /Voln Favor: (([,\d]+))/ - - lines = Lich::Util.quiet_command_xml("resources", //).join(" ") - - if lines =~ favor_pattern - current_favor = $1.gsub(',', '').to_i; - end - - debug_msg(@DEBUG_STATUS, "voln_favor | current_favor: #{current_favor} | called by #{caller[0]}") - return current_favor - end - def cast902() if Spell[902].known? && Spell[902].affordable? waitrt? @@ -6715,7 +7128,7 @@ class Bigshot end def cast_signs(single_cast = false) - debug_msg(@DEBUG_COMMANDS, "cast_signs | single_cast: #{single_cast} | called by #{caller[0]}") + debug_msg(@DEBUG_COMMANDS, "cast_signs | single_cast: #{single_cast}") @SIGNS.each do |i| if i =~ /\b650\s?(\w+)?\s?(\w+)?\s?/ @@ -6826,14 +7239,13 @@ class Bigshot next if [140, 211, 215, 219, 240, 919, 1619, 1650].include?(sign.num) && Effects::Cooldowns.active?(sign.name) if [9805, 9806, 9816].include?(sign.num) && !sign.active? && @CHECK_FAVOR - current_favor = voln_favor() symbol_cost = { 9805 => 0.1, 9806 => 0.1, 9816 => 0.5 } # symbol of return cost from wiki (over estimates slightly) favor_cost = ((2161 / 97) * Stats.level) - (5222 / 97) sym_cost = favor_cost * symbol_cost[sign.num] - next if sym_cost > current_favor + next if sym_cost > Lich::Resources.voln_favor end # wrack? @@ -6860,7 +7272,7 @@ class Bigshot end def prepare_for_movement(move_signs = true) - debug_msg(@DEBUG_COMBAT, "prepare_for_movement| move_signs: #{move_signs} | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "prepare_for_movement| move_signs: #{move_signs}") reset_variables() change_stance(@WANDER_STANCE) @@ -6882,6 +7294,19 @@ class Bigshot sleep 0.2 end + def group_all_followers + until checkpcs.include?(@leader) + go2(@group.room_id) + sleep(0.5) + end + + until Lich::Gemstone::Group.members.map(&:noun).include?(@leader) + result = dothistimeout("join #{@leader}", 3, /You are already a member|You join|What were you referring to/) + groupcheck unless result =~ /What were you referring to/ + sleep 0.1 + end + end + def bs_move room = Room.current next_room_options = room.wayto.keys - @HUNTING_BOUNDARIES @@ -6906,7 +7331,7 @@ class Bigshot end def bs_wander(new_room: true) - debug_msg(@DEBUG_COMBAT, "bs_wander? | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "bs_wander?") $wander_rooms ||= Array.new @COMMANDS_REGISTRY = {} @@ -6945,17 +7370,20 @@ class Bigshot new_room = true if !@followers.all_present? || Lich::Gemstone::Group.broken? - @followers.add_event(:FOLLOW_NOW) # trigger rubber band message("yellow", "Waiting for followers....") - sleep(0.5) while (!@followers.all_present?) + + # Keep prompting followers until all are present + until @followers.all_present? + @followers.add_event(:FOLLOW_NOW) # trigger rubber band + sleep 2 + end + groupcheck end # Wait for follower to finish looting if needed - while !@followers.looting_done - $looting_inactive = @followers.looting_done - sleep 0.5 - end + wait_while { !@followers.looting_done } + $looting_inactive = true # Final room loot by leader for any missed items by follower if @FINAL_LOOT && leading? && !solo? && bigclaim? @@ -6988,7 +7416,7 @@ class Bigshot @followers.add_event(:GO2_HUNTING_ROOM) goto(@HUNTING_ROOM_ID) - @followers.add_event(:JOIN_LEADER) + @followers.add_event(:FOLLOW_NOW) message("yellow", "Waiting for followers....") if !@followers.all_present? sleep 0.5 while (!@followers.all_present?) else @@ -7026,7 +7454,7 @@ class Bigshot end def ranger_track # track bounty targets on cooldown - debug_msg(@DEBUG_COMBAT, "ranger_track | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "ranger_track") tracking_results = Regexp.union( /You don't have to go far\./, # target in the room, probably hidden. @@ -7053,7 +7481,7 @@ class Bigshot end def uncover - debug_msg(@DEBUG_COMBAT, "uncover | called by #{caller[0]}") + debug_msg(@DEBUG_COMBAT, "uncover") waitrt? return unless GameObj.targets.empty? @@ -7071,12 +7499,12 @@ class Bigshot def escape_rooms # Used to escape from Roa'ter swallowing unless checkroom("The Belly of the Beast").nil? - roater_escape() + creature_escape('worm') end # Used to escape from Hinterwilds Ooze swallowing unless checkroom("Ooze, Innards").nil? - ooze_escape() + creature_escape('ooze') end # Used to escape a Temporal Rift due to failed 930 casting @@ -7085,116 +7513,122 @@ class Bigshot end end - def roater_escape() - debug_msg(@DEBUG_COMBAT, "roater_escape | called by #{caller[0]}") + def find_escape_weapon(creature) + weapon = nil + weapon_container = nil + weapon_regex = creature == 'worm' ? @DAGGER_REGEX : @BLUNT_REGEX - dagger_found = false - container_ids = [] - empty_hands - res = Lich::Util.quiet_command_xml("inventory containers", /Your worn items are:/, / { attack_cmd: 'attack wall', room: 'The Belly of the Beast', weapon_regex: @DAGGER_REGEX }, + 'ooze' => { attack_cmd: 'kill organ', room: 'Ooze, Innards', weapon_regex: @BLUNT_REGEX } + } + + config = creature_config[creature] + + # If we have the correct type in our right hand just use it + right_hand = GameObj.right_hand + if right_hand.name != "Empty" && right_hand.name =~ config[:weapon_regex] && right_hand.type.include?("weapon") + weapon = right_hand + end + + if weapon.nil? empty_hands - else - echo "ooze_escape error, result: #{result}" - echo "Please report issue and you're on your own for escaping for now!" - sleep(0.5) until checkroom('Ooze, Innards').nil? - return true + weapon, container = find_escape_weapon(creature) end - res = Lich::Util.quiet_command_xml("inventory containers", /Your worn items are:/, /