├── locale └── .gitignore ├── thumbnail.png ├── sounds └── attacks │ ├── acid-end.ogg │ └── acid-mid.ogg ├── graphics ├── tiles │ └── fillableDirt │ │ ├── dirt1.png │ │ ├── dirt2.png │ │ ├── dirt4.png │ │ ├── dirt-side.png │ │ ├── dirt-inner-corner.png │ │ └── dirt-outer-corner.png ├── icons │ └── thief │ │ ├── crystal-drain.png │ │ └── crystal-drain-pole.png ├── acid-splash-purple │ ├── splash-1.png │ ├── splash-2.png │ ├── splash-3.png │ └── splash-4.png ├── entities │ ├── thief │ │ ├── crystal-drain.png │ │ └── crystal-drain-pole.png │ └── tunnel │ │ └── tunnelEntrance.png └── acid-projectile-purple │ ├── acid-projectile-purple.png │ └── acid-projectile-purple-shadow.png ├── visualizer.sh ├── info.json ├── .luacheckrc ├── .gitignore ├── NOTICE ├── .github └── FUNDING.yml ├── prototypes ├── utils │ ├── ColorUtils.lua │ ├── StickerUtils.lua │ ├── BombUtils.lua │ ├── ThiefUtils.lua │ ├── ProjectileUtils.lua │ ├── AttackFlame.lua │ ├── UnitSpawnerUtils.lua │ ├── StreamUtils.lua │ ├── UpdatesVanilla.lua │ ├── BeamUtils.lua │ ├── AttackBall.lua │ ├── ImageUtils.lua │ └── DroneUtils.lua ├── buildings │ ├── tunnel.lua │ ├── ChunkScanner.lua │ └── UpdatesVanilla.lua ├── UnitClasses.lua ├── tile │ └── fillableDirt.lua ├── Poison.lua ├── samples │ └── healingBiter.lua └── EnergyThief.lua ├── .dir-locals.el ├── data-updates.lua ├── README.md ├── action.fish ├── libs ├── Utils.lua ├── UnitUtils.lua └── MathUtils.lua ├── data-final-fixes.lua ├── data.lua ├── visualizer ├── parseState.rkt └── visual.rkt └── tests.lua /locale/.gitignore: -------------------------------------------------------------------------------- 1 | /.directory 2 | -------------------------------------------------------------------------------- /thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/thumbnail.png -------------------------------------------------------------------------------- /sounds/attacks/acid-end.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/sounds/attacks/acid-end.ogg -------------------------------------------------------------------------------- /sounds/attacks/acid-mid.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/sounds/attacks/acid-mid.ogg -------------------------------------------------------------------------------- /graphics/tiles/fillableDirt/dirt1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/tiles/fillableDirt/dirt1.png -------------------------------------------------------------------------------- /graphics/tiles/fillableDirt/dirt2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/tiles/fillableDirt/dirt2.png -------------------------------------------------------------------------------- /graphics/tiles/fillableDirt/dirt4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/tiles/fillableDirt/dirt4.png -------------------------------------------------------------------------------- /graphics/icons/thief/crystal-drain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/icons/thief/crystal-drain.png -------------------------------------------------------------------------------- /graphics/acid-splash-purple/splash-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/acid-splash-purple/splash-1.png -------------------------------------------------------------------------------- /graphics/acid-splash-purple/splash-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/acid-splash-purple/splash-2.png -------------------------------------------------------------------------------- /graphics/acid-splash-purple/splash-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/acid-splash-purple/splash-3.png -------------------------------------------------------------------------------- /graphics/acid-splash-purple/splash-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/acid-splash-purple/splash-4.png -------------------------------------------------------------------------------- /graphics/entities/thief/crystal-drain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/entities/thief/crystal-drain.png -------------------------------------------------------------------------------- /graphics/tiles/fillableDirt/dirt-side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/tiles/fillableDirt/dirt-side.png -------------------------------------------------------------------------------- /graphics/entities/tunnel/tunnelEntrance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/entities/tunnel/tunnelEntrance.png -------------------------------------------------------------------------------- /graphics/icons/thief/crystal-drain-pole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/icons/thief/crystal-drain-pole.png -------------------------------------------------------------------------------- /graphics/entities/thief/crystal-drain-pole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/entities/thief/crystal-drain-pole.png -------------------------------------------------------------------------------- /graphics/tiles/fillableDirt/dirt-inner-corner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/tiles/fillableDirt/dirt-inner-corner.png -------------------------------------------------------------------------------- /graphics/tiles/fillableDirt/dirt-outer-corner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/tiles/fillableDirt/dirt-outer-corner.png -------------------------------------------------------------------------------- /graphics/acid-projectile-purple/acid-projectile-purple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/acid-projectile-purple/acid-projectile-purple.png -------------------------------------------------------------------------------- /visualizer.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env racket 2 | #lang racket 3 | 4 | (require "visualizer/visual.rkt") 5 | (provide (all-from-out "visualizer/visual.rkt")) 6 | 7 | (visualize) 8 | -------------------------------------------------------------------------------- /graphics/acid-projectile-purple/acid-projectile-purple-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veden/Rampant/HEAD/graphics/acid-projectile-purple/acid-projectile-purple-shadow.png -------------------------------------------------------------------------------- /info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Rampant", 3 | "factorio_version" : "1.1", 4 | "version" : "3.3.4", 5 | "title" : "Rampant", 6 | "author" : "Veden", 7 | "homepage" : "https://forums.factorio.com/viewtopic.php?f=94&t=31445", 8 | "dependencies" : ["base >= 1.1.81", "? bobenemies", "? Natural_Evolution_Enemies >= 0.17.0", "? Clockwork", "? Kux-OrbitalIonCannon", "? Orbital Ion Cannon", "? ArmouredBiters", "? Krastorio2", "? SchallAlienLoot >= 0.17.6", "! zhcnremake"] 9 | } 10 | -------------------------------------------------------------------------------- /.luacheckrc: -------------------------------------------------------------------------------- 1 | globals = { 2 | "mods", 3 | "game", 4 | "util", 5 | "data", 6 | "remote", 7 | "settings", 8 | "commands", 9 | "global", 10 | "rendering", 11 | "table_size", 12 | "script", 13 | "defines", 14 | "ProcessorG", 15 | "ConstantsG", 16 | "ChunkPropertyUtilsG", 17 | "ChunkUtilsG", 18 | "MapUtilsG", 19 | "MathUtilsG", 20 | "SquadG", 21 | "unitUtilsG", 22 | "BaseUtilsG", 23 | "UtilsG" 24 | } 25 | 26 | max_line_length = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Lua sources 2 | luac.out 3 | 4 | # luarocks build files 5 | *.src.rock 6 | *.zip 7 | *.tar.gz 8 | 9 | # Object files 10 | *.o 11 | *.os 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | *.def 26 | *.exp 27 | 28 | # Shared objects (inc. Windows DLLs) 29 | *.dll 30 | *.so 31 | *.so.* 32 | *.dylib 33 | 34 | # Executables 35 | *.exe 36 | *.out 37 | *.app 38 | *.i*86 39 | *.x86_64 40 | *.hex 41 | *.bat 42 | 43 | # extra 44 | *.lua# 45 | *.rkt~ 46 | *.lua~ 47 | *.dumpjump 48 | /.emacs.desktop 49 | /.emacs.desktop.lock 50 | /README.html 51 | /c.org 52 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | 14 | ko_fi: rampant 15 | -------------------------------------------------------------------------------- /prototypes/utils/ColorUtils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local colorUtils = {} 18 | 19 | -- module code 20 | 21 | function colorUtils.makeColor(r_,g_,b_,a_) 22 | return { r = r_ * a_, g = g_ * a_, b = b_ * a_, a = a_ } 23 | end 24 | 25 | return colorUtils 26 | -------------------------------------------------------------------------------- /prototypes/buildings/tunnel.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | data:extend({ 18 | { 19 | type = "simple-entity", 20 | name = "tunnel-entrance-rampant", 21 | flags = {"placeable-neutral", "placeable-off-grid", "not-on-map"}, 22 | icon = "__base__/graphics/icons/small-scorchmark.png", 23 | icon_size = 32, 24 | subgroup = "grass", 25 | order = "b[decorative]-k[tunnel-entrance]-a[big]", 26 | collision_box = {{-1.3, -1.3}, {1.3, 1.3}}, 27 | selection_box = {{-1.5, -1.5}, {1.5, 1.5}}, 28 | render_layer = "remnants", 29 | destructible = "false", 30 | max_health = 1, 31 | pictures = 32 | { 33 | { 34 | filename = "__Rampant__/graphics/entities/tunnel/tunnelEntrance.png", 35 | width = 142, 36 | height = 104, 37 | shift = {0, 0} 38 | } 39 | } 40 | } 41 | }) 42 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ;; for configuration - projectile-project-configure-cmd 2 | ;; for compilation - projectile-project-compilation-cmd 3 | ;; for testing - projectile-project-test-cmd 4 | ;; for installation - projectile-project-install-cmd 5 | ;; for packaging - projectile-project-package-cmd 6 | ;; for running - projectile-project-run-cmd 7 | ;; for configuring the test prefix - projectile-project-test-prefix 8 | ;; for configuring the test suffix - projectile-project-test-suffix 9 | ;; for configuring the related-files-fn property - projectile-project-related-files-fn 10 | ;; for configuring the src-dir property - projectile-project-src-dir 11 | ;; for configuring the test-dir property - projectile-project-test-dir 12 | ;; projectile-configure-use-separate-buffer 13 | ;; projectile-compile-use-separate-buffer 14 | ;; projectile-test-use-separate-buffer 15 | ;; projectile-package-use-separate-buffer 16 | ;; projectile-run-use-separate-buffer 17 | ;; projectile-install-use-separate-buffer 18 | 19 | 20 | ((nil . ((projectile-project-install-cmd . "./make.sh copy") 21 | (projectile-install-buffer-suffix . "install") 22 | 23 | (projectile-project-compilation-cmd . "luacheck .") 24 | (projectile-compile-buffer-suffix . "lint") 25 | 26 | (projectile-project-package-cmd . "./make.sh zip") 27 | (projectile-package-buffer-suffix . "install") 28 | 29 | (projectile-project-uninstall-cmd . "./make.sh clear") 30 | (projectile-uninstall-buffer-suffix . "install") 31 | 32 | (projectile-project-test-cmd . "./make.sh visualize") 33 | (projectile-test-buffer-suffix . "visual") 34 | 35 | (projectile-project-run-cmd . "factorio")))) 36 | -------------------------------------------------------------------------------- /prototypes/buildings/ChunkScanner.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | local smokeUtils = require("prototypes/utils/SmokeUtils") 17 | 18 | smokeUtils.makeNewCloud( 19 | { 20 | name = "build-clear", 21 | wind = false, 22 | scale = 9, 23 | duration = 540, 24 | cooldown = 10, 25 | tint = { r=0.7, g=0.2, b=0.7 } 26 | }, 27 | { 28 | type = "area", 29 | radius = 17, 30 | force = "not-same", 31 | action_delivery = 32 | { 33 | type = "instant", 34 | target_effects = 35 | { 36 | { 37 | type = "damage", 38 | damage = { amount = 1.1, type = "poison"} 39 | }, 40 | { 41 | type = "damage", 42 | damage = { amount = 1.1, type = "acid"} 43 | }, 44 | { 45 | type = "damage", 46 | damage = { amount = 1.1, type = "fire"} 47 | } 48 | } 49 | } 50 | } 51 | ) 52 | -------------------------------------------------------------------------------- /data-updates.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local vanillaUpdates = require("prototypes/utils/UpdatesVanilla") 18 | local attackBall = require("prototypes/utils/AttackBall") 19 | 20 | if settings.startup["rampant--useDumbProjectiles"].value or settings.startup["rampant--newEnemies"].value then 21 | attackBall.generateVanilla() 22 | vanillaUpdates.useDumbProjectiles() 23 | end 24 | 25 | for _, robot in pairs(data.raw["logistic-robot"]) do 26 | if (settings.startup["rampant--unkillableLogisticRobots"].value) then 27 | robot.resistances = {} 28 | for damageType, _ in pairs(data.raw["damage-type"]) do 29 | robot.resistances[damageType] = { 30 | type = damageType, 31 | percent = 100 32 | } 33 | end 34 | end 35 | end 36 | 37 | for _, robot in pairs(data.raw["construction-robot"]) do 38 | if (settings.startup["rampant--unkillableConstructionRobots"].value) then 39 | robot.resistances = {} 40 | for damageType, _ in pairs(data.raw["damage-type"]) do 41 | robot.resistances[damageType] = { 42 | type = damageType, 43 | percent = 100 44 | } 45 | end 46 | end 47 | end 48 | 49 | -------------------------------------------------------------------------------- /prototypes/UnitClasses.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | -- module code 18 | 19 | function generateMigration() 20 | local factions = {"neutral", "acid", "physical", "electric", "suicide", "nuclear", "fire", "inferno", "troll", "laser", "fast", "wasp", "spawner", "energy-thief", "poison"} 21 | 22 | for fi = 1, #factions do 23 | local faction = factions[fi] 24 | for t = 1, 10 do 25 | local adjT = t 26 | -- if t > 5 then 27 | -- adjT = t - 5 28 | -- end 29 | for v = 8, 20 do 30 | print(" [\"" .. faction .. "-biter-spawner-v" .. v .. "-t" .. t .. "-rampant\", \"" .. faction .. "-biter-spawner-v1-t" .. adjT .. "-rampant\"],") 31 | print(" [\"" .. faction .. "-spitter-spawner-v" .. v .. "-t" .. t .. "-rampant\", \"" .. faction .. "-spitter-spawner-v1-t" .. adjT .. "-rampant\"],") 32 | print(" [\"" .. faction .. "-worm-v" .. v .. "-t" .. t .. "-rampant\", \"" .. faction .. "-worm-v1-t" .. adjT .. "-rampant\"],") 33 | print(" [\"" .. faction .. "-hive-v" .. v .. "-t" .. t .. "-rampant\", \"" .. faction .. "-hive-v1-t" .. adjT .. "-rampant\"],") 34 | print(" [\"" .. faction .. "-biter-v" .. v .. "-t" .. t .. "-rampant\", \"" .. faction .. "-biter-v1-t" .. adjT .. "-rampant\"],") 35 | print(" [\"" .. faction .. "-spitter-v" .. v .. "-t" .. t .. "-rampant\", \"" .. faction .. "-spitter-v1-t" .. adjT .. "-rampant\"],") 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /prototypes/buildings/UpdatesVanilla.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local vanillaUpdates = {} 18 | 19 | function vanillaUpdates.addWallAcidResistance() 20 | local walls = data.raw["wall"] 21 | 22 | for _,wall in pairs(walls) do 23 | local foundAcid = false 24 | if wall.resistances then 25 | for _,resistance in pairs(wall.resistances) do 26 | if resistance.type == "acid" then 27 | if resistance.percent < 60 then 28 | resistance.percent = 60 29 | end 30 | foundAcid = true 31 | break 32 | end 33 | end 34 | if not foundAcid then 35 | wall.resistances[#wall.resistances+1] = {type="acid",percent=60} 36 | end 37 | end 38 | end 39 | 40 | walls = data.raw["gate"] 41 | for _,wall in pairs(walls) do 42 | local foundAcid = false 43 | if wall.resistances then 44 | for _,resistance in pairs(wall.resistances) do 45 | if resistance.type == "acid" then 46 | if resistance.percent < 60 then 47 | resistance.percent = 60 48 | end 49 | foundAcid = true 50 | break 51 | end 52 | end 53 | if not foundAcid then 54 | wall.resistances[#wall.resistances+1] = {type="acid",percent=60} 55 | end 56 | end 57 | end 58 | end 59 | 60 | return vanillaUpdates 61 | -------------------------------------------------------------------------------- /prototypes/tile/fillableDirt.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | data:extend({ 18 | { 19 | type = "tile", 20 | name = "fillableDirt", 21 | needs_correction = false, 22 | collision_mask = 23 | { 24 | "water-tile", 25 | }, 26 | layer = 40, 27 | variants = 28 | { 29 | main = 30 | { 31 | { 32 | picture = "__Rampant__/graphics/tiles/fillableDirt/dirt1.png", 33 | count = 8, 34 | size = 1 35 | } 36 | , 37 | { 38 | picture = "__Rampant__/graphics/tiles/fillableDirt/dirt2.png", 39 | count = 8, 40 | size = 2 41 | }, 42 | { 43 | picture = "__Rampant__/graphics/tiles/fillableDirt/dirt4.png", 44 | count = 6, 45 | size = 4 46 | } 47 | }, 48 | inner_corner = 49 | { 50 | picture = "__Rampant__/graphics/tiles/fillableDirt/dirt-inner-corner.png", 51 | count = 6 52 | }, 53 | outer_corner = 54 | { 55 | picture = "__Rampant__/graphics/tiles/fillableDirt/dirt-outer-corner.png", 56 | count = 6 57 | }, 58 | side = 59 | { 60 | picture = "__Rampant__/graphics/tiles/fillableDirt/dirt-side.png", 61 | count = 8 62 | } 63 | }, 64 | map_color={r=0.4196, g=0.3294, b=0.2196}, 65 | ageing=0 66 | } 67 | }) 68 | -------------------------------------------------------------------------------- /prototypes/utils/StickerUtils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | 18 | local stickerUtils = {} 19 | 20 | -- imported 21 | 22 | local math3d = require("math3d") 23 | 24 | -- module code 25 | 26 | function stickerUtils.makeSticker(attributes) 27 | local name = attributes.name .. "-sticker-rampant" 28 | 29 | local o = { 30 | type = "sticker", 31 | name = name, 32 | flags = {"not-on-map"}, 33 | 34 | animation = attributes.stickerAnimation or 35 | { 36 | filename = "__base__/graphics/entity/fire-flame/fire-flame-13.png", 37 | line_length = 8, 38 | width = 60, 39 | height = 118, 40 | frame_count = 25, 41 | axially_symmetrical = false, 42 | direction_count = 1, 43 | blend_mode = "normal", 44 | animation_speed = 2, 45 | scale = 0.175, 46 | tint = attributes.tint2 or { r = 1, g = 1, b = 1, a = 0.35 }, 47 | shift = math3d.vector2.mul({-0.078125, -1.8125}, 0.1), 48 | }, 49 | 50 | duration_in_ticks = attributes.stickerDuration or (30 * 60), 51 | target_movement_modifier_from = attributes.stickerMovementModifier or 1, 52 | target_movement_modifier_to = 1, 53 | vehicle_speed_modifier_from = (attributes.stickerMovementModifier and math.min(attributes.stickerMovementModifier * 1.25, 1)) or 1, 54 | vehicle_speed_modifier_to = 1, 55 | 56 | stickers_per_square_meter = 2, 57 | 58 | damage_per_tick = attributes.stickerDamagePerTick and { amount = attributes.stickerDamagePerTick or 100 / 60, 59 | type = attributes.stickerDamagePerTickType or "fire" }, 60 | spread_fire_entity = attributes.spawnEntityName, 61 | fire_spread_cooldown = attributes.fireSpreadCooldown, 62 | fire_spread_radius = attributes.fireSpreadRadius 63 | } 64 | 65 | data:extend({o}) 66 | return name 67 | end 68 | 69 | 70 | return stickerUtils 71 | -------------------------------------------------------------------------------- /prototypes/utils/BombUtils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local bombUtils = {} 18 | 19 | function bombUtils.makeAtomicBlast(attributes) 20 | local name = attributes.name .. "-atomic-blast-rampant" 21 | data:extend({{ 22 | type = "projectile", 23 | name = name, 24 | flags = {"not-on-map"}, 25 | acceleration = 0, 26 | action = 27 | { 28 | { 29 | type = "direct", 30 | action_delivery = 31 | { 32 | type = "instant", 33 | target_effects = 34 | { 35 | { 36 | type = "create-entity", 37 | entity_name = "explosion" 38 | } 39 | } 40 | } 41 | }, 42 | { 43 | type = "area", 44 | radius = 3, 45 | action_delivery = 46 | { 47 | type = "instant", 48 | target_effects = 49 | { 50 | type = "damage", 51 | damage = {amount = (attributes.damage * 2) or 400, 52 | type = attributes.damageType or "explosion"} 53 | } 54 | } 55 | } 56 | }, 57 | animation = 58 | { 59 | filename = "__core__/graphics/empty.png", 60 | frame_count = 1, 61 | width = 1, 62 | height = 1, 63 | priority = "high" 64 | }, 65 | shadow = 66 | { 67 | filename = "__core__/graphics/empty.png", 68 | frame_count = 1, 69 | width = 1, 70 | height = 1, 71 | priority = "high" 72 | } 73 | }}) 74 | return name 75 | end 76 | 77 | return bombUtils 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rampant Tactics 2 | Factorio Mod - Improves the enemies tactics by using potential fields (pheromones) allowing probing of defenses, retreats, reinforcements, counterattacking, breaching, raids, rallying death cry, and player hunting. Uses blockable biter projectiles. Adds new Enemies which can be disabled in mod settings. Difficulty setting in mod options menu. 3 | 4 | # Site 5 | 6 | https://mods.factorio.com/mod/Rampant 7 | 8 | # Forum Post 9 | 10 | https://forums.factorio.com/viewtopic.php?f=94&t=31445 11 | 12 | # Features 13 | 14 | - New Enemy Factions - Neutral, Acid, Fast, Physical, Electric, Inferno, Suicide, Fire, Nuclear, Laser, Troll, Wasp, Spawner 15 | - Swarming - Units will smoothly slide by one another allowing for streamlined attacking 16 | - Difficulty Scaling - A mod option to control how quickly the ai can perform actions like making attack waves. 17 | - Nocturnal Mode - A mod option to force biters to only attack at night. Does not yet affect vanilla attacks. Best use with clockwork or daynight extender mod 18 | - Recycling Biters - When large groups of biters form on the game map and aren't assigned to a unit group or near a base will be periodically removed and refunded to the ai causing attack waves proportional to the number of units removed. 19 | - Breaching - When biters are destroying structures nearby unit groups will come to join them 20 | - Frenzy squads - When a unit group gets close to a player or start combat they switch to attacking everything in there path for a set radius or until there is nothing left 21 | - Rabid squads - Is in a permanent frenzied state as soon as the group is formed 22 | - Tactical Retreats - These will take place when a unit group is in a chunk that has reached a death threshold 23 | - Unit Group Forming - Any chunks with spawners in it that is covered by a pollution or player clouds will form groups based on the evolution factor 24 | - Probing Behavior Against Defenses - unit groups will attempt to avoid chunks that are soaked in death 25 | - Player Hunting - Unit groups will track the player based on there emitted pheromone cloud 26 | - Rallying Death Cry - When a unit is killed on a chunk that is past the retreat threshold, the unit will attempt to summon reinforcements to help them 27 | - Counterattacks - When the player is in combat near nests they will send reinforcements to unit groups 28 | - Reinforcements - Nests will send assistance to nearby nests under attack by the player 29 | - No Homing Projectiles - All projectiles are fired at locations and no longer track the player 30 | - Pathfinding - Unit groups will use potential fields to perform only single step pathfinding allowing for efficient and dynamic pathing 31 | - Peace mode - If something sets peace mode, Rampant will respect it 32 | - Ion Cannon Reaction - Firing the Ion Cannon will cause nests around the blast site to form into an attack wave and agitate all biters 33 | - Rocket Reaction - Firing the rocket from the rocket silo will cause the biters to form extra attack waves 34 | - Blockable Projectiles - Some of the biters projectiles can now be blocked by walls and trees 35 | - Raiding AI state - The AI will periodically send attack waves based on building proximity and not just pollution 36 | - Migration AI State - Where the ai looks for resources patches to setup new bases 37 | - Sieging AI state - Where the ai does a migration event but also builds towards the player and their base 38 | - Onslaught AI state - Where the ai gains 2x credits per logic cycle that can be used on units and buildings 39 | - Vanilla AI Replacement - The default expansion and attack waves can be completely turned off and allow Rampant to work its magic 40 | 41 | -------------------------------------------------------------------------------- /action.fish: -------------------------------------------------------------------------------- 1 | #!/bin/fish 2 | 3 | argparse --min-args=1 --max-args=1 'dir=?' 'serverDir=?' 'silent' 'auth=?' 'help' -- $argv 4 | or return 2 5 | 6 | if test $_flag_help 7 | echo "commands are: 8 | copy: --dir, --serverDir 9 | zip: --dir, --serverDir, --silent 10 | upload: --dir, --auth 11 | options: 12 | --dir= 13 | --serverDir= 14 | --auth= 15 | --help=" 16 | return 0 17 | end 18 | 19 | function copyFiles --argument-names copyFolder 20 | mkdir -p $copyFolder 21 | 22 | cp --verbose ./*.lua $copyFolder 23 | cp --verbose ./*.png $copyFolder 24 | cp --verbose ./*.json $copyFolder 25 | cp --verbose -r ./sounds $copyFolder 26 | cp --verbose -r ./locale $copyFolder 27 | cp --verbose -r ./libs $copyFolder 28 | cp --verbose -r ./graphics $copyFolder 29 | cp --verbose -r ./prototypes $copyFolder 30 | cp --verbose -r ./migrations $copyFolder 31 | cp --verbose ./COPYING $copyFolder 32 | cp --verbose ./changelog.txt $copyFolder 33 | cp --verbose ./README.md $copyFolder 34 | end 35 | 36 | set modFolder $_flag_dir 37 | if test -z "$modFolder" 38 | set modFolder "/mnt/gallery/gameFiles/factorio/mods" 39 | end 40 | 41 | set modName (jq -r .name info.json) 42 | set modVersion (jq -r .version info.json) 43 | set title $modName"_"$modVersion 44 | set zipName "$modFolder/$title.zip" 45 | set modPath "$modFolder/$title" 46 | 47 | switch $argv[1] 48 | case copy 49 | echo "copying" 50 | rm -f $zipName 51 | 52 | copyFiles $modPath 53 | 54 | if test -n "$_flag_serverDir" 55 | copyFiles $_flag_serverDir 56 | end 57 | case zip 58 | if test -z "$_flag_silent" 59 | echo "zipping" 60 | end 61 | rm -rf $modPath 62 | rm -f $zipName 63 | ln -s (pwd) $title 64 | 65 | set zipProgress (zip $zipName \ 66 | $title/*.lua $title/*.png $title/*.json $title/sounds/**/* $title/locale/**/* \ 67 | $title/libs/**/* $title/graphics/**/* $title/prototypes/**/* $title/migrations/**/* \ 68 | $title/COPYING $title/changelog.txt $title/README.md) 69 | 70 | if test -z "$_flag_silent" 71 | echo $zipProgress 72 | end 73 | 74 | if test -n "$_flag_serverDir" 75 | cp $zipName $_flag_serverDir 76 | end 77 | 78 | rm ./$title 79 | 80 | echo $zipName 81 | 82 | case upload 83 | echo "init uploading" 84 | 85 | set initResponse (curl --no-progress-meter -X POST "https://mods.factorio.com/api/v2/mods/releases/init_upload" \ 86 | -H "Authorization: Bearer $_flag_auth" -H "Content-Type: multipart/form-data" \ 87 | -F mod=$modName) 88 | 89 | if test (echo "$initResponse" | jq -r .error '-') != "null" 90 | echo "init upload failed: $initResponse" 91 | return 3 92 | end 93 | 94 | echo "uploading" 95 | 96 | set finishResponse (curl --no-progress-meter -X POST (echo "$initResponse" | jq -r .upload_url '-') \ 97 | -H "Authorization: Bearer $_flag_auth" -H "Content-Type: multipart/form-data" \ 98 | -F file="@$_flag_dir") 99 | 100 | if test (echo "$finishResponse" | jq -r .error '-') != "null" 101 | echo "finish upload failed: $finishResponse" 102 | return 4 103 | else 104 | echo "upload success: $finishResponse" 105 | end 106 | 107 | case '*' 108 | echo "commands are: copy, zip" 109 | return 1 110 | end 111 | -------------------------------------------------------------------------------- /prototypes/Poison.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | -- imports 18 | 19 | local constants = require("__Rampant__/libs/Constants") 20 | local smokeUtils = require("utils/SmokeUtils") 21 | 22 | -- constants 23 | 24 | local poison = {} 25 | 26 | -- imported functions 27 | 28 | local makeCloud = smokeUtils.makeCloud 29 | 30 | function poison.addFactionAddon() 31 | 32 | for i=1,constants.TIERS do 33 | makeCloud( 34 | { 35 | name = "poison-cloud-v" .. i, 36 | scale = 0.80 + (i * 0.15), 37 | wind = true, 38 | slowdown = -1.3, 39 | duration = 10 * (i * 5), 40 | cooldown = 5 41 | }, 42 | { 43 | type = "direct", 44 | action_delivery = 45 | { 46 | type = "instant", 47 | target_effects = 48 | { 49 | type = "nested-result", 50 | action = 51 | { 52 | { 53 | type = "area", 54 | radius = 2 + (i * 0.5), 55 | force = "ally", 56 | entity_flags = {"placeable-enemy"}, 57 | action_delivery = 58 | { 59 | type = "instant", 60 | target_effects = 61 | { 62 | type = "damage", 63 | damage = { amount = -2 * i, type = "healing"} 64 | } 65 | } 66 | }, 67 | { 68 | type = "area", 69 | radius = 2 + (i * 0.5), 70 | force = "enemy", 71 | action_delivery = 72 | { 73 | type = "instant", 74 | target_effects = 75 | { 76 | type = "damage", 77 | damage = { amount = 0.9 * i, type = "poison"} 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | ) 86 | end 87 | 88 | end 89 | 90 | return poison 91 | -------------------------------------------------------------------------------- /libs/Utils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | if UtilsG then 18 | return UtilsG 19 | end 20 | local Utils = {} 21 | 22 | -- 23 | 24 | local Constants = require("Constants") 25 | 26 | -- 27 | 28 | local CHUNK_SIZE = Constants.CHUNK_SIZE 29 | 30 | -- 31 | 32 | local mFloor = math.floor 33 | local sSub = string.sub 34 | local sGMatch = string.gmatch 35 | 36 | -- 37 | 38 | function Utils.isRampantSetting(str) 39 | return sSub(str, 1, #"rampant--") == "rampant--" 40 | end 41 | 42 | function Utils.split(str) 43 | local result = {} 44 | for i in sGMatch(str, "[a-zA-Z-]+") do 45 | result[#result+1] = i 46 | end 47 | return result 48 | end 49 | 50 | function Utils.isMember(str, set) 51 | for _,s in pairs(set) do 52 | if str == s then 53 | return true 54 | end 55 | end 56 | return false 57 | end 58 | 59 | function Utils.intersection(set1, set2) 60 | local result = {} 61 | for s1 in pairs(set1) do 62 | for s2 in pairs(set2) do 63 | if s1 == s2 then 64 | result[#result+1] = s1 65 | break 66 | end 67 | end 68 | end 69 | return result 70 | end 71 | 72 | function Utils.setPositionInQuery(query, position) 73 | local point = query.position 74 | point[1] = position.x 75 | point[2] = position.y 76 | end 77 | 78 | function Utils.setPositionInCommand(cmd, position) 79 | local point = cmd.destination 80 | point[1] = position.x 81 | point[2] = position.y 82 | end 83 | 84 | function Utils.setPositionXYInQuery(query, x, y) 85 | local point = query.position 86 | point[1] = x 87 | point[2] = y 88 | end 89 | 90 | function Utils.setAreaInQuery(query, topLeftPosition, size) 91 | local area = query.area 92 | area[1][1] = topLeftPosition.x 93 | area[1][2] = topLeftPosition.y 94 | area[2][1] = topLeftPosition.x + size 95 | area[2][2] = topLeftPosition.y + size 96 | end 97 | 98 | function Utils.setAreaInQueryChunkSize(query, topLeftPosition) 99 | local area = query.area 100 | area[1][1] = topLeftPosition.x 101 | area[1][2] = topLeftPosition.y 102 | area[2][1] = topLeftPosition.x + CHUNK_SIZE 103 | area[2][2] = topLeftPosition.y + CHUNK_SIZE 104 | end 105 | 106 | function Utils.setPointAreaInQuery(query, position, size) 107 | local area = query.area 108 | area[1][1] = position.x - size 109 | area[1][2] = position.y - size 110 | area[2][1] = position.x + size 111 | area[2][2] = position.y + size 112 | end 113 | 114 | function Utils.setAreaYInQuery(query, y1, y2) 115 | local area = query.area 116 | area[1][2] = y1 117 | area[2][2] = y2 118 | end 119 | 120 | function Utils.setAreaXInQuery(query, x1, x2) 121 | local area = query.area 122 | area[1][1] = x1 123 | area[2][1] = x2 124 | end 125 | 126 | function Utils.validPlayer(player) 127 | if player and player.valid then 128 | local char = player.character 129 | return char and char.valid 130 | end 131 | return false 132 | end 133 | 134 | function Utils.getTimeStringFromTick(tick) 135 | 136 | local tickToSeconds = tick / 60 137 | 138 | local days = mFloor(tickToSeconds / 86400) 139 | local hours = mFloor((tickToSeconds % 86400) / 3600) 140 | local minutes = mFloor((tickToSeconds % 3600) / 60) 141 | local seconds = mFloor(tickToSeconds % 60) 142 | return days .. "d " .. hours .. "h " .. minutes .. "m " .. seconds .. "s" 143 | end 144 | 145 | UtilsG = Utils 146 | return Utils 147 | -------------------------------------------------------------------------------- /prototypes/utils/ThiefUtils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local thiefUtils = {} 18 | 19 | -- module code 20 | 21 | function thiefUtils.makeDrainCrystal(attributes) 22 | local name = attributes.name .. "-drain-rampant" 23 | local itemName = attributes.name .. "-item-drain-rampant" 24 | 25 | data:extend({ 26 | { 27 | type = "item", 28 | name = itemName, 29 | icon = "__Rampant__/graphics/icons/thief/crystal-drain.png", 30 | icon_size = 32, 31 | flags = {"hidden"}, 32 | subgroup = "energy", 33 | order = "e[accumulator]-a[accumulator]", 34 | place_result = name, 35 | stack_size = 50 36 | }, 37 | 38 | { 39 | type = "radar", 40 | name = name, 41 | icon = "__Rampant__/graphics/icons/thief/crystal-drain.png", 42 | icon_size = 32, 43 | flags = {"placeable-enemy"}, 44 | minable = {hardness = 0.2, mining_time = 0.5, result = itemName}, 45 | max_health = attributes.health or 500, 46 | corpse = nil, 47 | collision_box = {{-0.9 * attributes.scale, -0.9 * attributes.scale}, {0.9 * attributes.scale, 0.9 * attributes.scale}}, 48 | selection_box = {{-1.1 * attributes.scale, -1.1 * attributes.scale}, {1.1 * attributes.scale, 1.1 * attributes.scale}}, 49 | energy_per_sector = "100MJ", 50 | max_distance_of_sector_revealed = 0, 51 | max_distance_of_nearby_sector_revealed = 0, 52 | energy_per_nearby_scan = "750kJ", 53 | dying_explosion = "radar-explosion", 54 | resistances = { 55 | { 56 | type = "physical", 57 | percent = 25 58 | }, 59 | { 60 | type = "fire", 61 | percent = 85 62 | }, 63 | { 64 | type = "electric", 65 | percent = 95 66 | }, 67 | { 68 | type = "laser", 69 | percent = 90 70 | } 71 | }, 72 | energy_source = 73 | { 74 | type = "electric", 75 | usage_priority = "primary-input" 76 | }, 77 | energy_usage = attributes.drain or "500kW", 78 | pictures = 79 | { 80 | filename = "__Rampant__/graphics/entities/thief/crystal-drain.png", 81 | priority = "low", 82 | width = 128, 83 | height = 128, 84 | scale = attributes.scale, 85 | apply_projection = false, 86 | direction_count = 32, 87 | animation_speed = 0.5, 88 | line_length = 8, 89 | shift = {0.65, 0} 90 | }, 91 | vehicle_impact_sound = { filename = "__base__/sound/car-metal-impact.ogg", volume = 0.65 }, 92 | working_sound = 93 | { 94 | sound = { 95 | { 96 | filename = "__base__/sound/accumulator-working.ogg" 97 | } 98 | }, 99 | apparent_volume = 2, 100 | }, 101 | radius_minimap_visualisation_color = { r = 0.059, g = 0.092, b = 0.8, a = 0.275 }, 102 | } 103 | }) 104 | return 105 | end 106 | 107 | return thiefUtils 108 | -------------------------------------------------------------------------------- /prototypes/utils/ProjectileUtils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local projectileUtils = {} 18 | 19 | function projectileUtils.makeProjectile(attributes, attack) 20 | local n = attributes.name .. "-projectile-rampant" 21 | 22 | data:extend({{ 23 | type = "projectile", 24 | name = n, 25 | flags = {"not-on-map"}, 26 | collision_box = attributes.attackCollisionBox or {{-0.025, -0.025}, {0.025, 0.025}}, 27 | hit_collision_mask = attributes.attackCollisionMask or {"player-layer", "train-layer", RampantGlobalVariables.projectileCollisionLayer}, 28 | direction_only = attributes.attackDirectionOnly, 29 | hit_at_collision_position = true, 30 | piercing_damage = attributes.attackPiercingDamage or 0, 31 | acceleration = attributes.attackAcceleration or 0.000001, 32 | max_speed = math.min(math.max(attributes.scale*0.60, 0.4), 0.7), 33 | force_condition = (settings.startup["rampant--disableCollidingProjectiles"].value and "not-same") or nil, 34 | action = attack, 35 | animation = 36 | { 37 | filename = "__base__/graphics/entity/acid-projectile/acid-projectile-head.png", 38 | line_length = 5, 39 | width = 22, 40 | height = 84, 41 | frame_count = 15, 42 | shift = util.mul_shift(util.by_pixel(-2, 30), attributes.scale*1.2 or 1), 43 | tint = attributes.tint2, 44 | priority = "high", 45 | scale = (attributes.scale*1.2 or 1), 46 | animation_speed = 1, 47 | hr_version = 48 | { 49 | filename = "__base__/graphics/entity/acid-projectile/hr-acid-projectile-head.png", 50 | line_length = 5, 51 | width = 42, 52 | height = 164, 53 | frame_count = 15, 54 | shift = util.mul_shift(util.by_pixel(-2, 31), attributes.scale*1.2 or 1), 55 | tint = attributes.tint2, 56 | priority = "high", 57 | scale = 0.5 * (attributes.scale*1.2 or 1), 58 | animation_speed = 1, 59 | } 60 | }, 61 | shadow = 62 | { 63 | filename = "__base__/graphics/entity/acid-projectile/acid-projectile-shadow.png", 64 | line_length = 15, 65 | width = 22, 66 | height = 84, 67 | frame_count = 15, 68 | priority = "high", 69 | shift = util.mul_shift(util.by_pixel(-2, 30), attributes.scale*1.2 or 1), 70 | draw_as_shadow = true, 71 | scale = (attributes.scale*1.2 or 1), 72 | animation_speed = 1, 73 | hr_version = 74 | { 75 | filename = "__base__/graphics/entity/acid-projectile/hr-acid-projectile-shadow.png", 76 | line_length = 15, 77 | width = 42, 78 | height = 164, 79 | frame_count = 15, 80 | shift = util.mul_shift(util.by_pixel(-2, 31), attributes.scale*1.2 or 1), 81 | draw_as_shadow = true, 82 | priority = "high", 83 | scale = 0.5 * (attributes.scale*1.2 or 1), 84 | animation_speed = 1, 85 | } 86 | }, 87 | -- rotatable = false, 88 | oriented_particle = true, 89 | shadow_scale_enabled = true, 90 | 91 | }}) 92 | 93 | return n 94 | end 95 | 96 | return projectileUtils 97 | -------------------------------------------------------------------------------- /libs/UnitUtils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | if (unitUtilsG) then 18 | return unitUtilsG 19 | end 20 | local unitUtils = {} 21 | 22 | -- imports 23 | 24 | local constants = require("Constants") 25 | local chunkPropertyUtils = require("ChunkPropertyUtils") 26 | 27 | -- constants 28 | 29 | local DEFINES_WIRE_TYPE_RED = defines.wire_type.red 30 | local DEFINES_WIRE_TYPE_GREEN = defines.wire_type.green 31 | 32 | local ENERGY_THIEF_CONVERSION_TABLE = constants.ENERGY_THIEF_CONVERSION_TABLE 33 | local ENERGY_THIEF_LOOKUP = constants.ENERGY_THIEF_LOOKUP 34 | 35 | local ENERGY_THIEF_DRAIN_CRYSTALS = constants.ENERGY_THIEF_DRAIN_CRYSTALS 36 | 37 | -- imported functions 38 | 39 | local setDrainPylons = chunkPropertyUtils.setDrainPylons 40 | 41 | -- module code 42 | 43 | local function convertTypeToDrainCrystal(evolutionFactor, entity) 44 | if (entity == "pole") then 45 | return "crystal-drain-pole-rampant" 46 | else 47 | if (entity == "smallUnit") then 48 | if (evolutionFactor < 0.25) then 49 | return ENERGY_THIEF_DRAIN_CRYSTALS[1] 50 | elseif (evolutionFactor < 0.50) then 51 | return ENERGY_THIEF_DRAIN_CRYSTALS[2] 52 | elseif (evolutionFactor < 0.75) then 53 | return ENERGY_THIEF_DRAIN_CRYSTALS[3] 54 | else 55 | return ENERGY_THIEF_DRAIN_CRYSTALS[4] 56 | end 57 | elseif (entity == "unit") then 58 | if (evolutionFactor < 0.25) then 59 | return ENERGY_THIEF_DRAIN_CRYSTALS[4] 60 | elseif (evolutionFactor < 0.50) then 61 | return ENERGY_THIEF_DRAIN_CRYSTALS[5] 62 | elseif (evolutionFactor < 0.75) then 63 | return ENERGY_THIEF_DRAIN_CRYSTALS[6] 64 | else 65 | return ENERGY_THIEF_DRAIN_CRYSTALS[7] 66 | end 67 | else 68 | if (evolutionFactor < 0.25) then 69 | return ENERGY_THIEF_DRAIN_CRYSTALS[7] 70 | elseif (evolutionFactor < 0.50) then 71 | return ENERGY_THIEF_DRAIN_CRYSTALS[8] 72 | elseif (evolutionFactor < 0.75) then 73 | return ENERGY_THIEF_DRAIN_CRYSTALS[9] 74 | else 75 | return ENERGY_THIEF_DRAIN_CRYSTALS[10] 76 | end 77 | end 78 | end 79 | end 80 | 81 | function unitUtils.createDrainPylon(map, cause, entity, entityType) 82 | if ((cause and ENERGY_THIEF_LOOKUP[cause.name]) or (not cause)) then 83 | local conversion = ENERGY_THIEF_CONVERSION_TABLE[entityType] 84 | if conversion then 85 | local newEntity = map.surface.create_entity({ 86 | position=entity.position, 87 | name=convertTypeToDrainCrystal(entity.force.evolution_factor, conversion), 88 | direction=entity.direction 89 | }) 90 | if (conversion == "pole") then 91 | local targetEntity = map.surface.create_entity({ 92 | position=entity.position, 93 | name="pylon-target-rampant", 94 | direction=entity.direction 95 | }) 96 | targetEntity.backer_name = "" 97 | local wires = entity.neighbours 98 | if wires then 99 | for _,v in pairs(wires.copper) do 100 | if (v.valid) then 101 | newEntity.connect_neighbour(v); 102 | end 103 | end 104 | for _,v in pairs(wires.red) do 105 | if (v.valid) then 106 | newEntity.connect_neighbour({ 107 | wire = DEFINES_WIRE_TYPE_RED, 108 | target_entity = v 109 | }); 110 | end 111 | end 112 | for _,v in pairs(wires.green) do 113 | if (v.valid) then 114 | newEntity.connect_neighbour({ 115 | wire = DEFINES_WIRE_TYPE_GREEN, 116 | target_entity = v 117 | }); 118 | end 119 | end 120 | end 121 | setDrainPylons(map, targetEntity, newEntity) 122 | elseif newEntity.backer_name then 123 | newEntity.backer_name = "" 124 | end 125 | end 126 | end 127 | end 128 | 129 | unitUtilsG = unitUtils 130 | return unitUtils 131 | -------------------------------------------------------------------------------- /libs/MathUtils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | if MathUtilsG then 18 | return MathUtilsG 19 | end 20 | local MathUtils = {} 21 | 22 | -- imports 23 | 24 | -- constants 25 | 26 | local TICKS_A_MINUTE = 60 * 60 27 | 28 | -- imported functions 29 | 30 | local mSqrt = math.sqrt 31 | local mLog10 = math.log10 32 | 33 | local mFloor = math.floor 34 | local mAbs = math.abs 35 | 36 | -- module code 37 | 38 | function MathUtils.roundToFloor(number, multiple) 39 | return mFloor(number / multiple) * multiple 40 | end 41 | 42 | function MathUtils.roundToNearest(number, multiple) 43 | local num = number + (multiple * 0.5) 44 | return num - (num % multiple) 45 | end 46 | 47 | function MathUtils.randomTickEvent(rg, tick, low, high) 48 | return tick + MathUtils.randomTickDuration(rg, low, high) 49 | end 50 | 51 | function MathUtils.randomTickDuration(rg, low, high) 52 | local range = high - low 53 | local minutesToTick = (range * rg()) + low 54 | return MathUtils.roundToNearest(TICKS_A_MINUTE * minutesToTick, 1) 55 | end 56 | 57 | function MathUtils.distort(xorRandom, num, stdDev, min, max) 58 | local amin = min or num * 0.70 59 | local amax = max or num * 1.30 60 | local sd = stdDev or 0.17 61 | if (num < 0) then 62 | local t = amin 63 | amin = amax 64 | amax = t 65 | end 66 | return MathUtils.roundToNearest(MathUtils.gaussianRandomRangeRG(num, num * sd, amin, amax, xorRandom), 0.01) 67 | end 68 | 69 | function MathUtils.linearInterpolation(percent, min, max) 70 | return ((max - min) * percent) + min 71 | end 72 | 73 | function MathUtils.xorRandom(state) 74 | local xor = bit32.bxor 75 | local lshift = bit32.lshift 76 | local rshift = bit32.rshift 77 | 78 | local seed = state + 32685453 79 | 80 | return function() 81 | seed = xor(seed, lshift(seed, 13)) 82 | seed = xor(seed, rshift(seed, 17)) 83 | seed = xor(seed, lshift(seed, 5)) 84 | return seed * 2.32830643654e-10 -- 2.32830643654e-10 = 1 / 2^32, 2.32830643708e-10 = 1 / ((2^32)-1) 85 | end 86 | end 87 | 88 | --[[ 89 | Used for gaussian random numbers 90 | --]] 91 | function MathUtils.gaussianRandomRG(mean, std_dev, rg) 92 | -- marsagliaPolarMethod 93 | local iid1 94 | local iid2 95 | local q 96 | repeat 97 | iid1 = 2 * rg() + -1 98 | iid2 = 2 * rg() + -1 99 | q = (iid1 * iid1) + (iid2 * iid2) 100 | until (q ~= 0) and (q < 1) 101 | local s = mSqrt((-2 * mLog10(q)) / q) 102 | local v = iid1 * s 103 | 104 | return mean + (v * std_dev) 105 | end 106 | 107 | function MathUtils.gaussianRandomRangeRG(mean, std_dev, min, max, rg) 108 | local r 109 | if (min >= max) then 110 | return min 111 | end 112 | repeat 113 | local iid1 114 | local iid2 115 | local q 116 | repeat 117 | iid1 = 2 * rg() + -1 118 | iid2 = 2 * rg() + -1 119 | q = (iid1 * iid1) + (iid2 * iid2) 120 | until (q ~= 0) and (q < 1) 121 | local s = mSqrt((-2 * mLog10(q)) / q) 122 | local v = iid1 * s 123 | r = mean + (v * std_dev) 124 | until (r >= min) and (r <= max) 125 | return r 126 | end 127 | 128 | function MathUtils.euclideanDistanceNamed(p1, p2) 129 | local xs = p1.x - p2.x 130 | local ys = p1.y - p2.y 131 | return ((xs * xs) + (ys * ys)) ^ 0.5 132 | end 133 | 134 | function MathUtils.euclideanDistancePoints(x1, y1, x2, y2) 135 | local xs = x1 - x2 136 | local ys = y1 - y2 137 | return ((xs * xs) + (ys * ys)) ^ 0.5 138 | end 139 | 140 | function MathUtils.manhattenDistancePoints(x1, y1, x2, y2) 141 | return mAbs((x1 - x2) + (y1 - y2)) 142 | end 143 | 144 | function MathUtils.euclideanDistanceArray(p1, p2) 145 | local xs = p1[1] - p2[1] 146 | local ys = p1[2] - p2[2] 147 | return ((xs * xs) + (ys * ys)) ^ 0.5 148 | end 149 | 150 | function MathUtils.distortPosition(rg, position, size) 151 | local xDistort = MathUtils.gaussianRandomRangeRG(1, 0.5, 0, 2, rg) - 1 152 | local yDistort = MathUtils.gaussianRandomRangeRG(1, 0.5, 0, 2, rg) - 1 153 | position.x = position.x + (xDistort * size) 154 | position.y = position.y + (yDistort * size) 155 | end 156 | 157 | function MathUtils.distortPositionConcentricCircles(rg, position, size, min) 158 | local xDistort = MathUtils.gaussianRandomRangeRG(1, 0.5, 0, 2, rg) - 1 159 | local yDistort = MathUtils.gaussianRandomRangeRG(1, 0.5, 0, 2, rg) - 1 160 | local xModifier = (xDistort * size) 161 | if xModifier < 0 then 162 | xModifier = xModifier + -min 163 | else 164 | xModifier = xModifier + min 165 | end 166 | local yModifier = (yDistort * size) 167 | if yModifier < 0 then 168 | yModifier = yModifier + -min 169 | else 170 | yModifier = yModifier + min 171 | end 172 | 173 | position.x = position.x + xModifier 174 | position.y = position.y + yModifier 175 | end 176 | 177 | MathUtilsG = MathUtils 178 | return MathUtils 179 | -------------------------------------------------------------------------------- /prototypes/utils/AttackFlame.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local attackFlame = {} 18 | 19 | -- imported 20 | 21 | local streamUtils = require("StreamUtils") 22 | local fireUtils = require("FireUtils") 23 | local stickerUtils = require("StickerUtils") 24 | 25 | -- constants 26 | 27 | local DISALLOW_FRIENDLY_FIRE = settings.startup["rampant--disallowFriendlyFire"].value 28 | 29 | -- imported functions 30 | 31 | local makeStream = streamUtils.makeStream 32 | local makeFire = fireUtils.makeFire 33 | local makeSticker = stickerUtils.makeSticker 34 | local makeSpreadEffect = fireUtils.makeSpreadEffect 35 | 36 | -- module code 37 | 38 | function attackFlame.createAttackFlame(attributes) 39 | 40 | local spawnEntityName = makeSpreadEffect({ 41 | name = attributes.name, 42 | tint2 = attributes.tint2, 43 | fireDamagePerTick = attributes.fireDamagePerTick, 44 | fireDamagePerTickType = attributes.fireDamagePerTickType, 45 | }) 46 | local stickerName = makeSticker({ 47 | name = attributes.name, 48 | spawnEntityName = spawnEntityName, 49 | stickerDuration = attributes.stickerDuration, 50 | stickerDamagePerTick = attributes.stickerDamagePerTick, 51 | stickerDamagePerTickType = attributes.stickerDamagePerTickType, 52 | stickerMovementModifier = attributes.stickerMovementModifier, 53 | tint2 = attributes.tint2, 54 | fireSpreadRadius = attributes.fireSpreadRadius 55 | }) 56 | local fireName = makeFire({ 57 | name = attributes.name, 58 | tint2 = attributes.tint2 or {r=0, g=0.9, b=0, a=0.5}, 59 | spawnEntityName = spawnEntityName, 60 | fireDamagePerTick = attributes.fireDamagePerTick, 61 | fireDamagePerTickType = attributes.fireDamagePerTickType, 62 | damageMaxMultipler = attributes.damageMaxMultipler, 63 | multiplerIncrease = attributes.multiplerIncrease, 64 | multiplerDecrease = attributes.multiplerDecrease, 65 | stickerName = stickerName 66 | }) 67 | 68 | return makeStream({ 69 | name = attributes.name, 70 | tint2 = attributes.tint2 or {r=0, g=1, b=1, a=0.5}, 71 | particleTimeout = attributes.particleTimeout, 72 | scale = attributes.scale, 73 | actions = { 74 | { 75 | type = "area", 76 | radius = attributes.radius or 2.5, 77 | force = (DISALLOW_FRIENDLY_FIRE and "not-same") or nil, 78 | action_delivery = 79 | { 80 | type = "instant", 81 | target_effects = 82 | { 83 | { 84 | type = "create-sticker", 85 | sticker = stickerName, 86 | check_buildability = true 87 | }, 88 | { 89 | type = "create-entity", 90 | entity_name = "water-splash", 91 | tile_collision_mask = { "ground-tile" } 92 | }, 93 | { 94 | type = "damage", 95 | damage = { amount = attributes.damage, type = attributes.damageType or "fire" } 96 | } 97 | } 98 | } 99 | }, 100 | { 101 | type = "cluster", 102 | cluster_count = 2, 103 | distance = 2 + (0.1 * attributes.effectiveLevel), 104 | distance_deviation = 1.5, 105 | action_delivery = { 106 | type = "instant", 107 | target_effects = { 108 | { 109 | type="create-fire", 110 | entity_name = fireName, 111 | check_buildability = true, 112 | initial_ground_flame_count = 2, 113 | show_in_tooltip = true 114 | } 115 | } 116 | } 117 | }, 118 | { 119 | type = "direct", 120 | action_delivery = { 121 | type = "instant", 122 | target_effects = { 123 | type= "create-fire", 124 | entity_name = fireName, 125 | check_buildability = true, 126 | show_in_tooltip = true 127 | } 128 | } 129 | } 130 | } 131 | }) 132 | end 133 | 134 | return attackFlame 135 | -------------------------------------------------------------------------------- /data-final-fixes.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | local vanillaBuildings = require("prototypes/buildings/UpdatesVanilla") 17 | local constants = require("libs/Constants") 18 | 19 | if settings.startup["rampant--newEnemies"].value and mods["SchallAlienLoot"] then 20 | local SizeLootRampant = {1, 2, 3, 3, 4, 4, 4, 5, 5, 6} 21 | for _,faction in pairs(constants.FACTION_SET) do 22 | for v=1,settings.startup["rampant--newEnemyVariations"].value do 23 | for factionSize = 1, constants.TIERS do 24 | local effectiveLevel = constants.TIER_UPGRADE_SET[factionSize] 25 | SchallAlienLoot_add_spawner(faction.type.."-hive-v"..v.."-t"..factionSize.."-rampant") 26 | SchallAlienLoot_add_spawner(faction.type.."-spitter-spawner-v"..v.."-t"..factionSize.."-rampant") 27 | SchallAlienLoot_add_spawner(faction.type.."-biter-spawner-v"..v.."-t"..factionSize.."-rampant") 28 | SchallAlienLoot_add_worm(faction.type.."-worm-v"..v.."-t"..factionSize.."-rampant", factionSize) 29 | SchallAlienLoot_add_mover(faction.type.."-spitter-v"..v.."-t"..factionSize.."-rampant", SizeLootRampant[effectiveLevel]) 30 | SchallAlienLoot_add_mover(faction.type.."-biter-v"..v.."-t"..factionSize.."-rampant", SizeLootRampant[effectiveLevel]) 31 | end 32 | end 33 | end 34 | end 35 | 36 | for _, projectile in pairs(data.raw["projectile"]) do 37 | if not projectile.hit_collision_mask then 38 | projectile.hit_collision_mask = { 39 | "player-layer", 40 | "train-layer", 41 | RampantGlobalVariables.projectileCollisionLayer 42 | } 43 | else 44 | projectile.hit_collision_mask[#projectile.hit_collision_mask+1] = RampantGlobalVariables.projectileCollisionLayer 45 | end 46 | end 47 | 48 | if settings.startup["rampant--removeBloodParticles"].value then 49 | local explosions = data.raw["explosion"] 50 | 51 | for k,v in pairs(explosions) do 52 | if string.find(k, "blood") then 53 | v["created_effect"] = nil 54 | end 55 | end 56 | end 57 | 58 | if settings.startup["rampant--unitSpawnerBreath"].value then 59 | for _, unitSpawner in pairs(data.raw["unit-spawner"]) do 60 | if (string.find(unitSpawner.name, "hive") or string.find(unitSpawner.name, "biter") or 61 | string.find(unitSpawner.name, "spitter")) then 62 | if not unitSpawner.flags then 63 | unitSpawner.flags = {} 64 | end 65 | unitSpawner.flags[#unitSpawner.flags+1] = "breaths-air" 66 | end 67 | end 68 | end 69 | 70 | 71 | for k, unit in pairs(data.raw["unit"]) do 72 | if (string.find(k, "biter") or string.find(k, "spitter")) and unit.collision_box then 73 | if settings.startup["rampant--enableSwarm"].value then 74 | unit.collision_box = { 75 | {unit.collision_box[1][1] * 0.70, unit.collision_box[1][2] * 0.70}, 76 | {unit.collision_box[2][1] * 0.70, unit.collision_box[2][2] * 0.70} 77 | } 78 | end 79 | 80 | unit.affected_by_tiles = settings.startup["rampant--unitsAffectedByTiles"].value 81 | 82 | unit.ai_settings = { 83 | destroy_when_commands_fail = false, 84 | allow_try_return_to_spawner = true 85 | } 86 | end 87 | end 88 | 89 | if settings.startup["rampant--enableShrinkNestsAndWorms"].value then 90 | for k, unit in pairs(data.raw["unit-spawner"]) do 91 | if (string.find(k, "biter") or string.find(k, "spitter") or string.find(k, "hive")) and unit.collision_box then 92 | unit.collision_box = { 93 | {unit.collision_box[1][1] * 0.75, unit.collision_box[1][2] * 0.75}, 94 | {unit.collision_box[2][1] * 0.75, unit.collision_box[2][2] * 0.75} 95 | } 96 | end 97 | end 98 | 99 | for k, unit in pairs(data.raw["turret"]) do 100 | if string.find(k, "worm") and unit.collision_box then 101 | unit.collision_box = { 102 | {unit.collision_box[1][1] * 0.75, unit.collision_box[1][2] * 0.75}, 103 | {unit.collision_box[2][1] * 0.75, unit.collision_box[2][2] * 0.75} 104 | } 105 | end 106 | end 107 | end 108 | 109 | if settings.startup["rampant--enableLandfillOnDeath"].value then 110 | local particles = { 111 | "guts-entrails-particle-small-medium", 112 | "guts-entrails-particle-big" 113 | } 114 | 115 | for _,particleName in pairs(particles) do 116 | data.raw["optimized-particle"][particleName].ended_in_water_trigger_effect = { 117 | { 118 | type = "set-tile", 119 | tile_name = "landfill", 120 | radius = 0.5, 121 | }, 122 | { 123 | type = "script", 124 | effect_id = "deathLandfillParticle--rampant" 125 | } 126 | } 127 | end 128 | end 129 | 130 | if settings.startup["rampant--enableFadeTime"].value then 131 | for k, corpse in pairs(data.raw["corpse"]) do 132 | if (string.find(k, "biter") or string.find(k, "spitter") or string.find(k, "hive") or 133 | string.find(k, "worm") or string.find(k, "spawner")) then 134 | corpse.time_before_removed = settings.startup["rampant--unitAndSpawnerFadeTime"].value * 60 135 | end 136 | end 137 | end 138 | 139 | if settings.startup["rampant--addWallResistanceAcid"].value then 140 | vanillaBuildings.addWallAcidResistance() 141 | end 142 | -------------------------------------------------------------------------------- /prototypes/samples/healingBiter.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local smallbiterscale = 0.5 18 | local small_biter_tint1 = {r=0.56, g=0.46, b=0.42, a=0.65} 19 | local small_biter_tint2 = {r=1, g=0.63, b=0, a=0.4} 20 | 21 | data:extend({ 22 | { -- this defines a damage type so resistances don't effect this damage 23 | type = "damage-type", 24 | name = "healing" 25 | }, 26 | { -- this defines your healing cloud 27 | type = "smoke-with-trigger", 28 | name = "healing-cloud-overlord", 29 | flags = {"not-on-map"}, 30 | show_when_smoke_off = true, 31 | animation = 32 | { 33 | filename = "__base__/graphics/entity/cloud/cloud-45-frames.png", 34 | flags = { "compressed" }, 35 | priority = "low", 36 | width = 256, 37 | height = 256, 38 | frame_count = 45, 39 | animation_speed = 0.5, 40 | line_length = 7, 41 | scale = 3, 42 | }, 43 | slow_down_factor = 0, 44 | affected_by_wind = false, 45 | cyclic = true, 46 | duration = 60 * 5, 47 | fade_away_duration = 5 * 60, 48 | spread_duration = 10, 49 | color = { r = 0.0, g = 0.0, b = 0.9, a = 0.2 }, 50 | action = 51 | { 52 | type = "direct", 53 | action_delivery = 54 | { 55 | type = "instant", 56 | target_effects = 57 | { 58 | type = "nested-result", 59 | action = 60 | { 61 | type = "area", 62 | perimeter = 11, 63 | entity_flags = {"breaths-air"}, 64 | action_delivery = 65 | { 66 | type = "instant", 67 | target_effects = 68 | { 69 | type = "damage", 70 | damage = { amount = -2, type = "healing"} 71 | } 72 | } 73 | } 74 | } 75 | } 76 | }, 77 | action_cooldown = 30 78 | }, 79 | { -- this is your custom projectile that will create the healing cloud 80 | type = "projectile", 81 | name = "healing-orb-overlord", 82 | flags = {"not-on-map"}, 83 | acceleration = 0.005, 84 | action = 85 | { 86 | type = "direct", 87 | action_delivery = 88 | { 89 | type = "instant", 90 | target_effects = 91 | { 92 | { 93 | type = "play-sound", 94 | sound = 95 | { 96 | { 97 | filename = "__base__/sound/creatures/projectile-acid-burn-1.ogg", 98 | volume = 0.8 99 | }, 100 | { 101 | filename = "__base__/sound/creatures/projectile-acid-burn-2.ogg", 102 | volume = 0.8 103 | }, 104 | { 105 | filename = "__base__/sound/creatures/projectile-acid-burn-long-1.ogg", 106 | volume = 0.8 107 | }, 108 | { 109 | filename = "__base__/sound/creatures/projectile-acid-burn-long-2.ogg", 110 | volume = 0.8 111 | } 112 | } 113 | }, 114 | { 115 | type = "create-entity", 116 | entity_name = "healing-cloud-overlord" 117 | } 118 | } 119 | } 120 | }, 121 | animation = 122 | { 123 | filename = "__base__/graphics/entity/acid-projectile-purple/acid-projectile-purple.png", 124 | line_length = 5, 125 | width = 16, 126 | height = 18, 127 | frame_count = 33, 128 | priority = "high" 129 | }, 130 | shadow = 131 | { 132 | filename = "__base__/graphics/entity/acid-projectile-purple/acid-projectile-purple-shadow.png", 133 | line_length = 5, 134 | width = 28, 135 | height = 16, 136 | frame_count = 33, 137 | priority = "high", 138 | shift = {-0.09, 0.395} 139 | }, 140 | rotatable = false 141 | }, 142 | { 143 | type = "unit", 144 | name = "small-healing-biter-overlord", 145 | icon = "__base__/graphics/icons/small-biter.png", 146 | flags = {"placeable-player", "placeable-enemy", "placeable-off-grid", "breaths-air"}, 147 | max_health = 15, 148 | order = "b-b-a", 149 | subgroup="enemies", 150 | healing_per_tick = 0.01, 151 | collision_box = {{-0.2, -0.2}, {0.2, 0.2}}, 152 | selection_box = {{-0.4, -0.7}, {0.7, 0.4}}, 153 | attack_parameters = { 154 | type = "projectile", 155 | ammo_category = "rocket", 156 | damage_modifier = 10, 157 | cooldown = 150, 158 | projectile_center = {0, 0}, 159 | projectile_creation_distance = 0.6, 160 | range = 15, -- make this whatever distance you want the unit to stop at 161 | animation = biterattackanimation(smallbiterscale, small_biter_tint1, small_biter_tint2), 162 | sound = { 163 | filename = "__base__/sound/fight/pulse.ogg", 164 | volume = 0.7 165 | }, 166 | ammo_type = { 167 | type = "projectile", 168 | category = "biological", 169 | speed = 1, 170 | action = 171 | { 172 | { 173 | type = "direct", 174 | action_delivery = 175 | { 176 | type = "projectile", 177 | projectile = "healing-orb-overlord", 178 | starting_speed = 0.5, 179 | max_range = 1 -- the distance you want the cloud to appear from the unit 180 | } 181 | }, 182 | { 183 | type = "direct", 184 | action_delivery = 185 | { 186 | type = "projectile", 187 | projectile = "acid-projectile-purple", 188 | starting_speed = 0.5, 189 | max_range = 15 -- should be whatever range you want the unit to shoot for 190 | } 191 | } 192 | } 193 | } 194 | }, 195 | vision_distance = 30, 196 | movement_speed = 0.2, 197 | distance_per_frame = 0.1, 198 | pollution_to_join_attack = 200, 199 | distraction_cooldown = 300, 200 | min_pursue_time = 10 * 60, 201 | max_pursue_distance = 50, 202 | corpse = "small-biter-corpse", 203 | dying_explosion = "blood-explosion-small", 204 | dying_sound = make_biter_dying_sounds(0.4), 205 | working_sound = make_biter_calls(0.3), 206 | run_animation = biterrunanimation(smallbiterscale, small_biter_tint1, small_biter_tint2) 207 | } 208 | }) 209 | -------------------------------------------------------------------------------- /data.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | -- import 18 | 19 | local colorUtils = require("prototypes/utils/ColorUtils") 20 | local smokeUtils = require("prototypes/utils/SmokeUtils") 21 | local swarmUtils = require("prototypes/SwarmUtils") 22 | local collision_mask_util = require("collision-mask-util") 23 | 24 | -- imported functions 25 | 26 | local makeSmokeSoft = smokeUtils.makeSmokeSoft 27 | local makeSmokeWithGlow = smokeUtils.makeSmokeWithGlow 28 | local makeSmokeWithoutGlow = smokeUtils.makeSmokeWithoutGlow 29 | local makeSmokeAddingFuel = smokeUtils.makeSmokeAddingFuel 30 | 31 | local makeColor = colorUtils.makeColor 32 | 33 | -- module code 34 | 35 | makeSmokeSoft({name="the", softSmokeTint=makeColor(0.3, 0.75, 0.3, 0.1)}) 36 | makeSmokeWithGlow({name="the", smokeWithGlowTint=makeColor(0.3, 0.75, 0.3, 0.1)}) 37 | makeSmokeWithoutGlow({name="the", smokeWithoutGlowTint=makeColor(0.3, 0.75, 0.3, 0.1)}) 38 | makeSmokeAddingFuel({name="the"}) 39 | 40 | require("prototypes/buildings/ChunkScanner") 41 | 42 | RampantGlobalVariables = {} 43 | RampantGlobalVariables.projectileCollisionLayer = collision_mask_util.get_first_unused_layer() 44 | 45 | if not data.raw["corpse"]["acid-splash-purple"] then 46 | local attributes = {} 47 | 48 | data:extend({ 49 | { 50 | type = "corpse", 51 | name = "acid-splash-purple", 52 | flags = {"not-on-map"}, 53 | time_before_removed = 60 * 30, 54 | final_render_layer = "corpse", 55 | splash = 56 | { 57 | { 58 | filename = "__base__/graphics/entity/acid-splash/acid-splash-1.png", 59 | line_length = 8, 60 | direction_count = 1, 61 | width = 106, 62 | height = 116, 63 | frame_count = 26, 64 | shift = util.mul_shift(util.by_pixel(-12, -10), attributes.scale or 1), 65 | tint = attributes.tint, 66 | scale = (attributes.scale or 1), 67 | hr_version = { 68 | filename = "__base__/graphics/entity/acid-splash/hr-acid-splash-1.png", 69 | line_length = 8, 70 | direction_count = 1, 71 | width = 210, 72 | height = 224, 73 | frame_count = 26, 74 | shift = util.mul_shift(util.by_pixel(-12, -8), attributes.scale or 1), 75 | tint = attributes.tint, 76 | scale = 0.5 * (attributes.scale or 1), 77 | } 78 | }, 79 | { 80 | filename = "__base__/graphics/entity/acid-splash/acid-splash-2.png", 81 | line_length = 8, 82 | direction_count = 1, 83 | width = 88, 84 | height = 76, 85 | frame_count = 29, 86 | shift = util.mul_shift(util.by_pixel(-10, -18), attributes.scale or 1), 87 | tint = attributes.tint, 88 | scale = (attributes.scale or 1), 89 | hr_version = { 90 | filename = "__base__/graphics/entity/acid-splash/hr-acid-splash-2.png", 91 | line_length = 8, 92 | direction_count = 1, 93 | width = 174, 94 | height = 150, 95 | frame_count = 29, 96 | shift = util.mul_shift(util.by_pixel(-9, -17), attributes.scale or 1), 97 | tint = attributes.tint, 98 | scale = 0.5 * (attributes.scale or 1), 99 | } 100 | }, 101 | { 102 | filename = "__base__/graphics/entity/acid-splash/acid-splash-3.png", 103 | line_length = 8, 104 | direction_count = 1, 105 | width = 118, 106 | height = 104, 107 | frame_count = 29, 108 | shift = util.mul_shift(util.by_pixel(22, -16), attributes.scale or 1), 109 | tint = attributes.tint, 110 | scale = (attributes.scale or 1), 111 | hr_version = { 112 | filename = "__base__/graphics/entity/acid-splash/hr-acid-splash-3.png", 113 | line_length = 8, 114 | direction_count = 1, 115 | width = 236, 116 | height = 208, 117 | frame_count = 29, 118 | shift = util.mul_shift(util.by_pixel(22, -16), attributes.scale or 1), 119 | tint = attributes.tint, 120 | scale = 0.5 * (attributes.scale or 1), 121 | } 122 | }, 123 | { 124 | filename = "__base__/graphics/entity/acid-splash/acid-splash-4.png", 125 | line_length = 8, 126 | direction_count = 1, 127 | width = 128, 128 | height = 80, 129 | frame_count = 24, 130 | shift = util.mul_shift(util.by_pixel(16, -20), attributes.scale or 1), 131 | tint = attributes.tint, 132 | scale = (attributes.scale or 1), 133 | hr_version = { 134 | filename = "__base__/graphics/entity/acid-splash/hr-acid-splash-4.png", 135 | line_length = 8, 136 | direction_count = 1, 137 | width = 252, 138 | height = 154, 139 | frame_count = 24, 140 | shift = util.mul_shift(util.by_pixel(17, -19), attributes.scale or 1), 141 | tint = attributes.tint, 142 | scale = 0.5 * (attributes.scale or 1), 143 | } 144 | } 145 | }, 146 | splash_speed = 0.03 147 | } 148 | }) 149 | end 150 | 151 | if settings.startup["rampant--newEnemies"].value then 152 | swarmUtils.processFactions() 153 | end 154 | -------------------------------------------------------------------------------- /prototypes/utils/UnitSpawnerUtils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | 18 | local unitSpawnerUtils = {} 19 | 20 | function unitSpawnerUtils.spawner_integration(scale) 21 | return 22 | { 23 | filename = "__base__/graphics/entity/spawner/spawner-idle-integration.png", 24 | variation_count = 4, 25 | width = 258, 26 | height = 188, 27 | shift = util.by_pixel(2, -2), 28 | frame_count = 1, 29 | scale = scale * 1.01, 30 | line_length = 1, 31 | hr_version = 32 | { 33 | filename = "__base__/graphics/entity/spawner/hr-spawner-idle-integration.png", 34 | variation_count = 4, 35 | width = 522, 36 | height = 380, 37 | shift = util.by_pixel(3, -3), 38 | frame_count = 1, 39 | line_length = 1, 40 | scale = scale * 0.51 41 | } 42 | } 43 | end 44 | 45 | function unitSpawnerUtils.spawner_idle_animation(variation, tint, scale, tint2) 46 | return 47 | { 48 | layers = 49 | { 50 | { 51 | filename = "__base__/graphics/entity/spawner/spawner-idle.png", 52 | line_length = 4, 53 | width = 248, 54 | height = 180, 55 | frame_count = 8, 56 | animation_speed = 0.18, 57 | direction_count = 1, 58 | scale = scale, 59 | run_mode = "forward-then-backward", 60 | shift = util.by_pixel(2, -4), 61 | tint = tint, 62 | y = variation * 180 * 2, 63 | hr_version = 64 | { 65 | filename = "__base__/graphics/entity/spawner/hr-spawner-idle.png", 66 | line_length = 4, 67 | width = 490, 68 | height = 354, 69 | frame_count = 8, 70 | animation_speed = 0.18, 71 | direction_count = 1, 72 | scale = scale * 0.5, 73 | tint = tint, 74 | run_mode = "forward-then-backward", 75 | shift = util.by_pixel(3, -2), 76 | y = variation * 354 * 2, 77 | } 78 | }, 79 | { 80 | filename = "__base__/graphics/entity/spawner/spawner-idle-mask.png", 81 | flags = { "mask" }, 82 | width = 140, 83 | height = 118, 84 | frame_count = 8, 85 | animation_speed = 0.18, 86 | run_mode = "forward-then-backward", 87 | shift = util.by_pixel(-1.5 + (-0.5 * scale), -11 + (-3 * scale)), 88 | line_length = 4, 89 | tint = tint2, 90 | scale = scale, 91 | y = variation * 118 * 2, 92 | hr_version = 93 | { 94 | filename = "__base__/graphics/entity/spawner/hr-spawner-idle-mask.png", 95 | flags = { "mask" }, 96 | width = 276, 97 | height = 234, 98 | frame_count = 8, 99 | animation_speed = 0.18, 100 | run_mode = "forward-then-backward", 101 | shift = util.by_pixel(3 + (-2 * (scale * 2.5)), -0.8 + (-8 * (scale * 1.55))), 102 | line_length = 4, 103 | tint = tint2, 104 | y = variation * 234 * 2, 105 | scale = scale * 0.5 106 | } 107 | } 108 | } 109 | } 110 | end 111 | 112 | function unitSpawnerUtils.spawner_die_animation(variation, tint, scale, tint2) 113 | return 114 | { 115 | layers = 116 | { 117 | { 118 | filename = "__base__/graphics/entity/spawner/spawner-die.png", 119 | line_length = 8, 120 | width = 248, 121 | height = 178, 122 | frame_count = 8, 123 | direction_count = 1, 124 | shift = util.by_pixel(2, -2), 125 | y = variation * 178, 126 | tint = tint, 127 | scale = scale, 128 | hr_version = 129 | { 130 | filename = "__base__/graphics/entity/spawner/hr-spawner-die.png", 131 | line_length = 8, 132 | width = 490, 133 | height = 354, 134 | frame_count = 8, 135 | direction_count = 1, 136 | tint = tint, 137 | shift = util.by_pixel(3, -2), 138 | y = variation * 354, 139 | scale = scale * 0.5 140 | } 141 | }, 142 | { 143 | filename = "__base__/graphics/entity/spawner/spawner-die-mask.png", 144 | flags = { "mask" }, 145 | width = 140, 146 | height = 118, 147 | frame_count = 8, 148 | direction_count = 1, 149 | shift = util.by_pixel(-2, -14), 150 | scale = scale, 151 | line_length = 8, 152 | tint = tint2, 153 | y = variation * 118, 154 | he_version = 155 | { 156 | filename = "__base__/graphics/entity/spawner/hr-spawner-die-mask.png", 157 | flags = { "mask" }, 158 | width = 276, 159 | height = 234, 160 | frame_count = 8, 161 | direction_count = 1, 162 | shift = util.by_pixel(-1, -14), 163 | line_length = 8, 164 | tint = tint2, 165 | y = variation * 234, 166 | scale = scale * 0.5 167 | } 168 | } 169 | } 170 | } 171 | end 172 | 173 | return unitSpawnerUtils 174 | -------------------------------------------------------------------------------- /prototypes/utils/StreamUtils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local streamUtils = {} 18 | 19 | -- module code 20 | 21 | function streamUtils.makeStream(attributes) 22 | local softSmokeName = "the-soft-smoke-rampant" 23 | local name = attributes.name .. "-stream-rampant" 24 | data:extend( 25 | { 26 | { 27 | type = "stream", 28 | name = name, 29 | flags = {"not-on-map"}, 30 | stream_light = {intensity = 1, size = 4}, 31 | ground_light = {intensity = 0.8, size = 4}, 32 | 33 | smoke_sources = 34 | { 35 | { 36 | name = softSmokeName, 37 | frequency = 0.05, --0.25, 38 | position = {0.0, 0}, -- -0.8}, 39 | starting_frame_deviation = 60 40 | } 41 | }, 42 | particle_buffer_size = 90, 43 | particle_spawn_interval = 1, 44 | particle_spawn_timeout = attributes.particleTimeout or 6, 45 | particle_vertical_acceleration = attributes.particleVerticalAcceleration or 0.01, 46 | particle_horizontal_speed = attributes.particleHoizontalSpeed or 0.6, 47 | particle_horizontal_speed_deviation = attributes.particleHoizontalSpeedDeviation or 0.0025, 48 | particle_start_alpha = 0.5, 49 | particle_end_alpha = 1, 50 | particle_start_scale = 0.2, 51 | particle_loop_frame_count = 3, 52 | particle_fade_out_threshold = 0.9, 53 | particle_loop_exit_threshold = 0.25, 54 | action = attributes.actions, 55 | 56 | spine_animation = 57 | { 58 | filename = "__base__/graphics/entity/flamethrower-fire-stream/flamethrower-fire-stream-spine.png", 59 | blend_mode = "additive", 60 | tint = attributes.tint2, 61 | line_length = 4, 62 | width = 32, 63 | height = 18, 64 | frame_count = 32, 65 | axially_symmetrical = false, 66 | direction_count = 1, 67 | animation_speed = 2, 68 | shift = {0, 0}, 69 | }, 70 | 71 | shadow = 72 | { 73 | filename = "__Rampant__/graphics/acid-projectile-purple/acid-projectile-purple-shadow.png", 74 | line_length = 5, 75 | width = 28, 76 | height = 16, 77 | frame_count = 33, 78 | priority = "high", 79 | shift = {-0.09, 0.395} 80 | }, 81 | 82 | particle = 83 | { 84 | filename = "__base__/graphics/entity/flamethrower-fire-stream/flamethrower-explosion.png", 85 | priority = "extra-high", 86 | width = 64, 87 | tint = attributes.tint2, 88 | height = 64, 89 | frame_count = 32, 90 | line_length = 8 91 | }, 92 | oriented_particle = true, 93 | shadow_scale_enabled = true 94 | } 95 | } 96 | ) 97 | return name 98 | end 99 | 100 | function streamUtils.makeAcidStream(info) 101 | local attributes = util.table.deepcopy(info) 102 | local name = attributes.name .. "-acid-stream-rampant" 103 | 104 | local acidStream = { 105 | type = "stream", 106 | name = name, 107 | flags = {"not-on-map"}, 108 | --stream_light = {intensity = 1, size = 4}, 109 | --ground_light = {intensity = 0.8, size = 4}, 110 | 111 | particle_buffer_size = 90, 112 | particle_spawn_timeout = attributes.particleTimeout or 6, 113 | particle_vertical_acceleration = attributes.particleVerticalAcceleration or 0.01, 114 | particle_horizontal_speed = attributes.particleHoizontalSpeed or 0.6, 115 | particle_horizontal_speed_deviation = attributes.particleHoizontalSpeedDeviation or 0.0025, 116 | particle_spawn_interval = 1, 117 | -- particle_spawn_timeout = attributes.particle_spawn_timeout, 118 | -- particle_vertical_acceleration = 0.005 * 0.60 *1.5, --x 119 | -- particle_horizontal_speed = 0.2* 0.75 * 1.5 * 1.5, --x 120 | -- particle_horizontal_speed_deviation = 0.005 * 0.70, 121 | particle_start_alpha = 0.5, 122 | particle_end_alpha = 1, 123 | particle_alpha_per_part = 0.8, 124 | particle_scale_per_part = 0.8, 125 | particle_loop_frame_count = 15, 126 | --particle_fade_out_threshold = 0.95, 127 | particle_fade_out_duration = 2, 128 | particle_loop_exit_threshold = 0.25, 129 | special_neutral_target_damage = {amount = 1, type = "acid"}, 130 | initial_action = attributes.actions, 131 | particle = { 132 | filename = "__base__/graphics/entity/acid-projectile/acid-projectile-head.png", 133 | line_length = 5, 134 | width = 22, 135 | height = 84, 136 | frame_count = 15, 137 | shift = util.mul_shift(util.by_pixel(-2, 30), attributes.scale), 138 | tint = attributes.tint2, 139 | priority = "high", 140 | scale = attributes.scale, 141 | animation_speed = 1, 142 | hr_version = 143 | { 144 | filename = "__base__/graphics/entity/acid-projectile/hr-acid-projectile-head.png", 145 | line_length = 5, 146 | width = 42, 147 | height = 164, 148 | frame_count = 15, 149 | shift = util.mul_shift(util.by_pixel(-2, 31), attributes.scale), 150 | tint = attributes.tint2, 151 | priority = "high", 152 | scale = 0.5 * attributes.scale, 153 | animation_speed = 1, 154 | } 155 | }, 156 | spine_animation = { 157 | filename = "__base__/graphics/entity/acid-projectile/acid-projectile-tail.png", 158 | line_length = 5, 159 | width = 66, 160 | height = 12, 161 | frame_count = 15, 162 | shift = util.mul_shift(util.by_pixel(0, -2), attributes.scale), 163 | tint = attributes.tint2, 164 | priority = "high", 165 | scale = attributes.scale, 166 | animation_speed = 1, 167 | hr_version = 168 | { 169 | filename = "__base__/graphics/entity/acid-projectile/hr-acid-projectile-tail.png", 170 | line_length = 5, 171 | width = 132, 172 | height = 20, 173 | frame_count = 15, 174 | shift = util.mul_shift(util.by_pixel(0, -1), attributes.scale), 175 | tint = attributes.tint2, 176 | priority = "high", 177 | scale = 0.5 * attributes.scale, 178 | animation_speed = 1, 179 | } 180 | }, 181 | shadow = { 182 | filename = "__base__/graphics/entity/acid-projectile/acid-projectile-shadow.png", 183 | line_length = 15, 184 | width = 22, 185 | height = 84, 186 | frame_count = 15, 187 | priority = "high", 188 | shift = util.mul_shift(util.by_pixel(-2, 30), attributes.scale), 189 | draw_as_shadow = true, 190 | scale = attributes.scale, 191 | animation_speed = 1, 192 | hr_version = 193 | { 194 | filename = "__base__/graphics/entity/acid-projectile/hr-acid-projectile-shadow.png", 195 | line_length = 15, 196 | width = 42, 197 | height = 164, 198 | frame_count = 15, 199 | shift = util.mul_shift(util.by_pixel(-2, 31), attributes.scale), 200 | draw_as_shadow = true, 201 | priority = "high", 202 | scale = 0.5 * attributes.scale, 203 | animation_speed = 1, 204 | } 205 | }, 206 | 207 | oriented_particle = true, 208 | shadow_scale_enabled = true, 209 | } 210 | 211 | data:extend({ 212 | acidStream 213 | }) 214 | return name 215 | end 216 | 217 | return streamUtils 218 | -------------------------------------------------------------------------------- /prototypes/utils/UpdatesVanilla.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local vanillaUpdates = {} 18 | 19 | local biterUtils = require("BiterUtils") 20 | 21 | function vanillaUpdates.useDumbProjectiles() 22 | local turrets = data.raw["turret"]; 23 | 24 | local attackType = "projectile" 25 | 26 | turrets["small-worm-turret"]["attack_parameters"] = biterUtils.createRangedAttack( 27 | { 28 | cooldown = 60, 29 | range = 25, 30 | min_range = 5, 31 | turn_range = 1, 32 | attackType = "projectile", 33 | fire_penalty = 0, 34 | damageModifier = 0.9, 35 | effectiveLevel = 1, 36 | scale = 0.8 37 | }, 38 | "acid-ball-2-" .. attackType .. "-rampant") 39 | 40 | turrets["medium-worm-turret"]["attack_parameters"] = biterUtils.createRangedAttack( 41 | { 42 | cooldown = 60, 43 | range = 30, 44 | min_range = 3, 45 | turn_range = 1, 46 | attackType = "projectile", 47 | fire_penalty = 0, 48 | damageModifier = 0.87, 49 | effectiveLevel = 3, 50 | scale = 1 51 | }, 52 | "acid-ball-3-" .. attackType .. "-rampant") 53 | 54 | 55 | turrets["big-worm-turret"]["attack_parameters"] = biterUtils.createRangedAttack( 56 | { 57 | cooldown = 60, 58 | range = 38, 59 | min_range = 3, 60 | attackType = "projectile", 61 | turn_range = 1, 62 | fire_penalty = 0, 63 | effectiveLevel = 5, 64 | scale = 1.2 65 | }, 66 | "acid-ball-4-" .. attackType .. "-rampant") 67 | 68 | turrets["behemoth-worm-turret"]["attack_parameters"] = biterUtils.createRangedAttack( 69 | { 70 | cooldown = 60, 71 | range = 48, 72 | min_range = 3, 73 | attackType = "projectile", 74 | turn_range = 1, 75 | fire_penalty = 0, 76 | effectiveLevel = 7, 77 | scale = 1.5 78 | }, 79 | "acid-ball-5-" .. attackType .. "-rampant") 80 | 81 | local units = data.raw["unit"]; 82 | 83 | local unit = units["small-spitter"] 84 | unit["attack_parameters"] = biterUtils.createRangedAttack( 85 | { 86 | cooldown = 100, 87 | range = 13, 88 | warmup = 30, 89 | min_range = 3, 90 | turn_range = 1, 91 | attackType = "projectile", 92 | effectiveLevel = 1, 93 | fire_penalty = 15, 94 | scale = biterUtils.findRunScale(unit) 95 | }, 96 | "acid-ball-" .. attackType .. "-rampant", 97 | spitterattackanimation(biterUtils.findRunScale(unit), 98 | tint_1_spitter_small, 99 | tint_2_spitter_small)) 100 | 101 | unit = units["medium-spitter"] 102 | unit["attack_parameters"] = biterUtils.createRangedAttack( 103 | { 104 | cooldown = 95, 105 | range = 14, 106 | min_range = 3, 107 | attackType = "projectile", 108 | warmup = 30, 109 | turn_range = 1, 110 | effectiveLevel = 3, 111 | fire_penalty = 15, 112 | scale = biterUtils.findRunScale(unit) 113 | }, 114 | "acid-ball-1-" .. attackType .. "-rampant", 115 | spitterattackanimation(biterUtils.findRunScale(unit), 116 | tint_1_spitter_medium, 117 | tint_2_spitter_medium)) 118 | 119 | unit = units["big-spitter"] 120 | unit["attack_parameters"] = biterUtils.createRangedAttack( 121 | { 122 | cooldown = 90, 123 | range = 15, 124 | min_range = 3, 125 | attackType = "projectile", 126 | warmup = 30, 127 | turn_range = 1, 128 | effectiveLevel = 5, 129 | fire_penalty = 15, 130 | scale = biterUtils.findRunScale(unit) 131 | }, 132 | "acid-ball-2-direction-" .. attackType .. "-rampant", 133 | spitterattackanimation(biterUtils.findRunScale(unit), 134 | tint_1_spitter_big, 135 | tint_2_spitter_big)) 136 | 137 | unit = units["behemoth-spitter"] 138 | unit["attack_parameters"] = biterUtils.createRangedAttack( 139 | { 140 | cooldown = 90, 141 | range = 16, 142 | min_range = 3, 143 | warmup = 30, 144 | attackType = "projectile", 145 | turn_range = 1, 146 | effectiveLevel = 7, 147 | fire_penalty = 15, 148 | scale = biterUtils.findRunScale(unit) 149 | }, 150 | "acid-ball-3-direction-" .. attackType .. "-rampant", 151 | spitterattackanimation(biterUtils.findRunScale(unit), 152 | tint_1_spitter_behemoth, 153 | tint_2_spitter_behemoth)) 154 | 155 | unit = units["small-biter"] 156 | unit["attack_parameters"]["ammo_type"]["action"] = { 157 | { 158 | type = "area", 159 | radius = 0.2, 160 | force = "enemy", 161 | ignore_collision_condition = true, 162 | action_delivery = 163 | { 164 | type = "instant", 165 | target_effects = 166 | { 167 | type = "damage", 168 | damage = { amount = 7 * 0.75, type = "physical" } 169 | } 170 | } 171 | }, 172 | { 173 | type = "direct", 174 | action_delivery = 175 | { 176 | type = "instant", 177 | target_effects = 178 | { 179 | type = "damage", 180 | damage = { amount = 7 * 0.25, type = "physical" } 181 | } 182 | } 183 | } 184 | } 185 | 186 | unit = units["medium-biter"] 187 | unit["attack_parameters"]["ammo_type"]["action"] = { 188 | { 189 | type = "area", 190 | radius = 0.6, 191 | force = "enemy", 192 | ignore_collision_condition = true, 193 | action_delivery = 194 | { 195 | type = "instant", 196 | target_effects = 197 | { 198 | type = "damage", 199 | damage = { amount = 15 * 0.75, type = "physical" } 200 | } 201 | } 202 | }, 203 | { 204 | type = "direct", 205 | action_delivery = 206 | { 207 | type = "instant", 208 | target_effects = 209 | { 210 | type = "damage", 211 | damage = { amount = 15 * 0.25, type = "physical" } 212 | } 213 | } 214 | } 215 | } 216 | 217 | unit = units["big-biter"] 218 | unit["attack_parameters"]["ammo_type"]["action"] = { 219 | { 220 | type = "area", 221 | radius = 0.9, 222 | force = "enemy", 223 | ignore_collision_condition = true, 224 | action_delivery = 225 | { 226 | type = "instant", 227 | target_effects = 228 | { 229 | type = "damage", 230 | damage = { amount = 30 * 0.75, type = "physical" } 231 | } 232 | } 233 | }, 234 | { 235 | type = "direct", 236 | action_delivery = 237 | { 238 | type = "instant", 239 | target_effects = 240 | { 241 | type = "damage", 242 | damage = { amount = 30 * 0.25, type = "physical" } 243 | } 244 | } 245 | } 246 | } 247 | 248 | unit = units["behemoth-biter"] 249 | unit["attack_parameters"]["ammo_type"]["action"] = { 250 | { 251 | type = "area", 252 | radius = 1.2, 253 | force = "enemy", 254 | ignore_collision_condition = true, 255 | action_delivery = 256 | { 257 | type = "instant", 258 | target_effects = 259 | { 260 | type = "damage", 261 | damage = { amount = 90 * 0.75, type = "physical" } 262 | } 263 | } 264 | }, 265 | { 266 | type = "direct", 267 | action_delivery = 268 | { 269 | type = "instant", 270 | target_effects = 271 | { 272 | type = "damage", 273 | damage = { amount = 90 * 0.25, type = "physical" } 274 | } 275 | } 276 | } 277 | } 278 | end 279 | 280 | return vanillaUpdates 281 | -------------------------------------------------------------------------------- /visualizer/parseState.rkt: -------------------------------------------------------------------------------- 1 | ;; Copyright (C) 2022 veden 2 | 3 | ;; This program is free software: you can redistribute it and/or modify 4 | ;; it under the terms of the GNU General Public License as published by 5 | ;; the Free Software Foundation, either version 3 of the License, or 6 | ;; (at your option) any later version. 7 | 8 | ;; This program is distributed in the hope that it will be useful, 9 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ;; GNU General Public License for more details. 12 | 13 | ;; You should have received a copy of the GNU General Public License 14 | ;; along with this program. If not, see . 15 | 16 | 17 | (module AiState racket 18 | (provide (all-defined-out)) 19 | 20 | (require math/statistics) 21 | 22 | (struct AiState (chunks 23 | chunksLookup 24 | minMaxes) 25 | #:transparent) 26 | 27 | (struct MinMax (min max) 28 | #:transparent) 29 | 30 | (struct ChunkRange (x 31 | y 32 | movement 33 | base 34 | player 35 | resource 36 | enemy 37 | passable 38 | tick 39 | rating 40 | nests 41 | worms 42 | rally 43 | retreat 44 | resourceGen 45 | playerGen 46 | deathGen 47 | scoreResourceKamikaze 48 | scoreResource 49 | scoreSiegeKamikaze 50 | scoreSiege 51 | scoreAttackKamikaze 52 | scoreAttack 53 | pollution 54 | aNe 55 | aRNe 56 | squads 57 | baseAlign 58 | hives 59 | traps 60 | utility 61 | vg 62 | kamikaze) 63 | #:transparent) 64 | 65 | (struct Chunk (x 66 | y 67 | movement 68 | base 69 | player 70 | resource 71 | enemy 72 | passable 73 | tick 74 | rating 75 | nests 76 | worms 77 | rally 78 | retreat 79 | resourceGen 80 | playerGen 81 | deathGen 82 | scoreResourceKamikaze 83 | scoreResource 84 | scoreSiegeKamikaze 85 | scoreSiege 86 | scoreAttackKamikaze 87 | scoreAttack 88 | pollution 89 | aNe 90 | aRNe 91 | squads 92 | baseAlign 93 | hives 94 | traps 95 | utility 96 | vg 97 | kamikaze) 98 | #:transparent) 99 | 100 | (require threading) 101 | 102 | (define (getFile filePath) 103 | (call-with-input-file filePath 104 | (lambda (port) 105 | (port->string port)))) 106 | 107 | (define (stringToChunk str) 108 | (apply Chunk 109 | (map string->number 110 | (string-split str ",")))) 111 | 112 | (define (chunk->string chunk) 113 | (string-append "x:" (~v (Chunk-x chunk)) "\n" 114 | "y:" (~v (Chunk-y chunk)) "\n" 115 | "m:" (~v (Chunk-movement chunk)) "\n" 116 | "b:" (~v (Chunk-base chunk)) "\n" 117 | "p:" (~v (Chunk-player chunk)) "\n" 118 | "r:" (~v (Chunk-resource chunk)) "\n" 119 | "e:" (~v (Chunk-enemy chunk)) "\n" 120 | "pa:" (~v (Chunk-passable chunk)) "\n" 121 | "t:" (~v (Chunk-tick chunk)) "\n" 122 | "rat:" (~v (Chunk-rating chunk)) "\n" 123 | "ne:" (~v (Chunk-nests chunk)) "\n" 124 | "wo:" (~v (Chunk-worms chunk)) "\n")) 125 | 126 | (define (chunk->string2 chunk) 127 | (string-append "ral:" (~v (Chunk-rally chunk)) "\n" 128 | "ret:" (~v (Chunk-retreat chunk)) "\n" 129 | "rG:" (~v (Chunk-resourceGen chunk)) "\n" 130 | "pG:" (~v (Chunk-playerGen chunk)) "\n" 131 | "dG:" (~v (Chunk-deathGen chunk)) "\n" 132 | "sA:" (~v (Chunk-scoreAttack chunk)) "\n" 133 | "sAK:" (~v (Chunk-scoreAttackKamikaze chunk)) "\n" 134 | "sS:" (~v (Chunk-scoreSiege chunk)) "\n" 135 | "sSK:" (~v (Chunk-scoreSiegeKamikaze chunk)) "\n" 136 | "sR:" (~v (Chunk-scoreResource chunk)) "\n")) 137 | 138 | (define (chunk->string3 chunk) 139 | (string-append "sRK:" (~v (Chunk-scoreResourceKamikaze chunk)) "\n" 140 | "pu:" (~v (Chunk-pollution chunk)) "\n" 141 | "aN:" (~v (Chunk-aNe chunk)) "\n" 142 | "aRN:" (~v (Chunk-aRNe chunk)) "\n" 143 | "sqs:" (~v (Chunk-squads chunk)) "\n" 144 | "bA:" (~v (Chunk-baseAlign chunk)) "\n" 145 | "H:" (~v (Chunk-hives chunk)) "\n" 146 | "T:" (~v (Chunk-traps chunk)) "\n" 147 | "U:" (~v (Chunk-utility chunk)) "\n" 148 | "vg:" (~v (Chunk-vg chunk)) "\n" 149 | "kam:" (~v (Chunk-kamikaze chunk)) "\n")) 150 | 151 | (define (normalizeRange xs) 152 | (let* ((sDev (stddev xs)) 153 | (sMean (mean xs)) 154 | (target (* 2.5 sDev)) 155 | (cleanXs (filter (lambda (x) 156 | (<= (abs (- x sMean)) target)) 157 | xs))) 158 | (MinMax (apply min cleanXs) 159 | (apply max cleanXs)))) 160 | 161 | (define (findChunkPropertiesMinMax chunks) 162 | (let ((xs (map Chunk-x chunks)) 163 | (ys (map Chunk-y chunks)) 164 | (movements (map Chunk-movement chunks)) 165 | (bases (map Chunk-base chunks)) 166 | (players (map Chunk-player chunks)) 167 | (resources (map Chunk-resource chunks)) 168 | (enemy (map Chunk-enemy chunks)) 169 | (passables (map Chunk-passable chunks)) 170 | (ticks (map Chunk-tick chunks)) 171 | (ratings (map Chunk-rating chunks)) 172 | (nests (map Chunk-nests chunks)) 173 | (worms (map Chunk-worms chunks)) 174 | (rallys (map Chunk-rally chunks)) 175 | (retreats (map Chunk-retreat chunks)) 176 | (rGens (map Chunk-resourceGen chunks)) 177 | (pGens (map Chunk-playerGen chunks)) 178 | (dGens (map Chunk-deathGen chunks)) 179 | (sRKs (map Chunk-scoreResourceKamikaze chunks)) 180 | (sRs (map Chunk-scoreResource chunks)) 181 | (sSKs (map Chunk-scoreSiegeKamikaze chunks)) 182 | (sSs (map Chunk-scoreSiege chunks)) 183 | (sAKs (map Chunk-scoreAttackKamikaze chunks)) 184 | (sAs (map Chunk-scoreAttack chunks)) 185 | (pol (map Chunk-pollution chunks)) 186 | (aNe (map Chunk-aNe chunks)) 187 | (aRNe (map Chunk-aRNe chunks)) 188 | (sqs (map Chunk-squads chunks)) 189 | (bA (map Chunk-baseAlign chunks)) 190 | (H (map Chunk-hives chunks)) 191 | (T (map Chunk-traps chunks)) 192 | (U (map Chunk-utility chunks)) 193 | (vg (map Chunk-vg chunks)) 194 | (kamikaze (map Chunk-kamikaze chunks))) 195 | 196 | (ChunkRange (MinMax (apply min xs) (apply max xs)) 197 | (MinMax (apply min ys) (apply max ys)) 198 | (normalizeRange movements) 199 | (normalizeRange bases) 200 | (normalizeRange players) 201 | (normalizeRange resources) 202 | (normalizeRange enemy) 203 | (MinMax (apply min passables) (apply max passables)) 204 | (normalizeRange ticks) 205 | (normalizeRange ratings) 206 | (MinMax (apply min nests) (apply max nests)) 207 | (MinMax (apply min worms) (apply max worms)) 208 | (MinMax (apply min rallys) (apply max rallys)) 209 | (MinMax (apply min retreats) (apply max retreats)) 210 | (MinMax (apply min rGens) (apply max rGens)) 211 | (MinMax (apply min pGens) (apply max pGens)) 212 | (MinMax (apply min dGens) (apply max dGens)) 213 | (normalizeRange sRKs) 214 | (normalizeRange sRs) 215 | (normalizeRange sSKs) 216 | (normalizeRange sSs) 217 | (normalizeRange sAKs) 218 | (normalizeRange sAs) 219 | (normalizeRange pol) 220 | (MinMax (apply min aNe) (apply max aNe)) 221 | (MinMax (apply min aRNe) (apply max aRNe)) 222 | (MinMax (apply min sqs) (apply max sqs)) 223 | (MinMax (apply min bA) (apply max bA)) 224 | (MinMax (apply min H) (apply max H)) 225 | (MinMax (apply min T) (apply max T)) 226 | (MinMax (apply min U) (apply max U)) 227 | (MinMax (apply min vg) (apply max vg)) 228 | (normalizeRange kamikaze) 229 | ))) 230 | 231 | (define (readState filePath) 232 | (let* ((replayChunks (getFile filePath)) 233 | (chunks (map stringToChunk (string-split replayChunks "\n"))) 234 | (minMaxes (findChunkPropertiesMinMax chunks))) 235 | (AiState chunks 236 | (apply hash 237 | (apply append 238 | (map (lambda (chunk) 239 | (list (list (Chunk-x chunk) 240 | (Chunk-y chunk)) 241 | chunk)) 242 | chunks))) 243 | minMaxes))) 244 | 245 | (define (test) 246 | (AiState-minMaxes (readState "/data/games/factorio/script-output/rampantState.txt")))) 247 | -------------------------------------------------------------------------------- /prototypes/utils/BeamUtils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local beamUtils = {} 18 | 19 | local DISALLOW_FRIENDLY_FIRE = settings.startup["rampant--disallowFriendlyFire"].value 20 | 21 | function beamUtils.makeBubble(attributes) 22 | local name = attributes.name .. "-bubble-rampant" 23 | data:extend({{ 24 | type = "explosion", 25 | name = name, 26 | flags = {"not-on-map"}, 27 | animation_speed = 1, 28 | animations = 29 | { 30 | { 31 | filename = "__base__/graphics/entity/laser-bubble/laser-bubble.png", 32 | priority = "extra-high", 33 | width = 8, 34 | height = 8, 35 | tint = attributes.tint2, 36 | frame_count = 5 37 | } 38 | }, 39 | light = {intensity = 1, size = 10, color = attributes.tint2 or {r = 1.0, g = 1.0, b = 1.0}}, 40 | smoke = "smoke-fast", 41 | smoke_count = 2, 42 | smoke_slow_down_factor = 1 43 | }}) 44 | return name 45 | end 46 | 47 | function beamUtils.makeLaser(attributes) 48 | local name = attributes.name .. "-laser-rampant" 49 | data:extend({{ 50 | type = "projectile", 51 | name = name , 52 | flags = {"not-on-map"}, 53 | collision_box = attributes.collisionBox or {{-0.3, -1.1}, {0.3, 1.1}}, 54 | acceleration = attributes.acceleration or 0.03, 55 | force_condition = (settings.startup["rampant--disableCollidingProjectiles"].value and "not-same") or nil, 56 | action = 57 | { 58 | type = "direct", 59 | force = (DISALLOW_FRIENDLY_FIRE and "not-same") or nil, 60 | action_delivery = 61 | { 62 | type = "instant", 63 | target_effects = 64 | { 65 | { 66 | type = "create-entity", 67 | entity_name = attributes.attackBubble or "laser-bubble" 68 | }, 69 | { 70 | type = "damage", 71 | damage = { amount = attributes.damage or 5, type = attributes.damageType or "laser"} 72 | } 73 | } 74 | } 75 | }, 76 | light = {intensity = 0.5, size = 10}, 77 | animation = 78 | { 79 | filename = "__base__/graphics/entity/laser/laser-to-tint-medium.png", 80 | tint = attributes.tint2 or {r=1.0, g=0.0, b=0.0}, 81 | frame_count = 1, 82 | width = 12, 83 | height = 33, 84 | priority = "high", 85 | blend_mode = "additive" 86 | }, 87 | speed = 0.15 88 | }}) 89 | return name 90 | end 91 | 92 | function beamUtils.makeBeam(attributes) 93 | local result = 94 | { 95 | type = "beam", 96 | flags = {"not-on-map"}, 97 | width = attributes.width or 0.5, 98 | collision_box = attributes.collisionBox or {{-0.3, -1.1}, {0.3, 1.1}}, 99 | damage_interval = attributes.damageInterval or 20, 100 | action = 101 | { 102 | type = "direct", 103 | force = (DISALLOW_FRIENDLY_FIRE and "not-same") or nil, 104 | action_delivery = 105 | { 106 | type = "instant", 107 | target_effects = 108 | { 109 | { 110 | type = "damage", 111 | damage = { amount = attributes.damage or 10, type = attributes.damageType or "electric"} 112 | } 113 | } 114 | } 115 | }, 116 | start = 117 | { 118 | filename = "__base__/graphics/entity/beam/tileable-beam-START.png", 119 | line_length = 4, 120 | width = 52, 121 | height = 40, 122 | frame_count = 16, 123 | axially_symmetrical = false, 124 | direction_count = 1, 125 | shift = {-0.03125, 0}, 126 | hr_version = { 127 | filename = "__base__/graphics/entity/beam/hr-tileable-beam-START.png", 128 | line_length = 4, 129 | width = 94, 130 | height = 66, 131 | frame_count = 16, 132 | axially_symmetrical = false, 133 | direction_count = 1, 134 | shift = {0.53125, 0}, 135 | scale = 0.5, 136 | } 137 | }, 138 | ending = 139 | { 140 | filename = "__base__/graphics/entity/beam/tileable-beam-END.png", 141 | line_length = 4, 142 | width = 49, 143 | height = 54, 144 | frame_count = 16, 145 | axially_symmetrical = false, 146 | direction_count = 1, 147 | shift = {-0.046875, 0}, 148 | hr_version = { 149 | filename = "__base__/graphics/entity/beam/hr-tileable-beam-END.png", 150 | line_length = 4, 151 | width = 91, 152 | height = 93, 153 | frame_count = 16, 154 | axially_symmetrical = false, 155 | direction_count = 1, 156 | shift = {-0.078125, -0.046875}, 157 | scale = 0.5, 158 | } 159 | }, 160 | head = 161 | { 162 | filename = "__base__/graphics/entity/beam/beam-head.png", 163 | line_length = 16, 164 | width = 45, 165 | height = 39, 166 | frame_count = 16, 167 | animation_speed = 0.5, 168 | blend_mode = "additive-soft", 169 | }, 170 | tail = 171 | { 172 | filename = "__base__/graphics/entity/beam/beam-tail.png", 173 | line_length = 16, 174 | width = 45, 175 | height = 39, 176 | frame_count = 16, 177 | blend_mode = "additive-soft", 178 | }, 179 | body = 180 | { 181 | { 182 | filename = "__base__/graphics/entity/beam/beam-body-1.png", 183 | line_length = 16, 184 | width = 45, 185 | height = 39, 186 | frame_count = 16, 187 | blend_mode = "additive-soft", 188 | }, 189 | { 190 | filename = "__base__/graphics/entity/beam/beam-body-2.png", 191 | line_length = 16, 192 | width = 45, 193 | height = 39, 194 | frame_count = 16, 195 | blend_mode = "additive-soft", 196 | }, 197 | { 198 | filename = "__base__/graphics/entity/beam/beam-body-3.png", 199 | line_length = 16, 200 | width = 45, 201 | height = 39, 202 | frame_count = 16, 203 | blend_mode = "additive-soft", 204 | }, 205 | { 206 | filename = "__base__/graphics/entity/beam/beam-body-4.png", 207 | line_length = 16, 208 | width = 45, 209 | height = 39, 210 | frame_count = 16, 211 | blend_mode = "additive-soft", 212 | }, 213 | { 214 | filename = "__base__/graphics/entity/beam/beam-body-5.png", 215 | line_length = 16, 216 | width = 45, 217 | height = 39, 218 | frame_count = 16, 219 | blend_mode = "additive-soft", 220 | }, 221 | { 222 | filename = "__base__/graphics/entity/beam/beam-body-6.png", 223 | line_length = 16, 224 | width = 45, 225 | height = 39, 226 | frame_count = 16, 227 | blend_mode = "additive-soft", 228 | }, 229 | } 230 | } 231 | 232 | result.working_sound = 233 | { 234 | { 235 | filename = "__base__/sound/fight/electric-beam.ogg", 236 | volume = 0.7 237 | } 238 | } 239 | 240 | local name = attributes.name .. "-beam-rampant" 241 | result.name = name 242 | 243 | data:extend({result}) 244 | return name 245 | end 246 | 247 | return beamUtils 248 | -------------------------------------------------------------------------------- /prototypes/EnergyThief.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | 18 | -- imports 19 | 20 | local thiefUtils = require("utils/ThiefUtils") 21 | local constants = require("libs/Constants") 22 | 23 | -- constants 24 | 25 | local energyThief = {} 26 | 27 | -- imported functions 28 | 29 | local TIERS = constants.TIERS 30 | 31 | local makeDrainCrystal = thiefUtils.makeDrainCrystal 32 | 33 | function energyThief.addFactionAddon() 34 | 35 | data:extend({ 36 | { 37 | type = "simple-entity-with-force", 38 | name = "drain-trigger-rampant", 39 | render_layer = "object", 40 | icon = "__base__/graphics/icons/steel-chest.png", 41 | icon_size = 32, 42 | flags = {"placeable-neutral", "player-creation"}, 43 | order = "s-e-w-f", 44 | minable = {mining_time = 1, result = "drain-trigger-rampant"}, 45 | max_health = 100, 46 | selectable_in_game = false, 47 | corpse = nil, 48 | collision_box = {{-0.35, -0.35}, {0.35, 0.35}}, 49 | selection_box = {{-0.5, -0.5}, {0.5, 0.5}}, 50 | picture = 51 | { 52 | filename = "__core__/graphics/empty.png", 53 | priority = "extra-high", 54 | width = 1, 55 | height = 1, 56 | shift = {0, 0} 57 | } 58 | }, 59 | 60 | { 61 | type = "item", 62 | name = "drain-trigger-rampant", 63 | icon = "__Rampant__/graphics/icons/thief/crystal-drain.png", 64 | icon_size = 32, 65 | icon_mipmaps = 1, 66 | flags = {"hidden"}, 67 | subgroup = "energy", 68 | order = "e[accumulator]-a[accumulator]", 69 | place_result = "drain-trigger-rampant", 70 | stack_size = 50 71 | }, 72 | 73 | { 74 | type = "item", 75 | name = "crystal-drain-pole-rampant", 76 | icon = "__Rampant__/graphics/icons/thief/crystal-drain.png", 77 | icon_size = 32, 78 | icon_mipmaps = 1, 79 | flags = {"hidden"}, 80 | subgroup = "energy", 81 | order = "e[accumulator]-a[accumulator]", 82 | place_result = "crystal-drain-pole-rampant", 83 | stack_size = 50 84 | }, 85 | 86 | { 87 | type = "electric-pole", 88 | name = "crystal-drain-pole-rampant", 89 | icon = "__Rampant__/graphics/icons/thief/crystal-drain.png", 90 | icon_size = 32, 91 | icon_mipmaps = 1, 92 | flags = {"hidden"}, 93 | selectable_in_game = false, 94 | minable = {hardness = 0.2, mining_time = 0.5, result = "big-electric-pole"}, 95 | max_health = 750, 96 | healing_per_tick = 0.02, 97 | corpse = nil, 98 | resistances = 99 | { 100 | { 101 | type = "physical", 102 | percent = 25 103 | }, 104 | { 105 | type = "fire", 106 | percent = 85 107 | }, 108 | { 109 | type = "electric", 110 | percent = 95 111 | }, 112 | { 113 | type = "laser", 114 | percent = 90 115 | } 116 | }, 117 | collision_box = {{-0.55, -0.55}, {0.55, 0.55}}, 118 | selection_box = {{-0.55, -0.55}, {0.55, 0.55}}, 119 | drawing_box = {{-1, -3}, {1, 0.5}}, 120 | maximum_wire_distance = 30, 121 | supply_area_distance = 9, 122 | vehicle_impact_sound = { filename = "__base__/sound/car-metal-impact.ogg", volume = 0.65 }, 123 | pictures = 124 | { 125 | filename = "__Rampant__/graphics/entities/thief/crystal-drain-pole.png", 126 | priority = "high", 127 | width = 168, 128 | height = 130, 129 | direction_count = 4, 130 | shift = {1.6, -1.4} 131 | }, 132 | connection_points = 133 | { 134 | { 135 | shadow = 136 | { 137 | copper = {2.7, 0}, 138 | green = {1.8, 0}, 139 | red = {3.6, 0} 140 | }, 141 | wire = 142 | { 143 | copper = {0, -2.5}, 144 | green = {-0.59375, -2.5}, 145 | red = {0.625, -2.5} 146 | } 147 | }, 148 | { 149 | shadow = 150 | { 151 | copper = {3.1, 0.2}, 152 | green = {2.3, -0.3}, 153 | red = {3.8, 0.6} 154 | }, 155 | wire = 156 | { 157 | copper = {-0.0625, -2.5}, 158 | green = {-0.5, -3}, 159 | red = {0.34375, -2} 160 | } 161 | }, 162 | { 163 | shadow = 164 | { 165 | copper = {2.9, 0.06}, 166 | green = {3.0, -0.6}, 167 | red = {3.0, 0.8} 168 | }, 169 | wire = 170 | { 171 | copper = {-0.09375, -2.5}, 172 | green = {-0.09375, -3}, 173 | red = {-0.09375, -2} 174 | } 175 | }, 176 | { 177 | shadow = 178 | { 179 | copper = {3.1, 0.2}, 180 | green = {3.8, -0.3}, 181 | red = {2.35, 0.6} 182 | }, 183 | wire = 184 | { 185 | copper = {-0.0625, -2.4}, 186 | green = {0.375, -2.9}, 187 | red = {-0.46875, -2.4} 188 | } 189 | } 190 | }, 191 | radius_visualisation_picture = 192 | { 193 | filename = "__base__/graphics/entity/small-electric-pole/electric-pole-radius-visualization.png", 194 | width = 12, 195 | height = 12, 196 | priority = "extra-high-no-scale" 197 | } 198 | } 199 | }) 200 | 201 | local chest = util.table.deepcopy(data.raw["radar"]["radar"]) 202 | chest.name = "pylon-target-rampant" 203 | chest.icon = "__Rampant__/graphics/icons/thief/crystal-drain.png" 204 | chest.icon_size = 32 205 | chest.corpse = nil 206 | chest.icon_mipmaps = 1 207 | chest.flags = {"not-repairable", "not-on-map", "hidden"} 208 | chest.subgroup = "enemies" 209 | chest.next_upgrade = nil 210 | chest.backer_name = false 211 | chest.rotation_speed = 0 212 | data.raw["simple-entity-with-force"]["drain-trigger-rampant"].dying_explosion = chest.dying_explosion 213 | chest.pictures = { 214 | layers={ 215 | { 216 | filename = "__Rampant__/graphics/entities/thief/crystal-drain-pole.png", 217 | priority = "high", 218 | width = 168, 219 | height = 130, 220 | direction_count = 4, 221 | shift = {1.6, -1.4} 222 | } 223 | } 224 | } 225 | chest.max_health = 750 226 | chest.resistances = 227 | { 228 | { 229 | type = "physical", 230 | percent = 25 231 | }, 232 | { 233 | type = "fire", 234 | percent = 85 235 | }, 236 | { 237 | type = "electric", 238 | percent = 95 239 | }, 240 | { 241 | type = "laser", 242 | percent = 90 243 | } 244 | } 245 | chest.energy_usage = "500kW" 246 | -- chest.collision_mask = {} 247 | chest.collision_box = nil 248 | chest.selection_box = {{-0.55, -0.55}, {0.55, 0.55}} 249 | chest.minable.result = "pylon-target-rampant" 250 | chest.working_sound = { 251 | sound = { 252 | { 253 | filename = "__base__/sound/accumulator-working.ogg" 254 | } 255 | }, 256 | apparent_volume = 2, 257 | } 258 | 259 | data:extend({ 260 | chest, 261 | 262 | { 263 | type = "item", 264 | name = "pylon-target-rampant", 265 | icon = "__Rampant__/graphics/icons/thief/crystal-drain.png", 266 | icon_size = 32, 267 | icon_mipmaps = 1, 268 | flags = {"hidden"}, 269 | subgroup = "enemies", 270 | order = "a[items]-h[steel-collector]", 271 | place_result = "pylon-target-rampant", 272 | stack_size = 50 273 | } 274 | }) 275 | 276 | 277 | for i=1,TIERS do 278 | local drainCrystalAttributes = { 279 | name = "crystal-v" .. i, 280 | drain = i * 1.3 .. "MW", 281 | scale = (i * 0.1) + 0.5, 282 | health = 400 * i 283 | } 284 | 285 | makeDrainCrystal(drainCrystalAttributes) 286 | end 287 | 288 | end 289 | 290 | return energyThief 291 | -------------------------------------------------------------------------------- /tests.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local tests = {} 18 | 19 | local constants = require("libs/Constants") 20 | local chunkUtils = require("libs/ChunkUtils") 21 | local chunkPropertyUtils = require("libs/ChunkPropertyUtils") 22 | 23 | function tests.chunkCount() 24 | local count = 0 25 | for _,map in pairs(global.universe.maps) do 26 | count = count + #map.processQueue 27 | end 28 | print(count) 29 | end 30 | 31 | function tests.fillableDirtTest() 32 | local playerPosition = game.players[1].position 33 | local chunkX = math.floor(playerPosition.x * 0.03125) * 32 34 | local chunkY = math.floor(playerPosition.y * 0.03125) * 32 35 | game.get_surface(global.natives.activeSurface).set_tiles({{name="fillableDirt", position={chunkX-1, chunkY-1}}, 36 | {name="fillableDirt", position={chunkX, chunkY-1}}, 37 | {name="fillableDirt", position={chunkX-1, chunkY}}, 38 | {name="fillableDirt", position={chunkX, chunkY}}}, 39 | false) 40 | end 41 | 42 | function tests.tunnelTest() 43 | local playerPosition = game.players[1].position 44 | local chunkX = math.floor(playerPosition.x * 0.03125) * 32 45 | local chunkY = math.floor(playerPosition.y * 0.03125) * 32 46 | game.get_surface(global.natives.activeSurface).create_entity({name="tunnel-entrance-rampant", position={chunkX, chunkY}}) 47 | end 48 | 49 | function tests.reveal (size) 50 | local pos = game.player.character.position 51 | game.player.force.chart(game.player.surface, 52 | {{x=-size+pos.x, y=-size+pos.y}, {x=size+pos.x, y=size+pos.y}}) 53 | end 54 | 55 | function tests.showBaseGrid(time) 56 | local map = global.universe.maps[game.player.surface.index] 57 | local chunks = map.chunkToBase 58 | for chunk in pairs(chunks) do 59 | local count = chunkPropertyUtils.getEnemyStructureCount(map, chunk) 60 | chunkUtils.mapScanEnemyChunk(chunk, map, game.tick) 61 | local newCount = chunkPropertyUtils.getEnemyStructureCount(map, chunk) 62 | if newCount ~= count then 63 | constants.gpsDebug(chunk.x+16,chunk.y+16, "f2:" .. tostring(count) .. "/" .. tostring(newCount)) 64 | chunkUtils.colorChunk(chunk, game.player.surface.index, {0.3, 0.1, 0.1, 0.6}, time and tonumber(time)) 65 | else 66 | chunkUtils.colorChunk(chunk, game.player.surface.index, nil, time and tonumber(time)) 67 | end 68 | end 69 | end 70 | 71 | -- function tests.showMovementGrid() 72 | -- local chunks = global.map.processQueue 73 | -- for i=1,#chunks do 74 | -- local chunk = chunks[i] 75 | -- local color = "concrete" 76 | -- if (chunkPropertyUtils.getPassable(global.map, chunk) == constants.CHUNK_ALL_DIRECTIONS) then 77 | -- color = "hazard-concrete-left" 78 | -- elseif (chunkPropertyUtils.getPassable(global.map, chunk) == constants.CHUNK_NORTH_SOUTH) then 79 | -- color = "concrete" 80 | -- elseif (chunkPropertyUtils.getPassable(global.map, chunk) == constants.CHUNK_EAST_WEST) then 81 | -- color = "stone-path" 82 | -- end 83 | -- chunkUtils.colorChunk(chunk.x, chunk.y, color, game.get_surface(global.natives.activeSurface)) 84 | -- end 85 | -- end 86 | 87 | -- function tests.colorResourcePoints() 88 | -- local chunks = global.map.processQueue 89 | -- for i=1,#chunks do 90 | -- local chunk = chunks[i] 91 | -- local color = "concrete" 92 | -- if (chunk[constants.RESOURCE_GENERATOR] ~= 0) and (chunk[constants.NEST_COUNT] ~= 0) then 93 | -- color = "hazard-concrete-left" 94 | -- elseif (chunk[constants.RESOURCE_GENERATOR] ~= 0) then 95 | -- color = "deepwater" 96 | -- elseif (chunk[constants.NEST_COUNT] ~= 0) then 97 | -- color = "stone-path" 98 | -- end 99 | -- chunkUtils.colorChunk(chunk.x, chunk.y, color, game.get_surface(global.natives.activeSurface)) 100 | -- end 101 | -- end 102 | 103 | function tests.entityStats(name, d) 104 | local playerPosition = game.players[1].position 105 | local chunkX = math.floor(playerPosition.x * 0.03125) * 32 106 | local chunkY = math.floor(playerPosition.y * 0.03125) * 32 107 | local a = game.get_surface(global.natives.activeSurface).create_entity({name=name, position={chunkX, chunkY}}) 108 | if d then 109 | a['direction'] = d 110 | end 111 | print(serpent.dump(a)) 112 | a.destroy() 113 | end 114 | 115 | local function lookupIndexFaction(targetFaction) 116 | for i=1,#constants.FACTION_SET do 117 | if constants.FACTION_SET[i].type == targetFaction then 118 | return i 119 | end 120 | end 121 | return 0 122 | end 123 | 124 | local function scoreResourceLocationKamikaze(_, neighborChunk) 125 | local settle = neighborChunk[constants.RESOURCE_PHEROMONE] 126 | return settle 127 | - (neighborChunk[constants.PLAYER_PHEROMONE] * constants.PLAYER_PHEROMONE_MULTIPLER) 128 | - neighborChunk[constants.ENEMY_PHEROMONE] 129 | end 130 | 131 | local function scoreSiegeLocationKamikaze(_, neighborChunk) 132 | local settle = neighborChunk[constants.BASE_PHEROMONE] 133 | + neighborChunk[constants.RESOURCE_PHEROMONE] * 0.5 134 | + (neighborChunk[constants.PLAYER_PHEROMONE] * constants.PLAYER_PHEROMONE_MULTIPLER) 135 | - neighborChunk[constants.ENEMY_PHEROMONE] 136 | 137 | return settle 138 | end 139 | 140 | local function scoreResourceLocation(map, neighborChunk) 141 | local settle = (neighborChunk[constants.RESOURCE_PHEROMONE]) 142 | return settle 143 | - (neighborChunk[constants.PLAYER_PHEROMONE] * constants.PLAYER_PHEROMONE_MULTIPLER) 144 | - neighborChunk[constants.ENEMY_PHEROMONE] 145 | end 146 | 147 | local function scoreSiegeLocation(map, neighborChunk) 148 | local settle = neighborChunk[constants.BASE_PHEROMONE] 149 | + neighborChunk[constants.RESOURCE_PHEROMONE] * 0.5 150 | + (neighborChunk[constants.PLAYER_PHEROMONE] * constants.PLAYER_PHEROMONE_MULTIPLER) 151 | - neighborChunk[constants.ENEMY_PHEROMONE] 152 | 153 | return settle 154 | end 155 | 156 | local function scoreAttackLocation(map, neighborChunk) 157 | local damage = neighborChunk[constants.BASE_PHEROMONE] + 158 | (neighborChunk[constants.PLAYER_PHEROMONE] * constants.PLAYER_PHEROMONE_MULTIPLER) 159 | return damage 160 | end 161 | 162 | local function scoreAttackKamikazeLocation(_, neighborChunk) 163 | local damage = neighborChunk[constants.BASE_PHEROMONE] + (neighborChunk[constants.PLAYER_PHEROMONE] * constants.PLAYER_PHEROMONE_MULTIPLER) 164 | return damage 165 | end 166 | 167 | function tests.exportAiState() 168 | 169 | local printState = function () 170 | local map = global.universe.maps[game.players[1].surface.index] 171 | local chunks = map.processQueue 172 | local s = "" 173 | for i=1,#chunks do 174 | local chunk = chunks[i] 175 | 176 | local base = chunk.base 177 | local alignmentCount = 0 178 | 179 | if base then 180 | if (#base.alignment == 2) then 181 | alignmentCount = (math.abs(base.x) * 10000) + (math.abs(base.y) * 10000) + (lookupIndexFaction(base.alignment[1]) * 100) + lookupIndexFaction(base.alignment[2]) 182 | else 183 | alignmentCount = (math.abs(base.x) * 10000) + (math.abs(base.y) * 10000) + lookupIndexFaction(base.alignment[1]) 184 | end 185 | end 186 | 187 | s = s .. table.concat({chunk.x, 188 | chunk.y, 189 | chunkPropertyUtils.getCombinedDeathGeneratorRating(chunk), 190 | chunk[constants.BASE_PHEROMONE], 191 | chunk[constants.PLAYER_PHEROMONE], 192 | chunk[constants.RESOURCE_PHEROMONE], 193 | chunk[constants.ENEMY_PHEROMONE], 194 | chunkPropertyUtils.getPassable(chunk), 195 | chunk[constants.CHUNK_TICK], 196 | chunkPropertyUtils.getPathRating(chunk), 197 | chunk.nestCount or 0, 198 | chunk.turretCount or 0, 199 | chunkPropertyUtils.getRallyTick(chunk) or 0, 200 | chunkPropertyUtils.getRetreatTick(chunk) or 0, 201 | chunk.resourceGenerator or 0, 202 | chunk.playerBaseGenerator or 0, 203 | chunkPropertyUtils.getCombinedDeathGenerator(chunk), 204 | scoreResourceLocationKamikaze(map, chunk), 205 | scoreResourceLocation(map, chunk), 206 | scoreSiegeLocationKamikaze(map, chunk), 207 | scoreSiegeLocation(map, chunk), 208 | scoreAttackKamikazeLocation(map, chunk), 209 | scoreAttackLocation(map, chunk), 210 | game.get_surface(game.players[1].surface.index).get_pollution(chunk), 211 | (chunkPropertyUtils.isActiveNest(chunk) and 1) or 0, 212 | (chunkPropertyUtils.isActiveRaidNest(chunk) and 1) or 0, 213 | table_size(chunk.squads or {}), 214 | alignmentCount, 215 | chunk.hiveCount or 0, 216 | chunk.trapCount or 0, 217 | chunk.utilityCount or 0, 218 | global.universe.chunkToVictory[chunk.id] or 0, 219 | chunk[constants.KAMIKAZE_PHEROMONE] 220 | }, ",") .. "\n" 221 | end 222 | game.write_file("rampantState.txt", s, false) 223 | end 224 | 225 | return function(interval) 226 | if not interval then 227 | interval = 0 228 | else 229 | interval = tonumber(interval) 230 | end 231 | 232 | printState() 233 | 234 | if (interval > 0) then 235 | script.on_nth_tick(interval, printState) 236 | end 237 | end 238 | end 239 | 240 | function tests.dumpEnvironment(x) 241 | print (serpent.dump(global[x])) 242 | end 243 | 244 | return tests 245 | -------------------------------------------------------------------------------- /visualizer/visual.rkt: -------------------------------------------------------------------------------- 1 | ;; Copyright (C) 2022 veden 2 | 3 | ;; This program is free software: you can redistribute it and/or modify 4 | ;; it under the terms of the GNU General Public License as published by 5 | ;; the Free Software Foundation, either version 3 of the License, or 6 | ;; (at your option) any later version. 7 | 8 | ;; This program is distributed in the hope that it will be useful, 9 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ;; GNU General Public License for more details. 12 | 13 | ;; You should have received a copy of the GNU General Public License 14 | ;; along with this program. If not, see . 15 | 16 | (module AiVisualizer racket 17 | (provide (all-defined-out)) 18 | 19 | (require "parseState.rkt") 20 | (provide (all-from-out "parseState.rkt")) 21 | (require racket/gui/base) 22 | (require plot) 23 | 24 | (define CHUNK_SIZE 32) 25 | 26 | (define INVALID_CHUNK (Chunk -1 -1 0 0 0 27 | 0 0 0 0 0 28 | 0 0 0 0 0 29 | 0 0 0 0 0 30 | 0 0 0 0 0 31 | 0 0 0 0 0 32 | 0 0 0)) 33 | 34 | (define windowX 500) 35 | (define windowY 0) 36 | (define windowWidth 1024) 37 | (define windowHeight 1024) 38 | 39 | (define activeHighlight null) 40 | (define activeLayer "movement") 41 | 42 | (define (normalize v low high) 43 | (/ (- v low) 44 | (- high low))) 45 | 46 | (define (fromNormalize x low high) 47 | (+ (* (- high low) 48 | x) 49 | low)) 50 | 51 | (define (roundTo x digits) 52 | (* (floor (/ x digits)) 53 | digits)) 54 | 55 | (define (visualize) 56 | (define frameWithEvents% (class frame% 57 | (define/override (on-subwindow-char r event) 58 | (when (eq? (send event get-key-code) #\c) 59 | (exit)) 60 | (super on-subwindow-char r event)) 61 | (super-new))) 62 | 63 | (define (newFrame width height x y [label ""]) 64 | (new frameWithEvents% 65 | [label label] 66 | [width width] 67 | [height height] 68 | [x x] 69 | [y y])) 70 | 71 | (define templates (list '(250 750 0 0 "controls") 72 | (list windowWidth windowHeight windowX windowY "map"))) 73 | (define frames (map (lambda (frame) 74 | (match-let (((list width height x y name) frame)) 75 | (newFrame width height x y name))) 76 | templates)) 77 | 78 | (define mainFrame (first frames)) 79 | (define mapFrame (second frames)) 80 | 81 | (define activeChunkSet null) 82 | (define activeChunkSetLookup null) 83 | (define activeChunkMinMaxSet null) 84 | 85 | (define topPanel (new panel% 86 | [parent mainFrame] 87 | (alignment '(left top)))) 88 | 89 | (define botPanel (new panel% 90 | [parent mainFrame] 91 | (alignment '(right bottom)))) 92 | 93 | (define statusBox (new message% 94 | [parent topPanel] 95 | [label (~v "")] 96 | [vert-margin 16])) 97 | (define siteBox (new message% 98 | [parent topPanel] 99 | [label ""] 100 | [vert-margin 30])) 101 | (define siteBox2 (new message% 102 | [parent topPanel] 103 | [label ""] 104 | [horiz-margin 300])) 105 | (define siteBox3 (new message% 106 | [parent topPanel] 107 | [label ""] 108 | [vert-margin 300])) 109 | 110 | (new button% 111 | [parent mainFrame] 112 | [label "Quit"] 113 | (callback (lambda (button event) 114 | (exit)))) 115 | 116 | (define tileWidth 0) 117 | (define tileHeight 0) 118 | 119 | (define minX 0) 120 | (define maxX 0) 121 | (define minY 0) 122 | (define maxY 0) 123 | 124 | (define canvasWithEvents% (class canvas% 125 | (define/override (on-event event) 126 | (match (send event get-event-type) 127 | ((== 'motion) (displayChunk (send event get-x) (send event get-y))) 128 | ((== 'left-down) (displayHighlight (send event get-x) (send event get-y))) 129 | ((== 'right-down) (begin (set! activeHighlight null) 130 | (refresh (send this get-dc)))) 131 | (t (super on-event event)))) 132 | (super-new))) 133 | 134 | (define (refresh dc) 135 | (when (not (null? dc)) 136 | (drawFrame dc))) 137 | 138 | (define drawFrame (lambda (context) 139 | null)) 140 | 141 | (define canvass (map (lambda (frame) 142 | (let ((c (new canvasWithEvents% 143 | [parent frame] 144 | [paint-callback (lambda (canvas dc) 145 | (drawFrame dc))]))) 146 | (send c set-canvas-background (make-object color% 111 111 111)) 147 | c)) 148 | (cdr frames))) 149 | (define dcs (map (lambda (canvas) 150 | (send canvas get-dc)) 151 | canvass)) 152 | 153 | (define (showVisual dc aiState) 154 | (let* ((chunkMinMaxes (AiState-minMaxes aiState)) 155 | (minMaxX (ChunkRange-x chunkMinMaxes)) 156 | (minMaxY (ChunkRange-y chunkMinMaxes))) 157 | (set! activeChunkSet (AiState-chunks aiState)) 158 | (set! activeChunkMinMaxSet chunkMinMaxes) 159 | (set! activeChunkSetLookup (AiState-chunksLookup aiState)) 160 | 161 | (when (Chunk? activeHighlight) 162 | (set! activeHighlight (findChunk (Chunk-x activeHighlight) 163 | (Chunk-y activeHighlight)))) 164 | 165 | (set! minX (MinMax-min minMaxX)) 166 | (set! maxX (MinMax-max minMaxX)) 167 | (set! minY (MinMax-min minMaxY)) 168 | (set! maxY (MinMax-max minMaxY)) 169 | 170 | (set! tileWidth (ceiling (/ windowWidth (+ (abs (/ (- maxX minX) CHUNK_SIZE)) 3)))) 171 | (set! tileHeight (ceiling (/ windowHeight (+ (abs (/ (- maxY minY) CHUNK_SIZE)) 3)))) 172 | 173 | (refresh dc) 174 | 175 | (thread (lambda () 176 | (sync (filesystem-change-evt "/mnt/gallery/gameFiles/factorio/script-output/rampantState.txt")) 177 | (showVisual dc (readState "/mnt/gallery/gameFiles/factorio/script-output/rampantState.txt")))))) 178 | 179 | (define dcMap (first dcs)) 180 | 181 | (showVisual dcMap (readState "/mnt/gallery/gameFiles/factorio/script-output/rampantState.txt")) 182 | 183 | (define (chunkX->screenX x) 184 | (roundTo (* (normalize x minX maxX) 185 | windowWidth) 186 | tileWidth)) 187 | 188 | (define (chunkY->screenY y) 189 | (roundTo (* (normalize y minY maxY) 190 | windowHeight) 191 | tileHeight)) 192 | 193 | (define (screenX->chunkX x) 194 | (roundTo (fromNormalize (/ x windowWidth) 195 | minX 196 | maxX) 197 | CHUNK_SIZE)) 198 | 199 | (define (screenY->chunkY y) 200 | (roundTo (fromNormalize (/ y windowHeight) 201 | minY 202 | maxY) 203 | CHUNK_SIZE)) 204 | 205 | (set! drawFrame (lambda (context) 206 | (send context suspend-flush) 207 | (send context clear) 208 | (let ((chunkField (eval (string->symbol (string-append "Chunk-" activeLayer)))) 209 | (chunkRangeField (eval (string->symbol (string-append "ChunkRange-" activeLayer))))) 210 | (map (lambda (chunk) 211 | (let ((x (chunkX->screenX (Chunk-x chunk))) 212 | (y (chunkY->screenY (Chunk-y chunk)))) 213 | (if (eq? activeHighlight chunk) 214 | (send context set-pen (make-object color% 255 255 255) 1 'solid) 215 | (send context set-pen (make-object color% 0 0 0) 1 'solid)) 216 | (define (dcDraw dc property minMax) 217 | (scaleColor dc property (MinMax-min minMax) (MinMax-max minMax)) 218 | (send dc draw-rectangle x y tileWidth tileHeight)) 219 | (dcDraw context 220 | (chunkField chunk) 221 | (chunkRangeField activeChunkMinMaxSet)))) 222 | activeChunkSet)) 223 | (send context resume-flush))) 224 | 225 | (define (findChunk x y) 226 | (hash-ref activeChunkSetLookup (list x y) INVALID_CHUNK)) 227 | 228 | (define (displayChunk x y) 229 | (let ((chunk (if (Chunk? activeHighlight) 230 | activeHighlight 231 | (findChunk (screenX->chunkX x) 232 | (screenY->chunkY y))))) 233 | (send siteBox set-label 234 | (chunk->string chunk)) 235 | (send siteBox2 set-label 236 | (chunk->string2 chunk)) 237 | (send siteBox3 set-label 238 | (chunk->string3 chunk)))) 239 | 240 | (define (displayHighlight x y) 241 | ;; (display (list (screenX->chunkX x) 242 | ;; (screenY->chunkY y) 243 | ;; x 244 | ;; y)) 245 | ;; (display "\n") 246 | 247 | (let ((chunk (findChunk (screenX->chunkX x) 248 | (screenY->chunkY y)))) 249 | (set! activeHighlight chunk)) 250 | (refresh dcMap)) 251 | 252 | (define (normalizeLog x low high) 253 | (if (= (- high low) 0) 254 | 0 255 | (/ (- x low) 256 | (- high low)))) 257 | 258 | (define (scaleColor dc value low high) 259 | (let* ((v (normalizeLog value low high)) 260 | (r (cond ((= v 0) 0) 261 | ((> 0.75 v) 150) 262 | ((> 0.50 v) 100) 263 | (#t 50))) 264 | (g (inexact->exact (round (* v 255))))) 265 | (send dc 266 | set-brush 267 | (make-object color% 268 | (min (max r 0) 255) 269 | (min (max g 0) 255) 270 | 0) 271 | 'solid))) 272 | 273 | 274 | (new button% 275 | [parent mainFrame] 276 | [label "Retry"] 277 | (callback (lambda (button event) 278 | (showVisual dcMap (readState "/mnt/gallery/gameFiles/factorio/script-output/rampantState.txt"))))) 279 | 280 | (new radio-box% 281 | [label "Show Layer"] 282 | [choices (list "movement" "base" "player" "resource" "enemy" "passable" "tick" "rating" "nests" "worms" "rally" "retreat" "resourceGen" "playerGen" "deathGen" "scoreResourceKamikaze" "scoreResource" "scoreSiegeKamikaze" "scoreSiege" "scoreAttackKamikaze" "scoreAttack" "pollution" "aNe" "aRNe" "squads" "baseAlign" "hives" "traps" "utility" "vg" "kamikaze")] 283 | [selection 0] 284 | [parent botPanel] 285 | (callback (lambda (radioButton event) 286 | (set! activeLayer (send radioButton get-item-label (send radioButton get-selection))) 287 | (refresh dcMap)))) 288 | 289 | (map (lambda (f) 290 | (send f show #t)) 291 | frames))) 292 | -------------------------------------------------------------------------------- /prototypes/utils/AttackBall.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | -- import 18 | 19 | local fireUtils = require("FireUtils") 20 | local stickerUtils = require("StickerUtils") 21 | local streamUtils = require("StreamUtils") 22 | local projectileUtils = require("ProjectileUtils") 23 | 24 | -- constants 25 | 26 | local DISALLOW_FRIENDLY_FIRE = settings.startup["rampant--disallowFriendlyFire"].value 27 | 28 | -- imported functions 29 | 30 | local makeStream = streamUtils.makeStream 31 | local makeSticker = stickerUtils.makeSticker 32 | local makeProjectile = projectileUtils.makeProjectile 33 | local makeAcidSplashFire = fireUtils.makeAcidSplashFire 34 | local makeFire = fireUtils.makeFire 35 | local makeSpreadEffect = fireUtils.makeSpreadEffect 36 | 37 | -- dumb acid projectiles 38 | local AttackBall = {} 39 | 40 | function AttackBall.createAttackBall(attributes) 41 | 42 | local templateAOEDamage = { amount = attributes.damage * 0.75, type = attributes.damageType or "acid" } 43 | local templateDirectDamage = { amount = attributes.damage * 0.25, type = attributes.damageType or "acid" } 44 | local templateArea = { 45 | type = "area", 46 | radius = attributes.radius, 47 | force = (DISALLOW_FRIENDLY_FIRE and "not-same") or attributes.force or nil, 48 | ignore_collision_condition = true, 49 | action_delivery = (attributes.areaActionDelivery and attributes.areaActionDelivery(attributes)) or 50 | { 51 | { 52 | type = "instant", 53 | target_effects = (attributes.areaEffects and attributes.areaEffects(attributes)) or 54 | { 55 | { 56 | type = "damage", 57 | damage = templateAOEDamage 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | local targetEffects 65 | if attributes.attackPointEffects then 66 | targetEffects = (attributes.attackPointEffects and attributes.attackPointEffects(attributes)) 67 | else 68 | local rec = { 69 | { 70 | type = "damage", 71 | damage = templateDirectDamage 72 | }, 73 | { 74 | type = "create-entity", 75 | entity_name = "water-splash", 76 | tile_collision_mask = { "ground-tile" } 77 | }, 78 | { 79 | type = "play-sound", 80 | sound = 81 | { 82 | { 83 | filename = "__base__/sound/creatures/projectile-acid-burn-1.ogg", 84 | volume = 0.25 + (attributes.effectiveLevel * 0.05) 85 | }, 86 | { 87 | filename = "__base__/sound/creatures/projectile-acid-burn-2.ogg", 88 | volume = 0.25 + (attributes.effectiveLevel * 0.05) 89 | }, 90 | { 91 | filename = "__base__/sound/creatures/projectile-acid-burn-long-1.ogg", 92 | volume = 0.25 + (attributes.effectiveLevel * 0.05) 93 | }, 94 | { 95 | filename = "__base__/sound/creatures/projectile-acid-burn-long-2.ogg", 96 | volume = 0.25 + (attributes.effectiveLevel * 0.05) 97 | } 98 | } 99 | } 100 | } 101 | if not attributes.noAcidPuddle then 102 | rec[#rec+1] = { 103 | type="create-fire", 104 | entity_name = makeAcidSplashFire(attributes, attributes.stickerName or makeSticker(attributes)), 105 | check_buildability = true, 106 | initial_ground_flame_count = 1, 107 | show_in_tooltip = true 108 | } 109 | end 110 | targetEffects = rec 111 | end 112 | 113 | local templateActions = { 114 | templateArea, 115 | { 116 | type = "direct", 117 | force = (DISALLOW_FRIENDLY_FIRE and "not-same") or nil, 118 | action_delivery = { 119 | type = "instant", 120 | target_effects = targetEffects 121 | } 122 | } 123 | } 124 | 125 | local name 126 | if (attributes.attackType == "stream") then 127 | attributes.actions = templateActions 128 | name = makeStream(attributes) 129 | else 130 | name = makeProjectile(attributes, templateActions) 131 | end 132 | 133 | return name 134 | end 135 | 136 | function AttackBall.createSpitFire(attributes) 137 | local spawnEntityName = makeSpreadEffect({ 138 | name = attributes.name, 139 | tint2 = attributes.tint2, 140 | fireDamagePerTick = attributes.fireDamagePerTick, 141 | fireDamagePerTickType = attributes.fireDamagePerTickType, 142 | }) 143 | local stickerName = makeSticker({ 144 | name = attributes.name, 145 | spawnEntityName = spawnEntityName, 146 | stickerDuration = attributes.stickerDuration, 147 | stickerDamagePerTick = attributes.stickerDamagePerTick, 148 | stickerDamagePerTickType = attributes.stickerDamagePerTickType, 149 | stickerMovementModifier = attributes.stickerMovementModifier, 150 | tint2 = attributes.tint2, 151 | fireSpreadRadius = attributes.fireSpreadRadius 152 | }) 153 | local fireName = makeFire({ 154 | name = attributes.name, 155 | tint2 = attributes.tint2 or {r=0, g=0.9, b=0, a=0.5}, 156 | spawnEntityName = spawnEntityName, 157 | fireDamagePerTick = attributes.fireDamagePerTick, 158 | fireDamagePerTickType = attributes.fireDamagePerTickType, 159 | damageMaxMultipler = attributes.damageMaxMultipler, 160 | multiplerIncrease = attributes.multiplerIncrease, 161 | multiplerDecrease = attributes.multiplerDecrease, 162 | stickerName = stickerName 163 | }) 164 | 165 | return makeProjectile(attributes, 166 | { 167 | { 168 | type = "area", 169 | radius = attributes.radius or 2.5, 170 | force = "not-same", 171 | action_delivery = 172 | { 173 | type = "instant", 174 | target_effects = 175 | { 176 | { 177 | type = "create-sticker", 178 | sticker = stickerName, 179 | check_buildability = true 180 | }, 181 | { 182 | type = "create-entity", 183 | entity_name = "water-splash", 184 | tile_collision_mask = { "ground-tile" } 185 | }, 186 | { 187 | type = "damage", 188 | damage = { amount = attributes.damage, type = attributes.damageType or "fire" } 189 | } 190 | } 191 | } 192 | }, 193 | { 194 | type = "cluster", 195 | cluster_count = 2, 196 | distance = 2 + (0.1 * attributes.effectiveLevel), 197 | distance_deviation = 1.5, 198 | action_delivery = { 199 | type = "instant", 200 | target_effects = { 201 | { 202 | type="create-fire", 203 | entity_name = fireName, 204 | check_buildability = true, 205 | initial_ground_flame_count = 2, 206 | show_in_tooltip = true 207 | } 208 | } 209 | } 210 | }, 211 | { 212 | type = "direct", 213 | action_delivery = { 214 | type = "instant", 215 | target_effects = { 216 | type= "create-fire", 217 | entity_name = fireName, 218 | check_buildability = true, 219 | show_in_tooltip = true 220 | } 221 | } 222 | } 223 | } 224 | ) 225 | end 226 | 227 | function AttackBall.generateVanilla() 228 | AttackBall.createAttackBall({name="acid-ball", scale=0.5, directionOnly=true, attackType="projectile", tint2={r=0, g=1, b=0.3, a=0.5}, damage=4, damagePerTick=0.1, stickerName="acid-sticker-small", radius=1.2, effectiveLevel=1}) 229 | AttackBall.createAttackBall({name="acid-ball-1", scale=0.65, directionOnly=true, attackType="projectile", tint2={r=0, g=1, b=0.3, a=0.5}, damage=7.5, damagePerTick=0.2, stickerName="acid-sticker-medium", radius=1.3, effectiveLevel=3}) 230 | 231 | AttackBall.createAttackBall({name="acid-ball-2-direction", scale=0.85, directionOnly=true, attackType="projectile", tint2={r=0, g=1, b=0.3, a=0.5}, damage=11.25, damagePerTick=0.03, stickerName="acid-sticker-big", radius=1.4, effectiveLevel=5}) 232 | AttackBall.createAttackBall({name="acid-ball-3-direction", scale=1.0, directionOnly=true, attackType="projectile", tint2={r=0, g=1, b=0.3, a=0.5}, damage=15, damagePerTick=0.55, stickerName="acid-sticker-behemoth", radius=1.5, effectiveLevel=7}) 233 | 234 | AttackBall.createAttackBall({name="acid-ball-2", scale=0.85, attackType="projectile", tint2={r=0, g=1, b=0.3, a=0.5}, damage=11.25, damagePerTick=0.2, stickerName="acid-sticker-small", radius=1.4, effectiveLevel=3}) 235 | AttackBall.createAttackBall({name="acid-ball-3", scale=1.0, attackType="projectile", tint2={r=0, g=1, b=0.3, a=0.5}, damage=15, damagePerTick=0.5, stickerName="acid-sticker-medium", radius=1.5, effectiveLevel=5}) 236 | AttackBall.createAttackBall({name="acid-ball-4", scale=1.2, attackType="projectile", tint2={r=0, g=1, b=0.3, a=0.5}, damage=22.5, damagePerTick=0.75, stickerName="acid-sticker-big", radius=1.75, effectiveLevel=7}) 237 | AttackBall.createAttackBall({name="acid-ball-5", scale=1.3, attackType="projectile", tint2={r=0, g=1, b=0.3, a=0.5}, damage=32.5, damagePerTick=1, stickerName="acid-sticker-behemoth", radius=2, effectiveLevel=8}) 238 | end 239 | 240 | return AttackBall 241 | -------------------------------------------------------------------------------- /prototypes/utils/ImageUtils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | 17 | local imageUtils = {} 18 | 19 | -- module code 20 | 21 | local function foreach(table_, fun_) 22 | for _, tab in pairs(table_) do fun_(tab) end 23 | return table_ 24 | end 25 | 26 | function imageUtils.create_burnt_patch_pictures(attributes) 27 | local base = { 28 | filename = "__base__/graphics/entity/fire-flame/burnt-patch.png", 29 | line_length = 3, 30 | width = 115, 31 | height = 56, 32 | frame_count = 9, 33 | axially_symmetrical = false, 34 | tint = attributes.tint2, 35 | direction_count = 1, 36 | shift = {-0.09375, 0.125}, 37 | } 38 | 39 | local variations = {} 40 | 41 | for y=1,(base.frame_count / base.line_length) do 42 | for x=1,base.line_length do 43 | table.insert(variations, 44 | { 45 | filename = base.filename, 46 | width = base.width, 47 | height = base.height, 48 | tint = base.tint, 49 | shift = base.shift, 50 | x = (x-1) * base.width, 51 | y = (y-1) * base.height, 52 | }) 53 | end 54 | end 55 | 56 | return variations 57 | end 58 | 59 | 60 | function imageUtils.create_small_tree_flame_animations(opts) 61 | local fire_blend_mode = opts.blend_mode or "additive" 62 | local fire_animation_speed = opts.animation_speed or 0.5 63 | local fire_scale = opts.scale or 1 64 | local fire_tint = opts.tint2 65 | local fire_flags = { "compressed" } 66 | local retval = { 67 | { 68 | filename = "__base__/graphics/entity/fire-flame/tree-fire-flame-01-a.png", 69 | line_length = 8, 70 | width = 38, 71 | height = 110, 72 | frame_count = 32, 73 | axially_symmetrical = false, 74 | direction_count = 1, 75 | shift = {-0.03125, -1.5}, 76 | blend_mode = fire_blend_mode, 77 | animation_speed = fire_animation_speed, 78 | scale = fire_scale, 79 | tint = fire_tint, 80 | flags = fire_flags 81 | }, 82 | { 83 | filename = "__base__/graphics/entity/fire-flame/tree-fire-flame-01-b.png", 84 | line_length = 8, 85 | width = 39, 86 | height = 111, 87 | frame_count = 32, 88 | axially_symmetrical = false, 89 | direction_count = 1, 90 | shift = {-0.078125, -1.51562}, 91 | blend_mode = fire_blend_mode, 92 | animation_speed = fire_animation_speed, 93 | scale = fire_scale, 94 | tint = fire_tint, 95 | flags = fire_flags 96 | }, 97 | { 98 | filename = "__base__/graphics/entity/fire-flame/tree-fire-flame-01-c.png", 99 | line_length = 8, 100 | width = 44, 101 | height = 108, 102 | frame_count = 32, 103 | axially_symmetrical = false, 104 | direction_count = 1, 105 | shift = {-0.15625, -1.5}, 106 | blend_mode = fire_blend_mode, 107 | animation_speed = fire_animation_speed, 108 | scale = fire_scale, 109 | tint = fire_tint, 110 | flags = fire_flags 111 | }, 112 | { 113 | filename = "__base__/graphics/entity/fire-flame/tree-fire-flame-03-a.png", 114 | line_length = 8, 115 | width = 38, 116 | height = 110, 117 | frame_count = 23, 118 | axially_symmetrical = false, 119 | direction_count = 1, 120 | shift = {-0.03125, -1.5}, 121 | blend_mode = fire_blend_mode, 122 | animation_speed = fire_animation_speed, 123 | scale = fire_scale, 124 | tint = fire_tint, 125 | flags = fire_flags 126 | }, 127 | { 128 | filename = "__base__/graphics/entity/fire-flame/tree-fire-flame-03-b.png", 129 | line_length = 8, 130 | width = 34, 131 | height = 98, 132 | frame_count = 23, 133 | axially_symmetrical = false, 134 | direction_count = 1, 135 | shift = {-0.03125, -1.34375}, 136 | blend_mode = fire_blend_mode, 137 | animation_speed = fire_animation_speed, 138 | scale = fire_scale, 139 | tint = fire_tint, 140 | flags = fire_flags 141 | }, 142 | { 143 | filename = "__base__/graphics/entity/fire-flame/tree-fire-flame-03-c.png", 144 | line_length = 8, 145 | width = 39, 146 | height = 111, 147 | frame_count = 23, 148 | axially_symmetrical = false, 149 | direction_count = 1, 150 | shift = {-0.078125, -1.51562}, 151 | blend_mode = fire_blend_mode, 152 | animation_speed = fire_animation_speed, 153 | scale = fire_scale, 154 | tint = fire_tint, 155 | flags = fire_flags 156 | } 157 | } 158 | 159 | return foreach(retval, function(tab) 160 | if tab.shift and tab.scale then tab.shift = { tab.shift[1] * tab.scale, tab.shift[2] * tab.scale } end 161 | end) 162 | end 163 | 164 | function imageUtils.create_fire_pictures(opts) 165 | local fire_blend_mode = opts.blend_mode or "additive" 166 | local fire_animation_speed = opts.animation_speed or 0.5 167 | local fire_scale = opts.scale or 1 168 | local fire_tint = opts.tint2 169 | local fire_flags = { "compressed" } 170 | local retval = { 171 | { 172 | filename = "__base__/graphics/entity/fire-flame/fire-flame-13.png", 173 | line_length = 8, 174 | width = 60, 175 | height = 118, 176 | frame_count = 25, 177 | axially_symmetrical = false, 178 | direction_count = 1, 179 | blend_mode = fire_blend_mode, 180 | animation_speed = fire_animation_speed, 181 | scale = fire_scale, 182 | tint = fire_tint, 183 | flags = fire_flags, 184 | shift = { -0.0390625, -0.90625 } 185 | }, 186 | { 187 | filename = "__base__/graphics/entity/fire-flame/fire-flame-12.png", 188 | line_length = 8, 189 | width = 63, 190 | height = 116, 191 | frame_count = 25, 192 | axially_symmetrical = false, 193 | direction_count = 1, 194 | blend_mode = fire_blend_mode, 195 | animation_speed = fire_animation_speed, 196 | scale = fire_scale, 197 | tint = fire_tint, 198 | flags = fire_flags, 199 | shift = { -0.015625, -0.914065 } 200 | }, 201 | { 202 | filename = "__base__/graphics/entity/fire-flame/fire-flame-11.png", 203 | line_length = 8, 204 | width = 61, 205 | height = 122, 206 | frame_count = 25, 207 | axially_symmetrical = false, 208 | direction_count = 1, 209 | blend_mode = fire_blend_mode, 210 | animation_speed = fire_animation_speed, 211 | scale = fire_scale, 212 | tint = fire_tint, 213 | flags = fire_flags, 214 | shift = { -0.0078125, -0.90625 } 215 | }, 216 | { 217 | filename = "__base__/graphics/entity/fire-flame/fire-flame-10.png", 218 | line_length = 8, 219 | width = 65, 220 | height = 108, 221 | frame_count = 25, 222 | axially_symmetrical = false, 223 | direction_count = 1, 224 | blend_mode = fire_blend_mode, 225 | animation_speed = fire_animation_speed, 226 | scale = fire_scale, 227 | tint = fire_tint, 228 | flags = fire_flags, 229 | shift = { -0.0625, -0.64844 } 230 | }, 231 | { 232 | filename = "__base__/graphics/entity/fire-flame/fire-flame-09.png", 233 | line_length = 8, 234 | width = 64, 235 | height = 101, 236 | frame_count = 25, 237 | axially_symmetrical = false, 238 | direction_count = 1, 239 | blend_mode = fire_blend_mode, 240 | animation_speed = fire_animation_speed, 241 | scale = fire_scale, 242 | tint = fire_tint, 243 | flags = fire_flags, 244 | shift = { -0.03125, -0.695315 } 245 | }, 246 | { 247 | filename = "__base__/graphics/entity/fire-flame/fire-flame-08.png", 248 | line_length = 8, 249 | width = 50, 250 | height = 98, 251 | frame_count = 32, 252 | axially_symmetrical = false, 253 | direction_count = 1, 254 | blend_mode = fire_blend_mode, 255 | animation_speed = fire_animation_speed, 256 | scale = fire_scale, 257 | tint = fire_tint, 258 | flags = fire_flags, 259 | shift = { -0.0546875, -0.77344 } 260 | }, 261 | { 262 | filename = "__base__/graphics/entity/fire-flame/fire-flame-07.png", 263 | line_length = 8, 264 | width = 54, 265 | height = 84, 266 | frame_count = 32, 267 | axially_symmetrical = false, 268 | direction_count = 1, 269 | blend_mode = fire_blend_mode, 270 | animation_speed = fire_animation_speed, 271 | scale = fire_scale, 272 | tint = fire_tint, 273 | flags = fire_flags, 274 | shift = { 0.015625, -0.640625 } 275 | }, 276 | { 277 | filename = "__base__/graphics/entity/fire-flame/fire-flame-06.png", 278 | line_length = 8, 279 | width = 65, 280 | height = 92, 281 | frame_count = 32, 282 | axially_symmetrical = false, 283 | direction_count = 1, 284 | blend_mode = fire_blend_mode, 285 | animation_speed = fire_animation_speed, 286 | scale = fire_scale, 287 | tint = fire_tint, 288 | flags = fire_flags, 289 | shift = { 0, -0.83594 } 290 | }, 291 | { 292 | filename = "__base__/graphics/entity/fire-flame/fire-flame-05.png", 293 | line_length = 8, 294 | width = 59, 295 | height = 103, 296 | frame_count = 32, 297 | axially_symmetrical = false, 298 | direction_count = 1, 299 | blend_mode = fire_blend_mode, 300 | animation_speed = fire_animation_speed, 301 | scale = fire_scale, 302 | tint = fire_tint, 303 | flags = fire_flags, 304 | shift = { 0.03125, -0.882815 } 305 | }, 306 | { 307 | filename = "__base__/graphics/entity/fire-flame/fire-flame-04.png", 308 | line_length = 8, 309 | width = 67, 310 | height = 130, 311 | frame_count = 32, 312 | axially_symmetrical = false, 313 | direction_count = 1, 314 | blend_mode = fire_blend_mode, 315 | animation_speed = fire_animation_speed, 316 | scale = fire_scale, 317 | tint = fire_tint, 318 | flags = fire_flags, 319 | shift = { 0.015625, -1.109375 } 320 | }, 321 | { 322 | filename = "__base__/graphics/entity/fire-flame/fire-flame-03.png", 323 | line_length = 8, 324 | width = 74, 325 | height = 117, 326 | frame_count = 32, 327 | axially_symmetrical = false, 328 | direction_count = 1, 329 | blend_mode = fire_blend_mode, 330 | animation_speed = fire_animation_speed, 331 | scale = fire_scale, 332 | tint = fire_tint, 333 | flags = fire_flags, 334 | shift = { 0.046875, -0.984375 } 335 | }, 336 | { 337 | filename = "__base__/graphics/entity/fire-flame/fire-flame-02.png", 338 | line_length = 8, 339 | width = 74, 340 | height = 114, 341 | frame_count = 32, 342 | axially_symmetrical = false, 343 | direction_count = 1, 344 | blend_mode = fire_blend_mode, 345 | animation_speed = fire_animation_speed, 346 | scale = fire_scale, 347 | tint = fire_tint, 348 | flags = fire_flags, 349 | shift = { 0.0078125, -0.96875 } 350 | }, 351 | { 352 | filename = "__base__/graphics/entity/fire-flame/fire-flame-01.png", 353 | line_length = 8, 354 | width = 66, 355 | height = 119, 356 | frame_count = 32, 357 | axially_symmetrical = false, 358 | direction_count = 1, 359 | blend_mode = fire_blend_mode, 360 | animation_speed = fire_animation_speed, 361 | scale = fire_scale, 362 | tint = fire_tint, 363 | flags = fire_flags, 364 | shift = { -0.0703125, -1.039065 } 365 | }, 366 | } 367 | return foreach(retval, function(tab) 368 | if tab.shift and tab.scale then tab.shift = { tab.shift[1] * tab.scale, tab.shift[2] * tab.scale } end 369 | end) 370 | end 371 | 372 | return imageUtils 373 | -------------------------------------------------------------------------------- /prototypes/utils/DroneUtils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2022 veden 2 | 3 | -- This program is free software: you can redistribute it and/or modify 4 | -- it under the terms of the GNU General Public License as published by 5 | -- the Free Software Foundation, either version 3 of the License, or 6 | -- (at your option) any later version. 7 | 8 | -- This program is distributed in the hope that it will be useful, 9 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | -- GNU General Public License for more details. 12 | 13 | -- You should have received a copy of the GNU General Public License 14 | -- along with this program. If not, see . 15 | 16 | local biterUtils = require("BiterUtils") 17 | local util = require ("util") 18 | 19 | local droneUtils = {} 20 | 21 | local DISALLOW_FRIENDLY_FIRE = settings.startup["rampant--disallowFriendlyFire"].value 22 | 23 | function droneUtils.makeDrone(attributes) 24 | local n = attributes.name .. "-drone-rampant" 25 | local resistances = {} 26 | for k,v in pairs(attributes.resistances) do 27 | v.type = k 28 | resistances[#resistances+1] = v 29 | end 30 | -- attributes.name = name 31 | 32 | local drone = { 33 | type = "combat-robot", 34 | name = n, 35 | localised_name = biterUtils.getLocalisedName(attributes), 36 | icon = "__base__/graphics/icons/defender.png", 37 | icon_size = 32, 38 | flags = attributes.flags or {"placeable-off-grid", "not-on-map", "not-repairable", "breaths-air", "hidden"}, 39 | subgroup="capsule", 40 | order="e-a-a", 41 | max_health = attributes.health or 60, 42 | healing_per_tick = attributes.healing, 43 | alert_when_damaged = false, 44 | collision_box = {{0, 0}, {0, 0}}, 45 | collision_mask = {RampantGlobalVariables.projectileCollisionLayer}, 46 | selection_box = {{-0.5, -1.5}, {0.5, -0.5}}, 47 | distance_per_frame = attributes.distancePerFrame or 0, 48 | time_to_live = attributes.ttl or (60 * 45), 49 | follows_player = attributes.followsPlayer, 50 | friction = attributes.friction or 0.01, 51 | range_from_player = attributes.rangeFromPlayer or 6.0, 52 | speed = attributes.movement or 0, 53 | destroy_action = attributes.death, 54 | attack_parameters = attributes.attack, 55 | idle = 56 | { 57 | layers = 58 | { 59 | { 60 | filename = "__base__/graphics/entity/defender-robot/defender-robot.png", 61 | priority = "high", 62 | line_length = 16, 63 | width = 32, 64 | tint = attributes.tint2, 65 | height = 33, 66 | frame_count = 1, 67 | direction_count = 16, 68 | shift = {0, 0.015625}, 69 | scale = attributes.scale, 70 | hr_version = { 71 | filename = "__base__/graphics/entity/defender-robot/hr-defender-robot.png", 72 | priority = "high", 73 | line_length = 16, 74 | width = 56, 75 | height = 59, 76 | tint = attributes.tint2, 77 | frame_count = 1, 78 | direction_count = 16, 79 | shift = util.by_pixel(0, 0.25), 80 | scale = attributes.scale * 0.5 81 | } 82 | }, 83 | { 84 | filename = "__base__/graphics/entity/defender-robot/defender-robot-mask.png", 85 | priority = "high", 86 | line_length = 16, 87 | width = 18, 88 | height = 16, 89 | tint = attributes.tint2, 90 | frame_count = 1, 91 | direction_count = 16, 92 | shift = {0, -0.125}, 93 | -- apply_runtime_tint = true, 94 | scale = attributes.scale, 95 | hr_version = { 96 | filename = "__base__/graphics/entity/defender-robot/hr-defender-robot-mask.png", 97 | priority = "high", 98 | line_length = 16, 99 | width = 28, 100 | height = 21, 101 | tint = attributes.tint2, 102 | frame_count = 1, 103 | direction_count = 16, 104 | shift = util.by_pixel(0, -4.75), 105 | -- apply_runtime_tint = true, 106 | scale = attributes.scale * 0.5 107 | } 108 | }, 109 | } 110 | }, 111 | shadow_idle = 112 | { 113 | filename = "__base__/graphics/entity/defender-robot/defender-robot-shadow.png", 114 | priority = "high", 115 | line_length = 16, 116 | width = 43, 117 | height = 23, 118 | frame_count = 1, 119 | direction_count = 16, 120 | shift = {0.859375, 0.609375}, 121 | scale = attributes.scale, 122 | hr_version = { 123 | filename = "__base__/graphics/entity/defender-robot/hr-defender-robot-shadow.png", 124 | priority = "high", 125 | line_length = 16, 126 | width = 88, 127 | height = 50, 128 | frame_count = 1, 129 | direction_count = 16, 130 | shift = util.by_pixel(25.5, 19), 131 | scale = attributes.scale * 0.5 132 | } 133 | }, 134 | in_motion = 135 | { 136 | layers = 137 | { 138 | { 139 | filename = "__base__/graphics/entity/defender-robot/defender-robot.png", 140 | priority = "high", 141 | line_length = 16, 142 | width = 32, 143 | tint = attributes.tint, 144 | height = 33, 145 | frame_count = 1, 146 | direction_count = 16, 147 | shift = {0, 0.015625}, 148 | y = 33, 149 | scale = attributes.scale, 150 | hr_version = { 151 | filename = "__base__/graphics/entity/defender-robot/hr-defender-robot.png", 152 | priority = "high", 153 | line_length = 16, 154 | width = 56, 155 | tint = attributes.tint, 156 | height = 59, 157 | frame_count = 1, 158 | direction_count = 16, 159 | shift = util.by_pixel(0, 0.25), 160 | y = 59, 161 | scale = attributes.scale * 0.5 162 | } 163 | }, 164 | { 165 | filename = "__base__/graphics/entity/defender-robot/defender-robot-mask.png", 166 | priority = "high", 167 | line_length = 16, 168 | width = 18, 169 | height = 16, 170 | frame_count = 1, 171 | direction_count = 16, 172 | tint = attributes.tint2, 173 | shift = {0, -0.125}, 174 | y = 16, 175 | scale = attributes.scale, 176 | hr_version = { 177 | filename = "__base__/graphics/entity/defender-robot/hr-defender-robot-mask.png", 178 | priority = "high", 179 | line_length = 16, 180 | width = 28, 181 | height = 21, 182 | frame_count = 1, 183 | direction_count = 16, 184 | tint = attributes.tint2, 185 | shift = util.by_pixel(0, -4.75), 186 | y = 21, 187 | scale = attributes.scale * 0.5 188 | } 189 | }, 190 | } 191 | }, 192 | shadow_in_motion = 193 | { 194 | filename = "__base__/graphics/entity/defender-robot/defender-robot-shadow.png", 195 | priority = "high", 196 | line_length = 16, 197 | width = 43, 198 | height = 23, 199 | frame_count = 1, 200 | direction_count = 16, 201 | shift = {0.859375, 0.609375}, 202 | scale = attributes.scale, 203 | hr_version = { 204 | filename = "__base__/graphics/entity/defender-robot/hr-defender-robot-shadow.png", 205 | priority = "high", 206 | line_length = 16, 207 | width = 88, 208 | height = 50, 209 | frame_count = 1, 210 | direction_count = 16, 211 | shift = util.by_pixel(25.5, 19), 212 | scale = attributes.scale * 0.5 213 | } 214 | } 215 | } 216 | if attributes.appendFlags then 217 | for flag in pairs(attributes.appendFlags) do 218 | drone.flags[#drone.flags+1] = flag 219 | end 220 | end 221 | return drone 222 | end 223 | 224 | function droneUtils.createCapsuleProjectile(attributes, entityName) 225 | local n = attributes.name .. "-capsule-rampant" 226 | 227 | local actions = { 228 | { 229 | type = "direct", 230 | force = (DISALLOW_FRIENDLY_FIRE and "not-same") or nil, 231 | action_delivery = 232 | { 233 | type = "instant", 234 | source_effects = attributes.sourceEffect and attributes.sourceEffect(attributes), 235 | target_effects = 236 | { 237 | { 238 | type = "create-entity", 239 | show_in_tooltip = true, 240 | trigger_created_entity = attributes.triggerCreated, 241 | entity_name = entityName, 242 | check_buildability = attributes.checkBuildability 243 | }, 244 | { 245 | type = "damage", 246 | damage = {amount = attributes.damage or 5, type = attributes.damageType or "explosion"} 247 | } 248 | } 249 | } 250 | } 251 | } 252 | 253 | -- if attributes.sourceEffect then 254 | -- actions[#actions+1] = attributes.sourceEffect(attributes) 255 | -- end 256 | 257 | local cap = { 258 | type = "projectile", 259 | name = n, 260 | flags = {"not-on-map"}, 261 | collision_box = attributes.collisionBox or {{-0.01, -0.01}, {0.01, 0.01}}, 262 | collision_mask = attributes.collisionMask, 263 | direction_only = attributes.attackDirectionOnly, 264 | piercing_damage = attributes.piercingDamage or 0, 265 | force_condition = (settings.startup["rampant--disableCollidingProjectiles"].value and "not-same") or nil, 266 | acceleration = attributes.acceleration or 0.01, 267 | action = actions, 268 | light = {intensity = 0.5, size = 4}, 269 | enable_drawing_with_mask = true, 270 | animation = { 271 | layers = { 272 | { 273 | filename = "__base__/graphics/entity/combat-robot-capsule/defender-capsule.png", 274 | flags = { "no-crop" }, 275 | frame_count = 1, 276 | width = 28, 277 | height = 20, 278 | tint = attributes.tint2, 279 | scale = attributes.scale, 280 | priority = "high" 281 | }, 282 | { 283 | filename = "__base__/graphics/entity/combat-robot-capsule/defender-capsule-mask.png", 284 | flags = { "no-crop" }, 285 | frame_count = 1, 286 | width = 28, 287 | height = 20, 288 | tint = attributes.tint2, 289 | scale = attributes.scale, 290 | priority = "high", 291 | }, 292 | }, 293 | }, 294 | shadow = 295 | { 296 | filename = "__base__/graphics/entity/combat-robot-capsule/defender-capsule-shadow.png", 297 | flags = { "no-crop" }, 298 | frame_count = 1, 299 | width = 26, 300 | height = 20, 301 | scale = attributes.scale, 302 | priority = "high" 303 | }, 304 | smoke = { 305 | { 306 | name = "the-soft-smoke-rampant", 307 | deviation = {0.15, 0.15}, 308 | frequency = 1, 309 | position = {0, 0}, 310 | starting_frame = 3, 311 | starting_frame_deviation = 5, 312 | starting_frame_speed_deviation = 5 313 | } 314 | } 315 | } 316 | 317 | data:extend({cap}) 318 | return n 319 | end 320 | 321 | return droneUtils 322 | --------------------------------------------------------------------------------