diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f698b4c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "oarc_utils"] + path = locale + url = https://github.com/Oarcinae/FactorioUtils diff --git a/bps.lua b/bps.lua deleted file mode 100644 index aee1534..0000000 --- a/bps.lua +++ /dev/null @@ -1,665 +0,0 @@ --- bps.lua --- Nov 2016 - --- Modified by Oarc . Taken from 3Ra with permission. - --- Blueprint String --- A 3Ra Gaming revision --- Original Author: DaveMcW - -local BlueprintString = require "locale/blueprintstring/blueprintstring" -BlueprintString.COMPRESS_STRINGS = true -BlueprintString.LINE_LENGTH = 120 - --- Initialise player GUI --- @param player -local function init_gui(player) - if (not player.force.technologies["automated-construction"].researched) then - return - end - - if (not player.gui.top["blueprint-string-button"]) then - player.gui.top.add { type = "button", name = "blueprint-string-button", caption = "BPS" } - end -end - --- Initialise map -function bps_init() - for _, player in pairs(game.players) do - init_gui(player) - end -end - --- Initialise player --- @param event on_player_joined event -function bps_player_joined(event) - init_gui(game.players[event.player_index]) -end - --- Handle research completion --- @param event on_research_finished event -function bps_on_research_finished(event) - if (event.research.name == "automated-construction") then - for _, player in pairs(game.players) do - if (event.research.force.name == player.force.name) then - init_gui(player) - end - end - end -end - --- Expand player's gui --- @param player target player -local function expand_gui(player) - local frame = player.gui.left["blueprint-string"] - if (frame) then - frame.destroy() - else - frame = player.gui.left.add { type = "frame", name = "blueprint-string" } - frame.add { type = "label", caption = { "textbox-caption" } } - frame.add { type = "textfield", name = "blueprint-string-text" } - frame.add { type = "button", name = "blueprint-string-load", caption = "Load" } - frame.add { type = "button", name = "blueprint-string-save", caption = "Save" } - frame.add { type = "button", name = "blueprint-string-save-all", caption = "Save All" } - frame.add { type = "button", name = "blueprint-string-upgrade", caption = "Upgrade" } - end -end - --- Trim string of whitespace --- @param s string --- @return trimmed string -local function trim(s) - return (s:gsub("^%s*(.-)%s*$", "%1")) -end - --- Get the amount of a certain item type in an inventory --- @param inventory inventory to filter --- @param type type of item to filter --- @return array of items -local function filter(inventory, type) - local stacks = {} - if (inventory) then - for i = 1, #inventory do - if (inventory[i].valid_for_read and inventory[i].type == type) then - stacks[i] = inventory[i] - end - end - end - return stacks -end - --- Return the blueprints inside a book --- @param book blueprint book item --- @return array of blueprints -local function book_inventory(book) - local blueprints = {} - local active = book.get_inventory(defines.inventory.item_active) - local main = book.get_inventory(defines.inventory.item_main) - - if (active[1].valid_for_read and active[1].type == "blueprint") then - blueprints[1] = active[1] - end - - for i = 1, #main do - if (main[i].valid_for_read and main[i].type == "blueprint") then - blueprints[i + 1] = main[i] - end - end - - return blueprints -end - --- Check if the player is holding a blueprint --- @param player target player --- @return bool player is holding blueprint -local function holding_blueprint(player) - return (player.cursor_stack.valid_for_read and player.cursor_stack.type == "blueprint") -end - --- Check if the blueprint being held is empty --- @param player target player --- @return bool blueprint is not empty -local function holding_valid_blueprint(player) - return (holding_blueprint(player) and player.cursor_stack.is_blueprint_setup()) -end - --- Check if the player is holding a blueprint book --- @param player target player --- @return bool player is holding book -local function holding_book(player) - return (player.cursor_stack.valid_for_read and player.cursor_stack.type == "blueprint-book") -end - --- Find a player's empty blueprint or craft one if unavailable --- @param player target player --- @param no_crafting if true then a blueprint will not be crafted, even if one is unavailable --- @return empty blueprint -local function find_empty_blueprint(player, no_crafting) - if (holding_blueprint(player)) then - if (player.cursor_stack.is_blueprint_setup()) then - player.cursor_stack.set_blueprint_entities(nil) - player.cursor_stack.set_blueprint_tiles(nil) - player.cursor_stack.label = "" - end - return player.cursor_stack - end - - local main = player.get_inventory(defines.inventory.player_main) - local quickbar = player.get_inventory(defines.inventory.player_quickbar) - - local stacks = filter(quickbar, "blueprint") - for i, stack in pairs(filter(main, "blueprint")) do - stacks[#quickbar + i] = stack - end - for _, stack in pairs(stacks) do - if (not stack.is_blueprint_setup()) then - return stack - end - end - - if (no_crafting) then - return nil - end - - -- Craft a new blueprint - if (player.can_insert("blueprint") and player.get_item_count("advanced-circuit") >= 1) then - player.remove_item { name = "advanced-circuit", count = 1 } - if (player.insert("blueprint") == 1) then - return find_empty_blueprint(player, true) - end - end - - return nil -end - --- Find an empty book, or craft one if unavailable --- @param player target player --- @param slots number of slots needed --- @param no_crafting bool to allow crafting or not --- @return empty blueprint book -local function find_empty_book(player, slots, no_crafting) - if (holding_book(player)) then - for _, page in pairs(book_inventory(player.cursor_stack)) do - if (page.is_blueprint_setup()) then - page.set_blueprint_entities(nil) - page.set_blueprint_tiles(nil) - page.label = "" - end - end - return player.cursor_stack - end - - local advanced_circuits = player.get_item_count("advanced-circuit") - local main = player.get_inventory(defines.inventory.player_main) - local quickbar = player.get_inventory(defines.inventory.player_quickbar) - local first_empty_book = nil - local books = filter(quickbar, "blueprint-book") - for i, book in pairs(filter(main, "blueprint-book")) do - books[#quickbar + i] = book - end - for _, book in pairs(books) do - local empty = true - local pages = 0 - for _, page in pairs(book_inventory(book)) do - if (page.is_blueprint_setup()) then - empty = false - end - pages = pages + 1 - end - if (empty) then - if (slots <= pages + advanced_circuits) then - return book - end - if (not first_empty_book) then - first_empty_book = book - end - end - end - - if (first_empty_book) then - -- We can't afford to craft all the blueprints, but at least we have an empty book - return first_empty_book - end - - if (no_crafting) then - return nil - end - - -- Craft a new book - if (player.can_insert("blueprint-book") and advanced_circuits >= 15 + slots) then - player.remove_item { name = "advanced-circuit", count = 15 } - if (player.insert("blueprint-book") == 1) then - return find_empty_book(player, slots, true) - end - end - - return nil -end - --- Convert string into blueprint --- @param blueprint empty blueprint that the data will be loaded into --- @param data blueprint data --- @return error if one occurred -local function load_blueprint_data(blueprint, data) - if (not data.icons or type(data.icons) ~= "table" or #data.icons < 1) then - return { "unknown-format" } - end - - status, result = pcall(blueprint.set_blueprint_entities, data.entities) - if (not status) then - blueprint.set_blueprint_entities(nil) - return { "blueprint-api-error", result } - end - - status, result = pcall(blueprint.set_blueprint_tiles, data.tiles) - if (not status) then - blueprint.set_blueprint_entities(nil) - blueprint.set_blueprint_tiles(nil) - return { "blueprint-api-error", result } - end - - if (blueprint.is_blueprint_setup()) then - status, result = pcall(function() blueprint.blueprint_icons = data.icons end) - if (not status) then - blueprint.set_blueprint_entities(nil) - blueprint.set_blueprint_tiles(nil) - return { "blueprint-icon-error", result } - end - end - - blueprint.label = data.name or "" - - return nil -end - --- Call the required local functions to load a blueprint --- @param player player that is loading the blueprint -local function load_blueprint(player) - local textbox = player.gui.left["blueprint-string"]["blueprint-string-text"] - local data = trim(textbox.text) - if (data == "") then - player.print({ "no-string" }) - return - end - - local blueprint_format = BlueprintString.fromString(data) - if (not blueprint_format or type(blueprint_format) ~= "table") then - textbox.text = "" - player.print({ "unknown-format" }) - return - end - - local blueprint = nil - local book = nil - local active = nil - local main = nil - if (not blueprint_format.book) then - -- Blueprint - if (holding_book(player)) then - player.print({ "need-blueprint" }) - return - end - - blueprint = find_empty_blueprint(player) - if (not blueprint) then - player.print({ "no-empty-blueprint" }) - return - end - else - -- Blueprint Book - if (type(blueprint_format.book) ~= "table") then - textbox.text = "" - player.print({ "unknown-format" }) - return - end - - if (holding_blueprint(player)) then - player.print({ "need-blueprint-book" }) - return - end - - local page_count = 0 - for _, page in pairs(blueprint_format.book) do - page_count = page_count + 1 - end - if (page_count < 1) then - textbox.text = "" - player.print({ "unknown-format" }) - return - end - - local slots = math.min(page_count, game.item_prototypes["blueprint-book"].inventory_size + 1) - book = find_empty_book(player, slots) - if (not book) then - player.print({ "no-empty-blueprint" }) - return - end - - active = book.get_inventory(defines.inventory.item_active) - main = book.get_inventory(defines.inventory.item_main) - - local advanced_circuits = slots - active.get_item_count("blueprint") - main.get_item_count("blueprint") - if (advanced_circuits > player.get_item_count("advanced-circuit")) then - player.print({ "need-advanced-circuit", advanced_circuits }) - return - end - - if (advanced_circuits > 0) then - player.remove_item { name = "advanced-circuit", count = advanced_circuits } - end - - -- Create the required blueprints - if (blueprint_format.book[1]) then - active[1].set_stack("blueprint") - else - active[1].clear() - end - for i = 1, #main do - if (blueprint_format.book[i + 1]) then - main[i].set_stack("blueprint") - else - main[i].clear() - end - end - - -- If we have extra blueprints, put them back in - local extra_blueprints = -advanced_circuits - if (extra_blueprints > 0 and not active[1].valid_for_read) then - active[1].set_stack("blueprint") - extra_blueprints = extra_blueprints - 1 - end - for i = 1, #main do - if (extra_blueprints > 0 and not main[i].valid_for_read) then - main[i].set_stack("blueprint") - extra_blueprints = extra_blueprints - 1 - end - end - end - - textbox.text = "" - - -- Blueprint - if (not book) then - local error = load_blueprint_data(blueprint, blueprint_format) - if (error) then - player.print(error) - end - return - end - - -- Blueprint Book - if (blueprint_format.book[1]) then - local error = load_blueprint_data(active[1], blueprint_format.book[1]) - if (error and error[1] ~= "unknown-format") then - player.print(error) - end - end - for i = 1, #main do - if (blueprint_format.book[i + 1]) then - local error = load_blueprint_data(main[i], blueprint_format.book[i + 1]) - if (error and error[1] ~= "unknown-format") then - player.print(error) - end - end - end - book.label = blueprint_format.name or "" -end - -local duplicate_filenames --- Fix incorrect file name -local function fix_filename(player, filename) - if (#game.players > 1 and player.name and player.name ~= "") then - local name = player.name - filename = name .. "-" .. filename - end - - filename = filename:gsub("[/\\:*?\"<>|]", "_") - - local lowercase = filename:lower() - if (duplicate_filenames[lowercase]) then - duplicate_filenames[lowercase] = duplicate_filenames[lowercase] + 1 - filename = filename .. "-" .. duplicate_filenames[lowercase] - else - duplicate_filenames[lowercase] = 1 - end - - return filename -end - -local blueprints_saved --- Save blueprint as file -local function blueprint_to_file(player, stack, filename) - local blueprint_format = { - entities = stack.get_blueprint_entities(), - tiles = stack.get_blueprint_tiles(), - icons = stack.blueprint_icons, - name = stack.label, - } - - local data = BlueprintString.toString(blueprint_format) - filename = fix_filename(player, filename) - game.write_file("blueprint-string/" .. filename .. ".txt", data, false, player.index) - blueprints_saved = blueprints_saved + 1 -end - --- Save blueprint book as file -local function book_to_file(player, book, filename) - local blueprint_format = { book = {} } - - for position, stack in pairs(book_inventory(book)) do - if (stack.is_blueprint_setup()) then - blueprint_format.book[position] = { - entities = stack.get_blueprint_entities(), - tiles = stack.get_blueprint_tiles(), - icons = stack.blueprint_icons, - name = stack.label, - } - end - end - if (book.label) then - blueprint_format.name = book.label - end - - local data = BlueprintString.toString(blueprint_format) - filename = fix_filename(player, filename) - game.write_file("blueprint-string/" .. filename .. ".txt", data, false, player.index) - blueprints_saved = blueprints_saved + 1 -end - -local function save_blueprint_as(player, filename) - blueprints_saved = 0 - duplicate_filenames = {} - - if (not holding_valid_blueprint(player) and not holding_book(player)) then - player.print({ "no-blueprint-in-hand" }) - return - end - - if (not filename or filename == "") then - player.print({ "no-filename" }) - return - end - - filename = filename:sub(1, 100) - - if (player.cursor_stack.type == "blueprint") then - blueprint_to_file(player, player.cursor_stack, filename) - elseif (player.cursor_stack.type == "blueprint-book") then - book_to_file(player, player.cursor_stack, filename) - end - - local prompt = player.gui.center["blueprint-string-filename-prompt"] - if (prompt) then prompt.destroy() end - - if (blueprints_saved > 0) then - player.print({ "blueprint-saved-as", filename }) - else - player.print({ "blueprints-not-saved" }) - end -end - -local function save_blueprint(player) - if (not holding_valid_blueprint(player) and not holding_book(player)) then - player.print({ "no-blueprint-in-hand" }) - return - end - - if (player.cursor_stack.label) then - save_blueprint_as(player, player.cursor_stack.label) - else - bps_prompt_for_filename(player) - end -end - -local function save_all(player) - blueprints_saved = 0 - duplicate_filenames = {} - - local main = player.get_inventory(defines.inventory.player_main) - local quickbar = player.get_inventory(defines.inventory.player_quickbar) - - for position, stack in pairs(filter(quickbar, "blueprint")) do - if (stack.is_blueprint_setup()) then - local filename = "toolbar-" .. position - if (stack.label) then - filename = stack.label - end - blueprint_to_file(player, stack, filename) - end - end - - for position, stack in pairs(filter(main, "blueprint")) do - if (stack.is_blueprint_setup()) then - local filename = "inventory-" .. position - if (stack.label) then - filename = stack.label - end - blueprint_to_file(player, stack, filename) - end - end - - for position, stack in pairs(filter(quickbar, "blueprint-book")) do - local filename = "toolbar-" .. position - if (stack.label) then - filename = stack.label - end - book_to_file(player, stack, filename) - end - - for position, stack in pairs(filter(main, "blueprint-book")) do - local filename = "inventory-" .. position - if (stack.label) then - filename = stack.label - end - book_to_file(player, stack, filename) - end - - if (blueprints_saved > 0) then - player.print({ "blueprints-saved", blueprints_saved }) - else - player.print({ "blueprints-not-saved" }) - end -end - -function bps_prompt_for_filename(player) - local frame = player.gui.center["blueprint-string-filename-prompt"] - if (frame) then - frame.destroy() - end - - frame = player.gui.center.add { type = "frame", direction = "vertical", name = "blueprint-string-filename-prompt" } - local line1 = frame.add { type = "flow", direction = "horizontal" } - line1.add { type = "label", caption = { "save-as" } } - frame.add { type = "textfield", name = "blueprint-string-filename" } - local line2 = frame.add { type = "flow", direction = "horizontal" } - line2.add { type = "button", name = "blueprint-string-filename-save", caption = { "save" }, font_color = white } - line2.add { type = "button", name = "blueprint-string-filename-cancel", caption = { "cancel" }, font_color = white } -end - --- Check a blueprint for a certain entity --- @param blueprint blueprint to check --- @param entities array of entities to check --- @return bool blueprint contains entity -local function contains_entities(blueprint, entities) - if not blueprint.entities then - return false - end - - for _, e in pairs(blueprint.entities) do - if entities[e.name] then - return true - end - end - - return false -end - -local function upgrade_blueprint(player) - if (not holding_valid_blueprint(player)) then - player.print({ "no-blueprint-in-hand" }) - return - end - local entities = player.cursor_stack.get_blueprint_entities() - local tiles = player.cursor_stack.get_blueprint_tiles() - - local offset = { x = -0.5, y = -0.5 } - local rail_entities = {} - rail_entities["straight-rail"] = true - rail_entities["curved-rail"] = true - rail_entities["rail-signal"] = true - rail_entities["rail-chain-signal"] = true - rail_entities["train-stop"] = true - rail_entities["smart-train-stop"] = true - if contains_entities(entities, rail_entities) then - offset = { x = -1, y = -1 } - end - - if (entities) then - for _, entity in pairs(entities) do - entity.position = { x = entity.position.x + offset.x, y = entity.position.y + offset.y } - end - player.cursor_stack.set_blueprint_entities(entities) - end - if (tiles) then - for _, entity in pairs(tiles) do - tile.position = { x = tile.position.x + offset.x, y = tile.position.y + offset.y } - end - player.cursor_stack.set_blueprint_tiles(tiles) - end -end - --- Handle GUI click -function bps_on_gui_click(event) - if not (event and event.element and event.element.valid) then return end - local player = game.players[event.element.player_index] - local name = event.element.name - if (name == "blueprint-string-load") then - load_blueprint(player) - elseif (name == "blueprint-string-save-all") then - save_all(player) - elseif (name == "blueprint-string-save") then - save_blueprint(player) - elseif (name == "blueprint-string-filename-save") then - save_blueprint_as(player, player.gui.center["blueprint-string-filename-prompt"]["blueprint-string-filename"].text) - elseif (name == "blueprint-string-filename-cancel") then - player.gui.center["blueprint-string-filename-prompt"].destroy() - elseif (name == "blueprint-string-button") then - expand_gui(player) - elseif (name == "blueprint-string-upgrade") then - upgrade_blueprint(player) - end -end - -function bps_on_robot_built_entity(event) - local entity = event.created_entity - if (entity and entity.type == "assembling-machine" and entity.recipe and not entity.recipe.enabled) then - entity.recipe = nil - end -end - - --- Event.register(-1, bps_init) --- Event.register(defines.events.on_player_created, player_joined) --- Event.register(defines.events.on_research_finished, on_research_finished) --- Event.register(defines.events.on_gui_click, on_gui_click) --- Event.register(defines.events.on_robot_built_entity, on_robot_built_entity) \ No newline at end of file diff --git a/control.lua b/control.lua index 89e6fc2..0827d9b 100755 --- a/control.lua +++ b/control.lua @@ -24,17 +24,19 @@ -- 3. Put mods into their own files where possible (RSO has multiple) --- My Scenario Includes -require("oarc_utils") +-- Generic Utility Includes +require("locale/oarc_utils") +require("locale/rso/rso_control") +require("locale/frontier_silo") +require("locale/tag") +require("locale/bps") + +-- Main Configuration File require("config") --- Include Mods +-- Scenario Specific Includes require("separate_spawns") require("separate_spawns_guis") -require("rso_control") -require("frontier_silo") -require("tag") -require("bps") -------------------------------------------------------------------------------- @@ -177,10 +179,6 @@ script.on_event(defines.events.on_player_joined_game, function(event) end) script.on_event(defines.events.on_player_created, function(event) - if ENABLE_RSO then - RSO_PlayerCreated(event) - end - if ENABLE_LONGREACH then GivePlayerLongReach(game.players[event.player_index]) end diff --git a/frontier_silo.lua b/frontier_silo.lua deleted file mode 100755 index 78dd0f1..0000000 --- a/frontier_silo.lua +++ /dev/null @@ -1,79 +0,0 @@ --- frontier_silo.lua --- Nov 2016 - -require("config") -require("oarc_utils") - --- Create a rocket silo -local function CreateRocketSilo(surface, chunkArea) - if CheckIfInArea(global.siloPosition, chunkArea) then - - -- Delete any entities beneat the silo? - for _, entity in pairs(surface.find_entities_filtered{area = {{global.siloPosition.x-5, global.siloPosition.y-6},{global.siloPosition.x+6, global.siloPosition.y+6}}}) do - entity.destroy() - end - - -- Set tiles below the silo - local tiles = {} - local i = 1 - for dx = -6,6 do - for dy = -7,6 do - tiles[i] = {name = "grass", position = {global.siloPosition.x+dx, global.siloPosition.y+dy}} - i=i+1 - end - end - surface.set_tiles(tiles, false) - tiles = {} - i = 1 - for dx = -5,5 do - for dy = -6,5 do - tiles[i] = {name = "concrete", position = {global.siloPosition.x+dx, global.siloPosition.y+dy}} - i=i+1 - end - end - surface.set_tiles(tiles, true) - - -- Create silo and assign to main force - local silo = surface.create_entity{name = "rocket-silo", position = {global.siloPosition.x+0.5, global.siloPosition.y}, force = MAIN_FORCE} - silo.destructible = false - silo.minable = false - end -end - --- Remove rocket silo from recipes -function RemoveRocketSiloRecipe(event) - RemoveRecipe(event, "rocket-silo") -end - --- Generates the rocket silo during chunk generation event --- Includes a crop circle -function GenerateRocketSiloChunk(event) - local surface = event.surface - if surface.name ~= "nauvis" then return end - local chunkArea = event.area - - local chunkAreaCenter = {x=chunkArea.left_top.x+(CHUNK_SIZE/2), - y=chunkArea.left_top.y+(CHUNK_SIZE/2)} - local safeArea = {left_top= - {x=global.siloPosition.x-150, - y=global.siloPosition.y-150}, - right_bottom= - {x=global.siloPosition.x+150, - y=global.siloPosition.y+150}} - - - -- Clear enemies directly next to the rocket - if CheckIfInArea(chunkAreaCenter,safeArea) then - for _, entity in pairs(surface.find_entities_filtered{area = chunkArea, force = "enemy"}) do - entity.destroy() - end - end - - -- Create rocket silo - CreateRocketSilo(surface, chunkArea) - CreateCropCircle(surface, global.siloPosition, chunkArea, 40) -end - -function ChartRocketSiloArea(force) - force.chart(game.surfaces["nauvis"], {{global.siloPosition.x-(CHUNK_SIZE*2), global.siloPosition.y-(CHUNK_SIZE*2)}, {global.siloPosition.x+(CHUNK_SIZE*2), global.siloPosition.y+(CHUNK_SIZE*2)}}) -end \ No newline at end of file diff --git a/locale b/locale new file mode 160000 index 0000000..7faacf8 --- /dev/null +++ b/locale @@ -0,0 +1 @@ +Subproject commit 7faacf8eca0d9e08184b5ceefa330d08289f78cb diff --git a/oarc_utils.lua b/oarc_utils.lua deleted file mode 100644 index fce3ae7..0000000 --- a/oarc_utils.lua +++ /dev/null @@ -1,626 +0,0 @@ --- oarc_utils.lua --- Nov 2016 --- --- My general purpose utility functions for factorio --- Also contains some constants and gui styles - - --------------------------------------------------------------------------------- --- Useful constants --------------------------------------------------------------------------------- -CHUNK_SIZE = 32 -MAX_FORCES = 64 -TICKS_PER_SECOND = 60 -TICKS_PER_MINUTE = TICKS_PER_SECOND * 60 --------------------------------------------------------------------------------- - - --------------------------------------------------------------------------------- --- GUI Label Styles --------------------------------------------------------------------------------- -my_fixed_width_style = { - minimal_width = 450, - maximal_width = 450 -} -my_label_style = { - minimal_width = 450, - maximal_width = 450, - maximal_height = 10, - font_color = {r=1,g=1,b=1} -} -my_note_style = { - minimal_width = 450, - maximal_height = 10, - font = "default-small-semibold", - font_color = {r=1,g=0.5,b=0.5} -} -my_warning_style = { - minimal_width = 450, - maximal_width = 450, - maximal_height = 10, - font_color = {r=1,g=0.1,b=0.1} -} -my_spacer_style = { - minimal_width = 450, - maximal_width = 450, - minimal_height = 20, - maximal_height = 20, - font_color = {r=0,g=0,b=0} -} -my_small_button_style = { - font = "default-small-semibold" -} -my_color_red = {r=1,g=0.1,b=0.1} - - --------------------------------------------------------------------------------- --- General Helper Functions --------------------------------------------------------------------------------- - --- Print debug only to me while testing. --- Should remove this if you are hosting it yourself. -function DebugPrint(msg) - if ((game.players["Oarc"] ~= nil) and (global.oarcDebugEnabled)) then - game.players["Oarc"].print("DEBUG: " .. msg) - end -end - --- Prints flying text. --- Color is optional -function FlyingText(msg, pos, color) - local surface = game.surfaces["nauvis"] - if color == nil then - surface.create_entity({ name = "flying-text", position = pos, text = msg }) - else - surface.create_entity({ name = "flying-text", position = pos, text = msg, color = color }) - end -end - --- Broadcast messages -function SendBroadcastMsg(msg) - for name,player in pairs(game.connected_players) do - player.print(msg) - end -end - -function formattime(ticks) - local seconds = ticks / 60 - local minutes = math.floor((seconds)/60) - local seconds = math.floor(seconds - 60*minutes) - return string.format("%dm:%02ds", minutes, seconds) -end - --- Simple function to get total number of items in table -function TableLength(T) - local count = 0 - for _ in pairs(T) do count = count + 1 end - return count -end - --- Chart area for a force -function ChartArea(force, position, chunkDist) - force.chart(game.surfaces["nauvis"], - {{position.x-(CHUNK_SIZE*chunkDist), - position.y-(CHUNK_SIZE*chunkDist)}, - {position.x+(CHUNK_SIZE*chunkDist), - position.y+(CHUNK_SIZE*chunkDist)}}) -end - --- Give player these default items. -function GivePlayerItems(player) - player.insert{name="pistol", count=1} - player.insert{name="firearm-magazine", count=100} -end - --- Additional starter only items -function GivePlayerStarterItems(player) - GivePlayerItems(player) - player.insert{name="iron-plate", count=8} - player.insert{name="burner-mining-drill", count = 1} - player.insert{name="stone-furnace", count = 1} -end - --- Check if given position is in area bounding box -function CheckIfInArea(point, area) - if ((point.x > area.left_top.x) and (point.x < area.right_bottom.x)) then - if ((point.y > area.left_top.y) and (point.y < area.right_bottom.y)) then - return true - end - end - return false -end - --- Ceasefire --- All forces are always neutral -function SetCeaseFireBetweenAllForces() - for name,team in pairs(game.forces) do - if name ~= "neutral" and name ~= "enemy" then - for x,y in pairs(game.forces) do - if x ~= "neutral" and x ~= "enemy" then - team.set_cease_fire(x,true) - end - end - end - end -end - --- Undecorator -function RemoveDecorationsArea(surface, area) - for _, entity in pairs(surface.find_entities_filtered{area = area, type="decorative"}) do - entity.destroy() - end -end - --- Remove fish -function RemoveFish(surface, area) - for _, entity in pairs(surface.find_entities_filtered{area = area, type="fish"}) do - entity.destroy() - end -end - --- Apply a style option to a GUI -function ApplyStyle (guiIn, styleIn) - for k,v in pairs(styleIn) do - guiIn.style[k]=v - end -end - --- Get a random 1 or -1 -function RandomNegPos() - if (math.random(0,1) == 1) then - return 1 - else - return -1 - end -end - --- Create a random direction vector to look in -function GetRandomVector() - local randVec = {x=0,y=0} - while ((randVec.x == 0) and (randVec.y == 0)) do - randVec.x = math.random(-3,3) - randVec.y = math.random(-3,3) - end - DebugPrint("direction: x=" .. randVec.x .. ", y=" .. randVec.y) - return randVec -end - --- Check for ungenerated chunks around a specific chunk --- +/- chunkDist in x and y directions -function IsChunkAreaUngenerated(chunkPos, chunkDist) - for x=-chunkDist, chunkDist do - for y=-chunkDist, chunkDist do - local checkPos = {x=chunkPos.x+x, - y=chunkPos.y+y} - if (game.surfaces["nauvis"].is_chunk_generated(checkPos)) then - return false - end - end - end - return true -end - --- Clear out enemies around an area with a certain distance -function ClearNearbyEnemies(player, safeDist) - local safeArea = {left_top= - {x=player.position.x-safeDist, - y=player.position.y-safeDist}, - right_bottom= - {x=player.position.x+safeDist, - y=player.position.y+safeDist}} - - for _, entity in pairs(player.surface.find_entities_filtered{area = safeArea, force = "enemy"}) do - entity.destroy() - end -end - --- Function to find coordinates of ungenerated map area in a given direction --- starting from the center of the map -function FindMapEdge(directionVec) - local position = {x=0,y=0} - local chunkPos = {x=0,y=0} - - -- Keep checking chunks in the direction of the vector - while(true) do - - -- Set some absolute limits. - if ((math.abs(chunkPos.x) > 1000) or (math.abs(chunkPos.y) > 1000)) then - break - - -- If chunk is already generated, keep looking - elseif (game.surfaces["nauvis"].is_chunk_generated(chunkPos)) then - chunkPos.x = chunkPos.x + directionVec.x - chunkPos.y = chunkPos.y + directionVec.y - - -- Found a possible ungenerated area - else - - chunkPos.x = chunkPos.x + directionVec.x - chunkPos.y = chunkPos.y + directionVec.y - - -- Check there are no generated chunks in a 10x10 area. - if IsChunkAreaUngenerated(chunkPos, 5) then - position.x = (chunkPos.x*CHUNK_SIZE) + (CHUNK_SIZE/2) - position.y = (chunkPos.y*CHUNK_SIZE) + (CHUNK_SIZE/2) - break - end - end - end - - DebugPrint("spawn: x=" .. position.x .. ", y=" .. position.y) - return position -end - --- Find random coordinates within a given distance away --- maxTries is the recursion limit basically. -function FindUngeneratedCoordinates(minDistChunks, maxDistChunks) - local position = {x=0,y=0} - local chunkPos = {x=0,y=0} - - local maxTries = 100 - local tryCounter = 0 - - local minDistSqr = minDistChunks^2 - local maxDistSqr = maxDistChunks^2 - - while(true) do - chunkPos.x = math.random(0,maxDistChunks) * RandomNegPos() - chunkPos.y = math.random(0,maxDistChunks) * RandomNegPos() - - local distSqrd = chunkPos.x^2 + chunkPos.y^2 - - -- Enforce a max number of tries - tryCounter = tryCounter + 1 - if (tryCounter > maxTries) then - DebugPrint("FindUngeneratedCoordinates - Max Tries Hit!") - break - - -- Check that the distance is within the min,max specified - elseif ((distSqrd < minDistSqr) or (distSqrd > maxDistSqr)) then - -- Keep searching! - - -- Check there are no generated chunks in a 10x10 area. - elseif IsChunkAreaUngenerated(chunkPos, 5) then - position.x = (chunkPos.x*CHUNK_SIZE) + (CHUNK_SIZE/2) - position.y = (chunkPos.y*CHUNK_SIZE) + (CHUNK_SIZE/2) - break -- SUCCESS - end - end - - DebugPrint("spawn: x=" .. position.x .. ", y=" .. position.y) - return position -end - --- Return steel chest entity (or nil) -function DropEmptySteelChest(player) - local pos = player.surface.find_non_colliding_position("steel-chest", player.position, 15, 1) - if not pos then - return nil - end - local grave = player.surface.create_entity{name="steel-chest", position=pos, force="neutral"} - return grave -end - --- Gravestone soft mod. With my own modifications/improvements. -function DropGravestoneChests(player) - - local grave - local count = 0 - - -- Use "game.player.cursorstack" to get items in player's hand. - - -- Loop through a players different inventories - -- Put it all into the chest - -- If the chest is full, create a new chest. - for i, id in ipairs{ - defines.inventory.player_armor, - defines.inventory.player_main, - defines.inventory.player_quickbar, - defines.inventory.player_guns, - defines.inventory.player_ammo, - defines.inventory.player_tools, - defines.inventory.player_trash} do - local inv = player.get_inventory(id) - if (not inv.is_empty()) then - for j = 1, #inv do - if inv[j].valid_for_read then - - -- Create a chest when counter is reset - if (count == 0) then - grave = DropEmptySteelChest(player) - if (grave == nil) then - player.print("Not able to place a chest nearby! Some items lost!") - return - end - grave_inv = grave.get_inventory(defines.inventory.chest) - end - count = count + 1 - - grave_inv[count].set_stack(inv[j]) - - -- Reset counter when chest is full - if (count == #grave_inv) then - count = 0 - end - end - end - end - end - - if (grave ~= nil) then - player.print("Successfully dropped your items into a chest! Go get them quick!") - end -end - --- Enforce a circle of land, also adds trees in a ring around the area. -function CreateCropCircle(surface, centerPos, chunkArea, tileRadius) - - local tileRadSqr = tileRadius^2 - - local dirtTiles = {} - for i=chunkArea.left_top.x,chunkArea.right_bottom.x,1 do - for j=chunkArea.left_top.y,chunkArea.right_bottom.y,1 do - - -- This ( X^2 + Y^2 ) is used to calculate if something - -- is inside a circle area. - local distVar = math.floor((centerPos.x - i)^2 + (centerPos.y - j)^2) - - -- Fill in all unexpected water in a circle - if (distVar < tileRadSqr) then - if (surface.get_tile(i,j).collides_with("water-tile") or ENABLE_SPAWN_FORCE_GRASS) then - table.insert(dirtTiles, {name = "grass", position ={i,j}}) - end - end - - -- Create a circle of trees around the spawn point. - if ((distVar < tileRadSqr-200) and - (distVar > tileRadSqr-300)) then - surface.create_entity({name="tree-01", amount=1, position={i, j}}) - end - end - end - - surface.set_tiles(dirtTiles) -end - --- COPIED FROM jvmguy! --- Enforce a square of land, with a tree border --- this is equivalent to the CreateCropCircle code -function CreateCropOctagon(surface, centerPos, chunkArea, tileRadius) - - local dirtTiles = {} - for i=chunkArea.left_top.x,chunkArea.right_bottom.x,1 do - for j=chunkArea.left_top.y,chunkArea.right_bottom.y,1 do - - local distVar1 = math.floor(math.max(math.abs(centerPos.x - i), math.abs(centerPos.y - j))) - local distVar2 = math.floor(math.abs(centerPos.x - i) + math.abs(centerPos.y - j)) - local distVar = math.max(distVar1, distVar2 * 0.707); - - -- Fill in all unexpected water in a circle - if (distVar < tileRadius+2) then - if (surface.get_tile(i,j).collides_with("water-tile") or ENABLE_SPAWN_FORCE_GRASS) then - table.insert(dirtTiles, {name = "grass", position ={i,j}}) - end - end - - -- Create a tree ring - if ((distVar < tileRadius) and - (distVar > tileRadius-2)) then - surface.create_entity({name="tree-01", amount=1, position={i, j}}) - end - end - end surface.set_tiles(dirtTiles) -end - -function CreateWaterStrip(surface, leftPos, length) - local waterTiles = {} - for i=0,length,1 do - table.insert(waterTiles, {name = "water", position={leftPos.x+i,leftPos.y}}) - end - surface.set_tiles(waterTiles) -end - - --- Adjust alien params -function ConfigureAlienStartingParams() - game.map_settings.enemy_evolution.time_factor=0 - game.map_settings.enemy_evolution.destroy_factor = game.map_settings.enemy_evolution.destroy_factor / ENEMY_DESTROY_FACTOR_DIVISOR - game.map_settings.enemy_evolution.pollution_factor = game.map_settings.enemy_evolution.pollution_factor / ENEMY_POLLUTION_FACTOR_DIVISOR - game.map_settings.enemy_expansion.enabled = ENEMY_EXPANSION -end - --- Add Long Reach to Character -function GivePlayerLongReach(player) - player.character.character_build_distance_bonus = BUILD_DIST_BONUS - player.character.character_reach_distance_bonus = REACH_DIST_BONUS - -- player.character.character_resource_reach_distance_bonus = RESOURCE_DIST_BONUS -end - - -function SetRandomSiloPosition() - if (global.siloPosition == nil) then - -- Get an X,Y on a circle far away. - distX = math.random(0,SILO_CHUNK_DISTANCE_X) - distY = RandomNegPos() * math.floor(math.sqrt(SILO_CHUNK_DISTANCE_X^2 - distX^2)) - distX = RandomNegPos() * distX - - -- Set those values. - local siloX = distX*CHUNK_SIZE + CHUNK_SIZE/2 - local siloY = distY*CHUNK_SIZE + CHUNK_SIZE/2 - global.siloPosition = {x = siloX, y = siloY} - end -end - --- Use preset value -function SetFixedSiloPosition() - if (global.siloPosition == nil) then - global.siloPosition = SILO_POSITION - end -end - --- Transfer Items Between Inventory --- Returns the number of items that were successfully transferred. --- Returns -1 if item not available. --- Returns -2 if can't place item into destInv (ERROR) -function TransferItems(srcInv, destEntity, itemStack) - -- Check if item is in srcInv - if (srcInv.get_item_count(itemStack.name) == 0) then - return -1 - end - - -- Check if can insert into destInv - if (not destEntity.can_insert(itemStack)) then - return -2 - end - - -- Insert items - local itemsRemoved = srcInv.remove(itemStack) - itemStack.count = itemsRemoved - return destEntity.insert(itemStack) -end - --- Attempts to transfer at least some of one type of item from an array of items. --- Use this to try transferring several items in order --- It returns once it successfully inserts at least some of one type. -function TransferItemMultipleTypes(srcInv, destEntity, itemNameArray, itemCount) - local ret = 0 - for _,itemName in pairs(itemNameArray) do - ret = TransferItems(srcInv, destEntity, {name=itemName, count=itemCount}) - if (ret > 0) then - return ret -- Return the value succesfully transferred - end - end - return ret -- Return the last error code -end - -function AutofillTurret(player, turret) - local mainInv = player.get_inventory(defines.inventory.player_main) - - -- Attempt to transfer some ammo - local ret = TransferItemMultipleTypes(mainInv, turret, {"piercing-rounds-magazine","firearm-magazine"}, AUTOFILL_TURRET_AMMO_QUANTITY) - - -- Check the result and print the right text to inform the user what happened. - if (ret > 0) then - -- Inserted ammo successfully - -- FlyingText("Inserted ammo x" .. ret, turret.position, my_color_red) - elseif (ret == -1) then - FlyingText("Out of ammo!", turret.position, my_color_red) - elseif (ret == -2) then - FlyingText("Autofill ERROR! - Report this bug!", turret.position, my_color_red) - end -end - -function AutoFillVehicle(player, vehicle) - local mainInv = player.get_inventory(defines.inventory.player_main) - - -- Attempt to transfer some fuel - if ((vehicle.name == "car") or (vehicle.name == "tank") or (vehicle.name == "diesel-locomotive")) then - TransferItemMultipleTypes(mainInv, vehicle, {"raw-wood", "coal", "solid-fuel"}, 50) - end - - -- Attempt to transfer some ammo - if ((vehicle.name == "car") or (vehicle.name == "tank")) then - TransferItemMultipleTypes(mainInv, vehicle, {"piercing-rounds-magazine","firearm-magazine"}, 100) - end - - -- Attempt to transfer some tank shells - if (vehicle.name == "tank") then - TransferItemMultipleTypes(mainInv, vehicle, {"explosive-cannon-shell", "cannon-shell"}, 100) - end -end - - --------------------------------------------------------------------------------- --- EVENT SPECIFIC FUNCTIONS --------------------------------------------------------------------------------- - --- Display messages to a user everytime they join -function PlayerJoinedMessages(event) - local player = game.players[event.player_index] - player.print(WELCOME_MSG) - player.print(GAME_MODE_MSG) - player.print(MODULES_ENABLED) -end - --- Create the gravestone chests for a player when they die -function CreateGravestoneChestsOnDeath(event) - DropGravestoneChests(game.players[event.player_index]) -end - --- Remove decor to save on file size -function UndecorateOnChunkGenerate(event) - local surface = event.surface - local chunkArea = event.area - RemoveDecorationsArea(surface, chunkArea) - RemoveFish(surface, chunkArea) -end - --- Give player items on respawn --- Intended to be the default behavior when not using separate spawns -function PlayerRespawnItems(event) - GivePlayerItems(game.players[event.player_index]) -end - -function PlayerSpawnItems(event) - GivePlayerStarterItems(game.players[event.player_index]) -end - --- Autofill softmod -function Autofill(event) - local player = game.players[event.player_index] - local eventEntity = event.created_entity - - if (eventEntity.name == "gun-turret") then - AutofillTurret(player, eventEntity) - end - - if ((eventEntity.name == "car") or (eventEntity.name == "tank") or (eventEntity.name == "diesel-locomotive")) then - AutoFillVehicle(player, eventEntity) - end -end - --- General purpose event function for removing a particular recipe -function RemoveRecipe(event, recipeName) - local recipes = event.research.force.recipes - if recipes[recipeName] then - recipes[recipeName].enabled = false - end -end - --------------------------------------------------------------------------------- --- UNUSED CODE --- Either didn't work, or not used or not tested.... --------------------------------------------------------------------------------- - - --- THIS DOES NOT WORK IN SCENARIOS! --- function DisableVanillaResouresAndEnemies() - --- local map_gen_ctrls = game.surfaces["nauvis"].map_gen_settings.autoplace_controls - --- map_gen_ctrls["coal"].size = "none" --- map_gen_ctrls["stone"].size = "none" --- map_gen_ctrls["iron-ore"].size = "none" --- map_gen_ctrls["copper-ore"].size = "none" --- map_gen_ctrls["crude-oil"].size = "none" --- map_gen_ctrls["enemy-base"].size = "none" --- end - - - --- Shared vision for other forces? UNTESTED --- function ShareVisionForAllForces() --- for _,f in pairs(game.forces) do --- for _,p in pairs(game.connected_players) do --- if (f.name ~= p.force.name) then --- local visionArea = {left_top= --- {x=p.x-(CHUNK_SIZE*3), --- y=p.y-(CHUNK_SIZE*3)}, --- right_bottom= --- {x=p.x+(CHUNK_SIZE*3), --- y=p.y+(CHUNK_SIZE*3)}} --- f.chart(game.surfaces["nauvis"], visionArea) --- end --- end --- end --- end \ No newline at end of file diff --git a/rso_config.lua b/rso_config.lua deleted file mode 100644 index 58b5101..0000000 --- a/rso_config.lua +++ /dev/null @@ -1,52 +0,0 @@ -debug_enabled = false - -region_size = 10 -- alternative mean to control how further away resources would be, default - 256 tiles or 8 chunks - -- each region is region_size*region_size chunks - -- each chunk is 32*32 tiles - -use_donut_shapes = false -- setting this to false will remove donuts from possible resource layouts - -starting_area_size = 0.8 -- starting area in regions, safe from random nonsense - -absolute_resource_chance = 0.50 -- chance to spawn an resource in a region -starting_richness_mult = 1 -- multiply starting area richness for resources -global_richness_mult = 1 -- multiply richness for all resources except starting area -global_size_mult = 1 -- multiply size for all ores, doesn't affect starting area - -absolute_enemy_chance = 3 -- chance to spawn enemies per sector (can be more then one base if spawned) -enemy_base_size_multiplier = 1 -- all base sizes will be multiplied by this - larger number means bigger bases - -multi_resource_active = false -- global switch for multi resource chances -multi_resource_richness_factor = 0.60 -- any additional resource is multiplied by this value times resources-1 -multi_resource_size_factor = 0.90 -multi_resource_chance_diminish = 0.6 -- diminishing effect factor on multi_resource_chance - -min_amount=250 -- default value for minimum amount of resource in single pile - -richness_distance_factor=0.9 -- exponent for richness distance factor calculation -size_distance_factor=0.15 -- exponent for size distance factor calculation - -deterministic = true -- set to false to use system for all decisions math.random - --- mode is no longer used by generation process - it autodetects endless resources --- endless_resource_mode = false -- if true, the size of each resource is modified by the following modifier. Use with the endless resources mod. -endless_resource_mode_sizeModifier = 0.80 - --- This setting isn't used anywhere in the soft mod version of RSO -- OARC --- Just set it from Oarc's config.lua (look for ENEMY_EXPANSION) --- disableEnemyExpansion = false -- allows for disabling of in-game biter base building - -use_RSO_biter_spawning = true -- enables spawning of biters controlled by RSO mod - less enemies around with more space between bases -use_vanilla_biter_spawning = false -- enables using of vanilla spawning - -biter_ratio_segment=3 --the ratio components determining how many biters to spitters will be spawned -spitter_ratio_segment=1 --eg. 1 and 1 -> equal number of biters and spitters, 10 and 1 -> 10 times as many biters to spitters - -useEnemiesInPeaceMod = false -- additional override for peace mod detection - when set to true it will spawn enemies normally, needs to have enemies enabled in peace mod - --- Always leave this setting to true in this soft mod scenario version! -- OARC -ignoreMapGenSettings = true -- stops the default behaviour of reading map gen settings - -useResourceCollisionDetection = true -- enables avoidace calculations to reduce ores overlaping of each other -resourceCollisionDetectionRatio = 0.8 -- at least this much of ore field needs to be placable to spawn it -resourceCollisionFieldSkip = true -- determines if ore field should be skipped completely if placement based on ratio failed diff --git a/rso_control.lua b/rso_control.lua deleted file mode 100644 index d3d9bbf..0000000 --- a/rso_control.lua +++ /dev/null @@ -1,1378 +0,0 @@ -require("rso_config") -require("util") -require("rso_resource_config") -require("oarc_utils") -require("config") - -local MB=require "locale/rso/metaball" -local drand = require 'locale/rso/drand' -local rng = drand.mwvc -if not deterministic then rng = drand.sys_rand end - --- math shortcuts -local floor = math.floor -local abs = math.abs -local cos = math.cos -local sin = math.sin -local pi = math.pi -local max = math.max - -local function round(value) - return math.floor(value + 0.5) -end - -local function debug(str) - if debug_enabled then - game.players[1].print(str) - end -end - --- constants -local CHUNK_SIZE = 32 -local REGION_TILE_SIZE = CHUNK_SIZE*region_size -local MIN_BALL_DISTANCE = CHUNK_SIZE/6 -local P_BALL_SIZE_FACTOR = 0.7 -local N_BALL_SIZE_FACTOR = 0.95 -local NEGATIVE_MODIFICATOR = 123456 - -local meta_shapes = nil - -if use_donut_shapes then - meta_shapes = {MB.MetaEllipse, MB.MetaSquare, MB.MetaDonut} -else - meta_shapes = {MB.MetaEllipse, MB.MetaSquare} -end - --- local globals -local index_is_built = false -local max_allotment = 0 -local rgen = nil -local distance = util.distance -local spawner_probability_edge = 0 -- below this value a biter spawner, above/equal this value a spitter spawner -local invalidResources = {} -local config = nil -local configIndexed = nil - --- map gen settings mapping - -local startingAreaMultiplier = -{ - none = 0, - ["very-low"] = 0.25, - low = 0.5, - normal = 1, - high = 1.5, - ["very-high"] = 2, -} - -local frequencyAllotmentMultiplier = -{ - ["very-low"] = 0.5, - low = 0.75, - normal = 1, - high = 1.5, - ["very-high"] = 2, -} - -local sizeMultiplier = -{ - none = 0, - ["very-low"] = 0.5, - low = 0.75, - normal = 1, - high = 1.25, - ["very-high"] = 1.5, -} - -local richnessMultiplier = -{ - ["very-low"] = 0.125, - low = 0.25, - normal = 1, - high = 2, - ["very-high"] = 4, -} - -local entityFrequencyMultiplier = -{ - ["very-low"] = 0.25, - low = 0.5, - normal = 1, - high = 2, - ["very-high"] = 4, -} - -local entitySizeMultiplier = -{ - none = 0, - ["very-low"] = 0.5, - low = 0.75, - normal = 1, - high = 2, - ["very-high"] = 4, -} - ---[[ HELPER METHODS ]]-- - -local function normalize(n) --keep numbers at (positive) 32 bits - return floor(n) % 0x80000000 -end - -local function bearing(origin, dest) - -- finds relative angle - local xd = dest.x - origin.x - local yd = dest.y - origin.y - return math.atan2(xd, yd); -end - -local function str2num(s) - local num = 0 - for i=1,s:len() do - num=num + (s:byte(i) - 33)*i - end - return num -end - -local function mult_for_pos(pos) - local num = 0 - local x = pos.x - local y = pos.y - - if x == 0 then x = 0.5 end - if y == 0 then y = 0.5 end - if x < 0 then - x = abs(x) + NEGATIVE_MODIFICATOR - end - if y < 0 then - y = abs(y) + NEGATIVE_MODIFICATOR - end - - return drand.lcg(y, 'mvc'):random(0)*drand.lcg(x, 'nr'):random(0) -end - -local function rng_for_reg_pos(pos) - local rgen = rng(normalize(global.seed*mult_for_pos(pos))) - rgen:random() - rgen:random() - rgen:random() - return rgen -end - -local function rng_restricted_angle(restrictions) - local rng = rgen:random() - local x_scale, y_scale - local deformX = rgen:random() * 2 - 1 - local deformY = rgen:random() * 2 - 1 - - if restrictions=='xy' then - y_scale=1.0 + deformY*0.5 - x_scale=1.0 + deformX*0.5 - angle = rng*pi*2 - elseif restrictions=='x' then - y_scale=1.0 + deformY*0.6 - x_scale=1.0 + deformX*0.6 - angle = rng*pi/2 - pi/4 - elseif restrictions=='y' then - y_scale=1.0 + deformY*0.6 - x_scale=1.0 + deformX*0.6 - angle = rng*pi/2 + pi/2 - else - y_scale=1.0 + deformY*0.3 - x_scale=1.0 + deformX*0.3 - angle = rng*pi*2 - end - - return angle, x_scale, y_scale -end - -local function vary_by_percentage(x, p) - return x + (0.5 - rgen:random())*2*x*p -end - - -local function remove_trees(surface, x, y, x_size, y_size ) - local bb={{x - x_size, y - y_size}, {x + x_size, y + y_size}} - for _, entity in pairs(surface.find_entities_filtered{area = bb, type="tree"}) do - if entity.valid then - entity.destroy() - end - end -end - -local function removeDecorations(surface, x, y, width, height ) - local bb={{x, y}, {x + width, y + height}} - for _, entity in pairs(surface.find_entities_filtered{area = bb, type="decorative"}) do - if entity.valid then - entity.destroy() - end - end -end - -local function find_intersection(surface, x, y) - -- try to get position in between of valid chunks by probing map - -- this may breaks determinism of generation, but so far it returned on first if - local gt = surface.get_tile - local restriction = '' - if gt(x + CHUNK_SIZE*2, y + CHUNK_SIZE*2).valid and gt(x - CHUNK_SIZE*2, y - CHUNK_SIZE*2).valid and gt(x + CHUNK_SIZE*2, y - CHUNK_SIZE*2).valid and gt(x - CHUNK_SIZE*2, y + CHUNK_SIZE*2).valid then - restriction = 'xy' - elseif gt(x + CHUNK_SIZE*2, y + CHUNK_SIZE*2).valid and gt(x + CHUNK_SIZE*2, y).valid and gt(x, y + CHUNK_SIZE*2).valid then - x=x + CHUNK_SIZE/2 - y=y + CHUNK_SIZE/2 - restriction = 'xy' - elseif gt(x + CHUNK_SIZE*2, y - CHUNK_SIZE*2).valid and gt(x + CHUNK_SIZE*2, y).valid and gt(x, y - CHUNK_SIZE*2).valid then - x=x + CHUNK_SIZE/2 - y=y - CHUNK_SIZE/2 - restriction = 'xy' - elseif gt(x - CHUNK_SIZE*2, y + CHUNK_SIZE*2).valid and gt(x - CHUNK_SIZE*2, y).valid and gt(x, y + CHUNK_SIZE*2).valid then - x=x - CHUNK_SIZE/2 - y=y + CHUNK_SIZE/2 - restriction = 'xy' - elseif gt(x - CHUNK_SIZE*2, y - CHUNK_SIZE*2).valid and gt(x - CHUNK_SIZE*2, y).valid and gt(x, y - CHUNK_SIZE*2).valid then - x=x - CHUNK_SIZE/2 - y=y - CHUNK_SIZE/2 - restriction = 'xy' - elseif gt(x + CHUNK_SIZE*2, y).valid then - x=x + CHUNK_SIZE/2 - restriction = 'x' - elseif gt(x - CHUNK_SIZE*2, y).valid then - x=x - CHUNK_SIZE/2 - restriction = 'x' - elseif gt(x, y + CHUNK_SIZE*2).valid then - y=y + CHUNK_SIZE/2 - restriction = 'y' - elseif gt(x, y - CHUNK_SIZE*2).valid then - y=y - CHUNK_SIZE/2 - restriction = 'y' - end - return x, y, restriction -end - -local function find_random_chunk(r_x, r_y) - local offset_x=rgen:random(region_size)-1 - local offset_y=rgen:random(region_size)-1 - local c_x=r_x*REGION_TILE_SIZE + offset_x*CHUNK_SIZE - local c_y=r_y*REGION_TILE_SIZE + offset_y*CHUNK_SIZE - return c_x, c_y -end - -local function is_same_region(c_x1, c_y1, c_x2, c_y2) - if not floor(c_x1/REGION_TILE_SIZE) == floor(c_x2/REGION_TILE_SIZE) then - return false - end - if not floor(c_y1/REGION_TILE_SIZE) == floor(c_y2/REGION_TILE_SIZE) then - return false - end - return true -end - -local function find_random_neighbour_chunk(ocx, ocy) - -- somewhat bruteforce and unoptimized - local x_dir = rgen:random(-1,1) - local y_dir = rgen:random(-1,1) - local ncx = ocx + x_dir*CHUNK_SIZE - local ncy = ocy + y_dir*CHUNK_SIZE - if is_same_region(ncx, ncy, ocx, ocy) then - return ncx, ncy - end - - ncx = ocx - x_dir*CHUNK_SIZE - ncy = ocy - y_dir*CHUNK_SIZE - if is_same_region(ncx, ncy, ocx, ocy) then - return ncx, ncy - end - - ncx = ocx - x_dir*CHUNK_SIZE - if is_same_region(ncx, ocy, ocx, ocy) then - return ncx, ocy - end - - ncy = ocy - y_dir*CHUNK_SIZE - if is_same_region(ocx, ncy, ocx, ocy) then - return ocx, ncy - end - - return ocx, ocy -end - -local function isInStartingArea( regionX, regionY ) - - for idx, pos in pairs( global.startingAreas ) do - - local adjustedX = regionX - pos.x / REGION_TILE_SIZE - local adjustedY = regionY - pos.y / REGION_TILE_SIZE - if ((adjustedX * adjustedX + adjustedY * adjustedY) <= starting_area_size * starting_area_size) then - return true - end - end - - return false -end - --- modifies the resource size - only used in endless_resource_mode -local function modify_resource_size(resourceName, resourceSize, startingArea) - - if not startingArea then - resourceSize = math.ceil(resourceSize * global_size_mult) - end - - resourceEntity = game.entity_prototypes[resourceName] - if resourceEntity and resourceEntity.infinite_resource then - - newResourceSize = resourceSize * endless_resource_mode_sizeModifier - - -- make sure it's still an integer - newResourceSize = math.ceil(newResourceSize) - -- make sure it's not 0 - if newResourceSize == 0 then newResourceSize = 1 end - return newResourceSize - else - return resourceSize - end -end - ---[[ SPAWN METHODS ]]-- - -local locationOrder = -{ - { x = 0, y = 0 }, - { x = -1, y = 0 }, - { x = 1, y = 0 }, - { x = 0, y = -1 }, - { x = 0, y = 1 }, - { x = -1, y = -1 }, - { x = 1, y = -1 }, - { x = -1, y = 1 }, - { x = 1, y = 1 } -} - ---[[ entity-field ]]-- -local function spawn_resource_ore(surface, rname, pos, size, richness, startingArea, restrictions) - -- blob generator, centered at pos, size controls blob diameter - restrictions = restrictions or '' - debug("Entering spawn_resource_ore "..rname.." at:"..pos.x..","..pos.y.." size:"..size.." richness:"..richness.." isStart:"..tostring(startingArea).." restrictions:"..restrictions) - - size = modify_resource_size(rname, size, startingArea) - local radius = size / 2 -- to radius - - local p_balls={} - local n_balls={} - local MIN_BALL_DISTANCE = math.min(MIN_BALL_DISTANCE, radius/2) - - local maxPradius = 0 - local outside = { xmin = 1e10, xmax = -1e10, ymin = 1e10, ymax = -1e10 } - local inside = { xmin = 1e10, xmax = -1e10, ymin = 1e10, ymax = -1e10 } - - local function adjustRadius(radius, scaleX, scaleY, up) - return radius - end - - local function updateRect(rect, x, y, radius) - rect.xmin = math.min(rect.xmin, x - radius) - rect.xmax = math.max(rect.xmax, x + radius) - rect.ymin = math.min(rect.ymin, y - radius) - rect.ymax = math.max(rect.ymax, y + radius) - end - - local function updateRects(x, y, radius, scaleX, scaleY) - local adjustedRadius = adjustRadius(radius, scaleX, scaleY, true) - local radiusMax = adjustedRadius * 3 -- arbitrary multiplier - needs to be big enough to not cut any metaballs - updateRect(outside, x, y, radiusMax) - updateRect(inside, x, y, adjustedRadius) - end - - local function generate_p_ball() - local angle, x_scale, y_scale, x, y, b_radius, shape - angle, x_scale, y_scale=rng_restricted_angle(restrictions) - local dev = radius / 2 + rgen:random() * radius / 4--math.min(CHUNK_SIZE/3, radius*1.5) - local dev_x, dev_y = pos.x, pos.y - x = rgen:random(-dev, dev)+dev_x - y = rgen:random(-dev, dev)+dev_y - if p_balls[#p_balls] and distance(p_balls[#p_balls], {x=x, y=y}) < MIN_BALL_DISTANCE then - local new_angle = bearing(p_balls[#p_balls], {x=x, y=y}) - debug("Move ball old xy @ "..x..","..y) - x=(cos(new_angle)*MIN_BALL_DISTANCE) + x - y=(sin(new_angle)*MIN_BALL_DISTANCE) + y - debug("Move ball new xy @ "..x..","..y) - end - - if #p_balls == 0 then - b_radius = ( 3 * radius / 4 + rgen:random() * radius / 4) -- * (P_BALL_SIZE_FACTOR^#p_balls) - else - b_radius = ( radius / 4 + rgen:random() * radius / 2) -- * (P_BALL_SIZE_FACTOR^#p_balls) - end - - - if #p_balls > 0 then - local tempRect = table.deepcopy(inside) - updateRect(tempRect, x, y, adjustRadius(b_radius, x_scale, y_scale)) - local rectSize = math.max(tempRect.xmax - tempRect.xmin, tempRect.ymax - tempRect.ymin) - local targetSize = size * 1.25 - debug("Rect size "..rectSize.." targetSize "..targetSize) - if rectSize > targetSize then - local widthLeft = (targetSize - (inside.xmax - inside.xmin)) - local heightLeft = (targetSize - (inside.ymax - inside.ymin)) - local widthMod = math.min(x - inside.xmin, inside.xmax - x) - local heightMod = math.min(y - inside.ymin, inside.ymax - y) - local radiusBackup = b_radius - b_radius = math.min(widthLeft + widthMod, heightLeft + heightMod) - b_radius = adjustRadius(b_radius, x_scale, y_scale, false) - debug("Reduced ball radius from "..radiusBackup.." to "..b_radius.." widthLeft:"..widthLeft.." heightLeft:"..heightLeft.." widthMod:"..widthMod.." heightMod:"..heightMod) - end - end - - if b_radius < 2 and #p_balls == 0 then - b_radius = 2 - end - - if b_radius > 0 then - - maxPradius = math.max(maxPradius, b_radius) - shape = meta_shapes[rgen:random(1,#meta_shapes)] - local radiusText = "" - if shape.type == "MetaDonut" then - local inRadius = b_radius / 4 + b_radius / 2 * rgen:random() - radiusText = " inRadius:"..inRadius - p_balls[#p_balls+1] = shape:new(x, y, b_radius, inRadius, angle, x_scale, y_scale, 1.1) - else - p_balls[#p_balls+1] = shape:new(x, y, b_radius, angle, x_scale, y_scale, 1.1) - end - updateRects(x, y, b_radius, x_scale, y_scale) - - debug("P+Ball "..shape.type.." @ "..x..","..y.." radius: "..b_radius..radiusText.." angle: "..math.deg(angle).." scale: "..x_scale..", "..y_scale) - end - end - - local function generate_n_ball(i) - local angle, x_scale, y_scale, x, y, b_radius, shape - angle, x_scale, y_scale=rng_restricted_angle('xy') - if p_balls[i] then - local new_angle = p_balls[i].angle + pi*rgen:random(0,1) + (rgen:random()-0.5)*pi/2 - local dist = p_balls[i].radius - x=(cos(new_angle)*dist) + p_balls[i].x - y=(sin(new_angle)*dist) + p_balls[i].y - angle = p_balls[i].angle + pi/2 + (rgen:random()-0.5)*pi*2/3 - else - x = rgen:random(-radius, radius)+pos.x - y = rgen:random(-radius, radius)+pos.y - end - - if p_balls[i] then - b_radius = (p_balls[i].radius / 4 + rgen:random() * p_balls[i].radius / 2) -- * (N_BALL_SIZE_FACTOR^#n_balls) - else - b_radius = (radius / 4 + rgen:random() * radius / 2) -- * (N_BALL_SIZE_FACTOR^#n_balls) - end - - if b_radius < 1 then - b_radius = 1 - end - - shape = meta_shapes[rgen:random(1,#meta_shapes)] - local radiusText = "" - if shape.type == "MetaDonut" then - local inRadius = b_radius / 4 + b_radius / 2 * rgen:random() - radiusText = " inRadius:"..inRadius - n_balls[#n_balls+1] = shape:new(x, y, b_radius, inRadius, angle, x_scale, y_scale, 1.15) - else - n_balls[#n_balls+1] = shape:new(x, y, b_radius, angle, x_scale, y_scale, 1.15) - end - -- updateRects(x, y, b_radius, x_scale, y_scale) -- should not be needed here - only positive ball can generate ore - debug("N-Ball "..shape.type.." @ "..x..","..y.." radius: "..b_radius..radiusText.." angle: "..math.deg(angle).." scale: "..x_scale..", "..y_scale) - end - - local function calculate_force(x,y) - local p_force = 0 - local n_force = 0 - for _,ball in ipairs(p_balls) do - p_force = p_force + ball:force(x,y) - end - for _,ball in ipairs(n_balls) do - n_force = n_force + ball:force(x,y) - end - local totalForce = 0 - if p_force > n_force then - totalForce = 1 - 1/(p_force - n_force) - end - --debug("Force at "..x..","..y.." p:"..p_force.." n:"..n_force.." result:"..totalForce) - --return (1 - 1/p_force) - n_force - return totalForce - end - - local max_p_balls = 2 - local min_amount = config[rname].min_amount or min_amount - if restrictions == 'xy' then - max_p_balls = 3 - end - - radius = math.min(radius, 2*CHUNK_SIZE) - - local force - -- generate blobs - for i=1,max_p_balls do - generate_p_ball() - end - - for i=1,rgen:random(1, #p_balls) do - generate_n_ball(i) - end - - - local _total = 0 - local oreLocations = {} - local forceTotal = 0 - - -- fill the map - for y=outside.ymin, outside.ymax do - - for x=outside.xmin, outside.xmax do - force = calculate_force(x, y) - if force > 0 then - oreLocations[#oreLocations + 1] = {x = x, y = y, force = force, valid = false} - forceTotal = forceTotal + force - end - end - end - - local validCount, resOffsetX, resOffsetY, ratio - - for _,locationOffset in ipairs(locationOrder) do - validCount = 0 - resOffsetX = locationOffset.x * CHUNK_SIZE - resOffsetY = locationOffset.y * CHUNK_SIZE - - for _, location in ipairs(oreLocations) do - - local newX = location.x + resOffsetX - local newY = location.y + resOffsetY - location.valid = false - if surface.can_place_entity{name = rname, position = {x = newX,y = newY}} then - location.valid = true - validCount = validCount + 1 - end - end - - ratio = 0 - - if validCount > 0 then - ratio = validCount / #oreLocations - end - - debug("Valid ratio ".. ratio) - - if not useResourceCollisionDetection then - break - end - - if ratio > resourceCollisionDetectionRatio then - break - elseif resourceCollisionFieldSkip then -- in case no valid ratio was found we skip the field completely - validCount = 0 - end - end - - if validCount > 0 then - local rectSize = ((inside.xmax - inside.xmin) + (inside.ymax - inside.ymin)) / 2 - - local sizeMultiplier = rectSize ^ 0.6 - local minSize = richness * 5 * sizeMultiplier - local maxSize = richness * 10 * sizeMultiplier - local approxDepositSize = rgen:random(minSize, maxSize) - - approxDepositSize = approxDepositSize - validCount * min_amount - - if approxDepositSize < 0 then - approxDepositSize = 100 * validCount - end - - local forceFactor = approxDepositSize / forceTotal - - -- don't create very dense resources in starting area - another field will be generated - if startingArea and forceFactor > 4000 then - forceFactor = rgen:random(3000, 4000) - end - - debug( "Force total:"..forceTotal.." sizeMin:"..minSize.." sizeMax:"..maxSize.." factor:"..forceFactor.." location#:"..validCount.." rectSize:"..rectSize.." sizeMultiplier:"..sizeMultiplier) - local richnessMultiplier = global_richness_mult - - if startingArea then - richnessMultiplier = starting_richness_mult - end - - -- infinite ore handling for Angels Ores mod - local infiniteOrePresent = false - local infiniteOreName = "infinite-"..rname - local minimumInfiniteOreAmount = nil - local spawnName = rname - - if game.entity_prototypes[infiniteOreName] then - infiniteOrePresent = true - minimumInfiniteOreAmount = game.entity_prototypes[infiniteOreName].minimum_resource_amount - end - - if startingArea and not infiniteResourceInStartArea then - infiniteOrePresent = false - end - - for _,location in ipairs(oreLocations) do - if location.valid then - - local amount = floor(( forceFactor * location.force + min_amount ) * richnessMultiplier) - - if amount > 1e9 then - amount = 1e9 - end - - _total = _total + amount - - spawnName = rname - if infiniteOrePresent and location.force > infiniteResourceSpawnThreshold then - spawnName = infiniteOreName - if minimumInfiniteOreAmount and amount < minimumInfiniteOreAmount then - amount = minimumInfiniteOreAmount - end - end - - if amount > 0 then - surface.create_entity{name = spawnName, - position = {location.x + resOffsetX,location.y + resOffsetY}, - force = game.forces.neutral, - amount = amount} - end - end - end - - end - - if debug_enabled then - debug("Total amount: ".._total) - debug("Leaving spawn_resource_ore") - end - return _total -end - ---[[ entity-liquid ]]-- -local function spawn_resource_liquid(surface, rname, pos, size, richness, startingArea, restrictions) - restrictions = restrictions or '' - debug("Entering spawn_resource_liquid "..rname.." "..pos.x..","..pos.y.." "..size.." "..richness.." "..tostring(startingArea).." "..restrictions) - local _total = 0 - local max_radius = rgen:random()*CHUNK_SIZE/2 + CHUNK_SIZE - - richness = ( 0.75 + rgen:random() / 2 ) * richness * size - - resourceEntity = game.entity_prototypes[rname] - - - local total_share = 0 - local avg_share = 1/size - local angle = rgen:random()*pi*2 - local saved = 0 - while total_share < 1 do - local new_share = vary_by_percentage(avg_share, 0.25) - if new_share + total_share > 1 then - new_share = 1 - total_share - end - total_share = new_share + total_share - if new_share < avg_share/10 then - -- too small - break - end - local amount = floor(richness*new_share) + saved - - local richnessMultiplier = global_richness_mult - - if startingArea then - richnessMultiplier = starting_richness_mult - end - - --if amount >= game.entity_prototypes[rname].minimum then - if amount >= config[rname].minimum_amount then - saved = 0 - for try=1,5 do - local dist = rgen:random()*(max_radius - max_radius*0.1) - angle = angle + pi/4 + rgen:random()*pi/2 - local x, y = pos.x + cos(angle)*dist, pos.y + sin(angle)*dist - if surface.can_place_entity{name = rname, position = {x,y}} then - debug("@ "..x..","..y.." amount: "..amount.." new_share: "..new_share.." try: "..try) - amount = floor(amount * richnessMultiplier) - - if amount > 1e9 then - amount = 1e9 - end - - _total = _total + amount - - if amount > 0 then - surface.create_entity{name = rname, - position = {x,y}, - force = game.forces.neutral, - amount = amount, - direction = rgen:random(4)} - end - break - elseif not startingArea then -- we don't want to make ultra rich nodes in starting area - failing to make them will add second spawn in different location - entities = surface.find_entities_filtered{area = {{x-2.75, y-2.75}, {x+2.75, y+2.75}}, name=rname} - if entities and #entities > 0 then - _total = _total + amount - for k, ent in pairs(entities) do - ent.amount = ent.amount + floor(amount/#entities) - end - break - end - end - end - else - saved = amount - end - end - debug("Total amount: ".._total) - debug("Leaving spawn_resource_liquid") - return _total -end - -local spawnerTable = nil - -local function initSpawnerTable() - if spawnerTable == nil then - spawnerTable = {} - spawnerTable["biter-spawner"] = game.entity_prototypes["biter-spawner"] ~= nil - spawnerTable["bob-biter-spawner"] = game.entity_prototypes["bob-biter-spawner"] ~= nil - spawnerTable["spitter-spawner"] = game.entity_prototypes["spitter-spawner"] ~= nil - spawnerTable["bob-spitter-spawner"] = game.entity_prototypes["bob-spitter-spawner"] ~= nil - end -end - -local function spawn_entity(surface, ent, r_config, x, y) - if not use_RSO_biter_spawning then return end - local size=rgen:random(r_config.size.min, r_config.size.max) - - local _total = 0 - local r_distance = distance({x=0,y=0},{x=x/REGION_TILE_SIZE,y=y/REGION_TILE_SIZE}) - - local distanceMultiplier = math.min(r_distance^r_config.size_per_region_factor, 5) - if r_config.size_per_region_factor then - size = size*distanceMultiplier - end - - size = size * enemy_base_size_multiplier - - debug("Entering spawn_entity "..ent.." "..x..","..y.." "..size) - - local maxAttemptCount = 5 - local distancePerAttempt = 0.2 - - initSpawnerTable() - - for i=1,size do - for attempt = 1, maxAttemptCount do - local richness=r_config.richness*(r_distance^richness_distance_factor) - local max_d = floor(CHUNK_SIZE*(0.5 + distancePerAttempt*attempt)) - local s_x = x + rgen:random(0, floor(max_d - r_config.clear_range[1])) - max_d/2 + r_config.clear_range[1] - local s_y = y + rgen:random(0, floor(max_d - r_config.clear_range[2])) - max_d/2 + r_config.clear_range[2] - - if surface.get_tile(s_x, s_y).valid then - - remove_trees(surface, s_x, s_y, r_config.clear_range[1], r_config.clear_range[2]) - - local spawnerName = nil - - if spawner_probability_edge > 0 then - - bigSpawnerChance = rgen:random() - - if rgen:random() < spawner_probability_edge then - if ( useBobEntity and bigSpawnerChance > 0.75 ) then - spawnerName = "bob-biter-spawner" - else - spawnerName = "biter-spawner" - end - else - if ( useBobEntity and bigSpawnerChance > 0.75 ) then - spawnerName = "bob-spitter-spawner" - else - spawnerName = "spitter-spawner" - end - end - end - - if spawnerName and spawnerTable[spawnerName] then - if surface.can_place_entity{name=spawnerName, position={s_x, s_y}} then - _total = _total + richness - debug(spawnerName.." @ "..s_x..","..s_y.." placed on "..attempt.." attempt") - - surface.create_entity{name=spawnerName, position={s_x, s_y}, force=game.forces[r_config.force], amount=floor(richness)}--, direction=rgen:random(4) - -- else - -- debug("Entity "..spawnerName.." spawn failed") - break; - else - if attempt == maxAttemptCount then - debug(spawnerName.." @ "..s_x..","..s_y.." failed to spawn") - end - end - else - debug("Entity "..spawnerName.." doesn't exist") - end - end - end - - if r_config.sub_spawn_probability then - local sub_spawn_prob = r_config.sub_spawn_probability*math.min(r_config.sub_spawn_max_distance_factor, r_config.sub_spawn_distance_factor^r_distance) - if rgen:random() < sub_spawn_prob then - for i=1,(rgen:random(r_config.sub_spawn_size.min, r_config.sub_spawn_size.max)*distanceMultiplier) do - local allotment_max = 0 - -- build table - for k,v in pairs(r_config.sub_spawns) do - if not v.min_distance or r_distance > v.min_distance then - local allotment = v.allotment - if v.allotment_distance_factor then - allotment = allotment * (v.allotment_distance_factor^r_distance) - end - v.allotment_range ={min = allotment_max, max = allotment_max + allotment} - allotment_max = allotment_max + allotment - else - v.allotment_range = nil - end - end - local sub_type = rgen:random(0, allotment_max) - for sub_spawn,v in pairs(r_config.sub_spawns) do - if v.allotment_range and sub_type >= v.allotment_range.min and sub_type <= v.allotment_range.max then - for attempt = 1, maxAttemptCount do - local max_d = floor(CHUNK_SIZE*distancePerAttempt*attempt) - s_x = x + rgen:random(max_d) - max_d/2 - s_y = y + rgen:random(max_d) - max_d/2 - remove_trees(surface, s_x, s_y, v.clear_range[1], v.clear_range[2]) - if surface.can_place_entity{name=sub_spawn, position={s_x, s_y}} then - surface.create_entity{name=sub_spawn, position={s_x, s_y}, force=game.forces[r_config.force]}--, direction=rgen:random(4) - debug("Rolled subspawn "..sub_spawn.." @ "..s_x..","..s_x.." after "..attempt.." attempts") - break; - else - if attempt == maxAttemptCount then - debug("Rolling subspawn "..sub_spawn.." @ "..s_x..","..s_x.." failed") - end - end - end - break - end - end - end - end - end - end - debug("Total amount: ".._total) - debug("Leaving spawn_entity") -end - ---[[ EVENT/INIT METHODS ]]-- - -local function spawn_starting_resources( surface, index ) - - if global.startingAreas[index].spawned then return end - if surface.map_gen_settings.starting_area == "none" and not ignoreMapGenSettings then return end -- starting area disabled by map gen - if starting_area_size < 0.1 then return end -- skip spawning if starting area is to small - - local position = global.startingAreas[index] - - rgen = rng_for_reg_pos( position ) - local status = true - for index,v in ipairs(configIndexed) do - if v.starting then - local prob = rgen:random() -- probability that this resource is spawned - debug("starting resource probability rolled "..prob) - if v.starting.probability > 0 and prob <= v.starting.probability then - local total = 0 - local radius = 25 - local min_threshold = 0 - - if v.type == "resource-ore" then - min_threshold = v.starting.richness * rgen:random(5, 10) -- lets make sure that there is at least 10-15 times starting richness ore at start - elseif v.type == "resource-liquid" then - min_threshold = v.starting.richness * 0.5 * v.starting.size - end - - while (radius < 200) and (total < min_threshold) do - local angle = rgen:random() * pi * 2 - local dist = rgen:random() * 30 + radius * 2 - local pos = { x = floor(cos(angle) * dist) + position.x, y = floor(sin(angle) * dist) + position.y } - if v.type == "resource-ore" then - total = total + spawn_resource_ore(surface, v.name, pos, v.starting.size, v.starting.richness, true) - elseif v.type == "resource-liquid" then - total = total + spawn_resource_liquid(surface, v.name, pos, v.starting.size, v.starting.richness, true) - end - radius = radius + 10 - end - if total < min_threshold then - status = false - end - end - end - end - - global.startingAreas[index].spawned = true -end - -local function modifyMinMax(value, mod) - value.min = round( value.min * mod ) - value.max = round( value.max * mod ) -end - -local function prebuild_config_data(surface) - if index_is_built then return false end - - local mapGenSettings = nil - - if not ignoreMapGenSettings then - mapGenSettings = surface.map_gen_settings - end - local autoPlaceSettings = nil - if mapGenSettings then - autoPlaceSettings = mapGenSettings.autoplace_controls - end - - configIndexed = {} - -- build additional indexed array to the associative array - for res_name, res_conf in pairs(config) do - if res_conf.valid then -- only add valid resources - res_conf.name = res_name - - local settingsForResource = nil - local isEntity = (res_conf.type == "entity") - local addResource = true - - local autoplaceName = res_name - - if res_conf.autoplace_name then - autoplaceName = res_conf.autoplace_name - end - - if autoPlaceSettings then - settingsForResource = autoPlaceSettings[autoplaceName] - end - - if settingsForResource then - local allotmentMod = nil - local sizeMod = nil - if isEntity then - allotmentMod = entityFrequencyMultiplier[settingsForResource.frequency] - sizeMod = entitySizeMultiplier[settingsForResource.size] - else - allotmentMod =frequencyAllotmentMultiplier[settingsForResource.frequency] - sizeMod = sizeMultiplier[settingsForResource.size] - end - - local richnessMod = richnessMultiplier[settingsForResource.richness] - - - debug(res_name .. " allotment mod " .. allotmentMod .. " size mod " .. sizeMod .. " richness mod " .. richnessMod ) - - - if allotmentMod then - if isEntity then - res_conf.absolute_probability = res_conf.absolute_probability * allotmentMod - debug("Entity chance modified to "..res_conf.absolute_probability) - else - res_conf.allotment = round( res_conf.allotment * allotmentMod ) - end - end - - if sizeMod ~= nil and sizeMod == 0 then - addResource = false - end - - if sizeMod then - modifyMinMax(res_conf.size, sizeMod) - - if res_conf.starting then - res_conf.starting.size = round( res_conf.starting.size * sizeMod ) - end - - if isEntity then - if res_conf.sub_spawn_size then - modifyMinMax(res_conf.sub_spawn_size, sizeMod) - end - modifyMinMax(res_conf.spawns_per_region, sizeMod) - end - end - - if richnessMod then - if type == "resource-ore" then - res_conf.richness = round( res_conf.richness * richnessMod ) - elseif type == "resource-liquid" then - modifyMinMax(res_conf.richness, richnessMod) - end - - if res_conf.starting then - res_conf.starting.richness = round( res_conf.starting.richness * richnessMod ) - end - end - end - - if addResource then - configIndexed[#configIndexed + 1] = res_conf - if res_conf.multi_resource and multi_resource_active then - local new_list = {} - for sub_res_name, allotment in pairs(res_conf.multi_resource) do - if config[sub_res_name] and config[sub_res_name].valid then - new_list[#new_list+1] = {name = sub_res_name, allotment = allotment} - end - end - table.sort(new_list, function(a, b) return a.name < b.name end) - res_conf.multi_resource = new_list - else - res_conf.multi_resource_chance = nil - end - end - end - end - - table.sort(configIndexed, function(a, b) return a.name < b.name end) - - local pr=0 - for index,v in pairs(config) do - if v.along_resource_probability then - v.along_resource_probability_range={min=pr, max=pr+v.along_resource_probability} - pr=pr+v.along_resource_probability - end - if v.allotment and v.allotment > 0 then - v.allotment_range={min=max_allotment, max=max_allotment+v.allotment} - max_allotment=max_allotment+v.allotment - end - end - - if mapGenSettings and mapGenSettings.starting_area then - local multiplier = startingAreaMultiplier[mapGenSettings.starting_area] - if multiplier ~= nil then - starting_area_size = starting_area_size * multiplier - debug("Starting area "..starting_area_size) - end - end - - index_is_built = true -end - --- set up the probabilty segments from which to roll between for biter and spitter spawners -local function calculate_spawner_ratio() - if (biter_ratio_segment ~= 0 and spitter_ratio_segment ~= 0) and biter_ratio_segment >= 0 and spitter_ratio_segment >= 0 then - spawner_probability_edge=biter_ratio_segment/(biter_ratio_segment+spitter_ratio_segment) -- normalize to between 0 and 1 - end -end - -local function checkConfigForInvalidResources() - --make sure that every resource in the config is actually available. - --call this function, before the auxiliary config is prebuilt! - if index_is_built then return end - - local prototypes = game.entity_prototypes - - for resourceName, resourceConfig in pairs(config) do - if prototypes[resourceName] or resourceConfig.type == "entity" then - resourceConfig.valid = true - else - -- resource was in config, but it doesn't exist in game files anymore - mark it invalid - resourceConfig.valid = false - - table.insert(invalidResources, "Resource not available: " .. resourceName) - debug("Resource not available: " .. resourceName) - end - - if resourceConfig.valid and resourceConfig.type ~= "entity" then - if prototypes[resourceName].autoplace_specification == nil then - resourceConfig.valid = false - debug("Resource "..resourceName.." invalidated - autoplace not present") - end - end - end -end - -local function roll_region(c_x, c_y) - --in what region is this chunk? - local r_x=floor(c_x/REGION_TILE_SIZE) - local r_y=floor(c_y/REGION_TILE_SIZE) - local r_data = nil - --don't spawn stuff in starting area - if isInStartingArea( c_x/REGION_TILE_SIZE, c_y/REGION_TILE_SIZE ) then - return false - end - - if global.regions[r_x] and global.regions[r_x][r_y] then - r_data = global.regions[r_x][r_y] - else - --if this chunk is the first in its region to be generated - if not global.regions[r_x] then global.regions[r_x] = {} end - global.regions[r_x][r_y]={} - r_data = global.regions[r_x][r_y] - rgen = rng_for_reg_pos{x=r_x,y=r_y} - - local rollCount = math.ceil(#configIndexed / 10) - 1 -- 0 based counter is more convenient here - rollCount = math.min(rollCount, 3) - - for rollNumber = 0,rollCount do - - local resourceChance = absolute_resource_chance - rollNumber * 0.1 - --absolute chance to spawn resource - local abct = rgen:random() - debug("Rolling resource "..abct.." against "..resourceChance.." roll "..rollNumber) - if abct <= resourceChance then - local res_type=rgen:random(1, max_allotment) - for index,v in ipairs(configIndexed) do - if v.allotment_range and ((res_type >= v.allotment_range.min) and (res_type <= v.allotment_range.max)) then - debug("Rolled primary resource "..v.name.." with res_type="..res_type.." @ "..r_x..","..r_y) - local num_spawns=rgen:random(v.spawns_per_region.min, v.spawns_per_region.max) - local last_spawn_coords = {} - local along_ - for i=1,num_spawns do - local c_x, c_y = find_random_chunk(r_x, r_y) - if not r_data[c_x] then r_data[c_x] = {} end - if not r_data[c_x][c_y] then r_data[c_x][c_y] = {} end - local c_data = r_data[c_x][c_y] - c_data[#c_data+1]={v.name, rollNumber} - last_spawn_coords[#last_spawn_coords+1] = {c_x, c_y} - debug("Rolled primary chunk "..v.name.." @ "..c_x.."."..c_y.." reg: "..r_x..","..r_y) - -- Along resource spawn, only once - if i == 1 then - local am_roll = rgen:random() - for index,vv in ipairs(configIndexed) do - if vv.along_resource_probability_range and am_roll >= vv.along_resource_probability_range.min and am_roll <= vv.along_resource_probability_range.max then - c_data = r_data[c_x][c_y] - c_data[#c_data+1]={vv.name, rollNumber} - debug("Rolled along "..vv.name.." @ "..c_x.."."..c_y.." reg: "..r_x..","..r_y) - end - end - end - end - -- roll multiple resources in same region - local deep=0 - while v.multi_resource_chance and rgen:random() <= v.multi_resource_chance*(multi_resource_chance_diminish^deep) do - deep = deep + 1 - local max_allotment = 0 - for index,sub_res in pairs(v.multi_resource) do max_allotment=max_allotment+sub_res.allotment end - - local res_type=rgen:random(1, max_allotment) - local min=0 - for _, sub_res in pairs(v.multi_resource) do - if (res_type >= min) and (res_type <= sub_res.allotment + min) then - local last_coords = last_spawn_coords[rgen:random(1, #last_spawn_coords)] - local c_x, c_y = find_random_neighbour_chunk(last_coords[1], last_coords[2]) -- in same as primary resource chunk - if not r_data[c_x] then r_data[c_x] = {} end - if not r_data[c_x][c_y] then r_data[c_x][c_y] = {} end - local c_data = r_data[c_x][c_y] - c_data[#c_data+1]={sub_res.name, deep} - debug("Rolled multiple "..sub_res.name..":"..deep.." with res_type="..res_type.." @ "..c_x.."."..c_y.." reg: "..r_x.."."..r_y) - break - else - min = min + sub_res.allotment - end - end - end - break - end - end - - end - end - -- roll for absolute_probability - this rolls the enemies - - for index,v in ipairs(configIndexed) do - if v.absolute_probability then - local prob_factor = 1 - if v.probability_distance_factor then - prob_factor = math.min(v.max_probability_distance_factor, v.probability_distance_factor^distance({x=0,y=0},{x=r_x,y=r_y})) - end - local abs_roll = rgen:random() - if abs_roll 10 then - global.startingAreas[1].spawned = true - end - end - - calculate_spawner_ratio() - spawn_starting_resources(surface, 1 ) - - initDone = true - - if game.surfaces["nauvis"].map_gen_settings.autoplace_controls["iron-ore"].size ~= "none" then - game.players[1].print("RSO WARNING - VANILLA iron-ore GEN IS NOT DISABLED!") - end - if game.surfaces["nauvis"].map_gen_settings.autoplace_controls["copper-ore"].size ~= "none" then - game.players[1].print("RSO WARNING - VANILLA copper-ore GEN IS NOT DISABLED!") - end - if game.surfaces["nauvis"].map_gen_settings.autoplace_controls["crude-oil"].size ~= "none" then - game.players[1].print("RSO WARNING - VANILLA crude-oil GEN IS NOT DISABLED!") - end - if game.surfaces["nauvis"].map_gen_settings.autoplace_controls["enemy-base"].size ~= "none" then - game.players[1].print("RSO WARNING - VANILLA enemy-base GEN IS NOT DISABLED!") - end - if game.surfaces["nauvis"].map_gen_settings.autoplace_controls["stone"].size ~= "none" then - game.players[1].print("RSO WARNING - VANILLA stone GEN IS NOT DISABLED!") - end - if game.surfaces["nauvis"].map_gen_settings.autoplace_controls["coal"].size ~= "none" then - game.players[1].print("RSO WARNING - VANILLA coal GEN IS NOT DISABLED!") - end - end - - script.on_event(defines.events.on_tick, nil) -end - --- Oarc removed this. No idea if this will break save game loads. --- Tested it once and it seemed to work? --- function RSO_delayedInit() --- script.on_event(defines.events.on_tick, RSO_init) --- end - -function RSO_ChunkGenerated(event) - local c_x = event.area.left_top.x - local c_y = event.area.left_top.y - - RSO_init() - - roll_region(c_x, c_y) - roll_chunk(event.surface, c_x, c_y) -end - -function RSO_PlayerCreated(event) - RSO_init() - local player = game.players[event.player_index] - - if debug_enabled then - printResourceProbability(player) - end -end - diff --git a/rso_resource_config.lua b/rso_resource_config.lua deleted file mode 100644 index dfbcc1f..0000000 --- a/rso_resource_config.lua +++ /dev/null @@ -1,159 +0,0 @@ - -local function fillVanillaConfig() - - config["iron-ore"] = { - type="resource-ore", - - -- general spawn params - allotment=100, -- how common resource is - spawns_per_region={min=1, max=1}, --number of chunks - richness=10000, -- resource_ore has only one richness value - resource-liquid has min/max - - size={min=20, max=30}, -- rough radius of area, too high value can produce square shaped areas - min_amount=350, - - -- resource provided at starting location - -- probability: 1 = 100% chance to be in starting area - -- 0 = resource is not in starting area - starting={richness=8000, size=25, probability=1}, - - multi_resource_chance=0.20, -- absolute value - multi_resource={ - ["iron-ore"] = 2, -- ["resource_name"] = allotment - ['copper-ore'] = 4, - ["coal"] = 4, - ["stone"] = 4, - } - } - - config["copper-ore"] = { - type="resource-ore", - - allotment=90, - spawns_per_region={min=1, max=1}, - richness=10000, - size={min=20, max=30}, - min_amount=350, - - starting={richness=6000, size=25, probability=1}, - - multi_resource_chance=0.20, - multi_resource={ - ["iron-ore"] = 4, - ['copper-ore'] = 2, - ["coal"] = 4, - ["stone"] = 4, - } - } - - config["coal"] = { - type="resource-ore", - - allotment=80, - - spawns_per_region={min=1, max=1}, - size={min=15, max=25}, - richness=8000, - min_amount=350, - - starting={richness=6000, size=20, probability=1}, - - multi_resource_chance=0.30, - multi_resource={ - ["crude-oil"] = 1, - ["iron-ore"] = 3, - ['copper-ore'] = 3, - } - } - - config["stone"] = { - type="resource-ore", - - allotment=60, - spawns_per_region={min=1, max=1}, - richness=6000, - size={min=15, max=20}, - min_amount=250, - - starting={richness=5000, size=16, probability=1}, - - multi_resource_chance=0.30, - multi_resource={ - ["coal"] = 4, - ["iron-ore"] = 3, - ['copper-ore'] = 3, - } - } - - config["crude-oil"] = { - type="resource-liquid", - minimum_amount=10000, - allotment=70, - spawns_per_region={min=1, max=2}, - richness={min=10000, max=20000}, -- richness per resource spawn - size={min=3, max=7}, - - starting={richness=20000, size=2, probability=1}, - - multi_resource_chance=0.20, - multi_resource={ - ["coal"] = 4, - } - } -end - -local function fillEnemies() - - config["enemy-base"] = { - type="entity", - force="enemy", - clear_range = {6, 6}, - - spawns_per_region={min=2,max=4}, - size={min=2,max=4}, - size_per_region_factor=1, - richness=3, - - absolute_probability=absolute_enemy_chance, -- chance to spawn in region - probability_distance_factor=1.1, -- relative increase per region - max_probability_distance_factor=3, -- absolute value - - along_resource_probability=0.20, -- chance to spawn in resource chunk anyway, absolute value. Can happen once per resource. - - sub_spawn_probability=0.1, -- chance for this entity to spawn anything from sub_spawns table, absolute value - sub_spawn_size={min=1, max=2}, -- in same chunk - sub_spawn_distance_factor=1.01, - sub_spawn_max_distance_factor=1.5, - sub_spawns={ - ["small-worm-turret"]={ - min_distance=0, - allotment=200, - allotment_distance_factor=0.99, - clear_range = {2, 2}, - }, - ["medium-worm-turret"]={ - min_distance=10, - allotment=100, - allotment_distance_factor=1.01, - clear_range = {2, 2}, - }, - ["big-worm-turret"]={ - min_distance=20, - allotment=100, - allotment_distance_factor=1.015, - clear_range = {2, 2}, - } - } - } - -end - -function loadResourceConfig() - - config={} - - fillVanillaConfig() - fillEnemies() - - return config -end diff --git a/tag.lua b/tag.lua deleted file mode 100644 index 91bd931..0000000 --- a/tag.lua +++ /dev/null @@ -1,56 +0,0 @@ -function CreateTagGui(event) - local player = game.players[event.player_index] - if player.gui.top.tag == nil then - player.gui.top.add{name="tag", type="button", caption="Tag"} - end -end - --- Tag list -local roles = { - {display_name = "[Solo]"}, - {display_name = "[Mining]"}, - {display_name = "[Power]"}, - {display_name = "[Oil]"}, - {display_name = "[Smelt]"}, - {display_name = "[Rail]"}, - {display_name = "[Defense]"}, - {display_name = "[Circuits]"}, - {display_name = "[Labs]"}, - {display_name = "[Logistics]"}, - {display_name = "[Misc]"}, - {display_name = "[Aliens]"}, - {display_name = "[Rocket]"}, - {display_name = "[AFK]"}, - {display_name = "Clear"}} - -local function ExpangTagGui(player) - local frame = player.gui.left["tag-panel"] - if (frame) then - frame.destroy() - else - local frame = player.gui.left.add{type="frame", name="tag-panel", caption="What are you doing:"} - for _, role in pairs(roles) do - frame.add{type="button", caption=role.display_name, name=role.display_name} - end - end -end - -function TagGuiClick(event) - if not (event and event.element and event.element.valid) then return end - local player = game.players[event.element.player_index] - local name = event.element.name - - if (name == "tag") then - ExpangTagGui(player) - end - - if (name == "Clear") then - player.tag = "" - return - end - - for _, role in pairs(roles) do - if (name == role.display_name) then - player.tag = role.display_name end - end -end