local assets = { Asset("SCRIPT", "scripts/prefabs/pocketwatch_common.lua"), Asset("ANIM", "anim/pocketwatch.zip"), Asset("ANIM", "anim/pocketwatch_marble.zip"), Asset("ANIM", "anim/pocketwatch_recall.zip"), Asset("ANIM", "anim/pocketwatch_warp.zip"), Asset("ANIM", "anim/pocketwatch_wood.zip"), } local prefabs = { "pocketwatch_cast_fx", "pocketwatch_cast_fx_mount", "pocketwatch_heal_fx", "pocketwatch_heal_fx_mount", "pocketwatch_ground_fx", "pocketwatch_warp_marker", "pocketwatch_warpback_fx", "pocketwatch_warpbackout_fx", "pocketwatch_revive_reviver", } local PocketWatchCommon = require "prefabs/pocketwatch_common" ------------------------------------------------------------------------------- local function increasePerishable(perishable, amount) local cur = perishable:GetPercent() perishable:SetPercent(cur + amount) end local function Heal_DoCastSpell(inst, doer) local health = doer.components.health local inv = doer and doer.components.inventory local perishamount = inst.unspoil_rate if inv then local pack = inv:GetEquippedItem(EQUIPSLOTS.BODY) local helm = inv:GetEquippedItem(EQUIPSLOTS.HEAD) if helm and helm.components.perishable then increasePerishable(helm.components.perishable, perishamount) end if pack and pack.components.container then for k = 1, pack.components.container.numslots do local item = pack.components.container.slots[k] if item and item.components.edible and item.components.perishable then increasePerishable(item.components.perishable, perishamount) end end end for k = 1, inv.maxslots do local item = inv.itemslots[k] if item and item.components.edible and item.components.perishable then increasePerishable(item.components.perishable, perishamount) end end end if health ~= nil and not health:IsDead() then doer.components.oldager:StopDamageOverTime() health:DoDelta(TUNING.POCKETWATCH_HEAL_HEALING, true, inst.prefab) local fx = SpawnPrefab((doer.components.rider ~= nil and doer.components.rider:IsRiding()) and "pocketwatch_heal_fx_mount" or "pocketwatch_heal_fx") fx.entity:SetParent(doer.entity) inst.components.rechargeable:Discharge(TUNING.POCKETWATCH_HEAL_COOLDOWN) return true end end local MOUNTED_CAST_TAGS = {"pocketwatch_mountedcast"} local function healfn() local inst = PocketWatchCommon.common_fn("pocketwatch", "pocketwatch_marble", Heal_DoCastSpell, true, MOUNTED_CAST_TAGS) if not TheWorld.ismastersim then return inst end inst.castfxcolour = {255 / 255, 241 / 255, 236 / 255} return inst end ------------------------------------------------------------------------------- local PLAYERSKELETON_TAG = {"playerskeleton"} local function revive_onActivateResurrection(inst, target) local x, y, z = target.Transform:GetWorldPosition() local playerskeletons = TheSim:FindEntities(x, y, z, 1, PLAYERSKELETON_TAG) for i, skeleton in ipairs(playerskeletons) do if skeleton.userid == target.userid then if skeleton.components.lootdropper ~= nil then skeleton.components.lootdropper:DropLoot() end skeleton:Remove() break end end end local function revive_revivier_onActivateResurrection(inst, target) revive_onActivateResurrection(inst, target) inst:Remove() end local function ReviveOwner(inst) local owner = inst.components.inventoryitem:GetGrandOwner() if owner == nil or not owner:HasTag("playerghost") then inst:Remove() return end if owner.last_death_shardid == TheShard:GetShardId() then owner:PushEvent("respawnfromghost", { source = inst }) end end local function revive_reviverfn() -- this is used to revive players after pocketwatch_revive migrates them to another shard local inst = CreateEntity() inst.entity:AddTransform() inst.entity:AddNetwork() inst.entity:SetPristine() if not TheWorld.ismastersim then return inst end inst:AddComponent("inventoryitem") inst.components.inventoryitem:SetSinks(true) inst:ListenForEvent("activateresurrection", revive_revivier_onActivateResurrection) inst:DoTaskInTime(0, ReviveOwner) return inst end ------------------------------------------------------------------------------- local function Revive_CanTarget(inst, doer, target) -- This is a client side function return target ~= nil and target:HasTag("playerghost") and not target:HasTag("reviving") end local function Revive_DoCastSpell(inst, doer, target) if Revive_CanTarget(inst, doer, target) and inst.components.pocketwatch.inactive then if target.last_death_shardid ~= nil and target.last_death_shardid ~= TheShard:GetShardId() then -- if the player is about to get teleported to another shard, give them this item so they will revive on the other side target.components.inventory:GiveItem(SpawnPrefab("pocketwatch_revive_reviver")) end target:PushEvent("respawnfromghost", { source = inst, from_haunt = doer == target }) inst.components.rechargeable:Discharge(TUNING.POCKETWATCH_REVIVE_COOLDOWN) return true end return false, "REVIVE_FAILED" end local function Revive_OnHaunt(inst, haunter) inst.components.hauntable.hauntvalue = TUNING.HAUNT_SMALL if haunter:HasTag("pocketwatchcaster") and inst.components.pocketwatch:CastSpell(haunter, haunter) then inst.components.lootdropper:DropLoot() SpawnPrefab("brokentool").Transform:SetPosition(inst.Transform:GetWorldPosition()) inst:Remove() -- cannot withstand the paradox of being haunted by Wanda’s timeline else Launch(inst, haunter, TUNING.LAUNCH_SPEED_SMALL) end end local function revivefn() local inst = PocketWatchCommon.common_fn("pocketwatch", "pocketwatch_wood", Revive_DoCastSpell, false, MOUNTED_CAST_TAGS) inst.GetActionVerb_CAST_POCKETWATCH = "REVIVER" inst.pocketwatch_CanTarget = Revive_CanTarget if not TheWorld.ismastersim then return inst end inst.castfxcolour = {219 / 255, 153 / 255, 109 / 255} inst.components.pocketwatch.CanCastFn = Revive_CanTarget inst.components.hauntable:SetOnHauntFn(Revive_OnHaunt) inst:ListenForEvent("activateresurrection", revive_onActivateResurrection) return inst end ------------------------------------------------------------------------------- local function recallmarker_ShowMarker(inst, viewer) inst.Network:SetClassifiedTarget(viewer) if viewer ~= ThePlayer then -- hide it from the locally hosted server player inst.AnimState:OverrideMultColour(0, 0, 0, 0) end end local function recallmarker_RemoveMarker(inst, viewer) if inst:IsAsleep() then inst:Remove() else inst.AnimState:PlayAnimation("idle_pst") inst.AnimState:PushAnimation("off", false) inst:DoTaskInTime(0.5, inst.Remove) end end local function recallmarkerfn() local inst = CreateEntity() inst.entity:AddTransform() inst.entity:AddAnimState() inst.entity:AddNetwork() inst.AnimState:SetBuild("pocketwatch_warp_marker") inst.AnimState:SetBank("pocketwatch_warp_marker") inst.AnimState:SetOrientation(ANIM_ORIENTATION.OnGround) inst.AnimState:SetLayer(LAYER_BACKGROUND) inst.AnimState:PlayAnimation("idle_pre") inst.AnimState:PushAnimation("idle_loop", true) inst.AnimState:SetMultColour(0.6, 0.6, 0.6, 0.6) inst:AddTag("NOBLOCK") inst:AddTag("FX") inst.entity:SetPristine() if not TheWorld.ismastersim then return inst end inst.persists = false inst.ShowMarker = recallmarker_ShowMarker inst.RemoveMarker = recallmarker_RemoveMarker return inst end local function DelayedMarkTalker(player) -- if the player starts moving right away then we can skip this if player.sg == nil or player.sg:HasStateTag("idle") then player.components.talker:Say(GetString(player, "ANNOUNCE_POCKETWATCH_MARK")) end end local function Recall_DoCastSpell(inst, doer, target, pos) local recallmark = inst.components.recallmark if recallmark:IsMarked() then if Shard_IsWorldAvailable(recallmark.recall_worldid) then inst.components.rechargeable:Discharge(TUNING.POCKETWATCH_RECALL_COOLDOWN) doer.sg.statemem.warpback = {dest_worldid = recallmark.recall_worldid, dest_x = recallmark.recall_x, dest_y = 0, dest_z = recallmark.recall_z, reset_warp = true} return true else return false, "SHARD_UNAVAILABLE" end else local x, y, z = doer.Transform:GetWorldPosition() inst.components.recallmark:MarkPosition(x, y, z) inst.SoundEmitter:PlaySound("wanda2/characters/wanda/watch/MarkPosition") doer:DoTaskInTime(12 * FRAMES, DelayedMarkTalker) return true end end local function Recall_ItemTradeTest(inst, item, giver) if item == nil then return false elseif string.sub(item.prefab, -3) ~= "gem" then return false, "NOTGEM" elseif item.prefab ~= "purplegem" then return false, "WRONGGEM" end return true end local function Recall_OnGemGiven(inst, giver, item) local portal_watch = SpawnPrefab("pocketwatch_portal") portal_watch:onPreBuilt(giver, {pocketwatch_recall = {[inst] = 1}}) local container = inst.components.inventoryitem:GetContainer() if container ~= nil then local slot = inst.components.inventoryitem:GetSlotNum() inst:Remove() container:GiveItem(portal_watch, slot) else local x, y, z = inst.Transform:GetWorldPosition() inst:Remove() portal_watch.Transform:SetPosition(x, y, z) end portal_watch.SoundEmitter:PlaySound("dontstarve/common/telebase_gemplace") end local function Recall_OnBuiltFn(inst, builder) builder.components.builder:AddRecipe("pocketwatch_portal") end local function Recall_GetActionVerb(inst, doer, target) return inst:HasTag("recall_unmarked") and "RECALL_MARK" or "RECALL" end local RECALL_WATCH_TAGS = {"pocketwatch_warp_casting", "gemsocket"} local function recallfn() local inst = PocketWatchCommon.common_fn("pocketwatch", "pocketwatch_recall", Recall_DoCastSpell, true, RECALL_WATCH_TAGS) inst.GetActionVerb_CAST_POCKETWATCH = Recall_GetActionVerb if not TheWorld.ismastersim then return inst end inst:AddComponent("trader") inst.components.trader:SetAbleToAcceptTest(Recall_ItemTradeTest) inst.components.trader.onaccept = Recall_OnGemGiven inst.OnBuiltFn = Recall_OnBuiltFn PocketWatchCommon.MakeRecallMarkable(inst) return inst end ------------------------------------------------------------------------------- local function warpmarker_SetMarkerViewer(inst, viewer) inst.Network:SetClassifiedTarget(viewer) if viewer ~= ThePlayer then -- hide it from the locally hosted server player inst.AnimState:OverrideMultColour(0, 0, 0, 0) end end local function warpmarker_HideMarker(inst) if inst.inuse then inst.inuse = false inst.AnimState:PlayAnimation("mark"..inst.anim_id.."_pst") inst.AnimState:PushAnimation("off", false) end end local function warpmarker_ShowMarker(inst) inst.anim_id = math.random(4) inst.AnimState:PlayAnimation("mark"..inst.anim_id.."_pre") inst.AnimState:PushAnimation("mark"..inst.anim_id.."_loop", true) inst.inuse = true inst.Transform:SetRotation(math.random(360)) inst:Show() end local function warpmarkerfn() local inst = CreateEntity() inst.entity:AddTransform() inst.entity:AddAnimState() inst.entity:AddNetwork() inst.AnimState:SetBuild("pocketwatch_warp_marker") inst.AnimState:SetBank("pocketwatch_warp_marker") inst.AnimState:SetOrientation(ANIM_ORIENTATION.OnGround) inst.AnimState:SetLayer(LAYER_BACKGROUND) inst.AnimState:PlayAnimation("off") inst.AnimState:SetMultColour(0.6, 0.6, 0.6, 0.6) inst:Hide() inst:AddTag("NOBLOCK") inst:AddTag("FX") inst.entity:SetPristine() if not TheWorld.ismastersim then return inst end inst.persists = false inst.SetMarkerViewer = warpmarker_SetMarkerViewer inst.ShowMarker = warpmarker_ShowMarker inst.HideMarker = warpmarker_HideMarker return inst end local function Warp_DoCastSpell(inst, doer) local tx, ty, tz = doer.components.positionalwarp:GetHistoryPosition(false) if tx ~= nil then inst.components.rechargeable:Discharge(TUNING.POCKETWATCH_WARP_COOLDOWN) doer.sg.statemem.warpback = {dest_x = tx, dest_y = ty, dest_z = tz} return true end return false, "WARP_NO_POINTS_LEFT" end local WARP_WATCH_TAGS = {"pocketwatch_warp", "pocketwatch_warp_casting"} local function warp_hidemarker(inst) if inst.marker_owner ~= nil and inst.marker_owner:IsValid() then inst.marker_owner:PushEvent("hide_warp_marker") end inst.marker_owner = nil end local function warp_showmarker(inst) warp_hidemarker(inst) inst.marker_owner = inst.components.inventoryitem:GetGrandOwner() if inst.marker_owner ~= nil then inst.marker_owner:PushEvent("show_warp_marker") end end local function warpfn() local inst = PocketWatchCommon.common_fn("pocketwatch", "pocketwatch_warp", Warp_DoCastSpell, true, WARP_WATCH_TAGS) inst.GetActionVerb_CAST_POCKETWATCH = "WARP" if not TheWorld.ismastersim then return inst end inst:ListenForEvent("onputininventory", warp_showmarker) inst:ListenForEvent("onownerputininventory", warp_showmarker) inst:ListenForEvent("ondropped", warp_hidemarker) inst:ListenForEvent("onownerdropped", warp_hidemarker) inst:ListenForEvent("onremove", warp_hidemarker) return inst end -------------------------------------------------------------------------------- return Prefab("pocketwatch_heal", healfn, assets, prefabs), Prefab("pocketwatch_revive", revivefn, assets, prefabs), Prefab("pocketwatch_revive_reviver", revive_reviverfn), Prefab("pocketwatch_warp", warpfn, assets, prefabs), Prefab("pocketwatch_warp_marker", warpmarkerfn, {Asset("ANIM", "anim/pocketwatch_warp_marker.zip")}), Prefab("pocketwatch_recall", recallfn, assets, prefabs), Prefab("pocketwatch_recall_marker", recallmarkerfn, {Asset("ANIM", "anim/pocketwatch_warp_marker.zip")})