Skip to content

Instantly share code, notes, and snippets.

@Silvyre
Last active July 4, 2022 20:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Silvyre/7e73756f108dc987a0d12d609e615371 to your computer and use it in GitHub Desktop.
Save Silvyre/7e73756f108dc987a0d12d609e615371 to your computer and use it in GitHub Desktop.
πŸ§™πŸ‘ AutoControl for Colonist.io (ACCio): play Colonist.io using your keyboard! Setup instructions in comments below.
{"customEntities":{"script":[["1",{"name":"wall.js","value":{"mdTime":1641400238352,"srcCode":"await ACCio(_ => {\n socketGameSend.buildCityWall();\n selectNextAfterDelay();\n});"}}],["2",{"name":"setup.js","value":{"mdTime":1656815986599,"srcCode":" try {\n return ACCio(_ => console.log(\"ACCio is already enabled! Try refreshing if you're having trouble.\"));\n }\n catch (e) { console.error(e) }\n\n try {\n await ACtl.runInPageCtx('https://cdn.jsdelivr.net/gh/CoeJoder/waitForKeyElements.js@v1.2/waitForKeyElements.js');\n }\n catch {\n return ACtl.runInPageCtx(_ => console.error('ACCio failed to load all required libraries.'));\n }\n\n ACCio = new Proxy(ACtl.runInPageCtx, {\n apply: (target, thisArg, argumentsList) => {\n if (['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) {\n return;\n }\n\n try { return target(...argumentsList); }\n catch (e) { console.error(e) }\n }\n });\n\n await ACtl.runInPageCtx(_ => waitForKeyElements('canvas[style*=\"touch-action: none\"]', async _ => {\n if (window.accioEnabled) {\n return;\n }\n\n await new Promise(r => setTimeout(r, 500));\n\n if (!uiGameTradeInfoController) {\n console.error('ACCio could not find the required Window attribute - try refreshing the page.');\n return;\n }\n\n canvas = document.querySelector('canvas');\n gameManager = uiGameTradeInfoController.uiGameManager;\n socketGameSend = gameManager.socketGameSend;\n eventController = gameManager.eventController;\n mechanics = eventController.uiGameMechanics;\n tradeController = gameManager.tradeController;\n bankController = gameManager.bankController;\n mapController = gameManager.mapController;\n mapView = mapController.mapView;\n highlightController = mapController.highlightController;\n highlightDiceController = mechanics.find(o => o.highlightDiceNumbers);\n cardController = mechanics.find(o => o.getCardFromHand);\n popupController = mechanics.find(o => o.popupW);\n selectPlayerToStealController = mechanics.find(o => o.updateCheckmark);\n robberController = mechanics.find(o => o.lastRobberTile);\n stateController = mechanics.find(o => o.displayActionState);\n expandableButtons = gameManager?.actionsController?.buttonViews?.filter(o => o.expandedButtons?.length >= 0) || [];\n turnButtonView = gameManager.actionsController.buttonViews.find(o => o.action.toString().includes('TurnButton'));\n CardCountView = mechanics.find(o => o.initResourceCardCountViews).cardCountViewContainer.children.at(-1).constructor;\n gameState = gameManager.gameState;\n mapState = gameState.mapState;\n myColor = gameState.myColor;\n hideBankCards = gameState.gameSettings.hideBankCards;\n tileCorners = mapState.tileState.tileCorners;\n tileEdges = mapState.tileState.tileEdges;\n tileFaces = mapState.tileState.tiles;\n hexH = mapView.hexagonHeight;\n hexW = mapView.hexagonWidth;\n\n a_ = ['None', 'GameSetupPlaceSettlement', 'GameSetupPlaceCity', 'GameSetupRoadPlacement', 'PlaceRoad', 'PlaceRoadForFree', 'PlaceSettlement', 'PlaceCity', 'PlaceCityWithDiscount', 'PlaceShip', 'PlaceShipForFree', 'SelectShipToMove', 'MoveShip', 'PlaceCityWall', 'PlaceCityWallForFree', 'PlaceKnight', 'PlaceKnightForFree', 'UpgradeKnight', 'UpgradeKnightForFree', 'ActivateKnight', 'SelectKnightToTakeAction', 'SelectKnightAction', 'MoveKnightOffTurn', 'SelectKnightToRemove', 'PlaceRobberOrPirate', 'PlaceRobberOnly', 'PlacePirateOnly', 'SelectWhoToRob', 'SelectCardsToDiscard', 'SelectProgressCardsToDiscard', 'Place2MoreRoadBuilding', 'Place1MoreRoadBuilding', 'Select2ResourcesForYearOfPlenty', 'Select1ResourceForMonopoly', 'SelectCardsForPendingDistributions', 'PlaceMetropolis', 'SelectProgressCard', 'SelectCityToPillage', 'PlaceMerchant', 'SelectDiceForAlchemist', 'SelectTileProductionNumbersToSwap', 'SelectPlayerForDeserter', 'SelectRoadToRemove', 'DiplomatSelectWhetherToPlaceNewEdgePiece', 'IntrigueSelectKnightToMove', 'SpySelectPlayer', 'SpySelectProgressCard', 'SelectCardsForWedding', 'CommercialHarborSelectPlayerAndResource', 'CommercialHarborOpponentSelectsCard', 'MasterMerchantSelectPlayer', 'MasterMerchantSelectCards', 'MerchantFleetSelectResource', 'ResourceMonopolySelectResource', 'CommodityMonopolySelectResource'].reduce((obj, e, idx) => ({ [e]: idx, ...obj }), {});\n b_ = ['Empty', 'Settlement', 'City', 'DestroyedCity', 'KnightActiveLevel1', 'KnightActiveLevel2', 'KnightActiveLevel3', 'KnightInactiveLevel1', 'KnightInactiveLevel2', 'KnightInactiveLevel3'].reduce((obj, e, idx) => ({ [e]: idx, ...obj }), {});\n c_ = ['ResourceBack', 'Lumber', 'Brick', 'Wool', 'Grain', 'Ore', 'DevelopmentBack', 'Knight', 'VictoryPoint', 'Monopoly', 'RoadBuilding', 'YearOfPlenty', 'CommodityBack', 'Cloth', 'Coin', 'Paper', 'ProgressCardTradeBack', 'ProgressCardTradeCommercialHarbor', 'ProgressCardTradeMasterMerchant', 'ProgressCardTradeMerchant', 'ProgressCardTradeMerchantFleet', 'ProgressCardTradeResourceMonopoly', 'ProgressCardTradeCommodityMonopoly', 'ProgressCardPoliticsBack', 'ProgressCardPoliticsBishop', 'ProgressCardPoliticsConstitution', 'ProgressCardPoliticsDeserter', 'ProgressCardPoliticsDiplomat', 'ProgressCardPoliticsIntrigue', 'ProgressCardPoliticsSaboteur', 'ProgressCardPoliticsSpy', 'ProgressCardPoliticsWarlord', 'ProgressCardPoliticsWedding', 'ProgressCardScienceBack', 'ProgressCardScienceAlchemist', 'ProgressCardScienceCrane', 'ProgressCardScienceEngineer', 'ProgressCardScienceInventor', 'ProgressCardScienceIrrigation', 'ProgressCardScienceMedicine', 'ProgressCardScienceMining', 'ProgressCardSciencePrinter', 'ProgressCardScienceRoadBuilding', 'ProgressCardScienceSmith'].reduce((obj, e, idx) => ({ [e]: idx, [idx]: e, ...obj }), {});\n e_ = ['ContinueGame', 'GameStateUpdated', 'PlayerControllerStateUpdated', 'DiceStateUpdated', 'BankStateUpdated', 'MapStateUpdated', 'GameSettingsUpdated', 'AmountOfCardsToDiscardUpdated', 'AmountOfCardsToSelect', 'SelectCardsToDiscard', 'SelectCardsFromBankUsingDevelopmentCard', 'SelectCardsForPendingDistribution', 'SelectCardToGiveToAnotherPlayer', 'ConfirmUseDevelopmentCard', 'PlayersTakingAction', 'PlayersDoneTakingAction', 'StealDevelopmentCardFromPlayer', 'StealResourceCardFromPlayer', 'SelectPlayer', 'SelectPlayerForDevelopmentCard', 'SelectPlayerAndCard', 'SelectDice', 'GivePlayerCardFromTile', 'ExchangeCards', 'CloseTradeUI', 'ShowTradeUI', 'EmbargoStatusUpdated', 'RemoveTradeOffer', 'TradeOfferRejected', 'TradeOfferAccepted', 'NewTradeOfferReceived', 'BoldTextInTradeWindow', 'CloseDiscardCardUI', 'ShowDiscardCardUI', 'ClosePopupUI', 'CancelSpecialBuildPhase', 'SetRoadEdgeHighlight', 'SetShipEdgeHighlight', 'SetCornerHighlight', 'SetTileHighlight', 'HighlightDiceNumbersForInventor', 'VoteReceived', 'PlayerReceivedAchievement', 'CavasResized', 'ClickedMultiButton', 'SpectatorCountUpdated', 'KnightStateReceived', 'KnightStateUpdated', 'BarbarianInvasionStateUpdated', 'CityImprovementStateReceived', 'CityImprovementStateUpdated', 'ForceShowCityImprovementButtons', 'UnlockedCityImprovments', 'PlayerReceivedMetropolis', 'MerchantStateReceived', 'MerchantStateUpdated', 'GameEnd', 'GameEndRematchTextUpdated', 'GameEndShowMap', 'GameEndHideMap', 'ActivateRedDice', 'HoveredOverPlayerInformationArrow', 'PointerOutPlayerInformationView', 'RobberMoved', 'MerchantBankTradeRatioActive'].reduce((obj, e, idx) => ({ [e]: idx, ...obj }), {});\n n_ = ['ChatSubmit', 'UpdateNickName', 'ClickedLobbyGameListButton', 'RequestLobbyRoomList', 'ClickedStartTutorial', 'ClickedBotGame', 'ClickedCreateRoom', 'ClickedJoinRoom', 'AccessGameLink', 'ClickedSpectateGame', 'ClickedReconnectGame', 'ClickedChangeIcon', 'EnteredMatchmaking', 'ClickedDice', 'SelectedTile', 'SelectedTiles', 'SelectedPlayer', 'PassedTurn', 'SelectedCards', 'BuyDevelopmentCard', 'WantToBuildRoad', 'ConfirmBuildRoad', 'ConfirmRemoveRoad', 'WantToBuildSettlement', 'ConfirmBuildSettlement', 'WantToBuildCity', 'ConfirmBuildCity', 'WantToBuildShip', 'ConfirmBuildShip', 'WantToMoveShip', 'SelectedShipToMove', 'ConfirmMoveShip', 'WantToBuildCityWall', 'ConfirmBuildCityWall', 'WantToPlaceKnight', 'ConfirmPlaceKnight', 'WantToUpgradeKnight', 'ConfirmUpgradeKnight', 'WantToActivateKnight', 'ConfirmActivateKnight', 'WantToTakeKnightAction', 'SelectKnightToTakeAction', 'ConfirmMoveKnight', 'ConfirmMoveKnightOffTurn', 'ConfirmRemoveKnight', 'ConfirmCityUpgrade', 'ConfirmBuildMetropolis', 'ConfirmPillageCity', 'ConfirmDicePair', 'CancelAction', 'ClickedDevelpomentCard', 'CreatedPlayerOffer', 'AcceptedOffer', 'TakeAcceptedOffer', 'RejectedOffer', 'CreatedCounterOffer', 'ClickedEmbargo', 'RequestActionSwap', 'RequestSpecialBuildPhase', 'CancelSpecialBuildPhase', 'RoomReadyToStart', 'RoomSelectColor', 'RoomStartGame', 'RoomKickPlayer', 'RoomAddBot', 'RoomSettingChangePrivateGame', 'RoomSettingUpdateGameModeSetting', 'RoomSettingUpdateMapSetting', 'RoomSettingUpdateDiceSetting', 'RoomSettingChangeVictoryPointsToWin', 'RoomSettingChangeKarmaActive', 'RoomSettingChangeCardDiscardLimit', 'RoomSettingChangeFriendlyRobber', 'RoomSettingChangeMaxPlayers', 'RoomSettingChangeHideBankCards', 'RoomSettingChangeGameSpeed', 'RoomSettingChangeBotSpeed', 'ClickedRematch', 'Vote', 'ClickedFindGame', 'ClickedCancelFindGame', 'DisconnectedFromSocketServer', 'RequestToJoinMatchmakingMatch', 'ExitedMatchmaking'].reduce((obj, e, idx) => ({ [e]: idx, ...obj }), {});\n t_ = ['GameSetup', 'Dice', 'Turn', 'GameEnd', 'SpecialBuildPhase'].reduce((obj, e, idx) => ({ [e]: idx, ...obj }), {});\n p_ = ['None', 'Robber', 'Pirate'].reduce((obj, e, idx) => ({ [e]: idx, ...obj }), {});\n et_ = ['Empty', 'Road', 'Ship'].reduce((obj, e, idx) => ({ [e]: idx, ...obj }), {});\n\n cType = { North: 0, South: 1 };\n eType = { NorthWest: 0, West: 1, SouthWest: 2 };\n\n selectedHL = null;\n selectedDiceHL = null;\n selectedDiceView = null;\n selectedInventorTile = null;\n hoveredOverPlayerInformationArrow = false;\n lastPlayer = null;\n lastTurnState = null;\n lastActionState = null;\n lastEvent = null;\n passTurnTexture = null;\n\n const gameMode = gameState.gameSettings?.settingTexts()?.at(4)?.at(1);\n isSF = gameMode?.includes('Seafarers');\n isCK = gameMode?.includes('Cities & Knights');\n\n const moveInArray = (arr, fromIdx, toIdx) => {\n const elem = arr.at(fromIdx);\n arr.splice(fromIdx, 1);\n arr.splice(toIdx, 0, elem);\n };\n\n const highlightBg = (view, color) => {\n if (!view.hasHighlightedBg) {\n view.hasHighlightedBg = true;\n }\n else {\n view.children.splice(0, 1);\n }\n\n view.drawBg();\n moveInArray(view.children, -1, 0);\n\n view.bg.geometry.graphicsData.at(0).fillStyle.color = color || 0xffffff;\n view.bg.geometry.graphicsData.at(0).fillStyle.alpha = color ? 0.3 : 0;\n };\n\n const _activateEndTurn = turnButtonView.activateEndTurn.bind(turnButtonView);\n const _deactivateEndTurn = turnButtonView.deactivateEndTurn.bind(turnButtonView);\n const _sendEvent = eventController.sendEvent.bind(eventController);\n\n eventController.sendEvent = async (v, e) => {\n _sendEvent(v, e);\n \n await new Promise(r => setTimeout(r, 100));\n \n const takingTurn = gameState.currentTurnPlayerColor == myColor;\n const takingOffTurnAction = gameState.playersTakingOffTurnAction.includes(myColor);\n\n if (takingTurn && gameState.currentTurnState == t_.Dice && !getPopupView()) {\n const canUseAlchemist = getCardViews().flatMap(cv => cv.cardData.allowableTurnStates).includes(t_.Dice) && gameState.currentActionState == a_.None;\n \n if (canUseAlchemist) {\n selectNextCardAfterDelay(25);\n return; \n }\n }\n\n canvas.style.border = `2px solid ${takingTurn || takingOffTurnAction ? 'orange' : 'transparent'}`; \n };\n\n tradeController.rightExchangeController.updateBankRatios = () => {\n const that = tradeController.rightExchangeController;\n const e = gameState.myBankTradeRatios.getAllBankTradeRatios()\n , t = that.bankRatioContainer.width / e.length;\n e.sort((e,t)=>e.resource > t.resource ? 1 : -1);\n for (const [a,i] of e.entries()) {\n const e = new CardCountView(t * a,0,t,that.bankRatioContainer.height,that.bankRatioContainer,i.resource);\n e.updateText(i.tradeRatio + \":1\");\n e.view.setClickAction(() => that.exchangeView.cardContainer.addNewCardOfType(i.resource));\n that.cardViews.push(e);\n that.cardViewsWithBoldedText.includes(e.cardData.cardEnum) && e.boldText();\n if (i.tradeRatio < 4) {\n highlightBg(e, i.tradeRatio == 2 ? 0x00ff00 : 0xffff00);\n }\n }\n };\n\n const getEnhancedPopupInfoForDistribution = (e, forAqueduct = true) => {\n const t = e.selectCardFormat.amountOfCardsToSelect;\n const warnings = [];\n const reminders = [];\n\n for (let resource = c_.Lumber; resource <= c_.Ore; resource++) {\n let amt = getQuestionMarkAmountForCard(gameState.bank.totalResourceCardOfType(resource), hideBankCards);\n\n const myProduction = getNonGoldProductionForNumber(\n [myColor],\n gameState.lastDice1 + gameState.lastDice2,\n resource\n );\n\n const otherProduction = getNonGoldProductionForNumber(\n gameState.getOtherActivePlayerColors(),\n gameState.lastDice1 + gameState.lastDice2,\n resource\n );\n\n const totalProduction = t + otherProduction + (forAqueduct ? 0 : myProduction);\n\n if (amt == 0) {\n warnings.push(`⚠️ The bank has no ${c_[resource]} left!`);\n }\n else if (hideBankCards ? amt == '?' : amt <= 7) {\n if (totalProduction > 1 && totalProduction <= 7) {\n warnings.push(`⚠️ The bank is low on ${c_[resource]}.`);\n }\n else if (totalProduction > 8) {\n warnings.push(`⚠️ The bank will not have enough ${c_[resource]} for everyone!`);\n }\n }\n\n if (!forAqueduct && myProduction > 0 && amt != 0) {\n reminders.push(`βž• You will also produce ${myProduction} ${c_[resource]}.`);\n }\n }\n\n if (warnings.length) {\n warnings.unshift('\\n');\n }\n\n if (reminders.length) {\n reminders.unshift('\\n');\n }\n\n return {\n imageTextureName: forAqueduct ? \"icon_aqueduct\" : \"tile_gold\",\n title: `${warnings.length ? '⚠️ ' : ''}${forAqueduct ? \"Aqueduct\" : \"Gold Tile\"}`,\n body: \"Take \" + (1 == t ? t + \" resource\" : t + \" resources\") + \" from the bank\" + warnings.join('\\n') + reminders.join('\\n')\n }\n };\n\n if (isCK) {\n const _setNewView = popupController.setNewView.bind(popupController);\n\n popupController.setNewView = (e, t) => {\n if (e?.popupImageAndDescription?.constructor?.getPopupInformationForAqueductDistribution) {\n e.popupImageAndDescription.constructor.getPopupInformationForAqueductDistribution = e => getEnhancedPopupInfoForDistribution(e);\n e.popupImageAndDescription.constructor.getPopupInformationForGoldTileDistribution = e => getEnhancedPopupInfoForDistribution(e, false);\n }\n _setNewView(e, t);\n }\n }\n\n const _displayActionState = stateController.displayActionState.bind(stateController);\n\n stateController.displayActionState = e => {\n _displayActionState(e);\n\n const takingOffTurnAction = gameState.playersTakingOffTurnAction.includes(myColor);\n\n if (\n !takingOffTurnAction &&\n gameState.currentTurnPlayerColor == lastPlayer &&\n gameState.currentTurnState == lastTurnState &&\n gameState.currentActionState == lastActionState &&\n gameState.currentActionState != a_.SelectCityToPillage\n ) {\n return;\n }\n\n if ([\n a_.SelectCityToPillage,\n a_.Place1MoreRoadBuilding,\n a_.PlaceRobberOnly,\n a_.PlaceRobberOrPirate,\n a_.PlacePirateOnly,\n a_.MoveKnightOffTurn\n ].includes(gameState.currentActionState) && lastActionState) {\n selectNextAfterDelay();\n }\n\n if ([\n a_.SelectProgressCard,\n a_.SelectProgressCardsToDiscard,\n a_.SpySelectProgressCard\n ].includes(gameState.currentActionState) && lastActionState) {\n selectNextCardAfterDelay(25);\n }\n\n lastPlayer = gameState.currentTurnPlayerColor;\n lastTurnState = gameState.currentTurnState;\n lastActionState = gameState.currentActionState;\n };\n\n getQuestionMarkAmountForCard = (e, t) => t ? 0 == e ? \"0\" : e <= 7 ? \"?\" : e <= 13 ? \"??\" : \"???\" : String(e);\n\n bankController.update =_=> {\n const e = gameState.bank\n , t = gameState.gameSettings.hideBankCards\n , a = e.totalDevelopmentCards()\n , i = (0,\n getQuestionMarkAmountForCard)(a, t);\n bankController.developmentCardCountView.updateText(i);\n let shouldExpandBank = false;\n for (const a of bankController.resourceCardCountViews) {\n const i = e.totalResourceCardOfType(a.cardData.cardEnum);\n a.updateText((0,\n getQuestionMarkAmountForCard)(i, t))\n if (i <= 7) {\n highlightBg(a, i == 0 ? 1 : 0xff0000);\n if (i == 0 || a.cardData.cardEnum >= c_.Cloth) {\n shouldExpandBank = true;\n }\n }\n else {\n highlightBg(a);\n }\n }\n if (shouldExpandBank && !bankController.isBankInterfaceExpanded()) {\n bankController.expandBankInterface();\n }\n else {\n bankController.contractBankInterface();\n }\n };\n\n getMyCorners =_=> tileCorners.reduce((acc, tc, idx) => {\n if (tc.owner == myColor) {\n acc.push(idx);\n }\n return acc;\n }, []);\n\n getMyEdges =_=> tileEdges.reduce((acc, te, idx) => {\n if (te.owner == myColor) {\n acc.push(idx);\n }\n return acc;\n }, []);\n\n getNonGoldProductionForNumber = (players, n, resource) => {\n const myCorners = tileCorners.filter(tc => (!players || players.includes(tc.owner)) && tc.buildingType >= b_.Settlement && tc.buildingType <= b_.DestroyedCity);\n\n const commMap = { [c_.Wool]: c_.Cloth, [c_.Ore]: c_.Coin, [c_.Lumber]: c_.Paper };\n\n const production = tileFaces.reduce((cardEnums, t) => {\n if (t.diceNumber == n && t.tileType >= c_.Lumber && t.tileType <= c_.Ore && !robberController.lastRobberTile.sameTileHex(t)) {\n\n t.hexFace.corners().forEach(c => {\n const building = myCorners.find(({ hexCorner: hc }) => hc.sameCorner(c));\n\n if (!building) {\n return;\n }\n\n cardEnums.push(t.tileType);\n\n if (building.buildingType == b_.City) {\n cardEnums.push(isCK ? commMap[t.tileType] || t.tileType : t.tileType);\n }\n });\n }\n return cardEnums;\n }, []);\n\n return resource ? production.filter(res => res == resource).length : production;\n };\n\n openTrade = (withCommodities = false) => {\n const controller = mechanics.find(o => o.getTradeOffer);\n\n for (let c = c_.Lumber; c <= (withCommodities ? c_.Paper : c_.Ore); c++) {\n if (c == c_.Ore + 1) {\n c = c_.Cloth;\n }\n\n const offer = controller.getTradeOffer();\n const wanted = offer.wantedResources.cards;\n\n if (!wanted.includes(c) && tradeController.leftExchangeController.canAddResource(c)) {\n offer.offeredResources.addCards([c]);\n\n const offerToCounter = getLastCounterableOffer();\n\n if (offerToCounter) {\n socketGameSend.createdCounterOffer(offerToCounter.id, offer);\n }\n else {\n socketGameSend.createdPlayerOffer(offer);\n }\n }\n }\n };\n\n getLastCounterableOffer =_=> uiGameTradeInfoController.offers.find(({ offer, idx }) => (\n !offer.isCounterOffer &&\n offer.creator != myColor &&\n (!offerWasAcceptedByMe(offer) || remainingOffersAreUncounterable(idx))\n ))?.offer;\n\n counterOffer =_=> {\n const offer = getLastCounterableOffer();\n if (offer) {\n tradeController.editOffer(offer);\n }\n };\n\n clickCardInInventory = (card, cardViews) => {\n cardViews.some(cv => {\n if (cv.cardData.cardEnum == card) {\n return cv.view._events.click.fn() || true;\n }\n });\n };\n\n selectProgressCard = (reverse = false) => {\n const a = [...new Set((getPopupView()?.selectCardContainer?.cardsToSelectFrom || getPopupView().playerCardContainer).cardInventory.getAllCardEnums().filter(e => e > c_.Paper))];\n\n const b = [...a];\n b.unshift(b.pop());\n\n const nextCardMap = Object.assign(...b.map((k, i) => ({ [k]: a[i] })));\n const prevCardMap = Object.assign(...a.map((k, i) => ({ [k]: b[i] })));\n\n const selectFromInv = (getPopupView()?.selectCardContainer?.cardsToSelectFrom || getPopupView().playerCardContainer).cardInventory.cardViews.filter(({ cardData: c }) => c.cardEnum > c_.Paper);\n const selectedInv = (getPopupView()?.selectCardContainer?.cardsSelected || getPopupView().selectedCardContainer).cardInventory;\n\n let [selected] = selectedInv.getAllCardEnums();\n\n clickCardInInventory(selected ? (reverse ? prevCardMap[selected] : nextCardMap[selected]) : a.at(0 - reverse), selectFromInv);\n };\n\n getPopupView =_=> popupController?.currentView;\n\n getSelectedCard = getAlchemist => getCardViews().find(({ cardData: c }) =>\n c.cardEnum == (getAlchemist ? c_.ProgressCardScienceAlchemist : getPopupView()?.cardEnum));\n\n selectCardFromHand = resource => {\n try { tradeController.clickedTrade(); }\n catch (e) { console.error(e) }\n\n try { !tradeController.editingOffer && counterOffer(); }\n catch (e) { console.error(e) }\n\n try { getPopupView().playerCardContainer.cardClickAction(resource); }\n catch (e) { console.error(e) }\n\n try { getPopupView().selectCardView.clickedCardToSelectFrom(resource); }\n catch (e) { console.error(e) }\n\n tradeController.leftExchangeController.clickedResourceCard(resource);\n };\n\n selectCardsFromHand = (resource, max = false) => {\n try { tradeController.clickedTrade(); }\n catch (e) { console.error(e) }\n\n const numResources = tradeController.leftExchangeController.getAllActiveCards().filter(r => r == resource).length;\n const { tradeRatio } = gameState.myBankTradeRatios.bankTradeRatios.find(r => r.resource == resource);\n const numToAdd = Math.floor(numResources / tradeRatio) ** max * tradeRatio * (numResources >= tradeRatio);\n\n [...Array(numToAdd)].forEach(_ => {\n try { getPopupView().playerCardContainer.cardClickAction(resource); }\n catch (e) { console.error(e) }\n\n try { getPopupView().selectCardView.clickedCardToSelectFrom(resource); }\n catch (e) { console.error(e) }\n\n try { tradeController.leftExchangeController.clickedResourceCard(resource); }\n catch (e) { console.error(e) }\n });\n };\n\n selectCardFromElsewhere = resource => {\n try {\n if (getPopupView()?.clickedTopResource) {\n try { getPopupView()?.clickedTopResource(resource); }\n catch (e) { console.error(e) }\n }\n else {\n try { tradeController.clickedTrade(); }\n catch (e) { console.error(e) }\n\n try { if (!tradeController.editingOffer) { counterOffer(); } }\n catch (e) { console.error(e) }\n\n try { clickCardInInventory(resource, getPopupView().selectedCardContainer.cardInventory.cardViews); }\n catch (e) { console.error(e) }\n }\n }\n catch (e) { console.error(e) }\n\n tradeController.rightExchangeController.exchangeView.cardContainer.addNewCardOfType(resource);\n };\n\n offerWasAcceptedByMe = offer => {\n return uiGameTradeInfoController.tradeInfoContainer?.children?.some(c => {\n const playerIcon = c?.leftProposal?.children[2];\n const acceptedOfferFound = playerIcon?.children[1]?._texture?.textureCacheIds?.includes('icon_check');\n\n return acceptedOfferFound && offer.id == c?.offer?.id && playerIcon?.playerState?.color == myColor;\n });\n };\n\n remainingOffersAreUncounterable = idx => {\n const remainingOffers = uiGameTradeInfoController.offers.slice(idx + 1);\n return !remainingOffers.some(offer => !offer.isCounterOffer);\n };\n\n setSelectedHighlight = hl => selectedHL = hl || getHighlightViews().at(0);\n\n getSelectedHighlight =_=> {\n if (!getHighlightViews().includes(selectedHL)) {\n setSelectedHighlight();\n }\n\n return selectedHL;\n };\n\n getHighlightedPart =_=> {\n const cornersHighlighted = highlightController.corners.length;\n const edgesHighlighted = highlightController.roadEdges.length || highlightController.shipEdges.length;\n const tilesHighlighted = highlightController.tiles.length;\n\n const multiplePartTypesHighlighted = [ cornersHighlighted, edgesHighlighted, tilesHighlighted ].filter(Boolean).length > 1;\n\n if (multiplePartTypesHighlighted) {\n return 'Multiple';\n }\n else if (cornersHighlighted) {\n return 'Corner';\n }\n else if (edgesHighlighted) {\n return 'Edge';\n }\n else if (tilesHighlighted) {\n return 'Face';\n }\n };\n\n multiplePartTypesHighlighted =_=> {\n return getHighlightedPart() == 'Multiple';\n };\n\n xyzToHexPart = (x, y, z, part) => {\n return this[`tile${part}s`].find(({ [`hex${part}`]: p }) =>\n p.x == x && p.y == y && (part == 'Face' || p.z == z)\n )[`hex${part}`];\n };\n\n hexPartToPixels = ({ x, y, z }, p) => {\n try {\n const part = p || getHighlightedPart();\n const toPixel = part == 'Edge' ? 'centerPointToPixel' : 'toPixel';\n\n return xyzToHexPart(x, y, z, part)[toPixel](mapView.mapCenter, hexH / 2);\n }\n catch (e) { console.error(e) }\n };\n\n pixelsToHexPart = ({ x, y }, p) => {\n const part = p || getHighlightedPart();\n const toPixel = part == 'Edge' ? 'centerPointToPixel' : 'toPixel';\n const tolerance = part == 'Face' ? hexH / 3 : 10;\n\n return this[`tile${part}s`].find(({ [`hex${part}`]: hexPart }) => {\n const p = hexPart[toPixel](mapView.mapCenter, hexH / 2);\n return Math.abs(p.x - x) < tolerance && Math.abs(p.y - y) < tolerance;\n })[`hex${part}`];\n };\n\n getNextHighlightInArray = (direction = 'up') => {\n const hl = getSelectedHighlight();\n const reverse = ['down', 'left'].includes(direction);\n\n if (reverse) {\n return getHighlightViews().at(\n (getHighlightViews().indexOf(hl) + 1) % getHighlightViews().length\n );\n }\n else {\n const nextIdx = getHighlightViews().indexOf(hl) - 1;\n return getHighlightViews().at(nextIdx >= 0 ? nextIdx : getHighlightViews().length - 1);\n }\n };\n\n findHighlight = (coords, part) => {\n if (!coords) {\n return;\n }\n\n try {\n const { x, y } = coords;\n const tolerance = (part || getHighlightedPart()) == 'Face' ? hexH / 3 : 10;\n return getHighlightViews().find(({ center: c }) => Math.abs(c.x - x) < tolerance && Math.abs(c.y - y) < tolerance);\n }\n catch (e) { console.error(e) }\n };\n\n getHighlightInDirection = dir => {\n if (multiplePartTypesHighlighted()) {\n return getNextHighlightInArray(dir);\n }\n\n const hl = getSelectedHighlight();\n const hlPart = getHighlightedPart();\n const current = pixelsToHexPart(hl.center);\n\n const { x: currX, y: currY, z: currZ } = current;\n\n if (hlPart == 'Corner') {\n if (gameState.currentActionState == a_.PlaceKnight || getHighlightViews().length < tileCorners.filter(c => !c.restrictedStartingPlacement).length / 4) {\n return getNextHighlightInArray(dir);\n }\n\n this.upCorner = d => ({ x: currX + Math.floor((d + !currZ) / 2), y: currY - Math.floor((d + !currZ) / 2) * 2, z: (d + currZ) % 2 });\n this.downCorner = d => ({ x: currX - Math.floor((d + currZ) / 2), y: currY + Math.floor((d + currZ) / 2) * 2, z: (d + currZ) % 2 });\n this.rightCorner = d => ({ x: currX + Math.floor((d + !currZ) / 2), y: currY - (d % 2) * (1 - currZ * 2), z: (d + currZ) % 2 });\n this.leftCorner = d => ({ x: currX - Math.floor((d + currZ) / 2), y: currY - (d % 2) * (1 - currZ * 2), z: (d + currZ) % 2 });\n\n const maxDistance = Math.ceil(Math.sqrt(tileCorners.length) + 2);\n\n for (let distance = 1; distance <= maxDistance; distance++) {\n const nextHex = this[`${dir}Corner`](distance);\n\n const nextHL = findHighlight(hexPartToPixels(nextHex));\n if (nextHL) {\n return nextHL;\n }\n }\n\n return hl;\n }\n else if (hlPart == 'Edge') {\n return getNextHighlightInArray(dir);\n }\n else if (hlPart == 'Face') {\n if (getHighlightViews().length < tileFaces.length / 5) {\n return getNextHighlightInArray(dir);\n }\n\n this.upFace = d => ({ x: currX + d, y: currY - d });\n this.downFace = d => ({ x: currX - d, y: currY + d });\n this.rightFace = d => ({ x: currX + d, y: currY + 0 });\n this.leftFace = d => ({ x: currX - d, y: currY + 0 });\n\n const maxDistance = Math.ceil(Math.sqrt(tileFaces.length) + 2);\n\n for (let distance = 1; distance <= maxDistance; distance++) {\n const nextHex = this[`${dir}Face`](distance);\n\n const nextHL = findHighlight(hexPartToPixels(nextHex));\n if (nextHL) {\n return nextHL;\n }\n }\n\n const someDirectionWorks = [upFace, downFace, rightFace, leftFace].filter(f => f != this[`${dir}Face`]).some(face => {\n return Array.from(Array(maxDistance).keys()).some(distance => {\n return findHighlight(hexPartToPixels(face(distance + 1)));\n });\n });\n\n return someDirectionWorks ? hl : getNextHighlightInArray(dir);\n }\n };\n\n getHighlightViews =_=> highlightController.highlightViews;\n\n selectHighlight = hl => {\n if (!highlightController.highlightViews.length) {\n return;\n }\n\n const\n roadAndShipEdges = highlightController.shipEdges.filter(e => highlightController.roadEdges.includes(e)),\n allEdges = [...new Set([...highlightController.roadEdges, ...highlightController.shipEdges])],\n idx = getHighlightViews().indexOf(hl);\n\n let tileCornerWillBeHighlighted = false;\n let tileFaceWillBeHighlighted = false;\n\n try { tileCornerWillBeHighlighted = findHighlight(hexPartToPixels(pixelsToHexPart(hl.center, 'Corner'), 'Corner'), 'Corner') } catch (e) { console.error(e) }\n try { tileFaceWillBeHighlighted = findHighlight(hexPartToPixels(pixelsToHexPart(hl.center, 'Face'), 'Face'), 'Face') } catch (e) { console.error(e) }\n\n if (gameState.currentTurnState == t_.GameSetup || roadAndShipEdges.includes(allEdges[idx])) {\n hl._events.click.fn();\n }\n else if (multiplePartTypesHighlighted() ? tileFaceWillBeHighlighted : highlightController.tiles.length) {\n const\n face = pixelsToHexPart(hl.center, 'Face'),\n a = mapController.mapView.getTileLeftCenterCircle(face),\n i = 2.5 * a.radius,\n e = .7 * i * highlightController.buttonScaleForMobile,\n o = a.x - e / 2,\n n = a.y + i / 2;\n\n gameState.currentActionState == a_.SelectKnightAction\n ? highlightController.confirmActionController.showConfirmation(a.x - i / 2, a.y - i / 2, i, o, n, e, _ => hl._events.click.fn())\n : hl._events.click.fn();\n }\n else if (multiplePartTypesHighlighted() ? false : allEdges.length) {\n const\n edge = tileEdges.at(allEdges[idx]).hexEdge,\n a = edge.endPoints()[0].toPixel(mapView.mapCenter, hexH / 2),\n i = edge.endPoints()[1].toPixel(mapView.mapCenter, hexH / 2),\n n = Math.sqrt((a.x - i.x) ** 2 + (a.y - i.y) ** 2) / 2,\n o = (a.x + i.x) / 2 - n / 2,\n s = (a.y + i.y) / 2 - n / 2,\n e = 1.1 * n,\n r = (a.x + i.x) / 2 - e / 2,\n l = (a.y + i.y) / 2 + n / 2;\n\n highlightController.confirmActionController.showConfirmation(o, s, n, r, l, e, _ => {\n highlightController.confirmedEdgeSelection(allEdges[idx]);\n });\n\n const edgeIdx = tileEdges.findIndex(({ hexEdge }) => hexEdge == edge);\n const { roadEdges, shipEdges } = highlightController;\n const roadOnlyEdges = roadEdges.filter(e => !shipEdges.includes(e));\n const shipOnlyEdges = shipEdges.filter(e => !roadEdges.includes(e));\n\n if (roadOnlyEdges.includes(edgeIdx) && isSF) {\n highlightController.confirmActionController.whiteHighlightView._tintRGB += 5e2;\n }\n else if (shipOnlyEdges.includes(edgeIdx)) {\n highlightController.confirmActionController.whiteHighlightView._tintRGB -= 2e3;\n }\n }\n else if (multiplePartTypesHighlighted() ? tileCornerWillBeHighlighted : highlightController.corners.length) {\n const\n corner = tileCorners.at(highlightController.corners[idx]).hexCorner,\n a = corner.toPixel(mapView.mapCenter, hexH / 2),\n i = 1.2 * mapView.cornerCircleR,\n e = 1.1 * i * highlightController.buttonScaleForMobile,\n n = a.x - e / 2,\n o = a.y + i / 2;\n\n highlightController.confirmActionController.showConfirmation(a.x - i / 2, a.y - i / 2, i, n, o, e, ()=>{\n highlightController.confirmedCornerSelection(highlightController.corners[idx])\n });\n }\n\n setSelectedHighlight(hl);\n highlightController.highlightViews.pop();\n };\n\n deselectHighlight =_=> selectedHL = null;\n\n selectNextPlayer = (reverse = false) => {\n try {\n let playerAvatars, selectPlayer, firstPlayer, selectedPlayerIdx = 0 - reverse;\n\n const pv = getPopupView() || selectPlayerToStealController;\n\n if (selectPlayerToStealController?.isWrongState() == false) {\n selectPlayer = player => pv.selectPlayerAction(player);\n playerAvatars = pv.ui.playerAvatarContainer.playerAvatars;\n }\n else if (pv.selectPlayerAction) {\n selectPlayer = player => pv.selectPlayerAction(player);\n playerAvatars = (pv.selectPlayerView || pv).playerSelectorContainer.playerAvatars;\n }\n else {\n selectPlayer = player => pv.playerClickPlayerAvatar(player);\n playerAvatars = (pv.selectPlayerView || pv).playerSelectorContainer.playerAvatars;\n }\n\n selectedPlayer = playerAvatars.find((avatar, idx) => {\n if (idx == 0) {\n firstPlayer = avatar.playerState.color;\n }\n if (avatar.alpha == 1) {\n selectedPlayerIdx = idx;\n return true;\n }\n })?.playerState?.color;\n\n selectPlayer(\n playerAvatars.at((selectedPlayerIdx + 1 * (selectedPlayer && (reverse ? -1 : 1))) % playerAvatars.length).playerState.color\n );\n }\n catch (e) { console.error(e) }\n };\n\n selectNext = (direction = 'up') => {\n try {\n selectNextPlayer(['down', 'left'].includes(direction));\n }\n catch (e) { console.error(e) }\n\n try {\n if (gameState.currentActionState == a_.SelectTileProductionNumbersToSwap) {\n selectNextInventorTile(direction);\n return;\n }\n }\n catch (e) { console.error(e) }\n\n try {\n let currHL = getSelectedHighlight();\n let nextHL;\n\n if (currHL && !getHighlightViews().length) {\n return;\n }\n\n if (currHL) {\n nextHL = getHighlightInDirection(direction);\n }\n\n selectHighlight(nextHL || currHL || getHighlightViews()[0]);\n }\n catch (e) { console.error(e) }\n };\n\n confirmSelection =_=> {\n if (!selectPlayerToStealController.isWrongState()) {\n selectPlayerToStealController.checkAction();\n return;\n }\n\n const hl = getSelectedHighlight();\n\n if (hl) {\n try { \n highlightController.confirmActionController.confirmButton._events.click.fn();\n }\n catch (e) { console.error(e) }\n }\n };\n\n selectNextAfterDelay = (ms = 200) => setTimeout(_ => selectNext(), ms);\n\n selectNextPlayerAfterDelay = (ms = 150) => setTimeout(_ => selectNextPlayer(), ms);\n\n selectNextCardAfterDelay = (ms = 150) => setTimeout(_ => selectNextCard(), ms);\n\n selectNextDiceViewAfterDelay = (ms = 150) => setTimeout(_ => {\n if (getPopupView()?.bottomDiceContainer && !getPopupView().bottomDiceContainer.isDiceSelected()) {\n selectNextDiceView();\n }\n }, ms);\n\n closePopUps =_=> document.querySelector('.popup-notification .btn_general_check')?.click();\n\n getCardContainerView =_=> cardController.cardContainerView;\n\n getCardViews =_=> getCardContainerView().cardInventory.cardViews.slice(getCardContainerView().getPartitionCardIndex());\n\n canSelectCardNextAgain = currentCard => {\n const { cardEnum } = currentCard.cardData;\n const devCardVP = gameState.getPlayerWithColor(myColor).victoryPointState.totalVictoryPointsOfType([2]);\n const numCards = getCardViews().length;\n\n return (\n numCards > 1 &&\n numCards > devCardVP &&\n [...new Set(getCardContainerView().cardInventory.cardViews.reduce((acc, { cardData: c }) => { if (c.cardTypeEnum > 1 && ![c_.VictoryPoint, cardEnum].includes(c.cardEnum)) { acc.push(c.cardEnum) }; return acc; }, []))].length\n );\n };\n\n hasDevelopmentOrProgressCards =_=> getCardViews().find(({ cardData: c }) => c.cardTypeEnum > 1);\n\n selectNextInventorTile = dir => {\n let inventorTiles = highlightDiceController.diceProbabilityViewsBeingHighlighted;\n\n if (!selectedInventorTile) {\n selectedInventorTile = inventorTiles.at(0);\n }\n else {\n const { x: currX, y: currY } = selectedInventorTile.hexFace;\n\n inventorTiles = inventorTiles.filter(({ hexFace: f }) => !(f.x == currX && f.y == currY));\n\n this.upFace = d => ({ x: currX + d, y: currY - d });\n this.downFace = d => ({ x: currX - d, y: currY + d });\n this.rightFace = d => ({ x: currX + d, y: currY + 0 });\n this.leftFace = d => ({ x: currX - d, y: currY + 0 });\n\n const maxDistance = Math.ceil(Math.sqrt(tileFaces.length) + 2);\n\n for (let distance = 1; distance <= maxDistance; distance++) {\n const n = this[`${dir}Face`](distance);\n const nextInventorTile = inventorTiles.find(({ hexFace: f }) => f.x == n.x && f.y == n.y);\n if (nextInventorTile) {\n selectedInventorTile = nextInventorTile;\n break;\n }\n }\n }\n\n const\n face = selectedInventorTile.hexFace,\n a = mapController.mapView.getTileLeftCenterCircle(face),\n i = 2.5 * a.radius,\n e = .7 * i * highlightController.buttonScaleForMobile,\n o = a.x - e / 2,\n n = a.y + i / 2;\n\n highlightController.confirmActionController.showConfirmation(a.x - i, a.y - i / 3, i * 2, o, n - i, e, _ => selectedInventorTile.diceProbabilityView._events.click.fn());\n highlightController.confirmActionController.whiteHighlightView._alpha = 0.75;\n };\n\n selectNextDiceView = (reverse = false) => {\n try {\n if (!getPopupView()?.topDiceContainer || getPopupView()?.areBothDiceSelected()) {\n return;\n }\n\n let diceViews;\n let currentDiceView;\n\n if (!getPopupView().topDiceContainer.isDiceSelected()) {\n diceViews = getPopupView().topDiceContainer.diceViews;\n }\n else {\n diceViews = getPopupView().bottomDiceContainer.diceViews;\n }\n\n const currentDV = diceViews.find(v => v._alpha == 1);\n const selectedNum = currentDV?.diceNumber;\n\n const nextDV = diceViews.at(selectedNum ? (selectedNum - reverse * 2) % 6 : 0 - reverse);\n\n selectedDiceView?.inactiveOpacity();\n selectedDiceView = nextDV;\n selectedDiceView.activeOpacity();\n }\n catch (e) { console.error(e) }\n };\n\n selectNextCard = (reverse = false, multiple = 1) => {\n try {\n if (gameState.currentActionState == a_.SelectDiceForAlchemist) {\n selectNextDiceView(reverse);\n return;\n }\n }\n catch (e) { console.error(e) }\n\n try {\n selectProgressCard(reverse);\n return;\n }\n catch (e) { console.error(e) }\n\n try {\n if (!hasDevelopmentOrProgressCards()) {\n return;\n }\n\n const canUseAlchemist = gameState.currentTurnState == t_.Dice && getCardViews().flatMap(cv => cv.cardData.allowableTurnStates).includes(t_.Dice) && gameState.currentActionState == a_.None;\n\n let c = getSelectedCard(canUseAlchemist);\n let shouldSelectNext, nextCardEnum;\n\n if (!c) {\n c = getCardViews().at(0 - reverse);\n shouldSelectNext = c.cardData.cardEnum == c_.VictoryPoint;\n }\n else if (!canUseAlchemist) {\n const oldEnum = c.cardData.cardEnum;\n const idx = getCardViews().indexOf(c);\n c = getCardViews().at((idx + multiple * (1 - reverse * 2)) % getCardViews().length);\n shouldSelectNext = [oldEnum, c_.VictoryPoint, c_.ProgressCardScienceAlchemist].includes(c.cardData.cardEnum);\n }\n\n if (shouldSelectNext && canSelectCardNextAgain(c)) {\n selectNextCard(reverse, multiple + 1);\n return;\n }\n\n if (gameState.currentActionState == a_.SelectProgressCardsToDiscard) {\n getPopupView().playerCardContainer.cardInventory.cardViews.find(d => d.cardData.cardEnum == c.cardData.cardEnum).view._events.click.fn();\n }\n else if (c.isValidTurnState() && c.canClickCard()) {\n const { imageFileName: imageTextureName, tipTitle: title, popupBody: body, cardEnum } = c.cardData;\n const cardEvent = { cardEnum, popupInformation: { imageTextureName, title, body } };\n\n if ([c_.ProgressCardTradeMerchant, c_.ProgressCardTradeMerchantFleet].includes(cardEnum)) {\n const hasTwoForOnePorts = tileCorners.filter(tc => tc.owner == myColor && tc.portType > 1 && tc.buildingType >= b_.Settlement && tc.buildingType <= b_.DestroyedCity).length;\n\n if (hasTwoForOnePorts) {\n cardEvent.popupInformation.title = `⚠️ ${cardEvent.popupInformation.title}`;\n cardEvent.popupInformation.body += '\\n\\n⚠️ You own at least one 2:1 port.';\n }\n }\n else if ([c_.ProgressCardScienceIrrigation, c_.ProgressCardScienceMining].includes(cardEnum)) {\n const warnings = [];\n const resource = cardEnum == c_.ProgressCardScienceIrrigation ? c_.Grain : c_.Ore;\n const amt = getQuestionMarkAmountForCard(gameState.bank.totalResourceCardOfType(resource), hideBankCards);\n\n if (amt == 0) {\n warnings.push(`⚠️ The bank has no ${c_[resource]} left!`);\n }\n else if (hideBankCards ? amt == '?' : amt <= 7) {\n warnings.push(`⚠️ The bank is low on ${c_[resource]}.`);\n }\n\n if (warnings.length) {\n warnings.unshift('\\n');\n }\n\n cardEvent.popupInformation.title = `${warnings.length ? '⚠️ ': ''}${cardEvent.popupInformation.title}`;\n cardEvent.popupInformation.body += warnings.join('\\n');\n }\n else if (cardEnum == c_.ProgressCardScienceRoadBuilding && isSF) {\n const pirateAffectedEdges = tileFaces.find(t => t.tilePieceTypes == p_.Pirate).hexFace.corners().reduce((adjEdges, c) => {\n const edges = c.touchingEdges().filter(edge => !adjEdges.some(e => e.sameEdge(edge)));\n return [...edges, ...adjEdges];\n }, []);\n\n const myPirateAffectedEdges = tileEdges.filter(tc => tc.owner == myColor && tc.type == et_.Ship && pirateAffectedEdges.some(pc => tc.hexEdge.sameEdge(pc)));\n\n const pirateAffectedCorners = tileFaces.find(t => t.tilePieceTypes == p_.Pirate).hexFace.corners();\n\n const myPirateAffectedCorners = tileCorners.filter(tc => tc.owner == myColor && tc.buildingType >= 1 && tc.buildingType <= 3 && pirateAffectedCorners.some(pc => tc.hexCorner.sameCorner(pc)));\n\n if ([...myPirateAffectedEdges, ...myPirateAffectedCorners].length) {\n cardEvent.popupInformation.title = `⚠️ ${cardEvent.popupInformation.title}`;\n cardEvent.popupInformation.body += '\\n\\n⚠️ Nearby Pirate Ship may restrict ship placements.';\n }\n }\n\n socketGameSend.requestActionSwap(n_.ClickedDevelpomentCard);\n eventController.sendEvent(cardEvent, e_.ConfirmUseDevelopmentCard);\n }\n }\n catch (e) { console.error(e) }\n };\n\n accioEnabled = true;\n console.log('πŸ§™πŸ‘ ACCio enabled!');\n }, false, 1000));"}}],["3",{"name":"cityWallRandom.js","value":{"mdTime":1638883706807,"srcCode":"await ACCio(_ => getMyCorners().forEach(i => socketGameSend.confirmBuildCityWall(i)));"}}],["4",{"name":"city.js","value":{"mdTime":1641400221535,"srcCode":"await ACCio(_ => {\n socketGameSend.buildCity();\n selectNextAfterDelay();\n});"}}],["5",{"name":"ship.js","value":{"mdTime":1641431002339,"srcCode":"await ACCio(_ => {\n try {\n highlightController.confirmActionController.shipButton.action();\n return;\n }\n catch {}\n\n socketGameSend.buildShip();\n selectNextAfterDelay();\n});"}}],["6",{"name":"road.js","value":{"mdTime":1641430970391,"srcCode":"await ACCio(_ => {\n try {\n highlightController.confirmActionController.roadButton.action();\n return;\n }\n catch {}\n\n socketGameSend.buildRoad();\n selectNextAfterDelay();\n});"}}],["7",{"name":"settlement.js","value":{"mdTime":1641400194348,"srcCode":"await ACCio(_ => {\n socketGameSend.buildSettlement();\n selectNextAfterDelay();\n});"}}],["8",{"name":"cityRandom.js","value":{"mdTime":1638883422454,"srcCode":"await ACCio(_ => getMyCorners().forEach(i => socketGameSend.confirmBuildCity(i)));"}}],["9",{"name":"dev_ci.js","value":{"mdTime":1643079245372,"srcCode":"await ACCio(_ => {\n try { socketGameSend.buyDevCard(); }\n catch {}\n\n [0, 1, 2].forEach(i => socketGameSend.confirmCityUpgrade(i));\n\n selectNextAfterDelay(350);\n\n try { expandableButtons.forEach(btn => btn.hideButtons()); }\n catch {}\n});"}}],["10",{"name":"moveShip.js","value":{"mdTime":1641400250902,"srcCode":"await ACCio(_ => {\n socketGameSend.moveShip();\n selectNextAfterDelay();\n});"}}],["11",{"name":"moveShipRandom.js","value":{"mdTime":1638885054603,"srcCode":"await ACCio(_ => getMyEdges().forEach(i => socketGameSend.selectedShipToMove(i)));"}}],["12",{"name":"knight.js","value":{"mdTime":1641400258286,"srcCode":"await ACCio(_ => {\n socketGameSend.placeKnight();\n selectNextAfterDelay();\n});"}}],["13",{"name":"knightActivate.js","value":{"mdTime":1641400262568,"srcCode":"await ACCio(_ => {\n socketGameSend.activateKnight();\n selectNextAfterDelay();\n});"}}],["14",{"name":"knightActivateRandom.js","value":{"mdTime":1638885401691,"srcCode":"await ACCio(_ => getMyCorners().forEach(i => socketGameSend.confirmActivateKnight(i)));"}}],["15",{"name":"knightUpgrade.js","value":{"mdTime":1641400267074,"srcCode":"await ACCio(_ => {\n socketGameSend.upgradeKnight();\n selectNextAfterDelay();\n});"}}],["16",{"name":"knightUpgradeRandom.js","value":{"mdTime":1638885668955,"srcCode":"await ACCio(_ => getMyCorners().forEach(i => socketGameSend.confirmUpgradeKnight(i)));"}}],["17",{"name":"knightAction.js","value":{"mdTime":1641400277160,"srcCode":"await ACCio(_ => {\n socketGameSend.takeKnightAction();\n selectNextAfterDelay();\n});"}}],["18",{"name":"knightActionRandom.js","value":{"mdTime":1638885831922,"srcCode":"await ACCio(_ => getMyCorners().forEach(i => socketGameSend.confirmSelectKnightToTakeAction(i)));"}}],["19",{"name":"lumber.js","value":{"mdTime":1638886315826,"srcCode":"await ACCio(_ => selectCardFromHand(c_.Lumber));"}}],["20",{"name":"brick.js","value":{"mdTime":1638886360847,"srcCode":"await ACCio(_ => selectCardFromHand(c_.Brick));"}}],["21",{"name":"wool.js","value":{"mdTime":1638886395821,"srcCode":"await ACCio(_ => selectCardFromHand(c_.Wool));"}}],["22",{"name":"grain.js","value":{"mdTime":1638886412570,"srcCode":"await ACCio(_ => selectCardFromHand(c_.Grain));"}}],["23",{"name":"ore.js","value":{"mdTime":1638886425248,"srcCode":"await ACCio(_ => selectCardFromHand(c_.Ore));"}}],["24",{"name":"cloth.js","value":{"mdTime":1638886434463,"srcCode":"await ACCio(_ => selectCardFromHand(c_.Cloth));"}}],["25",{"name":"paper.js","value":{"mdTime":1638886445634,"srcCode":"await ACCio(_ => selectCardFromHand(c_.Paper));"}}],["26",{"name":"coin.js","value":{"mdTime":1638886478080,"srcCode":"await ACCio(_ => selectCardFromHand(c_.Coin));"}}],["27",{"name":"lumberOther.js","value":{"mdTime":1638886838021,"srcCode":"await ACCio(_ => selectCardFromElsewhere(c_.Lumber));"}}],["28",{"name":"brickOther.js","value":{"mdTime":1638886940054,"srcCode":"await ACCio(_ => selectCardFromElsewhere(c_.Brick));"}}],["29",{"name":"woolOther.js","value":{"mdTime":1638889800513,"srcCode":"await ACCio(_ => selectCardFromElsewhere(c_.Wool));"}}],["30",{"name":"grainOther.js","value":{"mdTime":1638889810527,"srcCode":"await ACCio(_ => selectCardFromElsewhere(c_.Grain));"}}],["31",{"name":"oreOther.js","value":{"mdTime":1638889842864,"srcCode":"await ACCio(_ => selectCardFromElsewhere(c_.Ore));"}}],["32",{"name":"clothOther.js","value":{"mdTime":1638889862038,"srcCode":"await ACCio(_ => selectCardFromElsewhere(c_.Cloth));"}}],["33",{"name":"coinOther.js","value":{"mdTime":1638889877528,"srcCode":"await ACCio(_ => selectCardFromElsewhere(c_.Coin));"}}],["34",{"name":"paperOther.js","value":{"mdTime":1638889889359,"srcCode":"await ACCio(_ => selectCardFromElsewhere(c_.Paper));"}}],["35",{"name":"tradeSubmit.js","value":{"mdTime":1638891729426,"srcCode":"await ACCio(_ => tradeController.clickedActionButton());"}}],["36",{"name":"tradeComplete.js","value":{"mdTime":1641750242798,"srcCode":"await ACCio(_ => { \n uiGameTradeInfoController.offers.some(o => {\n return o.view?.creatorProposal?.playerAvatars?.some?.(p => { \n const acceptedOfferFound = p?.children[1]?._texture?.textureCacheIds?.includes('icon_check');\n\n if (acceptedOfferFound) {\n socketGameSend.takeAcceptedOffer(o.offer.id, p.playerState.color);\n }\n \n return acceptedOfferFound;\n });\n });\n});"}}],["37",{"name":"tradeAgree.js","value":{"mdTime":1638891844419,"srcCode":"await ACCio(_ => {\n uiGameTradeInfoController.offers.some(({ offer }) => {\n if (!offer.isCounterOffer && !offerWasAcceptedByMe(offer)) {\n socketGameSend.acceptedOffer(offer.id);\n offer.updateResponse(myColor, 1);\n return true;\n }\n });\n});\n"}}],["38",{"name":"tradeReject.js","value":{"mdTime":1640833006776,"srcCode":"await ACCio(_ => {\n uiGameTradeInfoController.offers.some(({ offer }) => {\n if (!offer.isCounterOffer && !offerWasAcceptedByMe(offer)) {\n socketGameSend.rejectedOffer(offer.id);\n return true;\n }\n });\n});\n"}}],["39",{"name":"tradeCounterOffer.js","value":{"mdTime":1639109694127,"srcCode":"await ACCio(_ => {\n uiGameTradeInfoController.offers.some(({ offer, idx }) => {\n if (\n !offer.isCounterOffer\n && offer.creator != myColor\n && (!offerWasAcceptedByMe(offer) || remainingOffersAreUncounterable(idx))\n ) {\n tradeController.editOffer(offer);\n return true;\n }\n });\n});\n"}}],["40",{"name":"tradeClearAll.js","value":{"mdTime":1639177751199,"srcCode":"await ACCio(_ => {\n try { if (!tradeController.editingOffer) { counterOffer(); } }\n catch {}\n\n tradeController.resetTradeWindow();\n});"}}],["41",{"name":"tradeClearOffered.js","value":{"mdTime":1639177759580,"srcCode":"await ACCio(_ => {\n try { if (!tradeController.editingOffer) { counterOffer(); } }\n catch {}\n\n tradeController.leftExchangeController.updatePlayerCards();\n});"}}],["42",{"name":"tradeClearWanted.js","value":{"mdTime":1639177765751,"srcCode":"await ACCio(_ => {\n try { if (!tradeController.editingOffer) { counterOffer(); } }\n catch {}\n\n tradeController.rightExchangeController.updateAndShowUI();\n});"}}],["43",{"name":"cancel.js","value":{"mdTime":1641751089872,"srcCode":"await ACCio(_ => {\n try { socketGameSend.cancelAction(); }\n catch {}\n\n try { getPopupView().clickedCancelAction(); }\n catch {}\n\n try { tradeController.closeTradeUI(); }\n catch {}\n\n try { expandableButtons.forEach(btn => btn.hideButtons()); }\n catch {}\n\n try { closePopUps(); }\n catch {}\n});"}}],["44",{"name":"roll.js","value":{"mdTime":1638891360485,"srcCode":"await ACCio(_ => socketGameSend.clickedDice());"}}],["45",{"name":"endTurn.js","value":{"mdTime":1641435789407,"srcCode":"await ACCio(_ => {\n socketGameSend.clickedPassTurn();\n});\n"}}],["46",{"name":"surprise.js","value":{"mdTime":1639842899027,"srcCode":"await ACCio(_ => {\n const getWordiestMethodName = obj => {\n return Object.getOwnPropertyNames(Object.getPrototypeOf(obj)).sort((a, b) => b.length - a.length)[0];\n };\n\n const getPropHash = obj => {\n return hash(Object.getOwnPropertyNames(Object.getPrototypeOf(obj)).join(''));\n };\n\n const hash = str => {\n let hash = 0, i, chr;\n if (str.length === 0) {\n return hash;\n }\n for (i = 0; i < str.length; i++) {\n chr = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + chr;\n hash |= 0;\n }\n return hash;\n };\n\n const decrypt = (key, data) => {\n return Array.from(data, (c, i) => String.fromCharCode(c.charCodeAt() ^ key.charCodeAt(i % key.length))).join('').split(',');\n };\n\n const propHash = 689972192;\n const ciphertext = '1\\x07\\x1D\\x11\\x16*\\x12\\x16U|\\x05\\x02\\x16\\v-0\\n\\x17I=\\x1E\\x04\\n*C\\x11\\x06\\x10V\\x03\\x02\\x17N\\x01\\x11\\x1B\\x01\\x06D\\x02\"0\\x1D?M';\n\n const obj = mechanics.find(o => getPropHash(o) === propHash);\n\n if (!obj) {\n console.error('ACCio says: the surprise requires the Cities & Knights expansion!');\n return;\n }\n\n const key = getWordiestMethodName(obj);\n obj[key](...decrypt(key, ciphertext));\n});"}}],["47",{"name":"setupCheckWindow.js","value":{"mdTime":1638983152658,"srcCode":""}}],["48",{"name":"specialBuild.js","value":{"mdTime":1641108352273,"srcCode":"await ACCio(_ => {\n mechanics.find(o => o.toggleSpecialBuildPhase).clickedSpecialBuildPhaseButton();\n});"}}],["49",{"name":"initialize.js","value":{"mdTime":1639073885133,"srcCode":"ACCio = async _ => {\n await ACtl.runInPageCtx(_ => {\n console.error('ACCio called');\n });\n}"}}],["50",{"name":"selectRight.js","value":{"mdTime":1639612904254,"srcCode":"await ACCio(_ => selectNext('right'));"}}],["51",{"name":"selectLeft.js","value":{"mdTime":1639613001043,"srcCode":"await ACCio(_ => selectNext('left'));"}}],["52",{"name":"selectConfirm.js","value":{"mdTime":1643346254777,"srcCode":"await ACCio(_ => {\n const shouldSelectNext = getPopupView();\n\n try { getPopupView().clickedCheckAction(); }\n catch {}\n\n try { getPopupView().checkAction(); }\n catch {}\n\n try {\n getPopupView().selectCardView.confirmationButtons.checkButton.action();\n }\n catch {}\n \n confirmSelection();\n\n if (shouldSelectNext) {\n try {\n selectedDiceView?._events?.click?.fn();\n selectNextDiceViewAfterDelay();\n }\n catch {}\n\n selectNextAfterDelay();\n }\n\n try {\n highlightDiceController.confirmationPopup.confirmationButtons.checkButton.action();\n }\n catch {}\n\n try {\n if (!shouldSelectNext) {\n selectNextPlayerAfterDelay();\n }\n }\n catch {}\n\n closePopUps();\n});"}}],["53",{"name":"selectNextCard.js","value":{"mdTime":1639276833094,"srcCode":"await ACCio(_ => selectNextCard());"}}],["54",{"name":"selectPrevCard.js","value":{"mdTime":1639276857770,"srcCode":"await ACCio(_ => selectNextCard(true));"}}],["55",{"name":"selectUp.js","value":{"mdTime":1639613039482,"srcCode":"await ACCio(_ => selectNext('up'));"}}],["56",{"name":"selectDown.js","value":{"mdTime":1639613091176,"srcCode":"await ACCio(_ => selectNext('down'));"}}],["57",{"name":"tradeOpen.js","value":{"mdTime":1640827121476,"srcCode":"await ACCio(_ => openTrade());\n"}}],["58",{"name":"tradeOpenComms.js","value":{"mdTime":1640827352788,"srcCode":"await ACCio(_ => openTrade(true));\n"}}],["59",{"name":"tradeRejectAll.js","value":{"mdTime":1641435796202,"srcCode":"await ACCio(_ => {\n try {\n uiGameTradeInfoController.offers.forEach(({ offer }) => {\n socketGameSend.rejectedOffer(offer.id);\n });\n } catch {}\n});\n"}}],["60",{"name":"clothRatio.js","value":{"mdTime":1643048255027,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Cloth));"}}],["61",{"name":"togglePlayerInfo.js","value":{"mdTime":1642973242899,"srcCode":"ACCio(_ => {\n if (!isCK) {\n return;\n }\n\n if (!hoveredOverPlayerInformationArrow) {\n eventController.sendEvent(null, e_.HoveredOverPlayerInformationArrow);\n }\n else {\n eventController.sendEvent(null, e_.PointerOutPlayerInformationView);\n }\n\n hoveredOverPlayerInformationArrow ^= true;\n});"}}],["62",{"name":"lumberRatio.js","value":{"mdTime":1643047995499,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Lumber));"}}],["63",{"name":"brickRatio.js","value":{"mdTime":1643048010513,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Brick));"}}],["64",{"name":"woolRatio.js","value":{"mdTime":1643048073354,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Wool));"}}],["65",{"name":"grainRatio.js","value":{"mdTime":1643048134255,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Grain));"}}],["66",{"name":"oreRatio.js","value":{"mdTime":1643048168205,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Ore));"}}],["67",{"name":"coinRatio.js","value":{"mdTime":1643048304676,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Coin));"}}],["68",{"name":"paperRatio.js","value":{"mdTime":1643048416328,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Paper));"}}],["69",{"name":"lumberRatioMax.js","value":{"mdTime":1643074072330,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Lumber, true));"}}],["70",{"name":"brickRatioMax.js","value":{"mdTime":1643074372836,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Brick, true));"}}],["71",{"name":"woolRatioMax.js","value":{"mdTime":1643074405810,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Wool, true));"}}],["72",{"name":"grainRatioMax.js","value":{"mdTime":1643074436960,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Grain, true));"}}],["73",{"name":"oreRatioMax.js","value":{"mdTime":1643074466680,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Ore, true));"}}],["74",{"name":"clothRatioMax.js","value":{"mdTime":1643074493191,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Cloth, true));"}}],["75",{"name":"coinRatioMax.js","value":{"mdTime":1643074524005,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Coin, true));"}}],["76",{"name":"paperRatioMax.js","value":{"mdTime":1643074550805,"srcCode":"await ACCio(_ => selectCardsFromHand(c_.Paper, true));"}}],["77",{"name":"focusChat.js","value":{"mdTime":1643345509179,"srcCode":"ACCio(_ => document.querySelector('#game-chat-input').focus());"}}],["78",{"name":"focusGame.js","value":{"mdTime":1643345754188,"srcCode":"ACtl.runInPageCtx(_ => document.activeElement.blur());"}}]],"tse":[]},"mouseGest":{},"scrtEdtr":{"theme":"default ACtrl-dark"},"sections":[{"id":"2","name":"ACCio – Setup πŸ§™"},{"id":"1","name":"ACCio – BasicsΒ β€†πŸŽ²"},{"id":"3","name":"ACCio – Cards β€†πŸ‘"},{"id":"4","name":"ACCio – Trades πŸ’±"},{"id":"5","name":"ACCio – Selectβ€†β€†πŸ§©"}],"trigActList":[["64",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:19"}}],"targets":"currentTab"}],"sctnId":"3","title":"LUMBER: select a Lumber card from your hand.","triggers":[{"combins":[{"block":0,"eventId":529,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["56",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:27"}}],"targets":"currentTab"}],"sctnId":"3","title":"LUMBER (other): select a Lumber card from elsewhere (e.g. wanted cards, cards-to-discard, Aqueduct...)","triggers":[{"combins":[{"block":0,"eventId":529,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["41",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:62"}}],"targets":"currentTab"}],"sctnId":"3","title":"LUMBER (ratio): select a number of Lumber cards from your hand equal to your trade ratio (4, 3 or 2).","triggers":[{"combins":[{"block":0,"eventId":529,"preconds":[{"keyEvt":17},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["70",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:69"}}],"targets":"currentTab"}],"sctnId":"3","title":"LUMBER (max. ratio): select a number of Lumber cards equal to the highest multiple of your trade ratio.","triggers":[{"combins":[{"block":1,"eventId":529,"preconds":[{"keyEvt":17},{"keyEvt":18},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["63",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:20"}}],"targets":"currentTab"}],"sctnId":"3","title":"BRICK: select a Brick card from your hand.","triggers":[{"combins":[{"block":0,"eventId":530,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["55",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:28"}}],"targets":"currentTab"}],"sctnId":"3","title":"BRICK (other): select a Brick card from elsewhere.","triggers":[{"combins":[{"block":0,"eventId":530,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["42",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:63"}}],"targets":"currentTab"}],"sctnId":"3","title":"BRICK (ratio): select a number of Brick cards from your hand equal to your trade ratio (4, 3 or 2).","triggers":[{"combins":[{"block":0,"eventId":530,"preconds":[{"keyEvt":17},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["72",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:70"}}],"targets":"currentTab"}],"sctnId":"3","title":"BRICK (max. ratio): select a number of Brick cards equal to the highest multiple of your trade ratio.","triggers":[{"combins":[{"block":0,"eventId":530,"preconds":[{"keyEvt":17},{"keyEvt":18},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["62",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:21"}}],"targets":"currentTab"}],"sctnId":"3","title":"WOOL: select a Wool card from your hand. πŸ‘","triggers":[{"combins":[{"block":0,"eventId":531,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["54",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:29"}}],"targets":"currentTab"}],"sctnId":"3","title":"WOOL (other): select a Wool card from elsewhere. πŸ‘","triggers":[{"combins":[{"block":0,"eventId":531,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["43",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:64"}}],"targets":"currentTab"}],"sctnId":"3","title":"WOOL (ratio): select a number of Wool cards from your hand equal to your trade ratio (4, 3 or 2). πŸ‘","triggers":[{"combins":[{"block":0,"eventId":531,"preconds":[{"keyEvt":17},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["74",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:71"}}],"targets":"currentTab"}],"sctnId":"3","title":"WOOL (max. ratio): select a number of Wool cards equal to the highest multiple of your trade ratio.","triggers":[{"combins":[{"block":0,"eventId":531,"preconds":[{"keyEvt":17},{"keyEvt":18},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["61",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:22"}}],"targets":"currentTab"}],"sctnId":"3","title":"GRAIN: select a Grain card from your hand.","triggers":[{"combins":[{"block":0,"eventId":532,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["53",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:30"}}],"targets":"currentTab"}],"sctnId":"3","title":"GRAIN (other): select a Grain card from elsewhere.","triggers":[{"combins":[{"block":0,"eventId":532,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["65",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:65"}}],"targets":"currentTab"}],"sctnId":"3","title":"GRAIN (ratio): select a number of Grain cards from your hand equal to your trade ratio (4, 3 or 2).","triggers":[{"combins":[{"block":0,"eventId":532,"preconds":[{"keyEvt":17},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["75",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:72"}}],"targets":"currentTab"}],"sctnId":"3","title":"GRAIN (max. ratio): select a number of Grain cards equal to the highest multiple of your trade ratio.","triggers":[{"combins":[{"block":0,"eventId":532,"preconds":[{"keyEvt":17},{"keyEvt":18},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["60",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:23"}}],"targets":"currentTab"}],"sctnId":"3","title":"ORE: select an Ore card from your hand.","triggers":[{"combins":[{"block":0,"eventId":533,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["52",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:31"}}],"targets":"currentTab"}],"sctnId":"3","title":"ORE (other): select an Ore card from elsewhere.","triggers":[{"combins":[{"block":0,"eventId":533,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["66",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:66"}}],"targets":"currentTab"}],"sctnId":"3","title":"ORE (ratio): select a number of Ore cards from your hand equal to your trade ratio (4, 3 or 2).","triggers":[{"combins":[{"block":0,"eventId":533,"preconds":[{"keyEvt":17},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["76",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:73"}}],"targets":"currentTab"}],"sctnId":"3","title":"ORE (max. ratio): select a number of Ore cards equal to the highest multiple of your trade ratio.","triggers":[{"combins":[{"block":0,"eventId":533,"preconds":[{"keyEvt":17},{"keyEvt":18},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["59",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:24"}}],"targets":"currentTab"}],"sctnId":"3","title":"CLOTH: select a Cloth card from your hand.","triggers":[{"combins":[{"block":0,"eventId":534,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["51",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:32"}}],"targets":"currentTab"}],"sctnId":"3","title":"CLOTH (other): select a Cloth card from elsewhere.","triggers":[{"combins":[{"block":0,"eventId":534,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["67",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:60"}}],"targets":"currentTab"}],"sctnId":"3","title":"CLOTH (ratio): select a number of Cloth cards from your hand equal to your trade ratio (4, 3 or 2).","triggers":[{"combins":[{"block":0,"eventId":534,"preconds":[{"keyEvt":17},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["77",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:74"}}],"targets":"currentTab"}],"sctnId":"3","title":"CLOTH (max. ratio): select a number of Cloth cards equal to the highest multiple of your trade ratio.","triggers":[{"combins":[{"block":0,"eventId":534,"preconds":[{"keyEvt":17},{"keyEvt":18},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["58",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:26"}}],"targets":"currentTab"}],"sctnId":"3","title":"COIN: select a Coin card from your hand.","triggers":[{"combins":[{"block":0,"eventId":535,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["50",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:33"}}],"targets":"currentTab"}],"sctnId":"3","title":"COIN (other): select a Coin card from elsewhere.","triggers":[{"combins":[{"block":0,"eventId":535,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["68",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:67"}}],"targets":"currentTab"}],"sctnId":"3","title":"COIN (ratio): select a number of Coin cards from your hand equal to your trade ratio (4, 3 or 2).","triggers":[{"combins":[{"block":0,"eventId":535,"preconds":[{"keyEvt":17},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["78",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:75"}}],"targets":"currentTab"}],"sctnId":"3","title":"COIN (max. ratio): select a number of Coin cards equal to the highest multiple of your trade ratio.","triggers":[{"combins":[{"block":0,"eventId":535,"preconds":[{"keyEvt":17},{"keyEvt":18},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["57",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:25"}}],"targets":"currentTab"}],"sctnId":"3","title":"PAPER: select a Paper card from your hand.","triggers":[{"combins":[{"block":0,"eventId":536,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["49",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:34"}}],"targets":"currentTab"}],"sctnId":"3","title":"PAPER (other): select a Paper card from elsewhere.","triggers":[{"combins":[{"block":0,"eventId":536,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["69",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:68"}}],"targets":"currentTab"}],"sctnId":"3","title":"PAPER (ratio): select a number of Paper cards from your hand equal to your trade ratio (4, 3 or 2).","triggers":[{"combins":[{"block":0,"eventId":536,"preconds":[{"keyEvt":17},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["79",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:76"}}],"targets":"currentTab"}],"sctnId":"3","title":"PAPER (max. ratio): select a number of Paper cards equal to the highest multiple of your trade ratio.","triggers":[{"combins":[{"block":0,"eventId":536,"preconds":[{"keyEvt":17},{"keyEvt":18},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["71",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:77"}}],"targets":"currentTab"}],"sctnId":"1","title":"FOCUS CHAT: shifts focus from the game canvas to the text chat.","triggers":[{"combins":[{"block":1,"eventId":9,"preconds":[],"repCount":2,"wildcard":2}],"preconds":{"caretState":{"off":true},"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["73",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:78"}}],"targets":"currentTab"}],"sctnId":"1","title":"FOCUS GAME: shifts focus from text chat to the game canvas.","triggers":[{"combins":[{"block":1,"eventId":9,"preconds":[],"repCount":2,"wildcard":2}],"preconds":{"caretState":{"on":true},"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["45",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:44"}}],"targets":"currentTab"}],"sctnId":"1","title":"DICE ROLL: rolls the dice.","triggers":[{"combins":[{"block":0,"eventId":68,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["44",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:43"}}],"targets":"currentTab"}],"sctnId":"1","title":"CANCEL: cancels current action, closes your trade window and dismisses pop-ups.","triggers":[{"combins":[{"block":0,"eventId":27,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["46",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:43"}}],"targets":"currentTab"},{"sequence":[{"action":"runScript","params":{"scriptId":"script:59"}}],"targets":"currentTab"},{"sequence":[{"action":"runScript","params":{"scriptId":"script:45"}}],"targets":"currentTab"}],"sctnId":"1","title":"END TURN / REJECT OFFERS: ends your turn and/or rejects/cancels all trade offers.","triggers":[{"combins":[{"block":0,"eventId":190,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["2",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:6"}}],"targets":"currentTab"}],"sctnId":"1","title":"ROAD: build a Road at spot of your choice.","triggers":[{"combins":[{"block":0,"eventId":82,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["1",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:7"}}],"targets":"currentTab"}],"sctnId":"1","title":"SETTLEMENT: build a Settlement at a spot of your choice.","triggers":[{"combins":[{"block":0,"eventId":83,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["4",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:4"}}],"targets":"currentTab"}],"sctnId":"1","title":"CITY (choice): build a City at a spot of your choice.","triggers":[{"combins":[{"block":0,"eventId":67,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["5",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:4"}}],"targets":"currentTab"},{"sequence":[{"action":"runScript","params":{"scriptId":"script:8"}}],"targets":"currentTab"}],"sctnId":"1","title":"CITY (random): build and place a City at a randomly-determined spot.","triggers":[{"combins":[{"block":0,"eventId":67,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["6",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:9"}}],"targets":"currentTab"}],"sctnId":"1","title":"DEV CARD / CITY UPGRADE: buy a Development Card or all available City Upgrades.","triggers":[{"combins":[{"block":0,"eventId":86,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["14",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:48"}}],"targets":"currentTab"}],"sctnId":"1","title":"SPECIAL BUILD PHASE: request a Special Build Phase.","triggers":[{"combins":[{"block":0,"eventId":32,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["7",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:1"}}],"targets":"currentTab"}],"sctnId":"1","title":"CITY WALL (choice): build a City Wall upon a City of your choice.","triggers":[{"combins":[{"block":0,"eventId":87,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["8",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:1"}}],"targets":"currentTab"},{"sequence":[{"action":"runScript","params":{"scriptId":"script:3"}}],"targets":"currentTab"}],"sctnId":"1","title":"CITY WALL (random): build and place a City Wall upon a randomly-chosen City.","triggers":[{"combins":[{"block":0,"eventId":87,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["9",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:5"}}],"targets":"currentTab"}],"sctnId":"1","title":"SHIP: build a Ship at a spot of your choice.","triggers":[{"combins":[{"block":0,"eventId":69,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["10",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:10"}}],"targets":"currentTab"}],"sctnId":"1","title":"MOVE SHIP (choice): move an available Ship of your choice to a spot of your choice.","triggers":[{"combins":[{"block":0,"eventId":81,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["21",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:10"}}],"targets":"currentTab"},{"sequence":[{"action":"runScript","params":{"scriptId":"script:11"}}],"targets":"currentTab"}],"sctnId":"1","title":"MOVE SHIP (random): move a randomly-chosen available Ship to a spot of your choice.","triggers":[{"combins":[{"block":0,"eventId":81,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["11",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:12"}}],"targets":"currentTab"}],"sctnId":"1","title":"KNIGHT: build a Knight at a spot of your choice.","triggers":[{"combins":[{"block":0,"eventId":75,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["22",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:13"}}],"targets":"currentTab"}],"sctnId":"1","title":"ACTIVATE KNIGHT (choice): 'feed' a Knight of your choice.","triggers":[{"combins":[{"block":0,"eventId":70,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["23",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:13"}}],"targets":"currentTab"},{"sequence":[{"action":"runScript","params":{"scriptId":"script:14"}}],"targets":"currentTab"}],"sctnId":"1","title":"ACTIVATE KNIGHT (random): 'feed' a randomly-chosen Knight.","triggers":[{"combins":[{"block":0,"eventId":70,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["12",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:15"}}],"targets":"currentTab"}],"sctnId":"1","title":"UPGRADE KNIGHT (choice): upgrade a Knight of your choice.","triggers":[{"combins":[{"block":0,"eventId":85,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["24",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:15"}}],"targets":"currentTab"},{"sequence":[{"action":"runScript","params":{"scriptId":"script:16"}}],"targets":"currentTab"}],"sctnId":"1","title":"UPGRADE KNIGHT (random): upgrade a randomly-chosen Knight.","triggers":[{"combins":[{"block":0,"eventId":85,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["13",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:17"}}],"targets":"currentTab"}],"sctnId":"1","title":"KNIGHT ACTION (choice): take a Knight Action of your choice using a Knight of your choice.","triggers":[{"combins":[{"block":0,"eventId":65,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["25",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:17"}}],"targets":"currentTab"},{"sequence":[{"action":"runScript","params":{"scriptId":"script:18"}}],"targets":"currentTab"}],"sctnId":"1","title":"KNIGHT ACTION (random): take a Knight Action of your choice using a randomly-chosen Knight.","triggers":[{"combins":[{"block":0,"eventId":65,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["40",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:61"}}],"targets":"currentTab"}],"sctnId":"1","title":"C&K INFO TOGGLE: toggle player information view (e.g. to see progress card VP, defender VP)","triggers":[{"combins":[{"block":0,"eventId":73,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["47",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:46"}}],"targets":"currentTab"}],"sctnId":"1","title":"Surprise! ✨ (requires Cities & Knights)","triggers":[{"combins":[{"block":0,"eventId":65,"preconds":[],"wildcard":2},{"block":0,"eventId":67,"preconds":[],"wildcard":2},{"block":0,"eventId":67,"preconds":[],"wildcard":2},{"block":0,"eventId":73,"preconds":[],"wildcard":2},{"block":0,"eventId":79,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["48",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:2"}}],"targets":"eventTabs"}],"sctnId":"2","title":"Enables ACCio automatically. (If this doesn't work, try using the manual command below.)","triggers":[{"combins":[{"block":1,"eventId":2054,"preconds":[],"wildcard":1}],"preconds":{"evtUrl":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["31",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:2"}}],"targets":"currentTab"}],"sctnId":"2","title":"Enables ACCio manually. (If this doesn't work automatically, try refreshing your browser window.)","triggers":[{"combins":[{"block":0,"eventId":65,"preconds":[{"keyEvt":17},{"keyEvt":18},{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["28",{"title":"Welcome to ACCio! Check out the 'Basics' section to start!","triggers":[{"combins":[]}]}],["39",{"title":"Revision #110 – https://gist.github.com/Silvyre/7e73756f108dc987a0d12d609e615371","triggers":[{"combins":[],"preconds":{}}]}],["3",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:50"}}],"targets":"currentTab"}],"sctnId":"5","title":"SELECT RIGHT: select an item (tile/corner/edge/avatar) to the right of the currently-selected one.","triggers":[{"combins":[{"block":0,"eventId":524,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["29",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:51"}}],"targets":"currentTab"}],"sctnId":"5","title":"SELECT LEFT: select an item to the left of the currently-selected one.","triggers":[{"combins":[{"block":0,"eventId":522,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["34",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:55"}}],"targets":"currentTab"}],"sctnId":"5","title":"SELECT UP: select an item above the currently-selected one.","triggers":[{"combins":[{"block":0,"eventId":523,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["35",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:56"}}],"targets":"currentTab"}],"sctnId":"5","title":"SELECT DOWN: select an item below the currently-selected one.","triggers":[{"combins":[{"block":0,"eventId":525,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["32",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:53"}}],"targets":"currentTab"}],"sctnId":"5","title":"SELECT NEXT CARD: select the next Development/Progress Card (e.g. in your hand or a pop-up)","triggers":[{"combins":[{"block":0,"eventId":524,"preconds":[{"keyEvt":17}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["33",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:54"}}],"targets":"currentTab"}],"sctnId":"5","title":"SELECT PREVIOUS CARD: select the previous Development/Progress Card.","triggers":[{"combins":[{"block":0,"eventId":522,"preconds":[{"keyEvt":17}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["30",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:52"}}],"targets":"currentTab"}],"sctnId":"5","title":"CONFIRM SELECTION: confirm your selection (of any of the above, cards to discard, etc.)","triggers":[{"combins":[{"block":0,"eventId":516,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["27",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:35"}}],"targets":"currentTab"}],"sctnId":"4","title":"SUBMIT OFFER: submit your current trade offer.","triggers":[{"combins":[{"block":0,"eventId":516,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["36",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:57"}}],"targets":"currentTab"}],"sctnId":"4","title":"\"OPEN\" RESOURCE TRADE: submits your offer multiple times, each time adding a different Resource.","triggers":[{"combins":[{"block":0,"eventId":79,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["37",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:58"}}],"targets":"currentTab"}],"sctnId":"4","title":"\"OPEN\" COMMODITY TRADE: as above, but also uses your Commodities to create offers.","triggers":[{"combins":[{"block":0,"eventId":79,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["26",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:37"}}],"targets":"currentTab"}],"sctnId":"4","title":"AGREE TO OFFER: agree to the topmost unaccepted trade being offered to you.","triggers":[{"combins":[{"block":0,"eventId":516,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["20",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:39"}}],"targets":"currentTab"}],"sctnId":"4","title":"COUNTER-OFFER: start a counter-offer on the bottom-most unaccepted trade being offered to you.","triggers":[{"combins":[{"block":0,"eventId":191,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["19",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:38"}}],"targets":"currentTab"}],"sctnId":"4","title":"REJECT FIRST OFFER: rejects/rescinds the topmost (oldest) unaccepted trade offer.","triggers":[{"combins":[{"block":0,"eventId":220,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["38",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:59"}}],"targets":"currentTab"}],"sctnId":"4","title":"CLOSE ALL OFFERS: reject/rescind all trade offers.","triggers":[{"combins":[{"block":0,"eventId":220,"preconds":[{"keyEvt":16}],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["18",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:36"}}],"targets":"currentTab"}],"sctnId":"4","title":"COMPLETE TRADE: complete the top-leftmost agreed-upon trade offer.","triggers":[{"combins":[{"block":0,"eventId":187,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["17",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:40"}}],"targets":"currentTab"}],"sctnId":"4","title":"RESET TRADE WINDOW: clear all cards from both sides of your trade window.","triggers":[{"combins":[{"block":0,"eventId":8,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["16",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:41"}}],"targets":"currentTab"}],"sctnId":"4","title":"CLEAR OFFERED: clear all cards from the left side of your trade window.","triggers":[{"combins":[{"block":0,"eventId":219,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}],["15",{"actions":[{"sequence":[{"action":"runScript","params":{"scriptId":"script:42"}}],"targets":"currentTab"}],"sctnId":"4","title":"CLEAR WANTED: clear all cards from the right side of your trade window.","triggers":[{"combins":[{"block":0,"eventId":221,"preconds":[],"wildcard":2}],"preconds":{"urlTests":[{"part":1,"type":"equals","value":"colonist.io"}]}}]}]],"userVars":{}}
@Silvyre
Copy link
Author

Silvyre commented Dec 7, 2021

AutoControl for Colonist.io (ACCio) πŸ§™πŸ‘

Features

πŸ§™ Fully-customizable hotkeys for building, trading and more!

ACCio.interface.mp4

ACCio is powered by the AutoControl extension for Chrome, which allows for extremely versatile hotkey customization.

πŸƒ "Open-ended" trading!

Open.ended.trading.mp4

Easily create "open-ended" trade offers!

⚑ Build lightning fast!

Build.things.lightning.fast.with.ACCio.mp4

Click-free construction! Use your arrow keys to move game pieces!

πŸ’± Trading = Typing!

(Video preview coming soon!)

One-click counter-offers? Yes, please!

πŸ‘ Other features

  • Control all building and Development/Progress Cards using your keyboard.
  • Persistently toggle C&K player information views (e.g. to view everyone's Progress Card VP and Defender VP) without hovering.
  • Color-coded trade ratios and bank amounts.
  • Helpful reminders and warnings conditionally displayed on various pop-ups.
  • And much more!

Setup (Chrome on Windows)

(Note on system requirements: ACCio is only compatible with Chrome on Windows. If you use another operating system⁠—or would prefer to not use Chrome⁠—you may wish to try out my colonist_hotkeys.js script.)

  1. Add the AutoControl extension for Chrome.
  2. Open AutoControl's user interface, which can be accessed by clicking on the AutoControl extension's button within Chrome's Extensions menu, or by pasting this URL into your browser's address bar:
    chrome-extension://lkaihdpfpifdlgoapbfocpmekbokmcfd/main.html#options
  3. Within the "Options" page, under the "Synchronize, Save, Restore Settings" heading, click on the "Restore from File" button.
  4. Within the File Explorer window that pops up, instead of selecting a file, copy, paste and enter the following URL:
    https://gist.githubusercontent.com/Silvyre/7e73756f108dc987a0d12d609e615371/raw/ACCio.dat

At this point, ACCio's hotkeys should be usable after loading into any game.

AutoControl's documentation can be found here.


Notes

  • If any hotkeys don't work after entering a game, try using the 'manual setup' hotkey (Ctrl+Alt+Shift+A). If that doesn't work, refreshing should.
  • If the hotkeys suddenly stop working, try clicking on the game canvas. (This needs to be done after chatting, too.)
  • ACCio is a work-in-progress, and I'm open to feedback/suggestions/contributions (Discord: Silvyre#0561)

To-do

  • More promotional video clips (in-progress!)

Known Issues

  • Some hotkeys for accepting/confirming trades are not functioning perfectly. Likely an easy fix.
  • Commercial Harbour sometimes requires refreshing afterward to fix your hand. Also likely an easy fix.
  • C&K Aqueduct/Gold warning messages may not show up the first time after loading into a game. (This is due to the roundabout way the message text gets added.)

@AutoControl-app
Copy link

Hi there.
A few suggestions about your settings.

The setup action with the Tab load begins event should use the Event URL condition instead of the URL condition.
The URL condition applies to the focused tab, whereas the Event URL condition applies to the tab that triggered the Tab load begins event. The latter is what you want to achieve here.
image

Also, on the action side, you must apply the script to the Event tab. This way the script will be injected into the colonist.io tab whether it's the focused tab or not.

It's also preferable to add a wildcard to the Tab load begins event (as shown above). This way the trigger will fire the action even if some keys or buttons are being held down at the exact moment the Tab load begins event occurred.

It might also work better in your case to use the Tab load ends event instead of Tab load begins. That way your script will be injected when the page is fully loaded and all functions and variables in the page (that your script might depend on) are already initialized.

@Silvyre
Copy link
Author

Silvyre commented Jan 23, 2022

Thanks for the tips, @AutoControl-app! ❀️

@CrookedJ
Copy link

This is awesome, I love it a ton.

One weird thing I've noticed is the selecting, e.g. when building a road. The direction you press doesn't correspond with what you see, it's following a different path. Haven't looked at it myself, but possibly something they did changing the layout/code since it was written?

@Silvyre
Copy link
Author

Silvyre commented May 24, 2022

Hey @CrookedJ, glad to hear you're enjoying ACCio!

The hex edge selection logic I implemented isn't very smart: it just cycles through the (unordered) list of highlighted edges without first attempting to find the edge that best corresponds to the arrow key pressed:

getHighlightInDirection = dir => {
  if (multiplePartTypesHighlighted()) {
    return getNextHighlightInArray(dir);
  }

  const hlPart = getHighlightedPart();

  if (hlPart == 'Corner') {
    ... // sometimes smart, sometimes not smart
  }
  else if (hlPart == 'Edge') {
    return getNextHighlightInArray(dir); // not smart!
  }
  ... 
}

A better edge selection algorithm would search through the list of highlighted edges and find the closest edge in the direction pressed.

@CrookedJ
Copy link

CrookedJ commented Jun 29, 2022

After this week's update, using the . hotkey to end turn also prompts for a username change.
image

EDIT:
So I looked some more and did some testing. End turn actually does 3 things,

  1. (script43 - cancel.js) cancel actions/close trade ui/close popups
  2. (script59 - tradeRejectAll.js) reject trade offers
  3. (script45 - endTurn.js) end turn

cancel.js is the issue (same one that runs when hitting ESC) - pressing ESC also pops the username change popup

Hope that helps, thanks

EDIT 2:
Shift+Enter also makes it popup

but it doesn't ALWAYS popup. πŸ€·β€β™‚οΈ

@Silvyre
Copy link
Author

Silvyre commented Jul 3, 2022

@CrookedJ I made a change to fix this in revision #111. If you find any pop-ups that you are no longer able to close using ACCio as a result of this change, please let me know! Thank you!

@CrookedJ
Copy link

CrookedJ commented Jul 3, 2022

Will do, cheers mate!

@CrookedJ
Copy link

CrookedJ commented Jul 3, 2022

Found a bug - End of game scoreboard doesn't show up

EDIT: Well it did on another game so not sure

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment