├── .gitignore ├── LICENSE ├── README.md ├── bundles ├── bundle.properties ├── bundle_ru.properties └── bundle_uk_UA.properties ├── mod.json └── scripts ├── input ├── busy.js ├── conveyor.js └── core-drag.js ├── interact ├── auto-fill.js ├── auto-unit.js ├── interact-timer.js └── schematic-selector.js ├── main.js ├── other ├── extend-zoom.js └── mine.js ├── settings.js ├── ui ├── alerts │ ├── alert.js │ ├── losing-support.js │ └── under-attack.js ├── blocks │ ├── block-info-ui.js │ ├── efficiency.js │ └── progress-bar.js ├── other │ ├── bottom-panel-ui.js │ ├── power-ui.js │ ├── resource-rate-ui.js │ ├── schematics-table-ui.js │ └── settings-ui.js └── units │ ├── draw-cycle.js │ ├── health-shield-bar.js │ ├── logic-tracker.js │ ├── player-tracker.js │ └── units-table-ui.js ├── units ├── core-units.js ├── support-units.js └── units-counter.js └── utils ├── ai ├── adjacent-position.js └── pathfind.js ├── camera.js ├── difference.js ├── draw ├── bar-builder.js ├── build-plan.js └── draw-tasks.js ├── event ├── drag.js └── events.js ├── formatting.js ├── icons.js ├── iteration-tools.js ├── output-wrapper.js ├── polyfill.js ├── relative-value.js └── schematics.js /.gitignore: -------------------------------------------------------------------------------- 1 | TODO 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ferlern 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Extended UI 2 | 3 | Mod for game mindustry that extend your ui with some useful things. 4 | 5 | You can easily disable any of the following features in the settings -> graphics -> extended UI if you don't like it. 6 | Some features is disabled by default. It'll not work if u doesn't enable it first. 7 | 8 | (if you have low fps with a large number of units, turn off health bars). 9 | 10 | ## UI 11 | 12 | The main part of the modification. Works only on your client. Most of the features from here are enabled by default. 13 | 14 | ### Schematics table 15 | 16 | Fast access to your schematics. Splitted into categories (first row of buttons). 17 | 18 | Configurable — right click (double tap on mobile) 19 | 20 | 1. on label to set cathegory name. 21 | 2. on cathegory button (first row) to set their image. 22 | 3. on shematic button (all another) to set their image or shematic. 23 | 4. u can also change amount of rows/columns or change button size on mod settings. 24 | 25 | ![ucf-schematics](https://cdn.discordapp.com/attachments/606977691757051920/953003359235891263/unknown.png)![cf-schematics](https://cdn.discordapp.com/attachments/606977691757051920/953004472941027328/unknown.png) 26 | 27 | ### Units table 28 | 29 | Displays up to 6 unit types that the best 8 team on the map has, sorted by relative value. 30 | 31 | Also have some additional features: 32 | 33 | 1. Hover over unit sprite on table to see where's he and distance to. 34 | 2. Tap on unit sprite on table to track this type of unit. 35 | 36 | ![units-table](https://cdn.discordapp.com/attachments/606977691757051920/950541449554976788/unknown.png) 37 | 38 | ### Power bar 39 | 40 | Shows global power status. Also makes warning about net separating 41 | 42 | ![power-bar](https://cdn.discordapp.com/attachments/606977691757051920/950107054088015952/unknown.png) 43 | ![power-warning](https://cdn.discordapp.com/attachments/606977691757051920/950106865902182480/unknown.png) 44 | 45 | ### Testing (disabled by default) 46 | 47 | Shows buildings effectiveness for testing schemes (WIP) 48 | 49 | ![buildings-effectiveness](https://cdn.discordapp.com/attachments/606977691757051920/954434576644731000/unknown.png) 50 | 51 | Shows which processor controls units for testing logic 52 | 53 | ![logic-line](https://cdn.discordapp.com/attachments/606977691757051920/954039066305888326/unknown.png) 54 | 55 | ### Resource rate 56 | 57 | Shows how much resources you produce 58 | 59 | ![rate](https://cdn.discordapp.com/attachments/606977691757051920/989816706987876432/EpicPen_jJO6PgFNf1.png) 60 | 61 | ### Other 62 | 63 | Extends min and max zoom limits 64 | 65 | Saving schemes larger than 35x35. Works in observer mode 66 | 67 | Mine sand and other unmineble ores 68 | 69 | Factory progress bar 70 | 71 | ![progress-bar](https://cdn.discordapp.com/attachments/606977691757051920/951186180895023165/unknown.png) 72 | 73 | Unit health & shield bars (takes a lot of fps) 74 | 75 | ![health-shield-bar](https://cdn.discordapp.com/attachments/606977691757051920/951889454824579092/unknown.png) 76 | 77 | Shows additional information about enemy blocks 78 | 79 | ![enemy-resources](https://cdn.discordapp.com/attachments/606977691757051920/953751760273543238/unknown.png) 80 | ![enemy-power](https://cdn.discordapp.com/attachments/606977691757051920/953751888044625991/unknown.png) 81 | 82 | Tracks other players cursor 83 | 84 | ![enemy-cursor](https://cdn.discordapp.com/attachments/606977691757051920/954038645420068934/unknown.png) 85 | 86 | Better conveyor/juction pathfind 87 | 88 | ![pathfind](https://cdn.discordapp.com/attachments/606977691757051920/989817162749317130/EpicPen_DsuGQN1oXs.gif) 89 | 90 | ## Interact 91 | 92 | An additional part of the modification. Interacts with the game's API automatically (so you **may be banned** for using them, be careful) 93 | 94 | All interactions use a general timer, configurable in the settings (500ms by default). Values ​​below **250ms** will often lead to temporary blocking from servers, so I don't recommend it. 95 | 96 | Completely disabled by default. 97 | 98 | ### Auto fill bildings 99 | 100 | Automatically puts resources from your drone into buildings that: 101 | 102 | 1. Can accept them 103 | 2. Can use them (for example, generators, but not containers) 104 | 105 | If `Allow interact with core automatically` is enabled: 106 | 107 | 1. Takes resources from cores if nearby buildings need them 108 | 2. Puts resources back to core if they don't fit 109 | 110 | Everything is quite simple: for generators, the first suitable resource is selected (for example, coal for combustion), for turrets — best ammo, for everything else all's clear 111 | 112 | ![0ms-abuse](https://cdn.discordapp.com/attachments/606977691757051920/961997293022744616/0ms_abuse.gif "0ms abuse") 113 | -------------------------------------------------------------------------------- /bundles/bundle.properties: -------------------------------------------------------------------------------- 1 | eui.name = Extended UI 2 | eui.settings = Extended UI settings 3 | eui.load-error = Extended UI: cant load {0} \nPlease report this bug on GitHub 4 | 5 | setting.eui-showPowerBar.name = Show power bar 6 | setting.eui-showFactoryProgress.name = Show factory progress bar 7 | setting.eui-showUnitBar.name = Show unit health and shield bars 8 | setting.eui-ShowUnitTable.name = Show unit table 9 | setting.eui-ShowSchematicsTable.name = Show schematics table 10 | setting.eui-ShowSchematicsPreview.name = Show schematic preview 11 | setting.eui-ShowBlockInfo.name = Show block info 12 | setting.eui-ShowAlerts.name = Show alerts 13 | setting.eui-ShowAlertsBottom.name = Show alerts at the bottom of the screen 14 | setting.eui-ShowResourceRate.name = Show resource rate 15 | setting.eui-SchematicsTableRows.name = Schematics table rows 16 | setting.eui-SchematicsTableColumns.name = Schematics table columns 17 | setting.eui-SchematicsTableButtonSize.name = Schematic buttons size 18 | setting.eui-ShowEfficiency.name = Show build efficiency (restart required due to possible bugs) 19 | setting.eui-EfficiencyTimer.name = Amount of seconds to reset efficiency 20 | setting.eui-TrackPlayerCursor.name = Track player cursor 21 | setting.eui-playerCursorStyle.name = Player cursor Style 22 | setting.eui-ShowOwnCursor.name = Show own cursor 23 | setting.eui-TrackLogicControl.name = Track logic control 24 | setting.eui-maxZoom.name = Zoom limit 25 | setting.eui-makeMineble.name = Sand mining 26 | setting.eui-action-delay.name = Delay between any automatic actions 27 | setting.eui-showInteractSettings.name = Show auto interaction settings 28 | setting.eui-DragBlock.name = Drag core to create vault 29 | setting.eui-DragPathfind.name = Drag conveyor to start pathfinding 30 | 31 | block-info.power = Power 32 | block-info.stored = Stored 33 | 34 | schematics-table.dialog.change-cathegory-name.title = New category name 35 | schematics-table.dialog.edit-schematic-button.title = Select image and input schematic name 36 | schematics-table.dialog.change-image.title = Select image 37 | schematics-table.dialog.change-image.setted-announce-text = Setted as 38 | schematics-table.dialog.change-schematic.title = Insert schematic name 39 | schematics-table.default-cathegory-tooltip = Category name not set yet 40 | schematics-table.default-cathegory-desktop-name = Right click to set 41 | schematics-table.default-cathegory-mobile-name = Double tap on pencil to set name AND image 42 | schematics-table.use-schematic = Use 43 | 44 | units-table.button.hide.tooltip = Show or hide units table 45 | units-table.button.core-units.tooltip = Hide core defender 46 | units-table.button.support-units.tooltip = Hide support units 47 | 48 | alerts.losing-support = You are losing support units 49 | alerts.small-attack = You are under small attack 50 | alerts.medium-attack = You are under attack 51 | alerts.large-attack = You are under large attack 52 | alerts.massive-attack = You are under massive attack 53 | 54 | interaction-settings.button.core.tooltip = Allow interact with core automatically 55 | interaction-settings.button.auto-fill.tooltip = Auto fill buildings 56 | interaction-settings.button.auto-unit.tooltip = Control specified unit when it spawns 57 | interaction-settings.button.schem-selection.tooltip = Create scheme by area selection 58 | -------------------------------------------------------------------------------- /bundles/bundle_ru.properties: -------------------------------------------------------------------------------- 1 | eui.name = Extended UI 2 | eui.settings = Настройки Extended UI 3 | eui.load-error = Extended UI: ошибка при загрузке {0} \nПожалуйста, сообщите об ошибке на GitHub 4 | 5 | setting.eui-showPowerBar.name = Отображать энергию 6 | setting.eui-showFactoryProgress.name = Отображать прогресс фабрик 7 | setting.eui-showUnitBar.name = Отображать полоски здоровья и щитов 8 | setting.eui-ShowUnitTable.name = Отображать таблицу юнитов 9 | setting.eui-ShowSchematicsTable.name = Отображать таблицу схем 10 | setting.eui-ShowSchematicsPreview.name = Отображать превью схем 11 | setting.eui-ShowBlockInfo.name = Отображать информацию о блоках 12 | setting.eui-ShowAlerts.name = Отображать оповещения 13 | setting.eui-ShowAlertsBottom.name = Отображать оповещения в нижней части экрана 14 | setting.eui-ShowResourceRate.name = Отображать прирост ресурсов 15 | setting.eui-SchematicsTableRows.name = Количество рядов в таблице схем 16 | setting.eui-SchematicsTableColumns.name = Количество столбцов в таблице схем 17 | setting.eui-SchematicsTableButtonSize.name = Размер кнопок в таблице схем 18 | setting.eui-ShowEfficiency.name = Отображать эффективность блоков (необходим перезапуск из-за возможных багов) 19 | setting.eui-EfficiencyTimer.name = Таймер подсчёта эффективности 20 | setting.eui-TrackPlayerCursor.name = Отображать курсор других игроков 21 | setting.eui-playerCursorStyle.name = Стиль курсоров игроков 22 | setting.eui-ShowOwnCursor.name = Отображать свой курсор 23 | setting.eui-TrackLogicControl.name = Рисовать линии от процессора к контролируемым юнитам 24 | setting.eui-maxZoom.name = Максимальный зум 25 | setting.eui-makeMineble.name = Добыча песка 26 | setting.eui-action-delay.name = Задержка между любыми автоматическими действиями 27 | setting.eui-showInteractSettings.name = Отображать настройки автоматических действий 28 | setting.eui-DragBlock.name = Создавать хранилища при перетаскивании ядра 29 | setting.eui-DragPathfind.name = Использовать поиск пути при перетаскивании конвейеров 30 | 31 | block-info.power = Энергия 32 | block-info.stored = Накоплено 33 | 34 | schematics-table.dialog.change-cathegory-name.title = Новое название вкладки 35 | schematics-table.dialog.edit-schematic-button.title = Выберите изображение и введите название схемы 36 | schematics-table.dialog.change-image.title = Выберите изображение 37 | schematics-table.dialog.change-image.setted-announce-text = Установлено как 38 | schematics-table.dialog.change-schematic.title = Введите название схемы 39 | schematics-table.default-cathegory-tooltip = Название ещё не установлено 40 | schematics-table.default-cathegory-desktop-name = Щелкните правой кнопкой мыши 41 | schematics-table.default-cathegory-mobile-name = Двойное нажатие на карандаш для настройки 42 | schematics-table.use-schematic = Использовать 43 | 44 | units-table.button.hide.tooltip = Скрыть таблицу 45 | units-table.button.core-units.tooltip = Скрыть защитников ядра 46 | units-table.button.support-units.tooltip = Скрыть юнитов поддержки 47 | 48 | alerts.losing-support = Вы теряете юнитов поддержки 49 | alerts.small-attack = Вы под небольшой атакой 50 | alerts.medium-attack = Вы под атакой 51 | alerts.large-attack = Вы под сильной атакой 52 | alerts.massive-attack = Вы под массированной атакой 53 | 54 | interaction-settings.button.core.tooltip = Разрешить взаимодействовать с ядром автоматически 55 | interaction-settings.button.auto-fill.tooltip = Автоматически заряжать постройки ресурсами 56 | interaction-settings.button.auto-unit.tooltip = Брать контроль над указанным юнитом 57 | interaction-settings.button.schem-selection.tooltip = Создать схему выбором области 58 | -------------------------------------------------------------------------------- /bundles/bundle_uk_UA.properties: -------------------------------------------------------------------------------- 1 | eui.name = Extended UI 2 | eui.settings = Налаштування Extended UI 3 | eui.load-error = Extended UI: виникла помилка {0} \nПовідомте про неї на GitHub 4 | 5 | setting.eui-showPowerBar.name = Показувати індикатор енергії 6 | setting.eui-showFactoryProgress.name = Показувати прогрес будування одиниць 7 | setting.eui-showUnitBar.name = Показувати здоров'я та щит одиниць 8 | setting.eui-ShowUnitTable.name = Показувати кількість одиниць 9 | setting.eui-ShowSchematicsTable.name = Показувати таблицю схем 10 | setting.eui-ShowSchematicsPreview.name = Показувати поперд. перегляд схем 11 | setting.eui-ShowBlockInfo.name = Показувати інформацію про блок 12 | setting.eui-ShowAlerts.name = Показувати попередження 13 | setting.eui-ShowAlertsBottom.name = Показувати попередження у нижній частині екрану 14 | setting.eui-ShowResourceRate.name = Показувати зміну кількості ресурсів 15 | setting.eui-SchematicsTableRows.name = Кількість рядків таблиці схем 16 | setting.eui-SchematicsTableColumns.name = Кількість стовпців таблиці схем 17 | setting.eui-SchematicsTableButtonSize.name = Розмір кнопок таблиці схем 18 | setting.eui-ShowEfficiency.name = Показувати ефективність (потрібен перезапуск) 19 | setting.eui-EfficiencyTimer.name = Кількість секунд перед скиданням ефективності 20 | setting.eui-TrackPlayerCursor.name = Відстежувати курсор гравців 21 | setting.eui-playerCursorStyle.name = Стиль курсорів гравців 22 | setting.eui-ShowOwnCursor.name = Відстежувати свій курсор 23 | setting.eui-TrackLogicControl.name = Відстежувати контроль процесорів 24 | setting.eui-maxZoom.name = Обмеження масштабу 25 | setting.eui-makeMineble.name = Видобувати пісок 26 | setting.eui-action-delay.name = Затримка між автоматичними діями 27 | setting.eui-showInteractSettings.name = Показати налаштування автоматичної взаємодії 28 | setting.eui-DragBlock.name = Будувати склади при перетягуванні ядра 29 | setting.eui-DragPathfind.name = Перетягніть конвеєр, щоби почати пошук шляху 30 | 31 | block-info.power = Енергія 32 | block-info.stored = Заряд 33 | 34 | schematics-table.dialog.change-cathegory-name.title = Назва нової категорії 35 | schematics-table.dialog.edit-schematic-button.title = Виберіть значок та введіть назву схеми 36 | schematics-table.dialog.change-image.title = Виберіть значок 37 | schematics-table.dialog.change-image.setted-announce-text = Вибрано 38 | schematics-table.dialog.change-schematic.title = Уведіть назву схеми 39 | schematics-table.default-cathegory-tooltip = Назва категорії не встановлена 40 | schematics-table.default-cathegory-desktop-name = Натисніть ПКМ, щоб установити 41 | schematics-table.default-cathegory-mobile-name = Зробіть коротке натискання олівця двічі, щоб установити назву ТА значок 42 | schematics-table.use-schematic = Використати 43 | 44 | units-table.button.hide.tooltip = Таблиця схем 45 | units-table.button.core-units.tooltip = Приховати захисників ядра 46 | units-table.button.support-units.tooltip = Приховати одиниці підтримки 47 | 48 | alerts.losing-support = Ви втрачаєте одиниці підтримки 49 | alerts.small-attack = Ви піддаєтеся невеликій атаці 50 | alerts.medium-attack = Вас атакують 51 | alerts.large-attack = Ви зазнаєте великої атаки 52 | alerts.massive-attack = Ви зазнаєте масованої атаки 53 | 54 | interaction-settings.button.core.tooltip = Дозволити автоматично взаємодіяти з ядром 55 | interaction-settings.button.auto-fill.tooltip = Автоматично заповнювати блоки 56 | interaction-settings.button.auto-unit.tooltip = Керувати вказаною одиницею, коли вона з'явиться 57 | interaction-settings.button.schem-selection.tooltip = Створити схему вибором області 58 | -------------------------------------------------------------------------------- /mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "extended-ui", 3 | "displayName": "[orange]extended[gold] UI", 4 | "author": "wizer", 5 | "description": "UNSTABLE DEVELOPMENT VERSION. Extend your ui with some useful things.\n\nYou can easily disable any of the mod features in the settings -> graphics -> extended UI.\n\nIf u have any bugs or ideas create an issue on github or contact me in discord: WiZeR1233#9336", 6 | "version": "0.74", 7 | "minGameVersion": "120", 8 | "hidden": true 9 | } 10 | -------------------------------------------------------------------------------- /scripts/input/busy.js: -------------------------------------------------------------------------------- 1 | exports.isBusy = function() { 2 | return Vars.ui.chatfrag.shown() || Vars.ui.schematics.isShown() || Vars.control.input.isUsingSchematic() || Vars.control.input.selectedBlock(); 3 | } 4 | -------------------------------------------------------------------------------- /scripts/input/conveyor.js: -------------------------------------------------------------------------------- 1 | const euiEvents = require("extended-ui/utils/event/events"); 2 | const drawPlans = require("extended-ui/utils/draw/build-plan"); 3 | const adjacentPosition = require("extended-ui/utils/ai/adjacent-position"); 4 | const pathfind = require("extended-ui/utils/ai/pathfind"); 5 | const busy = require("extended-ui/input/busy"); 6 | 7 | const pathfindSelector = (block) => { 8 | if (block == Blocks.conveyor || 9 | block == Blocks.titaniumConveyor) return pathfind.conveyorPathfind; 10 | if (block == Blocks.junction) return pathfind.junctionPathfind; 11 | if (block == Blocks.underflowGate) return pathfind.gatePathfind; 12 | } 13 | 14 | let isListen = false; 15 | let buildPlans = []; 16 | let lastStartTile = null; 17 | let lastMouseTile = null; 18 | 19 | Events.run(Trigger.draw, () => { 20 | if (buildPlans) { 21 | drawPlans.draw(buildPlans); 22 | } 23 | }); 24 | 25 | const listener = (startPos, startTile, pos, mouseTile) => { 26 | if (Core.input.keyTap(Packages.arc.input.KeyCode.mouseRight)) { 27 | buildPlans = []; 28 | endListen(); 29 | return; 30 | } 31 | if (startTile == lastStartTile && mouseTile == lastMouseTile) return; 32 | if (startTile == mouseTile) { 33 | buildPlans = []; 34 | return; 35 | } 36 | 37 | lastStartTile = startTile; 38 | lastMouseTile = mouseTile; 39 | const destination = adjacentPosition.find(mouseTile, startTile, Blocks.copperWall); 40 | if (!destination) return; 41 | 42 | const startBlock = startTile.block(); 43 | buildPlans = pathfindSelector(startBlock)(startTile, destination, mouseTile, startBlock); 44 | } 45 | 46 | euiEvents.on(euiEvents.eventType.dragStarted, (startPos, startTile) => { 47 | if (startTile && pathfindSelector(startTile.block()) && !busy.isBusy() && !isListen) { 48 | startListen(); 49 | } 50 | }); 51 | 52 | euiEvents.on(euiEvents.eventType.dragEnded, () => { 53 | if (isListen) endListen(); 54 | }); 55 | 56 | function startListen() { 57 | if (Core.settings.getBool("eui-DragPathfind", false)) { 58 | isListen = true; 59 | euiEvents.on(euiEvents.eventType.dragged, listener); 60 | } 61 | } 62 | 63 | function endListen() { 64 | if (buildPlans) { 65 | buildPlans.forEach(plan => Vars.player.unit().addBuild(plan)); 66 | buildPlans = []; 67 | } 68 | isListen = false; 69 | euiEvents.removeListener(euiEvents.eventType.dragged, listener); 70 | } 71 | -------------------------------------------------------------------------------- /scripts/input/core-drag.js: -------------------------------------------------------------------------------- 1 | const euiEvents = require("extended-ui/utils/event/events"); 2 | const drawPlans = require("extended-ui/utils/draw/build-plan"); 3 | const adjacentPosition = require("extended-ui/utils/ai/adjacent-position"); 4 | const busy = require("extended-ui/input/busy"); 5 | 6 | const targetBlock = Blocks.vault; 7 | let isListen = false; 8 | let buildPlan = null; 9 | 10 | Events.run(Trigger.draw, () => { 11 | if (buildPlan) { 12 | drawPlans.drawOne(buildPlan); 13 | } 14 | }); 15 | 16 | const listener = (startPos, startTile, pos, mouseTile) => { 17 | if (Core.input.keyTap(Packages.arc.input.KeyCode.mouseRight)) { 18 | buildPlan = null; 19 | endListen(); 20 | return; 21 | } 22 | 23 | if (mouseTile.block() instanceof CoreBlock) { 24 | buildPlan = null; 25 | return; 26 | } 27 | 28 | const position = adjacentPosition.find(startTile, mouseTile, targetBlock); 29 | if (!position) { 30 | buildPlan = null; 31 | return; 32 | } 33 | 34 | buildPlan = new BuildPlan(position.x, position.y, 0, targetBlock); 35 | } 36 | 37 | euiEvents.on(euiEvents.eventType.dragStarted, (startPos, startTile) => { 38 | if (startTile && startTile.block() instanceof CoreBlock && !busy.isBusy() &&!isListen) { 39 | startListen(); 40 | } 41 | }); 42 | 43 | euiEvents.on(euiEvents.eventType.dragEnded, () => { 44 | if (isListen) { 45 | endListen(); 46 | } 47 | }); 48 | 49 | function startListen() { 50 | if (Core.settings.getBool("eui-DragBlock", false)) { 51 | isListen = true; 52 | euiEvents.on(euiEvents.eventType.dragged, listener); 53 | } 54 | } 55 | 56 | function endListen() { 57 | if (buildPlan) { 58 | Vars.player.unit().addBuild(buildPlan); 59 | buildPlan = null; 60 | } 61 | isListen = false; 62 | euiEvents.removeListener(euiEvents.eventType.dragged, listener); 63 | } 64 | -------------------------------------------------------------------------------- /scripts/interact/auto-fill.js: -------------------------------------------------------------------------------- 1 | const timer = require("extended-ui/interact/interact-timer"); 2 | 3 | Events.run(Trigger.update, () => { 4 | if (!Core.settings.getBool("eui-auto-fill", false) || !timer.canInteract()) return; 5 | const player = Vars.player; 6 | const stack = player.unit().stack; 7 | const team = player.team(); 8 | const core = player.closestCore(); 9 | const isCoreAvailible = Core.settings.getBool("eui-interact-core", false) && core; 10 | 11 | let tranfered = false; 12 | let request = null; 13 | 14 | Vars.indexer.eachBlock(team, player.x, player.y, Vars.buildingRange, () => true, b => { 15 | if (!timer.canInteract()) return; 16 | 17 | const block = b.tile.block(); 18 | if (!block.consumes.has(ConsumeType.item)) return; 19 | 20 | if (b.acceptStack(stack.item, stack.amount, player.unit()) >= 5) { 21 | Call.transferInventory(player, b); 22 | timer.increase(); 23 | tranfered = true; 24 | } 25 | 26 | if (!isCoreAvailible || request) return; 27 | if (block instanceof ItemTurret) { 28 | if (!b.ammo.isEmpty()) return; 29 | request = getBestAmmo(block, core); 30 | } else if (block instanceof UnitFactory) { 31 | request = getUnitFactoryRequest(b, block, core); 32 | } else if (b.items) { 33 | request = getItemRequest(b, block, core); 34 | } 35 | }); 36 | if (!isCoreAvailible || tranfered || !request || !player.within(core, Vars.buildingRange)) return; 37 | 38 | if (stack.amount) { 39 | Call.transferInventory(player, core); 40 | } else { 41 | Call.requestItem(player, core, request, 999); 42 | } 43 | timer.increase(); 44 | }); 45 | 46 | function getBestAmmo(turret, core) { 47 | let best = null; 48 | let bestDamage = 0; 49 | turret.ammoTypes.each((item, ammo) => { 50 | let totalDamage = ammo.damage + ammo.splashDamage; 51 | if (totalDamage > bestDamage && core.items.get(item) >= 20) { 52 | best = item; 53 | bestDamage = totalDamage; 54 | } 55 | }); 56 | return best; 57 | } 58 | 59 | function getUnitFactoryRequest(build, block, core) { 60 | if (build.currentPlan == -1) return null; 61 | const stacks = block.plans.get(build.currentPlan).requirements 62 | 63 | return findRequiredItem(stacks, build, core); 64 | } 65 | 66 | function getItemRequest(build, block, core) { 67 | const consumesItems = block.consumes.get(ConsumeType.item); 68 | if (!consumesItems) return null; 69 | 70 | if (consumesItems instanceof ConsumeItemFilter) { 71 | return getFilterRequest(consumesItems, build, core); 72 | } else if (consumesItems instanceof ConsumeItems) { 73 | return findRequiredItem(consumesItems.items, build, core); 74 | } else { 75 | return null; 76 | } 77 | } 78 | 79 | function getFilterRequest(filter, build, core) { 80 | const items = Vars.content.items(); 81 | const bits = new Bits; 82 | let bitIndex = 0; 83 | let item 84 | 85 | filter.applyItemFilter(bits); 86 | do { 87 | bitIndex = bits.nextSetBit(bitIndex); 88 | 89 | if (bitIndex == -1) break; 90 | // ignore blast compound; it will explode generators 91 | if (bitIndex == 14) { 92 | bitIndex++; 93 | continue; 94 | } 95 | 96 | item = items.get(bitIndex) 97 | if (core.items.get(item) >= 20) { 98 | if (build.acceptStack(item, 20, Vars.player.unit()) >= 5) { 99 | return item; 100 | } else { 101 | return null; 102 | } 103 | } 104 | bitIndex++; 105 | } while (bitIndex > 0); 106 | return null; 107 | } 108 | 109 | function findRequiredItem(stacks, build, core) { 110 | for (let itemStack of stacks) { 111 | let item = itemStack.item; 112 | if (core.items.get(item) >= 20 && build.acceptStack(item, 20, Vars.player.unit()) >= 5) { 113 | return item; 114 | } 115 | } 116 | return null; 117 | } 118 | -------------------------------------------------------------------------------- /scripts/interact/auto-unit.js: -------------------------------------------------------------------------------- 1 | const timer = require("extended-ui/interact/interact-timer"); 2 | const BreakException = {}; 3 | 4 | const tryTime = 300; 5 | 6 | let prevSelectedUnitType = null; 7 | let checkEndTime = 0; 8 | 9 | Events.run(Trigger.update, () => { 10 | const selectedUnitType = Core.settings.getString("eui-auto-unit"); 11 | 12 | if (selectedUnitType != prevSelectedUnitType) { 13 | prevSelectedUnitType = selectedUnitType; 14 | checkEndTime = Time.time + tryTime; 15 | } 16 | if (Time.time > checkEndTime) return; 17 | if (!isCheckNeeded(selectedUnitType)) return; 18 | 19 | try { 20 | Groups.unit.each((unit) => { 21 | if (unit.isAI() && !unit.dead && isEligible(unit, selectedUnitType)) { 22 | Call.unitControl(Vars.player, unit); 23 | timer.increase(); 24 | checkEndTime = 0; 25 | throw BreakException; 26 | } 27 | }); 28 | } catch (e) { 29 | if (e !== BreakException) throw e; 30 | } 31 | }); 32 | 33 | Events.on(UnitCreateEvent, (event) => { 34 | const selectedUnitType = Core.settings.getString("eui-auto-unit"); 35 | const unit = event.unit; 36 | 37 | if (!isCheckNeeded(selectedUnitType)) return; 38 | if (!isEligible(unit, selectedUnitType)) return; 39 | checkEndTime = Time.time + tryTime; 40 | }); 41 | 42 | function isCheckNeeded(selectedUnitType) { 43 | const player = Vars.player; 44 | const currentType = player.unit().type; 45 | 46 | if (!currentType || !selectedUnitType || currentType.toString() == selectedUnitType) return false; 47 | return true; 48 | } 49 | 50 | function isEligible(unit, selectedUnitType) { 51 | const player = Vars.player; 52 | if (unit.team != player.team()) return false; 53 | if (unit.type.toString() != selectedUnitType) return false; 54 | return true; 55 | } 56 | -------------------------------------------------------------------------------- /scripts/interact/interact-timer.js: -------------------------------------------------------------------------------- 1 | let timer = 0; 2 | 3 | Events.on(WorldLoadEvent, () => { 4 | timer = Time.time; 5 | }); 6 | 7 | exports.increase = function() { 8 | timer = Time.time + Time.toSeconds * (Core.settings.getInt("eui-action-delay", 500) / 1000); 9 | timer += 0.01; // prevent overflow 10 | } 11 | 12 | exports.canInteract = function() { 13 | return Time.time >= timer; 14 | } 15 | -------------------------------------------------------------------------------- /scripts/interact/schematic-selector.js: -------------------------------------------------------------------------------- 1 | const euiEvents = require("extended-ui/utils/event/events"); 2 | const schematicsCreate = require("extended-ui/utils/schematics"); 3 | 4 | let drawing = false; 5 | let startDrawX = 0; 6 | let startDrawY = 0; 7 | let endDrawX = 0; 8 | let endDrawY = 0; 9 | 10 | Events.run(Trigger.draw, () => { 11 | if (drawing) { 12 | Draw.draw(Layer.overlayUI+0.01, () => { 13 | Draw.z(Layer.darkness + 1); 14 | 15 | Lines.stroke(1, Pal.accent); 16 | Lines.rect(startDrawX, startDrawY, endDrawX - startDrawX, endDrawY - startDrawY); 17 | Draw.reset(); 18 | }); 19 | } 20 | }); 21 | 22 | const coordListener = (startPos, startTile, pos, mouseTile) => { 23 | startDrawX = startPos.x; 24 | startDrawY = startPos.y; 25 | endDrawX = pos.x; 26 | endDrawY = pos.y; 27 | } 28 | 29 | 30 | const creator = (startPos, startTile, pos, mouseTile) => { 31 | if (!mouseTile || !startTile) return; 32 | 33 | Vars.control.input.lastSchematic = schematicsCreate.create( 34 | Math.min(startTile.centerX(), mouseTile.centerX()), Math.min(startTile.centerY(), mouseTile.centerY()), 35 | Math.max(startTile.centerX(), mouseTile.centerX()), Math.max(startTile.centerY(), mouseTile.centerY()) 36 | ); 37 | 38 | if (Vars.control.input.lastSchematic != null) { 39 | Vars.control.input.useSchematic(Vars.control.input.lastSchematic); 40 | euiEvents.emit(euiEvents.eventType.schemSelectionEnd); 41 | euiEvents.removeListener(euiEvents.eventType.dragEnded, creator); 42 | drawing = false; 43 | }; 44 | } 45 | 46 | euiEvents.on(euiEvents.eventType.schemSelectionButtonPresed, (active) => { 47 | if (active) { 48 | euiEvents.on(euiEvents.eventType.dragEnded, creator); 49 | euiEvents.on(euiEvents.eventType.dragged, coordListener); 50 | drawing = true; 51 | } else { 52 | euiEvents.removeListener(euiEvents.eventType.dragEnded, creator); 53 | euiEvents.removeListener(euiEvents.eventType.dragged, coordListener); 54 | drawing = false; 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /scripts/main.js: -------------------------------------------------------------------------------- 1 | global.eui = {} // global mod object for fast access to important mod functions from console 2 | global.eui.relativeValue = require("extended-ui/utils/relative-value"); 3 | global.eui.drawTasks = require("extended-ui/utils/draw/draw-tasks"); 4 | 5 | const output = require("extended-ui/utils/output-wrapper"); 6 | 7 | const modules = [ 8 | "utils/polyfill", 9 | "utils/event/drag", 10 | "utils/draw/build-plan", 11 | 12 | "input/core-drag", 13 | "input/conveyor", 14 | 15 | "interact/auto-fill", 16 | "interact/auto-unit", 17 | "interact/schematic-selector", 18 | 19 | "ui/other/settings-ui", 20 | "ui/other/schematics-table-ui", 21 | "ui/other/power-ui", 22 | "ui/other/resource-rate-ui", 23 | "ui/other/bottom-panel-ui", 24 | "ui/blocks/block-info-ui", 25 | "ui/blocks/progress-bar", 26 | "ui/blocks/efficiency", 27 | "ui/units/units-table-ui", 28 | "ui/units/draw-cycle", 29 | "ui/alerts/losing-support", 30 | "ui/alerts/under-attack", 31 | 32 | "other/extend-zoom", 33 | "other/mine", 34 | ] 35 | 36 | for (let module of modules) { 37 | try { 38 | require("extended-ui/" + module); 39 | } catch(e) { 40 | log("Extended UI: can't load " + module + "\nIn " + e.fileName + "#" + e.lineNumber + " " + e.name + ': ' + e.message); 41 | output.debug(Core.bundle.format("eui.load-error", module)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /scripts/other/extend-zoom.js: -------------------------------------------------------------------------------- 1 | let minZoom = 0.5; 2 | let maxZoom = 25; 3 | 4 | function setZoom(min, max) { 5 | Vars.renderer.minZoom = min; 6 | Vars.renderer.maxZoom = max; 7 | } 8 | 9 | Events.run(Trigger.update, () => { 10 | //TODO convert to linear scale 11 | const newMinZoom = 2.5 - Core.settings.getInt("eui-maxZoom", 10)/5; 12 | 13 | if (newMinZoom != minZoom) { 14 | setZoom(newMinZoom, maxZoom); 15 | minZoom = newMinZoom; 16 | } 17 | }); 18 | 19 | setZoom(minZoom, maxZoom); 20 | -------------------------------------------------------------------------------- /scripts/other/mine.js: -------------------------------------------------------------------------------- 1 | let prevStatus = false; 2 | let nowMineble = []; 3 | 4 | Events.run(Trigger.update, () => { 5 | const status = Core.settings.getBool("eui-makeMineble", false); 6 | if (status == prevStatus) return; 7 | 8 | if (status) { 9 | makeAllMineble(); 10 | } else { 11 | removeMineble(); 12 | } 13 | prevStatus = status; 14 | }); 15 | 16 | function makeAllMineble() { 17 | Vars.content.blocks().each((b) => { 18 | if (!b.playerUnmineable) return; 19 | b.playerUnmineable = false; 20 | nowMineble.push(b); 21 | }); 22 | } 23 | 24 | function removeMineble() { 25 | nowMineble.forEach((b) => { 26 | b.playerUnmineable = true; 27 | }); 28 | nowMineble = []; 29 | } 30 | -------------------------------------------------------------------------------- /scripts/settings.js: -------------------------------------------------------------------------------- 1 | // Bars 2 | exports.factoryProgressBarSize = 4; 3 | exports.unitBarSize = 4; 4 | 5 | exports.unitBarDisplayTime = 5000; 6 | exports.unitBarFadeTime = 3000; 7 | -------------------------------------------------------------------------------- /scripts/ui/alerts/alert.js: -------------------------------------------------------------------------------- 1 | exports.BaseAlert = function(start, cancel) { 2 | this.enabled = false; 3 | this.start = () => { 4 | if (!this.enabled) start(); 5 | this.enabled = true; 6 | } 7 | this.cancel = () => { 8 | if (this.enabled) cancel(); 9 | this.enabled = false; 10 | } 11 | alerts.push(this); 12 | } 13 | 14 | const alerts = []; 15 | let prevStatus = false; 16 | 17 | Events.on(ClientLoadEvent, () => { 18 | Timer.schedule(update, 0, 1); 19 | }); 20 | 21 | function update() { 22 | const status = Core.settings.getBool("eui-ShowAlerts", true); 23 | if (status != prevStatus) { 24 | if (status) { 25 | for (let i of alerts) i.start(); 26 | } else { 27 | for (let i of alerts) i.cancel(); 28 | } 29 | prevStatus = status; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /scripts/ui/alerts/losing-support.js: -------------------------------------------------------------------------------- 1 | const Alerts = require("extended-ui/ui/alerts/alert"); 2 | const output = require("extended-ui/utils/output-wrapper"); 3 | const supportUnits = require("extended-ui/units/support-units"); 4 | 5 | const maxTime = 60*300 // 5 min; 6 | 7 | let sended; 8 | let timer; 9 | 10 | Events.on(EventType.WorldLoadEvent, () => { 11 | sended = false; 12 | timer = Time.time; 13 | }); 14 | 15 | let event = (event) => { 16 | const unit = event.unit; 17 | if (sended || !supportUnits.includes(unit.type.toString()) || unit.team != Vars.player.team()) return; 18 | if (Time.time - timer < maxTime) { 19 | output.ingameAlert(Core.bundle.get("alerts.losing-support")); 20 | sended = true; 21 | } 22 | } 23 | 24 | new Alerts.BaseAlert( 25 | () => { 26 | Events.on(UnitDestroyEvent, event); 27 | }, 28 | () => { 29 | Events.remove(UnitDestroyEvent, event); 30 | } 31 | ) 32 | -------------------------------------------------------------------------------- /scripts/ui/alerts/under-attack.js: -------------------------------------------------------------------------------- 1 | const Alerts = require("extended-ui/ui/alerts/alert"); 2 | const output = require("extended-ui/utils/output-wrapper"); 3 | const unitsCounter = require("extended-ui/units/units-counter"); 4 | const relativeValue = require("extended-ui/utils/relative-value"); 5 | const drawTasks = require("extended-ui/utils/draw/draw-tasks"); 6 | 7 | const sendCooldown = 60 * 60 // 1 min; 8 | const searchSize = 36 * 8; // 36 blocks around destroyer 9 | const destroyerSearchSize = 80 * 8; // 80 blocks away from destroyed 10 | const AttackSizes = { 11 | "massive": 67500, // ≈4.5 corvus 12 | "large": 22500, // ≈6.5 quad 13 | "medium": 7500, // ≈22.6 zenith 14 | "small": 2500, // ≈7.5 zenith 15 | } 16 | 17 | let lastSendTime; 18 | let lastCheckTime; 19 | 20 | Events.on(EventType.WorldLoadEvent, () => { 21 | lastSendTime = -sendCooldown; 22 | lastCheckTime = -sendCooldown; 23 | }); 24 | 25 | let event = (event) => { 26 | if (Time.time - 6 < lastCheckTime) return; // No more than 10 times per second. Reactor explosion can cause multiple useless checks 27 | if (Time.time - sendCooldown < lastCheckTime) return; 28 | 29 | const tile = event.tile; 30 | if (tile.team() != Vars.player.team()) return; 31 | 32 | const x = tile.x * 8; 33 | const y = tile.y * 8; 34 | let currentAttackValue = 0; 35 | 36 | lastCheckTime = Time.time; 37 | 38 | const destroyer = Units.closestEnemy(Vars.player.team(), x, y, destroyerSearchSize, (unit) => { 39 | if (!unitsCounter.isDangerous(unit)) return false; 40 | if (Mathf.dst(x, y, unit.x, unit.y) > unit.range()*1.5) return false; 41 | return true; 42 | }); 43 | if (!destroyer) return; 44 | 45 | Units.nearbyEnemies(Vars.player.team(), destroyer.x - searchSize, destroyer.y - searchSize, searchSize*2, searchSize*2, (unit) => { 46 | if (!unitsCounter.isDangerous(unit)) return; 47 | currentAttackValue += relativeValue.getUnitValue(unit.type.toString()); 48 | }); 49 | 50 | for (let [name, value] of Object.entries(AttackSizes)) { 51 | if (currentAttackValue > value) { 52 | output.ingameAlert(Core.bundle.get("alerts." + name + "-attack"), drawTasks.divergingCircles(x, y, {color: Color.red})); 53 | lastSendTime = Time.time; 54 | return; 55 | } 56 | } 57 | } 58 | 59 | new Alerts.BaseAlert( 60 | () => { 61 | Events.on(BlockDestroyEvent, event); 62 | }, 63 | () => { 64 | Events.remove(BlockDestroyEvent, event); 65 | } 66 | ) 67 | -------------------------------------------------------------------------------- /scripts/ui/blocks/block-info-ui.js: -------------------------------------------------------------------------------- 1 | const formattingUtil = require("extended-ui/utils/formatting"); 2 | let contentTable; 3 | 4 | let hovered = null; 5 | let isPlayerTeam = null; 6 | 7 | let isBuilded = false; 8 | 9 | Events.on(ClientLoadEvent, () => { 10 | Vars.ui.hudGroup.fill(null, t => { 11 | contentTable = t.table(Styles.black3).margin(4).get(); 12 | contentTable.visibility = () => infoTableVisibility(); 13 | t.bottom().left(); 14 | t.pack(); 15 | }); 16 | }) 17 | 18 | Events.run(Trigger.update, () => { 19 | if (!Core.settings.getBool("eui-ShowBlockInfo", true)) { 20 | if (isBuilded) { 21 | clearTable(); 22 | } 23 | hovered = null; 24 | return; 25 | } 26 | 27 | if (!contentTable) return; 28 | 29 | let x = Core.input.mouseX(); 30 | let y = Core.input.mouseY(); 31 | 32 | let pos = Core.input.mouseWorld(x, y); 33 | let mouseTile = Vars.world.tileWorld(pos.x, pos.y); 34 | 35 | if (!mouseTile) return; 36 | 37 | let build = mouseTile.build; 38 | 39 | if (!build) { 40 | if (isBuilded) { 41 | clearTable(); 42 | } 43 | hovered = null; 44 | return; 45 | } 46 | 47 | isPlayerTeam = build.team == Vars.player.team(); 48 | hovered = build; 49 | 50 | if (isRebuildNeeded(build)) { 51 | rebuildTable(build); 52 | } 53 | }); 54 | 55 | Events.run(Trigger.draw, () => { 56 | if (hovered && hovered.range && !isPlayerTeam) { 57 | Draw.draw(Layer.overlayUI+0.01, () => { 58 | let realRange; 59 | 60 | if (hovered.realRange) { 61 | realRange = hovered.realRange(); 62 | } else { 63 | realRange = hovered.range(); 64 | } 65 | 66 | Drawf.dashCircle(hovered.x, hovered.y, realRange, hovered.team.color); 67 | }); 68 | } 69 | }) 70 | 71 | function isRebuildNeeded(build) { 72 | //Useless? Should be rebuilded every frame to get last info about hovered block 73 | return true; 74 | } 75 | 76 | function rebuildTable(build) { 77 | clearTable(); 78 | buildTable(build); 79 | } 80 | 81 | function clearTable() { 82 | if (!isBuilded) return; 83 | 84 | contentTable.clearChildren(); 85 | isBuilded = false; 86 | } 87 | 88 | function buildTable(build) { 89 | const power = build.power; 90 | const items = build.items; 91 | const config = build.config(); 92 | const displayPower = power && !isPlayerTeam; 93 | const displayItems = items && build.items.total() > 0 && (!isPlayerTeam || build.items.total() <= 50); 94 | const displayConfig = typeof config == "string" && !isPlayerTeam; 95 | if (![displayPower, displayItems, displayConfig].includes(true)) return; 96 | 97 | if (displayPower) { 98 | const powerTable = contentTable.table().get(); 99 | const graph = build.power.graph; 100 | 101 | const storedNetPower = graph.getBatteryStored(); 102 | const maxNetPower = graph.getTotalBatteryCapacity(); 103 | const currentNetPower = graph.getPowerBalance(); 104 | 105 | powerTable.label(() => { 106 | return Core.bundle.get("block-info.power") + ": " + formattingUtil.powerToString(currentNetPower, []); 107 | }) 108 | powerTable.row(); 109 | if (maxNetPower) { 110 | powerTable.label(() => Core.bundle.get("block-info.stored") + ": " + Math.round(storedNetPower/maxNetPower*100) + "%"); 111 | } 112 | contentTable.row(); 113 | } 114 | if (displayItems) { 115 | const resourcesTable = contentTable.table().get(); 116 | let i = 0; 117 | build.items.each((item,amount) => { 118 | resourcesTable.image(item.icon(Cicon.small)).left(); 119 | resourcesTable.label(() => { 120 | return amount.toString(); 121 | }).padLeft(2).left().padRight(4); 122 | 123 | if(++i % 4 == 0) { 124 | resourcesTable.row(); 125 | } 126 | }); 127 | contentTable.row(); 128 | } 129 | if (displayConfig) { 130 | const configTable = contentTable.table().get(); 131 | configTable.label(() => { 132 | return build.config(); 133 | }) 134 | contentTable.row(); 135 | } 136 | isBuilded = true; 137 | } 138 | 139 | function infoTableVisibility() { 140 | return Vars.ui.hudfrag.shown && isBuilded; 141 | } 142 | -------------------------------------------------------------------------------- /scripts/ui/blocks/efficiency.js: -------------------------------------------------------------------------------- 1 | const barBuilder = require("extended-ui/utils/draw/bar-builder"); 2 | 3 | let storage = new Map(); 4 | 5 | Events.on(EventType.WorldLoadEvent, () => { 6 | storage = new Map(); 7 | }); 8 | 9 | function EfficiencyLabel() { 10 | this.replace = false; 11 | this.draw = function(build) { 12 | if (!Core.settings.getBool("eui-ShowEfficiency", false)) return; 13 | 14 | let efficiency = countEfficiency(build); 15 | 16 | let text = barBuilder.buildPercentLabel(efficiency); 17 | Draw.z(Layer.effect+1); 18 | barBuilder.drawLabel(text, build.x, build.y, Color.white, true); 19 | Draw.reset(); 20 | } 21 | } 22 | 23 | Events.on(ClientLoadEvent, () => { 24 | if (!Core.settings.getBool("eui-ShowEfficiency", false)) return; 25 | const version = Version.number; 26 | const is6 = version < 7; 27 | const is7 = version >= 7; 28 | Vars.content.blocks().each((block) => { 29 | 30 | let base; 31 | if (block instanceof Fracker) { 32 | base = Fracker.FrackerBuild; 33 | } else if (block instanceof SolidPump) { 34 | base = SolidPump.SolidPumpBuild; 35 | } else if (block instanceof Separator) { 36 | base = Separator.SeparatorBuild; 37 | } else if (block instanceof LiquidConverter) { 38 | base = LiquidConverter.LiquidConverterBuild; 39 | } else if (is7 && block instanceof AttributeCrafter) { 40 | base = AttributeCrafter.AttributeCrafterBuild; 41 | } else if (is6 && block instanceof Cultivator) { 42 | base = Cultivator.CultivatorBuild; 43 | } else if (is6 && block instanceof AttributeSmelter) { 44 | base = AttributeSmelter.AttributeSmelterBuild; 45 | } else if (block instanceof GenericCrafter) { 46 | base = GenericCrafter.GenericCrafterBuild; 47 | } else if (block instanceof Drill) { 48 | base = Drill.DrillBuild; 49 | // } 50 | // else if (block instanceof ImpactReactor) { 51 | // base = ImpactReactor.ImpactReactorBuild; 52 | // } else if (block instanceof NuclearReactor) { 53 | // base = NuclearReactor.NuclearReactorBuild; 54 | // } else if (block instanceof DecayGenerator) { 55 | // base = DecayGenerator.DecayGeneratorBuild; no 56 | // } else if (block instanceof SingleTypeGenerator) { 57 | // base = SingleTypeGenerator.GeneratorBuild; no 58 | // } else if (block instanceof BurnerGenerator) { 59 | // base = BurnerGenerator.BurnerGeneratorBuild; no 60 | } else { 61 | return; 62 | } 63 | 64 | block.buildType = () => { 65 | return extend(base, block, { 66 | drawables: [ 67 | new EfficiencyLabel(), 68 | ], 69 | draw() { 70 | let replaced = false; 71 | for (let i = 0; i < this.drawables.length; i++) { 72 | if (this.drawables[i].replace) { 73 | replaced = true; 74 | break; 75 | } 76 | } 77 | if (!replaced) { 78 | this.super$draw(); 79 | } 80 | for (let i = 0; i < this.drawables.length; i++) { 81 | this.drawables[i].draw(this); 82 | } 83 | } 84 | }); 85 | } 86 | }); 87 | }); 88 | 89 | //TODO does not work correctly for liquid 90 | function countEfficiency(build) { 91 | const state = storage.get(build.id); 92 | const points = build.status().toString() == "active" ? 0.001 : 0; 93 | const currentTime = Time.time / 60 * 1000; // convert to milliseconds 94 | const timer = Core.settings.getInt("eui-EfficiencyTimer", 15); 95 | const millisecondTimer = timer * 1000; 96 | 97 | 98 | if (!state) { 99 | storage.set(build.id, { 100 | prevValue: 0, 101 | value: 0, 102 | time: currentTime, 103 | startTime: currentTime, 104 | }); 105 | return 0; 106 | } else { 107 | const passed_time = currentTime - state.time; 108 | const startTime = state.startTime; 109 | 110 | if (currentTime - startTime > millisecondTimer) { 111 | state.prevValue = state.value; 112 | state.value = 0; 113 | state.startTime = currentTime; 114 | state.time = currentTime; 115 | return state.prevValue / timer; 116 | } else { 117 | const measurement = (currentTime - startTime) / millisecondTimer; 118 | 119 | state.value += passed_time * points; 120 | state.time = currentTime; 121 | 122 | let countedValue = state.value*measurement / (timer*measurement); 123 | let countedPrevValue = state.prevValue*(1 - measurement) / timer; 124 | return countedValue + countedPrevValue; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /scripts/ui/blocks/progress-bar.js: -------------------------------------------------------------------------------- 1 | const settings = require("extended-ui/settings"); 2 | const barBuilder = require("extended-ui/utils/draw/bar-builder"); 3 | 4 | 5 | const factoryProgressBarSize = settings.factoryProgressBarSize; 6 | 7 | function ProgressBar() { 8 | this.replace = false; 9 | this.draw = function(build) { 10 | if (!Core.settings.getBool("eui-showFactoryProgress", true)) return; 11 | if (build.currentPlan == -1) return; 12 | 13 | let progress = 0; 14 | 15 | if (build instanceof UnitFactory.UnitFactoryBuild) { 16 | progress = build.progress / build.block.plans.get(build.currentPlan).time; 17 | } else { 18 | progress = build.progress / build.block.constructTime; 19 | } 20 | 21 | let text = barBuilder.buildPercentLabel(progress); 22 | barBuilder.draw( 23 | build.x, build.y, progress, build.block.size, factoryProgressBarSize, text, build.team.color, 1 24 | ) 25 | } 26 | } 27 | 28 | Events.on(ClientLoadEvent, event => { 29 | Vars.content.blocks().each((block) => { 30 | let base; 31 | 32 | if (block instanceof UnitFactory) { 33 | base = UnitFactory.UnitFactoryBuild; 34 | } else if (block instanceof Reconstructor) { 35 | base = Reconstructor.ReconstructorBuild; 36 | } else { 37 | return; 38 | } 39 | 40 | block.buildType = () => { 41 | return extend(base, block, { 42 | drawables: [ 43 | new ProgressBar(), 44 | ], 45 | draw() { 46 | let replaced = false; 47 | for (let i = 0; i < this.drawables.length; i++) { 48 | if (this.drawables[i].replace) { 49 | replaced = true; 50 | break; 51 | } 52 | } 53 | if (!replaced) { 54 | this.super$draw(); 55 | } 56 | for (let i = 0; i < this.drawables.length; i++) { 57 | this.drawables[i].draw(this); 58 | } 59 | } 60 | }); 61 | } 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /scripts/ui/other/bottom-panel-ui.js: -------------------------------------------------------------------------------- 1 | const iconsUtil = require("extended-ui/utils/icons"); 2 | const coreUnits = require("extended-ui/units/core-units"); 3 | const euiEvents = require("extended-ui/utils/event/events"); 4 | 5 | let selectUnitDialog; 6 | let contentTable = null; 7 | let isBuilded = false; 8 | let showSettings = false; 9 | let schemSelection = false; 10 | let interactCore = Core.settings.getBool("eui-interact-core", false); 11 | let fillBuildings = Core.settings.getBool("eui-auto-fill", false); 12 | let selectedUnit = Core.settings.getString("eui-auto-unit"); 13 | 14 | euiEvents.on(euiEvents.eventType.schemSelectionEnd, () => schemSelection = false); 15 | 16 | Events.run(Trigger.update, () => { 17 | if (!Core.settings.getBool("eui-showInteractSettings", true)) { 18 | if (isBuilded) { 19 | clearTable(); 20 | } 21 | return; 22 | }; 23 | if (isRebuildNeeded()) rebuildTable(); 24 | }); 25 | 26 | Events.on(ClientLoadEvent, () => { 27 | Vars.ui.hudGroup.fill(null, t => { 28 | contentTable = t.table().get(); 29 | t.center().bottom(); 30 | t.pack(); 31 | }); 32 | contentTable.visibility = () => tableVisibility(); 33 | 34 | const size = 568; 35 | 36 | selectUnitDialog = new BaseDialog(Core.bundle.get("schematics-table.dialog.change-image.title")); 37 | selectUnitDialog.addCloseButton(); 38 | 39 | const unitSprites = iconsUtil.getUnitSprites(); 40 | unitSprites['cancel'] = iconsUtil.getByName("cancel"); 41 | 42 | let r = 0; 43 | selectUnitDialog.cont.pane(table => { 44 | for (let [unitName, unitImage] of Object.entries(unitSprites)) { 45 | if (unitName == "block" || coreUnits.includes(unitName)) continue; 46 | const setted_name = unitName; 47 | let imageButton = table.button(unitImage, Styles.cleari, () => { 48 | Core.settings.put("eui-auto-unit", setted_name); 49 | selectedUnit = setted_name; 50 | rebuildTable(); 51 | selectUnitDialog.hide(); 52 | }).size(48).pad(4).get(); 53 | imageButton.resizeImage(48*0.8); 54 | 55 | if (++r % 10 == 0) table.row(); 56 | } 57 | }).size(size, size); 58 | }); 59 | 60 | function isRebuildNeeded() { 61 | if (!isBuilded) return true; 62 | return false; 63 | } 64 | 65 | function rebuildTable() { 66 | clearTable(); 67 | buildTable(); 68 | } 69 | 70 | function buildTable() { 71 | if (!contentTable) return; 72 | isBuilded = true; 73 | 74 | contentTable.button(Icon.upOpen, Styles.selecti, () => { 75 | showSettings = !showSettings; 76 | rebuildTable(); 77 | }).width(64).height(16).marginBottom(3); 78 | if (!showSettings) return; 79 | 80 | contentTable.row(); 81 | const buttonTable = contentTable.table().get(); 82 | buttonTable.defaults().size(32); 83 | buttonTable.button(iconsUtil.getByName("core-nucleus"), Styles.clearToggleTransi, () => { 84 | interactCore = !interactCore; 85 | Core.settings.put("eui-interact-core", interactCore); 86 | }).tooltip(Core.bundle.get("interaction-settings.button.core.tooltip")).update(b => { 87 | b.setChecked(interactCore); 88 | }).get().resizeImage(32*0.8); 89 | 90 | buttonTable.button(Icon.box, Styles.clearToggleTransi, () => { 91 | fillBuildings = !fillBuildings; 92 | Core.settings.put("eui-auto-fill", fillBuildings); 93 | }).tooltip(Core.bundle.get("interaction-settings.button.auto-fill.tooltip")).update(b => { 94 | b.setChecked(fillBuildings); 95 | }).get().resizeImage(32*0.8); 96 | 97 | buttonTable.button(iconsUtil.getByName(selectedUnit), Styles.clearTransi, () => { 98 | selectUnitDialog.show(); 99 | }).tooltip(Core.bundle.get("interaction-settings.button.auto-unit.tooltip")).get().resizeImage(32*0.8); 100 | 101 | if (!Vars.mobile) { 102 | buttonTable.button(Icon.save, Styles.clearToggleTransi, () => { 103 | schemSelection = !schemSelection; 104 | euiEvents.emit(euiEvents.eventType.schemSelectionButtonPresed, schemSelection); 105 | }).tooltip(Core.bundle.get("interaction-settings.button.schem-selection.tooltip")).update(b => { 106 | b.setChecked(schemSelection); 107 | }).get().resizeImage(32*0.8); 108 | } 109 | } 110 | 111 | 112 | function clearTable() { 113 | if (!isBuilded) return; 114 | 115 | contentTable.clearChildren(); 116 | isBuilded = false; 117 | } 118 | 119 | function tableVisibility() { 120 | return Vars.ui.hudfrag.shown && isBuilded; 121 | } 122 | -------------------------------------------------------------------------------- /scripts/ui/other/power-ui.js: -------------------------------------------------------------------------------- 1 | const iterationTools = require("extended-ui/utils/iteration-tools"); 2 | const formattingUtil = require("extended-ui/utils/formatting"); 3 | 4 | const powerBarDefaultWidth = 300; 5 | const powerBarDefaultHeight = 25; 6 | 7 | let graphs = []; 8 | let coreItems; 9 | let storedNetPower; 10 | let maxNetPower; 11 | let currentNetPower; 12 | 13 | let debugTimer; 14 | 15 | 16 | Events.run(Trigger.update, () => { 17 | if (!Core.settings.getBool("eui-showPowerBar", true)) return; 18 | 19 | let newStoredNetPower = 0; 20 | let newMaxNetPower = 0; 21 | let newCurrentNetPower = 0; 22 | let newGraphs = []; 23 | 24 | let getAllPowerGraphs = (tile) => { 25 | if (tile.build && tile.build.power) { 26 | let graph = tile.build.power.graph; 27 | 28 | if (!newGraphs.includes(graph)) { 29 | 30 | 31 | newStoredNetPower += graph.getBatteryStored(); 32 | newMaxNetPower += graph.getTotalBatteryCapacity(); 33 | newCurrentNetPower += graph.getPowerBalance(); 34 | 35 | // storing more than 100 graphs can provide some lags 36 | if (graph.getPowerBalance() && newGraphs.length < 100) newGraphs.push(graph); 37 | } 38 | } 39 | } 40 | 41 | iterationTools.iterateSeq(getAllPowerGraphs, Vars.indexer.getAllied(Vars.player.team(), BlockFlag.generator).iterator()); 42 | iterationTools.iterateSeq(getAllPowerGraphs, Vars.indexer.getAllied(Vars.player.team(), BlockFlag.reactor).iterator()); 43 | 44 | // when player remove power node, power go to 0 for ~half of a second somehow. 45 | if (currentNetPower && !newCurrentNetPower) { 46 | if (!debugTimer) { 47 | debugTimer = Date.now(); 48 | return; 49 | } else if (Date.now() < debugTimer + 500) { 50 | return; 51 | } 52 | } 53 | 54 | debugTimer = 0; 55 | 56 | storedNetPower = newStoredNetPower; 57 | maxNetPower = newMaxNetPower; 58 | currentNetPower = newCurrentNetPower; 59 | graphs = newGraphs; 60 | }); 61 | 62 | Events.on(ClientLoadEvent, () => { 63 | let powerBar = new Bar(prov(() => formattingUtil.powerToString(currentNetPower, graphs)), prov(() => Pal.accent), floatp(() => currentPowerStatus())); 64 | 65 | if (Version.number < 7) { 66 | coreItems = Vars.ui.hudGroup.find("coreitems"); 67 | coreItems.row(); 68 | coreItems.getCells().get(0).padBottom(6); 69 | coreItems.add(powerBar).visible(() => powerBarVisible()).width(powerBarDefaultWidth).height(powerBarDefaultHeight).pad(4); 70 | } else { 71 | Vars.ui.hudGroup.fill(cons(t => { 72 | t.add(powerBar).width(powerBarDefaultWidth).height(powerBarDefaultHeight).visible(() => { 73 | return (powerBarVisible() && Vars.ui.hudfrag.shown); 74 | }); 75 | t.top().right().marginRight(160).marginTop(10); 76 | t.pack(); 77 | })); 78 | } 79 | }); 80 | 81 | function currentPowerStatus() { 82 | if (!maxNetPower) { 83 | return 0; 84 | } 85 | return storedNetPower / maxNetPower; 86 | } 87 | 88 | function powerBarVisible() { 89 | return Core.settings.getBool("eui-showPowerBar", true) && (Boolean(storedNetPower) || Boolean(currentNetPower)); 90 | } 91 | -------------------------------------------------------------------------------- /scripts/ui/other/resource-rate-ui.js: -------------------------------------------------------------------------------- 1 | const Difference = require("extended-ui/utils/difference"); 2 | const formattingUtil = require("extended-ui/utils/formatting"); 3 | 4 | const diffs = {}; 5 | 6 | let contentTable; 7 | let coreItemsCell; // for v6 8 | let coreItemsCollapser; // for v7 9 | let oldCoreItemsTable; 10 | 11 | let isReplaced = false; 12 | 13 | Events.on(ClientLoadEvent, () => { 14 | contentTable = new Table(Styles.black6); 15 | contentTable.pack(); 16 | 17 | if (Version.number < 7) { 18 | const coreInfoTable = Vars.ui.hudGroup.find("coreitems"); 19 | oldCoreItemsTable = coreInfoTable.getChildren().get(0); 20 | coreItemsCell = coreInfoTable.getCell(oldCoreItemsTable); 21 | } else { 22 | coreItemsCollapser = Vars.ui.hudGroup.find('coreinfo').getChildren().get(1).getChildren().get(0); 23 | oldCoreItemsTable = coreItemsCollapser.getChildren().get(0); 24 | } 25 | Timer.schedule(update, 0, 3); 26 | }); 27 | 28 | Events.run(Trigger.update, () => { 29 | if (!isReplaced) return; 30 | rebuildTable(); 31 | }); 32 | 33 | function update() { 34 | if (Core.settings.getBool("eui-ShowResourceRate", false)) { 35 | if (!isReplaced) { 36 | isReplaced = true; 37 | if (Version.number < 7) { 38 | coreItemsCell.setElement(contentTable); 39 | } else { 40 | coreItemsCollapser.setTable(contentTable); 41 | } 42 | } 43 | } else { 44 | if (isReplaced) { 45 | isReplaced = false; 46 | if (Version.number < 7) { 47 | coreItemsCell.setElement(oldCoreItemsTable); 48 | } else { 49 | coreItemsCollapser.setTable(oldCoreItemsTable); 50 | } 51 | } 52 | } 53 | } 54 | 55 | function rebuildTable() { 56 | clearTable(); 57 | buildTable(); 58 | } 59 | 60 | function buildTable() { 61 | const resourcesTable = contentTable.table().get(); 62 | const currentItems = Vars.player.team().items(); 63 | let i = 0; 64 | currentItems.each((item,amount) => { 65 | let diff = diffs[item] 66 | if (!diff) { 67 | diff = new Difference(1000, amount); 68 | diffs[item] = diff; 69 | } 70 | const difference = diff.difference(amount); 71 | const color = difference >= 0 ? '[green]' : '[red]'; 72 | const sign = difference >= 0 ? '+' : ''; 73 | 74 | resourcesTable.image(item.icon(Cicon.small)).left(); 75 | resourcesTable.label(() => { 76 | return formattingUtil.numberToString(amount); 77 | }).padLeft(2).left().padRight(1); 78 | resourcesTable.label(() => { 79 | return "(" + color + sign + formattingUtil.numberToString(Math.round(difference)) + "[white])"; 80 | }).left().padRight(2); 81 | 82 | if(++i % 4 == 0) { 83 | resourcesTable.row(); 84 | } 85 | }); 86 | contentTable.row(); 87 | } 88 | 89 | function clearTable() { 90 | contentTable.clearChildren(); 91 | } 92 | -------------------------------------------------------------------------------- /scripts/ui/other/schematics-table-ui.js: -------------------------------------------------------------------------------- 1 | const iconsUtil = require("extended-ui/utils/icons"); 2 | 3 | let isBuilded = false; 4 | let contentTable; 5 | let previewTable; 6 | let setCategoryNameDialog; 7 | 8 | let currentCategory = 0; 9 | let lastCategory = 0; 10 | 11 | let schematicButtonSize; 12 | let categoryButtonSize; 13 | 14 | let rows; 15 | let columns; 16 | 17 | let oldRows; 18 | let oldColumns; 19 | let oldSize; 20 | 21 | let hovered = null; 22 | 23 | //for mobile version 24 | let lastTaped; 25 | let lastTapTime; 26 | 27 | Events.on(ClientLoadEvent, () => { 28 | Vars.ui.hudGroup.fill(null, t => { 29 | previewTable = t.table(Styles.black3).get(); 30 | previewTable.visibility = () => previewTableVisibility(); 31 | t.center(); 32 | t.pack(); 33 | }); 34 | 35 | setCategoryNameDialog = new BaseDialog(Core.bundle.get("schematics-table.dialog.change-cathegory-name.title")); 36 | setCategoryNameDialog.addCloseButton(); 37 | setCategoryNameDialog.cont.pane(table => { 38 | table.field(null, text => { 39 | if (!text) return; 40 | 41 | Core.settings.put("category" + currentCategory + "name", text); 42 | rebuildTable(); 43 | }).growX(); 44 | }).size(320, 320); 45 | }); 46 | 47 | Events.run(Trigger.update, () => { 48 | if (!Core.settings.getBool("eui-ShowSchematicsTable", true)) { 49 | if (isBuilded) { 50 | clearTable(); 51 | } 52 | return; 53 | } 54 | 55 | rows = Core.settings.getInt("eui-SchematicsTableRows", 4); 56 | columns = Core.settings.getInt("eui-SchematicsTableColumns", 5); 57 | schematicButtonSize = Core.settings.getInt("eui-SchematicsTableButtonSize", 30); 58 | categoryButtonSize = schematicButtonSize + 2; 59 | 60 | if (!contentTable) { 61 | setMarker(); 62 | } 63 | 64 | if (isRebuildNeeded()) { 65 | rebuildTable(); 66 | } 67 | 68 | if (hovered && contentTable.hasMouse()) { 69 | rebuildPreviewTable(); 70 | } else { 71 | hovered = null; 72 | } 73 | }); 74 | 75 | function showEditSchematicButtonDialog(currentCategory, column, row) { 76 | const size = Vars.mobile ? 320 : 560 77 | const schematicString = getSchematicString(currentCategory, column, row); 78 | const editSchematicButtonDialog = new BaseDialog(Core.bundle.get("schematics-table.dialog.edit-schematic-button.title")); 79 | editSchematicButtonDialog.addCloseButton(); 80 | 81 | addEditImageTable(editSchematicButtonDialog, schematicString + "image", size); 82 | editSchematicButtonDialog.cont.row(); 83 | addEditSchematicTable(editSchematicButtonDialog, schematicString); 84 | 85 | editSchematicButtonDialog.show(); 86 | } 87 | 88 | function showEditImageDialog(name) { 89 | const size = Vars.mobile ? 320 : 640 90 | const editImageDialog = new BaseDialog(Core.bundle.get("schematics-table.dialog.change-image.title")); 91 | editImageDialog.addCloseButton(); 92 | 93 | addEditImageTable(editImageDialog, name, size); 94 | 95 | editImageDialog.show(); 96 | } 97 | 98 | function addEditImageTable(dialog, name, size) { 99 | const iconsAndSprites = [iconsUtil.getIcons(), iconsUtil.getSprites()]; 100 | 101 | dialog.cont.pane(table => { 102 | for (let images of iconsAndSprites) { 103 | let r = 0; 104 | table.pane(table => { 105 | for (let image of Object.entries(images)) { 106 | const setted_name = image[0]; 107 | let imageButton = table.button(image[1], Styles.cleari, () => { 108 | Vars.ui.announce(Core.bundle.get("schematics-table.dialog.change-image.setted-announce-text") + " " + setted_name); 109 | Core.settings.put(name, setted_name); 110 | 111 | rebuildTable(); 112 | }).size(48).pad(4).get(); 113 | imageButton.resizeImage(48*0.8); 114 | 115 | if (++r % 8 == 0) table.row(); 116 | } 117 | }).top(); 118 | } 119 | }).size(size*2, size); 120 | } 121 | 122 | function addEditSchematicTable(dialog, name) { 123 | let text = Core.bundle.get("schematics-table.dialog.change-schematic.title") 124 | dialog.cont.pane(table => { 125 | table.labelWrap(text).growX(); 126 | table.row(); 127 | table.field(Core.settings.getString(name, ""), text => { 128 | Core.settings.put(name, text); 129 | rebuildTable(); 130 | }).growX(); 131 | }).size(Core.graphics.getWidth()/2, 80); 132 | } 133 | 134 | function setMarker() { 135 | let overlayMarker = Vars.ui.hudGroup.getChildren().get(2); 136 | overlayMarker.row(); 137 | contentTable = overlayMarker.table(Styles.black3).top().right().get(); 138 | contentTable.visibility = () => isBuilded; 139 | } 140 | 141 | function isRebuildNeeded() { 142 | if (!isBuilded) return true; 143 | 144 | if (!oldColumns || !oldRows || !oldSize) { 145 | oldRows = rows; 146 | oldColumns = columns; 147 | oldSize = schematicButtonSize; 148 | } 149 | if (rows != oldRows || columns != oldColumns || oldSize != schematicButtonSize) { 150 | oldRows = rows; 151 | oldColumns = columns; 152 | oldSize = schematicButtonSize; 153 | return true; 154 | } 155 | 156 | if (lastCategory != currentCategory) { 157 | lastCategory = currentCategory; 158 | return true; 159 | } 160 | 161 | return false; 162 | } 163 | 164 | function rebuildTable() { 165 | clearTable(); 166 | buildTable(); 167 | } 168 | 169 | function buildTable() { 170 | const wrapped = contentTable.table().margin(3).get(); 171 | let imageButton; 172 | 173 | const categoryButtonsTable = wrapped.table().get(); 174 | for (let i = 0; i < columns; i++) { 175 | const index = i; 176 | imageButton = categoryButtonsTable.button(getCategoryImage(index), Styles.clearToggleTransi, ()=>{ 177 | currentCategory = index; 178 | }).update(b => { 179 | b.setChecked(currentCategory == index); 180 | }).width(categoryButtonSize).height(categoryButtonSize).tooltip(getCategoryTooltip(index)).get(); 181 | imageButton.resizeImage(categoryButtonSize*0.8); 182 | if (!Vars.mobile) { 183 | imageButton.clicked(Packages.arc.input.KeyCode.mouseRight, () => showEditImageDialog("category" + index + "image")); 184 | } else { 185 | imageButton.clicked(() => { 186 | if (mobileDoubleTap("category" + index + "image")) { 187 | showEditImageDialog("category" + index + "image"); 188 | // Clicks on label from the phone impossible? so this is here 189 | setCategoryNameDialog.show(); 190 | } 191 | }); 192 | } 193 | } 194 | 195 | wrapped.row(); 196 | let categoryLabel = wrapped.labelWrap(getCategoryLabelText()).width(categoryButtonSize*columns).padTop(6).padBottom(6).get(); 197 | categoryLabel.setAlignment(Align.center); 198 | if (!Vars.mobile) { 199 | categoryLabel.clicked(Packages.arc.input.KeyCode.mouseRight, () => setCategoryNameDialog.show()); 200 | } 201 | 202 | wrapped.row(); 203 | const schematicButtonsTable = wrapped.table().get(); 204 | for (let i = 0; i < rows; i++) { 205 | const row = i; 206 | for (let j = 0; j < columns; j++) { 207 | const column = j; 208 | const schematic = findSchematic(currentCategory, column, row); 209 | 210 | imageButton = schematicButtonsTable.button(getSchematicImage(column, row), Styles.defaulti, ()=>{ 211 | if (schematic) Vars.control.input.useSchematic(schematic); 212 | }).update(b => { 213 | b.setDisabled(false); 214 | }).width(schematicButtonSize).height(schematicButtonSize).pad(1).tooltip(getSchematicTooltip(schematic)).get(); 215 | 216 | imageButton.resizeImage(schematicButtonSize*0.6); 217 | imageButton.hovered(() => { 218 | hovered = schematic; 219 | }); 220 | if (!Vars.mobile) { 221 | imageButton.clicked(Packages.arc.input.KeyCode.mouseRight, () => showEditSchematicButtonDialog(currentCategory, column, row)); 222 | } else { 223 | imageButton.clicked(() => { 224 | if (mobileDoubleTap(getSchematicString(currentCategory, column, row))) { 225 | showEditSchematicButtonDialog(currentCategory, column, row); 226 | } 227 | }); 228 | } 229 | } 230 | schematicButtonsTable.row(); 231 | } 232 | 233 | isBuilded = true; 234 | } 235 | 236 | function clearTable() { 237 | if (!isBuilded) return; 238 | 239 | contentTable.clearChildren(); 240 | isBuilded = false; 241 | } 242 | 243 | function rebuildPreviewTable() { 244 | previewTable.clearChildren(); 245 | 246 | const requirements = hovered.requirements(); 247 | const powerConsumption = hovered.powerConsumption() * 60; 248 | const powerProduction = hovered.powerProduction() * 60; 249 | const core = Vars.player.core(); 250 | 251 | previewTable.add(new SchematicsDialog.SchematicImage(hovered)).maxSize(800); 252 | previewTable.row(); 253 | 254 | previewTable.table(null, requirementsTable => { 255 | let i = 0; 256 | requirements.each((item, amount) => { 257 | requirementsTable.image(item.icon(Cicon.small)).left(); 258 | requirementsTable.label(() => { 259 | if (core == null || Vars.state.rules.infiniteResources || core.items.has(item, amount)) return "[lightgray]" + amount; 260 | return (core.items.has(item, amount) ? "[lightgray]" : "[scarlet]") + Math.min(core.items.get(item), amount) + "[lightgray]/" + amount; 261 | }).padLeft(2).left().padRight(4); 262 | 263 | if (++i % 4 == 0) { 264 | requirementsTable.row(); 265 | } 266 | }); 267 | }); 268 | 269 | previewTable.row(); 270 | 271 | if (powerConsumption || powerProduction) { 272 | previewTable.table(null, powerTable => { 273 | 274 | if (powerProduction) { 275 | powerTable.image(Icon.powerSmall).color(Pal.powerLight).padRight(3); 276 | powerTable.add("+" + Strings.autoFixed(powerProduction, 2)).color(Pal.powerLight).left(); 277 | 278 | if (powerConsumption) { 279 | powerTable.add().width(15); 280 | } 281 | } 282 | 283 | if (powerConsumption) { 284 | powerTable.image(Icon.powerSmall).color(Pal.remove).padRight(3); 285 | powerTable.add("-" + Strings.autoFixed(powerConsumption, 2)).color(Pal.remove).left(); 286 | } 287 | }); 288 | } 289 | } 290 | 291 | function previewTableVisibility() { 292 | return Core.settings.getBool("eui-ShowSchematicsPreview", true) && Boolean(contentTable) && contentTable.visible && Boolean(hovered); 293 | } 294 | 295 | function getCategoryTooltip(categoryId) { 296 | return Core.settings.getString("category" + categoryId + "name", Core.bundle.get("schematics-table.default-cathegory-tooltip")); 297 | } 298 | 299 | function getCategoryLabelText() { 300 | let defaultText; 301 | 302 | if (Vars.mobile) { 303 | defaultText = Core.bundle.get("schematics-table.default-cathegory-mobile-name"); 304 | } else { 305 | defaultText = Core.bundle.get("schematics-table.default-cathegory-desktop-name"); 306 | } 307 | 308 | return Core.settings.getString("category" + currentCategory + "name", defaultText); 309 | } 310 | 311 | function getCategoryImage(categoryId) { 312 | return iconsUtil.getByName(Core.settings.getString("category" + categoryId + "image")); 313 | } 314 | 315 | function getSchematicImage(column, row) { 316 | return iconsUtil.getByName(Core.settings.getString(getSchematicString(currentCategory, column, row) + "image")); 317 | } 318 | 319 | function getSchematicString(category, column, row) { 320 | return "schematic" + category + "." + column + "." + row; 321 | } 322 | 323 | function getSchematicTooltip(schematic) { 324 | if (schematic) { 325 | return Core.bundle.get("schematics-table.use-schematic") + " " + schematic.name(); 326 | } else { 327 | return Core.bundle.get("schematics-table.default-cathegory-desktop-name"); 328 | } 329 | } 330 | 331 | function findSchematic(category, column, row) { 332 | let name = Core.settings.getString(getSchematicString(category, column, row)); 333 | let schem = null; 334 | Vars.schematics.all().each((s) => { 335 | if(s.name() == name) { 336 | schem = s; 337 | } 338 | }); 339 | return schem; 340 | } 341 | 342 | function mobileDoubleTap(name) { 343 | if (lastTaped == name && Date.now() - lastTapTime < 250) { 344 | return true; 345 | } else { 346 | lastTaped = name; 347 | lastTapTime = Date.now(); 348 | return false; 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /scripts/ui/other/settings-ui.js: -------------------------------------------------------------------------------- 1 | Events.on(EventType.ClientLoadEvent, () => { 2 | const settings = Vars.ui.settings.graphics; 3 | settings.row(); 4 | settings.button(Core.bundle.get("eui.name"), Styles.defaultt, () => extendedUIDialogSettings.show()).width(240).height(50); 5 | 6 | const extendedUIDialogSettings = new BaseDialog(Core.bundle.get("eui.settings")); 7 | extendedUIDialogSettings.addCloseButton(); 8 | extendedUIDialogSettings.buttons.defaults().size(240, 60); 9 | 10 | extendedUIDialogSettings.cont.pane((() => { 11 | 12 | let contentTable; 13 | if (Version.number < 7) { 14 | contentTable = new Packages.arc.scene.ui.SettingsDialog.SettingsTable(); 15 | } else { 16 | contentTable = new SettingsMenuDialog.SettingsTable(); 17 | } 18 | 19 | contentTable.checkPref("eui-showPowerBar", true); 20 | contentTable.checkPref("eui-showFactoryProgress", true); 21 | contentTable.checkPref("eui-showUnitBar", true); 22 | contentTable.checkPref("eui-ShowUnitTable", true); 23 | contentTable.checkPref("eui-ShowBlockInfo", true); 24 | contentTable.checkPref("eui-ShowAlerts", true); 25 | contentTable.checkPref("eui-ShowAlertsBottom", false); 26 | contentTable.checkPref("eui-ShowResourceRate", false); 27 | contentTable.checkPref("eui-ShowSchematicsTable", true); 28 | contentTable.checkPref("eui-ShowSchematicsPreview", true); 29 | contentTable.sliderPref("eui-SchematicsTableRows", 4, 2, 10, 1, i => i); 30 | contentTable.sliderPref("eui-SchematicsTableColumns", 5, 4, 8, 1, i => i); 31 | contentTable.sliderPref("eui-SchematicsTableButtonSize", 30, 20, 80, 5, i => i); 32 | contentTable.checkPref("eui-ShowEfficiency", false); 33 | contentTable.sliderPref("eui-EfficiencyTimer", 15, 10, 180, 5, i => i); 34 | contentTable.checkPref("eui-TrackPlayerCursor", false); 35 | contentTable.sliderPref("eui-playerCursorStyle", 7, 1, 7, 1, i => i); 36 | contentTable.checkPref("eui-ShowOwnCursor", false); 37 | contentTable.checkPref("eui-TrackLogicControl", false); 38 | contentTable.sliderPref("eui-maxZoom", 10, 1, 10, 1, i => i); 39 | contentTable.checkPref("eui-makeMineble", false); 40 | contentTable.checkPref("eui-showInteractSettings", true); 41 | contentTable.sliderPref("eui-action-delay", 500, 0, 3000, 25, i => i + " ms"); 42 | if (!Vars.mobile) { 43 | contentTable.checkPref("eui-DragBlock", false); 44 | contentTable.checkPref("eui-DragPathfind", false); 45 | } 46 | 47 | return contentTable; 48 | })()); 49 | }); 50 | -------------------------------------------------------------------------------- /scripts/ui/units/draw-cycle.js: -------------------------------------------------------------------------------- 1 | const bars = require("extended-ui/ui/units/health-shield-bar"); 2 | const playerTracker = require("extended-ui/ui/units/player-tracker"); 3 | const logicTracker = require("extended-ui/ui/units/logic-tracker"); 4 | 5 | const force = false; 6 | 7 | let showUnitBar; 8 | let trackPlayerCursor; 9 | let trackLogicControl; 10 | 11 | Events.run(Trigger.draw, () => { 12 | showUnitBar = Core.settings.getBool("eui-showUnitBar", true); 13 | trackPlayerCursor = Core.settings.getBool("eui-TrackPlayerCursor", false); 14 | trackLogicControl = Core.settings.getBool("eui-TrackLogicControl", false); 15 | 16 | if (!showUnitBar && !trackLogicControl && !trackPlayerCursor) return; 17 | 18 | Groups.unit.each((unit) => { 19 | if (Core.settings.getBool("eui-showUnitBar", true)) { 20 | if (bars.drawUnitHealthBar(unit, force)) { 21 | bars.drawUnitShieldBar(unit, true, force); 22 | } else { 23 | bars.drawUnitShieldBar(unit, false, force); 24 | } 25 | } 26 | if (Core.settings.getBool("eui-TrackPlayerCursor", false)) { 27 | let player = unit.player; 28 | if (player) { 29 | playerTracker.drawCursor(player); 30 | } 31 | } 32 | if (Core.settings.getBool("eui-TrackLogicControl", false)) { 33 | if (unit.controller instanceof LogicAI) { 34 | logicTracker.drawLogicLine(unit); 35 | } 36 | } 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /scripts/ui/units/health-shield-bar.js: -------------------------------------------------------------------------------- 1 | importPackage(Packages.arc.util.pooling); 2 | 3 | const settings = require("extended-ui/settings"); 4 | const iterationTools = require("extended-ui/utils/iteration-tools"); 5 | const barBuilder = require("extended-ui/utils/draw/bar-builder"); 6 | const camera = require("extended-ui/utils/camera"); 7 | 8 | const unitBarSize = settings.unitBarSize; 9 | 10 | const unitBarDisplayTime = settings.unitBarDisplayTime; 11 | const unitBarFadeTime = settings.unitBarFadeTime; 12 | const unitBarTotalDisplayTime = unitBarDisplayTime + unitBarFadeTime; 13 | 14 | let damaged = new Map(); 15 | 16 | Events.on(EventType.WorldLoadEvent, () => { 17 | damaged = new Map(); 18 | }); 19 | 20 | exports.drawUnitShieldBar = function(unit, offset, force) { 21 | let prevStatus = damaged.get(unit.id); 22 | 23 | let maxShield = 0; 24 | if (prevStatus && prevStatus.maxShield) { 25 | maxShield = prevStatus.maxShield 26 | } else { 27 | const shieldFinder = (ability) => { 28 | if (ability.max > maxShield) { 29 | maxShield = ability.max; 30 | } 31 | } 32 | 33 | iterationTools.iterateSeq(shieldFinder, unit.abilities.iterator()); 34 | if (!maxShield) { 35 | maxShield = 40; 36 | } 37 | } 38 | 39 | let value = unit.shield / maxShield; 40 | 41 | if (!prevStatus) { 42 | damaged.set(unit.id, {shield: value, maxShield: Math.max(unit.shield, maxShield), time: Date.now()}); 43 | } else if (prevStatus.shield != value) { 44 | prevStatus.shield = value; 45 | prevStatus.time = Date.now(); 46 | } 47 | if (!prevStatus.maxShield) { 48 | prevStatus.maxShield = Math.max(unit.shield, maxShield); 49 | } 50 | 51 | const unitX = unit.x; 52 | const unitY = offset ? unit.y + unitBarSize - 1 : unit.y; 53 | 54 | if (!isBarNecessary(unitX, unitY, value, prevStatus, force)) return; 55 | const alpha = getBarAlpha(prevStatus, force); 56 | 57 | // let text = barBuilder.buildPercentLabel(value); not sure about it. 58 | Draw.draw(Layer.overlayUI+0.01, ()=>{ 59 | barBuilder.draw( 60 | unitX, unitY+2, value, unit.hitSize/6, unitBarSize, "", Pal.accent, alpha 61 | ) 62 | }); 63 | } 64 | 65 | exports.drawUnitHealthBar = function(unit, force) { 66 | let value = unit.health / unit.maxHealth; 67 | let prevStatus = damaged.get(unit.id); 68 | 69 | if (!prevStatus) { 70 | damaged.set(unit.id, {health: value, time: Date.now()}); 71 | } else if (prevStatus.health != value) { 72 | prevStatus.health = value; 73 | prevStatus.time = Date.now(); 74 | } 75 | 76 | const unitX = unit.x; 77 | const unitY = unit.y; 78 | 79 | if (!isBarNecessary(unitX, unitY, value, prevStatus, force)) return; 80 | const alpha = getBarAlpha(prevStatus, force); 81 | 82 | // let text = barBuilder.buildPercentLabel(value); not sure about it. 83 | Draw.draw(Layer.overlayUI+0.01, ()=>{ 84 | barBuilder.draw( 85 | unitX, unitY+2, value, unit.hitSize/6, unitBarSize, "", Color.scarlet, alpha 86 | ) 87 | }); 88 | 89 | return true; 90 | } 91 | 92 | function isBarNecessary(x, y, value, prevStatus, force) { 93 | if (!camera.isIn(x, y)) return false; 94 | if (value <= 0) return false; 95 | if (force) return true; 96 | if (value >= 1) return false; 97 | if (!prevStatus) return true; 98 | 99 | if (Date.now() - prevStatus.time < unitBarTotalDisplayTime ) { 100 | return true; 101 | } else { 102 | return false; 103 | } 104 | } 105 | 106 | function getBarAlpha(prevStatus, force) { 107 | if (!prevStatus || force) return 1; 108 | 109 | return 1 - (Date.now() - prevStatus.time - unitBarDisplayTime)/unitBarFadeTime; 110 | } 111 | -------------------------------------------------------------------------------- /scripts/ui/units/logic-tracker.js: -------------------------------------------------------------------------------- 1 | exports.drawLogicLine = function(unit) { 2 | const unitX = unit.x; 3 | const unitY = unit.y; 4 | const processor = unit.controller.controller; 5 | if (!processor) return; 6 | const processorX = processor.x; 7 | const processorY = processor.y; 8 | 9 | Draw.draw(Layer.overlayUI+0.01, () => { 10 | Lines.stroke(1, Color.purple); 11 | Draw.alpha(0.7); 12 | Lines.line(unitX, unitY, processorX, processorY); 13 | Draw.reset(); 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /scripts/ui/units/player-tracker.js: -------------------------------------------------------------------------------- 1 | exports.drawCursor = function(player) { 2 | //For Players who's set up don't show the system cursor properly, at all or for the hell of it 3 | if (player == Vars.player && !Core.settings.getBool("eui-ShowOwnCursor", false)) return; 4 | 5 | const unitX = player.x; 6 | const unitY = player.y; 7 | const cursorX = player.mouseX; 8 | const cursorY = player.mouseY; 9 | const style = Core.settings.getInt("eui-playerCursorStyle"); 10 | const drawLine = () => { 11 | Lines.stroke(1, player.team().color); 12 | Draw.alpha(0.7); 13 | Lines.line(unitX, unitY, cursorX, cursorY); 14 | Draw.reset(); // it is necessary? 15 | } 16 | 17 | Draw.draw(Layer.overlayUI+0.01, () => { 18 | if (style == 1) { //square (Inspired from Mindustry Ranked Server's spectator mode ) 19 | Drawf.square(cursorX, cursorY, 2, player.team().color); 20 | } else if (style == 2) { //square + line 21 | drawLine() 22 | Drawf.square(cursorX, cursorY, 2, player.team().color); 23 | } else if (style == 3) { //Circle 24 | Drawf.circles(cursorX, cursorY, 1, player.team().color); 25 | } else if (style == 4) { //Circle + line 26 | drawLine() 27 | Drawf.circles(cursorX, cursorY, 1, player.team().color); 28 | } else if (style == 5) { //Target 29 | Drawf.target(cursorX, cursorY, 3, player.team().color); 30 | } else if (style == 6) { // Target + Line 31 | drawLine() 32 | Drawf.target(cursorX, cursorY, 3, player.team().color); 33 | } else { //Line (original) 34 | drawLine() 35 | } 36 | }); 37 | Draw.reset(); 38 | } -------------------------------------------------------------------------------- /scripts/ui/units/units-table-ui.js: -------------------------------------------------------------------------------- 1 | const unitsCounter = require("extended-ui/units/units-counter"); 2 | const barBuilder = require("extended-ui/utils/draw/bar-builder"); 3 | 4 | const granulatiry = 6; 5 | const maxToDisplay = 8; 6 | 7 | let prevUnitsUiVisible = true; 8 | let unitsUiVisible = true; 9 | let hideCoreUnits = false; 10 | let hideSupportUnits = false; 11 | let isBuilded = false; 12 | let holdedEntity = null; 13 | let hoveredEntity = null; 14 | let amountToDisplay = 0; 15 | let updateTimer = Date.now(); 16 | 17 | let overlayMarker; 18 | let contentTable; 19 | let unitTable; 20 | 21 | Events.run(Trigger.update, () => { 22 | if (!Core.settings.getBool("eui-ShowUnitTable", true)) { 23 | if (isBuilded) { 24 | clearTable(); 25 | } 26 | hoveredEntity = null; 27 | return; 28 | } 29 | 30 | if (hoveredEntity && !contentTable.hasMouse()) hoveredEntity = null; 31 | 32 | if (!overlayMarker) { 33 | setMarker(); 34 | } 35 | 36 | // updated so rarely because fast makes click on label impossible 37 | const timer = Date.now(); 38 | if (timer - 500 < updateTimer) return; 39 | updateTimer = timer; 40 | 41 | const unitsValueTop = unitsCounter.getUnitsValueTop(maxToDisplay, granulatiry, hideCoreUnits, hideSupportUnits); 42 | amountToDisplay = unitsValueTop.length; 43 | 44 | if (isRebuildNeeded()) { 45 | rebuildTable(); 46 | } 47 | 48 | 49 | unitTable.clearChildren() 50 | for (let [, teamInfo] of unitsValueTop) { 51 | const team = teamInfo.team; 52 | const teamUnits = teamInfo.units; 53 | 54 | for (let [, unitInfo] of Object.entries(teamUnits)) { 55 | const entity = unitInfo.entity; 56 | const amount = unitInfo.amount; 57 | unitTable.label(() => { 58 | return getTeamColor(team) + amount + '[white]'; 59 | }).left(); 60 | const image = unitTable.image(entity.type.icon(Cicon.small)).left().padRight(5).padBottom(2).maxSize(24).get(); 61 | image.hovered(() => { 62 | hoveredEntity = entity; 63 | }); 64 | image.clicked(() => { 65 | if (!holdedEntity || !isSameEntity(holdedEntity, entity)) { 66 | holdedEntity = entity; 67 | } else { 68 | holdedEntity = null; 69 | } 70 | }); 71 | 72 | if (holdedEntity && holdedEntity.dead && isSameEntity(holdedEntity, entity)) { 73 | holdedEntity = entity; 74 | } 75 | }; 76 | unitTable.row(); 77 | } 78 | }); 79 | 80 | Events.run(Trigger.draw, () => { 81 | if (!Core.settings.getBool("eui-ShowUnitTable", true)) return; 82 | let entity; 83 | if (hoveredEntity && !hoveredEntity.dead) { 84 | entity = hoveredEntity; 85 | } else if (holdedEntity && !holdedEntity.dead) { 86 | entity = holdedEntity; 87 | } else { 88 | return 89 | } 90 | 91 | Draw.draw(Layer.overlayUI+0.01, () => { 92 | let x; 93 | let y; 94 | 95 | if (Vars.player.unit() instanceof NullUnit) { 96 | const position = Core.camera.position; 97 | x = position.x; 98 | y = position.y; 99 | } else { 100 | const unit = Vars.player.unit(); 101 | x = unit.x; 102 | y = unit.y; 103 | } 104 | const distance = Mathf.dst(x, y, entity.x, entity.y); 105 | const text = Math.round(distance / 8).toString(); 106 | 107 | Draw.color(entity.team.color); 108 | Lines.line(x, y, entity.x, entity.y); 109 | if (distance > 80) barBuilder.drawLabel(text, x, y + 20, Color.white, true); 110 | }); 111 | }) 112 | 113 | Events.on(EventType.WorldLoadEvent, () => { 114 | holdedEntity = null; 115 | }); 116 | 117 | function rebuildTable() { 118 | clearTable(); 119 | buildTable(); 120 | } 121 | 122 | function clearTable() { 123 | if (!isBuilded) return; 124 | 125 | contentTable.clearChildren(); 126 | isBuilded = false; 127 | } 128 | 129 | function buildTable() { 130 | const buttonSize = 40; 131 | 132 | let unitTableButtons = contentTable.table().top().left().margin(3).get(); 133 | 134 | unitTableButtons.button(Icon.play, Styles.defaulti, () => { 135 | unitsUiVisible = !unitsUiVisible; 136 | }).width(buttonSize).height(buttonSize).pad(1).name("show").tooltip(Core.bundle.get("units-table.button.hide.tooltip")); 137 | 138 | let imageButton = unitTableButtons.button(new TextureRegionDrawable(Icon.players), Styles.defaulti, () => { 139 | hideCoreUnits = !hideCoreUnits; 140 | }).update(b => b.setChecked(hideCoreUnits)).width(buttonSize).height(buttonSize).pad(1).name("core-units").tooltip(Core.bundle.get("units-table.button.core-units.tooltip")).get(); 141 | imageButton.visibility = () => unitsUiVisible; 142 | imageButton.resizeImage(buttonSize*0.6); 143 | 144 | imageButton = unitTableButtons.button(new TextureRegionDrawable(Icon.github), Styles.defaulti, () => { 145 | hideSupportUnits = !hideSupportUnits; 146 | }).update(b => b.setChecked(hideSupportUnits)).width(buttonSize).height(buttonSize).pad(1).name("support-units").tooltip(Core.bundle.get("units-table.button.support-units.tooltip")).get(); 147 | imageButton.visibility = () => unitsUiVisible; 148 | imageButton.resizeImage(buttonSize*0.6); 149 | 150 | contentTable.row(); 151 | 152 | unitTable = contentTable.table().margin(3).get(); 153 | unitTable.visibility = () => unitsUiVisible; 154 | 155 | isBuilded = true; 156 | } 157 | 158 | function isRebuildNeeded() { 159 | if (!isBuilded) return true; 160 | return false; 161 | } 162 | 163 | function setMarker() { 164 | const contentTableStyle = Version.number > 6 ? Tex.buttonEdge4 : Styles.black3 165 | 166 | overlayMarker = Vars.ui.hudGroup.find("waves"); 167 | overlayMarker.row(); 168 | contentTable = overlayMarker.table(contentTableStyle).update((t) => { 169 | if (prevUnitsUiVisible != unitsUiVisible) { 170 | t.setBackground(unitsUiVisible ? contentTableStyle : Styles.none); 171 | prevUnitsUiVisible = unitsUiVisible; 172 | } 173 | }).name("unit-table").top().left().marginBottom(2).marginTop(2); 174 | contentTable = contentTable.get(); 175 | contentTable.visibility = () => isBuilded && Boolean(amountToDisplay); // Boolean really neccessary 176 | } 177 | 178 | function isSameEntity(entity1, entity2) { 179 | if (entity1 == null || entity2 == null) return false; 180 | return entity1.team == entity2.team && entity1.type == entity2.type; 181 | } 182 | 183 | function getTeamColor(team) { 184 | return "[#"+team.color.toString()+"]"; 185 | } 186 | -------------------------------------------------------------------------------- /scripts/units/core-units.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | 'alpha', 'beta', 'gamma' 3 | ] 4 | -------------------------------------------------------------------------------- /scripts/units/support-units.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | 'mono', 'poly', 'mega' 3 | ] 4 | -------------------------------------------------------------------------------- /scripts/units/units-counter.js: -------------------------------------------------------------------------------- 1 | const iterationTools = require("extended-ui/utils/iteration-tools"); 2 | const relativeValue = require("extended-ui/utils/relative-value"); 3 | const supportUnits = require("extended-ui/units/support-units"); 4 | const coreUnits = require("extended-ui/units/core-units"); 5 | 6 | exports.getUnitsValueTop = function(amountToDisplay, granulatiry, hideCoreUnits, hideSupportUnits) { 7 | let unitsIterator = Groups.unit.iterator(); 8 | let top = new Map(); 9 | 10 | let unitCounter = (unit) => { 11 | if (hideCoreUnits && coreUnits.includes(unit.type.toString())) return; 12 | if (hideSupportUnits && supportUnits.includes(unit.type.toString())) return; 13 | 14 | let team = unit.team; 15 | let units; 16 | 17 | if (!top.has(team.id)) { 18 | top.set(team.id, {team: team, units: {}}); 19 | } 20 | units = top.get(team.id).units; 21 | 22 | if (!units[unit.type]) { 23 | units[unit.type] = {amount: 0, entity: unit}; 24 | } 25 | units[unit.type].amount++; 26 | } 27 | 28 | iterationTools.iterateSeq(unitCounter, unitsIterator); 29 | 30 | top.forEach((teamInfo, team_id) => { 31 | let value = 0; 32 | let units = teamInfo.units; 33 | for (let unit in units) { 34 | let unitValue = units[unit].amount * relativeValue.getUnitValue(unit); 35 | units[unit].value = unitValue; 36 | value += unitValue; 37 | } 38 | top.get(team_id).units = Object.fromEntries( 39 | Object.entries(units) 40 | .sort(([,a],[,b]) => b.value - a.value) 41 | .slice(0, granulatiry) 42 | ); 43 | top.get(team_id).value = value; 44 | }) 45 | 46 | return Array.from(top.entries()).sort((a, b) => b[1].value - a[1].value).slice(0, amountToDisplay); 47 | } 48 | 49 | exports.isDangerous = function(unit) { 50 | let type = unit.type.toString(); 51 | if (coreUnits.includes(type)) return false; 52 | if (supportUnits.includes(type)) return false; 53 | return true; 54 | } 55 | -------------------------------------------------------------------------------- /scripts/utils/ai/adjacent-position.js: -------------------------------------------------------------------------------- 1 | exports.find = function(fromTile, toTile, targetBlock, ingoreBlocks) { 2 | if (!fromTile.build) return fromTile; 3 | 4 | const stepAmount = targetBlock.size/2 + fromTile.block().size/2; 5 | const perimeterTiles = getPerimeterTiles(fromTile, stepAmount); 6 | 7 | let tile = null; 8 | let minDistance = Infinity; 9 | perimeterTiles.forEach((t) => { 10 | const vectorX = toTile.x - t.x; 11 | const vectorY = toTile.y - t.y; 12 | const distance = Mathf.dst(vectorX, vectorY); 13 | 14 | if (distance < minDistance) { 15 | if (!ingoreBlocks && !(new BuildPlan(t.centerX(), t.centerY(), 0, targetBlock).placeable(Vars.player.team()))) return; 16 | 17 | minDistance = distance; 18 | tile = t; 19 | } 20 | }); 21 | 22 | return tile; 23 | } 24 | 25 | function getPerimeterTiles(tile, size) { 26 | const tiles = []; 27 | const x = tile.build.x/8; 28 | const y = tile.build.y/8; 29 | 30 | for (let i = -size; i <= size; i++) { 31 | for (let j = -size; j <= size; j++) { 32 | let xoffset = Math.abs(i); 33 | let yoffset = Math.abs(j); 34 | if (xoffset + yoffset >= size*2) continue; 35 | if (xoffset < size && yoffset < size) continue; 36 | tiles.push(Vars.world.tile(Math.floor(x + i), Math.floor(y + j))); 37 | } 38 | } 39 | 40 | return tiles; 41 | } 42 | -------------------------------------------------------------------------------- /scripts/utils/ai/pathfind.js: -------------------------------------------------------------------------------- 1 | const debug = false; 2 | let unpassebleTiles = []; 3 | let jumpPassebleTiles = []; 4 | let rotationBlockedTile = []; 5 | let linearPassebleTiles = []; 6 | 7 | if (debug) { 8 | Events.run(Trigger.draw, () => { 9 | Draw.draw(Layer.overlayUI+0.01, ()=>{ 10 | Lines.stroke(1, Color.green); 11 | for (let tile of linearPassebleTiles) { 12 | Lines.circle(tile.x * 8, tile.y * 8, 1); 13 | } 14 | Lines.stroke(1, Color.blue); 15 | for (let tile of rotationBlockedTile) { 16 | Lines.circle(tile.x * 8, tile.y * 8, 1); 17 | } 18 | Lines.stroke(1, Color.yellow); 19 | for (let tile of jumpPassebleTiles) { 20 | Lines.circle(tile.x * 8, tile.y * 8, 1); 21 | } 22 | Lines.stroke(1, Color.red); 23 | for (let tile of unpassebleTiles) { 24 | Lines.circle(tile.x * 8, tile.y * 8, 1); 25 | } 26 | Draw.reset(); 27 | }); 28 | }); 29 | } 30 | 31 | exports.conveyorPathfind = function (source, target, lastRotationTo, conveyor) { 32 | const placeable = (tile, rotationTo) => { 33 | return passable(tile, conveyor) && !tile.build && isNoTransportationContact(tile, conveyor); 34 | //(!tile.build || !isSameTransportationAxis(rotationTo, tile) && isPathClear(inverseRotation(rotationTo), tile)); 35 | } 36 | const pathLinear = new PathLinear(conveyor, placeable); 37 | const pathRotation = new PathRotation(conveyor, placeable, 1); 38 | const pathJumps = [new PathJump(Blocks.itemBridge, 3, 4)] 39 | //, new PathJump(Blocks.phaseConveyor, 11, 10)]; 40 | 41 | return pathfind(source, target, lastRotationTo, pathLinear, pathRotation, pathJumps); 42 | } 43 | 44 | exports.junctionPathfind = function (source, target, lastRotationTo, block) { 45 | const linearPlaceable = (tile, rotationTo) => { 46 | return passable(tile, block) && !tile.build; 47 | } 48 | const rotationPlaceable = (tile, rotationTo) => { 49 | return linearPlaceable(tile, rotationTo) && isNoTransportationContact(tile, block); 50 | } 51 | const pathLinear = new PathLinear(block, linearPlaceable); 52 | const pathRotation = new PathRotation(Blocks.underflowGate, rotationPlaceable, 1); 53 | const pathJumps = [new PathJump(Blocks.itemBridge, 3, 4)] 54 | // new PathJump(Blocks.phaseConveyor, 11, 10)]; 55 | 56 | return pathfind(source, target, lastRotationTo, pathLinear, pathRotation, pathJumps); 57 | } 58 | 59 | exports.gatePathfind = function (source, target, lastRotationTo) { 60 | return exports.junctionPathfind(source, target, lastRotationTo, Blocks.junction); 61 | } 62 | 63 | function pathfind(source, target, lastRotationTo, pathLinear, pathRotation, pathJumps) { 64 | const blockedTilesMap = new Map(); 65 | const tiles = blockPathfind(source, target, pathLinear, pathRotation, pathJumps, blockedTilesMap); 66 | if (!tiles) return []; 67 | 68 | const rotationsMap = makeRotationsMap(tiles); 69 | const jumpsMap = makeJumpsMap(tiles, blockedTilesMap, rotationsMap, pathJumps); 70 | global.eui.pm = blockedTilesMap; 71 | return planner(tiles, lastRotationTo, pathLinear, pathRotation, rotationsMap, jumpsMap); 72 | } 73 | 74 | function blockPathfind(source, target, pathLinear, pathRotation, pathJumps, unplacebleTilesJumps) { 75 | if (debug) { 76 | unpassebleTiles = []; 77 | jumpPassebleTiles = []; 78 | rotationBlockedTile = []; 79 | linearPassebleTiles = []; 80 | } 81 | if (!passable(target, pathLinear.block)) return null; 82 | 83 | const rotations = new Map(); 84 | const distanceSourceTarget = Math.abs(source.centerX() - target.centerX()) + Math.abs(source.centerY() - target.centerY()); 85 | const center = { x: (target.centerX() + source.centerX())/2, y: (target.centerY() + source.centerY())/2 }; 86 | const R = distanceSourceTarget/2; 87 | const R2 = R*R; 88 | 89 | const tilesSeq = Astar.pathfind(source, target, { 90 | cost: function (from, tile) { 91 | const isCheckedBefore = rotations.has(tile.pos()); 92 | if (isCheckedBefore) return 0; 93 | const rotationFrom = rotations.get(from.pos()); 94 | const rotationTo = tile.relativeTo(from); 95 | const isPathRotated = rotationFrom != rotationTo; 96 | const passableTile = pathLinear.placeable(tile, rotationTo); 97 | let cost = 1; 98 | 99 | if (rotationFrom == inverseRotation(rotationTo)) return 0; 100 | 101 | rotations.set(tile.pos(), rotationTo); 102 | 103 | const fromPossibleJumps = unplacebleTilesJumps.get(from.pos()); 104 | let jump; 105 | let minPathJump; 106 | if (fromPossibleJumps) { 107 | jump = fromPossibleJumps.find(jump => jump.direction == rotationTo); 108 | if (!jump) { 109 | if (debug) unpassebleTiles.push(tile); 110 | return 27145; 111 | } 112 | 113 | minPathJump = pathJumps.find(pathJump => pathJump.length >= jump.cost); 114 | if (!minPathJump) { 115 | if (debug) unpassebleTiles.push(tile); 116 | return 27145; 117 | } 118 | } 119 | 120 | if (isPathRotated && !pathRotation.placeable(from, rotationFrom)) { 121 | if (debug) { 122 | rotationBlockedTile.push(tile); 123 | } 124 | return 1000; 125 | } 126 | 127 | if (!passableTile) { 128 | if (debug) jumpPassebleTiles.push(tile); 129 | 130 | let tilePossibleJumps = unplacebleTilesJumps.get(tile.pos()); 131 | if (!tilePossibleJumps) tilePossibleJumps = []; 132 | 133 | if (!jump) { 134 | jump = new PossibleJump(rotationTo, 1); 135 | tilePossibleJumps.push(jump); 136 | } else { 137 | tilePossibleJumps.push(new PossibleJump(rotationTo, jump.cost + 1)); 138 | } 139 | 140 | unplacebleTilesJumps.set(tile.pos(), tilePossibleJumps); 141 | 142 | if (!minPathJump) { 143 | cost += pathJumps[0].cost; 144 | } else { 145 | cost += minPathJump.cost; 146 | } 147 | } else { 148 | if (debug) { 149 | linearPassebleTiles.push(tile); 150 | } 151 | } 152 | 153 | if (isPathRotated) cost += pathRotation.cost; 154 | 155 | const ore = tile.overlay().itemDrop; 156 | if (ore) { 157 | cost += gerOreCost(ore); 158 | } 159 | 160 | return cost; 161 | } 162 | }, (tile) => { 163 | const dx = tile.centerX() - center.x; 164 | const dy = tile.centerY() - center.y; 165 | const inCircle = dx * dx + dy * dy < R2 + 49; 166 | if (inCircle) return true; 167 | 168 | return false; 169 | }); 170 | 171 | const tiles = tilesSeq.removeAll((tile) => { 172 | return tile == null; 173 | }).toArray(); 174 | 175 | if (tiles.length > 0) { 176 | const tmp = []; 177 | tmp.push(source); 178 | for (let i = 0; i < tiles.length; i++) { 179 | tmp.push(tiles[i]); 180 | } 181 | return tmp; 182 | } 183 | return null; 184 | } 185 | 186 | function planner(tiles, lastRotationTo, pathLinear, pathRotation, rotationsMap, jumpsMap) { 187 | const makePlan = (tile, block, rotation) => { 188 | return new BuildPlan(tile.x, tile.y, rotation || 0, block); 189 | } 190 | 191 | const plans = []; 192 | const last = tiles[tiles.length - 1]; 193 | if (!last) return plans; 194 | 195 | let rotation; 196 | for (let i = 0; i < tiles.length - 1; i++) { 197 | let current = tiles[i]; 198 | let prev = tiles[i - 1]; 199 | let jump = jumpsMap.get(current.pos()); 200 | rotation = rotationsMap.get(current.pos()); 201 | let prevRotation = prev ? rotationsMap.get(prev.pos()) : -1; 202 | 203 | let buildPlan; 204 | if (jump) { 205 | if (typeof jump == "object") { 206 | let target = jump.target; 207 | let block = jump.pathJump.block; 208 | buildPlan = makePlan(current, block, rotation); 209 | 210 | if (target) { 211 | buildPlan.config = new Point2(target.x - current.x, target.y - current.y); 212 | } 213 | 214 | } 215 | } else if (rotation != prevRotation) { 216 | buildPlan = makePlan(current, pathRotation.block, rotation); 217 | } else { 218 | buildPlan = makePlan(current, pathLinear.block, rotation); 219 | } 220 | 221 | if (buildPlan) plans.push(buildPlan); 222 | } 223 | 224 | let lastRotation = rotationsMap.get(last.pos()); 225 | const lastRotationToBuild = lastRotationTo.build; 226 | if (lastRotationToBuild) { 227 | for (let i = 0; i < 4; i++) { 228 | let neighbour = last.nearby(i); 229 | 230 | if (neighbour.build != null && neighbour.build == lastRotationToBuild) { 231 | lastRotation = last.relativeTo(neighbour); 232 | } 233 | } 234 | } 235 | 236 | let lastPlan; 237 | if (jumpsMap.has(last.pos())) { 238 | lastPlan = makePlan(last, jumpsMap.get(last.pos()).pathJump.block, lastRotation); 239 | } else if (rotation != lastRotation && !pathLinear.block.rotate) { 240 | lastPlan = makePlan(last, pathRotation.block, lastRotation); 241 | } else { 242 | lastPlan = makePlan(last, pathLinear.block, lastRotation); 243 | } 244 | 245 | plans.push(lastPlan); 246 | return plans; 247 | } 248 | 249 | function PathLinear(block, placeable) { 250 | this.block = block; 251 | this.placeable = placeable; 252 | } 253 | 254 | function PathRotation(block, placeable, cost) { 255 | this.block = block; 256 | this.placeable = placeable; 257 | this.cost = cost; 258 | } 259 | 260 | function PathJump(block, length, cost) { 261 | this.block = block; 262 | this.length = length; 263 | this.cost = cost; 264 | } 265 | 266 | function PossibleJump(direction, cost) { 267 | this.direction = direction; 268 | this.cost = cost; 269 | } 270 | 271 | function makeRotationsMap(tiles) { 272 | const length = tiles.length; 273 | const rotationMap = new Map(); 274 | for (let i = 0; i < length - 1; i++) { 275 | let current = tiles[i]; 276 | let next = tiles[i + 1]; 277 | let rotation = current.relativeTo(next); 278 | rotationMap.set(current.pos(), rotation); 279 | } 280 | 281 | const last = tiles[length - 1]; 282 | const penultimate = tiles[length - 2]; 283 | 284 | const lastRoration = penultimate ? rotationMap.get(penultimate.pos()) : 0; 285 | 286 | rotationMap.set(last.pos(), lastRoration); 287 | 288 | return rotationMap; 289 | } 290 | 291 | function makeJumpsMap(tiles, blockedTilesMap, rotationsMap, pathJumps) { 292 | const length = tiles.length; 293 | const jumpsMap = new Map(); 294 | const tileblocked = (tile) => { 295 | if (!tile) return false; 296 | return blockedTilesMap.get(tile.pos()) && tile != tiles[length - 1]; 297 | } 298 | for (let i = 0; i < length; i++) { 299 | if (!tileblocked(tiles[i+1])) continue; 300 | if (jumpsMap.has(tiles[i].pos())) continue; 301 | 302 | let j = i; 303 | let maxJumpSize = 0; 304 | let jumpSize = 0; 305 | let matchingTiles = [tiles[i]]; 306 | 307 | do { 308 | j++; 309 | if (!tileblocked(tiles[j])) { 310 | matchingTiles.push(tiles[j]); 311 | if (maxJumpSize < jumpSize) maxJumpSize = jumpSize; 312 | jumpSize = 0; 313 | } else { 314 | jumpSize++; 315 | jumpsMap.set(tiles[j].pos(), 'overlap'); 316 | } 317 | } while (tileblocked(tiles[j]) || tileblocked(tiles[j + 1])); 318 | 319 | let minPathJump = pathJumps.find(pj => pj.length >= maxJumpSize); 320 | if (!minPathJump) return jumpsMap; 321 | 322 | for (let muchTileIndex = 0; muchTileIndex < matchingTiles.length - 1; muchTileIndex++) { 323 | let muchTile = matchingTiles[muchTileIndex]; 324 | let nextMuchTile = matchingTiles[muchTileIndex + 1]; 325 | 326 | jumpsMap.set(muchTile.pos(), {target: nextMuchTile, pathJump: minPathJump}); 327 | jumpsMap.set(nextMuchTile.pos(), {target: null, pathJump: minPathJump}); 328 | } 329 | } 330 | return jumpsMap; 331 | } 332 | 333 | function inverseRotation(rotation) { 334 | return (rotation + 2) % 4; 335 | } 336 | 337 | function isPathClear(direction, tile) { 338 | let tilex = tile.x; 339 | let tiley = tile.y; 340 | tilex += directionToX(direction); 341 | tiley += directionToY(direction); 342 | let tile = Vars.world.tile(tilex, tiley); 343 | if (!tile.build && tile.passable()) return true; 344 | return false; 345 | } 346 | 347 | function directionToX(direction) { 348 | if (direction % 2 != 0) return 0; 349 | return direction == 0 ? 1 : -1; 350 | } 351 | 352 | function directionToY(direction) { 353 | if (direction % 2 == 0) return 0; 354 | return direction == 1 ? -1 : 1; 355 | } 356 | 357 | function isSameTransportationAxis(rotation, tile) { 358 | return rotation % 2 == tile.build.rotation % 2; 359 | } 360 | 361 | function passable(tile, block) { 362 | return tile.passable() && (tile.block() == block || (new BuildPlan(tile.centerX(), tile.centerY(), 0, block)).placeable(Vars.player.team())); 363 | } 364 | 365 | function isNoTransportationContact (tile, block) { 366 | return isNoContact(tile, block, block => { 367 | return (block.group == BlockGroup.drills || 368 | (block.group == BlockGroup.transportation && !isLinearTransporter(block)) && !(block instanceof CoreBlock) || 369 | block instanceof GenericCrafter); 370 | }); 371 | } 372 | 373 | function isNoContact(tile, block, shouldAvoid) { 374 | let noContact = true; 375 | const edges = Edges.getEdges(block.size); 376 | edges.forEach(point2D => { 377 | let neighbour = Vars.world.tile(tile.x + point2D.x, tile.y + point2D.y); 378 | if (!neighbour) return; 379 | 380 | if (shouldAvoid(neighbour.block())) { 381 | noContact = false; 382 | } 383 | }); 384 | return noContact; 385 | } 386 | 387 | function gerOreCost(ore) { 388 | return 1 + ore.hardness; 389 | } 390 | 391 | function isLinearTransporter(block) { 392 | return block == Blocks.conveyor || 393 | block == Blocks.titaniumConveyor || 394 | block == Blocks.armoredConveyor || 395 | block == Blocks.plastaniumConveyor || 396 | block == Blocks.junction || 397 | block == Blocks.vault || 398 | block == Blocks.container || 399 | block == Blocks.conduit || 400 | block == Blocks.pulseConduit || 401 | block == Blocks.platedConduit; 402 | } 403 | -------------------------------------------------------------------------------- /scripts/utils/camera.js: -------------------------------------------------------------------------------- 1 | exports.isIn = function(x, y) { 2 | const camera = Core.camera; 3 | return (Math.abs(camera.position.x - x) < camera.width*0.5 && Math.abs(camera.position.y - y) < camera.height*0.5); 4 | } 5 | -------------------------------------------------------------------------------- /scripts/utils/difference.js: -------------------------------------------------------------------------------- 1 | module.exports = function (countTimer, startValue, episodeDuration) { 2 | this.episodeDuration = episodeDuration || 1000; 3 | this.countTimer = countTimer; 4 | this.episodesAmount = countTimer / this.episodeDuration; 5 | this.prevDifference = 0; 6 | this.currentValue = startValue || 0; 7 | this.currentTime = Time.time / 60 * 1000; // convert to milliseconds 8 | 9 | this.difference = (value) => { 10 | const time = Time.time / 60 * 1000; 11 | const currentDifference = value - this.currentValue; 12 | 13 | if (time - this.currentTime > this.countTimer) { 14 | this.prevDifference = currentDifference; 15 | this.currentValue = value; 16 | this.currentTime = time; 17 | return this.prevDifference / this.episodesAmount; 18 | } else { 19 | const measurement = (time - this.currentTime) / this.countTimer; 20 | 21 | const countedDifference = currentDifference*measurement / this.episodesAmount; 22 | const countedPrevDifference = this.prevDifference*(1 - measurement) / this.episodesAmount; 23 | return countedDifference + countedPrevDifference; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scripts/utils/draw/bar-builder.js: -------------------------------------------------------------------------------- 1 | importPackage(Packages.arc.util.pooling); 2 | 3 | const fontScale = 0.25 / Scl.scl(1.0); 4 | const borderSize = 1; 5 | 6 | exports.draw = function(drawX, drawY, value, targetSizeInBlocks, barSize, labelText, color, alpha) { 7 | if (!value) return; 8 | 9 | const blockPixelSize = targetSizeInBlocks*8; 10 | const startX = drawX - blockPixelSize/2 - barSize; 11 | const startY = drawY + blockPixelSize/2; 12 | const endY = startY + barSize; 13 | 14 | const barLenght = blockPixelSize + barSize*2; 15 | const innerBarLenght = barLenght - borderSize*2 16 | const barHeight = barSize; 17 | const innerBarHeight = barHeight - borderSize*2; 18 | 19 | const fillSize = (innerBarLenght) * value; 20 | 21 | Draw.z(Layer.darkness+1); 22 | 23 | Lines.stroke(borderSize, Pal.darkerGray); 24 | Draw.alpha(alpha); 25 | Lines.rect(startX, startY, barLenght, barHeight); 26 | 27 | Draw.color(color, alpha); 28 | Fill.rect(drawX - (innerBarLenght * (1 - value))/2, startY + barSize/2, fillSize, innerBarHeight); 29 | 30 | if (labelText) { 31 | exports.drawLabel(labelText, drawX, endY + 4, Color.white); 32 | } 33 | 34 | Draw.reset(); 35 | } 36 | 37 | exports.buildPercentLabel = function(value) { 38 | return Math.round(value*100) + "%"; 39 | } 40 | 41 | exports.buildValueLabel = function(value) { 42 | // might be useful in future 43 | } 44 | 45 | exports.drawLabel = function(text, x, y, color, useIntegerPositions) { 46 | const lay = Pools.obtain(GlyphLayout, prov(()=>{return new GlyphLayout()})); 47 | let font; 48 | 49 | //change font because outline works bad with integer position 50 | if (useIntegerPositions) { 51 | font = Fonts.def; 52 | font.setUseIntegerPositions(true); 53 | } else { 54 | font = Fonts.outline; 55 | font.setUseIntegerPositions(false); 56 | } 57 | 58 | font.getData().setScale(fontScale); 59 | 60 | lay.setText(font, text); 61 | 62 | font.setColor(color); 63 | font.draw(text, x - lay.width/2, y + lay.height/2); 64 | font.getData().setScale(1); 65 | 66 | Pools.free(lay); 67 | } 68 | -------------------------------------------------------------------------------- /scripts/utils/draw/build-plan.js: -------------------------------------------------------------------------------- 1 | exports.draw = function(plans) { 2 | Draw.draw(Layer.plans + 0.01, ()=>{ 3 | const echable = new Eachable(plans); 4 | plans.forEach(plan => { 5 | plan.block.drawRequestRegion(plan, echable); 6 | plan.block.drawRequestConfigTop(plan, echable); 7 | }); 8 | }); 9 | } 10 | 11 | exports.drawOne = function(plan) { 12 | exports.draw([plan]); 13 | } 14 | -------------------------------------------------------------------------------- /scripts/utils/draw/draw-tasks.js: -------------------------------------------------------------------------------- 1 | exports.divergingCircles = (x, y, parameters) => { 2 | if (!parameters) parameters = {}; 3 | const maxRadius = parameters.maxRadius || 2000; 4 | const startRadius = parameters.startRadius || 0; 5 | const color = parameters.color; 6 | const growSpeed = parameters.growSpeed || 1; 7 | const circlesAmount = parameters.circlesAmount || 3; 8 | 9 | let radius = startRadius; 10 | let startTime = Time.time; 11 | tasks.push(() => { 12 | Draw.color(color); 13 | for (let i = 0; i < circlesAmount; i++) { 14 | if (color) { 15 | Drawf.circles(x, y, radius * (1 + 0.2*i), color); 16 | } else { 17 | Drawf.circles(x, y, radius * (1 + 0.2*i)); 18 | } 19 | } 20 | radius += (Time.time - startTime) / 8 * growSpeed; 21 | if (radius > maxRadius) return true; 22 | }); 23 | } 24 | 25 | const tasks = []; 26 | 27 | Events.run(Trigger.draw, () => { 28 | Draw.draw(Layer.overlayUI+0.01, ()=>{ 29 | for (let task of tasks) { 30 | if (task()) { 31 | const index = tasks.indexOf(task); 32 | if (index > -1) tasks.splice(index, 1); 33 | } 34 | } 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /scripts/utils/event/drag.js: -------------------------------------------------------------------------------- 1 | const euiEvents = require("extended-ui/utils/event/events"); 2 | 3 | let startPos; 4 | let startTile; 5 | let dragging = false; 6 | 7 | euiEvents.eventType.dragStarted = "dragStarted"; 8 | euiEvents.eventType.dragged = "dragged"; 9 | euiEvents.eventType.dragEnded = "dragEnded"; 10 | 11 | 12 | Events.run(Trigger.update, () => { 13 | const tap = Core.input.keyTap(Packages.arc.input.KeyCode.mouseLeft); 14 | const release = Core.input.keyRelease(Packages.arc.input.KeyCode.mouseLeft); 15 | 16 | if (dragging || tap || release) { 17 | let pos = Core.input.mouseWorld(Core.input.mouseX(), Core.input.mouseY()); 18 | let mouseTile = Vars.world.tileWorld(pos.x, pos.y); 19 | 20 | if (!mouseTile) return; 21 | 22 | if (tap) { 23 | startPos = { x: pos.x, y: pos.y }; 24 | startTile = mouseTile; 25 | dragging = true; 26 | euiEvents.emit(euiEvents.eventType.dragStarted, startPos, startTile); 27 | } 28 | if (release) { 29 | if (dragging) { 30 | dragging = false; 31 | euiEvents.emit(euiEvents.eventType.dragEnded, startPos, startTile, pos, mouseTile); 32 | } 33 | } 34 | if (dragging) euiEvents.emit(euiEvents.eventType.dragged, startPos, startTile, pos, mouseTile); 35 | } 36 | }); 37 | -------------------------------------------------------------------------------- /scripts/utils/event/events.js: -------------------------------------------------------------------------------- 1 | exports.eventType = { 2 | schemSelectionButtonPresed: 'schemSelectionButtonPresed', 3 | schemSelectionEnd: 'schemSelectionEnd', 4 | } 5 | 6 | exports.on = function (event, listener) { 7 | if (typeof event === 'undefined') { 8 | throw new Error('Event name should be defined'); 9 | } 10 | if (typeof events[event] !== 'object') { 11 | events[event] = []; 12 | } 13 | 14 | events[event].push(listener); 15 | }; 16 | 17 | exports.removeListener = function (event, listener) { 18 | if (typeof event === 'undefined') { 19 | throw new Error('Event name should be defined'); 20 | } 21 | let idx; 22 | 23 | if (typeof events[event] === 'object') { 24 | idx = events[event].indexOf(listener); 25 | 26 | if (idx > -1) { 27 | events[event].splice(idx, 1); 28 | } 29 | } 30 | }; 31 | 32 | exports.emit = function (event) { 33 | if (typeof event === 'undefined') { 34 | print('EUI_WARNING: undefined event emitted'); 35 | } 36 | let i, listeners, length, args = [].slice.call(arguments, 1); 37 | 38 | if (typeof events[event] == 'object') { 39 | listeners = events[event].slice(); 40 | length = listeners.length; 41 | 42 | for (i = 0; i < length; i++) { 43 | listeners[i].apply(this, args); 44 | } 45 | } 46 | }; 47 | 48 | exports.once = function (event, listener) { 49 | exports.on(event, function g () { 50 | exports.removeListener(event, g); 51 | listener.apply(this, arguments); 52 | }); 53 | }; 54 | 55 | const events = {}; 56 | global.eui.events = events; 57 | -------------------------------------------------------------------------------- /scripts/utils/formatting.js: -------------------------------------------------------------------------------- 1 | exports.powerToString = function(currentNetPower, graphs) { 2 | let num = Math.round(currentNetPower*60); 3 | 4 | let graphString = graphs.length > 1 ? " (sep " + graphs.length + ')' : ''; 5 | let sign = num > 0 ? '+' : ''; 6 | let color = num >= 0 ? '[stat]' : '[red]'; 7 | 8 | let powerString = exports.numberToString(num, 1); 9 | return color + sign + powerString + '[white]' + graphString; 10 | } 11 | 12 | exports.numberToString = function(num, triplets) { 13 | triplets = triplets || 0; 14 | let power = Math.floor(Math.log(Math.abs(num)) / Math.log(1000)) - triplets; 15 | if (power > 0) { 16 | // somehow crashed on this string one time. Idk how 17 | // TODO remove this stupid hack 18 | try { 19 | return num.toString().slice(0, (-3*power + 1)).splice(-1, '.') + 'k'.repeat(power); 20 | } catch (e) { 21 | return num.toString(); 22 | } 23 | } else { 24 | return num.toString(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scripts/utils/icons.js: -------------------------------------------------------------------------------- 1 | exports.getAll = function() { 2 | return Object.assign({}, allIcons, allSprites); 3 | } 4 | 5 | exports.getIcons = function() { 6 | if (Object.keys(allIcons).length == 0) { 7 | Icon.icons.each((name, icon) => { 8 | allIcons[name] = new TextureRegionDrawable(icon); 9 | }); 10 | } 11 | return allIcons; 12 | } 13 | 14 | exports.getSprites = function() { 15 | if (Object.keys(allSprites).length == 0) { 16 | setupSprites(); 17 | } 18 | return allSprites; 19 | } 20 | 21 | exports.getUnitSprites = function() { 22 | if (spriteStorage.length == 0) setupSprites(); 23 | return spriteStorage[2]; 24 | } 25 | 26 | exports.getByName = function(name) { 27 | return exports.getIcons()[name] || exports.getSprites()[name] || new TextureRegionDrawable(Icon.pencil); 28 | } 29 | 30 | const spriteClasses = [Items, Liquids, UnitTypes, StatusEffects, Blocks] 31 | const spriteStorage = []; 32 | let allIcons = {}; 33 | let allSprites = {}; 34 | 35 | function setupSprites() { 36 | for (let spriteClass of spriteClasses) { 37 | let sprites = []; 38 | for (let key in spriteClass) { 39 | let item = spriteClass[key]; 40 | if (!item || !(typeof item.icon === 'function')) continue; 41 | try { 42 | sprites[item.name] = new TextureRegionDrawable(item.icon(Cicon.medium)); 43 | allSprites[item.name] = new TextureRegionDrawable(item.icon(Cicon.medium)); 44 | } catch (e) {} 45 | } 46 | spriteStorage.push(sprites); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /scripts/utils/iteration-tools.js: -------------------------------------------------------------------------------- 1 | exports.iterateSeq = function (func, seqIterator) { 2 | while ( seqIterator.hasNext() ) { 3 | func(seqIterator.next()); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /scripts/utils/output-wrapper.js: -------------------------------------------------------------------------------- 1 | exports.debug = function(text) { 2 | let properties = {showTime: 10}; 3 | exports.addInQueue(() => (Vars.ui.announce(text, 10)), properties); 4 | } 5 | 6 | exports.ingameAlert = function(text, drawTask) { 7 | let properties = { 8 | showTime: 5, 9 | delayer() { 10 | return !Vars.ui.hudfrag.shown; 11 | }, 12 | }; 13 | exports.addInQueue(() => { 14 | showToast(Icon.warning, text); 15 | if (drawTask) drawTask(); 16 | }, properties); 17 | } 18 | 19 | exports.addInQueue = function(sender, properties) { 20 | const queueItem = new QueueItem(sender, properties) 21 | 22 | let size = queue.push(queueItem); 23 | if (size > maxQueueSize) queue.shift(); 24 | } 25 | 26 | Events.run(Trigger.update, () => { 27 | let item = QueuePop(); 28 | if (item) { 29 | item.sender(); 30 | } 31 | }); 32 | 33 | const maxQueueSize = 50; 34 | let nextTime = Date.now() + 10000; 35 | let id = 0; 36 | let queue = []; 37 | 38 | function QueueItem(sender, properties) { 39 | this.id = ++id; 40 | this.sender = sender; 41 | if (!properties) properties = {}; 42 | 43 | this.delayer = properties.delayer || (() => false); 44 | this.name = properties.name || null; 45 | this.showTime = properties.showTime || 0; 46 | } 47 | 48 | function QueuePop() { 49 | if (nextTime > Date.now() || queue.length == 0) return; 50 | 51 | let item = queue.shift(); 52 | 53 | if (item.delayer()) { 54 | queue.push(item); 55 | nextTime = Date.now() + 1000; 56 | return; 57 | } 58 | 59 | nextTime = Date.now() + item.showTime*1000; 60 | return item; 61 | } 62 | 63 | // from https://github.com/QmelZ/hackustry/blob/master/scripts/libs/toast.js 64 | function showToast(icon, text){ 65 | if(!icon || !text) return; 66 | 67 | let table = new Table(Tex.button); 68 | table.update(() => { 69 | if(!Vars.ui.hudfrag.shown) table.remove(); 70 | }); 71 | table.margin(12); 72 | table.image(icon).pad(3); 73 | table.add(text).wrap().width(280).get().setAlignment(Align.center, Align.center); 74 | table.pack(); 75 | 76 | let container = Core.scene.table(); 77 | if (Core.settings.getBool("eui-ShowAlertsBottom", false)) { 78 | //TODO what is this random numbers? (4.2, 4.8) 79 | container.setTranslation(0, -table.getMarginBottom() * 4.2); 80 | Vars.state.isMenu() ? container.bottom().left().add(table) : container.bottom().add(table); 81 | container.actions( 82 | Actions.translateBy(0, table.getMarginBottom() * 4.2, 1, Interp.fade), 83 | Actions.delay(2), 84 | Actions.run(() => container.actions( 85 | Actions.translateBy(0, -table.getMarginBottom() * 4.8 , 1, Interp.fade), 86 | Actions.remove() 87 | )) 88 | ); 89 | } else { 90 | Vars.state.isMenu() ? container.top().right().add(table) : container.top().add(table); 91 | container.setTranslation(0, table.getPrefHeight()); 92 | container.actions( 93 | Actions.translateBy(0, -table.getPrefHeight(), 1, Interp.fade), 94 | Actions.delay(2.5), 95 | Actions.run(() => container.actions( 96 | Actions.translateBy(0, table.getPrefHeight(), 1, Interp.fade), 97 | Actions.remove() 98 | )) 99 | ); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /scripts/utils/polyfill.js: -------------------------------------------------------------------------------- 1 | Object.fromEntries = function(entries) { 2 | if (!entries || !entries[Symbol.iterator]) { throw new Error('Object.fromEntries() requires a single iterable argument'); } 3 | let obj = {}; 4 | for (let [key, value] of entries) { 5 | obj[key] = value; 6 | } 7 | return obj; 8 | }; 9 | 10 | Object.entries = function(obj) { 11 | let ownProps = Object.keys( obj ); 12 | let i = ownProps.length; 13 | let resArray = new Array(i); 14 | while (i--) 15 | resArray[i] = [ownProps[i], obj[ownProps[i]]]; 16 | 17 | return resArray; 18 | }; 19 | 20 | if (String.prototype.splice === undefined) { 21 | /** 22 | * Splices text within a string. 23 | * @param {int} offset The position to insert the text at (before) 24 | * @param {string} text The text to insert 25 | * @param {int} [removeCount=0] An optional number of characters to overwrite 26 | * @returns {string} A modified string containing the spliced text. 27 | */ 28 | String.prototype.splice = function(offset, text, removeCount) { 29 | removeCount = removeCount || 0; 30 | let calculatedOffset = offset < 0 ? this.length + offset : offset; 31 | return this.substring(0, calculatedOffset) + 32 | text + this.substring(calculatedOffset + removeCount); 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /scripts/utils/relative-value.js: -------------------------------------------------------------------------------- 1 | exports.getResourceValue = function(name) { 2 | return resourceValues[name] || 0; 3 | } 4 | exports.getUnitValue = function(name) { 5 | return unitsValues[name] || 0; 6 | } 7 | 8 | let resourceValues = { 9 | copper: 1/2.90, 10 | lead: 1/2.90, 11 | sand: 1/3.42, 12 | coal: 1/2.52, 13 | titanium: 1/2.23, 14 | thorium: 1/1.99, 15 | } 16 | 17 | resourceValues.energy = resourceValues.coal/120; 18 | resourceValues.water = resourceValues.energy*3; 19 | resourceValues.oil = resourceValues.energy*180/15 + resourceValues.water*9/15 + resourceValues.sand/15; 20 | resourceValues.cryo = resourceValues.energy*5 + resourceValues.water + resourceValues.titanium/24; 21 | resourceValues.graphite = resourceValues.coal*1.7; 22 | resourceValues.silicon = resourceValues.sand*2 + resourceValues.coal*1 + resourceValues.energy*20; 23 | resourceValues.metaglss = resourceValues.sand*1 + resourceValues.lead*1 + resourceValues.energy*30; 24 | resourceValues.plastanium = resourceValues.titanium*2 + resourceValues.energy*180 + resourceValues.oil*15; 25 | resourceValues.phaseFabric = resourceValues.sand*10 + resourceValues.thorium*4 + resourceValues.energy*600; 26 | resourceValues.surgeAlloy = resourceValues.silicon*3 + resourceValues.titanium*2 + resourceValues.copper*7 + resourceValues.energy*240/1.25; 27 | 28 | let tierValues = { 29 | second: resourceValues.silicon*40 + resourceValues.graphite*40 + resourceValues.energy*180*10 30 | } 31 | 32 | tierValues.third = tierValues.second + resourceValues.silicon*130 + resourceValues.titanium*80 + resourceValues.metaglss*40 + resourceValues.energy*360*30; 33 | tierValues.fourth = tierValues.third + resourceValues.silicon*850 + resourceValues.titanium*750 + resourceValues.plastanium*650 34 | + resourceValues.cryo*60*90, resourceValues.energy*780*90; 35 | tierValues.fifth = tierValues.fourth + resourceValues.silicon*1000 + resourceValues.plastanium*600 + resourceValues.surgeAlloy*500 36 | + resourceValues.phaseFabric*350 + resourceValues.cryo*180*240 + resourceValues.energy*1500*240; 37 | 38 | let unitsValues = { 39 | alpha: 1, 40 | beta: 2, 41 | gamma: 3, 42 | dagger: resourceValues.silicon*10 + resourceValues.lead*10 + resourceValues.energy*72*15, 43 | nova: resourceValues.silicon*30 + resourceValues.lead*20 + resourceValues.titanium*20 + resourceValues.energy*72*40, 44 | crawler: resourceValues.coal*20 + resourceValues.silicon*10 + resourceValues.energy*72*12, 45 | flare: resourceValues.silicon*15 + resourceValues.energy*72*15, 46 | mono: resourceValues.silicon*30 + resourceValues.lead*15 + resourceValues.energy*72*35, 47 | risso: resourceValues.silicon*20 + resourceValues.metaglss*35 + resourceValues.energy*72*45, 48 | retusa: resourceValues.silicon*15 + resourceValues.metaglss*25 + resourceValues.titanium*20 + resourceValues.energy*72*50 49 | } 50 | 51 | { 52 | const anotherUnits = [ 53 | { 54 | name: "mace", base: "dagger", tier: "second" 55 | }, 56 | { 57 | name: "fortress", base: "dagger", tier: "third" 58 | }, 59 | { 60 | name: "scepter", base: "dagger", tier: "fourth" 61 | }, 62 | { 63 | name: "reign", base: "dagger", tier: "fifth" 64 | }, 65 | { 66 | name: "pulsar", base: "nova", tier: "second" 67 | }, 68 | { 69 | name: "quasar", base: "nova", tier: "third" 70 | }, 71 | { 72 | name: "vela", base: "nova", tier: "fourth" 73 | }, 74 | { 75 | name: "corvus", base: "nova", tier: "fifth" 76 | }, 77 | { 78 | name: "atrax", base: "crawler", tier: "second" 79 | }, 80 | { 81 | name: "spiroct", base: "crawler", tier: "third" 82 | }, 83 | { 84 | name: "arkyid", base: "crawler", tier: "fourth" 85 | }, 86 | { 87 | name: "toxopid", base: "crawler", tier: "fifth" 88 | }, 89 | { 90 | name: "horizon", base: "flare", tier: "second" 91 | }, 92 | { 93 | name: "zenith", base: "flare", tier: "third" 94 | }, 95 | { 96 | name: "antumbra", base: "flare", tier: "fourth" 97 | }, 98 | { 99 | name: "eclipse", base: "flare", tier: "fifth" 100 | }, 101 | { 102 | name: "poly", base: "mono", tier: "second" 103 | }, 104 | { 105 | name: "mega", base: "mono", tier: "third" 106 | }, 107 | { 108 | name: "quad", base: "mono", tier: "fourth" 109 | }, 110 | { 111 | name: "oct", base: "mono", tier: "fifth" 112 | }, 113 | { 114 | name: "minke", base: "risso", tier: "second" 115 | }, 116 | { 117 | name: "bryde", base: "risso", tier: "third" 118 | }, 119 | { 120 | name: "sei", base: "risso", tier: "fourth" 121 | }, 122 | { 123 | name: "omura", base: "risso", tier: "fifth" 124 | }, 125 | { 126 | name: "oxynoe", base: "retusa", tier: "second" 127 | }, 128 | { 129 | name: "cyerce", base: "retusa", tier: "third" 130 | }, 131 | { 132 | name: "aegires", base: "retusa", tier: "fourth" 133 | }, 134 | { 135 | name: "navanax", base: "retusa", tier: "fifth" 136 | }, 137 | ] 138 | 139 | for (let unit of anotherUnits) { 140 | unitsValues[unit.name] = unitsValues[unit.base] + tierValues[unit.tier]; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /scripts/utils/schematics.js: -------------------------------------------------------------------------------- 1 | // from https://github.com/Pointifix/EvictionToolkit/blob/main/MindustryToolkit/scripts/schematics.js 2 | 3 | exports.create = function(x, y, x2, y2) { 4 | if (x == x2 && y == y2) return null; 5 | 6 | let ox = x, oy = y, ox2 = x2, oy2 = y2; 7 | 8 | let tiles = new Seq(); 9 | 10 | let minx = x2, miny = y2, maxx = x, maxy = y; 11 | let found = false; 12 | for(let cx = x; cx <= x2; cx++){ 13 | for(let cy = y; cy <= y2; cy++){ 14 | let linked = Vars.world.build(cx, cy); 15 | let realBlock = linked == null ? null : linked instanceof ConstructBlock.ConstructBuild ? cons.current : linked.block; 16 | 17 | if(linked != null && realBlock != null && (realBlock.isVisible() || realBlock instanceof CoreBlock)){ 18 | let top = realBlock.size/2; 19 | let bot = realBlock.size % 2 == 1 ? -realBlock.size/2 : -(realBlock.size - 1)/2; 20 | minx = Math.min(linked.tileX() + bot, minx); 21 | miny = Math.min(linked.tileY() + bot, miny); 22 | maxx = Math.max(linked.tileX() + top, maxx); 23 | maxy = Math.max(linked.tileY() + top, maxy); 24 | found = true; 25 | } 26 | } 27 | } 28 | 29 | if(found){ 30 | x = minx; 31 | y = miny; 32 | x2 = maxx; 33 | y2 = maxy; 34 | }else{ 35 | return new Schematic(new Seq(), new StringMap(), 1, 1); 36 | } 37 | 38 | let width = x2 - x + 1, height = y2 - y + 1; 39 | let offsetX = -x, offsetY = -y; 40 | let counted = new IntSet(); 41 | for(let cx = ox; cx <= ox2; cx++){ 42 | for(let cy = oy; cy <= oy2; cy++){ 43 | let tile = Vars.world.build(cx, cy); 44 | let realBlock = tile == null ? null : tile instanceof ConstructBlock.ConstructBuild ? cons.current : tile.block; 45 | 46 | if(tile != null && !counted.contains(tile.pos()) && realBlock != null 47 | && (realBlock.isVisible() || realBlock instanceof CoreBlock)){ 48 | let config = tile instanceof ConstructBlock.ConstructBuild ? cons.lastConfig : tile.config(); 49 | 50 | try { 51 | tiles.add(new Schematic.Stile(realBlock, tile.tileX() + offsetX, tile.tileY() + offsetY, config, tile.rotation)); 52 | } catch (e) { 53 | // is message neded? Sometimes it throws an error but error is not important 54 | } 55 | counted.add(tile.pos()); 56 | } 57 | } 58 | } 59 | 60 | return new Schematic(tiles, new StringMap(), width, height); 61 | } 62 | --------------------------------------------------------------------------------