--[[ Lua code. See documentation: http://berserk-games.com/knowledgebase/scripting/ --]] --[[ The onLoad event is called after the game save finishes loading. --]] function onLoad() --[[ print('onLoad!') --]] init() -- Setup... publicDeckURL="https://arkhamdb.com/api/public/decklist/" privateDeckURL="https://arkhamdb.com/api/public/deck/" cardURL="https://arkhamdb.com/api/public/card/" tabooListURL="https://arkhamdb.com/api/public/taboos/" subnameCards={{name="Randolph Carter",xp=0},{name="Dream Diary",xp=3},{name="Relic of Ages",xp=0},{name="The Necronomicon",xp=0},{name="Archaic Glyphs",xp=3},{name="Strange Solution",xp=4},{name="Relic of Ages",xp=0}, {name="Ancient Stone",xp=4}, {"Dream Diary",xp=0}} extraPermanents={["Duke"]=true,["Sophie"]=true} multiClassCards={{name=".45 Thompson",xp=3},{name="Scroll of Secrets",xp=3},{name="Tennessee Sour Mash",xp=3},{name="Enchanted Blade",xp=3},{name="Grisly Totem",xp=3}} returningCards={{name="On Your Own"}} advancedSignatures={{name="Daisy's Tote Bag"},{name="The Necronomicon"}} bondedCardsOneThree={{name="Hallowed Mirror",bondedName="Soothing Melody",bondedCode=05314},{name="Occult Lexicon",bondedName="Blood-Rite",bondedCode=05317},{name="The Hungering Blade",bondedName="Bloodlust",bondedCode=06019},{name="Nightmare Bauble",bondedName="Dream Parasite",bondedCode=06331},{name="Miss Doyle",bondedName="Hope",bondedCode=06031,bondedNameSecond="Zeal",bondedCodeSecond=06032,bondedNameThird="Augur",bondedCodeThird=06033}} bondedCardsBoolean = {{name="Gate Box",bondedName="Dream-Gate",bondedCode="06015a",bondCount=1},{name="Crystallizer of Dreams",bondedName="Guardian of the Crystallizer",bondedCode=06025,bondCount=2},{name="Dream Diary",bondedName="Essence of the Dream",bondedCode=06113,bondCount=1},{name="Empty Vessel",bondedName="Wish Eater",bondedCode=06277,bondCount=1},{name="Segment of Onyx",bondedName="Pendant of the Queen",bondedCode=06022,bondCount=1},{name="Stargazing",bondedName="The Stars Are Right",bondedCode=06028,bondCount=2},{name="Summoned Hound",bondedName="Unbound Beast",bondedCode=06283,bondCount=2}} privateDeck = true permanents = true local tileGUID = '928c8e' tile = getObjectFromGUID(tileGUID) makeText() makeButton() makeCheckboxPP() makeCheckboxPerms() -- Get current taboolist tabooList = {} WebRequest.get(tabooListURL, self, 'tabooListCallback') end function spawnZone() -- Clean up scripting zone if pcZone then pcZone.destruct() end if weaknessZone then weaknessZone.destruct() end deckPos = LocalPos(self,{-1.85,1.5,1.8}) permPos = LocalPos(self,{-4.63,1.5,1.8}) local pcZonePos = LocalPos(self,{4.75, 2.6 , 1.8}) zoneSpawn = {position = pcZonePos , scale = { 10.57, 5.1, 10.47 } , type = 'ScriptingTrigger' -- , callback = 'zoneCallback' , callback_owner = self , rotation = self.getRotation() } pcZone = spawnObject(zoneSpawn) -- get a scripting zone at the weakness deck --local weaknessZonePosition = self.positionToWorld({-7.5, 2.6 , 1.8}) local weaknessZonePosition = LocalPos(self, {-7.5, 0 , 1.8}) weaknessZone = spawnObject({ position = weaknessZonePosition, type = 'ScriptingTrigger', rotation = self.getRotation() }) for i=1,1 do coroutine.yield(0) end local objectsInZone = pcZone.getObjects() for i,v in pairs(objectsInZone) do if v.tag == 'Deck' then playerCardDeck = v end end local objectsInZone = weaknessZone.getObjects() for i,v in pairs(objectsInZone) do if v.getName() == 'All Weaknesses' then weaknessDeck = v end end -- Get deck from ArkhamDB.. local deckURL if privateDeck then deckURL = privateDeckURL else deckURL = publicDeckURL end WebRequest.get(deckURL .. deckID, self, 'deckReadCallback') return 1 end function init() cardList = {} doneSlots = 0 playerCardDeck = {} weaknessDeck = {} totalCards = 0 tabooID = 0 end function buttonClicked() -- Reset init() -- Spawn scripting zone startLuaCoroutine(self, "spawnZone") end function checkboxPPClicked() buttons = tile.getButtons() for k,v in pairs(buttons) do if (v.label == "Private deck") then local button_parameters = {} button_parameters.label = "Public deck" button_parameters.index = v.index tile.editButton(button_parameters) privateDeck = false else if (v.label == "Public deck") then local button_parameters = {} button_parameters.label = "Private deck" button_parameters.index = v.index tile.editButton(button_parameters) privateDeck = true end end end end function checkboxPermsClicked() buttons = tile.getButtons() for k,v in pairs(buttons) do if (v.label == "Permanents") then local button_parameters = {} button_parameters.label = "No permanents" button_parameters.index = v.index tile.editButton(button_parameters) permanents = false else if (v.label == "No permanents") then local button_parameters = {} button_parameters.label = "Permanents" button_parameters.index = v.index tile.editButton(button_parameters) permanents = true end end end end function deckReadCallback(req) -- Result check.. if req.is_done and not req.is_error then if string.find(req.text, "") then broadcastToAll("Private deck "..deckID.." is not shared", {0.5,0.5,0.5}) return end JsonDeckRes = JSON.decode(req.text) else print (req.error) return end if (JsonDeckRes == nil) then broadcastToAll("Deck not found!", {0.5,0.5,0.5}) return else print("Found decklist: "..JsonDeckRes.name) end -- Count number of cards in decklist numSlots=0 for cardid,number in pairs(JsonDeckRes.slots) do numSlots = numSlots + 1 end -- Check for taboos tabooID = JsonDeckRes.taboo_id if tabooID then print("Using List of Taboos from "..tabooList[tabooID].date..".") end -- Save card id, number in table and request card info from ArkhamDB for cardID,number in pairs(JsonDeckRes.slots) do local row = {} row.cardName = "" row.cardCount = number cardList[cardID] = row WebRequest.get(cardURL .. cardID, self, 'cardReadCallback') totalCards = totalCards + number end end function cardReadCallback(req) -- Result check.. if req.is_done and not req.is_error then -- Find unicode before using JSON.decode since it doesnt handle hex UTF-16 local tmpText = string.gsub(req.text,"\\u(%w%w%w%w)", convertHexToDec) JsonCardRes = JSON.decode(tmpText) else print(req.error) return end -- Update card name in table if(JsonCardRes.xp == nil or JsonCardRes.xp == 0) then cardList[JsonCardRes.code].cardName = JsonCardRes.real_name else cardList[JsonCardRes.code].cardName = JsonCardRes.real_name .. " (" .. JsonCardRes.xp .. ")" end -- Make Subname blank if it does not exist if (JsonCardRes.subname == nil and (JsonCardRes.subtype_name ~= "Basic Weakness" and JsonCardRes.subtype_name ~= "Weakness")) then cardList[JsonCardRes.code].subName = "" end -- Check for subname for k,v in pairs(subnameCards) do if (v.name == JsonCardRes.real_name and (v.xp == JsonCardRes.xp or JsonCardRes.xp == nil)) then cardList[JsonCardRes.code].subName = JsonCardRes.subname end end -- Check for multiclass for k,v in pairs(multiClassCards) do if (v.name == JsonCardRes.real_name and (v.xp == JsonCardRes.xp or JsonCardRes.xp == nil)) then cardList[JsonCardRes.code].subName = JsonCardRes.faction_name end end -- Check for returning cards (cards with the same name/level but different and from a new set) for k,v in pairs(returningCards) do if (v.name == JsonCardRes.real_name and JsonCardRes.pack_name == "Return to the Forgotten Age") then cardList[JsonCardRes.code].subName = "Permanent" end end -- Check for advanced signature cards (this happens after subname check, so it should be fine) for k,v in pairs(advancedSignatures) do if (v.name == JsonCardRes.real_name and JsonCardRes.pack_name == "Read or Die") then if (v.name == "Daisy's Tote Bag") then cardList[JsonCardRes.code].subName = "Advanced" else cardList[JsonCardRes.code].subName = "John Dee Translation (Advanced)" end end end -- Check if card is permanent (if separation is selected) if permanents then if (JsonCardRes.permanent == true or extraPermanents[JsonCardRes.real_name]) then cardList[JsonCardRes.code].permanent = true else cardList[JsonCardRes.code].permanent = false end else cardList[JsonCardRes.code].permanent = false end -- Check for '1:3' bonding cards, which always brings in 3 bonded cards, where the original card is limit 1 per deck. for k,v in pairs(bondedCardsOneThree) do if (v.name == JsonCardRes.real_name) then -- Check for Miss Doyle, who has a special exception since her 3 cards are different if (v.name == "Miss Doyle") then local row = {} local rowSecond = {} local rowThird = {} row.cardName = v.bondedName rowSecond.cardName = v.bondedNameSecond rowThird.cardName = v.bondedNameThird row.cardCount = 1 rowSecond.cardCount = 1 rowThird.cardCount = 1 row.permanent = true rowSecond.permanent = true rowThird.permanent = true cardList[v.bondedCode] = row cardList[v.bondedCodeSecond] = rowSecond cardList[v.bondedCodeThird] = rowThird else local row = {} row.cardName = v.bondedName row.cardCount = 3 row.permanent = true cardList[v.bondedCode] = row end end end -- Check for more complicated bonding cards for k,v in pairs(bondedCardsBoolean) do if (v.name == JsonCardRes.real_name and cardList[v.bondedCode] == nil) then local row = {} row.cardName = v.bondedName row.cardCount = v.bondCount row.permanent = true cardList[v.bondedCode] = row end end -- Check for Taboos (add " (Taboo)" to card name) if tabooID then for k,v in pairs(tabooList[tabooID].cards) do if v.code == JsonCardRes.code then cardList[JsonCardRes.code].cardName = cardList[JsonCardRes.code].cardName .. " (Taboo)" end end end -- Update number of processed slots, if complete, start building the deck doneSlots = doneSlots + 1 if (doneSlots == numSlots) then createDeck() end end function tabooListCallback(req) -- Result check.. if req.is_done and not req.is_error then -- Find unicode before using JSON.decode since it doesnt handle hex UTF-16 local tmpText = string.gsub(req.text,"\\u(%w%w%w%w)", convertHexToDec) JsonTabooRes = JSON.decode(tmpText) else print(req.error) return end for k,v in pairs(JsonTabooRes) do local row = {} row.date = v.date_start row.cards = JSON.decode(v.cards) tabooList[v.id] = row end end function createDeck() -- Create clone of playerCardDeck to use for drawing cards local cloneParams = {} cloneParams.position = {0,0,50} tmpDeck = playerCardDeck.clone(cloneParams) for k,v in pairs(cardList) do searchForCard(v.cardName, v.subName, v.cardCount, v.permanent) end tmpDeck.destruct() end function searchForCard(cardName, subName, cardCount, permanent) if cardName == 'Random Basic Weakness' and weaknessDeck then -- pull a weakness card instead weaknessDeck.shuffle() local taken = weaknessDeck.takeObject({ position = deckPos, index = 0, smooth = false, params = { cardName, cardCount, false } }) -- just special case the one permanent for now if taken.getName() == 'Indebted' then taken.setPosition(permPos) end broadcastToAll("Drew random basic weakness: " .. taken.getName()) return end allCards = tmpDeck.getObjects() for k,v in pairs(allCards) do if (v.nickname == cardName) then if(subName == nil or v.description == subName) then tmpDeck.takeObject({ position = {10, 0, 20}, callback = 'cardTaken', callback_owner=self, index = v.index, smooth = false, params = { cardName, cardCount, permanent, v.guid } }) print('Added '.. cardCount .. ' of ' .. cardName) return end end end broadcastToAll("Card not found: "..cardName, {0.5,0.5,0.5}) end function cardTaken(card, params) -- Check destination deck (permanent?) local destPos if (params[3] == true) then -- permanent card destPos = permPos else destPos = deckPos end if (card.getName() == params[1]) then for i=1,params[2]-1,1 do local cloneParams = {} cloneParams.position=destPos card.clone(cloneParams) end card.setPosition(destPos) else print('Wrong card: ' .. card.getName()) tmpDeck.putObject(card) end end function makeText() -- Create textbox local input_parameters = {} input_parameters.input_function = "inputTyped" input_parameters.function_owner = self input_parameters.position = {0.7,0.2,-0.3} input_parameters.width = 1620 input_parameters.scale = {0.1,0.1,0.1} input_parameters.height = 600 input_parameters.font_size = 500 input_parameters.tooltip = "*****PLEASE USE A PRIVATE DECK IF JUST FOR TTS TO AVOID FLOODING ARKHAMDB PUBLIC DECK LISTS!*****\nInput deck ID from ArkhamDB URL of the published version of the deck\nExample: For the URL 'https://arkhamdb.com/decklist/view/101/knowledge-overwhelming-solo-deck-1.0', you should input '101'" input_parameters.alignment = 3 -- (1 = Automatic, 2 = Left, 3 = Center, 4 = Right, 5 = Justified) –Optional input_parameters.value="" tile.createInput(input_parameters) end function makeButton() -- Create Button local button_parameters = {} button_parameters.click_function = "buttonClicked" button_parameters.function_owner = self button_parameters.position = {-0.26,0.1,0.37} button_parameters.width = 300 button_parameters.height = 4 button_parameters.tooltip = "Click to build your deck!" tile.createButton(button_parameters) end function makeCheckboxPP() local checkbox_parameters = {} checkbox_parameters.click_function = "checkboxPPClicked" checkbox_parameters.function_owner = self checkbox_parameters.position = {-0.7,0.2,-0.3} checkbox_parameters.width = 2200 checkbox_parameters.height = 500 checkbox_parameters.tooltip = "Click to toggle Private/Public deck ID" checkbox_parameters.label = "Private deck" checkbox_parameters.font_size = 350 checkbox_parameters.scale = {0.1,0.1,0.1} tile.createButton(checkbox_parameters) end function makeCheckboxPerms() local checkbox_parameters = {} checkbox_parameters.click_function = "checkboxPermsClicked" checkbox_parameters.function_owner = self checkbox_parameters.position = {-0.2,0.2,-0.3} checkbox_parameters.width = 2200 checkbox_parameters.height = 500 checkbox_parameters.tooltip = "Click to toggle separate permanents" checkbox_parameters.label = "Permanents" checkbox_parameters.font_size = 350 checkbox_parameters.scale = {0.1,0.1,0.1} tile.createButton(checkbox_parameters) end -- Function to convert utf-16 hex to actual character since JSON.decode doesn't seem to handle utf-16 hex very well.. function convertHexToDec(a) return string.char(tonumber(a,16)) end -------------- -------------- -- Start of Dzikakulka's positioning script -- Return position "position" in "object"'s frame of reference -- (most likely the only function you want to directly access) function LocalPos(object, position) local rot = object.getRotation() local lPos = {position[1], position[2], position[3]} -- Z-X-Y extrinsic local zRot = RotMatrix('z', rot['z']) lPos = RotateVector(zRot, lPos) local xRot = RotMatrix('x', rot['x']) lPos = RotateVector(xRot, lPos) local yRot = RotMatrix('y', rot['y']) lPos = RotateVector(yRot, lPos) return Vect_Sum(lPos, object.getPosition()) end -- Build rotation matrix -- 1st table = 1st row, 2nd table = 2nd row etc function RotMatrix(axis, angDeg) local ang = math.rad(angDeg) local cs = math.cos local sn = math.sin if axis == 'x' then return { { 1, 0, 0 }, { 0, cs(ang), -1*sn(ang) }, { 0, sn(ang), cs(ang) } } elseif axis == 'y' then return { { cs(ang), 0, sn(ang) }, { 0, 1, 0 }, { -1*sn(ang), 0, cs(ang) } } elseif axis == 'z' then return { { cs(ang), -1*sn(ang), 0 }, { sn(ang), cs(ang), 0 }, { 0, 0, 1 } } end end -- Apply given rotation matrix on given vector -- (multiply matrix and column vector) function RotateVector(rotMat, vect) local out = {0, 0, 0} for i=1,3,1 do for j=1,3,1 do out[i] = out[i] + rotMat[i][j]*vect[j] end end return out end -- Sum of two vectors (of any size) function Vect_Sum(vec1, vec2) local out = {} local k = 1 while vec1[k] ~= nil and vec2[k] ~= nil do out[k] = vec1[k] + vec2[k] k = k+1 end return out end -- End Dzikakulka's positioning script function inputTyped(objectInputTyped, playerColorTyped, input_value, selected) deckID = input_value end --[[ The onUpdate event is called once per frame. --]] function onUpdate () --[[ print('onUpdate loop!') --]] end