UO Sagas: AFK Peacemaking & Discordance

Created: 11 days ago on 06/03/2025, 08:48:10 AM
Size: 17712

Description: Description included in script

This script was crafted with ChatGPT 04-mini-high using the SAGAS documentation as reference, but also using Elly Strikehursts bard script for a base

--[[ AFK Bard Trainer (Peacemaking → Discordance Sequence, with Journal Checks) Author: Elly Strikehurst Ingame Last Updated: 2025-06-04 Description: This script continuously trains Bard skills “Peacemaking” and “Discordance” on the nearest hostile targets. It uses a Tambourine (graphic 0x0E9D) from your backpack when prompted. The script supports three modes: 1. Peacemaking only 2. Discordance only 3. Combined Peacemaking → Discordance sequence (casts Peacemaking first, then Discordance on the same target). Features: • Finds the nearest hostile mobile (notoriety = Enemy) within configurable ranges for each skill. • Casts “SongOfPeacemaking” or “SongOfDiscordance” and waits for the “What instrument shall you play?” prompt. - Automatically “uses” a Tambourine from your backpack to satisfy the prompt. • Waits for the respective targeting prompt (“Whom do you wish to calm” or “Choose the target for your song of discordance”) and targets the hostile mobile. • Parses journal messages to adjust delays: - “That creature is already being calmed.” → if Peacemaking prompt appears but target already calmed, waits 5 seconds. - “You play your hypnotic music, stopping the battle.” → after successful Peacemaking, waits a normal training delay (7 seconds). - “You must wait a few seconds before you can play another song.” → delay 5 seconds before retrial. - “That creature is already in discord.” → if Discordance prompt appears but target already in Discord, waits 5 seconds. - “Discord effect ended on target” → if Discord ended, waits 2 seconds to allow re-cast. • Maintains independent timers for each skill, or a combined timer when both are enabled sequentially. • Prints helpful overhead messages for status, errors, and cooldowns. Configuration Parameters: usePeacemaking = true -- enable Peacemaking training useDiscordance = true -- enable Discordance training peacemakingRange = 12 -- tile range to search for targets for Peacemaking discordanceRange = 12 -- tile range to search for targets for Discordance How It Works: - If both usePeacemaking and useDiscordance are true, the script enters a “combined” mode: 1. Stage 0: Cast Peacemaking on the nearest hostile within peacemakingRange. Depending on journal feedback, sets a delay before casting Discordance. 2. Stage 1: Cast Discordance on the same hostile (if still in range), then set a delay before returning to Stage 0. - If only one skill is enabled, it will cast that skill on any hostile in range, rechecking after the appropriate delay. - Journal monitoring ensures the script adapts to cooldown messages, “already” messages, and “effect ended” messages, rather than blindly waiting fixed intervals. Usage: 1. Place a Tambourine (graphic 0x0E9D) in your backpack. 2. Run this script in the UO Sagas Assistant. 3. AFK—your character will cast Peacemaking and/or Discordance on the nearest hostile, switching instruments as needed. Note: - Make sure you have sufficient mana, reagents (if required by your server), and that you are in an area where hostile targets can be found. - Adjust peacemakingRange, discordanceRange, and delays if your shard’s requirements differ. ]] -- Enable/disable skills local usePeacemaking = true local useDiscordance = true -- Ranges (tiles) to find nearest hostile local peacemakingRange = 12 local discordanceRange = 12 -- Internal State local peacemakingTimer = 0 -- next time allowed to cast Peacemaking local discordanceTimer = 0 -- next time allowed to cast Discordance local combinedTimer = 0 -- controls sequential flow when both enabled local combinedStage = 0 -- 0 = ready for Peacemaking, 1 = ready for Discordance local lastOverheadTime = 0 -- Helper: calculate Euclidean distance local function getDistance(x1, y1, x2, y2) return math.sqrt((x2 - x1) ^ 2 + (y2 - y1) ^ 2) end -- Helper: sort a list of mobiles by distance to player local function sortByDistance(enemies) local px, py = Player.X, Player.Y table.sort(enemies, function(a, b) return getDistance(px, py, a.X, a.Y) < getDistance(px, py, b.X, b.Y) end) return enemies end -- Helper: find the first Tambourine (graphic 0x0E9D) in backpack local function findTambourine() local items = Items.FindByFilter({ graphics = { 0x0E9D }, onground = false }) or {} for _, it in ipairs(items) do if it.RootContainer == Player.Serial then return it end end return nil end -- Helper: find the nearest hostile (notoriety 5 = Enemy) within a given range local function getNearestHostileInRange(range) local filter = { human = false, notoriety = { 5 }, rangemax = range, dead = false } local enemies = Mobiles.FindByFilter(filter) or {} if #enemies == 0 then return nil end enemies = sortByDistance(enemies) return enemies[1] end -- Peacemaking Logic (returns next delay in seconds) local function songPeacemakingOn(target) -- Cast SongOfPeacemaking Journal.Clear() if not Spells.Cast("SongOfPeacemaking") then Messages.Overhead("Failed to cast Peacemaking", 33, Player.Serial) return 2 end -- Wait up to 3s for “What instrument shall you play” prompt local startInstr = os.clock() while os.clock() - startInstr < 3 do if Journal.Contains("What instrument shall you play") then Journal.Clear() local tamb = findTambourine() if tamb then Player.UseObject(tamb.Serial) Messages.Overhead("Playing Tambourine for Peacemaking", 68, Player.Serial) else Messages.Overhead("No Tambourine!", 33, Player.Serial) return 2 end break end Pause(50) end -- Wait up to 3s for relevant prompts: local startTarget = os.clock() while os.clock() - startTarget < 3 do -- Cooldown message if Journal.Contains("You must wait a few seconds before you can play another song") then Journal.Clear() Messages.Overhead("Peacemaking: Cooldown message", 33, Player.Serial) return 5 end -- Already being calmed if Journal.Contains("That creature is already being calmed.") then Journal.Clear() Messages.Overhead("Already being calmed", 33, Player.Serial) return 5 end -- “Whom do you wish to calm” prompt if Journal.Contains("Whom do you wish to calm") then Journal.Clear() if Targeting.WaitForTarget(3000) then Targeting.Target(target.Serial) Messages.Overhead("Peacemaking → " .. (target.Name or "Unknown"), 70, Player.Serial) -- After success, wait for “You play your hypnotic music, stopping the battle” local startFinish = os.clock() while os.clock() - startFinish < 2 do if Journal.Contains("You play your hypnotic music, stopping the battle") then Journal.Clear() break end Pause(50) end return 7 -- normal training delay else Messages.Overhead("Peacemaking: No target cursor", 33, Player.Serial) return 2 end end Pause(50) end -- Prompt never appeared Messages.Overhead("Peacemaking: Prompt timeout", 33, Player.Serial) return 2 end -- Discordance Logic (returns next delay in seconds) local function songDiscordanceOn(target) -- Cast SongOfDiscordance Journal.Clear() if not Spells.Cast("SongOfDiscordance") then Messages.Overhead("Failed to cast Discordance", 33, Player.Serial) return 2 end -- Wait up to 3s for “What instrument shall you play” prompt local startInstr = os.clock() while os.clock() - startInstr < 3 do if Journal.Contains("What instrument shall you play") then Journal.Clear() local tamb = findTambourine() if tamb then Player.UseObject(tamb.Serial) Messages.Overhead("Playing Tambourine for Discordance", 68, Player.Serial) else Messages.Overhead("No Tambourine!", 33, Player.Serial) return 2 end break end Pause(50) end -- Wait up to 3s for relevant prompts: local startTarget = os.clock() while os.clock() - startTarget < 3 do -- Already in discord if Journal.Contains("That creature is already in discord.") then Journal.Clear() Messages.Overhead("Already in Discord", 33, Player.Serial) return 5 end -- Discord ended if Journal.Contains("Discord effect ended on target") then Journal.Clear() Messages.Overhead("Discord ended, can recast soon", 32, Player.Serial) return 2 end -- “Choose the target for your song of discordance” prompt if Journal.Contains("Choose the target for your song of discordance") then Journal.Clear() if Targeting.WaitForTarget(3000) then Targeting.Target(target.Serial) Messages.Overhead("Discordance → " .. (target.Name or "Unknown"), 70, Player.Serial) return 7 -- normal training delay else Messages.Overhead("Discordance: No target cursor", 33, Player.Serial) return 2 end end Pause(50) end -- Prompt never appeared Messages.Overhead("Discordance: Prompt timeout", 33, Player.Serial) return 2 end -- Combined Logic (Peacemaking → Discordance Sequence) local function songPeacemakeThenDiscord() local now = os.time() if now < combinedTimer then return end -- Find nearest hostile in both ranges local targetPM = getNearestHostileInRange(peacemakingRange) local targetDC = getNearestHostileInRange(discordanceRange) -- If no hostile at all, message once if not (targetPM or targetDC) then if now > lastOverheadTime then Messages.Overhead("No hostiles in range", 32, Player.Serial) lastOverheadTime = now + 1 end return end -- Prefer a common target (if in both ranges) or whichever exists local target = targetPM or targetDC -- Stage 0: attempt Peacemaking first if combinedStage == 0 and usePeacemaking and targetPM then local delay = songPeacemakingOn(target) combinedStage = 1 combinedTimer = now + delay return end -- Stage 1: attempt Discordance next if combinedStage == 1 and useDiscordance and targetDC then local delay = songDiscordanceOn(target) combinedStage = 0 combinedTimer = now + delay return end -- If targetPM is nil but Stage 0, skip to Stage 1 if combinedStage == 0 and not targetPM then combinedStage = 1 combinedTimer = now return end -- If targetDC is nil but Stage 1, reset to Stage 0 if combinedStage == 1 and not targetDC then combinedStage = 0 combinedTimer = now return end end -- Initialization Messages.Overhead( "Bard Trainer Loaded (Peacemaking=" .. tostring(usePeacemaking) .. ", Discordance=" .. tostring(useDiscordance) .. ")", 70, Player.Serial ) -- Main Loop repeat if not Player.IsDead then if usePeacemaking and useDiscordance then songPeacemakeThenDiscord() else -- Independent Peacemaking if usePeacemaking then local now = os.time() if now >= peacemakingTimer then local target = getNearestHostileInRange(peacemakingRange) if target then local delay = songPeacemakingOn(target) peacemakingTimer = now + delay elseif now > lastOverheadTime then Messages.Overhead("No hostiles in range", 32, Player.Serial) lastOverheadTime = now + 1 end end end -- Independent Discordance if useDiscordance then local now = os.time() if now >= discordanceTimer then local target = getNearestHostileInRange(discordanceRange) if target then local delay = songDiscordanceOn(target) discordanceTimer = now + delay elseif now > lastOverheadTime then Messages.Overhead("No hostiles in range", 32, Player.Serial) lastOverheadTime = now + 1 end end end end end Pause(100) until false
View list of scripts
Disclaimer: This is a fan made site and is not directly associated with Ultima Online or UO staff.