Advertisement
Guest User

Untitled

a guest
Jun 19th, 2019
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.83 KB | None | 0 0
  1. ----#include Decker
  2. do
  3. Decker = {}
  4.  
  5. -- provide unique ID starting from 20 for present decks
  6. local nextID
  7. do
  8. local _nextID = 20
  9. nextID = function()
  10. _nextID = _nextID + 1
  11. return tostring(_nextID)
  12. end
  13. end
  14.  
  15. -- Asset signature (equality comparison)
  16. local function assetSignature(assetData)
  17. return table.concat({
  18. assetData.FaceURL,
  19. assetData.BackURL,
  20. assetData.NumWidth,
  21. assetData.NumHeight,
  22. assetData.BackIsHidden and 'hb' or '',
  23. assetData.UniqueBack and 'ub' or ''
  24. })
  25. end
  26. -- Asset ID storage to avoid new ones for identical assets
  27. local idLookup = {}
  28. local function assetID(assetData)
  29. local sig = assetSignature(assetData)
  30. local key = idLookup[sig]
  31. if not key then
  32. key = nextID()
  33. idLookup[sig] = key
  34. end
  35. return key
  36. end
  37.  
  38. local assetMeta = {
  39. deck = function(self, cardNum, options)
  40. return Decker.AssetDeck(self, cardNum, options)
  41. end
  42. }
  43. assetMeta = {__index = assetMeta}
  44.  
  45. -- Create a new CustomDeck asset
  46. function Decker.Asset(face, back, options)
  47. local asset = {}
  48. options = options or {}
  49. asset.data = {
  50. FaceURL = face or error('Decker.Asset: faceImg link required'),
  51. BackURL = back or error('Decker.Asset: backImg link required'),
  52. NumWidth = options.width or 1,
  53. NumHeight = options.height or 1,
  54. BackIsHidden = true,
  55. UniqueBack = options.uniqueBack or false
  56. }
  57. -- Reuse ID if asset existing
  58. asset.id = assetID(asset.data)
  59. return setmetatable(asset, assetMeta)
  60. end
  61. -- Pull a Decker.Asset from card JSONs CustomDeck entry
  62. local function assetFromData(assetData)
  63. return setmetatable({data = assetData, id = assetID(assetData)}, assetMeta)
  64. end
  65.  
  66. -- Create a base for JSON objects
  67. function Decker.BaseObject()
  68. return {
  69. Name = 'Base',
  70. Transform = {
  71. posX = 0, posY = 5, posZ = 0,
  72. rotX = 0, rotY = 0, rotZ = 0,
  73. scaleX = 1, scaleY = 1, scaleZ = 1
  74. },
  75. Nickname = '',
  76. Description = '',
  77. ColorDiffuse = { r = 1, g = 1, b = 1 },
  78. Locked = false,
  79. Grid = true,
  80. Snap = true,
  81. Autoraise = true,
  82. Sticky = true,
  83. Tooltip = true,
  84. GridProjection = false,
  85. Hands = false,
  86. XmlUI = '',
  87. LuaScript = '',
  88. LuaScriptState = '',
  89. GUID = 'deadbf'
  90. }
  91. end
  92. -- Typical paramters map with defaults
  93. local commonMap = {
  94. name = {field = 'Nickname', default = ''},
  95. desc = {field = 'Description', default = ''},
  96. script = {field = 'LuaScript', default = ''},
  97. xmlui = {field = 'XmlUI', default = ''},
  98. scriptState = {field = 'LuaScriptState', default = ''},
  99. locked = {field = 'Locked', default = false},
  100. tooltip = {field = 'Tooltip', default = true},
  101. guid = {field = 'GUID', default = 'deadbf'},
  102. }
  103. -- Apply some basic parameters on base JSON object
  104. function Decker.SetCommonOptions(obj, options)
  105. options = options or {}
  106. for k,v in pairs(commonMap) do
  107. -- can't use and/or logic cause of boolean fields
  108. if options[k] ~= nil then
  109. obj[v.field] = options[k]
  110. else
  111. obj[v.field] = v.default
  112. end
  113. end
  114. -- passthrough unrecognized keys
  115. for k,v in pairs(options) do
  116. if not commonMap[k] then
  117. obj[k] = v
  118. end
  119. end
  120. end
  121. -- default spawnObjectJSON params since it doesn't like blank fields
  122. local function defaultParams(params, json)
  123. params = params or {}
  124. params.json = json
  125. params.position = params.position or {0, 5, 0}
  126. params.rotation = params.rotation or {0, 0, 0}
  127. params.scale = params.scale or {1, 1, 1}
  128. if params.sound == nil then
  129. params.sound = true
  130. end
  131. return params
  132. end
  133.  
  134. -- For copy method
  135. local deepcopy
  136. deepcopy = function(t)
  137. local copy = {}
  138. for k,v in pairs(t) do
  139. if type(v) == 'table' then
  140. copy[k] = deepcopy(v)
  141. else
  142. copy[k] = v
  143. end
  144. end
  145. return copy
  146. end
  147. -- meta for all Decker derived objects
  148. local commonMeta = {
  149. -- return object JSON string, used cached if present
  150. _cache = function(self)
  151. if not self.json then
  152. self.json = JSON.encode(self.data)
  153. end
  154. return self.json
  155. end,
  156. -- invalidate JSON string cache
  157. _recache = function(self)
  158. self.json = nil
  159. return self
  160. end,
  161. spawn = function(self, params)
  162. params = defaultParams(params, self:_cache())
  163. return spawnObjectJSON(params)
  164. end,
  165. copy = function(self)
  166. return setmetatable(deepcopy(self), getmetatable(self))
  167. end,
  168. setCommon = function(self, options)
  169. Decker.SetCommonOptions(self.data, options)
  170. return self
  171. end,
  172. }
  173. -- apply common part on a specific metatable
  174. local function customMeta(mt)
  175. for k,v in pairs(commonMeta) do
  176. mt[k] = v
  177. end
  178. mt.__index = mt
  179. return mt
  180. end
  181.  
  182. -- DeckerCard metatable
  183. local cardMeta = {
  184. setAsset = function(self, asset)
  185. local cardIndex = self.data.CardID:sub(-2, -1)
  186. self.data.CardID = asset.id .. cardIndex
  187. self.data.CustomDeck = {[asset.id] = asset.data}
  188. return self:_recache()
  189. end,
  190. getAsset = function(self)
  191. local deckID = next(self.data.CustomDeck)
  192. return assetFromData(self.data.CustomDeck[deckID])
  193. end,
  194. -- reset deck ID to a consistent value script-wise
  195. _recheckDeckID = function(self)
  196. local oldID = next(self.data.CustomDeck)
  197. local correctID = assetID(self.data.CustomDeck[oldID])
  198. if oldID ~= correctID then
  199. local cardIndex = self.data.CardID:sub(-2, -1)
  200. self.data.CardID = correctID .. cardIndex
  201. self.data.CustomDeck[correctID] = self.data.CustomDeck[oldID]
  202. self.data.CustomDeck[oldID] = nil
  203. end
  204. return self
  205. end
  206. }
  207. cardMeta = customMeta(cardMeta)
  208. -- Create a DeckerCard from an asset
  209. function Decker.Card(asset, row, col, options)
  210. row, col = row or 1, col or 1
  211. options = options or {}
  212. local card = Decker.BaseObject()
  213. card.Name = 'Card'
  214. -- optional custom fields
  215. Decker.SetCommonOptions(card, options)
  216. if options.sideways ~= nil then
  217. card.SidewaysCard = options.sideways
  218. -- FIXME passthrough set that field, find some more elegant solution
  219. card.sideways = nil
  220. end
  221. -- CardID string is parent deck ID concat with its 0-based index (always two digits)
  222. local num = (row-1)*asset.data.NumWidth + col - 1
  223. num = string.format('%02d', num)
  224. card.CardID = asset.id .. num
  225. -- just the parent asset reference needed
  226. card.CustomDeck = {[asset.id] = asset.data}
  227.  
  228. local obj = setmetatable({data = card}, cardMeta)
  229. obj:_cache()
  230. return obj
  231. end
  232.  
  233.  
  234. -- DeckerDeck meta
  235. local deckMeta = {
  236. count = function(self)
  237. return #self.data.DeckIDs
  238. end,
  239. -- Transform index into positive
  240. index = function(self, ind)
  241. if ind < 0 then
  242. return self:count() + ind + 1
  243. else
  244. return ind
  245. end
  246. end,
  247. swap = function(self, i1, i2)
  248. local ri1, ri2 = self:index(i1), self:index(i2)
  249. assert(ri1 > 0 and ri1 <= self:count(), 'DeckObj.rearrange: index ' .. i1 .. ' out of bounds')
  250. assert(ri2 > 0 and ri2 <= self:count(), 'DeckObj.rearrange: index ' .. i2 .. ' out of bounds')
  251. self.data.DeckIDs[ri1], self.data.DeckIDs[ri2] = self.data.DeckIDs[ri2], self.data.DeckIDs[ri1]
  252. local co = self.data.ContainedObjects
  253. co[ri1], co[ri2] = co[ri2], co[ri1]
  254. return self:_recache()
  255. end,
  256. -- rebuild self.data.CustomDeck based on contained cards
  257. _rescanDeckIDs = function(self, id)
  258. local cardIDs = {}
  259. for k,card in ipairs(self.data.ContainedObjects) do
  260. local cardID = next(card.CustomDeck)
  261. if not cardIDs[cardID] then
  262. cardIDs[cardID] = card.CustomDeck[cardID]
  263. end
  264. end
  265. -- eeh, GC gotta earn its keep as well
  266. -- FIXME if someone does shitton of removals, may cause performance issues?
  267. self.data.CustomDeck = cardIDs
  268. end,
  269. remove = function(self, ind, skipRescan)
  270. local rind = self:index(ind)
  271. assert(rind > 0 and rind <= self:count(), 'DeckObj.remove: index ' .. ind .. ' out of bounds')
  272. local card = self.data.ContainedObjects[rind]
  273. table.remove(self.data.DeckIDs, rind)
  274. table.remove(self.data.ContainedObjects, rind)
  275. if not skipRescan then
  276. self:_rescanDeckIDs(next(card.CustomDeck))
  277. end
  278. return self:_recache()
  279. end,
  280. removeMany = function(self, ...)
  281. local indices = {...}
  282. table.sort(indices, function(e1,e2) return self:index(e1) > self:index(e2) end)
  283. for _,ind in ipairs(indices) do
  284. self:remove(ind, true)
  285. end
  286. self:_rescanDeckIDs()
  287. return self:_recache()
  288. end,
  289. insert = function(self, card, ind)
  290. ind = ind or (self:count() + 1)
  291. local rind = self:index(ind)
  292. assert(rind > 0 and rind <= (self:count()+1), 'DeckObj.insert: index ' .. ind .. ' out of bounds')
  293. table.insert(self.data.DeckIDs, rind, card.data.CardID)
  294. table.insert(self.data.ContainedObjects, rind, card.data)
  295. local id = next(card.data.CustomDeck)
  296. if not self.data.CustomDeck[id] then
  297. self.data.CustomDeck[id] = card.data.CustomDeck[id]
  298. end
  299. return self:_recache()
  300. end,
  301. reverse = function(self)
  302. local s,e = 1, self:count()
  303. while s < e do
  304. self:swap(s, e)
  305. s = s+1
  306. e = e-1
  307. end
  308. return self:_recache()
  309. end,
  310. cardAt = function(self, ind)
  311. local rind = self:index(ind)
  312. assert(rind > 0 and rind <= (self:count()+1), 'DeckObj.insert: index ' .. ind .. ' out of bounds')
  313. local card = setmetatable({data = deepcopy(self.data.ContainedObjects[rind])}, cardMeta)
  314. card:_cache()
  315. return card
  316. end,
  317. switchAssets = function(self, replaceTable)
  318. -- destructure replace table into
  319. -- [ID_to_replace] -> [ID_to_replace_with]
  320. -- [new_asset_ID] -> [new_asset_data]
  321. local idReplace = {}
  322. local assets = {}
  323. for oldAsset, newAsset in pairs(replaceTable) do
  324. assets[newAsset.id] = newAsset.data
  325. idReplace[oldAsset.id] = newAsset.id
  326. end
  327. -- update deckIDs
  328. for k,cardID in ipairs(self.data.DeckIDs) do
  329. local deckID, cardInd = cardID:sub(1, -3), cardID:sub(-2, -1)
  330. if idReplace[deckID] then
  331. self.data.DeckIDs[k] = idReplace[deckID] .. cardInd
  332. end
  333. end
  334. -- update CustomDeck data - nil replaced
  335. for replacedID in pairs(idReplace) do
  336. if self.data.CustomDeck[replacedID] then
  337. self.data.CustomDeck[replacedID] = nil
  338. end
  339. end
  340. -- update CustomDeck data - add replacing
  341. for _,replacingID in pairs(idReplace) do
  342. self.data.CustomDeck[replacingID] = assets[replacingID]
  343. end
  344. -- update card data
  345. for k,cardData in ipairs(self.data.ContainedObjects) do
  346. local deckID = next(cardData.CustomDeck)
  347. if idReplace[deckID] then
  348. cardData.CustomDeck[deckID] = nil
  349. cardData.CustomDeck[idReplace[deckID]] = assets[idReplace[deckID]]
  350. end
  351. end
  352. return self:_recache()
  353. end,
  354. getAssets = function(self)
  355. local assets = {}
  356. for id,assetData in pairs(self.data.CustomDeck) do
  357. assets[#assets+1] = assetFromData(assetData)
  358. end
  359. return assets
  360. end
  361. }
  362. deckMeta = customMeta(deckMeta)
  363. -- Create DeckerDeck object from DeckerCards
  364. function Decker.Deck(cards, options)
  365. local deck = Decker.BaseObject()
  366. deck.Name = 'Deck'
  367. Decker.SetCommonOptions(deck, options)
  368. deck.DeckIDs = {}
  369. deck.CustomDeck = {}
  370. deck.ContainedObjects = {}
  371. for _,card in ipairs(cards) do
  372. deck.DeckIDs[#deck.DeckIDs+1] = card.data.CardID
  373. local id = next(card.data.CustomDeck)
  374. if not deck.CustomDeck[id] then
  375. deck.CustomDeck[id] = card.data.CustomDeck[id]
  376. end
  377. deck.ContainedObjects[#deck.ContainedObjects+1] = card.data
  378. end
  379.  
  380. local obj = setmetatable({data = deck}, deckMeta)
  381. obj:_cache()
  382. return obj
  383. end
  384. -- Create DeckerDeck from an asset using X cards on its sheet
  385. function Decker.AssetDeck(asset, cardNum, options)
  386. cardNum = cardNum or asset.data.NumWidth * asset.data.NumHeight
  387. local row, col, width = 1, 1, asset.data.NumWidth
  388. local cards = {}
  389. for k=1,cardNum do
  390. cards[#cards+1] = Decker.Card(asset, row, col)
  391. col = col+1
  392. if col > width then
  393. row, col = row+1, 1
  394. end
  395. end
  396. return Decker.Deck(cards, options)
  397. end
  398. end
  399.  
  400. ----#include Decker
  401.  
  402. function onLoad()
  403. self.UI.setAttribute('language', 'text', 'en')
  404. self.UI.setAttribute('card_back_color', 'text', 'default')
  405. end
  406.  
  407. function selectRandomDeck()
  408. GetRandomDeck(function(error, deckid)
  409. if error then return print('Request for Random Deck failed:\n'..error) end
  410. FetchAndBuild(deckid)
  411. end)
  412. end
  413.  
  414. function FetchDeck()
  415. local url = self.UI.getAttribute('deckURL', 'text')
  416. if not url or string.len(url) == 0 then return broadcastToAll("Deck URL is empty. Please enter URL of your deck.", {1, 1, 1}) end
  417. local deckid = url:match("%w+-%w+-%w+-%w+-%w+")
  418. FetchAndBuild(deckid)
  419. end
  420.  
  421. function FetchAndBuild(deckid)
  422. GetDeck(deckid,
  423. function(deck)
  424. broadcastToAll("Your deck is "..deck.name..".\nDeck houses are: "..deck._links.houses[1]..", "..deck._links.houses[2]..", "..deck._links.houses[3]..".", {1, 1, 1})
  425. local string = 'Deck Name:\n'..deck.name..'\n\nDeck ID: '..deck.id..'\n\nCard List:'
  426. for _, card in ipairs(deck.card_list) do string = string..'\n'..card.card_title..": "..card.card_number end
  427. self.UI.setAttribute('deckInput', 'text', string)
  428. BuildQueued = true
  429. if BuildQueued then Wait.frames(function() NewBuild(deck) end, 1) end
  430. end)
  431. end
  432.  
  433. function NewBuild(deck)
  434. local Deck = Decker.Deck({})
  435. local color = self.UI.getAttribute('card_back_color', 'text')
  436. local language = self.UI.getAttribute('language', 'text')
  437. for _,card in ipairs(deck.card_list) do
  438. local cardFace = ''
  439. local cardBack = ''
  440. if card.is_legacy == true and card.is_maverick == true then
  441. cardFace = 'https://images.skyjedi.com/custom/'..language..'/'..card.expansion..'/'..card.card_number..'/'..card.house..'/legmav'
  442. elseif card.is_legacy == true then
  443. cardFace = 'https://images.skyjedi.com/custom/'..language..'/'..card.expansion..'/'..card.card_number..'/'..card.house..'/legacy'
  444. elseif card.is_maverick == true then
  445. cardFace = 'https://images.skyjedi.com/custom/'..language..'/'..card.expansion..'/'..card.card_number..'/'..card.house..'/maverick'
  446. else
  447. cardFace = 'https://images.skyjedi.com/keyforge/'..language..'/'..card.expansion..'/'..card.card_number..'.png'
  448. end
  449. if color == 'default' then
  450. cardBack = 'https://images.skyjedi.com/custom/'..deck.expansion..'/'..deck._links.houses[1]..'/'..deck._links.houses[2]..'/'..deck._links.houses[3]..'/cardback'
  451. else
  452. cardBack = 'https://images.skyjedi.com/custom/'..color..'/'..deck._links.houses[1]..'/'..deck._links.houses[2]..'/'..deck._links.houses[3]..'/cardback'
  453. end
  454. local cardAsset = Decker.Asset(cardFace, cardBack)
  455. local final = Decker.Card(cardAsset, 1,1,{name = card.card_title, desc = deck.name})
  456. Deck:insert(final)
  457. end
  458. local someDeck = Deck:spawn({position = { - 45.25, 3, 5.90 }, rotation = {0, 90, 180}, scale = {1.5, 1.5, 1.5}})
  459. someDeck.setName(deck.name)
  460. someDeck.shuffle()
  461. end
  462.  
  463. function InputFieldtextPolyfill(_, value, id) self.UI.setAttribute(id, 'text', value) end
  464.  
  465. function GetDeck(deck_id, callback)
  466. keyForgeAPI("/decks/"..deck_id.."/?links=cards",
  467. function(error, data)
  468. if error then return print('Request for '..deck_id..' failed:\n'..error) end
  469. local cards = {}
  470. for k, v in ipairs(data._linked.cards) do cards[v.id] = v end
  471. local card_list = {}
  472. for _, v in ipairs(data.data._links.cards) do
  473. cards[v].is_legacy = false
  474. for x, id in ipairs(data.data.set_era_cards.Legacy) do
  475. if cards[v].id == id then
  476. cards[v].is_legacy = true
  477. broadcastToAll(cards[v].card_title .. ' is Legacy!', {1, 0, 0})
  478. end
  479. end
  480. if cards[v].is_maverick == true then
  481. broadcastToAll(cards[v].card_title .. ' is Maverick of '.. cards[v].house..'!', {1, 0, 0})
  482. end
  483. card_list[#card_list + 1] = cards[v]
  484. end
  485. data.data.card_list = card_list
  486. callback(data.data)
  487. end)
  488. end
  489.  
  490. function GetRandomDeck(callback)
  491. WebRequest.get('https://decksofkeyforge.com/api/decks/random',
  492. function(webReturn)
  493. if webReturn.is_error then return callback(true, webReturn.error) end
  494. if string.len(webReturn.text) == 0 then return callback(true, "Empty response") end
  495. local success, data = pcall(function() return webReturn.text end)
  496. if not success then return callback(true, data) end
  497. if data.message then return callback(true, data) end
  498. callback(false, data)
  499. end)
  500. end
  501.  
  502. function keyForgeAPI(route, callback)
  503. WebRequest.get('https://www.keyforgegame.com/api'..route,
  504. function(webReturn)
  505. if webReturn.is_error then return callback(true, webReturn.error) end
  506. if string.len(webReturn.text) == 0 then return callback(true, "Empty response") end
  507. local success, data = pcall(function() return JSON.decode(webReturn.text) end)
  508. if not success then return callback(true, data) end
  509. if data.message then return callback(true, data) end
  510. callback(false, data)
  511. end)
  512. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement