Merge pull request #48 from argonui/spawn_bag

Create SpawnBag to replace card memory bags
This commit is contained in:
Buhallin 2022-11-17 01:44:01 -08:00 committed by GitHub
commit d830314598
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 298 additions and 26 deletions

View File

@ -5,31 +5,11 @@
"z": 0 "z": 0
}, },
"Autoraise": true, "Autoraise": true,
"Bag": {
"Order": 0
},
"ColorDiffuse": { "ColorDiffuse": {
"b": 1, "b": 1,
"g": 1, "g": 1,
"r": 1 "r": 1
}, },
"ContainedObjects_order": [
"GuardianoftheCrystallizer.100e24",
"Augur.143e38",
"Bloodlust.14c7e0",
"WishEater.20570c",
"PendantoftheQueen.29f699",
"TheStarsAreRight.406a2b",
"UnboundBeast.4a8eef",
"Blood-Rite.4cc413",
"Hope.4da36d",
"Zeal.506382",
"EssenceoftheDream.6ad46b",
"SoothingMelody.6ced1f",
"DreamParasite.cb91bd",
"Dream-Gate.fa4c1e"
],
"ContainedObjects_path": "Bonded.c5261f",
"CustomMesh": { "CustomMesh": {
"CastShadows": true, "CastShadows": true,
"ColliderURL": "", "ColliderURL": "",
@ -48,7 +28,7 @@
"MaterialIndex": 3, "MaterialIndex": 3,
"MeshURL": "https://pastebin.com/raw/ALrYhQGb", "MeshURL": "https://pastebin.com/raw/ALrYhQGb",
"NormalURL": "", "NormalURL": "",
"TypeIndex": 6 "TypeIndex": 0
}, },
"Description": "", "Description": "",
"DragSelectable": true, "DragSelectable": true,
@ -61,12 +41,10 @@
"IgnoreFoW": false, "IgnoreFoW": false,
"LayoutGroupSortIndex": 0, "LayoutGroupSortIndex": 0,
"Locked": true, "Locked": true,
"LuaScriptState_path": "Bonded.c5261f.luascriptstate", "LuaScript": "require(\"playercards/spawnbag/BondedBag\")",
"LuaScript_path": "Bonded.c5261f.ttslua", "LuaScriptState": "{\"spawnBagState\":{\"placed\":[],\"placedObjects\":[]}}",
"MaterialIndex": -1,
"MeasureMovement": false, "MeasureMovement": false,
"MeshIndex": -1, "Name": "Custom_Model",
"Name": "Custom_Model_Bag",
"Nickname": "Bonded", "Nickname": "Bonded",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,

View File

@ -1,3 +1,9 @@
-- Amount to shift for the next card (zShift) or next row of cards (xShift)
-- Note that the table rotation is weird, and the X axis is vertical while the
-- Z axis is horizontal
local SPREAD_Z_SHIFT = -2.3
Spawner = { } Spawner = { }
-- Spawns a list of cards at the given position/rotation. This will separate cards by size - -- Spawns a list of cards at the given position/rotation. This will separate cards by size -
@ -39,6 +45,18 @@ Spawner.spawnCards = function(cardList, pos, rot, sort, callback)
Spawner.spawn(miniCards, position, rot, callback) Spawner.spawn(miniCards, position, rot, callback)
end end
Spawner.spawnCardSpread = function(cardList, startPos, rot, sort, callback)
if (sort) then
table.sort(cardList, Spawner.cardComparator)
end
local position = { x = startPos.x, y = startPos.y, z = startPos.z }
for _, card in ipairs(cardList) do
Spawner.spawn({ card }, position, rot, callback)
position.z = position.z + SPREAD_Z_SHIFT
end
end
-- Spawn a specific list of cards. This method is for internal use and should not be called -- Spawn a specific list of cards. This method is for internal use and should not be called
-- directly, use spawnCards instead. -- directly, use spawnCards instead.
-- @param cardList: A list of Player Card data structures (data/metadata) -- @param cardList: A list of Player Card data structures (data/metadata)

View File

@ -0,0 +1,77 @@
require("playercards/spawnbag/SpawnBag")
SPAWN_SPEC = {
name = "BondedCards",
cards = {
"05314", -- Soothing Melody
"06277", -- Wish Eater
"06019", -- Bloodlust
"06022", -- Pendant of the Queen
"05317", -- Blood-rite
"06113", -- Essence of the Dream
"06028", -- Stars Are Right
"06025", -- Guardian of the Crystallizer
"06283", -- Unbound Beast
"06032", -- Zeal
"06031", -- Hope
"06033", -- Augur
"06331", -- Dream Parasite
"06015a", -- Dream-Gate
},
globalPos = { x = -33.88, y = 1.5, z = 85.61 },
rotation = { x = 0, y = 270, z = 0 },
spread = true,
}
function onLoad(savedData)
if (savedData ~= nil) then
local saveState = JSON.decode(savedData)
if (saveState.spawnBagState ~= nil) then
SpawnBag.loadFromSave(saveState.spawnBagState)
end
end
createActionButtons()
end
function onSave()
local saveState = {
spawnBagState = SpawnBag.getStateForSave(),
}
return JSON.encode(saveState)
end
function createActionButtons()
self.createButton({
label="Place",
click_function="buttonClick_place",
function_owner=self,
position={1,0.1,2.1},
rotation={0,0,0},
height=350,
width=800,
font_size=250,
color={0,0,0},
font_color={1,1,1}
})
self.createButton({
label="Recall",
click_function="buttonClick_recall",
function_owner=self,
position={-1,0.1,2.1},
rotation={0,0,0},
height=350,
width=800,
font_size=250,
color={0,0,0},
font_color={1,1,1}
})
end
function buttonClick_place()
SpawnBag.spawn(SPAWN_SPEC)
end
-- Recalls objects to bag from table
function buttonClick_recall()
SpawnBag.recall()
end

View File

@ -0,0 +1,199 @@
require("playercards/PlayerCardSpawner")
-- Allows spawning of defined lists of cards which will be created from the template in the All
-- Player Cards bag. SpawnBag.spawn will create objects based on a table definition, while
-- SpawnBag.recall will clean them all up. Recall will be limited to a small area around the
-- spawned objects. Objects moved out of this area will not be cleaned up.
--
-- SpawnSpec: Spawning requires a spawn specification with the following structure:
-- {
-- name: Name of this spawn content, used for internal tracking. Multiple specs can be spawned,
-- but each requires a separate name
-- cards: A list of card IDs to be spawned
-- globalPos: Where the spawned objects should be placed, in global coordinates. This should be
-- a valid Vector with x, y, and z defined, e.g. { x = 5, y = 1, z = 15 }
-- rotation: Rotation for the spawned objects. X=180 should be used for face down items. As with
-- globalPos, this should be a valid Vector with x, y, and z defined
-- spread: Optional. If present and true, cards will be spawned next to each other in a spread
-- moving to the right. globalPos will define the location of the first card, each after that
-- will be moved a predefined distance
-- }
-- See BondedBag.ttslua for an example
SpawnBag = { }
-- To assist debugging, will draw a box around the recall zone when it's set up
local SHOW_RECALL_ZONE = false
local ALL_CARDS_GUID = "15bb07"
-- Distance to expand the recall zone around any added object.
local RECALL_BUFFER_X = 0.9
local RECALL_BUFFER_Z = 0.5
-- In order to mimic the behavior of the previous memory buttons we use a temporary bag when
-- recalling objects. This bag is tiny and transparent, and will be placed at the same location as
-- this object. Once all placed cards are recalled bag to this bag, it will be destroyed
local RECALL_BAG = {
Name = "Bag",
Transform = {
scaleX = 0.01,
scaleY = 0.01,
scaleZ = 0.01,
},
ColorDiffuse = {
r = 0,
g = 0,
b = 0,
a = 0,
},
Locked = true,
Grid = true,
Snap = false,
Tooltip = false,
}
-- Tracks what has been placed by this "bag" so they can be recalled
local placedSpecs = { }
local placedObjectGuids = { }
local recallZone = nil
-- Loads a table of saved state, extracted during the parent object's onLoad
SpawnBag.loadFromSave = function(saveTable)
placedSpecs = saveTable.placed
placedObjectGuids = saveTable.placedObjects
recallZone = saveTable.recall
end
-- Generates a table of save state that can be included in the parent object's onSave
SpawnBag.getStateForSave = function()
return {
placed = placedSpecs,
placedObjects = placedObjectGuids,
recall = recallZone,
}
end
-- Places the given spawnSpec on the table. See SpawnBag.ttslua header for spawnSpec table data and
-- examples
SpawnBag.spawn = function(spawnSpec)
-- Limit to one placement at a time
if (placedSpecs[spawnSpec.name]) then
return
end
if (spawnSpec == nil) then
-- TODO: error here
return
end
local cardsToSpawn = { }
local allCardsBag = getObjectFromGUID(ALL_CARDS_GUID)
for _, cardId in ipairs(spawnSpec.cards) do
local cardData = allCardsBag.call("getCardById", { id = cardId })
if (cardData ~= nil) then
table.insert(cardsToSpawn, cardData)
else
-- TODO: error here
end
end
if (spawnSpec.spread) then
Spawner.spawnCardSpread(cardsToSpawn, spawnSpec.globalPos, spawnSpec.rotation, false, recordPlacedObject)
else
Spawner.spawnCards(cardsToSpawn, spawnSpec.globalPos, spawnSpec.rotation, false, recordPlacedObject)
end
placedSpecs[spawnSpec.name] = true
end
-- Recalls all spawned objects to the bag, and clears the placedObjectGuids list
SpawnBag.recall = function()
local trash = spawnObjectData({data = RECALL_BAG, position = self.getPosition()})
for guid, _ in pairs(placedObjectGuids) do
local obj = getObjectFromGUID(guid)
if (obj ~= nil) then
if (isInRecallZone(obj)) then
trash.putObject(obj)
end
placedObjectGuids[guid] = nil
end
end
trash.destruct()
-- We've recalled everything we can, some cards may have been moved out of the
-- card area. Just reset at this point.
placedSpecs = { }
placedObjectGuids = { }
recallZone = nil
end
-- Callback for when an object has been spawned. Tracks the object for later recall and updates the
-- recall zone.
function recordPlacedObject(spawned)
placedObjectGuids[spawned.getGUID()] = true
expandRecallZone(spawned)
end
-- Expands the current recall zone based on the position of the given object. The recall zone will
-- be maintained as the bounding box of the extreme object positions, plus a small amount of buffer
function expandRecallZone(spawnedCard)
local pos = spawnedCard.getPosition()
if (recallZone == nil) then
-- First card out of the bag, initialize surrounding that
recallZone = { }
recallZone.upperLeft = { x = pos.x + RECALL_BUFFER_X, z = pos.z + RECALL_BUFFER_Z }
recallZone.lowerRight = { x = pos.x - RECALL_BUFFER_X, z = pos.z - RECALL_BUFFER_Z }
return
else
if (pos.x > recallZone.upperLeft.x) then
recallZone.upperLeft.x = pos.x + RECALL_BUFFER_X
end
if (pos.x < recallZone.lowerRight.x) then
recallZone.lowerRight.x = pos.x - RECALL_BUFFER_X
end
if (pos.z > recallZone.upperLeft.z) then
recallZone.upperLeft.z = pos.z + RECALL_BUFFER_Z
end
if (pos.z < recallZone.lowerRight.z) then
recallZone.lowerRight.z = pos.z - RECALL_BUFFER_Z
end
end
if (SHOW_RECALL_ZONE) then
local y = 1.5
local thick = 0.05
Global.setVectorLines({
{
points = { {recallZone.upperLeft.x,y,recallZone.upperLeft.z}, {recallZone.upperLeft.x,y,recallZone.lowerRight.z} },
color = {1,0,0},
thickness = thick,
rotation = {0,0,0},
},
{
points = { {recallZone.upperLeft.x,y,recallZone.lowerRight.z}, {recallZone.lowerRight.x,y,recallZone.lowerRight.z} },
color = {1,0,0},
thickness = thick,
rotation = {0,0,0},
},
{
points = { {recallZone.lowerRight.x,y,recallZone.lowerRight.z}, {recallZone.lowerRight.x,y,recallZone.upperLeft.z} },
color = {1,0,0},
thickness = thick,
rotation = {0,0,0},
},
{
points = { {recallZone.lowerRight.x,y,recallZone.upperLeft.z}, {recallZone.upperLeft.x,y,recallZone.upperLeft.z} },
color = {1,0,0},
thickness = thick,
rotation = {0,0,0},
},
})
end
end
-- Checks to see if the given object is in the current recall zone. If there isn't a recall zone,
-- will return true so that everything can be easily cleaned up.
function isInRecallZone(obj)
if (recallZone == nil) then
return true
end
local pos = obj.getPosition()
return (pos.x < recallZone.upperLeft.x and pos.x > recallZone.lowerRight.x
and pos.z < recallZone.upperLeft.z and pos.z > recallZone.lowerRight.z)
end