-- Bundled by luabundle {"version":"1.6.0"}
local __bundle_require, __bundle_loaded, __bundle_register, __bundle_modules = (function(superRequire)
	local loadingPlaceholder = {[{}] = true}

	local register
	local modules = {}

	local require
	local loaded = {}

	register = function(name, body)
		if not modules[name] then
			modules[name] = body
		end
	end

	require = function(name)
		local loadedModule = loaded[name]

		if loadedModule then
			if loadedModule == loadingPlaceholder then
				return nil
			end
		else
			if not modules[name] then
				if not superRequire then
					local identifier = type(name) == 'string' and '\"' .. name .. '\"' or tostring(name)
					error('Tried to require ' .. identifier .. ', but no such module has been registered')
				else
					return superRequire(name)
				end
			end

			loaded[name] = loadingPlaceholder
			loadedModule = modules[name](require, loaded, register, modules)
			loaded[name] = loadedModule
		end

		return loadedModule
	end

	return require, loaded, register, modules
end)(nil)
__bundle_register("core/tour/TourManager", function(require, _LOADED, __bundle_register, __bundle_modules)
do
  require("core/tour/TourScript")
  require("core/tour/TourCard")
  local TourManager = { }
  local internal = { }

  -- Base IDs for various tour card UI elements.  Actual IDs will have _[playerColor] appended
  local CARD_ID = "tourCard"
  local LEFT_NARRATOR_ID = "tourNarratorImageLeft"
  local RIGHT_NARRATOR_ID = "tourNarratorImageRight"
  local BUBBLE_ID = "tourSpeechBubble"
  local TEXT_ID = "tourText"
  local NEXT_BUTTON_ID = "tourNext"
  local STOP_BUTTON_ID = "tourStop"

  -- Table centerpoint for the camera hook object.  Camera handling is a bit erratic so it doesn't
  -- always land right where you think it's going to, but it's close
  local HOOK_CAMERA_HOME = {
    x = -30.2,
    y = 60,
    z = 0,
  }

  -- Default (0) position for the camera, as defined in the mod.  If we don't recreate this position
  -- EXACTLY when exiting the tour then camera controls get weird
  local DEFAULT_CAMERA_POS = {
    position = { x = -22.265, y = -2.5, z = 5.2575},
    pitch=64.343,
    yaw=90.333,
    distance=104.7}

  -- Global XML coordinates where we can present a card
  local SCREEN_POSITIONS = {
    center = "0 0 0",
    north = "0 300 0",
    east = "600 0 0",
    west = "-600 0 0",
    south = "0 -300 0",
    -- Northwest is only used by the Mandy card, move it a little right than standard so it's
    -- closer to the importer
    northwest = "-500 300 0",
    northeast = "600 300 0",
    southwest = "-600 -300 0",
    -- Used by the Diana and Wini cards referencing the bottom-right global controls, moved a little
    -- closer to them
    southeast = "730 -365 0"
  }

  -- Tracks the current state of the tours.  Keyed by player color to keep each player's tour
  -- separate, will hold the camera hook and current card.
  local tourState = { }

  -- Kicks off the tour by initializing the card and camera hook.  A callback on the hook creation
  -- will then show the first card.
  ---@param playerColor String Player color to start the tour for
  TourManager.startTour = function(playerColor)
    tourState[playerColor] = {
      currentCardIndex = 1
    }
    -- Camera gets really screwy when we finalize if we don't start settled in ThirdPerson at the
    -- default position before attaching to the hook.  Unfortunately there are no callbacks for when
    -- the movement is done, but the delay seems to handle it
    Player[playerColor].setCameraMode("ThirdPerson")
    Player[playerColor].lookAt(DEFAULT_CAMERA_POS)
    -- Initial camera rotation is painfully slow.  White and Orange players are likely oriented
    -- correctly, but need a longer start delay for Green and Red
    local delay = 0.5
    if playerColor ~= "White" and playerColor ~= "Orange" then
      delay = 2
      broadcastToColor("Starting the tour, please wait...", playerColor)
    end
    Wait.time(function()
      internal.createTourCard(playerColor)
      -- XML update to add the new card takes a few frames to load, wait for it to finish then
      -- create the hook
      Wait.condition(
          function()
            internal.createCameraHook(playerColor)
          end,
          function()
            return not Global.UI.loading
          end
      )
    end, delay)
  end

  -- Shows the next card in the tour script.  This method is exposed (rather than being part of
  -- internal) because the XMLUI callbacks expect the method to be on the object directly.
  ---@param player Player object to show the next card for, provided by XMLUI callback
  function nextCard(player)
    internal.hideCard(player.color)
    Wait.time(function()
      tourState[player.color].currentCardIndex = tourState[player.color].currentCardIndex + 1
      if tourState[player.color].currentCardIndex > #TOUR_SCRIPT then
        internal.finalizeTour(player.color)
      else
        internal.showCurrentCard(player.color)
      end
    end, 0.3)
  end

  -- Ends the tour and cleans up the camera.  This method is exposed (rather than being part of
  -- internal) because the XMLUI callbacks expect the method to be on the object directly.
  ---@param player Player object to end the tour for, provided by XMLUI callback
  function stopTour(player)
    internal.hideCard(player.color)
    Wait.time(function()
      internal.finalizeTour(player.color)
    end, 0.3)
  end

  -- Updates the card UI for the script at the current index, moves the camera to the proper
  -- position, and shows the card.
  ---@param playerColor String Player color to show the current card for
  internal.showCurrentCard = function(playerColor)
    internal.updateCardDisplay(playerColor)
    local delay = 0
    local cardIndex = tourState[playerColor].currentCardIndex
    local hook = getObjectFromGUID(tourState[playerColor].cameraHookGuid)

    if not TOUR_SCRIPT[cardIndex].skipCentering then
      hook.setPositionSmooth(HOOK_CAMERA_HOME, false, false)
      delay = delay + 0.5
    end
    local lookPos
    if TOUR_SCRIPT[cardIndex].showObj ~= nil then
      local lookAtObj = getObjectFromGUID(TOUR_SCRIPT[cardIndex].showObj)
      lookPos = lookAtObj.getPosition()
      lookPos.y = TOUR_SCRIPT[cardIndex].distanceFromObj or 0
      -- Since camera isn't directly above the hook, changing the Y affects the visual position of
      -- whatever object we're trying to look at.  This is an approximation, but close enough to
      -- keep the object more centered
      lookPos.x = lookPos.x - lookPos.y / 2
    elseif TOUR_SCRIPT[cardIndex].showPos ~= nil then
      lookPos = TOUR_SCRIPT[cardIndex].showPos
    end
    if lookPos ~= nil then
      Wait.time(function()
        hook.setPositionSmooth(lookPos, false, false)
      end, delay)
      delay = delay + 0.5
    end
    Wait.time(function() Global.UI.show(internal.getUiId(CARD_ID, playerColor)) end, delay)
  end

  -- Hides the current card being shown to a player.  This can be in preparation for showing the
  -- next card, or ending the tour.
  ---@param playerColor String Player color to hide the current card for
  internal.hideCard = function(playerColor)
    Global.UI.hide(internal.getUiId(CARD_ID, playerColor))
  end

  -- Cleans up all the various resources associated with the tour, and (hopefully) resets the
  -- camera to the default position.  Camera handling is erratic, the final card in the script
  -- should include instructions for the player to fix it.
  ---@param playerColor String Player color to clean up
  internal.finalizeTour = function(playerColor)
    local cameraHook = getObjectFromGUID(tourState[playerColor].cameraHookGuid)
    cameraHook.destruct()
    Player[playerColor].setCameraMode("ThirdPerson")
    tourState[playerColor] = nil
    Wait.frames(function()
      Player[playerColor].lookAt(DEFAULT_CAMERA_POS)
    end, 3)
  end

  -- Updates the card UI to show the appropriate card configuration.
  ---@param playerColor String Player color to update card for
  internal.updateCardDisplay = function(playerColor)
    local index = tourState[playerColor].currentCardIndex
    Global.UI.setAttribute(internal.getUiId(LEFT_NARRATOR_ID, playerColor), "image", "Inv-" .. TOUR_SCRIPT[index].narrator)
    Global.UI.setAttribute(internal.getUiId(RIGHT_NARRATOR_ID, playerColor), "image", "Inv-" .. TOUR_SCRIPT[index].narrator)
    Global.UI.setAttribute(internal.getUiId(TEXT_ID, playerColor), "text", "\"" .. TOUR_SCRIPT[index].text .. "\"")
    local cardPos = TOUR_SCRIPT[index].position or "north"
    Global.UI.setAttribute(internal.getUiId(CARD_ID, playerColor), "position", SCREEN_POSITIONS[cardPos])
    Global.UI.setAttribute(internal.getUiId(NEXT_BUTTON_ID, playerColor), "active", index < #TOUR_SCRIPT)

    -- Adjust images so the narrator is on the left or right, as defined by the card
    if TOUR_SCRIPT[index].speakerSide == "right" then
      Global.UI.setAttribute(internal.getUiId(LEFT_NARRATOR_ID, playerColor), "active", false)
      Global.UI.setAttribute(internal.getUiId(RIGHT_NARRATOR_ID, playerColor), "active", true)
      Global.UI.setAttribute(internal.getUiId(BUBBLE_ID, playerColor), "rotation", "0 180 0")
      Global.UI.setAttribute(internal.getUiId(TEXT_ID, playerColor), "offsetXY", "-15 -15")
      Global.UI.setAttribute(internal.getUiId(NEXT_BUTTON_ID, playerColor), "offsetXY", "-35 -45")
      Global.UI.setAttribute(internal.getUiId(STOP_BUTTON_ID, playerColor), "offsetXY", "5 -45")
    else
      Global.UI.setAttribute(internal.getUiId(LEFT_NARRATOR_ID, playerColor), "active", true)
      Global.UI.setAttribute(internal.getUiId(RIGHT_NARRATOR_ID, playerColor), "active", false)
      Global.UI.setAttribute(internal.getUiId(BUBBLE_ID, playerColor), "rotation", "0 0 0")
      Global.UI.setAttribute(internal.getUiId(TEXT_ID, playerColor), "offsetXY", "15 -15")
      Global.UI.setAttribute(internal.getUiId(NEXT_BUTTON_ID, playerColor), "offsetXY", "-5 -45")
      Global.UI.setAttribute(internal.getUiId(STOP_BUTTON_ID, playerColor), "offsetXY", "35 -45")
    end
  end

  -- Creates a small, transparent object which the camera will be attached to in order to move the
  -- user's view around the table.  This should be called only at the beginning of the tour. Once
  -- creation is complete the user's camera will be attached to the hook and the first card will be
  -- shown.
  ---@param playerColor String Player color to create the hook for
  internal.createCameraHook = function(playerColor)
    local hookData = {
      Name = "BlockSquare",
      Transform = {
        posX = HOOK_CAMERA_HOME.x,
        posY = HOOK_CAMERA_HOME.y,
        posZ = HOOK_CAMERA_HOME.z,
        rotX = 0,
        rotY = 270.0,
        rotZ = 0,
        scaleX = 0.1,
        scaleY = 0.1,
        scaleZ = 0.1,
      },
      ColorDiffuse = {
        r = 0,
        g = 0,
        b = 0,
        a = 0,
      },
      Locked = true,
      GMNotes = playerColor
    }

    spawnObjectData({ data = hookData, callback_function = internal.onHookCreated })
  end

  -- Callback for creation of the camera hook object.  Will attach the camera and show the current
  -- (presumably first) card.
  ---@param hook Created object
  internal.onHookCreated = function(hook)
    local playerColor = hook.getGMNotes()
    tourState[playerColor].cameraHookGuid = hook.getGUID()
    Player[playerColor].attachCameraToObject({
      object = hook,
      offset = { x = -20, y = 30, z = 0 }
    })
    internal.showCurrentCard(playerColor)
  end

  -- Creates an XMLUI entry in Global for a player-specific tour card.  Dynamically creating this
  -- is somewhat complex, but ensures we can properly handle any player color.
  ---@param playerColor String Player color to create the card for
  internal.createTourCard = function(playerColor)
    -- Make sure the card doesn't exist before we create a new one
    if Global.UI.getAttributes(internal.getUiId(CARD_ID, playerColor)) ~= nil then
      return
    end
    tourCardTemplate.attributes.id = internal.getUiId(CARD_ID, playerColor)
    tourCardTemplate.children[1].attributes.id = internal.getUiId(LEFT_NARRATOR_ID, playerColor)
    tourCardTemplate.children[2].attributes.id = internal.getUiId(RIGHT_NARRATOR_ID, playerColor)
    tourCardTemplate.children[3].attributes.id = internal.getUiId(BUBBLE_ID, playerColor)
    tourCardTemplate.children[4].attributes.id = internal.getUiId(TEXT_ID, playerColor)
    tourCardTemplate.children[5].attributes.id = internal.getUiId(NEXT_BUTTON_ID, playerColor)
    tourCardTemplate.children[5].attributes.onClick = self.getGUID().."/nextCard"
    tourCardTemplate.children[6].attributes.id = internal.getUiId(STOP_BUTTON_ID, playerColor)
    tourCardTemplate.children[6].attributes.onClick = self.getGUID().."/stopTour"
    internal.setDeepVisibility(tourCardTemplate, playerColor)

    local globalXml = Global.UI.getXmlTable()
    table.insert(globalXml, tourCardTemplate)
    Global.UI.setXmlTable(globalXml)
  end

  -- Panels don't cause their children to inherit their visibility value, so this recurses down the
  -- XML table to set all children to the same visibility.
  ---@param xmlUi Table.  Lua table describing the XML
  ---@param playerColor String.  String color of the player to make this visible for
  internal.setDeepVisibility = function(xmlUi, playerColor)
    xmlUi.attributes.visibility = "" .. playerColor
    if xmlUi.children ~= nil then
      for _, child in ipairs(xmlUi.children) do
        internal.setDeepVisibility(child, playerColor)
      end
    end
  end

  internal.getUiId = function(baseId, playerColor)
    return baseId .. "_" .. playerColor
  end

  return TourManager
end
end)
__bundle_register("core/tour/TourCard", function(require, _LOADED, __bundle_register, __bundle_modules)
-- Table definition for the tour card layout.  This is functionally XMLUI in Lua form, but using
-- this for dynamic creation ensures we can handle any player color without needing 10
-- near-duplicate definitions in Global.xml

tourCardTemplate = {
  tag = "Panel",
  attributes = {
    id = "tourCard",
    height = 215,
    width = 330,
    rotation = "0 0 0",
    position = "0 300 30",
    showAnimation = "FadeIn",
    hideAnimation = "FadeOut",
    active=false,
  },
  children = {
    {
      tag = "Image",
      attributes = {
        id = "tourNarratorImageLeft",
        height=120,
        width=80,
        rectAlignment="UpperLeft",
        offsetXY = "-80 0",
        -- Image will be set when the card is updated
      }
    },
    {
      tag = "Image",
      attributes = {
        id = "tourNarratorImageRight",
        active = false,
        height=125,
        width=80,
        rectAlignment="UpperRight",
        offsetXY = "80 0"
        -- Image will be set when the card is updated
      }
    },
    {
      tag = "Image",
      attributes = {
        id = "tourSpeechBubble",
        color = "#F5F5DC",
        height = 215,
        width = 330,
        rectAlignment = "MiddleCenter",
        image = "SpeechBubble",
      },
    },
    {
      tag = "Text",
      attributes = {
        id = "tourText",
        -- Everything on this is double-sized and scaled down to keep the text sharps
        height = 370,
        width = 520,
        scale = "0.5 0.5 1",
        rectAlignment = "UpperCenter",
        offsetXY = "15 -15",
        resizeTextForBestFit = true,
        resizeTextMinSize = 20,
        resizeTextMaxSize = 32,
        color = "#050505",
        alignment = "UpperLeft",
        horizontalOverflow = "wrap",
      }
    },
    {
      tag = "Image",
      attributes = {
        id = "tourNext",
        height = 45,
        width = 45,
        rectAlignment = "LowerRight",
        offsetXY = "-5 -45",
        image = "NextArrow"
      },
    },
    {
      tag = "Image",
      attributes = {
        id = "tourStop",
        height = 45,
        width = 45,
        rectAlignment = "LowerLeft",
        offsetXY = "35 -45",
        image = "Exit"
      }
    },
  }
}
end)
__bundle_register("core/tour/TourScript", function(require, _LOADED, __bundle_register, __bundle_modules)
-- Script for the SCED tour.  Documentation and definitions to come.

TOUR_SCRIPT = {
  {
    narrator = "Roland",
    text = "Despite my best efforts, looks like you found us.  You may live to regret that.  As long as you're here though we might as well show you around.\n\nUse the arrow to move forward, and if the horrors get to be too much you can quit whenever you like.  Ready to get started?",
    position = "center"
  },
  {
    narrator = "Darrell",
    text = "Cameras can be tricky things.  Best you leave handling it to the professionals during the tour.  Don't try to move the camera until the tour is complete.\n\nOnce we're done, remember you can use the 'p' key to switch back to third-person mode, and the spacebar to reset the position.",
    position = "center",
    speakerSide = "right",
  },
  {
    narrator = "Daisy",
    text = "If you're new to the game, the library here has everything you'll need.  A little research can go a long way, and looking into old newspapers for the weird and unusual can yield some surprisingly helpful information.\n\nI put a few right there that might prove enlightening.",
    showObj = "d99993",
    distanceFromObj = 20,
    position = "west",
    speakerSide = "right"
  },
  {
    narrator = "Mandy",
    text = "To survive what's coming you'll need a deck.  If it's safely hidden away on ArkhamDB you can load it here, and even find the newest version after an upgrade without changing the ID.\n\nNo need to publish all your decks, use 'Private' and you can see it.  Just make sure to select 'Make your decks public' in ArkhamDB.",
    showObj = "a28140",
    distanceFromObj = -5,
    position = "northwest",
    skipCentering = true,
  },
  {
    narrator = "Daniela",
    text = "I prefer the hands-on approach to building things, if you do too you can build a deck yourself.\n\nAll the cards you could ever need are here, laid out like a disassembled engine.  Place the cards on the table, copy them for your deck, and you'll be ready for anything.",
    showObj = "2d30ee",
    distanceFromObj = -7,
    position = "south",
    speakerSide = "right"
  },
  {
    narrator = "Finn",
    text = "Ready to face the unknown?  We've smuggled shocking revelations and devious enemies from all over the world.  Download the campaign you want to play, then Place it on the table to see the scenarios.\n\nJust remember - if it turns out to be too much for you, I was never here.",
    showObj = "aca04c",
    distanceFromObj = 20,
    position = "northwest",
  },
  {
    narrator = "Diana",
    text = "These symbols on the bottom right are a repository of arcane knowledge, containing all the official content to download plus some deviously creative works from fans.  One should beware those who seem too fond of the darkness, but you cannot deny the quality of their efforts.\n\nDon't see anything here?  Only promoted players can access these.",
    position = "southeast",
  },
  {
    narrator = "Winifred",
    text = "No good aviator would fly a plane she didn't know and hadn't tweaked a bit herself.  The gear icon contains settings to customize your play experience, from alternate ways to track your clues to a variety of helpers to streamline the game.\n\nEverything here is optional, but who doesn't want to go as fast as they can?  Just remember that all settings affect all players, so strap in and trust your pilot!",
    position = "southeast",
  },
  {
    narrator = "Amina",
    text = "This is the Mythos area.  Encounter cards, acts, and agenda will all be placed here while the large map below is where you will be exploring - be sure to set the number of investigators!\n\nYou can count doom on the agenda by clicking the large counter, and the smaller will automatically count doom tokens on the table.  The chaos bag is in that book over on the right, and you can add or remove tokens from it whenever you need.",
    showPos = { x = -2.85, y = 0, z = 0.55 },
    position = "north",
    speakerSide = "right"
  },
  {
    narrator = "Gloria",
    text = "The evils that lurk in this world are out there, creeping ever closer.  When they find you, this will easily draw a card from the encounter deck.  The deck will even reshuffle itself when needed, for the enemies we face are unending.",
    showPos = { x = -35, y = -20, z = 28 },
    position = "west",
  },
  {
    narrator = "Jacqueline",
    text = "When the ire of fate finds you and the chaos looms, this large button will draw a chaos token.  Click it again to return the token to the bag.\n\nWhether a vision of the future or a curse from the opponents we face, if you need additional tokens a right-click will draw more.  I wish you luck, but have a vision of red tentacles reaching for you...",
    showPos = { x = -35, y = -20, z = 4.25 },
    position = "north",
    skipCentering = true,
    speakerSide = "right"
  },
  {
    narrator = "Preston",
    text = "I can afford to buy what I need, but for those less well-off we've provided an endless pool of tokens to track your game.  Simply drag one out of the pools here.\n\nResources are my favorite of course, but damage and horror are as inevitable as taxes.  I leave those to my bookkeeper though.  Those tokens can work like counters, use the number keys to change the value.",
    showObj = "9fadf9",
    position = "north",
    skipCentering = true,
    speakerSide = "right"
  },
  {
    narrator = "Norman",
    text = "That's the end of the tour, but there's much more to discover if you look in the right places.  Some cards have helpers on the right-click menu, and every new version adds new content and functions.\n\nDon't be afraid to explore, and best of luck out there!  We'll all need it...",
    position = "center",
    speakerSide = "right"
  },
}
end)
__bundle_register("__root", function(require, _LOADED, __bundle_register, __bundle_modules)
require("core/tour/TourStarter")
end)
__bundle_register("core/tour/TourStarter", function(require, _LOADED, __bundle_register, __bundle_modules)
local tourManager = require("core/tour/TourManager")

function onLoad()
  self.createButton({
    click_function = "startTour",
    function_owner = self,
    position = { 1.27, 0.05, 0.035},
    width = 500,
    height = 20,
    color = { 0, 0, 0, 0 },
    -- TTS has a minium height for buttons, have to scale the Z-axis down to get the right size
    scale = { 1, 1, 0.82 },
    tooltip = "Start the Tour",
  })
  self.createButton({
    click_function = "deleteStarter",
    function_owner = self,
    position = { 1.27, 0.05, 0.309},
    width = 500,
    height = 20,
    color = { 0, 0, 0, 0 },
    -- TTS has a minium height for buttons, have to scale the Z-axis down to get the right size
    scale = { 1, 1, 0.82 },
    tooltip = "Delete this Panel",
  })
end

function startTour(_, playerColor, _)
  tourManager.startTour(playerColor)
end

function deleteStarter(_, _, _)
  self.destruct()
end
end)
return __bundle_require("__root")