· 8 years ago · Feb 17, 2018, 07:10 AM
1function getModVersion() return 1.43 end --version of this mod (for compatibility checks with external tools)
2
3math.randomseed( os.time() )
4
5m_iEventCardResult = 0 --"dice" result for event cards
6
7m_Die1 = nil
8function getDieOne() return m_Die1 end
9m_Die2 = nil
10function getDieTwo() return m_Die2 end
11m_EventDie = nil
12function getEventDie() return m_EventDie end
13--bag containing items and code for explorers & pirates expansion
14m_EaPBag = nil
15function setEaPBag(p)
16 if m_EaPBag ~= nil then --remove old EaP Bag so they don't start to accumulate if multiple game boxes are spawned
17 m_EaPBag.call('silentDestroy')
18 end
19 m_EaPBag = p.BagObj
20end
21function getEaPBag() return m_EaPBag end
22--bag containing items for the Traders & Barbarians campaigns
23m_TaBBag = nil
24function setTaBBag(p)
25 if m_TaBBag ~= nil then --remove old TaB Bag so they don't start to accumulate if multiple game boxes are spawned
26 m_TaBBag.destruct()
27 end
28 m_TaBBag = p.BagObj
29end
30function getTaBBag() return m_TaBBag end
31
32m_MapScript = nil
33
34m_Robber = nil
35
36-- Item spawn bags
37m_ResourceDecks = {Bag = 'dbd7df', Deck = {['Lumber'] = 'b40904', ['Bricks'] = 'f24c89', ['Wool'] = '89b19c', ['Grain'] = 'a69e8e', ['Ore'] = '1ec6da'}}
38m_ResourceDecks_ext = {Bag = 'b2dbfd', Deck = {['Lumber'] = 'a875da', ['Bricks'] = '761ae4', ['Wool'] = '0212ac', ['Grain'] = '2fd393', ['Ore'] = 'e29e0d'}}
39m_DevCardDecks = {Bag = '789036', Deck = {['2P'] = '807c38', ['34P'] = '138ade', ['56P'] = '8581dd', ['34P_S7'] = '5f0252', ['56P_S7'] = '4f7c58'}}
40m_HelpersItems = {Bag = 'ba7910', Item = {['Deck'] = 'b08360', ['Board'] = 'a1982d'}}
41m_FishermenItems = {Bag = '240e82', Item = {['Board'] = '8ba2d4', ['Deck'] = '8a62f6', ['Deck_ext'] = 'e9e546'}}
42m_DeckEventCards = 'de471f'
43m_OilspDisasterBoard = 'feee7b'
44--C&K
45m_CKitems = {Bag = '818554', Item = {['DeckPolitics'] = '688db9', ['DeckScience'] = '7feefa', ['DeckTrade'] = '1c5356', ['DeckDefender'] = '361e4e', ['DeckDefender_ext'] = '5e14df',
46 ['ChartPolitics'] = '85dcd8', ['ChartScience'] = 'cd7181', ['ChartTrade'] = 'e51b9c', }
47 }
48m_CommodityDecks = {Bag = 'fe36b2', Deck = {['Paper'] = 'e47fa7', ['Cloth'] = '428625', ['Coins'] = '37560d'}}
49m_CommodityDecks_ext = {Bag = 'f2c12f', Deck = {['Paper'] = '8817c0', ['Cloth'] = '7b2437', ['Coins'] = '851e28'}}
50m_BarbarianTile = 'bb752d'
51
52--hex tiles spawn info
53m_HexBag = 'd969d2'
54m_HexObjects = {
55 ['ocean'] = { iNext = 1, Tile = {'5569d9'} },
56 ['desert'] = { iNext = 1, Tile = {'796f8c'} },
57 ['gold'] = { iNext = 1, Tile = {'8fb9e0'} },
58 ['s6_desert'] = { iNext = 1, Tile = {'a4bcb9'} }, --special snap points for Scenario 6 tribe islands
59 ['s6_gold'] = { iNext = 1, Tile = {'211572'} }, --special snap points for Scenario 6 tribe islands
60 ['ocean_skull'] = { iNext = 1, Tile = {'40a31f'} },
61 ['forest'] = { iNext = 1, Tile = {'be454e', 'cfe8a1', 'eeefbd', '4155e2'} },
62 ['hills'] = { iNext = 1, Tile = {'beae30', '140046', '0fd3e3', '75dc80'} },
63 ['pasture'] = { iNext = 1, Tile = {'9a0be3', 'dce3b4', '5cdc3a', 'fc5a9b'} },
64 ['fields'] = { iNext = 1, Tile = {'4522c2', '829f12', '437304', '1e6e2d'} },
65 ['mountain'] = { iNext = 1, Tile = {'818cb1', 'a39c2f', 'c6098d'} },
66 ['oilsource'] = {iNext = 1, Tile = {'09bfd7'} },
67}
68function getHexObjectList() return m_HexObjects end
69function setHexObjectList(List) m_HexObjects = List end
70m_iSpawnedHexes = 0
71m_iLoadingHexes = 999 --TODO: multiple hex tiles have the same GUID in the frame they are spawned in and even when their onLoad event is triggered ... so just count them to figure out when they are all loaded
72
73m_UnlockOnLoad = {}
74function addToUnlockList(p) table.insert(m_UnlockOnLoad, p.Obj) end
75
76m_PlayerColors = { --(it is actually the colors of the players items...)
77 ['Orange'] = { r = 1.0, g = 0.544700563, b = 0.188231677 },
78 ['Red'] = { r = 0.7921569, g = 0.0, b = 0.0 },
79 ['White'] = { r = 0.99649936, g = 1.0, b = 0.996506333 },
80 ['Purple'] = { r = 0.5528258, g = 0.164702222, b = 1.0 },
81 ['Blue'] = { r = 0.149015814, g = 0.419941843, b = 0.8235495 },
82 ['Green'] = { r = 0.201173037, g = 0.8549019, b = 0.188231409 },
83}
84function getPlayerColor(p) return m_PlayerColors[p.sPlayer] end
85
86-- Control panel related globals
87m_bRandomizeMap = false
88function getStateRandomize() return m_bRandomizeMap end
89function setStateRandomize(p) m_bRandomizeMap = p.bRandom end
90m_bHiddenNumbers = false
91function getStateHiddenNumbers() return m_bHiddenNumbers end
92function setStateHiddenNumbers(p) m_bHiddenNumbers = p.bHide end
93m_bHiddenTiles = false
94function getStateHiddenTiles() return m_bHiddenTiles end
95function setStateHiddenTiles(p) m_bHiddenTiles = p.bHide end
96m_bExpCK = false
97function getBtnStateCK() return m_bExpCK end
98function setBtnStateCK(p) m_bExpCK = p.bEnable end
99m_bMapLoaded = false
100function isMapLoaded() return m_bMapLoaded end
101m_bAutoSort = true
102function getAutoSort() return m_bAutoSort end
103function setAutoSort(p) m_bAutoSort = p.bEnable end
104m_bDeckTooltip = false
105function getDeckTooltip() return m_bDeckTooltip end
106function setDeckTooltip(p)
107 m_bDeckTooltip = p.bEnable
108 --find all decks with a resource card and enable / disable the tooltip
109 local AllObj = getAllObjects()
110 for i, Obj in pairs(AllObj) do
111 if Obj.tag == 'Deck' then
112 local sCardName = Obj.getObjects()[1].nickname
113 if sCardName == 'Lumber' or sCardName == 'Bricks' or sCardName == 'Wool' or sCardName == 'Grain' or sCardName == 'Ore' or
114 sCardName == 'Paper' or sCardName == 'Cloth' or sCardName == 'Coins' then
115 Obj.tooltip = m_bDeckTooltip
116 end
117 end
118 end
119end
120m_bDisplayCardCount = true
121function getDisplayCardCount() return m_bDisplayCardCount end
122function setDisplayCardCount(p) m_bDisplayCardCount = p.bEnable end
123m_bDiceDealButton = true --show deal button next to dice after rolling
124m_iDealCards = 0 --selected option on CP page 6
125
126function getDealCardsSetting() return m_iDealCards end
127function setDealCardsSetting(p)
128 m_iDealCards = p.iSetting
129 if m_iDealCards == 0 then --Both fixed deal button + deal button next to rolled dice
130 m_MapScript.call('showDealButton') --make sure the fixed deal button is enabled
131 m_bDiceDealButton = true
132 elseif m_iDealCards == 1 then --Fixed deal button only
133 m_MapScript.call('showDealButton')
134 m_MapScript.call('hideDealButtonDie') --remove any active buttons
135 m_bDiceDealButton = false
136 elseif m_iDealCards == 2 then --dice button only
137 m_MapScript.call('hideDealButton')
138 m_bDiceDealButton = true
139 elseif m_iDealCards == 3 then --none
140 m_MapScript.call('hideDealButton')
141 m_MapScript.call('hideDealButtonDie') --remove any active buttons
142 m_bDiceDealButton = false
143 end
144end
145
146--Settings for scenario 9 (randomized map)
147m_iS9NumGold = -1 --default
148function getStateNumGold() return m_iS9NumGold end
149function setStateNumGold(p) m_iS9NumGold = p.iNew end
150m_iS9PercentHidden = 0
151function getStatePercentHidden() return m_iS9PercentHidden end
152function setStatePercentHidden(p) m_iS9PercentHidden = p.iNew end
153
154--Mini Expansions
155m_iFrenemiesPlayers = 0
156function getStateFrenemies() return m_iFrenemiesPlayers end
157function setStateFrenemies(p) m_iFrenemiesPlayers = p.iNew end
158m_bEnableHelpers = false
159function getStateHelpers() return m_bEnableHelpers end
160function setStateHelpers(p) m_bEnableHelpers = p.bEnable end
161m_bEnableFishermen = false
162function getStateFishermen() return m_bEnableFishermen end
163function setStateFishermen(p) m_bEnableFishermen = p.bEnable end
164m_FishermenCardBoard = nil
165function getFishermenCardBoard() return m_FishermenCardBoard end
166m_bEnableHarborMaster = false
167function getStateHarborMaster() return m_bEnableHarborMaster end
168function setStateHarborMaster(p) m_bEnableHarborMaster = p.bEnable end
169m_bEnableWelfare = false
170function getStateWelfare() return m_bEnableWelfare end
171function setStateWelfare(p) m_bEnableWelfare = p.bEnable end
172m_bEnableEventCards = false
173function getStateEventCards() return m_bEnableEventCards end
174function setStateEventCards(p) m_bEnableEventCards = p.bEnable end
175m_bEpidemic = false
176function isEpidemic() return m_bEpidemic end
177m_bEnableRivers = false
178function getStateRivers() return m_bEnableRivers end
179function setStateRivers(p) m_bEnableRivers = p.bEnable end
180m_bEnableCaravans = false
181function getStateCaravans() return m_bEnableCaravans end
182function setStateCaravans(p) m_bEnableCaravans = p.bEnable end
183m_bEnableBarbarianAttack = false
184m_bColorDieSpawned = false --switch to make the color die spawn only on one of the castle fields when two are in the game (5-6 players)
185function getStateBarbarianAttack() return m_bEnableBarbarianAttack end
186function setStateBarbarianAttack(p) m_bEnableBarbarianAttack = p.bEnable end
187m_bEnableTraders = false
188function getStateTraders() return m_bEnableTraders end
189function setStateTraders(p) m_bEnableTraders = p.bEnable end
190m_bEnableOilsprings = false
191function getStateOilsprings() return m_bEnableOilsprings end
192function setStateOilsprings(p) m_bEnableOilsprings = p.bEnable end
193
194--Restriction for the deal function to work only once per roll
195m_bAllowDeal = false
196function isDealAllowed() return m_bAllowDeal end
197function setAllowDeal(p) m_bAllowDeal = p.bEnable end
198
199--Dice monitoring to control token highlighting
200m_CheckDice = {}
201function queueDieCheck(p)
202 if m_bMapLoaded then
203 local bAdd = true
204 for i, Die in pairs(m_CheckDice) do --make sure each die is in the list only once
205 if Die == p.DieObj then
206 bAdd = false
207 break
208 elseif Die == nil then --if we detect a nil object remove it to make sure we do not disable the update function if #m_CheckDie returns 0
209 table.remove(m_CheckDice, i)
210 end
211 end
212 if bAdd then
213 table.insert(m_CheckDice, p.DieObj)
214 end
215 return true
216 else --map is still loading, die not in monitoring queue
217 return false
218 end
219end
220
221--Tool C/K counter: Objects to be informed when something is done to knight tokens (City/Knights counter)
222m_KnightEventReceiver = {}
223function registerKnightEventReceiver(p) table.insert(m_KnightEventReceiver, p.Obj) end
224function reportKnightCollision()
225 for i, Receiver in pairs(m_KnightEventReceiver) do
226 if Receiver ~= nil then
227 Receiver.call('onKnightCollision')
228 end
229 end
230end
231function resetCKcounter()
232 for i, Receiver in pairs(m_KnightEventReceiver) do
233 if Receiver ~= nil then
234 Receiver.call('resetCounter')
235 end
236 end
237end
238
239--Tool Dice Logger: Object to be informed when dice have been rolled / cards dealt (dice logger)
240m_DiceLogger = nil
241function registerDiceLogger(p)
242 if m_DiceLogger ~= nil then
243 m_DiceLogger.call('setStatus', {bActive = false}) --tell the old logger that it will no longer be updated
244 end
245 m_DiceLogger = p.Obj
246end
247function onCardsDealt(p) --called from map script when the deal cards function is used
248 if m_DiceLogger ~= nil then
249 m_DiceLogger.call('onCardsDealt', {iResult = p.iResult})
250 end
251
252 --interface for dice event receivers (automatic barbarian ship / E&P)
253 for i, Receiver in ipairs(m_DiceEventReceiver) do
254 if Receiver ~= nil then
255 Receiver.call('onCardsDealt', {iResult = p.iResult, Yields = p.Yields})
256 end
257 end
258end
259
260--Tool Dice event receiver: Objects to be informed when dice have been rolled (auto moving barbarian ship)
261m_DiceEventReceiver = {}
262function registerDiceEventReceiver(p) table.insert(m_DiceEventReceiver, p.Obj) end
263function removeDiceEventReceiver(p)
264 for i, Receiver in pairs(m_DiceEventReceiver) do
265 if Receiver == p.Obj then
266 table.remove(m_DiceEventReceiver, i)
267 break
268 end
269 end
270end
271
272m_CurScenario = {}
273
274--[[ The OnLoad function. This is called after everything in the game save finishes loading.
275Most of your script code goes here. --]]
276function onLoad(save_state)
277-- get the scripting zone object that handles map related stuff
278 m_MapScript = getObjectFromGUID('42fd8e')
279
280-- load saved data
281 local saved_data = JSON.decode(save_state)
282 if saved_data ~= nil then
283 if saved_data.m_CurScenario ~= nil then m_CurScenario = saved_data.m_CurScenario end
284 if saved_data.m_bRandomizeMap ~= nil then m_bRandomizeMap = saved_data.m_bRandomizeMap end
285 if saved_data.m_bHiddenNumbers ~= nil then m_bHiddenNumbers = saved_data.m_bHiddenNumbers end
286 if saved_data.m_bHiddenTiles ~= nil then m_bHiddenTiles = saved_data.m_bHiddenTiles end
287 if saved_data.m_bExpCK ~= nil then m_bExpCK = saved_data.m_bExpCK end
288 if saved_data.m_bMapLoaded ~= nil then m_bMapLoaded = saved_data.m_bMapLoaded end
289 if saved_data.m_bAutoSort ~= nil then m_bAutoSort = saved_data.m_bAutoSort end
290 if saved_data.m_iDealCards ~= nil then
291 m_iDealCards = saved_data.m_iDealCards
292 setDealCardsSetting({iSetting = m_iDealCards})
293 end
294 if saved_data.m_bDeckTooltip ~= nil then m_bDeckTooltip = saved_data.m_bDeckTooltip end
295 if saved_data.m_bDisplayCardCount ~= nil then m_bDisplayCardCount = saved_data.m_bDisplayCardCount end
296 if saved_data.m_iS9NumGold ~= nil then m_iS9NumGold = saved_data.m_iS9NumGold end
297 if saved_data.m_iS9PercentHidden ~= nil then m_iS9PercentHidden = saved_data.m_iS9PercentHidden end
298 if saved_data.m_iFrenemiesPlayers ~= nil then m_iFrenemiesPlayers = saved_data.m_iFrenemiesPlayers end
299 if saved_data.m_bEnableHelpers ~= nil then m_bEnableHelpers = saved_data.m_bEnableHelpers end
300 if saved_data.m_bEnableFishermen ~= nil then m_bEnableFishermen = saved_data.m_bEnableFishermen end
301 if saved_data.m_bEnableHarborMaster ~= nil then m_bEnableHarborMaster = saved_data.m_bEnableHarborMaster end
302 if saved_data.m_bEnableWelfare ~= nil then m_bEnableWelfare = saved_data.m_bEnableWelfare end
303 if saved_data.m_bEnableEventCards ~= nil then m_bEnableEventCards = saved_data.m_bEnableEventCards end
304 if saved_data.m_bEnableRivers ~= nil then m_bEnableRivers = saved_data.m_bEnableRivers end
305 if saved_data.m_bEnableCaravans ~= nil then m_bEnableCaravans = saved_data.m_bEnableCaravans end
306 if saved_data.m_bEnableBarbarianAttack ~= nil then m_bEnableBarbarianAttack = saved_data.m_bEnableBarbarianAttack end
307 if saved_data.m_bEnableTraders ~= nil then m_bEnableTraders = saved_data.m_bEnableTraders end
308 if saved_data.m_iEventCardResult ~= nil then m_iEventCardResult = saved_data.m_iEventCardResult end
309
310 if saved_data.m_sGUIDFishermenBoard ~= nil then
311 m_FishermenCardBoard = getObjectFromGUID(saved_data.m_sGUIDFishermenBoard)
312 end
313 if saved_data.m_sGUID_Die1 ~= nil then
314 m_Die1 = getObjectFromGUID(saved_data.m_sGUID_Die1)
315 else
316 m_Die1 = getObjectFromGUID('af8b2c')
317 end
318 if saved_data.m_sGUID_Die2 ~= nil then
319 m_Die2 = getObjectFromGUID(saved_data.m_sGUID_Die2)
320 else
321 m_Die2 = getObjectFromGUID('750997')
322 end
323 if saved_data.m_sGUID_EventDie ~= nil then
324 m_EventDie = getObjectFromGUID(saved_data.m_sGUID_EventDie)
325 end
326 end
327end
328
329function onSave()
330 local ToSave = {
331 m_CurScenario = m_CurScenario,
332 m_bRandomizeMap = m_bRandomizeMap,
333 m_bHiddenNumbers = m_bHiddenNumbers,
334 m_bHiddenTiles = m_bHiddenTiles,
335 m_bExpCK = m_bExpCK,
336 m_bMapLoaded = m_bMapLoaded,
337 m_bAutoSort = m_bAutoSort,
338 m_bDeckTooltip = m_bDeckTooltip,
339 m_bDisplayCardCount = m_bDisplayCardCount,
340 m_iDealCards = m_iDealCards,
341 m_iS9NumGold = m_iS9NumGold,
342 m_iS9PercentHidden = m_iS9PercentHidden,
343 m_iFrenemiesPlayers = m_iFrenemiesPlayers,
344 m_bEnableHelpers = m_bEnableHelpers,
345 m_bEnableFishermen = m_bEnableFishermen,
346 m_bEnableHarborMaster = m_bEnableHarborMaster,
347 m_bEnableWelfare = m_bEnableWelfare,
348 m_bEnableEventCards = m_bEnableEventCards,
349 m_bEnableRivers = m_bEnableRivers,
350 m_bEnableCaravans = m_bEnableCaravans,
351 m_bEnableBarbarianAttack = m_bEnableBarbarianAttack,
352 m_bEnableTraders = m_bEnableTraders,
353 m_iEventCardResult = m_iEventCardResult,
354 }
355 if m_FishermenCardBoard ~= nil then
356 ToSave.m_sGUIDFishermenBoard = m_FishermenCardBoard.getGUID()
357 end
358 if m_Die1 ~= nil then
359 ToSave.m_sGUID_Die1 = m_Die1.getGUID()
360 end
361 if m_Die2 ~= nil then
362 ToSave.m_sGUID_Die2 = m_Die2.getGUID()
363 end
364 if m_EventDie ~= nil then
365 ToSave.m_sGUID_EventDie = m_EventDie.getGUID()
366 end
367
368 return JSON.encode(ToSave)
369end
370
371function getScenarioID()
372 return m_CurScenario.sID
373end
374function isUsingExtension()
375 return m_CurScenario.bExtension
376end
377
378function checkButtonPrivileges(p)
379 local PPlayer = Player[p.sColor]
380 if PPlayer ~= nil then
381 if PPlayer.admin then
382 return true
383 else
384 broadcastToColor('Only promoted players can use the control panel', p.sColor, {1.0,1.0,1.0})
385 end
386 end
387 return false
388end
389
390--[[ removeOverlay
391 Removes the quick start overlay if it still exists.
392 Called from onMapLoad and from onDestroy of control panel page 1 to make it disappear when the page gets changed
393]]
394function removeOverlay()
395 local Obj = getObjectFromGUID('6905af')
396 if Obj ~= nil then
397 Obj.destruct()
398 end
399end
400
401--when a number die is interacted with it registers as die 1 or die 2 to make the script more robust and allow for deletion and spawning of dice (e.g. when put in a bag)
402function registerNumberDie(p)
403 if p.iDie == 1 then
404 if m_Die1 == nil then --old reference is invalid (die got deleted)
405 m_Die1 = p.DieObj
406 end
407 elseif p.iDie == 2 then
408 if m_Die2 == nil then --old reference is invalid (die got deleted)
409 m_Die2 = p.DieObj
410 end
411 elseif p.iDie == 3 and isCKenabled() then --Event die will register as die 3
412 if m_EventDie == nil then
413 m_EventDie = p.DieObj
414 end
415 end
416end
417
418function isCKenabled()
419 if m_CurScenario.bUseCK ~= nil then
420 return m_CurScenario.bUseCK
421 end
422 return false
423end
424function isWelfareEnabled()
425 if m_CurScenario.bUseWelfare ~= nil then
426 return m_CurScenario.bUseWelfare
427 end
428 return false
429end
430function isFishermenEnabled()
431 if m_CurScenario.bUseFishermen ~= nil then
432 return m_CurScenario.bUseFishermen
433 end
434 return false
435end
436function isEventCardsEnabled()
437 if m_CurScenario.bUseEventCards ~= nil then
438 return m_CurScenario.bUseEventCards
439 end
440 return false
441end
442function isRiversEnabled()
443 if m_CurScenario.bUseRivers ~= nil then
444 return m_CurScenario.bUseRivers
445 end
446 return false
447end
448function getDiceValue()
449 if isEventCardsEnabled() then
450 return m_iEventCardResult
451 elseif m_Die1 ~= nil and m_Die2 ~= nil then
452 return (m_Die1.getValue() + m_Die2.getValue())
453 end
454 return 0
455end
456function getDie2Value()
457 if m_Die2 ~= nil then
458 return m_Die2.getValue()
459 end
460 return 0
461end
462function getEventDieValue()
463 if m_EventDie ~= nil then
464 local iVal = m_EventDie.getValue()
465 if iVal == 1 or iVal == 2 or iVal == 6 then
466 return 'barbarians'
467 elseif iVal == 3 then
468 return 'science'
469 elseif iVal == 4 then
470 return 'trade'
471 elseif iVal == 5 then
472 return 'politics'
473 end
474 end
475 return nil
476end
477
478function callbackDeck(Obj, p)
479 Obj.setRotation(p.vRot)
480 Obj.setPosition(p.vPos)
481 if p.bShuffle == true then
482 Obj.shuffle()
483 end
484end
485
486function setupDecks()
487 --find appropriate card decks to spawn
488 local SpawnDef_Resources = m_ResourceDecks
489 local SpawnDef_DevCards = m_DevCardDecks
490 local SpawnDef_Commodities = m_CommodityDecks
491 local SpawnDef_Items = m_CKitems
492
493 if m_CurScenario.bExtension == true then
494 SpawnDef_Resources = m_ResourceDecks_ext
495 SpawnDef_Commodities = m_CommodityDecks_ext
496 end
497
498 -- spawn decks & put them in the proper position
499 local params = { callback = 'callbackDeck',
500 callback_owner = Global,
501 params = nil,
502 }
503 if isCKenabled() == true then
504 local BagResources = getObjectFromGUID(SpawnDef_Resources.Bag)
505 if BagResources ~= nil then
506 local vBagPos = BagResources.getPosition()
507 local iDecksSpawned = 0 --counter to spawn every deck at another height to avoid decks merging
508 --Lumber
509 params.guid = SpawnDef_Resources.Deck['Lumber']
510 params.params = {vPos = {-24.5,1.0,8.77}, vRot = {0.0,180.0,0.0}}
511 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
512 local Deck = takeObjectWorkaround(BagResources, params)
513 Deck.tooltip = m_bDeckTooltip
514 iDecksSpawned = iDecksSpawned + 1
515 --Bricks
516 params.guid = SpawnDef_Resources.Deck['Bricks']
517 params.params = {vPos = {-24.5,1.0,1.77}, vRot = {0.0,180.0,0.0}}
518 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
519 local Deck = takeObjectWorkaround(BagResources, params)
520 Deck.tooltip = m_bDeckTooltip
521 iDecksSpawned = iDecksSpawned + 1
522 --Wool
523 params.guid = SpawnDef_Resources.Deck['Wool']
524 params.params = {vPos = {-24.5,1.0,5.27}, vRot = {0.0,180.0,0.0}}
525 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
526 local Deck = takeObjectWorkaround(BagResources, params)
527 Deck.tooltip = m_bDeckTooltip
528 iDecksSpawned = iDecksSpawned + 1
529 --Grain
530 params.guid = SpawnDef_Resources.Deck['Grain']
531 params.params = {vPos = {-27.0,1.0,1.77}, vRot = {0.0,180.0,0.0}}
532 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
533 local Deck = takeObjectWorkaround(BagResources, params)
534 Deck.tooltip = m_bDeckTooltip
535 iDecksSpawned = iDecksSpawned + 1
536 --Ore
537 params.guid = SpawnDef_Resources.Deck['Ore']
538 params.params = {vPos = {-24.5,1.0,-1.73}, vRot = {0.0,180.0,0.0}}
539 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
540 local Deck = takeObjectWorkaround(BagResources, params)
541 Deck.tooltip = m_bDeckTooltip
542 iDecksSpawned = iDecksSpawned + 1
543 end
544 params.position = nil
545
546 local BagCommodities = getObjectFromGUID(SpawnDef_Commodities.Bag)
547 if BagCommodities ~= nil then
548 local vBagPos = BagCommodities.getPosition()
549 local iDecksSpawned = 0 --counter to spawn every deck at another height to avoid decks merging
550 --Paper
551 params.guid = SpawnDef_Commodities.Deck['Paper']
552 params.params = {vPos = {-27.0,1.0,8.77}, vRot = {0.0,180.0,0.0}}
553 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
554 local Deck = takeObjectWorkaround(BagCommodities, params)
555 Deck.tooltip = m_bDeckTooltip
556 iDecksSpawned = iDecksSpawned + 1
557 --Cloth
558 params.guid = SpawnDef_Commodities.Deck['Cloth']
559 params.params = {vPos = {-27.0,1.0,5.27}, vRot = {0.0,180.0,0.0}}
560 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
561 local Deck = takeObjectWorkaround(BagCommodities, params)
562 Deck.tooltip = m_bDeckTooltip
563 iDecksSpawned = iDecksSpawned + 1
564 --Coins
565 params.guid = SpawnDef_Commodities.Deck['Coins']
566 params.params = {vPos = {-27.0,1.0,-1.73}, vRot = {0.0,180.0,0.0}}
567 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
568 local Deck = takeObjectWorkaround(BagCommodities, params)
569 Deck.tooltip = m_bDeckTooltip
570 iDecksSpawned = iDecksSpawned + 1
571 end
572 params.position = nil
573
574 local BagCKitems = getObjectFromGUID(SpawnDef_Items.Bag)
575 if BagCKitems ~= nil then
576 local vBagPos = BagCKitems.getPosition()
577 local iDecksSpawned = 0
578 --Politics
579 params.guid = SpawnDef_Items.Item['DeckPolitics']
580 params.params = {vPos = {-24.5,1.0,-5.23}, vRot = {0.0,180.0,180.0}, bShuffle = true}
581 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
582 takeObjectWorkaround(BagCKitems, params)
583 iDecksSpawned = iDecksSpawned + 1
584 --Science
585 params.guid = SpawnDef_Items.Item['DeckScience']
586 params.params = {vPos = {-24.5,1.0,-8.73}, vRot = {0.0,180.0,180.0}, bShuffle = true}
587 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
588 takeObjectWorkaround(BagCKitems, params)
589 iDecksSpawned = iDecksSpawned + 1
590 --Trade
591 params.guid = SpawnDef_Items.Item['DeckTrade']
592 params.params = {vPos = {-27.0,1.0,-5.23}, vRot = {0.0,180.0,180.0}, bShuffle = true}
593 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
594 takeObjectWorkaround(BagCKitems, params)
595 iDecksSpawned = iDecksSpawned + 1
596
597 if m_CurScenario.bExtension == true then
598 params.guid = SpawnDef_Items.Item['DeckDefender_ext']
599 else
600 params.guid = SpawnDef_Items.Item['DeckDefender']
601 end
602 params.params = {vPos = {-27.0,1.0,-8.73}, vRot = {0.0,180.0,180.0}}
603 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
604 takeObjectWorkaround(BagCKitems, params)
605 end
606 else --isCKenabled()
607 local BagResources = getObjectFromGUID(SpawnDef_Resources.Bag)
608 if BagResources ~= nil then
609 local vBagPos = BagResources.getPosition()
610 local iDecksSpawned = 0
611 --Lumber
612 params.guid = SpawnDef_Resources.Deck['Lumber']
613 params.params = {vPos = {-24.5,1.0,8.77}, vRot = {0.0,180.0,0.0}}
614 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
615 local Deck = takeObjectWorkaround(BagResources, params)
616 Deck.tooltip = m_bDeckTooltip
617 iDecksSpawned = iDecksSpawned + 1
618 --Bricks
619 params.guid = SpawnDef_Resources.Deck['Bricks']
620 params.params = {vPos = {-24.5,1.0,5.27}, vRot = {0.0,180.0,0.0}}
621 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
622 local Deck = takeObjectWorkaround(BagResources, params)
623 Deck.tooltip = m_bDeckTooltip
624 iDecksSpawned = iDecksSpawned + 1
625 --Wool
626 params.guid = SpawnDef_Resources.Deck['Wool']
627 params.params = {vPos = {-24.5,1.0,1.77}, vRot = {0.0,180.0,0.0}}
628 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
629 local Deck = takeObjectWorkaround(BagResources, params)
630 Deck.tooltip = m_bDeckTooltip
631 iDecksSpawned = iDecksSpawned + 1
632 --Grain
633 params.guid = SpawnDef_Resources.Deck['Grain']
634 params.params = {vPos = {-24.5,1.0,-1.73}, vRot = {0.0,180.0,0.0}}
635 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
636 local Deck = takeObjectWorkaround(BagResources, params)
637 Deck.tooltip = m_bDeckTooltip
638 iDecksSpawned = iDecksSpawned + 1
639 --Ore
640 params.guid = SpawnDef_Resources.Deck['Ore']
641 params.params = {vPos = {-24.5,1.0,-5.23}, vRot = {0.0,180.0,0.0}}
642 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
643 local Deck = takeObjectWorkaround(BagResources, params)
644 Deck.tooltip = m_bDeckTooltip
645 iDecksSpawned = iDecksSpawned + 1
646 end
647 params.position = nil
648
649 local BagDev = getObjectFromGUID(SpawnDef_DevCards.Bag)
650 if BagDev ~= nil then
651 params.guid = nil
652 if m_CurScenario.sID == 'S7' or m_CurScenario.sID == 'T4' then --pirate scenario needs a deck with Knights instead of VPs (use it for Treasures Scenario 4, too)
653 if m_CurScenario.bExtension == true then
654 params.guid = SpawnDef_DevCards.Deck['56P_S7']
655 else
656 params.guid = SpawnDef_DevCards.Deck['34P_S7']
657 end
658 elseif m_CurScenario.bUseBAttack then
659 local TaBBag = getTaBBag()
660 if TaBBag ~= nil then
661 params.guid = TaBBag.getVar('m_Spawn_DevCards_BarbAttck')
662 BagDev = TaBBag
663 end
664 elseif m_CurScenario.bUseTraders then
665 local TaBBag = getTaBBag()
666 if TaBBag ~= nil then
667 if m_CurScenario.bExtension == true then
668 params.guid = TaBBag.getVar('m_Spawn_DevCards_TaB_ext')
669 else
670 params.guid = TaBBag.getVar('m_Spawn_DevCards_TaB')
671 end
672 BagDev = TaBBag
673 end
674 elseif getScenarioID() == 'B2' then --2 player version: smaller development card deck
675 params.guid = SpawnDef_DevCards.Deck['2P']
676 else
677 if m_CurScenario.bExtension == true then
678 params.guid = SpawnDef_DevCards.Deck['56P']
679 else
680 params.guid = SpawnDef_DevCards.Deck['34P']
681 end
682 end
683 if params.guid ~= nil then
684 params.params = {vPos = {-27.0,1.0,-5.23}, vRot = {0.0,180.0,180.0}, bShuffle = true}
685 takeObjectWorkaround(BagDev, params)
686 end
687 end
688 end
689
690 return true
691end
692
693--[[ The Update function. This is called once per frame.
694 This monitors the dice and triggers the token highlighting once they are all resting.
695 The dice wanting to be monitored register themselves in m_CheckDice when there is physical interaction with them so this update function will only do something
696 after the first die is manipulated until all dice are resting.
697--]]
698function onUpdate()
699 --Check if all dice registered to be monitored are resting and trigger hightlighting once they are
700 if #m_CheckDice > 0 then
701 local bResting = true
702 for i, Die in pairs(m_CheckDice) do
703 if Die ~= nil then
704 if Die.resting == false then
705 bResting = false
706 break
707 end
708 else --die got deleted -> remove from monitoring queue
709 table.remove(m_CheckDice, i)
710 end
711 end
712
713 if bResting then --all monitored dice are resting -> highlight number tokens and display the deal button
714 if m_MapScript ~= nil then
715 local iResult = getDiceValue()
716 if iResult > 0 then
717 m_MapScript.call('highlightChits', { iNum = iResult })
718
719 --Check card limits and display card count on a 7
720 if m_bDisplayCardCount then
721 if iResult == 7 then
722 startLuaCoroutine(self, 'checkCardLimits')
723 else
724 --in 90% of cases the texts will be deleted already through the turn end event / players discarding their cards
725 --however to avoid annoying players with the text in special cases -> make sure it is gone
726 startLuaCoroutine(self, 'clearCardCounts')
727 end
728 end
729
730 --Handle Deal button next to dice
731 if m_bDiceDealButton then
732 m_MapScript.call('hideDealButtonDie') --There is no onRoll event in which we can remove the button + there is no event when a dice is set to a number -> just make sure any old button is gone before showing a new one
733 if m_Die1 ~= nil then --event cards...
734 if iResult ~= 7 then
735 m_MapScript.call('showDealButtonDie', {DieObj = m_Die1})
736 elseif isCKenabled() and getEventDieValue() ~= 'barbarians' then
737 --for Cities & Knights we have to show the button also at a seven in case progress cards have to be dealt
738 m_MapScript.call('showDealButtonDie', {DieObj = m_Die1})
739 end
740 end
741 end
742
743 --interface for external tool dice logger
744 if m_DiceLogger ~= nil then
745 m_DiceLogger.call('onDiceRolled', {iResult = iResult}) --TODO: when an onRoll event gets implemented transmit the color as well
746 end
747
748 --interface for dice event receivers (automatic barbarian ship)
749 for i, Receiver in ipairs(m_DiceEventReceiver) do
750 if Receiver ~= nil then
751 Receiver.call('onDiceRolled', {iResult = iResult})
752 end
753 end
754 end
755 end
756
757 --tell dice that monitoring stopped so they will announce when they are interacted with again
758 for i = 1, #m_CheckDice do
759 if m_CheckDice[i] ~= nil then --it seems dice can be invalidated between the first loop in this routine and this one??!?!
760 m_CheckDice[i].call('setQueueStatus', {bActive = false})
761 end
762 end
763
764 --reset die monitoring list
765 m_CheckDice = {}
766 --enable deal cards function
767 m_bAllowDeal = true
768 end
769 end
770end
771
772--Reset/spawn items in the player area depending on the selected expansions/scenario
773function setupPlayerItems(params)
774 local bAddShips = params.bAddShips
775 local iNumChits = params.iNumChits
776 local bAddWarShips = false
777 if params.bAddWarShips == true then
778 bAddWarShips = true
779 end
780
781 local AllObj = getAllObjects()
782
783 local PlayerPos = {
784 ['Orange'] = { vStart = {x=-20.4,y=1.245,z=-17.0}, bLTR = false },
785 ['Red'] = { vStart = {x=8.8,y=1.245,z=-17.0}, bLTR = false },
786 ['White'] = { vStart = {x=38.9,y=1.245,z=-17.0}, bLTR = false },
787 ['Purple'] = { vStart = {x=19.9,y=1.245,z=17.0}, bLTR = true },
788 ['Blue'] = { vStart = {x=-9.3,y=1.245,z=17.0}, bLTR = true },
789 ['Green'] = { vStart = {x=-38.9,y=1.245,z=17.0}, bLTR = true }
790 }
791
792 local BuildOrder = {
793 --[[ iNum = Number of items that should be there
794 iStack = how many of them are in one "stack" (column)
795 fZSpace = vert space between objects
796 fXSpace = space between stacks of the same type
797 fYSpace = vertical space between items
798 fXGap = additional space to fXSpace to the next object type
799 fZGap = modifier for the Z starting point for this object
800 fYOffset = Offset for height
801 vRot = Rotation of this object
802 ]]--
803 ['City'] = {iNum = 4, iStack = 999, fZSpace = 0.5, fXSpace = 0.0, fXGap = 0.0, fZGap = 0.0, fYOffset = 0.0, vRot = {0.0,-90.0,0.0}},
804 ['Settlement'] = {iNum = 5, iStack = 3, fZSpace = 0.5, fXSpace = 0.7, fXGap = 1.1, fZGap = 0.0, fYOffset = -0.07, vRot = {0.0,90.0,0.0}},
805 ['Road'] = {iNum = 15, iStack = 8, fZSpace = 0.25, fXSpace = 1.2, fXGap = 1.0, fZGap = -0.1, fYOffset = -0.19, vRot = {0.0,0.0,0.0}},
806 ['Bridge'] = {iNum = 0, iStack = 3, fZSpace = 0.4, fXSpace = 0.0, fYSpace = 0.2, fXGap = 0.0, fZGap = -0.1, fYOffset = 0.3, vRot = {0.0,0.0,0.0}},
807 ['Ship'] = {iNum = 15, iStack = 5, fZSpace = 0.4, fXSpace = 1.1, fXGap = 1.3, fZGap = -0.05, fYOffset = 0.0, vRot = {0.0,0.0,0.0}},
808 ['Warship'] = {iNum = 7, iStack = 7, fZSpace = 0.4, fXSpace = 0.0, fXGap = 0.0, fZGap = -0.09, fYOffset = 0.0, vRot = {0.0,0.0,0.0}},
809 ['Chit'] = {iNum = iNumChits, iStack = 1, fZSpace = 0.0, fXSpace = 0.0, fYSpace = 0.12, fXGap = 0.0, fZGap = 0.5, fYOffset = -0.28, vRot = {0.0,-90.0,0.0}},
810 ['Knight'] = {iNum = 0, iStack = 6, fZSpace = 0.4, fXSpace = 0.0, fYSpace = 0.22, fXGap = 0.0, fZGap = -0.1, fYOffset = -0.17, vRot = {0.0,0.0,0.0}}, --knight for the Barbarian Attack scenario
811 ['Wall'] = {iNum = 3, iStack = 1, fZSpace = 0.0, fXSpace = 0.0, fYSpace = 0.15, fXGap = 1.3, fZGap = 0.25, fYOffset = -0.21, vRot = {0.0,0.0,0.0}},
812 ['Merchant'] = {iNum = 1, iStack = 1, fZSpace = 0.0, fXSpace = 0.0, fXGap = 0.0, fZGap = 1.2, fYOffset = 0.44, vRot = {0.0,0.0,0.0}},
813 ['Mighty Knight'] = {iNum = 2, iStack = 1, fZSpace = 0.0, fXSpace = 0.0, fYSpace = 0.19, fXGap = 1.3, fZGap = 0.15, fYOffset = -0.18, vRot = {0.0,0.0,180.0}},
814 ['Strong Knight'] = {iNum = 2, iStack = 1, fZSpace = 0.0, fXSpace = 0.0, fYSpace = 0.19, fXGap = 0.0, fZGap = 1.0, fYOffset = -0.18, vRot = {0.0,0.0,180.0}},
815 ['Basic Knight'] = {iNum = 2, iStack = 1, fZSpace = 0.0, fXSpace = 0.0, fYSpace = 0.19, fXGap = 0.0, fZGap = 1.85, fYOffset = -0.18, vRot = {0.0,0.0,180.0}},
816 }
817
818 if m_CurScenario.bUseBAttack and isCKenabled() == false then
819 BuildOrder['Knight'].iNum = 6
820 BuildOrder['Knight'].fXGap = 1.3
821 end
822
823 if bAddWarShips == true then
824 --spawn one row of ships less when warships are used
825 BuildOrder['Ship'].iNum = 10
826 end
827
828 if getScenarioID() == 'A7' then
829 BuildOrder['Ship'].iNum = 4
830 end
831 if getScenarioID() == 'A10' then
832 BuildOrder['Ship'].iNum = 5
833 end
834 if getScenarioID() == 'T3' then
835 BuildOrder['City'].iNum = 8
836 end
837 if m_CurScenario.bUseRivers then
838 BuildOrder['Bridge'].iNum = 3
839 end
840 if getScenarioID() == 'A8' then
841 BuildOrder['Bridge'].iNum = 3
842 end
843 if getScenarioID() == 'B2' then --2 player version: fewer buildings are available to each player
844 BuildOrder['Settlement'].iNum = 4
845 BuildOrder['City'].iNum = 3
846 end
847
848 for sCol, Opt in pairs(PlayerPos) do
849 local Objects = {
850 ['City'] = {},
851 ['Settlement'] = {},
852 ['Road'] = {},
853 ['Bridge'] = {},
854 ['Ship'] = {},
855 ['Warship'] = {},
856 ['Chit'] = {},
857 ['Knight'] = {},
858 ['Wall'] = {},
859 ['Merchant'] = {},
860 ['Mighty Knight'] = {},
861 ['Strong Knight'] = {},
862 ['Basic Knight'] = {},
863 }
864
865 --Find all building models for this color
866 for i, Obj in pairs(AllObj) do
867 local sName = Obj.getName()
868 if Obj.getDescription() == sCol or Obj.getVar('sPlayer') == sCol then
869 if sName == 'City' or sName == 'Settlement' or sName == 'Road' or sName == 'Ship' or sName == 'Warship'
870 or sName == 'Wall' or sName == 'Merchant' or sName == 'Basic Knight' or sName == 'Strong Knight' or sName == 'Mighty Knight' or sName == 'Bridge' then
871 table.insert(Objects[sName], Obj)
872 end
873 end
874
875 if sName == 'Indicator Special Build Phase' then
876 destroyObject(Obj) --destroy existing indicators (simpler than to implement some association to a color for repositioning...)
877 elseif string.sub(sName, 1, 10) == 'Catan Chit' then --Delete all existing Catan Chits
878 destroyObject(Obj)
879 end
880 end
881
882 --Make sure the number of items is good
883 for sItem, Order in pairs(BuildOrder) do
884 --destroy unneeded objects
885 if (isCKenabled() == false and (sItem == 'Wall' or sItem == 'Merchant' or sItem == 'Basic Knight' or sItem == 'Strong Knight' or sItem == 'Mighty Knight')) then
886 for i, Obj in pairs(Objects[sItem]) do
887 destroyObject(Obj)
888 end
889 elseif (bAddShips == false and sItem == 'Ship') or (bAddWarShips == false and sItem == 'Warship') then
890 for i, Obj in pairs(Objects[sItem]) do
891 destroyObject(Obj)
892 end
893 else
894 --check if new items need to be spawned
895 local iCur = #Objects[sItem]
896 while iCur < Order.iNum do
897 table.insert(Objects[sItem], 'X') --spawn a new item
898 iCur = iCur + 1
899 end
900 --delete excess items
901 while iCur > Order.iNum do
902 local DelObj = table.remove(Objects[sItem], #Objects[sItem])
903 destroyObject(DelObj)
904 iCur = iCur - 1
905 end
906 end
907 end
908
909 --Place the items on the table
910 local vPos = {x=Opt.vStart.x, y=Opt.vStart.y, z=Opt.vStart.z}
911 local fModX = 1.0
912 local fModZ = 1.0
913 if Opt.bLTR == false then
914 fModX = -1.0
915 end
916 if Opt.vStart.z < 0.0 then
917 fModZ = -1.0
918 end
919
920 for sItem, V in pairs(BuildOrder) do
921 if V.iNum == 0 or (sItem == 'Ship' and bAddShips == false) or (sItem == 'Warship' and bAddWarShips == false) then
922 --Nothing to be spawned - skip these columns
923 else
924 vPos.z = Opt.vStart.z + (V.fZGap * fModZ)
925 vPos.y = Opt.vStart.y + V.fYOffset
926 vPos.x = vPos.x + (V.fXGap * fModX)
927 if Objects[sItem] ~= nil then
928 for i, Obj in pairs(Objects[sItem]) do
929 if Obj == 'X' then
930 if sItem == 'Ship' and bAddShips == true then --ship missing -> spawn it
931 Obj = spawnShip({vPosition = vPos, vRotation = V.vRot, sPlayer = sCol})
932 elseif sItem == 'Warship' and bAddWarShips == true then
933 Obj = spawnShip({vPosition = vPos, vRotation = V.vRot, sPlayer = sCol, bIsWarShip = true})
934 elseif sItem == 'Chit' then
935 Obj = spawnCatanChit({vPosition = vPos, vRotation = {V.vRot[1],V.vRot[2]+(90.0*fModZ),V.vRot[3]}, sPlayer = sCol, sCatanChitDesc = params.sCatanChitDesc})
936 elseif sItem == 'Wall' and isCKenabled() == true then
937 Obj = spawnWall({vPosition = vPos, vRotation = V.vRot, sPlayer = sCol})
938 elseif sItem == 'Merchant' and isCKenabled() == true then
939 Obj = spawnMerchant({vPosition = vPos, vRotation = {V.vRot[1],V.vRot[2],V.vRot[3]}, sPlayer = sCol})
940 elseif sItem == 'Basic Knight' and isCKenabled() == true then
941 Obj = spawnKnight({vPosition = vPos, vRotation = {V.vRot[1],V.vRot[2]+(90.0*fModZ),V.vRot[3]}, sPlayer = sCol, sType = 'Basic'})
942 elseif sItem == 'Strong Knight' and isCKenabled() == true then
943 Obj = spawnKnight({vPosition = vPos, vRotation = {V.vRot[1],V.vRot[2]+(90.0*fModZ),V.vRot[3]}, sPlayer = sCol, sType = 'Strong'})
944 elseif sItem == 'Mighty Knight' and isCKenabled() == true then
945 Obj = spawnKnight({vPosition = vPos, vRotation = {V.vRot[1],V.vRot[2]+(90.0*fModZ),V.vRot[3]}, sPlayer = sCol, sType = 'Mighty'})
946 elseif sItem == 'Settlement' then --never should have implemented this scenario :'( only needed for S7
947 Obj = spawnSettlement(vPos, sCol)
948 elseif sItem == 'City' then
949 --find a city to clone (attached snap points cannot be spawned)
950 local SrcObj = nil
951 for i = 1, #Objects['City'] do
952 if Objects['City'][i] ~= 'X' then
953 SrcObj = Objects['City'][i]
954 break
955 end
956 end
957 if SrcObj ~= nil then
958 Obj = SrcObj.clone({ position = {vPos.x, vPos.y, vPos.z} })
959 else
960 --user deleted all cities or a game of Explorers & Pirates was previously loaded
961 --->try to load a city through the E&P expansion
962 local NewCity = spawnCity(vPos, {V.vRot[1],V.vRot[2]*fModX,V.vRot[3]}, sCol)
963 if NewCity ~= nil then
964 Obj = NewCity
965 end
966 end
967 elseif sItem == 'Bridge' then
968 Obj = spawnBridge(vPos, sCol)
969 elseif sItem == 'Knight' then
970 Obj = spawnBAttackKnight({vPosition = vPos, vRotation = vRot, sPlayer = sCol})
971 end
972 else
973 if sItem == 'Basic Knight' or sItem == 'Strong Knight' or sItem == 'Mighty Knight' then
974 Obj.setRotation({V.vRot[1],V.vRot[2]+(90.0*fModZ),V.vRot[3]})
975 elseif sItem == 'City' then --rotate cities to settlements so no items are spawned to high next to one to avoid the snap points grabbing them
976 Obj.setRotation({V.vRot[1],V.vRot[2]*fModX,V.vRot[3]})
977 else
978 Obj.setRotation({V.vRot[1],V.vRot[2],V.vRot[3]})
979 end
980 Obj.setPosition(vPos)
981 end
982
983 if i % V.iStack == 0 then --start new stack
984 vPos.z = Opt.vStart.z + (V.fZGap * fModZ)
985 vPos.x = vPos.x + (V.fXSpace * fModX)
986 if V.fYSpace ~= nil then --stack vertical
987 vPos.y = vPos.y + V.fYSpace
988 else
989 vPos.y = Opt.vStart.y + V.fYOffset
990 end
991 else
992 vPos.z = vPos.z + (V.fZSpace * fModZ)
993 end
994 end
995 end
996 end
997 end
998 end
999
1000 --Spawn pawns as indicators for the special build phase when playing with 5-6 players
1001 if m_CurScenario.bExtension == true and m_MapScript ~= nil then
1002 for sPlayer, Info in pairs(m_PlayerColors) do
1003 --spawn the pawn next to the counter
1004 local CounterObj = getObjectFromGUID(m_MapScript.call('getCounterGUID', {sColor = sPlayer}))
1005 if CounterObj ~= nil then
1006 local vCounterPos = CounterObj.getPosition()
1007 local fXoff = 2.0
1008 if vCounterPos.z > 0.0 then
1009 fXoff = fXoff * -1.0
1010 end
1011 local vPos = {vCounterPos.x + fXoff, 1.0, vCounterPos.z}
1012 spawnSpecialBuildPhasePawn({vPos = vPos, sColor = sPlayer})
1013 end
1014 end
1015 end
1016end
1017
1018--handle 'Shuffle Cards' buttons for player hands: show button only for used colors / hide them for unused
1019function onPlayerChangeColor(player_color)
1020 if player_color == nil then return end
1021 local Zones = { ['Green'] = 'd866a9',
1022 ['Blue'] = 'f37d36',
1023 ['Purple'] = 'a60284',
1024 ['Orange'] = '15c3aa',
1025 ['Red'] = '24632e',
1026 ['White'] = 'cdde1f',
1027 }
1028
1029 for sColor, sGUID in pairs(Zones) do
1030 local Zone = getObjectFromGUID(sGUID)
1031 if Zone ~= nil then
1032 if sColor == player_color then --spawn the button for the newly occupied color
1033 Zone.call('createShuffleButton')
1034 else --check unoccupied player zones and remove the shuffle buttons if there are no more cards in the player area
1035 if Player[sColor] ~= nil and Player[sColor].seated == false then
1036 if #Player[sColor].getHandObjects() == 0 then
1037 Zone.clearButtons()
1038 end
1039 end
1040 end
1041 end
1042 end
1043end
1044
1045--shuffle the resource card in a player hand
1046function shuffleHandCards(p)
1047 --[[
1048 if p.sZoneColor ~= p.sPlayer then --players may only press their own button
1049 broadcastToColor('You can only shuffle your own cards', p.sPlayer, {1.0,1.0,1.0})
1050 return
1051 end
1052 --]]
1053
1054 local Cards = Player[p.sZoneColor].getHandObjects()
1055 local ShuffleItems = {}
1056 for i, Card in pairs(Cards) do
1057 if Card.tag == 'Card' then
1058 local sName = Card.getName()
1059 --find all resource / commodity cards to shuffle
1060 if sName == 'Lumber' or sName == 'Bricks' or sName == 'Wool' or sName == 'Grain' or sName == 'Ore' or sName == 'Paper' or sName == 'Cloth' or sName == 'Coins' then
1061 table.insert(ShuffleItems, Card)
1062 end
1063 end
1064 end
1065 --randomize resource card positions
1066 if #ShuffleItems > 1 then
1067 for i = #ShuffleItems, 2, -1 do
1068 local iRand = math.random(i)
1069 local vPosCard = ShuffleItems[i].getPosition()
1070 local vPosRand = ShuffleItems[iRand].getPosition()
1071 ShuffleItems[i].setPosition(vPosRand)
1072 ShuffleItems[iRand].setPosition(vPosCard)
1073 end
1074 --make all cards move a bit as feedback that the shuffle function did something and to obfuscate whether cards changed position or not
1075 local fHandCenter = Player[p.sZoneColor].getPlayerHand().pos_x
1076 for i = 1, #ShuffleItems do
1077 local vPosNew = ShuffleItems[i].getPosition()
1078 local fOffset = -0.2
1079 if vPosNew.x < fHandCenter then
1080 fOffset = 0.2
1081 end
1082 ShuffleItems[i].setPosition({vPosNew.x+fOffset,vPosNew.y,vPosNew.z})
1083 end
1084 end
1085end
1086
1087-- spawn the pirate ship (called from the control panel on map load)
1088function spawnPirate(params)
1089 local vRot = {0.0,160.0,0.0} --make him look like he is going somewhere :-)
1090 if params.vRot ~= nil then
1091 vRot = params.vRot
1092 end
1093 local ShipObj = spawnShip({vPosition = params.vPos, vRotation = vRot, vScale = {1.2,1.2,1.2}})
1094 if ShipObj ~= nil then
1095 ShipObj.setName('Pirate Ship (Arrrr)')
1096 ShipObj.setDescription('May be moved INSTEAD of the robber. The pirate is placed in the center of ocean tiles and blocks new ships from being built on this tile. Ships adjacent to the hex cannot be moved.')
1097 ShipObj.setColorTint({0.15, 0.15, 0.15})
1098 ShipObj.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'seafarers_add\') end') --so it will be deleted when the next map is spawned
1099 end
1100end
1101
1102function spawnShip(p)
1103 local params = {}
1104 params.type = 'Custom_Model'
1105 params.position = {p.vPosition.x, p.vPosition.y, p.vPosition.z}
1106 params.rotation = p.vRotation
1107 params.scale = {1.0, 1.0, 1.0}
1108 if p.vScale ~= nil then
1109 params.scale = p.vScale
1110 end
1111 params.snap_to_grid = false
1112
1113 local custom = {}
1114 if p.bIsWarShip == true then
1115 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379838327/CFAC6A762AE832DB84513D78947D65D0DE34A39D/'
1116 else
1117 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379843989/82018A077B99A1206F2B74A1FBAFF2D4304083EF/'
1118 end
1119 custom.diffuse = ''
1120 custom.convex = true
1121 custom.type = 0 --generic
1122 custom.material = 1 --wood
1123
1124 local ShipObj = spawnObject(params)
1125 ShipObj.setCustomObject(custom)
1126
1127 if p.sPlayer ~= nil then
1128 if p.bIsWarShip == true then
1129 ShipObj.setName('Warship')
1130 else
1131 ShipObj.setName('Ship')
1132 end
1133 ShipObj.setDescription(p.sPlayer)
1134 ShipObj.setColorTint(m_PlayerColors[p.sPlayer])
1135 end
1136
1137 ShipObj.setLock(true)
1138 addToUnlockList({Obj = ShipObj})
1139
1140 return ShipObj
1141end
1142
1143--Spawn pawn as an indicator for the special build phase with 5-6 players
1144function spawnSpecialBuildPhasePawn(p)
1145 local params = {}
1146 params.type = 'PlayerPawn'
1147 params.position = p.vPos
1148 params.scale = {1.2, 1.0, 1.2}
1149
1150 local Pawn = spawnObject(params)
1151 Pawn.setName('Indicator Special Build Phase')
1152 Pawn.setDescription('Move this pawn in front of your player area before the current player ends their turn to indicate you want to build something in the special build phase. (for 5-6 Players)')
1153 Pawn.setColorTint(m_PlayerColors[p.sColor])
1154 Pawn.setLuaScript([[ m_ActiveIndic = nil
1155 function onPlayerTurnEnd(player_color_end, player_color_next)
1156 if m_ActiveIndic ~= nil then
1157 m_ActiveIndic.destruct()
1158 end
1159 --check if this pawn is outside the player area and if so spawn an arrow to draw attention
1160 local vPos = self.getPosition()
1161 if vPos.z > -16.2 and vPos.z < 16.2 then
1162 m_ActiveIndic = Global.call('spawnIndicatorArrow', {vPos = {vPos.x, vPos.y + 1.0 + (self.getScale().y * 1.4), vPos.z}, vScale = {1.0, 1.0, 1.0}, vColor = {1.0,0.8627,0.05098}, fTTL = 6.7})
1163 end
1164 end
1165 function onPickUp(player_color) --remove active arrow when player picks up the pawn
1166 if m_ActiveIndic ~= nil then
1167 m_ActiveIndic.destruct()
1168 end
1169 end
1170 ]])
1171end
1172
1173--[[ callbackGeneral
1174 Callback function used by many different objects.
1175 params:
1176 vRot oblig. vector rotation
1177 vPos oblig. vector position
1178 vScale vector scale
1179 bLock bool lock the object
1180 ThResume coroutine Coroutine to call after processing the callback
1181--]]
1182function callbackGeneral(Obj, p)
1183 Obj.setRotation(p.vRot)
1184 Obj.setPosition(p.vPos)
1185 if p.vScale ~= nil then
1186 Obj.setScale(p.vScale)
1187 end
1188 if p.bLock == true then
1189 Obj.setLock(true)
1190 end
1191 if p.ThResume ~= nil then
1192 coroutine.resume(p.ThResume)
1193 end
1194end
1195
1196function callbackHex(Obj, p)
1197 callbackGeneral(Obj, p)
1198 callback_HexLoaded()
1199end
1200
1201function spawnMetropolisGate(p)
1202 local params = {}
1203 params.type = 'Custom_Model'
1204 params.position = {p.vPosition.x,p.vPosition.y,p.vPosition.z}
1205 params.rotation = p.vRotation
1206
1207 local custom = {}
1208 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379846524/3B83C6691E306D61176BBD975A0F31CBCB6F7F71/'
1209 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379849079/1F532EF7B11F7242E8B164E77838D2E028B83D69/'
1210 custom.diffuse = ''
1211 custom.type = 1 --figurine
1212 custom.material = 1 --wood
1213
1214 local Gate = spawnObject(params)
1215 Gate.setCustomObject(custom)
1216 --Add script for scoring: the points have to be awarded when the metropolis is touching a certain players city
1217 Gate.setLuaScript( [[ m_bLoaded = false
1218 function onSave()
1219 local ToSave = { sScorer = self.getVar('sScorer') }
1220 return JSON.encode(ToSave)
1221 end
1222 function onLoad(save_state)
1223 local saved_data = JSON.decode(save_state)
1224 if saved_data ~= nil then
1225 self.setVar('sScorer', saved_data.sScorer)
1226 end
1227 m_bLoaded = true --workaround: when rewinding time onCollisionEnter can be called before own lua scripts in mapscript are initialized
1228 end
1229 function onCollisionEnter(collision_info)
1230 if self == nil or m_bLoaded == false then
1231 return
1232 end
1233 if self.getVar('sScorer') ~= nil then --score is applied already, ignore this collision
1234 return
1235 end
1236 if collision_info == nil then return end
1237 local ColObj = collision_info.collision_object
1238 if ColObj.getName() == 'City' then
1239 local MapScript = getObjectFromGUID('42fd8e')
1240 if MapScript ~= nil then
1241 --check if the collision is within the map area
1242 local bScore = false
1243 local ChkObj = MapScript.getObjects()
1244 for i, Obj in ipairs(ChkObj) do
1245 if Obj == ColObj then
1246 bScore = true
1247 break
1248 end
1249 end
1250 --award the points
1251 if bScore == true then
1252 if true == MapScript.call('addScore', {iScore = 2, sPlayer = ColObj.getDescription()}) then
1253 self.setVar('sScorer', ColObj.getDescription())
1254 end
1255 end
1256 end
1257 end
1258 end
1259 function onPickUp(player_color) --remove the score from the previous scorer when the gate is picked up
1260 local sOldScorer = self.getVar('sScorer')
1261 if sOldScorer ~= nil then
1262 self.setVar('sScorer', nil)
1263 local MapScript = getObjectFromGUID('42fd8e')
1264 if MapScript ~= nil then
1265 MapScript.call('subtractScore',{iScore = 2, sPlayer = sOldScorer})
1266 end
1267 end
1268 end ]] )
1269
1270 Gate.setName('Metropolis - ' .. p.sType)
1271 Gate.setDescription('2 Victory Points!\nPlace it on one of your cities if you reach stage 4 in the ' .. p.sType .. ' category first. If someone else manages to get to stage 5 first you have to give the metropolis to this player.')
1272 if p.sType == 'Politics' then
1273 Gate.setColorTint({0.0,0.37647,0.67843})
1274 elseif p.sType == 'Science' then
1275 Gate.setColorTint({0.0,0.64313,0.32156})
1276 elseif p.sType == 'Trade' then
1277 Gate.setColorTint({0.97255,0.74902,0.08235})
1278 end
1279end
1280
1281function spawnWall(p)
1282 local params = {}
1283 params.type = 'Custom_Model'
1284 params.position = {p.vPosition.x,p.vPosition.y,p.vPosition.z}
1285 params.rotation = p.vRotation
1286
1287 local custom = {}
1288 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379855106/394C23AB4BD900AAD134BD8F8F217B4F5DF2BA82/'
1289 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379857723/2029EA7FC070B4CD260492063BE0D7E7129852E8/'
1290 custom.diffuse = ''
1291 custom.type = 0 --generic
1292 custom.material = 1 --wood
1293
1294 local Wall = spawnObject(params)
1295 Wall.setCustomObject(custom)
1296
1297 Wall.setName('Wall')
1298 if p.sPlayer ~= nil then
1299 Wall.setDescription(p.sPlayer)
1300 Wall.setColorTint(m_PlayerColors[p.sPlayer])
1301 end
1302end
1303
1304function spawnKnight(p)
1305 local params = {}
1306 params.type = 'Custom_Model'
1307 params.position = {p.vPosition.x,p.vPosition.y,p.vPosition.z}
1308 params.rotation = p.vRotation
1309 params.scale = {0.9, 0.9, 0.9}
1310
1311 local custom = {}
1312 if p.sType == 'Mighty' then
1313 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601380173660/814EB937A7102F76BF68D7D891190B1C895C2DF4/'
1314 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380178917/0E84E940D57266A700908F2B3EFFD20662B2CABF/'
1315 elseif p.sType == 'Strong' then
1316 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601380175749/27E1062B3DD72D7CDAC4A776C02D9088CF0B67D1/'
1317 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380180091/6D683F3DF23C367CCAAC0191C384D2906389EE45/'
1318 elseif p.sType == 'Basic' then
1319 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601380177408/EFE31FE4FF37E006E1825A56DC56E7F5678D4618/'
1320 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380181828/C1384EA9F5BFAFCA59A27456243C4DC601C9C361/'
1321 end
1322 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379865464/95822A8471E23E0A048D10243B63BF31137336F2/'
1323 custom.type = 0 --generic
1324 custom.material = 1 --wood
1325
1326 local KnightToken = spawnObject(params)
1327 KnightToken.setCustomObject(custom)
1328
1329 KnightToken.setName(p.sType .. ' Knight')
1330 if p.sPlayer ~= nil then
1331 KnightToken.setDescription(p.sPlayer)
1332 KnightToken.setColorTint(m_PlayerColors[p.sPlayer])
1333 end
1334
1335 --report the collision to possible receivers (Cities / Knights counter tool)
1336 KnightToken.setLuaScript([[ m_bActive = false
1337 function onLoad(save_state)
1338 m_bActive = true --workaround for possible collision calls before stuff is initialized
1339 end
1340 function onCollisionEnter(collision_info)
1341 if m_bActive then
1342 Global.call('reportKnightCollision')
1343 end
1344 end
1345 function onPickUp(player_color)
1346 Global.call('reportKnightCollision')
1347 end ]])
1348end
1349
1350function spawnMerchant(p)
1351 local params = {}
1352 params.type = 'Custom_Model'
1353 params.position = {p.vPosition.x,p.vPosition.y,p.vPosition.z}
1354 params.rotation = p.vRotation
1355
1356 local custom = {}
1357 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379869495/388CFE018FFB1969CBD518D22737FD5D6D4D5818/'
1358 custom.collider = ''
1359 custom.diffuse = ''
1360 custom.type = 1 --figurine
1361 custom.material = 1 --wood
1362
1363 local Trader = spawnObject(params)
1364 Trader.setCustomObject(custom)
1365 Trader.use_snap_points = false
1366
1367 Trader.setName('Merchant')
1368 Trader.setDescription('When you play the progress card Merchant you may place your Merchant on a hex adjacent to one of your Settlements/Cities. As long as your merchant is active you may trade the resource he is placed on 2:1 with the bank. There can only be one merchant active at a time. If another player has his merchant on the map, he has to remove it. You get 1 Victory Point for an active merchant.')
1369 if p.sPlayer ~= nil then
1370 Trader.setLuaScript('function onLoad() self.setVar(\'sPlayer\', \'' .. p.sPlayer .. '\') end')
1371 Trader.setColorTint(m_PlayerColors[p.sPlayer])
1372 end
1373end
1374
1375function spawnCatanChit(p)
1376 local params = {}
1377 params.type = 'Custom_Tile'
1378 params.position = {p.vPosition.x, p.vPosition.y, p.vPosition.z}
1379 params.rotation = p.vRotation
1380 params.scale = {0.37, 1.0, 0.37}
1381
1382 local custom = {}
1383 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379873242/F2E9C25F70C5154ACD5407238EFBCA0AC527A625/'
1384 custom.type = 0 --Box
1385 custom.thickness = 0.1
1386
1387 local Chit = spawnObject(params)
1388 Chit.setCustomObject(custom)
1389
1390 Chit.setName('Catan Chit')
1391 if p.sCatanChitDesc ~= nil then
1392 Chit.setDescription(p.sCatanChitDesc)
1393 end
1394 if p.sPlayer ~= nil then
1395 Chit.setName('Catan Chit - ' .. p.sPlayer)
1396 Chit.setColorTint(m_PlayerColors[p.sPlayer])
1397 end
1398 Chit.setLock(true)
1399
1400 if p.bLock ~= true then
1401 addToUnlockList({Obj = Chit})
1402 end
1403
1404 return Chit
1405end
1406
1407function spawnSettlement(vPos, sColor)
1408 local params = {}
1409 params.type = 'Custom_Model'
1410 params.position = {vPos.x,vPos.y,vPos.z}
1411 params.rotation = {0.0,90.0,0.0}
1412
1413 local custom = {}
1414 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379876219/C36F94BAF590D30E6CE987015C610096E4CFB1A0/'
1415 custom.type = 0 --generic
1416 custom.material = 1 --wood
1417
1418 local Settlement = spawnObject(params)
1419 Settlement.setCustomObject(custom)
1420
1421 Settlement.setName('Settlement')
1422 Settlement.setDescription(sColor)
1423 Settlement.setColorTint(m_PlayerColors[sColor])
1424end
1425
1426--[[ spawnCity
1427 Does not actually spawn a city because the attached snap points cannot be spawned.
1428 Will clone a City template of the Explorers & Pirates expansion bag
1429 TODO: redo all item spawns if they ever add scripting support for snap points
1430--]]
1431function spawnCity(vPos, vRot, sColor)
1432 local City = nil
1433 local EaPBag = getEaPBag()
1434 if EaPBag ~= nil then
1435 City = EaPBag.call('spawnCity', {vPos = vPos, vRot = vRot, sColor = sColor})
1436 end
1437 return City
1438end
1439
1440function spawnBridge(vPos, sColor)
1441 local params = {}
1442 params.type = 'Custom_Model'
1443 params.position = {vPos.x,vPos.y,vPos.z}
1444
1445 local custom = {}
1446 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379879290/3E67CC0119F31A97C5C8E221EE9A23C873FF03CB/'
1447 custom.collider = ''
1448 custom.diffuse = ''
1449 custom.type = 0 --generic
1450 custom.material = 1 --wood
1451
1452 local Bridge = spawnObject(params)
1453 Bridge.setCustomObject(custom)
1454
1455 Bridge.setName('Bridge')
1456 Bridge.setDescription(sColor)
1457 Bridge.setColorTint(m_PlayerColors[sColor])
1458end
1459
1460--Spawn knight token needed for the Barbarian Attack scenario
1461function spawnBAttackKnight(p)
1462 local params = {}
1463 params.type = 'Custom_Model'
1464 params.position = {p.vPosition.x,p.vPosition.y,p.vPosition.z}
1465 params.rotation = p.vRotation
1466
1467 local custom = {}
1468 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379883118/6CB181322189657641C89B44DB60CAD8DC5B880E/'
1469 custom.diffuse = ''
1470 custom.type = 1 --figurine
1471 custom.material = 1 --wood
1472
1473 local Knight = spawnObject(params)
1474 Knight.setCustomObject(custom)
1475 Knight.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'battack\') end')
1476
1477 Knight.setName('Knight')
1478 if p.sPlayer ~= nil then
1479 Knight.setDescription(p.sPlayer)
1480 Knight.setColorTint(m_PlayerColors[p.sPlayer])
1481 end
1482end
1483
1484function spawnBarbarians(p)
1485 if m_CurScenario.bUseBAttack then
1486 return --no barbarian tile when playing the "Barbarian Attack" campaign
1487 end
1488
1489 local vPos = {p.fXPos, 1.0, 1.018}
1490 --spawn the sea tile with the barbarian "path"
1491 local TileBag = getObjectFromGUID(m_BarbarianTile)
1492 if TileBag ~= nil then
1493 local vInitPos = TileBag.getPosition()
1494 vInitPos.x = vInitPos.x + 6.0 --make sure it is not captured by the spawn bag right after spawning
1495 local params = {position = vInitPos,
1496 callback = 'callbackGeneral',
1497 callback_owner = Global,
1498 params = {vPos = vPos, vRot = {0.0, -115.0, 0.0}, bLock = true}
1499 }
1500
1501 takeObjectWorkaround(TileBag, params)
1502 end
1503
1504 --spawn the ship
1505 local params = {}
1506 params.type = 'Custom_Model'
1507 params.position = {vPos[1] + 0.5, vPos[2] + 1.0, vPos[3] + 1.6}
1508 params.rotation = {0.0,155.0,0.0}
1509 params.scale = {0.7, 0.7, 0.7}
1510
1511 local custom = {}
1512 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379886620/8A66725468D9E84827F10A4E973138C525409A87/'
1513 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379888618/799E1DC2DD38BE927934E136AFC02AE3DE97563C/'
1514 custom.type = 1 --figurine
1515 custom.material = 1 --wood
1516
1517 local ShipObj = spawnObject(params)
1518 ShipObj.setCustomObject(custom)
1519
1520 ShipObj.setName('Barbarian Ship')
1521 ShipObj.setDescription('Advance the Barbarian Ship whenever the icon is rolled on the event die')
1522 ShipObj.setColorTint({0.15, 0.15, 0.15})
1523 ShipObj.setLuaScript([[ m_bDisplayRaidInfo = false --workaround to prevent message spam when multiple collisions are registered
1524 function onCollisionEnter(collision_info)
1525 if collision_info == nil then return end
1526 if collision_info.collision_object.tag == 'Dice' then
1527 --make barbarian (mostly) un-knockoverable from dice
1528 self.setLock(true)
1529 elseif m_bDisplayRaidInfo and collision_info.collision_object.getDescription() == 'Barbarian route' then
1530 --check if the barbarians hit the raid field and post City and Knight count in the chat
1531 local vPosRoute = collision_info.collision_object.getPosition()
1532 local vPosShip = self.getPosition()
1533 local fDistanceX = math.abs(vPosShip.x - vPosRoute.x - (-0.47))
1534 local fDistanceZ = math.abs(vPosShip.z - vPosRoute.z - (-1.67))
1535 if fDistanceX < 0.12 and fDistanceZ < 0.12 then
1536 m_bDisplayRaidInfo = false --prevent the message from being sent again until ship is picked up
1537 local MapScript = getObjectFromGUID('42fd8e')
1538 if MapScript ~= nil then
1539 MapScript.call('displayBarbarianRaidMessage')
1540 end
1541 end
1542 end
1543 end
1544 function onCollisionExit(collision_info)
1545 if collision_info == nil then return end
1546 if collision_info.collision_object.tag == 'Dice' then
1547 self.setLock(false)
1548 end
1549 end
1550 function onPickUp(player_color)
1551 m_bDisplayRaidInfo = true --picking the ship up unlocks the message again
1552 end
1553 ]])
1554 ShipObj.setLock(true)
1555 addToUnlockList({Obj = ShipObj})
1556end
1557
1558--Goods tokens for the Traders & Barbarians campaign -> called from the layout definition in the control panel
1559function spawnGoodsToken(p)
1560 local params = {}
1561 params.type = 'Custom_Tile'
1562 params.position = p.vPos
1563 params.rotation = p.vRot
1564 params.scale = {0.5, 1.0, 0.5}
1565 local custom = {}
1566 if p.sFront == 'castle' then
1567 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601380186899/B5240B0937C7183C98D014DE7685BDC8583547D8/'
1568 elseif p.sFront == 'glass_factory' then
1569 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601380192420/750676486E726958714E8528E64DC5AE428A3579/'
1570 elseif p.sFront == 'quarry' then
1571 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601380193703/F1DDC3B5E87DC1A52A2CF661CE19ABDA3CC5C955/'
1572 end
1573 if p.sBack == 'glass' then
1574 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380191908/A0BBD817C67E9FEE8270845B3898070D9AA0EC69/'
1575 elseif p.sBack == 'marble' then
1576 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380193059/1ECD9EB112AF7414F9126FA748D21B3F62086719/'
1577 elseif p.sBack == 'sand' then
1578 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380194233/C6C3B2A1E765F9DC5AA4ACD6C79DE625D929588D/'
1579 elseif p.sBack == 'tools' then
1580 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380194618/2C6CC8F25692AD86F32D6F2D9AB95CED65AC8E32/'
1581 end
1582 custom.type = 0 --Box
1583 custom.thickness = 0.06
1584
1585 local Token = spawnObject(params)
1586 Token.setCustomObject(custom)
1587 Token.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'traders\') end')
1588 if p.sFront == 'glass_factory' then
1589 Token.setColorTint({0.216,0.596,0.231})
1590 elseif p.sFront == 'quarry' then
1591 Token.setColorTint({0.804,0.616,0.224})
1592 elseif p.sFront == 'castle' then
1593 Token.setColorTint({0.537,0.839,0.188})
1594 end
1595 Token.setName('Commodity Token')
1596 Token.use_snap_points = false
1597 Token.dynamic_friction = 1.0
1598 Token.static_friction = 1.0
1599 Token.drag = 2.0
1600 Token.setLock(true)
1601 addToUnlockList({Obj = Token})
1602
1603 return Token
1604end
1605
1606--spawn specialist card decks
1607function spawnSpecialistCards(p)
1608 local DeckBag = getObjectFromGUID('cc360f')
1609 if DeckBag ~= nil then
1610 local params = {callback = 'callbackGeneral',
1611 callback_owner = Global,
1612 params = {vPos = p.vPos, vRot = {0.0, 180.0, 0.0}}
1613 }
1614 takeObjectWorkaround(DeckBag, params)
1615 end
1616end
1617
1618-- find the robber
1619function getRobber()
1620 if m_Robber ~= nil then
1621 return m_Robber
1622 else
1623 local AllObj = getAllObjects()
1624 for i, Obj in ipairs(AllObj) do
1625 if Obj.getName() == 'Robber' then
1626 m_Robber = Obj
1627 return Obj
1628 end
1629 end
1630 end
1631 return nil
1632end
1633
1634function placeRobber(p)
1635 local Robber = getRobber()
1636 if Robber ~= nil then
1637 Robber.setPosition(p.vPos)
1638 Robber.setLock(true) -- lock for now - it will be unlocked when all hex tiles are spawned
1639 end
1640 -- put him in the list of objects to unlock after hexes have finished loading
1641 addToUnlockList({Obj = Robber})
1642end
1643
1644
1645--[[ Dice-Force field for hand zones
1646 bounces dice incoming from the table side back onto the table
1647]]--
1648function onObjectEnterScriptingZone(zone, enter_object)
1649 if enter_object ~= nil and enter_object.held_by_color == nil then --allow people to carry dice into the hand zones
1650 if enter_object.tag == 'Dice' then
1651 if string.sub(zone.getDescription(), 18, 21) == 'Hand' then
1652 local fMod = 1.0
1653 if zone.getPosition().z < 0 then fMod = -1.0 end
1654
1655 local vVel = enter_object.getVelocity()
1656 if (enter_object.getPosition().z * fMod) < 18.3 and (vVel.z * fMod) > 0 then
1657 enter_object.addForce({vVel.x*-0.1,vVel.y*0.8,vVel.z*-1.3}, 4)
1658 end
1659 end
1660 end
1661 end
1662end
1663
1664--[[ Function for the button to align certain tiles to the 'inofficial' grid within the border frames
1665 This is called when the button on page 2 of the control panel is pressed
1666]]--
1667function alignHexes()
1668 if isMapLoaded() == false then
1669 return true
1670 end
1671 --find the positions of the border tiles to determine map size and offset
1672 local Borders = { ['left'] = nil, ['top'] = nil, ['right'] = nil, ['bottom'] = nil }
1673 local AllObj = getAllObjects()
1674 for i, Obj in ipairs(AllObj) do
1675 local sObjType = Obj.getVar('sObjType')
1676 if sObjType ~= nil then
1677 if sObjType == 'border' then
1678 --find leftmost border tile
1679 if Borders['left'] ~= nil then
1680 if Borders['left'].getPosition().x > Obj.getPosition().x then
1681 Borders['left'] = Obj
1682 end
1683 else
1684 Borders['left'] = Obj
1685 end
1686 --find topmost border tile
1687 if Borders['top'] ~= nil then
1688 if Borders['top'].getPosition().z < Obj.getPosition().z then
1689 Borders['top'] = Obj
1690 end
1691 else
1692 Borders['top'] = Obj
1693 end
1694 --find rightmost border tile
1695 if Borders['right'] ~= nil then
1696 if Borders['right'].getPosition().x < Obj.getPosition().x then
1697 Borders['right'] = Obj
1698 end
1699 else
1700 Borders['right'] = Obj
1701 end
1702 --find bottommost border tile
1703 if Borders['bottom'] ~= nil then
1704 if Borders['bottom'].getPosition().z > Obj.getPosition().z then
1705 Borders['bottom'] = Obj
1706 end
1707 else
1708 Borders['bottom'] = Obj
1709 end
1710 end
1711 end
1712 end
1713
1714 --calculate possible hex positions
1715 if Borders['left'] == nil or Borders['top'] == nil or Borders['right'] == nil or Borders['bottom'] == nil then
1716 print('Error: Unable to determine border frame positions')
1717 return false
1718 end
1719
1720 local HexPos = {}
1721 local vPos = Borders['left'].getPosition()
1722 local fXStart = Borders['left'].getPosition().x
1723 local fXEnd = Borders['right'].getPosition().x - 3.48
1724 local fZEnd = Borders['top'].getPosition().z
1725 local fZ = Borders['left'].getPosition().z --start in the middle
1726
1727 while fZ < fZEnd do
1728 local fX = fXStart
1729 while fX < fXEnd do
1730 fX = fX + 3.48
1731 table.insert(HexPos, {x=fX, y=0.965, z=fZ})
1732 if fZ > 1.0 or fZ < -1.0 then --insert positions for the rows above and below the center row in the same loop
1733 table.insert(HexPos, {x=fX, y=0.965, z=fZ * -1})
1734 end
1735 end
1736 fXStart = fXStart + 1.74
1737 fXEnd = fXEnd - 1.74
1738 fZ = fZ + 3.01
1739 end
1740
1741 --Find all hex tiles tiles within the map scripting zone
1742 local HexTiles = {}
1743 local NumTokens = {}
1744 local SearchZone = getObjectFromGUID('42fd8e')
1745 local AllObj = SearchZone.getObjects()
1746 for i, Obj in ipairs(AllObj) do
1747 if Obj.getLock() == false then
1748 local sType = Obj.getVar('sObjType')
1749 if sType == 'hex' then
1750 table.insert(HexTiles, Obj)
1751 elseif sType == 'numtoken' then
1752 table.insert(NumTokens, Obj)
1753 end
1754 end
1755 end
1756
1757 --find the closest hex for each position
1758 local MoveList = {}
1759 for i, Pos in ipairs(HexPos) do
1760 local iBestHex = -1
1761 local fBestHexDist = 3.0
1762 --find closest hex
1763 for j, Hex in pairs(HexTiles) do
1764 local fDistance = math.abs(Pos.x - Hex.getPosition().x) + math.abs(Pos.z - Hex.getPosition().z)
1765 if fDistance < fBestHexDist then
1766 iBestHex = j
1767 fBestHexDist = fDistance
1768 end
1769 end
1770 --find closest number token
1771 local iBestToken = -1
1772 local fBestTokenDist = 2.2
1773 for j, Token in pairs(NumTokens) do
1774 local fDistance = math.abs(Pos.x - Token.getPosition().x) + math.abs(Pos.z - Token.getPosition().z)
1775 if fDistance < fBestTokenDist then
1776 iBestToken = j
1777 fBestTokenDist = fDistance
1778 end
1779 end
1780
1781 if iBestHex >= 0 then
1782 local MoveItem = {Hex = HexTiles[iBestHex], vPos = Pos}
1783 if iBestToken >= 0 and HexTiles[iBestHex].getDescription() ~= 'Lake' then --save the matching hex to this token, ignore lakes because they can have 2 - 4 num tokens
1784 MoveItem.Token = NumTokens[iBestToken]
1785 end
1786 table.insert(MoveList, MoveItem)
1787 HexTiles[iBestHex] = nil --this hex found its new home, ignore it from now on
1788 if iBestToken >= 0 then
1789 NumTokens[iBestToken] = nil
1790 end
1791 end
1792 end
1793
1794 --position the hexes & tokens
1795 for i, Item in ipairs(MoveList) do
1796 local zRot = Item.Hex.getRotation().z
1797 if zRot < 230.0 and zRot > 150.0 then --it is probably meant to be upside down
1798 zRot = 180.0
1799 else
1800 zRot = 0.0
1801 end
1802
1803 if Item.Token ~= nil then
1804 local zRotToken = Item.Token.getRotation().z
1805 if zRotToken < 230.0 and zRotToken > 150.0 then --it is probably meant to be upside down
1806 zRotToken = 180.0
1807 else
1808 zRotToken = 0.0
1809 end
1810 Item.Token.setRotation({0.0,180.0,zRotToken})
1811 Item.Token.setPosition({Item.vPos.x, Item.vPos.y + 0.115, Item.vPos.z})
1812 end
1813
1814 --rotate to the closest valid angle (default is 90.0 but in case someone wants to place river hexes we need to consider the current rotation)
1815 local yRot = Item.Hex.getRotation().y
1816 yRot = math.floor(yRot / 60) * 60 + 30
1817 Item.Hex.setRotation({0.0,yRot,zRot}) --make sure it is oriented properly to fit in with the other tiles
1818 if zRot == 180.0 then
1819 Item.Hex.setPosition({Item.vPos.x, 1.07, Item.vPos.z})
1820 else
1821 Item.Hex.setPosition(Item.vPos)
1822 Item.Hex.setLock(true)
1823 end
1824 end
1825
1826 return true
1827end
1828
1829--[[
1830 Map creation
1831]]--
1832--[[ createMap
1833 Parameters: PlacemInfo = list of all hexes to be used + parameters for placement of these hexes
1834
1835 Members of PlacemInfo:
1836 pos - XYZ vector -> coordinates to place the tiles in
1837
1838 sHexType - type of hex to spawn
1839 bRandHex - Flag if this tile should be randomized
1840 bFlipped - Start this tile upside down (to be explored by players)
1841 iNumToken - Number token to into this position by default (nil = do not place one on this pos/terrain hex)
1842
1843 vRot - orientation of the border piece
1844
1845 sHarborType - Type of the harbor tile to put in this position by default (nil = do not place one on this pos) values: 'generic','wool','lumber','grain','ore','bricks'
1846 bRandHarbor - Flag if this harbor should be randomized
1847 vHarborRot - Rotation for the harbor object
1848
1849 iGroup - Tile group - needed for seafarers scenarios where certain islands can be randomized but should always have the same resources
1850
1851 bRobberPos - Start the robber on this tile
1852]]--
1853function createMap( p )
1854 local PlacemInfo = p.PlacemInfo
1855
1856 --find position to place the stack of flipped tokens depending on the map size
1857 local vPosTokenStack = {30.0,1.0,-3.0}
1858 if m_MapScript ~= nil then
1859 vPosTokenStack[1] = ((m_MapScript.getScale().x / 2) + m_MapScript.getPosition().x) + 3.0
1860 end
1861
1862-- 1. Randomize Hexes/Numbers/Harbors which should be randomized
1863 shuffleHexes( PlacemInfo )
1864 shuffleChips( PlacemInfo )
1865 shuffleHarbors( PlacemInfo )
1866 if m_CurScenario.bUseFishermen then
1867 setFishTiles( PlacemInfo )
1868 end
1869
1870-- 2. Spawn objects
1871 local iTokenStack = 0
1872 for i, Tile in pairs(PlacemInfo) do
1873
1874 -- Spawn hex tile
1875 if Tile.sHexType == 'border' or Tile.sHexType == 'corner' or Tile.sHexType == 'side' then
1876 spawnBorderTile(Tile.sHexType, Tile.pos, Tile.vRot)
1877 elseif Tile.sHexType == 'lake' then
1878 spawnLake({vPosition = Tile.pos, bSecLake = Tile.bSecLake})
1879 elseif Tile.sHexType == 'castle' then
1880 spawnCastle(Tile.pos)
1881 else
1882 if m_bHiddenTiles and Tile.sHexType ~= 'ocean' then --hide all tiles option enabled
1883 Tile.bFlipped = true
1884 end
1885
1886 local HexTile = spawnHex({sType = Tile.sHexType, vPos = {Tile.pos.x,Tile.pos.y,Tile.pos.z}, vRot = Tile.vRot, bFlipped = Tile.bFlipped})
1887 if Tile.bRobberPos and Tile.bFlipped ~= true then
1888 local vPos = Tile.pos
1889 vPos.y = vPos.y + 1.0
1890 placeRobber({vPos = vPos})
1891 end
1892 end
1893
1894 -- Spawn number chips
1895 local Token = nil
1896 if Tile.iNumToken ~= nil then
1897 local vPos = Tile.pos
1898 vPos.y = 1.085
1899 if m_bHiddenNumbers or Tile.bFlipped then
1900 if Tile.bFlipped then
1901 --the tile this token belongs to is upside down -> spawn it next to the map upside down on a stack
1902 iTokenStack = iTokenStack + 1
1903 vPos.x = vPosTokenStack[1]
1904 --vPos.x = 22.0
1905 vPos.y = 1.0 + iTokenStack * 0.1 --when spawning stuff close together they seem to get pushed in the table .. strange ... this here give me very little "wiggle" though
1906 --vPos.z = -2.0
1907 vPos.z = vPosTokenStack[3]
1908 if iTokenStack > 20 then
1909 vPos.z = vPosTokenStack[3] - 2.1
1910 vPos.y = 1.0 + (iTokenStack - 20) * 0.1
1911 end
1912 end
1913 spawnHiddenNumberToken({iNum = Tile.iNumToken, vPos = vPos})
1914 else
1915 if Tile.iNumTokenSec ~= nil then --hex gets 2 number tokens (used with rivers of catan -> 1 tile with 2 and 12)
1916 spawnNumberToken( {iNum = Tile.iNumToken, vPosition = {x=vPos.x-0.7,y=vPos.y,z=vPos.z}, vScale = {1.35,1.5,1.35}, vRotation = {0.0,180.0,0.0} } )
1917 spawnNumberToken( {iNum = Tile.iNumTokenSec, vPosition = {x=vPos.x+0.7,y=vPos.y,z=vPos.z}, vScale = {1.35,1.5,1.35}, vRotation = {0.0,180.0,0.0} } )
1918 else
1919 spawnNumberToken( {iNum = Tile.iNumToken, vPosition = vPos, vRotation = {0.0,180.0,0.0} } )
1920 end
1921 end
1922 end
1923
1924 -- Spawn harbor tiles
1925 local HarborTile = nil
1926 if Tile.sHarborType ~= nil then
1927 local vPos = Tile.pos
1928 vPos.y = 1.073
1929 HarborTile = spawnHarborTile({sType=Tile.sHarborType, vPos=vPos, vRot=Tile.vHarborRot})
1930 HarborTile.setLock(true)
1931 end
1932
1933 -- Spawn fish grounds for Fishermen expansion
1934 if Tile.vFishRot ~= nil then
1935 local FishTile = nil
1936 local vPos = Tile.pos
1937 vPos.y = 1.073
1938 FishTile = spawnFishTile({vPos=vPos, vRot=Tile.vFishRot})
1939 FishTile.setLock(true)
1940
1941 --spawn the number token too
1942 if Tile.iNumTokenFish ~= nil then
1943 local vPos = Tile.pos
1944 vPos.y = 1.085
1945 spawnNumberTokenFish( {iNum = Tile.iNumTokenFish, vPosition = vPos, vRotation = {0.0,180.0,0.0}, FishTile = FishTile } )
1946 end
1947 end
1948 end
1949end
1950
1951function spawnHex(p)
1952 local bFlipped = p.bFlipped or false
1953 local bLock = (bFlipped ~= true) --keep flipped hexes unlocked
1954 if p.bLock ~= nil then --additional scenario 4 uses the bLock parameter to spawn some unlocked ocean tiles
1955 bLock = p.bLock
1956 end
1957 local vRot = p.vRot or {0.0,90.0,0.0}
1958 if bFlipped then
1959 vRot[3] = 180.0
1960 end
1961
1962 local params = {}
1963 params.callback_owner = Global
1964 params.callback = 'callbackHex'
1965 params.params = {vPos = p.vPos, vRot = vRot, bLock = bLock}
1966
1967 local HexDef = m_HexObjects[p.sType]
1968 local SpawnBag = getObjectFromGUID(m_HexBag)
1969 if m_CurScenario.bUseCaravans or m_CurScenario.bUseRivers or m_CurScenario.bUseBAttack or m_CurScenario.bUseTraders then
1970 if HexDef == nil then
1971 SpawnBag = getTaBBag()
1972 HexDef = SpawnBag.call('getHexDef', {sType = p.sType})
1973 end
1974 end
1975 if SpawnBag ~= nil and HexDef ~= nil then
1976 local iNext = HexDef.iNext + 1
1977 if iNext > #HexDef.Tile then
1978 iNext = 1
1979 end
1980
1981 params.guid = HexDef.Tile[iNext]
1982 HexDef.iNext = iNext
1983
1984 m_iSpawnedHexes = m_iSpawnedHexes + 1
1985 --Workaround as of v7.8: hex tiles must not touch when taken from the bag before being positioned by the callback (Please Berserk, give me scripting access to snap points :'( )
1986 --Make sure hex tiles to not touch each other after being taken out of their bags
1987 local fYoff = 0.25
1988 if m_iSpawnedHexes % 2 == 0 then --alternate the height so we can put them closer together (otherwise there is a noticable delay for hexes that are put far away from their bag)
1989 fYoff = -0.25
1990 end
1991 local iRow = math.floor(m_iSpawnedHexes / 10)
1992 local fZoff = 0.0
1993 fZoff = fZoff - (iRow * - 5.0)
1994 local vBagPos = SpawnBag.getPosition()
1995 local iRowHex = m_iSpawnedHexes - (iRow * 10)
1996 params.position = {vBagPos.x - iRowHex * 1.76, vBagPos.y + fYoff, vBagPos.z + fZoff}
1997 --/
1998
1999 local HexObj = takeObjectWorkaround(SpawnBag, params)
2000 HexObj.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'hex\') end')
2001 if HexObj.getDescription() == '' then
2002 HexObj.setDescription(HexObj.getName())
2003 end
2004 m_iLoadingHexes = m_iLoadingHexes + 1
2005
2006 --If the tile is spawned flipped the name/description must not give away what type of tile it is
2007 --so we give it some custom scripts to remove the name/description and reset it once it is flipped back (discovered)
2008 if bFlipped then
2009 local sName = HexObj.getDescription()
2010 if sName == '' then
2011 sName = HexObj.getName()
2012 end
2013 HexObj.setLuaScript([[ m_sDesc = ']] .. sName .. [['
2014 function onLoad(save_state) self.setVar('sObjType', 'hex') end
2015 function onCollisionEnter(collision_info)
2016 local zRot = self.getRotation().z
2017 if zRot < 230.0 and zRot > 150.0 then
2018 if self.getDescription() ~= '' then
2019 self.setDescription('')
2020 end
2021 elseif self.getDescription() == '' then
2022 self.setDescription(m_sDesc)
2023 end
2024 end]]
2025 )
2026 end
2027 end
2028end
2029
2030function spawnBorderTile(sType, vPosition, vRotation)
2031 local params = {}
2032 params.type = 'Custom_Model'
2033 params.position = {vPosition.x, 1.015, vPosition.z}
2034 params.rotation = vRotation
2035 params.callback = 'lockObject'
2036 params.callback_owner = Global
2037
2038 local custom = {}
2039 if sType == 'border' then
2040 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601380204690/A9DD85903F9977C30C680739868BED6BD0A11FAF/'
2041 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380209162/AE668F8B0369BBE7CE2D7F088AC9AEAB6143F086/'
2042 elseif sType == 'corner' then
2043 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601380205664/E08A940216107C4A1AFBB9295E192D449FFF6720/'
2044 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380209469/91F6A9E1947BA1154A6A62AA511B640147BD1A09/'
2045 elseif sType == 'side' then
2046 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/847089545433329233/1F25DBBB05E468E83C9B32F7EB53DB293EB42F18/'
2047 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/847089545433330296/F99FBF3D1ED01F6196F19FEB2ADC1BA353A4683D/'
2048 end
2049 custom.collider = custom.mesh
2050 custom.convex = false
2051 custom.type = 0
2052 custom.material = 1
2053
2054 local Tile = spawnObject(params)
2055 Tile.setCustomObject(custom)
2056 Tile.setDescription('Ocean')
2057
2058 --set a custom variable to identify the object type when the map is reset
2059 Tile.setVar('sObjType', 'border')
2060 Tile.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'border\') end') --keep the variable on rewind
2061
2062 return Tile
2063end
2064
2065function spawnDie(iDie, vPos, vRot)
2066 local params = {}
2067 params.type = 'Die_6'
2068 params.position = {vPos.x,vPos.y,vPos.z}
2069 params.rotation = vRot
2070 params.scale = {1.24999976, 1.24999976, 1.24999976}
2071
2072 local Die = spawnObject(params)
2073 Die.setColorTint({0.90588,0.82745,0.72157})
2074 Die.setLuaScript([[ m_bActive = false
2075 function enableDie() m_bActive = true end
2076 m_sLastPlayer = nil
2077 m_bInQueue = false
2078 function setQueueStatus(p) m_bInQueue = p.bActive end
2079 function onLoad(save_state) m_bActive = true end
2080 function onCollisionEnter(collision_info)
2081 if m_bInQueue == false then
2082 if m_bActive then
2083 m_bInQueue = Global.call('queueDieCheck', {DieObj = self})
2084 Global.call('registerNumberDie', {DieObj = self, iDie = ]] .. iDie .. [[})
2085 end
2086 end
2087 end
2088 function onPickUp(player_color)
2089 m_sLastPlayer = player_color
2090 local MapScript = getObjectFromGUID('42fd8e')
2091 if MapScript ~= nil then MapScript.call('hideDealButtonDie') end
2092 end
2093 function getLastPlayerColor() return m_sLastPlayer end
2094 ]])
2095 --[[TODO: enable if Berserk finally implements such an event
2096 function onRoll(player_color)
2097 m_sLastPlayer = player_color
2098 --hide the deal button if it is still visible
2099 local MapScript = getObjectFromGUID('42fd8e')
2100 if MapScript ~= nil then
2101 MapScript.call('hideDealButtonDie')
2102 end
2103 end
2104 --]]
2105 return Die
2106end
2107--[[ Spawn custom event die for Cities & Knights
2108]]
2109function spawnEventDie(vPos)
2110 local params = {}
2111 params.type = 'Custom_Dice'
2112 params.position = vPos
2113 params.rotation = {0.0,0.0,0.0}
2114 params.scale = {1.24999976, 1.24999976, 1.24999976}
2115 local custom = {
2116 type = 1, --6 sided
2117 image = 'http://cloud-3.steamusercontent.com/ugc/155773601379893824/EC932B85B64F5B484FDF35B18B0E7AFE066F1293/'
2118 }
2119
2120 local Die = spawnObject(params)
2121 Die.setCustomObject(custom)
2122 Die.setLuaScript([[ m_bActive = false
2123 function enableDie() m_bActive = true end
2124 m_bInQueue = false
2125 function setQueueStatus(p) m_bInQueue = p.bActive end
2126 function onLoad(save_state) m_bActive = true end
2127 function onCollisionEnter(collision_info)
2128 if m_bInQueue == false then
2129 if m_bActive then
2130 m_bInQueue = Global.call('queueDieCheck', {DieObj = self})
2131 Global.call('registerNumberDie', {DieObj = self, iDie = 3})
2132 end
2133 end
2134 end
2135 ]])
2136 return Die
2137end
2138
2139function spawnHarborTile(p)
2140 local params = {}
2141 params.type = 'Custom_Model'
2142 params.position = {p.vPos.x, p.vPos.y, p.vPos.z}
2143 params.rotation = p.vRot
2144
2145 local custom = {}
2146 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379896629/5FBC5A99616B48F733C49103F95F80D1ABA676D8/'
2147 custom.collider = custom.mesh
2148 if p.sType == 'generic' then
2149 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380215722/13D5E6345E5F179AC0E4B39B0E8E03B4146CEF82/'
2150 elseif p.sType == 'bricks' then
2151 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380214901/8B907632A00BA41614D1E3DA7D7EF49EA1604E1F/'
2152 elseif p.sType == 'grain' then
2153 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380216515/9E2813DD7E2CF9553AFAC42449B14AE653EFDC4C/'
2154 elseif p.sType == 'lumber' then
2155 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380217126/9563E370591EE97786DB2BDBC57E5C48BCF91D73/'
2156 elseif p.sType == 'ore' then
2157 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380217927/17FDA65567BF05F63CF0780540D13C580F227B39/'
2158 elseif p.sType == 'wool' then
2159 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380218943/98A114E362A57FFF06BE329661C811FB1135CC95/'
2160 end
2161 custom.type = 0 --generic
2162 custom.material = 1 --wood
2163
2164 local HarborObj = spawnObject(params)
2165 HarborObj.setCustomObject(custom)
2166
2167 if p.bNoLock == true then
2168 addToUnlockList({Obj = HarborObj})
2169 end
2170
2171 HarborObj.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'harbor\') end') --keep the variable on rewind
2172
2173 if p.sType == 'generic' then
2174 HarborObj.setName('Any 3 : 1')
2175 HarborObj.setDescription('You may exchange 3 identical resource cards for any 1 other resource card during your turn.')
2176 elseif p.sType == 'wool' then
2177 HarborObj.setName('Wool 2 : 1')
2178 HarborObj.setDescription('You may exchange 2 Wool resource cards for any 1 other resource card during your turn.')
2179 elseif p.sType == 'lumber' then
2180 HarborObj.setName('Lumber 2 : 1')
2181 HarborObj.setDescription('You may exchange 2 Lumber resource cards for any 1 other resource card during your turn.')
2182 elseif p.sType == 'bricks' then
2183 HarborObj.setName('Brick 2 : 1')
2184 HarborObj.setDescription('You may exchange 2 Bricks resource cards for any 1 other resource card during your turn.')
2185 elseif p.sType == 'grain' then
2186 HarborObj.setName('Grain 2 : 1')
2187 HarborObj.setDescription('You may exchange 2 Grain resource cards for any 1 other resource card during your turn.')
2188 elseif p.sType == 'ore' then
2189 HarborObj.setName('Ore 2 : 1')
2190 HarborObj.setDescription('You may exchange 2 Ore resource cards for any 1 other resource card during your turn.')
2191 end
2192
2193 return HarborObj
2194end
2195
2196function spawnFishTile(p)
2197 local params = {}
2198 params.type = 'Custom_Model'
2199 params.position = {p.vPos.x, p.vPos.y, p.vPos.z}
2200 params.rotation = p.vRot
2201
2202 local custom = {}
2203 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379899883/77EF7A4AE8268E8DA3553A4FA4457000507CB5B2/'
2204 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379902556/884151AB27FD9EA3F96B222C5CF96EA059AE56CB/'
2205 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379904511/62647B9C144D291B436069F2A819C468D1130D75/'
2206 custom.type = 0 --generic
2207 custom.material = 1 --wood
2208
2209 local FishTile = spawnObject(params)
2210 FishTile.setCustomObject(custom)
2211
2212 if p.bNoLock == true then
2213 addToUnlockList({Obj = FishTile})
2214 end
2215
2216 --set a custom variable to identify the object type when the map is reset
2217 FishTile.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'fishermen\') end') --keep the variable on rewind
2218
2219 return FishTile
2220end
2221function spawnLake(p)
2222 local params = {}
2223 params.type = 'Custom_Tile'
2224 params.position = {p.vPosition.x, 0.96, p.vPosition.z}
2225 params.rotation = {0.0,90.0,0.0}
2226 params.scale = {1.735, 1.0, 2.0}
2227 local custom = {}
2228 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379906588/A6AD5B05B94BABDCB8F1A4121E5C3003CC2A4277/'
2229 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601379910328/F0F3F849353CEADE84EFD7EBC9633E2D515D065B/'
2230 custom.thickness = 0.1
2231 custom.type = 1
2232
2233 local Tile = spawnObject(params)
2234 Tile.setCustomObject(custom)
2235 Tile.setDescription('Lake')
2236 Tile.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'hex\') end')
2237 Tile.setColorTint({0.99,0.906,0.62})
2238 if p.bLock ~= false then
2239 Tile.setLock(true)
2240 end
2241
2242 --Spawn the number tokens for the lake
2243 local Tokens = {}
2244 if p.bSecLake == true then --secondary lake for 5-6 players with different number tokens
2245 table.insert(Tokens, spawnNumberTokenFish( {iNum = 10, vPosition = {x=p.vPosition.x-0.5,y=1.075,z=p.vPosition.z}, vRotation = {0.0,180.0,0.0}} ))
2246 table.insert(Tokens, spawnNumberTokenFish( {iNum = 4, vPosition = {x=p.vPosition.x+0.5,y=1.075,z=p.vPosition.z}, vRotation = {0.0,180.0,0.0}} ))
2247 else
2248 table.insert(Tokens, spawnNumberTokenFish( {iNum = 11, vPosition = {x=p.vPosition.x-0.5,y=1.075,z=p.vPosition.z+0.5}, vRotation = {0.0,180.0,0.0}} ))
2249 table.insert(Tokens, spawnNumberTokenFish( {iNum = 12, vPosition = {x=p.vPosition.x+0.5,y=1.075,z=p.vPosition.z+0.5}, vRotation = {0.0,180.0,0.0}} ))
2250 table.insert(Tokens, spawnNumberTokenFish( {iNum = 2, vPosition = {x=p.vPosition.x-0.5,y=1.075,z=p.vPosition.z-0.5}, vRotation = {0.0,180.0,0.0}} ))
2251 table.insert(Tokens, spawnNumberTokenFish( {iNum = 3, vPosition = {x=p.vPosition.x+0.5,y=1.075,z=p.vPosition.z-0.5}, vRotation = {0.0,180.0,0.0}} ))
2252 end
2253
2254 if p.bLock == false then
2255 for i, Token in ipairs(Tokens) do
2256 addToUnlockList({Obj = Token})
2257 Token.use_snap_points = false
2258 end
2259 end
2260
2261 return Tile
2262end
2263
2264function spawnCastle(vPosition)
2265 local params = {}
2266 params.type = 'Custom_Tile'
2267 params.position = {vPosition.x, 0.96, vPosition.z}
2268 params.rotation = {0.0,90.0,0.0}
2269 params.scale = {1.735, 1.0, 2.0}
2270 local custom = {}
2271 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379912853/A299D139509BD49544CE5E5029B2261D26442D4E/'
2272 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601379910328/F0F3F849353CEADE84EFD7EBC9633E2D515D065B/'
2273 custom.thickness = 0.1
2274 custom.type = 1
2275
2276 local Tile = spawnObject(params)
2277 Tile.setCustomObject(custom)
2278 Tile.setDescription('Castle')
2279 Tile.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'hex\') end')
2280 Tile.setColorTint({0.99,0.906,0.62})
2281 Tile.setLock(true)
2282
2283 if m_bColorDieSpawned == false then
2284 local params = {}
2285 params.type = 'Custom_Dice'
2286 params.position = {vPosition.x, 2.0, vPosition.z}
2287 params.rotation = {0.0,-90.0,0.0}
2288 params.scale = {1.24999976, 1.24999976, 1.24999976}
2289
2290 local Die = spawnObject(params)
2291 Die.setCustomObject({image = 'http://cloud-3.steamusercontent.com/ugc/155773601379914796/5C44A4A31E8ACF20DBDABF7F19B86D8EDBF077CC/'})
2292 Die.setName('Color Die')
2293 Die.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'battack\') end')
2294 m_bColorDieSpawned = true
2295 end
2296
2297 return Tile
2298end
2299
2300function lockObject( Tile )
2301 Tile.setLock(true)
2302end
2303
2304function spawnTABBarbarian(p)
2305 local params = {}
2306 params.type = 'Custom_Model'
2307 params.position = p.vPos
2308 params.rotation = p.vRot
2309 local custom = {}
2310 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379916581/CC5F1A1245557667D4C8A638D33EDACDD211776B/'
2311 custom.diffuse = ''
2312 custom.type = 1 --figurine
2313 custom.material = 1 --wood
2314 local Barbarian = spawnObject(params)
2315 Barbarian.setCustomObject(custom)
2316 Barbarian.setName('Barbarian')
2317 Barbarian.setColorTint({0.745,0.608,0.4})
2318 return Barbarian
2319end
2320
2321function spawnHiddenNumberToken(p)
2322 Token = spawnNumberToken( {iNum = p.iNum, vPosition = p.vPos, vScale = p.vScale, vRotation = {0.0,180.0,180.0}} )
2323 local sScript = 'm_sName = \'' .. tostring(p.iNum) .. '\' ' ..
2324 Token.getLuaScript() .. --onload event & registration with scriptzone map
2325 --Routine to hide the object name while upside down
2326 ' function onCollisionEnter(collision_info) ' ..
2327 'local zRot = self.getRotation().z ' ..
2328 'if zRot < 230.0 and zRot > 150.0 then ' ..
2329 'if self.getName() ~= \'\' then self.setName(\'\') end ' ..
2330 'elseif self.getName() == \'\' then ' ..
2331 'self.setName(m_sName) ' ..
2332 'end ' ..
2333 'end'
2334 Token.setLuaScript(sScript)
2335 Token.setName('')
2336
2337 addToUnlockList({Obj = Token})
2338
2339 return Token
2340end
2341
2342function spawnNumberToken( p )
2343 local params = {}
2344 params.type = 'Custom_Model'
2345 params.position = {p.vPosition.x, p.vPosition.y, p.vPosition.z}
2346 params.rotation = p.vRotation
2347 params.scale = {1.5, 1.5, 1.5}
2348 if p.vScale ~= nil then
2349 params.scale = p.vScale
2350 end
2351
2352 local custom = {}
2353 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379918528/3DCDB66BED81A36614EE1ADDF4EE9BAC28F3F581/'
2354 if p.iNum == 2 then
2355 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380226244/D1B2EE97E84DEE989B29A73D9BEF7863C9C38DC5/'
2356 elseif p.iNum == 3 then
2357 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380226640/9EC721CFC6B970FA037495CD5FC2945669ECF5FE/'
2358 elseif p.iNum == 4 then
2359 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380226972/08D00021E63566357B54B42F96603C818D22ABCD/'
2360 elseif p.iNum == 5 then
2361 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380227302/EC88CC96CDB45A4DD1CD4926EB040CAB07206B1E/'
2362 elseif p.iNum == 6 then
2363 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380227728/A8C37F7BBA9AAD1059063731CC5E09BE1B3C954B/'
2364 elseif p.iNum == 8 then
2365 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380228044/668F73AFD5E1166AD5136756E0A857E67FA2B2A9/'
2366 elseif p.iNum == 9 then
2367 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380228423/A9138776CAF26E2B390E14D795F8CD203D3E1A7D/'
2368 elseif p.iNum == 10 then
2369 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380228887/27E401D034ED756729D0936F72350F6BA5F2A0A5/'
2370 elseif p.iNum == 11 then
2371 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380229190/7AC8D6001FB37322A545E77FA748862B1C303E29/'
2372 elseif p.iNum == 12 then
2373 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380229536/7352353D5C5DE259F280D323FB32929DD2C857D7/'
2374 end
2375 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379920973/0B3B12438279975756D988CC1F7756D3E152F2D7/'
2376 custom.type = 0 --generic
2377 custom.material = 3 --cardboard
2378
2379 local Token = spawnObject(params)
2380 Token.setName(p.iNum)
2381 Token.setCustomObject(custom)
2382
2383 --set a custom variable to identify the object type when the map is reset
2384 Token.setVar('sObjType', 'numtoken')
2385 Token.setLuaScript('function onLoad() ' ..
2386 'self.setVar(\'sObjType\', \'numtoken\') ' .. --keep the variable on rewind
2387 'self.setVar(\'iNum\', ' .. p.iNum .. ') ' ..
2388 'startLuaCoroutine(self, \'registerToken\') ' .. --Workaround: onLoad is called before the new object has its own GUID (when copied it still has the GUID of the source object...) -> wait a frame and then register the new token
2389 'end ' ..
2390 'function registerToken() ' ..
2391 'coroutine.yield(0) ' ..
2392 'local MapScript = getObjectFromGUID(\'42fd8e\') ' ..
2393 'if MapScript ~= nil then ' ..
2394 'MapScript.call(\'addNumberChip\', { TokenObj = self, iNum = self.getVar(\'iNum\') }) ' ..
2395 'return true ' ..
2396 'end ' ..
2397 'return false ' ..
2398 'end')
2399
2400 if m_CurScenario.bUseBAttack then --when tokens are turned upside down in this scenario check if any buildings get blocked from it
2401 Token.setLuaScript( Token.getLuaScript() ..
2402 [[
2403 function onCollisionEnter(collision_info)
2404 if collision_info ~= nil then
2405 if collision_info.collision_object.getVar('sObjType') == 'hex' then
2406 local TaBBag = Global.call('getTaBBag')
2407 if TaBBag ~= nil then
2408 TaBBag.call('checkBarbarianBlocks')
2409 end
2410 end
2411 end
2412 end
2413 ]]
2414 )
2415 end
2416
2417 Token.setLock(true)
2418
2419 return Token
2420end
2421
2422function spawnNumberTokenFish(p)
2423 local params = {}
2424 params.type = 'Custom_Model'
2425 params.position = {p.vPosition.x, p.vPosition.y, p.vPosition.z}
2426 params.rotation = p.vRotation
2427 params.scale = {0.95, 1.0, 0.95}
2428 if p.vScale ~= nil then
2429 params.scale = p.vScale
2430 end
2431 params.callback = 'callbackNumberTokenFish'
2432 params.callback_owner = Global
2433 params.params = { vScale = params.scale, FishTile = p.FishTile }
2434
2435 local custom = {}
2436 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379918528/3DCDB66BED81A36614EE1ADDF4EE9BAC28F3F581/'
2437 if p.iNum == 2 then
2438 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380226244/D1B2EE97E84DEE989B29A73D9BEF7863C9C38DC5/'
2439 elseif p.iNum == 3 then
2440 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380226640/9EC721CFC6B970FA037495CD5FC2945669ECF5FE/'
2441 elseif p.iNum == 4 then
2442 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380226972/08D00021E63566357B54B42F96603C818D22ABCD/'
2443 elseif p.iNum == 5 then
2444 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380227302/EC88CC96CDB45A4DD1CD4926EB040CAB07206B1E/'
2445 elseif p.iNum == 6 then
2446 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380227728/A8C37F7BBA9AAD1059063731CC5E09BE1B3C954B/'
2447 elseif p.iNum == 8 then
2448 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380228044/668F73AFD5E1166AD5136756E0A857E67FA2B2A9/'
2449 elseif p.iNum == 9 then
2450 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380228423/A9138776CAF26E2B390E14D795F8CD203D3E1A7D/'
2451 elseif p.iNum == 10 then
2452 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380228887/27E401D034ED756729D0936F72350F6BA5F2A0A5/'
2453 elseif p.iNum == 11 then
2454 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380229190/7AC8D6001FB37322A545E77FA748862B1C303E29/'
2455 elseif p.iNum == 12 then
2456 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380229536/7352353D5C5DE259F280D323FB32929DD2C857D7/'
2457 end
2458 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379920973/0B3B12438279975756D988CC1F7756D3E152F2D7/'
2459 custom.type = 0 --generic
2460 custom.material = 3 --cardboard
2461
2462 local Token = spawnObject(params)
2463 Token.setName(p.iNum)
2464 Token.setCustomObject(custom)
2465 Token.setColorTint({0.305882335,0.499539942,1.0})
2466
2467 Token.setLuaScript([[ m_sGUIDFishTile = nil
2468 function setFishTileGUID(p) m_sGUIDFishTile = p.sGUID end
2469 function getFishTileGUID() return m_sGUIDFishTile end
2470 function onSave()
2471 return JSON.encode({sGUIDFishTile = m_sGUIDFishTile})
2472 end
2473 function onLoad(saved_state)
2474 self.setVar('sObjType', 'numtoken')
2475 self.setVar('bFishToken', true)
2476 self.setVar('iNum', ]] .. p.iNum .. [[)
2477 local saved_data = JSON.decode(saved_state)
2478 if saved_data ~= nil then
2479 m_sGUIDFishTile = saved_data.sGUIDFishTile
2480 end
2481 startLuaCoroutine(self, 'registerToken')
2482 end
2483 function registerToken()
2484 --workaround: when a token is copied it will have the guid of the original item initially -> wait 1 frame
2485 coroutine.yield(0)
2486 local MapScript = getObjectFromGUID('42fd8e')
2487 if MapScript ~= nil then
2488 MapScript.call('addNumberChip', { TokenObj = self, iNum = self.getVar('iNum') })
2489 return true
2490 end
2491 return false
2492 end
2493 function onPickUp(player_color)
2494 m_sGUIDFishTile = nil --invalidate associated tile and find new one
2495 end
2496 function onCollisionEnter(collision_info)
2497 if m_sGUIDFishTile == nil then
2498 m_sGUIDFishTile = getFishTile()
2499 end
2500 end
2501 function getFishTile()
2502 local AllObj = getAllObjects()
2503 local vPos = self.getPosition()
2504 for i, Obj in ipairs(AllObj) do
2505 if Obj.getVar('sObjType') == 'fishermen' then
2506 local fDist = math.abs(Obj.getPosition().x - vPos.x) + math.abs(Obj.getPosition().z - vPos.z)
2507 if fDist < 1.5 then
2508 return Obj.getGUID()
2509 end
2510 end
2511 end
2512 end]]
2513 )
2514 Token.setLock(true)
2515
2516 return Token
2517end
2518function callbackNumberTokenFish(Object, p)
2519 if p.FishTile ~= nil then --set GUID of associated fish tile in the callback since the fish tile object does not have a GUID earlier
2520 Object.call('setFishTileGUID', {sGUID = p.FishTile.getGUID()})
2521 end
2522end
2523
2524--[[ spawn treasure tokens for TDD and additional scenarios
2525 Called from the respective control panel that defines the layout
2526--]]
2527function spawnTreasureToken(p)
2528 local params = {}
2529 params.type = 'Custom_Tile'
2530 params.position = p.vPos
2531 params.rotation = {0.0,180.0,0.0}
2532 params.scale = {0.45, 1.0, 0.45}
2533
2534 local custom = {}
2535 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379924745/1741D18558247F22A7DF9AD4317BE5701C798B69/'
2536 if p.sBack == '1res' then
2537 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380238738/7212F420BC6BFE34D9C9E5D912644304E794868E/'
2538 elseif p.sBack == '2res' then
2539 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380240691/C9E5CEABB085A5B87D34A6B6AEC037DA75CC88EB/'
2540 elseif p.sBack == '3res' then
2541 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380241222/822EF373AA3E68D059F1E7FE6351125D1051690D/'
2542 elseif p.sBack == 'dev' then
2543 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380241827/611419F9D4152FF0C394FC3D6FC60E3824C7904C/'
2544 elseif p.sBack == 'roads' then
2545 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380242274/A3FAA5B5A4C46E2A93B5B543B4B7042706FAEBA3/'
2546 elseif p.sBack == 'robber' then
2547 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380243237/D8065F9548E0183F0080FA7064C9379CAFA73757/'
2548 elseif p.sBack == 'vp' then
2549 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380244249/9A58840F1DD7F84E20C1019FC61A509352574138/'
2550 end
2551 custom.type = 0 --Box
2552 custom.thickness = 0.06
2553
2554 local Token = spawnObject(params)
2555 Token.setCustomObject(custom)
2556
2557 Token.setColorTint({0.93,0.53,0.0})
2558 Token.setName('Treasure Chest')
2559 local sDesc = ''
2560 if sBack == '1res' then
2561 sDesc = 'You may take any 1 resource from the bank'
2562 elseif sBack == '2res' then
2563 sDesc = 'You may take any 2 resources from the bank'
2564 elseif sBack == '3res' then
2565 sDesc = 'You get 1 Grain, 1 Wool, 1 Bricks'
2566 elseif sBack == 'dev' then
2567 sDesc = 'You may take 1 Development card (when playing with Cities & Knights: 1 Progress card)'
2568 elseif sBack == 'roads' then
2569 sDesc = 'You may build 2 roads/ships for free'
2570 end
2571 Token.setLuaScript('m_sDesc = \'' .. sDesc .. '\'\n' ..
2572 'function onCollisionEnter(collision_info) ' ..
2573 'local zRot = self.getRotation().z ' ..
2574 'if zRot < 0.0 then zRot = 360.0 + zRot end ' ..
2575 'if zRot < 230.0 and zRot > 130.0 then ' .. --it's facing down
2576 'if self.getDescription() == \'\' then ' ..
2577 'self.setDescription(m_sDesc) ' ..
2578 'end ' ..
2579 'else ' ..
2580 'if self.getDescription() ~= \'\' then ' ..
2581 'self.setDescription(\'\') ' ..
2582 'end ' ..
2583 'end ' ..
2584 'end ')
2585
2586 Token.setLock(true)
2587 Global.call('addToUnlockList', {Obj = Token})
2588
2589 return Token
2590end
2591
2592--[[ Spawn victory point card Longest Road ]]--
2593function spawnLongestRoadCard(vPos, vRot)
2594 local params = {}
2595 params.type = 'Custom_Tile'
2596 params.position = vPos
2597 params.rotation = vRot
2598 params.scale = {2.11755562,1.0,2.11755562}
2599 local custom = {}
2600 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379927983/C7109CE55B4403815B5515C29EC5E3B9E534FEEC/'
2601 custom.type = 0 --Box
2602 custom.thickness = 0.06
2603
2604 local Tile = spawnObject(params)
2605 Tile.setCustomObject(custom)
2606 Tile.setName('Longest Road')
2607 Tile.setDescription('2 Victory Points!\nThis card goes to the player with the longest unbroken road of at least 5 segments. Another player who builds a longer road takes this card.')
2608 Tile.setLock(true)
2609 addToUnlockList({Obj = Tile})
2610end
2611
2612--[[ Spawn victory point card Largest Army ]]--
2613function spawnLargestArmyCard(vPos, vRot)
2614 local params = {}
2615 params.type = 'Custom_Tile'
2616 params.position = vPos
2617 params.rotation = vRot
2618 params.scale = {2.11755562,1.0,2.11755562}
2619 local custom = {}
2620 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379932520/2B23103F9A5E74B60009817693B851FDF4D980B0/'
2621 custom.type = 0 --Box
2622 custom.thickness = 0.06
2623
2624 local Tile = spawnObject(params)
2625 Tile.setCustomObject(custom)
2626 Tile.setName('Largest Army')
2627 Tile.setDescription('2 Victory Points!\nThe first player to play 3 Knight cards gets this card. Another player who plays more Knight cards takes this card.')
2628 Tile.setLock(true)
2629 addToUnlockList({Obj = Tile})
2630end
2631
2632--[[ Spawn victory point card Harbormaster ]]--
2633function spawnHarbormasterCard(vPos, vRot)
2634 local params = {}
2635 params.type = 'Custom_Tile'
2636 params.position = vPos
2637 params.rotation = vRot
2638 params.scale = {2.11755562,1.0,2.11755562}
2639 local custom = {}
2640 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379935112/3E2BFEAEF079A9B4F94E071DE363B7BC3646D03A/'
2641 custom.type = 0 --Box
2642 custom.thickness = 0.06
2643
2644 local Tile = spawnObject(params)
2645 Tile.setCustomObject(custom)
2646 Tile.setName('Harbormaster')
2647 Tile.setDescription('2 Victory Points!\nThe first player to acquire 3 harbor points gets this card. Another player who acquires more harbor points takes this card.')
2648 Tile.setLock(true)
2649 addToUnlockList({Obj = Tile})
2650end
2651
2652--[[ Randomize terrain hexes in the placement info table ]]--
2653function shuffleHexes( InfoTab )
2654 -- Create sequential table with all indexes of InfoTab that can be randomized
2655 local Keys = {}
2656 for i, Line in pairs(InfoTab) do
2657 if Line.bRandHex then
2658 table.insert(Keys, i)
2659 end
2660 end
2661
2662 if #Keys <= 1 then
2663 return
2664 end
2665
2666 for i = #Keys, 2, -1 do
2667 local iRand = nil
2668 local iRUpper = i
2669 local iCount = 0
2670 while true do
2671 iCount = iCount + 1
2672 iRand = math.random(iRUpper)
2673 if InfoTab[Keys[iRand]].iGroup == InfoTab[Keys[i]].iGroup then --allow tile groups to be shuffled only amongst each other
2674 local bSwitch = true
2675 if m_CurScenario.bUseFishermen == true then --make sure the lake tile is not put next to the ocean
2676 if InfoTab[Keys[i]].sHexType == 'lake' then --check if the new position is adjacent to water
2677 local OceanTiles = getOceanNeighbors(InfoTab, Keys[iRand])
2678 if #OceanTiles == 1 then --check if the adjacent ocean tile is the tile we are moving right now
2679 if OceanTiles[1].index == Keys[i] --it is the tile we are moving
2680 and InfoTab[Keys[iRand]].sHexType ~= 'ocean' then --do not allow if we are switching with an ocean tile
2681 else
2682 bSwitch = false
2683 end
2684 elseif #OceanTiles > 0 then
2685 bSwitch = false
2686 end
2687
2688 elseif InfoTab[Keys[iRand]].sHexType == 'lake' then --check if the current hex is adjacent to ocean (is it a valid position for a lake)
2689 local OceanTiles = getOceanNeighbors(InfoTab, Keys[i])
2690 if #OceanTiles == 1 then
2691 if OceanTiles[1].index == Keys[iRand] and InfoTab[Keys[i]].sHexType ~= 'ocean' then
2692 else
2693 bSwitch = false
2694 end
2695 elseif #OceanTiles > 0 then
2696 bSwitch = false
2697 end
2698 end
2699 end
2700 if bSwitch then
2701 break
2702 end
2703 end
2704 --when looking for a spot for lake tiles it might happen that the last two positions are lakes
2705 --> just switch it with any hex
2706 if iCount >= #Keys then
2707 iRUpper = #Keys
2708 end
2709 end
2710
2711 if (InfoTab[Keys[i]].bRandNum ~= false and InfoTab[Keys[iRand]].bRandNum ~= false)
2712 or (InfoTab[Keys[i]].iNumToken ~= nil and InfoTab[Keys[iRand]].iNumToken ~= nil) then --do not switch a hex with a fixed number token with one that does not have a token (e.g. desert)
2713 InfoTab[Keys[i]].sHexType, InfoTab[Keys[iRand]].sHexType = InfoTab[Keys[iRand]].sHexType, InfoTab[Keys[i]].sHexType
2714 end
2715 if InfoTab[Keys[i]].bRandNum ~= false and InfoTab[Keys[iRand]].bRandNum ~= false then --do not switch the number tokens, they are supposed to be fixed
2716 InfoTab[Keys[i]].iNumToken, InfoTab[Keys[iRand]].iNumToken = InfoTab[Keys[iRand]].iNumToken, InfoTab[Keys[i]].iNumToken
2717 InfoTab[Keys[i]].iNumTokenSec, InfoTab[Keys[iRand]].iNumTokenSec = InfoTab[Keys[iRand]].iNumTokenSec, InfoTab[Keys[i]].iNumTokenSec
2718 end
2719 InfoTab[Keys[i]].bRobberPos, InfoTab[Keys[iRand]].bRobberPos = InfoTab[Keys[iRand]].bRobberPos, InfoTab[Keys[i]].bRobberPos
2720 InfoTab[Keys[i]].bSecLake, InfoTab[Keys[iRand]].bSecLake = InfoTab[Keys[iRand]].bSecLake, InfoTab[Keys[i]].bSecLake
2721 InfoTab[Keys[i]].sOilSource, InfoTab[Keys[iRand]].sOilSource = InfoTab[Keys[iRand]].sOilSource, InfoTab[Keys[i]].sOilSource
2722 end
2723end
2724
2725--check the tiles adjacent to a given position index and return a table with all tiles that are ocean
2726function getOceanNeighbors(PlacemTable, idx)
2727 local OceanList = {}
2728 for sDir, iTile in pairs(NBRS[idx]) do
2729 if PlacemTable[iTile] ~= nil then
2730 local sType = PlacemTable[iTile].sHexType
2731 if sType == 'ocean' or sType == 'border' or sType == 'corner' or sType == 'side' or sType == 'lake' then
2732 table.insert(OceanList,{index = iTile, sDir = sDir})
2733 end
2734 end
2735 end
2736 return OceanList
2737end
2738
2739--[[ Randomize number chips assigned to terrain hexes
2740 This function will also make sure there are no two red numbers (6, 8) next to each other
2741 ]]--
2742function shuffleChips( InfoTab )
2743 -- Create sequential table with all indexes of InfoTab that can be randomized
2744 local Keys = {}
2745 for iIdx, Line in pairs(InfoTab) do
2746 if Line.iNumToken ~= nil and ((Line.bRandHex and Line.bRandNum ~= false) or Line.bRandNum == true) then
2747 table.insert(Keys, iIdx)
2748 end
2749 end
2750
2751 if #Keys <= 1 then
2752 return
2753 end
2754
2755 -- Randomize the numbers
2756 for i = #Keys, 2, -1 do
2757 while true do
2758 local iRand = math.random(i)
2759 if InfoTab[Keys[iRand]].iGroup == InfoTab[Keys[i]].iGroup then --allow tile groups to be shuffled only amongst each other
2760 InfoTab[Keys[i]].iNumToken, InfoTab[Keys[iRand]].iNumToken = InfoTab[Keys[iRand]].iNumToken, InfoTab[Keys[i]].iNumToken
2761 InfoTab[Keys[i]].iNumTokenSec, InfoTab[Keys[iRand]].iNumTokenSec = InfoTab[Keys[iRand]].iNumTokenSec, InfoTab[Keys[i]].iNumTokenSec
2762 break
2763 end
2764 end
2765 end
2766
2767 -- Check for neighboring red numbers and replace one of them with a black token
2768 for i=1, #Keys do
2769 if isChipRed( InfoTab[Keys[i]].iNumToken ) and InfoTab[Keys[i]].bRandNum ~= false then
2770 --check if the chip has any red neighbor chips
2771 local bSwitch = false
2772 for sDir, iNbIdx in pairs(NBRS[Keys[i]]) do
2773 if InfoTab[iNbIdx] ~= nil then
2774 if isChipRed( InfoTab[iNbIdx].iNumToken ) then
2775 bSwitch = true
2776 break
2777 end
2778 end
2779 end
2780
2781 if bSwitch then
2782 replaceRedChip( InfoTab, Keys, Keys[i] )
2783 end
2784 end
2785 end
2786end
2787
2788--[[ Randomize harbors defined in the map tile placement info table ]]--
2789function shuffleHarbors( InfoTab )
2790 -- Create sequential table with all indexes of InfoTab that can be randomized
2791 local Keys = {}
2792 for i, Line in pairs(InfoTab) do
2793 if Line.sHarborType ~= nil and Line.bRandHarbor then
2794 table.insert(Keys, i)
2795 end
2796 end
2797
2798 if #Keys <= 1 then
2799 return
2800 end
2801
2802 for i = #Keys, 2, -1 do
2803 local iRand = nil
2804 while true do
2805 iRand = math.random(i)
2806 if InfoTab[Keys[iRand]].iGroup == InfoTab[Keys[i]].iGroup then --allow tile groups to be shuffled only amongst each other
2807 break
2808 end
2809 end
2810
2811 InfoTab[Keys[i]].sHarborType, InfoTab[Keys[iRand]].sHarborType = InfoTab[Keys[iRand]].sHarborType, InfoTab[Keys[i]].sHarborType
2812 end
2813end
2814
2815--[[ Define in which positions to spawn fish tiles depending on the position of the land tiles & assign token numbers ]]--
2816function setFishTiles( InfoTab )
2817 local FishTiles = {}
2818 -- Create sequential table with all indexes of InfoTab that can be randomized
2819 local Keys = {}
2820 for i, Line in pairs(InfoTab) do
2821 if Line.vFishRot ~= nil then
2822 if Line.bVarFish == true then
2823 table.insert(Keys, i)
2824 else
2825 table.insert(FishTiles, i)
2826 end
2827 end
2828 end
2829
2830 --randomize the order of variable fish tiles
2831 if #Keys > 0 then
2832 for i = #Keys, 2, -1 do
2833 local iRand = math.random(i)
2834 Keys[i], Keys[iRand] = Keys[iRand], Keys[i]
2835 end
2836 end
2837
2838 local iTodo = 6
2839 local Numbers = {4,5,6,8,9,10}
2840 if m_CurScenario.bExtension == true then
2841 iTodo = 8
2842 Numbers = {4,5,6,8,9,10,5,9}
2843 end
2844 iTodo = iTodo - #FishTiles
2845
2846 --randomize the order of numbers
2847 for i = #Numbers, 2, -1 do
2848 local iRand = math.random(i)
2849 Numbers[i], Numbers[iRand] = Numbers[iRand], Numbers[i]
2850 end
2851
2852 --check which of the possible fish tile locations are valid
2853 for iRun = 1, 2 do
2854 local bOptimize = true
2855 if iRun == 2 then
2856 bOptimize = false --we tried positioning all fish tiles but did not find a valid spot for all -> check again without min spacing
2857 end
2858 for i, iKey in pairs(Keys) do
2859 if iTodo == 0 then break end
2860
2861 local sType = InfoTab[iKey].sHexType
2862 if sType == 'ocean' or sType == 'border' or sType == 'corner' then --tile itself must be an ocean tile
2863 local OceanTiles = getOceanNeighbors(InfoTab, iKey)
2864 local TestDir = {}
2865 local fOrient = InfoTab[iKey].vFishRot[2]
2866 if fOrient == 180.0 then --N
2867 TestDir = {'NW','NE'}
2868 elseif fOrient == -120.0 then --NE
2869 TestDir = {'NE', 'E'}
2870 elseif fOrient == -60.0 then --SE
2871 TestDir = {'E', 'SE'}
2872 elseif fOrient == 0.0 then --S
2873 TestDir = {'SE', 'SW'}
2874 elseif fOrient == 60.0 then --SW
2875 TestDir = {'SW', 'W'}
2876 elseif fOrient == 120.0 then --NW
2877 TestDir = {'W', 'NW'}
2878 end
2879 local bSpawn = true
2880 for j = 1, #TestDir do
2881 for k = 1, #OceanTiles do
2882 if OceanTiles[k].sDir == TestDir[j] then --ocean tile in relevant direction -> invalid position for a fish tile
2883 bSpawn = false
2884 break
2885 end
2886 end
2887 end
2888
2889 if bSpawn == true and bOptimize == true then --do not spawn a fish tile when one is already next to it (distribute them more evenly)
2890 local CheckNBRS = NBRS[iKey]
2891 for sDir, idx in pairs(CheckNBRS) do
2892 for idxFish = 1, #FishTiles do
2893 if FishTiles[idxFish] == idx then
2894 bSpawn = false
2895 break
2896 end
2897 end
2898 end
2899 end
2900
2901 if bSpawn then
2902 table.insert(FishTiles, iKey)
2903 Keys[i] = nil
2904 iTodo = iTodo - 1
2905 end
2906 end
2907 end
2908 if iTodo == 0 then break end
2909 end
2910
2911 --assign a number token to each tile
2912 for i = 1, #FishTiles do
2913 if InfoTab[FishTiles[i]].iNumTokenFish == nil then
2914 InfoTab[FishTiles[i]].iNumTokenFish = Numbers[i]
2915 end
2916 end
2917
2918 --remove fish tile info for tiles that should not receive a fish tile
2919 for i, iLine in pairs(Keys) do
2920 InfoTab[iLine].vFishRot = nil
2921 end
2922end
2923
2924--[[ Checks if a number chip GUID is a red number (6, 8)
2925 Returns true when it is either a 6 or an 8]]--
2926function isChipRed( iNumToken )
2927 if iNumToken == 6 or iNumToken == 8 then
2928 return true
2929 end
2930 return false
2931end
2932
2933--[[ Find a black number chip that has no red neighboring chips and switch it with the passed chip ]]--
2934function replaceRedChip( InfoTab, Keys, iHexIdx )
2935 local iRand = nil
2936 local bFound = true
2937 local iCounter = 1
2938 local HexInfo = InfoTab[iHexIdx]
2939 --search for a random black chip that doesn't have any red neighbors
2940 while iCounter < 30 do
2941 iRand = math.random(1, #Keys)
2942 if InfoTab[Keys[iRand]].iGroup == HexInfo.iGroup then
2943 iCounter = iCounter + 1
2944 bFound = true
2945 if InfoTab[Keys[iRand]].bRandNum == false then
2946 bFound = false
2947 elseif isChipRed( InfoTab[Keys[iRand]].iNumToken ) then
2948 bFound = false
2949 else
2950 --target chip is black -> check if there are any red neighbors
2951 for sDir, iNbIdx in pairs(NBRS[Keys[iRand]]) do
2952 if InfoTab[iNbIdx] ~= nil and InfoTab[iNbIdx].iNumToken ~= nil then
2953 if iNbIdx ~= iHexIdx then --ignore the token to be switched when checking for red neighbors
2954 if isChipRed( InfoTab[iNbIdx].iNumToken ) then
2955 bFound = false
2956 break
2957 end
2958 end
2959 end
2960 end
2961 end
2962 if bFound then
2963 HexInfo.iNumToken, InfoTab[Keys[iRand]].iNumToken = InfoTab[Keys[iRand]].iNumToken, HexInfo.iNumToken
2964 HexInfo.iNumTokenSec, InfoTab[Keys[iRand]].iNumTokenSec = InfoTab[Keys[iRand]].iNumTokenSec, HexInfo.iNumTokenSec
2965 break
2966 end
2967 end
2968 end
2969end
2970
2971--[[ spawnIndicatorArrow
2972 spawn a little twirling/dancing arrow
2973 - called from MapScript to indicate settlements / cities that receive resources from gold fields
2974 - called from the special build phase indicator pawns when a turn ends and they are in front of the player area
2975]]
2976function spawnIndicatorArrow(p)
2977 p.fHeight = p.fHeight or 0.25 --default height (up and down movement distance)
2978
2979 local params = {}
2980 params.type = 'Custom_Model'
2981 params.position = p.vPos
2982 params.scale = p.vScale
2983
2984 local custom = {}
2985 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379937202/9CB668278FFE49D61A4917473D98D59CD54BB369/'
2986 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379939502/63697125130DFF7A7B53ADDA8798B847C80FC799/'
2987 custom.diffuse = ''
2988 custom.type = 0 --generic
2989
2990 local Arrow = spawnObject(params)
2991 Arrow.setCustomObject(custom)
2992 Arrow.interactable = false
2993 Arrow.use_gravity = false
2994 Arrow.setColorTint(p.vColor)
2995 Arrow.setLuaScript([[ m_fCreated = 0.0
2996 function onLoad(save_state)
2997 Timer.create({
2998 identifier = self.guid .. '_Timer',
2999 function_name = 'destroyArrow',
3000 function_owner = self,
3001 delay = ]] .. p.fTTL .. [[
3002 })
3003 self.setVar('sObjType', 'arrow')
3004 m_fCreated = os.time()
3005 end
3006 function destroyArrow()
3007 self.destruct()
3008 end
3009 function onDestroy()
3010 Timer.destroy(self.guid .. '_Timer')
3011 end
3012 function onUpdate()
3013 self.rotate({0.0,20.0,0.0})
3014 local fYtrans = math.sin((os.time()-m_fCreated)*3.0) * ]] .. p.fHeight .. [[
3015 self.translate({0.0,fYtrans,0.0})
3016 end
3017 ]])
3018 return Arrow
3019end
3020
3021--[[ checkCardLimits
3022 Called from update function when a 7 is rolled. This will count the resource cards for all players to check if they
3023 are exceeding the limit for the robber. If so, the number of cards is displayed by the relevant scripting zone.
3024--]]
3025function checkCardLimits()
3026 coroutine.yield(0)
3027 coroutine.yield(0)
3028
3029 local PlayerData = {
3030 ['Orange'] = {iLimit = 7, iCards = 0, sGUIDzone = '15c3aa' },
3031 ['Red'] = {iLimit = 7, iCards = 0, sGUIDzone = '24632e' },
3032 ['White'] = {iLimit = 7, iCards = 0, sGUIDzone = 'cdde1f' },
3033 ['Purple'] = {iLimit = 7, iCards = 0, sGUIDzone = 'a60284' },
3034 ['Blue'] = {iLimit = 7, iCards = 0, sGUIDzone = 'f37d36' },
3035 ['Green'] = {iLimit = 7, iCards = 0, sGUIDzone = 'd866a9' },
3036 }
3037
3038-- Determine the card limit for each player
3039 --Find out the current card limit for each player when playing with C&K
3040 if isCKenabled() then
3041 if m_MapScript == nil then
3042 return false
3043 else
3044 local MapObj = m_MapScript.getObjects()
3045 for i = 1, #MapObj do
3046 if MapObj[i].getName() == 'Wall' then
3047 local sColor = MapObj[i].getDescription()
3048 if PlayerData[sColor] ~= nil then
3049 PlayerData[sColor].iLimit = PlayerData[sColor].iLimit + 2
3050 end
3051 end
3052 end
3053 end
3054 end
3055 --In Treasures scenario T2 players can increase their card limit by holding 1 treasure
3056 if getScenarioID() == 'T2' then
3057 for sColor, Info in pairs(PlayerData) do
3058 local Zone = getObjectFromGUID(Info.sGUIDzone)
3059 if Zone ~= nil then
3060 local ZoneObj = Zone.getObjects()
3061 for i = 1, #ZoneObj do
3062 if ZoneObj[i].getName() == 'Treasure Chest' then
3063 --PlayerData[sColor].iLimit = PlayerData[sColor].iLimit + 2
3064 Info.iLimit = Info.iLimit + 2
3065 break --only the first treasure increases the limit
3066 end
3067 end
3068 end
3069 end
3070 end
3071
3072-- Count the cards held by each player
3073 for sColor, Info in pairs(PlayerData) do
3074 local HandObj = Player[sColor].getHandObjects()
3075 for i = 1, #HandObj do
3076 local sName = HandObj[i].getName()
3077 if sName == 'Lumber' or sName == 'Bricks' or sName == 'Wool' or sName == 'Grain' or sName == 'Ore' or
3078 sName == 'Coins' or sName == 'Cloth' or sName == 'Paper' then
3079 Info.iCards = Info.iCards + 1
3080 end
3081 end
3082 end
3083
3084-- Check which players exceed their limit
3085 for sColor, Info in pairs(PlayerData) do
3086 if Info.iCards > Info.iLimit then
3087 local Zone = getObjectFromGUID(Info.sGUIDzone)
3088 Zone.call('displayCardCount', {iCards = Info.iCards})
3089 end
3090 end
3091
3092 return true
3093end
3094--clears the 3D text showing the card counts when a player exceeded the limit on a 7
3095--run as a coroutine to make sure not to block anything important by this little gimmick (it gets called from update on a roll that is not 7)
3096function clearCardCounts()
3097 coroutine.yield(0)
3098 coroutine.yield(0)
3099
3100 local Zones = {
3101 '15c3aa', --Orange
3102 '24632e', --Red
3103 'cdde1f', --White
3104 'a60284', --Purple
3105 'f37d36', --Blue
3106 'd866a9', --Green
3107 }
3108 for i = 1, #Zones do
3109 local PlZone = getObjectFromGUID(Zones[i])
3110 if PlZone ~= nil then
3111 PlZone.call('clearCardCount')
3112 end
3113 end
3114 return true
3115end
3116
3117function callback_HexLoaded()
3118 m_iLoadingHexes = m_iLoadingHexes - 1
3119
3120 if m_iLoadingHexes == 0 then --all hex tiles have finished loading -> unlock all number tokens so they drop on the map
3121 for i, Obj in ipairs(m_UnlockOnLoad) do
3122 if Obj ~= nil then
3123 Obj.setLock(false)
3124 end
3125 end
3126 m_UnlockOnLoad = {}
3127
3128 --reset the counters -> cannot be done right after setting up the player items because the set position seems to trigger the onLeave event 1 frame later...
3129 if m_MapScript ~= nil then
3130 m_MapScript.call('setCountersEnabled', {bEnable = true})
3131 end
3132
3133 --consider the game loaded (enable dice highlighting etc.)
3134 m_bMapLoaded = true
3135 end
3136end
3137
3138--[[ workaround infinite bag preview
3139 This will clone a spawn bag, draw the first object from it and then delete the bag again
3140 made necessary because the object preview of inifinite bags will load the object even if
3141 it is still in the bag
3142]]--
3143function takeObjectWorkaround(Bag, params)
3144 local vPos = Bag.getPosition()
3145 local CloneBag = Bag.clone({position = vPos})
3146 CloneBag.setPosition(vPos) --otherwise the bag will be spawned very high above the original bag...
3147 local Obj = CloneBag.takeObject(params)
3148 CloneBag.destruct()
3149 return Obj
3150end
3151
3152--[[
3153 Spawn items for Cities & Knights (most work is done in setupPlayerItems and setupDecks however...)
3154--]]
3155function spawnCKExpansion()
3156 local ThRoutine = coroutine.create( function (ThNext)
3157 coroutine.yield(1)
3158
3159 --event die
3160 if m_EventDie ~= nil then
3161 m_EventDie.setRotation({0.0,0.0,0.0})
3162 m_EventDie.setPosition({x=-31.0,y=1.373,z=-1.5})
3163 else
3164 m_EventDie = spawnEventDie({-31.0,1.373,-1.5})
3165 end
3166
3167 --Spawn Metropolis gates
3168 local vPosMetrP = {x=-23.0,y=1.3,z=-11.4}
3169 local vPosMetrS = {x=-23.0,y=1.3,z=-12.4}
3170 local vPosMetrT = {x=-23.0,y=1.3,z=-13.4}
3171 if m_CurScenario.bUseFishermen then --move metropolises out of the way of the fishermen board
3172 vPosMetrP.x = -18.0
3173 vPosMetrS.x = -18.0
3174 vPosMetrT.x = -18.0
3175 end
3176 spawnMetropolisGate({vPosition=vPosMetrP, vRotation={0.0,90.0,0.0}, sType='Politics'})
3177 spawnMetropolisGate({vPosition=vPosMetrS, vRotation={0.0,90.0,0.0}, sType='Science'})
3178 spawnMetropolisGate({vPosition=vPosMetrT, vRotation={0.0,90.0,0.0}, sType='Trade'})
3179
3180 --color the second die red
3181 if m_Die2 ~= nil then
3182 m_Die2.setColorTint({0.9,0.0,0.0})
3183 end
3184
3185 --spawn the city upgrade charts in all player areas
3186 local ChartPos = { ['Orange'] = { x=-41.1385, y=0.9852, z=-19.29 },
3187 ['Red'] = { x=-11.52, y=0.9852, z=-19.29 },
3188 ['White'] = { x=17.7826, y=0.9852, z=-19.29 },
3189 ['Purple'] = { x=41.1845, y=0.9852, z=19.29 },
3190 ['Blue'] = { x=11.156, y=0.9852, z=19.29 },
3191 ['Green'] = { x=-18.0121, y=0.9852, z=19.29 }
3192 }
3193 local CKitemsBag = getObjectFromGUID(m_CKitems.Bag)
3194 if CKitemsBag ~= nil then
3195 local vInitPos = CKitemsBag.getPosition()
3196 vInitPos.x = vInitPos.x + 4.0
3197
3198 local params = { callback = 'callbackGeneral', callback_owner = Global }
3199 local iNum = 0
3200 for sCol, vPos in pairs(ChartPos) do
3201 local vRot = {0.0,0.0,0.0}
3202 local fModX = -1.0
3203 if vPos.z < 0.0 then
3204 vRot[2] = 180.0
3205 fModX = 1.0
3206 end
3207 params.guid = m_CKitems.Item['ChartPolitics']
3208 params.params = {vPos = vPos, vRot = vRot, bLock = true}
3209 params.position = {vInitPos.x, vInitPos.y - iNum * 0.3, vInitPos.z} --vary the heigt of the initial position to avoid them colliding and being placed at an angle
3210 takeObjectWorkaround(CKitemsBag, params)
3211 iNum = iNum + 1
3212 params.guid = m_CKitems.Item['ChartScience']
3213 params.params = {vPos = {x=vPos.x+(3.255*fModX),y=vPos.y,z=vPos.z}, vRot = vRot, bLock = true}
3214 params.position = {vInitPos.x, vInitPos.y - iNum * 0.3, vInitPos.z} --vary the heigt of the initial position to avoid them colliding and being placed at an angle
3215 takeObjectWorkaround(CKitemsBag, params)
3216 iNum = iNum + 1
3217 params.guid = m_CKitems.Item['ChartTrade']
3218 params.params = {vPos = {x=vPos.x+(3.255*2*fModX),y=vPos.y,z=vPos.z}, vRot = vRot, bLock = true}
3219 params.position = {vInitPos.x, vInitPos.y - iNum * 0.3, vInitPos.z} --vary the heigt of the initial position to avoid them colliding and being placed at an angle
3220 takeObjectWorkaround(CKitemsBag, params)
3221 iNum = iNum + 1
3222 end
3223 end
3224
3225
3226 if ThNext ~= nil then
3227 coroutine.resume(ThNext)
3228 end
3229 return true
3230 end)
3231 return ThRoutine
3232end
3233
3234--[[ spawn objects for the Frenemies mini-expansion
3235 Called from onMapLoad when a map is loaded and the mini expansion enabled
3236]]--
3237function spawnFrenemiesExpansion()
3238 local ThRoutine = coroutine.create( function (ThNext)
3239 coroutine.yield(1)
3240
3241 local iPlayers = m_iFrenemiesPlayers
3242 --board:
3243 local params = {}
3244 params.type = 'Custom_Tile'
3245 params.position = {-23.032, 0.96, 12.938}
3246 params.rotation = {0.0,180.0,0.0}
3247 params.scale = {1.1,1.0,1.1}
3248 local custom = {}
3249 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379941451/382D6A4EAD28F35910F6FC56A9DF65FAF7CFD1AD/'
3250 custom.type = 0 --Box
3251 custom.thickness = 0.06
3252
3253 local Board = spawnObject(params)
3254 Board.setCustomObject(custom)
3255 Board.setDescription('Frenemies Mini-Expansion\nPut spent favor tokens into the bags.')
3256 Board.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
3257 Board.setColorTint({0.902,0.892,0.8314})
3258 Board.setLock(true)
3259
3260 --spawn discard bags:
3261 params = {}
3262 params.type = 'Bag'
3263 params.rotation = {0.0,180.0,0.0}
3264 params.scale = {0.475,0.475,0.475}
3265 --Trader
3266 params.position = {-27.0, 0.872, 14.64}
3267 local TokenBag = spawnObject(params)
3268 TokenBag.setName('Trader Tokens')
3269 TokenBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
3270 TokenBag.setColorTint({0.9255,0.91,0.796})
3271 TokenBag.setLock(true)
3272 --Merchant
3273 params.position = {-25.0, 0.872, 14.64}
3274 local TokenBag = spawnObject(params)
3275 TokenBag.setName('Merchant Tokens')
3276 TokenBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
3277 TokenBag.setColorTint({0.78,0.851,0.992})
3278 TokenBag.setLock(true)
3279 --Road Builder
3280 params.position = {-23.0, 0.872, 14.64}
3281 local TokenBag = spawnObject(params)
3282 TokenBag.setName('Road Builder Tokens')
3283 TokenBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
3284 TokenBag.setColorTint({0.824,0.616,0.412})
3285 TokenBag.setLock(true)
3286 --Scholar
3287 params.position = {-21.0, 0.872, 14.64}
3288 local TokenBag = spawnObject(params)
3289 TokenBag.setName('Scholar Tokens')
3290 TokenBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
3291 TokenBag.setColorTint({0.949,0.843,0.337})
3292 TokenBag.setLock(true)
3293 --Master Builder
3294 params.position = {-19.0, 0.872, 14.64}
3295 local TokenBag = spawnObject(params)
3296 TokenBag.setName('Master Builder Tokens')
3297 TokenBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
3298 TokenBag.setColorTint({0.737,0.89,0.533})
3299 TokenBag.setLock(true)
3300
3301 -- Bag to place tokens in
3302 params = {}
3303 params.type = 'Bag'
3304 params.position = {-19.0, 0.849, 8.77}
3305 params.rotation = {0.0,180.0,0.0}
3306 params.scale = {0.8,0.6,0.8}
3307 local TokenBag = spawnObject(params)
3308 TokenBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
3309 TokenBag.setName('Frenemies Favor Tokens')
3310 TokenBag.setColorTint({0.902,0.892,0.8314})
3311
3312 --generate list with required amount of tokens
3313 local Tokens = {}
3314 local iNum = math.ceil(4.2 * iPlayers)
3315 for i = 1, iNum do
3316 table.insert(Tokens, 'book')
3317 table.insert(Tokens, 'compass')
3318 end
3319 iNum = iPlayers * 2
3320 for i = 1, iNum do
3321 table.insert(Tokens, 'ship')
3322 table.insert(Tokens, 'shovel')
3323 table.insert(Tokens, 'wagon')
3324 end
3325 for i = #Tokens, 2, -1 do --make the order random
3326 local iRand = math.random(i)
3327 Tokens[i],Tokens[iRand] = Tokens[iRand],Tokens[i]
3328 end
3329
3330 --spawn the tokens to be put into the bag
3331 for i, sType in pairs(Tokens) do
3332 local params = {}
3333 params.type = 'Custom_Model'
3334 params.position = {-70.0, 200.0, -40.0}
3335 params.rotation = {0.0,180.0,180.0}
3336 local custom = {}
3337 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379943471/32300EFCC842A3D85EFCEDE3E61E9639BC1F683D/'
3338 if sType == 'book' then
3339 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380251523/05B4E90B3B3000828AED2A94974B3FE5C0EC92D1/'
3340 elseif sType == 'compass' then
3341 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380252189/5843A89BB35F414B15BCEC579D0434DF801DFA77/'
3342 elseif sType == 'ship' then
3343 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380252779/30C6B37EB353430B9D906A81E37B9F2CB2F684DE/'
3344 elseif sType == 'shovel' then
3345 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380253308/297E093104E94D8DC7DFFF244D2BABBE59CBD58F/'
3346 elseif sType == 'wagon' then
3347 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380253938/17064D72B64B52A816CD26497542A8AFADF31242/'
3348 end
3349 custom.type = 0 --generic
3350 custom.material = 3 --cardboard
3351 local Token = spawnObject(params)
3352 Token.setCustomObject(custom)
3353 Token.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
3354 Token.setColorTint({0.902,0.892,0.8314})
3355 TokenBag.putObject(Token)
3356 end
3357
3358 --Spawn victory point tokens
3359 iNum = iPlayers * 2
3360 if iPlayers == 6 then iNum = 13 end
3361 local params = {}
3362 params.type = 'Custom_Model'
3363 params.rotation = {0.0,180.0,0.0}
3364 local custom = {}
3365 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379943471/32300EFCC842A3D85EFCEDE3E61E9639BC1F683D/'
3366 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379946921/965DEA504C0F686ECA7C1BE850FE8CE9DA03BD5E/'
3367 custom.type = 0 --generic
3368 custom.material = 3 --cardboard
3369 local vPos = {-19.505, 1.0, 11.0}
3370 for i = 1, iNum do
3371 vPos[2] = vPos[2] + 0.1
3372 params.position = vPos
3373 local Token = spawnObject(params)
3374 Token.setCustomObject(custom)
3375 Token.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
3376 Token.setColorTint({0.733,0.259,0.184})
3377 Token.setName('1 Victory Point')
3378
3379 if i == math.ceil(iNum / 2) then
3380 vPos[1] = vPos[1] + 1.1
3381 vPos[2] = 1.0
3382 end
3383 end
3384
3385 if ThNext ~= nil then
3386 coroutine.resume(ThNext)
3387 end
3388 return true
3389 end)
3390 return ThRoutine
3391end
3392
3393--[[ spawn objects for the Helpers of Catan mini-expansion
3394 Called from onMapLoad when a map is loaded and mini expansion enabled
3395]]--
3396function spawnHelpersExpansion()
3397 local ThRoutine = coroutine.create( function (ThNext)
3398 coroutine.yield()
3399
3400 local ThCurrent = coroutine.running()
3401
3402 m_HelpersItems = {Bag = 'ba7910', Item = {['Deck'] = 'b08360', ['Board'] = 'a1982d'}}
3403 local SpawnBag = getObjectFromGUID(m_HelpersItems.Bag)
3404 if SpawnBag ~= nil then
3405 local params = {}
3406 params.guid = m_HelpersItems.Item['Deck']
3407 params.callback_owner = Global
3408 params.callback = 'callbackGeneral'
3409 params.params = {vPos = {-21.2,1.1,-7.71}, vRot = {0.0,180.0,0.0}, bLock = false, ThResume = ThCurrent}
3410 local Deck = takeObjectWorkaround(SpawnBag, params)
3411
3412 coroutine.yield()
3413
3414 params = {}
3415 params.guid = m_HelpersItems.Item['Board']
3416 params.callback_owner = Global
3417 params.callback = 'callbackGeneral'
3418 params.position = {-50.0,4.0,0.0}
3419 params.rotation = {10.0,90.0,0.0}
3420 params.params = {vPos = {-50.0,4.0,0.0}, vRot = {10.0,90.0,0.0}, bLock = true, ThResume = ThCurrent}
3421 local Board = takeObjectWorkaround(SpawnBag, params)
3422 Board.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'helpers\') end')
3423 coroutine.yield()
3424
3425 --take individual cards from the deck and place them on display
3426 local vBasePos = {-52.64,6.0,0.0}
3427 local params = {}
3428 params.callback_owner = Global
3429 params.callback = 'callbackGeneral'
3430
3431 --Jean
3432 local Cards = Deck.getObjects()
3433 for i = 1, #Cards do
3434 if Cards[i].nickname == 'Jean' then
3435 local vCardPos = {vBasePos[1], vBasePos[2], vBasePos[3] - 7.5}
3436 params.index = Cards[i].index
3437 params.position = vCardPos
3438 params.params = {vPos = vCardPos, vRot = {10.0,90.0,0.0}, bLock = false, ThResume = ThCurrent}
3439 Deck.takeObject(params)
3440 coroutine.yield()
3441 break
3442 end
3443 end
3444 --Vincent
3445 Cards = Deck.getObjects()
3446 for i = 1, #Cards do
3447 if Cards[i].nickname == 'Vincent' then
3448 local vCardPos = {vBasePos[1], vBasePos[2], vBasePos[3] - 3.77}
3449 params.index = Cards[i].index
3450 params.position = vCardPos
3451 params.params = {vPos = vCardPos, vRot = {10.0,90.0,0.0}, bLock = false, ThResume = ThCurrent}
3452 Deck.takeObject(params)
3453 coroutine.yield()
3454 break
3455 end
3456 end
3457
3458 --Lin
3459 Cards = Deck.getObjects()
3460 for i = 1, #Cards do
3461 if Cards[i].nickname == 'Lin' then
3462 local vCardPos = {vBasePos[1], vBasePos[2], vBasePos[3] - 0.04}
3463 params.index = Cards[i].index
3464 params.position = vCardPos
3465 params.params = {vPos = vCardPos, vRot = {10.0,90.0,0.0}, bLock = false, ThResume = ThCurrent}
3466 Deck.takeObject(params)
3467 coroutine.yield()
3468 break
3469 end
3470 end
3471
3472 --Hilde
3473 Cards = Deck.getObjects()
3474 for i = 1, #Cards do
3475 if Cards[i].nickname == 'Hilde' then
3476 local vCardPos = {vBasePos[1], vBasePos[2], vBasePos[3] + 3.69}
3477 params.index = Cards[i].index
3478 params.position = vCardPos
3479 params.params = {vPos = vCardPos, vRot = {10.0,90.0,0.0}, bLock = false, ThResume = ThCurrent}
3480 Deck.takeObject(params)
3481 coroutine.yield()
3482 break
3483 end
3484 end
3485 end
3486
3487 if ThNext ~= nil then
3488 coroutine.resume(ThNext)
3489 end
3490 return true
3491 end)
3492 return ThRoutine
3493end
3494
3495--[[ spawn objects for the Fishermen of Catan mini-expansion
3496 Called from onMapLoad when a map is loaded and mini expansion enabled
3497]]--
3498function spawnFishermenExpansion()
3499 local ThRoutine = coroutine.create( function (ThNext)
3500 coroutine.yield(1)
3501
3502 local fZOffset = 0.0
3503 if m_CurScenario.bUseCK == true then
3504 fZOffset = -3.65
3505 end
3506
3507 local FishermenBag = getObjectFromGUID(m_FishermenItems.Bag)
3508 if FishermenBag ~= nil then
3509 local vInitPos = FishermenBag.getPosition()
3510 vInitPos.x = vInitPos.x + 6.0 --make sure it is not captured by the spawn bag right after spawning
3511 local params = {}
3512 params.guid = m_FishermenItems.Item['Board']
3513 params.callback_owner = Global
3514 params.callback = 'callbackGeneral'
3515 params.params = {position = vInitPos, vPos = {-25.75,0.97,-8.75 + fZOffset}, vRot = {0.0,180.0,0.0}, vScale = {0.13,0.13,0.13}, bLock = true}
3516 m_FishermenCardBoard = takeObjectWorkaround(FishermenBag, params)
3517 m_FishermenCardBoard.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'fishermen\') end')
3518
3519 local sDeckGUID = nil
3520 if m_CurScenario.bExtension == true then
3521 sDeckGUID = m_FishermenItems.Item['Deck_ext']
3522 else
3523 sDeckGUID = m_FishermenItems.Item['Deck']
3524 end
3525 local params = {}
3526 params.guid = sDeckGUID
3527 params.callback_owner = Global
3528 params.callback = 'callbackDeck'
3529 params.params = {vPos = {-27.0,2.0,-8.75 + fZOffset}, vRot = {0.0,180.0,180.0}, bShuffle = true}
3530 takeObjectWorkaround(FishermenBag, params)
3531 end
3532
3533 --spawn fishmarket tiles for each player
3534 local params = {}
3535 params.type = 'Custom_Tile'
3536 params.scale = {2.92764568,1.0,2.92764568}
3537
3538 local custom = {}
3539 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379949257/D7E42C4DFAD38761601AB6B1B4313BF51A3ECE14/'
3540 custom.type = 0 --Box
3541 custom.thickness = 0.03
3542
3543 local MarketPos = {
3544 ['Orange'] = {vPos = {-23.186,0.96,-21.727}, vRot = {0.0,180.0,0.0}},
3545 ['Red'] = {vPos = {5.960,0.96,-21.727}, vRot = {0.0,180.0,0.0}},
3546 ['White'] = {vPos = {36.061,0.96,-21.727}, vRot = {0.0,180.0,0.0}},
3547 ['Purple'] = {vPos = {22.763,0.96,21.727}, vRot = {0.0,0.0,0.0}},
3548 ['Blue'] = {vPos = {-6.457,0.96,21.727}, vRot = {0.0,0.0,0.0}},
3549 ['Green'] = {vPos = {-36.047,0.96,21.727}, vRot = {0.0,0.0,0.0}},
3550 }
3551 for sCol, Info in pairs(MarketPos) do
3552 params.position = Info.vPos
3553 params.rotation = Info.vRot
3554
3555 local Tile = spawnObject(params)
3556 Tile.setCustomObject(custom)
3557 Tile.setName('The Fish Market')
3558 Tile.setColorTint({0.643,0.784,0.839})
3559 Tile.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'fishermen\') end')
3560 Tile.setLock(true)
3561 end
3562
3563 if ThNext ~= nil then
3564 coroutine.resume(ThNext)
3565 end
3566 return true
3567 end)
3568 return ThRoutine
3569end
3570
3571--[[ spawn objects for the unoffical Welfare Variant mini-expansion
3572 Called from onMapLoad when a map is loaded and mini expansion enabled
3573]]--
3574function spawnWelfareExpansion()
3575 local ThRoutine = coroutine.create( function (ThNext)
3576 coroutine.yield(1)
3577
3578 --spawn infinite bag to take coins from
3579 local params = {}
3580 params.type = 'Infinite_Bag'
3581 params.position = {-21.0,0.95,5.27}
3582 params.rotation = {0.0,330.0,0.0}
3583 params.scale = {0.8,0.6,0.8}
3584 local SupplyBag = spawnObject(params)
3585 SupplyBag.setName('Welfare Coins Supply')
3586 SupplyBag.setDescription('Each time a player does not receive any resource or fish cards during a turn, they receive a welfare coin instead.')
3587 SupplyBag.setColorTint({190/255,157/255,132/255})
3588 SupplyBag.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'welfare\') end')
3589 SupplyBag.setLock(true)
3590
3591 --put the welfare coin into the infinite bag
3592 local params = {}
3593 params.type = 'Custom_Model'
3594 params.position = {-21.0,2.95,5.27}
3595 params.scale = {0.5,1.0,0.5}
3596 custom = {}
3597 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379951416/DF35BA080143304EC36CC9A25CF45E333A19724E/'
3598 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/177162202731459822/F6E9126E4AFDC0AEED84E746E5107FC3C7B0B399/'
3599 custom.type = 5 --Chip
3600 custom.material = 2 --metal
3601 custom.specular_sharpness = 0.0
3602 custom.specular_intensity = 0.0
3603 local Coin = spawnObject(params)
3604 Coin.setCustomObject(custom)
3605 Coin.setName('Welfare Coin')
3606 Coin.setDescription('You can exchange welfare coins for resource or commodity cards. The exchange rate depends on your current victory points.')
3607 Coin.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'welfare\') end')
3608 SupplyBag.putObject(Coin)
3609
3610 --spawn normal bags for each player
3611 local BagPos = {
3612 ['Orange'] = {-23.186,0.96,-15.0},
3613 ['Red'] = {5.960,0.96,-15.0},
3614 ['White'] = {36.061,0.96,-15.0},
3615 ['Purple'] = {22.763,0.96,15.0},
3616 ['Blue'] = {-6.457,0.96,15.0},
3617 ['Green'] = {-36.047,0.96,15.0},
3618 }
3619
3620 local params = {}
3621 params.type = 'Bag'
3622 params.rotation = {0.0,180.0,0.0}
3623 params.scale = {0.8,0.6,0.8}
3624 for sCol, vPos in pairs(BagPos) do
3625 params.position = vPos
3626 local Bag = spawnObject(params)
3627 Bag.setName('Welfare Coins')
3628 Bag.setDescription(sCol)
3629 Bag.setColorTint(getPlayerColor({sPlayer = sCol}))
3630 Bag.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'welfare\') end')
3631 end
3632
3633 if ThNext ~= nil then
3634 coroutine.resume(ThNext)
3635 end
3636 return true
3637 end)
3638 return ThRoutine
3639end
3640
3641-- spawn the event card deck, shuffle it and place it so that the New Year card is 6th from the bottom
3642function spawnEventCardsExpansion()
3643 local ThRoutine = coroutine.create( function (ThNext)
3644 coroutine.yield()
3645
3646 if m_Die1 ~= nil then destroyObject(m_Die1) end
3647 if m_Die2 ~= nil and isCKenabled() == false then destroyObject(m_Die2) end
3648
3649 m_iEventCardResult = 0
3650
3651 local DeckSpawnBag = getObjectFromGUID(m_DeckEventCards)
3652 if DeckSpawnBag ~= nil then
3653 local ThCurrent = coroutine.running()
3654 local Deck = takeObjectWorkaround(DeckSpawnBag, { rotation = {0.0,180.0,180.0},
3655 callback = 'callbackGeneral', callback_owner = Global,
3656 params = {vPos = {x=-33.5,y=1.2,z=0.0}, vRot = {0.0,180.0,180.0}, ThResume = ThCurrent} })
3657 coroutine.yield() --resumed from deck callback
3658 Deck.takeObject({ position = {x=-33.5,y=1.8,z=0.0}, rotation = {0.0,180.0,180.0},
3659 callback = 'callbackGeneral', callback_owner = Global,
3660 params = {vPos = {x=-33.5,y=1.8,z=0.0}, vRot = {0.0,180.0,180.0}, ThResume = ThCurrent} }) -- pick the new year card
3661 coroutine.yield()
3662 Deck.shuffle()
3663
3664 -- spawn the 31 cards on top first to retain the stack name --TODO: come back here when scripting features for deck manipulation have been extended!
3665 for i = 1, 31 do
3666 if i == 31 then
3667 Deck.takeObject({ position = {x=-33.5,y=(14.6-i*0.4),z=0.0}, rotation = {0.0,180.0,180.0},
3668 callback = 'callbackGeneral', callback_owner = Global,
3669 params = {vPos = {x=-33.5,y=(14.6-i*0.4),z=0.0}, vRot = {0.0,180.0,180.0}, ThResume = ThCurrent} })
3670 coroutine.yield()
3671 else
3672 Deck.takeObject({ position = {x=-33.5,y=(14.6-i*0.4),z=0.0}, rotation = {0.0,180.0,180.0} })
3673 end
3674 end
3675 end
3676
3677 if ThNext ~= nil then
3678 coroutine.resume(ThNext)
3679 end
3680 return true
3681 end)
3682 return ThRoutine
3683end
3684function onObjectPickUp(player_color, picked_up_object)
3685 if m_CurScenario.bUseEventCards then
3686 if picked_up_object.getDescription() == 'Event Card' then
3687 m_bAllowDeal = true --reset the block for the deal cards button
3688 local sName = picked_up_object.getName()
3689 local iResult = tonumber(string.sub(sName,1,2))
3690 if iResult == nil then --no event (new year) -> remove highlighting, no auto deal
3691 m_iEventCardResult = 0
3692 else
3693 m_iEventCardResult = iResult
3694 --check if it is the epidemic event cards for the auto deal to take into consideration
3695 if string.sub(sName, 5, 12) == 'Epidemic' then
3696 m_bEpidemic = true
3697 else
3698 m_bEpidemic = false
3699 end
3700 end
3701
3702 if m_MapScript ~= nil then
3703 m_MapScript.call('highlightChits', { iNum = m_iEventCardResult })
3704 end
3705 end
3706 end
3707end
3708
3709--[[ spawn objects for the Oilsprings mini-expansion
3710 Called from onMapLoad when a map is loaded and mini expansion enabled
3711]]--
3712function spawnOilspringsExpansion()
3713 local ThRoutine = coroutine.create( function (ThNext)
3714 coroutine.yield(1)
3715
3716 local ThCurrent = coroutine.running()
3717
3718 --spawn building costs tiles for each player
3719 local params = {}
3720 params.type = 'Custom_Tile'
3721 params.scale = {1.47602439,1.0,1.47602439}
3722 local custom = {}
3723 custom.image = 'http://cloud-3.steamusercontent.com/ugc/447367065311953591/2DA2B003948C9298B7C5B56E61F9F099958AEEF0/'
3724 custom.type = 0 --Box
3725 custom.thickness = 0.03
3726
3727 local MarketPos = {
3728 ['Orange'] = {vPos = {-21.170314,0.96,-20.2506}, vRot = {0.0,180.0,0.0}},
3729 ['Red'] = {vPos = {7.975686,0.96,-20.2506}, vRot = {0.0,180.0,0.0}},
3730 ['White'] = {vPos = {38.0,0.96,-20.2506}, vRot = {0.0,180.0,0.0}},
3731 ['Purple'] = {vPos = {20.93,0.96,20.2506}, vRot = {0.0,0.0,0.0}},
3732 ['Blue'] = {vPos = {-8.39,0.96,20.2506}, vRot = {0.0,0.0,0.0}},
3733 ['Green'] = {vPos = {-38.0,0.96,20.2506}, vRot = {0.0,0.0,0.0}},
3734 }
3735 for sCol, Info in pairs(MarketPos) do
3736 params.position = Info.vPos
3737 --adjust position when the fishermen expansion is enabled so they don't clip
3738 if m_CurScenario.bUseFishermen then
3739 local fMod = 1.0
3740 if Info.vRot[2] == 180.0 then
3741 fMod = -1.0
3742 end
3743 params.position[1] = params.position[1] + (6.2 * fMod)
3744 if m_CurScenario.bUseCK then
3745 params.position[3] = params.position[3] + (1.2 * fMod)
3746 end
3747 end
3748 params.rotation = Info.vRot
3749
3750 local Tile = spawnObject(params)
3751 Tile.setCustomObject(custom)
3752 Tile.setName('Building Costs - Oil')
3753 Tile.setColorTint({0.396,0.31,0.165})
3754 Tile.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'oilsprings\') end')
3755 Tile.setLock(true)
3756 end
3757
3758 --spawn VP token for environment champion
3759 local vPosToken = {-25.6,1.6,-10.0}
3760 if isCKenabled() then
3761 vPosToken[3] = vPosToken[3] - 2.4
3762 if m_CurScenario.bUseFishermen then vPosToken[1] = vPosToken[1] + 5.0 end
3763 else
3764 if m_CurScenario.bUseFishermen then vPosToken[3] = vPosToken[3] - 2.6 end
3765 end
3766 local params = {}
3767 params.type = 'Custom_Tile'
3768 params.scale = {1.31135309,1.0,1.31135309}
3769 params.position = vPosToken
3770 params.rotation = {0.0,180.0,0.0}
3771 local custom = {}
3772 custom.image = 'http://cloud-3.steamusercontent.com/ugc/447367065312046784/6A1E16C62E626C281D151DB743C2835277FAD339/'
3773 custom.type = 0 --Box
3774 custom.thickness = 0.09
3775
3776 local Token = spawnObject(params)
3777 Token.setCustomObject(custom)
3778 Token.setName('Champion of the Environment')
3779 Token.setDescription('This token is awarded to the player who has sequestered the most oil (at least 3) - 1 Victory Point!')
3780 Token.setColorTint({0.396,0.31,0.165})
3781 Token.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'oilsprings\') end')
3782
3783 --spawn vp tokens for sequestered oil
3784 local params = {}
3785 params.type = 'Custom_Model'
3786 params.rotation = {0.0,180.0,0.0}
3787 local custom = {}
3788 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379943471/32300EFCC842A3D85EFCEDE3E61E9639BC1F683D/'
3789 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379946921/965DEA504C0F686ECA7C1BE850FE8CE9DA03BD5E/'
3790 custom.type = 0 --generic
3791 custom.material = 3 --cardboard
3792 local vPosVP = {vPosToken[1]+3.6, 1.0, vPosToken[3]+1.0}
3793 for i = 1, 7 do
3794 vPosVP[2] = vPosVP[2] + 0.1
3795 params.position = vPosVP
3796 local Token = spawnObject(params)
3797 Token.setCustomObject(custom)
3798 Token.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'oilsprings\') end') --keep the variable on rewind
3799 Token.setColorTint({0.733,0.259,0.184})
3800 Token.setName('1 Victory Point')
3801 Token.setDescription('For each 3 oil a player has sequestered they receive 1 victory point token.')
3802 end
3803
3804 --spawn the metropolis tokens
3805 local iNum = 4
3806 if m_CurScenario.bExtension then
3807 iNum = 6
3808 end
3809 local vPosMetr = {vPosToken[1]+3.6, 1.0, vPosToken[3]-1.0}
3810 local params = {}
3811 params.type = 'Custom_Tile'
3812 params.scale = {0.433980554,1.0,0.433980554}
3813 params.rotation = {0.0,180.0,0.0}
3814 local custom = {}
3815 custom.image = 'http://cloud-3.steamusercontent.com/ugc/447367065314774775/4C7ED5485D4BC2F799144BB4756567173CB1B00D/'
3816 custom.type = 0 --Box
3817 custom.thickness = 0.09
3818 for i = 1, iNum do
3819 vPosMetr[2] = vPosMetr[2] + 0.3
3820 params.position = vPosMetr
3821 local Token = spawnObject(params)
3822 Token.setCustomObject(custom)
3823 Token.setColorTint({0.1686,0.1333,0.0980})
3824 Token.setName('Oilsprings Metropolis')
3825 --Handle scoring for the metropolis token (similar to C&K metropolis gate)
3826 Token.setLuaScript([[ m_bLoaded = false
3827 function onSave()
3828 local ToSave = { sScorer = self.getVar('sScorer') }
3829 return JSON.encode(ToSave)
3830 end
3831 function onLoad(save_state)
3832 self.setVar('sObjType', 'oilsprings')
3833 local saved_data = JSON.decode(save_state)
3834 if saved_data ~= nil then
3835 self.setVar('sScorer', saved_data.sScorer)
3836 end
3837 m_bLoaded = true
3838 end
3839 function onCollisionEnter(collision_info)
3840 if self == nil or m_bLoaded == false then
3841 return
3842 end
3843 if self.getVar('sScorer') ~= nil then
3844 return
3845 end
3846 if collision_info == nil then return end
3847 local ColObj = collision_info.collision_object
3848 if ColObj.getName() == 'City' or ColObj.getName() == 'Wall' then
3849 local MapScript = getObjectFromGUID('42fd8e')
3850 if MapScript ~= nil then
3851 local bScore = false
3852 local ChkObj = MapScript.getObjects()
3853 for i, Obj in ipairs(ChkObj) do
3854 if Obj == ColObj then
3855 bScore = true
3856 break
3857 end
3858 end
3859 --award the points
3860 if bScore == true then
3861 if true == MapScript.call('addScore', {iScore = 1, sPlayer = ColObj.getDescription()}) then
3862 self.setVar('sScorer', ColObj.getDescription())
3863 end
3864 end
3865 end
3866 end
3867 end
3868 function onPickUp(player_color) --remove the score from the previous scorer when the metropolis is picked up
3869 local sOldScorer = self.getVar('sScorer')
3870 if sOldScorer ~= nil then
3871 self.setVar('sScorer', nil)
3872 local MapScript = getObjectFromGUID('42fd8e')
3873 if MapScript ~= nil then
3874 MapScript.call('subtractScore',{iScore = 1, sPlayer = sOldScorer})
3875 end
3876 end
3877 end
3878 ]])
3879 end
3880
3881 --spawn oil tokens
3882 iNum = 15
3883 if m_CurScenario.bExtension then
3884 iNum = 21
3885 end
3886 local vPosOil = {-22.0, 1.0, 9.77}
3887 local params = {}
3888 params.type = 'Custom_Tile'
3889 params.scale = {0.5,1.0,0.5}
3890 params.rotation = {0.0,180.0,0.0}
3891 local custom = {}
3892 custom.image = 'http://cloud-3.steamusercontent.com/ugc/447367065316098092/00DD562112DCC6C988257222AD3BDD1766A94D32/'
3893 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/447367065316098574/22C436B3023D0DA28B97E58C1877E361A56B7C17/'
3894 custom.type = 0 --Box
3895 custom.thickness = 0.08
3896 custom.stackable = true
3897 for i = 1, iNum do
3898 vPosOil[2] = vPosOil[2] + 0.1
3899 params.position = vPosOil
3900 local Token = spawnObject(params)
3901 Token.setCustomObject(custom)
3902 Token.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'oilsprings\') end') --keep the variable on rewind
3903 Token.setColorTint({0.749,0.5765,0.298})
3904 Token.setName('Oil Token')
3905 if i == 10 then
3906 vPosOil[2] = 1.0
3907 vPosOil[3] = vPosOil[3] - 1.3
3908 end
3909 end
3910
3911 --spawn the disaster board + oil usage indicator
3912 local BagBoard = getObjectFromGUID(m_OilspDisasterBoard)
3913 local vPosBoard = {30.0,1.0,-6.0}
3914 if m_MapScript ~= nil then
3915 vPosBoard[1] = (m_MapScript.getScale().x / 2) + m_MapScript.getPosition().x + 4.7
3916 end
3917 local TrackerBoard = takeObjectWorkaround(BagBoard, {position = vPosBoard, rotation = {0.0,180.0,0.0},
3918 callback = 'callbackGeneral', callback_owner = Global,
3919 params = {vPos = vPosBoard, vRot = {0.0,180.0,0.0},bLock = true,ThResume = ThCurrent} })
3920 TrackerBoard.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'oilsprings\') end')
3921 coroutine.yield(1)
3922 --spawn the indicator chip when the board is initialized
3923 vPosBoard[1] = vPosBoard[1] - 2.2949516
3924 vPosBoard[2] = 1.3
3925 vPosBoard[3] = vPosBoard[3] - 1.25559335
3926 local params = {}
3927 params.type = 'Custom_Tile'
3928 params.scale = {0.291973174,1.0,0.291973174}
3929 params.position = vPosBoard
3930 params.rotation = {0.0,180.0,0.0}
3931 local custom = {}
3932 custom.image = 'http://cloud-3.steamusercontent.com/ugc/447367065314718788/F8DFFEA39131486BC0AE09F946B710E4CC62E03D/'
3933 custom.type = 0 --Box
3934 custom.thickness = 0.03
3935 local Token = spawnObject(params)
3936 Token.setCustomObject(custom)
3937 Token.setName('Disaster Track Marker')
3938 Token.setColorTint({0.9961,0.8824,0.6627})
3939 Token.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'oilsprings\') end')
3940
3941 if ThNext ~= nil then
3942 coroutine.resume(ThNext)
3943 end
3944 return true
3945 end)
3946 return ThRoutine
3947end
3948
3949--[[Spawn additional items for the Rivers of Catan expansion
3950 the rivers hexes are defined in the scenario definition and the bridges are spawned from setupPlayerItems ]]--
3951function spawnRiversExpansion()
3952 local ThRoutine = coroutine.create( function (ThNext)
3953 coroutine.yield(1)
3954
3955 --determine spawn location from the mapscript dimensions (map size)
3956 local vPos = {30.0,1.0,-9.0}
3957 if m_MapScript ~= nil then
3958 vPos[1] = (m_MapScript.getScale().x / 2) + m_MapScript.getPosition().x
3959 end
3960
3961 --spawn wealthiest settler token
3962 local custom = {}
3963 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379953587/A738B7C427B1903BFE09D9FCD69C233FAD9D539B/'
3964 custom.type = 0 --Box
3965 custom.thickness = 0.04
3966
3967 local params = {}
3968 params.type = 'Custom_Tile'
3969 params.scale = {0.5,1.0,0.5}
3970
3971 params.position = vPos
3972 params.rotation = {0.0,180.0,0.0}
3973 local Wealthiest = spawnObject(params)
3974 Wealthiest.setCustomObject(custom)
3975 Wealthiest.setName('Wealthiest Settler')
3976 Wealthiest.setDescription('The player who has solely the most gold gets this token for +1 Victory Point.')
3977 Wealthiest.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'rivers\') end')
3978 Wealthiest.setColorTint({0.671, 0.141, 0.169})
3979
3980 --spawn poor settler tokens
3981 vPos[3] = vPos[3] - 1.5
3982 for i=1, 6 do
3983 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379955708/5897F1D78836BD89645EE6090D50F18F07D58A72/'
3984 local Token = spawnObject(params)
3985 Token.setCustomObject(custom)
3986 Token.setName('Poor Settler')
3987 Token.setDescription('The player(s) who has the least amount of gold gets this token for -2 Victory Points.')
3988 Token.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'rivers\') end')
3989 Token.setColorTint({0.839, 0.706, 0.596})
3990 vPos[2] = vPos[2] + 0.4
3991 end
3992
3993 --spawn gold coins
3994 params = {}
3995 params.type = 'Custom_Model'
3996 params.position = {vPos[1]+2.2, 1.0, vPos[3]}
3997 params.rotation = {0.0,180.0,0.0}
3998 params.scale = {0.5,1.0,0.5}
3999
4000 custom = {}
4001 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379951416/DF35BA080143304EC36CC9A25CF45E333A19724E/'
4002 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379959874/DBC1827EF70AE3FC788D21E4C979A9A392F9B5FA/'
4003 custom.type = 5 --Chip
4004 custom.material = 2 --metal
4005 custom.specular_sharpness = 1.0
4006 custom.specular_intensity = 0.5
4007
4008 for i=1, 100 do
4009 local Chip = spawnObject(params)
4010 Chip.setCustomObject(custom)
4011 Chip.setName('Gold')
4012 Chip.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'rivers\') end') --keep the variable on rewind
4013 Chip.setColorTint({0.984,0.867,0.341})
4014 params.position[2] = params.position[2] + 0.13
4015 if i % 25 == 0 then
4016 params.position[1] = params.position[1] + 1.8
4017 params.position[2] = 1.0
4018 if i == 50 then
4019 params.position[1] = params.position[1] - 3.6
4020 params.position[3] = params.position[3] + 1.8
4021 end
4022 end
4023 end
4024
4025 if ThNext ~= nil then
4026 coroutine.resume(ThNext)
4027 end
4028 return true
4029 end)
4030 return ThRoutine
4031end
4032--[[Spawn additional items for the Caravans expansion
4033 Spawn camels figurines next to the map ]]--
4034function spawnCaravansExpansion()
4035 local ThRoutine = coroutine.create( function (ThNext)
4036 coroutine.yield(1)
4037
4038 --determine spawn location from the mapscript dimensions (map size)
4039 local vPos = {30.0,1.5,-9.0}
4040 if m_MapScript ~= nil then
4041 vPos[1] = (m_MapScript.getScale().x / 2) + m_MapScript.getPosition().x
4042 vPos[1] = vPos[1] - 1.0
4043 end
4044
4045 --spawn camels
4046 params = {}
4047 params.type = 'Custom_Model'
4048 params.position = {vPos[1], vPos[2], vPos[3]}
4049 params.rotation = {0.0,90.0,0.0}
4050 custom = {}
4051 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601380258198/1FD357DB6672A2502EC30E2A1D8FE3432B076570/'
4052 custom.diffuse = ''
4053 custom.type = 1 --figurine
4054 custom.material = 1 --wood
4055
4056 local iNumCamels = 22
4057 if m_CurScenario.bExtension then --5-6 players
4058 iNumCamels = 33
4059 end
4060 for i = 1, iNumCamels do
4061 local Camel = spawnObject(params)
4062 Camel.setCustomObject(custom)
4063 Camel.setName('Camel')
4064 --Beware some inception incoming - I apologize to anyone trying to figure out what is happening here ;-)
4065 --In prose: make the camel generate a scripting zone around itself when it is placed on the map. This scripting zone will monitor incoming/leaving objects to make sure any buildings get score added/deducted when being placed/removed between 2 camels
4066 Camel.setLuaScript( [[m_Zone = nil
4067 function onSave()
4068 local sGUID = nil
4069 if m_Zone ~= nil then sGUID = m_Zone.getGUID() end
4070 return JSON.encode({sZoneGUID = sGUID})
4071 end
4072 function onLoad(save_state)
4073 self.setVar('sObjType', 'caravans')
4074 local saved_data = JSON.decode(save_state)
4075 if saved_data ~= nil then if saved_data.sZoneGUID ~= nil then m_Zone = getObjectFromGUID(saved_data.sZoneGUID) end end
4076 end
4077 --Destroy the scripting zone for scoring when the camel is picked up
4078 function onPickUp(player_color)
4079 if m_Zone ~= nil then destroyObject(m_Zone) end
4080 end
4081 --When a camel is placed, spawn a scripting zone to track settlements/cities entering leaving it to apply score
4082 function onCollisionEnter(collision_info)
4083 if m_Zone ~= nil or collision_info == nil then
4084 return
4085 end
4086 local sObjType = collision_info.collision_object.getVar('sObjType')
4087 if sObjType == 'numtoken' or sObjType == 'hex' or collision_info.collision_object.getName() == 'Road' then
4088 --Spawn scripting zones to manage scoring for settlements / cities entering leaving the position between 2 camels
4089 m_Zone = spawnObject({ type = 'ScriptingTrigger', position = self.getPosition(), rotation = {0.0,self.getRotation().y,0.0}, scale = {3.5,1.0,2.6} })
4090 m_Zone.setName('camel_zone')
4091 m_Zone.setLuaScript([=[ function onLoad(save_state) self.setVar('sObjType', 'caravans') end
4092 function checkScoring(p)
4093 local Camels = {}
4094 local ConnectedB = {}
4095 local UnConnectedB = {}
4096 local Objects = self.getObjects()
4097 if p.enter_object ~= nil then --add this object because it is not returned from getObjects when entering
4098 table.insert(Objects, p.enter_object)
4099 end
4100 if p.leave_object ~= nil then
4101 --it is a connected building that is leaving -> remove score and connection
4102 local iPosAppend = string.find(p.leave_object.getName(), '(Connected to Caravan)')
4103 if iPosAppend ~= nil then
4104 iPosAppend = iPosAppend - 3
4105 p.leave_object.setName(string.sub(p.leave_object.getName(), 1, iPosAppend)) --remove "(Connected to Caravan)" from the name
4106 local MapScript = getObjectFromGUID('42fd8e')
4107 if MapScript ~= nil then
4108 MapScript.call('subtractScore', {iScore = 1, sPlayer = p.leave_object.getDescription()})
4109 end
4110 return
4111 elseif p.leave_object.getName() == 'Camel' then --remove leaving object if it is a camel because it is still in getObjects when the leave event is triggered
4112 for j = 1, #Objects do
4113 if Objects[j] == p.leave_object then
4114 table.remove(Objects, j)
4115 break
4116 end
4117 end
4118 end
4119 end
4120
4121 --get other camels in range
4122 for i, Obj in pairs(Objects) do
4123 if Obj.getName() == 'Camel' then
4124 table.insert(Camels, Obj)
4125 elseif Obj.getName() == 'Settlement' or Obj.getName() == 'City' then
4126 table.insert(UnConnectedB, Obj)
4127 elseif Obj.getName() == 'Settlement (Connected to Caravan)' or Obj.getName() == 'City (Connected to Caravan)' then
4128 table.insert(ConnectedB, Obj)
4129 end
4130 end
4131
4132 --check normal settlements / cities if they should be connected now
4133 if #UnConnectedB > 0 then
4134 for i, Obj in pairs(UnConnectedB) do
4135 local fX = Obj.getPosition().x
4136 local fZ = Obj.getPosition().z
4137 local iConnections = 0
4138 for j = 1, #Camels do
4139 local fDistance = math.abs(Camels[j].getPosition().x - fX) + math.abs(Camels[j].getPosition().z - fZ)
4140 if fDistance < 2.9 then
4141 iConnections = iConnections + 1
4142 end
4143 end
4144 if iConnections >= 2 then
4145 Obj.setName(Obj.getName() .. ' (Connected to Caravan)')
4146 local MapScript = getObjectFromGUID('42fd8e')
4147 if MapScript ~= nil then
4148 MapScript.call('addScore', {iScore = 1, sPlayer = Obj.getDescription()})
4149 end
4150 end
4151 end
4152 end
4153 --check buildings connected to caravans if they are still connected to 2 camels
4154 if #ConnectedB > 0 then
4155 for i, Obj in pairs(ConnectedB) do
4156 local iConnections = 0
4157 if Obj.held_by_color == nil then --a building held by a player (it is being picked up) always gets disconnected, so don't bother counting camels
4158 local fX = Obj.getPosition().x
4159 local fZ = Obj.getPosition().z
4160 for j = 1, #Camels do
4161 local fDistance = math.abs(Camels[j].getPosition().x - fX) + math.abs(Camels[j].getPosition().z - fZ)
4162 if fDistance < 2.9 then
4163 iConnections = iConnections + 1
4164 end
4165 end
4166 end
4167 if iConnections < 2 then
4168 local iPosAppend = string.find(Obj.getName(), '(Connected to Caravan)')
4169 if iPosAppend ~= nil then
4170 iPosAppend = iPosAppend - 3
4171 Obj.setName(string.sub(Obj.getName(), 1, iPosAppend)) --remove "(Connected to Caravan)" from the name
4172 local MapScript = getObjectFromGUID('42fd8e')
4173 if MapScript ~= nil then
4174 MapScript.call('subtractScore', {iScore = 1, sPlayer = Obj.getDescription()})
4175 end
4176 end
4177 end
4178 end
4179 end
4180 end
4181 function onObjectEnterScriptingZone(zone, enter_object)
4182 if zone == self and enter_object ~= nil then
4183 if enter_object.getName() == 'Camel' or enter_object.getName() == 'Settlement' or enter_object.getName() == 'City' then
4184 checkScoring({enter_object = enter_object})
4185 end
4186 end
4187 end
4188 function onObjectLeaveScriptingZone(zone, leave_object)
4189 if zone == self and leave_object ~= nil then
4190 if leave_object.getName() == 'Camel' or string.find(leave_object.getName(),'(Connected to Caravan)') ~= nil then
4191 checkScoring({leave_object = leave_object})
4192 end
4193 end
4194 end ]=]
4195 )
4196 end
4197 end]])
4198 Camel.setColorTint({0.576,0.357,0.263})
4199
4200 params.position[1] = params.position[1] + 0.4
4201 if i % 11 == 0 then
4202 params.position[1] = vPos[1]
4203 params.position[3] = params.position[3] - 1.0
4204 end
4205 end
4206
4207 if ThNext ~= nil then
4208 coroutine.resume(ThNext)
4209 end
4210 return true
4211 end)
4212 return ThRoutine
4213end
4214
4215--[[Spawn additional items for the Barbarian Attack campaign
4216 Spawn barbarian figurines & gold next to the map + special die on the castle hex
4217 The knight figures for players are spawned from setupPlayerItems
4218]]--
4219function spawnBarbarianAttackExpansion()
4220 local ThRoutine = coroutine.create( function (ThNext)
4221 coroutine.yield(1)
4222
4223 --determine spawn location from the mapscript dimensions (map size)
4224 local vPos = {30.0,1.5,-9.0}
4225 if m_MapScript ~= nil then
4226 vPos[1] = (m_MapScript.getScale().x / 2) + m_MapScript.getPosition().x
4227 end
4228
4229 --Spawn a bag for the barbarians
4230 params = {}
4231 params.type = 'Bag'
4232 params.position = vPos
4233 params.rotation = {0.0,180.0,0.0}
4234 params.scale = {0.8,0.6,0.8}
4235 if isCKenabled() then
4236 params.type = 'Infinite_Bag' --unlimited amounts of barbarians when played with C&K
4237 end
4238
4239 local BarbBag = spawnObject(params)
4240 BarbBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'battack\') end') --keep the variable on rewind
4241 BarbBag.setName('Barbarians')
4242 BarbBag.setColorTint({0.745,0.608,0.4})
4243
4244 --spawn the barbarian figures over the bag
4245 local iNum = 36
4246 if m_CurScenario.bExtension then
4247 iNum = 48
4248 end
4249 for i = 1, iNum do
4250 local Barbarian = spawnTABBarbarian({vPos = {vPos[1],80.0, vPos[3]}, vRot = {320.0,0.0,0.0}})
4251 Barbarian.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'battack\') end')
4252 Barbarian.use_snap_points = false
4253 BarbBag.putObject(Barbarian)
4254
4255 if isCKenabled() then --infinite bag => spawn just one
4256 break
4257 end
4258 end
4259
4260 --spawn gold coins
4261 params = {}
4262 params.type = 'Custom_Model'
4263 params.position = {vPos[1]+2.2, 1.0, vPos[3]}
4264 params.rotation = {0.0,180.0,0.0}
4265 params.scale = {0.5,1.0,0.5}
4266 custom = {}
4267 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379951416/DF35BA080143304EC36CC9A25CF45E333A19724E/'
4268 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379959874/DBC1827EF70AE3FC788D21E4C979A9A392F9B5FA/'
4269 custom.type = 5 --Chip
4270 custom.material = 2 --metal
4271 custom.specular_sharpness = 1.0
4272 custom.specular_intensity = 0.5
4273
4274 for i=1, 100 do
4275 local Chip = spawnObject(params)
4276 Chip.setCustomObject(custom)
4277 Chip.setName('Gold')
4278 Chip.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'battack\') end') --keep the variable on rewind
4279 Chip.setColorTint({0.984,0.867,0.341})
4280 params.position[2] = params.position[2] + 0.13
4281 if i % 25 == 0 then
4282 params.position[1] = params.position[1] + 1.8
4283 params.position[2] = 1.0
4284 if i == 50 then
4285 params.position[1] = params.position[1] - 3.6
4286 params.position[3] = params.position[3] + 1.8
4287 end
4288 end
4289 end
4290
4291 if ThNext ~= nil then
4292 coroutine.resume(ThNext)
4293 end
4294 return true
4295 end)
4296 return ThRoutine
4297end
4298
4299--[[Spawn additional items for the Traders & Barbarians campaign
4300 Spawn the player wagons and the baggage train cards next to the player areas
4301 Spawn gold next to the map
4302 The development cards deck for this expansion is spawned from setupDecks
4303 Barbarian figures are spawed from the map definition
4304]]--
4305function spawnTradersAndBarbariansExpansion()
4306 local ThRoutine = coroutine.create( function (ThNext)
4307 coroutine.yield(1)
4308
4309 --determine spawn location from the mapscript dimensions (map size)
4310 local vPos = {30.0,1.5,-9.0}
4311 if m_MapScript ~= nil then
4312 vPos[1] = (m_MapScript.getScale().x / 2) + m_MapScript.getPosition().x
4313 end
4314
4315 --spawn gold coins
4316 local params = {}
4317 params.type = 'Custom_Model'
4318 params.position = {vPos[1]+2.2, 1.0, vPos[3]}
4319 params.rotation = {0.0,180.0,0.0}
4320 params.scale = {0.5,1.0,0.5}
4321 custom = {}
4322 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379951416/DF35BA080143304EC36CC9A25CF45E333A19724E/'
4323 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379959874/DBC1827EF70AE3FC788D21E4C979A9A392F9B5FA/'
4324 custom.type = 5 --Chip
4325 custom.material = 2 --metal
4326 custom.specular_sharpness = 1.0
4327 custom.specular_intensity = 0.5
4328
4329 for i=1, 70 do
4330 local Chip = spawnObject(params)
4331 Chip.setCustomObject(custom)
4332 Chip.setName('Gold')
4333 Chip.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'traders\') end') --keep the variable on rewind
4334 Chip.setColorTint({0.984,0.867,0.341})
4335 params.position[2] = params.position[2] + 0.13
4336 if i % 25 == 0 then
4337 params.position[1] = params.position[1] + 1.8
4338 params.position[2] = 1.0
4339 if i == 50 then
4340 params.position[1] = params.position[1] - 3.6
4341 params.position[3] = params.position[3] + 1.8
4342 end
4343 end
4344 end
4345
4346 local PlayerDef = { ['Green'] = {vPosWagon = {-29.6 + 3.0, 1.2, 15.0}, DeckBag = '8d01db', vPosDeck = {-29.6, 1.2, 15.0}, vPosGold = {-37.4,1.2,19.0}},
4347 ['Blue'] = {vPosWagon = {-0.1 - 3.0, 1.2, 15.0}, DeckBag = '5a1d43', vPosDeck = {-0.1, 1.2, 15.0}, vPosGold = {-7.8,1.2,19.0}},
4348 ['Purple'] = {vPosWagon = {29.4 - 3.0, 1.2, 15.0}, DeckBag = '3beb06', vPosDeck = {29.4, 1.2, 15.0}, vPosGold = {21.4,1.2,19.0}},
4349 ['White'] = {vPosWagon = {29.4 - 3.0, 1.2, -15.0}, DeckBag = '6f9264', vPosDeck = {29.4, 1.2, -15.0}, vPosGold = {37.4,1.2,-19.0}},
4350 ['Red'] = {vPosWagon = {-0.15 - 3.0, 1.2, -15.0}, DeckBag = 'b787f3', vPosDeck = {-0.15, 1.2, -15.0}, vPosGold = {7.4,1.2,-19.0}},
4351 ['Orange'] = {vPosWagon = {-29.6 + 3.0, 1.2, -15.0}, DeckBag = '26b465', vPosDeck = {-29.6, 1.2, -15.0}, vPosGold = {-21.9,1.2,-19.0}},
4352 }
4353
4354 if m_CurScenario.bUseFrenemies then --move greens cards to the side a bit
4355 PlayerDef['Green'].vPosWagon[1] = PlayerDef['Green'].vPosWagon[1] - 2.5
4356 PlayerDef['Green'].vPosDeck[1] = PlayerDef['Green'].vPosDeck[1] - 2.5
4357 end
4358 if isCKenabled() then --move oranges cards to the side a bit
4359 PlayerDef['Orange'].vPosWagon[1] = PlayerDef['Orange'].vPosWagon[1] - 2.5
4360 PlayerDef['Orange'].vPosDeck[1] = PlayerDef['Orange'].vPosDeck[1] - 2.5
4361 end
4362
4363 --spawn 5 initial gold coins for each player
4364 for sCol, Info in pairs(PlayerDef) do
4365 params.position = Info.vPosGold
4366 for i=1, 5 do
4367 local Chip = spawnObject(params)
4368 Chip.setCustomObject(custom)
4369 Chip.setName('Gold')
4370 Chip.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'traders\') end') --keep the variable on rewind
4371 Chip.setColorTint({0.984,0.867,0.341})
4372 params.position[2] = params.position[2] + 0.13
4373 end
4374 end
4375
4376 --spawn wagon figure for each player & the baggage train decks
4377 params = {}
4378 params.type = 'Custom_Model'
4379 params.rotation = {0.0,90.0,0.0}
4380 custom = {}
4381 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379962685/10B747E09EEDE274FDB3F79B87288E78B8ED33CF/'
4382 custom.diffuse = ''
4383 custom.type = 1 --figurine
4384 custom.material = 3 --wood
4385
4386 local TaBBag = getTaBBag()
4387 for sCol, Info in pairs(PlayerDef) do
4388 --spawn the wagon
4389 params.position = Info.vPosWagon
4390 local Wagon = spawnObject(params)
4391 Wagon.setCustomObject(custom)
4392 Wagon.setName('Baggage Train')
4393 Wagon.setDescription(sCol)
4394 Wagon.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'traders\') end') --keep the variable on rewind
4395 Wagon.setColorTint(m_PlayerColors[sCol])
4396 Wagon.use_snap_points = false
4397
4398 --spawn the baggage train card deck
4399 if TaBBag ~= nil then
4400 local vPosDeck = {Info.vPosDeck[1]-1.2,Info.vPosDeck[2],Info.vPosDeck[3]}
4401 local vPosCard = {Info.vPosDeck[1]+1.2,Info.vPosDeck[2],Info.vPosDeck[3]}
4402 local vRot = {0.0,180.0,180.0}
4403 if sCol == 'Green' or sCol == 'Blue' or sCol == 'Purple' then
4404 vPosDeck = {Info.vPosDeck[1]+1.2,Info.vPosDeck[2],Info.vPosDeck[3]}
4405 vPosCard = {Info.vPosDeck[1]-1.2,Info.vPosDeck[2],Info.vPosDeck[3]}
4406 vRot = {0.0,0.0,180.0}
4407 end
4408 takeObjectWorkaround(TaBBag, { guid = TaBBag.call('getBaggageDeckGUID', {sCol = sCol}),
4409 position = vPosDeck,
4410 rotation = vRot,
4411 callback = 'callbackDrawCard',
4412 callback_owner = Global,
4413 params = {vCardPos = vPosCard, vRot = {vRot[1],vRot[2],0.0}}
4414 } )
4415 end
4416 end
4417
4418 if ThNext ~= nil then
4419 coroutine.resume(ThNext)
4420 end
4421 return true
4422 end)
4423 return ThRoutine
4424end
4425function callbackDrawCard(Deck, p)
4426 Deck.takeObject({index = 0, position = p.vCardPos, rotation = p.vRot})
4427end
4428
4429function isRiversCompatible(p)
4430 local sID = p.sScenarioID
4431 if sID == 'B' or sID == 'B2' or sID == 'S1' or sID == 'S4' or sID == 'S5' or sID == 'T1' or sID == 'T3' then
4432 return true
4433 end
4434 return false
4435end
4436function isCaravansCompatible(p)
4437 local sID = p.sScenarioID
4438 if sID == 'B' or sID == 'B2' or sID == 'S1' or sID == 'S4' or sID == 'S5' or sID == 'T1' or sID == 'T3' then
4439 return true
4440 end
4441 return false
4442end
4443function isBAttackCompatible(p)
4444 local sID = p.sScenarioID
4445 if sID == 'B' or sID == 'B2' or sID == 'S1' or sID == 'T1' or sID == 'T3' then
4446 return true
4447 end
4448 return false
4449end
4450function isTradersCompatible(p)
4451 local sID = p.sScenarioID
4452 if sID == 'B' or sID == 'B2' then
4453 return true
4454 end
4455 return false
4456end
4457
4458--[[ onMapLoad
4459 Called from the control panel, executes general tasks to be done when loading a new map:
4460 - Clean up old object
4461 - Setting map script zone size
4462 - Setting scenario related stuff
4463 - Manage Largest Army and Longest Road cards
4464 - Spawn correct dice and put them in the starting position
4465 - Put the robber back into the starting position
4466 - etc.
4467 Parameters:
4468 Scenario - table:
4469 sID - identifies the scenario
4470 sInfoText - info text to show in the bottom right corner
4471 iVPTarget - target VP for the current scenario
4472 iVPadjCK - additional VP needed to win when playing with C&K
4473 fCatanChitScoreMap - Score to give to a player for a Catan Chit on the map
4474 fCatanChitScoreHand - Score to give to a player for a Catan Chit in their player area
4475 vMapScale - size of the map script zone (so it covers the entire playing area)
4476]]--
4477function onMapLoad(p)
4478 --check if it is an Explorers & Pirates scenario (ID starts with C)
4479 local bIsEaP = (string.sub(p.Scenario.sID,1,1) == 'C')
4480
4481 m_bMapLoaded = false
4482
4483 --remove the quick start overlay
4484 removeOverlay()
4485
4486 --reset dice monitoring queue
4487 for i = 1, #m_CheckDice do
4488 if m_CheckDice[i] ~= nil then
4489 m_CheckDice[i].call('setQueueStatus', {bActive = false})
4490 end
4491 end
4492 m_CheckDice = {}
4493
4494 --reset dice roll statistics
4495 if m_DiceLogger ~= nil then
4496 m_DiceLogger.call('resetStats')
4497 end
4498
4499 m_UnlockOnLoad = {} --list of objects to be unlocked once all hex tiles are loaded
4500 m_bEpidemic = false --for deal function with the EventCard expansion
4501
4502 --[[
4503 Activate the loaded scenario info and rules
4504 --]]
4505 local sScenarioID = p.Scenario.sID
4506 m_CurScenario = {}
4507 m_CurScenario.sID = sScenarioID
4508 m_CurScenario.bUseCK = m_bExpCK
4509 m_CurScenario.bUseFishermen = m_bEnableFishermen
4510 m_CurScenario.bUseEventCards = m_bEnableEventCards
4511 m_CurScenario.bUseWelfare = m_bEnableWelfare
4512 m_CurScenario.bUseFrenemies = (m_iFrenemiesPlayers > 0)
4513 if m_bEnableRivers then
4514 if getTaBBag() == nil then
4515 m_bEnableRivers = false
4516 else
4517 m_CurScenario.bUseRivers = isRiversCompatible({sScenarioID = sScenarioID}) --Only enable the rivers expansion if it works for this scenario
4518 if m_CurScenario.bUseRivers == false then printToAll('[i]The Rivers of Catan[/i] is not compatible with this scenario and will not be loaded.', {1.0,0.8627,0.05098}) end
4519 end
4520 else
4521 m_CurScenario.bUseRivers = false
4522 end
4523 if m_bEnableCaravans then
4524 if getTaBBag() == nil then
4525 m_bEnableCaravans = false
4526 else
4527 m_CurScenario.bUseCaravans = isCaravansCompatible({sScenarioID = sScenarioID}) --Only enable the caravans expansion if it works for this scenario
4528 if m_CurScenario.bUseCaravans == false then printToAll('[i]The Caravans[/i] is not compatible with this scenario and will not be loaded.', {1.0,0.8627,0.05098}) end
4529 end
4530 else
4531 m_CurScenario.bUseCaravans = false
4532 end
4533 if m_bEnableBarbarianAttack then
4534 if getTaBBag() == nil then
4535 m_bEnableBarbarianAttack = false
4536 else
4537 m_bColorDieSpawned = false
4538 m_CurScenario.bUseBAttack = isBAttackCompatible({sScenarioID = sScenarioID}) --Only enable the barbarian attack expansion if it works for this scenario
4539 if m_CurScenario.bUseBAttack == false then printToAll('[i]Barbarian Attack[/i] is not compatible with this scenario and will not be loaded.', {1.0,0.8627,0.05098}) end
4540 end
4541 else
4542 m_CurScenario.bUseBAttack = false
4543 end
4544 if m_bEnableTraders then
4545 if getTaBBag() == nil then
4546 m_bEnableTraders = false
4547 else
4548 m_CurScenario.bUseTraders = isTradersCompatible({sScenarioID = sScenarioID}) --Only enable the barbarian attack expansion if it works for this scenario
4549 if m_CurScenario.bUseTraders == false then printToAll('[i]Traders & Barbarians[/i] is not compatible with this scenario and will not be loaded.', {1.0,0.8627,0.05098}) end
4550 end
4551 else
4552 m_CurScenario.bUseTraders = false
4553 end
4554 if m_bEnableOilsprings then
4555 if sScenarioID ~= 'B' then --the Oilsprings only makes sense with the basic game
4556 m_bEnableOilsprings = false
4557 printToAll('[i]Oilsprings[/i] is not compatible with this scenario and will not be loaded.', {1.0,0.8627,0.05098})
4558 else
4559 --Oilsprings cannot be combined with any T&B scenario
4560 if m_CurScenario.bUseRivers or m_CurScenario.bUseCaravans or m_CurScenario.bUseBAttack or m_CurScenario.bUseTraders then
4561 m_bEnableOilsprings = false
4562 printToAll('[i]Oilsprings[/i] cannot be combined with [i]Traders & Barbarians[/i] campaigns and will not be loaded.', {1.0,0.8627,0.05098})
4563 end
4564 end
4565 end
4566
4567 m_CurScenario.bExtension = false --5-6 Player layout
4568 if p.bExtension == true then
4569 m_CurScenario.bExtension = true
4570 end
4571
4572 --[[
4573 Calculate target VP shown in the bottom right info box
4574 --]]
4575 local iTargetVP = p.Scenario.iVPTarget
4576 --increase the target VP display shown in the infobox when expansions are enabled
4577 if m_bExpCK then iTargetVP = iTargetVP + p.Scenario.iVPadjCK end
4578 if m_iFrenemiesPlayers > 0 then iTargetVP = iTargetVP + 1 end
4579 if m_bEnableHarborMaster then iTargetVP = iTargetVP + 1 end
4580 if m_bEnableOilsprings then iTargetVP = iTargetVP + 2 end
4581 if m_CurScenario.bUseCaravans then iTargetVP = iTargetVP + 2 end
4582 if m_CurScenario.bUseBAttack then iTargetVP = iTargetVP + 2 end
4583 if m_CurScenario.bUseTraders then iTargetVP = iTargetVP + 3 end
4584 setNotes(string.gsub(p.Scenario.sInfoText, '&VP&', iTargetVP))
4585
4586 --[[
4587 Reset everything in the map script zone:
4588 - highlighting of tokens (reset list of active tokens -> repopulated when new tokens are spawned)
4589 - score keeping
4590 --]]
4591 if m_MapScript ~= nil then
4592 m_MapScript.call('onMapLoad')
4593
4594 m_MapScript.call('setCatanChitScore', { fOnMap = p.Scenario.fCatanChitScoreMap, fOnHand = p.Scenario.fCatanChitScoreHand })
4595
4596 m_MapScript.setScale(p.vMapScale)
4597
4598 m_MapScript.call('clearCounters')
4599 m_MapScript.call('setCountersEnabled', {bEnable = false})
4600 end
4601
4602 m_iLoadingHexes = 0
4603 m_iSpawnedHexes = 0
4604
4605 --[[
4606 Find objects to be deleted or repositioned
4607 --]]
4608 local LargestArmy = nil
4609 local LongestRoad = nil
4610 local Harbormaster = nil
4611 local Robber = nil
4612 local BuildingCosts = {}
4613 local AllObj = getAllObjects()
4614 for i, Obj in ipairs(AllObj) do
4615 local sObjType = Obj.getVar('sObjType')
4616 local sObjName = Obj.getName()
4617 if sObjType == 'hex' or sObjType == 'numtoken' or sObjType == 'border' or sObjType == 'harbor' or sObjType == 'seafarers_add' or
4618 sObjType == 'frenemies' or sObjType == 'channel' or sObjType == 'helpers' or sObjType == 'fishermen' or
4619 sObjType == 'rivers' or sObjType == 'caravans' or sObjType == 'battack' or sObjType == 'traders' or sObjType == 'oilsprings' or
4620 sObjType == 'welfare' or
4621 sObjType == 'eap' then
4622 destroyObject(Obj)
4623 elseif Obj.tag == 'Deck' or Obj.tag == 'Card' then
4624 destroyObject(Obj)
4625 elseif sObjName == 'Treasure Chest' or sObjName == 'Dragon' or sObjName == 'Barbarian Ship' then
4626 destroyObject(Obj)
4627 elseif sObjName == 'Robber' then
4628 Robber = Obj
4629 elseif sObjName == 'Largest Army' then
4630 LargestArmy = Obj
4631 elseif sObjName == 'Longest Road' then
4632 LongestRoad = Obj
4633 elseif sObjName == 'Harbormaster' then
4634 Harbormaster = Obj
4635 elseif sObjName == 'Longest Shipping Line' then
4636 destroyObject(Obj)
4637 elseif sObjName == 'Building Costs' then
4638 table.insert(BuildingCosts, Obj)
4639 elseif sObjName == 'Blocked Settlement' then
4640 Obj.setName('Settlement')
4641 elseif sObjName == 'Blocked City' then
4642 Obj.setName('City')
4643 elseif sObjName == 'Settlement (Connected to Caravan)' then
4644 Obj.setName('Settlement')
4645 elseif sObjName == 'City (Connected to Caravan)' then
4646 Obj.setName('City')
4647 elseif string.sub(sObjName, 1, 10) == 'Catan Chit' then --Delete all existing Catan Chits
4648 destroyObject(Obj)
4649 elseif string.sub(sObjName, 1, 10) == 'Metropolis' then
4650 destroyObject(Obj)
4651 elseif string.sub(Obj.getDescription(),1,15) == 'Barbarian route' then
4652 destroyObject(Obj) --destroy and respawn it in the proper position from the control panel if needed
4653 elseif Obj.getVar('sUpgradeType') ~= nil then --destroy and respawn city upgrade charts
4654 destroyObject(Obj)
4655 end
4656 end
4657
4658 --[[
4659 Move the robber out of the way of spawning hexes to the starting position or spawn / delete it if necessary
4660 --]]
4661 local bUseRobber = true
4662 if sScenarioID == 'S7' or sScenarioID == 'T4' or m_CurScenario.bUseBAttack or m_CurScenario.bUseTraders or bIsEaP then
4663 bUseRobber = false
4664 end
4665 if Robber ~= nil then
4666 if bUseRobber == false then
4667 destroyObject(Robber)
4668 else
4669 Robber.setRotation({0.0,0.0,0.0})
4670 Robber.setPosition({x=-31.0,y=0.97,z=1.81})
4671 end
4672 elseif bUseRobber then --spawn new robber
4673 local params = {}
4674 params.type = 'Custom_Model'
4675 params.position = {-31.0, 0.97, 1.81}
4676 params.rotation = {0.0,0.0,0.0}
4677 params.scale = {1.25, 1.25, 1.25}
4678 local custom = {}
4679 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379964516/39142B17D65D9A9EA7175C2DDA61B52BF6FEE730/'
4680 custom.diffuse = ''
4681 custom.type = 1 --figurine
4682 custom.material = 1 --wood
4683 custom.specular_intensity = 0.04
4684
4685 m_Robber = spawnObject(params)
4686 m_Robber.setCustomObject(custom)
4687 m_Robber.setColorTint({0.12,0.12,0.12})
4688 m_Robber.setName('Robber')
4689 m_Robber.setDescription('The Robber has to be moved when a 7 is rolled or a Knight is played. Put it in the center of a resource tile and steal a resource card from any 1 player having settlements/cities on this tile.\nAs long as the robber is blocking a hex, no one will gain resources from this field.')
4690 end
4691
4692 --[[
4693 Position/spawn/delete the special victory point cards (longest road, largest army, harbormaster card)
4694 --]]
4695 local vPosLR = {-25.6,1.1,-10.0}
4696 if isCKenabled() then
4697 vPosLR[3] = vPosLR[3] - 2.4
4698 if m_bEnableFishermen then
4699 vPosLR[1] = vPosLR[1] + 5.0
4700 end
4701 else
4702 if m_bEnableFishermen then
4703 vPosLR[3] = vPosLR[3] - 2.6
4704 end
4705 end
4706 if LongestRoad ~= nil then
4707 if sScenarioID == 'S6' or sScenarioID == 'S7' or m_CurScenario.bUseTraders or bIsEaP then --no longest road in these scenarios
4708 destroyObject(LongestRoad)
4709 else -- put it in the proper position
4710 LongestRoad.setRotation({0.0,90.0,0.0})
4711 LongestRoad.setPosition(vPosLR)
4712 LongestRoad.setLock(true)
4713 addToUnlockList({Obj = LongestRoad})
4714 end
4715 else
4716 if sScenarioID ~= 'S6' and sScenarioID ~= 'S7' and m_CurScenario.bUseTraders == false and bIsEaP == false then
4717 --longest road does not exist but we need it -> spawn new one
4718 spawnLongestRoadCard(vPosLR, {0.0,90.0,0.0})
4719 end
4720 end
4721
4722 local vPosLA = {-26.2,0.96,-10.0}
4723 if m_bEnableFishermen then
4724 vPosLA[3] = vPosLA[3] - 2.6
4725 end
4726 if LargestArmy ~= nil then
4727 if isCKenabled() or sScenarioID == 'S7' or sScenarioID == 'T4' or m_CurScenario.bUseBAttack or bIsEaP then --no largest army with the C&K expansion and in scenario 7 & T4
4728 destroyObject(LargestArmy)
4729 else -- put it in the proper position
4730 LargestArmy.setRotation({0.0,90.0,0.0})
4731 LargestArmy.setPosition(vPosLA)
4732 LargestArmy.setLock(true)
4733 addToUnlockList({Obj = LargestArmy})
4734 end
4735 else
4736 if isCKenabled() == false and sScenarioID ~= 'S7' and sScenarioID ~= 'T4' and m_CurScenario.bUseBAttack == false and bIsEaP == false then
4737 --largest army does not exist but we need it -> spawn new one
4738 spawnLargestArmyCard(vPosLA, {0.0,90.0,0.0})
4739 end
4740 end
4741
4742 --harbor master mini expansion
4743 if m_bEnableHarborMaster == false then
4744 if Harbormaster ~= nil then
4745 destroyObject(Harbormaster)
4746 end
4747 else
4748 local vPosHM = {-25.0,1.3,-10.0}
4749 if isCKenabled() then
4750 vPosHM[1] = vPosHM[1] - 0.6
4751 vPosHM[3] = vPosHM[3] - 3.2
4752 if m_bEnableFishermen then
4753 vPosHM[1] = vPosHM[1] + 5.0
4754 end
4755 else
4756 if m_bEnableFishermen then
4757 vPosHM[3] = vPosHM[3] - 2.6
4758 end
4759 end
4760 if Harbormaster ~= nil then
4761 Harbormaster.setRotation({0.0,90.0,0.0})
4762 Harbormaster.setPosition(vPosHM)
4763 Harbormaster.setLock(true)
4764 addToUnlockList({Obj = Harbormaster})
4765 else
4766 spawnHarbormasterCard(vPosHM, {0.0,90.0,0.0})
4767 end
4768 end
4769
4770 --[[
4771 Select the proper state for the building costs cards
4772 TODO: adjust if there ever is a proper "getState" function
4773 --]]
4774 for i, Card in ipairs(BuildingCosts) do
4775 local iAvailableState = Card.getStates()[1].id -- getState returns all _other_ available states
4776 if isCKenabled() == true then
4777 if iAvailableState == 2 then --means state 2 is not set currently
4778 Card.setState(2)
4779 end
4780 else
4781 if iAvailableState == 1 then
4782 Card.setState(1)
4783 end
4784 end
4785 --Explorers & Pirates may hide the building costs --> reset it
4786 local vPos = Card.getPosition()
4787 Card.setPosition({vPos.x,0.96,vPos.z})
4788 Card.tooltip = true
4789 Card.interactable = true
4790 end
4791
4792 --[[
4793 Position dice and reset color
4794 --]]
4795 if m_Die1 ~= nil then
4796 m_Die1.setRotation({-90.0,0.0,0.0})
4797 m_Die1.setPosition({x=-31.0,y=1.373,z=0.514})
4798 else
4799 if m_bEnableEventCards == false then
4800 m_Die1 = spawnDie(1, {x=-31.0,y=1.373,z=0.514}, {-90.0,0.0,0.0})
4801 end
4802 end
4803 if m_Die2 ~= nil then
4804 m_Die2.setRotation({-90.0,0.0,0.0})
4805 m_Die2.setPosition({x=-31.0,y=1.373,z=-0.5})
4806 m_Die2.setColorTint({0.90588,0.82745,0.72157})
4807 else
4808 if m_bEnableEventCards == false or isCKenabled() then
4809 m_Die2 = spawnDie(2, {x=-31.0,y=1.373,z=-0.5}, {-90.0,0.0,0.0})
4810 end
4811 end
4812 --remove event die if no longer needed
4813 if isCKenabled() == false and m_EventDie ~= nil then
4814 destroyObject(m_EventDie)
4815 end
4816
4817 --[[
4818 Spawn selected expansions
4819 A coroutine is prepared for each of the selected expansions they are each given a reference to the coroutine for the
4820 next expansion to be loaded. The first routine is started and will automatically trigger the next one once it is done loading.
4821 --]]
4822 local SpawnRoutines = {}
4823 --Cities & Knights
4824 if isCKenabled() then table.insert(SpawnRoutines, spawnCKExpansion()) end
4825 --The Fishermen of Catan
4826 if m_bEnableFishermen then table.insert(SpawnRoutines, spawnFishermenExpansion()) end
4827 --Welfare Variant
4828 if m_bEnableWelfare then table.insert(SpawnRoutines, spawnWelfareExpansion()) end
4829 --The Rivers of Catan
4830 if m_CurScenario.bUseRivers then table.insert(SpawnRoutines, spawnRiversExpansion()) end
4831 --The Caravans
4832 if m_CurScenario.bUseCaravans then table.insert(SpawnRoutines, spawnCaravansExpansion()) end
4833 --Barbarian Attack
4834 if m_CurScenario.bUseBAttack then table.insert(SpawnRoutines, spawnBarbarianAttackExpansion()) end
4835 --Traders & Barbarians
4836 if m_CurScenario.bUseTraders then table.insert(SpawnRoutines, spawnTradersAndBarbariansExpansion()) end
4837 --Frenemies
4838 if m_iFrenemiesPlayers > 0 then table.insert(SpawnRoutines, spawnFrenemiesExpansion()) end
4839 --Oilsprings
4840 if m_bEnableOilsprings then table.insert(SpawnRoutines, spawnOilspringsExpansion()) end
4841 --Helpers of Catan
4842 if m_bEnableHelpers then table.insert(SpawnRoutines, spawnHelpersExpansion()) end
4843 --Catan Event Cards
4844 if m_bEnableEventCards then table.insert(SpawnRoutines, spawnEventCardsExpansion()) end
4845
4846 --pass each routine the next routine to start when it finishes
4847 for i = 1, #SpawnRoutines do
4848 coroutine.resume(SpawnRoutines[i], SpawnRoutines[i+1])
4849 end
4850 --trigger the first spawn function to spawn all selected ones
4851 if #SpawnRoutines > 0 then
4852 coroutine.resume(SpawnRoutines[1])
4853 end
4854end
4855
4856--Neighboring tile definition
4857function getNBRS() return NBRS end
4858NBRS = { [1] = {['NE'] = 67, ['E'] = 2, ['SE'] = 10, ['SW'] = 9, ['W'] = 97, ['NW'] = 66},
4859 [2] = {['NE'] = 68, ['E'] = 3, ['SE'] = 11, ['SW'] = 10, ['W'] = 1, ['NW'] = 67},
4860 [3] = {['NE'] = 69, ['E'] = 4, ['SE'] = 12, ['SW'] = 11, ['W'] = 2, ['NW'] = 68},
4861 [4] = {['NE'] = 70, ['E'] = 5, ['SE'] = 13, ['SW'] = 12, ['W'] = 3, ['NW'] = 69},
4862 [5] = {['NE'] = 71, ['E'] = 6, ['SE'] = 14, ['SW'] = 13, ['W'] = 4, ['NW'] = 70},
4863 [6] = {['NE'] = 72, ['E'] = 7, ['SE'] = 15, ['SW'] = 14, ['W'] = 5, ['NW'] = 71},
4864 [7] = {['NE'] = 73, ['E'] = 8, ['SE'] = 16, ['SW'] = 15, ['W'] = 6, ['NW'] = 72},
4865 [8] = {['NE'] = 74, ['E'] = 75, ['SE'] = 17, ['SW'] = 16, ['W'] = 7, ['NW'] = 73},
4866 [9] = {['NE'] = 1, ['E'] = 10, ['SE'] = 19, ['SW'] = 18, ['W'] = 96, ['NW'] = 97},
4867 [10] = {['NE'] = 2, ['E'] = 11, ['SE'] = 20, ['SW'] = 19, ['W'] = 9, ['NW'] = 1},
4868 [11] = {['NE'] = 3, ['E'] = 12, ['SE'] = 21, ['SW'] = 20, ['W'] = 10, ['NW'] = 2},
4869 [12] = {['NE'] = 4, ['E'] = 13, ['SE'] = 22, ['SW'] = 21, ['W'] = 11, ['NW'] = 3},
4870 [13] = {['NE'] = 5, ['E'] = 14, ['SE'] = 23, ['SW'] = 22, ['W'] = 12, ['NW'] = 4},
4871 [14] = {['NE'] = 6, ['E'] = 15, ['SE'] = 24, ['SW'] = 23, ['W'] = 13, ['NW'] = 5},
4872 [15] = {['NE'] = 7, ['E'] = 16, ['SE'] = 25, ['SW'] = 24, ['W'] = 14, ['NW'] = 6},
4873 [16] = {['NE'] = 8, ['E'] = 17, ['SE'] = 26, ['SW'] = 25, ['W'] = 15, ['NW'] = 7},
4874 [17] = {['NE'] = 75, ['E'] = 76, ['SE'] = 27, ['SW'] = 26, ['W'] = 16, ['NW'] = 8},
4875 [18] = {['NE'] = 9, ['E'] = 19, ['SE'] = 29, ['SW'] = 28, ['W'] = 95, ['NW'] = 96},
4876 [19] = {['NE'] = 10, ['E'] = 20, ['SE'] = 30, ['SW'] = 29, ['W'] = 18, ['NW'] = 9},
4877 [20] = {['NE'] = 11, ['E'] = 21, ['SE'] = 31, ['SW'] = 30, ['W'] = 19, ['NW'] = 10},
4878 [21] = {['NE'] = 12, ['E'] = 22, ['SE'] = 32, ['SW'] = 31, ['W'] = 20, ['NW'] = 11},
4879 [22] = {['NE'] = 13, ['E'] = 23, ['SE'] = 33, ['SW'] = 32, ['W'] = 21, ['NW'] = 12},
4880 [23] = {['NE'] = 14, ['E'] = 24, ['SE'] = 34, ['SW'] = 33, ['W'] = 22, ['NW'] = 13},
4881 [24] = {['NE'] = 15, ['E'] = 25, ['SE'] = 35, ['SW'] = 34, ['W'] = 23, ['NW'] = 14},
4882 [25] = {['NE'] = 16, ['E'] = 26, ['SE'] = 36, ['SW'] = 35, ['W'] = 24, ['NW'] = 15},
4883 [26] = {['NE'] = 17, ['E'] = 27, ['SE'] = 37, ['SW'] = 36, ['W'] = 25, ['NW'] = 16},
4884 [27] = {['NE'] = 76, ['E'] = 77, ['SE'] = 38, ['SW'] = 37, ['W'] = 26, ['NW'] = 17},
4885 [28] = {['NE'] = 18, ['E'] = 29, ['SE'] = 39, ['SW'] = 93, ['W'] = 94, ['NW'] = 95},
4886 [29] = {['NE'] = 19, ['E'] = 30, ['SE'] = 40, ['SW'] = 39, ['W'] = 28, ['NW'] = 18},
4887 [30] = {['NE'] = 20, ['E'] = 31, ['SE'] = 41, ['SW'] = 40, ['W'] = 29, ['NW'] = 19},
4888 [31] = {['NE'] = 21, ['E'] = 32, ['SE'] = 42, ['SW'] = 41, ['W'] = 30, ['NW'] = 20},
4889 [32] = {['NE'] = 22, ['E'] = 33, ['SE'] = 43, ['SW'] = 42, ['W'] = 31, ['NW'] = 21},
4890 [33] = {['NE'] = 23, ['E'] = 34, ['SE'] = 44, ['SW'] = 43, ['W'] = 32, ['NW'] = 22},
4891 [34] = {['NE'] = 24, ['E'] = 35, ['SE'] = 45, ['SW'] = 44, ['W'] = 33, ['NW'] = 23},
4892 [35] = {['NE'] = 25, ['E'] = 36, ['SE'] = 46, ['SW'] = 45, ['W'] = 34, ['NW'] = 24},
4893 [36] = {['NE'] = 26, ['E'] = 37, ['SE'] = 47, ['SW'] = 46, ['W'] = 35, ['NW'] = 25},
4894 [37] = {['NE'] = 27, ['E'] = 38, ['SE'] = 48, ['SW'] = 47, ['W'] = 36, ['NW'] = 26},
4895 [38] = {['NE'] = 77, ['E'] = 78, ['SE'] = 79, ['SW'] = 48, ['W'] = 37, ['NW'] = 27},
4896 [39] = {['NE'] = 29, ['E'] = 40, ['SE'] = 49, ['SW'] = 92, ['W'] = 93, ['NW'] = 28},
4897 [40] = {['NE'] = 30, ['E'] = 41, ['SE'] = 50, ['SW'] = 49, ['W'] = 39, ['NW'] = 29},
4898 [41] = {['NE'] = 31, ['E'] = 42, ['SE'] = 51, ['SW'] = 50, ['W'] = 40, ['NW'] = 30},
4899 [42] = {['NE'] = 32, ['E'] = 43, ['SE'] = 52, ['SW'] = 51, ['W'] = 41, ['NW'] = 31},
4900 [43] = {['NE'] = 33, ['E'] = 44, ['SE'] = 53, ['SW'] = 52, ['W'] = 42, ['NW'] = 32},
4901 [44] = {['NE'] = 34, ['E'] = 45, ['SE'] = 54, ['SW'] = 53, ['W'] = 43, ['NW'] = 33},
4902 [45] = {['NE'] = 35, ['E'] = 46, ['SE'] = 55, ['SW'] = 54, ['W'] = 44, ['NW'] = 34},
4903 [46] = {['NE'] = 36, ['E'] = 47, ['SE'] = 56, ['SW'] = 55, ['W'] = 45, ['NW'] = 35},
4904 [47] = {['NE'] = 37, ['E'] = 48, ['SE'] = 57, ['SW'] = 56, ['W'] = 46, ['NW'] = 36},
4905 [48] = {['NE'] = 38, ['E'] = 79, ['SE'] = 80, ['SW'] = 57, ['W'] = 47, ['NW'] = 37},
4906 [49] = {['NE'] = 40, ['E'] = 50, ['SE'] = 58, ['SW'] = 91, ['W'] = 92, ['NW'] = 39},
4907 [50] = {['NE'] = 41, ['E'] = 51, ['SE'] = 59, ['SW'] = 58, ['W'] = 49, ['NW'] = 40},
4908 [51] = {['NE'] = 42, ['E'] = 52, ['SE'] = 60, ['SW'] = 59, ['W'] = 50, ['NW'] = 41},
4909 [52] = {['NE'] = 43, ['E'] = 53, ['SE'] = 61, ['SW'] = 60, ['W'] = 51, ['NW'] = 42},
4910 [53] = {['NE'] = 44, ['E'] = 54, ['SE'] = 62, ['SW'] = 61, ['W'] = 52, ['NW'] = 43},
4911 [54] = {['NE'] = 45, ['E'] = 55, ['SE'] = 63, ['SW'] = 62, ['W'] = 53, ['NW'] = 44},
4912 [55] = {['NE'] = 46, ['E'] = 56, ['SE'] = 64, ['SW'] = 63, ['W'] = 54, ['NW'] = 45},
4913 [56] = {['NE'] = 47, ['E'] = 57, ['SE'] = 65, ['SW'] = 64, ['W'] = 55, ['NW'] = 46},
4914 [57] = {['NE'] = 48, ['E'] = 80, ['SE'] = 81, ['SW'] = 65, ['W'] = 56, ['NW'] = 47},
4915 [58] = {['NE'] = 50, ['E'] = 59, ['SE'] = 89, ['SW'] = 90, ['W'] = 91, ['NW'] = 49},
4916 [59] = {['NE'] = 51, ['E'] = 60, ['SE'] = 88, ['SW'] = 89, ['W'] = 58, ['NW'] = 50},
4917 [60] = {['NE'] = 52, ['E'] = 61, ['SE'] = 87, ['SW'] = 88, ['W'] = 59, ['NW'] = 51},
4918 [61] = {['NE'] = 53, ['E'] = 62, ['SE'] = 86, ['SW'] = 87, ['W'] = 60, ['NW'] = 52},
4919 [62] = {['NE'] = 54, ['E'] = 63, ['SE'] = 85, ['function getModVersion() return 1.43 end --version of this mod (for compatibility checks with external tools)
4920
4921math.randomseed( os.time() )
4922
4923m_iEventCardResult = 0 --"dice" result for event cards
4924
4925m_Die1 = nil
4926function getDieOne() return m_Die1 end
4927m_Die2 = nil
4928function getDieTwo() return m_Die2 end
4929m_EventDie = nil
4930function getEventDie() return m_EventDie end
4931--bag containing items and code for explorers & pirates expansion
4932m_EaPBag = nil
4933function setEaPBag(p)
4934 if m_EaPBag ~= nil then --remove old EaP Bag so they don't start to accumulate if multiple game boxes are spawned
4935 m_EaPBag.call('silentDestroy')
4936 end
4937 m_EaPBag = p.BagObj
4938end
4939function getEaPBag() return m_EaPBag end
4940--bag containing items for the Traders & Barbarians campaigns
4941m_TaBBag = nil
4942function setTaBBag(p)
4943 if m_TaBBag ~= nil then --remove old TaB Bag so they don't start to accumulate if multiple game boxes are spawned
4944 m_TaBBag.destruct()
4945 end
4946 m_TaBBag = p.BagObj
4947end
4948function getTaBBag() return m_TaBBag end
4949
4950m_MapScript = nil
4951
4952m_Robber = nil
4953
4954-- Item spawn bags
4955m_ResourceDecks = {Bag = 'dbd7df', Deck = {['Lumber'] = 'b40904', ['Bricks'] = 'f24c89', ['Wool'] = '89b19c', ['Grain'] = 'a69e8e', ['Ore'] = '1ec6da'}}
4956m_ResourceDecks_ext = {Bag = 'b2dbfd', Deck = {['Lumber'] = 'a875da', ['Bricks'] = '761ae4', ['Wool'] = '0212ac', ['Grain'] = '2fd393', ['Ore'] = 'e29e0d'}}
4957m_DevCardDecks = {Bag = '789036', Deck = {['2P'] = '807c38', ['34P'] = '138ade', ['56P'] = '8581dd', ['34P_S7'] = '5f0252', ['56P_S7'] = '4f7c58'}}
4958m_HelpersItems = {Bag = 'ba7910', Item = {['Deck'] = 'b08360', ['Board'] = 'a1982d'}}
4959m_FishermenItems = {Bag = '240e82', Item = {['Board'] = '8ba2d4', ['Deck'] = '8a62f6', ['Deck_ext'] = 'e9e546'}}
4960m_DeckEventCards = 'de471f'
4961m_OilspDisasterBoard = 'feee7b'
4962--C&K
4963m_CKitems = {Bag = '818554', Item = {['DeckPolitics'] = '688db9', ['DeckScience'] = '7feefa', ['DeckTrade'] = '1c5356', ['DeckDefender'] = '361e4e', ['DeckDefender_ext'] = '5e14df',
4964 ['ChartPolitics'] = '85dcd8', ['ChartScience'] = 'cd7181', ['ChartTrade'] = 'e51b9c', }
4965 }
4966m_CommodityDecks = {Bag = 'fe36b2', Deck = {['Paper'] = 'e47fa7', ['Cloth'] = '428625', ['Coins'] = '37560d'}}
4967m_CommodityDecks_ext = {Bag = 'f2c12f', Deck = {['Paper'] = '8817c0', ['Cloth'] = '7b2437', ['Coins'] = '851e28'}}
4968m_BarbarianTile = 'bb752d'
4969
4970--hex tiles spawn info
4971m_HexBag = 'd969d2'
4972m_HexObjects = {
4973 ['ocean'] = { iNext = 1, Tile = {'5569d9'} },
4974 ['desert'] = { iNext = 1, Tile = {'796f8c'} },
4975 ['gold'] = { iNext = 1, Tile = {'8fb9e0'} },
4976 ['s6_desert'] = { iNext = 1, Tile = {'a4bcb9'} }, --special snap points for Scenario 6 tribe islands
4977 ['s6_gold'] = { iNext = 1, Tile = {'211572'} }, --special snap points for Scenario 6 tribe islands
4978 ['ocean_skull'] = { iNext = 1, Tile = {'40a31f'} },
4979 ['forest'] = { iNext = 1, Tile = {'be454e', 'cfe8a1', 'eeefbd', '4155e2'} },
4980 ['hills'] = { iNext = 1, Tile = {'beae30', '140046', '0fd3e3', '75dc80'} },
4981 ['pasture'] = { iNext = 1, Tile = {'9a0be3', 'dce3b4', '5cdc3a', 'fc5a9b'} },
4982 ['fields'] = { iNext = 1, Tile = {'4522c2', '829f12', '437304', '1e6e2d'} },
4983 ['mountain'] = { iNext = 1, Tile = {'818cb1', 'a39c2f', 'c6098d'} },
4984 ['oilsource'] = {iNext = 1, Tile = {'09bfd7'} },
4985}
4986function getHexObjectList() return m_HexObjects end
4987function setHexObjectList(List) m_HexObjects = List end
4988m_iSpawnedHexes = 0
4989m_iLoadingHexes = 999 --TODO: multiple hex tiles have the same GUID in the frame they are spawned in and even when their onLoad event is triggered ... so just count them to figure out when they are all loaded
4990
4991m_UnlockOnLoad = {}
4992function addToUnlockList(p) table.insert(m_UnlockOnLoad, p.Obj) end
4993
4994m_PlayerColors = { --(it is actually the colors of the players items...)
4995 ['Orange'] = { r = 1.0, g = 0.544700563, b = 0.188231677 },
4996 ['Red'] = { r = 0.7921569, g = 0.0, b = 0.0 },
4997 ['White'] = { r = 0.99649936, g = 1.0, b = 0.996506333 },
4998 ['Purple'] = { r = 0.5528258, g = 0.164702222, b = 1.0 },
4999 ['Blue'] = { r = 0.149015814, g = 0.419941843, b = 0.8235495 },
5000 ['Green'] = { r = 0.201173037, g = 0.8549019, b = 0.188231409 },
5001}
5002function getPlayerColor(p) return m_PlayerColors[p.sPlayer] end
5003
5004-- Control panel related globals
5005m_bRandomizeMap = false
5006function getStateRandomize() return m_bRandomizeMap end
5007function setStateRandomize(p) m_bRandomizeMap = p.bRandom end
5008m_bHiddenNumbers = false
5009function getStateHiddenNumbers() return m_bHiddenNumbers end
5010function setStateHiddenNumbers(p) m_bHiddenNumbers = p.bHide end
5011m_bHiddenTiles = false
5012function getStateHiddenTiles() return m_bHiddenTiles end
5013function setStateHiddenTiles(p) m_bHiddenTiles = p.bHide end
5014m_bExpCK = false
5015function getBtnStateCK() return m_bExpCK end
5016function setBtnStateCK(p) m_bExpCK = p.bEnable end
5017m_bMapLoaded = false
5018function isMapLoaded() return m_bMapLoaded end
5019m_bAutoSort = true
5020function getAutoSort() return m_bAutoSort end
5021function setAutoSort(p) m_bAutoSort = p.bEnable end
5022m_bDeckTooltip = false
5023function getDeckTooltip() return m_bDeckTooltip end
5024function setDeckTooltip(p)
5025 m_bDeckTooltip = p.bEnable
5026 --find all decks with a resource card and enable / disable the tooltip
5027 local AllObj = getAllObjects()
5028 for i, Obj in pairs(AllObj) do
5029 if Obj.tag == 'Deck' then
5030 local sCardName = Obj.getObjects()[1].nickname
5031 if sCardName == 'Lumber' or sCardName == 'Bricks' or sCardName == 'Wool' or sCardName == 'Grain' or sCardName == 'Ore' or
5032 sCardName == 'Paper' or sCardName == 'Cloth' or sCardName == 'Coins' then
5033 Obj.tooltip = m_bDeckTooltip
5034 end
5035 end
5036 end
5037end
5038m_bDisplayCardCount = true
5039function getDisplayCardCount() return m_bDisplayCardCount end
5040function setDisplayCardCount(p) m_bDisplayCardCount = p.bEnable end
5041m_bDiceDealButton = true --show deal button next to dice after rolling
5042m_iDealCards = 0 --selected option on CP page 6
5043
5044function getDealCardsSetting() return m_iDealCards end
5045function setDealCardsSetting(p)
5046 m_iDealCards = p.iSetting
5047 if m_iDealCards == 0 then --Both fixed deal button + deal button next to rolled dice
5048 m_MapScript.call('showDealButton') --make sure the fixed deal button is enabled
5049 m_bDiceDealButton = true
5050 elseif m_iDealCards == 1 then --Fixed deal button only
5051 m_MapScript.call('showDealButton')
5052 m_MapScript.call('hideDealButtonDie') --remove any active buttons
5053 m_bDiceDealButton = false
5054 elseif m_iDealCards == 2 then --dice button only
5055 m_MapScript.call('hideDealButton')
5056 m_bDiceDealButton = true
5057 elseif m_iDealCards == 3 then --none
5058 m_MapScript.call('hideDealButton')
5059 m_MapScript.call('hideDealButtonDie') --remove any active buttons
5060 m_bDiceDealButton = false
5061 end
5062end
5063
5064--Settings for scenario 9 (randomized map)
5065m_iS9NumGold = -1 --default
5066function getStateNumGold() return m_iS9NumGold end
5067function setStateNumGold(p) m_iS9NumGold = p.iNew end
5068m_iS9PercentHidden = 0
5069function getStatePercentHidden() return m_iS9PercentHidden end
5070function setStatePercentHidden(p) m_iS9PercentHidden = p.iNew end
5071
5072--Mini Expansions
5073m_iFrenemiesPlayers = 0
5074function getStateFrenemies() return m_iFrenemiesPlayers end
5075function setStateFrenemies(p) m_iFrenemiesPlayers = p.iNew end
5076m_bEnableHelpers = false
5077function getStateHelpers() return m_bEnableHelpers end
5078function setStateHelpers(p) m_bEnableHelpers = p.bEnable end
5079m_bEnableFishermen = false
5080function getStateFishermen() return m_bEnableFishermen end
5081function setStateFishermen(p) m_bEnableFishermen = p.bEnable end
5082m_FishermenCardBoard = nil
5083function getFishermenCardBoard() return m_FishermenCardBoard end
5084m_bEnableHarborMaster = false
5085function getStateHarborMaster() return m_bEnableHarborMaster end
5086function setStateHarborMaster(p) m_bEnableHarborMaster = p.bEnable end
5087m_bEnableWelfare = false
5088function getStateWelfare() return m_bEnableWelfare end
5089function setStateWelfare(p) m_bEnableWelfare = p.bEnable end
5090m_bEnableEventCards = false
5091function getStateEventCards() return m_bEnableEventCards end
5092function setStateEventCards(p) m_bEnableEventCards = p.bEnable end
5093m_bEpidemic = false
5094function isEpidemic() return m_bEpidemic end
5095m_bEnableRivers = false
5096function getStateRivers() return m_bEnableRivers end
5097function setStateRivers(p) m_bEnableRivers = p.bEnable end
5098m_bEnableCaravans = false
5099function getStateCaravans() return m_bEnableCaravans end
5100function setStateCaravans(p) m_bEnableCaravans = p.bEnable end
5101m_bEnableBarbarianAttack = false
5102m_bColorDieSpawned = false --switch to make the color die spawn only on one of the castle fields when two are in the game (5-6 players)
5103function getStateBarbarianAttack() return m_bEnableBarbarianAttack end
5104function setStateBarbarianAttack(p) m_bEnableBarbarianAttack = p.bEnable end
5105m_bEnableTraders = false
5106function getStateTraders() return m_bEnableTraders end
5107function setStateTraders(p) m_bEnableTraders = p.bEnable end
5108m_bEnableOilsprings = false
5109function getStateOilsprings() return m_bEnableOilsprings end
5110function setStateOilsprings(p) m_bEnableOilsprings = p.bEnable end
5111
5112--Restriction for the deal function to work only once per roll
5113m_bAllowDeal = false
5114function isDealAllowed() return m_bAllowDeal end
5115function setAllowDeal(p) m_bAllowDeal = p.bEnable end
5116
5117--Dice monitoring to control token highlighting
5118m_CheckDice = {}
5119function queueDieCheck(p)
5120 if m_bMapLoaded then
5121 local bAdd = true
5122 for i, Die in pairs(m_CheckDice) do --make sure each die is in the list only once
5123 if Die == p.DieObj then
5124 bAdd = false
5125 break
5126 elseif Die == nil then --if we detect a nil object remove it to make sure we do not disable the update function if #m_CheckDie returns 0
5127 table.remove(m_CheckDice, i)
5128 end
5129 end
5130 if bAdd then
5131 table.insert(m_CheckDice, p.DieObj)
5132 end
5133 return true
5134 else --map is still loading, die not in monitoring queue
5135 return false
5136 end
5137end
5138
5139--Tool C/K counter: Objects to be informed when something is done to knight tokens (City/Knights counter)
5140m_KnightEventReceiver = {}
5141function registerKnightEventReceiver(p) table.insert(m_KnightEventReceiver, p.Obj) end
5142function reportKnightCollision()
5143 for i, Receiver in pairs(m_KnightEventReceiver) do
5144 if Receiver ~= nil then
5145 Receiver.call('onKnightCollision')
5146 end
5147 end
5148end
5149function resetCKcounter()
5150 for i, Receiver in pairs(m_KnightEventReceiver) do
5151 if Receiver ~= nil then
5152 Receiver.call('resetCounter')
5153 end
5154 end
5155end
5156
5157--Tool Dice Logger: Object to be informed when dice have been rolled / cards dealt (dice logger)
5158m_DiceLogger = nil
5159function registerDiceLogger(p)
5160 if m_DiceLogger ~= nil then
5161 m_DiceLogger.call('setStatus', {bActive = false}) --tell the old logger that it will no longer be updated
5162 end
5163 m_DiceLogger = p.Obj
5164end
5165function onCardsDealt(p) --called from map script when the deal cards function is used
5166 if m_DiceLogger ~= nil then
5167 m_DiceLogger.call('onCardsDealt', {iResult = p.iResult})
5168 end
5169
5170 --interface for dice event receivers (automatic barbarian ship / E&P)
5171 for i, Receiver in ipairs(m_DiceEventReceiver) do
5172 if Receiver ~= nil then
5173 Receiver.call('onCardsDealt', {iResult = p.iResult, Yields = p.Yields})
5174 end
5175 end
5176end
5177
5178--Tool Dice event receiver: Objects to be informed when dice have been rolled (auto moving barbarian ship)
5179m_DiceEventReceiver = {}
5180function registerDiceEventReceiver(p) table.insert(m_DiceEventReceiver, p.Obj) end
5181function removeDiceEventReceiver(p)
5182 for i, Receiver in pairs(m_DiceEventReceiver) do
5183 if Receiver == p.Obj then
5184 table.remove(m_DiceEventReceiver, i)
5185 break
5186 end
5187 end
5188end
5189
5190m_CurScenario = {}
5191
5192--[[ The OnLoad function. This is called after everything in the game save finishes loading.
5193Most of your script code goes here. --]]
5194function onLoad(save_state)
5195-- get the scripting zone object that handles map related stuff
5196 m_MapScript = getObjectFromGUID('42fd8e')
5197
5198-- load saved data
5199 local saved_data = JSON.decode(save_state)
5200 if saved_data ~= nil then
5201 if saved_data.m_CurScenario ~= nil then m_CurScenario = saved_data.m_CurScenario end
5202 if saved_data.m_bRandomizeMap ~= nil then m_bRandomizeMap = saved_data.m_bRandomizeMap end
5203 if saved_data.m_bHiddenNumbers ~= nil then m_bHiddenNumbers = saved_data.m_bHiddenNumbers end
5204 if saved_data.m_bHiddenTiles ~= nil then m_bHiddenTiles = saved_data.m_bHiddenTiles end
5205 if saved_data.m_bExpCK ~= nil then m_bExpCK = saved_data.m_bExpCK end
5206 if saved_data.m_bMapLoaded ~= nil then m_bMapLoaded = saved_data.m_bMapLoaded end
5207 if saved_data.m_bAutoSort ~= nil then m_bAutoSort = saved_data.m_bAutoSort end
5208 if saved_data.m_iDealCards ~= nil then
5209 m_iDealCards = saved_data.m_iDealCards
5210 setDealCardsSetting({iSetting = m_iDealCards})
5211 end
5212 if saved_data.m_bDeckTooltip ~= nil then m_bDeckTooltip = saved_data.m_bDeckTooltip end
5213 if saved_data.m_bDisplayCardCount ~= nil then m_bDisplayCardCount = saved_data.m_bDisplayCardCount end
5214 if saved_data.m_iS9NumGold ~= nil then m_iS9NumGold = saved_data.m_iS9NumGold end
5215 if saved_data.m_iS9PercentHidden ~= nil then m_iS9PercentHidden = saved_data.m_iS9PercentHidden end
5216 if saved_data.m_iFrenemiesPlayers ~= nil then m_iFrenemiesPlayers = saved_data.m_iFrenemiesPlayers end
5217 if saved_data.m_bEnableHelpers ~= nil then m_bEnableHelpers = saved_data.m_bEnableHelpers end
5218 if saved_data.m_bEnableFishermen ~= nil then m_bEnableFishermen = saved_data.m_bEnableFishermen end
5219 if saved_data.m_bEnableHarborMaster ~= nil then m_bEnableHarborMaster = saved_data.m_bEnableHarborMaster end
5220 if saved_data.m_bEnableWelfare ~= nil then m_bEnableWelfare = saved_data.m_bEnableWelfare end
5221 if saved_data.m_bEnableEventCards ~= nil then m_bEnableEventCards = saved_data.m_bEnableEventCards end
5222 if saved_data.m_bEnableRivers ~= nil then m_bEnableRivers = saved_data.m_bEnableRivers end
5223 if saved_data.m_bEnableCaravans ~= nil then m_bEnableCaravans = saved_data.m_bEnableCaravans end
5224 if saved_data.m_bEnableBarbarianAttack ~= nil then m_bEnableBarbarianAttack = saved_data.m_bEnableBarbarianAttack end
5225 if saved_data.m_bEnableTraders ~= nil then m_bEnableTraders = saved_data.m_bEnableTraders end
5226 if saved_data.m_iEventCardResult ~= nil then m_iEventCardResult = saved_data.m_iEventCardResult end
5227
5228 if saved_data.m_sGUIDFishermenBoard ~= nil then
5229 m_FishermenCardBoard = getObjectFromGUID(saved_data.m_sGUIDFishermenBoard)
5230 end
5231 if saved_data.m_sGUID_Die1 ~= nil then
5232 m_Die1 = getObjectFromGUID(saved_data.m_sGUID_Die1)
5233 else
5234 m_Die1 = getObjectFromGUID('af8b2c')
5235 end
5236 if saved_data.m_sGUID_Die2 ~= nil then
5237 m_Die2 = getObjectFromGUID(saved_data.m_sGUID_Die2)
5238 else
5239 m_Die2 = getObjectFromGUID('750997')
5240 end
5241 if saved_data.m_sGUID_EventDie ~= nil then
5242 m_EventDie = getObjectFromGUID(saved_data.m_sGUID_EventDie)
5243 end
5244 end
5245end
5246
5247function onSave()
5248 local ToSave = {
5249 m_CurScenario = m_CurScenario,
5250 m_bRandomizeMap = m_bRandomizeMap,
5251 m_bHiddenNumbers = m_bHiddenNumbers,
5252 m_bHiddenTiles = m_bHiddenTiles,
5253 m_bExpCK = m_bExpCK,
5254 m_bMapLoaded = m_bMapLoaded,
5255 m_bAutoSort = m_bAutoSort,
5256 m_bDeckTooltip = m_bDeckTooltip,
5257 m_bDisplayCardCount = m_bDisplayCardCount,
5258 m_iDealCards = m_iDealCards,
5259 m_iS9NumGold = m_iS9NumGold,
5260 m_iS9PercentHidden = m_iS9PercentHidden,
5261 m_iFrenemiesPlayers = m_iFrenemiesPlayers,
5262 m_bEnableHelpers = m_bEnableHelpers,
5263 m_bEnableFishermen = m_bEnableFishermen,
5264 m_bEnableHarborMaster = m_bEnableHarborMaster,
5265 m_bEnableWelfare = m_bEnableWelfare,
5266 m_bEnableEventCards = m_bEnableEventCards,
5267 m_bEnableRivers = m_bEnableRivers,
5268 m_bEnableCaravans = m_bEnableCaravans,
5269 m_bEnableBarbarianAttack = m_bEnableBarbarianAttack,
5270 m_bEnableTraders = m_bEnableTraders,
5271 m_iEventCardResult = m_iEventCardResult,
5272 }
5273 if m_FishermenCardBoard ~= nil then
5274 ToSave.m_sGUIDFishermenBoard = m_FishermenCardBoard.getGUID()
5275 end
5276 if m_Die1 ~= nil then
5277 ToSave.m_sGUID_Die1 = m_Die1.getGUID()
5278 end
5279 if m_Die2 ~= nil then
5280 ToSave.m_sGUID_Die2 = m_Die2.getGUID()
5281 end
5282 if m_EventDie ~= nil then
5283 ToSave.m_sGUID_EventDie = m_EventDie.getGUID()
5284 end
5285
5286 return JSON.encode(ToSave)
5287end
5288
5289function getScenarioID()
5290 return m_CurScenario.sID
5291end
5292function isUsingExtension()
5293 return m_CurScenario.bExtension
5294end
5295
5296function checkButtonPrivileges(p)
5297 local PPlayer = Player[p.sColor]
5298 if PPlayer ~= nil then
5299 if PPlayer.admin then
5300 return true
5301 else
5302 broadcastToColor('Only promoted players can use the control panel', p.sColor, {1.0,1.0,1.0})
5303 end
5304 end
5305 return false
5306end
5307
5308--[[ removeOverlay
5309 Removes the quick start overlay if it still exists.
5310 Called from onMapLoad and from onDestroy of control panel page 1 to make it disappear when the page gets changed
5311]]
5312function removeOverlay()
5313 local Obj = getObjectFromGUID('6905af')
5314 if Obj ~= nil then
5315 Obj.destruct()
5316 end
5317end
5318
5319--when a number die is interacted with it registers as die 1 or die 2 to make the script more robust and allow for deletion and spawning of dice (e.g. when put in a bag)
5320function registerNumberDie(p)
5321 if p.iDie == 1 then
5322 if m_Die1 == nil then --old reference is invalid (die got deleted)
5323 m_Die1 = p.DieObj
5324 end
5325 elseif p.iDie == 2 then
5326 if m_Die2 == nil then --old reference is invalid (die got deleted)
5327 m_Die2 = p.DieObj
5328 end
5329 elseif p.iDie == 3 and isCKenabled() then --Event die will register as die 3
5330 if m_EventDie == nil then
5331 m_EventDie = p.DieObj
5332 end
5333 end
5334end
5335
5336function isCKenabled()
5337 if m_CurScenario.bUseCK ~= nil then
5338 return m_CurScenario.bUseCK
5339 end
5340 return false
5341end
5342function isWelfareEnabled()
5343 if m_CurScenario.bUseWelfare ~= nil then
5344 return m_CurScenario.bUseWelfare
5345 end
5346 return false
5347end
5348function isFishermenEnabled()
5349 if m_CurScenario.bUseFishermen ~= nil then
5350 return m_CurScenario.bUseFishermen
5351 end
5352 return false
5353end
5354function isEventCardsEnabled()
5355 if m_CurScenario.bUseEventCards ~= nil then
5356 return m_CurScenario.bUseEventCards
5357 end
5358 return false
5359end
5360function isRiversEnabled()
5361 if m_CurScenario.bUseRivers ~= nil then
5362 return m_CurScenario.bUseRivers
5363 end
5364 return false
5365end
5366function getDiceValue()
5367 if isEventCardsEnabled() then
5368 return m_iEventCardResult
5369 elseif m_Die1 ~= nil and m_Die2 ~= nil then
5370 return (m_Die1.getValue() + m_Die2.getValue())
5371 end
5372 return 0
5373end
5374function getDie2Value()
5375 if m_Die2 ~= nil then
5376 return m_Die2.getValue()
5377 end
5378 return 0
5379end
5380function getEventDieValue()
5381 if m_EventDie ~= nil then
5382 local iVal = m_EventDie.getValue()
5383 if iVal == 1 or iVal == 2 or iVal == 6 then
5384 return 'barbarians'
5385 elseif iVal == 3 then
5386 return 'science'
5387 elseif iVal == 4 then
5388 return 'trade'
5389 elseif iVal == 5 then
5390 return 'politics'
5391 end
5392 end
5393 return nil
5394end
5395
5396function callbackDeck(Obj, p)
5397 Obj.setRotation(p.vRot)
5398 Obj.setPosition(p.vPos)
5399 if p.bShuffle == true then
5400 Obj.shuffle()
5401 end
5402end
5403
5404function setupDecks()
5405 --find appropriate card decks to spawn
5406 local SpawnDef_Resources = m_ResourceDecks
5407 local SpawnDef_DevCards = m_DevCardDecks
5408 local SpawnDef_Commodities = m_CommodityDecks
5409 local SpawnDef_Items = m_CKitems
5410
5411 if m_CurScenario.bExtension == true then
5412 SpawnDef_Resources = m_ResourceDecks_ext
5413 SpawnDef_Commodities = m_CommodityDecks_ext
5414 end
5415
5416 -- spawn decks & put them in the proper position
5417 local params = { callback = 'callbackDeck',
5418 callback_owner = Global,
5419 params = nil,
5420 }
5421 if isCKenabled() == true then
5422 local BagResources = getObjectFromGUID(SpawnDef_Resources.Bag)
5423 if BagResources ~= nil then
5424 local vBagPos = BagResources.getPosition()
5425 local iDecksSpawned = 0 --counter to spawn every deck at another height to avoid decks merging
5426 --Lumber
5427 params.guid = SpawnDef_Resources.Deck['Lumber']
5428 params.params = {vPos = {-24.5,1.0,8.77}, vRot = {0.0,180.0,0.0}}
5429 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5430 local Deck = takeObjectWorkaround(BagResources, params)
5431 Deck.tooltip = m_bDeckTooltip
5432 iDecksSpawned = iDecksSpawned + 1
5433 --Bricks
5434 params.guid = SpawnDef_Resources.Deck['Bricks']
5435 params.params = {vPos = {-24.5,1.0,1.77}, vRot = {0.0,180.0,0.0}}
5436 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5437 local Deck = takeObjectWorkaround(BagResources, params)
5438 Deck.tooltip = m_bDeckTooltip
5439 iDecksSpawned = iDecksSpawned + 1
5440 --Wool
5441 params.guid = SpawnDef_Resources.Deck['Wool']
5442 params.params = {vPos = {-24.5,1.0,5.27}, vRot = {0.0,180.0,0.0}}
5443 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5444 local Deck = takeObjectWorkaround(BagResources, params)
5445 Deck.tooltip = m_bDeckTooltip
5446 iDecksSpawned = iDecksSpawned + 1
5447 --Grain
5448 params.guid = SpawnDef_Resources.Deck['Grain']
5449 params.params = {vPos = {-27.0,1.0,1.77}, vRot = {0.0,180.0,0.0}}
5450 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5451 local Deck = takeObjectWorkaround(BagResources, params)
5452 Deck.tooltip = m_bDeckTooltip
5453 iDecksSpawned = iDecksSpawned + 1
5454 --Ore
5455 params.guid = SpawnDef_Resources.Deck['Ore']
5456 params.params = {vPos = {-24.5,1.0,-1.73}, vRot = {0.0,180.0,0.0}}
5457 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5458 local Deck = takeObjectWorkaround(BagResources, params)
5459 Deck.tooltip = m_bDeckTooltip
5460 iDecksSpawned = iDecksSpawned + 1
5461 end
5462 params.position = nil
5463
5464 local BagCommodities = getObjectFromGUID(SpawnDef_Commodities.Bag)
5465 if BagCommodities ~= nil then
5466 local vBagPos = BagCommodities.getPosition()
5467 local iDecksSpawned = 0 --counter to spawn every deck at another height to avoid decks merging
5468 --Paper
5469 params.guid = SpawnDef_Commodities.Deck['Paper']
5470 params.params = {vPos = {-27.0,1.0,8.77}, vRot = {0.0,180.0,0.0}}
5471 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5472 local Deck = takeObjectWorkaround(BagCommodities, params)
5473 Deck.tooltip = m_bDeckTooltip
5474 iDecksSpawned = iDecksSpawned + 1
5475 --Cloth
5476 params.guid = SpawnDef_Commodities.Deck['Cloth']
5477 params.params = {vPos = {-27.0,1.0,5.27}, vRot = {0.0,180.0,0.0}}
5478 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5479 local Deck = takeObjectWorkaround(BagCommodities, params)
5480 Deck.tooltip = m_bDeckTooltip
5481 iDecksSpawned = iDecksSpawned + 1
5482 --Coins
5483 params.guid = SpawnDef_Commodities.Deck['Coins']
5484 params.params = {vPos = {-27.0,1.0,-1.73}, vRot = {0.0,180.0,0.0}}
5485 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5486 local Deck = takeObjectWorkaround(BagCommodities, params)
5487 Deck.tooltip = m_bDeckTooltip
5488 iDecksSpawned = iDecksSpawned + 1
5489 end
5490 params.position = nil
5491
5492 local BagCKitems = getObjectFromGUID(SpawnDef_Items.Bag)
5493 if BagCKitems ~= nil then
5494 local vBagPos = BagCKitems.getPosition()
5495 local iDecksSpawned = 0
5496 --Politics
5497 params.guid = SpawnDef_Items.Item['DeckPolitics']
5498 params.params = {vPos = {-24.5,1.0,-5.23}, vRot = {0.0,180.0,180.0}, bShuffle = true}
5499 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5500 takeObjectWorkaround(BagCKitems, params)
5501 iDecksSpawned = iDecksSpawned + 1
5502 --Science
5503 params.guid = SpawnDef_Items.Item['DeckScience']
5504 params.params = {vPos = {-24.5,1.0,-8.73}, vRot = {0.0,180.0,180.0}, bShuffle = true}
5505 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5506 takeObjectWorkaround(BagCKitems, params)
5507 iDecksSpawned = iDecksSpawned + 1
5508 --Trade
5509 params.guid = SpawnDef_Items.Item['DeckTrade']
5510 params.params = {vPos = {-27.0,1.0,-5.23}, vRot = {0.0,180.0,180.0}, bShuffle = true}
5511 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5512 takeObjectWorkaround(BagCKitems, params)
5513 iDecksSpawned = iDecksSpawned + 1
5514
5515 if m_CurScenario.bExtension == true then
5516 params.guid = SpawnDef_Items.Item['DeckDefender_ext']
5517 else
5518 params.guid = SpawnDef_Items.Item['DeckDefender']
5519 end
5520 params.params = {vPos = {-27.0,1.0,-8.73}, vRot = {0.0,180.0,180.0}}
5521 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5522 takeObjectWorkaround(BagCKitems, params)
5523 end
5524 else --isCKenabled()
5525 local BagResources = getObjectFromGUID(SpawnDef_Resources.Bag)
5526 if BagResources ~= nil then
5527 local vBagPos = BagResources.getPosition()
5528 local iDecksSpawned = 0
5529 --Lumber
5530 params.guid = SpawnDef_Resources.Deck['Lumber']
5531 params.params = {vPos = {-24.5,1.0,8.77}, vRot = {0.0,180.0,0.0}}
5532 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5533 local Deck = takeObjectWorkaround(BagResources, params)
5534 Deck.tooltip = m_bDeckTooltip
5535 iDecksSpawned = iDecksSpawned + 1
5536 --Bricks
5537 params.guid = SpawnDef_Resources.Deck['Bricks']
5538 params.params = {vPos = {-24.5,1.0,5.27}, vRot = {0.0,180.0,0.0}}
5539 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5540 local Deck = takeObjectWorkaround(BagResources, params)
5541 Deck.tooltip = m_bDeckTooltip
5542 iDecksSpawned = iDecksSpawned + 1
5543 --Wool
5544 params.guid = SpawnDef_Resources.Deck['Wool']
5545 params.params = {vPos = {-24.5,1.0,1.77}, vRot = {0.0,180.0,0.0}}
5546 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5547 local Deck = takeObjectWorkaround(BagResources, params)
5548 Deck.tooltip = m_bDeckTooltip
5549 iDecksSpawned = iDecksSpawned + 1
5550 --Grain
5551 params.guid = SpawnDef_Resources.Deck['Grain']
5552 params.params = {vPos = {-24.5,1.0,-1.73}, vRot = {0.0,180.0,0.0}}
5553 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5554 local Deck = takeObjectWorkaround(BagResources, params)
5555 Deck.tooltip = m_bDeckTooltip
5556 iDecksSpawned = iDecksSpawned + 1
5557 --Ore
5558 params.guid = SpawnDef_Resources.Deck['Ore']
5559 params.params = {vPos = {-24.5,1.0,-5.23}, vRot = {0.0,180.0,0.0}}
5560 params.position = {vBagPos.x + 5.0,vBagPos.y + iDecksSpawned * 2.0,vBagPos.z}
5561 local Deck = takeObjectWorkaround(BagResources, params)
5562 Deck.tooltip = m_bDeckTooltip
5563 iDecksSpawned = iDecksSpawned + 1
5564 end
5565 params.position = nil
5566
5567 local BagDev = getObjectFromGUID(SpawnDef_DevCards.Bag)
5568 if BagDev ~= nil then
5569 params.guid = nil
5570 if m_CurScenario.sID == 'S7' or m_CurScenario.sID == 'T4' then --pirate scenario needs a deck with Knights instead of VPs (use it for Treasures Scenario 4, too)
5571 if m_CurScenario.bExtension == true then
5572 params.guid = SpawnDef_DevCards.Deck['56P_S7']
5573 else
5574 params.guid = SpawnDef_DevCards.Deck['34P_S7']
5575 end
5576 elseif m_CurScenario.bUseBAttack then
5577 local TaBBag = getTaBBag()
5578 if TaBBag ~= nil then
5579 params.guid = TaBBag.getVar('m_Spawn_DevCards_BarbAttck')
5580 BagDev = TaBBag
5581 end
5582 elseif m_CurScenario.bUseTraders then
5583 local TaBBag = getTaBBag()
5584 if TaBBag ~= nil then
5585 if m_CurScenario.bExtension == true then
5586 params.guid = TaBBag.getVar('m_Spawn_DevCards_TaB_ext')
5587 else
5588 params.guid = TaBBag.getVar('m_Spawn_DevCards_TaB')
5589 end
5590 BagDev = TaBBag
5591 end
5592 elseif getScenarioID() == 'B2' then --2 player version: smaller development card deck
5593 params.guid = SpawnDef_DevCards.Deck['2P']
5594 else
5595 if m_CurScenario.bExtension == true then
5596 params.guid = SpawnDef_DevCards.Deck['56P']
5597 else
5598 params.guid = SpawnDef_DevCards.Deck['34P']
5599 end
5600 end
5601 if params.guid ~= nil then
5602 params.params = {vPos = {-27.0,1.0,-5.23}, vRot = {0.0,180.0,180.0}, bShuffle = true}
5603 takeObjectWorkaround(BagDev, params)
5604 end
5605 end
5606 end
5607
5608 return true
5609end
5610
5611--[[ The Update function. This is called once per frame.
5612 This monitors the dice and triggers the token highlighting once they are all resting.
5613 The dice wanting to be monitored register themselves in m_CheckDice when there is physical interaction with them so this update function will only do something
5614 after the first die is manipulated until all dice are resting.
5615--]]
5616function onUpdate()
5617 --Check if all dice registered to be monitored are resting and trigger hightlighting once they are
5618 if #m_CheckDice > 0 then
5619 local bResting = true
5620 for i, Die in pairs(m_CheckDice) do
5621 if Die ~= nil then
5622 if Die.resting == false then
5623 bResting = false
5624 break
5625 end
5626 else --die got deleted -> remove from monitoring queue
5627 table.remove(m_CheckDice, i)
5628 end
5629 end
5630
5631 if bResting then --all monitored dice are resting -> highlight number tokens and display the deal button
5632 if m_MapScript ~= nil then
5633 local iResult = getDiceValue()
5634 if iResult > 0 then
5635 m_MapScript.call('highlightChits', { iNum = iResult })
5636
5637 --Check card limits and display card count on a 7
5638 if m_bDisplayCardCount then
5639 if iResult == 7 then
5640 startLuaCoroutine(self, 'checkCardLimits')
5641 else
5642 --in 90% of cases the texts will be deleted already through the turn end event / players discarding their cards
5643 --however to avoid annoying players with the text in special cases -> make sure it is gone
5644 startLuaCoroutine(self, 'clearCardCounts')
5645 end
5646 end
5647
5648 --Handle Deal button next to dice
5649 if m_bDiceDealButton then
5650 m_MapScript.call('hideDealButtonDie') --There is no onRoll event in which we can remove the button + there is no event when a dice is set to a number -> just make sure any old button is gone before showing a new one
5651 if m_Die1 ~= nil then --event cards...
5652 if iResult ~= 7 then
5653 m_MapScript.call('showDealButtonDie', {DieObj = m_Die1})
5654 elseif isCKenabled() and getEventDieValue() ~= 'barbarians' then
5655 --for Cities & Knights we have to show the button also at a seven in case progress cards have to be dealt
5656 m_MapScript.call('showDealButtonDie', {DieObj = m_Die1})
5657 end
5658 end
5659 end
5660
5661 --interface for external tool dice logger
5662 if m_DiceLogger ~= nil then
5663 m_DiceLogger.call('onDiceRolled', {iResult = iResult}) --TODO: when an onRoll event gets implemented transmit the color as well
5664 end
5665
5666 --interface for dice event receivers (automatic barbarian ship)
5667 for i, Receiver in ipairs(m_DiceEventReceiver) do
5668 if Receiver ~= nil then
5669 Receiver.call('onDiceRolled', {iResult = iResult})
5670 end
5671 end
5672 end
5673 end
5674
5675 --tell dice that monitoring stopped so they will announce when they are interacted with again
5676 for i = 1, #m_CheckDice do
5677 if m_CheckDice[i] ~= nil then --it seems dice can be invalidated between the first loop in this routine and this one??!?!
5678 m_CheckDice[i].call('setQueueStatus', {bActive = false})
5679 end
5680 end
5681
5682 --reset die monitoring list
5683 m_CheckDice = {}
5684 --enable deal cards function
5685 m_bAllowDeal = true
5686 end
5687 end
5688end
5689
5690--Reset/spawn items in the player area depending on the selected expansions/scenario
5691function setupPlayerItems(params)
5692 local bAddShips = params.bAddShips
5693 local iNumChits = params.iNumChits
5694 local bAddWarShips = false
5695 if params.bAddWarShips == true then
5696 bAddWarShips = true
5697 end
5698
5699 local AllObj = getAllObjects()
5700
5701 local PlayerPos = {
5702 ['Orange'] = { vStart = {x=-20.4,y=1.245,z=-17.0}, bLTR = false },
5703 ['Red'] = { vStart = {x=8.8,y=1.245,z=-17.0}, bLTR = false },
5704 ['White'] = { vStart = {x=38.9,y=1.245,z=-17.0}, bLTR = false },
5705 ['Purple'] = { vStart = {x=19.9,y=1.245,z=17.0}, bLTR = true },
5706 ['Blue'] = { vStart = {x=-9.3,y=1.245,z=17.0}, bLTR = true },
5707 ['Green'] = { vStart = {x=-38.9,y=1.245,z=17.0}, bLTR = true }
5708 }
5709
5710 local BuildOrder = {
5711 --[[ iNum = Number of items that should be there
5712 iStack = how many of them are in one "stack" (column)
5713 fZSpace = vert space between objects
5714 fXSpace = space between stacks of the same type
5715 fYSpace = vertical space between items
5716 fXGap = additional space to fXSpace to the next object type
5717 fZGap = modifier for the Z starting point for this object
5718 fYOffset = Offset for height
5719 vRot = Rotation of this object
5720 ]]--
5721 ['City'] = {iNum = 4, iStack = 999, fZSpace = 0.5, fXSpace = 0.0, fXGap = 0.0, fZGap = 0.0, fYOffset = 0.0, vRot = {0.0,-90.0,0.0}},
5722 ['Settlement'] = {iNum = 5, iStack = 3, fZSpace = 0.5, fXSpace = 0.7, fXGap = 1.1, fZGap = 0.0, fYOffset = -0.07, vRot = {0.0,90.0,0.0}},
5723 ['Road'] = {iNum = 15, iStack = 8, fZSpace = 0.25, fXSpace = 1.2, fXGap = 1.0, fZGap = -0.1, fYOffset = -0.19, vRot = {0.0,0.0,0.0}},
5724 ['Bridge'] = {iNum = 0, iStack = 3, fZSpace = 0.4, fXSpace = 0.0, fYSpace = 0.2, fXGap = 0.0, fZGap = -0.1, fYOffset = 0.3, vRot = {0.0,0.0,0.0}},
5725 ['Ship'] = {iNum = 15, iStack = 5, fZSpace = 0.4, fXSpace = 1.1, fXGap = 1.3, fZGap = -0.05, fYOffset = 0.0, vRot = {0.0,0.0,0.0}},
5726 ['Warship'] = {iNum = 7, iStack = 7, fZSpace = 0.4, fXSpace = 0.0, fXGap = 0.0, fZGap = -0.09, fYOffset = 0.0, vRot = {0.0,0.0,0.0}},
5727 ['Chit'] = {iNum = iNumChits, iStack = 1, fZSpace = 0.0, fXSpace = 0.0, fYSpace = 0.12, fXGap = 0.0, fZGap = 0.5, fYOffset = -0.28, vRot = {0.0,-90.0,0.0}},
5728 ['Knight'] = {iNum = 0, iStack = 6, fZSpace = 0.4, fXSpace = 0.0, fYSpace = 0.22, fXGap = 0.0, fZGap = -0.1, fYOffset = -0.17, vRot = {0.0,0.0,0.0}}, --knight for the Barbarian Attack scenario
5729 ['Wall'] = {iNum = 3, iStack = 1, fZSpace = 0.0, fXSpace = 0.0, fYSpace = 0.15, fXGap = 1.3, fZGap = 0.25, fYOffset = -0.21, vRot = {0.0,0.0,0.0}},
5730 ['Merchant'] = {iNum = 1, iStack = 1, fZSpace = 0.0, fXSpace = 0.0, fXGap = 0.0, fZGap = 1.2, fYOffset = 0.44, vRot = {0.0,0.0,0.0}},
5731 ['Mighty Knight'] = {iNum = 2, iStack = 1, fZSpace = 0.0, fXSpace = 0.0, fYSpace = 0.19, fXGap = 1.3, fZGap = 0.15, fYOffset = -0.18, vRot = {0.0,0.0,180.0}},
5732 ['Strong Knight'] = {iNum = 2, iStack = 1, fZSpace = 0.0, fXSpace = 0.0, fYSpace = 0.19, fXGap = 0.0, fZGap = 1.0, fYOffset = -0.18, vRot = {0.0,0.0,180.0}},
5733 ['Basic Knight'] = {iNum = 2, iStack = 1, fZSpace = 0.0, fXSpace = 0.0, fYSpace = 0.19, fXGap = 0.0, fZGap = 1.85, fYOffset = -0.18, vRot = {0.0,0.0,180.0}},
5734 }
5735
5736 if m_CurScenario.bUseBAttack and isCKenabled() == false then
5737 BuildOrder['Knight'].iNum = 6
5738 BuildOrder['Knight'].fXGap = 1.3
5739 end
5740
5741 if bAddWarShips == true then
5742 --spawn one row of ships less when warships are used
5743 BuildOrder['Ship'].iNum = 10
5744 end
5745
5746 if getScenarioID() == 'A7' then
5747 BuildOrder['Ship'].iNum = 4
5748 end
5749 if getScenarioID() == 'A10' then
5750 BuildOrder['Ship'].iNum = 5
5751 end
5752 if getScenarioID() == 'T3' then
5753 BuildOrder['City'].iNum = 8
5754 end
5755 if m_CurScenario.bUseRivers then
5756 BuildOrder['Bridge'].iNum = 3
5757 end
5758 if getScenarioID() == 'A8' then
5759 BuildOrder['Bridge'].iNum = 3
5760 end
5761 if getScenarioID() == 'B2' then --2 player version: fewer buildings are available to each player
5762 BuildOrder['Settlement'].iNum = 4
5763 BuildOrder['City'].iNum = 3
5764 end
5765
5766 for sCol, Opt in pairs(PlayerPos) do
5767 local Objects = {
5768 ['City'] = {},
5769 ['Settlement'] = {},
5770 ['Road'] = {},
5771 ['Bridge'] = {},
5772 ['Ship'] = {},
5773 ['Warship'] = {},
5774 ['Chit'] = {},
5775 ['Knight'] = {},
5776 ['Wall'] = {},
5777 ['Merchant'] = {},
5778 ['Mighty Knight'] = {},
5779 ['Strong Knight'] = {},
5780 ['Basic Knight'] = {},
5781 }
5782
5783 --Find all building models for this color
5784 for i, Obj in pairs(AllObj) do
5785 local sName = Obj.getName()
5786 if Obj.getDescription() == sCol or Obj.getVar('sPlayer') == sCol then
5787 if sName == 'City' or sName == 'Settlement' or sName == 'Road' or sName == 'Ship' or sName == 'Warship'
5788 or sName == 'Wall' or sName == 'Merchant' or sName == 'Basic Knight' or sName == 'Strong Knight' or sName == 'Mighty Knight' or sName == 'Bridge' then
5789 table.insert(Objects[sName], Obj)
5790 end
5791 end
5792
5793 if sName == 'Indicator Special Build Phase' then
5794 destroyObject(Obj) --destroy existing indicators (simpler than to implement some association to a color for repositioning...)
5795 elseif string.sub(sName, 1, 10) == 'Catan Chit' then --Delete all existing Catan Chits
5796 destroyObject(Obj)
5797 end
5798 end
5799
5800 --Make sure the number of items is good
5801 for sItem, Order in pairs(BuildOrder) do
5802 --destroy unneeded objects
5803 if (isCKenabled() == false and (sItem == 'Wall' or sItem == 'Merchant' or sItem == 'Basic Knight' or sItem == 'Strong Knight' or sItem == 'Mighty Knight')) then
5804 for i, Obj in pairs(Objects[sItem]) do
5805 destroyObject(Obj)
5806 end
5807 elseif (bAddShips == false and sItem == 'Ship') or (bAddWarShips == false and sItem == 'Warship') then
5808 for i, Obj in pairs(Objects[sItem]) do
5809 destroyObject(Obj)
5810 end
5811 else
5812 --check if new items need to be spawned
5813 local iCur = #Objects[sItem]
5814 while iCur < Order.iNum do
5815 table.insert(Objects[sItem], 'X') --spawn a new item
5816 iCur = iCur + 1
5817 end
5818 --delete excess items
5819 while iCur > Order.iNum do
5820 local DelObj = table.remove(Objects[sItem], #Objects[sItem])
5821 destroyObject(DelObj)
5822 iCur = iCur - 1
5823 end
5824 end
5825 end
5826
5827 --Place the items on the table
5828 local vPos = {x=Opt.vStart.x, y=Opt.vStart.y, z=Opt.vStart.z}
5829 local fModX = 1.0
5830 local fModZ = 1.0
5831 if Opt.bLTR == false then
5832 fModX = -1.0
5833 end
5834 if Opt.vStart.z < 0.0 then
5835 fModZ = -1.0
5836 end
5837
5838 for sItem, V in pairs(BuildOrder) do
5839 if V.iNum == 0 or (sItem == 'Ship' and bAddShips == false) or (sItem == 'Warship' and bAddWarShips == false) then
5840 --Nothing to be spawned - skip these columns
5841 else
5842 vPos.z = Opt.vStart.z + (V.fZGap * fModZ)
5843 vPos.y = Opt.vStart.y + V.fYOffset
5844 vPos.x = vPos.x + (V.fXGap * fModX)
5845 if Objects[sItem] ~= nil then
5846 for i, Obj in pairs(Objects[sItem]) do
5847 if Obj == 'X' then
5848 if sItem == 'Ship' and bAddShips == true then --ship missing -> spawn it
5849 Obj = spawnShip({vPosition = vPos, vRotation = V.vRot, sPlayer = sCol})
5850 elseif sItem == 'Warship' and bAddWarShips == true then
5851 Obj = spawnShip({vPosition = vPos, vRotation = V.vRot, sPlayer = sCol, bIsWarShip = true})
5852 elseif sItem == 'Chit' then
5853 Obj = spawnCatanChit({vPosition = vPos, vRotation = {V.vRot[1],V.vRot[2]+(90.0*fModZ),V.vRot[3]}, sPlayer = sCol, sCatanChitDesc = params.sCatanChitDesc})
5854 elseif sItem == 'Wall' and isCKenabled() == true then
5855 Obj = spawnWall({vPosition = vPos, vRotation = V.vRot, sPlayer = sCol})
5856 elseif sItem == 'Merchant' and isCKenabled() == true then
5857 Obj = spawnMerchant({vPosition = vPos, vRotation = {V.vRot[1],V.vRot[2],V.vRot[3]}, sPlayer = sCol})
5858 elseif sItem == 'Basic Knight' and isCKenabled() == true then
5859 Obj = spawnKnight({vPosition = vPos, vRotation = {V.vRot[1],V.vRot[2]+(90.0*fModZ),V.vRot[3]}, sPlayer = sCol, sType = 'Basic'})
5860 elseif sItem == 'Strong Knight' and isCKenabled() == true then
5861 Obj = spawnKnight({vPosition = vPos, vRotation = {V.vRot[1],V.vRot[2]+(90.0*fModZ),V.vRot[3]}, sPlayer = sCol, sType = 'Strong'})
5862 elseif sItem == 'Mighty Knight' and isCKenabled() == true then
5863 Obj = spawnKnight({vPosition = vPos, vRotation = {V.vRot[1],V.vRot[2]+(90.0*fModZ),V.vRot[3]}, sPlayer = sCol, sType = 'Mighty'})
5864 elseif sItem == 'Settlement' then --never should have implemented this scenario :'( only needed for S7
5865 Obj = spawnSettlement(vPos, sCol)
5866 elseif sItem == 'City' then
5867 --find a city to clone (attached snap points cannot be spawned)
5868 local SrcObj = nil
5869 for i = 1, #Objects['City'] do
5870 if Objects['City'][i] ~= 'X' then
5871 SrcObj = Objects['City'][i]
5872 break
5873 end
5874 end
5875 if SrcObj ~= nil then
5876 Obj = SrcObj.clone({ position = {vPos.x, vPos.y, vPos.z} })
5877 else
5878 --user deleted all cities or a game of Explorers & Pirates was previously loaded
5879 --->try to load a city through the E&P expansion
5880 local NewCity = spawnCity(vPos, {V.vRot[1],V.vRot[2]*fModX,V.vRot[3]}, sCol)
5881 if NewCity ~= nil then
5882 Obj = NewCity
5883 end
5884 end
5885 elseif sItem == 'Bridge' then
5886 Obj = spawnBridge(vPos, sCol)
5887 elseif sItem == 'Knight' then
5888 Obj = spawnBAttackKnight({vPosition = vPos, vRotation = vRot, sPlayer = sCol})
5889 end
5890 else
5891 if sItem == 'Basic Knight' or sItem == 'Strong Knight' or sItem == 'Mighty Knight' then
5892 Obj.setRotation({V.vRot[1],V.vRot[2]+(90.0*fModZ),V.vRot[3]})
5893 elseif sItem == 'City' then --rotate cities to settlements so no items are spawned to high next to one to avoid the snap points grabbing them
5894 Obj.setRotation({V.vRot[1],V.vRot[2]*fModX,V.vRot[3]})
5895 else
5896 Obj.setRotation({V.vRot[1],V.vRot[2],V.vRot[3]})
5897 end
5898 Obj.setPosition(vPos)
5899 end
5900
5901 if i % V.iStack == 0 then --start new stack
5902 vPos.z = Opt.vStart.z + (V.fZGap * fModZ)
5903 vPos.x = vPos.x + (V.fXSpace * fModX)
5904 if V.fYSpace ~= nil then --stack vertical
5905 vPos.y = vPos.y + V.fYSpace
5906 else
5907 vPos.y = Opt.vStart.y + V.fYOffset
5908 end
5909 else
5910 vPos.z = vPos.z + (V.fZSpace * fModZ)
5911 end
5912 end
5913 end
5914 end
5915 end
5916 end
5917
5918 --Spawn pawns as indicators for the special build phase when playing with 5-6 players
5919 if m_CurScenario.bExtension == true and m_MapScript ~= nil then
5920 for sPlayer, Info in pairs(m_PlayerColors) do
5921 --spawn the pawn next to the counter
5922 local CounterObj = getObjectFromGUID(m_MapScript.call('getCounterGUID', {sColor = sPlayer}))
5923 if CounterObj ~= nil then
5924 local vCounterPos = CounterObj.getPosition()
5925 local fXoff = 2.0
5926 if vCounterPos.z > 0.0 then
5927 fXoff = fXoff * -1.0
5928 end
5929 local vPos = {vCounterPos.x + fXoff, 1.0, vCounterPos.z}
5930 spawnSpecialBuildPhasePawn({vPos = vPos, sColor = sPlayer})
5931 end
5932 end
5933 end
5934end
5935
5936--handle 'Shuffle Cards' buttons for player hands: show button only for used colors / hide them for unused
5937function onPlayerChangeColor(player_color)
5938 if player_color == nil then return end
5939 local Zones = { ['Green'] = 'd866a9',
5940 ['Blue'] = 'f37d36',
5941 ['Purple'] = 'a60284',
5942 ['Orange'] = '15c3aa',
5943 ['Red'] = '24632e',
5944 ['White'] = 'cdde1f',
5945 }
5946
5947 for sColor, sGUID in pairs(Zones) do
5948 local Zone = getObjectFromGUID(sGUID)
5949 if Zone ~= nil then
5950 if sColor == player_color then --spawn the button for the newly occupied color
5951 Zone.call('createShuffleButton')
5952 else --check unoccupied player zones and remove the shuffle buttons if there are no more cards in the player area
5953 if Player[sColor] ~= nil and Player[sColor].seated == false then
5954 if #Player[sColor].getHandObjects() == 0 then
5955 Zone.clearButtons()
5956 end
5957 end
5958 end
5959 end
5960 end
5961end
5962
5963--shuffle the resource card in a player hand
5964function shuffleHandCards(p)
5965 --[[
5966 if p.sZoneColor ~= p.sPlayer then --players may only press their own button
5967 broadcastToColor('You can only shuffle your own cards', p.sPlayer, {1.0,1.0,1.0})
5968 return
5969 end
5970 --]]
5971
5972 local Cards = Player[p.sZoneColor].getHandObjects()
5973 local ShuffleItems = {}
5974 for i, Card in pairs(Cards) do
5975 if Card.tag == 'Card' then
5976 local sName = Card.getName()
5977 --find all resource / commodity cards to shuffle
5978 if sName == 'Lumber' or sName == 'Bricks' or sName == 'Wool' or sName == 'Grain' or sName == 'Ore' or sName == 'Paper' or sName == 'Cloth' or sName == 'Coins' then
5979 table.insert(ShuffleItems, Card)
5980 end
5981 end
5982 end
5983 --randomize resource card positions
5984 if #ShuffleItems > 1 then
5985 for i = #ShuffleItems, 2, -1 do
5986 local iRand = math.random(i)
5987 local vPosCard = ShuffleItems[i].getPosition()
5988 local vPosRand = ShuffleItems[iRand].getPosition()
5989 ShuffleItems[i].setPosition(vPosRand)
5990 ShuffleItems[iRand].setPosition(vPosCard)
5991 end
5992 --make all cards move a bit as feedback that the shuffle function did something and to obfuscate whether cards changed position or not
5993 local fHandCenter = Player[p.sZoneColor].getPlayerHand().pos_x
5994 for i = 1, #ShuffleItems do
5995 local vPosNew = ShuffleItems[i].getPosition()
5996 local fOffset = -0.2
5997 if vPosNew.x < fHandCenter then
5998 fOffset = 0.2
5999 end
6000 ShuffleItems[i].setPosition({vPosNew.x+fOffset,vPosNew.y,vPosNew.z})
6001 end
6002 end
6003end
6004
6005-- spawn the pirate ship (called from the control panel on map load)
6006function spawnPirate(params)
6007 local vRot = {0.0,160.0,0.0} --make him look like he is going somewhere :-)
6008 if params.vRot ~= nil then
6009 vRot = params.vRot
6010 end
6011 local ShipObj = spawnShip({vPosition = params.vPos, vRotation = vRot, vScale = {1.2,1.2,1.2}})
6012 if ShipObj ~= nil then
6013 ShipObj.setName('Pirate Ship (Arrrr)')
6014 ShipObj.setDescription('May be moved INSTEAD of the robber. The pirate is placed in the center of ocean tiles and blocks new ships from being built on this tile. Ships adjacent to the hex cannot be moved.')
6015 ShipObj.setColorTint({0.15, 0.15, 0.15})
6016 ShipObj.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'seafarers_add\') end') --so it will be deleted when the next map is spawned
6017 end
6018end
6019
6020function spawnShip(p)
6021 local params = {}
6022 params.type = 'Custom_Model'
6023 params.position = {p.vPosition.x, p.vPosition.y, p.vPosition.z}
6024 params.rotation = p.vRotation
6025 params.scale = {1.0, 1.0, 1.0}
6026 if p.vScale ~= nil then
6027 params.scale = p.vScale
6028 end
6029 params.snap_to_grid = false
6030
6031 local custom = {}
6032 if p.bIsWarShip == true then
6033 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379838327/CFAC6A762AE832DB84513D78947D65D0DE34A39D/'
6034 else
6035 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379843989/82018A077B99A1206F2B74A1FBAFF2D4304083EF/'
6036 end
6037 custom.diffuse = ''
6038 custom.convex = true
6039 custom.type = 0 --generic
6040 custom.material = 1 --wood
6041
6042 local ShipObj = spawnObject(params)
6043 ShipObj.setCustomObject(custom)
6044
6045 if p.sPlayer ~= nil then
6046 if p.bIsWarShip == true then
6047 ShipObj.setName('Warship')
6048 else
6049 ShipObj.setName('Ship')
6050 end
6051 ShipObj.setDescription(p.sPlayer)
6052 ShipObj.setColorTint(m_PlayerColors[p.sPlayer])
6053 end
6054
6055 ShipObj.setLock(true)
6056 addToUnlockList({Obj = ShipObj})
6057
6058 return ShipObj
6059end
6060
6061--Spawn pawn as an indicator for the special build phase with 5-6 players
6062function spawnSpecialBuildPhasePawn(p)
6063 local params = {}
6064 params.type = 'PlayerPawn'
6065 params.position = p.vPos
6066 params.scale = {1.2, 1.0, 1.2}
6067
6068 local Pawn = spawnObject(params)
6069 Pawn.setName('Indicator Special Build Phase')
6070 Pawn.setDescription('Move this pawn in front of your player area before the current player ends their turn to indicate you want to build something in the special build phase. (for 5-6 Players)')
6071 Pawn.setColorTint(m_PlayerColors[p.sColor])
6072 Pawn.setLuaScript([[ m_ActiveIndic = nil
6073 function onPlayerTurnEnd(player_color_end, player_color_next)
6074 if m_ActiveIndic ~= nil then
6075 m_ActiveIndic.destruct()
6076 end
6077 --check if this pawn is outside the player area and if so spawn an arrow to draw attention
6078 local vPos = self.getPosition()
6079 if vPos.z > -16.2 and vPos.z < 16.2 then
6080 m_ActiveIndic = Global.call('spawnIndicatorArrow', {vPos = {vPos.x, vPos.y + 1.0 + (self.getScale().y * 1.4), vPos.z}, vScale = {1.0, 1.0, 1.0}, vColor = {1.0,0.8627,0.05098}, fTTL = 6.7})
6081 end
6082 end
6083 function onPickUp(player_color) --remove active arrow when player picks up the pawn
6084 if m_ActiveIndic ~= nil then
6085 m_ActiveIndic.destruct()
6086 end
6087 end
6088 ]])
6089end
6090
6091--[[ callbackGeneral
6092 Callback function used by many different objects.
6093 params:
6094 vRot oblig. vector rotation
6095 vPos oblig. vector position
6096 vScale vector scale
6097 bLock bool lock the object
6098 ThResume coroutine Coroutine to call after processing the callback
6099--]]
6100function callbackGeneral(Obj, p)
6101 Obj.setRotation(p.vRot)
6102 Obj.setPosition(p.vPos)
6103 if p.vScale ~= nil then
6104 Obj.setScale(p.vScale)
6105 end
6106 if p.bLock == true then
6107 Obj.setLock(true)
6108 end
6109 if p.ThResume ~= nil then
6110 coroutine.resume(p.ThResume)
6111 end
6112end
6113
6114function callbackHex(Obj, p)
6115 callbackGeneral(Obj, p)
6116 callback_HexLoaded()
6117end
6118
6119function spawnMetropolisGate(p)
6120 local params = {}
6121 params.type = 'Custom_Model'
6122 params.position = {p.vPosition.x,p.vPosition.y,p.vPosition.z}
6123 params.rotation = p.vRotation
6124
6125 local custom = {}
6126 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379846524/3B83C6691E306D61176BBD975A0F31CBCB6F7F71/'
6127 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379849079/1F532EF7B11F7242E8B164E77838D2E028B83D69/'
6128 custom.diffuse = ''
6129 custom.type = 1 --figurine
6130 custom.material = 1 --wood
6131
6132 local Gate = spawnObject(params)
6133 Gate.setCustomObject(custom)
6134 --Add script for scoring: the points have to be awarded when the metropolis is touching a certain players city
6135 Gate.setLuaScript( [[ m_bLoaded = false
6136 function onSave()
6137 local ToSave = { sScorer = self.getVar('sScorer') }
6138 return JSON.encode(ToSave)
6139 end
6140 function onLoad(save_state)
6141 local saved_data = JSON.decode(save_state)
6142 if saved_data ~= nil then
6143 self.setVar('sScorer', saved_data.sScorer)
6144 end
6145 m_bLoaded = true --workaround: when rewinding time onCollisionEnter can be called before own lua scripts in mapscript are initialized
6146 end
6147 function onCollisionEnter(collision_info)
6148 if self == nil or m_bLoaded == false then
6149 return
6150 end
6151 if self.getVar('sScorer') ~= nil then --score is applied already, ignore this collision
6152 return
6153 end
6154 if collision_info == nil then return end
6155 local ColObj = collision_info.collision_object
6156 if ColObj.getName() == 'City' then
6157 local MapScript = getObjectFromGUID('42fd8e')
6158 if MapScript ~= nil then
6159 --check if the collision is within the map area
6160 local bScore = false
6161 local ChkObj = MapScript.getObjects()
6162 for i, Obj in ipairs(ChkObj) do
6163 if Obj == ColObj then
6164 bScore = true
6165 break
6166 end
6167 end
6168 --award the points
6169 if bScore == true then
6170 if true == MapScript.call('addScore', {iScore = 2, sPlayer = ColObj.getDescription()}) then
6171 self.setVar('sScorer', ColObj.getDescription())
6172 end
6173 end
6174 end
6175 end
6176 end
6177 function onPickUp(player_color) --remove the score from the previous scorer when the gate is picked up
6178 local sOldScorer = self.getVar('sScorer')
6179 if sOldScorer ~= nil then
6180 self.setVar('sScorer', nil)
6181 local MapScript = getObjectFromGUID('42fd8e')
6182 if MapScript ~= nil then
6183 MapScript.call('subtractScore',{iScore = 2, sPlayer = sOldScorer})
6184 end
6185 end
6186 end ]] )
6187
6188 Gate.setName('Metropolis - ' .. p.sType)
6189 Gate.setDescription('2 Victory Points!\nPlace it on one of your cities if you reach stage 4 in the ' .. p.sType .. ' category first. If someone else manages to get to stage 5 first you have to give the metropolis to this player.')
6190 if p.sType == 'Politics' then
6191 Gate.setColorTint({0.0,0.37647,0.67843})
6192 elseif p.sType == 'Science' then
6193 Gate.setColorTint({0.0,0.64313,0.32156})
6194 elseif p.sType == 'Trade' then
6195 Gate.setColorTint({0.97255,0.74902,0.08235})
6196 end
6197end
6198
6199function spawnWall(p)
6200 local params = {}
6201 params.type = 'Custom_Model'
6202 params.position = {p.vPosition.x,p.vPosition.y,p.vPosition.z}
6203 params.rotation = p.vRotation
6204
6205 local custom = {}
6206 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379855106/394C23AB4BD900AAD134BD8F8F217B4F5DF2BA82/'
6207 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379857723/2029EA7FC070B4CD260492063BE0D7E7129852E8/'
6208 custom.diffuse = ''
6209 custom.type = 0 --generic
6210 custom.material = 1 --wood
6211
6212 local Wall = spawnObject(params)
6213 Wall.setCustomObject(custom)
6214
6215 Wall.setName('Wall')
6216 if p.sPlayer ~= nil then
6217 Wall.setDescription(p.sPlayer)
6218 Wall.setColorTint(m_PlayerColors[p.sPlayer])
6219 end
6220end
6221
6222function spawnKnight(p)
6223 local params = {}
6224 params.type = 'Custom_Model'
6225 params.position = {p.vPosition.x,p.vPosition.y,p.vPosition.z}
6226 params.rotation = p.vRotation
6227 params.scale = {0.9, 0.9, 0.9}
6228
6229 local custom = {}
6230 if p.sType == 'Mighty' then
6231 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601380173660/814EB937A7102F76BF68D7D891190B1C895C2DF4/'
6232 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380178917/0E84E940D57266A700908F2B3EFFD20662B2CABF/'
6233 elseif p.sType == 'Strong' then
6234 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601380175749/27E1062B3DD72D7CDAC4A776C02D9088CF0B67D1/'
6235 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380180091/6D683F3DF23C367CCAAC0191C384D2906389EE45/'
6236 elseif p.sType == 'Basic' then
6237 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601380177408/EFE31FE4FF37E006E1825A56DC56E7F5678D4618/'
6238 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380181828/C1384EA9F5BFAFCA59A27456243C4DC601C9C361/'
6239 end
6240 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379865464/95822A8471E23E0A048D10243B63BF31137336F2/'
6241 custom.type = 0 --generic
6242 custom.material = 1 --wood
6243
6244 local KnightToken = spawnObject(params)
6245 KnightToken.setCustomObject(custom)
6246
6247 KnightToken.setName(p.sType .. ' Knight')
6248 if p.sPlayer ~= nil then
6249 KnightToken.setDescription(p.sPlayer)
6250 KnightToken.setColorTint(m_PlayerColors[p.sPlayer])
6251 end
6252
6253 --report the collision to possible receivers (Cities / Knights counter tool)
6254 KnightToken.setLuaScript([[ m_bActive = false
6255 function onLoad(save_state)
6256 m_bActive = true --workaround for possible collision calls before stuff is initialized
6257 end
6258 function onCollisionEnter(collision_info)
6259 if m_bActive then
6260 Global.call('reportKnightCollision')
6261 end
6262 end
6263 function onPickUp(player_color)
6264 Global.call('reportKnightCollision')
6265 end ]])
6266end
6267
6268function spawnMerchant(p)
6269 local params = {}
6270 params.type = 'Custom_Model'
6271 params.position = {p.vPosition.x,p.vPosition.y,p.vPosition.z}
6272 params.rotation = p.vRotation
6273
6274 local custom = {}
6275 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379869495/388CFE018FFB1969CBD518D22737FD5D6D4D5818/'
6276 custom.collider = ''
6277 custom.diffuse = ''
6278 custom.type = 1 --figurine
6279 custom.material = 1 --wood
6280
6281 local Trader = spawnObject(params)
6282 Trader.setCustomObject(custom)
6283 Trader.use_snap_points = false
6284
6285 Trader.setName('Merchant')
6286 Trader.setDescription('When you play the progress card Merchant you may place your Merchant on a hex adjacent to one of your Settlements/Cities. As long as your merchant is active you may trade the resource he is placed on 2:1 with the bank. There can only be one merchant active at a time. If another player has his merchant on the map, he has to remove it. You get 1 Victory Point for an active merchant.')
6287 if p.sPlayer ~= nil then
6288 Trader.setLuaScript('function onLoad() self.setVar(\'sPlayer\', \'' .. p.sPlayer .. '\') end')
6289 Trader.setColorTint(m_PlayerColors[p.sPlayer])
6290 end
6291end
6292
6293function spawnCatanChit(p)
6294 local params = {}
6295 params.type = 'Custom_Tile'
6296 params.position = {p.vPosition.x, p.vPosition.y, p.vPosition.z}
6297 params.rotation = p.vRotation
6298 params.scale = {0.37, 1.0, 0.37}
6299
6300 local custom = {}
6301 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379873242/F2E9C25F70C5154ACD5407238EFBCA0AC527A625/'
6302 custom.type = 0 --Box
6303 custom.thickness = 0.1
6304
6305 local Chit = spawnObject(params)
6306 Chit.setCustomObject(custom)
6307
6308 Chit.setName('Catan Chit')
6309 if p.sCatanChitDesc ~= nil then
6310 Chit.setDescription(p.sCatanChitDesc)
6311 end
6312 if p.sPlayer ~= nil then
6313 Chit.setName('Catan Chit - ' .. p.sPlayer)
6314 Chit.setColorTint(m_PlayerColors[p.sPlayer])
6315 end
6316 Chit.setLock(true)
6317
6318 if p.bLock ~= true then
6319 addToUnlockList({Obj = Chit})
6320 end
6321
6322 return Chit
6323end
6324
6325function spawnSettlement(vPos, sColor)
6326 local params = {}
6327 params.type = 'Custom_Model'
6328 params.position = {vPos.x,vPos.y,vPos.z}
6329 params.rotation = {0.0,90.0,0.0}
6330
6331 local custom = {}
6332 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379876219/C36F94BAF590D30E6CE987015C610096E4CFB1A0/'
6333 custom.type = 0 --generic
6334 custom.material = 1 --wood
6335
6336 local Settlement = spawnObject(params)
6337 Settlement.setCustomObject(custom)
6338
6339 Settlement.setName('Settlement')
6340 Settlement.setDescription(sColor)
6341 Settlement.setColorTint(m_PlayerColors[sColor])
6342end
6343
6344--[[ spawnCity
6345 Does not actually spawn a city because the attached snap points cannot be spawned.
6346 Will clone a City template of the Explorers & Pirates expansion bag
6347 TODO: redo all item spawns if they ever add scripting support for snap points
6348--]]
6349function spawnCity(vPos, vRot, sColor)
6350 local City = nil
6351 local EaPBag = getEaPBag()
6352 if EaPBag ~= nil then
6353 City = EaPBag.call('spawnCity', {vPos = vPos, vRot = vRot, sColor = sColor})
6354 end
6355 return City
6356end
6357
6358function spawnBridge(vPos, sColor)
6359 local params = {}
6360 params.type = 'Custom_Model'
6361 params.position = {vPos.x,vPos.y,vPos.z}
6362
6363 local custom = {}
6364 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379879290/3E67CC0119F31A97C5C8E221EE9A23C873FF03CB/'
6365 custom.collider = ''
6366 custom.diffuse = ''
6367 custom.type = 0 --generic
6368 custom.material = 1 --wood
6369
6370 local Bridge = spawnObject(params)
6371 Bridge.setCustomObject(custom)
6372
6373 Bridge.setName('Bridge')
6374 Bridge.setDescription(sColor)
6375 Bridge.setColorTint(m_PlayerColors[sColor])
6376end
6377
6378--Spawn knight token needed for the Barbarian Attack scenario
6379function spawnBAttackKnight(p)
6380 local params = {}
6381 params.type = 'Custom_Model'
6382 params.position = {p.vPosition.x,p.vPosition.y,p.vPosition.z}
6383 params.rotation = p.vRotation
6384
6385 local custom = {}
6386 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379883118/6CB181322189657641C89B44DB60CAD8DC5B880E/'
6387 custom.diffuse = ''
6388 custom.type = 1 --figurine
6389 custom.material = 1 --wood
6390
6391 local Knight = spawnObject(params)
6392 Knight.setCustomObject(custom)
6393 Knight.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'battack\') end')
6394
6395 Knight.setName('Knight')
6396 if p.sPlayer ~= nil then
6397 Knight.setDescription(p.sPlayer)
6398 Knight.setColorTint(m_PlayerColors[p.sPlayer])
6399 end
6400end
6401
6402function spawnBarbarians(p)
6403 if m_CurScenario.bUseBAttack then
6404 return --no barbarian tile when playing the "Barbarian Attack" campaign
6405 end
6406
6407 local vPos = {p.fXPos, 1.0, 1.018}
6408 --spawn the sea tile with the barbarian "path"
6409 local TileBag = getObjectFromGUID(m_BarbarianTile)
6410 if TileBag ~= nil then
6411 local vInitPos = TileBag.getPosition()
6412 vInitPos.x = vInitPos.x + 6.0 --make sure it is not captured by the spawn bag right after spawning
6413 local params = {position = vInitPos,
6414 callback = 'callbackGeneral',
6415 callback_owner = Global,
6416 params = {vPos = vPos, vRot = {0.0, -115.0, 0.0}, bLock = true}
6417 }
6418
6419 takeObjectWorkaround(TileBag, params)
6420 end
6421
6422 --spawn the ship
6423 local params = {}
6424 params.type = 'Custom_Model'
6425 params.position = {vPos[1] + 0.5, vPos[2] + 1.0, vPos[3] + 1.6}
6426 params.rotation = {0.0,155.0,0.0}
6427 params.scale = {0.7, 0.7, 0.7}
6428
6429 local custom = {}
6430 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379886620/8A66725468D9E84827F10A4E973138C525409A87/'
6431 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379888618/799E1DC2DD38BE927934E136AFC02AE3DE97563C/'
6432 custom.type = 1 --figurine
6433 custom.material = 1 --wood
6434
6435 local ShipObj = spawnObject(params)
6436 ShipObj.setCustomObject(custom)
6437
6438 ShipObj.setName('Barbarian Ship')
6439 ShipObj.setDescription('Advance the Barbarian Ship whenever the icon is rolled on the event die')
6440 ShipObj.setColorTint({0.15, 0.15, 0.15})
6441 ShipObj.setLuaScript([[ m_bDisplayRaidInfo = false --workaround to prevent message spam when multiple collisions are registered
6442 function onCollisionEnter(collision_info)
6443 if collision_info == nil then return end
6444 if collision_info.collision_object.tag == 'Dice' then
6445 --make barbarian (mostly) un-knockoverable from dice
6446 self.setLock(true)
6447 elseif m_bDisplayRaidInfo and collision_info.collision_object.getDescription() == 'Barbarian route' then
6448 --check if the barbarians hit the raid field and post City and Knight count in the chat
6449 local vPosRoute = collision_info.collision_object.getPosition()
6450 local vPosShip = self.getPosition()
6451 local fDistanceX = math.abs(vPosShip.x - vPosRoute.x - (-0.47))
6452 local fDistanceZ = math.abs(vPosShip.z - vPosRoute.z - (-1.67))
6453 if fDistanceX < 0.12 and fDistanceZ < 0.12 then
6454 m_bDisplayRaidInfo = false --prevent the message from being sent again until ship is picked up
6455 local MapScript = getObjectFromGUID('42fd8e')
6456 if MapScript ~= nil then
6457 MapScript.call('displayBarbarianRaidMessage')
6458 end
6459 end
6460 end
6461 end
6462 function onCollisionExit(collision_info)
6463 if collision_info == nil then return end
6464 if collision_info.collision_object.tag == 'Dice' then
6465 self.setLock(false)
6466 end
6467 end
6468 function onPickUp(player_color)
6469 m_bDisplayRaidInfo = true --picking the ship up unlocks the message again
6470 end
6471 ]])
6472 ShipObj.setLock(true)
6473 addToUnlockList({Obj = ShipObj})
6474end
6475
6476--Goods tokens for the Traders & Barbarians campaign -> called from the layout definition in the control panel
6477function spawnGoodsToken(p)
6478 local params = {}
6479 params.type = 'Custom_Tile'
6480 params.position = p.vPos
6481 params.rotation = p.vRot
6482 params.scale = {0.5, 1.0, 0.5}
6483 local custom = {}
6484 if p.sFront == 'castle' then
6485 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601380186899/B5240B0937C7183C98D014DE7685BDC8583547D8/'
6486 elseif p.sFront == 'glass_factory' then
6487 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601380192420/750676486E726958714E8528E64DC5AE428A3579/'
6488 elseif p.sFront == 'quarry' then
6489 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601380193703/F1DDC3B5E87DC1A52A2CF661CE19ABDA3CC5C955/'
6490 end
6491 if p.sBack == 'glass' then
6492 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380191908/A0BBD817C67E9FEE8270845B3898070D9AA0EC69/'
6493 elseif p.sBack == 'marble' then
6494 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380193059/1ECD9EB112AF7414F9126FA748D21B3F62086719/'
6495 elseif p.sBack == 'sand' then
6496 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380194233/C6C3B2A1E765F9DC5AA4ACD6C79DE625D929588D/'
6497 elseif p.sBack == 'tools' then
6498 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380194618/2C6CC8F25692AD86F32D6F2D9AB95CED65AC8E32/'
6499 end
6500 custom.type = 0 --Box
6501 custom.thickness = 0.06
6502
6503 local Token = spawnObject(params)
6504 Token.setCustomObject(custom)
6505 Token.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'traders\') end')
6506 if p.sFront == 'glass_factory' then
6507 Token.setColorTint({0.216,0.596,0.231})
6508 elseif p.sFront == 'quarry' then
6509 Token.setColorTint({0.804,0.616,0.224})
6510 elseif p.sFront == 'castle' then
6511 Token.setColorTint({0.537,0.839,0.188})
6512 end
6513 Token.setName('Commodity Token')
6514 Token.use_snap_points = false
6515 Token.dynamic_friction = 1.0
6516 Token.static_friction = 1.0
6517 Token.drag = 2.0
6518 Token.setLock(true)
6519 addToUnlockList({Obj = Token})
6520
6521 return Token
6522end
6523
6524--spawn specialist card decks
6525function spawnSpecialistCards(p)
6526 local DeckBag = getObjectFromGUID('cc360f')
6527 if DeckBag ~= nil then
6528 local params = {callback = 'callbackGeneral',
6529 callback_owner = Global,
6530 params = {vPos = p.vPos, vRot = {0.0, 180.0, 0.0}}
6531 }
6532 takeObjectWorkaround(DeckBag, params)
6533 end
6534end
6535
6536-- find the robber
6537function getRobber()
6538 if m_Robber ~= nil then
6539 return m_Robber
6540 else
6541 local AllObj = getAllObjects()
6542 for i, Obj in ipairs(AllObj) do
6543 if Obj.getName() == 'Robber' then
6544 m_Robber = Obj
6545 return Obj
6546 end
6547 end
6548 end
6549 return nil
6550end
6551
6552function placeRobber(p)
6553 local Robber = getRobber()
6554 if Robber ~= nil then
6555 Robber.setPosition(p.vPos)
6556 Robber.setLock(true) -- lock for now - it will be unlocked when all hex tiles are spawned
6557 end
6558 -- put him in the list of objects to unlock after hexes have finished loading
6559 addToUnlockList({Obj = Robber})
6560end
6561
6562
6563--[[ Dice-Force field for hand zones
6564 bounces dice incoming from the table side back onto the table
6565]]--
6566function onObjectEnterScriptingZone(zone, enter_object)
6567 if enter_object ~= nil and enter_object.held_by_color == nil then --allow people to carry dice into the hand zones
6568 if enter_object.tag == 'Dice' then
6569 if string.sub(zone.getDescription(), 18, 21) == 'Hand' then
6570 local fMod = 1.0
6571 if zone.getPosition().z < 0 then fMod = -1.0 end
6572
6573 local vVel = enter_object.getVelocity()
6574 if (enter_object.getPosition().z * fMod) < 18.3 and (vVel.z * fMod) > 0 then
6575 enter_object.addForce({vVel.x*-0.1,vVel.y*0.8,vVel.z*-1.3}, 4)
6576 end
6577 end
6578 end
6579 end
6580end
6581
6582--[[ Function for the button to align certain tiles to the 'inofficial' grid within the border frames
6583 This is called when the button on page 2 of the control panel is pressed
6584]]--
6585function alignHexes()
6586 if isMapLoaded() == false then
6587 return true
6588 end
6589 --find the positions of the border tiles to determine map size and offset
6590 local Borders = { ['left'] = nil, ['top'] = nil, ['right'] = nil, ['bottom'] = nil }
6591 local AllObj = getAllObjects()
6592 for i, Obj in ipairs(AllObj) do
6593 local sObjType = Obj.getVar('sObjType')
6594 if sObjType ~= nil then
6595 if sObjType == 'border' then
6596 --find leftmost border tile
6597 if Borders['left'] ~= nil then
6598 if Borders['left'].getPosition().x > Obj.getPosition().x then
6599 Borders['left'] = Obj
6600 end
6601 else
6602 Borders['left'] = Obj
6603 end
6604 --find topmost border tile
6605 if Borders['top'] ~= nil then
6606 if Borders['top'].getPosition().z < Obj.getPosition().z then
6607 Borders['top'] = Obj
6608 end
6609 else
6610 Borders['top'] = Obj
6611 end
6612 --find rightmost border tile
6613 if Borders['right'] ~= nil then
6614 if Borders['right'].getPosition().x < Obj.getPosition().x then
6615 Borders['right'] = Obj
6616 end
6617 else
6618 Borders['right'] = Obj
6619 end
6620 --find bottommost border tile
6621 if Borders['bottom'] ~= nil then
6622 if Borders['bottom'].getPosition().z > Obj.getPosition().z then
6623 Borders['bottom'] = Obj
6624 end
6625 else
6626 Borders['bottom'] = Obj
6627 end
6628 end
6629 end
6630 end
6631
6632 --calculate possible hex positions
6633 if Borders['left'] == nil or Borders['top'] == nil or Borders['right'] == nil or Borders['bottom'] == nil then
6634 print('Error: Unable to determine border frame positions')
6635 return false
6636 end
6637
6638 local HexPos = {}
6639 local vPos = Borders['left'].getPosition()
6640 local fXStart = Borders['left'].getPosition().x
6641 local fXEnd = Borders['right'].getPosition().x - 3.48
6642 local fZEnd = Borders['top'].getPosition().z
6643 local fZ = Borders['left'].getPosition().z --start in the middle
6644
6645 while fZ < fZEnd do
6646 local fX = fXStart
6647 while fX < fXEnd do
6648 fX = fX + 3.48
6649 table.insert(HexPos, {x=fX, y=0.965, z=fZ})
6650 if fZ > 1.0 or fZ < -1.0 then --insert positions for the rows above and below the center row in the same loop
6651 table.insert(HexPos, {x=fX, y=0.965, z=fZ * -1})
6652 end
6653 end
6654 fXStart = fXStart + 1.74
6655 fXEnd = fXEnd - 1.74
6656 fZ = fZ + 3.01
6657 end
6658
6659 --Find all hex tiles tiles within the map scripting zone
6660 local HexTiles = {}
6661 local NumTokens = {}
6662 local SearchZone = getObjectFromGUID('42fd8e')
6663 local AllObj = SearchZone.getObjects()
6664 for i, Obj in ipairs(AllObj) do
6665 if Obj.getLock() == false then
6666 local sType = Obj.getVar('sObjType')
6667 if sType == 'hex' then
6668 table.insert(HexTiles, Obj)
6669 elseif sType == 'numtoken' then
6670 table.insert(NumTokens, Obj)
6671 end
6672 end
6673 end
6674
6675 --find the closest hex for each position
6676 local MoveList = {}
6677 for i, Pos in ipairs(HexPos) do
6678 local iBestHex = -1
6679 local fBestHexDist = 3.0
6680 --find closest hex
6681 for j, Hex in pairs(HexTiles) do
6682 local fDistance = math.abs(Pos.x - Hex.getPosition().x) + math.abs(Pos.z - Hex.getPosition().z)
6683 if fDistance < fBestHexDist then
6684 iBestHex = j
6685 fBestHexDist = fDistance
6686 end
6687 end
6688 --find closest number token
6689 local iBestToken = -1
6690 local fBestTokenDist = 2.2
6691 for j, Token in pairs(NumTokens) do
6692 local fDistance = math.abs(Pos.x - Token.getPosition().x) + math.abs(Pos.z - Token.getPosition().z)
6693 if fDistance < fBestTokenDist then
6694 iBestToken = j
6695 fBestTokenDist = fDistance
6696 end
6697 end
6698
6699 if iBestHex >= 0 then
6700 local MoveItem = {Hex = HexTiles[iBestHex], vPos = Pos}
6701 if iBestToken >= 0 and HexTiles[iBestHex].getDescription() ~= 'Lake' then --save the matching hex to this token, ignore lakes because they can have 2 - 4 num tokens
6702 MoveItem.Token = NumTokens[iBestToken]
6703 end
6704 table.insert(MoveList, MoveItem)
6705 HexTiles[iBestHex] = nil --this hex found its new home, ignore it from now on
6706 if iBestToken >= 0 then
6707 NumTokens[iBestToken] = nil
6708 end
6709 end
6710 end
6711
6712 --position the hexes & tokens
6713 for i, Item in ipairs(MoveList) do
6714 local zRot = Item.Hex.getRotation().z
6715 if zRot < 230.0 and zRot > 150.0 then --it is probably meant to be upside down
6716 zRot = 180.0
6717 else
6718 zRot = 0.0
6719 end
6720
6721 if Item.Token ~= nil then
6722 local zRotToken = Item.Token.getRotation().z
6723 if zRotToken < 230.0 and zRotToken > 150.0 then --it is probably meant to be upside down
6724 zRotToken = 180.0
6725 else
6726 zRotToken = 0.0
6727 end
6728 Item.Token.setRotation({0.0,180.0,zRotToken})
6729 Item.Token.setPosition({Item.vPos.x, Item.vPos.y + 0.115, Item.vPos.z})
6730 end
6731
6732 --rotate to the closest valid angle (default is 90.0 but in case someone wants to place river hexes we need to consider the current rotation)
6733 local yRot = Item.Hex.getRotation().y
6734 yRot = math.floor(yRot / 60) * 60 + 30
6735 Item.Hex.setRotation({0.0,yRot,zRot}) --make sure it is oriented properly to fit in with the other tiles
6736 if zRot == 180.0 then
6737 Item.Hex.setPosition({Item.vPos.x, 1.07, Item.vPos.z})
6738 else
6739 Item.Hex.setPosition(Item.vPos)
6740 Item.Hex.setLock(true)
6741 end
6742 end
6743
6744 return true
6745end
6746
6747--[[
6748 Map creation
6749]]--
6750--[[ createMap
6751 Parameters: PlacemInfo = list of all hexes to be used + parameters for placement of these hexes
6752
6753 Members of PlacemInfo:
6754 pos - XYZ vector -> coordinates to place the tiles in
6755
6756 sHexType - type of hex to spawn
6757 bRandHex - Flag if this tile should be randomized
6758 bFlipped - Start this tile upside down (to be explored by players)
6759 iNumToken - Number token to into this position by default (nil = do not place one on this pos/terrain hex)
6760
6761 vRot - orientation of the border piece
6762
6763 sHarborType - Type of the harbor tile to put in this position by default (nil = do not place one on this pos) values: 'generic','wool','lumber','grain','ore','bricks'
6764 bRandHarbor - Flag if this harbor should be randomized
6765 vHarborRot - Rotation for the harbor object
6766
6767 iGroup - Tile group - needed for seafarers scenarios where certain islands can be randomized but should always have the same resources
6768
6769 bRobberPos - Start the robber on this tile
6770]]--
6771function createMap( p )
6772 local PlacemInfo = p.PlacemInfo
6773
6774 --find position to place the stack of flipped tokens depending on the map size
6775 local vPosTokenStack = {30.0,1.0,-3.0}
6776 if m_MapScript ~= nil then
6777 vPosTokenStack[1] = ((m_MapScript.getScale().x / 2) + m_MapScript.getPosition().x) + 3.0
6778 end
6779
6780-- 1. Randomize Hexes/Numbers/Harbors which should be randomized
6781 shuffleHexes( PlacemInfo )
6782 shuffleChips( PlacemInfo )
6783 shuffleHarbors( PlacemInfo )
6784 if m_CurScenario.bUseFishermen then
6785 setFishTiles( PlacemInfo )
6786 end
6787
6788-- 2. Spawn objects
6789 local iTokenStack = 0
6790 for i, Tile in pairs(PlacemInfo) do
6791
6792 -- Spawn hex tile
6793 if Tile.sHexType == 'border' or Tile.sHexType == 'corner' or Tile.sHexType == 'side' then
6794 spawnBorderTile(Tile.sHexType, Tile.pos, Tile.vRot)
6795 elseif Tile.sHexType == 'lake' then
6796 spawnLake({vPosition = Tile.pos, bSecLake = Tile.bSecLake})
6797 elseif Tile.sHexType == 'castle' then
6798 spawnCastle(Tile.pos)
6799 else
6800 if m_bHiddenTiles and Tile.sHexType ~= 'ocean' then --hide all tiles option enabled
6801 Tile.bFlipped = true
6802 end
6803
6804 local HexTile = spawnHex({sType = Tile.sHexType, vPos = {Tile.pos.x,Tile.pos.y,Tile.pos.z}, vRot = Tile.vRot, bFlipped = Tile.bFlipped})
6805 if Tile.bRobberPos and Tile.bFlipped ~= true then
6806 local vPos = Tile.pos
6807 vPos.y = vPos.y + 1.0
6808 placeRobber({vPos = vPos})
6809 end
6810 end
6811
6812 -- Spawn number chips
6813 local Token = nil
6814 if Tile.iNumToken ~= nil then
6815 local vPos = Tile.pos
6816 vPos.y = 1.085
6817 if m_bHiddenNumbers or Tile.bFlipped then
6818 if Tile.bFlipped then
6819 --the tile this token belongs to is upside down -> spawn it next to the map upside down on a stack
6820 iTokenStack = iTokenStack + 1
6821 vPos.x = vPosTokenStack[1]
6822 --vPos.x = 22.0
6823 vPos.y = 1.0 + iTokenStack * 0.1 --when spawning stuff close together they seem to get pushed in the table .. strange ... this here give me very little "wiggle" though
6824 --vPos.z = -2.0
6825 vPos.z = vPosTokenStack[3]
6826 if iTokenStack > 20 then
6827 vPos.z = vPosTokenStack[3] - 2.1
6828 vPos.y = 1.0 + (iTokenStack - 20) * 0.1
6829 end
6830 end
6831 spawnHiddenNumberToken({iNum = Tile.iNumToken, vPos = vPos})
6832 else
6833 if Tile.iNumTokenSec ~= nil then --hex gets 2 number tokens (used with rivers of catan -> 1 tile with 2 and 12)
6834 spawnNumberToken( {iNum = Tile.iNumToken, vPosition = {x=vPos.x-0.7,y=vPos.y,z=vPos.z}, vScale = {1.35,1.5,1.35}, vRotation = {0.0,180.0,0.0} } )
6835 spawnNumberToken( {iNum = Tile.iNumTokenSec, vPosition = {x=vPos.x+0.7,y=vPos.y,z=vPos.z}, vScale = {1.35,1.5,1.35}, vRotation = {0.0,180.0,0.0} } )
6836 else
6837 spawnNumberToken( {iNum = Tile.iNumToken, vPosition = vPos, vRotation = {0.0,180.0,0.0} } )
6838 end
6839 end
6840 end
6841
6842 -- Spawn harbor tiles
6843 local HarborTile = nil
6844 if Tile.sHarborType ~= nil then
6845 local vPos = Tile.pos
6846 vPos.y = 1.073
6847 HarborTile = spawnHarborTile({sType=Tile.sHarborType, vPos=vPos, vRot=Tile.vHarborRot})
6848 HarborTile.setLock(true)
6849 end
6850
6851 -- Spawn fish grounds for Fishermen expansion
6852 if Tile.vFishRot ~= nil then
6853 local FishTile = nil
6854 local vPos = Tile.pos
6855 vPos.y = 1.073
6856 FishTile = spawnFishTile({vPos=vPos, vRot=Tile.vFishRot})
6857 FishTile.setLock(true)
6858
6859 --spawn the number token too
6860 if Tile.iNumTokenFish ~= nil then
6861 local vPos = Tile.pos
6862 vPos.y = 1.085
6863 spawnNumberTokenFish( {iNum = Tile.iNumTokenFish, vPosition = vPos, vRotation = {0.0,180.0,0.0}, FishTile = FishTile } )
6864 end
6865 end
6866 end
6867end
6868
6869function spawnHex(p)
6870 local bFlipped = p.bFlipped or false
6871 local bLock = (bFlipped ~= true) --keep flipped hexes unlocked
6872 if p.bLock ~= nil then --additional scenario 4 uses the bLock parameter to spawn some unlocked ocean tiles
6873 bLock = p.bLock
6874 end
6875 local vRot = p.vRot or {0.0,90.0,0.0}
6876 if bFlipped then
6877 vRot[3] = 180.0
6878 end
6879
6880 local params = {}
6881 params.callback_owner = Global
6882 params.callback = 'callbackHex'
6883 params.params = {vPos = p.vPos, vRot = vRot, bLock = bLock}
6884
6885 local HexDef = m_HexObjects[p.sType]
6886 local SpawnBag = getObjectFromGUID(m_HexBag)
6887 if m_CurScenario.bUseCaravans or m_CurScenario.bUseRivers or m_CurScenario.bUseBAttack or m_CurScenario.bUseTraders then
6888 if HexDef == nil then
6889 SpawnBag = getTaBBag()
6890 HexDef = SpawnBag.call('getHexDef', {sType = p.sType})
6891 end
6892 end
6893 if SpawnBag ~= nil and HexDef ~= nil then
6894 local iNext = HexDef.iNext + 1
6895 if iNext > #HexDef.Tile then
6896 iNext = 1
6897 end
6898
6899 params.guid = HexDef.Tile[iNext]
6900 HexDef.iNext = iNext
6901
6902 m_iSpawnedHexes = m_iSpawnedHexes + 1
6903 --Workaround as of v7.8: hex tiles must not touch when taken from the bag before being positioned by the callback (Please Berserk, give me scripting access to snap points :'( )
6904 --Make sure hex tiles to not touch each other after being taken out of their bags
6905 local fYoff = 0.25
6906 if m_iSpawnedHexes % 2 == 0 then --alternate the height so we can put them closer together (otherwise there is a noticable delay for hexes that are put far away from their bag)
6907 fYoff = -0.25
6908 end
6909 local iRow = math.floor(m_iSpawnedHexes / 10)
6910 local fZoff = 0.0
6911 fZoff = fZoff - (iRow * - 5.0)
6912 local vBagPos = SpawnBag.getPosition()
6913 local iRowHex = m_iSpawnedHexes - (iRow * 10)
6914 params.position = {vBagPos.x - iRowHex * 1.76, vBagPos.y + fYoff, vBagPos.z + fZoff}
6915 --/
6916
6917 local HexObj = takeObjectWorkaround(SpawnBag, params)
6918 HexObj.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'hex\') end')
6919 if HexObj.getDescription() == '' then
6920 HexObj.setDescription(HexObj.getName())
6921 end
6922 m_iLoadingHexes = m_iLoadingHexes + 1
6923
6924 --If the tile is spawned flipped the name/description must not give away what type of tile it is
6925 --so we give it some custom scripts to remove the name/description and reset it once it is flipped back (discovered)
6926 if bFlipped then
6927 local sName = HexObj.getDescription()
6928 if sName == '' then
6929 sName = HexObj.getName()
6930 end
6931 HexObj.setLuaScript([[ m_sDesc = ']] .. sName .. [['
6932 function onLoad(save_state) self.setVar('sObjType', 'hex') end
6933 function onCollisionEnter(collision_info)
6934 local zRot = self.getRotation().z
6935 if zRot < 230.0 and zRot > 150.0 then
6936 if self.getDescription() ~= '' then
6937 self.setDescription('')
6938 end
6939 elseif self.getDescription() == '' then
6940 self.setDescription(m_sDesc)
6941 end
6942 end]]
6943 )
6944 end
6945 end
6946end
6947
6948function spawnBorderTile(sType, vPosition, vRotation)
6949 local params = {}
6950 params.type = 'Custom_Model'
6951 params.position = {vPosition.x, 1.015, vPosition.z}
6952 params.rotation = vRotation
6953 params.callback = 'lockObject'
6954 params.callback_owner = Global
6955
6956 local custom = {}
6957 if sType == 'border' then
6958 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601380204690/A9DD85903F9977C30C680739868BED6BD0A11FAF/'
6959 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380209162/AE668F8B0369BBE7CE2D7F088AC9AEAB6143F086/'
6960 elseif sType == 'corner' then
6961 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601380205664/E08A940216107C4A1AFBB9295E192D449FFF6720/'
6962 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380209469/91F6A9E1947BA1154A6A62AA511B640147BD1A09/'
6963 elseif sType == 'side' then
6964 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/847089545433329233/1F25DBBB05E468E83C9B32F7EB53DB293EB42F18/'
6965 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/847089545433330296/F99FBF3D1ED01F6196F19FEB2ADC1BA353A4683D/'
6966 end
6967 custom.collider = custom.mesh
6968 custom.convex = false
6969 custom.type = 0
6970 custom.material = 1
6971
6972 local Tile = spawnObject(params)
6973 Tile.setCustomObject(custom)
6974 Tile.setDescription('Ocean')
6975
6976 --set a custom variable to identify the object type when the map is reset
6977 Tile.setVar('sObjType', 'border')
6978 Tile.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'border\') end') --keep the variable on rewind
6979
6980 return Tile
6981end
6982
6983function spawnDie(iDie, vPos, vRot)
6984 local params = {}
6985 params.type = 'Die_6'
6986 params.position = {vPos.x,vPos.y,vPos.z}
6987 params.rotation = vRot
6988 params.scale = {1.24999976, 1.24999976, 1.24999976}
6989
6990 local Die = spawnObject(params)
6991 Die.setColorTint({0.90588,0.82745,0.72157})
6992 Die.setLuaScript([[ m_bActive = false
6993 function enableDie() m_bActive = true end
6994 m_sLastPlayer = nil
6995 m_bInQueue = false
6996 function setQueueStatus(p) m_bInQueue = p.bActive end
6997 function onLoad(save_state) m_bActive = true end
6998 function onCollisionEnter(collision_info)
6999 if m_bInQueue == false then
7000 if m_bActive then
7001 m_bInQueue = Global.call('queueDieCheck', {DieObj = self})
7002 Global.call('registerNumberDie', {DieObj = self, iDie = ]] .. iDie .. [[})
7003 end
7004 end
7005 end
7006 function onPickUp(player_color)
7007 m_sLastPlayer = player_color
7008 local MapScript = getObjectFromGUID('42fd8e')
7009 if MapScript ~= nil then MapScript.call('hideDealButtonDie') end
7010 end
7011 function getLastPlayerColor() return m_sLastPlayer end
7012 ]])
7013 --[[TODO: enable if Berserk finally implements such an event
7014 function onRoll(player_color)
7015 m_sLastPlayer = player_color
7016 --hide the deal button if it is still visible
7017 local MapScript = getObjectFromGUID('42fd8e')
7018 if MapScript ~= nil then
7019 MapScript.call('hideDealButtonDie')
7020 end
7021 end
7022 --]]
7023 return Die
7024end
7025--[[ Spawn custom event die for Cities & Knights
7026]]
7027function spawnEventDie(vPos)
7028 local params = {}
7029 params.type = 'Custom_Dice'
7030 params.position = vPos
7031 params.rotation = {0.0,0.0,0.0}
7032 params.scale = {1.24999976, 1.24999976, 1.24999976}
7033 local custom = {
7034 type = 1, --6 sided
7035 image = 'http://cloud-3.steamusercontent.com/ugc/155773601379893824/EC932B85B64F5B484FDF35B18B0E7AFE066F1293/'
7036 }
7037
7038 local Die = spawnObject(params)
7039 Die.setCustomObject(custom)
7040 Die.setLuaScript([[ m_bActive = false
7041 function enableDie() m_bActive = true end
7042 m_bInQueue = false
7043 function setQueueStatus(p) m_bInQueue = p.bActive end
7044 function onLoad(save_state) m_bActive = true end
7045 function onCollisionEnter(collision_info)
7046 if m_bInQueue == false then
7047 if m_bActive then
7048 m_bInQueue = Global.call('queueDieCheck', {DieObj = self})
7049 Global.call('registerNumberDie', {DieObj = self, iDie = 3})
7050 end
7051 end
7052 end
7053 ]])
7054 return Die
7055end
7056
7057function spawnHarborTile(p)
7058 local params = {}
7059 params.type = 'Custom_Model'
7060 params.position = {p.vPos.x, p.vPos.y, p.vPos.z}
7061 params.rotation = p.vRot
7062
7063 local custom = {}
7064 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379896629/5FBC5A99616B48F733C49103F95F80D1ABA676D8/'
7065 custom.collider = custom.mesh
7066 if p.sType == 'generic' then
7067 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380215722/13D5E6345E5F179AC0E4B39B0E8E03B4146CEF82/'
7068 elseif p.sType == 'bricks' then
7069 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380214901/8B907632A00BA41614D1E3DA7D7EF49EA1604E1F/'
7070 elseif p.sType == 'grain' then
7071 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380216515/9E2813DD7E2CF9553AFAC42449B14AE653EFDC4C/'
7072 elseif p.sType == 'lumber' then
7073 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380217126/9563E370591EE97786DB2BDBC57E5C48BCF91D73/'
7074 elseif p.sType == 'ore' then
7075 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380217927/17FDA65567BF05F63CF0780540D13C580F227B39/'
7076 elseif p.sType == 'wool' then
7077 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380218943/98A114E362A57FFF06BE329661C811FB1135CC95/'
7078 end
7079 custom.type = 0 --generic
7080 custom.material = 1 --wood
7081
7082 local HarborObj = spawnObject(params)
7083 HarborObj.setCustomObject(custom)
7084
7085 if p.bNoLock == true then
7086 addToUnlockList({Obj = HarborObj})
7087 end
7088
7089 HarborObj.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'harbor\') end') --keep the variable on rewind
7090
7091 if p.sType == 'generic' then
7092 HarborObj.setName('Any 3 : 1')
7093 HarborObj.setDescription('You may exchange 3 identical resource cards for any 1 other resource card during your turn.')
7094 elseif p.sType == 'wool' then
7095 HarborObj.setName('Wool 2 : 1')
7096 HarborObj.setDescription('You may exchange 2 Wool resource cards for any 1 other resource card during your turn.')
7097 elseif p.sType == 'lumber' then
7098 HarborObj.setName('Lumber 2 : 1')
7099 HarborObj.setDescription('You may exchange 2 Lumber resource cards for any 1 other resource card during your turn.')
7100 elseif p.sType == 'bricks' then
7101 HarborObj.setName('Brick 2 : 1')
7102 HarborObj.setDescription('You may exchange 2 Bricks resource cards for any 1 other resource card during your turn.')
7103 elseif p.sType == 'grain' then
7104 HarborObj.setName('Grain 2 : 1')
7105 HarborObj.setDescription('You may exchange 2 Grain resource cards for any 1 other resource card during your turn.')
7106 elseif p.sType == 'ore' then
7107 HarborObj.setName('Ore 2 : 1')
7108 HarborObj.setDescription('You may exchange 2 Ore resource cards for any 1 other resource card during your turn.')
7109 end
7110
7111 return HarborObj
7112end
7113
7114function spawnFishTile(p)
7115 local params = {}
7116 params.type = 'Custom_Model'
7117 params.position = {p.vPos.x, p.vPos.y, p.vPos.z}
7118 params.rotation = p.vRot
7119
7120 local custom = {}
7121 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379899883/77EF7A4AE8268E8DA3553A4FA4457000507CB5B2/'
7122 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379902556/884151AB27FD9EA3F96B222C5CF96EA059AE56CB/'
7123 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379904511/62647B9C144D291B436069F2A819C468D1130D75/'
7124 custom.type = 0 --generic
7125 custom.material = 1 --wood
7126
7127 local FishTile = spawnObject(params)
7128 FishTile.setCustomObject(custom)
7129
7130 if p.bNoLock == true then
7131 addToUnlockList({Obj = FishTile})
7132 end
7133
7134 --set a custom variable to identify the object type when the map is reset
7135 FishTile.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'fishermen\') end') --keep the variable on rewind
7136
7137 return FishTile
7138end
7139function spawnLake(p)
7140 local params = {}
7141 params.type = 'Custom_Tile'
7142 params.position = {p.vPosition.x, 0.96, p.vPosition.z}
7143 params.rotation = {0.0,90.0,0.0}
7144 params.scale = {1.735, 1.0, 2.0}
7145 local custom = {}
7146 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379906588/A6AD5B05B94BABDCB8F1A4121E5C3003CC2A4277/'
7147 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601379910328/F0F3F849353CEADE84EFD7EBC9633E2D515D065B/'
7148 custom.thickness = 0.1
7149 custom.type = 1
7150
7151 local Tile = spawnObject(params)
7152 Tile.setCustomObject(custom)
7153 Tile.setDescription('Lake')
7154 Tile.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'hex\') end')
7155 Tile.setColorTint({0.99,0.906,0.62})
7156 if p.bLock ~= false then
7157 Tile.setLock(true)
7158 end
7159
7160 --Spawn the number tokens for the lake
7161 local Tokens = {}
7162 if p.bSecLake == true then --secondary lake for 5-6 players with different number tokens
7163 table.insert(Tokens, spawnNumberTokenFish( {iNum = 10, vPosition = {x=p.vPosition.x-0.5,y=1.075,z=p.vPosition.z}, vRotation = {0.0,180.0,0.0}} ))
7164 table.insert(Tokens, spawnNumberTokenFish( {iNum = 4, vPosition = {x=p.vPosition.x+0.5,y=1.075,z=p.vPosition.z}, vRotation = {0.0,180.0,0.0}} ))
7165 else
7166 table.insert(Tokens, spawnNumberTokenFish( {iNum = 11, vPosition = {x=p.vPosition.x-0.5,y=1.075,z=p.vPosition.z+0.5}, vRotation = {0.0,180.0,0.0}} ))
7167 table.insert(Tokens, spawnNumberTokenFish( {iNum = 12, vPosition = {x=p.vPosition.x+0.5,y=1.075,z=p.vPosition.z+0.5}, vRotation = {0.0,180.0,0.0}} ))
7168 table.insert(Tokens, spawnNumberTokenFish( {iNum = 2, vPosition = {x=p.vPosition.x-0.5,y=1.075,z=p.vPosition.z-0.5}, vRotation = {0.0,180.0,0.0}} ))
7169 table.insert(Tokens, spawnNumberTokenFish( {iNum = 3, vPosition = {x=p.vPosition.x+0.5,y=1.075,z=p.vPosition.z-0.5}, vRotation = {0.0,180.0,0.0}} ))
7170 end
7171
7172 if p.bLock == false then
7173 for i, Token in ipairs(Tokens) do
7174 addToUnlockList({Obj = Token})
7175 Token.use_snap_points = false
7176 end
7177 end
7178
7179 return Tile
7180end
7181
7182function spawnCastle(vPosition)
7183 local params = {}
7184 params.type = 'Custom_Tile'
7185 params.position = {vPosition.x, 0.96, vPosition.z}
7186 params.rotation = {0.0,90.0,0.0}
7187 params.scale = {1.735, 1.0, 2.0}
7188 local custom = {}
7189 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379912853/A299D139509BD49544CE5E5029B2261D26442D4E/'
7190 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601379910328/F0F3F849353CEADE84EFD7EBC9633E2D515D065B/'
7191 custom.thickness = 0.1
7192 custom.type = 1
7193
7194 local Tile = spawnObject(params)
7195 Tile.setCustomObject(custom)
7196 Tile.setDescription('Castle')
7197 Tile.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'hex\') end')
7198 Tile.setColorTint({0.99,0.906,0.62})
7199 Tile.setLock(true)
7200
7201 if m_bColorDieSpawned == false then
7202 local params = {}
7203 params.type = 'Custom_Dice'
7204 params.position = {vPosition.x, 2.0, vPosition.z}
7205 params.rotation = {0.0,-90.0,0.0}
7206 params.scale = {1.24999976, 1.24999976, 1.24999976}
7207
7208 local Die = spawnObject(params)
7209 Die.setCustomObject({image = 'http://cloud-3.steamusercontent.com/ugc/155773601379914796/5C44A4A31E8ACF20DBDABF7F19B86D8EDBF077CC/'})
7210 Die.setName('Color Die')
7211 Die.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'battack\') end')
7212 m_bColorDieSpawned = true
7213 end
7214
7215 return Tile
7216end
7217
7218function lockObject( Tile )
7219 Tile.setLock(true)
7220end
7221
7222function spawnTABBarbarian(p)
7223 local params = {}
7224 params.type = 'Custom_Model'
7225 params.position = p.vPos
7226 params.rotation = p.vRot
7227 local custom = {}
7228 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379916581/CC5F1A1245557667D4C8A638D33EDACDD211776B/'
7229 custom.diffuse = ''
7230 custom.type = 1 --figurine
7231 custom.material = 1 --wood
7232 local Barbarian = spawnObject(params)
7233 Barbarian.setCustomObject(custom)
7234 Barbarian.setName('Barbarian')
7235 Barbarian.setColorTint({0.745,0.608,0.4})
7236 return Barbarian
7237end
7238
7239function spawnHiddenNumberToken(p)
7240 Token = spawnNumberToken( {iNum = p.iNum, vPosition = p.vPos, vScale = p.vScale, vRotation = {0.0,180.0,180.0}} )
7241 local sScript = 'm_sName = \'' .. tostring(p.iNum) .. '\' ' ..
7242 Token.getLuaScript() .. --onload event & registration with scriptzone map
7243 --Routine to hide the object name while upside down
7244 ' function onCollisionEnter(collision_info) ' ..
7245 'local zRot = self.getRotation().z ' ..
7246 'if zRot < 230.0 and zRot > 150.0 then ' ..
7247 'if self.getName() ~= \'\' then self.setName(\'\') end ' ..
7248 'elseif self.getName() == \'\' then ' ..
7249 'self.setName(m_sName) ' ..
7250 'end ' ..
7251 'end'
7252 Token.setLuaScript(sScript)
7253 Token.setName('')
7254
7255 addToUnlockList({Obj = Token})
7256
7257 return Token
7258end
7259
7260function spawnNumberToken( p )
7261 local params = {}
7262 params.type = 'Custom_Model'
7263 params.position = {p.vPosition.x, p.vPosition.y, p.vPosition.z}
7264 params.rotation = p.vRotation
7265 params.scale = {1.5, 1.5, 1.5}
7266 if p.vScale ~= nil then
7267 params.scale = p.vScale
7268 end
7269
7270 local custom = {}
7271 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379918528/3DCDB66BED81A36614EE1ADDF4EE9BAC28F3F581/'
7272 if p.iNum == 2 then
7273 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380226244/D1B2EE97E84DEE989B29A73D9BEF7863C9C38DC5/'
7274 elseif p.iNum == 3 then
7275 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380226640/9EC721CFC6B970FA037495CD5FC2945669ECF5FE/'
7276 elseif p.iNum == 4 then
7277 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380226972/08D00021E63566357B54B42F96603C818D22ABCD/'
7278 elseif p.iNum == 5 then
7279 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380227302/EC88CC96CDB45A4DD1CD4926EB040CAB07206B1E/'
7280 elseif p.iNum == 6 then
7281 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380227728/A8C37F7BBA9AAD1059063731CC5E09BE1B3C954B/'
7282 elseif p.iNum == 8 then
7283 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380228044/668F73AFD5E1166AD5136756E0A857E67FA2B2A9/'
7284 elseif p.iNum == 9 then
7285 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380228423/A9138776CAF26E2B390E14D795F8CD203D3E1A7D/'
7286 elseif p.iNum == 10 then
7287 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380228887/27E401D034ED756729D0936F72350F6BA5F2A0A5/'
7288 elseif p.iNum == 11 then
7289 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380229190/7AC8D6001FB37322A545E77FA748862B1C303E29/'
7290 elseif p.iNum == 12 then
7291 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380229536/7352353D5C5DE259F280D323FB32929DD2C857D7/'
7292 end
7293 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379920973/0B3B12438279975756D988CC1F7756D3E152F2D7/'
7294 custom.type = 0 --generic
7295 custom.material = 3 --cardboard
7296
7297 local Token = spawnObject(params)
7298 Token.setName(p.iNum)
7299 Token.setCustomObject(custom)
7300
7301 --set a custom variable to identify the object type when the map is reset
7302 Token.setVar('sObjType', 'numtoken')
7303 Token.setLuaScript('function onLoad() ' ..
7304 'self.setVar(\'sObjType\', \'numtoken\') ' .. --keep the variable on rewind
7305 'self.setVar(\'iNum\', ' .. p.iNum .. ') ' ..
7306 'startLuaCoroutine(self, \'registerToken\') ' .. --Workaround: onLoad is called before the new object has its own GUID (when copied it still has the GUID of the source object...) -> wait a frame and then register the new token
7307 'end ' ..
7308 'function registerToken() ' ..
7309 'coroutine.yield(0) ' ..
7310 'local MapScript = getObjectFromGUID(\'42fd8e\') ' ..
7311 'if MapScript ~= nil then ' ..
7312 'MapScript.call(\'addNumberChip\', { TokenObj = self, iNum = self.getVar(\'iNum\') }) ' ..
7313 'return true ' ..
7314 'end ' ..
7315 'return false ' ..
7316 'end')
7317
7318 if m_CurScenario.bUseBAttack then --when tokens are turned upside down in this scenario check if any buildings get blocked from it
7319 Token.setLuaScript( Token.getLuaScript() ..
7320 [[
7321 function onCollisionEnter(collision_info)
7322 if collision_info ~= nil then
7323 if collision_info.collision_object.getVar('sObjType') == 'hex' then
7324 local TaBBag = Global.call('getTaBBag')
7325 if TaBBag ~= nil then
7326 TaBBag.call('checkBarbarianBlocks')
7327 end
7328 end
7329 end
7330 end
7331 ]]
7332 )
7333 end
7334
7335 Token.setLock(true)
7336
7337 return Token
7338end
7339
7340function spawnNumberTokenFish(p)
7341 local params = {}
7342 params.type = 'Custom_Model'
7343 params.position = {p.vPosition.x, p.vPosition.y, p.vPosition.z}
7344 params.rotation = p.vRotation
7345 params.scale = {0.95, 1.0, 0.95}
7346 if p.vScale ~= nil then
7347 params.scale = p.vScale
7348 end
7349 params.callback = 'callbackNumberTokenFish'
7350 params.callback_owner = Global
7351 params.params = { vScale = params.scale, FishTile = p.FishTile }
7352
7353 local custom = {}
7354 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379918528/3DCDB66BED81A36614EE1ADDF4EE9BAC28F3F581/'
7355 if p.iNum == 2 then
7356 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380226244/D1B2EE97E84DEE989B29A73D9BEF7863C9C38DC5/'
7357 elseif p.iNum == 3 then
7358 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380226640/9EC721CFC6B970FA037495CD5FC2945669ECF5FE/'
7359 elseif p.iNum == 4 then
7360 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380226972/08D00021E63566357B54B42F96603C818D22ABCD/'
7361 elseif p.iNum == 5 then
7362 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380227302/EC88CC96CDB45A4DD1CD4926EB040CAB07206B1E/'
7363 elseif p.iNum == 6 then
7364 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380227728/A8C37F7BBA9AAD1059063731CC5E09BE1B3C954B/'
7365 elseif p.iNum == 8 then
7366 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380228044/668F73AFD5E1166AD5136756E0A857E67FA2B2A9/'
7367 elseif p.iNum == 9 then
7368 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380228423/A9138776CAF26E2B390E14D795F8CD203D3E1A7D/'
7369 elseif p.iNum == 10 then
7370 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380228887/27E401D034ED756729D0936F72350F6BA5F2A0A5/'
7371 elseif p.iNum == 11 then
7372 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380229190/7AC8D6001FB37322A545E77FA748862B1C303E29/'
7373 elseif p.iNum == 12 then
7374 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380229536/7352353D5C5DE259F280D323FB32929DD2C857D7/'
7375 end
7376 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379920973/0B3B12438279975756D988CC1F7756D3E152F2D7/'
7377 custom.type = 0 --generic
7378 custom.material = 3 --cardboard
7379
7380 local Token = spawnObject(params)
7381 Token.setName(p.iNum)
7382 Token.setCustomObject(custom)
7383 Token.setColorTint({0.305882335,0.499539942,1.0})
7384
7385 Token.setLuaScript([[ m_sGUIDFishTile = nil
7386 function setFishTileGUID(p) m_sGUIDFishTile = p.sGUID end
7387 function getFishTileGUID() return m_sGUIDFishTile end
7388 function onSave()
7389 return JSON.encode({sGUIDFishTile = m_sGUIDFishTile})
7390 end
7391 function onLoad(saved_state)
7392 self.setVar('sObjType', 'numtoken')
7393 self.setVar('bFishToken', true)
7394 self.setVar('iNum', ]] .. p.iNum .. [[)
7395 local saved_data = JSON.decode(saved_state)
7396 if saved_data ~= nil then
7397 m_sGUIDFishTile = saved_data.sGUIDFishTile
7398 end
7399 startLuaCoroutine(self, 'registerToken')
7400 end
7401 function registerToken()
7402 --workaround: when a token is copied it will have the guid of the original item initially -> wait 1 frame
7403 coroutine.yield(0)
7404 local MapScript = getObjectFromGUID('42fd8e')
7405 if MapScript ~= nil then
7406 MapScript.call('addNumberChip', { TokenObj = self, iNum = self.getVar('iNum') })
7407 return true
7408 end
7409 return false
7410 end
7411 function onPickUp(player_color)
7412 m_sGUIDFishTile = nil --invalidate associated tile and find new one
7413 end
7414 function onCollisionEnter(collision_info)
7415 if m_sGUIDFishTile == nil then
7416 m_sGUIDFishTile = getFishTile()
7417 end
7418 end
7419 function getFishTile()
7420 local AllObj = getAllObjects()
7421 local vPos = self.getPosition()
7422 for i, Obj in ipairs(AllObj) do
7423 if Obj.getVar('sObjType') == 'fishermen' then
7424 local fDist = math.abs(Obj.getPosition().x - vPos.x) + math.abs(Obj.getPosition().z - vPos.z)
7425 if fDist < 1.5 then
7426 return Obj.getGUID()
7427 end
7428 end
7429 end
7430 end]]
7431 )
7432 Token.setLock(true)
7433
7434 return Token
7435end
7436function callbackNumberTokenFish(Object, p)
7437 if p.FishTile ~= nil then --set GUID of associated fish tile in the callback since the fish tile object does not have a GUID earlier
7438 Object.call('setFishTileGUID', {sGUID = p.FishTile.getGUID()})
7439 end
7440end
7441
7442--[[ spawn treasure tokens for TDD and additional scenarios
7443 Called from the respective control panel that defines the layout
7444--]]
7445function spawnTreasureToken(p)
7446 local params = {}
7447 params.type = 'Custom_Tile'
7448 params.position = p.vPos
7449 params.rotation = {0.0,180.0,0.0}
7450 params.scale = {0.45, 1.0, 0.45}
7451
7452 local custom = {}
7453 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379924745/1741D18558247F22A7DF9AD4317BE5701C798B69/'
7454 if p.sBack == '1res' then
7455 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380238738/7212F420BC6BFE34D9C9E5D912644304E794868E/'
7456 elseif p.sBack == '2res' then
7457 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380240691/C9E5CEABB085A5B87D34A6B6AEC037DA75CC88EB/'
7458 elseif p.sBack == '3res' then
7459 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380241222/822EF373AA3E68D059F1E7FE6351125D1051690D/'
7460 elseif p.sBack == 'dev' then
7461 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380241827/611419F9D4152FF0C394FC3D6FC60E3824C7904C/'
7462 elseif p.sBack == 'roads' then
7463 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380242274/A3FAA5B5A4C46E2A93B5B543B4B7042706FAEBA3/'
7464 elseif p.sBack == 'robber' then
7465 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380243237/D8065F9548E0183F0080FA7064C9379CAFA73757/'
7466 elseif p.sBack == 'vp' then
7467 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/155773601380244249/9A58840F1DD7F84E20C1019FC61A509352574138/'
7468 end
7469 custom.type = 0 --Box
7470 custom.thickness = 0.06
7471
7472 local Token = spawnObject(params)
7473 Token.setCustomObject(custom)
7474
7475 Token.setColorTint({0.93,0.53,0.0})
7476 Token.setName('Treasure Chest')
7477 local sDesc = ''
7478 if sBack == '1res' then
7479 sDesc = 'You may take any 1 resource from the bank'
7480 elseif sBack == '2res' then
7481 sDesc = 'You may take any 2 resources from the bank'
7482 elseif sBack == '3res' then
7483 sDesc = 'You get 1 Grain, 1 Wool, 1 Bricks'
7484 elseif sBack == 'dev' then
7485 sDesc = 'You may take 1 Development card (when playing with Cities & Knights: 1 Progress card)'
7486 elseif sBack == 'roads' then
7487 sDesc = 'You may build 2 roads/ships for free'
7488 end
7489 Token.setLuaScript('m_sDesc = \'' .. sDesc .. '\'\n' ..
7490 'function onCollisionEnter(collision_info) ' ..
7491 'local zRot = self.getRotation().z ' ..
7492 'if zRot < 0.0 then zRot = 360.0 + zRot end ' ..
7493 'if zRot < 230.0 and zRot > 130.0 then ' .. --it's facing down
7494 'if self.getDescription() == \'\' then ' ..
7495 'self.setDescription(m_sDesc) ' ..
7496 'end ' ..
7497 'else ' ..
7498 'if self.getDescription() ~= \'\' then ' ..
7499 'self.setDescription(\'\') ' ..
7500 'end ' ..
7501 'end ' ..
7502 'end ')
7503
7504 Token.setLock(true)
7505 Global.call('addToUnlockList', {Obj = Token})
7506
7507 return Token
7508end
7509
7510--[[ Spawn victory point card Longest Road ]]--
7511function spawnLongestRoadCard(vPos, vRot)
7512 local params = {}
7513 params.type = 'Custom_Tile'
7514 params.position = vPos
7515 params.rotation = vRot
7516 params.scale = {2.11755562,1.0,2.11755562}
7517 local custom = {}
7518 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379927983/C7109CE55B4403815B5515C29EC5E3B9E534FEEC/'
7519 custom.type = 0 --Box
7520 custom.thickness = 0.06
7521
7522 local Tile = spawnObject(params)
7523 Tile.setCustomObject(custom)
7524 Tile.setName('Longest Road')
7525 Tile.setDescription('2 Victory Points!\nThis card goes to the player with the longest unbroken road of at least 5 segments. Another player who builds a longer road takes this card.')
7526 Tile.setLock(true)
7527 addToUnlockList({Obj = Tile})
7528end
7529
7530--[[ Spawn victory point card Largest Army ]]--
7531function spawnLargestArmyCard(vPos, vRot)
7532 local params = {}
7533 params.type = 'Custom_Tile'
7534 params.position = vPos
7535 params.rotation = vRot
7536 params.scale = {2.11755562,1.0,2.11755562}
7537 local custom = {}
7538 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379932520/2B23103F9A5E74B60009817693B851FDF4D980B0/'
7539 custom.type = 0 --Box
7540 custom.thickness = 0.06
7541
7542 local Tile = spawnObject(params)
7543 Tile.setCustomObject(custom)
7544 Tile.setName('Largest Army')
7545 Tile.setDescription('2 Victory Points!\nThe first player to play 3 Knight cards gets this card. Another player who plays more Knight cards takes this card.')
7546 Tile.setLock(true)
7547 addToUnlockList({Obj = Tile})
7548end
7549
7550--[[ Spawn victory point card Harbormaster ]]--
7551function spawnHarbormasterCard(vPos, vRot)
7552 local params = {}
7553 params.type = 'Custom_Tile'
7554 params.position = vPos
7555 params.rotation = vRot
7556 params.scale = {2.11755562,1.0,2.11755562}
7557 local custom = {}
7558 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379935112/3E2BFEAEF079A9B4F94E071DE363B7BC3646D03A/'
7559 custom.type = 0 --Box
7560 custom.thickness = 0.06
7561
7562 local Tile = spawnObject(params)
7563 Tile.setCustomObject(custom)
7564 Tile.setName('Harbormaster')
7565 Tile.setDescription('2 Victory Points!\nThe first player to acquire 3 harbor points gets this card. Another player who acquires more harbor points takes this card.')
7566 Tile.setLock(true)
7567 addToUnlockList({Obj = Tile})
7568end
7569
7570--[[ Randomize terrain hexes in the placement info table ]]--
7571function shuffleHexes( InfoTab )
7572 -- Create sequential table with all indexes of InfoTab that can be randomized
7573 local Keys = {}
7574 for i, Line in pairs(InfoTab) do
7575 if Line.bRandHex then
7576 table.insert(Keys, i)
7577 end
7578 end
7579
7580 if #Keys <= 1 then
7581 return
7582 end
7583
7584 for i = #Keys, 2, -1 do
7585 local iRand = nil
7586 local iRUpper = i
7587 local iCount = 0
7588 while true do
7589 iCount = iCount + 1
7590 iRand = math.random(iRUpper)
7591 if InfoTab[Keys[iRand]].iGroup == InfoTab[Keys[i]].iGroup then --allow tile groups to be shuffled only amongst each other
7592 local bSwitch = true
7593 if m_CurScenario.bUseFishermen == true then --make sure the lake tile is not put next to the ocean
7594 if InfoTab[Keys[i]].sHexType == 'lake' then --check if the new position is adjacent to water
7595 local OceanTiles = getOceanNeighbors(InfoTab, Keys[iRand])
7596 if #OceanTiles == 1 then --check if the adjacent ocean tile is the tile we are moving right now
7597 if OceanTiles[1].index == Keys[i] --it is the tile we are moving
7598 and InfoTab[Keys[iRand]].sHexType ~= 'ocean' then --do not allow if we are switching with an ocean tile
7599 else
7600 bSwitch = false
7601 end
7602 elseif #OceanTiles > 0 then
7603 bSwitch = false
7604 end
7605
7606 elseif InfoTab[Keys[iRand]].sHexType == 'lake' then --check if the current hex is adjacent to ocean (is it a valid position for a lake)
7607 local OceanTiles = getOceanNeighbors(InfoTab, Keys[i])
7608 if #OceanTiles == 1 then
7609 if OceanTiles[1].index == Keys[iRand] and InfoTab[Keys[i]].sHexType ~= 'ocean' then
7610 else
7611 bSwitch = false
7612 end
7613 elseif #OceanTiles > 0 then
7614 bSwitch = false
7615 end
7616 end
7617 end
7618 if bSwitch then
7619 break
7620 end
7621 end
7622 --when looking for a spot for lake tiles it might happen that the last two positions are lakes
7623 --> just switch it with any hex
7624 if iCount >= #Keys then
7625 iRUpper = #Keys
7626 end
7627 end
7628
7629 if (InfoTab[Keys[i]].bRandNum ~= false and InfoTab[Keys[iRand]].bRandNum ~= false)
7630 or (InfoTab[Keys[i]].iNumToken ~= nil and InfoTab[Keys[iRand]].iNumToken ~= nil) then --do not switch a hex with a fixed number token with one that does not have a token (e.g. desert)
7631 InfoTab[Keys[i]].sHexType, InfoTab[Keys[iRand]].sHexType = InfoTab[Keys[iRand]].sHexType, InfoTab[Keys[i]].sHexType
7632 end
7633 if InfoTab[Keys[i]].bRandNum ~= false and InfoTab[Keys[iRand]].bRandNum ~= false then --do not switch the number tokens, they are supposed to be fixed
7634 InfoTab[Keys[i]].iNumToken, InfoTab[Keys[iRand]].iNumToken = InfoTab[Keys[iRand]].iNumToken, InfoTab[Keys[i]].iNumToken
7635 InfoTab[Keys[i]].iNumTokenSec, InfoTab[Keys[iRand]].iNumTokenSec = InfoTab[Keys[iRand]].iNumTokenSec, InfoTab[Keys[i]].iNumTokenSec
7636 end
7637 InfoTab[Keys[i]].bRobberPos, InfoTab[Keys[iRand]].bRobberPos = InfoTab[Keys[iRand]].bRobberPos, InfoTab[Keys[i]].bRobberPos
7638 InfoTab[Keys[i]].bSecLake, InfoTab[Keys[iRand]].bSecLake = InfoTab[Keys[iRand]].bSecLake, InfoTab[Keys[i]].bSecLake
7639 InfoTab[Keys[i]].sOilSource, InfoTab[Keys[iRand]].sOilSource = InfoTab[Keys[iRand]].sOilSource, InfoTab[Keys[i]].sOilSource
7640 end
7641end
7642
7643--check the tiles adjacent to a given position index and return a table with all tiles that are ocean
7644function getOceanNeighbors(PlacemTable, idx)
7645 local OceanList = {}
7646 for sDir, iTile in pairs(NBRS[idx]) do
7647 if PlacemTable[iTile] ~= nil then
7648 local sType = PlacemTable[iTile].sHexType
7649 if sType == 'ocean' or sType == 'border' or sType == 'corner' or sType == 'side' or sType == 'lake' then
7650 table.insert(OceanList,{index = iTile, sDir = sDir})
7651 end
7652 end
7653 end
7654 return OceanList
7655end
7656
7657--[[ Randomize number chips assigned to terrain hexes
7658 This function will also make sure there are no two red numbers (6, 8) next to each other
7659 ]]--
7660function shuffleChips( InfoTab )
7661 -- Create sequential table with all indexes of InfoTab that can be randomized
7662 local Keys = {}
7663 for iIdx, Line in pairs(InfoTab) do
7664 if Line.iNumToken ~= nil and ((Line.bRandHex and Line.bRandNum ~= false) or Line.bRandNum == true) then
7665 table.insert(Keys, iIdx)
7666 end
7667 end
7668
7669 if #Keys <= 1 then
7670 return
7671 end
7672
7673 -- Randomize the numbers
7674 for i = #Keys, 2, -1 do
7675 while true do
7676 local iRand = math.random(i)
7677 if InfoTab[Keys[iRand]].iGroup == InfoTab[Keys[i]].iGroup then --allow tile groups to be shuffled only amongst each other
7678 InfoTab[Keys[i]].iNumToken, InfoTab[Keys[iRand]].iNumToken = InfoTab[Keys[iRand]].iNumToken, InfoTab[Keys[i]].iNumToken
7679 InfoTab[Keys[i]].iNumTokenSec, InfoTab[Keys[iRand]].iNumTokenSec = InfoTab[Keys[iRand]].iNumTokenSec, InfoTab[Keys[i]].iNumTokenSec
7680 break
7681 end
7682 end
7683 end
7684
7685 -- Check for neighboring red numbers and replace one of them with a black token
7686 for i=1, #Keys do
7687 if isChipRed( InfoTab[Keys[i]].iNumToken ) and InfoTab[Keys[i]].bRandNum ~= false then
7688 --check if the chip has any red neighbor chips
7689 local bSwitch = false
7690 for sDir, iNbIdx in pairs(NBRS[Keys[i]]) do
7691 if InfoTab[iNbIdx] ~= nil then
7692 if isChipRed( InfoTab[iNbIdx].iNumToken ) then
7693 bSwitch = true
7694 break
7695 end
7696 end
7697 end
7698
7699 if bSwitch then
7700 replaceRedChip( InfoTab, Keys, Keys[i] )
7701 end
7702 end
7703 end
7704end
7705
7706--[[ Randomize harbors defined in the map tile placement info table ]]--
7707function shuffleHarbors( InfoTab )
7708 -- Create sequential table with all indexes of InfoTab that can be randomized
7709 local Keys = {}
7710 for i, Line in pairs(InfoTab) do
7711 if Line.sHarborType ~= nil and Line.bRandHarbor then
7712 table.insert(Keys, i)
7713 end
7714 end
7715
7716 if #Keys <= 1 then
7717 return
7718 end
7719
7720 for i = #Keys, 2, -1 do
7721 local iRand = nil
7722 while true do
7723 iRand = math.random(i)
7724 if InfoTab[Keys[iRand]].iGroup == InfoTab[Keys[i]].iGroup then --allow tile groups to be shuffled only amongst each other
7725 break
7726 end
7727 end
7728
7729 InfoTab[Keys[i]].sHarborType, InfoTab[Keys[iRand]].sHarborType = InfoTab[Keys[iRand]].sHarborType, InfoTab[Keys[i]].sHarborType
7730 end
7731end
7732
7733--[[ Define in which positions to spawn fish tiles depending on the position of the land tiles & assign token numbers ]]--
7734function setFishTiles( InfoTab )
7735 local FishTiles = {}
7736 -- Create sequential table with all indexes of InfoTab that can be randomized
7737 local Keys = {}
7738 for i, Line in pairs(InfoTab) do
7739 if Line.vFishRot ~= nil then
7740 if Line.bVarFish == true then
7741 table.insert(Keys, i)
7742 else
7743 table.insert(FishTiles, i)
7744 end
7745 end
7746 end
7747
7748 --randomize the order of variable fish tiles
7749 if #Keys > 0 then
7750 for i = #Keys, 2, -1 do
7751 local iRand = math.random(i)
7752 Keys[i], Keys[iRand] = Keys[iRand], Keys[i]
7753 end
7754 end
7755
7756 local iTodo = 6
7757 local Numbers = {4,5,6,8,9,10}
7758 if m_CurScenario.bExtension == true then
7759 iTodo = 8
7760 Numbers = {4,5,6,8,9,10,5,9}
7761 end
7762 iTodo = iTodo - #FishTiles
7763
7764 --randomize the order of numbers
7765 for i = #Numbers, 2, -1 do
7766 local iRand = math.random(i)
7767 Numbers[i], Numbers[iRand] = Numbers[iRand], Numbers[i]
7768 end
7769
7770 --check which of the possible fish tile locations are valid
7771 for iRun = 1, 2 do
7772 local bOptimize = true
7773 if iRun == 2 then
7774 bOptimize = false --we tried positioning all fish tiles but did not find a valid spot for all -> check again without min spacing
7775 end
7776 for i, iKey in pairs(Keys) do
7777 if iTodo == 0 then break end
7778
7779 local sType = InfoTab[iKey].sHexType
7780 if sType == 'ocean' or sType == 'border' or sType == 'corner' then --tile itself must be an ocean tile
7781 local OceanTiles = getOceanNeighbors(InfoTab, iKey)
7782 local TestDir = {}
7783 local fOrient = InfoTab[iKey].vFishRot[2]
7784 if fOrient == 180.0 then --N
7785 TestDir = {'NW','NE'}
7786 elseif fOrient == -120.0 then --NE
7787 TestDir = {'NE', 'E'}
7788 elseif fOrient == -60.0 then --SE
7789 TestDir = {'E', 'SE'}
7790 elseif fOrient == 0.0 then --S
7791 TestDir = {'SE', 'SW'}
7792 elseif fOrient == 60.0 then --SW
7793 TestDir = {'SW', 'W'}
7794 elseif fOrient == 120.0 then --NW
7795 TestDir = {'W', 'NW'}
7796 end
7797 local bSpawn = true
7798 for j = 1, #TestDir do
7799 for k = 1, #OceanTiles do
7800 if OceanTiles[k].sDir == TestDir[j] then --ocean tile in relevant direction -> invalid position for a fish tile
7801 bSpawn = false
7802 break
7803 end
7804 end
7805 end
7806
7807 if bSpawn == true and bOptimize == true then --do not spawn a fish tile when one is already next to it (distribute them more evenly)
7808 local CheckNBRS = NBRS[iKey]
7809 for sDir, idx in pairs(CheckNBRS) do
7810 for idxFish = 1, #FishTiles do
7811 if FishTiles[idxFish] == idx then
7812 bSpawn = false
7813 break
7814 end
7815 end
7816 end
7817 end
7818
7819 if bSpawn then
7820 table.insert(FishTiles, iKey)
7821 Keys[i] = nil
7822 iTodo = iTodo - 1
7823 end
7824 end
7825 end
7826 if iTodo == 0 then break end
7827 end
7828
7829 --assign a number token to each tile
7830 for i = 1, #FishTiles do
7831 if InfoTab[FishTiles[i]].iNumTokenFish == nil then
7832 InfoTab[FishTiles[i]].iNumTokenFish = Numbers[i]
7833 end
7834 end
7835
7836 --remove fish tile info for tiles that should not receive a fish tile
7837 for i, iLine in pairs(Keys) do
7838 InfoTab[iLine].vFishRot = nil
7839 end
7840end
7841
7842--[[ Checks if a number chip GUID is a red number (6, 8)
7843 Returns true when it is either a 6 or an 8]]--
7844function isChipRed( iNumToken )
7845 if iNumToken == 6 or iNumToken == 8 then
7846 return true
7847 end
7848 return false
7849end
7850
7851--[[ Find a black number chip that has no red neighboring chips and switch it with the passed chip ]]--
7852function replaceRedChip( InfoTab, Keys, iHexIdx )
7853 local iRand = nil
7854 local bFound = true
7855 local iCounter = 1
7856 local HexInfo = InfoTab[iHexIdx]
7857 --search for a random black chip that doesn't have any red neighbors
7858 while iCounter < 30 do
7859 iRand = math.random(1, #Keys)
7860 if InfoTab[Keys[iRand]].iGroup == HexInfo.iGroup then
7861 iCounter = iCounter + 1
7862 bFound = true
7863 if InfoTab[Keys[iRand]].bRandNum == false then
7864 bFound = false
7865 elseif isChipRed( InfoTab[Keys[iRand]].iNumToken ) then
7866 bFound = false
7867 else
7868 --target chip is black -> check if there are any red neighbors
7869 for sDir, iNbIdx in pairs(NBRS[Keys[iRand]]) do
7870 if InfoTab[iNbIdx] ~= nil and InfoTab[iNbIdx].iNumToken ~= nil then
7871 if iNbIdx ~= iHexIdx then --ignore the token to be switched when checking for red neighbors
7872 if isChipRed( InfoTab[iNbIdx].iNumToken ) then
7873 bFound = false
7874 break
7875 end
7876 end
7877 end
7878 end
7879 end
7880 if bFound then
7881 HexInfo.iNumToken, InfoTab[Keys[iRand]].iNumToken = InfoTab[Keys[iRand]].iNumToken, HexInfo.iNumToken
7882 HexInfo.iNumTokenSec, InfoTab[Keys[iRand]].iNumTokenSec = InfoTab[Keys[iRand]].iNumTokenSec, HexInfo.iNumTokenSec
7883 break
7884 end
7885 end
7886 end
7887end
7888
7889--[[ spawnIndicatorArrow
7890 spawn a little twirling/dancing arrow
7891 - called from MapScript to indicate settlements / cities that receive resources from gold fields
7892 - called from the special build phase indicator pawns when a turn ends and they are in front of the player area
7893]]
7894function spawnIndicatorArrow(p)
7895 p.fHeight = p.fHeight or 0.25 --default height (up and down movement distance)
7896
7897 local params = {}
7898 params.type = 'Custom_Model'
7899 params.position = p.vPos
7900 params.scale = p.vScale
7901
7902 local custom = {}
7903 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379937202/9CB668278FFE49D61A4917473D98D59CD54BB369/'
7904 custom.collider = 'http://cloud-3.steamusercontent.com/ugc/155773601379939502/63697125130DFF7A7B53ADDA8798B847C80FC799/'
7905 custom.diffuse = ''
7906 custom.type = 0 --generic
7907
7908 local Arrow = spawnObject(params)
7909 Arrow.setCustomObject(custom)
7910 Arrow.interactable = false
7911 Arrow.use_gravity = false
7912 Arrow.setColorTint(p.vColor)
7913 Arrow.setLuaScript([[ m_fCreated = 0.0
7914 function onLoad(save_state)
7915 Timer.create({
7916 identifier = self.guid .. '_Timer',
7917 function_name = 'destroyArrow',
7918 function_owner = self,
7919 delay = ]] .. p.fTTL .. [[
7920 })
7921 self.setVar('sObjType', 'arrow')
7922 m_fCreated = os.time()
7923 end
7924 function destroyArrow()
7925 self.destruct()
7926 end
7927 function onDestroy()
7928 Timer.destroy(self.guid .. '_Timer')
7929 end
7930 function onUpdate()
7931 self.rotate({0.0,20.0,0.0})
7932 local fYtrans = math.sin((os.time()-m_fCreated)*3.0) * ]] .. p.fHeight .. [[
7933 self.translate({0.0,fYtrans,0.0})
7934 end
7935 ]])
7936 return Arrow
7937end
7938
7939--[[ checkCardLimits
7940 Called from update function when a 7 is rolled. This will count the resource cards for all players to check if they
7941 are exceeding the limit for the robber. If so, the number of cards is displayed by the relevant scripting zone.
7942--]]
7943function checkCardLimits()
7944 coroutine.yield(0)
7945 coroutine.yield(0)
7946
7947 local PlayerData = {
7948 ['Orange'] = {iLimit = 7, iCards = 0, sGUIDzone = '15c3aa' },
7949 ['Red'] = {iLimit = 7, iCards = 0, sGUIDzone = '24632e' },
7950 ['White'] = {iLimit = 7, iCards = 0, sGUIDzone = 'cdde1f' },
7951 ['Purple'] = {iLimit = 7, iCards = 0, sGUIDzone = 'a60284' },
7952 ['Blue'] = {iLimit = 7, iCards = 0, sGUIDzone = 'f37d36' },
7953 ['Green'] = {iLimit = 7, iCards = 0, sGUIDzone = 'd866a9' },
7954 }
7955
7956-- Determine the card limit for each player
7957 --Find out the current card limit for each player when playing with C&K
7958 if isCKenabled() then
7959 if m_MapScript == nil then
7960 return false
7961 else
7962 local MapObj = m_MapScript.getObjects()
7963 for i = 1, #MapObj do
7964 if MapObj[i].getName() == 'Wall' then
7965 local sColor = MapObj[i].getDescription()
7966 if PlayerData[sColor] ~= nil then
7967 PlayerData[sColor].iLimit = PlayerData[sColor].iLimit + 2
7968 end
7969 end
7970 end
7971 end
7972 end
7973 --In Treasures scenario T2 players can increase their card limit by holding 1 treasure
7974 if getScenarioID() == 'T2' then
7975 for sColor, Info in pairs(PlayerData) do
7976 local Zone = getObjectFromGUID(Info.sGUIDzone)
7977 if Zone ~= nil then
7978 local ZoneObj = Zone.getObjects()
7979 for i = 1, #ZoneObj do
7980 if ZoneObj[i].getName() == 'Treasure Chest' then
7981 --PlayerData[sColor].iLimit = PlayerData[sColor].iLimit + 2
7982 Info.iLimit = Info.iLimit + 2
7983 break --only the first treasure increases the limit
7984 end
7985 end
7986 end
7987 end
7988 end
7989
7990-- Count the cards held by each player
7991 for sColor, Info in pairs(PlayerData) do
7992 local HandObj = Player[sColor].getHandObjects()
7993 for i = 1, #HandObj do
7994 local sName = HandObj[i].getName()
7995 if sName == 'Lumber' or sName == 'Bricks' or sName == 'Wool' or sName == 'Grain' or sName == 'Ore' or
7996 sName == 'Coins' or sName == 'Cloth' or sName == 'Paper' then
7997 Info.iCards = Info.iCards + 1
7998 end
7999 end
8000 end
8001
8002-- Check which players exceed their limit
8003 for sColor, Info in pairs(PlayerData) do
8004 if Info.iCards > Info.iLimit then
8005 local Zone = getObjectFromGUID(Info.sGUIDzone)
8006 Zone.call('displayCardCount', {iCards = Info.iCards})
8007 end
8008 end
8009
8010 return true
8011end
8012--clears the 3D text showing the card counts when a player exceeded the limit on a 7
8013--run as a coroutine to make sure not to block anything important by this little gimmick (it gets called from update on a roll that is not 7)
8014function clearCardCounts()
8015 coroutine.yield(0)
8016 coroutine.yield(0)
8017
8018 local Zones = {
8019 '15c3aa', --Orange
8020 '24632e', --Red
8021 'cdde1f', --White
8022 'a60284', --Purple
8023 'f37d36', --Blue
8024 'd866a9', --Green
8025 }
8026 for i = 1, #Zones do
8027 local PlZone = getObjectFromGUID(Zones[i])
8028 if PlZone ~= nil then
8029 PlZone.call('clearCardCount')
8030 end
8031 end
8032 return true
8033end
8034
8035function callback_HexLoaded()
8036 m_iLoadingHexes = m_iLoadingHexes - 1
8037
8038 if m_iLoadingHexes == 0 then --all hex tiles have finished loading -> unlock all number tokens so they drop on the map
8039 for i, Obj in ipairs(m_UnlockOnLoad) do
8040 if Obj ~= nil then
8041 Obj.setLock(false)
8042 end
8043 end
8044 m_UnlockOnLoad = {}
8045
8046 --reset the counters -> cannot be done right after setting up the player items because the set position seems to trigger the onLeave event 1 frame later...
8047 if m_MapScript ~= nil then
8048 m_MapScript.call('setCountersEnabled', {bEnable = true})
8049 end
8050
8051 --consider the game loaded (enable dice highlighting etc.)
8052 m_bMapLoaded = true
8053 end
8054end
8055
8056--[[ workaround infinite bag preview
8057 This will clone a spawn bag, draw the first object from it and then delete the bag again
8058 made necessary because the object preview of inifinite bags will load the object even if
8059 it is still in the bag
8060]]--
8061function takeObjectWorkaround(Bag, params)
8062 local vPos = Bag.getPosition()
8063 local CloneBag = Bag.clone({position = vPos})
8064 CloneBag.setPosition(vPos) --otherwise the bag will be spawned very high above the original bag...
8065 local Obj = CloneBag.takeObject(params)
8066 CloneBag.destruct()
8067 return Obj
8068end
8069
8070--[[
8071 Spawn items for Cities & Knights (most work is done in setupPlayerItems and setupDecks however...)
8072--]]
8073function spawnCKExpansion()
8074 local ThRoutine = coroutine.create( function (ThNext)
8075 coroutine.yield(1)
8076
8077 --event die
8078 if m_EventDie ~= nil then
8079 m_EventDie.setRotation({0.0,0.0,0.0})
8080 m_EventDie.setPosition({x=-31.0,y=1.373,z=-1.5})
8081 else
8082 m_EventDie = spawnEventDie({-31.0,1.373,-1.5})
8083 end
8084
8085 --Spawn Metropolis gates
8086 local vPosMetrP = {x=-23.0,y=1.3,z=-11.4}
8087 local vPosMetrS = {x=-23.0,y=1.3,z=-12.4}
8088 local vPosMetrT = {x=-23.0,y=1.3,z=-13.4}
8089 if m_CurScenario.bUseFishermen then --move metropolises out of the way of the fishermen board
8090 vPosMetrP.x = -18.0
8091 vPosMetrS.x = -18.0
8092 vPosMetrT.x = -18.0
8093 end
8094 spawnMetropolisGate({vPosition=vPosMetrP, vRotation={0.0,90.0,0.0}, sType='Politics'})
8095 spawnMetropolisGate({vPosition=vPosMetrS, vRotation={0.0,90.0,0.0}, sType='Science'})
8096 spawnMetropolisGate({vPosition=vPosMetrT, vRotation={0.0,90.0,0.0}, sType='Trade'})
8097
8098 --color the second die red
8099 if m_Die2 ~= nil then
8100 m_Die2.setColorTint({0.9,0.0,0.0})
8101 end
8102
8103 --spawn the city upgrade charts in all player areas
8104 local ChartPos = { ['Orange'] = { x=-41.1385, y=0.9852, z=-19.29 },
8105 ['Red'] = { x=-11.52, y=0.9852, z=-19.29 },
8106 ['White'] = { x=17.7826, y=0.9852, z=-19.29 },
8107 ['Purple'] = { x=41.1845, y=0.9852, z=19.29 },
8108 ['Blue'] = { x=11.156, y=0.9852, z=19.29 },
8109 ['Green'] = { x=-18.0121, y=0.9852, z=19.29 }
8110 }
8111 local CKitemsBag = getObjectFromGUID(m_CKitems.Bag)
8112 if CKitemsBag ~= nil then
8113 local vInitPos = CKitemsBag.getPosition()
8114 vInitPos.x = vInitPos.x + 4.0
8115
8116 local params = { callback = 'callbackGeneral', callback_owner = Global }
8117 local iNum = 0
8118 for sCol, vPos in pairs(ChartPos) do
8119 local vRot = {0.0,0.0,0.0}
8120 local fModX = -1.0
8121 if vPos.z < 0.0 then
8122 vRot[2] = 180.0
8123 fModX = 1.0
8124 end
8125 params.guid = m_CKitems.Item['ChartPolitics']
8126 params.params = {vPos = vPos, vRot = vRot, bLock = true}
8127 params.position = {vInitPos.x, vInitPos.y - iNum * 0.3, vInitPos.z} --vary the heigt of the initial position to avoid them colliding and being placed at an angle
8128 takeObjectWorkaround(CKitemsBag, params)
8129 iNum = iNum + 1
8130 params.guid = m_CKitems.Item['ChartScience']
8131 params.params = {vPos = {x=vPos.x+(3.255*fModX),y=vPos.y,z=vPos.z}, vRot = vRot, bLock = true}
8132 params.position = {vInitPos.x, vInitPos.y - iNum * 0.3, vInitPos.z} --vary the heigt of the initial position to avoid them colliding and being placed at an angle
8133 takeObjectWorkaround(CKitemsBag, params)
8134 iNum = iNum + 1
8135 params.guid = m_CKitems.Item['ChartTrade']
8136 params.params = {vPos = {x=vPos.x+(3.255*2*fModX),y=vPos.y,z=vPos.z}, vRot = vRot, bLock = true}
8137 params.position = {vInitPos.x, vInitPos.y - iNum * 0.3, vInitPos.z} --vary the heigt of the initial position to avoid them colliding and being placed at an angle
8138 takeObjectWorkaround(CKitemsBag, params)
8139 iNum = iNum + 1
8140 end
8141 end
8142
8143
8144 if ThNext ~= nil then
8145 coroutine.resume(ThNext)
8146 end
8147 return true
8148 end)
8149 return ThRoutine
8150end
8151
8152--[[ spawn objects for the Frenemies mini-expansion
8153 Called from onMapLoad when a map is loaded and the mini expansion enabled
8154]]--
8155function spawnFrenemiesExpansion()
8156 local ThRoutine = coroutine.create( function (ThNext)
8157 coroutine.yield(1)
8158
8159 local iPlayers = m_iFrenemiesPlayers
8160 --board:
8161 local params = {}
8162 params.type = 'Custom_Tile'
8163 params.position = {-23.032, 0.96, 12.938}
8164 params.rotation = {0.0,180.0,0.0}
8165 params.scale = {1.1,1.0,1.1}
8166 local custom = {}
8167 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379941451/382D6A4EAD28F35910F6FC56A9DF65FAF7CFD1AD/'
8168 custom.type = 0 --Box
8169 custom.thickness = 0.06
8170
8171 local Board = spawnObject(params)
8172 Board.setCustomObject(custom)
8173 Board.setDescription('Frenemies Mini-Expansion\nPut spent favor tokens into the bags.')
8174 Board.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
8175 Board.setColorTint({0.902,0.892,0.8314})
8176 Board.setLock(true)
8177
8178 --spawn discard bags:
8179 params = {}
8180 params.type = 'Bag'
8181 params.rotation = {0.0,180.0,0.0}
8182 params.scale = {0.475,0.475,0.475}
8183 --Trader
8184 params.position = {-27.0, 0.872, 14.64}
8185 local TokenBag = spawnObject(params)
8186 TokenBag.setName('Trader Tokens')
8187 TokenBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
8188 TokenBag.setColorTint({0.9255,0.91,0.796})
8189 TokenBag.setLock(true)
8190 --Merchant
8191 params.position = {-25.0, 0.872, 14.64}
8192 local TokenBag = spawnObject(params)
8193 TokenBag.setName('Merchant Tokens')
8194 TokenBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
8195 TokenBag.setColorTint({0.78,0.851,0.992})
8196 TokenBag.setLock(true)
8197 --Road Builder
8198 params.position = {-23.0, 0.872, 14.64}
8199 local TokenBag = spawnObject(params)
8200 TokenBag.setName('Road Builder Tokens')
8201 TokenBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
8202 TokenBag.setColorTint({0.824,0.616,0.412})
8203 TokenBag.setLock(true)
8204 --Scholar
8205 params.position = {-21.0, 0.872, 14.64}
8206 local TokenBag = spawnObject(params)
8207 TokenBag.setName('Scholar Tokens')
8208 TokenBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
8209 TokenBag.setColorTint({0.949,0.843,0.337})
8210 TokenBag.setLock(true)
8211 --Master Builder
8212 params.position = {-19.0, 0.872, 14.64}
8213 local TokenBag = spawnObject(params)
8214 TokenBag.setName('Master Builder Tokens')
8215 TokenBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
8216 TokenBag.setColorTint({0.737,0.89,0.533})
8217 TokenBag.setLock(true)
8218
8219 -- Bag to place tokens in
8220 params = {}
8221 params.type = 'Bag'
8222 params.position = {-19.0, 0.849, 8.77}
8223 params.rotation = {0.0,180.0,0.0}
8224 params.scale = {0.8,0.6,0.8}
8225 local TokenBag = spawnObject(params)
8226 TokenBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
8227 TokenBag.setName('Frenemies Favor Tokens')
8228 TokenBag.setColorTint({0.902,0.892,0.8314})
8229
8230 --generate list with required amount of tokens
8231 local Tokens = {}
8232 local iNum = math.ceil(4.2 * iPlayers)
8233 for i = 1, iNum do
8234 table.insert(Tokens, 'book')
8235 table.insert(Tokens, 'compass')
8236 end
8237 iNum = iPlayers * 2
8238 for i = 1, iNum do
8239 table.insert(Tokens, 'ship')
8240 table.insert(Tokens, 'shovel')
8241 table.insert(Tokens, 'wagon')
8242 end
8243 for i = #Tokens, 2, -1 do --make the order random
8244 local iRand = math.random(i)
8245 Tokens[i],Tokens[iRand] = Tokens[iRand],Tokens[i]
8246 end
8247
8248 --spawn the tokens to be put into the bag
8249 for i, sType in pairs(Tokens) do
8250 local params = {}
8251 params.type = 'Custom_Model'
8252 params.position = {-70.0, 200.0, -40.0}
8253 params.rotation = {0.0,180.0,180.0}
8254 local custom = {}
8255 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379943471/32300EFCC842A3D85EFCEDE3E61E9639BC1F683D/'
8256 if sType == 'book' then
8257 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380251523/05B4E90B3B3000828AED2A94974B3FE5C0EC92D1/'
8258 elseif sType == 'compass' then
8259 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380252189/5843A89BB35F414B15BCEC579D0434DF801DFA77/'
8260 elseif sType == 'ship' then
8261 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380252779/30C6B37EB353430B9D906A81E37B9F2CB2F684DE/'
8262 elseif sType == 'shovel' then
8263 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380253308/297E093104E94D8DC7DFFF244D2BABBE59CBD58F/'
8264 elseif sType == 'wagon' then
8265 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601380253938/17064D72B64B52A816CD26497542A8AFADF31242/'
8266 end
8267 custom.type = 0 --generic
8268 custom.material = 3 --cardboard
8269 local Token = spawnObject(params)
8270 Token.setCustomObject(custom)
8271 Token.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
8272 Token.setColorTint({0.902,0.892,0.8314})
8273 TokenBag.putObject(Token)
8274 end
8275
8276 --Spawn victory point tokens
8277 iNum = iPlayers * 2
8278 if iPlayers == 6 then iNum = 13 end
8279 local params = {}
8280 params.type = 'Custom_Model'
8281 params.rotation = {0.0,180.0,0.0}
8282 local custom = {}
8283 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379943471/32300EFCC842A3D85EFCEDE3E61E9639BC1F683D/'
8284 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379946921/965DEA504C0F686ECA7C1BE850FE8CE9DA03BD5E/'
8285 custom.type = 0 --generic
8286 custom.material = 3 --cardboard
8287 local vPos = {-19.505, 1.0, 11.0}
8288 for i = 1, iNum do
8289 vPos[2] = vPos[2] + 0.1
8290 params.position = vPos
8291 local Token = spawnObject(params)
8292 Token.setCustomObject(custom)
8293 Token.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'frenemies\') end') --keep the variable on rewind
8294 Token.setColorTint({0.733,0.259,0.184})
8295 Token.setName('1 Victory Point')
8296
8297 if i == math.ceil(iNum / 2) then
8298 vPos[1] = vPos[1] + 1.1
8299 vPos[2] = 1.0
8300 end
8301 end
8302
8303 if ThNext ~= nil then
8304 coroutine.resume(ThNext)
8305 end
8306 return true
8307 end)
8308 return ThRoutine
8309end
8310
8311--[[ spawn objects for the Helpers of Catan mini-expansion
8312 Called from onMapLoad when a map is loaded and mini expansion enabled
8313]]--
8314function spawnHelpersExpansion()
8315 local ThRoutine = coroutine.create( function (ThNext)
8316 coroutine.yield()
8317
8318 local ThCurrent = coroutine.running()
8319
8320 m_HelpersItems = {Bag = 'ba7910', Item = {['Deck'] = 'b08360', ['Board'] = 'a1982d'}}
8321 local SpawnBag = getObjectFromGUID(m_HelpersItems.Bag)
8322 if SpawnBag ~= nil then
8323 local params = {}
8324 params.guid = m_HelpersItems.Item['Deck']
8325 params.callback_owner = Global
8326 params.callback = 'callbackGeneral'
8327 params.params = {vPos = {-21.2,1.1,-7.71}, vRot = {0.0,180.0,0.0}, bLock = false, ThResume = ThCurrent}
8328 local Deck = takeObjectWorkaround(SpawnBag, params)
8329
8330 coroutine.yield()
8331
8332 params = {}
8333 params.guid = m_HelpersItems.Item['Board']
8334 params.callback_owner = Global
8335 params.callback = 'callbackGeneral'
8336 params.position = {-50.0,4.0,0.0}
8337 params.rotation = {10.0,90.0,0.0}
8338 params.params = {vPos = {-50.0,4.0,0.0}, vRot = {10.0,90.0,0.0}, bLock = true, ThResume = ThCurrent}
8339 local Board = takeObjectWorkaround(SpawnBag, params)
8340 Board.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'helpers\') end')
8341 coroutine.yield()
8342
8343 --take individual cards from the deck and place them on display
8344 local vBasePos = {-52.64,6.0,0.0}
8345 local params = {}
8346 params.callback_owner = Global
8347 params.callback = 'callbackGeneral'
8348
8349 --Jean
8350 local Cards = Deck.getObjects()
8351 for i = 1, #Cards do
8352 if Cards[i].nickname == 'Jean' then
8353 local vCardPos = {vBasePos[1], vBasePos[2], vBasePos[3] - 7.5}
8354 params.index = Cards[i].index
8355 params.position = vCardPos
8356 params.params = {vPos = vCardPos, vRot = {10.0,90.0,0.0}, bLock = false, ThResume = ThCurrent}
8357 Deck.takeObject(params)
8358 coroutine.yield()
8359 break
8360 end
8361 end
8362 --Vincent
8363 Cards = Deck.getObjects()
8364 for i = 1, #Cards do
8365 if Cards[i].nickname == 'Vincent' then
8366 local vCardPos = {vBasePos[1], vBasePos[2], vBasePos[3] - 3.77}
8367 params.index = Cards[i].index
8368 params.position = vCardPos
8369 params.params = {vPos = vCardPos, vRot = {10.0,90.0,0.0}, bLock = false, ThResume = ThCurrent}
8370 Deck.takeObject(params)
8371 coroutine.yield()
8372 break
8373 end
8374 end
8375
8376 --Lin
8377 Cards = Deck.getObjects()
8378 for i = 1, #Cards do
8379 if Cards[i].nickname == 'Lin' then
8380 local vCardPos = {vBasePos[1], vBasePos[2], vBasePos[3] - 0.04}
8381 params.index = Cards[i].index
8382 params.position = vCardPos
8383 params.params = {vPos = vCardPos, vRot = {10.0,90.0,0.0}, bLock = false, ThResume = ThCurrent}
8384 Deck.takeObject(params)
8385 coroutine.yield()
8386 break
8387 end
8388 end
8389
8390 --Hilde
8391 Cards = Deck.getObjects()
8392 for i = 1, #Cards do
8393 if Cards[i].nickname == 'Hilde' then
8394 local vCardPos = {vBasePos[1], vBasePos[2], vBasePos[3] + 3.69}
8395 params.index = Cards[i].index
8396 params.position = vCardPos
8397 params.params = {vPos = vCardPos, vRot = {10.0,90.0,0.0}, bLock = false, ThResume = ThCurrent}
8398 Deck.takeObject(params)
8399 coroutine.yield()
8400 break
8401 end
8402 end
8403 end
8404
8405 if ThNext ~= nil then
8406 coroutine.resume(ThNext)
8407 end
8408 return true
8409 end)
8410 return ThRoutine
8411end
8412
8413--[[ spawn objects for the Fishermen of Catan mini-expansion
8414 Called from onMapLoad when a map is loaded and mini expansion enabled
8415]]--
8416function spawnFishermenExpansion()
8417 local ThRoutine = coroutine.create( function (ThNext)
8418 coroutine.yield(1)
8419
8420 local fZOffset = 0.0
8421 if m_CurScenario.bUseCK == true then
8422 fZOffset = -3.65
8423 end
8424
8425 local FishermenBag = getObjectFromGUID(m_FishermenItems.Bag)
8426 if FishermenBag ~= nil then
8427 local vInitPos = FishermenBag.getPosition()
8428 vInitPos.x = vInitPos.x + 6.0 --make sure it is not captured by the spawn bag right after spawning
8429 local params = {}
8430 params.guid = m_FishermenItems.Item['Board']
8431 params.callback_owner = Global
8432 params.callback = 'callbackGeneral'
8433 params.params = {position = vInitPos, vPos = {-25.75,0.97,-8.75 + fZOffset}, vRot = {0.0,180.0,0.0}, vScale = {0.13,0.13,0.13}, bLock = true}
8434 m_FishermenCardBoard = takeObjectWorkaround(FishermenBag, params)
8435 m_FishermenCardBoard.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'fishermen\') end')
8436
8437 local sDeckGUID = nil
8438 if m_CurScenario.bExtension == true then
8439 sDeckGUID = m_FishermenItems.Item['Deck_ext']
8440 else
8441 sDeckGUID = m_FishermenItems.Item['Deck']
8442 end
8443 local params = {}
8444 params.guid = sDeckGUID
8445 params.callback_owner = Global
8446 params.callback = 'callbackDeck'
8447 params.params = {vPos = {-27.0,2.0,-8.75 + fZOffset}, vRot = {0.0,180.0,180.0}, bShuffle = true}
8448 takeObjectWorkaround(FishermenBag, params)
8449 end
8450
8451 --spawn fishmarket tiles for each player
8452 local params = {}
8453 params.type = 'Custom_Tile'
8454 params.scale = {2.92764568,1.0,2.92764568}
8455
8456 local custom = {}
8457 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379949257/D7E42C4DFAD38761601AB6B1B4313BF51A3ECE14/'
8458 custom.type = 0 --Box
8459 custom.thickness = 0.03
8460
8461 local MarketPos = {
8462 ['Orange'] = {vPos = {-23.186,0.96,-21.727}, vRot = {0.0,180.0,0.0}},
8463 ['Red'] = {vPos = {5.960,0.96,-21.727}, vRot = {0.0,180.0,0.0}},
8464 ['White'] = {vPos = {36.061,0.96,-21.727}, vRot = {0.0,180.0,0.0}},
8465 ['Purple'] = {vPos = {22.763,0.96,21.727}, vRot = {0.0,0.0,0.0}},
8466 ['Blue'] = {vPos = {-6.457,0.96,21.727}, vRot = {0.0,0.0,0.0}},
8467 ['Green'] = {vPos = {-36.047,0.96,21.727}, vRot = {0.0,0.0,0.0}},
8468 }
8469 for sCol, Info in pairs(MarketPos) do
8470 params.position = Info.vPos
8471 params.rotation = Info.vRot
8472
8473 local Tile = spawnObject(params)
8474 Tile.setCustomObject(custom)
8475 Tile.setName('The Fish Market')
8476 Tile.setColorTint({0.643,0.784,0.839})
8477 Tile.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'fishermen\') end')
8478 Tile.setLock(true)
8479 end
8480
8481 if ThNext ~= nil then
8482 coroutine.resume(ThNext)
8483 end
8484 return true
8485 end)
8486 return ThRoutine
8487end
8488
8489--[[ spawn objects for the unoffical Welfare Variant mini-expansion
8490 Called from onMapLoad when a map is loaded and mini expansion enabled
8491]]--
8492function spawnWelfareExpansion()
8493 local ThRoutine = coroutine.create( function (ThNext)
8494 coroutine.yield(1)
8495
8496 --spawn infinite bag to take coins from
8497 local params = {}
8498 params.type = 'Infinite_Bag'
8499 params.position = {-21.0,0.95,5.27}
8500 params.rotation = {0.0,330.0,0.0}
8501 params.scale = {0.8,0.6,0.8}
8502 local SupplyBag = spawnObject(params)
8503 SupplyBag.setName('Welfare Coins Supply')
8504 SupplyBag.setDescription('Each time a player does not receive any resource or fish cards during a turn, they receive a welfare coin instead.')
8505 SupplyBag.setColorTint({190/255,157/255,132/255})
8506 SupplyBag.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'welfare\') end')
8507 SupplyBag.setLock(true)
8508
8509 --put the welfare coin into the infinite bag
8510 local params = {}
8511 params.type = 'Custom_Model'
8512 params.position = {-21.0,2.95,5.27}
8513 params.scale = {0.5,1.0,0.5}
8514 custom = {}
8515 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379951416/DF35BA080143304EC36CC9A25CF45E333A19724E/'
8516 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/177162202731459822/F6E9126E4AFDC0AEED84E746E5107FC3C7B0B399/'
8517 custom.type = 5 --Chip
8518 custom.material = 2 --metal
8519 custom.specular_sharpness = 0.0
8520 custom.specular_intensity = 0.0
8521 local Coin = spawnObject(params)
8522 Coin.setCustomObject(custom)
8523 Coin.setName('Welfare Coin')
8524 Coin.setDescription('You can exchange welfare coins for resource or commodity cards. The exchange rate depends on your current victory points.')
8525 Coin.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'welfare\') end')
8526 SupplyBag.putObject(Coin)
8527
8528 --spawn normal bags for each player
8529 local BagPos = {
8530 ['Orange'] = {-23.186,0.96,-15.0},
8531 ['Red'] = {5.960,0.96,-15.0},
8532 ['White'] = {36.061,0.96,-15.0},
8533 ['Purple'] = {22.763,0.96,15.0},
8534 ['Blue'] = {-6.457,0.96,15.0},
8535 ['Green'] = {-36.047,0.96,15.0},
8536 }
8537
8538 local params = {}
8539 params.type = 'Bag'
8540 params.rotation = {0.0,180.0,0.0}
8541 params.scale = {0.8,0.6,0.8}
8542 for sCol, vPos in pairs(BagPos) do
8543 params.position = vPos
8544 local Bag = spawnObject(params)
8545 Bag.setName('Welfare Coins')
8546 Bag.setDescription(sCol)
8547 Bag.setColorTint(getPlayerColor({sPlayer = sCol}))
8548 Bag.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'welfare\') end')
8549 end
8550
8551 if ThNext ~= nil then
8552 coroutine.resume(ThNext)
8553 end
8554 return true
8555 end)
8556 return ThRoutine
8557end
8558
8559-- spawn the event card deck, shuffle it and place it so that the New Year card is 6th from the bottom
8560function spawnEventCardsExpansion()
8561 local ThRoutine = coroutine.create( function (ThNext)
8562 coroutine.yield()
8563
8564 if m_Die1 ~= nil then destroyObject(m_Die1) end
8565 if m_Die2 ~= nil and isCKenabled() == false then destroyObject(m_Die2) end
8566
8567 m_iEventCardResult = 0
8568
8569 local DeckSpawnBag = getObjectFromGUID(m_DeckEventCards)
8570 if DeckSpawnBag ~= nil then
8571 local ThCurrent = coroutine.running()
8572 local Deck = takeObjectWorkaround(DeckSpawnBag, { rotation = {0.0,180.0,180.0},
8573 callback = 'callbackGeneral', callback_owner = Global,
8574 params = {vPos = {x=-33.5,y=1.2,z=0.0}, vRot = {0.0,180.0,180.0}, ThResume = ThCurrent} })
8575 coroutine.yield() --resumed from deck callback
8576 Deck.takeObject({ position = {x=-33.5,y=1.8,z=0.0}, rotation = {0.0,180.0,180.0},
8577 callback = 'callbackGeneral', callback_owner = Global,
8578 params = {vPos = {x=-33.5,y=1.8,z=0.0}, vRot = {0.0,180.0,180.0}, ThResume = ThCurrent} }) -- pick the new year card
8579 coroutine.yield()
8580 Deck.shuffle()
8581
8582 -- spawn the 31 cards on top first to retain the stack name --TODO: come back here when scripting features for deck manipulation have been extended!
8583 for i = 1, 31 do
8584 if i == 31 then
8585 Deck.takeObject({ position = {x=-33.5,y=(14.6-i*0.4),z=0.0}, rotation = {0.0,180.0,180.0},
8586 callback = 'callbackGeneral', callback_owner = Global,
8587 params = {vPos = {x=-33.5,y=(14.6-i*0.4),z=0.0}, vRot = {0.0,180.0,180.0}, ThResume = ThCurrent} })
8588 coroutine.yield()
8589 else
8590 Deck.takeObject({ position = {x=-33.5,y=(14.6-i*0.4),z=0.0}, rotation = {0.0,180.0,180.0} })
8591 end
8592 end
8593 end
8594
8595 if ThNext ~= nil then
8596 coroutine.resume(ThNext)
8597 end
8598 return true
8599 end)
8600 return ThRoutine
8601end
8602function onObjectPickUp(player_color, picked_up_object)
8603 if m_CurScenario.bUseEventCards then
8604 if picked_up_object.getDescription() == 'Event Card' then
8605 m_bAllowDeal = true --reset the block for the deal cards button
8606 local sName = picked_up_object.getName()
8607 local iResult = tonumber(string.sub(sName,1,2))
8608 if iResult == nil then --no event (new year) -> remove highlighting, no auto deal
8609 m_iEventCardResult = 0
8610 else
8611 m_iEventCardResult = iResult
8612 --check if it is the epidemic event cards for the auto deal to take into consideration
8613 if string.sub(sName, 5, 12) == 'Epidemic' then
8614 m_bEpidemic = true
8615 else
8616 m_bEpidemic = false
8617 end
8618 end
8619
8620 if m_MapScript ~= nil then
8621 m_MapScript.call('highlightChits', { iNum = m_iEventCardResult })
8622 end
8623 end
8624 end
8625end
8626
8627--[[ spawn objects for the Oilsprings mini-expansion
8628 Called from onMapLoad when a map is loaded and mini expansion enabled
8629]]--
8630function spawnOilspringsExpansion()
8631 local ThRoutine = coroutine.create( function (ThNext)
8632 coroutine.yield(1)
8633
8634 local ThCurrent = coroutine.running()
8635
8636 --spawn building costs tiles for each player
8637 local params = {}
8638 params.type = 'Custom_Tile'
8639 params.scale = {1.47602439,1.0,1.47602439}
8640 local custom = {}
8641 custom.image = 'http://cloud-3.steamusercontent.com/ugc/447367065311953591/2DA2B003948C9298B7C5B56E61F9F099958AEEF0/'
8642 custom.type = 0 --Box
8643 custom.thickness = 0.03
8644
8645 local MarketPos = {
8646 ['Orange'] = {vPos = {-21.170314,0.96,-20.2506}, vRot = {0.0,180.0,0.0}},
8647 ['Red'] = {vPos = {7.975686,0.96,-20.2506}, vRot = {0.0,180.0,0.0}},
8648 ['White'] = {vPos = {38.0,0.96,-20.2506}, vRot = {0.0,180.0,0.0}},
8649 ['Purple'] = {vPos = {20.93,0.96,20.2506}, vRot = {0.0,0.0,0.0}},
8650 ['Blue'] = {vPos = {-8.39,0.96,20.2506}, vRot = {0.0,0.0,0.0}},
8651 ['Green'] = {vPos = {-38.0,0.96,20.2506}, vRot = {0.0,0.0,0.0}},
8652 }
8653 for sCol, Info in pairs(MarketPos) do
8654 params.position = Info.vPos
8655 --adjust position when the fishermen expansion is enabled so they don't clip
8656 if m_CurScenario.bUseFishermen then
8657 local fMod = 1.0
8658 if Info.vRot[2] == 180.0 then
8659 fMod = -1.0
8660 end
8661 params.position[1] = params.position[1] + (6.2 * fMod)
8662 if m_CurScenario.bUseCK then
8663 params.position[3] = params.position[3] + (1.2 * fMod)
8664 end
8665 end
8666 params.rotation = Info.vRot
8667
8668 local Tile = spawnObject(params)
8669 Tile.setCustomObject(custom)
8670 Tile.setName('Building Costs - Oil')
8671 Tile.setColorTint({0.396,0.31,0.165})
8672 Tile.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'oilsprings\') end')
8673 Tile.setLock(true)
8674 end
8675
8676 --spawn VP token for environment champion
8677 local vPosToken = {-25.6,1.6,-10.0}
8678 if isCKenabled() then
8679 vPosToken[3] = vPosToken[3] - 2.4
8680 if m_CurScenario.bUseFishermen then vPosToken[1] = vPosToken[1] + 5.0 end
8681 else
8682 if m_CurScenario.bUseFishermen then vPosToken[3] = vPosToken[3] - 2.6 end
8683 end
8684 local params = {}
8685 params.type = 'Custom_Tile'
8686 params.scale = {1.31135309,1.0,1.31135309}
8687 params.position = vPosToken
8688 params.rotation = {0.0,180.0,0.0}
8689 local custom = {}
8690 custom.image = 'http://cloud-3.steamusercontent.com/ugc/447367065312046784/6A1E16C62E626C281D151DB743C2835277FAD339/'
8691 custom.type = 0 --Box
8692 custom.thickness = 0.09
8693
8694 local Token = spawnObject(params)
8695 Token.setCustomObject(custom)
8696 Token.setName('Champion of the Environment')
8697 Token.setDescription('This token is awarded to the player who has sequestered the most oil (at least 3) - 1 Victory Point!')
8698 Token.setColorTint({0.396,0.31,0.165})
8699 Token.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'oilsprings\') end')
8700
8701 --spawn vp tokens for sequestered oil
8702 local params = {}
8703 params.type = 'Custom_Model'
8704 params.rotation = {0.0,180.0,0.0}
8705 local custom = {}
8706 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379943471/32300EFCC842A3D85EFCEDE3E61E9639BC1F683D/'
8707 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379946921/965DEA504C0F686ECA7C1BE850FE8CE9DA03BD5E/'
8708 custom.type = 0 --generic
8709 custom.material = 3 --cardboard
8710 local vPosVP = {vPosToken[1]+3.6, 1.0, vPosToken[3]+1.0}
8711 for i = 1, 7 do
8712 vPosVP[2] = vPosVP[2] + 0.1
8713 params.position = vPosVP
8714 local Token = spawnObject(params)
8715 Token.setCustomObject(custom)
8716 Token.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'oilsprings\') end') --keep the variable on rewind
8717 Token.setColorTint({0.733,0.259,0.184})
8718 Token.setName('1 Victory Point')
8719 Token.setDescription('For each 3 oil a player has sequestered they receive 1 victory point token.')
8720 end
8721
8722 --spawn the metropolis tokens
8723 local iNum = 4
8724 if m_CurScenario.bExtension then
8725 iNum = 6
8726 end
8727 local vPosMetr = {vPosToken[1]+3.6, 1.0, vPosToken[3]-1.0}
8728 local params = {}
8729 params.type = 'Custom_Tile'
8730 params.scale = {0.433980554,1.0,0.433980554}
8731 params.rotation = {0.0,180.0,0.0}
8732 local custom = {}
8733 custom.image = 'http://cloud-3.steamusercontent.com/ugc/447367065314774775/4C7ED5485D4BC2F799144BB4756567173CB1B00D/'
8734 custom.type = 0 --Box
8735 custom.thickness = 0.09
8736 for i = 1, iNum do
8737 vPosMetr[2] = vPosMetr[2] + 0.3
8738 params.position = vPosMetr
8739 local Token = spawnObject(params)
8740 Token.setCustomObject(custom)
8741 Token.setColorTint({0.1686,0.1333,0.0980})
8742 Token.setName('Oilsprings Metropolis')
8743 --Handle scoring for the metropolis token (similar to C&K metropolis gate)
8744 Token.setLuaScript([[ m_bLoaded = false
8745 function onSave()
8746 local ToSave = { sScorer = self.getVar('sScorer') }
8747 return JSON.encode(ToSave)
8748 end
8749 function onLoad(save_state)
8750 self.setVar('sObjType', 'oilsprings')
8751 local saved_data = JSON.decode(save_state)
8752 if saved_data ~= nil then
8753 self.setVar('sScorer', saved_data.sScorer)
8754 end
8755 m_bLoaded = true
8756 end
8757 function onCollisionEnter(collision_info)
8758 if self == nil or m_bLoaded == false then
8759 return
8760 end
8761 if self.getVar('sScorer') ~= nil then
8762 return
8763 end
8764 if collision_info == nil then return end
8765 local ColObj = collision_info.collision_object
8766 if ColObj.getName() == 'City' or ColObj.getName() == 'Wall' then
8767 local MapScript = getObjectFromGUID('42fd8e')
8768 if MapScript ~= nil then
8769 local bScore = false
8770 local ChkObj = MapScript.getObjects()
8771 for i, Obj in ipairs(ChkObj) do
8772 if Obj == ColObj then
8773 bScore = true
8774 break
8775 end
8776 end
8777 --award the points
8778 if bScore == true then
8779 if true == MapScript.call('addScore', {iScore = 1, sPlayer = ColObj.getDescription()}) then
8780 self.setVar('sScorer', ColObj.getDescription())
8781 end
8782 end
8783 end
8784 end
8785 end
8786 function onPickUp(player_color) --remove the score from the previous scorer when the metropolis is picked up
8787 local sOldScorer = self.getVar('sScorer')
8788 if sOldScorer ~= nil then
8789 self.setVar('sScorer', nil)
8790 local MapScript = getObjectFromGUID('42fd8e')
8791 if MapScript ~= nil then
8792 MapScript.call('subtractScore',{iScore = 1, sPlayer = sOldScorer})
8793 end
8794 end
8795 end
8796 ]])
8797 end
8798
8799 --spawn oil tokens
8800 iNum = 15
8801 if m_CurScenario.bExtension then
8802 iNum = 21
8803 end
8804 local vPosOil = {-22.0, 1.0, 9.77}
8805 local params = {}
8806 params.type = 'Custom_Tile'
8807 params.scale = {0.5,1.0,0.5}
8808 params.rotation = {0.0,180.0,0.0}
8809 local custom = {}
8810 custom.image = 'http://cloud-3.steamusercontent.com/ugc/447367065316098092/00DD562112DCC6C988257222AD3BDD1766A94D32/'
8811 custom.image_bottom = 'http://cloud-3.steamusercontent.com/ugc/447367065316098574/22C436B3023D0DA28B97E58C1877E361A56B7C17/'
8812 custom.type = 0 --Box
8813 custom.thickness = 0.08
8814 custom.stackable = true
8815 for i = 1, iNum do
8816 vPosOil[2] = vPosOil[2] + 0.1
8817 params.position = vPosOil
8818 local Token = spawnObject(params)
8819 Token.setCustomObject(custom)
8820 Token.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'oilsprings\') end') --keep the variable on rewind
8821 Token.setColorTint({0.749,0.5765,0.298})
8822 Token.setName('Oil Token')
8823 if i == 10 then
8824 vPosOil[2] = 1.0
8825 vPosOil[3] = vPosOil[3] - 1.3
8826 end
8827 end
8828
8829 --spawn the disaster board + oil usage indicator
8830 local BagBoard = getObjectFromGUID(m_OilspDisasterBoard)
8831 local vPosBoard = {30.0,1.0,-6.0}
8832 if m_MapScript ~= nil then
8833 vPosBoard[1] = (m_MapScript.getScale().x / 2) + m_MapScript.getPosition().x + 4.7
8834 end
8835 local TrackerBoard = takeObjectWorkaround(BagBoard, {position = vPosBoard, rotation = {0.0,180.0,0.0},
8836 callback = 'callbackGeneral', callback_owner = Global,
8837 params = {vPos = vPosBoard, vRot = {0.0,180.0,0.0},bLock = true,ThResume = ThCurrent} })
8838 TrackerBoard.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'oilsprings\') end')
8839 coroutine.yield(1)
8840 --spawn the indicator chip when the board is initialized
8841 vPosBoard[1] = vPosBoard[1] - 2.2949516
8842 vPosBoard[2] = 1.3
8843 vPosBoard[3] = vPosBoard[3] - 1.25559335
8844 local params = {}
8845 params.type = 'Custom_Tile'
8846 params.scale = {0.291973174,1.0,0.291973174}
8847 params.position = vPosBoard
8848 params.rotation = {0.0,180.0,0.0}
8849 local custom = {}
8850 custom.image = 'http://cloud-3.steamusercontent.com/ugc/447367065314718788/F8DFFEA39131486BC0AE09F946B710E4CC62E03D/'
8851 custom.type = 0 --Box
8852 custom.thickness = 0.03
8853 local Token = spawnObject(params)
8854 Token.setCustomObject(custom)
8855 Token.setName('Disaster Track Marker')
8856 Token.setColorTint({0.9961,0.8824,0.6627})
8857 Token.setLuaScript('function onLoad(saved_state) self.setVar(\'sObjType\', \'oilsprings\') end')
8858
8859 if ThNext ~= nil then
8860 coroutine.resume(ThNext)
8861 end
8862 return true
8863 end)
8864 return ThRoutine
8865end
8866
8867--[[Spawn additional items for the Rivers of Catan expansion
8868 the rivers hexes are defined in the scenario definition and the bridges are spawned from setupPlayerItems ]]--
8869function spawnRiversExpansion()
8870 local ThRoutine = coroutine.create( function (ThNext)
8871 coroutine.yield(1)
8872
8873 --determine spawn location from the mapscript dimensions (map size)
8874 local vPos = {30.0,1.0,-9.0}
8875 if m_MapScript ~= nil then
8876 vPos[1] = (m_MapScript.getScale().x / 2) + m_MapScript.getPosition().x
8877 end
8878
8879 --spawn wealthiest settler token
8880 local custom = {}
8881 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379953587/A738B7C427B1903BFE09D9FCD69C233FAD9D539B/'
8882 custom.type = 0 --Box
8883 custom.thickness = 0.04
8884
8885 local params = {}
8886 params.type = 'Custom_Tile'
8887 params.scale = {0.5,1.0,0.5}
8888
8889 params.position = vPos
8890 params.rotation = {0.0,180.0,0.0}
8891 local Wealthiest = spawnObject(params)
8892 Wealthiest.setCustomObject(custom)
8893 Wealthiest.setName('Wealthiest Settler')
8894 Wealthiest.setDescription('The player who has solely the most gold gets this token for +1 Victory Point.')
8895 Wealthiest.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'rivers\') end')
8896 Wealthiest.setColorTint({0.671, 0.141, 0.169})
8897
8898 --spawn poor settler tokens
8899 vPos[3] = vPos[3] - 1.5
8900 for i=1, 6 do
8901 custom.image = 'http://cloud-3.steamusercontent.com/ugc/155773601379955708/5897F1D78836BD89645EE6090D50F18F07D58A72/'
8902 local Token = spawnObject(params)
8903 Token.setCustomObject(custom)
8904 Token.setName('Poor Settler')
8905 Token.setDescription('The player(s) who has the least amount of gold gets this token for -2 Victory Points.')
8906 Token.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'rivers\') end')
8907 Token.setColorTint({0.839, 0.706, 0.596})
8908 vPos[2] = vPos[2] + 0.4
8909 end
8910
8911 --spawn gold coins
8912 params = {}
8913 params.type = 'Custom_Model'
8914 params.position = {vPos[1]+2.2, 1.0, vPos[3]}
8915 params.rotation = {0.0,180.0,0.0}
8916 params.scale = {0.5,1.0,0.5}
8917
8918 custom = {}
8919 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379951416/DF35BA080143304EC36CC9A25CF45E333A19724E/'
8920 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379959874/DBC1827EF70AE3FC788D21E4C979A9A392F9B5FA/'
8921 custom.type = 5 --Chip
8922 custom.material = 2 --metal
8923 custom.specular_sharpness = 1.0
8924 custom.specular_intensity = 0.5
8925
8926 for i=1, 100 do
8927 local Chip = spawnObject(params)
8928 Chip.setCustomObject(custom)
8929 Chip.setName('Gold')
8930 Chip.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'rivers\') end') --keep the variable on rewind
8931 Chip.setColorTint({0.984,0.867,0.341})
8932 params.position[2] = params.position[2] + 0.13
8933 if i % 25 == 0 then
8934 params.position[1] = params.position[1] + 1.8
8935 params.position[2] = 1.0
8936 if i == 50 then
8937 params.position[1] = params.position[1] - 3.6
8938 params.position[3] = params.position[3] + 1.8
8939 end
8940 end
8941 end
8942
8943 if ThNext ~= nil then
8944 coroutine.resume(ThNext)
8945 end
8946 return true
8947 end)
8948 return ThRoutine
8949end
8950--[[Spawn additional items for the Caravans expansion
8951 Spawn camels figurines next to the map ]]--
8952function spawnCaravansExpansion()
8953 local ThRoutine = coroutine.create( function (ThNext)
8954 coroutine.yield(1)
8955
8956 --determine spawn location from the mapscript dimensions (map size)
8957 local vPos = {30.0,1.5,-9.0}
8958 if m_MapScript ~= nil then
8959 vPos[1] = (m_MapScript.getScale().x / 2) + m_MapScript.getPosition().x
8960 vPos[1] = vPos[1] - 1.0
8961 end
8962
8963 --spawn camels
8964 params = {}
8965 params.type = 'Custom_Model'
8966 params.position = {vPos[1], vPos[2], vPos[3]}
8967 params.rotation = {0.0,90.0,0.0}
8968 custom = {}
8969 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601380258198/1FD357DB6672A2502EC30E2A1D8FE3432B076570/'
8970 custom.diffuse = ''
8971 custom.type = 1 --figurine
8972 custom.material = 1 --wood
8973
8974 local iNumCamels = 22
8975 if m_CurScenario.bExtension then --5-6 players
8976 iNumCamels = 33
8977 end
8978 for i = 1, iNumCamels do
8979 local Camel = spawnObject(params)
8980 Camel.setCustomObject(custom)
8981 Camel.setName('Camel')
8982 --Beware some inception incoming - I apologize to anyone trying to figure out what is happening here ;-)
8983 --In prose: make the camel generate a scripting zone around itself when it is placed on the map. This scripting zone will monitor incoming/leaving objects to make sure any buildings get score added/deducted when being placed/removed between 2 camels
8984 Camel.setLuaScript( [[m_Zone = nil
8985 function onSave()
8986 local sGUID = nil
8987 if m_Zone ~= nil then sGUID = m_Zone.getGUID() end
8988 return JSON.encode({sZoneGUID = sGUID})
8989 end
8990 function onLoad(save_state)
8991 self.setVar('sObjType', 'caravans')
8992 local saved_data = JSON.decode(save_state)
8993 if saved_data ~= nil then if saved_data.sZoneGUID ~= nil then m_Zone = getObjectFromGUID(saved_data.sZoneGUID) end end
8994 end
8995 --Destroy the scripting zone for scoring when the camel is picked up
8996 function onPickUp(player_color)
8997 if m_Zone ~= nil then destroyObject(m_Zone) end
8998 end
8999 --When a camel is placed, spawn a scripting zone to track settlements/cities entering leaving it to apply score
9000 function onCollisionEnter(collision_info)
9001 if m_Zone ~= nil or collision_info == nil then
9002 return
9003 end
9004 local sObjType = collision_info.collision_object.getVar('sObjType')
9005 if sObjType == 'numtoken' or sObjType == 'hex' or collision_info.collision_object.getName() == 'Road' then
9006 --Spawn scripting zones to manage scoring for settlements / cities entering leaving the position between 2 camels
9007 m_Zone = spawnObject({ type = 'ScriptingTrigger', position = self.getPosition(), rotation = {0.0,self.getRotation().y,0.0}, scale = {3.5,1.0,2.6} })
9008 m_Zone.setName('camel_zone')
9009 m_Zone.setLuaScript([=[ function onLoad(save_state) self.setVar('sObjType', 'caravans') end
9010 function checkScoring(p)
9011 local Camels = {}
9012 local ConnectedB = {}
9013 local UnConnectedB = {}
9014 local Objects = self.getObjects()
9015 if p.enter_object ~= nil then --add this object because it is not returned from getObjects when entering
9016 table.insert(Objects, p.enter_object)
9017 end
9018 if p.leave_object ~= nil then
9019 --it is a connected building that is leaving -> remove score and connection
9020 local iPosAppend = string.find(p.leave_object.getName(), '(Connected to Caravan)')
9021 if iPosAppend ~= nil then
9022 iPosAppend = iPosAppend - 3
9023 p.leave_object.setName(string.sub(p.leave_object.getName(), 1, iPosAppend)) --remove "(Connected to Caravan)" from the name
9024 local MapScript = getObjectFromGUID('42fd8e')
9025 if MapScript ~= nil then
9026 MapScript.call('subtractScore', {iScore = 1, sPlayer = p.leave_object.getDescription()})
9027 end
9028 return
9029 elseif p.leave_object.getName() == 'Camel' then --remove leaving object if it is a camel because it is still in getObjects when the leave event is triggered
9030 for j = 1, #Objects do
9031 if Objects[j] == p.leave_object then
9032 table.remove(Objects, j)
9033 break
9034 end
9035 end
9036 end
9037 end
9038
9039 --get other camels in range
9040 for i, Obj in pairs(Objects) do
9041 if Obj.getName() == 'Camel' then
9042 table.insert(Camels, Obj)
9043 elseif Obj.getName() == 'Settlement' or Obj.getName() == 'City' then
9044 table.insert(UnConnectedB, Obj)
9045 elseif Obj.getName() == 'Settlement (Connected to Caravan)' or Obj.getName() == 'City (Connected to Caravan)' then
9046 table.insert(ConnectedB, Obj)
9047 end
9048 end
9049
9050 --check normal settlements / cities if they should be connected now
9051 if #UnConnectedB > 0 then
9052 for i, Obj in pairs(UnConnectedB) do
9053 local fX = Obj.getPosition().x
9054 local fZ = Obj.getPosition().z
9055 local iConnections = 0
9056 for j = 1, #Camels do
9057 local fDistance = math.abs(Camels[j].getPosition().x - fX) + math.abs(Camels[j].getPosition().z - fZ)
9058 if fDistance < 2.9 then
9059 iConnections = iConnections + 1
9060 end
9061 end
9062 if iConnections >= 2 then
9063 Obj.setName(Obj.getName() .. ' (Connected to Caravan)')
9064 local MapScript = getObjectFromGUID('42fd8e')
9065 if MapScript ~= nil then
9066 MapScript.call('addScore', {iScore = 1, sPlayer = Obj.getDescription()})
9067 end
9068 end
9069 end
9070 end
9071 --check buildings connected to caravans if they are still connected to 2 camels
9072 if #ConnectedB > 0 then
9073 for i, Obj in pairs(ConnectedB) do
9074 local iConnections = 0
9075 if Obj.held_by_color == nil then --a building held by a player (it is being picked up) always gets disconnected, so don't bother counting camels
9076 local fX = Obj.getPosition().x
9077 local fZ = Obj.getPosition().z
9078 for j = 1, #Camels do
9079 local fDistance = math.abs(Camels[j].getPosition().x - fX) + math.abs(Camels[j].getPosition().z - fZ)
9080 if fDistance < 2.9 then
9081 iConnections = iConnections + 1
9082 end
9083 end
9084 end
9085 if iConnections < 2 then
9086 local iPosAppend = string.find(Obj.getName(), '(Connected to Caravan)')
9087 if iPosAppend ~= nil then
9088 iPosAppend = iPosAppend - 3
9089 Obj.setName(string.sub(Obj.getName(), 1, iPosAppend)) --remove "(Connected to Caravan)" from the name
9090 local MapScript = getObjectFromGUID('42fd8e')
9091 if MapScript ~= nil then
9092 MapScript.call('subtractScore', {iScore = 1, sPlayer = Obj.getDescription()})
9093 end
9094 end
9095 end
9096 end
9097 end
9098 end
9099 function onObjectEnterScriptingZone(zone, enter_object)
9100 if zone == self and enter_object ~= nil then
9101 if enter_object.getName() == 'Camel' or enter_object.getName() == 'Settlement' or enter_object.getName() == 'City' then
9102 checkScoring({enter_object = enter_object})
9103 end
9104 end
9105 end
9106 function onObjectLeaveScriptingZone(zone, leave_object)
9107 if zone == self and leave_object ~= nil then
9108 if leave_object.getName() == 'Camel' or string.find(leave_object.getName(),'(Connected to Caravan)') ~= nil then
9109 checkScoring({leave_object = leave_object})
9110 end
9111 end
9112 end ]=]
9113 )
9114 end
9115 end]])
9116 Camel.setColorTint({0.576,0.357,0.263})
9117
9118 params.position[1] = params.position[1] + 0.4
9119 if i % 11 == 0 then
9120 params.position[1] = vPos[1]
9121 params.position[3] = params.position[3] - 1.0
9122 end
9123 end
9124
9125 if ThNext ~= nil then
9126 coroutine.resume(ThNext)
9127 end
9128 return true
9129 end)
9130 return ThRoutine
9131end
9132
9133--[[Spawn additional items for the Barbarian Attack campaign
9134 Spawn barbarian figurines & gold next to the map + special die on the castle hex
9135 The knight figures for players are spawned from setupPlayerItems
9136]]--
9137function spawnBarbarianAttackExpansion()
9138 local ThRoutine = coroutine.create( function (ThNext)
9139 coroutine.yield(1)
9140
9141 --determine spawn location from the mapscript dimensions (map size)
9142 local vPos = {30.0,1.5,-9.0}
9143 if m_MapScript ~= nil then
9144 vPos[1] = (m_MapScript.getScale().x / 2) + m_MapScript.getPosition().x
9145 end
9146
9147 --Spawn a bag for the barbarians
9148 params = {}
9149 params.type = 'Bag'
9150 params.position = vPos
9151 params.rotation = {0.0,180.0,0.0}
9152 params.scale = {0.8,0.6,0.8}
9153 if isCKenabled() then
9154 params.type = 'Infinite_Bag' --unlimited amounts of barbarians when played with C&K
9155 end
9156
9157 local BarbBag = spawnObject(params)
9158 BarbBag.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'battack\') end') --keep the variable on rewind
9159 BarbBag.setName('Barbarians')
9160 BarbBag.setColorTint({0.745,0.608,0.4})
9161
9162 --spawn the barbarian figures over the bag
9163 local iNum = 36
9164 if m_CurScenario.bExtension then
9165 iNum = 48
9166 end
9167 for i = 1, iNum do
9168 local Barbarian = spawnTABBarbarian({vPos = {vPos[1],80.0, vPos[3]}, vRot = {320.0,0.0,0.0}})
9169 Barbarian.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'battack\') end')
9170 Barbarian.use_snap_points = false
9171 BarbBag.putObject(Barbarian)
9172
9173 if isCKenabled() then --infinite bag => spawn just one
9174 break
9175 end
9176 end
9177
9178 --spawn gold coins
9179 params = {}
9180 params.type = 'Custom_Model'
9181 params.position = {vPos[1]+2.2, 1.0, vPos[3]}
9182 params.rotation = {0.0,180.0,0.0}
9183 params.scale = {0.5,1.0,0.5}
9184 custom = {}
9185 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379951416/DF35BA080143304EC36CC9A25CF45E333A19724E/'
9186 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379959874/DBC1827EF70AE3FC788D21E4C979A9A392F9B5FA/'
9187 custom.type = 5 --Chip
9188 custom.material = 2 --metal
9189 custom.specular_sharpness = 1.0
9190 custom.specular_intensity = 0.5
9191
9192 for i=1, 100 do
9193 local Chip = spawnObject(params)
9194 Chip.setCustomObject(custom)
9195 Chip.setName('Gold')
9196 Chip.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'battack\') end') --keep the variable on rewind
9197 Chip.setColorTint({0.984,0.867,0.341})
9198 params.position[2] = params.position[2] + 0.13
9199 if i % 25 == 0 then
9200 params.position[1] = params.position[1] + 1.8
9201 params.position[2] = 1.0
9202 if i == 50 then
9203 params.position[1] = params.position[1] - 3.6
9204 params.position[3] = params.position[3] + 1.8
9205 end
9206 end
9207 end
9208
9209 if ThNext ~= nil then
9210 coroutine.resume(ThNext)
9211 end
9212 return true
9213 end)
9214 return ThRoutine
9215end
9216
9217--[[Spawn additional items for the Traders & Barbarians campaign
9218 Spawn the player wagons and the baggage train cards next to the player areas
9219 Spawn gold next to the map
9220 The development cards deck for this expansion is spawned from setupDecks
9221 Barbarian figures are spawed from the map definition
9222]]--
9223function spawnTradersAndBarbariansExpansion()
9224 local ThRoutine = coroutine.create( function (ThNext)
9225 coroutine.yield(1)
9226
9227 --determine spawn location from the mapscript dimensions (map size)
9228 local vPos = {30.0,1.5,-9.0}
9229 if m_MapScript ~= nil then
9230 vPos[1] = (m_MapScript.getScale().x / 2) + m_MapScript.getPosition().x
9231 end
9232
9233 --spawn gold coins
9234 local params = {}
9235 params.type = 'Custom_Model'
9236 params.position = {vPos[1]+2.2, 1.0, vPos[3]}
9237 params.rotation = {0.0,180.0,0.0}
9238 params.scale = {0.5,1.0,0.5}
9239 custom = {}
9240 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379951416/DF35BA080143304EC36CC9A25CF45E333A19724E/'
9241 custom.diffuse = 'http://cloud-3.steamusercontent.com/ugc/155773601379959874/DBC1827EF70AE3FC788D21E4C979A9A392F9B5FA/'
9242 custom.type = 5 --Chip
9243 custom.material = 2 --metal
9244 custom.specular_sharpness = 1.0
9245 custom.specular_intensity = 0.5
9246
9247 for i=1, 70 do
9248 local Chip = spawnObject(params)
9249 Chip.setCustomObject(custom)
9250 Chip.setName('Gold')
9251 Chip.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'traders\') end') --keep the variable on rewind
9252 Chip.setColorTint({0.984,0.867,0.341})
9253 params.position[2] = params.position[2] + 0.13
9254 if i % 25 == 0 then
9255 params.position[1] = params.position[1] + 1.8
9256 params.position[2] = 1.0
9257 if i == 50 then
9258 params.position[1] = params.position[1] - 3.6
9259 params.position[3] = params.position[3] + 1.8
9260 end
9261 end
9262 end
9263
9264 local PlayerDef = { ['Green'] = {vPosWagon = {-29.6 + 3.0, 1.2, 15.0}, DeckBag = '8d01db', vPosDeck = {-29.6, 1.2, 15.0}, vPosGold = {-37.4,1.2,19.0}},
9265 ['Blue'] = {vPosWagon = {-0.1 - 3.0, 1.2, 15.0}, DeckBag = '5a1d43', vPosDeck = {-0.1, 1.2, 15.0}, vPosGold = {-7.8,1.2,19.0}},
9266 ['Purple'] = {vPosWagon = {29.4 - 3.0, 1.2, 15.0}, DeckBag = '3beb06', vPosDeck = {29.4, 1.2, 15.0}, vPosGold = {21.4,1.2,19.0}},
9267 ['White'] = {vPosWagon = {29.4 - 3.0, 1.2, -15.0}, DeckBag = '6f9264', vPosDeck = {29.4, 1.2, -15.0}, vPosGold = {37.4,1.2,-19.0}},
9268 ['Red'] = {vPosWagon = {-0.15 - 3.0, 1.2, -15.0}, DeckBag = 'b787f3', vPosDeck = {-0.15, 1.2, -15.0}, vPosGold = {7.4,1.2,-19.0}},
9269 ['Orange'] = {vPosWagon = {-29.6 + 3.0, 1.2, -15.0}, DeckBag = '26b465', vPosDeck = {-29.6, 1.2, -15.0}, vPosGold = {-21.9,1.2,-19.0}},
9270 }
9271
9272 if m_CurScenario.bUseFrenemies then --move greens cards to the side a bit
9273 PlayerDef['Green'].vPosWagon[1] = PlayerDef['Green'].vPosWagon[1] - 2.5
9274 PlayerDef['Green'].vPosDeck[1] = PlayerDef['Green'].vPosDeck[1] - 2.5
9275 end
9276 if isCKenabled() then --move oranges cards to the side a bit
9277 PlayerDef['Orange'].vPosWagon[1] = PlayerDef['Orange'].vPosWagon[1] - 2.5
9278 PlayerDef['Orange'].vPosDeck[1] = PlayerDef['Orange'].vPosDeck[1] - 2.5
9279 end
9280
9281 --spawn 5 initial gold coins for each player
9282 for sCol, Info in pairs(PlayerDef) do
9283 params.position = Info.vPosGold
9284 for i=1, 5 do
9285 local Chip = spawnObject(params)
9286 Chip.setCustomObject(custom)
9287 Chip.setName('Gold')
9288 Chip.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'traders\') end') --keep the variable on rewind
9289 Chip.setColorTint({0.984,0.867,0.341})
9290 params.position[2] = params.position[2] + 0.13
9291 end
9292 end
9293
9294 --spawn wagon figure for each player & the baggage train decks
9295 params = {}
9296 params.type = 'Custom_Model'
9297 params.rotation = {0.0,90.0,0.0}
9298 custom = {}
9299 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379962685/10B747E09EEDE274FDB3F79B87288E78B8ED33CF/'
9300 custom.diffuse = ''
9301 custom.type = 1 --figurine
9302 custom.material = 3 --wood
9303
9304 local TaBBag = getTaBBag()
9305 for sCol, Info in pairs(PlayerDef) do
9306 --spawn the wagon
9307 params.position = Info.vPosWagon
9308 local Wagon = spawnObject(params)
9309 Wagon.setCustomObject(custom)
9310 Wagon.setName('Baggage Train')
9311 Wagon.setDescription(sCol)
9312 Wagon.setLuaScript('function onLoad() self.setVar(\'sObjType\', \'traders\') end') --keep the variable on rewind
9313 Wagon.setColorTint(m_PlayerColors[sCol])
9314 Wagon.use_snap_points = false
9315
9316 --spawn the baggage train card deck
9317 if TaBBag ~= nil then
9318 local vPosDeck = {Info.vPosDeck[1]-1.2,Info.vPosDeck[2],Info.vPosDeck[3]}
9319 local vPosCard = {Info.vPosDeck[1]+1.2,Info.vPosDeck[2],Info.vPosDeck[3]}
9320 local vRot = {0.0,180.0,180.0}
9321 if sCol == 'Green' or sCol == 'Blue' or sCol == 'Purple' then
9322 vPosDeck = {Info.vPosDeck[1]+1.2,Info.vPosDeck[2],Info.vPosDeck[3]}
9323 vPosCard = {Info.vPosDeck[1]-1.2,Info.vPosDeck[2],Info.vPosDeck[3]}
9324 vRot = {0.0,0.0,180.0}
9325 end
9326 takeObjectWorkaround(TaBBag, { guid = TaBBag.call('getBaggageDeckGUID', {sCol = sCol}),
9327 position = vPosDeck,
9328 rotation = vRot,
9329 callback = 'callbackDrawCard',
9330 callback_owner = Global,
9331 params = {vCardPos = vPosCard, vRot = {vRot[1],vRot[2],0.0}}
9332 } )
9333 end
9334 end
9335
9336 if ThNext ~= nil then
9337 coroutine.resume(ThNext)
9338 end
9339 return true
9340 end)
9341 return ThRoutine
9342end
9343function callbackDrawCard(Deck, p)
9344 Deck.takeObject({index = 0, position = p.vCardPos, rotation = p.vRot})
9345end
9346
9347function isRiversCompatible(p)
9348 local sID = p.sScenarioID
9349 if sID == 'B' or sID == 'B2' or sID == 'S1' or sID == 'S4' or sID == 'S5' or sID == 'T1' or sID == 'T3' then
9350 return true
9351 end
9352 return false
9353end
9354function isCaravansCompatible(p)
9355 local sID = p.sScenarioID
9356 if sID == 'B' or sID == 'B2' or sID == 'S1' or sID == 'S4' or sID == 'S5' or sID == 'T1' or sID == 'T3' then
9357 return true
9358 end
9359 return false
9360end
9361function isBAttackCompatible(p)
9362 local sID = p.sScenarioID
9363 if sID == 'B' or sID == 'B2' or sID == 'S1' or sID == 'T1' or sID == 'T3' then
9364 return true
9365 end
9366 return false
9367end
9368function isTradersCompatible(p)
9369 local sID = p.sScenarioID
9370 if sID == 'B' or sID == 'B2' then
9371 return true
9372 end
9373 return false
9374end
9375
9376--[[ onMapLoad
9377 Called from the control panel, executes general tasks to be done when loading a new map:
9378 - Clean up old object
9379 - Setting map script zone size
9380 - Setting scenario related stuff
9381 - Manage Largest Army and Longest Road cards
9382 - Spawn correct dice and put them in the starting position
9383 - Put the robber back into the starting position
9384 - etc.
9385 Parameters:
9386 Scenario - table:
9387 sID - identifies the scenario
9388 sInfoText - info text to show in the bottom right corner
9389 iVPTarget - target VP for the current scenario
9390 iVPadjCK - additional VP needed to win when playing with C&K
9391 fCatanChitScoreMap - Score to give to a player for a Catan Chit on the map
9392 fCatanChitScoreHand - Score to give to a player for a Catan Chit in their player area
9393 vMapScale - size of the map script zone (so it covers the entire playing area)
9394]]--
9395function onMapLoad(p)
9396 --check if it is an Explorers & Pirates scenario (ID starts with C)
9397 local bIsEaP = (string.sub(p.Scenario.sID,1,1) == 'C')
9398
9399 m_bMapLoaded = false
9400
9401 --remove the quick start overlay
9402 removeOverlay()
9403
9404 --reset dice monitoring queue
9405 for i = 1, #m_CheckDice do
9406 if m_CheckDice[i] ~= nil then
9407 m_CheckDice[i].call('setQueueStatus', {bActive = false})
9408 end
9409 end
9410 m_CheckDice = {}
9411
9412 --reset dice roll statistics
9413 if m_DiceLogger ~= nil then
9414 m_DiceLogger.call('resetStats')
9415 end
9416
9417 m_UnlockOnLoad = {} --list of objects to be unlocked once all hex tiles are loaded
9418 m_bEpidemic = false --for deal function with the EventCard expansion
9419
9420 --[[
9421 Activate the loaded scenario info and rules
9422 --]]
9423 local sScenarioID = p.Scenario.sID
9424 m_CurScenario = {}
9425 m_CurScenario.sID = sScenarioID
9426 m_CurScenario.bUseCK = m_bExpCK
9427 m_CurScenario.bUseFishermen = m_bEnableFishermen
9428 m_CurScenario.bUseEventCards = m_bEnableEventCards
9429 m_CurScenario.bUseWelfare = m_bEnableWelfare
9430 m_CurScenario.bUseFrenemies = (m_iFrenemiesPlayers > 0)
9431 if m_bEnableRivers then
9432 if getTaBBag() == nil then
9433 m_bEnableRivers = false
9434 else
9435 m_CurScenario.bUseRivers = isRiversCompatible({sScenarioID = sScenarioID}) --Only enable the rivers expansion if it works for this scenario
9436 if m_CurScenario.bUseRivers == false then printToAll('[i]The Rivers of Catan[/i] is not compatible with this scenario and will not be loaded.', {1.0,0.8627,0.05098}) end
9437 end
9438 else
9439 m_CurScenario.bUseRivers = false
9440 end
9441 if m_bEnableCaravans then
9442 if getTaBBag() == nil then
9443 m_bEnableCaravans = false
9444 else
9445 m_CurScenario.bUseCaravans = isCaravansCompatible({sScenarioID = sScenarioID}) --Only enable the caravans expansion if it works for this scenario
9446 if m_CurScenario.bUseCaravans == false then printToAll('[i]The Caravans[/i] is not compatible with this scenario and will not be loaded.', {1.0,0.8627,0.05098}) end
9447 end
9448 else
9449 m_CurScenario.bUseCaravans = false
9450 end
9451 if m_bEnableBarbarianAttack then
9452 if getTaBBag() == nil then
9453 m_bEnableBarbarianAttack = false
9454 else
9455 m_bColorDieSpawned = false
9456 m_CurScenario.bUseBAttack = isBAttackCompatible({sScenarioID = sScenarioID}) --Only enable the barbarian attack expansion if it works for this scenario
9457 if m_CurScenario.bUseBAttack == false then printToAll('[i]Barbarian Attack[/i] is not compatible with this scenario and will not be loaded.', {1.0,0.8627,0.05098}) end
9458 end
9459 else
9460 m_CurScenario.bUseBAttack = false
9461 end
9462 if m_bEnableTraders then
9463 if getTaBBag() == nil then
9464 m_bEnableTraders = false
9465 else
9466 m_CurScenario.bUseTraders = isTradersCompatible({sScenarioID = sScenarioID}) --Only enable the barbarian attack expansion if it works for this scenario
9467 if m_CurScenario.bUseTraders == false then printToAll('[i]Traders & Barbarians[/i] is not compatible with this scenario and will not be loaded.', {1.0,0.8627,0.05098}) end
9468 end
9469 else
9470 m_CurScenario.bUseTraders = false
9471 end
9472 if m_bEnableOilsprings then
9473 if sScenarioID ~= 'B' then --the Oilsprings only makes sense with the basic game
9474 m_bEnableOilsprings = false
9475 printToAll('[i]Oilsprings[/i] is not compatible with this scenario and will not be loaded.', {1.0,0.8627,0.05098})
9476 else
9477 --Oilsprings cannot be combined with any T&B scenario
9478 if m_CurScenario.bUseRivers or m_CurScenario.bUseCaravans or m_CurScenario.bUseBAttack or m_CurScenario.bUseTraders then
9479 m_bEnableOilsprings = false
9480 printToAll('[i]Oilsprings[/i] cannot be combined with [i]Traders & Barbarians[/i] campaigns and will not be loaded.', {1.0,0.8627,0.05098})
9481 end
9482 end
9483 end
9484
9485 m_CurScenario.bExtension = false --5-6 Player layout
9486 if p.bExtension == true then
9487 m_CurScenario.bExtension = true
9488 end
9489
9490 --[[
9491 Calculate target VP shown in the bottom right info box
9492 --]]
9493 local iTargetVP = p.Scenario.iVPTarget
9494 --increase the target VP display shown in the infobox when expansions are enabled
9495 if m_bExpCK then iTargetVP = iTargetVP + p.Scenario.iVPadjCK end
9496 if m_iFrenemiesPlayers > 0 then iTargetVP = iTargetVP + 1 end
9497 if m_bEnableHarborMaster then iTargetVP = iTargetVP + 1 end
9498 if m_bEnableOilsprings then iTargetVP = iTargetVP + 2 end
9499 if m_CurScenario.bUseCaravans then iTargetVP = iTargetVP + 2 end
9500 if m_CurScenario.bUseBAttack then iTargetVP = iTargetVP + 2 end
9501 if m_CurScenario.bUseTraders then iTargetVP = iTargetVP + 3 end
9502 setNotes(string.gsub(p.Scenario.sInfoText, '&VP&', iTargetVP))
9503
9504 --[[
9505 Reset everything in the map script zone:
9506 - highlighting of tokens (reset list of active tokens -> repopulated when new tokens are spawned)
9507 - score keeping
9508 --]]
9509 if m_MapScript ~= nil then
9510 m_MapScript.call('onMapLoad')
9511
9512 m_MapScript.call('setCatanChitScore', { fOnMap = p.Scenario.fCatanChitScoreMap, fOnHand = p.Scenario.fCatanChitScoreHand })
9513
9514 m_MapScript.setScale(p.vMapScale)
9515
9516 m_MapScript.call('clearCounters')
9517 m_MapScript.call('setCountersEnabled', {bEnable = false})
9518 end
9519
9520 m_iLoadingHexes = 0
9521 m_iSpawnedHexes = 0
9522
9523 --[[
9524 Find objects to be deleted or repositioned
9525 --]]
9526 local LargestArmy = nil
9527 local LongestRoad = nil
9528 local Harbormaster = nil
9529 local Robber = nil
9530 local BuildingCosts = {}
9531 local AllObj = getAllObjects()
9532 for i, Obj in ipairs(AllObj) do
9533 local sObjType = Obj.getVar('sObjType')
9534 local sObjName = Obj.getName()
9535 if sObjType == 'hex' or sObjType == 'numtoken' or sObjType == 'border' or sObjType == 'harbor' or sObjType == 'seafarers_add' or
9536 sObjType == 'frenemies' or sObjType == 'channel' or sObjType == 'helpers' or sObjType == 'fishermen' or
9537 sObjType == 'rivers' or sObjType == 'caravans' or sObjType == 'battack' or sObjType == 'traders' or sObjType == 'oilsprings' or
9538 sObjType == 'welfare' or
9539 sObjType == 'eap' then
9540 destroyObject(Obj)
9541 elseif Obj.tag == 'Deck' or Obj.tag == 'Card' then
9542 destroyObject(Obj)
9543 elseif sObjName == 'Treasure Chest' or sObjName == 'Dragon' or sObjName == 'Barbarian Ship' then
9544 destroyObject(Obj)
9545 elseif sObjName == 'Robber' then
9546 Robber = Obj
9547 elseif sObjName == 'Largest Army' then
9548 LargestArmy = Obj
9549 elseif sObjName == 'Longest Road' then
9550 LongestRoad = Obj
9551 elseif sObjName == 'Harbormaster' then
9552 Harbormaster = Obj
9553 elseif sObjName == 'Longest Shipping Line' then
9554 destroyObject(Obj)
9555 elseif sObjName == 'Building Costs' then
9556 table.insert(BuildingCosts, Obj)
9557 elseif sObjName == 'Blocked Settlement' then
9558 Obj.setName('Settlement')
9559 elseif sObjName == 'Blocked City' then
9560 Obj.setName('City')
9561 elseif sObjName == 'Settlement (Connected to Caravan)' then
9562 Obj.setName('Settlement')
9563 elseif sObjName == 'City (Connected to Caravan)' then
9564 Obj.setName('City')
9565 elseif string.sub(sObjName, 1, 10) == 'Catan Chit' then --Delete all existing Catan Chits
9566 destroyObject(Obj)
9567 elseif string.sub(sObjName, 1, 10) == 'Metropolis' then
9568 destroyObject(Obj)
9569 elseif string.sub(Obj.getDescription(),1,15) == 'Barbarian route' then
9570 destroyObject(Obj) --destroy and respawn it in the proper position from the control panel if needed
9571 elseif Obj.getVar('sUpgradeType') ~= nil then --destroy and respawn city upgrade charts
9572 destroyObject(Obj)
9573 end
9574 end
9575
9576 --[[
9577 Move the robber out of the way of spawning hexes to the starting position or spawn / delete it if necessary
9578 --]]
9579 local bUseRobber = true
9580 if sScenarioID == 'S7' or sScenarioID == 'T4' or m_CurScenario.bUseBAttack or m_CurScenario.bUseTraders or bIsEaP then
9581 bUseRobber = false
9582 end
9583 if Robber ~= nil then
9584 if bUseRobber == false then
9585 destroyObject(Robber)
9586 else
9587 Robber.setRotation({0.0,0.0,0.0})
9588 Robber.setPosition({x=-31.0,y=0.97,z=1.81})
9589 end
9590 elseif bUseRobber then --spawn new robber
9591 local params = {}
9592 params.type = 'Custom_Model'
9593 params.position = {-31.0, 0.97, 1.81}
9594 params.rotation = {0.0,0.0,0.0}
9595 params.scale = {1.25, 1.25, 1.25}
9596 local custom = {}
9597 custom.mesh = 'http://cloud-3.steamusercontent.com/ugc/155773601379964516/39142B17D65D9A9EA7175C2DDA61B52BF6FEE730/'
9598 custom.diffuse = ''
9599 custom.type = 1 --figurine
9600 custom.material = 1 --wood
9601 custom.specular_intensity = 0.04
9602
9603 m_Robber = spawnObject(params)
9604 m_Robber.setCustomObject(custom)
9605 m_Robber.setColorTint({0.12,0.12,0.12})
9606 m_Robber.setName('Robber')
9607 m_Robber.setDescription('The Robber has to be moved when a 7 is rolled or a Knight is played. Put it in the center of a resource tile and steal a resource card from any 1 player having settlements/cities on this tile.\nAs long as the robber is blocking a hex, no one will gain resources from this field.')
9608 end
9609
9610 --[[
9611 Position/spawn/delete the special victory point cards (longest road, largest army, harbormaster card)
9612 --]]
9613 local vPosLR = {-25.6,1.1,-10.0}
9614 if isCKenabled() then
9615 vPosLR[3] = vPosLR[3] - 2.4
9616 if m_bEnableFishermen then
9617 vPosLR[1] = vPosLR[1] + 5.0
9618 end
9619 else
9620 if m_bEnableFishermen then
9621 vPosLR[3] = vPosLR[3] - 2.6
9622 end
9623 end
9624 if LongestRoad ~= nil then
9625 if sScenarioID == 'S6' or sScenarioID == 'S7' or m_CurScenario.bUseTraders or bIsEaP then --no longest road in these scenarios
9626 destroyObject(LongestRoad)
9627 else -- put it in the proper position
9628 LongestRoad.setRotation({0.0,90.0,0.0})
9629 LongestRoad.setPosition(vPosLR)
9630 LongestRoad.setLock(true)
9631 addToUnlockList({Obj = LongestRoad})
9632 end
9633 else
9634 if sScenarioID ~= 'S6' and sScenarioID ~= 'S7' and m_CurScenario.bUseTraders == false and bIsEaP == false then
9635 --longest road does not exist but we need it -> spawn new one
9636 spawnLongestRoadCard(vPosLR, {0.0,90.0,0.0})
9637 end
9638 end
9639
9640 local vPosLA = {-26.2,0.96,-10.0}
9641 if m_bEnableFishermen then
9642 vPosLA[3] = vPosLA[3] - 2.6
9643 end
9644 if LargestArmy ~= nil then
9645 if isCKenabled() or sScenarioID == 'S7' or sScenarioID == 'T4' or m_CurScenario.bUseBAttack or bIsEaP then --no largest army with the C&K expansion and in scenario 7 & T4
9646 destroyObject(LargestArmy)
9647 else -- put it in the proper position
9648 LargestArmy.setRotation({0.0,90.0,0.0})
9649 LargestArmy.setPosition(vPosLA)
9650 LargestArmy.setLock(true)
9651 addToUnlockList({Obj = LargestArmy})
9652 end
9653 else
9654 if isCKenabled() == false and sScenarioID ~= 'S7' and sScenarioID ~= 'T4' and m_CurScenario.bUseBAttack == false and bIsEaP == false then
9655 --largest army does not exist but we need it -> spawn new one
9656 spawnLargestArmyCard(vPosLA, {0.0,90.0,0.0})
9657 end
9658 end
9659
9660 --harbor master mini expansion
9661 if m_bEnableHarborMaster == false then
9662 if Harbormaster ~= nil then
9663 destroyObject(Harbormaster)
9664 end
9665 else
9666 local vPosHM = {-25.0,1.3,-10.0}
9667 if isCKenabled() then
9668 vPosHM[1] = vPosHM[1] - 0.6
9669 vPosHM[3] = vPosHM[3] - 3.2
9670 if m_bEnableFishermen then
9671 vPosHM[1] = vPosHM[1] + 5.0
9672 end
9673 else
9674 if m_bEnableFishermen then
9675 vPosHM[3] = vPosHM[3] - 2.6
9676 end
9677 end
9678 if Harbormaster ~= nil then
9679 Harbormaster.setRotation({0.0,90.0,0.0})
9680 Harbormaster.setPosition(vPosHM)
9681 Harbormaster.setLock(true)
9682 addToUnlockList({Obj = Harbormaster})
9683 else
9684 spawnHarbormasterCard(vPosHM, {0.0,90.0,0.0})
9685 end
9686 end
9687
9688 --[[
9689 Select the proper state for the building costs cards
9690 TODO: adjust if there ever is a proper "getState" function
9691 --]]
9692 for i, Card in ipairs(BuildingCosts) do
9693 local iAvailableState = Card.getStates()[1].id -- getState returns all _other_ available states
9694 if isCKenabled() == true then
9695 if iAvailableState == 2 then --means state 2 is not set currently
9696 Card.setState(2)
9697 end
9698 else
9699 if iAvailableState == 1 then
9700 Card.setState(1)
9701 end
9702 end
9703 --Explorers & Pirates may hide the building costs --> reset it
9704 local vPos = Card.getPosition()
9705 Card.setPosition({vPos.x,0.96,vPos.z})
9706 Card.tooltip = true
9707 Card.interactable = true
9708 end
9709
9710 --[[
9711 Position dice and reset color
9712 --]]
9713 if m_Die1 ~= nil then
9714 m_Die1.setRotation({-90.0,0.0,0.0})
9715 m_Die1.setPosition({x=-31.0,y=1.373,z=0.514})
9716 else
9717 if m_bEnableEventCards == false then
9718 m_Die1 = spawnDie(1, {x=-31.0,y=1.373,z=0.514}, {-90.0,0.0,0.0})
9719 end
9720 end
9721 if m_Die2 ~= nil then
9722 m_Die2.setRotation({-90.0,0.0,0.0})
9723 m_Die2.setPosition({x=-31.0,y=1.373,z=-0.5})
9724 m_Die2.setColorTint({0.90588,0.82745,0.72157})
9725 else
9726 if m_bEnableEventCards == false or isCKenabled() then
9727 m_Die2 = spawnDie(2, {x=-31.0,y=1.373,z=-0.5}, {-90.0,0.0,0.0})
9728 end
9729 end
9730 --remove event die if no longer needed
9731 if isCKenabled() == false and m_EventDie ~= nil then
9732 destroyObject(m_EventDie)
9733 end
9734
9735 --[[
9736 Spawn selected expansions
9737 A coroutine is prepared for each of the selected expansions they are each given a reference to the coroutine for the
9738 next expansion to be loaded. The first routine is started and will automatically trigger the next one once it is done loading.
9739 --]]
9740 local SpawnRoutines = {}
9741 --Cities & Knights
9742 if isCKenabled() then table.insert(SpawnRoutines, spawnCKExpansion()) end
9743 --The Fishermen of Catan
9744 if m_bEnableFishermen then table.insert(SpawnRoutines, spawnFishermenExpansion()) end
9745 --Welfare Variant
9746 if m_bEnableWelfare then table.insert(SpawnRoutines, spawnWelfareExpansion()) end
9747 --The Rivers of Catan
9748 if m_CurScenario.bUseRivers then table.insert(SpawnRoutines, spawnRiversExpansion()) end
9749 --The Caravans
9750 if m_CurScenario.bUseCaravans then table.insert(SpawnRoutines, spawnCaravansExpansion()) end
9751 --Barbarian Attack
9752 if m_CurScenario.bUseBAttack then table.insert(SpawnRoutines, spawnBarbarianAttackExpansion()) end
9753 --Traders & Barbarians
9754 if m_CurScenario.bUseTraders then table.insert(SpawnRoutines, spawnTradersAndBarbariansExpansion()) end
9755 --Frenemies
9756 if m_iFrenemiesPlayers > 0 then table.insert(SpawnRoutines, spawnFrenemiesExpansion()) end
9757 --Oilsprings
9758 if m_bEnableOilsprings then table.insert(SpawnRoutines, spawnOilspringsExpansion()) end
9759 --Helpers of Catan
9760 if m_bEnableHelpers then table.insert(SpawnRoutines, spawnHelpersExpansion()) end
9761 --Catan Event Cards
9762 if m_bEnableEventCards then table.insert(SpawnRoutines, spawnEventCardsExpansion()) end
9763
9764 --pass each routine the next routine to start when it finishes
9765 for i = 1, #SpawnRoutines do
9766 coroutine.resume(SpawnRoutines[i], SpawnRoutines[i+1])
9767 end
9768 --trigger the first spawn function to spawn all selected ones
9769 if #SpawnRoutines > 0 then
9770 coroutine.resume(SpawnRoutines[1])
9771 end
9772end
9773
9774--Neighboring tile definition
9775function getNBRS() return NBRS end
9776NBRS = { [1] = {['NE'] = 67, ['E'] = 2, ['SE'] = 10, ['SW'] = 9, ['W'] = 97, ['NW'] = 66},
9777 [2] = {['NE'] = 68, ['E'] = 3, ['SE'] = 11, ['SW'] = 10, ['W'] = 1, ['NW'] = 67},
9778 [3] = {['NE'] = 69, ['E'] = 4, ['SE'] = 12, ['SW'] = 11, ['W'] = 2, ['NW'] = 68},
9779 [4] = {['NE'] = 70, ['E'] = 5, ['SE'] = 13, ['SW'] = 12, ['W'] = 3, ['NW'] = 69},
9780 [5] = {['NE'] = 71, ['E'] = 6, ['SE'] = 14, ['SW'] = 13, ['W'] = 4, ['NW'] = 70},
9781 [6] = {['NE'] = 72, ['E'] = 7, ['SE'] = 15, ['SW'] = 14, ['W'] = 5, ['NW'] = 71},
9782 [7] = {['NE'] = 73, ['E'] = 8, ['SE'] = 16, ['SW'] = 15, ['W'] = 6, ['NW'] = 72},
9783 [8] = {['NE'] = 74, ['E'] = 75, ['SE'] = 17, ['SW'] = 16, ['W'] = 7, ['NW'] = 73},
9784 [9] = {['NE'] = 1, ['E'] = 10, ['SE'] = 19, ['SW'] = 18, ['W'] = 96, ['NW'] = 97},
9785 [10] = {['NE'] = 2, ['E'] = 11, ['SE'] = 20, ['SW'] = 19, ['W'] = 9, ['NW'] = 1},
9786 [11] = {['NE'] = 3, ['E'] = 12, ['SE'] = 21, ['SW'] = 20, ['W'] = 10, ['NW'] = 2},
9787 [12] = {['NE'] = 4, ['E'] = 13, ['SE'] = 22, ['SW'] = 21, ['W'] = 11, ['NW'] = 3},
9788 [13] = {['NE'] = 5, ['E'] = 14, ['SE'] = 23, ['SW'] = 22, ['W'] = 12, ['NW'] = 4},
9789 [14] = {['NE'] = 6, ['E'] = 15, ['SE'] = 24, ['SW'] = 23, ['W'] = 13, ['NW'] = 5},
9790 [15] = {['NE'] = 7, ['E'] = 16, ['SE'] = 25, ['SW'] = 24, ['W'] = 14, ['NW'] = 6},
9791 [16] = {['NE'] = 8, ['E'] = 17, ['SE'] = 26, ['SW'] = 25, ['W'] = 15, ['NW'] = 7},
9792 [17] = {['NE'] = 75, ['E'] = 76, ['SE'] = 27, ['SW'] = 26, ['W'] = 16, ['NW'] = 8},
9793 [18] = {['NE'] = 9, ['E'] = 19, ['SE'] = 29, ['SW'] = 28, ['W'] = 95, ['NW'] = 96},
9794 [19] = {['NE'] = 10, ['E'] = 20, ['SE'] = 30, ['SW'] = 29, ['W'] = 18, ['NW'] = 9},
9795 [20] = {['NE'] = 11, ['E'] = 21, ['SE'] = 31, ['SW'] = 30, ['W'] = 19, ['NW'] = 10},
9796 [21] = {['NE'] = 12, ['E'] = 22, ['SE'] = 32, ['SW'] = 31, ['W'] = 20, ['NW'] = 11},
9797 [22] = {['NE'] = 13, ['E'] = 23, ['SE'] = 33, ['SW'] = 32, ['W'] = 21, ['NW'] = 12},
9798 [23] = {['NE'] = 14, ['E'] = 24, ['SE'] = 34, ['SW'] = 33, ['W'] = 22, ['NW'] = 13},
9799 [24] = {['NE'] = 15, ['E'] = 25, ['SE'] = 35, ['SW'] = 34, ['W'] = 23, ['NW'] = 14},
9800 [25] = {['NE'] = 16, ['E'] = 26, ['SE'] = 36, ['SW'] = 35, ['W'] = 24, ['NW'] = 15},
9801 [26] = {['NE'] = 17, ['E'] = 27, ['SE'] = 37, ['SW'] = 36, ['W'] = 25, ['NW'] = 16},
9802 [27] = {['NE'] = 76, ['E'] = 77, ['SE'] = 38, ['SW'] = 37, ['W'] = 26, ['NW'] = 17},
9803 [28] = {['NE'] = 18, ['E'] = 29, ['SE'] = 39, ['SW'] = 93, ['W'] = 94, ['NW'] = 95},
9804 [29] = {['NE'] = 19, ['E'] = 30, ['SE'] = 40, ['SW'] = 39, ['W'] = 28, ['NW'] = 18},
9805 [30] = {['NE'] = 20, ['E'] = 31, ['SE'] = 41, ['SW'] = 40, ['W'] = 29, ['NW'] = 19},
9806 [31] = {['NE'] = 21, ['E'] = 32, ['SE'] = 42, ['SW'] = 41, ['W'] = 30, ['NW'] = 20},
9807 [32] = {['NE'] = 22, ['E'] = 33, ['SE'] = 43, ['SW'] = 42, ['W'] = 31, ['NW'] = 21},
9808 [33] = {['NE'] = 23, ['E'] = 34, ['SE'] = 44, ['SW'] = 43, ['W'] = 32, ['NW'] = 22},
9809 [34] = {['NE'] = 24, ['E'] = 35, ['SE'] = 45, ['SW'] = 44, ['W'] = 33, ['NW'] = 23},
9810 [35] = {['NE'] = 25, ['E'] = 36, ['SE'] = 46, ['SW'] = 45, ['W'] = 34, ['NW'] = 24},
9811 [36] = {['NE'] = 26, ['E'] = 37, ['SE'] = 47, ['SW'] = 46, ['W'] = 35, ['NW'] = 25},
9812 [37] = {['NE'] = 27, ['E'] = 38, ['SE'] = 48, ['SW'] = 47, ['W'] = 36, ['NW'] = 26},
9813 [38] = {['NE'] = 77, ['E'] = 78, ['SE'] = 79, ['SW'] = 48, ['W'] = 37, ['NW'] = 27},
9814 [39] = {['NE'] = 29, ['E'] = 40, ['SE'] = 49, ['SW'] = 92, ['W'] = 93, ['NW'] = 28},
9815 [40] = {['NE'] = 30, ['E'] = 41, ['SE'] = 50, ['SW'] = 49, ['W'] = 39, ['NW'] = 29},
9816 [41] = {['NE'] = 31, ['E'] = 42, ['SE'] = 51, ['SW'] = 50, ['W'] = 40, ['NW'] = 30},
9817 [42] = {['NE'] = 32, ['E'] = 43, ['SE'] = 52, ['SW'] = 51, ['W'] = 41, ['NW'] = 31},
9818 [43] = {['NE'] = 33, ['E'] = 44, ['SE'] = 53, ['SW'] = 52, ['W'] = 42, ['NW'] = 32},
9819 [44] = {['NE'] = 34, ['E'] = 45, ['SE'] = 54, ['SW'] = 53, ['W'] = 43, ['NW'] = 33},
9820 [45] = {['NE'] = 35, ['E'] = 46, ['SE'] = 55, ['SW'] = 54, ['W'] = 44, ['NW'] = 34},
9821 [46] = {['NE'] = 36, ['E'] = 47, ['SE'] = 56, ['SW'] = 55, ['W'] = 45, ['NW'] = 35},
9822 [47] = {['NE'] = 37, ['E'] = 48, ['SE'] = 57, ['SW'] = 56, ['W'] = 46, ['NW'] = 36},
9823 [48] = {['NE'] = 38, ['E'] = 79, ['SE'] = 80, ['SW'] = 57, ['W'] = 47, ['NW'] = 37},
9824 [49] = {['NE'] = 40, ['E'] = 50, ['SE'] = 58, ['SW'] = 91, ['W'] = 92, ['NW'] = 39},
9825 [50] = {['NE'] = 41, ['E'] = 51, ['SE'] = 59, ['SW'] = 58, ['W'] = 49, ['NW'] = 40},
9826 [51] = {['NE'] = 42, ['E'] = 52, ['SE'] = 60, ['SW'] = 59, ['W'] = 50, ['NW'] = 41},
9827 [52] = {['NE'] = 43, ['E'] = 53, ['SE'] = 61, ['SW'] = 60, ['W'] = 51, ['NW'] = 42},
9828 [53] = {['NE'] = 44, ['E'] = 54, ['SE'] = 62, ['SW'] = 61, ['W'] = 52, ['NW'] = 43},
9829 [54] = {['NE'] = 45, ['E'] = 55, ['SE'] = 63, ['SW'] = 62, ['W'] = 53, ['NW'] = 44},
9830 [55] = {['NE'] = 46, ['E'] = 56, ['SE'] = 64, ['SW'] = 63, ['W'] = 54, ['NW'] = 45},
9831 [56] = {['NE'] = 47, ['E'] = 57, ['SE'] = 65, ['SW'] = 64, ['W'] = 55, ['NW'] = 46},
9832 [57] = {['NE'] = 48, ['E'] = 80, ['SE'] = 81, ['SW'] = 65, ['W'] = 56, ['NW'] = 47},
9833 [58] = {['NE'] = 50, ['E'] = 59, ['SE'] = 89, ['SW'] = 90, ['W'] = 91, ['NW'] = 49},
9834 [59] = {['NE'] = 51, ['E'] = 60, ['SE'] = 88, ['SW'] = 89, ['W'] = 58, ['NW'] = 50},
9835 [60] = {['NE'] = 52, ['E'] = 61, ['SE'] = 87, ['SW'] = 88, ['W'] = 59, ['NW'] = 51},
9836 [61] = {['NE'] = 53, ['E'] = 62, ['SE'] = 86, ['SW'] = 87, ['W'] = 60, ['NW'] = 52},
9837 [62] = {['NE'] = 54, ['E'] = 63, ['SE'] = 85, ['SW'] = 86, ['W'] = 61, ['NW'] = 53},
9838 [63] = {['NE'] = 55, ['E'] = 64, ['SE'] = 84, ['SW'] = 85, ['W'] = 62, ['NW'] = 54},
9839 [64] = {['NE'] = 56, ['E'] = 65, ['SE'] = 83, ['SW'] = 84, ['W'] = 63, ['NW'] = 55},
9840 [65] = {['NE'] = 57, ['E'] = 81, ['SE'] = 82, ['SW'] = 83, ['W'] = 64, ['NW'] = 56},
9841 [66] = {['E'] = 67, ['SE'] = 1, ['SW'] = 97}, [67] = {['E'] = 68, ['SE'] = 2, ['SW'] = 1, ['W'] = 66},
9842 [68] = {['E'] = 69, ['SE'] = 3, ['SW'] = 2, ['W'] = 67}, [69] = {['E'] = 70, ['SE'] = 4, ['SW'] = 3, ['W'] = 68},
9843 [70] = {['E'] = 71, ['SE'] = 5, ['SW'] = 4, ['W'] = 69}, [71] = {['E'] = 72, ['SE'] = 6, ['SW'] = 5, ['W'] = 70},
9844 [72] = {['E'] = 73, ['SE'] = 7, ['SW'] = 6, ['W'] = 71}, [73] = {['E'] = 74, ['SE'] = 8, ['SW'] = 7, ['W'] = 72},
9845 [74] = {['E'] = 98, ['SE'] = 75, ['SW'] = 8, ['W'] = 73}, [75] = {['NE'] = 98, ['E'] = 99, ['SE'] = 76, ['SW'] = 17, ['W'] = 8, ['NW'] = 74},
9846 [76] = {['NE'] = 99, ['E'] = 100, ['SE'] = 77, ['SW'] = 27, ['W'] = 17, ['NW'] = 75},
9847 [77] = {['NE'] = 100, ['E'] = 101, ['SE'] = 78, ['SW'] = 38, ['W'] = 27, ['NW'] = 76},
9848 [78] = {['NE'] = 101, ['E'] = 102, ['SE'] = 103, ['SW'] = 79, ['W'] = 38, ['NW'] = 77},
9849 [79] = {['NE'] = 78, ['E'] = 103, ['SE'] = 104, ['SW'] = 80, ['W'] = 48, ['NW'] = 38},
9850 [80] = {['NE'] = 79, ['E'] = 104, ['SE'] = 105, ['SW'] = 81, ['W'] = 57, ['NW'] = 48},
9851 [81] = {['NE'] = 80, ['E'] = 105, ['SE'] = 106, ['SW'] = 82, ['W'] = 65, ['NW'] = 57},
9852 [82] = {['NE'] = 81, ['E'] = 106, ['W'] = 83, ['NW'] = 65},
9853 [83] = {['NE'] = 65, ['E'] = 82, ['W'] = 84, ['NW'] = 64}, [84] = {['NE'] = 64, ['E'] = 83, ['W'] = 85, ['NW'] = 63},
9854 [85] = {['NE'] = 63, ['E'] = 84, ['W'] = 86, ['NW'] = 62}, [86] = {['NE'] = 62, ['E'] = 85, ['W'] = 87, ['NW'] = 61},
9855 [87] = {['NE'] = 61, ['E'] = 86, ['W'] = 88, ['NW'] = 60}, [88] = {['NE'] = 60, ['E'] = 87, ['W'] = 89, ['NW'] = 59},
9856 [89] = {['NE'] = 59, ['E'] = 88, ['W'] = 90, ['NW'] = 58}, [90] = {['NE'] = 58, ['E'] = 89, ['NW'] = 91},
9857 [91] = {['NE'] = 49, ['E'] = 58, ['SE'] = 90, ['NW'] = 92}, [92] = {['NE'] = 39, ['E'] = 49, ['SE'] = 91, ['NW'] = 93},
9858 [93] = {['NE'] = 29, ['E'] = 39, ['SE'] = 92, ['NW'] = 94}, [94] = {['NE'] = 95, ['E'] = 28, ['SE'] = 93},
9859 [95] = {['NE'] = 96, ['E'] = 18, ['SE'] = 28, ['SW'] = 94}, [96] = {['NE'] = 97, ['E'] = 9, ['SE'] = 18, ['SW'] = 95},
9860 [97] = {['NE'] = 66, ['E'] = 1, ['SE'] = 9, ['SW'] = 96}, [98] = {['SE'] = 99, ['SW'] = 75, ['W'] = 74},
9861 [99] = {['SE'] = 100, ['SW'] = 76, ['W'] = 75, ['NW'] = 98}, [100] = {['SE'] = 101, ['SW'] = 77, ['W'] = 76, ['NW'] = 99},
9862 [101] = {['SE'] = 102, ['SW'] = 78, ['W'] = 77, ['NW'] = 100}, [102] = {['SW'] = 103, ['W'] = 78, ['NW'] = 101},
9863 [103] = {['NE'] = 102, ['SW'] = 104, ['W'] = 79, ['NW'] = 78}, [104] = {['NE'] = 103, ['SW'] = 105, ['W'] = 80, ['NW'] = 79},
9864 [105] = {['NE'] = 104, ['SW'] = 106, ['W'] = 81, ['NW'] = 80}, [106] = {['NE'] = 105, ['W'] = 82, ['NW'] = 81},
9865 }
9866SW'] = 86, ['W'] = 61, ['NW'] = 53},
9867 [63] = {['NE'] = 55, ['E'] = 64, ['SE'] = 84, ['SW'] = 85, ['W'] = 62, ['NW'] = 54},
9868 [64] = {['NE'] = 56, ['E'] = 65, ['SE'] = 83, ['SW'] = 84, ['W'] = 63, ['NW'] = 55},
9869 [65] = {['NE'] = 57, ['E'] = 81, ['SE'] = 82, ['SW'] = 83, ['W'] = 64, ['NW'] = 56},
9870 [66] = {['E'] = 67, ['SE'] = 1, ['SW'] = 97}, [67] = {['E'] = 68, ['SE'] = 2, ['SW'] = 1, ['W'] = 66},
9871 [68] = {['E'] = 69, ['SE'] = 3, ['SW'] = 2, ['W'] = 67}, [69] = {['E'] = 70, ['SE'] = 4, ['SW'] = 3, ['W'] = 68},
9872 [70] = {['E'] = 71, ['SE'] = 5, ['SW'] = 4, ['W'] = 69}, [71] = {['E'] = 72, ['SE'] = 6, ['SW'] = 5, ['W'] = 70},
9873 [72] = {['E'] = 73, ['SE'] = 7, ['SW'] = 6, ['W'] = 71}, [73] = {['E'] = 74, ['SE'] = 8, ['SW'] = 7, ['W'] = 72},
9874 [74] = {['E'] = 98, ['SE'] = 75, ['SW'] = 8, ['W'] = 73}, [75] = {['NE'] = 98, ['E'] = 99, ['SE'] = 76, ['SW'] = 17, ['W'] = 8, ['NW'] = 74},
9875 [76] = {['NE'] = 99, ['E'] = 100, ['SE'] = 77, ['SW'] = 27, ['W'] = 17, ['NW'] = 75},
9876 [77] = {['NE'] = 100, ['E'] = 101, ['SE'] = 78, ['SW'] = 38, ['W'] = 27, ['NW'] = 76},
9877 [78] = {['NE'] = 101, ['E'] = 102, ['SE'] = 103, ['SW'] = 79, ['W'] = 38, ['NW'] = 77},
9878 [79] = {['NE'] = 78, ['E'] = 103, ['SE'] = 104, ['SW'] = 80, ['W'] = 48, ['NW'] = 38},
9879 [80] = {['NE'] = 79, ['E'] = 104, ['SE'] = 105, ['SW'] = 81, ['W'] = 57, ['NW'] = 48},
9880 [81] = {['NE'] = 80, ['E'] = 105, ['SE'] = 106, ['SW'] = 82, ['W'] = 65, ['NW'] = 57},
9881 [82] = {['NE'] = 81, ['E'] = 106, ['W'] = 83, ['NW'] = 65},
9882 [83] = {['NE'] = 65, ['E'] = 82, ['W'] = 84, ['NW'] = 64}, [84] = {['NE'] = 64, ['E'] = 83, ['W'] = 85, ['NW'] = 63},
9883 [85] = {['NE'] = 63, ['E'] = 84, ['W'] = 86, ['NW'] = 62}, [86] = {['NE'] = 62, ['E'] = 85, ['W'] = 87, ['NW'] = 61},
9884 [87] = {['NE'] = 61, ['E'] = 86, ['W'] = 88, ['NW'] = 60}, [88] = {['NE'] = 60, ['E'] = 87, ['W'] = 89, ['NW'] = 59},
9885 [89] = {['NE'] = 59, ['E'] = 88, ['W'] = 90, ['NW'] = 58}, [90] = {['NE'] = 58, ['E'] = 89, ['NW'] = 91},
9886 [91] = {['NE'] = 49, ['E'] = 58, ['SE'] = 90, ['NW'] = 92}, [92] = {['NE'] = 39, ['E'] = 49, ['SE'] = 91, ['NW'] = 93},
9887 [93] = {['NE'] = 29, ['E'] = 39, ['SE'] = 92, ['NW'] = 94}, [94] = {['NE'] = 95, ['E'] = 28, ['SE'] = 93},
9888 [95] = {['NE'] = 96, ['E'] = 18, ['SE'] = 28, ['SW'] = 94}, [96] = {['NE'] = 97, ['E'] = 9, ['SE'] = 18, ['SW'] = 95},
9889 [97] = {['NE'] = 66, ['E'] = 1, ['SE'] = 9, ['SW'] = 96}, [98] = {['SE'] = 99, ['SW'] = 75, ['W'] = 74},
9890 [99] = {['SE'] = 100, ['SW'] = 76, ['W'] = 75, ['NW'] = 98}, [100] = {['SE'] = 101, ['SW'] = 77, ['W'] = 76, ['NW'] = 99},
9891 [101] = {['SE'] = 102, ['SW'] = 78, ['W'] = 77, ['NW'] = 100}, [102] = {['SW'] = 103, ['W'] = 78, ['NW'] = 101},
9892 [103] = {['NE'] = 102, ['SW'] = 104, ['W'] = 79, ['NW'] = 78}, [104] = {['NE'] = 103, ['SW'] = 105, ['W'] = 80, ['NW'] = 79},
9893 [105] = {['NE'] = 104, ['SW'] = 106, ['W'] = 81, ['NW'] = 80}, [106] = {['NE'] = 105, ['W'] = 82, ['NW'] = 81},
9894 }