Snoopy2016 Posted August 3, 2025 Share Posted August 3, 2025 I have a phonograph that can be activated by inserting a coin. The activation works as intended, but currently it still shows “Turn On” or “Turn Off” when hovering over it. I already implemented a simple mute/unmute logic. Is there a way to remove these default prompts and replace them with a custom context menu for Mute/Unmute instead? Any help would be greatly appreciated! ------------------------------------------------------------------ -- monkeygraph.lua - Coin-activated monkey music device with mute switch ------------------------------------------------------------------ require "prefabutil" ------------------------------------------------------------------ -- Assets & Constants ------------------------------------------------------------------ local assets = { Asset("ANIM", "anim/monkeygraph.zip"), Asset("IMAGE", "images/inventoryimages/monkeygraph.tex"), Asset("ATLAS", "images/inventoryimages/monkeygraph.xml"), } local SPAWN_RADIUS = 15 -- Radius for monkey spawning local RETURN_RADIUS = 90 -- Radius to attract monkeys from local PLAYER_CHECK_RADIUS = 30 -- Radius to check for drunk Guybrush local LOOP_HANDLE = "monkeygraph_loop" -- Sound handle local BASE_VOLUME = 0.05 -- Default volume when not muted local COIN_TIME = 30 * 60 -- 30 minutes per coin (seconds) ------------------------------------------------------------------ -- Utility Functions ------------------------------------------------------------------ local function GetRandomSpawnPosition(inst) if not TheWorld.ismastersim then return 0,0,0 end local x,y,z = inst.Transform:GetWorldPosition() local angle = math.random() * 2 * math.pi local dist = math.random() * SPAWN_RADIUS return x + math.cos(angle) * dist, 0, z + math.sin(angle) * dist end local function IsGuybrushDrunk(inst) local x,y,z = inst.Transform:GetWorldPosition() local players = TheSim:FindEntities(x,y,z, PLAYER_CHECK_RADIUS, {"player"}) for _,player in ipairs(players) do if player.prefab == "guybrush" and player.components.drunkenness and (player.components.drunkenness.current or 0) > 50 then return true end end return false end ------------------------------------------------------------------ -- Core Functionality ------------------------------------------------------------------ -- Sound Management local function UpdateSound(inst) if not inst.is_active then if inst.SoundEmitter then inst.SoundEmitter:KillSound(LOOP_HANDLE) end return end if inst.SoundEmitter then -- Start sound if active if not inst.SoundEmitter:PlayingSound(LOOP_HANDLE) then inst.SoundEmitter:PlaySound("soundtrack/custom/monkeytracks", LOOP_HANDLE, nil, true) end -- Set volume based on mute state local volume = inst.is_muted and 0 or BASE_VOLUME inst.SoundEmitter:SetVolume(LOOP_HANDLE, volume) end end -- Monkey Management local function ManageMonkeys(inst) -- Stop loops if inactive if not inst.is_active then if inst.attract_task then inst.attract_task:Cancel(); inst.attract_task = nil end if inst.spawn_task then inst.spawn_task:Cancel(); inst.spawn_task = nil end return end -- Start attraction loop if needed if inst.attract_task == nil then inst.attract_task = inst:DoPeriodicTask(10, function() if not inst.is_active then return end local x,y,z = inst.Transform:GetWorldPosition() local monkeys = TheSim:FindEntities(x,y,z, RETURN_RADIUS, {"powder_monkey"}) for _,monkey in ipairs(monkeys) do if monkey.components.locomotor then monkey.components.locomotor:GoToPoint(Vector3(x,y,z)) end end end) end -- Start spawning loop if needed if inst.spawn_task == nil then inst.spawn_task = inst:DoPeriodicTask(60, function() if not inst.is_active or not IsGuybrushDrunk(inst) then return end local x,y,z = GetRandomSpawnPosition(inst) local monkey = SpawnPrefab("powder_monkey") if monkey then monkey.Transform:SetPosition(x,y,z) end end) end end -- Timer Management local function UpdateTimer(inst, added_seconds) -- Calculate remaining time if timer is already running if inst.activation_timer ~= nil then if inst.activation_timer.timeleft ~= nil then -- Use timeleft if available inst.remaining_time = inst.activation_timer.timeleft elseif inst.last_timer_start ~= nil then -- Otherwise calculate from elapsed time local elapsed = math.max(GetTime() - inst.last_timer_start, 0) inst.remaining_time = math.max((inst.remaining_time or 0) - elapsed, 0) end inst.activation_timer:Cancel() inst.activation_timer = nil end -- Add new time and start timer inst.remaining_time = (inst.remaining_time or 0) + added_seconds inst.last_timer_start = GetTime() inst.activation_timer = inst:DoTaskInTime(inst.remaining_time, function() -- Deactivate inst.is_active = false inst.coin_inserted = false inst.remaining_time = 0 inst.last_timer_start = nil inst.activation_timer = nil inst:RemoveTag("coin_inserted") -- Update state UpdateSound(inst) ManageMonkeys(inst) inst.AnimState:PlayAnimation("idle") inst.entity:SetCanSleep(true) end) end -- State Management local function SetActive(inst, active) inst.is_active = active if active then inst.entity:SetCanSleep(false) inst.AnimState:PlayAnimation("play_loop", true) else inst.entity:SetCanSleep(true) inst.AnimState:PlayAnimation("idle") end UpdateSound(inst) ManageMonkeys(inst) end local function SetMuted(inst, muted, doer) -- Only allow muting when active if not inst.is_active then if doer and doer.components.talker then doer.components.talker:Say("Needs a guycoin to activate.") end return false end inst.is_muted = muted UpdateSound(inst) return true end ------------------------------------------------------------------ -- Component Functions ------------------------------------------------------------------ -- Machine Component Handlers local function onUnmute(inst, doer) return SetMuted(inst, false, doer) end local function onMute(inst, doer) return SetMuted(inst, true, doer) end -- Trader Component Handler local function OnInsertCoin(inst, doer, item) -- Always check if doer exists and has inventory if not doer or not doer.components.inventory or not doer.components.inventory:Has("guycoin", 1) then return false end -- Remove coin from inventory doer.components.inventory:ConsumeByName("guycoin", 1) -- Add message when coin is inserted if doer.components.talker then if not inst.is_active then doer.components.talker:Say("Says here... five minutes of fun. We'll see.") else doer.components.talker:Say("Another thirty minutes added!") end end -- First activation or extension if not inst.coin_inserted then inst.coin_inserted = true inst:AddTag("coin_inserted") end -- Activate if not already active if not inst.is_active then SetActive(inst, true) end -- Update timer UpdateTimer(inst, COIN_TIME) return true end -- Destruction Handlers local function onHammered(inst) SpawnPrefab("collapse_small").Transform:SetPosition(inst.Transform:GetWorldPosition()) inst.SoundEmitter:PlaySound("dontstarve/common/destroy_wood") if inst.activation_timer then inst.activation_timer:Cancel() end if inst.attract_task then inst.attract_task:Cancel() end if inst.spawn_task then inst.spawn_task:Cancel() end inst:Remove() end local function onWorked(inst, worker, workleft) if workleft <= 0 then onHammered(inst) else inst.AnimState:PlayAnimation("shifted") inst.AnimState:PushAnimation("idle", false) end end local function onBurnt(inst) inst.AnimState:PlayAnimation("burnt") if inst.activation_timer then inst.activation_timer:Cancel() end if inst.attract_task then inst.attract_task:Cancel() end if inst.spawn_task then inst.spawn_task:Cancel() end if inst.SoundEmitter then inst.SoundEmitter:KillSound(LOOP_HANDLE) end inst:DoTaskInTime(3.5, inst.Remove) end ------------------------------------------------------------------ -- Prefab Definition ------------------------------------------------------------------ local function fn() local inst = CreateEntity() inst:AddTag("structure") inst.entity:AddTransform() inst.Transform:SetScale(1.5, 1.5, 1.5) inst.entity:AddAnimState() inst.entity:AddMiniMapEntity() inst.entity:AddSoundEmitter() inst.entity:AddNetwork() inst.MiniMapEntity:SetIcon("monkeygraph.tex") inst.AnimState:SetBank("monkeygraph") inst.AnimState:SetBuild("monkeygraph") inst.AnimState:PlayAnimation("idle") MakeObstaclePhysics(inst, 1.0) inst.entity:SetPristine() if not TheWorld.ismastersim then return inst end -------------------------------------------------------------- -- Server-side Components & State -------------------------------------------------------------- -- Initialize state variables inst.is_active = false inst.is_muted = false inst.coin_inserted = false inst.remaining_time = 0 inst.last_timer_start = nil inst.activation_timer = nil inst.attract_task = nil inst.spawn_task = nil -- Inspectable component inst:AddComponent("inspectable") inst.components.inspectable.getspecialdescription = function(inst, viewer) if inst.is_active then if inst.is_muted then return "It's active for 30 minutes per coin. Sound is muted." else return "It's playing and seems to attract monkeys." end else if inst.coin_inserted then return "A coin was inserted. Insert more to extend time." else return "Insert a guycoin to activate (30 minutes per coin)." end end end -- Workable component inst:AddComponent("workable") inst.components.workable:SetWorkAction(ACTIONS.HAMMER) inst.components.workable:SetWorkLeft(6) inst.components.workable:SetOnFinishCallback(onHammered) inst.components.workable:SetOnWorkCallback(onWorked) -- Machine component (for mute functionality) inst:AddComponent("machine") inst.components.machine.turnonfn = onUnmute -- "Turn On" = Sound on inst.components.machine.turnofffn = onMute -- "Turn Off" = Sound off inst.components.machine.canturnoff = false inst.components.machine.caninteract = true inst.components.machine.ison = not inst.is_muted -- Trader component (for coin insertion) inst:AddComponent("trader") inst.components.trader:SetAcceptTest(function(inst, item) return item.prefab == "guycoin" end) inst.components.trader.onaccept = function(inst, giver, item) return OnInsertCoin(inst, giver, item) end -- Additional components inst:AddComponent("lootdropper") inst:AddComponent("burnable") inst.components.burnable:SetOnBurntFn(onBurnt) return inst end return Prefab("monkeygraph", fn, assets), MakePlacer("monkeygraph_placer", "monkeygraph", "monkeygraph", "idle") Link to comment https://forums.kleientertainment.com/forums/topic/167395-user-defined-context-menu-entry-for-toggling-audio-muteunmute/ Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now