From f684776a4dced4459337a39b22900fd773996d63 Mon Sep 17 00:00:00 2001 From: Ben Carter Date: Thu, 21 Aug 2025 23:50:06 -0400 Subject: [PATCH] audio updtes and core classes. --- modules/UI/shared/audioplayer.client.ts | 132 +++++++++++++++++++++++- modules/classes/mapzones.ts | 31 ++++++ modules/classes/server-utils.ts | 4 +- modules/classes/ui-utils.ts | 91 +++++++++++++++- modules/gameobject/gamblechest.ts | 28 ++++- 5 files changed, 277 insertions(+), 9 deletions(-) diff --git a/modules/UI/shared/audioplayer.client.ts b/modules/UI/shared/audioplayer.client.ts index 25c17f6..da8603d 100644 --- a/modules/UI/shared/audioplayer.client.ts +++ b/modules/UI/shared/audioplayer.client.ts @@ -2,7 +2,133 @@ let aio: AIO = {}; if(!aio.AddAddon()) { const audioHandlers = aio.AddHandlers('AIOAudioPlayer', {}); - audioHandlers.PlaySingleSound = (sound: string) => { - PlaySoundFile(sound, "Master"); - }; + + // Define an interface for audio options + interface AudioOptions { + volume?: number; // 0.0 to 1.0 (for relative volume control) + duration?: number; // Duration in seconds before restoring original volume + } + + // Track which sounds have been played for each player + const playedSounds: Record = {}; + + // Store the default SFX volume for restoration + let defaultSFXVolume: string | null = null; + + // Function to get the current SFX volume safely + function getSFXVolume(): string { + try { + // @ts-ignore - WoW API + return GetCVar("Sound_SFXVolume"); + } catch (e) { + return "1.0"; + } + } + + // Function to set the SFX volume safely + function setSFXVolume(volume: string): void { + try { + // @ts-ignore - WoW API + SetCVar("Sound_SFXVolume", volume); + } catch (e) { + // Silently fail if SetCVar is not available + } + } + + // Create a timer system for WoW 3.3.5 + let totalElapsed = 0; + let targetTime = 0; + let isTimerActive = false; + + // Create the timer frame + const timerFrame = CreateFrame("Frame", "hiddenTimingFrame"); + + // Set up the OnUpdate handler + timerFrame.SetScript("OnUpdate", function(self: WoWAPI.Frame, elapsed: number) { + if (!isTimerActive) return; + + // Accumulate elapsed time (elapsed is in seconds) + totalElapsed += elapsed; + + // Check if we've reached the target time + if (totalElapsed >= targetTime) { + // Restore the volume + if (defaultSFXVolume !== null) { + setSFXVolume(defaultSFXVolume); + } + + // Reset the timer + isTimerActive = false; + totalElapsed = 0; + } + }); + + // Function to start the timer + function startVolumeRestoreTimer(duration: number): void { + if (duration <= 0) return; + + // Set the timer parameters + totalElapsed = 0; + targetTime = duration; + isTimerActive = true; + } + + audioHandlers.PlaySingleSound = (sound: string, options?: AudioOptions, playerName?: string) => { + const volume = options?.volume !== undefined ? options.volume : 1.0; + const duration = options?.duration !== undefined ? options.duration : 0; + + if (defaultSFXVolume === null) { + defaultSFXVolume = getSFXVolume(); + } + + if (volume !== 1.0) { + setSFXVolume(volume.toString()); + + // Start the timer to restore volume if duration is specified + if (duration > 0) { + startVolumeRestoreTimer(duration); + } + } + + // Always play through SFX channel + // @ts-ignore - WoW API + PlaySoundFile(sound, "SFX"); + + // If player name is provided, track that this sound was played for this player + if (playerName) { + if (!playedSounds[playerName]) { + playedSounds[playerName] = []; + } + + // Add this sound to the player's played sounds list + if (!playedSounds[playerName].includes(sound)) { + playedSounds[playerName].push(sound); + } + } + }; + + // Add a function to check if a sound has been played for a player + audioHandlers.HasPlayedSound = (sound: string, playerName: string) => { + return playedSounds[playerName] && playedSounds[playerName].includes(sound); + }; + + // Add a function to reset played sounds for a player or instance + audioHandlers.ResetPlayedSounds = (playerName?: string) => { + if (playerName) { + // Reset for specific player + playedSounds[playerName] = []; + } else { + // Reset for all players + for (const player in playedSounds) { + playedSounds[player] = []; + } + } + }; + + // Add a function to restore the default volume + audioHandlers.RestoreDefaultVolume = () => { + if (defaultSFXVolume !== null) { + setSFXVolume(defaultSFXVolume); + } + }; } \ No newline at end of file diff --git a/modules/classes/mapzones.ts b/modules/classes/mapzones.ts index d556f1d..e6df914 100644 --- a/modules/classes/mapzones.ts +++ b/modules/classes/mapzones.ts @@ -2,6 +2,17 @@ import { Logger } from "./logger"; const logger = new Logger("MapZones"); +// Base Game map difficulties +export enum MapDifficulties { + NORMAL = 1, + HEROIC = 2, +} + +export enum MapTypes { + DUNGEON = 1, + RAID = 2, +} + export enum MapNames { // Classic WoW dungeons RAGEFIRE_CHASM = 'ragefire_chasm', @@ -534,3 +545,23 @@ export function getBossByName(name: string): Boss | undefined { export const BossIDs: Record = Object.fromEntries( Bosses.map(b => [b.entry, true]) ); + +export function isBossDbCheck(entry: number): boolean { + const sql = ` + select distinct entry + from acore_world.creature_template ct + left join creature c on ct.entry = c.id1 + left join map_dbc m on c.map = m.ID + where + (ct.\`rank\` = 3 and InstanceType > 0) + OR (ct.\`rank\` = 3 and ct.ScriptName like 'boss_%') + OR (ExpansionID = 1 and ct.ScriptName like '%boss%') + OR (m.InstanceType = 1 and ExperienceModifier = 2) + and entry = ${entry}`; + + const result = WorldDBQuery(sql); + if(result) { + return true; + } + return false; +} diff --git a/modules/classes/server-utils.ts b/modules/classes/server-utils.ts index b278b7f..e4f9e13 100644 --- a/modules/classes/server-utils.ts +++ b/modules/classes/server-utils.ts @@ -1,4 +1,6 @@ +import { timeStamp } from "console"; + // A function that will take a min and a max and return a random number between them export function rollDice(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1) + min); -} \ No newline at end of file +} diff --git a/modules/classes/ui-utils.ts b/modules/classes/ui-utils.ts index 982741a..b7047cf 100644 --- a/modules/classes/ui-utils.ts +++ b/modules/classes/ui-utils.ts @@ -16,4 +16,93 @@ export function colors(name: string) { } else { return colors.WHITE; } -} \ No newline at end of file +} + +/** + * Creates an item button with tooltip functionality + * @param parent The parent frame + * @param name Unique name for the button + * @param itemId The item entry ID + * @param size Size of the button (width and height) + * @param x X position relative to anchor point + * @param y Y position relative to anchor point + * @param anchorPoint Anchor point on the parent frame + * @returns The created button + */ +export function CreateItemButton( + parent: WoWAPI.Frame, + name: string, + itemId: number, + size: number = 36, + x: number = 0, + y: number = 0, + anchorPoint: WoWAPI.Point = "LEFT" +): WoWAPI.Button { + // Create or get existing button + + AIO_debug(`New button being generated for: ${name} inside of ${parent.GetName()}`); + + + // IF it already exists just update the texture and gametooltip + let button: WoWAPI.Button; + let iconTexture: WoWAPI.Texture; + button = _G[name]; + iconTexture = _G[`${name}_icon`]; + if(!button) { + button = CreateFrame("Button", name, parent); + button.SetSize(size, size); + + iconTexture = button.CreateTexture(`${name}_icon`, "BACKGROUND"); + iconTexture.SetAllPoints(); + } + + // const [itemName, itemLink] = GetItemInfo(itemId); + // if(!itemName) { + // AIO_debug(`Failed to retrieve item! ${itemId}`); + // return; + // } + + const itemTexture = GetItemIcon(itemId); + if(!itemTexture) { + AIO_debug(`Failed to retrieve item texture!`); + return; + } + + iconTexture.SetTexture(itemTexture); + + + // AIO_debug(`The item link is as follows: for item:::: ${itemId}:::::: ${itemLink} :::::: ${itemTexture}`); + + // Add tooltip handlers + button.SetScript("OnEnter", function(self) { + GameTooltip.SetOwner(self, "ANCHOR_RIGHT"); + GameTooltip.SetHyperlink(`item:${itemId}`); + GameTooltip.Show(); + }); + + button.SetScript("OnLeave", function() { + GameTooltip.Hide(); + }); + + // // Position the button + button.ClearAllPoints(); + button.SetPoint(anchorPoint, parent, anchorPoint, x, y); + + return button; +} + +// Just makes a frame closable by escape +/** @noSelf **/ +export function EscapeCloseable(frame: WoWAPI.Frame) { + _G[frame.GetName()] = frame; + tinsert(_G["UISpecialFrames"], frame.GetName()); +} + +/** @noSelf **/ +export function MakeDraggable(frame: WoWAPI.Frame) { + frame.SetMovable(true); + frame.EnableMouse(true); + frame.RegisterForDrag("LeftButton"); + frame.SetScript("OnDragStart", frame.StartMoving); + frame.SetScript("OnDragStop", frame.StopMovingOrSizing); +} diff --git a/modules/gameobject/gamblechest.ts b/modules/gameobject/gamblechest.ts index 7cd8eff..be148d6 100644 --- a/modules/gameobject/gamblechest.ts +++ b/modules/gameobject/gamblechest.ts @@ -253,7 +253,12 @@ const onSpell: player_event_on_spell_cast = (event: number, player: Player, spel for(let i = 0; i < members.length; i++) { player.SendChatMessageToPlayer(ChatMsg.CHAT_MSG_RAID_BOSS_EMOTE,Language.LANG_COMMON,`${player.GetName()} is taking a gamble!`, members[i]); - aio.Handle(members[i], 'AIOAudioPlayer', 'PlaySingleSound',sound); + // Use the updated audio player with player name and audio options + aio.Handle(members[i], 'AIOAudioPlayer', 'PlaySingleSound', sound, { + channel: "SFX", + volume: 0.5, + duration: 12.0 // Most opening sounds are short + }, members[i].GetName()); } } @@ -281,7 +286,12 @@ const onLootStateChange: gameobject_event_on_loot_state_change = (event: number, const players = gameObject.GetPlayersInRange(50); for(let i = 0; i < players.length; i++) { const player = players[i]; - aio.Handle(player, 'AIOAudioPlayer', 'PlaySingleSound', sound); + // Use the updated audio player with player name and audio options + aio.Handle(player, 'AIOAudioPlayer', 'PlaySingleSound', sound, { + channel: "SFX", + volume: 0.5, // Trap sounds should be louder + duration: 8.0 // Most trap sounds are short + }, player.GetName()); } if(player.IsAlive()) { @@ -304,7 +314,12 @@ const onLootStateChange: gameobject_event_on_loot_state_change = (event: number, for(let i = 0; i < members.length; i++) { members[i].SendBroadcastMessage(`{player.GetName()} has triggered trap number ${effect}, hate that guy!`); - aio.Handle(members[i], 'AIOAudioPlayer', 'PlaySingleSound',sound); + // Use the updated audio player with player name and audio options + aio.Handle(members[i], 'AIOAudioPlayer', 'PlaySingleSound', sound, { + channel: "SFX", + volume: 0.5, + duration: 8.0 // Most trap sounds are short + }, members[i].GetName()); } player.CastSpell(player, effect, true); @@ -319,7 +334,12 @@ const onLootStateChange: gameobject_event_on_loot_state_change = (event: number, const players = gameObject.GetPlayersInRange(50); for(let i = 0; i < players.length; i++) { const player = players[i]; - aio.Handle(player, 'AIOAudioPlayer', 'PlaySingleSound', LootGoodSound); + // Use the updated audio player with player name and audio options + aio.Handle(player, 'AIOAudioPlayer', 'PlaySingleSound', LootGoodSound, { + channel: "SFX", + volume: 0.5, + duration: 25.0 // Loot sound is short + }, player.GetName()); } }