mirror of
https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn.git
synced 2024-12-04 09:43:00 +02:00
Merge pull request #144 from Oarcinae/space_age_v2_mod
Version 2.0 Mod Release
This commit is contained in:
commit
d8504739b1
.gitignoreLICENSEREADME.mdchangelog.txt
compat
control.luadata.luadevplan.txtexample-config.luaexample
graphics
icon_40x40.pnginfo.jsonlib
admin_commands.luaauto_decon_miners.luaconfig.luaconfig_parser.luafrontier_silo.luagame_opts.lua
gui_tabs
helper_commands.luaholding_pen.luamap_features.luanotepad.luaoarc_buy.luaoarc_enemies.luaoarc_global_cfg.luaoarc_gui_tabs.luaoarc_gui_utils.luaoarc_store_map_features.luaoarc_store_player_items.luaoarc_tests.luaoarc_utils.luaoffline_protection.luaplayer_list.luaregrowth_map.luargcommand.luarocket_launch.luascaled_enemies.luaseparate_spawns.luaseparate_spawns_guis.luashared_chests.luasharing.luatag.lualocale
scenarios/OARC
settings.luathumbnail.png
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
||||
config.lua
|
||||
releases
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Oarcinae
|
||||
Copyright (c) 2024 Oarcinae
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
26
README.md
26
README.md
@ -1,22 +1,26 @@
|
||||
# FactorioScenarioMultiplayerSpawn
|
||||
A custom scenario for allowing separate spawn locations in multiplayer. Designed for Co-op and PvE.
|
||||
# OARC Multiplayer Spawn
|
||||
A Factorio mod for allowing separate spawn locations in multiplayer. Designed for Co-op and PvE.
|
||||
|
||||
## WORK IN PROGRESS
|
||||
This is the currently in development branch for Factorio V2.0. It is not yet complete. If you want a stable version, please use the latest release.
|
||||
|
||||
## Read the WIKI
|
||||
The github wiki page will have the most up to date information. If you have questions, go there first, otherwise just ask.
|
||||
|
||||
## Versions
|
||||
Check releases for stable versions. 0.17 and 0.16 have final stable versions that will no longer be maintained.
|
||||
The latest master is for 1.0.0+
|
||||
Check releases for stable versions.
|
||||
2.0.X WILL BE the currently in work version that will support Factorio V2.0 with and without the Space Age expansion.
|
||||
1.1.X is the latest stable version for V1.0 that I plan to do minimal maintenance on.
|
||||
0.17 and 0.16 have final stable versions that will no longer be maintained.
|
||||
|
||||
## Status
|
||||
At this time I have no more planned features and will only be in support and maintenance mode.
|
||||
Currently working on adding support for V2.0 Factorio.
|
||||
Will be removing many of the soft-mod features and other feature creep things for the first version.
|
||||
Waiting on Space Age release so I can start working on support for that.
|
||||
|
||||
## Credit
|
||||
Several other portions of the code (tags, frontier style rocket silo) have also been adapted from other scenario code.
|
||||
|
||||
Credit to 3Ra for help as well: https://github.com/3RaGaming
|
||||
|
||||
Praise be to Mylon
|
||||
Thanks to https://github.com/vfinn (JustGoFly) and many others for their assistance!
|
||||
https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn/graphs/contributors
|
||||
|
||||
## Random Notes
|
||||
Feel free to submit bugs/fixes/requests/pulls/forks whatever you want.
|
||||
@ -24,5 +28,5 @@ Feel free to submit bugs/fixes/requests/pulls/forks whatever you want.
|
||||
I do not plan on supporting PvP, but I will help anyone who wants to make it a configurable option.
|
||||
|
||||
## Contact
|
||||
discord.gg/trnpcen
|
||||
https://discord.gg/trnpcen
|
||||
oarcinae@gmail.com
|
||||
|
22
changelog.txt
Normal file
22
changelog.txt
Normal file
@ -0,0 +1,22 @@
|
||||
---------------------------------------------------------------------------------------------------
|
||||
Version: 2.0.0
|
||||
Date: 2024-09-25
|
||||
Major Features:
|
||||
- Initial release of a formal mod (non-scenario) version. This is essentially a rewrite of the original scenario in mod form.
|
||||
- Added support for spawning on different surfaces in preparation for space age support.
|
||||
- Updated all GUIs to be more in line with style recommendations and to be more user friendly.
|
||||
- New preview GUI while spawn is being generated and instant transition to the new spawn when ready.
|
||||
- Exposed most of the mod settings in the custom in game mod GUI. This provides a nicer interface than the native mod settings allows.
|
||||
- New holding pen surface with restricted permissions for players to spawn into before being moved to their primary spawn.
|
||||
- Regrowth and world eater features can safely be toggled on/off at any time and supports multiple surfaces.
|
||||
Bugfixes:
|
||||
- Fixed an issue where active radars would block spawn areas from being cleaned up if a player left within the removal window.
|
||||
- Fixed an issue where regrowth would sometimes delete chunks with vehicles, robots or spidertron in them.
|
||||
- Added several mods as hidden dependencies to avoid fatal errors on startup due to incompatible load order issues.
|
||||
Optimizations:
|
||||
- New implementation of shared electricity should stop any possibility of desync with the old method as well as improve performance. (Uses cross-surface power connections.)
|
||||
- New implementation of shared items should also improve performance. (Uses linked-chest.)
|
||||
- Some changes to regrowth to improve performance (reducing unnecessary refresh areas).
|
||||
Info:
|
||||
- Several of the soft-mod features have been removed (including the coin shop).
|
||||
- Space age support will not be available until some time after it is released. I will need time to test and implement it. For now, this is provided as a feature for experimentation. You can test "secondary" spawns by enabling that feature in the settings, it is currently disabled by default.
|
@ -1,14 +0,0 @@
|
||||
Compat = Compat or {}
|
||||
|
||||
|
||||
function Compat.handle_factoriomaps()
|
||||
if remote.interfaces.factoriomaps then
|
||||
script.on_event(remote.call("factoriomaps", "get_start_capture_event_id"), function()
|
||||
|
||||
print("Starting factoriomaps-oarc integration script")
|
||||
|
||||
remote.call("factoriomaps", "surface_set_default", "oarc")
|
||||
|
||||
end)
|
||||
end
|
||||
end
|
530
control.lua
530
control.lua
@ -1,376 +1,219 @@
|
||||
-- control.lua
|
||||
-- Mar 2019
|
||||
-- Sep 2024
|
||||
-- ____ _____ _____
|
||||
-- / __ \ /\ | __ \ / ____|
|
||||
-- | | | | / \ | |__) | |
|
||||
-- | | | |/ /\ \ | _ /| |
|
||||
-- | |__| / ____ \| | \ \| |____
|
||||
-- \____/_/ \_\_| \_\\_____|
|
||||
|
||||
-- Oarc's Separated Spawn Scenario
|
||||
--
|
||||
-- I wanted to create a scenario that allows you to spawn in separate locations
|
||||
-- From there, I ended up adding a bunch of other minor/major features
|
||||
--
|
||||
-- Credit:
|
||||
-- Tags - Taken from WOGs scenario
|
||||
-- Rocket Silo - Taken from Frontier as an idea
|
||||
--
|
||||
-- Feel free to re-use anything you want. It would be nice to give me credit
|
||||
-- if you can.
|
||||
-- Oarc's Separated Spawn MOD V2
|
||||
-- I decided to rewrite my old scenario due to the coming changes in Factorio V2.0 and its new Space Age Expansion.
|
||||
|
||||
-- Change Overview:
|
||||
-- Support the scenario "as a mod" ONLY. Scenario merely provides a way to overwrite settings on_init.
|
||||
-- Removed a lot of unnecessary feature bloat.
|
||||
-- Move text to locale files where possible.
|
||||
|
||||
-- Major Features:
|
||||
-- Core feature allows for a safe, separate spawn area for each player.
|
||||
-- Players can choose to spawn with friends (buddy spawn) or join other bases.
|
||||
-- Offline protection from enemy attacks.
|
||||
-- Chunk cleanup to keep save file size down.
|
||||
-- Sharing of electricity and items between players.
|
||||
|
||||
-- To keep the scenario more manageable (for myself) I have done the following:
|
||||
-- 1. Keep all event calls in control.lua (here)
|
||||
-- 2. Put all config options in config.lua and provided an example-config.lua file too.
|
||||
-- 3. Put other stuff into their own files where possible.
|
||||
-- 4. Put all other files into lib folder
|
||||
-- 5. Provided an examples folder for example/recommended map gen settings
|
||||
|
||||
-- Generic Utility Includes
|
||||
require("lib/oarc_utils")
|
||||
|
||||
-- Other soft-mod type features.
|
||||
require("lib/frontier_silo")
|
||||
require("lib/tag")
|
||||
require("lib/game_opts")
|
||||
require("lib/player_list")
|
||||
require("lib/rocket_launch")
|
||||
require("lib/admin_commands")
|
||||
require("lib/config")
|
||||
require("lib/config_parser")
|
||||
require("lib/regrowth_map")
|
||||
require("lib/shared_chests")
|
||||
require("lib/notepad")
|
||||
require("lib/map_features")
|
||||
require("lib/oarc_buy")
|
||||
require("lib/auto_decon_miners")
|
||||
|
||||
-- For Philip. I currently do not use this and need to add proper support for
|
||||
-- commands like this in the future.
|
||||
-- require("lib/rgcommand")
|
||||
-- require("lib/helper_commands")
|
||||
|
||||
-- Main Configuration File
|
||||
require("config")
|
||||
|
||||
-- Save all config settings to global table.
|
||||
require("lib/oarc_global_cfg.lua")
|
||||
|
||||
-- Scenario Specific Includes
|
||||
require("lib/holding_pen")
|
||||
require("lib/separate_spawns")
|
||||
require("lib/separate_spawns_guis")
|
||||
require("lib/oarc_enemies")
|
||||
require("lib/oarc_gui_tabs")
|
||||
require("lib/offline_protection")
|
||||
require("lib/scaled_enemies")
|
||||
require("lib/sharing")
|
||||
|
||||
-- compatibility with mods
|
||||
require("compat/factoriomaps")
|
||||
-- TODO: Possibly remove this later?
|
||||
require("lib/oarc_tests")
|
||||
|
||||
-- Create a new surface so we can modify map settings at the start.
|
||||
GAME_SURFACE_NAME="oarc"
|
||||
|
||||
commands.add_command("trigger-map-cleanup",
|
||||
"Force immediate removal of all expired chunks (unused chunk removal mod)",
|
||||
RegrowthForceRemoveChunksCmd)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- ALL EVENT HANLDERS ARE HERE IN ONE PLACE!
|
||||
-- On Init - Only runs once the first time the game starts
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
----------------------------------------
|
||||
-- On Init - only runs once the first
|
||||
-- time the game starts
|
||||
----------------------------------------
|
||||
script.on_init(function(event)
|
||||
|
||||
-- FIRST
|
||||
InitOarcConfig()
|
||||
|
||||
-- Regrowth (always init so we can enable during play.)
|
||||
ValidateAndLoadConfig()
|
||||
RegrowthInit()
|
||||
|
||||
-- Create new game surface
|
||||
CreateGameSurface()
|
||||
|
||||
-- MUST be before other stuff, but after surface creation.
|
||||
InitSpawnGlobalsAndForces()
|
||||
CreateHoldingPenSurface() -- Must be after init spawn globals?
|
||||
|
||||
-- Frontier Silo Area Generation
|
||||
if (global.ocfg.frontier_rocket_silo and not global.ocfg.enable_magic_factories) then
|
||||
SpawnSilosAndGenerateSiloAreas()
|
||||
-- Useful for debugging and if players choose not to use the provided empty scenario.
|
||||
if remote.interfaces["freeplay"] then
|
||||
log("Freeplay interface detected. Disabling various freeplay features now!")
|
||||
remote.call("freeplay", "set_skip_intro", true)
|
||||
remote.call("freeplay", "set_disable_crashsite", true)
|
||||
remote.call("freeplay", "set_created_items", {})
|
||||
remote.call("freeplay", "set_respawn_items", {})
|
||||
end
|
||||
|
||||
-- Everyone do the shuffle. Helps avoid always starting at the same location.
|
||||
-- Needs to be done after the silo spawning.
|
||||
if (global.ocfg.enable_vanilla_spawns) then
|
||||
global.vanillaSpawns = FYShuffle(global.vanillaSpawns)
|
||||
log("Vanilla spawns:")
|
||||
log(serpent.block(global.vanillaSpawns))
|
||||
-- If there are any players that already exist, init them now.
|
||||
for _,player in pairs(game.players) do
|
||||
SeparateSpawnsInitPlayer(player.index)
|
||||
end
|
||||
|
||||
Compat.handle_factoriomaps()
|
||||
|
||||
if (global.ocfg.enable_coin_shop and global.ocfg.enable_chest_sharing) then
|
||||
SharedChestInitItems()
|
||||
end
|
||||
|
||||
if (global.ocfg.enable_coin_shop and global.ocfg.enable_magic_factories) then
|
||||
MagicFactoriesInit()
|
||||
end
|
||||
|
||||
OarcMapFeatureInitGlobalCounters()
|
||||
OarcAutoDeconOnInit()
|
||||
|
||||
-- Display starting point text as a display of dominance.
|
||||
RenderPermanentGroundText(game.surfaces[GAME_SURFACE_NAME], {x=-29,y=-30}, 40, "OARC", {0.9, 0.7, 0.3, 0.8})
|
||||
end)
|
||||
|
||||
script.on_load(function()
|
||||
Compat.handle_factoriomaps()
|
||||
end)
|
||||
--------------------------------------------------------------------------------
|
||||
-- On Configuration Changed - Only runs when the mod configuration changes
|
||||
--------------------------------------------------------------------------------
|
||||
-- oarc_new_spawn_created = script.generate_event_name()
|
||||
|
||||
-- script.on_configuration_changed(function(data)
|
||||
-- -- Regenerate event ID:
|
||||
-- end)
|
||||
|
||||
----------------------------------------
|
||||
-- Rocket launch event
|
||||
-- Used for end game win conditions / unlocking late game stuff
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_rocket_launched, function(event)
|
||||
RocketLaunchEvent(event)
|
||||
end)
|
||||
|
||||
|
||||
----------------------------------------
|
||||
-- Chunk Generation
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_chunk_generated, function(event)
|
||||
|
||||
if (event.surface.name ~= GAME_SURFACE_NAME) then return end
|
||||
|
||||
if global.ocfg.enable_regrowth then
|
||||
RegrowthChunkGenerate(event)
|
||||
end
|
||||
|
||||
if global.ocfg.enable_undecorator then
|
||||
UndecorateOnChunkGenerate(event)
|
||||
end
|
||||
|
||||
SeparateSpawnsGenerateChunk(event)
|
||||
|
||||
CreateHoldingPen(event.surface, event.area)
|
||||
end)
|
||||
|
||||
|
||||
----------------------------------------
|
||||
-- Gui Click
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_gui_click, function(event)
|
||||
|
||||
-- Don't interfere with other mod related stuff.
|
||||
if (event.element.get_mod() ~= nil) then return end
|
||||
|
||||
if global.ocfg.enable_tags then
|
||||
TagGuiClick(event)
|
||||
end
|
||||
|
||||
WelcomeTextGuiClick(event)
|
||||
SpawnOptsGuiClick(event)
|
||||
SpawnCtrlGuiClick(event)
|
||||
SharedSpwnOptsGuiClick(event)
|
||||
BuddySpawnOptsGuiClick(event)
|
||||
BuddySpawnWaitMenuClick(event)
|
||||
BuddySpawnRequestMenuClick(event)
|
||||
SharedSpawnJoinWaitMenuClick(event)
|
||||
|
||||
ClickOarcGuiButton(event)
|
||||
|
||||
if global.ocfg.enable_coin_shop then
|
||||
ClickOarcStoreButton(event)
|
||||
end
|
||||
|
||||
GameOptionsGuiClick(event)
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_gui_checked_state_changed, function (event)
|
||||
SpawnOptsRadioSelect(event)
|
||||
SpawnCtrlGuiOptionsSelect(event)
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_gui_selected_tab_changed, function (event)
|
||||
TabChangeOarcGui(event)
|
||||
|
||||
if global.ocfg.enable_coin_shop then
|
||||
TabChangeOarcStore(event)
|
||||
end
|
||||
script.on_event(defines.events.on_runtime_mod_setting_changed, function(event)
|
||||
if (not StringStartsWith(event.setting, "oarc-mod")) then return end
|
||||
RuntimeModSettingChanged(event)
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- Player Events
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_player_joined_game, function(event)
|
||||
PlayerJoinedMessages(event)
|
||||
ServerWriteFile("player_events", game.players[event.player_index].name .. " joined the game." .. "\n")
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_player_created, function(event)
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
-- Move the player to the game surface immediately.
|
||||
player.teleport({x=0,y=0}, GAME_SURFACE_NAME)
|
||||
|
||||
if global.ocfg.enable_long_reach then
|
||||
GivePlayerLongReach(player)
|
||||
end
|
||||
|
||||
SeparateSpawnsPlayerCreated(event.player_index, true)
|
||||
|
||||
InitOarcGuiTabs(player)
|
||||
|
||||
if global.ocfg.enable_coin_shop then
|
||||
InitOarcStoreGuiTabs(player)
|
||||
end
|
||||
SeparateSpawnsInitPlayer(event.player_index)
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_player_respawned, function(event)
|
||||
SeparateSpawnsPlayerRespawned(event)
|
||||
|
||||
PlayerRespawnItems(event)
|
||||
|
||||
if global.ocfg.enable_long_reach then
|
||||
GivePlayerLongReach(game.players[event.player_index])
|
||||
end
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_player_left_game, function(event)
|
||||
ServerWriteFile("player_events", game.players[event.player_index].name .. " left the game." .. "\n")
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
-- If players leave early, say goodbye.
|
||||
if (player and (player.online_time < (global.ocfg.minimum_online_time * TICKS_PER_MINUTE))) then
|
||||
log("Player left early: " .. player.name)
|
||||
SendBroadcastMsg(player.name .. "'s base was marked for immediate clean up because they left within "..global.ocfg.minimum_online_time.." minutes of joining.")
|
||||
RemoveOrResetPlayer(player, true, true, true, true)
|
||||
end
|
||||
SeparateSpawnsPlayerLeft(event)
|
||||
end)
|
||||
|
||||
-- script.on_event(defines.events.on_player_removed, function(event)
|
||||
-- Player is already deleted when this is called.
|
||||
-- end)
|
||||
script.on_event(defines.events.on_player_changed_surface, function(event)
|
||||
SeparateSpawnsPlayerChangedSurface(event)
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- Shared chat, so you don't have to type /s
|
||||
-- But you do lose your player colors across forces.
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_console_chat, function(event)
|
||||
if (global.ocfg.gameplay.enable_shared_team_chat) then
|
||||
if (event.player_index ~= nil) then
|
||||
ShareChatBetweenForces(game.players[event.player_index], event.message)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- On tick events. Stuff that needs to happen at regular intervals.
|
||||
-- Delayed events, delayed spawns, ...
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_tick, function(event)
|
||||
if global.ocfg.enable_regrowth then
|
||||
RegrowthOnTick()
|
||||
RegrowthForceRemovalOnTick()
|
||||
end
|
||||
|
||||
DelayedSpawnOnTick()
|
||||
|
||||
if global.ocfg.enable_chest_sharing then
|
||||
SharedChestsOnTick()
|
||||
end
|
||||
|
||||
if (global.ocfg.enable_chest_sharing and global.ocfg.enable_magic_factories) then
|
||||
MagicFactoriesOnTick()
|
||||
end
|
||||
|
||||
TimeoutSpeechBubblesOnTick()
|
||||
FadeoutRenderOnTick()
|
||||
|
||||
if global.ocfg.enable_miner_decon then
|
||||
OarcAutoDeconOnTick()
|
||||
if global.ocfg.regrowth.enable_regrowth then
|
||||
RegrowthOnTick()
|
||||
end
|
||||
RegrowthForceRemovalOnTick() -- Allows for abandoned base cleanup without regrowth enabled.
|
||||
|
||||
if global.ocfg.gameplay.modified_enemy_spawning then
|
||||
RestrictEnemyEvolutionOnTick()
|
||||
end
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- Chunk Generation
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_chunk_generated, function(event)
|
||||
if global.ocfg.regrowth.enable_regrowth then
|
||||
RegrowthChunkGenerate(event)
|
||||
end
|
||||
|
||||
CreateHoldingPenChunks(event)
|
||||
SeparateSpawnsGenerateChunk(event)
|
||||
|
||||
if global.ocfg.gameplay.modified_enemy_spawning then
|
||||
DowngradeWormsDistanceBasedOnChunkGenerate(event)
|
||||
DowngradeAndReduceEnemiesOnChunkGenerate(event)
|
||||
end
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- Radar Scanning
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_sector_scanned, function (event)
|
||||
if global.ocfg.enable_regrowth then
|
||||
if global.ocfg.regrowth.enable_regrowth then
|
||||
RegrowthSectorScan(event)
|
||||
end
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- Surface Generation
|
||||
----------------------------------------
|
||||
-- This is not called when the default surface "nauvis" is created as it will always exist!
|
||||
script.on_event(defines.events.on_surface_created, function(event)
|
||||
log("Surface created: " .. game.surfaces[event.surface_index].name)
|
||||
SeparateSpawnsSurfaceCreated(event)
|
||||
RegrowthSurfaceCreated(event)
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_pre_surface_deleted, function(event)
|
||||
log("Surface deleted: " .. game.surfaces[event.surface_index].name)
|
||||
SeparateSpawnsSurfaceDeleted(event)
|
||||
RegrowthSurfaceDeleted(event)
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- Various on "built" events
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_built_entity, function(event)
|
||||
if global.ocfg.enable_autofill then
|
||||
Autofill(event)
|
||||
if global.ocfg.regrowth.enable_regrowth then
|
||||
RegrowthMarkAreaSafeGivenTilePos(event.created_entity.surface.name, event.created_entity.position, 2, false)
|
||||
end
|
||||
|
||||
if global.ocfg.enable_regrowth then
|
||||
if (event.created_entity.surface.name ~= GAME_SURFACE_NAME) then return end
|
||||
RegrowthMarkAreaSafeGivenTilePos(event.created_entity.position, 2, false)
|
||||
end
|
||||
|
||||
if global.ocfg.enable_anti_grief then
|
||||
SetItemBlueprintTimeToLive(event)
|
||||
end
|
||||
|
||||
if global.ocfg.frontier_rocket_silo then
|
||||
BuildSiloAttempt(event)
|
||||
end
|
||||
-- For tracking spidertrons...
|
||||
RegrowthOnBuiltEntity(event)
|
||||
|
||||
-- if global.ocfg.enable_anti_grief then
|
||||
-- SetItemBlueprintTimeToLive(event)
|
||||
-- end
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_robot_built_entity, function (event)
|
||||
if global.ocfg.enable_regrowth then
|
||||
if (event.created_entity.surface.name ~= GAME_SURFACE_NAME) then return end
|
||||
RegrowthMarkAreaSafeGivenTilePos(event.created_entity.position, 2, false)
|
||||
end
|
||||
if global.ocfg.frontier_rocket_silo then
|
||||
BuildSiloAttempt(event)
|
||||
if global.ocfg.regrowth.enable_regrowth then
|
||||
RegrowthMarkAreaSafeGivenTilePos(event.created_entity.surface.name, event.created_entity.position, 2, false)
|
||||
end
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_player_built_tile, function (event)
|
||||
if global.ocfg.enable_regrowth then
|
||||
if (game.surfaces[event.surface_index].name ~= GAME_SURFACE_NAME) then return end
|
||||
|
||||
for k,v in pairs(event.tiles) do
|
||||
RegrowthMarkAreaSafeGivenTilePos(v.position, 2, false)
|
||||
if global.ocfg.regrowth.enable_regrowth then
|
||||
for _,v in pairs(event.tiles) do
|
||||
RegrowthMarkAreaSafeGivenTilePos(game.surfaces[event.surface_index].name, v.position, 2, false)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--If a player gets in or out of a vehicle, mark the area as safe so we don't delete the vehicle by accident.
|
||||
--Only world eater will clean up these chunks over time if it is enabled.
|
||||
script.on_event(defines.events.on_player_driving_changed_state, function (event)
|
||||
if global.ocfg.regrowth.enable_regrowth then
|
||||
RegrowthMarkAreaSafeGivenTilePos(event.entity.surface.name, event.entity.position, 1, false)
|
||||
end
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- On script_raised_built. This should help catch mods that
|
||||
-- place items that don't count as player_built and robot_built.
|
||||
-- Specifically FARL.
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.script_raised_built, function(event)
|
||||
if global.ocfg.enable_regrowth then
|
||||
if (event.entity.surface.name ~= GAME_SURFACE_NAME) then return end
|
||||
RegrowthMarkAreaSafeGivenTilePos(event.entity.position, 2, false)
|
||||
end
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- Shared chat, so you don't have to type /s
|
||||
-- But you do lose your player colors across forces.
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_console_chat, function(event)
|
||||
if (event.player_index) then
|
||||
ServerWriteFile("server_chat", game.players[event.player_index].name .. ": " .. event.message .. "\n")
|
||||
end
|
||||
if (global.ocfg.enable_shared_chat) then
|
||||
if (event.player_index ~= nil) then
|
||||
ShareChatBetweenForces(game.players[event.player_index], event.message)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- On Research Finished
|
||||
-- This is where you can permanently remove researched techs
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_research_finished, function(event)
|
||||
|
||||
-- Never allows players to build rocket-silos in "frontier" mode.
|
||||
if global.ocfg.frontier_rocket_silo and not global.ocfg.frontier_allow_build then
|
||||
RemoveRecipe(event.research.force, "rocket-silo")
|
||||
end
|
||||
|
||||
if global.ocfg.lock_goodies_rocket_launch and
|
||||
(not global.ocore.satellite_sent or not global.ocore.satellite_sent[event.research.force.name]) then
|
||||
for _,v in ipairs(LOCKED_RECIPES) do
|
||||
RemoveRecipe(event.research.force, v.r)
|
||||
end
|
||||
if global.ocfg.regrowth.enable_regrowth then
|
||||
RegrowthMarkAreaSafeGivenTilePos(event.entity.surface.name, event.entity.position, 2, false)
|
||||
end
|
||||
end)
|
||||
|
||||
@ -379,13 +222,15 @@ end)
|
||||
-- This is where I modify biter spawning based on location and other factors.
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_entity_spawned, function(event)
|
||||
if (global.ocfg.modified_enemy_spawning) then
|
||||
ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
end
|
||||
-- if (global.ocfg.gameplay.modified_enemy_spawning) then
|
||||
-- ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
-- end
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_biter_base_built, function(event)
|
||||
if (global.ocfg.modified_enemy_spawning) then
|
||||
ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
if (global.ocfg.gameplay.modified_enemy_spawning) then
|
||||
-- ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
ChangeEnemySpawnersToOtherForceOnBuilt(event)
|
||||
end
|
||||
end)
|
||||
|
||||
@ -394,58 +239,83 @@ end)
|
||||
-- This is where I remove biter waves on offline players
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_unit_group_finished_gathering, function(event)
|
||||
if (global.ocfg.enable_offline_protect) then
|
||||
OarcModifyEnemyGroup(event.group)
|
||||
if (global.ocfg.gameplay.enable_offline_protection) then
|
||||
OarcModifyEnemyGroup(event)
|
||||
end
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- On Corpse Timed Out
|
||||
-- Save player's stuff so they don't lose it if they can't get to the corpse fast enough.
|
||||
-- Gui Events
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_character_corpse_expired, function(event)
|
||||
DropGravestoneChestFromCorpse(event.corpse)
|
||||
script.on_event(defines.events.on_gui_click, function(event)
|
||||
if not event.element.valid then return end -- Should we ever react to invalid GUI elements?
|
||||
|
||||
SeparateSpawnsGuiClick(event)
|
||||
|
||||
ClickOarcGuiButton(event)
|
||||
ServerInfoGuiClick(event)
|
||||
SpawnCtrlGuiClick(event)
|
||||
SettingsControlsTabGuiClick(event)
|
||||
SettingsSurfaceControlsTabGuiClick(event)
|
||||
end)
|
||||
|
||||
--- Called when LuaGuiElement checked state is changed (related to checkboxes and radio buttons).
|
||||
script.on_event(defines.events.on_gui_checked_state_changed, function (event)
|
||||
if not event.element.valid then return end -- Should we ever react to invalid GUI elements?
|
||||
|
||||
----------------------------------------
|
||||
-- On Gui Text Change
|
||||
-- For capturing text entry.
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_gui_text_changed, function(event)
|
||||
NotepadOnGuiTextChange(event)
|
||||
SeparateSpawnsGuiCheckedStateChanged(event)
|
||||
|
||||
SpawnCtrlGuiOptionsSelect(event)
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_gui_selected_tab_changed, function (event)
|
||||
if not event.element.valid then return end -- Should we ever react to invalid GUI elements?
|
||||
|
||||
OarcGuiSelectedTabChanged(event)
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- On Gui Closed
|
||||
-- For capturing player escaping custom GUI so we can close it using ESC key.
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_gui_closed, function(event)
|
||||
OarcGuiOnGuiClosedEvent(event)
|
||||
if global.ocfg.enable_coin_shop then
|
||||
OarcStoreOnGuiClosedEvent(event)
|
||||
end
|
||||
OarcGuiClosed(event)
|
||||
end)
|
||||
|
||||
|
||||
--- For sliders and other value changing elements.
|
||||
script.on_event(defines.events.on_gui_value_changed, function(event)
|
||||
if not event.element.valid then return end -- Should we ever react to invalid GUI elements?
|
||||
|
||||
SeparateSpawnsGuiValueChanged(event)
|
||||
SettingsControlsTabGuiValueChanged(event)
|
||||
end)
|
||||
|
||||
--- For dropdowns and listboxes.
|
||||
script.on_event(defines.events.on_gui_selection_state_changed, function(event)
|
||||
if not event.element.valid then return end -- Should we ever react to invalid GUI elements?
|
||||
|
||||
SeparateSpawnsGuiSelectionStateChanged(event)
|
||||
SettingsControlsTabGuiSelectionStateChanged(event)
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_gui_text_changed, function(event)
|
||||
if not event.element.valid then return end -- Should we ever react to invalid GUI elements?
|
||||
|
||||
SettingsControlsTabGuiTextChanged(event)
|
||||
end)
|
||||
|
||||
script.on_event(defines.events.on_gui_confirmed, function(event)
|
||||
if not event.element.valid then return end -- Should we ever react to invalid GUI elements?
|
||||
|
||||
SettingsControlsTabGuiTextconfirmed(event)
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- On enemies killed
|
||||
-- For coin generation and stuff
|
||||
-- Remote Interface
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_post_entity_died, function(event)
|
||||
if (game.surfaces[event.surface_index].name ~= GAME_SURFACE_NAME) then return end
|
||||
if global.ocfg.enable_coin_shop then
|
||||
CoinsFromEnemiesOnPostEntityDied(event)
|
||||
end
|
||||
end,
|
||||
{{filter="type", type = "unit"}, {filter="type", type = "unit-spawner"}, {filter="type", type = "turret"}})
|
||||
local oarc_mod_interface =
|
||||
{
|
||||
get_mod_settings = function()
|
||||
return OCFG
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
----------------------------------------
|
||||
-- Scripted auto decon for miners...
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_resource_depleted, function(event)
|
||||
if global.ocfg.enable_miner_decon then
|
||||
OarcAutoDeconOnResourceDepleted(event)
|
||||
end
|
||||
end)
|
||||
remote.add_interface("oarc_mod", oarc_mod_interface)
|
27
data.lua
Normal file
27
data.lua
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
-- I created a few custom entities for making it clear that the shared power poles and chests are special.
|
||||
|
||||
local oarc_linked_chest=table.deepcopy(data.raw["container"]["wooden-chest"])
|
||||
oarc_linked_chest.type="linked-container"
|
||||
oarc_linked_chest.name="oarc-linked-chest"
|
||||
oarc_linked_chest.inventory_type="with_filters_and_bar"
|
||||
oarc_linked_chest.inventory_size=settings.startup["oarc-mod-linked-chest-size"].value --[[@as integer]]
|
||||
oarc_linked_chest.picture.layers[1].filename = "__oarc-mod__/graphics/oarc-linked-chest.png"
|
||||
oarc_linked_chest.picture.layers[1].hr_version.filename = "__oarc-mod__/graphics/hr-oarc-linked-chest.png"
|
||||
|
||||
|
||||
local oarc_linked_power=table.deepcopy(data.raw["electric-pole"]["small-electric-pole"])
|
||||
oarc_linked_power.name="oarc-linked-power"
|
||||
oarc_linked_power.pictures.layers[1].filename = "__oarc-mod__/graphics/oarc-electric-pole.png"
|
||||
oarc_linked_power.pictures.layers[1].hr_version.filename = "__oarc-mod__/graphics/hr-oarc-electric-pole.png"
|
||||
|
||||
data:extend({
|
||||
{
|
||||
type = "sprite",
|
||||
name = "oarc-mod-sprite-40",
|
||||
filename = "__oarc-mod__/icon_40x40.png",
|
||||
width = 40,
|
||||
height = 40
|
||||
},
|
||||
oarc_linked_chest, oarc_linked_power
|
||||
})
|
141
devplan.txt
Normal file
141
devplan.txt
Normal file
@ -0,0 +1,141 @@
|
||||
ACTIVE ITEMS:
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
BACKLOG:
|
||||
|
||||
Not specific:
|
||||
|
||||
|
||||
Minor:
|
||||
- If dead when resetting spawn... possibly delay the opening of the welcome GUI or block spawning until character is spawned?
|
||||
- Expose old enemy scaling as an option? And/or remove unnecessary checks/logs
|
||||
- Refresh players in admin controls when dropdown is clicked
|
||||
- Add a setting for forcing primary spawns to only be on default surface maybe?
|
||||
|
||||
Performance:
|
||||
- User on_nth_tick for any tick % (modulo) operations.
|
||||
- SeparateSpawnsGenerateChunk should only search for closest spawn once and pass to sub functions!
|
||||
- Rework world eater to use less find_entities_filtered
|
||||
|
||||
Major:
|
||||
- Space Age Support (TBD)
|
||||
|
||||
|
||||
------------------------------------------------------SPACE AGE---------------------------------------------------------
|
||||
|
||||
- Map Settings changes?
|
||||
- Pollution changes (regrowth)?
|
||||
- Enemy changes?
|
||||
- Landing pad locations per FORCE limited to 1?
|
||||
- Surface names for space ships?
|
||||
- Spawner health tied to evolution?
|
||||
- Respawn position is surface specific? Each surface needs a separate respawn point? Default respawn behavior?
|
||||
- Confirm launch into scenario works (V2.0 fix supposedly) -- https://forums.factorio.com/110708
|
||||
- Radar quality affects regrowth safe range?
|
||||
- Update electric pole connections for shared power if things change in V2.0
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Other Ideas, Not Committed:
|
||||
- Add option to spawn on existing chunks (look for chunks with any entities in them, or use regrowth logic)
|
||||
- Add option for spawn pen to be on a specified surface (not sure this serves any real purpose)
|
||||
- Change "search for ungenerated spawn point" to be a "roll" button that the player can re-roll? Maybe it shows on the map in an icon where they might go first?
|
||||
- Make players join a "holding_pen" force instead of the main force? Might cause issues with chat and vision?
|
||||
- Separate chest sharing and electricity ()
|
||||
- Consider scrapping any overlapping mod settings from the config. ONLY keep the part that can't easily be done in the mod settings menu... NOT SURE about this.
|
||||
- Change enable_shared_team_vision to allow players to change this per player (like BNO)
|
||||
- Change enable_friendly_fire to be per team?
|
||||
- Allow players to spawn "near" an existing player (by request)
|
||||
- Allow players to restart at anytime via GUI button (configurable setting by admin)
|
||||
- Change regrowth to be list of surfaces indexed by surface name?
|
||||
- Figure out how to reset player inventory on player reset to avoid extra items? (save and load items?)
|
||||
- Work on space ex support?
|
||||
- Profile regrowth and try to improve performance ?
|
||||
- Possibly adjust easy/medium evo factors on new player joined?
|
||||
- Convert regrowth to a proper mod
|
||||
- Create shared electricty with a LIMITED output transfer rate using a custom accumulator?
|
||||
- Cleanup offline protection (Lots of commented out code)
|
||||
- Custom tips and tricks??
|
||||
|
||||
--------------------------------------------------------DONE------------------------------------------------------------
|
||||
|
||||
- First setup the mod settings and lua global settings.
|
||||
- Test out removing the creation of a custom surface and ensure map settings can be set as needed. Possibly create a separate surface, or just a separate area away from the origin, for starting players.
|
||||
- Start copying in the core pieces required for the primary spawning mechanic.
|
||||
- Document config and mod settings using Lua annotation as a custom class
|
||||
- Document global ocore as a custom class (with subclasses/types as needed)
|
||||
- Add multiple surfaces to options/settings [As a single boolean.]
|
||||
- Convert scenario to a mod.
|
||||
- Add multiple surfaces to Spawn GUI (Drop down selection?)
|
||||
- Configurable welcome/server messages in mod settings.
|
||||
- Check and update all functions using surfaces to clearly use either the LuaSurface obj OR a string name.
|
||||
- Create server settings admin GUI tab
|
||||
- Figure out how to define custom lua table/data structs to make syntax/linting work?
|
||||
- Setup multiplayer testing using multiple instances and some batch files.
|
||||
- Change Near/Far buttons to radio selection w/ text explanation and have a single Spawn button.
|
||||
- Refactor the spawn menu GUI (don't destroy the menu unless we need to, refresh only the elements we need to, save data to a global using tags?)
|
||||
- Remove separate buddy spawn menu?
|
||||
- Remove the shared spawn separate GUI window?
|
||||
- Redo DisplayBuddySpawnRequestMenu and DisplayBuddySpawnWaitMenu and move their events to the new event handlers
|
||||
- Refresh the spawn controls GUI when player accepts/rejects
|
||||
- Redo FindUngeneratedCoordinates to pick a random direction, and use the distance to get a starting point, instead of multiple tries.
|
||||
- Fix all GUI styling (buttons outside of content)
|
||||
- Force enable_world_eater to require enable_regrowth
|
||||
- Add warning for modifying surface settings after gameplay has started
|
||||
- Tooltips for GUI elements in spawn menu options!
|
||||
- Check all settings to see which CAN'T be changed during runtime. Possibly move these to startup.
|
||||
- FIGURE OUT CHUNK NOT IN MAP REGROWTH ISSUE!
|
||||
- Add validation for default starting surface name
|
||||
- Make server info headings consistent / Add heading for spawn controls (change button?)
|
||||
- Confirm regrowth should add new chunks when found (not only on chunk generate?)
|
||||
- Make disable main team setting work (hide radio button)
|
||||
- Support run time toggling of enable_shared_team_vision
|
||||
- Support run time toggling of enable_friendly_fire
|
||||
- Watch for chunks being generated to be able to move the player to their spawn as soon as it is done with the last chunk.
|
||||
- If this is their first spawn, give them items. Otherwise don't give new items and don't clear items either?
|
||||
- Change enable_spawning_on_other_surfaces to a start up setting?
|
||||
- Test out space expansion
|
||||
- Compare GUI mod button icons to space ex (looks better??)
|
||||
- Create surface blacklist setting.
|
||||
- Enable/Disable and show information about shared spawn based on shared spawn dropdown interactions.
|
||||
- Ensure updates to regrowth surfaces don't cause errors (update indexes)
|
||||
- Confirm regrowth does or does not delete chunks with robots in them?
|
||||
- Show the surface name in shared spawn join GUI
|
||||
- Show the surface name in the share spawn controls tab
|
||||
- Add rich text map location in spawn controls tab for current home location
|
||||
- Move sharedspawns data under unique spawns
|
||||
- "uniqueSpawns" should have a "primary" flag and be indexed by surface FIRST
|
||||
- Make respawn locations first be indexed by player, then surface
|
||||
- Offline protection re-implement!
|
||||
- Resolve regrowth issue with radars and confirm that when we mark chunks for removal, they can be refreshed still. (trace logic!)
|
||||
- Move "buddy" info to unique_spawns as well.
|
||||
- Fix search vector to use more variable vector'ing, always normalize vector, and then ensure the other reliant functions work still.
|
||||
- Add regrowth settings GUI tab? Not sure how the other settings fit in with a dedicated regrowth tab? Need to be able to enable/disable other surfaces during runtime?
|
||||
- Test multiple enemy forces to provide a way to scale evolution locally (need to continually set the evo factor back)
|
||||
- Change main_force_name to a startup setting! New players in spawn area should stay on default "player" force to avoid mod conflicts on player init?
|
||||
- In spawn controls, add a note if spawn is full (and maybe disable the shared spawn checkbox?)
|
||||
- Add rich text map location links for new spawns (print to chat)
|
||||
- Add in square bases again
|
||||
- TEST resizing spawn areas and moat sizes!
|
||||
- Create a function to create secondary uniqueSpawns for the same player
|
||||
- Expose primary AND secondary spawn info in spawn controls GUI tab
|
||||
- Shared items (proper)
|
||||
- Shared electricity (proper) (with configurable settings)
|
||||
- Add a show current respawn location button (same as show spawn location)
|
||||
- Expose some settings for adjusting easy and medium enemy evolution values
|
||||
- Add FAQ for enemy modifications
|
||||
- Add FAQ for item and energy sharing
|
||||
- FIX GetNextPlayerIndex!
|
||||
- Lots of localizations!
|
||||
- List all TO-DOs in code here.
|
||||
- Remove testing surfaces before release!
|
||||
- Change default surface selection to be the default surface if multiple are enabled
|
||||
- Test and make sure scenario settings overrides get written back to mod settings to avoid any out of sync settings.
|
||||
- Test teleporting to other surfaces
|
||||
- Test on_player_changed_surface
|
||||
- Run the profiler
|
||||
- Pull out general spawn config from surfaces config
|
||||
- Redo resource placement to be simpler (and make a linear layout for square base)
|
||||
- Default to selecting SELF in admin controls player dropdown?
|
||||
- Add refresh chunks around spidertrons based on their vision
|
@ -1,473 +0,0 @@
|
||||
-- example-config.lua (Rename this file to config.lua to use it)
|
||||
-- May 26 2020 (updated on)
|
||||
-- Configuration Options
|
||||
--
|
||||
-- You should be safe to leave most of the settings here as defaults if you want.
|
||||
-- The only thing you definitely want to change are the welcome messages.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Messages
|
||||
-- You will want to change some of these to be your own.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- This stuff is shown in the welcome GUI and Info panel. Make sure it's valid.
|
||||
WELCOME_MSG_TITLE = "[INSERT SERVER OWNER MSG HERE test title!]"
|
||||
WELCOME_MSG = "[INSERT SERVER OWNER MSG HERE test msg!]" -- Printed to player on join as well.
|
||||
SERVER_MSG = "Rules: Be polite. Ask before changing other players's stuff. Have fun!\n"..
|
||||
"This server is running a custom scenario that allows individual starting areas on the map."
|
||||
|
||||
SCENARIO_INFO_MSG = "Latest updates in this scenario version:\n"..
|
||||
"Item & energy sharing system! No attacks on your base while you are offline!\n"..
|
||||
"This scenario gives you and/or your friends your own starting area.\n"..
|
||||
"You can be on the main team or your own. All teams are friendly.\n"..
|
||||
"If you leave in the first 15 minutes, your base and character will be deleted!"
|
||||
|
||||
CONTACT_MSG = "Contact: SteamID:Oarc | oarcinae@gmail.com | Discord:Oarc#8695"
|
||||
DISCORD_INV = "discord.gg/trnpcen"
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- Module Enables
|
||||
-- Each of the following things enable special features. These can't be changed once the game starts.
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- This allows 2 players to spawn next to each other in the wilderness, each with their own starting point. It adds more
|
||||
-- GUI selection options.
|
||||
ENABLE_BUDDY_SPAWN = true
|
||||
|
||||
-- Frontier style rocket silo mode. This means you can't build silos, but some spawn out in the wild for you to use.
|
||||
-- if ENABLE_MAGIC_FACTORIES=false, you will find a few special areas to launch rockets from.
|
||||
-- If ENABLE_MAGIC_FACTORIES=true, you must buy a silo at one of the special chunks.
|
||||
FRONTIER_ROCKET_SILO_MODE = true
|
||||
|
||||
-- Enable Undecorator. Removes decorative items to reduce save file size.
|
||||
ENABLE_UNDECORATOR = true
|
||||
|
||||
-- Enable Tags (Players can add a name-tag to explain what type of role they are doing if they want.)
|
||||
ENABLE_TAGS = true
|
||||
|
||||
-- Enable Long Reach
|
||||
ENABLE_LONGREACH = true
|
||||
|
||||
-- Enable Autofill (My autofill is very simplistic, if you are using a similar mod disable this!)
|
||||
ENABLE_AUTOFILL = true
|
||||
|
||||
-- Enable auto decon of miners (My miner decon is very simplistic, if you are using a similar mod disable this!)
|
||||
ENABLE_MINER_AUTODECON = true
|
||||
|
||||
-- Enable Playerlist
|
||||
ENABLE_PLAYER_LIST = true
|
||||
PLAYER_LIST_OFFLINE_PLAYERS = true -- List offline players as well.
|
||||
|
||||
-- Enable shared vision between teams (all teams are COOP regardless)
|
||||
ENABLE_SHARED_TEAM_VISION = true
|
||||
|
||||
-- Cleans up unused chunks periodically. Helps keep map size down.
|
||||
ENABLE_REGROWTH = true
|
||||
-- This removes player bases when they leave shortly after joining. Only works if you have regrowth enabled!
|
||||
ENABLE_ABANDONED_BASE_REMOVAL = true
|
||||
|
||||
-- Enable the research queue by default for all forces.
|
||||
ENABLE_RESEARCH_QUEUE = true
|
||||
|
||||
-- This enables coin drops from enemies and a shop (GUI) to buy stuff from.
|
||||
ENABLE_COIN_SHOP = false
|
||||
|
||||
-- Enable item & energy sharing system.
|
||||
ENABLE_ITEM_AND_ENERGY_SHARING = false -- REQUIRES ENABLE_COIN_SHOP=true!
|
||||
|
||||
-- Enable magic chunks around the map that let you buy powerful factories that smelt/assemble/process very very quickly.
|
||||
ENABLE_MAGIC_FACTORIES = false -- REQUIRES ENABLE_COIN_SHOP=true!
|
||||
|
||||
-- This inhibits enemy attacks on bases where all players are offline.
|
||||
-- Not 100% guaranteed.
|
||||
ENABLE_OFFLINE_PROTECTION = true
|
||||
|
||||
-- This allows you to set the tech price multiplier for the game, but
|
||||
-- have it only affect the main force. We just pad all non-main forces lab prod bonus.
|
||||
-- This has no effect unless the tech multiplier is more than 1!
|
||||
ENABLE_FORCE_LAB_PROD_BONUS = true
|
||||
|
||||
-- Lock various recipes and technologies behind a rocket launch.
|
||||
-- Each team/force must launch their own rocket to unlock this!
|
||||
LOCK_GOODIES_UNTIL_ROCKET_LAUNCH = true
|
||||
LOCKED_TECHNOLOGIES = {
|
||||
{t="atomic-bomb"},{t="power-armor-mk2"},{t="artillery"},{t="spidertron"}
|
||||
}
|
||||
LOCKED_RECIPES = {
|
||||
{r="productivity-module-3"},{r="speed-module-3"}
|
||||
}
|
||||
|
||||
-- Give cheaty items on start.
|
||||
ENABLE_POWER_ARMOR_QUICK_START = false
|
||||
ENABLE_MODULAR_ARMOR_QUICK_START = false
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- MAP CONFIGURATION OPTIONS
|
||||
-- In past versions I had a way to config map settings here to be used for cmd
|
||||
-- line launching, but now you should just be using --map-gen-settings and
|
||||
-- --map-settings option since it works with --start-server-load-scenario
|
||||
-- Read the README.md file for instructions.
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- This scales resources so that even if you spawn "far away" from the center
|
||||
-- of the map, resources near to your spawn point scale so you aren't
|
||||
-- surrounded by 100M patches or something. This is useful depending on what
|
||||
-- map gen settings you pick.
|
||||
SCALE_RESOURCES_AROUND_SPAWNS = true
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- Alien Options
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- Adjust enemy spawning based on distance to spawns. All it does it make things
|
||||
-- more balanced based on your distance and makes the game a little easier.
|
||||
-- No behemoth worms everywhere just because you spawned far away.
|
||||
-- If you're trying out the vanilla spawning, you might want to disable this.
|
||||
OARC_MODIFIED_ENEMY_SPAWNING = true
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- Starting Items
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- Items provided to the player the first time they join
|
||||
PLAYER_SPAWN_START_ITEMS = {
|
||||
["pistol"]=1,
|
||||
["firearm-magazine"]=200,
|
||||
["iron-plate"]=100,
|
||||
["burner-mining-drill"] = 4,
|
||||
["stone-furnace"] = 4,
|
||||
["coal"] = 50,
|
||||
["stone"] = 50,
|
||||
|
||||
["coin"] = 2500, -- Don't give coins unless you have shared chests enabled.
|
||||
}
|
||||
|
||||
-- Items provided after EVERY respawn (disabled by default)
|
||||
PLAYER_RESPAWN_START_ITEMS = {
|
||||
-- ["pistol"]=1,
|
||||
-- ["firearm-magazine"]=100,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- Distance Options
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- This is the radius, in chunks, that a spawn area is from any other generated
|
||||
-- chunks. It ensures the spawn area isn't too near generated/explored/existing
|
||||
-- area. The larger you make this, the further away players will spawn from
|
||||
-- generated map area (even if it is not visible on the map!).
|
||||
CHECK_SPAWN_UNGENERATED_CHUNKS_RADIUS = 10
|
||||
|
||||
-- Near Distance in chunks
|
||||
-- When a player selects "near" spawn, they will be in or as close to this range as possible.
|
||||
NEAR_MIN_DIST = 50
|
||||
NEAR_MAX_DIST = 100
|
||||
|
||||
-- Far Distance in chunks
|
||||
-- When a player selects "far" spawn, they will be at least this distance away.
|
||||
FAR_MIN_DIST = 200
|
||||
FAR_MAX_DIST = 300
|
||||
|
||||
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- Resource & Spawn Circle Options
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- This is where you can modify what resources spawn, how much, where, etc.
|
||||
-- Once you have a config you like, it's a good idea to save it for later use
|
||||
-- so you don't lost it if you update the scenario.
|
||||
OARC_CFG = {
|
||||
|
||||
-- Misc spawn related config.
|
||||
gen_settings = {
|
||||
|
||||
-- THIS IS WHAT SETS THE SPAWN CIRCLE SIZE!
|
||||
-- Create a circle of land area for the spawn
|
||||
-- If you make this much bigger than a few chunks, good luck.
|
||||
land_area_tiles = CHUNK_SIZE*2,
|
||||
|
||||
-- Allow players to choose to spawn with a moat
|
||||
moat_choice_enabled = true,
|
||||
-- If there is a moat, this attempts to connect to land to avoid "turtling"
|
||||
moat_bridging = true,
|
||||
|
||||
-- If you change the spawn area size, you might have to adjust this as well
|
||||
moat_size_modifier = 1,
|
||||
|
||||
-- Start resource shape. true = circle, false = square.
|
||||
resources_circle_shape = true,
|
||||
|
||||
-- Force the land area circle at the spawn to be fully grass
|
||||
force_grass = true,
|
||||
|
||||
-- Spawn a circle/octagon of trees around the base outline.
|
||||
tree_circle = true,
|
||||
tree_octagon = false,
|
||||
|
||||
-- Add a crashed ship like a vanilla game (create_crash_site)
|
||||
-- Resources go in the ship itself. (5 slots)
|
||||
-- Wreakage is distributed in small pieces. (I recommend only 1 item type.)
|
||||
crashed_ship = true,
|
||||
crashed_ship_resources = {
|
||||
["electronic-circuit"] = 200,
|
||||
["iron-gear-wheel"] = 100,
|
||||
["copper-cable"] = 200,
|
||||
-- ["spidertron"] = 1,
|
||||
["steel-plate"] = 100
|
||||
},
|
||||
crashed_ship_wreakage = {
|
||||
["iron-plate"] = 100
|
||||
},
|
||||
},
|
||||
|
||||
-- Safe Spawn Area Options
|
||||
-- The default settings here are balanced for my recommended map gen settings (close to train world).
|
||||
safe_area =
|
||||
{
|
||||
-- Safe area has no aliens
|
||||
-- This is the radius in tiles of safe area.
|
||||
safe_radius = CHUNK_SIZE*6,
|
||||
|
||||
-- Warning area has significantly reduced aliens
|
||||
-- This is the radius in tiles of warning area.
|
||||
warn_radius = CHUNK_SIZE*12,
|
||||
|
||||
-- 1 : X (spawners alive : spawners destroyed) in this area
|
||||
warn_reduction = 20,
|
||||
|
||||
-- Danger area has slightly reduce aliens
|
||||
-- This is the radius in tiles of danger area.
|
||||
danger_radius = CHUNK_SIZE*32,
|
||||
|
||||
-- 1 : X (spawners alive : spawners destroyed) in this area
|
||||
danger_reduction = 5,
|
||||
},
|
||||
|
||||
-- Location of water strip (horizontal)
|
||||
water = {
|
||||
x_offset = -4,
|
||||
y_offset = -48,
|
||||
length = 8
|
||||
},
|
||||
|
||||
-- Handle placement of starting resources
|
||||
resource_rand_pos_settings =
|
||||
{
|
||||
-- Autoplace resources (randomly in circle)
|
||||
-- This will ignore the fixed x_offset/y_offset values in resource_tiles.
|
||||
-- Only works for resource_tiles at the moment, not oil patches/water.
|
||||
enabled = true,
|
||||
-- Distance from center of spawn that resources are placed.
|
||||
radius = 45,
|
||||
-- At what angle (in radians) do resources start.
|
||||
-- 0 means starts directly east.
|
||||
-- Resources are placed clockwise from there.
|
||||
angle_offset = 2.32, -- 2.32 is approx SSW.
|
||||
-- At what andle do we place the last resource.
|
||||
-- angle_offset and angle_final determine spacing and placement.
|
||||
angle_final = 4.46 -- 4.46 is approx NNW.
|
||||
},
|
||||
|
||||
-- Resource tiles
|
||||
-- If you are running with mods like bobs/angels, you'll want to customize this.
|
||||
resource_tiles =
|
||||
{
|
||||
["iron-ore"] =
|
||||
{
|
||||
amount = 1500,
|
||||
size = 18,
|
||||
x_offset = -29,
|
||||
y_offset = 16
|
||||
},
|
||||
["copper-ore"] =
|
||||
{
|
||||
amount = 1200,
|
||||
size = 18,
|
||||
x_offset = -28,
|
||||
y_offset = -3
|
||||
},
|
||||
["stone"] =
|
||||
{
|
||||
amount = 1200,
|
||||
size = 16,
|
||||
x_offset = -27,
|
||||
y_offset = -34
|
||||
},
|
||||
["coal"] =
|
||||
{
|
||||
amount = 1200,
|
||||
size = 16,
|
||||
x_offset = -27,
|
||||
y_offset = -20
|
||||
}--,
|
||||
-- ["uranium-ore"] =
|
||||
-- {
|
||||
-- amount = 0,
|
||||
-- size = 0,
|
||||
-- x_offset = 17,
|
||||
-- y_offset = -34
|
||||
-- }
|
||||
|
||||
-- ####### Bobs + Angels #######
|
||||
-- DISABLE STARTING OIL PATCHES!
|
||||
-- Coal = coal
|
||||
-- Saphirite = angels-ore1
|
||||
-- Stiratite = angels-ore3
|
||||
-- Rubyte = angels-ore5
|
||||
-- Bobmonium = angels-ore6
|
||||
|
||||
-- ########## Bobs Ore ##########
|
||||
-- Iron = iron-ore
|
||||
-- Copper = copper-ore
|
||||
-- Coal = coal
|
||||
-- Stone = stone
|
||||
-- Tin = tin-ore
|
||||
-- Lead (Galena) = lead-ore
|
||||
|
||||
-- See https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn/issues/11#issuecomment-479724909
|
||||
-- for full examples.
|
||||
},
|
||||
|
||||
-- Special resource patches like oil
|
||||
resource_patches =
|
||||
{
|
||||
["crude-oil"] =
|
||||
{
|
||||
num_patches = 2,
|
||||
amount = 900000,
|
||||
x_offset_start = -3,
|
||||
y_offset_start = 48,
|
||||
x_offset_next = 6,
|
||||
y_offset_next = 0
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
---------------------------------------
|
||||
-- Other Forces/Teams Options
|
||||
---------------------------------------
|
||||
|
||||
-- Separate teams
|
||||
-- This allows you to join your own force/team. Everyone is still COOP/PvE, all
|
||||
-- teams are friendly and cease-fire.
|
||||
ENABLE_SEPARATE_TEAMS = true
|
||||
|
||||
-- Main force is what default players join
|
||||
MAIN_FORCE = "Main Force"
|
||||
|
||||
-- Enable if players can allow others to join their base.
|
||||
-- And specify how many including the host are allowed.
|
||||
ENABLE_SHARED_SPAWNS = true
|
||||
MAX_PLAYERS_AT_SHARED_SPAWN = 3
|
||||
|
||||
-- Share local team chat with all teams
|
||||
-- This makes it so you don't have to use /s
|
||||
-- But it also means you can't talk privately with your own team.
|
||||
ENABLE_SHARED_TEAM_CHAT = true
|
||||
|
||||
---------------------------------------
|
||||
-- Special Action Cooldowns
|
||||
---------------------------------------
|
||||
RESPAWN_COOLDOWN_IN_MINUTES = 15
|
||||
|
||||
-- Require playes to be online for at least X minutes
|
||||
-- Else their character is removed and their spawn point is freed up for use
|
||||
MIN_ONLINE_TIME_IN_MINUTES = 15
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Frontier Rocket Silo Options
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Number of silos found in the wild.
|
||||
-- These will spawn in a circle at given distance from the center of the map
|
||||
-- If you set this number too high, you'll have a lot of delay at the start of the game.
|
||||
SILO_NUM_SPAWNS = 5
|
||||
|
||||
-- How many chunks away from the center of the map should the silo be spawned
|
||||
SILO_CHUNK_DISTANCE = 200
|
||||
|
||||
-- If this is enabled, you get silos at the positions specified below.
|
||||
-- (The other settings above are ignored in this case.)
|
||||
SILO_FIXED_POSITION = false
|
||||
|
||||
-- If you want to set fixed spawn locations for some silos.
|
||||
SILO_POSITIONS = {{x = -1000, y = -1000},
|
||||
{x = -1000, y = 1000},
|
||||
{x = 1000, y = -1000},
|
||||
{x = 1000, y = 1000}}
|
||||
|
||||
-- Set this to false so that you have to search for the silo's.
|
||||
ENABLE_SILO_VISION = true
|
||||
|
||||
-- Add beacons around the silo (Philip's mod)
|
||||
ENABLE_SILO_BEACONS = false
|
||||
ENABLE_SILO_RADAR = false
|
||||
|
||||
-- Allow silos to be built by the player, but forces them to build in
|
||||
-- the fixed locations. If this is false, silos are built and assigned
|
||||
-- only to the main force. This can cause a problem for non main forces
|
||||
-- when playing with LOCK_GOODIES_UNTIL_ROCKET_LAUNCH enabled.
|
||||
ENABLE_SILO_PLAYER_BUILD = true
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Long Reach Options
|
||||
--------------------------------------------------------------------------------
|
||||
BUILD_DIST_BONUS = 64
|
||||
REACH_DIST_BONUS = BUILD_DIST_BONUS
|
||||
RESOURCE_DIST_BONUS = 2
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Autofill Options
|
||||
--------------------------------------------------------------------------------
|
||||
AUTOFILL_TURRET_AMMO_QUANTITY = 10
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- ANTI-Griefing stuff ( I don't personally maintain this as I don't care for it.)
|
||||
-- These things were added from other people's requests/changes and are disabled by default.
|
||||
--------------------------------------------------------------------------------
|
||||
-- Enable this to disable deconstructing from map view, and setting a time limit
|
||||
-- on ghost placements.
|
||||
ENABLE_ANTI_GRIEFING = false
|
||||
|
||||
-- Makes blueprint ghosts dissapear if they have been placed longer than this
|
||||
-- ONLY has an effect if ENABLE_ANTI_GRIEFING is true!
|
||||
GHOST_TIME_TO_LIVE = 10 * TICKS_PER_MINUTE
|
||||
|
||||
-- I like keeping this off... set to true if you want to shoot your own chests
|
||||
-- and stuff.
|
||||
ENABLE_FRIENDLY_FIRE = false
|
||||
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
-- EXPERIMENTAL FEATURES
|
||||
-- The following things are not recommended unless you really know what you are doing and are okay with crashes and
|
||||
-- editing lua code.
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- This turns on writing chat and certain events to specific files so that I can use that for discord integration. I
|
||||
-- suggest you leave this off unless you know what you are doing.
|
||||
ENABLE_SERVER_WRITE_FILES = false
|
||||
|
||||
-- Enable this to have a vanilla style starting spawn. This changes the experience pretty drastically. If you enable
|
||||
-- this, you will NOT get the option to spawn using the "pre-fab" fixed layout spawns. This is because the spawn types
|
||||
-- just don't balance well with each other.
|
||||
ENABLE_VANILLA_SPAWNS = false
|
||||
|
||||
-- Vanilla spawn point options (only applicable if ENABLE_VANILLA_SPAWNS is enabled.)
|
||||
|
||||
-- Num total spawns pre-assigned (minimum number)
|
||||
-- Points are in an even grid layout.
|
||||
VANILLA_SPAWN_COUNT = 60
|
||||
|
||||
-- Num tiles between each spawn. (I recommend at least 1000)
|
||||
VANILLA_SPAWN_SPACING = 2000
|
||||
|
||||
-- Silo Islands
|
||||
-- This options is only valid when used with ENABLE_VANILLA_SPAWNS and FRONTIER_ROCKET_SILO_MODE!
|
||||
-- This spreads out rocket silos on every OTHER island/vanilla spawn
|
||||
SILO_ISLANDS_MODE = false
|
||||
|
||||
-- This is part of regrowth, and if both are enabled, any chunks which aren't active and have no entities will
|
||||
-- eventually be deleted over time. DO NOT USE THIS WITH MODS!
|
||||
ENABLE_WORLD_EATER = false
|
@ -10,14 +10,16 @@
|
||||
"water": 1,
|
||||
|
||||
"_comment_width+height": "Width and height of map, in tiles; 0 means infinite",
|
||||
"width": 0,
|
||||
"height": 0,
|
||||
"width": 1,
|
||||
"height": 1,
|
||||
|
||||
"_starting_area_comment": "Multiplier for 'biter free zone radius'",
|
||||
"starting_area": 1,
|
||||
|
||||
"peaceful_mode": false,
|
||||
|
||||
"default_enable_all_autoplace_controls": true,
|
||||
|
||||
"autoplace_controls":
|
||||
{
|
||||
"coal" : {"frequency" : 0.20, "richness" : 10.00, "size" : 0.20},
|
||||
|
@ -3,7 +3,7 @@
|
||||
{
|
||||
"recipe_difficulty": 0,
|
||||
"technology_difficulty": 0,
|
||||
"technology_price_multiplier": 3,
|
||||
"technology_price_multiplier": 1,
|
||||
"research_queue_setting": "always"
|
||||
},
|
||||
"pollution":
|
||||
@ -113,7 +113,8 @@
|
||||
"min_steps_to_check_path_find_termination": 2000,
|
||||
"start_to_goal_cost_multiplier_to_terminate_path_find": 500.0,
|
||||
"overload_levels": [0, 100, 500],
|
||||
"overload_multipliers": [2, 3, 4]
|
||||
"overload_multipliers": [2, 3, 4],
|
||||
"negative_path_cache_delay_interval": 20
|
||||
},
|
||||
"max_failed_behavior_count": 3
|
||||
}
|
||||
|
BIN
graphics/hr-oarc-electric-pole.png
Normal file
BIN
graphics/hr-oarc-electric-pole.png
Normal file
Binary file not shown.
After (image error) Size: 56 KiB |
BIN
graphics/hr-oarc-linked-chest.png
Normal file
BIN
graphics/hr-oarc-linked-chest.png
Normal file
Binary file not shown.
After (image error) Size: 22 KiB |
BIN
graphics/oarc-electric-pole.png
Normal file
BIN
graphics/oarc-electric-pole.png
Normal file
Binary file not shown.
After (image error) Size: 23 KiB |
BIN
graphics/oarc-linked-chest.png
Normal file
BIN
graphics/oarc-linked-chest.png
Normal file
Binary file not shown.
After (image error) Size: 14 KiB |
BIN
icon_40x40.png
Normal file
BIN
icon_40x40.png
Normal file
Binary file not shown.
After (image error) Size: 13 KiB |
19
info.json
Normal file
19
info.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "oarc-mod",
|
||||
"version": "2.0.0",
|
||||
"factorio_version": "1.1",
|
||||
"title": "Oarc Multiplayer Spawn",
|
||||
"author": "Oarcinae",
|
||||
"contact": "Mod Discussion Page, oarcinae@gmail.com, Discord:oarc",
|
||||
"homepage": "https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn",
|
||||
"description": "[[Description is in locale file!]]",
|
||||
"dependencies": [
|
||||
"base >= 1.1.0",
|
||||
"(?) dangOreus" ,
|
||||
"(?) Milestones",
|
||||
"(?) space-exploration",
|
||||
"(?) alien-biomes",
|
||||
"(?) sonaxaton-research-queue",
|
||||
"(?) helmod"
|
||||
]
|
||||
}
|
@ -1,153 +0,0 @@
|
||||
-- admin_commands.lua
|
||||
-- May 2019
|
||||
--
|
||||
-- Yay, admin commands!
|
||||
|
||||
require("lib/oarc_utils")
|
||||
|
||||
-- name :: string: Name of the command.
|
||||
-- tick :: uint: Tick the command was used.
|
||||
-- player_index :: uint (optional): The player who used the command. It will be missing if run from the server console.
|
||||
-- parameter :: string (optional): The parameter passed after the command, separated from the command by 1 space.
|
||||
|
||||
-- Give yourself or another player, power armor
|
||||
commands.add_command("give-power-armor-kit", "give a start kit", function(command)
|
||||
|
||||
local player = game.players[command.player_index]
|
||||
local target = player
|
||||
|
||||
if player ~= nil and player.admin then
|
||||
if (command.parameter ~= nil) then
|
||||
if game.players[command.parameter] ~= nil then
|
||||
target = game.players[command.parameter]
|
||||
else
|
||||
target.print("Invalid player target. Double check the player name?")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
GiveQuickStartPowerArmor(target)
|
||||
player.print("Gave a powerstart kit to " .. target.name)
|
||||
target.print("You have been given a power armor starting kit!")
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
commands.add_command("give-test-kit", "give a start kit", function(command)
|
||||
|
||||
local player = game.players[command.player_index]
|
||||
local target = player
|
||||
|
||||
if player ~= nil and player.admin then
|
||||
if (command.parameter ~= nil) then
|
||||
if game.players[command.parameter] ~= nil then
|
||||
target = game.players[command.parameter]
|
||||
else
|
||||
target.print("Invalid player target. Double check the player name?")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
GiveTestKit(target)
|
||||
player.print("Gave a test kit to " .. target.name)
|
||||
target.print("You have been given a test kit!")
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
commands.add_command("load-quickbar", "Pre-load quickbar shortcuts", function(command)
|
||||
|
||||
local p = game.players[command.player_index]
|
||||
|
||||
-- 1st Row
|
||||
p.set_quick_bar_slot(1, "transport-belt");
|
||||
p.set_quick_bar_slot(2, "small-electric-pole");
|
||||
p.set_quick_bar_slot(3, "inserter");
|
||||
p.set_quick_bar_slot(4, "underground-belt");
|
||||
p.set_quick_bar_slot(5, "splitter");
|
||||
|
||||
p.set_quick_bar_slot(6, "coal");
|
||||
p.set_quick_bar_slot(7, "repair-pack");
|
||||
p.set_quick_bar_slot(8, "gun-turret");
|
||||
p.set_quick_bar_slot(9, "stone-wall");
|
||||
p.set_quick_bar_slot(10, "radar");
|
||||
|
||||
-- 2nd Row
|
||||
p.set_quick_bar_slot(11, "stone-furnace");
|
||||
p.set_quick_bar_slot(12, "wooden-chest");
|
||||
p.set_quick_bar_slot(13, "steel-chest");
|
||||
p.set_quick_bar_slot(14, "assembling-machine-1");
|
||||
p.set_quick_bar_slot(15, "assembling-machine-2");
|
||||
|
||||
p.set_quick_bar_slot(16, nil);
|
||||
p.set_quick_bar_slot(17, nil);
|
||||
p.set_quick_bar_slot(18, nil);
|
||||
p.set_quick_bar_slot(19, nil);
|
||||
p.set_quick_bar_slot(20, nil);
|
||||
|
||||
-- 3rd Row
|
||||
p.set_quick_bar_slot(21, "electric-mining-drill");
|
||||
p.set_quick_bar_slot(22, "fast-inserter");
|
||||
p.set_quick_bar_slot(23, "long-handed-inserter");
|
||||
p.set_quick_bar_slot(24, "medium-electric-pole");
|
||||
p.set_quick_bar_slot(25, "big-electric-pole");
|
||||
|
||||
p.set_quick_bar_slot(26, "stack-inserter");
|
||||
p.set_quick_bar_slot(27, nil);
|
||||
p.set_quick_bar_slot(28, nil);
|
||||
p.set_quick_bar_slot(29, nil);
|
||||
p.set_quick_bar_slot(30, nil);
|
||||
|
||||
-- 4th Row
|
||||
p.set_quick_bar_slot(31, "fast-transport-belt");
|
||||
p.set_quick_bar_slot(32, "medium-electric-pole");
|
||||
p.set_quick_bar_slot(33, "fast-inserter");
|
||||
p.set_quick_bar_slot(34, "fast-underground-belt");
|
||||
p.set_quick_bar_slot(35, "fast-splitter");
|
||||
|
||||
p.set_quick_bar_slot(36, "stone-wall");
|
||||
p.set_quick_bar_slot(37, "repair-pack");
|
||||
p.set_quick_bar_slot(38, "gun-turret");
|
||||
p.set_quick_bar_slot(39, "laser-turret");
|
||||
p.set_quick_bar_slot(40, "radar");
|
||||
|
||||
-- 5th Row
|
||||
p.set_quick_bar_slot(41, "train-stop");
|
||||
p.set_quick_bar_slot(42, "rail-signal");
|
||||
p.set_quick_bar_slot(43, "rail-chain-signal");
|
||||
p.set_quick_bar_slot(44, "rail");
|
||||
p.set_quick_bar_slot(45, "big-electric-pole");
|
||||
|
||||
p.set_quick_bar_slot(46, "locomotive");
|
||||
p.set_quick_bar_slot(47, "cargo-wagon");
|
||||
p.set_quick_bar_slot(48, "fluid-wagon");
|
||||
p.set_quick_bar_slot(49, "pump");
|
||||
p.set_quick_bar_slot(50, "storage-tank");
|
||||
|
||||
-- 6th Row
|
||||
p.set_quick_bar_slot(51, "oil-refinery");
|
||||
p.set_quick_bar_slot(52, "chemical-plant");
|
||||
p.set_quick_bar_slot(53, "storage-tank");
|
||||
p.set_quick_bar_slot(54, "pump");
|
||||
p.set_quick_bar_slot(55, nil);
|
||||
|
||||
p.set_quick_bar_slot(56, "pipe");
|
||||
p.set_quick_bar_slot(57, "pipe-to-ground");
|
||||
p.set_quick_bar_slot(58, "assembling-machine-2");
|
||||
p.set_quick_bar_slot(59, "pump");
|
||||
p.set_quick_bar_slot(60, nil);
|
||||
|
||||
-- 7th Row
|
||||
p.set_quick_bar_slot(61, "roboport");
|
||||
p.set_quick_bar_slot(62, "logistic-chest-storage");
|
||||
p.set_quick_bar_slot(63, "logistic-chest-passive-provider");
|
||||
p.set_quick_bar_slot(64, "logistic-chest-requester");
|
||||
p.set_quick_bar_slot(65, "logistic-chest-buffer");
|
||||
|
||||
p.set_quick_bar_slot(66, "logistic-chest-active-provider");
|
||||
p.set_quick_bar_slot(67, "logistic-robot");
|
||||
p.set_quick_bar_slot(68, "construction-robot");
|
||||
p.set_quick_bar_slot(69, nil);
|
||||
p.set_quick_bar_slot(70, nil);
|
||||
|
||||
end)
|
@ -1,43 +0,0 @@
|
||||
-- auto_decon_miners.lua
|
||||
-- May 2020
|
||||
-- My shitty softmod version which is buggy
|
||||
|
||||
function OarcAutoDeconOnInit(event)
|
||||
if (not global.oarc_decon_miners) then
|
||||
global.oarc_decon_miners = {}
|
||||
end
|
||||
end
|
||||
|
||||
function OarcAutoDeconOnTick()
|
||||
if (global.oarc_decon_miners and (#global.oarc_decon_miners > 0)) then
|
||||
for i,miner in pairs(global.oarc_decon_miners) do
|
||||
if ((not miner) or (not miner.valid)) then
|
||||
table.remove(global.oarc_decon_miners, i)
|
||||
|
||||
else
|
||||
if (#miner.surface.find_entities_filtered{area = {{miner.position.x-3, miner.position.y-3},
|
||||
{miner.position.x+3, miner.position.y+3}},
|
||||
type = "resource", limit = 1} == 0) then
|
||||
miner.order_deconstruction(miner.force)
|
||||
end
|
||||
table.remove(global.oarc_decon_miners, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function OarcAutoDeconOnResourceDepleted(event)
|
||||
if (not global.oarc_decon_miners) then
|
||||
global.oarc_decon_miners = {}
|
||||
end
|
||||
if (event.entity and event.entity.position and event.entity.surface) then
|
||||
|
||||
local nearby_miners = event.entity.surface.find_entities_filtered{area = {{event.entity.position.x-1, event.entity.position.y-1},
|
||||
{event.entity.position.x+1, event.entity.position.y+1}},
|
||||
name = {"burner-mining-drill", "electric-mining-drill"}}
|
||||
|
||||
for i,v in pairs(nearby_miners) do
|
||||
table.insert(global.oarc_decon_miners, v)
|
||||
end
|
||||
end
|
||||
end
|
519
lib/config.lua
Normal file
519
lib/config.lua
Normal file
@ -0,0 +1,519 @@
|
||||
--[[
|
||||
__ __ ___ ___ __ ___ ___ _ _ ___ _ _ ___
|
||||
| \/ |/ _ \| \ \ \ / /_\ | _ \ \| |_ _| \| |/ __|
|
||||
| |\/| | (_) | |) | \ \/\/ / _ \| / .` || || .` | (_ |
|
||||
|_| |_|\___/|___/ \_/\_/_/ \_\_|_\_|\_|___|_|\_|\___|
|
||||
|
||||
DO NOT EDIT THIS FILE!
|
||||
DO NOT EDIT THIS FILE!
|
||||
DO NOT EDIT THIS FILE!
|
||||
|
||||
If you want to customize settings at init, edit the control.lua file in the scenarios/OARC folder and load that scenario!
|
||||
|
||||
]]
|
||||
|
||||
-- More settings are available here than are provided in the mod settings menu.
|
||||
-- Additionally, many settings are exposed in the game itself and can be changed in game.
|
||||
-- For convenience you, you can edit the control.lua file in the scenarios/OARC folder to override settings as well.
|
||||
-- That method is useful for headless server hosting where it's hard to configure mod settings.
|
||||
|
||||
---@alias SpawnShapeChoice "circle" | "octagon" | "square"
|
||||
SPAWN_SHAPE_CHOICE_CIRCLE = "circle"
|
||||
SPAWN_SHAPE_CHOICE_OCTAGON = "octagon"
|
||||
SPAWN_SHAPE_CHOICE_SQUARE = "square"
|
||||
|
||||
---@alias SpawnResourcesShapeChoice "circle" | "square"
|
||||
RESOURCES_SHAPE_CHOICE_CIRCLE = "circle"
|
||||
RESOURCES_SHAPE_CHOICE_SQUARE = "square"
|
||||
|
||||
---@type OarcConfigStartingItems
|
||||
NAUVIS_STARTER_ITEMS =
|
||||
{
|
||||
player_start_items = {
|
||||
["pistol"]=1,
|
||||
["firearm-magazine"]=200,
|
||||
["iron-plate"]=100,
|
||||
["burner-mining-drill"] = 4,
|
||||
["stone-furnace"] = 4,
|
||||
["coal"] = 50,
|
||||
["stone"] = 50,
|
||||
},
|
||||
player_respawn_items = {
|
||||
-- ["pistol"]=1,
|
||||
-- ["firearm-magazine"]=100,
|
||||
},
|
||||
|
||||
crashed_ship = true,
|
||||
crashed_ship_resources = {
|
||||
["electronic-circuit"] = 200,
|
||||
["iron-gear-wheel"] = 100,
|
||||
["copper-cable"] = 200,
|
||||
["steel-plate"] = 100
|
||||
},
|
||||
crashed_ship_wreakage = {
|
||||
["iron-plate"] = 100 -- I don't recommend more than 1 item type here!
|
||||
},
|
||||
}
|
||||
|
||||
---@type OarcConfigSpawn
|
||||
NAUVIS_SPAWN_CONFIG =
|
||||
{
|
||||
-- Safe Spawn Area Options
|
||||
-- The default settings here are balanced for my recommended map gen settings (close to train world).
|
||||
safe_area =
|
||||
{
|
||||
-- Safe area has no aliens
|
||||
-- This is the radius in tiles of safe area.
|
||||
safe_radius = CHUNK_SIZE*6,
|
||||
|
||||
-- Warning area has significantly reduced aliens
|
||||
-- This is the radius in tiles of warning area.
|
||||
warn_radius = CHUNK_SIZE*12,
|
||||
|
||||
-- 1 : X (spawners alive : spawners destroyed) in this area
|
||||
warn_reduction = 20,
|
||||
|
||||
-- Danger area has slightly reduced aliens
|
||||
-- This is the radius in tiles of danger area.
|
||||
danger_radius = CHUNK_SIZE*32,
|
||||
|
||||
-- 1 : X (spawners alive : spawners destroyed) in this area
|
||||
danger_reduction = 5,
|
||||
},
|
||||
|
||||
-- Location of water strip within the spawn area (2 horizontal rows)
|
||||
-- The offset is from the TOP (NORTH) of the spawn area.
|
||||
water = {
|
||||
x_offset = -4,
|
||||
y_offset = 10,
|
||||
length = 8,
|
||||
},
|
||||
|
||||
-- Location of shared power pole within the spawn area (if enabled)
|
||||
-- The offset is from the RIGHT (WEST) of the spawn area.
|
||||
shared_power_pole_position = {
|
||||
x_offset=-10,
|
||||
y_offset=0
|
||||
},
|
||||
|
||||
-- Location of shared chest within the spawn area (if enabled)
|
||||
-- The offset is from the RIGHT (WEST) of the spawn area.
|
||||
shared_chest_position = {
|
||||
x_offset=-10,
|
||||
y_offset=1
|
||||
},
|
||||
|
||||
-- Solid resource tiles
|
||||
-- If you are running with mods that add or change resources, you'll want to customize this.
|
||||
-- Offsets only are applicable if auto placement is disabled. Offsets are from CENTER of spawn area.
|
||||
solid_resources = {
|
||||
["iron-ore"] = {
|
||||
amount = 1500,
|
||||
size = 21,
|
||||
x_offset = -29,
|
||||
y_offset = 16
|
||||
},
|
||||
["copper-ore"] = {
|
||||
amount = 1200,
|
||||
size = 21,
|
||||
x_offset = -28,
|
||||
y_offset = -3
|
||||
},
|
||||
["stone"] = {
|
||||
amount = 1200,
|
||||
size = 21,
|
||||
x_offset = -27,
|
||||
y_offset = -34
|
||||
},
|
||||
["coal"] = {
|
||||
amount = 1200,
|
||||
size = 21,
|
||||
x_offset = -27,
|
||||
y_offset = -20
|
||||
}
|
||||
},
|
||||
|
||||
-- Fluid resource patches like oil
|
||||
-- If you are running with mods that add or change resources, you'll want to customize this.
|
||||
-- The offset is from the BOTTOM (SOUTH) of the spawn area.
|
||||
fluid_resources =
|
||||
{
|
||||
["crude-oil"] =
|
||||
{
|
||||
num_patches = 2,
|
||||
amount = 900000,
|
||||
-- Starting position offset (relative to bottom/south of spawn area)
|
||||
x_offset_start = -3,
|
||||
y_offset_start = -10,
|
||||
-- Additional position offsets for each new oil patch (relative to previous oil patch)
|
||||
x_offset_next = 6,
|
||||
y_offset_next = 0
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
---@type OarcConfigSurface
|
||||
NAUVIS_SURFACE_CONFIG =
|
||||
{
|
||||
starting_items = NAUVIS_STARTER_ITEMS,
|
||||
spawn_config = NAUVIS_SPAWN_CONFIG
|
||||
}
|
||||
|
||||
---@type OarcConfig
|
||||
OCFG = {
|
||||
|
||||
-- Server Info - This stuff is shown in the welcome GUI and Info panel.
|
||||
---@type OarcConfigServerInfo
|
||||
server_info = {
|
||||
welcome_msg_title = "Insert Server Title Here!",
|
||||
welcome_msg = "Insert Server Welcome Message Here!",
|
||||
discord_invite = "Insert Discord Invite Here!"
|
||||
},
|
||||
|
||||
-- General gameplay related settings that I didn't want to expose in the mod settings since these should
|
||||
-- basically always be enabled unless you're making serious changes.
|
||||
gameplay = {
|
||||
|
||||
-- Default setting for enabling spawning on other surfaces other than the default_surface.
|
||||
-- This is a STARTUP setting, so it can't be changed in game!!
|
||||
-- This is a STARTUP setting, so it can't be changed in game!!
|
||||
default_allow_spawning_on_other_surfaces = true,
|
||||
|
||||
-- The name of the main force.
|
||||
-- This is a STARTUP setting, so it can't be changed in game!!
|
||||
-- This is a STARTUP setting, so it can't be changed in game!!
|
||||
main_force_name = "Main Force",
|
||||
|
||||
-- At least one of these must be enabled! (enable_main_team and enable_separate_teams)
|
||||
-- Otherwise we default to enable_main_team = true
|
||||
-- Allow all players to join a primary force(team).
|
||||
enable_main_team = true,
|
||||
|
||||
-- Allow players to create their own force(team).
|
||||
enable_separate_teams = true,
|
||||
|
||||
-- Allow players to choose to spawn with a moat
|
||||
allow_moats_around_spawns = true,
|
||||
|
||||
-- If there is a moat, this makes a small path to land to avoid "turtling", but if the spawn
|
||||
-- is in the middle of water, it won't do anything.
|
||||
enable_moat_bridging = false,
|
||||
|
||||
-- This is the radius, in chunks, that a spawn area is from any other generated
|
||||
-- chunks. It ensures the spawn area isn't too near generated/explored/existing
|
||||
-- area. The larger you make this, the further away players will spawn from
|
||||
-- generated map area (even if it is not visible on the map!).
|
||||
minimum_distance_to_existing_chunks = 10,
|
||||
|
||||
-- The range in which a player can select how close to the center of the map they want to spawn.
|
||||
near_spawn_distance = 100,
|
||||
far_spawn_distance = 500,
|
||||
|
||||
-- This allows 2 players to spawn next to each other, each with their own starting area.
|
||||
enable_buddy_spawn = true,
|
||||
|
||||
-- This inhibits enemy attacks on bases where all players are offline. Not 100% guaranteed!
|
||||
enable_offline_protection = true,
|
||||
|
||||
-- Enable shared vision between teams (all teams are COOP regardless)
|
||||
enable_shared_team_vision = true,
|
||||
|
||||
-- Share local team chat with all teams
|
||||
-- This makes it so you don't have to use /s
|
||||
-- But it also means you can't talk privately with your own team.
|
||||
enable_shared_team_chat = true,
|
||||
|
||||
-- Enable if players can allow others to join their base.
|
||||
-- And specify how many including the host are allowed.
|
||||
enable_shared_spawns = true,
|
||||
number_of_players_per_shared_spawn = 3,
|
||||
|
||||
-- I like keeping this off... set to true if you want to shoot your own chests and stuff.
|
||||
enable_friendly_fire = false,
|
||||
|
||||
-- The default starting surface.
|
||||
default_surface = "nauvis",
|
||||
|
||||
-- Enable secondary spawns for players.
|
||||
-- This automatically creates a new spawn point when they first move to a separate spawns enabled surface.
|
||||
enable_secondary_spawns = false,
|
||||
|
||||
-- This scales resources so that even if you spawn "far away" from the center
|
||||
-- of the map, resources near to your spawn point scale so you aren't
|
||||
-- surrounded by 100M patches or something. This is useful depending on what
|
||||
-- map gen settings you pick.
|
||||
scale_resources_around_spawns = true,
|
||||
|
||||
-- Adjust enemy spawning based on distance to spawns. All it does it make things
|
||||
-- more balanced based on your distance and makes the game a little easier.
|
||||
-- No behemoth worms everywhere just because you spawned far away.
|
||||
-- If you're trying out the vanilla spawning, you might want to disable this.
|
||||
modified_enemy_spawning = true,
|
||||
|
||||
-- Enemy evolution factor for the easy force (inside warning area).
|
||||
modified_enemy_easy_evo = 0.0,
|
||||
|
||||
-- Enemy evolution factor for the medium force (inside danger area).
|
||||
modified_enemy_medium_evo = 0.3,
|
||||
|
||||
-- Require playes to be online for at least X minutes
|
||||
-- Else their character is removed and their spawn point is freed up for use
|
||||
minimum_online_time = 15,
|
||||
|
||||
-- Respawn cooldown in minutes.
|
||||
respawn_cooldown_min = 5,
|
||||
|
||||
-- Enable shared power between bases.
|
||||
-- Creates a special power pole for cross surface connections.
|
||||
enable_shared_power = false,
|
||||
|
||||
-- Enables a single shared chest using the native linked-chest entity in factorio.
|
||||
enable_shared_chest = false,
|
||||
},
|
||||
|
||||
-- This is a separate feature that is part of the mod that helps keep the map size down. Not required but useful.
|
||||
regrowth = {
|
||||
-- Cleans up unused chunks periodically. Helps keep map size down.
|
||||
-- See description in regrowth_map.lua for more details.
|
||||
enable_regrowth = false,
|
||||
|
||||
-- This is part of regrowth, and if both are enabled, any chunks which aren't active and have no entities
|
||||
-- will eventually be deleted over time. If this is disabled, any chunk with a player built entity will be
|
||||
-- marked permanently safe even if it is removed at a later time.
|
||||
-- DO NOT USE THIS WITH MODS! (unless you know what you're doing?)
|
||||
enable_world_eater = false,
|
||||
|
||||
-- This removes player bases when they leave shortly after joining.
|
||||
enable_abandoned_base_cleanup = true,
|
||||
|
||||
-- This is the interval in minutes that the regrowth cleanup will run.
|
||||
cleanup_interval = 60,
|
||||
},
|
||||
|
||||
-- General spawn settings (size, shape, etc.)
|
||||
spawn_general = {
|
||||
|
||||
-- Create a circle of land area for the spawn
|
||||
-- If you make this much bigger than a few chunks, good luck!
|
||||
-- (It takes a long time to generate new chunks!)
|
||||
spawn_radius_tiles = CHUNK_SIZE*2,
|
||||
|
||||
-- Width of the moat around the spawn area.
|
||||
-- If you change the spawn area size, you might have to adjust this as well.
|
||||
moat_width_tiles = 8,
|
||||
|
||||
-- Width of the tree ring around the spawn area.
|
||||
-- If you change the spawn area size, you might have to adjust this as well.
|
||||
tree_width_tiles = 5,
|
||||
|
||||
-- Starting resources deposits shape.
|
||||
resources_shape = RESOURCES_SHAPE_CHOICE_CIRCLE,
|
||||
|
||||
-- Force the land area circle at the spawn to be fully grass, otherwise it defaults to the existing terrain
|
||||
-- or uses landfill.
|
||||
force_grass = false,
|
||||
|
||||
-- Spawn a circle/octagon/square of trees around this base outline.
|
||||
shape = SPAWN_SHAPE_CHOICE_CIRCLE,
|
||||
},
|
||||
|
||||
-- Handle placement of starting resources within the spawn area.
|
||||
resource_placement =
|
||||
{
|
||||
-- Autoplace resources (randomly in circle)
|
||||
-- This will ignore the fixed x_offset/y_offset values in solid_resources.
|
||||
-- Only works for solid_resources at the moment, not oil patches/water.
|
||||
enabled = true,
|
||||
|
||||
-- Distance in tiles from the edge of spawn that resources are placed. Only applicable for circular spawns.
|
||||
distance_to_edge = 20,
|
||||
|
||||
-- At what angle (in radians) do resources start.
|
||||
-- 0 means starts directly east.
|
||||
-- Resources are placed clockwise from there.
|
||||
angle_offset = 2.32, -- 2.32 is approx SSW.
|
||||
|
||||
-- At what andle do we place the last resource.
|
||||
-- angle_offset and angle_final determine spacing and placement.
|
||||
angle_final = 4.46, -- 4.46 is approx NNW.
|
||||
|
||||
-- Vertical offset in tiles for the deposit resource placement. Starting from top-left corner.
|
||||
-- Only applicable for square spawns.
|
||||
vertical_offset = 20,
|
||||
|
||||
-- Horizontal offset in tiles for the deposit resource placement. Starting from top-left corner.
|
||||
-- Only applicable for square spawns.
|
||||
horizontal_offset = 20,
|
||||
|
||||
-- Spacing between resource deposits in tiles.
|
||||
-- Only applicable for square spawns.
|
||||
linear_spacing = 6,
|
||||
|
||||
-- Size multiplier for the starting resource deposits.
|
||||
size_multiplier = 1.0,
|
||||
|
||||
-- Amount multiplier for the starting resource deposits.
|
||||
amount_multiplier = 1.0,
|
||||
|
||||
},
|
||||
|
||||
-- Spawn configuration specific to each surface, including starting & respawn items.
|
||||
---@type table<string, OarcConfigSurface>
|
||||
surfaces_config =
|
||||
{
|
||||
["nauvis"] = {
|
||||
starting_items = NAUVIS_STARTER_ITEMS,
|
||||
spawn_config = NAUVIS_SPAWN_CONFIG
|
||||
},
|
||||
["vulcanus"] = {
|
||||
starting_items = NAUVIS_STARTER_ITEMS,
|
||||
spawn_config = NAUVIS_SPAWN_CONFIG
|
||||
},
|
||||
["fulgora"] = {
|
||||
starting_items = NAUVIS_STARTER_ITEMS,
|
||||
spawn_config = NAUVIS_SPAWN_CONFIG
|
||||
},
|
||||
["gleba"] = {
|
||||
starting_items = NAUVIS_STARTER_ITEMS,
|
||||
spawn_config = NAUVIS_SPAWN_CONFIG
|
||||
},
|
||||
["aquilo"] = {
|
||||
starting_items = NAUVIS_STARTER_ITEMS,
|
||||
spawn_config = NAUVIS_SPAWN_CONFIG
|
||||
}
|
||||
},
|
||||
|
||||
-- Surfaces blacklist (Ignore these surfaces completely for spawning and regrowth!)
|
||||
---@type table<integer, string>
|
||||
surfaces_blacklist = {
|
||||
HOLDING_PEN_SURFACE_NAME,
|
||||
},
|
||||
|
||||
-- Surfaces blacklist that match THE START of these strings
|
||||
-- (Ignore these surfaces completely for spawning and regrowth!)
|
||||
---@type table<integer, string>
|
||||
surfaces_blacklist_match = {
|
||||
-- Factorissimo Mod Surfaces
|
||||
"factory-power",
|
||||
"factory-floor",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--[[
|
||||
|
||||
_ _ _ _ _______ _____ ___ _ _ _ _ _ ___ _____ _ _____ ___ ___ _ _ ___
|
||||
| | | | | |/_\ |_ _\ \ / / _ \ __| /_\ | \| | \| |/ _ \_ _/_\_ _|_ _/ _ \| \| / __|
|
||||
| |_| |_| / _ \ | | \ V /| _/ _| / _ \| .` | .` | (_) || |/ _ \| | | | (_) | .` \__ \
|
||||
|____\___/_/ \_\ |_| |_| |_| |___| /_/ \_\_|\_|_|\_|\___/ |_/_/ \_\_| |___\___/|_|\_|___/
|
||||
|
||||
These are LUA type annotations for development and editor support.
|
||||
You can ignore this unless you're making changes to the mod, in which case it might be helpful.
|
||||
]]
|
||||
|
||||
---@class OarcConfig
|
||||
---@field server_info OarcConfigServerInfo Personalized server info for the welcome GUI and Info panel.
|
||||
---@field gameplay OarcConfigGameplaySettings Various mod gameplay settings
|
||||
---@field regrowth OarcConfigRegrowth Regrowth specific settings (keeps map size down)
|
||||
---@field spawn_general OarcConfigSpawnGeneral General spawn settings (size, shape, etc.)
|
||||
---@field resource_placement OarcConfigSpawnResourcePlacementSettings Resource placement settings
|
||||
---@field surfaces_config table<string, OarcConfigSurface> Spawn configuration (starting items and spawn area config) for each surface.
|
||||
---@field surfaces_blacklist table<string> List of surfaces to ignore automatically.
|
||||
---@field surfaces_blacklist_match table<string> List of surfaces to ignore automatically if the start of the string matches the surface name.
|
||||
|
||||
---@class OarcConfigServerInfo
|
||||
---@field welcome_msg_title string Title of welcome GUI window.
|
||||
---@field welcome_msg string Main welcome message. (Should provide mod info.)
|
||||
---@field discord_invite string Discord invite for easy copy paste.
|
||||
|
||||
---@class OarcConfigGameplaySettings
|
||||
---@field default_allow_spawning_on_other_surfaces boolean Default setting for enabling spawning on other surfaces other than the default_surface. This is a STARTUP setting, so it can't be changed in game.
|
||||
---@field main_force_name string The name of the main force. This is a STARTUP setting, so it can't be changed in game.
|
||||
---@field enable_main_team boolean Allows all players to join a primary force(team).
|
||||
---@field enable_separate_teams boolean Allows players to create their own force(team).
|
||||
---@field allow_moats_around_spawns boolean Allow players to choose to spawn with a moat
|
||||
---@field enable_moat_bridging boolean If there is a moat, this makes a small path to land to avoid "turtling", but if the spawn is in the middle of water, it won't do anything.
|
||||
---@field minimum_distance_to_existing_chunks number The radius, in chunks, that a spawn area is from any other generated chunks. It ensures the spawn area isn't too near generated/explored/existing area.
|
||||
---@field near_spawn_distance number The closest a player can spawn to the origin. (Not exact, but close).
|
||||
---@field far_spawn_distance number The furthest a player can spawn from the origin. (Not exact, but close).
|
||||
---@field enable_buddy_spawn boolean Allow 2 players to spawn next to each other, each with their own starting area.
|
||||
---@field enable_offline_protection boolean Inhibits enemy attacks on bases where all players are offline. Not 100% guaranteed!
|
||||
---@field enable_shared_team_vision boolean Enable shared vision between teams (all teams are COOP regardless)
|
||||
---@field enable_shared_team_chat boolean Share local team chat with all teams
|
||||
---@field enable_shared_spawns boolean Enable if players can allow others to join their spawn.
|
||||
---@field number_of_players_per_shared_spawn number Number of players allowed to join a shared spawn.
|
||||
---@field enable_friendly_fire boolean Set to true if you want to shoot your own chests and stuff.
|
||||
---@field default_surface string The starting surface of the main force.
|
||||
---@field enable_secondary_spawns boolean Enable secondary spawns for players. This automatically creates a new spawn point when they first move to a separate spawns enabled surface.
|
||||
---@field scale_resources_around_spawns boolean Scales resources so that even if you spawn "far away" from the center of the map, resources near to your spawn point scale so you aren't surrounded by 100M patches or something. This is useful depending on what map gen settings you pick.
|
||||
---@field modified_enemy_spawning boolean Adjust enemy spawning based on distance to spawns. All it does it make things more balanced based on your distance and makes the game a little easier. No behemoth worms everywhere just because you spawned far away.
|
||||
---@field modified_enemy_easy_evo number Enemy evolution factor for the easy force (inside warning area).
|
||||
---@field modified_enemy_medium_evo number Enemy evolution factor for the medium force (inside danger area).
|
||||
---@field minimum_online_time number Require playes to be online for at least X minutes Else their character is removed and their spawn point is freed up for use
|
||||
---@field respawn_cooldown_min number Respawn cooldown in minutes.
|
||||
---@field enable_shared_power boolean Enable shared power between bases. Creates a special power pole for cross surface connections.
|
||||
---@field enable_shared_chest boolean Enables a single shared chest using the native linked-chest entity in factorio.
|
||||
|
||||
---@class OarcConfigRegrowth
|
||||
---@field enable_regrowth boolean Cleans up unused chunks periodically. Helps keep map size down.
|
||||
---@field enable_world_eater boolean Checks inactive chunks to see if they are empty of entities and deletes them periodically.
|
||||
---@field enable_abandoned_base_cleanup boolean Removes player bases when they leave shortly after joining.
|
||||
---@field cleanup_interval number This is the interval in minutes that the regrowth cleanup will run.
|
||||
|
||||
---@class OarcConfigSurface
|
||||
---@field starting_items OarcConfigStartingItems Starting items for players on this surface (including crashed ship items)
|
||||
---@field spawn_config OarcConfigSpawn Spawn area config for this surface
|
||||
|
||||
---@class OarcConfigStartingItems
|
||||
---@field crashed_ship boolean Add a crashed ship like a vanilla game (create_crash_site) Resources go in the ship itself. (5 slots max!) Wreakage is distributed in small pieces. (I recommend only 1 item type.)
|
||||
---@field crashed_ship_resources table Items to be placed in the crashed ship.
|
||||
---@field crashed_ship_wreakage table Items to be placed in the crashed ship. (Recommend only 1 item type!)
|
||||
---@field player_start_items table Items provided to the player the first time they join
|
||||
---@field player_respawn_items table Items provided after EVERY respawn (disabled by default)
|
||||
|
||||
---@class OarcConfigSpawn
|
||||
---@field safe_area OarcConfigSpawnSafeArea How safe is the spawn area?
|
||||
---@field water OarcConfigSpawnWater Water strip settings
|
||||
---@field shared_power_pole_position OarcOffsetPosition Location of shared power pole relative to spawn center (if enabled)
|
||||
---@field shared_chest_position OarcOffsetPosition Location of shared chest relative to spawn center (if enabled)
|
||||
---@field solid_resources table<string, OarcConfigSolidResource> Spawn area config for solid resource tiles
|
||||
---@field fluid_resources table<string, OarcConfigFluidResource> Spawn area config for fluid resource patches (like oil)
|
||||
|
||||
---@class OarcConfigSpawnGeneral
|
||||
---@field spawn_radius_tiles number THIS IS WHAT SETS THE SPAWN CIRCLE SIZE! Create a circle of land area for the spawn If you make this much bigger than a few chunks, good luck.
|
||||
---@field moat_width_tiles number Width of the moat around the spawn area. If you change the spawn area size, you might have to adjust this as well.
|
||||
---@field tree_width_tiles number Width of the tree ring around the spawn area. If you change the spawn area size, you might have to adjust this as well.
|
||||
---@field resources_shape SpawnResourcesShapeChoice The starting resources deposits shape.
|
||||
---@field force_grass boolean Force the land area circle at the spawn to be fully grass, otherwise it defaults to the existing terrain.
|
||||
---@field shape SpawnShapeChoice Spawn a circle/octagon/square of trees around this base outline.
|
||||
|
||||
---@class OarcConfigSpawnSafeArea
|
||||
---@field safe_radius number Safe area has no aliens This is the radius in tiles of safe area.
|
||||
---@field warn_radius number Warning area has significantly reduced aliens This is the radius in tiles of warning area.
|
||||
---@field warn_reduction number 1 : X (spawners alive : spawners destroyed) in this area
|
||||
---@field danger_radius number Danger area has slightly reduce aliens This is the radius in tiles of danger area.
|
||||
---@field danger_reduction number 1 : X (spawners alive : spawners destroyed) in this area
|
||||
|
||||
---@class OarcConfigSpawnWater
|
||||
---@field x_offset number Location of water strip within the spawn area (horizontal)
|
||||
---@field y_offset number Location of water strip within the spawn area (vertical)
|
||||
---@field length number Length of water strip within the spawn area
|
||||
|
||||
---@alias OarcOffsetPosition { x_offset: number, y_offset: number } An offset position intended to be relative to the spawn center.
|
||||
|
||||
---@class OarcConfigSpawnResourcePlacementSettings
|
||||
---@field enabled boolean Autoplace resources. This will ignore the fixed x_offset/y_offset values in solid_resources. Only works for solid_resources at the moment, not oil patches/water.
|
||||
---@field distance_to_edge number Distance in tiles from the edge of spawn that resources are placed. Only applicable for circular spawns.
|
||||
---@field angle_offset number At what angle (in radians) do resources start. 0 means starts directly east. Resources are placed clockwise from there. Only applicable for circular spawns.
|
||||
---@field angle_final number At what andle do we place the last resource. angle_offset and angle_final determine spacing and placement. Only applicable for circular spawns.
|
||||
---@field vertical_offset number Vertical offset in tiles for the deposit resource placement. Only applicable for square spawns.
|
||||
---@field horizontal_offset number Horizontal offset in tiles for the deposit resource placement. Only applicable for square spawns.
|
||||
---@field linear_spacing number Spacing between resource deposits in tiles. Only applicable for square spawns.
|
||||
---@field size_multiplier number Size multiplier for the starting resource deposits.
|
||||
---@field amount_multiplier number Amount multiplier for the starting resource deposits.
|
||||
|
||||
---@alias OarcConfigSolidResource { amount: integer, size: integer, x_offset: integer, y_offset: integer } Amount and placement of solid resource tiles in the spawn area.
|
||||
---@alias OarcConfigFluidResource { num_patches: integer, amount: integer, x_offset_start: integer, y_offset_start: integer, x_offset_next: integer, y_offset_next: integer } Amount and placement of fluid resource patches in the spawn area.
|
370
lib/config_parser.lua
Normal file
370
lib/config_parser.lua
Normal file
@ -0,0 +1,370 @@
|
||||
-- This file is used to validate the config.lua file and handle any mod settings conflicts.
|
||||
-- DON'T JUDGE ME! I wanted to try and make a nice in game setting GUI since the native mod settings GUI is so limited.
|
||||
|
||||
---Provides a way to look up the config settings key from the mod settings key.
|
||||
---@alias OarcSettingsLookup { mod_key: string, ocfg_keys: table<integer, string>, type: string, text: LocalisedString? }
|
||||
|
||||
---@type table<string, OarcSettingsLookup>
|
||||
OCFG_KEYS =
|
||||
{
|
||||
["server_info_HEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "header", text = {"oarc-settings-section-header-server-info"}},
|
||||
["server_info.welcome_msg_title"] = {mod_key = "oarc-mod-welcome-msg-title" , ocfg_keys = {"server_info", "welcome_msg_title"}, type = "string"},
|
||||
["server_info.welcome_msg"] = {mod_key = "oarc-mod-welcome-msg" , ocfg_keys = {"server_info", "welcome_msg"}, type = "string"},
|
||||
["server_info.discord_invite"] = {mod_key = "oarc-mod-discord-invite" , ocfg_keys = {"server_info", "discord_invite"}, type = "string"},
|
||||
|
||||
["gameplay_HEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "header", text = {"oarc-settings-section-header-gameplay"}},
|
||||
["gameplay_spawn_choices_SUBHEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "subheader", text = {"oarc-settings-section-subheader-spawn-choices"}},
|
||||
["gameplay.enable_main_team"] = {mod_key = "oarc-mod-enable-main-team" , ocfg_keys = {"gameplay", "enable_main_team"}, type = "boolean"},
|
||||
["gameplay.enable_separate_teams"] = {mod_key = "oarc-mod-enable-separate-teams" , ocfg_keys = {"gameplay", "enable_separate_teams"}, type = "boolean"},
|
||||
-- STARTUP ["gameplay.enable_spawning_on_other_surfaces"] = {mod_key = "oarc-mod-default-allow-spawning-on-other-surfaces" , ocfg_keys = {"gameplay", "enable_spawning_on_other_surfaces"}, type = "boolean"},
|
||||
["gameplay.allow_moats_around_spawns"] = {mod_key = "oarc-mod-allow-moats-around-spawns" , ocfg_keys = {"gameplay", "allow_moats_around_spawns"}, type = "boolean"},
|
||||
["gameplay.enable_moat_bridging"] = {mod_key = "oarc-mod-enable-moat-bridging" , ocfg_keys = {"gameplay", "enable_moat_bridging"}, type = "boolean"},
|
||||
["gameplay.minimum_distance_to_existing_chunks"] = {mod_key = "oarc-mod-minimum-distance-to-existing-chunks" , ocfg_keys = {"gameplay", "minimum_distance_to_existing_chunks"}, type = "integer"},
|
||||
["gameplay.near_spawn_distance"] = {mod_key = "oarc-mod-near-spawn-distance" , ocfg_keys = {"gameplay", "near_spawn_distance"}, type = "integer"},
|
||||
["gameplay.far_spawn_distance"] = {mod_key = "oarc-mod-far-spawn-distance" , ocfg_keys = {"gameplay", "far_spawn_distance"}, type = "integer"},
|
||||
["gameplay.enable_buddy_spawn"] = {mod_key = "oarc-mod-enable-buddy-spawn" , ocfg_keys = {"gameplay", "enable_buddy_spawn"}, type = "boolean"},
|
||||
["gameplay.enable_shared_spawns"] = {mod_key = "oarc-mod-enable-shared-spawns" , ocfg_keys = {"gameplay", "enable_shared_spawns"}, type = "boolean"},
|
||||
["gameplay.number_of_players_per_shared_spawn"] = {mod_key = "oarc-mod-number-of-players-per-shared-spawn" , ocfg_keys = {"gameplay", "number_of_players_per_shared_spawn"}, type = "integer"},
|
||||
["gameplay.default_surface"] = {mod_key = "oarc-mod-default-surface" , ocfg_keys = {"gameplay", "default_surface"}, type = "string"},
|
||||
["gameplay.enable_secondary_spawns"] = {mod_key = "oarc-mod-enable-secondary-spawns" , ocfg_keys = {"gameplay", "enable_secondary_spawns"}, type = "boolean"},
|
||||
-- STARTUP ["gameplay.main_force_name"] = {mod_key = "oarc-mod-main-force-name" , ocfg_keys = {"gameplay", "main_force_name"}, type = "string"},
|
||||
|
||||
["gameplay_difficulty_scaling_SUBHEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "subheader", text = {"oarc-settings-section-subheader-difficulty-scaling"}},
|
||||
["gameplay.enable_offline_protection"] = {mod_key = "oarc-mod-enable-offline-protection" , ocfg_keys = {"gameplay", "enable_offline_protection"}, type = "boolean"},
|
||||
["gameplay.scale_resources_around_spawns"] = {mod_key = "oarc-mod-scale-resources-around-spawns" , ocfg_keys = {"gameplay", "scale_resources_around_spawns"}, type = "boolean"},
|
||||
["gameplay.modified_enemy_spawning"] = {mod_key = "oarc-mod-modified-enemy-spawning" , ocfg_keys = {"gameplay", "modified_enemy_spawning"}, type = "boolean"},
|
||||
["gameplay.modified_enemy_easy_evo"] = {mod_key = "oarc-mod-modified-enemy-easy-evo" , ocfg_keys = {"gameplay", "modified_enemy_easy_evo"}, type = "double"},
|
||||
["gameplay.modified_enemy_medium_evo"] = {mod_key = "oarc-mod-modified-enemy-medium-evo" , ocfg_keys = {"gameplay", "modified_enemy_medium_evo"}, type = "double"},
|
||||
|
||||
["gameplay_misc_SUBHEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "subheader", text = {"oarc-settings-section-subheader-gameplay-misc"}},
|
||||
["gameplay.enable_friendly_fire"] = {mod_key = "oarc-mod-enable-friendly-fire" , ocfg_keys = {"gameplay", "enable_friendly_fire"}, type = "boolean"},
|
||||
["gameplay.minimum_online_time"] = {mod_key = "oarc-mod-minimum-online-time" , ocfg_keys = {"gameplay", "minimum_online_time"}, type = "integer"},
|
||||
["gameplay.respawn_cooldown_min"] = {mod_key = "oarc-mod-respawn-cooldown-min" , ocfg_keys = {"gameplay", "respawn_cooldown_min"}, type = "integer"},
|
||||
|
||||
["gameplay_sharing_SUBHEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "subheader", text = {"oarc-settings-section-subheader-sharing"}},
|
||||
["gameplay.enable_shared_team_vision"] = {mod_key = "oarc-mod-enable-shared-team-vision" , ocfg_keys = {"gameplay", "enable_shared_team_vision"}, type = "boolean"},
|
||||
["gameplay.enable_shared_team_chat"] = {mod_key = "oarc-mod-enable-shared-team-chat" , ocfg_keys = {"gameplay", "enable_shared_team_chat"}, type = "boolean"},
|
||||
["gameplay.enable_shared_power"] = {mod_key = "oarc-mod-enable-shared-power" , ocfg_keys = {"gameplay", "enable_shared_power"}, type = "boolean"},
|
||||
["gameplay.enable_shared_chest"] = {mod_key = "oarc-mod-enable-shared-chest" , ocfg_keys = {"gameplay", "enable_shared_chest"}, type = "boolean"},
|
||||
|
||||
["regrowth_HEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "header", text = {"oarc-settings-section-header-regrowth"}},
|
||||
["regrowth_SUBHEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "subheader", text = {"oarc-settings-section-subheader-regrowth-warning"}},
|
||||
["regrowth.enable_regrowth"] = {mod_key = "oarc-mod-enable-regrowth" , ocfg_keys = {"regrowth", "enable_regrowth"}, type = "boolean"},
|
||||
["regrowth.enable_world_eater"] = {mod_key = "oarc-mod-enable-world-eater" , ocfg_keys = {"regrowth", "enable_world_eater"}, type = "boolean"},
|
||||
["regrowth.enable_abandoned_base_cleanup"] = {mod_key = "oarc-mod-enable-abandoned-base-cleanup" , ocfg_keys = {"regrowth", "enable_abandoned_base_cleanup"}, type = "boolean"},
|
||||
["regrowth.cleanup_interval"] = {mod_key = "oarc-mod-regrowth-cleanup-interval-min" , ocfg_keys = {"regrowth", "cleanup_interval"}, type = "integer"},
|
||||
|
||||
["general_spawn_HEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "header", text = {"oarc-settings-section-header-general-spawn"}},
|
||||
["spawn_general.spawn_radius_tiles"] = {mod_key = "oarc-mod-spawn-general-radius-tiles" , ocfg_keys = {"spawn_general", "spawn_radius_tiles"}, type = "integer"},
|
||||
["spawn_general.moat_width_tiles"] = {mod_key = "oarc-mod-spawn-general-moat-width-tiles" , ocfg_keys = {"spawn_general", "moat_width_tiles"}, type = "integer"},
|
||||
["spawn_general.tree_width_tiles"] = {mod_key = "oarc-mod-spawn-general-tree-width-tiles" , ocfg_keys = {"spawn_general", "tree_width_tiles"}, type = "integer"},
|
||||
["spawn_general.resources_shape"] = {mod_key = "oarc-mod-spawn-general-enable-resources-circle-shape" , ocfg_keys = {"spawn_general", "resources_shape"}, type = "string-list"},
|
||||
["spawn_general.force_grass"] = {mod_key = "oarc-mod-spawn-general-enable-force-grass" , ocfg_keys = {"spawn_general", "force_grass"}, type = "boolean"},
|
||||
["spawn_general.shape"] = {mod_key = "oarc-mod-spawn-general-shape" , ocfg_keys = {"spawn_general", "shape"}, type = "string-list"},
|
||||
|
||||
["resource_placement_HEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "header", text = {"oarc-settings-section-header-resource-placement"}},
|
||||
["resource_placement.enabled"] = {mod_key = "oarc-mod-resource-placement-enabled" , ocfg_keys = {"resource_placement", "enabled"}, type = "boolean"},
|
||||
["resource_placement.size_multiplier"] = {mod_key = "oarc-mod-resource-placement-size-multiplier" , ocfg_keys = {"resource_placement", "size_multiplier"}, type = "double"},
|
||||
["resource_placement.amount_multiplier"] = {mod_key = "oarc-mod-resource-placement-amount-multiplier" , ocfg_keys = {"resource_placement", "amount_multiplier"}, type = "double"},
|
||||
|
||||
["resource_placement_circle_SUBHEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "subheader", text = {"oarc-settings-section-subheader-resource-placement-circular"}},
|
||||
["resource_placement.distance_to_edge"] = {mod_key = "oarc-mod-resource-placement-distance-to-edge" , ocfg_keys = {"resource_placement", "distance_to_edge"}, type = "integer"},
|
||||
["resource_placement.angle_offset"] = {mod_key = "oarc-mod-resource-placement-angle-offset" , ocfg_keys = {"resource_placement", "angle_offset"}, type = "double"},
|
||||
["resource_placement.angle_final"] = {mod_key = "oarc-mod-resource-placement-angle-final" , ocfg_keys = {"resource_placement", "angle_final"}, type = "double"},
|
||||
|
||||
["resource_placement_square_SUBHEADER"] = {mod_key = "" , ocfg_keys = {""}, type = "subheader", text = {"oarc-settings-section-subheader-resource-placement-square"}},
|
||||
["resource_placement.vertical_offset"] = {mod_key = "oarc-mod-resource-placement-vertical-offset" , ocfg_keys = {"resource_placement", "vertical_offset"}, type = "integer"},
|
||||
["resource_placement.horizontal_offset"] = {mod_key = "oarc-mod-resource-placement-horizontal-offset" , ocfg_keys = {"resource_placement", "horizontal_offset"}, type = "integer"},
|
||||
["resource_placement.linear_spacing"] = {mod_key = "oarc-mod-resource-placement-linear-spacing" , ocfg_keys = {"resource_placement", "linear_spacing"}, type = "integer"},
|
||||
}
|
||||
|
||||
---Easy reverse lookup for mod settings keys.
|
||||
---@type table<string, string>
|
||||
OCFG_MOD_KEYS =
|
||||
{
|
||||
["oarc-mod-welcome-msg-title"] = "server_info.welcome_msg_title",
|
||||
["oarc-mod-welcome-msg"] = "server_info.welcome_msg",
|
||||
["oarc-mod-discord-invite"] = "server_info.discord_invite",
|
||||
|
||||
["oarc-mod-enable-main-team"] = "gameplay.enable_main_team",
|
||||
["oarc-mod-enable-separate-teams"] = "gameplay.enable_separate_teams",
|
||||
-- STARTUP ["oarc-mod-default-allow-spawning-on-other-surfaces"] = " ["gameplay.enable_spawning_on_other_surfaces",
|
||||
["oarc-mod-allow-moats-around-spawns"] = "gameplay.allow_moats_around_spawns",
|
||||
["oarc-mod-enable-moat-bridging"] = "gameplay.enable_moat_bridging",
|
||||
["oarc-mod-minimum-distance-to-existing-chunks"] = "gameplay.minimum_distance_to_existing_chunks",
|
||||
["oarc-mod-near-spawn-distance"] = "gameplay.near_spawn_distance",
|
||||
["oarc-mod-far-spawn-distance"] = "gameplay.far_spawn_distance",
|
||||
|
||||
["oarc-mod-enable-buddy-spawn"] = "gameplay.enable_buddy_spawn",
|
||||
["oarc-mod-enable-offline-protection"] = "gameplay.enable_offline_protection",
|
||||
["oarc-mod-enable-shared-team-vision"] = "gameplay.enable_shared_team_vision",
|
||||
["oarc-mod-enable-shared-team-chat"] = "gameplay.enable_shared_team_chat",
|
||||
["oarc-mod-enable-shared-spawns"] = "gameplay.enable_shared_spawns",
|
||||
["oarc-mod-number-of-players-per-shared-spawn"] = "gameplay.number_of_players_per_shared_spawn",
|
||||
["oarc-mod-enable-friendly-fire"] = "gameplay.enable_friendly_fire",
|
||||
|
||||
-- STARTUP ["oarc-mod-main-force-name"] = "gameplay.main_force_name",
|
||||
["oarc-mod-default-surface"] = "gameplay.default_surface",
|
||||
["oarc-mod-enable-secondary-spawns"] = "gameplay.enable_secondary_spawns",
|
||||
|
||||
["oarc-mod-scale-resources-around-spawns"] = "gameplay.scale_resources_around_spawns",
|
||||
["oarc-mod-modified-enemy-spawning"] = "gameplay.modified_enemy_spawning",
|
||||
["oarc-mod-modified-enemy-easy-evo"] = "gameplay.modified_enemy_easy_evo",
|
||||
["oarc-mod-modified-enemy-medium-evo"] = "gameplay.modified_enemy_medium_evo",
|
||||
|
||||
["oarc-mod-minimum-online-time"] = "gameplay.minimum_online_time",
|
||||
["oarc-mod-respawn-cooldown-min"] = "gameplay.respawn_cooldown_min",
|
||||
["oarc-mod-enable-shared-power"] = "gameplay.enable_shared_power",
|
||||
["oarc-mod-enable-shared-chest"] = "gameplay.enable_shared_chest",
|
||||
|
||||
["oarc-mod-enable-regrowth"] = "regrowth.enable_regrowth",
|
||||
["oarc-mod-enable-world-eater"] = "regrowth.enable_world_eater",
|
||||
["oarc-mod-enable-abandoned-base-cleanup"] = "regrowth.enable_abandoned_base_cleanup",
|
||||
["oarc-mod-regrowth-cleanup-interval-min"] = "regrowth.cleanup_interval",
|
||||
|
||||
["oarc-mod-spawn-general-radius-tiles"] = "spawn_general.spawn_radius_tiles",
|
||||
["oarc-mod-spawn-general-moat-width-tiles"] = "spawn_general.moat_width_tiles",
|
||||
["oarc-mod-spawn-general-tree-width-tiles"] = "spawn_general.tree_width_tiles",
|
||||
["oarc-mod-spawn-general-enable-resources-circle-shape"] = "spawn_general.resources_shape",
|
||||
["oarc-mod-spawn-general-enable-force-grass"] = "spawn_general.force_grass",
|
||||
["oarc-mod-spawn-general-shape"] = "spawn_general.shape",
|
||||
|
||||
["oarc-mod-resource-placement-enabled"] = "resource_placement.enabled",
|
||||
["oarc-mod-resource-placement-distance-to-edge"] = "resource_placement.distance_to_edge",
|
||||
["oarc-mod-resource-placement-angle-offset"] = "resource_placement.angle_offset",
|
||||
["oarc-mod-resource-placement-angle-final"] = "resource_placement.angle_final",
|
||||
["oarc-mod-resource-placement-vertical-offset"] = "resource_placement.vertical_offset",
|
||||
["oarc-mod-resource-placement-horizontal-offset"] = "resource_placement.horizontal_offset",
|
||||
["oarc-mod-resource-placement-linear-spacing"] = "resource_placement.linear_spacing",
|
||||
["oarc-mod-resource-placement-size-multiplier"] = "resource_placement.size_multiplier",
|
||||
["oarc-mod-resource-placement-amount-multiplier"] = "resource_placement.amount_multiplier"
|
||||
}
|
||||
|
||||
|
||||
function ValidateAndLoadConfig()
|
||||
|
||||
-- Check that each of the OCFG_MOD_KEYS has a corresponding OCFG_KEYS entry.
|
||||
for mod_key,ocfg_key in pairs(OCFG_MOD_KEYS) do
|
||||
if (OCFG_KEYS[ocfg_key] == nil) then
|
||||
error("OCFG_MOD_KEYS entry does not have a corresponding OCFG_KEYS entry: " .. mod_key .. " -> " .. ocfg_key)
|
||||
end
|
||||
end
|
||||
-- And check the opposite.
|
||||
for ocfg_key,entry in pairs(OCFG_KEYS) do
|
||||
if (entry.type ~= "header") and (entry.type ~= "subheader") and (OCFG_MOD_KEYS[entry.mod_key] == nil) then
|
||||
error("OCFG_KEYS entry does not have a corresponding OCFG_MOD_KEYS entry: " .. ocfg_key .. " -> " .. entry.mod_key)
|
||||
end
|
||||
end
|
||||
|
||||
-- Load the template config into the global table.
|
||||
---@class OarcConfig
|
||||
global.ocfg = OCFG
|
||||
|
||||
-- Check that each entry in OCFG matches the default value of the mod setting. This is just for my own sanity.
|
||||
-- Helps make sure mod default settings and my internal config are in sync.
|
||||
for _,entry in pairs(OCFG_KEYS) do
|
||||
if (entry.type ~= "header") and (entry.type ~= "subheader") then
|
||||
local mod_key = entry.mod_key
|
||||
local oarc_key = entry.ocfg_keys
|
||||
local mod_value = game.mod_setting_prototypes[mod_key].default_value
|
||||
local oarc_value = GetGlobalOarcConfigUsingKeyTable(oarc_key)
|
||||
if (mod_value ~= oarc_value) then
|
||||
error("OCFG value does not match mod setting: " .. mod_key .. " = " .. tostring(mod_value) .. " -> " .. serpent.block(oarc_key) .. " = " .. tostring(oarc_value))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
CacheModSettings() -- Get all mod settings and overwrite the defaults in OARC_CFG.
|
||||
|
||||
GetScenarioOverrideSettings() -- Get any scenario settings and overwrite both the mod settings and OARC_CFG.
|
||||
|
||||
ValidateSettings() -- These are validation checks that can't be done within the mod settings natively.
|
||||
end
|
||||
|
||||
function ValidateSettings()
|
||||
|
||||
-- Validate enable_main_team and enable_separate_teams.
|
||||
-- Force enable_main_team if both are disabled.
|
||||
if (not global.ocfg.gameplay.enable_main_team and not global.ocfg.gameplay.enable_separate_teams) then
|
||||
log("Both main force and separate teams are disabled! Enabling main force. Please check your mod settings or config!")
|
||||
global.ocfg.gameplay.enable_main_team = true
|
||||
settings.global["oarc-mod-enable-main-team"] = { value = true }
|
||||
SendBroadcastMsg("Invalid setting! Both main force and separate teams are disabled! Enabling main force.")
|
||||
end
|
||||
|
||||
-- Validate minimum is less than maximums
|
||||
if (global.ocfg.gameplay.near_spawn_distance >= global.ocfg.gameplay.far_spawn_distance) then
|
||||
log("Near spawn min distance is greater than or equal to near spawn max distance! Please check your mod settings or config!")
|
||||
global.ocfg.gameplay.far_spawn_distance = global.ocfg.gameplay.near_spawn_distance + 1
|
||||
settings.global["oarc-mod-far-spawn-distance"] = { value = global.ocfg.gameplay.far_spawn_distance }
|
||||
SendBroadcastMsg("Invalid setting! Near spawn min distance is greater than or equal to near spawn max distance!")
|
||||
end
|
||||
|
||||
-- Validate that regrowth is enabled if world eater is enabled.
|
||||
if (global.ocfg.regrowth.enable_world_eater and not global.ocfg.regrowth.enable_regrowth) then
|
||||
log("World eater is enabled but regrowth is not! Disabling world eater. Please check your mod settings or config!")
|
||||
global.ocfg.regrowth.enable_world_eater = false
|
||||
settings.global["oarc-mod-enable-world-eater"] = { value = false }
|
||||
SendBroadcastMsg("Invalid setting! World eater is enabled but regrowth is not! Disabling world eater.")
|
||||
end
|
||||
|
||||
-- Validate that default surface exists.
|
||||
if (game.surfaces[global.ocfg.gameplay.default_surface] == nil) then
|
||||
log("Default surface does not exist! Please check your mod settings or config!")
|
||||
global.ocfg.gameplay.default_surface = "nauvis"
|
||||
settings.global["oarc-mod-default-surface"] = { value = "nauvis" }
|
||||
SendBroadcastMsg("Invalid setting! Default surface does not exist! Setting to nauvis.")
|
||||
end
|
||||
|
||||
-- Validate that a "nauvis" surface config exists (nauvis is the default config fallback)
|
||||
-- This should only break with a bad scenario custom config.
|
||||
if (global.ocfg.surfaces_config["nauvis"] == nil) then
|
||||
error("nauvis surface config does not exist! Please check your mod settings or config!")
|
||||
end
|
||||
end
|
||||
|
||||
-- Read in the mod settings and copy them to the OARC_CFG table, overwriting the defaults in config.lua.
|
||||
function CacheModSettings()
|
||||
|
||||
log("Copying mod settings to OCFG table...")
|
||||
|
||||
-- Copy the global settings from the mod settings.
|
||||
-- Find the matching OARC setting and update it.
|
||||
for _,entry in pairs(OCFG_KEYS) do
|
||||
if (entry.type ~= "header") and (entry.type ~= "subheader") then
|
||||
SetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys, settings.global[entry.mod_key].value)
|
||||
end
|
||||
end
|
||||
|
||||
-- Special case for startup settings
|
||||
global.ocfg.gameplay.default_allow_spawning_on_other_surfaces = settings.startup["oarc-mod-default-allow-spawning-on-other-surfaces"].value --[[@as boolean]]
|
||||
global.ocfg.gameplay.main_force_name = settings.startup["oarc-mod-main-force-name"].value --[[@as string]]
|
||||
end
|
||||
|
||||
function GetScenarioOverrideSettings()
|
||||
|
||||
if remote.interfaces["oarc_scenario"] then
|
||||
|
||||
log("Getting scenario override settings...")
|
||||
local scenario_settings = remote.call("oarc_scenario", "get_scenario_settings")
|
||||
|
||||
-- Overwrite the non mod settings with the scenario settings.
|
||||
global.ocfg = scenario_settings
|
||||
|
||||
-- Override the mod settings with the scenario settings!
|
||||
for _,entry in pairs(OCFG_KEYS) do
|
||||
if (entry.type ~= "header") and (entry.type ~= "subheader") then
|
||||
local mod_key = entry.mod_key
|
||||
local oarc_key = entry.ocfg_keys
|
||||
local scenario_value = GetGlobalOarcConfigUsingKeyTable(oarc_key)
|
||||
if (scenario_value ~= nil) then
|
||||
local ok,result = pcall(function() settings.global[mod_key] = { value = scenario_value } end)
|
||||
if not ok then
|
||||
error("Error setting mod setting: " .. mod_key .. " = " .. tostring(scenario_value) .. "\n" .. "If you see this, you probably picked an invalid value for a setting override in the custom scenario.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
log("No scenario settings found.")
|
||||
end
|
||||
end
|
||||
|
||||
---Handles the event when a mod setting is changed in the mod settings menu.
|
||||
---@param event EventData.on_runtime_mod_setting_changed
|
||||
---@return nil
|
||||
function RuntimeModSettingChanged(event)
|
||||
|
||||
if (event.setting_type ~= "runtime-global") then
|
||||
return
|
||||
end
|
||||
|
||||
log("on_runtime_mod_setting_changed: " .. event.setting .. " = " .. tostring(settings.global[event.setting].value))
|
||||
|
||||
-- Find the matching OARC setting and update it.
|
||||
local found_setting = false
|
||||
|
||||
if (OCFG_MOD_KEYS[event.setting] ~= nil) then
|
||||
local oarc_setting_index = OCFG_MOD_KEYS[event.setting]
|
||||
local oarc_setting_table = OCFG_KEYS[oarc_setting_index]
|
||||
SetGlobalOarcConfigUsingKeyTable(oarc_setting_table.ocfg_keys, settings.global[event.setting].value)
|
||||
found_setting = true
|
||||
end
|
||||
|
||||
if (not found_setting) then
|
||||
error("Unknown oarc-mod setting changed: " .. event.setting)
|
||||
else
|
||||
ValidateSettings()
|
||||
ApplyRuntimeChanges(OCFG_MOD_KEYS[event.setting])
|
||||
end
|
||||
end
|
||||
|
||||
---A probably quit stupid function to let me lookup and set the global.ocfg entries using a key table.
|
||||
---@param key_table table<integer, string>
|
||||
---@param value any
|
||||
function SetGlobalOarcConfigUsingKeyTable(key_table, value)
|
||||
local number_of_keys = #key_table
|
||||
|
||||
if (number_of_keys == 1) then
|
||||
global.ocfg[key_table[1]] = value
|
||||
elseif (number_of_keys == 2) then
|
||||
global.ocfg[key_table[1]][key_table[2]] = value
|
||||
elseif (number_of_keys == 3) then
|
||||
global.ocfg[key_table[1]][key_table[2]][key_table[3]] = value
|
||||
else
|
||||
error("Invalid key_table length: " .. number_of_keys .. "\n" .. serpent.block(key_table))
|
||||
end
|
||||
end
|
||||
|
||||
---An equally stupid function to let me lookup the global.ocfg entries using a key table.
|
||||
---@param key_table table<integer, string>
|
||||
---@return any
|
||||
function GetGlobalOarcConfigUsingKeyTable(key_table)
|
||||
local number_of_keys = #key_table
|
||||
|
||||
if (number_of_keys == 1) then
|
||||
if (global.ocfg[key_table[1]] == nil) then
|
||||
error("Invalid key_table 1: " .. serpent.block(key_table))
|
||||
end
|
||||
return global.ocfg[key_table[1]]
|
||||
elseif (number_of_keys == 2) then
|
||||
if (global.ocfg[key_table[1]] == nil) or (global.ocfg[key_table[1]][key_table[2]] == nil) then
|
||||
error("Invalid key_table 2: " .. serpent.block(key_table))
|
||||
end
|
||||
return global.ocfg[key_table[1]][key_table[2]]
|
||||
elseif (number_of_keys == 3) then
|
||||
if (global.ocfg[key_table[1]] == nil) or
|
||||
(global.ocfg[key_table[1]][key_table[2]] == nil) or
|
||||
(global.ocfg[key_table[1]][key_table[2]][key_table[3]] == nil) then
|
||||
error("Invalid key_table 3: " .. serpent.block(key_table))
|
||||
end
|
||||
return global.ocfg[key_table[1]][key_table[2]][key_table[3]]
|
||||
else
|
||||
error("Invalid key_table length: " .. number_of_keys .. "\n" .. serpent.block(key_table))
|
||||
end
|
||||
end
|
||||
|
||||
---Handles any runtime changes that need more than just the setting change.
|
||||
---@param oarc_setting_index string
|
||||
---@return nil
|
||||
function ApplyRuntimeChanges(oarc_setting_index)
|
||||
|
||||
---Handle changing enable_shared_team_vision
|
||||
if (oarc_setting_index == "gameplay.enable_shared_team_vision") then
|
||||
for _,force in pairs(game.forces) do
|
||||
if (force.name ~= "neutral") and (force.name ~= "enemy") and (force.name ~= "enemy-easy") then
|
||||
force.share_chart = global.ocfg.gameplay.enable_shared_team_vision
|
||||
end
|
||||
end
|
||||
|
||||
---Handle changing enable_friendly_fire
|
||||
elseif (oarc_setting_index == "gameplay.enable_friendly_fire") then
|
||||
for _,force in pairs(game.forces) do
|
||||
if (force.name ~= "neutral") and (force.name ~= "enemy") and (force.name ~= "enemy-easy") then
|
||||
force.friendly_fire = global.ocfg.gameplay.enable_friendly_fire
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,401 +0,0 @@
|
||||
-- frontier_silo.lua
|
||||
-- Jan 2018
|
||||
-- My take on frontier silos for my Oarc scenario
|
||||
|
||||
require("config")
|
||||
require("lib/oarc_utils")
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Frontier style rocket silo stuff
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function SpawnSilosAndGenerateSiloAreas()
|
||||
|
||||
-- Special silo islands mode "boogaloo"
|
||||
if (global.ocfg.silo_islands) then
|
||||
|
||||
local num_spawns = #global.vanillaSpawns
|
||||
local new_spawn_list = {}
|
||||
|
||||
-- Pick out every OTHER vanilla spawn for the rocket silos.
|
||||
for k,v in pairs(global.vanillaSpawns) do
|
||||
if ((k <= num_spawns/2) and (k%2==1)) then
|
||||
SetFixedSiloPosition({x=v.x,y=v.y})
|
||||
elseif ((k > num_spawns/2) and (k%2==0)) then
|
||||
SetFixedSiloPosition({x=v.x,y=v.y})
|
||||
else
|
||||
table.insert(new_spawn_list, v)
|
||||
end
|
||||
end
|
||||
global.vanillaSpawns = new_spawn_list
|
||||
|
||||
-- A set of fixed silo positions
|
||||
elseif (global.ocfg.frontier_fixed_pos) then
|
||||
for k,v in pairs(global.ocfg.frontier_pos_table) do
|
||||
SetFixedSiloPosition(v)
|
||||
end
|
||||
|
||||
-- Random locations on a circle.
|
||||
else
|
||||
SetRandomSiloPosition(global.ocfg.frontier_silo_count)
|
||||
|
||||
end
|
||||
|
||||
-- Freezes the game at the start to generate all the chunks.
|
||||
GenerateRocketSiloAreas(game.surfaces[GAME_SURFACE_NAME])
|
||||
end
|
||||
|
||||
-- This creates a random silo position, stored to global.siloPosition
|
||||
-- It uses the config setting global.ocfg.frontier_silo_distance and spawns the
|
||||
-- silo somewhere on a circle edge with radius using that distance.
|
||||
function SetRandomSiloPosition(num_silos)
|
||||
if (global.siloPosition == nil) then
|
||||
global.siloPosition = {}
|
||||
end
|
||||
|
||||
local random_angle_offset = math.random(0, math.pi * 2)
|
||||
|
||||
for i=1,num_silos do
|
||||
local theta = ((math.pi * 2) / num_silos);
|
||||
local angle = (theta * i) + random_angle_offset;
|
||||
|
||||
local tx = (global.ocfg.frontier_silo_distance*CHUNK_SIZE * math.cos(angle))
|
||||
local ty = (global.ocfg.frontier_silo_distance*CHUNK_SIZE * math.sin(angle))
|
||||
|
||||
-- Ensure it's centered around a chunk
|
||||
local tx = (tx - (tx % CHUNK_SIZE)) + CHUNK_SIZE/2
|
||||
local ty = (ty - (ty % CHUNK_SIZE)) + CHUNK_SIZE/2
|
||||
|
||||
table.insert(global.siloPosition, {x=math.floor(tx), y=math.floor(ty)})
|
||||
|
||||
log("Silo position: " .. tx .. ", " .. ty .. ", " .. angle)
|
||||
end
|
||||
end
|
||||
|
||||
-- Sets the global.siloPosition var to the set in the config file
|
||||
function SetFixedSiloPosition(pos)
|
||||
table.insert(global.siloPosition, pos)
|
||||
end
|
||||
|
||||
-- Create a rocket silo at the specified positionmmmm
|
||||
-- Also makes sure tiles and entities are cleared if required.
|
||||
local function CreateRocketSilo(surface, siloPosition, force)
|
||||
|
||||
-- Delete any entities beneath the silo?
|
||||
for _, entity in pairs(surface.find_entities_filtered{area = {{siloPosition.x-5,
|
||||
siloPosition.y-6},
|
||||
{siloPosition.x+6,
|
||||
siloPosition.y+6}}}) do
|
||||
entity.destroy()
|
||||
end
|
||||
|
||||
-- Remove nearby enemies again
|
||||
for _, entity in pairs(surface.find_entities_filtered{area = {{siloPosition.x-(CHUNK_SIZE*4),
|
||||
siloPosition.y-(CHUNK_SIZE*4)},
|
||||
{siloPosition.x+(CHUNK_SIZE*4),
|
||||
siloPosition.y+(CHUNK_SIZE*4)}}, force = "enemy"}) do
|
||||
entity.destroy()
|
||||
end
|
||||
|
||||
-- Set tiles below the silo
|
||||
tiles = {}
|
||||
for dx = -6,5 do
|
||||
for dy = -6,5 do
|
||||
if (game.active_mods["oarc-restricted-build"]) then
|
||||
table.insert(tiles, {name = global.ocfg.locked_build_area_tile,
|
||||
position = {siloPosition.x+dx, siloPosition.y+dy}})
|
||||
else
|
||||
if ((dx % 2 == 0) or (dx % 2 == 0)) then
|
||||
table.insert(tiles, {name = "concrete",
|
||||
position = {siloPosition.x+dx, siloPosition.y+dy}})
|
||||
else
|
||||
table.insert(tiles, {name = "hazard-concrete-left",
|
||||
position = {siloPosition.x+dx, siloPosition.y+dy}})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
surface.set_tiles(tiles, true)
|
||||
|
||||
-- Create indestructible silo and assign to a force
|
||||
if not global.ocfg.frontier_allow_build then
|
||||
local silo = surface.create_entity{name = "rocket-silo", position = {siloPosition.x+0.5, siloPosition.y}, force = force}
|
||||
silo.destructible = false
|
||||
silo.minable = false
|
||||
end
|
||||
|
||||
-- TAG it on the main force at least.
|
||||
game.forces[global.ocfg.main_force].add_chart_tag(game.surfaces[GAME_SURFACE_NAME],
|
||||
{position=siloPosition, text="Rocket Silo",
|
||||
icon={type="item",name="rocket-silo"}})
|
||||
|
||||
-- Make silo safe from being removed.
|
||||
if global.ocfg.enable_regrowth then
|
||||
RegrowthMarkAreaSafeGivenTilePos(siloPosition, 5, true)
|
||||
end
|
||||
|
||||
if ENABLE_SILO_BEACONS then
|
||||
PhilipsBeacons(surface, siloPosition, game.forces[global.ocfg.main_force])
|
||||
end
|
||||
if ENABLE_SILO_RADAR then
|
||||
PhilipsRadar(surface, siloPosition, game.forces[global.ocfg.main_force])
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Generates all rocket silos, should be called after the areas are generated
|
||||
-- Includes a crop circle
|
||||
function GenerateAllSilos()
|
||||
|
||||
-- Create each silo in the list
|
||||
for idx,siloPos in pairs(global.siloPosition) do
|
||||
CreateRocketSilo(game.surfaces[GAME_SURFACE_NAME], siloPos, global.ocfg.main_force)
|
||||
end
|
||||
end
|
||||
|
||||
-- Validates any attempt to build a silo.
|
||||
-- Should be call in on_built_entity and on_robot_built_entity
|
||||
function BuildSiloAttempt(event)
|
||||
|
||||
-- Validation
|
||||
if (event.created_entity == nil) then return end
|
||||
|
||||
local e_name = event.created_entity.name
|
||||
if (event.created_entity.name == "entity-ghost") then
|
||||
e_name =event.created_entity.ghost_name
|
||||
end
|
||||
|
||||
if (e_name ~= "rocket-silo") then return end
|
||||
|
||||
-- Check if it's in the right area.
|
||||
local epos = event.created_entity.position
|
||||
|
||||
for k,v in pairs(global.siloPosition) do
|
||||
if (getDistance(epos, v) <= 1) then
|
||||
if (event.created_entity.name ~= "entity-ghost") then
|
||||
SendBroadcastMsg("Rocket silo has been built!")
|
||||
end
|
||||
return -- THIS MEANS WE SUCCESFULLY BUILT THE SILO (ghost or actual building.)
|
||||
end
|
||||
end
|
||||
|
||||
-- If we get here, means it wasn't in a valid position. Need to remove it.
|
||||
if (event.created_entity.last_user ~= nil) then
|
||||
FlyingText("Can't build silo here! Check the map!", epos, my_color_red, event.created_entity.surface)
|
||||
if (event.created_entity.name == "entity-ghost") then
|
||||
event.created_entity.destroy()
|
||||
else
|
||||
event.created_entity.last_user.mine_entity(event.created_entity, true)
|
||||
end
|
||||
else
|
||||
log("ERROR! Rocket-silo had no valid last user?!?!")
|
||||
end
|
||||
end
|
||||
|
||||
-- Generate clean land and trees around silo area
|
||||
function GenerateRocketSiloChunks()
|
||||
|
||||
-- Silo generation can take awhile depending on the number of silos.
|
||||
-- if (game.tick < #global.siloPosition*10*TICKS_PER_SECOND) then
|
||||
local surface = game.surfaces[GAME_SURFACE_NAME]
|
||||
-- local chunkArea = event.area
|
||||
|
||||
-- local chunkAreaCenter = {x=chunkArea.left_top.x+(CHUNK_SIZE/2),
|
||||
-- y=chunkArea.left_top.y+(CHUNK_SIZE/2)}
|
||||
|
||||
for _,siloPos in pairs(global.siloPosition) do
|
||||
local siloArea = {left_top=
|
||||
{x=siloPos.x-(CHUNK_SIZE*2),
|
||||
y=siloPos.y-(CHUNK_SIZE*2)},
|
||||
right_bottom=
|
||||
{x=siloPos.x+(CHUNK_SIZE*2),
|
||||
y=siloPos.y+(CHUNK_SIZE*2)}}
|
||||
|
||||
|
||||
-- Clear enemies directly next to the rocket
|
||||
-- if CheckIfInArea(chunkAreaCenter,siloArea) then
|
||||
for _, entity in pairs(surface.find_entities_filtered{area = siloArea, force = "enemy"}) do
|
||||
entity.destroy()
|
||||
end
|
||||
|
||||
-- Remove trees/resources inside the spawn area
|
||||
RemoveInCircle(surface, siloArea, "tree", siloPos, (CHUNK_SIZE*1.5)+5)
|
||||
RemoveInCircle(surface, siloArea, "resource", siloPos, (CHUNK_SIZE*1.5)+5)
|
||||
RemoveInCircle(surface, siloArea, "cliff", siloPos, (CHUNK_SIZE*1.5)+5)
|
||||
RemoveDecorationsArea(surface, siloArea)
|
||||
|
||||
-- Create rocket silo
|
||||
CreateCropOctagon(surface, siloPos, siloArea, (CHUNK_SIZE*1.5)+4, "landfill")
|
||||
-- end
|
||||
end
|
||||
-- end
|
||||
end
|
||||
|
||||
-- Generate chunks where we plan to place the rocket silos.
|
||||
function GenerateRocketSiloAreas(surface)
|
||||
for idx,siloPos in pairs(global.siloPosition) do
|
||||
surface.request_to_generate_chunks({siloPos.x, siloPos.y}, 1)
|
||||
end
|
||||
if (global.ocfg.frontier_silo_vision) then
|
||||
ChartRocketSiloAreas(surface, game.forces[global.ocfg.main_force])
|
||||
end
|
||||
|
||||
game.surfaces[GAME_SURFACE_NAME].force_generate_chunk_requests() -- Block and generate all to be sure.
|
||||
|
||||
GenerateRocketSiloChunks()
|
||||
GenerateAllSilos()
|
||||
end
|
||||
|
||||
-- Chart chunks where we plan to place the rocket silos.
|
||||
function ChartRocketSiloAreas(surface, force)
|
||||
for idx,siloPos in pairs(global.siloPosition) do
|
||||
force.chart(surface, {{siloPos.x-(CHUNK_SIZE*1),
|
||||
siloPos.y-(CHUNK_SIZE*1)},
|
||||
{siloPos.x+(CHUNK_SIZE*1),
|
||||
siloPos.y+(CHUNK_SIZE*1)}})
|
||||
end
|
||||
end
|
||||
|
||||
function PhilipsBeacons(surface, siloPos, force)
|
||||
|
||||
-- Add Beacons
|
||||
-- x = right, left; y = up, down
|
||||
-- top 1 left 1
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x-8, siloPos.y-8}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- top 2
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x-5, siloPos.y-8}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- top 3
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x-2, siloPos.y-8}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- top 4
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x+2, siloPos.y-8}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- top 5
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x+5, siloPos.y-8}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- top 6 right 1
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x+8, siloPos.y-8}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- left 2
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x-8, siloPos.y-5}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- left 3
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x-8, siloPos.y-2}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- left 4
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x-8, siloPos.y+2}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- left 5
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x-8, siloPos.y+5}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- left 6 bottom 1
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x-8, siloPos.y+8}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- left 7 bottom 2
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x-5, siloPos.y+8}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- right 2
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x+8, siloPos.y-5}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- right 3
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x+8, siloPos.y-2}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- right 4
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x+8, siloPos.y+2}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- right 5
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x+8, siloPos.y+5}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- right 6 bottom 3
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x+5, siloPos.y+8}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- right 7 bottom 4
|
||||
local beacon = surface.create_entity{name = "beacon", position = {siloPos.x+8, siloPos.y+8}, force = force}
|
||||
beacon.destructible = false
|
||||
beacon.minable = false
|
||||
-- substations
|
||||
-- top left
|
||||
local substation = surface.create_entity{name = "substation", position = {siloPos.x-5, siloPos.y-5}, force = force}
|
||||
substation.destructible = false
|
||||
substation.minable = false
|
||||
-- top right
|
||||
local substation = surface.create_entity{name = "substation", position = {siloPos.x+6, siloPos.y-5}, force = force}
|
||||
substation.destructible = false
|
||||
substation.minable = false
|
||||
-- bottom left
|
||||
local substation = surface.create_entity{name = "substation", position = {siloPos.x-5, siloPos.y+6}, force = force}
|
||||
substation.destructible = false
|
||||
substation.minable = false
|
||||
-- bottom right
|
||||
local substation = surface.create_entity{name = "substation", position = {siloPos.x+6, siloPos.y+6}, force = force}
|
||||
substation.destructible = false
|
||||
substation.minable = false
|
||||
|
||||
-- end adding beacons
|
||||
end
|
||||
|
||||
function PhilipsRadar(surface, siloPos, force)
|
||||
|
||||
local radar = surface.create_entity{name = "solar-panel", position = {siloPos.x-43, siloPos.y+3}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "solar-panel", position = {siloPos.x-43, siloPos.y-3}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "solar-panel", position = {siloPos.x-40, siloPos.y-6}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "solar-panel", position = {siloPos.x-37, siloPos.y-6}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "solar-panel", position = {siloPos.x-34, siloPos.y-6}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "solar-panel", position = {siloPos.x-34, siloPos.y-3}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "solar-panel", position = {siloPos.x-34, siloPos.y}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "solar-panel", position = {siloPos.x-34, siloPos.y+3}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "solar-panel", position = {siloPos.x-43, siloPos.y-6}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "solar-panel", position = {siloPos.x-40, siloPos.y+3}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "solar-panel", position = {siloPos.x-37, siloPos.y+3}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "radar", position = {siloPos.x-43, siloPos.y}, force = force}
|
||||
radar.destructible = false
|
||||
local substation = surface.create_entity{name = "substation", position = {siloPos.x-38, siloPos.y-1}, force = force}
|
||||
substation.destructible = false
|
||||
local radar = surface.create_entity{name = "accumulator", position = {siloPos.x-40, siloPos.y-1}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "accumulator", position = {siloPos.x-40, siloPos.y-3}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "accumulator", position = {siloPos.x-40, siloPos.y+1}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "accumulator", position = {siloPos.x-38, siloPos.y-3}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "accumulator", position = {siloPos.x-38, siloPos.y+1}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "accumulator", position = {siloPos.x-36, siloPos.y-1}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "accumulator", position = {siloPos.x-36, siloPos.y-3}, force = force}
|
||||
radar.destructible = false
|
||||
local radar = surface.create_entity{name = "accumulator", position = {siloPos.x-36, siloPos.y+1}, force = force}
|
||||
radar.destructible = false
|
||||
end
|
@ -1,160 +0,0 @@
|
||||
-- game_opts.lua
|
||||
-- Jan 2018
|
||||
-- Display current game options, maybe have some admin controls here
|
||||
|
||||
-- Main Configuration File
|
||||
require("config")
|
||||
require("lib/oarc_utils")
|
||||
require("lib/separate_spawns")
|
||||
|
||||
function GameOptionsGuiClick(event)
|
||||
if not (event and event.element and event.element.valid) then return end
|
||||
local player = game.players[event.player_index]
|
||||
local name = event.element.name
|
||||
|
||||
if (name == "ban_player") then
|
||||
local pIndex = event.element.parent.ban_players_dropdown.selected_index
|
||||
|
||||
if (pIndex ~= 0) then
|
||||
local banPlayer = event.element.parent.ban_players_dropdown.get_item(pIndex)
|
||||
if (game.players[banPlayer]) then
|
||||
game.ban_player(banPlayer, "Banned from admin panel.")
|
||||
log("Banning " .. banPlayer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (name == "restart_player") then
|
||||
local pIndex = event.element.parent.ban_players_dropdown.selected_index
|
||||
|
||||
if (pIndex ~= 0) then
|
||||
local resetPlayer = event.element.parent.ban_players_dropdown.get_item(pIndex)
|
||||
if (game.players[resetPlayer]) then
|
||||
RemoveOrResetPlayer(player, false, true, true, true)
|
||||
SeparateSpawnsPlayerCreated(resetPlayer, true)
|
||||
log("Resetting " .. resetPlayer)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Used by AddOarcGuiTab
|
||||
function CreateGameOptionsTab(tab_container, player)
|
||||
|
||||
if global.oarc_announcements ~= nil then
|
||||
AddLabel(tab_container, "announcement_info_label", "Server announcements:", my_label_header_style)
|
||||
AddLabel(tab_container, "announcement_info_txt", global.oarc_announcements, my_longer_label_style)
|
||||
AddSpacerLine(tab_container)
|
||||
end
|
||||
|
||||
-- General Server Info:
|
||||
AddLabel(tab_container, "info_1", global.ocfg.welcome_msg, my_longer_label_style)
|
||||
AddLabel(tab_container, "info_2", global.ocfg.server_rules, my_longer_label_style)
|
||||
AddLabel(tab_container, "info_3", global.ocfg.server_contact, my_longer_label_style)
|
||||
tab_container.add{type="textfield",
|
||||
tooltip="Come join the discord (copy this invite)!",
|
||||
text=DISCORD_INV}
|
||||
AddSpacerLine(tab_container)
|
||||
|
||||
-- Enemy Settings:
|
||||
local enemy_expansion_txt = "disabled"
|
||||
if game.map_settings.enemy_expansion.enabled then enemy_expansion_txt = "enabled" end
|
||||
|
||||
local enemy_text="Server Run Time: " .. formattime_hours_mins(game.tick) .. "\n" ..
|
||||
"Current Evolution: " .. string.format("%.4f", game.forces["enemy"].evolution_factor) .. "\n" ..
|
||||
"Enemy evolution time/pollution/destroy factors: " .. game.map_settings.enemy_evolution.time_factor .. "/" ..
|
||||
game.map_settings.enemy_evolution.pollution_factor .. "/" ..
|
||||
game.map_settings.enemy_evolution.destroy_factor .. "\n" ..
|
||||
"Enemy expansion is " .. enemy_expansion_txt
|
||||
|
||||
AddLabel(tab_container, "enemy_info", enemy_text, my_longer_label_style)
|
||||
AddSpacerLine(tab_container)
|
||||
|
||||
-- Soft Mods:
|
||||
local soft_mods_string = "Oarc Core"
|
||||
if (global.ocfg.enable_undecorator) then
|
||||
soft_mods_string = soft_mods_string .. ", Undecorator"
|
||||
end
|
||||
if (global.ocfg.enable_tags) then
|
||||
soft_mods_string = soft_mods_string .. ", Tags"
|
||||
end
|
||||
if (global.ocfg.enable_long_reach) then
|
||||
soft_mods_string = soft_mods_string .. ", Long Reach"
|
||||
end
|
||||
if (global.ocfg.enable_autofill) then
|
||||
soft_mods_string = soft_mods_string .. ", Auto Fill"
|
||||
end
|
||||
if (global.ocfg.enable_player_list) then
|
||||
soft_mods_string = soft_mods_string .. ", Player List"
|
||||
end
|
||||
if (global.ocfg.enable_regrowth) then
|
||||
soft_mods_string = soft_mods_string .. ", Regrowth"
|
||||
end
|
||||
if (global.ocfg.enable_chest_sharing) then
|
||||
soft_mods_string = soft_mods_string .. ", Item & Energy Sharing"
|
||||
end
|
||||
if (global.ocfg.enable_magic_factories) then
|
||||
soft_mods_string = soft_mods_string .. ", Special Map Chunks"
|
||||
end
|
||||
if (global.ocfg.enable_offline_protect) then
|
||||
soft_mods_string = soft_mods_string .. ", Offline Attack Inhibitor"
|
||||
end
|
||||
|
||||
local game_info_str = "Soft Mods: " .. soft_mods_string
|
||||
|
||||
-- Spawn options:
|
||||
if (global.ocfg.enable_separate_teams) then
|
||||
game_info_str = game_info_str.."\n".."You are allowed to spawn on your own team (have your own research tree). All teams are friendly!"
|
||||
end
|
||||
if (global.ocfg.enable_vanilla_spawns) then
|
||||
game_info_str = game_info_str.."\n".."You are spawned in a default style starting area."
|
||||
else
|
||||
game_info_str = game_info_str.."\n".."You are spawned with a fix set of starting resources."
|
||||
if (global.ocfg.enable_buddy_spawn) then
|
||||
game_info_str = game_info_str.."\n".."You can chose to spawn alongside a buddy if you spawn together at the same time."
|
||||
end
|
||||
end
|
||||
if (global.ocfg.enable_shared_spawns) then
|
||||
game_info_str = game_info_str.."\n".."Spawn hosts may choose to share their spawn and allow other players to join them."
|
||||
end
|
||||
if (global.ocfg.enable_separate_teams and global.ocfg.enable_shared_team_vision) then
|
||||
game_info_str = game_info_str.."\n".."Everyone (all teams) have shared vision."
|
||||
end
|
||||
if (global.ocfg.frontier_rocket_silo) then
|
||||
game_info_str = game_info_str.."\n".."Silos are only placeable in certain areas on the map!"
|
||||
end
|
||||
if (global.ocfg.enable_regrowth) then
|
||||
game_info_str = game_info_str.."\n".."Old parts of the map will slowly be deleted over time (chunks without any player buildings)."
|
||||
end
|
||||
if (global.ocfg.enable_power_armor_start or global.ocfg.enable_modular_armor_start) then
|
||||
game_info_str = game_info_str.."\n".."Quicker start enabled."
|
||||
end
|
||||
if (global.ocfg.lock_goodies_rocket_launch) then
|
||||
game_info_str = game_info_str.."\n".."Some technologies and recipes are locked until you launch a rocket!"
|
||||
end
|
||||
|
||||
|
||||
|
||||
AddLabel(tab_container, "game_info_label", game_info_str, my_longer_label_style)
|
||||
|
||||
if (global.ocfg.enable_abandoned_base_removal) then
|
||||
AddLabel(tab_container, "leave_warning_msg", "If you leave within " .. global.ocfg.minimum_online_time .. " minutes of joining, your base and character will be deleted.", my_longer_label_style)
|
||||
tab_container.leave_warning_msg.style.font_color=my_color_red
|
||||
end
|
||||
|
||||
-- Ending Spacer
|
||||
AddSpacerLine(tab_container)
|
||||
|
||||
-- ADMIN CONTROLS
|
||||
if (player.admin) then
|
||||
player_list = {}
|
||||
for _,player in pairs(game.connected_players) do
|
||||
table.insert(player_list, player.name)
|
||||
end
|
||||
tab_container.add{name = "ban_players_dropdown",
|
||||
type = "drop-down",
|
||||
items = player_list}
|
||||
tab_container.add{name="ban_player", type="button", caption="Ban Player"}
|
||||
tab_container.add{name="restart_player", type="button", caption="Restart Player"}
|
||||
end
|
||||
end
|
42
lib/gui_tabs/mod_info_faq.lua
Normal file
42
lib/gui_tabs/mod_info_faq.lua
Normal file
@ -0,0 +1,42 @@
|
||||
-- Contains the GUI for the regrowth controls tab.
|
||||
|
||||
---Used by AddOarcGuiTab
|
||||
---@param tab_container LuaGuiElement
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function CreateModInfoTab(tab_container, player)
|
||||
|
||||
local scroll_pane = tab_container.add {
|
||||
type = "scroll-pane",
|
||||
vertical_scroll_policy = "auto",
|
||||
}
|
||||
scroll_pane.style.maximal_height = GENERIC_GUI_MAX_HEIGHT
|
||||
scroll_pane.style.padding = 5
|
||||
|
||||
AddLabel(scroll_pane, nil, "Mod Info & FAQ", my_label_header2_style)
|
||||
AddSpacerLine(scroll_pane)
|
||||
|
||||
CreateFAQEntry(scroll_pane, { "oarc-mod-faq-what-is-this-mod" }, { "oarc-mod-faq-what-is-this-mod-answer" })
|
||||
CreateFAQEntry(scroll_pane, { "oarc-mod-faq-other-surfaces" }, { "oarc-mod-faq-other-surfaces-answer" })
|
||||
CreateFAQEntry(scroll_pane, { "oarc-mod-faq-secondary-spawns" }, { "oarc-mod-faq-secondary-spawns-answer" })
|
||||
CreateFAQEntry(scroll_pane, { "oarc-mod-faq-what-are-teams" }, { "oarc-mod-faq-what-are-teams-answer" })
|
||||
CreateFAQEntry(scroll_pane, { "oarc-mod-faq-shared-spawn" }, { "oarc-mod-faq-shared-spawn-answer" })
|
||||
CreateFAQEntry(scroll_pane, { "oarc-mod-faq-buddy-spawn" }, { "oarc-mod-faq-buddy-spawn-answer" })
|
||||
CreateFAQEntry(scroll_pane, { "oarc-mod-faq-regrowth" }, { "oarc-mod-faq-regrowth-answer" })
|
||||
CreateFAQEntry(scroll_pane, { "oarc-mod-faq-cleanup-abandoned" }, { "oarc-mod-faq-cleanup-abandoned-answer" })
|
||||
CreateFAQEntry(scroll_pane, { "oarc-mod-faq-offline-protection" }, { "oarc-mod-faq-offline-protection-answer" })
|
||||
CreateFAQEntry(scroll_pane, { "oarc-mod-faq-shared-power" }, { "oarc-mod-faq-shared-power-answer" })
|
||||
CreateFAQEntry(scroll_pane, { "oarc-mod-faq-shared-chest" }, { "oarc-mod-faq-shared-chest-answer" })
|
||||
|
||||
end
|
||||
|
||||
---Creates a FAQ entry in the tab
|
||||
---@param container LuaGuiElement
|
||||
---@param question LocalisedString
|
||||
---@param answer LocalisedString
|
||||
---@return nil
|
||||
function CreateFAQEntry(container, question, answer)
|
||||
AddLabel(container, nil, question, "caption_label")
|
||||
AddLabel(container, nil, answer, my_longer_label_style)
|
||||
AddSpacerLine(container)
|
||||
end
|
142
lib/gui_tabs/server_info.lua
Normal file
142
lib/gui_tabs/server_info.lua
Normal file
@ -0,0 +1,142 @@
|
||||
---Display current game options and server info, maybe have some admin controls here
|
||||
|
||||
---Creates the content for the game settings used by AddOarcGuiTab
|
||||
---@param tab_container LuaGuiElement
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function CreateServerInfoTab(tab_container, player)
|
||||
|
||||
-- General Server Info:
|
||||
if (global.ocfg.server_info.welcome_msg ~= " ") then
|
||||
AddLabel(tab_container, nil, {"oarc-server-info-tab-welcome-msg-title"}, "caption_label")
|
||||
AddLabel(tab_container, nil, global.ocfg.server_info.welcome_msg, my_longer_label_style)
|
||||
AddSpacerLine(tab_container)
|
||||
end
|
||||
|
||||
if (global.ocfg.server_info.discord_invite ~= " ") then
|
||||
local horizontal_flow = tab_container.add{
|
||||
type="flow", direction="horizontal"
|
||||
}
|
||||
AddLabel(horizontal_flow, nil, {"oarc-server-info-tab-discord-invite"}, "caption_label")
|
||||
horizontal_flow.add{
|
||||
type="textfield",
|
||||
tooltip={"oarc-server-info-tab-discord-invite-tooltip"},
|
||||
text=global.ocfg.server_info.discord_invite
|
||||
}
|
||||
AddSpacerLine(tab_container)
|
||||
end
|
||||
|
||||
AddLabel(tab_container, nil, {"oarc-server-info-tab-map-info-label"}, "caption_label")
|
||||
AddLabel(tab_container, nil, {"oarc-server-info-tab-server-run-time", FormatTimeHoursSecs(game.tick)}, my_label_style)
|
||||
--TODO: Add more stuff here maybe? Like in the old version?
|
||||
|
||||
if (global.ocfg.regrowth.enable_abandoned_base_cleanup) then
|
||||
local label = AddLabel(tab_container, nil, {"oarc-server-info-leave-warning", global.ocfg.gameplay.minimum_online_time}, my_longer_label_style)
|
||||
label.style.font_color=my_color_red
|
||||
end
|
||||
|
||||
-- Ending Spacer
|
||||
AddSpacerLine(tab_container)
|
||||
|
||||
-- ADMIN CONTROLS
|
||||
if (player.admin) then
|
||||
player_list = {}
|
||||
for _, p in pairs(game.connected_players) do
|
||||
table.insert(player_list, p.name)
|
||||
end
|
||||
|
||||
AddLabel(tab_container, nil, {"oarc-server-info-admin-controls"}, "caption_label")
|
||||
|
||||
local horizontal_flow = tab_container.add{
|
||||
type="flow", direction="horizontal"
|
||||
}
|
||||
horizontal_flow.style.horizontally_stretchable = true
|
||||
|
||||
AddLabel(horizontal_flow, nil, {"oarc-server-info-ban-select-player"}, my_label_style)
|
||||
local drop_down = horizontal_flow.add{
|
||||
name = "ban_players_dropdown",
|
||||
tags = { action = "oarc_server_info_tab", setting = "ban_players_dropdown" },
|
||||
type = "drop-down",
|
||||
items = player_list
|
||||
}
|
||||
|
||||
-- If there is only one player, select it by default (for testing convenience)
|
||||
if (#player_list == 1) then
|
||||
drop_down.selected_index = 1
|
||||
end
|
||||
|
||||
local dragger = horizontal_flow.add{
|
||||
type="empty-widget",
|
||||
style="draggable_space_header"
|
||||
}
|
||||
dragger.style.horizontally_stretchable = true
|
||||
|
||||
horizontal_flow.add{
|
||||
name="ban_player",
|
||||
tags = { action = "oarc_server_info_tab", setting = "ban_player" },
|
||||
type="button",
|
||||
caption={"oarc-server-info-button-ban-player"},
|
||||
style = "red_button"
|
||||
}
|
||||
horizontal_flow.add{
|
||||
name="restart_player",
|
||||
tags = { action = "oarc_server_info_tab", setting = "restart_player" },
|
||||
type="button",
|
||||
caption={"oarc-server-info-button-restart-player"},
|
||||
style = "red_button"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---Server info gui click event handler
|
||||
---@param event EventData.on_gui_click
|
||||
---@return nil
|
||||
function ServerInfoGuiClick(event)
|
||||
if not event.element.valid then return end
|
||||
local player = game.players[event.player_index]
|
||||
local tags = event.element.tags
|
||||
|
||||
if (tags.action ~= "oarc_server_info_tab") then
|
||||
return
|
||||
end
|
||||
|
||||
local player_dropdown = event.element.parent.ban_players_dropdown
|
||||
|
||||
if (tags.setting == "ban_player") then
|
||||
local pIndex = player_dropdown.selected_index
|
||||
|
||||
if (pIndex ~= 0) then
|
||||
local banPlayer = player_dropdown.get_item(pIndex)
|
||||
if (game.players[banPlayer]) then
|
||||
game.ban_player(banPlayer --[[@as string]], "Banned from admin panel by " .. player.name)
|
||||
log("Banning " .. banPlayer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (tags.setting == "restart_player") then
|
||||
local pIndex = player_dropdown.selected_index
|
||||
|
||||
if (pIndex ~= 0) then
|
||||
local resetPlayer = player_dropdown.get_item(pIndex)
|
||||
|
||||
if not game.players[resetPlayer] or not game.players[resetPlayer].connected then
|
||||
SendMsg(player.name, {"oarc-player-not-found", resetPlayer})
|
||||
return
|
||||
end
|
||||
|
||||
if PlayerHasDelayedSpawn(resetPlayer--[[@as string]]) then
|
||||
SendMsg(player.name, {"oarc-player-about-to-spawn", resetPlayer})
|
||||
return
|
||||
end
|
||||
|
||||
log("Resetting " .. resetPlayer)
|
||||
RemoveOrResetPlayer(game.players[resetPlayer], false)
|
||||
SeparateSpawnsInitPlayer(resetPlayer --[[@as string]])
|
||||
else
|
||||
SendMsg(player.name, {"oarc-player-none-selected"})
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
505
lib/gui_tabs/settings_controls.lua
Normal file
505
lib/gui_tabs/settings_controls.lua
Normal file
@ -0,0 +1,505 @@
|
||||
-- Contains the GUI for the controlling various settings of the mod.
|
||||
|
||||
---Creates the content in the tab used by AddOarcGuiTab
|
||||
---@param tab_container LuaGuiElement
|
||||
---@param player LuaPlayer
|
||||
function CreateSettingsControlsTab(tab_container, player)
|
||||
|
||||
if (player.admin) then
|
||||
local label = AddLabel(tab_container, nil, { "oarc-settings-tab-admin-warning" }, my_warning_style)
|
||||
|
||||
else
|
||||
local label = AddLabel(tab_container, nil, { "oarc-settings-tab-player-warning" }, my_warning_style)
|
||||
|
||||
end
|
||||
|
||||
local label = AddLabel(tab_container, nil, { "oarc-settings-tab-description" }, my_label_style)
|
||||
label.style.bottom_padding = 5
|
||||
|
||||
local flow = tab_container.add { type = "flow", direction = "horizontal", }
|
||||
|
||||
local scroll_pane_left = flow.add {
|
||||
type = "scroll-pane",
|
||||
direction = "vertical",
|
||||
vertical_scroll_policy = "always",
|
||||
}
|
||||
scroll_pane_left.style.maximal_height = GENERIC_GUI_MAX_HEIGHT
|
||||
scroll_pane_left.style.padding = 5
|
||||
scroll_pane_left.style.right_margin = 2
|
||||
CreateModSettingsSection(scroll_pane_left, player)
|
||||
|
||||
local scroll_pane_right = flow.add {
|
||||
type = "scroll-pane",
|
||||
direction = "vertical",
|
||||
vertical_scroll_policy = "always",
|
||||
}
|
||||
scroll_pane_right.style.maximal_height = GENERIC_GUI_MAX_HEIGHT
|
||||
scroll_pane_right.style.padding = 5
|
||||
scroll_pane_right.style.left_margin = 2
|
||||
CreateSurfaceSettingsSection(scroll_pane_right, player)
|
||||
end
|
||||
|
||||
---Create the content for the mod settings section
|
||||
---@param container LuaGuiElement
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function CreateModSettingsSection(container, player)
|
||||
AddLabel(container, nil, { "oarc-settings-tab-title-mod-settings" }, my_label_header2_style)
|
||||
|
||||
for index,entry in pairs(OCFG_KEYS) do
|
||||
if (entry.type == "header") then
|
||||
AddSpacerLine(container)
|
||||
AddLabel(container, nil, entry.text, "caption_label")
|
||||
elseif (entry.type == "subheader") then
|
||||
AddLabel(container, nil, entry.text, "bold_label")
|
||||
elseif (entry.type == "boolean") then
|
||||
AddCheckboxSetting(container, index, entry, player.admin)
|
||||
elseif (entry.type == "string") then
|
||||
AddTextfieldSetting(container, index, entry, player.admin)
|
||||
elseif (entry.type == "integer") then
|
||||
AddIntegerSetting(container, index, entry, player.admin)
|
||||
elseif (entry.type == "double") then
|
||||
AddDoubleSetting(container, index, entry, player.admin)
|
||||
elseif (entry.type == "string-list") then
|
||||
AddStringListDropdownSetting(container, index, entry, player.admin)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Create the content for the surface settings section
|
||||
---@param container LuaGuiElement
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function CreateSurfaceSettingsSection(container, player)
|
||||
AddLabel(container, nil, { "oarc-settings-tab-title-surface" }, my_label_header2_style)
|
||||
|
||||
--- Create a table with 3 columns. Surface Name, Spawning Enabled, Regrowth Enabled
|
||||
local surface_table = container.add {
|
||||
type = "table",
|
||||
name = "surface_table",
|
||||
column_count = 3,
|
||||
style = "bordered_table",
|
||||
}
|
||||
|
||||
--- Add the header row
|
||||
AddLabel(surface_table, nil, {"oarc-settings-tab-surface-column-header"}, "caption_label")
|
||||
AddLabel(surface_table, nil, {"oarc-settings-tab-surface-spawning-enabled"}, "caption_label")
|
||||
AddLabel(surface_table, nil, {"oarc-settings-tab-surface-regrowth-enabled"}, "caption_label")
|
||||
|
||||
--- Add the rows
|
||||
for name, allowed in pairs(global.oarc_surfaces --[[@as table<string, boolean>]]) do
|
||||
AddLabel(surface_table, nil, name, my_label_style)
|
||||
AddSurfaceCheckboxSetting(surface_table, name, "spawn_enabled", allowed, player.admin,
|
||||
{ "oarc-settings-tab-surface-checkbox-tooltip" })
|
||||
local regrowth_enabled = TableContains(global.rg.active_surfaces, name)
|
||||
AddSurfaceCheckboxSetting(surface_table, name, "regrowth_enabled", regrowth_enabled, player.admin,
|
||||
{"oarc-settings-tab-surface-regrowth-checkbox-tooltip"})
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---Handles the click event for the tab used by AddOarcGuiTab
|
||||
---@param event EventData.on_gui_click
|
||||
---@return nil
|
||||
function SettingsControlsTabGuiClick(event)
|
||||
if not (event.element.valid) then return end
|
||||
|
||||
local gui_elem = event.element
|
||||
if (gui_elem.tags.action ~= "oarc_settings_tab") then return end
|
||||
local index = gui_elem.tags.setting
|
||||
|
||||
local entry = OCFG_KEYS[index]
|
||||
if (entry.type == "boolean") then
|
||||
settings.global[entry.mod_key] = { value = gui_elem.state }
|
||||
end
|
||||
end
|
||||
|
||||
---Handles the text entry event for the tab used by AddOarcGuiTab
|
||||
---@param event EventData.on_gui_text_changed
|
||||
---@return nil
|
||||
function SettingsControlsTabGuiTextChanged(event)
|
||||
if not (event.element.valid) then return end
|
||||
|
||||
local gui_elem = event.element
|
||||
if (gui_elem.tags.action ~= "oarc_settings_tab") then return end
|
||||
local index = gui_elem.tags.setting
|
||||
local value = gui_elem.text
|
||||
local entry = OCFG_KEYS[index]
|
||||
|
||||
if (entry.type == "string") then
|
||||
gui_elem.style = "invalid_value_textfield"
|
||||
elseif (entry.type == "integer") then
|
||||
gui_elem.style = "invalid_value_textfield"
|
||||
gui_elem.style.width = 50
|
||||
end
|
||||
end
|
||||
|
||||
---Handles the confirmed text entry event
|
||||
---@param event EventData.on_gui_confirmed
|
||||
---@return nil
|
||||
function SettingsControlsTabGuiTextconfirmed(event)
|
||||
if not (event.element.valid) then return end
|
||||
|
||||
local gui_elem = event.element
|
||||
if (gui_elem.tags.action ~= "oarc_settings_tab") then return end
|
||||
local index = gui_elem.tags.setting
|
||||
local value = gui_elem.text
|
||||
local entry = OCFG_KEYS[index]
|
||||
|
||||
if (entry.type == "string") then
|
||||
if value == "" then -- Force a non-empty string!
|
||||
value = " "
|
||||
gui_elem.text = " "
|
||||
end
|
||||
gui_elem.style = "textbox"
|
||||
settings.global[entry.mod_key] = { value = gui_elem.text }
|
||||
elseif (entry.type == "integer") then
|
||||
local safe_value = GetSafeIntValueForModSetting(value, entry.mod_key)
|
||||
if not pcall(function() settings.global[entry.mod_key] = { value = safe_value } end) then
|
||||
settings.global[entry.mod_key] = { value = game.mod_setting_prototypes[entry.mod_key].default_value }
|
||||
log("Error setting value for " .. entry.mod_key .. " to " .. safe_value)
|
||||
end
|
||||
gui_elem.text = tostring(settings.global[entry.mod_key].value)
|
||||
gui_elem.style = "textbox"
|
||||
gui_elem.style.width = 50
|
||||
|
||||
local slider = gui_elem.parent["slider"]
|
||||
slider.slider_value = settings.global[entry.mod_key].value --[[@as integer]]
|
||||
elseif (entry.type == "double") then
|
||||
local safe_value = GetSafeDoubleValueForModSetting(value, entry.mod_key)
|
||||
if not pcall(function() settings.global[entry.mod_key] = { value = safe_value } end) then
|
||||
settings.global[entry.mod_key] = { value = game.mod_setting_prototypes[entry.mod_key].default_value }
|
||||
log("Error setting value for " .. entry.mod_key .. " to " .. safe_value)
|
||||
end
|
||||
gui_elem.text = string.format("%.2f", settings.global[entry.mod_key].value)
|
||||
gui_elem.style = "textbox"
|
||||
gui_elem.style.width = 50
|
||||
|
||||
local slider = gui_elem.parent["slider"]
|
||||
slider.slider_value = settings.global[entry.mod_key].value --[[@as number]]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---Handles slider value changes
|
||||
---@param event EventData.on_gui_value_changed
|
||||
---@return nil
|
||||
function SettingsControlsTabGuiValueChanged(event)
|
||||
if not (event.element.valid) then return end
|
||||
|
||||
local gui_elem = event.element
|
||||
if (gui_elem.tags.action ~= "oarc_settings_tab_slider") then return end
|
||||
local index = gui_elem.tags.setting
|
||||
local value = gui_elem.slider_value
|
||||
local entry = OCFG_KEYS[index]
|
||||
|
||||
if (entry.type == "integer") then
|
||||
local textfield = gui_elem.parent["textfield"]
|
||||
settings.global[entry.mod_key] = { value = value } -- Assumes that the slider can only produce valid inputs!
|
||||
textfield.text = tostring(value)
|
||||
elseif (entry.type == "double") then
|
||||
local textfield = gui_elem.parent["textfield"]
|
||||
settings.global[entry.mod_key] = { value = value } -- Assumes that the slider can only produce valid inputs!
|
||||
textfield.text = string.format("%.2f", value)
|
||||
end
|
||||
end
|
||||
|
||||
---Makes sure a given value is within the min/max range of a mod setting
|
||||
---@param input string|number|integer
|
||||
---@param mod_key string
|
||||
---@return integer
|
||||
function GetSafeIntValueForModSetting(input, mod_key)
|
||||
local value_num = tonumber(input)
|
||||
if not value_num then
|
||||
value_num = tonumber(game.mod_setting_prototypes[mod_key].default_value)
|
||||
else
|
||||
local minimum = game.mod_setting_prototypes[mod_key].minimum_value
|
||||
local maximum = game.mod_setting_prototypes[mod_key].maximum_value
|
||||
if minimum ~= nil then
|
||||
value_num = math.max(value_num, minimum)
|
||||
end
|
||||
if maximum ~= nil then
|
||||
value_num = math.min(value_num, maximum)
|
||||
end
|
||||
value_num = math.floor(value_num)
|
||||
end
|
||||
return value_num --[[@as integer]]
|
||||
end
|
||||
|
||||
---Makes sure a given value is within the min/max range of a mod setting (double)
|
||||
---@param input string|number|integer
|
||||
---@param mod_key string
|
||||
---@return number
|
||||
function GetSafeDoubleValueForModSetting(input, mod_key)
|
||||
local value_num = tonumber(input)
|
||||
if not value_num then
|
||||
value_num = tonumber(game.mod_setting_prototypes[mod_key].default_value)
|
||||
else
|
||||
local minimum = game.mod_setting_prototypes[mod_key].minimum_value
|
||||
local maximum = game.mod_setting_prototypes[mod_key].maximum_value
|
||||
if minimum ~= nil then
|
||||
value_num = math.max(value_num, minimum)
|
||||
end
|
||||
if maximum ~= nil then
|
||||
value_num = math.min(value_num, maximum)
|
||||
end
|
||||
end
|
||||
return value_num --[[@as number]]
|
||||
end
|
||||
|
||||
---Creates a checkbox setting
|
||||
---@param tab_container LuaGuiElement
|
||||
---@param index string
|
||||
---@param entry OarcSettingsLookup
|
||||
---@param enabled boolean
|
||||
---@return nil
|
||||
function AddCheckboxSetting(tab_container, index, entry, enabled)
|
||||
tab_container.add{
|
||||
type = "checkbox",
|
||||
caption = { "mod-setting-name."..entry.mod_key },
|
||||
state = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
|
||||
enabled = enabled,
|
||||
tooltip = { "mod-setting-description."..entry.mod_key },
|
||||
tags = { action = "oarc_settings_tab", setting = index },
|
||||
}
|
||||
end
|
||||
|
||||
---Creates a textfield setting
|
||||
---@param tab_container LuaGuiElement
|
||||
---@param index string
|
||||
---@param entry OarcSettingsLookup
|
||||
---@param enabled boolean
|
||||
---@return nil
|
||||
function AddTextfieldSetting(tab_container, index, entry, enabled)
|
||||
local horizontal_flow = tab_container.add {
|
||||
type = "flow",
|
||||
direction = "horizontal",
|
||||
}
|
||||
horizontal_flow.add {
|
||||
type = "label",
|
||||
caption = { "mod-setting-name."..entry.mod_key },
|
||||
tooltip = { "mod-setting-description."..entry.mod_key },
|
||||
}
|
||||
local dragger = horizontal_flow.add {
|
||||
type = "empty-widget",
|
||||
}
|
||||
dragger.style.horizontally_stretchable = true
|
||||
|
||||
local tooltip = {"", {"mod-setting-description."..entry.mod_key }, " ", { "oarc-settings-tab-text-field-enter-tooltip" }}
|
||||
|
||||
horizontal_flow.add {
|
||||
type = "textfield",
|
||||
caption = { "mod-setting-name."..entry.mod_key },
|
||||
text = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
|
||||
enabled = enabled,
|
||||
tooltip = tooltip,
|
||||
tags = { action = "oarc_settings_tab", setting = index },
|
||||
}
|
||||
end
|
||||
|
||||
---Creates an integer setting
|
||||
---@param tab_container LuaGuiElement
|
||||
---@param index string
|
||||
---@param entry OarcSettingsLookup
|
||||
---@param enabled boolean
|
||||
---@return nil
|
||||
function AddIntegerSetting(tab_container, index, entry, enabled)
|
||||
local horizontal_flow = tab_container.add {
|
||||
type = "flow",
|
||||
direction = "horizontal",
|
||||
}
|
||||
horizontal_flow.add {
|
||||
type = "label",
|
||||
caption = { "mod-setting-name."..entry.mod_key },
|
||||
tooltip = { "mod-setting-description."..entry.mod_key },
|
||||
}
|
||||
local dragger = horizontal_flow.add {
|
||||
type = "empty-widget",
|
||||
}
|
||||
dragger.style.horizontally_stretchable = true
|
||||
|
||||
local slider = horizontal_flow.add {
|
||||
name = "slider",
|
||||
type = "slider",
|
||||
minimum_value = game.mod_setting_prototypes[entry.mod_key].minimum_value,
|
||||
maximum_value = game.mod_setting_prototypes[entry.mod_key].maximum_value,
|
||||
value = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
|
||||
enabled = enabled,
|
||||
tooltip = { "mod-setting-description."..entry.mod_key },
|
||||
tags = { action = "oarc_settings_tab_slider", setting = index },
|
||||
discrete_values = true,
|
||||
value_step = 1,
|
||||
}
|
||||
|
||||
local tooltip = {"", {"mod-setting-description."..entry.mod_key }, " ", { "oarc-settings-tab-text-field-enter-tooltip" }}
|
||||
local textfield = horizontal_flow.add {
|
||||
name = "textfield",
|
||||
type = "textfield",
|
||||
numeric = true,
|
||||
caption = { "mod-setting-name."..entry.mod_key },
|
||||
text = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
|
||||
enabled = enabled,
|
||||
tooltip = tooltip,
|
||||
tags = { action = "oarc_settings_tab", setting = index },
|
||||
}
|
||||
textfield.style.width = 50
|
||||
end
|
||||
|
||||
---Creates a double setting
|
||||
---@param tab_container LuaGuiElement
|
||||
---@param index string
|
||||
---@param entry OarcSettingsLookup
|
||||
---@param enabled boolean
|
||||
---@return nil
|
||||
function AddDoubleSetting(tab_container, index, entry, enabled)
|
||||
local horizontal_flow = tab_container.add {
|
||||
type = "flow",
|
||||
direction = "horizontal",
|
||||
}
|
||||
horizontal_flow.add {
|
||||
type = "label",
|
||||
caption = { "mod-setting-name."..entry.mod_key },
|
||||
tooltip = { "mod-setting-description."..entry.mod_key },
|
||||
}
|
||||
local dragger = horizontal_flow.add {
|
||||
type = "empty-widget",
|
||||
}
|
||||
dragger.style.horizontally_stretchable = true
|
||||
|
||||
local slider = horizontal_flow.add {
|
||||
name = "slider",
|
||||
type = "slider",
|
||||
minimum_value = game.mod_setting_prototypes[entry.mod_key].minimum_value,
|
||||
maximum_value = game.mod_setting_prototypes[entry.mod_key].maximum_value,
|
||||
value = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
|
||||
enabled = enabled,
|
||||
tooltip = { "mod-setting-description."..entry.mod_key },
|
||||
tags = { action = "oarc_settings_tab_slider", setting = index },
|
||||
discrete_values = false,
|
||||
value_step = 0.01,
|
||||
}
|
||||
|
||||
local tooltip = {"", {"mod-setting-description."..entry.mod_key }, " ", { "oarc-settings-tab-text-field-enter-tooltip" }}
|
||||
local textfield = horizontal_flow.add {
|
||||
name = "textfield",
|
||||
type = "textfield",
|
||||
numeric = true,
|
||||
allow_decimal = true,
|
||||
caption = { "mod-setting-name."..entry.mod_key },
|
||||
text = string.format("%.2f", GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys)),
|
||||
enabled = enabled,
|
||||
tooltip = tooltip,
|
||||
tags = { action = "oarc_settings_tab", setting = index },
|
||||
}
|
||||
textfield.style.width = 50
|
||||
end
|
||||
|
||||
---Create a dropdown setting for a string setting with allowed_values set
|
||||
---@param tab_container LuaGuiElement
|
||||
---@param index string
|
||||
---@param entry OarcSettingsLookup
|
||||
---@param enabled boolean
|
||||
---@return nil
|
||||
function AddStringListDropdownSetting(tab_container, index, entry, enabled)
|
||||
local horizontal_flow = tab_container.add {
|
||||
type = "flow",
|
||||
direction = "horizontal",
|
||||
}
|
||||
horizontal_flow.add {
|
||||
type = "label",
|
||||
caption = { "mod-setting-name."..entry.mod_key },
|
||||
tooltip = { "mod-setting-description."..entry.mod_key },
|
||||
}
|
||||
local dragger = horizontal_flow.add {
|
||||
type = "empty-widget",
|
||||
}
|
||||
dragger.style.horizontally_stretchable = true
|
||||
|
||||
local allowed_values = game.mod_setting_prototypes[entry.mod_key].allowed_values --[[@as string[] ]]
|
||||
|
||||
local selected_index = 1
|
||||
for i,v in pairs(allowed_values) do
|
||||
if (v == GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys)) then
|
||||
selected_index = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local dropdown = horizontal_flow.add {
|
||||
type = "drop-down",
|
||||
items = allowed_values,
|
||||
selected_index = selected_index,
|
||||
enabled = enabled,
|
||||
tooltip = { "mod-setting-description."..entry.mod_key },
|
||||
tags = { action = "oarc_settings_tab", setting = index },
|
||||
}
|
||||
end
|
||||
|
||||
---Creates a checkbox setting for surface related settings.
|
||||
---@param parent LuaGuiElement
|
||||
---@param surface_name string
|
||||
---@param setting_name string
|
||||
---@param state boolean
|
||||
---@param admin boolean
|
||||
---@param tooltip LocalisedString
|
||||
---@return nil
|
||||
function AddSurfaceCheckboxSetting(parent, surface_name, setting_name, state, admin, tooltip)
|
||||
parent.add{
|
||||
name = surface_name.."_"..setting_name,
|
||||
type = "checkbox",
|
||||
state = state,
|
||||
tags = { action = "oarc_settings_tab_surfaces", setting = setting_name, surface = surface_name },
|
||||
enabled = admin,
|
||||
tooltip = tooltip,
|
||||
}
|
||||
end
|
||||
|
||||
---Handles the click event for surface related settings
|
||||
---@param event EventData.on_gui_click
|
||||
---@return nil
|
||||
function SettingsSurfaceControlsTabGuiClick(event)
|
||||
if not (event.element.valid) then return end
|
||||
|
||||
local gui_elem = event.element
|
||||
if (gui_elem.tags.action ~= "oarc_settings_tab_surfaces") then return end
|
||||
local setting_name = gui_elem.tags.setting
|
||||
local surface_name = gui_elem.tags.surface --[[@as string]]
|
||||
|
||||
if (setting_name == "spawn_enabled") then
|
||||
global.oarc_surfaces[surface_name] = gui_elem.state
|
||||
|
||||
if (#GetAllowedSurfaces() == 0) then
|
||||
log("Warning - GetAllowedSurfaces() - No surfaces found! Forcing default surface!")
|
||||
global.oarc_surfaces[global.ocfg.gameplay.default_surface] = true
|
||||
event.element.parent[global.ocfg.gameplay.default_surface.."_spawn_enabled"].state = true
|
||||
end
|
||||
elseif (setting_name == "regrowth_enabled") then
|
||||
|
||||
if (gui_elem.state) then
|
||||
if not IsRegrowthEnabledOnSurface(surface_name) then
|
||||
RegrowthEnableSurface(surface_name)
|
||||
end
|
||||
else
|
||||
if IsRegrowthEnabledOnSurface(surface_name) then
|
||||
RegrowthDisableSurface(surface_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---Handles dropdown selection events
|
||||
---@param event EventData.on_gui_selection_state_changed
|
||||
---@return nil
|
||||
function SettingsControlsTabGuiSelectionStateChanged(event)
|
||||
if not (event.element.valid) then return end
|
||||
|
||||
local gui_elem = event.element
|
||||
if (gui_elem.tags.action ~= "oarc_settings_tab") then return end
|
||||
local index = gui_elem.tags.setting
|
||||
local entry = OCFG_KEYS[index]
|
||||
|
||||
if (entry.type == "string-list") then
|
||||
settings.global[entry.mod_key] = { value = gui_elem.items[gui_elem.selected_index] }
|
||||
end
|
||||
end
|
377
lib/gui_tabs/spawn_controls.lua
Normal file
377
lib/gui_tabs/spawn_controls.lua
Normal file
@ -0,0 +1,377 @@
|
||||
-- Spawn control tab in the Oarc GUI, for things like sharing your base with others.
|
||||
|
||||
---Provides the content of the spawn control tab in the Oarc GUI.
|
||||
---@param tab_container LuaGuiElement
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function CreateSpawnControlsTab(tab_container, player)
|
||||
local spwnCtrls = tab_container.add {
|
||||
type = "scroll-pane",
|
||||
name = "spwn_ctrl_panel",
|
||||
caption = ""
|
||||
}
|
||||
ApplyStyle(spwnCtrls, my_fixed_width_style)
|
||||
spwnCtrls.style.maximal_height = 1000
|
||||
spwnCtrls.horizontal_scroll_policy = "never"
|
||||
|
||||
CreatePrimarySpawnInfo(player, spwnCtrls)
|
||||
CreateSecondarySpawnInfo(player, spwnCtrls)
|
||||
CreateSetRespawnLocationButton(player, spwnCtrls)
|
||||
|
||||
if global.ocfg.gameplay.enable_shared_spawns then
|
||||
CreateSharedSpawnControls(player, spwnCtrls)
|
||||
CreateJoinQueueControls(player, spwnCtrls)
|
||||
end
|
||||
end
|
||||
|
||||
---Display some general info about the player's home base (different from respawn point).
|
||||
---There should ONLY be one of these per player? Or maybe there are one per surface
|
||||
---@param player LuaPlayer
|
||||
---@param container LuaGuiElement
|
||||
---@return nil
|
||||
function CreatePrimarySpawnInfo(player, container)
|
||||
local primary_spawn = FindPrimaryUniqueSpawn(player.name)
|
||||
if (primary_spawn == nil) then return end
|
||||
|
||||
AddLabel(container, nil, { "oarc-primary-spawn-info-header" }, "caption_label")
|
||||
AddLabel(container, nil, { "oarc-primary-spawn-info-note" }, my_label_style)
|
||||
|
||||
local horizontal_flow = container.add { type = "flow", direction = "horizontal"}
|
||||
horizontal_flow.style.vertical_align = "center"
|
||||
AddLabel(horizontal_flow, nil, { "oarc-primary-spawn-info-surface-label",
|
||||
primary_spawn.surface_name,
|
||||
primary_spawn.position.x,
|
||||
primary_spawn.position.y}, my_label_style)
|
||||
|
||||
--Add empty widget
|
||||
local dragger = horizontal_flow.add {
|
||||
type = "empty-widget",
|
||||
style = "draggable_space_header"
|
||||
}
|
||||
dragger.style.horizontally_stretchable = true
|
||||
|
||||
CreateGPSButton(horizontal_flow, primary_spawn.surface_name, primary_spawn.position)
|
||||
|
||||
AddSpacerLine(container)
|
||||
end
|
||||
|
||||
---Display some general info about the player's secondary spawn points.
|
||||
---@param player LuaPlayer
|
||||
---@param container LuaGuiElement
|
||||
---@return nil
|
||||
function CreateSecondarySpawnInfo(player, container)
|
||||
local secondary_spawns = FindSecondaryUniqueSpawns(player.name)
|
||||
if (secondary_spawns == nil) or (table_size(secondary_spawns) == 0) then return end
|
||||
|
||||
AddLabel(container, nil, { "oarc-secondary-spawn-info-header" }, "caption_label")
|
||||
AddLabel(container, nil, { "oarc-secondary-spawn-info-note" }, my_label_style)
|
||||
|
||||
for _,secondary_spawn in pairs(secondary_spawns) do
|
||||
local horizontal_flow = container.add { type = "flow", direction = "horizontal"}
|
||||
horizontal_flow.style.vertical_align = "center"
|
||||
AddLabel(horizontal_flow, nil, { "oarc-secondary-spawn-info-surface-label",
|
||||
secondary_spawn.surface_name,
|
||||
secondary_spawn.position.x,
|
||||
secondary_spawn.position.y}, my_label_style)
|
||||
|
||||
--Add empty widget
|
||||
local dragger = horizontal_flow.add {
|
||||
type = "empty-widget",
|
||||
style = "draggable_space_header"
|
||||
}
|
||||
dragger.style.horizontally_stretchable = true
|
||||
|
||||
CreateGPSButton(horizontal_flow, secondary_spawn.surface_name, secondary_spawn.position)
|
||||
end
|
||||
|
||||
AddSpacerLine(container)
|
||||
end
|
||||
|
||||
---Display the shared spawn controls
|
||||
---@param player LuaPlayer
|
||||
---@param container LuaGuiElement
|
||||
---@return nil
|
||||
function CreateSharedSpawnControls(player, container)
|
||||
local primary_spawn = FindPrimaryUniqueSpawn(player.name)
|
||||
if (primary_spawn == nil) then return end
|
||||
|
||||
AddLabel(container, nil, { "oarc-shared-spawn-controls" }, "caption_label")
|
||||
|
||||
local shared_spawn_open = IsSharedSpawnOpen(primary_spawn.surface_name, player.name)
|
||||
local shared_spawn_full = IsSharedSpawnFull(primary_spawn.surface_name, player.name)
|
||||
|
||||
-- This checkbox allows people to join your base when they first start the game.
|
||||
local toggle = container.add {
|
||||
type = "checkbox",
|
||||
name = "accessToggle",
|
||||
tags = { action = "oarc_spawn_ctrl_tab", setting = "shared_access_toggle" },
|
||||
caption = { "oarc-shared-spawn-allow-joiners" },
|
||||
state = shared_spawn_open,
|
||||
enabled = not shared_spawn_full -- Disable if the shared spawn is full
|
||||
}
|
||||
|
||||
if shared_spawn_open and shared_spawn_full then
|
||||
AddLabel(container, nil, { "oarc-shared-spawn-full" }, my_note_style)
|
||||
end
|
||||
|
||||
ApplyStyle(toggle, my_fixed_width_style)
|
||||
end
|
||||
|
||||
---Display the set respawn location button in the spawn control tab.
|
||||
---@param player LuaPlayer
|
||||
---@param container LuaGuiElement
|
||||
---@return nil
|
||||
function CreateSetRespawnLocationButton(player, container)
|
||||
AddLabel(container, nil, { "oarc-set-respawn-loc-header" }, "caption_label")
|
||||
|
||||
--[[@type OarcPlayerSpawn]]
|
||||
local respawn_info = global.player_respawns[player.name][player.surface.name]
|
||||
|
||||
if (respawn_info == nil) then
|
||||
log("ERROR: No respawn info for player: " .. player.name)
|
||||
return
|
||||
end
|
||||
|
||||
local respawn_surface_name = respawn_info.surface
|
||||
local respawn_position = respawn_info.position
|
||||
|
||||
-- Display the current respawn location
|
||||
local horizontal_flow = container.add { type = "flow", direction = "horizontal"}
|
||||
horizontal_flow.style.vertical_align = "center"
|
||||
AddLabel(horizontal_flow, nil, { "oarc-set-respawn-loc-info-surface-label",
|
||||
respawn_surface_name,
|
||||
respawn_position.x,
|
||||
respawn_position.y }, my_label_style)
|
||||
|
||||
--Add empty widget
|
||||
local dragger = horizontal_flow.add {
|
||||
type = "empty-widget",
|
||||
style = "draggable_space_header"
|
||||
}
|
||||
dragger.style.horizontally_stretchable = true
|
||||
|
||||
CreateGPSButton(horizontal_flow, respawn_surface_name, respawn_position)
|
||||
|
||||
-- Sets the player's custom spawn point to their current location
|
||||
if ((game.tick - global.player_cooldowns[player.name].setRespawn) >
|
||||
(global.ocfg.gameplay.respawn_cooldown_min * TICKS_PER_MINUTE)) then
|
||||
local change_respawn_button = container.add {
|
||||
type = "button",
|
||||
tags = { action = "oarc_spawn_ctrl_tab", setting = "set_respawn_location" },
|
||||
name = "setRespawnLocation",
|
||||
caption = { "oarc-set-respawn-loc" },
|
||||
tooltip = { "oarc-set-respawn-loc-tooltip" },
|
||||
style = "red_button"
|
||||
}
|
||||
change_respawn_button.style.font = "default-small-semibold"
|
||||
else
|
||||
AddLabel(container, nil,
|
||||
{ "oarc-set-respawn-loc-cooldown", FormatTime((global.ocfg.gameplay.respawn_cooldown_min * TICKS_PER_MINUTE) -
|
||||
(game.tick - global.player_cooldowns[player.name].setRespawn)) }, my_note_style)
|
||||
end
|
||||
AddLabel(container, nil, { "oarc-set-respawn-note" }, my_label_style)
|
||||
AddSpacerLine(container)
|
||||
end
|
||||
|
||||
---Display a list of people in the join queue for a shared spawn.
|
||||
---@param player LuaPlayer
|
||||
---@param container LuaGuiElement
|
||||
---@return nil
|
||||
function CreateJoinQueueControls(player, container)
|
||||
local primary_spawn = FindPrimaryUniqueSpawn(player.name)
|
||||
if (primary_spawn == nil) then return end
|
||||
|
||||
local shared_spawn_open = IsSharedSpawnOpen(primary_spawn.surface_name, player.name)
|
||||
local shared_spawn_full = IsSharedSpawnFull(primary_spawn.surface_name, player.name)
|
||||
|
||||
-- Only show this if the player has an open and not full shared spawn
|
||||
if (not shared_spawn_open or shared_spawn_full) then return end
|
||||
|
||||
if (table_size(primary_spawn.join_queue) > 0) then
|
||||
AddLabel(container, nil, { "oarc-join-queue-header" }, "caption_label")
|
||||
AddLabel(container, "drop_down_msg_lbl1", { "oarc-select-player-join-queue" }, my_label_style)
|
||||
|
||||
local horizontal_flow = container.add { type = "flow", direction = "horizontal" }
|
||||
horizontal_flow.style.horizontally_stretchable = true
|
||||
|
||||
horizontal_flow.add {
|
||||
name = "join_queue_dropdown",
|
||||
type = "drop-down",
|
||||
items = primary_spawn.join_queue
|
||||
}
|
||||
|
||||
local dragger = horizontal_flow.add {
|
||||
type = "empty-widget",
|
||||
style = "draggable_space_header"
|
||||
}
|
||||
dragger.style.horizontally_stretchable = true
|
||||
|
||||
horizontal_flow.add {
|
||||
name = "accept_player_request",
|
||||
tags = { action = "oarc_spawn_ctrl_tab", setting = "accept_player_request" },
|
||||
type = "button",
|
||||
style = "green_button",
|
||||
caption = { "oarc-accept" }
|
||||
}
|
||||
horizontal_flow.add {
|
||||
name = "reject_player_request",
|
||||
tags = { action = "oarc_spawn_ctrl_tab", setting = "reject_player_request" },
|
||||
type = "button",
|
||||
style = "red_button",
|
||||
caption = { "oarc-reject" }
|
||||
}
|
||||
else
|
||||
AddLabel(container, "empty_join_queue_note1", { "oarc-no-player-join-reqs" }, my_note_style)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
---Display a GPS button for a specific location.
|
||||
---@param container LuaGuiElement
|
||||
---@param surface_name string
|
||||
---@param position MapPosition
|
||||
---@return nil
|
||||
function CreateGPSButton(container, surface_name, position)
|
||||
local gps_button = container.add {
|
||||
type = "sprite-button",
|
||||
sprite = "utility/gps_map_icon",
|
||||
tags = {
|
||||
action = "oarc_spawn_ctrl_tab",
|
||||
setting = "show_location",
|
||||
surface = surface_name,
|
||||
position = position
|
||||
},
|
||||
style = "slot_button",
|
||||
tooltip = { "oarc-spawn-info-location-button-tooltip" },
|
||||
}
|
||||
gps_button.style.width = 28
|
||||
gps_button.style.height = 28
|
||||
end
|
||||
|
||||
---Handle the gui checkboxes & radio buttons of the spawn control tab in the Oarc GUI.
|
||||
---@param event EventData.on_gui_checked_state_changed
|
||||
---@return nil
|
||||
function SpawnCtrlGuiOptionsSelect(event)
|
||||
if not event.element.valid then return end
|
||||
local player = game.players[event.player_index]
|
||||
local tags = event.element.tags
|
||||
|
||||
if (tags.action ~= "oarc_spawn_ctrl_tab") then
|
||||
return
|
||||
end
|
||||
|
||||
-- Handle changes to spawn sharing.
|
||||
if (tags.setting == "shared_access_toggle") then
|
||||
if event.element.state then
|
||||
SendBroadcastMsg({ "oarc-start-shared-base", player.name })
|
||||
else
|
||||
SendBroadcastMsg({ "oarc-stop-shared-base", player.name })
|
||||
end
|
||||
local primary_spawn = FindPrimaryUniqueSpawn(player.name)
|
||||
global.unique_spawns[primary_spawn.surface_name][player.name].open_access = event.element.state
|
||||
OarcGuiRefreshContent(player)
|
||||
|
||||
-- Refresh the shared spawn spawn gui for all players
|
||||
for _,p in pairs(game.connected_players) do
|
||||
RefreshSharedSpawnFrameIfExist(p)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Handle the gui click of the spawn control tab in the Oarc GUI.
|
||||
---@param event EventData.on_gui_click
|
||||
---@return nil
|
||||
function SpawnCtrlGuiClick(event)
|
||||
if not event.element.valid then return end
|
||||
local player = game.players[event.player_index]
|
||||
local tags = event.element.tags
|
||||
|
||||
if (tags.action ~= "oarc_spawn_ctrl_tab") then
|
||||
return
|
||||
end
|
||||
|
||||
-- Sets a new respawn point and resets the cooldown.
|
||||
if (tags.setting == "set_respawn_location") then
|
||||
SetPlayerRespawn(player.name, player.surface.name, player.position, true)
|
||||
OarcGuiRefreshContent(player)
|
||||
player.print({ "oarc-spawn-point-updated" })
|
||||
|
||||
-- Shows the spawn location on the map
|
||||
elseif (tags.setting == "show_location") then
|
||||
local surface_name = tags.surface --[[@as string]]
|
||||
local position = tags.position --[[@as MapPosition]]
|
||||
player.open_map(position, 0.05)
|
||||
player.print({"", { "oarc-spawn-gps-location" }, GetGPStext(surface_name, position)})
|
||||
|
||||
-- Accept or reject pending player join requests to a shared base
|
||||
elseif ((tags.setting == "accept_player_request") or (tags.setting == "reject_player_request")) then
|
||||
|
||||
if ((event.element.parent.join_queue_dropdown == nil) or
|
||||
(event.element.parent.join_queue_dropdown.selected_index == 0)) then
|
||||
player.print({ "oarc-selected-player-not-valid" })
|
||||
OarcGuiRefreshContent(player)
|
||||
return
|
||||
end
|
||||
|
||||
local join_queue_index = event.element.parent.join_queue_dropdown.selected_index
|
||||
local join_queue_player_choice = event.element.parent.join_queue_dropdown.get_item(join_queue_index) --[[@as string]]
|
||||
|
||||
-- Shouldn't be able to hit this since we force a GUI refresh when they leave?
|
||||
if ((game.players[join_queue_player_choice] == nil) or (not game.players[join_queue_player_choice].connected)) then
|
||||
player.print({ "oarc-selected-player-not-wait" })
|
||||
OarcGuiRefreshContent(player)
|
||||
return
|
||||
end
|
||||
|
||||
local primary_spawn = FindPrimaryUniqueSpawn(player.name)
|
||||
|
||||
if (tags.setting == "reject_player_request") then
|
||||
|
||||
RemovePlayerFromJoinQueue(join_queue_player_choice) -- This also refreshes the host gui
|
||||
|
||||
-- Inform the host that the player was rejected
|
||||
player.print({ "oarc-reject-joiner", join_queue_player_choice })
|
||||
-- Inform the player that their request was rejected
|
||||
SendMsg(join_queue_player_choice, { "oarc-your-request-rejected" })
|
||||
|
||||
-- Close the waiting players menu
|
||||
if (game.players[join_queue_player_choice].gui.screen.join_shared_spawn_wait_menu) then
|
||||
game.players[join_queue_player_choice].gui.screen.join_shared_spawn_wait_menu.destroy()
|
||||
DisplaySpawnOptions(game.players[join_queue_player_choice])
|
||||
end
|
||||
|
||||
elseif (tags.setting == "accept_player_request") then
|
||||
|
||||
-- Check if there is space first
|
||||
if (table_size(primary_spawn.joiners) >= global.ocfg.gameplay.number_of_players_per_shared_spawn - 1) then
|
||||
player.print({ "oarc-shared-spawn-full" })
|
||||
return
|
||||
end
|
||||
|
||||
-- Send an announcement
|
||||
SendBroadcastMsg({ "oarc-player-joining-base", join_queue_player_choice, player.name })
|
||||
|
||||
-- Close the waiting players menu
|
||||
if (game.players[join_queue_player_choice].gui.screen.join_shared_spawn_wait_menu) then
|
||||
game.players[join_queue_player_choice].gui.screen.join_shared_spawn_wait_menu.destroy()
|
||||
end
|
||||
|
||||
-- Spawn the player
|
||||
local joining_player = game.players[join_queue_player_choice]
|
||||
SetPlayerRespawn(joining_player.name, primary_spawn.surface_name, primary_spawn.position, true)
|
||||
SendPlayerToSpawn(primary_spawn.surface_name, joining_player)
|
||||
GivePlayerStarterItems(joining_player)
|
||||
table.insert(global.unique_spawns[primary_spawn.surface_name][player.name].joiners, joining_player.name)
|
||||
joining_player.force = game.players[player.name].force
|
||||
|
||||
-- Render some welcoming text...
|
||||
DisplayWelcomeGroundTextAtSpawn(joining_player, primary_spawn.surface_name, primary_spawn.position)
|
||||
|
||||
-- Unlock spawn control gui tab
|
||||
SetOarcGuiTabEnabled(joining_player, OARC_SPAWN_CTRL_TAB_NAME, true)
|
||||
|
||||
RemovePlayerFromJoinQueue(join_queue_player_choice) -- This also refreshes the host gui
|
||||
end
|
||||
end
|
||||
end
|
@ -1,67 +0,0 @@
|
||||
-- helper_commands.lua
|
||||
-- Jan 2018
|
||||
-- None of this is my code.
|
||||
|
||||
require("lib/oarc_utils")
|
||||
|
||||
commands.add_command("run", "change player speed bonus", function(command)
|
||||
local player = game.players[command.player_index];
|
||||
if player ~= nil then
|
||||
if (command.parameter ~= nil) then
|
||||
if command.parameter == "fast" then
|
||||
player.character_running_speed_modifier = 1
|
||||
elseif command.parameter == "slow" then
|
||||
player.character_running_speed_modifier = 0
|
||||
else
|
||||
player.print("run fast | slow");
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
commands.add_command("handcraft", "change player speed bonus", function(command)
|
||||
local player = game.players[command.player_index];
|
||||
if player ~= nil then
|
||||
if (command.parameter ~= nil) then
|
||||
if command.parameter == "fast" then
|
||||
player.character_crafting_speed_modifier = 5
|
||||
elseif command.parameter == "slow" then
|
||||
player.character_crafting_speed_modifier = 0
|
||||
else
|
||||
player.print("handcraft fast | slow");
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
commands.add_command("mine", "change player speed bonus", function(command)
|
||||
local player = game.players[command.player_index];
|
||||
if player ~= nil then
|
||||
if (command.parameter ~= nil) then
|
||||
if command.parameter == "fast" then
|
||||
player.character_mining_speed_modifier = 2
|
||||
elseif command.parameter == "slow" then
|
||||
player.character_mining_speed_modifier = 0
|
||||
else
|
||||
player.print("mine fast | slow");
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
commands.add_command("kit", "give a start kit", function(command)
|
||||
local player = game.players[command.player_index];
|
||||
if player ~= nil and player.admin then
|
||||
local target = player
|
||||
if (command.parameter ~= nil) then
|
||||
target = game.players[command.parameter]
|
||||
end
|
||||
if target ~= nil then
|
||||
GivePlayerStarterItems(target);
|
||||
player.print("gave a kit to " .. target.name);
|
||||
target.print("you have been given a start kit");
|
||||
else
|
||||
player.print("no player " .. command.parameter);
|
||||
end
|
||||
end
|
||||
end)
|
143
lib/holding_pen.lua
Normal file
143
lib/holding_pen.lua
Normal file
@ -0,0 +1,143 @@
|
||||
-- This file is used to create the holding pen area where players spawn in before being teleported to their own area.
|
||||
|
||||
HOLDING_PEN_SURFACE_NAME = "oarc_holding_pen"
|
||||
|
||||
function CreateHoldingPenSurface()
|
||||
|
||||
if game.surfaces[HOLDING_PEN_SURFACE_NAME] ~= nil then
|
||||
log("ERROR - Holding pen surface already exists!")
|
||||
return
|
||||
end
|
||||
|
||||
---@type MapGenSettings
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
local map_settings = {}
|
||||
map_settings.terrain_segmentation = "none"
|
||||
map_settings.water = "none"
|
||||
map_settings.starting_area = "none"
|
||||
map_settings.peaceful_mode = true
|
||||
map_settings.width = 64
|
||||
map_settings.height = 64
|
||||
|
||||
-- Create a new surface for the holding pen
|
||||
|
||||
local holding_pen_surface = game.create_surface(HOLDING_PEN_SURFACE_NAME, map_settings)
|
||||
holding_pen_surface.always_day = true
|
||||
holding_pen_surface.show_clouds = false
|
||||
holding_pen_surface.generate_with_lab_tiles = true
|
||||
|
||||
RenderPermanentGroundText(holding_pen_surface, {x=9,y=-7}, 5, "O", {0.9, 0.7, 0.3, 0.5}, "center")
|
||||
RenderPermanentGroundText(holding_pen_surface, {x=9,y=-4}, 5, "A", {0.9, 0.7, 0.3, 0.5}, "center")
|
||||
RenderPermanentGroundText(holding_pen_surface, {x=9,y=-1}, 5, "R", {0.9, 0.7, 0.3, 0.5}, "center")
|
||||
RenderPermanentGroundText(holding_pen_surface, {x=9,y=2}, 5, "C", {0.9, 0.7, 0.3, 0.5}, "center")
|
||||
end
|
||||
|
||||
---Creates a holding pen area
|
||||
---@param event EventData.on_chunk_generated
|
||||
---@return nil
|
||||
function CreateHoldingPenChunks(event)
|
||||
|
||||
local surface = event.surface
|
||||
local chunk_area = event.area
|
||||
local chunk_position = event.position
|
||||
|
||||
|
||||
if (surface.name ~= HOLDING_PEN_SURFACE_NAME) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Remove ALL entities in the chunk
|
||||
for _, entity in pairs(surface.find_entities(chunk_area)) do
|
||||
if entity.type ~= "character" then
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
|
||||
-- Place tiles and trees and water for the holding pen
|
||||
local tiles = {}
|
||||
for x=chunk_area.left_top.x,chunk_area.right_bottom.x,1 do
|
||||
for y=chunk_area.left_top.y,chunk_area.right_bottom.y,1 do
|
||||
local distance_sqr = math.floor(x^2 + y^2)
|
||||
|
||||
if (distance_sqr < 15^2) then
|
||||
table.insert(tiles, {name="grass-1", position={x, y}})
|
||||
elseif (distance_sqr < 20^2) then
|
||||
table.insert(tiles, {name="water", position={x, y}})
|
||||
|
||||
--10% chance of fish in water
|
||||
if (math.random(1,10) == 1) then
|
||||
surface.create_entity({name="fish", position={x + 0.5, y + 0.5}})
|
||||
end
|
||||
|
||||
else
|
||||
table.insert(tiles, {name="out-of-map", position={x, y}})
|
||||
end
|
||||
|
||||
if (distance_sqr >= 13^2) and (distance_sqr <= 15^2) then
|
||||
surface.create_entity({name="tree-01", position={x + 0.5, y + 0.5}})
|
||||
end
|
||||
end
|
||||
end
|
||||
surface.set_tiles(tiles)
|
||||
|
||||
-- If this is the bottom right chunk it's safe to place stuff inside the holding pen now.
|
||||
if (chunk_position.x == 2 and chunk_position.y == 2) then
|
||||
|
||||
PlaceResourcesInSemiCircleHoldingPen(surface, {x=0,y=0}, 0.2, 0.1)
|
||||
|
||||
CreateWaterStrip(surface, {x=-2,y=-11}, 4)
|
||||
CreateWaterStrip(surface, {x=-2,y=-10}, 4)
|
||||
|
||||
surface.create_entity({
|
||||
name = "crude-oil",
|
||||
amount = 90000,
|
||||
position = { 0, 9 }
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
---A special version of PlaceResourcesInSemiCircle for the holding pen
|
||||
---@param surface LuaSurface
|
||||
---@param position TilePosition --The center of the spawn area
|
||||
---@param size_mod number
|
||||
---@param amount_mod number
|
||||
---@return nil
|
||||
function PlaceResourcesInSemiCircleHoldingPen(surface, position, size_mod, amount_mod)
|
||||
|
||||
local resources = global.ocfg.surfaces_config["nauvis"].spawn_config.solid_resources
|
||||
|
||||
-- Create list of resource tiles
|
||||
---@type table<string>
|
||||
local r_list = {}
|
||||
for r_name, _ in pairs(resources) do
|
||||
if (r_name ~= "") then
|
||||
table.insert(r_list, r_name)
|
||||
end
|
||||
end
|
||||
---@type table<string>
|
||||
local shuffled_list = FYShuffle(r_list)
|
||||
|
||||
-- This places resources in a semi-circle
|
||||
local angle_offset = 2.32
|
||||
local num_resources = table_size(resources)
|
||||
local theta = ((4.46 - 2.32) / num_resources);
|
||||
local count = 0
|
||||
|
||||
-- Unique to the holding pen size
|
||||
local radius = 15 - 6
|
||||
|
||||
for _, r_name in pairs(shuffled_list) do
|
||||
local angle = (theta * count) + angle_offset;
|
||||
|
||||
local tx = (radius * math.cos(angle)) + position.x
|
||||
local ty = (radius * math.sin(angle)) + position.y
|
||||
|
||||
local pos = { x = math.floor(tx), y = math.floor(ty) }
|
||||
|
||||
local resourceConfig = resources[r_name]
|
||||
GenerateResourcePatch(surface, r_name, resourceConfig.size * size_mod, pos, resourceConfig.amount * amount_mod)
|
||||
count = count + 1
|
||||
end
|
||||
end
|
@ -1,970 +0,0 @@
|
||||
-- map_features.lua
|
||||
-- April 2020
|
||||
-- Oarc's clone of whistlestop factories maybe?
|
||||
|
||||
-- Generic Utility Includes
|
||||
require("lib/oarc_utils")
|
||||
|
||||
|
||||
-- Used to generate placement of buildings.
|
||||
MAGIC_BUILDING_MIN_DISTANCE = 40
|
||||
MAGIC_BUILDING_MAX_DISTANCE = FAR_MAX_DIST + 50
|
||||
MAGIC_BUILDING_CHUNK_SPREAD = 41
|
||||
|
||||
|
||||
POWER_USAGE_SCALING_FACTOR = 2
|
||||
|
||||
-- This is a table indexed by the single INPUT item!
|
||||
FURNACE_ENERGY_PER_CRAFT_SECOND = (180000 / 2) * POWER_USAGE_SCALING_FACTOR
|
||||
FURNACE_RECIPES = {
|
||||
["iron-ore"] = {recipe_name = "iron-plate",
|
||||
recipe_energy = 3.2*FURNACE_ENERGY_PER_CRAFT_SECOND,
|
||||
recipe_pollution = 0.053},
|
||||
["copper-ore"] = {recipe_name = "copper-plate",
|
||||
recipe_energy = 3.2*FURNACE_ENERGY_PER_CRAFT_SECOND,
|
||||
recipe_pollution = 0.053},
|
||||
["iron-plate"] = {recipe_name = "steel-plate",
|
||||
recipe_energy = 16*FURNACE_ENERGY_PER_CRAFT_SECOND,
|
||||
recipe_pollution = 0.267},
|
||||
["stone"] = {recipe_name = "stone-brick",
|
||||
recipe_energy = 3.2*FURNACE_ENERGY_PER_CRAFT_SECOND,
|
||||
recipe_pollution = 0.053},
|
||||
}
|
||||
|
||||
-- The chemplants/refineries/assemblers lookup their own recipes since they can be set by the player.
|
||||
CHEMPLANT_ENERGY_PER_CRAFT_SECOND = 210000 * POWER_USAGE_SCALING_FACTOR
|
||||
REFINERY_ENERGY_PER_CRAFT_SECOND = 420000 * POWER_USAGE_SCALING_FACTOR
|
||||
ASSEMBLER3_ENERGY_PER_CRAFT_SECOND = (375000 / 1.25) * POWER_USAGE_SCALING_FACTOR
|
||||
CENTRIFUGE_ENERGY_PER_CRAFT_SECOND = 350000 * POWER_USAGE_SCALING_FACTOR
|
||||
|
||||
CHEMPLANT_POLLUTION_PER_CRAFT_SECOND = 4/60
|
||||
REFINERY_POLLUTION_PER_CRAFT_SECOND = 6/60
|
||||
ASSEMBLER3_POLLUTION_PER_CRAFT_SECOND = 2/60
|
||||
CENTRIFUGE_POLLUTION_PER_CRAFT_SECOND = 4/60
|
||||
|
||||
|
||||
ENEMY_WORM_TURRETS =
|
||||
{
|
||||
[0] = "small-worm-turret",
|
||||
[1] = "medium-worm-turret",
|
||||
[2] = "big-worm-turret"
|
||||
}
|
||||
|
||||
NEUTRAL_FORCE_RECIPES =
|
||||
{
|
||||
-- Science packs
|
||||
["automation-science-pack"] = true,
|
||||
["chemical-science-pack"] = true,
|
||||
["logistic-science-pack"] = true,
|
||||
["military-science-pack"] = true,
|
||||
["production-science-pack"] = true,
|
||||
["utility-science-pack"] = true,
|
||||
|
||||
-- Oil Stuff
|
||||
["advanced-oil-processing"] = true,
|
||||
["basic-oil-processing"] = true,
|
||||
-- ["coal-liquefaction"] = true, -- Too difficult/costly to implement
|
||||
|
||||
["heavy-oil-cracking"] = true,
|
||||
["light-oil-cracking"] = true,
|
||||
|
||||
["solid-fuel-from-heavy-oil"] = true,
|
||||
["solid-fuel-from-light-oil"] = true,
|
||||
["solid-fuel-from-petroleum-gas"] = true,
|
||||
|
||||
["lubricant"] = true,
|
||||
["plastic-bar"] = true,
|
||||
["sulfur"] = true,
|
||||
["sulfuric-acid"] = true,
|
||||
|
||||
-- ["oil-refinery"] = true,
|
||||
-- ["explosives"] = true,
|
||||
|
||||
-- Modules
|
||||
["effectivity-module"] = true,
|
||||
["effectivity-module-2"] = true,
|
||||
["effectivity-module-3"] = true,
|
||||
["productivity-module"] = true,
|
||||
["productivity-module-2"] = true,
|
||||
["productivity-module-3"] = true,
|
||||
["speed-module"] = true,
|
||||
["speed-module-2"] = true,
|
||||
["speed-module-3"] = true,
|
||||
|
||||
-- Intermediates
|
||||
["advanced-circuit"] = true,
|
||||
["battery"] = true,
|
||||
["copper-cable"] = true,
|
||||
["copper-plate"] = true,
|
||||
["electric-engine-unit"] = true,
|
||||
["electronic-circuit"] = true,
|
||||
["engine-unit"] = true,
|
||||
["flying-robot-frame"] = true,
|
||||
["iron-gear-wheel"] = true,
|
||||
["iron-plate"] = true,
|
||||
["iron-stick"] = true,
|
||||
["low-density-structure"] = true,
|
||||
["processing-unit"] = true,
|
||||
["rocket-control-unit"] = true,
|
||||
["rocket-fuel"] = true,
|
||||
["steel-plate"] = true,
|
||||
["stone-brick"] = true,
|
||||
|
||||
-- Misc
|
||||
["concrete"] = true,
|
||||
["landfill"] = true,
|
||||
["rail"] = true,
|
||||
["solar-panel"] = true,
|
||||
["stone-wall"] = true,
|
||||
["empty-barrel"] = true,
|
||||
|
||||
-- Nuclear
|
||||
["uranium-processing"] = true,
|
||||
-- ["kovarex-enrichment-process"] = true,
|
||||
-- ["nuclear-fuel-reprocessing"] = true,
|
||||
|
||||
-- ["pipe"] = true,
|
||||
-- ["pipe-to-ground"] = true,
|
||||
}
|
||||
|
||||
function SetNeutralForceAllowedRecipes()
|
||||
|
||||
-- Neutral force requires recipes so that furnaces can smelt steel for example.
|
||||
-- game.forces["neutral"].enable_all_recipes()
|
||||
|
||||
-- Disable ALL recipes
|
||||
for i,v in pairs(game.forces["neutral"].recipes) do
|
||||
game.forces["neutral"].recipes[i].enabled = false;
|
||||
end
|
||||
|
||||
-- Enable only the ones we want
|
||||
for i,v in pairs(NEUTRAL_FORCE_RECIPES) do
|
||||
game.forces["neutral"].recipes[i].enabled = true;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function MagicFactoriesInit()
|
||||
|
||||
SetNeutralForceAllowedRecipes()
|
||||
|
||||
global.omagic = {}
|
||||
global.omagic.building_total_count = 0
|
||||
global.omagic.factory_positions = {}
|
||||
global.omagic.furnaces = {}
|
||||
global.omagic.chemplants = {}
|
||||
global.omagic.refineries = {}
|
||||
global.omagic.assemblers = {}
|
||||
global.omagic.centrifuges = {}
|
||||
|
||||
MagicFactoryChunkGenerator()
|
||||
|
||||
game.surfaces[GAME_SURFACE_NAME].force_generate_chunk_requests() -- Block and generate all to be sure.
|
||||
MagicalFactorySpawnAll()
|
||||
end
|
||||
|
||||
function MagicFactoryChunkGenerator()
|
||||
|
||||
-- This generates several circles of randomized chunk positions.
|
||||
for r=MAGIC_BUILDING_MIN_DISTANCE,MAGIC_BUILDING_MAX_DISTANCE,MAGIC_BUILDING_CHUNK_SPREAD do
|
||||
local random_angle_offset = math.random(0, math.pi * 2)
|
||||
local num_positions_for_circle = math.ceil((r/8)) -- This makes it so each circle has more dots, roughly spreads things out equally.
|
||||
|
||||
for i=1,num_positions_for_circle do
|
||||
local theta = ((math.pi * 2) / num_positions_for_circle);
|
||||
local angle = (theta * i) + random_angle_offset;
|
||||
|
||||
local chunk_x = MathRound((r * math.cos(angle)) + math.random(-2, 2))
|
||||
local chunk_y = MathRound((r * math.sin(angle)) + math.random(-2, 2))
|
||||
|
||||
if (not game.surfaces[GAME_SURFACE_NAME].is_chunk_generated({chunk_x,chunk_y})) then
|
||||
|
||||
table.insert(global.omagic.factory_positions, {x=chunk_x, y=chunk_y})
|
||||
game.surfaces[GAME_SURFACE_NAME].request_to_generate_chunks(GetCenterTilePosFromChunkPos({x=chunk_x, y=chunk_y}), 0)
|
||||
log("Magic furnace position: " .. chunk_x .. ", " .. chunk_y .. ", " .. angle)
|
||||
else
|
||||
log("Magic furnace collided with silo location?" .. chunk_x .. ", " .. chunk_y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
SendBroadcastMsg("Number magic chunks: " .. #global.omagic.factory_positions)
|
||||
end
|
||||
|
||||
function FindClosestMagicChunk(player)
|
||||
if (not player or not player.character) then return end
|
||||
return GetClosestPosFromTable(GetChunkPosFromTilePos(player.character.position), global.omagic.factory_positions)
|
||||
end
|
||||
|
||||
function IndicateClosestMagicChunk(player)
|
||||
local target_pos = GetCenterTilePosFromChunkPos(FindClosestMagicChunk(player))
|
||||
rendering.draw_line{color={r=0.5,g=0.5,b=0.5,a=0.5},
|
||||
width=2,
|
||||
from=player.character,
|
||||
to=target_pos,
|
||||
surface=player.character.surface,
|
||||
players={player},
|
||||
draw_on_ground=true,
|
||||
time_to_live=60*5}
|
||||
end
|
||||
|
||||
function MagicalFactorySpawnAll()
|
||||
for _,chunk_pos in pairs(global.omagic.factory_positions) do
|
||||
|
||||
local pos = GetCenterTilePosFromChunkPos(chunk_pos)
|
||||
local c_area = GetAreaFromChunkPos(chunk_pos)
|
||||
|
||||
-- Remove any entities in the chunk area.
|
||||
for _, entity in pairs(game.surfaces[GAME_SURFACE_NAME].find_entities_filtered{area=c_area}) do
|
||||
entity.destroy()
|
||||
end
|
||||
|
||||
-- Place landfill underneath
|
||||
local dirtTiles = {}
|
||||
for i=c_area.left_top.x,c_area.right_bottom.x,1 do
|
||||
for j=c_area.left_top.y,c_area.right_bottom.y,1 do
|
||||
table.insert(dirtTiles, {name = "landfill", position ={i,j}})
|
||||
end
|
||||
end
|
||||
game.surfaces[GAME_SURFACE_NAME].set_tiles(dirtTiles)
|
||||
|
||||
-- Yay colored tiles
|
||||
CreateFixedColorTileArea(game.surfaces[GAME_SURFACE_NAME],
|
||||
{left_top = {x=c_area.left_top.x+2, y=c_area.left_top.y+2},
|
||||
right_bottom = {x=c_area.right_bottom.x-2, y=c_area.right_bottom.y-2}},
|
||||
"black")
|
||||
|
||||
-- Make it safe from regrowth
|
||||
if global.ocfg.enable_regrowth then
|
||||
RegrowthMarkAreaSafeGivenTilePos(pos, 0, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SpawnEnemyTurret(pos)
|
||||
|
||||
local turret = game.surfaces[GAME_SURFACE_NAME].create_entity{name="gun-turret", position=pos, force="enemy"}
|
||||
local turret_inv = turret.get_inventory(defines.inventory.turret_ammo)
|
||||
turret_inv.insert({name="uranium-rounds-magazine", count=200})
|
||||
|
||||
end
|
||||
|
||||
function RequestSpawnSpecialChunk(player, spawn_function, feature_name)
|
||||
local closest_chunk = FindClosestMagicChunk(player)
|
||||
local player_chunk = GetChunkPosFromTilePos(player.character.position)
|
||||
if ((closest_chunk.x == player_chunk.x) and (closest_chunk.y == player_chunk.y)) then
|
||||
local chunk_area = GetAreaFromChunkPos(closest_chunk)
|
||||
|
||||
local entities = game.surfaces[GAME_SURFACE_NAME].find_entities_filtered{
|
||||
area={left_top = {chunk_area.left_top.x+1, chunk_area.left_top.y+1},
|
||||
right_bottom = {chunk_area.right_bottom.x-1, chunk_area.right_bottom.y-1}},
|
||||
force={"enemy"},
|
||||
invert=true}
|
||||
|
||||
-- Either there are no entities in the chunk (player is just on the boundary), or the only entity is the player.
|
||||
if ((#entities == 1) and (entities[1].player) and (entities[1].player == player)) or (#entities == 0) then
|
||||
spawn_function(closest_chunk)
|
||||
-- Teleport to center of chunk to be safe.
|
||||
SafeTeleport(player, game.surfaces[GAME_SURFACE_NAME], GetCenterTilePosFromChunkPos(closest_chunk))
|
||||
OarcMapFeaturePlayerCountChange(player, "special_chunks", feature_name, 1)
|
||||
return true
|
||||
else
|
||||
player.print("Looks like this chunk already has something in it other than just you the player?! " .. entities[1].name)
|
||||
return false
|
||||
end
|
||||
|
||||
else
|
||||
player.print("You need to be standing inside the special chunk!")
|
||||
return false
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function SpecialChunkHelperText(pos)
|
||||
RenderPermanentGroundText(game.surfaces[GAME_SURFACE_NAME].index,
|
||||
{x=pos.x-3.5,y=pos.y+1},
|
||||
1,
|
||||
"Supply energy to this interface!",
|
||||
{0.7,0.4,0.3,0.8})
|
||||
RenderPermanentGroundText(game.surfaces[GAME_SURFACE_NAME].index,
|
||||
{x=pos.x-4.5,y=pos.y+2},
|
||||
1,
|
||||
"Modules/beacons DO NOT have any effect!",
|
||||
{0.7,0.4,0.3,0.8})
|
||||
end
|
||||
|
||||
function spawnSpecialChunkInputElec(center_pos)
|
||||
local inputElec = game.surfaces[GAME_SURFACE_NAME].create_entity{name="electric-energy-interface", position=center_pos, force="neutral"}
|
||||
inputElec.destructible = false
|
||||
inputElec.minable = false
|
||||
inputElec.operable = false
|
||||
inputElec.electric_buffer_size = 1000000000
|
||||
inputElec.power_production = 0
|
||||
inputElec.power_usage = 0
|
||||
inputElec.energy = 0
|
||||
return inputElec
|
||||
end
|
||||
|
||||
function SpawnFurnaceChunk(chunk_pos)
|
||||
|
||||
center_pos = GetCenterTilePosFromChunkPos(chunk_pos)
|
||||
local furnace_chunk = {["energy_input"] = spawnSpecialChunkInputElec(center_pos),
|
||||
["entities"] = {}}
|
||||
|
||||
-- 4x furnaces
|
||||
table.insert(furnace_chunk.entities, SpawnMagicBuilding("electric-furnace", {x=center_pos.x-12,y=center_pos.y-12}))
|
||||
table.insert(furnace_chunk.entities, SpawnMagicBuilding("electric-furnace", {x=center_pos.x+11,y=center_pos.y-12}))
|
||||
table.insert(furnace_chunk.entities, SpawnMagicBuilding("electric-furnace", {x=center_pos.x-12,y=center_pos.y+11}))
|
||||
table.insert(furnace_chunk.entities, SpawnMagicBuilding("electric-furnace", {x=center_pos.x+11,y=center_pos.y+11}))
|
||||
|
||||
table.insert(global.omagic.furnaces, furnace_chunk)
|
||||
SpecialChunkHelperText(center_pos)
|
||||
end
|
||||
|
||||
function SpawnOilRefineryChunk(chunk_pos)
|
||||
|
||||
center_pos = GetCenterTilePosFromChunkPos(chunk_pos)
|
||||
|
||||
local oil_chunk = {["energy_input"] = spawnSpecialChunkInputElec(center_pos),
|
||||
["chemplants"] = {},
|
||||
["refineries"] = {}}
|
||||
|
||||
-- 2x Refineries
|
||||
table.insert(oil_chunk.refineries, SpawnMagicBuilding("oil-refinery", {x=center_pos.x-5,y=center_pos.y-8}))
|
||||
table.insert(oil_chunk.refineries, SpawnMagicBuilding("oil-refinery", {x=center_pos.x+5,y=center_pos.y-8}))
|
||||
|
||||
-- 6x Chem Plants
|
||||
table.insert(oil_chunk.chemplants, SpawnMagicBuilding("chemical-plant", {x=center_pos.x-10,y=center_pos.y+8}))
|
||||
table.insert(oil_chunk.chemplants, SpawnMagicBuilding("chemical-plant", {x=center_pos.x-6,y=center_pos.y+8}))
|
||||
table.insert(oil_chunk.chemplants, SpawnMagicBuilding("chemical-plant", {x=center_pos.x-2,y=center_pos.y+8}))
|
||||
table.insert(oil_chunk.chemplants, SpawnMagicBuilding("chemical-plant", {x=center_pos.x+2,y=center_pos.y+8}))
|
||||
table.insert(oil_chunk.chemplants, SpawnMagicBuilding("chemical-plant", {x=center_pos.x+6,y=center_pos.y+8}))
|
||||
table.insert(oil_chunk.chemplants, SpawnMagicBuilding("chemical-plant", {x=center_pos.x+10,y=center_pos.y+8}))
|
||||
|
||||
table.insert(global.omagic.refineries, oil_chunk)
|
||||
table.insert(global.omagic.chemplants, oil_chunk)
|
||||
SpecialChunkHelperText(center_pos)
|
||||
end
|
||||
|
||||
function SpawnAssemblyChunk(chunk_pos)
|
||||
|
||||
center_pos = GetCenterTilePosFromChunkPos(chunk_pos)
|
||||
local assembler_chunk = {["energy_input"] = spawnSpecialChunkInputElec(center_pos),
|
||||
["entities"] = {}}
|
||||
|
||||
-- 6x Assemblers
|
||||
table.insert(assembler_chunk.entities, SpawnMagicBuilding("assembling-machine-3", {x=center_pos.x-12,y=center_pos.y-12}))
|
||||
table.insert(assembler_chunk.entities, SpawnMagicBuilding("assembling-machine-3", {x=center_pos.x,y=center_pos.y-12}))
|
||||
table.insert(assembler_chunk.entities, SpawnMagicBuilding("assembling-machine-3", {x=center_pos.x+11,y=center_pos.y-12}))
|
||||
table.insert(assembler_chunk.entities, SpawnMagicBuilding("assembling-machine-3", {x=center_pos.x-12,y=center_pos.y+11}))
|
||||
table.insert(assembler_chunk.entities, SpawnMagicBuilding("assembling-machine-3", {x=center_pos.x-1,y=center_pos.y+11}))
|
||||
table.insert(assembler_chunk.entities, SpawnMagicBuilding("assembling-machine-3", {x=center_pos.x+11,y=center_pos.y+11}))
|
||||
|
||||
table.insert(global.omagic.assemblers, assembler_chunk)
|
||||
SpecialChunkHelperText(center_pos)
|
||||
end
|
||||
|
||||
function SpawnCentrifugeChunk(chunk_pos)
|
||||
|
||||
center_pos = GetCenterTilePosFromChunkPos(chunk_pos)
|
||||
local centrifuge_chunk = {["energy_input"] = spawnSpecialChunkInputElec(center_pos),
|
||||
["entities"] = {}}
|
||||
|
||||
-- 1 Centrifuge (MORE THAN ENOUGH!)
|
||||
table.insert(centrifuge_chunk.entities, SpawnMagicBuilding("centrifuge", {x=center_pos.x,y=center_pos.y-10}))
|
||||
|
||||
table.insert(global.omagic.centrifuges, centrifuge_chunk)
|
||||
SpecialChunkHelperText(center_pos)
|
||||
end
|
||||
|
||||
function SpawnSiloChunk(chunk_pos)
|
||||
|
||||
center_pos = GetCenterTilePosFromChunkPos(chunk_pos)
|
||||
|
||||
table.insert(global.siloPosition, center_pos)
|
||||
|
||||
RenderPermanentGroundText(game.surfaces[GAME_SURFACE_NAME].index,
|
||||
{x=center_pos.x-3.25,y=center_pos.y+6},
|
||||
1,
|
||||
"You can build a silo here!",
|
||||
{0.7,0.4,0.3,0.8})
|
||||
|
||||
-- Set tiles below the silo
|
||||
tiles = {}
|
||||
for dx = -6,5 do
|
||||
for dy = -6,5 do
|
||||
if (game.active_mods["oarc-restricted-build"]) then
|
||||
table.insert(tiles, {name = global.ocfg.locked_build_area_tile,
|
||||
position = {center_pos.x+dx, center_pos.y+dy}})
|
||||
else
|
||||
if ((dx % 2 == 0) or (dx % 2 == 0)) then
|
||||
table.insert(tiles, {name = "concrete",
|
||||
position = {center_pos.x+dx, center_pos.y+dy}})
|
||||
else
|
||||
table.insert(tiles, {name = "hazard-concrete-left",
|
||||
position = {center_pos.x+dx, center_pos.y+dy}})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
game.surfaces[GAME_SURFACE_NAME].set_tiles(tiles, true)
|
||||
end
|
||||
|
||||
function SpawnMagicBuilding(entity_name, position)
|
||||
local direction = defines.direction.north
|
||||
if (entity_name == "oil-refinery") then
|
||||
direction = defines.direction.south
|
||||
end
|
||||
local magic_building = game.surfaces[GAME_SURFACE_NAME].create_entity{name=entity_name, position=position, force="neutral", direction=direction}
|
||||
magic_building.destructible = false
|
||||
magic_building.minable = false
|
||||
magic_building.operable = true
|
||||
magic_building.active = false
|
||||
|
||||
global.omagic.building_total_count = global.omagic.building_total_count + 1
|
||||
|
||||
return magic_building
|
||||
end
|
||||
|
||||
function MagicFactoriesOnTick()
|
||||
MagicFurnaceOnTick()
|
||||
MagicChemplantOnTick()
|
||||
MagicRefineryOnTick()
|
||||
MagicAssemblerOnTick()
|
||||
MagicCentrifugeOnTick()
|
||||
end
|
||||
|
||||
-- Some helpful math:
|
||||
-- 94 per tick (max stack of ore in a smelter) (More like 2 or 3 ore per tick.)
|
||||
-- blue belt = 45 / sec
|
||||
-- 6 INPUT blue belts = 4.5 ore/tick (45 * 6 / 60) with productivity is an extra 0.9 maybe.
|
||||
function MagicFurnaceOnTick()
|
||||
if not global.omagic.furnaces then return end
|
||||
|
||||
for entry_idx,entry in pairs(global.omagic.furnaces) do
|
||||
|
||||
-- Validate the entry.
|
||||
if (entry == nil) or (entry.entities == nil) or (entry.energy_input == nil) or (not entry.energy_input.valid) then
|
||||
global.omagic.furnaces[entry_idx] = nil
|
||||
log("MagicFurnaceOnTick - Magic furnace entry removed?")
|
||||
goto next_furnace_entry
|
||||
end
|
||||
|
||||
local energy_share = entry.energy_input.energy/#entry.entities
|
||||
|
||||
for idx,furnace in pairs(entry.entities) do
|
||||
|
||||
if (furnace == nil) or (not furnace.valid) then
|
||||
global.omagic.furnaces[entry_idx] = nil
|
||||
log("MagicFurnaceOnTick - Magic furnace removed?")
|
||||
goto next_furnace_entry
|
||||
end
|
||||
|
||||
local input_inv = furnace.get_inventory(defines.inventory.furnace_source)
|
||||
local input_items = input_inv.get_contents()
|
||||
|
||||
-- We have something inside?
|
||||
local input_item_name = next(input_items)
|
||||
if not input_item_name then
|
||||
goto next_furnace
|
||||
end
|
||||
|
||||
-- Does the input item have a recipe?
|
||||
if not FURNACE_RECIPES[input_item_name] then
|
||||
log("MagicFurnaceOnTick - Missing FURNACE_RECIPES?")
|
||||
goto next_furnace
|
||||
end
|
||||
local recipe = game.forces["neutral"].recipes[FURNACE_RECIPES[input_item_name].recipe_name]
|
||||
if not recipe then
|
||||
log("MagicFurnaceOnTick - Missing neutral force recipes?")
|
||||
goto next_furnace
|
||||
end
|
||||
|
||||
-- Verify 1 ingredient type and 1 product type (for furnaces)
|
||||
if (#recipe.products ~= 1) or (#recipe.ingredients ~= 1) then
|
||||
log("MagicFurnaceOnTick - Recipe product/ingredient more than 1?")
|
||||
goto next_furnace
|
||||
end
|
||||
local recipe_ingredient = recipe.ingredients[next(recipe.ingredients)]
|
||||
local recipe_product = recipe.products[next(recipe.products)]
|
||||
|
||||
local output_inv = furnace.get_inventory(defines.inventory.furnace_result)
|
||||
|
||||
-- Can we insert at least 1 of the recipe result?
|
||||
-- if not output_inv.can_insert({name=recipe_product.name}) then goto next_furnace end
|
||||
local output_space = output_inv.get_insertable_count(recipe_product.name)
|
||||
|
||||
-- Calculate how many times we can make the recipe.
|
||||
local ingredient_limit = math.floor(input_items[input_item_name]/recipe_ingredient.amount)
|
||||
local output_limit = math.floor(output_space/recipe_product.amount)
|
||||
|
||||
-- Use shared energy pool
|
||||
local energy_limit = math.floor(energy_share/FURNACE_RECIPES[input_item_name].recipe_energy)
|
||||
local recipe_count = math.min(ingredient_limit, output_limit, energy_limit)
|
||||
|
||||
-- Hit a limit somewhere?
|
||||
if (recipe_count <= 0) then goto next_furnace end
|
||||
|
||||
-- Track energy usage
|
||||
entry.energy_input.energy = entry.energy_input.energy - (FURNACE_RECIPES[input_item_name].recipe_energy*recipe_count)
|
||||
furnace.surface.pollute(furnace.position, FURNACE_RECIPES[input_item_name].recipe_pollution*recipe_count)
|
||||
|
||||
-- Check if it has a last_user
|
||||
if (not furnace.last_user) then
|
||||
local player_entities = game.surfaces[GAME_SURFACE_NAME].find_entities_filtered{
|
||||
position=furnace.position,
|
||||
radius=10,
|
||||
force={"enemy", "neutral"},
|
||||
limit=1,
|
||||
invert=true}
|
||||
if (player_entities and player_entities[1] and player_entities[1].last_user) then
|
||||
furnace.last_user = player_entities[1].last_user
|
||||
end
|
||||
end
|
||||
|
||||
-- Subtract recipe count from input and Add recipe count to output
|
||||
input_inv.remove({name=recipe_ingredient.name, count=recipe_count*recipe_ingredient.amount})
|
||||
output_inv.insert({name=recipe_product.name, count=recipe_count*recipe_product.amount})
|
||||
furnace.products_finished = furnace.products_finished + recipe_count
|
||||
|
||||
-- If we have a user, do the stats
|
||||
if (furnace.last_user) then
|
||||
furnace.last_user.force.item_production_statistics.on_flow(recipe_ingredient.name, -recipe_count*recipe_ingredient.amount)
|
||||
furnace.last_user.force.item_production_statistics.on_flow(recipe_product.name, recipe_count*recipe_product.amount)
|
||||
end
|
||||
|
||||
::next_furnace::
|
||||
end
|
||||
|
||||
::next_furnace_entry::
|
||||
end
|
||||
end
|
||||
|
||||
function MagicChemplantOnTick()
|
||||
if not global.omagic.chemplants then return end
|
||||
|
||||
for entry_idx,entry in pairs(global.omagic.chemplants) do
|
||||
|
||||
-- Validate the entry.
|
||||
if (entry == nil) or (entry.chemplants == nil) or (entry.energy_input == nil) or (not entry.energy_input.valid) then
|
||||
global.omagic.chemplants[entry_idx] = nil
|
||||
log("MagicChemplantOnTick - Magic assembler entry removed?")
|
||||
goto next_chemplant_entry
|
||||
end
|
||||
|
||||
local energy_share = entry.energy_input.energy/(#entry.chemplants + #entry.refineries)
|
||||
|
||||
for idx,chemplant in pairs(entry.chemplants) do
|
||||
|
||||
if (chemplant == nil) or (not chemplant.valid) then
|
||||
global.omagic.chemplants[idx] = nil
|
||||
log("Magic chemplant removed?")
|
||||
goto next_chemplant_entry
|
||||
end
|
||||
|
||||
recipe = chemplant.get_recipe()
|
||||
|
||||
if (not recipe) then
|
||||
goto next_chemplant -- No recipe means do nothing.
|
||||
end
|
||||
|
||||
local energy_cost = recipe.energy * CHEMPLANT_ENERGY_PER_CRAFT_SECOND
|
||||
if (energy_share < energy_cost) then goto next_chemplant end -- Not enough energy!
|
||||
|
||||
local input_inv = chemplant.get_inventory(defines.inventory.assembling_machine_input)
|
||||
local input_items = input_inv.get_contents()
|
||||
local input_fluids = chemplant.get_fluid_contents()
|
||||
|
||||
for _,v in ipairs(recipe.ingredients) do
|
||||
if (not input_items[v.name] or (input_items[v.name] < v.amount)) then
|
||||
if (not input_fluids[v.name] or (input_fluids[v.name] < v.amount)) then
|
||||
goto next_chemplant -- Not enough ingredients
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local recipe_product = recipe.products[next(recipe.products)] -- Assume only 1 product.
|
||||
|
||||
if recipe_product.type == "fluid" then
|
||||
|
||||
if ((chemplant.get_fluid_count(recipe_product.name) + recipe_product.amount) > 100) then
|
||||
goto next_chemplant -- Not enough space for ouput
|
||||
end
|
||||
|
||||
chemplant.insert_fluid({name=recipe_product.name, amount=recipe_product.amount})
|
||||
if (chemplant.last_user) then
|
||||
chemplant.last_user.force.fluid_production_statistics.on_flow(recipe_product.name, recipe_product.amount)
|
||||
end
|
||||
|
||||
-- Otherwise it must be an item type
|
||||
else
|
||||
|
||||
local output_inv = chemplant.get_inventory(defines.inventory.assembling_machine_output)
|
||||
|
||||
-- Can we insert at least 1 of the recipe result?
|
||||
if not output_inv.can_insert({name=recipe_product.name, amount=recipe_product.amount}) then goto next_chemplant end
|
||||
|
||||
-- Add recipe count to output
|
||||
output_inv.insert({name=recipe_product.name, count=recipe_product.amount})
|
||||
if (chemplant.last_user) then
|
||||
chemplant.last_user.force.item_production_statistics.on_flow(recipe_product.name, recipe_product.amount)
|
||||
end
|
||||
end
|
||||
|
||||
-- Subtract ingredients from input
|
||||
for _,v in ipairs(recipe.ingredients) do
|
||||
if (input_items[v.name]) then
|
||||
input_inv.remove({name=v.name, count=v.amount})
|
||||
if (chemplant.last_user) then
|
||||
chemplant.last_user.force.item_production_statistics.on_flow(v.name, -v.amount)
|
||||
end
|
||||
elseif (input_fluids[v.name]) then
|
||||
chemplant.remove_fluid{name=v.name, amount=v.amount}
|
||||
if (chemplant.last_user) then
|
||||
chemplant.last_user.force.fluid_production_statistics.on_flow(v.name, -v.amount)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
chemplant.products_finished = chemplant.products_finished + 1
|
||||
|
||||
-- Track energy usage
|
||||
entry.energy_input.energy = entry.energy_input.energy - energy_cost
|
||||
chemplant.surface.pollute(chemplant.position, recipe.energy*CHEMPLANT_POLLUTION_PER_CRAFT_SECOND)
|
||||
|
||||
|
||||
::next_chemplant::
|
||||
end
|
||||
|
||||
::next_chemplant_entry::
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function MagicRefineryOnTick()
|
||||
if not global.omagic.refineries then return end
|
||||
|
||||
for entry_idx,entry in pairs(global.omagic.refineries) do
|
||||
|
||||
-- Validate the entry.
|
||||
if (entry == nil) or (entry.refineries == nil) or (entry.energy_input == nil) or (not entry.energy_input.valid) then
|
||||
global.omagic.refineries[entry_idx] = nil
|
||||
log("MagicRefineryOnTick - Magic assembler entry removed?")
|
||||
goto next_refinery_entry
|
||||
end
|
||||
|
||||
local energy_share = entry.energy_input.energy/(#entry.chemplants + #entry.refineries)
|
||||
|
||||
for idx,refinery in pairs(entry.refineries) do
|
||||
|
||||
if (refinery == nil) or (not refinery.valid) then
|
||||
global.omagic.refineries[idx] = nil
|
||||
log("Magic refinery removed?")
|
||||
goto next_refinery_entry
|
||||
end
|
||||
|
||||
recipe = refinery.get_recipe()
|
||||
|
||||
if (not recipe) then
|
||||
goto next_refinery -- No recipe means do nothing.
|
||||
end
|
||||
|
||||
local energy_cost = recipe.energy * REFINERY_ENERGY_PER_CRAFT_SECOND
|
||||
if (energy_share < energy_cost) then goto next_refinery end -- Not enough energy!
|
||||
|
||||
local fluidbox_copy = refinery.fluidbox
|
||||
|
||||
-- If recipe is COAL LIQUEFACTION: heavy(1), steam(2), heavy(3), light(4), petro(5)
|
||||
-- if (recipe.name == "coal-liquefaction") then
|
||||
|
||||
|
||||
-- If recipe is Advanced OIL: water(1), crude(2), heavy(3), light(4), petro(5)
|
||||
if (recipe.name == "advanced-oil-processing") then
|
||||
|
||||
if ((not refinery.fluidbox[1]) or (refinery.fluidbox[1].amount < 50)) then goto next_refinery end -- Not enough water
|
||||
if ((not refinery.fluidbox[2]) or (refinery.fluidbox[2].amount < 100)) then goto next_refinery end -- Not enough crude
|
||||
if ((refinery.fluidbox[3]) and (refinery.fluidbox[3].amount > 25)) then goto next_refinery end -- Not enough space for heavy
|
||||
if ((refinery.fluidbox[4]) and (refinery.fluidbox[4].amount > 45)) then goto next_refinery end -- Not enough space for light
|
||||
if ((refinery.fluidbox[5]) and (refinery.fluidbox[5].amount > 55)) then goto next_refinery end -- Not enough space for petro
|
||||
|
||||
refinery.remove_fluid{name="water", amount=50}
|
||||
refinery.remove_fluid{name="crude-oil", amount=100}
|
||||
refinery.insert_fluid({name="heavy-oil", amount=25})
|
||||
refinery.insert_fluid({name="light-oil", amount=45})
|
||||
refinery.insert_fluid({name="petroleum-gas", amount=55})
|
||||
|
||||
if (refinery.last_user) then
|
||||
refinery.last_user.force.fluid_production_statistics.on_flow("water", -50)
|
||||
refinery.last_user.force.fluid_production_statistics.on_flow("crude-oil", -100)
|
||||
refinery.last_user.force.fluid_production_statistics.on_flow("heavy-oil", 25)
|
||||
refinery.last_user.force.fluid_production_statistics.on_flow("light-oil", 45)
|
||||
refinery.last_user.force.fluid_production_statistics.on_flow("petroleum-gas", 55)
|
||||
end
|
||||
|
||||
-- If recipe is Basic OIL: crude(1), petro(2)
|
||||
elseif (recipe.name == "basic-oil-processing") then
|
||||
|
||||
if ((not refinery.fluidbox[1]) or (refinery.fluidbox[1].amount < 100)) then goto next_refinery end -- Not enough crude
|
||||
if ((refinery.fluidbox[2]) and (refinery.fluidbox[2].amount > 45)) then goto next_refinery end -- Not enough space for petro
|
||||
|
||||
refinery.remove_fluid{name="crude-oil", amount=100}
|
||||
refinery.insert_fluid({name="petroleum-gas", amount=45})
|
||||
|
||||
if (refinery.last_user) then
|
||||
refinery.last_user.force.fluid_production_statistics.on_flow("crude-oil", -100)
|
||||
refinery.last_user.force.fluid_production_statistics.on_flow("petroleum-gas", 45)
|
||||
end
|
||||
|
||||
else
|
||||
goto next_refinery -- Shouldn't hit this...
|
||||
end
|
||||
|
||||
refinery.products_finished = refinery.products_finished + 1
|
||||
|
||||
-- Track energy usage
|
||||
entry.energy_input.energy = entry.energy_input.energy - energy_cost
|
||||
refinery.surface.pollute(refinery.position, recipe.energy*REFINERY_POLLUTION_PER_CRAFT_SECOND)
|
||||
|
||||
::next_refinery::
|
||||
end
|
||||
|
||||
::next_refinery_entry::
|
||||
end
|
||||
end
|
||||
|
||||
function MagicAssemblerOnTick()
|
||||
if not global.omagic.assemblers then return end
|
||||
|
||||
for entry_idx,entry in pairs(global.omagic.assemblers) do
|
||||
|
||||
-- Validate the entry.
|
||||
if (entry == nil) or (entry.entities == nil) or (entry.energy_input == nil) or (not entry.energy_input.valid) then
|
||||
global.omagic.assemblers[entry_idx] = nil
|
||||
log("MagicAssemblerOnTick - Magic assembler entry removed?")
|
||||
goto next_assembler_entry
|
||||
end
|
||||
|
||||
local energy_share = entry.energy_input.energy/#entry.entities
|
||||
|
||||
for idx,assembler in pairs(entry.entities) do
|
||||
|
||||
if (assembler == nil) or (not assembler.valid) then
|
||||
global.omagic.assemblers[entry_idx] = nil
|
||||
log("MagicAssemblerOnTick - Magic assembler removed?")
|
||||
goto next_assembler_entry
|
||||
end
|
||||
|
||||
recipe = assembler.get_recipe()
|
||||
|
||||
if (not recipe) then
|
||||
goto next_assembler -- No recipe means do nothing.
|
||||
end
|
||||
|
||||
local energy_cost = recipe.energy * ASSEMBLER3_ENERGY_PER_CRAFT_SECOND
|
||||
if (energy_share < energy_cost) then goto next_assembler end -- Not enough energy!
|
||||
|
||||
-- Assume only 1 product and that it's an item!
|
||||
local recipe_product = recipe.products[next(recipe.products)]
|
||||
if recipe_product.type ~= "item" then goto next_assembler end
|
||||
|
||||
local input_inv = assembler.get_inventory(defines.inventory.assembling_machine_input)
|
||||
local input_items = input_inv.get_contents()
|
||||
local input_fluids = assembler.get_fluid_contents()
|
||||
|
||||
for _,v in ipairs(recipe.ingredients) do
|
||||
if (not input_items[v.name] or (input_items[v.name] < v.amount)) then
|
||||
if (not input_fluids[v.name] or (input_fluids[v.name] < v.amount)) then
|
||||
goto next_assembler -- Not enough ingredients
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local output_inv = assembler.get_inventory(defines.inventory.assembling_machine_output)
|
||||
if not output_inv.can_insert({name=recipe_product.name, amount=recipe_product.amount}) then
|
||||
goto next_assembler -- Can we insert the result?
|
||||
end
|
||||
|
||||
-- Add recipe count to output
|
||||
output_inv.insert({name=recipe_product.name, count=recipe_product.amount})
|
||||
if (assembler.last_user) then
|
||||
assembler.last_user.force.item_production_statistics.on_flow(recipe_product.name, recipe_product.amount)
|
||||
end
|
||||
|
||||
-- Subtract ingredients from input
|
||||
for _,v in ipairs(recipe.ingredients) do
|
||||
if (input_items[v.name]) then
|
||||
input_inv.remove({name=v.name, count=v.amount})
|
||||
if (assembler.last_user) then
|
||||
assembler.last_user.force.item_production_statistics.on_flow(v.name, -v.amount)
|
||||
end
|
||||
elseif (input_fluids[v.name]) then
|
||||
assembler.remove_fluid{name=v.name, amount=v.amount}
|
||||
if (assembler.last_user) then
|
||||
assembler.last_user.force.fluid_production_statistics.on_flow(v.name, -v.amount)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Track energy usage
|
||||
entry.energy_input.energy = entry.energy_input.energy - energy_cost
|
||||
assembler.surface.pollute(assembler.position, recipe.energy*ASSEMBLER3_POLLUTION_PER_CRAFT_SECOND)
|
||||
|
||||
assembler.products_finished = assembler.products_finished + 1
|
||||
|
||||
::next_assembler::
|
||||
end
|
||||
|
||||
::next_assembler_entry::
|
||||
end
|
||||
end
|
||||
|
||||
function MagicCentrifugeOnTick()
|
||||
if not global.omagic.centrifuges then return end
|
||||
|
||||
for entry_idx,entry in pairs(global.omagic.centrifuges) do
|
||||
|
||||
-- Validate the entry.
|
||||
if (entry == nil) or (entry.entities == nil) or (entry.energy_input == nil) or (not entry.energy_input.valid) then
|
||||
global.omagic.centrifuges[entry_idx] = nil
|
||||
log("MagicCentrifugeOnTick - Magic centrifuge entry removed?")
|
||||
goto next_centrifuge_entry
|
||||
end
|
||||
|
||||
local energy_share = entry.energy_input.energy/#entry.entities
|
||||
|
||||
for idx,centrifuge in pairs(entry.entities) do
|
||||
|
||||
if (centrifuge == nil) or (not centrifuge.valid) then
|
||||
global.omagic.centrifuges[entry_idx] = nil
|
||||
log("MagicCentrifugeOnTick - Magic centrifuge removed?")
|
||||
goto next_centrifuge_entry
|
||||
end
|
||||
|
||||
recipe = centrifuge.get_recipe()
|
||||
|
||||
if (not recipe) then
|
||||
goto next_centrifuge -- No recipe means do nothing.
|
||||
end
|
||||
|
||||
local energy_cost = recipe.energy * CENTRIFUGE_ENERGY_PER_CRAFT_SECOND
|
||||
if (energy_share < energy_cost) then goto next_centrifuge end -- Not enough energy!
|
||||
|
||||
local input_inv = centrifuge.get_inventory(defines.inventory.assembling_machine_input)
|
||||
local input_items = input_inv.get_contents()
|
||||
|
||||
for _,v in ipairs(recipe.ingredients) do
|
||||
if (not input_items[v.name] or (input_items[v.name] < v.amount)) then
|
||||
goto next_centrifuge -- Not enough ingredients
|
||||
end
|
||||
end
|
||||
|
||||
local output_inv = centrifuge.get_inventory(defines.inventory.assembling_machine_output)
|
||||
|
||||
local output_item, output_count
|
||||
|
||||
-- 10 uranium ore IN
|
||||
-- .993 uranium-238 and .007 uranium-235 OUT
|
||||
if (recipe.name == "uranium-processing") then
|
||||
|
||||
local rand_chance = math.random()
|
||||
|
||||
output_count = 1
|
||||
if (rand_chance <= .007) then
|
||||
output_item = "uranium-235"
|
||||
else
|
||||
output_item = "uranium-238"
|
||||
end
|
||||
|
||||
-- Check if we can insert at least 1 of BOTH.
|
||||
if not output_inv.can_insert({name="uranium-235", amount=output_count}) then
|
||||
goto next_centrifuge
|
||||
end
|
||||
if not output_inv.can_insert({name= "uranium-238", amount=output_count}) then
|
||||
goto next_centrifuge
|
||||
end
|
||||
|
||||
output_inv.insert({name=output_item, count=output_count})
|
||||
if (centrifuge.last_user) then
|
||||
centrifuge.last_user.force.item_production_statistics.on_flow(output_item, output_count)
|
||||
end
|
||||
|
||||
for _,v in ipairs(recipe.ingredients) do
|
||||
if (input_items[v.name]) then
|
||||
input_inv.remove({name=v.name, count=v.amount})
|
||||
if (centrifuge.last_user) then
|
||||
centrifuge.last_user.force.item_production_statistics.on_flow(v.name, -v.amount)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
goto next_centrifuge -- Unsupported!
|
||||
end
|
||||
|
||||
centrifuge.products_finished = centrifuge.products_finished + 1
|
||||
|
||||
-- Track energy usage
|
||||
entry.energy_input.energy = entry.energy_input.energy - energy_cost
|
||||
centrifuge.surface.pollute(centrifuge.position, recipe.energy*CENTRIFUGE_POLLUTION_PER_CRAFT_SECOND)
|
||||
|
||||
::next_centrifuge::
|
||||
end
|
||||
|
||||
::next_centrifuge_entry::
|
||||
end
|
||||
end
|
||||
|
||||
COIN_MULTIPLIER = 2
|
||||
|
||||
COIN_GENERATION_CHANCES = {
|
||||
["small-biter"] = 0.01,
|
||||
["medium-biter"] = 0.02,
|
||||
["big-biter"] = 0.05,
|
||||
["behemoth-biter"] = 1,
|
||||
|
||||
["small-spitter"] = 0.01,
|
||||
["medium-spitter"] = 0.02,
|
||||
["big-spitter"] = 0.05,
|
||||
["behemoth-spitter"] = 1,
|
||||
|
||||
["small-worm-turret"] = 5,
|
||||
["medium-worm-turret"] = 10,
|
||||
["big-worm-turret"] = 15,
|
||||
["behemoth-worm-turret"] = 25,
|
||||
|
||||
["biter-spawner"] = 20,
|
||||
["spitter-spawner"] = 20,
|
||||
}
|
||||
|
||||
function CoinsFromEnemiesOnPostEntityDied(event)
|
||||
if (not event.prototype or not event.prototype.name) then return end
|
||||
|
||||
local coin_chance = nil
|
||||
if (COIN_GENERATION_CHANCES[event.prototype.name]) then
|
||||
coin_chance = COIN_GENERATION_CHANCES[event.prototype.name]
|
||||
end
|
||||
|
||||
if (coin_chance) then
|
||||
DropCoins(event.position, coin_chance, event.force)
|
||||
end
|
||||
end
|
||||
|
||||
-- Drop coins, force is optional, decon is applied if force is not nil.
|
||||
function DropCoins(pos, count, force)
|
||||
|
||||
local drop_amount = 0
|
||||
|
||||
-- If count is less than 1, it represents a probability to drop a single coin
|
||||
if (count < 1) then
|
||||
if (math.random() < count) then
|
||||
drop_amount = 1
|
||||
end
|
||||
|
||||
-- If count is 1 or more, it represents a probability to drop at least that amount and up to 3x
|
||||
elseif (count >= 1) then
|
||||
drop_amount = math.random(count,count*COIN_MULTIPLIER)
|
||||
end
|
||||
|
||||
if drop_amount == 0 then return end
|
||||
game.surfaces[GAME_SURFACE_NAME].spill_item_stack(pos, {name="coin", count=math.floor(drop_amount)}, true, force, false) -- Set nil to force to auto decon.
|
||||
end
|
@ -1,35 +0,0 @@
|
||||
-- notepad.lua
|
||||
-- Oarc's simple notepad cause I keep forgetting what I want to do next.
|
||||
|
||||
|
||||
function CreateNotepadGuiTab(tab_container, player)
|
||||
|
||||
if global.oarc_notepad == nil then
|
||||
global.oarc_notepad = {}
|
||||
end
|
||||
|
||||
if global.oarc_notepad[player.name] == nil then
|
||||
global.oarc_notepad[player.name] = "Write something here...!"
|
||||
end
|
||||
|
||||
AddLabel(tab_container, "notepad_info", "Use this to take notes:", my_longer_label_style)
|
||||
|
||||
local txt_box = tab_container.add{type="text-box", name="oarc_notepad_textbox", text=global.oarc_notepad[player.name]}
|
||||
ApplyStyle(txt_box, my_notepad_fixed_width_style)
|
||||
|
||||
txt_box.focus()
|
||||
end
|
||||
|
||||
|
||||
function NotepadOnGuiTextChange(event)
|
||||
|
||||
if (event.element.name ~= "oarc_notepad_textbox") then return end
|
||||
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
if global.oarc_notepad == nil then
|
||||
global.oarc_notepad = {}
|
||||
end
|
||||
|
||||
global.oarc_notepad[player.name] = event.element.text
|
||||
end
|
283
lib/oarc_buy.lua
283
lib/oarc_buy.lua
@ -1,283 +0,0 @@
|
||||
-- oarc_buy.lua
|
||||
-- May 2020
|
||||
-- Adding microtransactions.
|
||||
|
||||
require("lib/oarc_store_player_items")
|
||||
require("lib/oarc_store_map_features")
|
||||
local mod_gui = require("mod-gui")
|
||||
|
||||
-- NAME of the top level element (outer frame)
|
||||
OARC_STORE_GUI = "oarc_store_gui"
|
||||
|
||||
OARC_PLAYER_STORE_GUI_TAB_NAME = "Item Store"
|
||||
OARC_MAP_FEATURE_GUI_TAB_NAME = "Special Store"
|
||||
|
||||
local OARC_STORE_TAB_CONTENT_FUNCTIONS = {}
|
||||
OARC_STORE_TAB_CONTENT_FUNCTIONS[OARC_PLAYER_STORE_GUI_TAB_NAME] = CreatePlayerStoreTab
|
||||
OARC_STORE_TAB_CONTENT_FUNCTIONS[OARC_MAP_FEATURE_GUI_TAB_NAME] = CreateMapFeatureStoreTab
|
||||
|
||||
function InitOarcStoreGuiTabs(player)
|
||||
CreateOarcStoreButton(player)
|
||||
CreateOarcStoreTabsPane(player)
|
||||
|
||||
-- Store for personal items
|
||||
AddOarcStoreTab(player, OARC_PLAYER_STORE_GUI_TAB_NAME)
|
||||
SetOarcStoreTabEnabled(player, OARC_PLAYER_STORE_GUI_TAB_NAME, true)
|
||||
|
||||
-- Store for map feature stuff
|
||||
AddOarcStoreTab(player, OARC_MAP_FEATURE_GUI_TAB_NAME)
|
||||
SetOarcStoreTabEnabled(player, OARC_MAP_FEATURE_GUI_TAB_NAME, true)
|
||||
|
||||
HideOarcStore(player)
|
||||
end
|
||||
|
||||
function CreateOarcStoreButton(player)
|
||||
if (mod_gui.get_button_flow(player).oarc_store == nil) then
|
||||
local b = mod_gui.get_button_flow(player).add{name="oarc_store",
|
||||
type="sprite-button",
|
||||
sprite="item/coin",
|
||||
style=mod_gui.button_style}
|
||||
b.style.padding=2
|
||||
end
|
||||
end
|
||||
|
||||
function DoesOarcStoreExist(player)
|
||||
return (mod_gui.get_frame_flow(player)[OARC_STORE_GUI] ~= nil)
|
||||
end
|
||||
|
||||
function IsOarcStoreVisible(player)
|
||||
local of = mod_gui.get_frame_flow(player)[OARC_STORE_GUI]
|
||||
return (of.visible)
|
||||
end
|
||||
|
||||
function ShowOarcStore(player)
|
||||
local of = mod_gui.get_frame_flow(player)[OARC_STORE_GUI]
|
||||
if (of == nil) then return end
|
||||
of.visible = true
|
||||
player.opened = of
|
||||
end
|
||||
|
||||
function HideOarcStore(player)
|
||||
local of = mod_gui.get_frame_flow(player)[OARC_STORE_GUI]
|
||||
if (of == nil) then return end
|
||||
of.visible = false
|
||||
player.opened = nil
|
||||
end
|
||||
|
||||
function GetOarcStoreTabsPane(player)
|
||||
if (mod_gui.get_frame_flow(player)[OARC_STORE_GUI] == nil) then
|
||||
return nil
|
||||
else
|
||||
return mod_gui.get_frame_flow(player)[OARC_STORE_GUI].store_if.store_tabs
|
||||
end
|
||||
end
|
||||
|
||||
function ClickOarcStoreButton(event)
|
||||
if not (event and event.element and event.element.valid) then return end
|
||||
local button = event.element
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
-- Don't allow any clicks on the store while player is dead!
|
||||
if (not player or player.ticks_to_respawn) then
|
||||
if (DoesOarcStoreExist(player)) then
|
||||
HideOarcStore(player)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if (button.name == "oarc_store") then
|
||||
if (not DoesOarcStoreExist(player)) then
|
||||
CreateOarcStoreTabsPane(player)
|
||||
else
|
||||
if (IsOarcStoreVisible(player)) then
|
||||
HideOarcStore(player)
|
||||
else
|
||||
ShowOarcStore(player)
|
||||
FakeTabChangeEventOarcStore(player)
|
||||
end
|
||||
end
|
||||
elseif ((button.parent ~= nil) and (button.parent.parent ~= nil)) then
|
||||
if (button.parent.parent.name == OARC_PLAYER_STORE_GUI_TAB_NAME.."_if") then
|
||||
OarcPlayerStoreButton(event)
|
||||
elseif (button.parent.parent.name == OARC_MAP_FEATURE_GUI_TAB_NAME.."_if") then
|
||||
OarcMapFeatureStoreButton(event)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TabChangeOarcStore(event)
|
||||
if (event.element.name ~= "store_tabs") then return end
|
||||
|
||||
local player = game.players[event.player_index]
|
||||
local otabs = event.element
|
||||
local selected_tab_name = otabs.tabs[otabs.selected_tab_index].tab.name
|
||||
|
||||
-- Clear all tab contents
|
||||
for i,t in pairs(otabs.tabs) do
|
||||
t.content.clear()
|
||||
end
|
||||
|
||||
SetOarcStoreTabContent(player, selected_tab_name)
|
||||
end
|
||||
|
||||
function FakeTabChangeEventOarcStore(player)
|
||||
local event = {}
|
||||
event.element = GetOarcStoreTabsPane(player)
|
||||
event.player_index = player.index
|
||||
TabChangeOarcStore(event)
|
||||
end
|
||||
|
||||
function CreateOarcStoreTabsPane(player)
|
||||
if (mod_gui.get_frame_flow(player)[OARC_STORE_GUI] == nil) then
|
||||
|
||||
-- OUTER FRAME (TOP GUI ELEMENT)
|
||||
local frame = mod_gui.get_frame_flow(player).add{
|
||||
type = 'frame',
|
||||
name = OARC_STORE_GUI,
|
||||
direction = "vertical"}
|
||||
frame.style.padding = 5
|
||||
|
||||
-- INNER FRAME
|
||||
local inside_frame = frame.add{
|
||||
type = "frame",
|
||||
name = "store_if",
|
||||
style = "inside_deep_frame",
|
||||
direction = "vertical"
|
||||
}
|
||||
|
||||
-- SUB HEADING w/ LABEL
|
||||
local subhead = inside_frame.add{
|
||||
type="frame",
|
||||
name="sub_header",
|
||||
style = "changelog_subheader_frame",
|
||||
direction = "vertical"}
|
||||
AddLabel(subhead, "store_info", "OARC Microtransactions and DLC", "subheader_caption_label")
|
||||
|
||||
-- TABBED PANE
|
||||
local store_tabs = inside_frame.add{
|
||||
name="store_tabs",
|
||||
type="tabbed-pane",
|
||||
style="tabbed_pane"}
|
||||
store_tabs.style.top_padding = 8
|
||||
end
|
||||
end
|
||||
|
||||
function AddOarcStoreTab(player, tab_name)
|
||||
-- if (not DoesOarcStoreExist(player)) then
|
||||
-- CreateOarcStoreTabsPane(player)
|
||||
-- end
|
||||
|
||||
-- Get the tabbed pane
|
||||
local otabs = GetOarcStoreTabsPane(player)
|
||||
|
||||
-- Create new tab
|
||||
local new_tab = otabs.add{
|
||||
type="tab",
|
||||
name=tab_name,
|
||||
caption=tab_name}
|
||||
|
||||
-- Create inside frame for content
|
||||
local tab_inside_frame = otabs.add{
|
||||
type="frame",
|
||||
name=tab_name.."_if",
|
||||
style = "inside_deep_frame",
|
||||
direction="vertical"}
|
||||
tab_inside_frame.style.left_margin = 10
|
||||
tab_inside_frame.style.right_margin = 10
|
||||
tab_inside_frame.style.top_margin = 4
|
||||
tab_inside_frame.style.bottom_margin = 4
|
||||
tab_inside_frame.style.padding = 5
|
||||
tab_inside_frame.style.horizontally_stretchable = true
|
||||
-- tab_inside_frame.style.vertically_stretchable = true
|
||||
-- tab_inside_frame.style.horizontally_squashable = true
|
||||
-- tab_inside_frame.style.vertically_squashable = true
|
||||
|
||||
-- Add the whole thing to the tab now.
|
||||
otabs.add_tab(new_tab, tab_inside_frame)
|
||||
|
||||
-- Disable all new tabs by default
|
||||
new_tab.enabled = false
|
||||
|
||||
-- If no other tabs are selected, select the first one.
|
||||
if (otabs.selected_tab_index == nil) then
|
||||
otabs.selected_tab_index = 1
|
||||
end
|
||||
end
|
||||
|
||||
function SetOarcStoreTabContent(player, tab_name)
|
||||
if (not DoesOarcStoreExist(player)) then return end
|
||||
|
||||
local otabs = GetOarcStoreTabsPane(player)
|
||||
|
||||
for _,t in ipairs(otabs.tabs) do
|
||||
if (t.tab.name == tab_name) then
|
||||
t.content.clear()
|
||||
OARC_STORE_TAB_CONTENT_FUNCTIONS[tab_name](t.content, player)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SetOarcStoreTabEnabled(player, tab_name, enable)
|
||||
if (not DoesOarcStoreExist(player)) then return end
|
||||
|
||||
local otabs = GetOarcStoreTabsPane(player)
|
||||
|
||||
for _,t in ipairs(otabs.tabs) do
|
||||
if (t.tab.name == tab_name) then
|
||||
t.tab.enabled = enable
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function OarcStoreOnGuiClosedEvent(event)
|
||||
if (event.element and (event.element.name == OARC_STORE_GUI)) then
|
||||
HideOarcStore(game.players[event.player_index])
|
||||
end
|
||||
end
|
||||
|
||||
commands.add_command("donate-coins", "Toss a Coin to Your Witcher", function(command)
|
||||
|
||||
local player = game.players[command.player_index]
|
||||
|
||||
if (command.parameter == nil) then
|
||||
player.print("Invalid parameters? /donate-coins [username] [amount]")
|
||||
return
|
||||
end
|
||||
|
||||
local target, amount
|
||||
local count = 1
|
||||
for i in string.gmatch(command.parameter, "%S+") do
|
||||
if (count == 1) then
|
||||
target = i
|
||||
end
|
||||
if (count == 2) then
|
||||
amount = i
|
||||
end
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
if (count ~= 3) then
|
||||
player.print("Invalid parameters (count = " ..count..")? /donate-coins [username] [amount]")
|
||||
return
|
||||
end
|
||||
|
||||
-- Validate all the things...
|
||||
if (game.players[target] and
|
||||
not game.players[target].ticks_to_respawn and
|
||||
amount and
|
||||
player and
|
||||
player.get_main_inventory()) then
|
||||
local target_player = game.players[target]
|
||||
local amount_number = tonumber(amount)
|
||||
if ((amount_number > 0) and (player.get_main_inventory().get_item_count("coin") >= amount_number)) then
|
||||
local transfer = target_player.get_main_inventory().insert({name="coin", count=amount_number})
|
||||
player.get_main_inventory().remove({name="coin", count=transfer})
|
||||
player.print("You transfered " .. transfer .. " coins to " .. target .. ".")
|
||||
target_player.print("You received " .. transfer .. " coins from " .. player.name .. ".")
|
||||
else
|
||||
player.print("You can't transfer what you don't have... (Not enough coins!)")
|
||||
end
|
||||
end
|
||||
end)
|
@ -1,163 +0,0 @@
|
||||
-- oarc_enemies.lua
|
||||
-- Feb 2020
|
||||
|
||||
-- This is my second attempt at modifying the normal enemy experience. The
|
||||
-- first attempt ended up in a wave attack system which wasn't well received.
|
||||
-- This attempt will try to intercept normal vanilla enemy groups and modify
|
||||
-- them based on player activity.
|
||||
|
||||
-- Basic logic:
|
||||
-- on_unit_group_finished_gathering we check what command is given.
|
||||
-- find destination position
|
||||
-- check for closest "player" using find_nearest_enemy function
|
||||
-- if a player is found, check if player is part of a shared spawn
|
||||
-- Remove the enemy group if no player in the shared spawn is online.
|
||||
|
||||
-- TODO:
|
||||
-- Add options for modifying the default waves or spawning additional special waves.
|
||||
-- Add option to disable attacks completely for a given spawn.
|
||||
|
||||
-- Generic Utility Includes
|
||||
require("lib/oarc_utils")
|
||||
|
||||
function OarcModifyEnemyGroup(group)
|
||||
|
||||
-- Check validity
|
||||
if ((group == nil) or (group.command == nil) or (group.force.name ~= "enemy")) then
|
||||
log("OarcModifyEnemyGroup ignoring INVALID group/command")
|
||||
return
|
||||
end
|
||||
|
||||
-- Make sure the attack is of a TYPE that we care about.
|
||||
if ((group.command.type == defines.command.attack) or
|
||||
(group.command.type == defines.command.attack_area) or
|
||||
(group.command.type == defines.command.build_base)) then
|
||||
log("OarcModifyEnemyGroup MODIFYING command TYPE=" .. group.command.type)
|
||||
else
|
||||
log("OarcModifyEnemyGroup ignoring command TYPE=" .. group.command.type)
|
||||
return
|
||||
end
|
||||
|
||||
-- defines.command.attack --> target --> target.position
|
||||
if (group.command.type == defines.command.attack) then
|
||||
log("OarcModifyEnemyGroup defines.command.attack NOT IMPLEMENTED YET!")
|
||||
return
|
||||
|
||||
-- defines.command.attack_area --> destination --> closest enemy (within 3 chunk radius?)
|
||||
-- defines.command.build_base --> destination --> closest enemy (expansion chunk distance?)
|
||||
else
|
||||
local destination = group.command.destination
|
||||
|
||||
local distance = CHUNK_SIZE*3
|
||||
if (group.command.type == defines.command.build_base) then
|
||||
distance = CHUNK_SIZE*(game.map_settings.enemy_expansion.max_expansion_distance)
|
||||
end
|
||||
|
||||
-- Find some enemies near the attack point.
|
||||
local target_entities = group.surface.find_entities_filtered{
|
||||
position=destination,
|
||||
radius=distance,
|
||||
force={"enemy", "neutral"},
|
||||
limit=50,
|
||||
invert=true}
|
||||
|
||||
-- Search through them all to find anything with a last_user.
|
||||
local target_entity = nil
|
||||
for _,target in ipairs(target_entities) do
|
||||
if (target.last_user ~= nil) then
|
||||
target_entity = target
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- No enemies nearby?
|
||||
if (target_entity == nil) then
|
||||
if (group.command.type == defines.command.attack_area) then
|
||||
if (global.enable_oe_debug) then
|
||||
SendBroadcastMsg("OarcModifyEnemyGroup find_nearest_enemy attack_area FAILED!?!? " .. GetGPStext(group.position) .. " Target: " .. GetGPStext(destination))
|
||||
end
|
||||
log("OarcModifyEnemyGroup UNEXPECTED find_nearest_enemy did not find anything!")
|
||||
for _,member in pairs(group.members) do
|
||||
member.destroy()
|
||||
end
|
||||
else
|
||||
log("OarcModifyEnemyGroup find_nearest_enemy did not find anything!")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Probably don't need this I hope?
|
||||
if (target_entity.force == "neutral") then
|
||||
log("OarcModifyEnemyGroup UNEXPECTED find_nearest_enemy found neutral target?")
|
||||
for _,member in pairs(group.members) do
|
||||
member.destroy()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Most common target will be a built entity with a "last_user"
|
||||
local target_player = target_entity.last_user
|
||||
|
||||
-- Target could also be a player character (more rare)
|
||||
if (target_player == nil) and (target_entity.type == "character") then
|
||||
target_player = target_entity.player
|
||||
end
|
||||
|
||||
-- I don't think this should happen...
|
||||
if ((target_player == nil) or (not target_player.valid)) then
|
||||
if (global.enable_oe_debug) then
|
||||
SendBroadcastMsg("ERROR?? target_player nil/invalid " .. GetGPStext(group.position) .. " Target: " .. GetGPStext(target_entity.position))
|
||||
end
|
||||
log("OarcModifyEnemyGroup ERROR?? target_player nil/invalid")
|
||||
for _,member in pairs(group.members) do
|
||||
member.destroy()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Is the target player online? Then the attack can go through.
|
||||
if (target_player.connected) then
|
||||
if (global.enable_oe_debug) then
|
||||
SendBroadcastMsg("Enemy group released (player): " .. GetGPStext(group.position) .. " Target: " .. GetGPStext(target_entity.position) .. " " .. target_player.name)
|
||||
end
|
||||
log("OarcModifyEnemyGroup RELEASING enemy group since player is ONLINE")
|
||||
return
|
||||
end
|
||||
|
||||
-- Find the shared spawn that the player is part of.
|
||||
-- This could be the own player's spawn (quite likely)
|
||||
local sharedSpawnOwnerName = FindPlayerSharedSpawn(target_player.name)
|
||||
|
||||
-- Is someone in the shared spawn online?
|
||||
if (sharedSpawnOwnerName ~= nil) then
|
||||
if (GetOnlinePlayersAtSharedSpawn(sharedSpawnOwnerName) > 0) then
|
||||
if (global.enable_oe_debug) then
|
||||
SendBroadcastMsg("Enemy group released (shared): " .. GetGPStext(group.position) .. " Target: " .. GetGPStext(target_entity.position) .. " " .. target_player.name)
|
||||
end
|
||||
log("OarcModifyEnemyGroup RELEASING enemy group since someone in the group is ONLINE")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Is there a buddy spawn and is the buddy online?
|
||||
local buddyName = global.ocore.buddyPairs[sharedSpawnOwnerName]
|
||||
if (buddyName ~= nil) and (game.players[buddyName] ~= nil) then
|
||||
if (game.players[buddyName].connected or (GetOnlinePlayersAtSharedSpawn(buddyName) > 0)) then
|
||||
if (global.enable_oe_debug) then
|
||||
SendBroadcastMsg("Enemy group released (buddy): " .. GetGPStext(group.position) .. " Target: " .. GetGPStext(target_entity.position) .. " " .. target_player.name)
|
||||
end
|
||||
log("OarcModifyEnemyGroup RELEASING enemy group since someone in the BUDDY PAIR is ONLINE")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Otherwise, we delete the group.
|
||||
if (global.enable_oe_debug) then
|
||||
SendBroadcastMsg("Enemy group deleted: " .. GetGPStext(group.position) .. " Target: " .. GetGPStext(target_entity.position) .. " " .. target_player.name)
|
||||
end
|
||||
for _,member in pairs(group.members) do
|
||||
member.destroy()
|
||||
end
|
||||
log("OarcModifyEnemyGroup REMOVED enemy group since nobody was online?")
|
||||
end
|
||||
end
|
@ -1,102 +0,0 @@
|
||||
-- oarc_global_cfg.lua
|
||||
-- April 2019
|
||||
--
|
||||
-- Here is where we store/init config values to the global table.
|
||||
-- Allows runtime modification of game settings if we want it.
|
||||
-- Also allows supporting both MOD and SCENARIO versions.
|
||||
|
||||
-- DON'T JUDGE ME
|
||||
|
||||
|
||||
-- That's a LOT of settings.
|
||||
function InitOarcConfig()
|
||||
|
||||
global.ocfg = {}
|
||||
|
||||
if (game.active_mods["clean-tutorial-grid"]) then
|
||||
global.ocfg.locked_build_area_tile = "clean-tutorial-grid"
|
||||
else
|
||||
global.ocfg.locked_build_area_tile = "tutorial-grid"
|
||||
end
|
||||
|
||||
-- SCENARIO VERSION (ONLY - no more mod version.)
|
||||
global.ocfg.welcome_title = WELCOME_MSG_TITLE
|
||||
global.ocfg.welcome_msg = WELCOME_MSG
|
||||
global.ocfg.server_rules = SERVER_MSG
|
||||
global.ocfg.minimum_online_time = MIN_ONLINE_TIME_IN_MINUTES
|
||||
global.ocfg.server_contact = CONTACT_MSG
|
||||
global.ocfg.enable_vanilla_spawns = ENABLE_VANILLA_SPAWNS
|
||||
global.ocfg.enable_buddy_spawn = ENABLE_BUDDY_SPAWN
|
||||
global.ocfg.frontier_rocket_silo = FRONTIER_ROCKET_SILO_MODE
|
||||
global.ocfg.silo_islands = SILO_ISLANDS_MODE
|
||||
global.ocfg.enable_undecorator = ENABLE_UNDECORATOR
|
||||
global.ocfg.enable_tags = ENABLE_TAGS
|
||||
global.ocfg.enable_long_reach = ENABLE_LONGREACH
|
||||
global.ocfg.enable_autofill = ENABLE_AUTOFILL
|
||||
global.ocfg.enable_miner_decon = ENABLE_MINER_AUTODECON
|
||||
global.ocfg.enable_player_list = ENABLE_PLAYER_LIST
|
||||
global.ocfg.list_offline_players = PLAYER_LIST_OFFLINE_PLAYERS
|
||||
global.ocfg.enable_shared_team_vision = ENABLE_SHARED_TEAM_VISION
|
||||
global.ocfg.enable_regrowth = ENABLE_REGROWTH
|
||||
global.ocfg.enable_abandoned_base_removal = ENABLE_ABANDONED_BASE_REMOVAL
|
||||
global.ocfg.enable_research_queue = ENABLE_RESEARCH_QUEUE
|
||||
global.ocfg.enable_coin_shop = ENABLE_COIN_SHOP
|
||||
global.ocfg.enable_chest_sharing = ENABLE_ITEM_AND_ENERGY_SHARING
|
||||
global.ocfg.enable_magic_factories = ENABLE_MAGIC_FACTORIES
|
||||
global.ocfg.enable_offline_protect = ENABLE_OFFLINE_PROTECTION
|
||||
global.ocfg.enable_power_armor_start = ENABLE_POWER_ARMOR_QUICK_START
|
||||
global.ocfg.enable_modular_armor_start = ENABLE_MODULAR_ARMOR_QUICK_START
|
||||
global.ocfg.lock_goodies_rocket_launch = LOCK_GOODIES_UNTIL_ROCKET_LAUNCH
|
||||
global.ocfg.scale_resources_around_spawns = SCALE_RESOURCES_AROUND_SPAWNS
|
||||
|
||||
global.ocfg.modified_enemy_spawning = OARC_MODIFIED_ENEMY_SPAWNING
|
||||
global.ocfg.near_dist_start = NEAR_MIN_DIST
|
||||
global.ocfg.near_dist_end = NEAR_MAX_DIST
|
||||
global.ocfg.far_dist_start = FAR_MIN_DIST
|
||||
global.ocfg.far_dist_end = FAR_MAX_DIST
|
||||
global.ocfg.vanilla_spawn_count = VANILLA_SPAWN_COUNT
|
||||
global.ocfg.vanilla_spawn_spacing = VANILLA_SPAWN_SPACING
|
||||
|
||||
global.ocfg.spawn_config = OARC_CFG
|
||||
|
||||
global.ocfg.enable_separate_teams = ENABLE_SEPARATE_TEAMS
|
||||
global.ocfg.main_force = MAIN_FORCE
|
||||
global.ocfg.enable_shared_spawns = ENABLE_SHARED_SPAWNS
|
||||
global.ocfg.max_players_shared_spawn = MAX_PLAYERS_AT_SHARED_SPAWN
|
||||
global.ocfg.enable_shared_chat = ENABLE_SHARED_TEAM_CHAT
|
||||
global.ocfg.respawn_cooldown_min = RESPAWN_COOLDOWN_IN_MINUTES
|
||||
global.ocfg.frontier_silo_count = SILO_NUM_SPAWNS
|
||||
global.ocfg.frontier_silo_distance = SILO_CHUNK_DISTANCE
|
||||
global.ocfg.frontier_fixed_pos = SILO_FIXED_POSITION
|
||||
global.ocfg.frontier_pos_table = SILO_POSITIONS
|
||||
global.ocfg.frontier_silo_vision = ENABLE_SILO_VISION
|
||||
global.ocfg.frontier_allow_build = ENABLE_SILO_PLAYER_BUILD
|
||||
|
||||
global.ocfg.enable_anti_grief = ENABLE_ANTI_GRIEFING
|
||||
global.ocfg.ghost_ttl = GHOST_TIME_TO_LIVE
|
||||
global.ocfg.enable_friendly_fire = ENABLE_FRIENDLY_FIRE
|
||||
|
||||
global.ocfg.enable_server_write_files = ENABLE_SERVER_WRITE_FILES
|
||||
|
||||
|
||||
-----------------------
|
||||
-- VALIDATION CHECKS --
|
||||
-----------------------
|
||||
|
||||
if (not global.ocfg.frontier_rocket_silo or not global.ocfg.enable_vanilla_spawns) then
|
||||
global.ocfg.silo_islands = false
|
||||
end
|
||||
|
||||
if (global.ocfg.enable_vanilla_spawns) then
|
||||
global.ocfg.enable_buddy_spawn = false
|
||||
end
|
||||
|
||||
if (not global.ocfg.enable_coin_shop) then
|
||||
global.ocfg.enable_chest_sharing = false
|
||||
end
|
||||
|
||||
if (not global.ocfg.enable_chest_sharing) then
|
||||
global.ocfg.enable_magic_factories = false
|
||||
end
|
||||
|
||||
end
|
@ -1,6 +1,10 @@
|
||||
-- oarc_gui_tabs.lua
|
||||
-- A nice way to organize the GUI tabs.
|
||||
|
||||
local mod_gui = require("mod-gui")
|
||||
require("lib/gui_tabs/server_info")
|
||||
require("lib/gui_tabs/spawn_controls")
|
||||
require("lib/gui_tabs/settings_controls")
|
||||
require("lib/gui_tabs/mod_info_faq")
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- GUI Tab Handler
|
||||
@ -10,117 +14,121 @@ local mod_gui = require("mod-gui")
|
||||
OARC_GUI = "oarc_gui"
|
||||
|
||||
-- LIST of all implemented tabs and their content Functions
|
||||
OARC_GAME_OPTS_GUI_TAB_NAME = "Server Info"
|
||||
OARC_SPAWN_CTRL_GUI_NAME = "Spawn Controls"
|
||||
OARC_PLAYER_LIST_GUI_TAB_NAME = "Players"
|
||||
OARC_TAGS_GUI_TAB_NAME = "Name Tags"
|
||||
OARC_ROCKETS_GUI_TAB_NAME = "Rockets"
|
||||
OARC_SHARED_ITEMS_GUI_TAB_NAME = "Shared Items"
|
||||
OARC_NOTEPAD_GUI_TAB_NAME = "Notepad"
|
||||
OARC_SERVER_INFO_TAB_NAME = "server_info"
|
||||
OARC_SPAWN_CTRL_TAB_NAME = "spawn_controls"
|
||||
OARC_CONFIG_CTRL_TAB_NAME = "settings"
|
||||
OARC_MOD_INFO_CTRL_TAB_NAME = "mod_info"
|
||||
|
||||
local OARC_GUI_TAB_CONTENT_FUNCTIONS = {}
|
||||
OARC_GUI_TAB_CONTENT_FUNCTIONS["Server Info"] = CreateGameOptionsTab
|
||||
OARC_GUI_TAB_CONTENT_FUNCTIONS["Spawn Controls"] = CreateSpawnCtrlGuiTab
|
||||
OARC_GUI_TAB_CONTENT_FUNCTIONS["Players"] = CreatePlayerListGuiTab
|
||||
OARC_GUI_TAB_CONTENT_FUNCTIONS["Name Tags"] = CreateTagGuiTab
|
||||
OARC_GUI_TAB_CONTENT_FUNCTIONS["Rockets"] = CreateRocketGuiTab
|
||||
OARC_GUI_TAB_CONTENT_FUNCTIONS["Shared Items"] = CreateSharedItemsGuiTab
|
||||
OARC_GUI_TAB_CONTENT_FUNCTIONS["Notepad"] = CreateNotepadGuiTab
|
||||
OARC_SERVER_INFO_TAB_LOCALIZED = {"oarc-server-info-tab-title"}
|
||||
OARC_SPAWN_CTRL_TAB_LOCALIZED = {"oarc-spawn-ctrls-tab-title"}
|
||||
OARC_CONFIG_CTRL_TAB_LOCALIZED = {"oarc-settings-tab-title"}
|
||||
OARC_MOD_INFO_CTRL_TAB_LOCALIZED = {"oarc-mod-info-tab-title"}
|
||||
|
||||
local OARC_GUI_TAB_CONTENT_FUNCTIONS = {
|
||||
[OARC_SERVER_INFO_TAB_NAME] = CreateServerInfoTab,
|
||||
[OARC_SPAWN_CTRL_TAB_NAME] = CreateSpawnControlsTab,
|
||||
[OARC_MOD_INFO_CTRL_TAB_NAME] = CreateModInfoTab,
|
||||
[OARC_CONFIG_CTRL_TAB_NAME] = CreateSettingsControlsTab,
|
||||
}
|
||||
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function InitOarcGuiTabs(player)
|
||||
|
||||
-- Make safe to call multiple times
|
||||
if (DoesOarcGuiExist(player)) then
|
||||
return
|
||||
end
|
||||
|
||||
CreateOarcGuiButton(player)
|
||||
|
||||
-- Add general info tab
|
||||
AddOarcGuiTab(player, OARC_GAME_OPTS_GUI_TAB_NAME)
|
||||
SetOarcGuiTabEnabled(player, OARC_GAME_OPTS_GUI_TAB_NAME, true)
|
||||
AddOarcGuiTab(player, OARC_SERVER_INFO_TAB_NAME, OARC_SERVER_INFO_TAB_LOCALIZED)
|
||||
SetOarcGuiTabEnabled(player, OARC_SERVER_INFO_TAB_NAME, true)
|
||||
|
||||
-- Spawn control tab, disabled by default
|
||||
AddOarcGuiTab(player, OARC_SPAWN_CTRL_GUI_NAME)
|
||||
AddOarcGuiTab(player, OARC_SPAWN_CTRL_TAB_NAME, OARC_SPAWN_CTRL_TAB_LOCALIZED)
|
||||
|
||||
-- If player list is enabled, create that
|
||||
if global.ocfg.enable_player_list then
|
||||
AddOarcGuiTab(player, OARC_PLAYER_LIST_GUI_TAB_NAME)
|
||||
SetOarcGuiTabEnabled(player, OARC_PLAYER_LIST_GUI_TAB_NAME, true)
|
||||
end
|
||||
-- Regrowth control tab
|
||||
AddOarcGuiTab(player, OARC_MOD_INFO_CTRL_TAB_NAME, OARC_MOD_INFO_CTRL_TAB_LOCALIZED)
|
||||
SetOarcGuiTabEnabled(player, OARC_MOD_INFO_CTRL_TAB_NAME, true)
|
||||
|
||||
-- Player tags
|
||||
if global.ocfg.enable_tags then
|
||||
AddOarcGuiTab(player, OARC_TAGS_GUI_TAB_NAME)
|
||||
SetOarcGuiTabEnabled(player, OARC_TAGS_GUI_TAB_NAME, true)
|
||||
end
|
||||
-- Settings control tab
|
||||
AddOarcGuiTab(player, OARC_CONFIG_CTRL_TAB_NAME, OARC_CONFIG_CTRL_TAB_LOCALIZED)
|
||||
SetOarcGuiTabEnabled(player, OARC_CONFIG_CTRL_TAB_NAME, true)
|
||||
|
||||
-- Rockets tab, only enable if one has been launched already
|
||||
AddOarcGuiTab(player, OARC_ROCKETS_GUI_TAB_NAME)
|
||||
if (global.ocore.satellite_sent) then
|
||||
SetOarcGuiTabEnabled(player, OARC_ROCKETS_GUI_TAB_NAME, true)
|
||||
end
|
||||
|
||||
if global.ocfg.enable_chest_sharing then
|
||||
AddOarcGuiTab(player, OARC_SHARED_ITEMS_GUI_TAB_NAME)
|
||||
SetOarcGuiTabEnabled(player, OARC_SHARED_ITEMS_GUI_TAB_NAME, true)
|
||||
end
|
||||
|
||||
AddOarcGuiTab(player, OARC_NOTEPAD_GUI_TAB_NAME)
|
||||
SetOarcGuiTabEnabled(player, OARC_NOTEPAD_GUI_TAB_NAME, true)
|
||||
|
||||
HideOarcGui(player)
|
||||
end
|
||||
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function CreateOarcGuiButton(player)
|
||||
if (mod_gui.get_button_flow(player).oarc_button == nil) then
|
||||
local b = mod_gui.get_button_flow(player).add{name="oarc_button",
|
||||
caption="CLICK ME FOR MORE INFO",
|
||||
type="sprite-button",
|
||||
-- sprite="utility/expand_dots",
|
||||
style=mod_gui.button_style}
|
||||
b.style.padding=2
|
||||
-- b.style.width=20
|
||||
if (mod_gui.get_button_flow(player).oarc_mod_gui_button == nil) then
|
||||
local b = mod_gui.get_button_flow(player).add{
|
||||
name="oarc_mod_gui_button",
|
||||
type="sprite-button",
|
||||
sprite = "oarc-mod-sprite-40",
|
||||
style="slot_button",
|
||||
tooltip={ "oarc-gui-tooltip" }
|
||||
}
|
||||
b.style.padding=0
|
||||
end
|
||||
end
|
||||
|
||||
---@param player LuaPlayer
|
||||
---@return boolean
|
||||
function DoesOarcGuiExist(player)
|
||||
return (mod_gui.get_frame_flow(player)[OARC_GUI] ~= nil)
|
||||
end
|
||||
|
||||
---@param player LuaPlayer
|
||||
---@return boolean
|
||||
function IsOarcGuiVisible(player)
|
||||
---@type LuaGuiElement
|
||||
local of = mod_gui.get_frame_flow(player)[OARC_GUI]
|
||||
return (of.visible)
|
||||
end
|
||||
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function ShowOarcGui(player)
|
||||
---@type LuaGuiElement
|
||||
local of = mod_gui.get_frame_flow(player)[OARC_GUI]
|
||||
if (of == nil) then return end
|
||||
of.visible = true
|
||||
player.opened = of
|
||||
end
|
||||
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function HideOarcGui(player)
|
||||
---@type LuaGuiElement
|
||||
local of = mod_gui.get_frame_flow(player)[OARC_GUI]
|
||||
if (of == nil) then return end
|
||||
of.visible = false
|
||||
player.opened = nil
|
||||
end
|
||||
|
||||
---@param player LuaPlayer
|
||||
---@return LuaGuiElement?
|
||||
function GetOarcGuiTabsPane(player)
|
||||
if (mod_gui.get_frame_flow(player)[OARC_GUI] == nil) then
|
||||
---@type LuaGuiElement
|
||||
local of = mod_gui.get_frame_flow(player)[OARC_GUI]
|
||||
if (of == nil) then
|
||||
return nil
|
||||
else
|
||||
return mod_gui.get_frame_flow(player)[OARC_GUI].oarc_if.oarc_tabs
|
||||
return of.oarc_if.oarc_tabs
|
||||
end
|
||||
end
|
||||
|
||||
---@param event EventData.on_gui_click
|
||||
---@return nil
|
||||
function ClickOarcGuiButton(event)
|
||||
if not (event and event.element and event.element.valid) then return end
|
||||
if not event.element.valid then return end
|
||||
local player = game.players[event.player_index]
|
||||
local name = event.element.name
|
||||
|
||||
if (name ~= "oarc_button") then return end
|
||||
|
||||
if (event.element.caption ~= "") then
|
||||
event.element.caption = ""
|
||||
event.element.style.width = 20
|
||||
event.element.sprite="utility/expand_dots"
|
||||
end
|
||||
if (name ~= "oarc_mod_gui_button") then return end
|
||||
|
||||
if (not DoesOarcGuiExist(player)) then
|
||||
CreateOarcGuiTabsPane(player)
|
||||
@ -129,33 +137,48 @@ function ClickOarcGuiButton(event)
|
||||
HideOarcGui(player)
|
||||
else
|
||||
ShowOarcGui(player)
|
||||
FakeTabChangeEventOarcGui(player)
|
||||
OarcGuiCreateContentOfTab(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TabChangeOarcGui(event)
|
||||
---@param event EventData.on_gui_selected_tab_changed
|
||||
---@return nil
|
||||
function OarcGuiSelectedTabChanged(event)
|
||||
if (event.element.name ~= "oarc_tabs") then return end
|
||||
OarcGuiCreateContentOfTab(game.players[event.player_index])
|
||||
end
|
||||
|
||||
local player = game.players[event.player_index]
|
||||
local otabs = event.element
|
||||
local selected_tab_name = otabs.tabs[otabs.selected_tab_index].tab.name
|
||||
---Set tab content to currently selected tab, clears all other tab content and refreshes the selected tab content!
|
||||
---Safe to call just to refresh the current tab.
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function OarcGuiCreateContentOfTab(player)
|
||||
local otabs = GetOarcGuiTabsPane(player)
|
||||
if (otabs == nil) then return end
|
||||
|
||||
-- Clear all tab contents
|
||||
for i,t in pairs(otabs.tabs) do
|
||||
local tab_name = otabs.tabs[otabs.selected_tab_index].tab.name
|
||||
|
||||
-- log("OarcGuiCreateContentOfTab: " .. tab_name)
|
||||
|
||||
for _,t in ipairs(otabs.tabs) do
|
||||
t.content.clear()
|
||||
if (t.tab.name == tab_name) then
|
||||
OARC_GUI_TAB_CONTENT_FUNCTIONS[tab_name](t.content, player)
|
||||
end
|
||||
end
|
||||
|
||||
SetOarGuiTabContent(player, selected_tab_name)
|
||||
end
|
||||
|
||||
function FakeTabChangeEventOarcGui(player)
|
||||
local event = {}
|
||||
event.element = GetOarcGuiTabsPane(player)
|
||||
event.player_index = player.index
|
||||
TabChangeOarcGui(event)
|
||||
---Just an alias for OarcGuiCreateContentOfTab
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function OarcGuiRefreshContent(player)
|
||||
-- log("Hit OarcGuiRefreshContent" .. player.name)
|
||||
OarcGuiCreateContentOfTab(player)
|
||||
end
|
||||
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function CreateOarcGuiTabsPane(player)
|
||||
|
||||
if (mod_gui.get_frame_flow(player)[OARC_GUI] == nil) then
|
||||
@ -179,8 +202,9 @@ function CreateOarcGuiTabsPane(player)
|
||||
local subhead = inside_frame.add{
|
||||
type="frame",
|
||||
name="sub_header",
|
||||
style = "changelog_subheader_frame"}
|
||||
AddLabel(subhead, "scen_info", "Scenario Info and Controls", "subheader_caption_label")
|
||||
style = "changelog_subheader_frame"
|
||||
}
|
||||
AddLabel(subhead, nil, {"oarc-gui-tab-header-label"}, "subheader_caption_label")
|
||||
|
||||
-- TABBED PANE
|
||||
local oarc_tabs = inside_frame.add{
|
||||
@ -193,7 +217,10 @@ end
|
||||
|
||||
-- Function creates a new tab.
|
||||
-- It adds whatever it wants to the provided scroll-pane.
|
||||
function AddOarcGuiTab(player, tab_name)
|
||||
---@param player LuaPlayer
|
||||
---@param tab_name string
|
||||
---@param localized_name LocalisedString
|
||||
function AddOarcGuiTab(player, tab_name, localized_name)
|
||||
if (not DoesOarcGuiExist(player)) then
|
||||
CreateOarcGuiTabsPane(player)
|
||||
end
|
||||
@ -201,11 +228,13 @@ function AddOarcGuiTab(player, tab_name)
|
||||
-- Get the tabbed pane
|
||||
local otabs = GetOarcGuiTabsPane(player)
|
||||
|
||||
if (otabs == nil) then return end
|
||||
|
||||
-- Create new tab
|
||||
local new_tab = otabs.add{
|
||||
type="tab",
|
||||
name=tab_name,
|
||||
caption=tab_name}
|
||||
caption=localized_name}
|
||||
|
||||
-- Create inside frame for content
|
||||
local tab_inside_frame = otabs.add{
|
||||
@ -235,21 +264,10 @@ function AddOarcGuiTab(player, tab_name)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function SetOarGuiTabContent(player, tab_name)
|
||||
if (not DoesOarcGuiExist(player)) then return end
|
||||
|
||||
local otabs = GetOarcGuiTabsPane(player)
|
||||
|
||||
for _,t in ipairs(otabs.tabs) do
|
||||
if (t.tab.name == tab_name) then
|
||||
t.content.clear()
|
||||
OARC_GUI_TAB_CONTENT_FUNCTIONS[tab_name](t.content, player)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---This sets the enable state of a tab.
|
||||
---@param player LuaPlayer
|
||||
---@param tab_name string
|
||||
---@param enable boolean
|
||||
function SetOarcGuiTabEnabled(player, tab_name, enable)
|
||||
if (not DoesOarcGuiExist(player)) then return end
|
||||
|
||||
@ -263,6 +281,9 @@ function SetOarcGuiTabEnabled(player, tab_name, enable)
|
||||
end
|
||||
end
|
||||
|
||||
---Switches the tab to the one specified.
|
||||
---@param player LuaPlayer
|
||||
---@param tab_name string
|
||||
function SwitchOarcGuiTab(player, tab_name)
|
||||
if (not DoesOarcGuiExist(player)) then return end
|
||||
|
||||
@ -271,14 +292,15 @@ function SwitchOarcGuiTab(player, tab_name)
|
||||
for i,t in pairs(otabs.tabs) do
|
||||
if (t.tab.name == tab_name) then
|
||||
otabs.selected_tab_index = i
|
||||
FakeTabChangeEventOarcGui(player)
|
||||
OarcGuiCreateContentOfTab(player)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function OarcGuiOnGuiClosedEvent(event)
|
||||
--@param event EventData.on_gui_closed
|
||||
function OarcGuiClosed(event)
|
||||
if (event.element and (event.element.name == "oarc_gui")) then
|
||||
HideOarcGui(game.players[event.player_index])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,16 +1,18 @@
|
||||
-- oarc_gui_utils.lua
|
||||
-- Mar 2019
|
||||
|
||||
-- Generic GUI stuff goes here.
|
||||
|
||||
GENERIC_GUI_MAX_HEIGHT = 500
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- GUI Styles
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_fixed_width_style = {
|
||||
minimal_width = 450,
|
||||
maximal_width = 450
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_label_style = {
|
||||
-- minimal_width = 450,
|
||||
-- maximal_width = 50,
|
||||
@ -19,6 +21,8 @@ my_label_style = {
|
||||
top_padding = 0,
|
||||
bottom_padding = 0
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_label_header_style = {
|
||||
single_line = false,
|
||||
font = "heading-1",
|
||||
@ -26,6 +30,17 @@ my_label_header_style = {
|
||||
top_padding = 0,
|
||||
bottom_padding = 0
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_label_header2_style = {
|
||||
single_line = false,
|
||||
font = "heading-2",
|
||||
font_color = {r=1,g=1,b=1},
|
||||
top_padding = 0,
|
||||
bottom_padding = 0
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_label_header_grey_style = {
|
||||
single_line = false,
|
||||
font = "heading-1",
|
||||
@ -33,40 +48,55 @@ my_label_header_grey_style = {
|
||||
top_padding = 0,
|
||||
bottom_padding = 0
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_note_style = {
|
||||
-- minimal_width = 450,
|
||||
single_line = false,
|
||||
font = "default-small-semibold",
|
||||
font = "default-semibold",
|
||||
font_color = {r=1,g=0.5,b=0.5},
|
||||
top_padding = 0,
|
||||
bottom_padding = 0
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_warning_style = {
|
||||
-- minimal_width = 450,
|
||||
-- maximal_width = 450,
|
||||
single_line = false,
|
||||
font_color = {r=1,g=0.1,b=0.1},
|
||||
font = "default-bold",
|
||||
font_color = {r=1,g=0.3,b=0.3},
|
||||
top_padding = 0,
|
||||
bottom_padding = 0
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_spacer_style = {
|
||||
minimal_height = 10,
|
||||
top_padding = 0,
|
||||
bottom_padding = 0
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_small_button_style = {
|
||||
font = "default-small-semibold"
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_player_list_fixed_width_style = {
|
||||
minimal_width = 200,
|
||||
maximal_width = 400,
|
||||
maximal_height = 200
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_shared_item_list_fixed_width_style = {
|
||||
minimal_width = 200,
|
||||
maximal_width = 600,
|
||||
maximal_height = 600
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_player_list_admin_style = {
|
||||
font = "default-semibold",
|
||||
font_color = {r=1,g=0.5,b=0.5},
|
||||
@ -75,6 +105,8 @@ my_player_list_admin_style = {
|
||||
bottom_padding = 0,
|
||||
single_line = false,
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_player_list_style = {
|
||||
font = "default-semibold",
|
||||
minimal_width = 200,
|
||||
@ -82,6 +114,8 @@ my_player_list_style = {
|
||||
bottom_padding = 0,
|
||||
single_line = false,
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_player_list_offline_style = {
|
||||
-- font = "default-semibold",
|
||||
font_color = {r=0.5,g=0.5,b=0.5},
|
||||
@ -90,11 +124,16 @@ my_player_list_offline_style = {
|
||||
bottom_padding = 0,
|
||||
single_line = false,
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_player_list_style_spacer = {
|
||||
minimal_height = 20,
|
||||
}
|
||||
---@type Color
|
||||
my_color_red = {r=1,g=0.1,b=0.1}
|
||||
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_longer_label_style = {
|
||||
maximal_width = 600,
|
||||
single_line = false,
|
||||
@ -102,6 +141,8 @@ my_longer_label_style = {
|
||||
top_padding = 0,
|
||||
bottom_padding = 0
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_longer_warning_style = {
|
||||
maximal_width = 600,
|
||||
single_line = false,
|
||||
@ -109,6 +150,8 @@ my_longer_warning_style = {
|
||||
top_padding = 0,
|
||||
bottom_padding = 0
|
||||
}
|
||||
---@type LuaStyle
|
||||
---@diagnostic disable-next-line: missing-fields
|
||||
my_notepad_fixed_width_style = {
|
||||
minimal_width = 600,
|
||||
maximal_width = 600,
|
||||
@ -124,29 +167,42 @@ my_notepad_fixed_width_style = {
|
||||
-- GUI Functions
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Apply a style option to a GUI
|
||||
function ApplyStyle (guiIn, styleIn)
|
||||
for k,v in pairs(styleIn) do
|
||||
guiIn.style[k]=v
|
||||
---Apply a style option to a GUI
|
||||
---@param gui_element LuaGuiElement
|
||||
---@param style_in table
|
||||
---@return nil
|
||||
function ApplyStyle (gui_element, style_in)
|
||||
for k,v in pairs(style_in) do
|
||||
gui_element.style[k]=v
|
||||
end
|
||||
end
|
||||
|
||||
-- Shorter way to add a label with a style
|
||||
function AddLabel(guiIn, name, message, style)
|
||||
local g = guiIn.add{name = name, type = "label",
|
||||
caption=message}
|
||||
---Shorter way to add a label with a style
|
||||
---@param gui_element LuaGuiElement
|
||||
---@param name string?
|
||||
---@param message LocalisedString
|
||||
---@param style table|string
|
||||
---@return LuaGuiElement
|
||||
function AddLabel(gui_element, name, message, style)
|
||||
local g = gui_element.add{name = name, type = "label", caption=message}
|
||||
if (type(style) == "table") then
|
||||
ApplyStyle(g, style)
|
||||
else
|
||||
g.style = style
|
||||
end
|
||||
return g
|
||||
end
|
||||
|
||||
-- Shorter way to add a spacer
|
||||
function AddSpacer(guiIn)
|
||||
ApplyStyle(guiIn.add{type = "label", caption=" "}, my_spacer_style)
|
||||
---Shorter way to add a spacer
|
||||
---@param gui_element LuaGuiElement
|
||||
---@return nil
|
||||
function AddSpacer(gui_element)
|
||||
ApplyStyle(gui_element.add{type = "label", caption=" "}, my_spacer_style)
|
||||
end
|
||||
|
||||
function AddSpacerLine(guiIn)
|
||||
ApplyStyle(guiIn.add{type = "line", direction="horizontal"}, my_spacer_style)
|
||||
---Shorter way to add a spacer line
|
||||
---@param gui_element LuaGuiElement
|
||||
---@return nil
|
||||
function AddSpacerLine(gui_element)
|
||||
ApplyStyle(gui_element.add{type = "line", direction="horizontal"}, my_spacer_style)
|
||||
end
|
@ -1,337 +0,0 @@
|
||||
-- oarc_store_map_features.lua
|
||||
-- May 2020
|
||||
-- Adding microtransactions.
|
||||
|
||||
require("lib/shared_chests")
|
||||
require("lib/map_features")
|
||||
local mod_gui = require("mod-gui")
|
||||
|
||||
OARC_STORE_MAP_TEXT =
|
||||
{
|
||||
special_chests = "Special buildings for sharing or monitoring items and energy. This will convert the closest wooden chest (to you) within 16 tiles into a special building of your choice. Make sure to leave enough space! The combinators and accumulators can take up several tiles around them.",
|
||||
special_chunks = "Map features that can be built on the special empty chunks found on the map. You must be standing inside an empty special chunk to be able to build these. Each player can only build one of each type. [color=red]THESE FEATURES ARE PERMANENT AND CAN NOT BE REMOVED![/color]",
|
||||
special_buttons = "Special buttons like teleporting home and placing waterfill.",
|
||||
reset_buttons = "Reset your player and base. [color=red]Choose carefully! Can't be undone.[/color] If you don't own a base and your own force, some options may not be available to you."
|
||||
}
|
||||
|
||||
-- N = number already purchased
|
||||
-- Cost = initial + (additional * ( N^multiplier ))
|
||||
OARC_STORE_MAP_FEATURES =
|
||||
{
|
||||
special_chests = {
|
||||
["logistic-chest-storage"] = {
|
||||
initial_cost = 200,
|
||||
additional_cost = 20,
|
||||
multiplier_cost = 2,
|
||||
max_cost = 2000,
|
||||
-- limit = 100,
|
||||
text="Input chest for storing shared items."},
|
||||
["logistic-chest-requester"] = {
|
||||
initial_cost = 200,
|
||||
additional_cost = 50,
|
||||
multiplier_cost = 2,
|
||||
max_cost = 4000,
|
||||
-- limit = 100,
|
||||
text="Output chest for requesting shared items."},
|
||||
["constant-combinator"] = {
|
||||
initial_cost = 50,
|
||||
text="Combinator setup to monitor shared items."},
|
||||
["accumulator"] = {
|
||||
initial_cost = 200,
|
||||
additional_cost = 50,
|
||||
multiplier_cost = 2,
|
||||
max_cost = 2000,
|
||||
-- limit = 100,
|
||||
text="INPUT for shared energy system. [color=red]Only starts to share once it is charged to 50%.[/color]"},
|
||||
["electric-energy-interface"] = {
|
||||
initial_cost = 200,
|
||||
additional_cost = 100,
|
||||
multiplier_cost = 2,
|
||||
max_cost = 4000,
|
||||
-- limit = 100,
|
||||
text="OUTPUT for shared energy system. [color=red]Will NOT power other special eletric interfaces! You especially can't power special chunks with this![/color]"},
|
||||
["deconstruction-planner"] = {
|
||||
initial_cost = 0,
|
||||
text="Removes the closest special building within range. NO REFUNDS!"},
|
||||
},
|
||||
|
||||
special_chunks = {
|
||||
["electric-furnace"] = {
|
||||
initial_cost = 1000,
|
||||
additional_cost = 1000,
|
||||
multiplier_cost = 2,
|
||||
-- limit = 3,
|
||||
text="Build a special furnace chunk here. Contains 4 furnaces that run at very high speeds. [color=red]Requires energy from the shared storage. Modules have no effect![/color]"},
|
||||
["oil-refinery"] = {
|
||||
initial_cost = 1000,
|
||||
additional_cost = 1000,
|
||||
multiplier_cost = 2,
|
||||
-- limit = 3,
|
||||
text="Build a special oil refinery chunk here. Contains 2 refineries and some chemical plants that run at very high speeds. [color=red]Requires energy from the shared storage. Modules have no effect![/color]"},
|
||||
["assembling-machine-3"] = {
|
||||
initial_cost = 1000,
|
||||
additional_cost = 1000,
|
||||
multiplier_cost = 2,
|
||||
-- limit = 3,
|
||||
text="Build a special assembly machine chunk here. Contains 6 assembling machines that run at very high speeds. [color=red]Requires energy from the shared storage. Modules have no effect![/color]"},
|
||||
["centrifuge"] = {
|
||||
initial_cost = 1000,
|
||||
additional_cost = 1000,
|
||||
multiplier_cost = 2,
|
||||
-- limit = 1,
|
||||
text="Build a special centrifuge chunk here. Contains 1 centrifuge that runs at very high speeds. [color=red]Requires energy from the shared storage. Modules have no effect![/color]"},
|
||||
["rocket-silo"] = {
|
||||
initial_cost = 1000,
|
||||
additional_cost = 0,
|
||||
multiplier_cost = 2,
|
||||
max_cost = 10000,
|
||||
-- limit = 2,
|
||||
text="Convert this special chunk into a rocket launch pad. This allows you to build a rocket silo here!"},
|
||||
},
|
||||
|
||||
-- special_chunks_upgrades = {
|
||||
-- ["big-electric-pole"] = {
|
||||
-- cost = 0,
|
||||
-- text = "Upgrade your special chunk so that it pulls power from the cloud! Refills the accumulator from the cloud automatically if it falls below 50%."
|
||||
-- }
|
||||
|
||||
-- }
|
||||
|
||||
special_buttons = {
|
||||
["assembling-machine-1"] = {
|
||||
initial_cost = 10,
|
||||
text="Teleport home."},
|
||||
["offshore-pump"] = {
|
||||
initial_cost = 50,
|
||||
text="Converts the closest empty wooden chest into a water tile!"
|
||||
}
|
||||
},
|
||||
|
||||
reset_buttons = {
|
||||
["electronic-circuit"] = {
|
||||
initial_cost = 5000,
|
||||
solo_force = true,
|
||||
text="DESTROY your base and restart. This allows you to choose a new spawn and will completely destroy all your buildings and your force. All technology progress will be reset. You get to keep your current items and armor! [color=red]THERE IS NO CONFIRMATION PROMPT! THIS CAN NOT BE UNDONE![/color]"
|
||||
},
|
||||
["advanced-circuit"] = {
|
||||
initial_cost = 5000,
|
||||
solo_force = true,
|
||||
text="ABANDON your base and restart. This allows you to choose a new spawn and will move all your buildings to a neutral force. They will still be on the map and can be interacted with, but will not be owned by any player or player force. All radars will be destroyed to help trim map size. You get to keep your current items and armor! [color=red]THERE IS NO CONFIRMATION PROMPT! THIS CAN NOT BE UNDONE![/color]"
|
||||
},
|
||||
["processing-unit"] = {
|
||||
initial_cost = 5000,
|
||||
text="Restart your game. This will reset your player, your force and your base. [color=red]THERE IS NO CONFIRMATION PROMPT! THIS CAN NOT BE UNDONE![/color]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function CreateMapFeatureStoreTab(tab_container, player)
|
||||
|
||||
local player_inv = player.get_main_inventory()
|
||||
if (player_inv == nil) then return end
|
||||
|
||||
local wallet = player_inv.get_item_count("coin")
|
||||
AddLabel(tab_container,
|
||||
"map_feature_store_wallet_lbl",
|
||||
"Coins Available: " .. wallet .. " [item=coin]",
|
||||
{top_margin=5, bottom_margin=5})
|
||||
AddLabel(tab_container, "coin_info", "Players start with some coins. Earn more coins by killing enemies.", my_note_style)
|
||||
|
||||
local line = tab_container.add{type="line", direction="horizontal"}
|
||||
line.style.top_margin = 5
|
||||
line.style.bottom_margin = 5
|
||||
|
||||
for category,section in pairs(OARC_STORE_MAP_FEATURES) do
|
||||
|
||||
if (not global.ocfg.enable_chest_sharing and (category == "special_chests")) then
|
||||
goto SKIP_CATEGORY
|
||||
end
|
||||
|
||||
if (not global.ocfg.enable_magic_factories and (category == "special_chunks")) then
|
||||
goto SKIP_CATEGORY
|
||||
end
|
||||
|
||||
AddLabel(tab_container,
|
||||
nil,
|
||||
OARC_STORE_MAP_TEXT[category],
|
||||
{bottom_margin=5, maximal_width = 400, single_line = false})
|
||||
local flow = tab_container.add{name = category, type="flow", direction="horizontal"}
|
||||
for item_name,item in pairs(section) do
|
||||
|
||||
local blocked = false
|
||||
if (item.solo_force and ((player.force.name == global.ocfg.main_force) or
|
||||
(not global.ocore.playerSpawns[player.name]))) then
|
||||
blocked = true
|
||||
end
|
||||
|
||||
local count = OarcMapFeaturePlayerCountGet(player, category, item_name)
|
||||
local cost = OarcMapFeatureCostScaling(player, category, item_name)
|
||||
local color = "[color=green]"
|
||||
if ((cost > wallet) or (cost < 0) or blocked) then
|
||||
color = "[color=red]"
|
||||
end
|
||||
local btn = flow.add{name=item_name,
|
||||
type="sprite-button",
|
||||
-- number=item.count,
|
||||
sprite="item/"..item_name,
|
||||
-- tooltip=item.text.." Cost: "..color..cost.."[/color] [item=coin]",
|
||||
style=mod_gui.button_style}
|
||||
if (cost < 0) then
|
||||
btn.enabled = false
|
||||
btn.tooltip = item.text .. "\n "..color..
|
||||
"Limit: ("..count.."/"..item.limit..") [/color]"
|
||||
elseif (blocked) then
|
||||
btn.enabled = false
|
||||
btn.tooltip = item.text .. " (This is only allowed for players on their own force that own the spawn. If you have other players on your force, they must reset first before you can use this.)" .." Cost: "..color..cost.."[/color] [item=coin]"
|
||||
elseif (item.limit) then
|
||||
btn.tooltip = item.text .. "\nCost: "..color..cost.."[/color] [item=coin] "..
|
||||
"Limit: ("..count.."/"..item.limit..")"
|
||||
else
|
||||
btn.tooltip = item.text.." Cost: "..color..cost.."[/color] [item=coin]"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Spacer
|
||||
local line2 = tab_container.add{type="line", direction="horizontal"}
|
||||
line2.style.top_margin = 5
|
||||
line2.style.bottom_margin = 5
|
||||
|
||||
::SKIP_CATEGORY::
|
||||
end
|
||||
end
|
||||
|
||||
function OarcMapFeatureInitGlobalCounters()
|
||||
global.oarc_store = {}
|
||||
global.oarc_store.pmf_counts = {}
|
||||
end
|
||||
|
||||
function OarcMapFeaturePlayerCreatedEvent(player)
|
||||
global.oarc_store.pmf_counts[player.name] = {}
|
||||
end
|
||||
|
||||
function OarcMapFeaturePlayerCountGet(player, category_name, feature_name)
|
||||
if (not global.oarc_store.pmf_counts[player.name][feature_name]) then
|
||||
global.oarc_store.pmf_counts[player.name][feature_name] = 0
|
||||
return 0
|
||||
end
|
||||
|
||||
return global.oarc_store.pmf_counts[player.name][feature_name]
|
||||
end
|
||||
|
||||
function OarcMapFeaturePlayerCountChange(player, category_name, feature_name, change)
|
||||
|
||||
if (not global.oarc_store.pmf_counts[player.name][feature_name]) then
|
||||
if (change < 0) then
|
||||
log("ERROR - OarcMapFeaturePlayerCountChange - Removing when count is not set??")
|
||||
end
|
||||
global.oarc_store.pmf_counts[player.name][feature_name] = change
|
||||
return
|
||||
end
|
||||
|
||||
-- Update count
|
||||
global.oarc_store.pmf_counts[player.name][feature_name] = global.oarc_store.pmf_counts[player.name][feature_name] + change
|
||||
|
||||
-- Make sure we don't go below 0.
|
||||
if (global.oarc_store.pmf_counts[player.name][feature_name] < 0) then
|
||||
global.oarc_store.pmf_counts[player.name][feature_name] = 0
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- Return cost (0 or more) or return -1 if disabled.
|
||||
function OarcMapFeatureCostScaling(player, category_name, feature_name)
|
||||
|
||||
local map_feature = OARC_STORE_MAP_FEATURES[category_name][feature_name]
|
||||
|
||||
-- Check limit first.
|
||||
local count = OarcMapFeaturePlayerCountGet(player, category_name, feature_name)
|
||||
if (map_feature.limit and (count >= map_feature.limit)) then
|
||||
return -1
|
||||
end
|
||||
|
||||
if (map_feature.initial_cost and map_feature.additional_cost and map_feature.multiplier_cost) then
|
||||
local calc_cost = (map_feature.initial_cost + (map_feature.additional_cost*(count^map_feature.multiplier_cost)))
|
||||
if (map_feature.max_cost) then
|
||||
return math.min(map_feature.max_cost, calc_cost)
|
||||
else
|
||||
return calc_cost
|
||||
end
|
||||
else
|
||||
return map_feature.initial_cost
|
||||
end
|
||||
end
|
||||
|
||||
function OarcMapFeatureStoreButton(event)
|
||||
local button = event.element
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
local player_inv = player.get_inventory(defines.inventory.character_main)
|
||||
if (player_inv == nil) then return end
|
||||
local wallet = player_inv.get_item_count("coin")
|
||||
|
||||
local map_feature = OARC_STORE_MAP_FEATURES[button.parent.name][button.name]
|
||||
|
||||
-- Calculate cost based on how many player has purchased?
|
||||
local cost = OarcMapFeatureCostScaling(player, button.parent.name, button.name)
|
||||
|
||||
-- Check if we have enough money
|
||||
if (wallet < cost) then
|
||||
player.print("You're broke! Go kill some enemies or beg for change...")
|
||||
return
|
||||
end
|
||||
|
||||
if (player.vehicle) then
|
||||
player.print("Sir, please step out of the vehicle before you try to make any purchases...")
|
||||
return
|
||||
end
|
||||
|
||||
-- Each button has a special function
|
||||
local result = false
|
||||
if (button.name == "logistic-chest-storage") then
|
||||
result = ConvertWoodenChestToSharedChestInput(player)
|
||||
elseif (button.name == "logistic-chest-requester") then
|
||||
result = ConvertWoodenChestToSharedChestOutput(player)
|
||||
elseif (button.name == "constant-combinator") then
|
||||
result = ConvertWoodenChestToSharedChestCombinators(player)
|
||||
elseif (button.name == "accumulator") then
|
||||
result = ConvertWoodenChestToShareEnergyInput(player)
|
||||
elseif (button.name == "electric-energy-interface") then
|
||||
result = ConvertWoodenChestToShareEnergyOutput(player)
|
||||
elseif (button.name == "deconstruction-planner") then
|
||||
result = DestroyClosestSharedChestEntity(player)
|
||||
elseif (button.name == "electric-furnace") then
|
||||
result = RequestSpawnSpecialChunk(player, SpawnFurnaceChunk, button.name)
|
||||
elseif (button.name == "oil-refinery") then
|
||||
result = RequestSpawnSpecialChunk(player, SpawnOilRefineryChunk, button.name)
|
||||
elseif (button.name == "assembling-machine-3") then
|
||||
result = RequestSpawnSpecialChunk(player, SpawnAssemblyChunk, button.name)
|
||||
elseif (button.name == "centrifuge") then
|
||||
result = RequestSpawnSpecialChunk(player, SpawnCentrifugeChunk, button.name)
|
||||
elseif (button.name == "rocket-silo") then
|
||||
result = RequestSpawnSpecialChunk(player, SpawnSiloChunk, button.name)
|
||||
elseif (button.name == "assembling-machine-1") then
|
||||
SendPlayerToSpawn(player)
|
||||
result = true
|
||||
elseif (button.name == "offshore-pump") then
|
||||
result = ConvertWoodenChestToWaterFill(player)
|
||||
elseif (button.name == "electronic-circuit") then
|
||||
ResetPlayerAndDestroyForce(player)
|
||||
result = true
|
||||
elseif (button.name == "advanced-circuit") then
|
||||
ResetPlayerAndAbandonForce(player)
|
||||
result = true
|
||||
elseif (button.name == "processing-unit") then
|
||||
ResetPlayerAndMergeForceToNeutral(player)
|
||||
result = true
|
||||
end
|
||||
|
||||
-- On success, we deduct money
|
||||
if (result) then
|
||||
player_inv.remove({name = "coin", count = cost})
|
||||
end
|
||||
|
||||
-- Refresh GUI:
|
||||
FakeTabChangeEventOarcStore(player)
|
||||
end
|
@ -1,160 +0,0 @@
|
||||
-- oarc_store_player_items.lua
|
||||
-- May 2020
|
||||
-- Adding microtransactions.
|
||||
|
||||
local mod_gui = require("mod-gui")
|
||||
|
||||
OARC_STORE_PLAYER_ITEMS =
|
||||
{
|
||||
["Guns"] = {
|
||||
["pistol"] = {cost = 1, count = 1, play_time_locked=false},
|
||||
["shotgun"] = {cost = 5, count = 1, play_time_locked=false},
|
||||
["submachine-gun"] = {cost = 10, count = 1, play_time_locked=false},
|
||||
["flamethrower"] = {cost = 50, count = 1, play_time_locked=true},
|
||||
["rocket-launcher"] = {cost = 50, count = 1, play_time_locked=true},
|
||||
-- ["railgun"] = {cost = 250, count = 1, play_time_locked=true}, -- SAD
|
||||
},
|
||||
|
||||
["Turrets"] = {
|
||||
["gun-turret"] = {cost = 25, count = 1, play_time_locked=false},
|
||||
["flamethrower-turret"] = {cost = 50, count = 1, play_time_locked=false},
|
||||
["laser-turret"] = {cost = 75, count = 1, play_time_locked=false},
|
||||
["artillery-turret"] = {cost = 500, count = 1, play_time_locked=true},
|
||||
},
|
||||
|
||||
["Ammo"] = {
|
||||
["firearm-magazine"] = {cost = 10, count = 10, play_time_locked=false},
|
||||
["piercing-rounds-magazine"] = {cost = 30, count = 10, play_time_locked=false},
|
||||
["shotgun-shell"] = {cost = 10, count = 10, play_time_locked=false},
|
||||
["flamethrower-ammo"] = {cost = 50, count = 10, play_time_locked=true},
|
||||
["rocket"] = {cost = 100, count = 10, play_time_locked=true},
|
||||
-- ["railgun-dart"] = {cost = 250, count = 10, play_time_locked=true}, -- SAD
|
||||
["atomic-bomb"] = {cost = 1000, count = 1, play_time_locked=true},
|
||||
["artillery-shell"] = {cost = 50, count = 1, play_time_locked=true},
|
||||
|
||||
},
|
||||
|
||||
["Special"] = {
|
||||
["repair-pack"] = {cost = 1, count = 1, play_time_locked=false},
|
||||
["raw-fish"] = {cost = 1, count = 1, play_time_locked=false},
|
||||
["grenade"] = {cost = 20, count = 10, play_time_locked=true},
|
||||
["cliff-explosives"] = {cost = 20, count = 10, play_time_locked=true},
|
||||
["artillery-targeting-remote"] = {cost = 500, count = 1, play_time_locked=true},
|
||||
},
|
||||
|
||||
["Capsules/Mines"] = {
|
||||
["land-mine"] = {cost = 20, count = 10, play_time_locked=false},
|
||||
["defender-capsule"] = {cost = 20, count = 10, play_time_locked=false},
|
||||
["distractor-capsule"] = {cost = 40, count = 10, play_time_locked=false},
|
||||
["destroyer-capsule"] = {cost = 60, count = 10, play_time_locked=false},
|
||||
["poison-capsule"] = {cost = 50, count = 10, play_time_locked=false},
|
||||
["slowdown-capsule"] = {cost = 25, count = 10, play_time_locked=false},
|
||||
},
|
||||
|
||||
["Armor"] = {
|
||||
["light-armor"] = {cost = 10, count = 1, play_time_locked=false},
|
||||
["heavy-armor"] = {cost = 20, count = 1, play_time_locked=false},
|
||||
["modular-armor"] = {cost = 200, count = 1, play_time_locked=false},
|
||||
["power-armor"] = {cost = 1000, count = 1, play_time_locked=false},
|
||||
["power-armor-mk2"] = {cost = 5000, count = 1, play_time_locked=false},
|
||||
},
|
||||
|
||||
["Power Equipment"] = {
|
||||
["fusion-reactor-equipment"] = {cost = 1000, count = 1, play_time_locked=false},
|
||||
["battery-equipment"] = {cost = 100, count = 1, play_time_locked=false},
|
||||
["battery-mk2-equipment"] = {cost = 1000, count = 1, play_time_locked=false},
|
||||
["solar-panel-equipment"] = {cost = 10, count = 1, play_time_locked=false},
|
||||
},
|
||||
|
||||
["Bot Equipment"] = {
|
||||
["personal-roboport-equipment"] = {cost = 100, count = 1, play_time_locked=false},
|
||||
["personal-roboport-mk2-equipment"] = {cost = 500, count = 1, play_time_locked=false},
|
||||
["construction-robot"] = {cost = 100, count = 10, play_time_locked=false},
|
||||
["roboport"] = {cost = 1000, count = 1, play_time_locked=false},
|
||||
["logistic-chest-storage"] = {cost = 100, count = 1, play_time_locked=false},
|
||||
},
|
||||
|
||||
["Misc Equipment"] = {
|
||||
["belt-immunity-equipment"] = {cost = 10, count = 1, play_time_locked=false},
|
||||
["exoskeleton-equipment"] = {cost = 100, count = 1, play_time_locked=false},
|
||||
["night-vision-equipment"] = {cost = 50, count = 1, play_time_locked=false},
|
||||
|
||||
["personal-laser-defense-equipment"] = {cost = 100, count = 1, play_time_locked=false},
|
||||
-- ["discharge-defense-equipment"] = {cost = 1, count = 1, play_time_locked=false},
|
||||
["energy-shield-equipment"] = {cost = 50, count = 1, play_time_locked=false},
|
||||
["energy-shield-mk2-equipment"] = {cost = 500, count = 1, play_time_locked=false},
|
||||
},
|
||||
|
||||
["Spidertron"] = {
|
||||
["spidertron"] = {cost = 5000, count = 1, play_time_locked=false},
|
||||
["spidertron-remote"] = {cost = 500, count = 1, play_time_locked=false},
|
||||
},
|
||||
}
|
||||
|
||||
function CreatePlayerStoreTab(tab_container, player)
|
||||
|
||||
local player_inv = player.get_main_inventory()
|
||||
if (player_inv == nil) then return end
|
||||
|
||||
local wallet = player_inv.get_item_count("coin")
|
||||
AddLabel(tab_container,
|
||||
"player_store_wallet_lbl",
|
||||
"Coins Available: " .. wallet .. " [item=coin]",
|
||||
{top_margin=5, bottom_margin=5})
|
||||
AddLabel(tab_container, "coin_info", "Players start with some coins. Earn more coins by killing enemies.", my_note_style)
|
||||
AddLabel(tab_container,
|
||||
"player_store_note_lbl",
|
||||
"Locked items become available after playing for awhile...",
|
||||
my_note_style)
|
||||
|
||||
local line = tab_container.add{type="line", direction="horizontal"}
|
||||
line.style.top_margin = 5
|
||||
line.style.bottom_margin = 5
|
||||
|
||||
for category,section in pairs(OARC_STORE_PLAYER_ITEMS) do
|
||||
local flow = tab_container.add{name = category, type="flow", direction="horizontal"}
|
||||
for item_name,item in pairs(section) do
|
||||
local color = "[color=green]"
|
||||
if (item.cost > wallet) then
|
||||
color = "[color=red]"
|
||||
end
|
||||
local btn = flow.add{name=item_name,
|
||||
type="sprite-button",
|
||||
number=item.count,
|
||||
sprite="item/"..item_name,
|
||||
tooltip=item_name .. " Cost: "..color..item.cost.."[/color] [item=coin]",
|
||||
style=mod_gui.button_style}
|
||||
if (item.play_time_locked and (player.online_time < TICKS_PER_MINUTE*15)) then
|
||||
btn.enabled = false
|
||||
end
|
||||
end
|
||||
local line2 = tab_container.add{type="line", direction="horizontal"}
|
||||
line2.style.top_margin = 5
|
||||
line2.style.bottom_margin = 5
|
||||
end
|
||||
end
|
||||
|
||||
function OarcPlayerStoreButton(event)
|
||||
local button = event.element
|
||||
local player = game.players[event.player_index]
|
||||
|
||||
local player_inv = player.get_inventory(defines.inventory.character_main)
|
||||
if (player_inv == nil) then return end
|
||||
|
||||
local category = button.parent.name
|
||||
|
||||
local item = OARC_STORE_PLAYER_ITEMS[category][button.name]
|
||||
|
||||
if (player_inv.get_item_count("coin") >= item.cost) then
|
||||
player_inv.insert({name = button.name, count = item.count})
|
||||
player_inv.remove({name = "coin", count = item.cost})
|
||||
|
||||
if (button.parent and button.parent.parent and button.parent.parent.player_store_wallet_lbl) then
|
||||
local wallet = player_inv.get_item_count("coin")
|
||||
button.parent.parent.player_store_wallet_lbl.caption = "Coins Available: " .. wallet .. " [item=coin]"
|
||||
end
|
||||
|
||||
else
|
||||
player.print("You're broke! Go kill some enemies or beg for change...")
|
||||
end
|
||||
end
|
214
lib/oarc_tests.lua
Normal file
214
lib/oarc_tests.lua
Normal file
@ -0,0 +1,214 @@
|
||||
local mod_gui = require("mod-gui")
|
||||
|
||||
---Test out all the fonts available in the game.
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function TestFonts(player)
|
||||
local font_list = {
|
||||
"compi",
|
||||
"compilatron-message-font",
|
||||
"count-font",
|
||||
"default",
|
||||
"default-bold",
|
||||
"default-dialog-button",
|
||||
"default-dropdown",
|
||||
"default-game",
|
||||
"default-large",
|
||||
"default-large-bold",
|
||||
"default-large-semibold",
|
||||
"default-listbox",
|
||||
"default-semibold",
|
||||
"default-small",
|
||||
"default-small-bold",
|
||||
"default-small-semibold",
|
||||
"default-tiny-bold",
|
||||
"heading-1",
|
||||
"heading-2",
|
||||
"heading-3",
|
||||
"locale-pick",
|
||||
"scenario-message-dialog",
|
||||
"technology-slot-level-font",
|
||||
"var",
|
||||
}
|
||||
|
||||
local test_frame = player.gui.screen.add{type="frame", name="font_test_frame", direction="vertical"}
|
||||
for _,font in pairs(font_list) do
|
||||
local test_text = test_frame.add{type="label", caption=font}
|
||||
test_text.style.font = font
|
||||
end
|
||||
|
||||
test_frame.auto_center = true
|
||||
end
|
||||
|
||||
function ClearTestFonts(player)
|
||||
if player.gui.screen.font_test_frame then
|
||||
player.gui.screen.font_test_frame.destroy()
|
||||
end
|
||||
end
|
||||
|
||||
---Test out all the button styles available in the game.
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function TestButtons(player)
|
||||
local button_styles = {
|
||||
"back_button",
|
||||
"big_slot_button",
|
||||
-- "blueprint_drop_slot_button",
|
||||
"blueprint_record_selection_button",
|
||||
"blueprint_record_slot_button",
|
||||
"browse_games_gui_toggle_favorite_off_button",
|
||||
"browse_games_gui_toggle_favorite_on_button",
|
||||
"cancel_close_button",
|
||||
-- "character_gui_entity_button",
|
||||
"choose_chat_icon_button",
|
||||
"choose_chat_icon_in_textbox_button",
|
||||
"close_button",
|
||||
"compact_slot_sized_button",
|
||||
"confirm_button",
|
||||
"confirm_double_arrow_button",
|
||||
"confirm_in_load_game_button",
|
||||
"control_settings_button",
|
||||
"control_settings_section_button",
|
||||
"current_research_info_button",
|
||||
"dark_button",
|
||||
"dark_rounded_button",
|
||||
"dialog_button",
|
||||
"drop_target_button",
|
||||
"dropdown_button",
|
||||
"entity_variation_button",
|
||||
"forward_button",
|
||||
"frame_action_button",
|
||||
"frame_button",
|
||||
"green_button",
|
||||
"highlighted_tool_button",
|
||||
"inventory_limit_slot_button",
|
||||
"left_slider_button",
|
||||
"locomotive_minimap_button",
|
||||
"logistic_slot_button",
|
||||
"map_generator_close_preview_button",
|
||||
"map_generator_confirm_button",
|
||||
"map_generator_preview_button",
|
||||
"map_view_add_button",
|
||||
"map_view_options_button",
|
||||
"menu_button",
|
||||
"mini_button",
|
||||
"mod_gui_button",
|
||||
"not_working_weapon_button",
|
||||
"open_armor_button",
|
||||
"other_settings_gui_button",
|
||||
"quick_bar_page_button",
|
||||
"quick_bar_slot_button",
|
||||
"recipe_slot_button",
|
||||
"red_back_button",
|
||||
"red_button",
|
||||
"red_confirm_button",
|
||||
"red_logistic_slot_button",
|
||||
"red_slot_button",
|
||||
"research_queue_cancel_button",
|
||||
"right_slider_button",
|
||||
"rounded_button",
|
||||
"shortcut_bar_button",
|
||||
"shortcut_bar_expand_button",
|
||||
"side_menu_button",
|
||||
"slider_button",
|
||||
"slot_button",
|
||||
"slot_sized_button",
|
||||
"station_train_status_button",
|
||||
"statistics_slot_button",
|
||||
"tile_variation_button",
|
||||
"tip_notice_button",
|
||||
"tip_notice_close_button",
|
||||
"tool_bar_open_button",
|
||||
"tool_button",
|
||||
"tracking_off_button",
|
||||
"tracking_on_button",
|
||||
"train_schedule_action_button",
|
||||
"train_schedule_add_station_button",
|
||||
"train_schedule_add_wait_condition_button",
|
||||
"train_schedule_comparison_type_button",
|
||||
"train_schedule_condition_time_selection_button",
|
||||
"train_schedule_delete_button",
|
||||
"train_schedule_fulfilled_delete_button",
|
||||
"train_schedule_fulfilled_item_select_button",
|
||||
"train_schedule_item_select_button",
|
||||
"train_schedule_temporary_station_delete_button",
|
||||
"train_status_button",
|
||||
-- "train_stop_entity_button",
|
||||
-- "wide_entity_button",
|
||||
"working_weapon_button",
|
||||
"yellow_logistic_slot_button",
|
||||
}
|
||||
|
||||
local test_frame = player.gui.screen.add{type="scroll-pane", name="button_test_frame", direction="vertical"}
|
||||
-- test_frame.auto_center = true
|
||||
test_frame.vertical_scroll_policy = "auto"
|
||||
test_frame.style.maximal_height = 800
|
||||
|
||||
for _,button_style in pairs(button_styles) do
|
||||
local test_button = test_frame.add
|
||||
{
|
||||
type="button",
|
||||
caption=button_style,
|
||||
style=button_style
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
function ClearTestButtons(player)
|
||||
if player.gui.screen.button_test_frame then
|
||||
player.gui.screen.button_test_frame.destroy()
|
||||
end
|
||||
end
|
||||
|
||||
function RecreateOarcGui(player)
|
||||
if (mod_gui.get_button_flow(player).oarc_button ~= nil) then
|
||||
mod_gui.get_button_flow(player).oarc_button.destroy()
|
||||
end
|
||||
|
||||
if (mod_gui.get_frame_flow(player)[OARC_GUI] ~= nil) then
|
||||
mod_gui.get_frame_flow(player)[OARC_GUI].destroy()
|
||||
end
|
||||
|
||||
InitOarcGuiTabs(player)
|
||||
end
|
||||
|
||||
|
||||
function SetNauvisChunksGenerated()
|
||||
local nauvis = game.surfaces["nauvis"]
|
||||
|
||||
for x = -100, 100, 1 do
|
||||
for y = -100, 100, 1 do
|
||||
nauvis.set_chunk_generated_status({x=x, y=y}, defines.chunk_generated_status.entities)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function FlagEnemyForce(player, enemy_force_name)
|
||||
|
||||
local enemy_force = game.forces[enemy_force_name]
|
||||
|
||||
player.force.set_friend(enemy_force, true)
|
||||
player.force.set_cease_fire(enemy_force, true)
|
||||
|
||||
end
|
||||
|
||||
function UnflagEnemyForce(player, enemy_force_name)
|
||||
|
||||
local enemy_force = game.forces[enemy_force_name]
|
||||
|
||||
player.force.set_friend(enemy_force, false)
|
||||
player.force.set_cease_fire(enemy_force, false)
|
||||
|
||||
end
|
||||
|
||||
function CreateTestSurfaces()
|
||||
|
||||
game.create_surface("vulcanus")
|
||||
game.create_surface("fulgora")
|
||||
game.create_surface("gleba")
|
||||
game.create_surface("aquilo")
|
||||
|
||||
end
|
2375
lib/oarc_utils.lua
2375
lib/oarc_utils.lua
File diff suppressed because it is too large
Load Diff
130
lib/offline_protection.lua
Normal file
130
lib/offline_protection.lua
Normal file
@ -0,0 +1,130 @@
|
||||
-- This attempt will try to intercept normal vanilla enemy groups and modify them based on player activity.
|
||||
|
||||
-- Basic logic:
|
||||
-- on_unit_group_finished_gathering we check what command is given.
|
||||
-- find destination position
|
||||
-- check for closest "player" using find_nearest_enemy function
|
||||
-- if a player is found, check if player is part of a shared spawn
|
||||
-- Remove the enemy group if no player in the shared spawn is online.
|
||||
|
||||
-- Generic Utility Includes
|
||||
require("lib/oarc_utils")
|
||||
|
||||
---This function is called when a unit group finishes gathering.
|
||||
---@param event EventData.on_unit_group_finished_gathering
|
||||
---@return nil
|
||||
function OarcModifyEnemyGroup(event)
|
||||
|
||||
local group = event.group
|
||||
|
||||
-- Check validity
|
||||
if ((group == nil) or (group.command == nil) or not TableContains(ENEMY_FORCES_NAMES, group.force.name)) then
|
||||
log("OarcModifyEnemyGroup ignoring INVALID group/command")
|
||||
return
|
||||
end
|
||||
|
||||
-- Make sure the attack is of a TYPE that we care about.
|
||||
if ((group.command.type == defines.command.attack_area) or
|
||||
(group.command.type == defines.command.build_base)) then
|
||||
-- log("OarcModifyEnemyGroup MODIFYING command TYPE=" .. group.command.type)
|
||||
else
|
||||
-- log("OarcModifyEnemyGroup ignoring command TYPE=" .. group.command.type)
|
||||
return
|
||||
end
|
||||
|
||||
-- (group.command.type == defines.command.attack) or
|
||||
-- defines.command.attack --> target --> target.position
|
||||
-- if (group.command.type == defines.command.attack) then
|
||||
-- log("OarcModifyEnemyGroup defines.command.attack NOT IMPLEMENTED YET!")
|
||||
-- return
|
||||
-- end
|
||||
|
||||
-- defines.command.attack_area --> destination --> closest enemy (within 3 chunk radius?)
|
||||
-- defines.command.build_base --> destination --> closest enemy (expansion chunk distance?)
|
||||
|
||||
local destination = group.command.destination
|
||||
|
||||
local search_radius = CHUNK_SIZE*3
|
||||
if (group.command.type == defines.command.build_base) then
|
||||
search_radius = CHUNK_SIZE * (game.map_settings.enemy_expansion.max_expansion_distance)
|
||||
end
|
||||
|
||||
-- Look for any player force targets near the destination point.
|
||||
local target_entities = group.surface.find_entities_filtered{
|
||||
position=destination,
|
||||
radius=search_radius,
|
||||
force=ENEMY_FORCES_NAMES_INCL_NEUTRAL,
|
||||
limit=50,
|
||||
invert=true}
|
||||
|
||||
-- Search through them all to find anything with a non-nil last_user.
|
||||
local target_entity = nil
|
||||
for _,target in ipairs(target_entities) do
|
||||
if (target.last_user ~= nil) then
|
||||
target_entity = target
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- No targets found with a last_user
|
||||
if (target_entity == nil) then
|
||||
|
||||
-- This is unexpected, not sure under which conditions this would happen.
|
||||
if (group.command.type == defines.command.attack_area) then
|
||||
-- SendBroadcastMsg("OarcModifyEnemyGroup find_nearest_enemy attack_area FAILED!?!? " .. GetGPStext(group.surface.name, group.position) .. " Target: " .. GetGPStext(group.surface.name, group.command.destination))
|
||||
log("ERROR - OarcModifyEnemyGroup find_nearest_enemy attack_area FAILED!?!?")
|
||||
-- for _,member in pairs(group.members) do
|
||||
-- member.destroy()
|
||||
-- end
|
||||
|
||||
-- This is fine, as the enemy group is just expanding / building bases
|
||||
else
|
||||
-- log("OarcModifyEnemyGroup find_nearest_enemy did not find anything!")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Most common target will be a built entity with a "last_user"
|
||||
local target_player = target_entity.last_user
|
||||
|
||||
-- -- Target could also be a player character (more rare)
|
||||
-- if (target_player == nil) and (target_entity.type == "character") then
|
||||
-- target_player = target_entity.player
|
||||
-- end
|
||||
|
||||
-- I don't think this should happen ever...
|
||||
if ((target_player == nil) or (not target_player.valid)) then
|
||||
-- SendBroadcastMsg("ERROR?? target_player nil/invalid " .. GetGPStext(group.surface.name, group.position) .. " Target: " .. GetGPStext(group.surface.name, target_entity.position))
|
||||
log("ERROR - OarcModifyEnemyGroup target_player nil/invalid?")
|
||||
-- for _,member in pairs(group.members) do
|
||||
-- member.destroy()
|
||||
-- end
|
||||
return
|
||||
end
|
||||
|
||||
-- Is the target player online? Then the attack can go through.
|
||||
if (target_player.connected) then
|
||||
-- SendBroadcastMsg("Enemy group released (player): " .. GetGPStext(group.surface.name, group.position) .. " Target: " .. GetGPStext(group.surface.name, target_entity.position) .. " " .. target_player.name)
|
||||
-- log("OarcModifyEnemyGroup RELEASING enemy group since player is ONLINE " .. target_player.name)
|
||||
return
|
||||
end
|
||||
|
||||
-- Find the shared spawn that the player is part of.
|
||||
-- This could be the own player's spawn (quite likely)
|
||||
local online_players = GetPlayersFromSameSpawn(target_player.name, false)
|
||||
|
||||
-- Is someone in the group online?
|
||||
if (#online_players > 0) then
|
||||
-- SendBroadcastMsg("Enemy group released (shared): " .. GetGPStext(group.surface.name, group.position) .. " Target: " .. GetGPStext(group.surface.name, target_entity.position) .. " " .. target_player.name)
|
||||
-- log("OarcModifyEnemyGroup RELEASING enemy group since someone in the group is ONLINE " .. target_player.name)
|
||||
return
|
||||
end
|
||||
|
||||
-- Otherwise, we delete the group.
|
||||
for _,member in pairs(group.members) do
|
||||
member.destroy()
|
||||
end
|
||||
-- SendBroadcastMsg("Enemy group deleted: " .. GetGPStext(group.surface.name, group.position) .. " Target: " .. GetGPStext(group.surface.name, target_entity.position) .. " " .. target_player.name)
|
||||
log("OarcModifyEnemyGroup REMOVED enemy group since nobody was online? " .. target_player.name)
|
||||
|
||||
end
|
@ -1,36 +0,0 @@
|
||||
-- oarc_player_list.lua
|
||||
-- Mar 2019
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Player List GUI - My own version
|
||||
--------------------------------------------------------------------------------
|
||||
function CreatePlayerListGuiTab(tab_container, player)
|
||||
local scrollFrame = tab_container.add{type="scroll-pane",
|
||||
name="playerList-panel",
|
||||
direction = "vertical"}
|
||||
ApplyStyle(scrollFrame, my_player_list_fixed_width_style)
|
||||
scrollFrame.horizontal_scroll_policy = "never"
|
||||
|
||||
AddLabel(scrollFrame, "online_title_msg", "Online Players:", my_label_header_style)
|
||||
for _,player in pairs(game.connected_players) do
|
||||
local caption_str = player.name.." ["..player.force.name.."]".." ("..formattime_hours_mins(player.online_time)..")"
|
||||
if (player.admin) then
|
||||
AddLabel(scrollFrame, player.name.."_plist", caption_str, my_player_list_admin_style)
|
||||
else
|
||||
AddLabel(scrollFrame, player.name.."_plist", caption_str, my_player_list_style)
|
||||
end
|
||||
end
|
||||
|
||||
-- List offline players
|
||||
if (global.ocfg.list_offline_players) then
|
||||
AddSpacerLine(scrollFrame)
|
||||
AddLabel(scrollFrame, "offline_title_msg", "Offline Players:", my_label_header_grey_style)
|
||||
for _,player in pairs(game.players) do
|
||||
if (not player.connected) then
|
||||
local caption_str = player.name.." ["..player.force.name.."]".." ("..formattime_hours_mins(player.online_time)..")"
|
||||
local text = scrollFrame.add{type="label", caption=caption_str, name=player.name.."_plist"}
|
||||
ApplyStyle(text, my_player_list_offline_style)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,7 +1,3 @@
|
||||
-- regrowth_map.lua
|
||||
-- Sep 2019
|
||||
-- REVERTED BACK TO SOFT MOD
|
||||
|
||||
-- Code tracks all chunks generated and allows for deleting of inactive chunks.
|
||||
--
|
||||
-- Basic rules of regrowth:
|
||||
@ -13,241 +9,506 @@
|
||||
-- the on_sector_scanned event.
|
||||
-- 5. Chunks timeout after 1 hour-ish, configurable
|
||||
|
||||
require("lib/oarc_utils")
|
||||
require("config")
|
||||
--- These chunks are marked for removal. They will be deleted by the regrowth system.
|
||||
--- If it gets refreshed before it is removed, then it will be marked safe again
|
||||
REGROWTH_FLAG_REMOVAL = -1
|
||||
|
||||
REGROWTH_TIMEOUT_TICKS = TICKS_PER_HOUR -- TICKS_PER_HOUR TICKS_PER_MINUTE
|
||||
--- If a chunk is marked "active", then it will only be checked by the "world eater" system if that is enabled.
|
||||
--- World eater does more extensive checks to see if a chunk might be safe to delete. For example, if a player builds
|
||||
--- stuff in a chunk it will be marked as "active" and won't be checked by the regrowth system.
|
||||
REGROWTH_FLAG_ACTIVE = -2
|
||||
|
||||
-- Init globals and set player join area to be off limits.
|
||||
--- These chunks will NEVER be deleted by the regrowth + world eater systems. However, they can be overwritten in some
|
||||
--- cases. Like when a player leaves the game early and their spawn is deleted.
|
||||
REGROWTH_FLAG_PERMANENT = -3
|
||||
|
||||
|
||||
|
||||
--- Radius in chunks around a player to mark as safe.
|
||||
REGROWTH_ACTIVE_AREA_AROUND_PLAYER = 2
|
||||
|
||||
---The removal list contains chunks that are marked for removal. Each entry is a table with the following fields:
|
||||
---@alias RemovalListEntry { pos : ChunkPosition, force: boolean, surface: string }
|
||||
|
||||
|
||||
---Init globals for regrowth
|
||||
---@return nil
|
||||
function RegrowthInit()
|
||||
global.rg = {}
|
||||
global.rg.player_refresh_index = nil
|
||||
global.rg.force_removal_flag = -2000
|
||||
global.rg.map = {}
|
||||
|
||||
global.rg.force_removal_flag = -2000 -- Set to a negative number to disable it by default
|
||||
|
||||
global.rg.current_surface = nil -- The current surface we are iterating through
|
||||
global.rg.current_surface_index = 1
|
||||
global.rg.active_surfaces = {} -- List of all surfaces with regrowth enabled
|
||||
global.rg.chunk_iter = nil -- We only iterate through onface at a time
|
||||
|
||||
global.rg.we_chunk_iter = nil
|
||||
global.rg.we_current_surface = nil
|
||||
global.rg.we_current_surface_index = 1
|
||||
|
||||
|
||||
---@type LuaEntity[]
|
||||
global.rg.spidertrons = {} -- List of all spidertrons in the game
|
||||
global.rg_spidertron_index = 1
|
||||
|
||||
---@type RemovalListEntry[]
|
||||
global.rg.removal_list = {}
|
||||
global.rg.chunk_iter = nil
|
||||
global.rg.world_eater_iter = nil
|
||||
global.rg.timeout_ticks = REGROWTH_TIMEOUT_TICKS
|
||||
|
||||
for surface_name,_ in pairs(game.surfaces) do
|
||||
InitSurface(surface_name --[[@as string]])
|
||||
end
|
||||
end
|
||||
|
||||
---Called when a new surface is created. This is used to add new surfaces to the regrowth map.
|
||||
---@param event EventData.on_surface_created
|
||||
---@return nil
|
||||
function RegrowthSurfaceCreated(event)
|
||||
InitSurface(game.surfaces[event.surface_index].name)
|
||||
end
|
||||
|
||||
---Called when a surface is deleted. This is used to remove surfaces from the regrowth map.
|
||||
---@param event EventData.on_pre_surface_deleted
|
||||
---@return nil
|
||||
function RegrowthSurfaceDeleted(event)
|
||||
log("WARNING - RegrowthSurfaceDeleted: " .. game.surfaces[event.surface_index].name)
|
||||
local surface_name = game.surfaces[event.surface_index].name
|
||||
RegrowthDisableSurface(surface_name )
|
||||
end
|
||||
|
||||
---Initialize the new surface for regrowth
|
||||
---@param surface_name string - The surface name to act on
|
||||
---@return nil
|
||||
function InitSurface(surface_name)
|
||||
|
||||
if (not IsSurfaceBlacklisted(surface_name) and not TableContains(global.rg.active_surfaces, surface_name)) then
|
||||
log("Adding surface to regrowth: " .. surface_name)
|
||||
|
||||
-- Add a new surface to the regrowth map (Don't overwrite if it already exists)
|
||||
if (global.rg[surface_name] == nil) then
|
||||
global.rg[surface_name] = {}
|
||||
end
|
||||
|
||||
-- This is a 2D array of chunk positions and their last tick updated / status (Don't overwrite if it already exists)
|
||||
if (global.rg[surface_name].map == nil) then
|
||||
global.rg[surface_name].map = {}
|
||||
end
|
||||
|
||||
-- Set the current surface to the first one found if none are set.
|
||||
if (global.rg.current_surface == nil) then
|
||||
global.rg.current_surface = surface_name
|
||||
global.rg.we_current_surface = surface_name
|
||||
end
|
||||
|
||||
global.rg[surface_name].active = true
|
||||
table.insert(global.rg.active_surfaces, surface_name)
|
||||
end
|
||||
end
|
||||
|
||||
function RegrowthDisableSurface(surface_name)
|
||||
|
||||
-- We don't want to delete the surface history in case it's re-enabled later!
|
||||
-- global.rg[surface_name] = nil
|
||||
|
||||
global.rg[surface_name].active = false
|
||||
TableRemoveOneUsingPairs(global.rg.active_surfaces, surface_name)
|
||||
|
||||
-- Make sure indices are reset if needed
|
||||
if (global.rg.current_surface == surface_name) then
|
||||
global.rg.current_surface = nil
|
||||
global.rg.current_surface_index = 1
|
||||
end
|
||||
if (global.rg.we_current_surface == surface_name) then
|
||||
global.rg.we_current_surface = nil
|
||||
global.rg.we_current_surface_index = 1
|
||||
end
|
||||
if #global.rg.active_surfaces > 0 then
|
||||
global.rg.current_surface = global.rg.active_surfaces[1]
|
||||
global.rg.we_current_surface = global.rg.active_surfaces[1]
|
||||
end
|
||||
end
|
||||
|
||||
---Simple check to see if a surface is enabled for regrowth
|
||||
---@param surface_name string - The surface name to act on
|
||||
---@return boolean
|
||||
function IsRegrowthEnabledOnSurface(surface_name)
|
||||
if (global.rg[surface_name] == nil) then return false end
|
||||
return global.rg[surface_name].active
|
||||
end
|
||||
|
||||
function RegrowthEnableSurface(surface_name)
|
||||
InitSurface(surface_name)
|
||||
end
|
||||
|
||||
---Trigger an immediate cleanup of any chunks that are marked for removal.
|
||||
---@return nil
|
||||
function TriggerCleanup()
|
||||
global.rg.force_removal_flag = game.tick
|
||||
end
|
||||
|
||||
function RegrowthForceRemoveChunksCmd(cmd_table)
|
||||
if (game.players[cmd_table.player_index].admin) then
|
||||
TriggerCleanup()
|
||||
end
|
||||
end
|
||||
-- Turn this into a admin GUI button.
|
||||
-- function RegrowthForceRemoveChunksCmd(cmd_table)
|
||||
-- if (game.players[cmd_table.player_index].admin) then
|
||||
-- TriggerCleanup()
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- Get the next player index available
|
||||
function GetNextPlayerIndex(player_index)
|
||||
if (not global.rg.player_refresh_index or not game.players[global.rg.player_refresh_index]) then
|
||||
---Get the next player index available. This is used to loop through ONLINE players to refresh the areas around them.
|
||||
---@return integer
|
||||
function GetNextConnectedPlayerIndex()
|
||||
if (global.rg.player_refresh_index == nil) or (game.connected_players[global.rg.player_refresh_index] == nil) then
|
||||
global.rg.player_refresh_index = 1
|
||||
else
|
||||
global.rg.player_refresh_index = global.rg.player_refresh_index + 1
|
||||
end
|
||||
|
||||
if (global.rg.player_refresh_index > #game.players) then
|
||||
if (global.rg.player_refresh_index > #game.connected_players) then
|
||||
global.rg.player_refresh_index = 1
|
||||
end
|
||||
|
||||
return global.rg.player_refresh_index
|
||||
end
|
||||
|
||||
-- Adds new chunks to the global table to track them.
|
||||
-- This should always be called first in the chunk generate sequence
|
||||
-- (Compared to other RSO & Oarc related functions...)
|
||||
function RegrowthChunkGenerate(event)
|
||||
local c_pos = GetChunkPosFromTilePos(event.area.left_top)
|
||||
---@alias ActiveSurfaceInfo { surface : string, index : integer }
|
||||
|
||||
-- Surface must be "added" first.
|
||||
if (global.rg == nil) then return end
|
||||
---Sets the current surface to the next active surface. This is used to loop through surfaces.
|
||||
---@param current_index integer - The current index in the active surfaces list
|
||||
---@return ActiveSurfaceInfo - The new current surface name and index
|
||||
function GetNextActiveSurface(current_index)
|
||||
|
||||
local count = #(global.rg.active_surfaces)
|
||||
local next_index = current_index + 1
|
||||
|
||||
if (next_index > count) then
|
||||
next_index = 1
|
||||
end
|
||||
|
||||
local next_surface = global.rg.active_surfaces[next_index]
|
||||
|
||||
return { surface = next_surface, index = next_index }
|
||||
end
|
||||
|
||||
---Adds new chunks to the global table to track them.
|
||||
---This should always be called first in the chunk generate sequence
|
||||
---(Compared to other RSO & Oarc related functions...)
|
||||
---@param event EventData.on_chunk_generated
|
||||
---@return nil
|
||||
function RegrowthChunkGenerate(event)
|
||||
local c_pos = event.position
|
||||
local surface_name = event.surface.name
|
||||
|
||||
-- Surface not init or not active, ignore it.
|
||||
if not IsRegrowthEnabledOnSurface(surface_name) then return end
|
||||
|
||||
-- If this is the first chunk in that row:
|
||||
if (global.rg.map[c_pos.x] == nil) then
|
||||
global.rg.map[c_pos.x] = {}
|
||||
if (global.rg[surface_name].map[c_pos.x] == nil) then
|
||||
global.rg[surface_name].map[c_pos.x] = {}
|
||||
end
|
||||
|
||||
-- Only update it if it isn't already set!
|
||||
if (global.rg.map[c_pos.x][c_pos.y] == nil) then
|
||||
global.rg.map[c_pos.x][c_pos.y] = game.tick
|
||||
if (global.rg[surface_name].map[c_pos.x][c_pos.y] == nil) then
|
||||
global.rg[surface_name].map[c_pos.x][c_pos.y] = game.tick
|
||||
-- log("RegrowthChunkGenerate: " .. c_pos.x .. "," .. c_pos.y .. " on surface: " .. surface_name)
|
||||
end
|
||||
end
|
||||
|
||||
-- Mark an area for "immediate" forced removal
|
||||
function RegrowthMarkAreaForRemoval(pos, chunk_radius)
|
||||
---Mark an area for "immediate" forced removal, this will override any pemranent flags.
|
||||
---@param surface_name string - The surface name to act on
|
||||
---@param pos TilePosition - The tile position to mark for removal
|
||||
---@param chunk_radius integer - The radius in chunks around the position to mark for removal
|
||||
---@return nil
|
||||
function RegrowthMarkAreaForRemoval(surface_name, pos, chunk_radius)
|
||||
local c_pos = GetChunkPosFromTilePos(pos)
|
||||
for i=-chunk_radius,chunk_radius do
|
||||
local x = c_pos.x+i
|
||||
for k=-chunk_radius,chunk_radius do
|
||||
local y = c_pos.y+k
|
||||
for i = -chunk_radius, chunk_radius do
|
||||
local x = c_pos.x + i
|
||||
for k = -chunk_radius, chunk_radius do
|
||||
local y = c_pos.y + k
|
||||
|
||||
if (global.rg.map[x] ~= nil) then
|
||||
global.rg.map[x][y] = nil
|
||||
if (global.rg[surface_name].map[x] == nil) then
|
||||
global.rg[surface_name].map[x] = {}
|
||||
end
|
||||
table.insert(global.rg.removal_list, {pos={x=x,y=y},force=true})
|
||||
end
|
||||
if (table_size(global.rg.map[x]) == 0) then
|
||||
global.rg.map[x] = nil
|
||||
global.rg[surface_name].map[x][y] = REGROWTH_FLAG_REMOVAL
|
||||
|
||||
---@type RemovalListEntry
|
||||
local removal_entry = { pos = { x = x, y = y }, force = true, surface = surface_name }
|
||||
table.insert(global.rg.removal_list, removal_entry)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Downgrades permanent flag to semi-permanent.
|
||||
function RegrowthMarkAreaNotPermanentOVERWRITE(pos, chunk_radius)
|
||||
local c_pos = GetChunkPosFromTilePos(pos)
|
||||
for i=-chunk_radius,chunk_radius do
|
||||
local x = c_pos.x+i
|
||||
for k=-chunk_radius,chunk_radius do
|
||||
local y = c_pos.y+k
|
||||
-- ---Downgrades permanent flag to semi-permanent.
|
||||
-- ---@param surface_name string - The surface name to act on
|
||||
-- ---@param pos TilePosition - The tile position to mark
|
||||
-- ---@param chunk_radius integer - The radius in chunks around the position to mark
|
||||
-- ---@return nil
|
||||
-- function RegrowthMarkAreaNotPermanentOVERWRITE(surface_name, pos, chunk_radius)
|
||||
-- local c_pos = GetChunkPosFromTilePos(pos)
|
||||
-- for i = -chunk_radius, chunk_radius do
|
||||
-- local x = c_pos.x + i
|
||||
-- for k = -chunk_radius, chunk_radius do
|
||||
-- local y = c_pos.y + k
|
||||
|
||||
if (global.rg.map[x] and global.rg.map[x][y] and (global.rg.map[x][y] == -2)) then
|
||||
global.rg.map[x][y] = -1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- if (global.rg[surface_name].map[x] and
|
||||
-- global.rg[surface_name].map[x][y] and
|
||||
-- (global.rg[surface_name].map[x][y] == REGROWTH_FLAG_PERMANENT)) then
|
||||
-- global.rg[surface_name].map[x][y] = REGROWTH_FLAG_ACTIVE
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- Marks a chunk containing a position to be relatively permanent.
|
||||
function MarkChunkSafe(c_pos, permanent)
|
||||
if (global.rg.map[c_pos.x] == nil) then
|
||||
global.rg.map[c_pos.x] = {}
|
||||
---Marks a chunk containing a position to be relatively permanent.
|
||||
---@param surface_name string - The surface name to act on
|
||||
---@param c_pos ChunkPosition - The chunk position to mark
|
||||
---@param permanent boolean - If true, the chunk will be marked as permanent
|
||||
---@return nil
|
||||
function MarkChunkSafe(surface_name, c_pos, permanent)
|
||||
if (global.rg[surface_name].map[c_pos.x] == nil) then
|
||||
global.rg[surface_name].map[c_pos.x] = {}
|
||||
end
|
||||
|
||||
if (permanent) then
|
||||
global.rg.map[c_pos.x][c_pos.y] = -2
|
||||
global.rg[surface_name].map[c_pos.x][c_pos.y] = REGROWTH_FLAG_PERMANENT
|
||||
|
||||
-- Make sure we don't overwrite...
|
||||
elseif (global.rg.map[c_pos.x][c_pos.y] and (global.rg.map[c_pos.x][c_pos.y] ~= -2)) then
|
||||
global.rg.map[c_pos.x][c_pos.y] = -1
|
||||
-- Make sure we don't overwrite unless it's a permanent flag
|
||||
elseif (global.rg[surface_name].map[c_pos.x][c_pos.y] and
|
||||
(global.rg[surface_name].map[c_pos.x][c_pos.y] ~= REGROWTH_FLAG_PERMANENT)) then
|
||||
global.rg[surface_name].map[c_pos.x][c_pos.y] = REGROWTH_FLAG_ACTIVE
|
||||
end
|
||||
end
|
||||
|
||||
-- Marks a safe area around a CHUNK position to be relatively permanent.
|
||||
function RegrowthMarkAreaSafeGivenChunkPos(c_pos, chunk_radius, permanent)
|
||||
if (global.rg == nil) then return end
|
||||
---Marks a safe area around a CHUNK position to be relatively permanent.
|
||||
---@param surface_name string - The surface name to act on
|
||||
---@param c_pos ChunkPosition - The chunk position to mark
|
||||
---@param chunk_radius integer - The radius in chunks around the position to mark
|
||||
---@param permanent boolean - If true, the chunk will be marked as permanent
|
||||
---@return nil
|
||||
function RegrowthMarkAreaSafeGivenChunkPos(surface_name, c_pos, chunk_radius, permanent)
|
||||
if (global.rg[surface_name] == nil) then return end
|
||||
|
||||
for i=-chunk_radius,chunk_radius do
|
||||
for j=-chunk_radius,chunk_radius do
|
||||
MarkChunkSafe({x=c_pos.x+i,y=c_pos.y+j}, permanent)
|
||||
for i = -chunk_radius, chunk_radius do
|
||||
for j = -chunk_radius, chunk_radius do
|
||||
MarkChunkSafe(surface_name, { x = c_pos.x + i, y = c_pos.y + j }, permanent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Marks a safe area around a TILE position to be relatively permanent.
|
||||
function RegrowthMarkAreaSafeGivenTilePos(pos, chunk_radius, permanent)
|
||||
if (global.rg == nil) then return end
|
||||
---Marks a safe area around a TILE position to be relatively permanent.
|
||||
---@param surface_name string - The surface name to act on
|
||||
---@param pos TilePosition - The tile position to mark
|
||||
---@param chunk_radius integer - The radius in chunks around the position to mark
|
||||
---@param permanent boolean - If true, the chunk will be marked as permanent
|
||||
---@return nil
|
||||
function RegrowthMarkAreaSafeGivenTilePos(surface_name, pos, chunk_radius, permanent)
|
||||
if not IsRegrowthEnabledOnSurface(surface_name) then return end
|
||||
|
||||
local c_pos = GetChunkPosFromTilePos(pos)
|
||||
RegrowthMarkAreaSafeGivenChunkPos(c_pos, chunk_radius, permanent)
|
||||
RegrowthMarkAreaSafeGivenChunkPos(surface_name, c_pos, chunk_radius, permanent)
|
||||
end
|
||||
|
||||
-- Refreshes timers on a chunk containing position
|
||||
function RefreshChunkTimer(pos, bonus_time)
|
||||
---Refreshes timers on a chunk containing position
|
||||
---@param surface_name string - The surface name to act on
|
||||
---@param pos TilePosition - The tile position to mark
|
||||
---@param bonus_time integer - The bonus time to add to the current game tick
|
||||
---@return nil
|
||||
function RefreshChunkTimer(surface_name, pos, bonus_time)
|
||||
local c_pos = GetChunkPosFromTilePos(pos)
|
||||
|
||||
if (global.rg.map[c_pos.x] == nil) then
|
||||
global.rg.map[c_pos.x] = {}
|
||||
if (global.rg[surface_name].map[c_pos.x] == nil) then
|
||||
global.rg[surface_name].map[c_pos.x] = {}
|
||||
end
|
||||
if (global.rg.map[c_pos.x][c_pos.y] >= 0) then
|
||||
global.rg.map[c_pos.x][c_pos.y] = game.tick + bonus_time
|
||||
if (global.rg[surface_name].map[c_pos.x][c_pos.y] >= REGROWTH_FLAG_REMOVAL) then
|
||||
global.rg[surface_name].map[c_pos.x][c_pos.y] = game.tick + bonus_time
|
||||
end
|
||||
end
|
||||
|
||||
-- Refreshes timers on all chunks around a certain area
|
||||
function RefreshArea(pos, chunk_radius, bonus_time)
|
||||
|
||||
---Refreshes timers on all chunks around a certain area
|
||||
---@param surface_name string - The surface name to act on
|
||||
---@param pos TilePosition - The tile position to mark
|
||||
---@param chunk_radius integer - The radius in chunks around the position to mark
|
||||
---@param bonus_time integer - The bonus time to add to the current game tick
|
||||
---@return nil
|
||||
function RefreshArea(surface_name, pos, chunk_radius, bonus_time)
|
||||
local c_pos = GetChunkPosFromTilePos(pos)
|
||||
|
||||
for i=-chunk_radius,chunk_radius do
|
||||
local x = c_pos.x+i
|
||||
for k=-chunk_radius,chunk_radius do
|
||||
local y = c_pos.y+k
|
||||
RefreshAreaChunkPosition(surface_name, c_pos, chunk_radius, bonus_time)
|
||||
end
|
||||
|
||||
if (global.rg.map[x] == nil) then
|
||||
global.rg.map[x] = {}
|
||||
---Refreshes timers on all chunks around a certain area
|
||||
---@param surface_name string - The surface name to act on
|
||||
---@param c_pos ChunkPosition - The chunk position to mark
|
||||
---@param chunk_radius integer - The radius in chunks around the position to mark
|
||||
---@param bonus_time integer - The bonus time to add to the current game tick
|
||||
---@return nil
|
||||
function RefreshAreaChunkPosition(surface_name, c_pos, chunk_radius, bonus_time)
|
||||
for i = -chunk_radius, chunk_radius do
|
||||
local x = c_pos.x + i
|
||||
for k = -chunk_radius, chunk_radius do
|
||||
local y = c_pos.y + k
|
||||
|
||||
if (global.rg[surface_name].map[x] == nil) then
|
||||
global.rg[surface_name].map[x] = {}
|
||||
end
|
||||
if ((global.rg.map[x][y] == nil) or (global.rg.map[x][y] >= 0)) then
|
||||
global.rg.map[x][y] = game.tick + bonus_time
|
||||
if ((global.rg[surface_name].map[x][y] == nil) or (global.rg[surface_name].map[x][y] >= REGROWTH_FLAG_REMOVAL)) then
|
||||
global.rg[surface_name].map[x][y] = game.tick + bonus_time
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Refreshes timers on all chunks near an ACTIVE radar
|
||||
---Refreshes timers on all chunks near an ACTIVE radar
|
||||
---@param event EventData.on_sector_scanned
|
||||
---@return nil
|
||||
function RegrowthSectorScan(event)
|
||||
if (event.radar.surface.name ~= GAME_SURFACE_NAME) then return end
|
||||
local surface_name = event.radar.surface.name
|
||||
|
||||
RefreshArea(event.radar.position, 14, 0)
|
||||
-- Surface not in regrowth map, ignore it.
|
||||
if (global.rg[surface_name] == nil) then return end
|
||||
|
||||
---@type integer
|
||||
local radar_range = event.radar.prototype.max_distance_of_nearby_sector_revealed --TODO: Space age quality might affect this?
|
||||
RefreshAreaChunkPosition(surface_name, event.chunk_position, radar_range, 0)
|
||||
end
|
||||
|
||||
-- Refresh all chunks near a single player. Cyles through all connected players.
|
||||
---Refresh all chunks near a single player. Cyles through all connected players.
|
||||
---@return nil
|
||||
function RefreshPlayerArea()
|
||||
player_index = GetNextPlayerIndex()
|
||||
player_index = GetNextConnectedPlayerIndex()
|
||||
if (player_index and game.connected_players[player_index]) then
|
||||
local player = game.connected_players[player_index]
|
||||
|
||||
if (not player.character) then return end
|
||||
if (player.character.surface.name ~= GAME_SURFACE_NAME) then return end
|
||||
local surface_name = player.surface.name
|
||||
|
||||
RefreshArea(player.position, 4, 0)
|
||||
if (not player.character) then return end
|
||||
if (global.rg[surface_name] == nil) or (not global.rg[surface_name].active) then return end
|
||||
|
||||
RefreshArea(surface_name, player.position, REGROWTH_ACTIVE_AREA_AROUND_PLAYER, 0)
|
||||
end
|
||||
end
|
||||
|
||||
-- Gets the next chunk the array map and checks to see if it has timed out.
|
||||
-- Adds it to the removal list if it has.
|
||||
function RegrowthSingleStepArray()
|
||||
---Updates the chunk_iter and returns the next chunk from it. May not be valid if it no chunks exist.
|
||||
---@return ChunkPositionAndArea?
|
||||
function GetNextChunkAndUpdateIter()
|
||||
|
||||
-- Make sure we have a valid iterator!
|
||||
if (not global.rg.chunk_iter or not global.rg.chunk_iter.valid) then
|
||||
global.rg.chunk_iter = game.surfaces[GAME_SURFACE_NAME].get_chunks()
|
||||
global.rg.chunk_iter = game.surfaces[global.rg.current_surface].get_chunks()
|
||||
end
|
||||
|
||||
local next_chunk = global.rg.chunk_iter()
|
||||
|
||||
-- Check if we reached the end
|
||||
if (not next_chunk) then
|
||||
global.rg.chunk_iter = game.surfaces[GAME_SURFACE_NAME].get_chunks()
|
||||
if (next_chunk == nil) then
|
||||
|
||||
-- Switch to the next active surface
|
||||
local next_surface_info = GetNextActiveSurface(global.rg.current_surface_index)
|
||||
global.rg.current_surface = next_surface_info.surface
|
||||
global.rg.current_surface_index = next_surface_info.index
|
||||
global.rg.chunk_iter = game.surfaces[global.rg.current_surface].get_chunks()
|
||||
next_chunk = global.rg.chunk_iter()
|
||||
end
|
||||
|
||||
-- Do we have it in our map?
|
||||
if (not global.rg.map[next_chunk.x] or not global.rg.map[next_chunk.x][next_chunk.y]) then
|
||||
return -- Chunk isn't in our map so we don't care?
|
||||
end
|
||||
return next_chunk
|
||||
end
|
||||
|
||||
---Updates the chunk_iter (for World Eater) and returns the next chunk from it. May not be valid if it no chunks exist.
|
||||
---@return ChunkPositionAndArea?
|
||||
function GetNextChunkAndUpdateWorldEaterIter()
|
||||
|
||||
-- Make sure we have a valid iterator!
|
||||
if (not global.rg.we_chunk_iter or not global.rg.we_chunk_iter.valid) then
|
||||
global.rg.we_chunk_iter = game.surfaces[global.rg.we_current_surface].get_chunks()
|
||||
end
|
||||
|
||||
local next_chunk = global.rg.we_chunk_iter()
|
||||
|
||||
-- Check if we reached the end
|
||||
if (next_chunk == nil) then
|
||||
|
||||
-- Switch to the next active surface
|
||||
local next_surface_info = GetNextActiveSurface(global.rg.we_current_surface_index)
|
||||
global.rg.we_current_surface = next_surface_info.surface
|
||||
global.rg.we_current_surface_index = next_surface_info.index
|
||||
global.rg.we_chunk_iter = game.surfaces[global.rg.we_current_surface].get_chunks()
|
||||
next_chunk = global.rg.we_chunk_iter()
|
||||
end
|
||||
|
||||
return next_chunk
|
||||
end
|
||||
|
||||
---Gets the next chunk the array map and checks to see if it has timed out.
|
||||
---Adds it to the removal list if it has.
|
||||
---@return nil
|
||||
function RegrowthSingleStepArray()
|
||||
|
||||
local next_chunk = GetNextChunkAndUpdateIter()
|
||||
if (next_chunk == nil) then return end
|
||||
local current_surface = global.rg.current_surface
|
||||
|
||||
-- It's possible that if regrowth is disabled/enabled during runtime we might miss on_chunk_generated.
|
||||
-- This will catch that case and add the chunk to the map.
|
||||
if (global.rg[current_surface].map[next_chunk.x] == nil) then
|
||||
global.rg[current_surface].map[next_chunk.x] = {}
|
||||
end
|
||||
if (global.rg[current_surface].map[next_chunk.x][next_chunk.y] == nil and game.surfaces[current_surface].is_chunk_generated(next_chunk)) then
|
||||
log("RegrowthSingleStepArray: Chunk not in map: " .. next_chunk.x .. "," .. next_chunk.y .. " on surface: " .. current_surface)
|
||||
local has_player_entities = CheckIfChunkHasAnyPlayerEntities(current_surface, next_chunk)
|
||||
if has_player_entities then
|
||||
global.rg[current_surface].map[next_chunk.x][next_chunk.y] = REGROWTH_FLAG_ACTIVE
|
||||
else
|
||||
global.rg[current_surface].map[next_chunk.x][next_chunk.y] = game.tick
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- If the chunk has timed out, add it to the removal list
|
||||
local c_timer = global.rg.map[next_chunk.x][next_chunk.y]
|
||||
if ((c_timer ~= nil) and (c_timer >= 0) and ((c_timer + global.rg.timeout_ticks) < game.tick)) then
|
||||
|
||||
local c_timer = global.rg[current_surface].map[next_chunk.x][next_chunk.y]
|
||||
local interval_ticks = global.ocfg.regrowth.cleanup_interval * TICKS_PER_MINUTE
|
||||
if ((c_timer ~= nil) and (c_timer >= 0) and ((c_timer + interval_ticks) < game.tick)) then
|
||||
-- Check chunk actually exists
|
||||
if (game.surfaces[GAME_SURFACE_NAME].is_chunk_generated({x=next_chunk.x, y=next_chunk.y})) then
|
||||
table.insert(global.rg.removal_list, {pos={x=next_chunk.x, y=next_chunk.y}, force=false})
|
||||
global.rg.map[next_chunk.x][next_chunk.y] = nil
|
||||
if (game.surfaces[current_surface].is_chunk_generated({ x = next_chunk.x, y = next_chunk.y })) then
|
||||
|
||||
---@type RemovalListEntry
|
||||
local removal_entry = {pos = {x = next_chunk.x, y = next_chunk.y }, force = false, surface = current_surface}
|
||||
table.insert(global.rg.removal_list, removal_entry)
|
||||
global.rg[current_surface].map[next_chunk.x][next_chunk.y] = REGROWTH_FLAG_REMOVAL
|
||||
else
|
||||
log("WARN - RegrowthSingleStepArray: Chunk not generated?: " .. next_chunk.x .. "," .. next_chunk.y .. " on surface: " .. current_surface)
|
||||
global.rg[current_surface].map[next_chunk.x][next_chunk.y] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove all chunks at same time to reduce impact to FPS/UPS
|
||||
---Remove all chunks at same time to reduce impact to FPS/UPS
|
||||
---@return nil
|
||||
function OarcRegrowthRemoveAllChunks()
|
||||
for key,c_remove in pairs(global.rg.removal_list) do
|
||||
for key, c_remove in pairs(global.rg.removal_list) do
|
||||
local c_pos = c_remove.pos
|
||||
local surface_name = c_remove.surface
|
||||
|
||||
-- Confirm chunk is still expired
|
||||
if (not global.rg.map[c_pos.x] or not global.rg.map[c_pos.x][c_pos.y]) then
|
||||
-- Confirm chunk is still marked for removal or is a force removal, if it's nil, something else happened?
|
||||
if (global.rg[surface_name].map[c_pos.x] ~= nil) then
|
||||
|
||||
-- If it is FORCE removal, then remove it regardless of pollution.
|
||||
if (c_remove.force) then
|
||||
game.surfaces[GAME_SURFACE_NAME].delete_chunk(c_pos)
|
||||
game.surfaces[surface_name].delete_chunk(c_pos)
|
||||
|
||||
-- If it is a normal timeout removal, don't do it if there is pollution in the chunk.
|
||||
elseif (game.surfaces[GAME_SURFACE_NAME].get_pollution({c_pos.x*32,c_pos.y*32}) > 0) then
|
||||
global.rg.map[c_pos.x][c_pos.y] = game.tick
|
||||
elseif (global.rg[surface_name].map[c_pos.x][c_pos.y] == REGROWTH_FLAG_REMOVAL) then
|
||||
|
||||
-- Else delete the chunk
|
||||
else
|
||||
game.surfaces[GAME_SURFACE_NAME].delete_chunk(c_pos)
|
||||
-- If it is a normal timeout removal, don't do it if there is pollution in the chunk.
|
||||
if (game.surfaces[surface_name].get_pollution({ c_pos.x * 32, c_pos.y * 32 }) > 0) then
|
||||
global.rg[surface_name].map[c_pos.x][c_pos.y] = game.tick
|
||||
|
||||
-- Else delete the chunk
|
||||
else
|
||||
game.surfaces[surface_name].delete_chunk(c_pos)
|
||||
global.rg[surface_name].map[c_pos.x][c_pos.y] = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
-- This should never happen, TODO: check if it does?
|
||||
error("ERROR - OarcRegrowthRemoveAllChunks: Chunk not in map: " .. c_pos.x .. "," .. c_pos.y .. " on surface: " .. surface_name)
|
||||
end
|
||||
|
||||
-- Remove entry
|
||||
@ -256,41 +517,46 @@ function OarcRegrowthRemoveAllChunks()
|
||||
|
||||
-- MUST GET A NEW CHUNK ITERATOR ON DELETE CHUNK!
|
||||
global.rg.chunk_iter = nil
|
||||
global.rg.world_eater_iter = nil
|
||||
global.rg.we_chunk_iter = nil
|
||||
end
|
||||
|
||||
-- This is the main work function, it checks a single chunk in the list
|
||||
-- per tick. It works according to the rules listed in the header of this
|
||||
-- file.
|
||||
---This is the main work function, it checks a single chunk in the list per tick. It works according to the rules
|
||||
---listed in the header of this file.
|
||||
---@return nil
|
||||
function RegrowthOnTick()
|
||||
|
||||
-- Every half a second, refresh all chunks near a single player
|
||||
-- Cyles through all players. Tick is offset by 2
|
||||
if ((game.tick % (30)) == 2) then
|
||||
RefreshPlayerArea()
|
||||
end
|
||||
if (#global.rg.active_surfaces > 0) then
|
||||
|
||||
-- Every tick, check a few points in the 2d array of the only active surface According to /measured-command this
|
||||
-- shouldn't take more than 0.1ms on average
|
||||
for i=1,20 do
|
||||
RegrowthSingleStepArray()
|
||||
end
|
||||
-- Every half a second, refresh all chunks near a single player
|
||||
-- Cyles through all players. Tick is offset by 2
|
||||
if ((game.tick % (30)) == 2) then
|
||||
RefreshPlayerArea()
|
||||
end
|
||||
|
||||
if (not global.world_eater_disable) then
|
||||
WorldEaterSingleStep()
|
||||
-- Refresh a single spidertron every tick
|
||||
RefreshSpidertronArea()
|
||||
|
||||
-- Every tick, check a few points in the 2d array of the only active surface
|
||||
for i = 1, 20 do
|
||||
RegrowthSingleStepArray()
|
||||
end
|
||||
|
||||
if (global.ocfg.regrowth.enable_world_eater) then
|
||||
WorldEaterSingleStep()
|
||||
end
|
||||
end
|
||||
|
||||
-- Allow enable/disable of auto cleanup, can change during runtime.
|
||||
local interval_ticks = global.rg.timeout_ticks
|
||||
local interval_ticks = global.ocfg.regrowth.cleanup_interval * TICKS_PER_MINUTE
|
||||
-- Send a broadcast warning before it happens.
|
||||
if ((game.tick % interval_ticks) == interval_ticks-(60*30 + 1)) then
|
||||
if ((game.tick % interval_ticks) == interval_ticks - (60 * 30 + 1)) then
|
||||
if (#global.rg.removal_list > 100) then
|
||||
SendBroadcastMsg("Map cleanup in 30 seconds... Unused and old map chunks will be deleted!")
|
||||
end
|
||||
end
|
||||
|
||||
-- Delete all listed chunks across all active surfaces
|
||||
if ((game.tick % interval_ticks) == interval_ticks-1) then
|
||||
if ((game.tick % interval_ticks) == interval_ticks - 1) then
|
||||
if (#global.rg.removal_list > 100) then
|
||||
OarcRegrowthRemoveAllChunks()
|
||||
SendBroadcastMsg("Map cleanup done, sorry for your loss.")
|
||||
@ -300,14 +566,13 @@ end
|
||||
|
||||
-- This function removes any chunks flagged but on demand.
|
||||
-- Controlled by the global.rg.force_removal_flag
|
||||
-- This function may be used outside of the normal regrowth modse.
|
||||
function RegrowthForceRemovalOnTick()
|
||||
-- Catch force remove flag
|
||||
if (game.tick == global.rg.force_removal_flag+60) then
|
||||
if (game.tick == global.rg.force_removal_flag + 60) then
|
||||
SendBroadcastMsg("Map cleanup (forced) in 30 seconds... Unused and old map chunks will be deleted!")
|
||||
end
|
||||
|
||||
if (game.tick == global.rg.force_removal_flag+(60*30 + 60)) then
|
||||
if (game.tick == global.rg.force_removal_flag + (60 * 30 + 60)) then
|
||||
OarcRegrowthRemoveAllChunks()
|
||||
SendBroadcastMsg("Map cleanup done, sorry for your loss.")
|
||||
end
|
||||
@ -315,71 +580,143 @@ end
|
||||
|
||||
function WorldEaterSingleStep()
|
||||
|
||||
-- Make sure we have a valid iterator!
|
||||
if (not global.rg.world_eater_iter or not global.rg.world_eater_iter.valid) then
|
||||
global.rg.world_eater_iter = game.surfaces[GAME_SURFACE_NAME].get_chunks()
|
||||
end
|
||||
|
||||
local next_chunk = global.rg.world_eater_iter()
|
||||
|
||||
-- Check if we reached the end
|
||||
if (not next_chunk) then
|
||||
global.rg.world_eater_iter = game.surfaces[GAME_SURFACE_NAME].get_chunks()
|
||||
next_chunk = global.rg.world_eater_iter()
|
||||
end
|
||||
local next_chunk = GetNextChunkAndUpdateWorldEaterIter()
|
||||
if (not next_chunk) then return end
|
||||
local current_surface = global.rg.we_current_surface
|
||||
|
||||
-- Do we have it in our map?
|
||||
if (not global.rg.map[next_chunk.x] or not global.rg.map[next_chunk.x][next_chunk.y]) then
|
||||
if (not global.rg[current_surface].map[next_chunk.x] or not global.rg[current_surface].map[next_chunk.x][next_chunk.y]) then
|
||||
return -- Chunk isn't in our map so we don't care?
|
||||
end
|
||||
end
|
||||
|
||||
-- Search for any abandoned radars and destroy them?
|
||||
local entities = game.surfaces[GAME_SURFACE_NAME].find_entities_filtered{area=next_chunk.area,
|
||||
force={global.ocore.abandoned_force},
|
||||
name="radar"}
|
||||
for k,v in pairs(entities) do
|
||||
local abandoned_radars = game.surfaces[current_surface].find_entities_filtered { area = next_chunk.area,
|
||||
force = { ABANDONED_FORCE_NAME },
|
||||
name = "radar" }
|
||||
for k, v in pairs(abandoned_radars) do
|
||||
v.die(nil)
|
||||
end
|
||||
|
||||
-- Search for any entities with _DESTROYED_ force and kill them.
|
||||
entities = game.surfaces[GAME_SURFACE_NAME].find_entities_filtered{area=next_chunk.area,
|
||||
force={global.ocore.destroyed_force}}
|
||||
for k,v in pairs(entities) do
|
||||
v.die(nil)
|
||||
-- local destroy_entities = game.surfaces[current_surface].find_entities_filtered { area = next_chunk.area,
|
||||
-- force = { DESTROYED_FORCE_NAME } }
|
||||
-- for k, v in pairs(destroy_entities) do
|
||||
-- v.die(nil)
|
||||
-- end
|
||||
|
||||
local c_timer = global.rg[current_surface].map[next_chunk.x][next_chunk.y]
|
||||
|
||||
-- Only check chunnks that are flagged as "active".
|
||||
-- Others are either permanent or will be handled by the default regrowth checks.
|
||||
if (c_timer == REGROWTH_FLAG_ACTIVE) then
|
||||
local area = {
|
||||
left_top = { next_chunk.area.left_top.x - 8, next_chunk.area.left_top.y - 8 },
|
||||
right_bottom = { next_chunk.area.right_bottom.x + 8, next_chunk.area.right_bottom.y + 8 }
|
||||
}
|
||||
|
||||
local entities = game.surfaces[current_surface].find_entities_filtered { area = area, force = ENEMY_FORCES_NAMES_INCL_NEUTRAL, invert = true }
|
||||
for _, v in pairs(entities) do
|
||||
if (v.last_user) then
|
||||
return -- This means we're done checking this chunk. It has an active player entity.
|
||||
end
|
||||
end
|
||||
|
||||
local moving_entities = game.surfaces[current_surface].find_entities_filtered {
|
||||
area = area,
|
||||
type = { "character", "logistics-robot", "construction-robot", "car", "spider-vehicle" },
|
||||
}
|
||||
if (#moving_entities > 0) then
|
||||
return -- It's possible there are some moving entities with no last user set.
|
||||
end
|
||||
|
||||
-- Destroy the entities that lack an owner! (player was removed)
|
||||
for _, v in pairs(entities) do
|
||||
if (v and v.valid) then
|
||||
v.die(nil)
|
||||
end
|
||||
end
|
||||
-- SendBroadcastMsg(next_chunk.x .. "," .. next_chunk.y .. " WorldEaterSingleStep")
|
||||
global.rg[current_surface].map[next_chunk.x][next_chunk.y] = game.tick -- Set the timer on it.
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---Checks if a chunk has any player entities in or near it.
|
||||
---@param surface_name string - The surface name to act on
|
||||
---@param chunk ChunkPositionAndArea - The chunk position to check
|
||||
---@return boolean
|
||||
function CheckIfChunkHasAnyPlayerEntities(surface_name, chunk)
|
||||
|
||||
-- Check around the chunk for anything overlapping to be safe!
|
||||
local area = {
|
||||
left_top = { chunk.area.left_top.x - 8, chunk.area.left_top.y - 8 },
|
||||
right_bottom = { chunk.area.right_bottom.x + 8, chunk.area.right_bottom.y + 8 }
|
||||
}
|
||||
|
||||
local entities = game.surfaces[surface_name].find_entities_filtered { area = area, force = ENEMY_FORCES_NAMES_INCL_NEUTRAL, invert = true }
|
||||
for _, v in pairs(entities) do
|
||||
if (v.last_user) then
|
||||
return true -- YES there is player stuff here.
|
||||
end
|
||||
end
|
||||
|
||||
-- If the chunk isn't marked permament, then check if we can remove it
|
||||
local c_timer = global.rg.map[next_chunk.x][next_chunk.y]
|
||||
if (c_timer == -1) then
|
||||
local moving_entities = game.surfaces[surface_name].find_entities_filtered {
|
||||
area = area,
|
||||
type = { "character", "logistics-robot", "construction-robot", "car", "spider-vehicle" },
|
||||
}
|
||||
if (#moving_entities > 0) then
|
||||
return true -- Any of these entities are player controlled and count!
|
||||
end
|
||||
|
||||
local area = {left_top = {next_chunk.area.left_top.x-8, next_chunk.area.left_top.y-8},
|
||||
right_bottom = {next_chunk.area.right_bottom.x+8, next_chunk.area.right_bottom.y+8}}
|
||||
return false
|
||||
end
|
||||
|
||||
local entities = game.surfaces[GAME_SURFACE_NAME].find_entities_filtered{area=area, force={"enemy", "neutral"}, invert=true}
|
||||
local total_count = #entities
|
||||
local has_last_user_set = false
|
||||
|
||||
if (total_count > 0) then
|
||||
for k,v in pairs(entities) do
|
||||
if (v.last_user or (v.type == "character") or string.contains(v.type, "robot")) then
|
||||
has_last_user_set = true
|
||||
return -- This means we're done checking this chunk.
|
||||
end
|
||||
end
|
||||
---When an entity is built, if it is a spidertron we add it to our list
|
||||
---@param event EventData.on_built_entity
|
||||
---@return nil
|
||||
function RegrowthOnBuiltEntity(event)
|
||||
if (event.created_entity and event.created_entity.valid and event.created_entity.type == "spider-vehicle") then
|
||||
|
||||
table.insert(global.rg.spidertrons, event.created_entity)
|
||||
log("Added spidertron to regrowth tracking")
|
||||
|
||||
-- If all entities found have no last user, then KILL all entities!
|
||||
if (not has_last_user_set) then
|
||||
for k,v in pairs(entities) do
|
||||
if (v and v.valid) then
|
||||
v.die(nil)
|
||||
end
|
||||
end
|
||||
-- SendBroadcastMsg(next_chunk.x .. "," .. next_chunk.y .. " WorldEaterSingleStep - ENTITIES FOUND")
|
||||
global.rg.map[next_chunk.x][next_chunk.y] = game.tick -- Set the timer on it.
|
||||
end
|
||||
else
|
||||
-- SendBroadcastMsg(next_chunk.x .. "," .. next_chunk.y .. " WorldEaterSingleStep - NO ENTITIES FOUND")
|
||||
global.rg.map[next_chunk.x][next_chunk.y] = game.tick -- Set the timer on it.
|
||||
if global.rg.spidertron_chunk_radius == nil then
|
||||
global.rg.spidertron_chunk_radius = game.entity_prototypes["spidertron"].chunk_exploration_radius
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---On tick, we refresh a single spidertron's area.
|
||||
---@return nil
|
||||
function RefreshSpidertronArea()
|
||||
if (#global.rg.spidertrons > 0) then
|
||||
|
||||
local spidertron = global.rg.spidertrons[global.rg_spidertron_index]
|
||||
|
||||
if (spidertron and spidertron.valid) then
|
||||
|
||||
--Check if this surface is active.
|
||||
local surface_name = spidertron.surface.name
|
||||
if (global.rg[surface_name] ~= nil) and (global.rg[surface_name].active) then
|
||||
RefreshArea(spidertron.surface.name, spidertron.position, global.rg.spidertron_chunk_radius, 0)
|
||||
end
|
||||
|
||||
UpdateSpidertronIndex() -- Go to next valid spidertron on the next tick
|
||||
else
|
||||
table.remove(global.rg.spidertrons, global.rg_spidertron_index)
|
||||
log("Removed spidertron from regrowth tracking")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Updates the spidertron index to the next one in the list.
|
||||
---@return nil
|
||||
function UpdateSpidertronIndex()
|
||||
global.rg_spidertron_index = global.rg_spidertron_index + 1
|
||||
if (global.rg_spidertron_index > #global.rg.spidertrons) then
|
||||
global.rg_spidertron_index = 1
|
||||
end
|
||||
end
|
@ -1,49 +0,0 @@
|
||||
|
||||
local function RemoveTileGhosts()
|
||||
local surface = game.player.surface
|
||||
for c in surface.get_chunks() do
|
||||
for key, entity in pairs(surface.find_entities_filtered({area={{c.x * 32, c.y * 32}, {c.x * 32 + 32, c.y * 32 + 32}}, name= "tile-ghost"})) do
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function RemoveBlueprintedModulesGhosts()
|
||||
local surface = game.player.surface
|
||||
for c in surface.get_chunks() do
|
||||
for key, entity in pairs(surface.find_entities_filtered({area={{c.x * 32, c.y * 32}, {c.x * 32 + 32, c.y * 32 + 32}}, name= "item-request-proxy"})) do
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function RemoveGhostEntities()
|
||||
local surface = game.player.surface
|
||||
for c in surface.get_chunks() do
|
||||
for key, entity in pairs(surface.find_entities_filtered({area={{c.x * 32, c.y * 32}, {c.x * 32 + 32, c.y * 32 + 32}}, name= "entity-ghost"})) do
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
commands.add_command("rg", "remove ghosts", function(command)
|
||||
local player = game.players[command.player_index];
|
||||
if player ~= nil and player.admin then
|
||||
if (command.parameter ~= nil) then
|
||||
if command.parameter == "all" then
|
||||
RemoveTileGhosts()
|
||||
RemoveBlueprintedModulesGhosts()
|
||||
RemoveGhostEntities()
|
||||
elseif command.parameter == "tiles" then
|
||||
RemoveTileGhosts()
|
||||
elseif command.parameter == "modules" then
|
||||
RemoveBlueprintedModulesGhosts()
|
||||
elseif command.parameter == "entities" then
|
||||
RemoveGhostEntities()
|
||||
else
|
||||
player.print("remove all ghostes | tiles | modules | entities");
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
@ -1,78 +0,0 @@
|
||||
-- rocket_launch.lua
|
||||
-- May 2019
|
||||
|
||||
-- This is meant to extract out any rocket launch related logic to support my oarc scenario designs.
|
||||
|
||||
require("lib/oarc_utils")
|
||||
require("config")
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Rocket Launch Event Code
|
||||
-- Controls the "win condition"
|
||||
--------------------------------------------------------------------------------
|
||||
function RocketLaunchEvent(event)
|
||||
local force = event.rocket.force
|
||||
|
||||
-- Notify players on force if rocket was launched without sat.
|
||||
if event.rocket.get_item_count("satellite") == 0 then
|
||||
for index, player in pairs(force.players) do
|
||||
player.print("You launched the rocket, but you didn't put a satellite inside.")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- First ever sat launch
|
||||
if not global.ocore.satellite_sent then
|
||||
global.ocore.satellite_sent = {}
|
||||
SendBroadcastMsg("Team " .. force.name .. " was the first to launch a rocket!")
|
||||
ServerWriteFile("rocket_events", "Team " .. force.name .. " was the first to launch a rocket!" .. "\n")
|
||||
|
||||
for name,player in pairs(game.players) do
|
||||
SetOarcGuiTabEnabled(player, OARC_ROCKETS_GUI_TAB_NAME, true)
|
||||
end
|
||||
end
|
||||
|
||||
-- Track additional satellites launched by this force
|
||||
if global.ocore.satellite_sent[force.name] then
|
||||
global.ocore.satellite_sent[force.name] = global.ocore.satellite_sent[force.name] + 1
|
||||
SendBroadcastMsg("Team " .. force.name .. " launched another rocket. Total " .. global.ocore.satellite_sent[force.name])
|
||||
ServerWriteFile("rocket_events", "Team " .. force.name .. " launched another rocket. Total " .. global.ocore.satellite_sent[force.name] .. "\n")
|
||||
|
||||
-- First sat launch for this force.
|
||||
else
|
||||
-- game.set_game_state{game_finished=true, player_won=true, can_continue=true}
|
||||
global.ocore.satellite_sent[force.name] = 1
|
||||
SendBroadcastMsg("Team " .. force.name .. " launched their first rocket!")
|
||||
ServerWriteFile("rocket_events", "Team " .. force.name .. " launched their first rocket!" .. "\n")
|
||||
|
||||
-- Unlock research and recipes
|
||||
if global.ocfg.lock_goodies_rocket_launch then
|
||||
for _,v in ipairs(LOCKED_TECHNOLOGIES) do
|
||||
EnableTech(force, v.t)
|
||||
end
|
||||
for _,v in ipairs(LOCKED_RECIPES) do
|
||||
if (force.technologies[v.r].researched) then
|
||||
AddRecipe(force, v.r)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CreateRocketGuiTab(tab_container, player)
|
||||
-- local frame = tab_container.add{type="frame", name="rocket-panel", caption="Satellites Launched:", direction = "vertical"}
|
||||
|
||||
AddLabel(tab_container, nil, "Satellites Launched:", my_label_header_style)
|
||||
|
||||
if (global.ocore.satellite_sent == nil) then
|
||||
AddLabel(tab_container, nil, "No launches yet.", my_label_style)
|
||||
else
|
||||
for force_name,sat_count in pairs(global.ocore.satellite_sent) do
|
||||
AddLabel(tab_container,
|
||||
"rc_"..force_name,
|
||||
"Team " .. force_name .. ": " .. tostring(sat_count),
|
||||
my_label_style)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
369
lib/scaled_enemies.lua
Normal file
369
lib/scaled_enemies.lua
Normal file
@ -0,0 +1,369 @@
|
||||
-- This handles scaling enemies in a few different ways to make sure that all players can have a reasonable experience
|
||||
-- even if they join the game late or are playing at a slower pace.
|
||||
|
||||
-- TODO: Plan for new enemies in space DLC.
|
||||
-- TODO: Plan for new enemies in space DLC.
|
||||
-- TODO: Plan for new enemies in space DLC.
|
||||
|
||||
|
||||
ENEMY_FORCE_EASY = "enemy-easy"
|
||||
ENEMY_FORCE_MEDIUM = "enemy-medium"
|
||||
ENEMY_FORCES_NAMES = {"enemy", ENEMY_FORCE_EASY, ENEMY_FORCE_MEDIUM}
|
||||
ENEMY_FORCES_NAMES_INCL_NEUTRAL = {"enemy", ENEMY_FORCE_EASY, ENEMY_FORCE_MEDIUM, "neutral"}
|
||||
|
||||
ENEMY_BUILT_TYPES = { "biter-spawner", "spitter-spawner", "small-worm-turret", "medium-worm-turret", "big-worm-turret", "behemoth-worm-turret" }
|
||||
|
||||
---Create a few extra enemy forces with fixed evolution factors for scaling down near player bases.
|
||||
---@return nil
|
||||
function CreateEnemyForces()
|
||||
|
||||
-- Create the enemy forces if they don't exist
|
||||
for _,force_name in pairs(ENEMY_FORCES_NAMES) do
|
||||
if (game.forces[force_name] == nil) then
|
||||
game.create_force(force_name)
|
||||
end
|
||||
|
||||
local enemy_force = game.forces[force_name]
|
||||
enemy_force.ai_controllable = true
|
||||
end
|
||||
|
||||
ConfigureEnemyForceRelationships()
|
||||
end
|
||||
|
||||
---Configures the friend and cease fire relationships between all forces and enemy forces.
|
||||
---@return nil
|
||||
function ConfigureEnemyForceRelationships()
|
||||
for _,force in pairs(game.forces) do
|
||||
|
||||
-- If this is an enemy force
|
||||
if (TableContains(ENEMY_FORCES_NAMES, force.name)) then
|
||||
|
||||
-- Make sure it IS friends with all other enemy forces.
|
||||
for _,enemy_force_name in pairs(ENEMY_FORCES_NAMES) do
|
||||
if (force.name ~= enemy_force_name) then -- Exclude self
|
||||
local enemy_force = game.forces[enemy_force_name]
|
||||
|
||||
force.set_friend(enemy_force, true)
|
||||
force.set_cease_fire(enemy_force, true)
|
||||
|
||||
enemy_force.set_friend(force, true)
|
||||
enemy_force.set_cease_fire(force, true)
|
||||
end
|
||||
end
|
||||
|
||||
-- If this is a non-enemy force
|
||||
else
|
||||
-- Make sure this force is NOT friends with any enemy forces.
|
||||
for _,enemy_force_name in pairs(ENEMY_FORCES_NAMES) do
|
||||
local enemy_force = game.forces[enemy_force_name]
|
||||
|
||||
force.set_friend(enemy_force, false)
|
||||
force.set_cease_fire(enemy_force, false)
|
||||
|
||||
enemy_force.set_friend(force, false)
|
||||
enemy_force.set_cease_fire(force, false)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
---Configures the friend and cease fire relationships between all enemy forces and a new player force.
|
||||
---@param new_player_force LuaForce
|
||||
---@return nil
|
||||
function ConfigureEnemyForceRelationshipsForNewPlayerForce(new_player_force)
|
||||
for _,enemy_force_name in pairs(ENEMY_FORCES_NAMES) do
|
||||
local enemy_force = game.forces[enemy_force_name]
|
||||
|
||||
new_player_force.set_friend(enemy_force, false)
|
||||
new_player_force.set_cease_fire(enemy_force, false)
|
||||
|
||||
enemy_force.set_friend(new_player_force, false)
|
||||
enemy_force.set_cease_fire(new_player_force, false)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Keep the enemy evolution factor in check.
|
||||
---@return nil
|
||||
function RestrictEnemyEvolutionOnTick()
|
||||
local base_evo_factor = game.forces["enemy"].evolution_factor
|
||||
|
||||
-- Restrict the evolution factor of the enemy forces
|
||||
game.forces[ENEMY_FORCE_EASY].evolution_factor = math.min(base_evo_factor, global.ocfg.gameplay.modified_enemy_easy_evo)
|
||||
game.forces[ENEMY_FORCE_MEDIUM].evolution_factor = math.min(base_evo_factor, global.ocfg.gameplay.modified_enemy_medium_evo)
|
||||
end
|
||||
|
||||
---Downgrades worms based on distance from origin and near/far spawn distances.
|
||||
---This helps make sure worms aren't too overwhelming even at these further spawn distances.
|
||||
---@param event EventData.on_chunk_generated
|
||||
---@return nil
|
||||
function DowngradeWormsDistanceBasedOnChunkGenerate(event)
|
||||
|
||||
---@type OarcConfigGameplaySettings
|
||||
local gameplay = global.ocfg.gameplay
|
||||
|
||||
if (util.distance({ x = 0, y = 0 }, event.area.left_top) < (gameplay.near_spawn_distance * CHUNK_SIZE)) then
|
||||
DowngradeWormsInArea(event.surface, event.area, 100, 100, 100)
|
||||
elseif (util.distance({ x = 0, y = 0 }, event.area.left_top) < (gameplay.far_spawn_distance * CHUNK_SIZE)) then
|
||||
DowngradeWormsInArea(event.surface, event.area, 50, 90, 100)
|
||||
elseif (util.distance({ x = 0, y = 0 }, event.area.left_top) < (gameplay.far_spawn_distance * CHUNK_SIZE * 2)) then
|
||||
DowngradeWormsInArea(event.surface, event.area, 20, 80, 97)
|
||||
else
|
||||
DowngradeWormsInArea(event.surface, event.area, 0, 20, 90)
|
||||
end
|
||||
end
|
||||
|
||||
---Downgrades enemies based on distance from origin and near/far spawn distances.
|
||||
---@param event EventData.on_chunk_generated
|
||||
---@return nil
|
||||
function DowngradeAndReduceEnemiesOnChunkGenerate(event)
|
||||
|
||||
local surface = event.surface
|
||||
local chunk_area = event.area
|
||||
|
||||
local closest_spawn = GetClosestUniqueSpawn(surface.name, chunk_area.left_top)
|
||||
if (closest_spawn == nil) then return end
|
||||
|
||||
local spawn_config --[[@as OarcConfigSpawn]] = global.ocfg.surfaces_config[surface.name].spawn_config
|
||||
local chunkAreaCenter = {
|
||||
x = chunk_area.left_top.x + (CHUNK_SIZE / 2),
|
||||
y = chunk_area.left_top.y + (CHUNK_SIZE / 2)
|
||||
}
|
||||
|
||||
-- Make chunks near a spawn safe by removing enemies
|
||||
if (util.distance(closest_spawn.position, chunkAreaCenter) < spawn_config.safe_area.safe_radius) then
|
||||
RemoveEnemiesInArea(surface, chunk_area)
|
||||
|
||||
-- Create a warning area with heavily reduced enemies
|
||||
elseif (util.distance(closest_spawn.position, chunkAreaCenter) < spawn_config.safe_area.warn_radius) then
|
||||
|
||||
-- TODO: Refactor this to reduce calls to find_entities_filtered!
|
||||
ReduceEnemiesInArea(surface, chunk_area, spawn_config.safe_area.warn_reduction)
|
||||
RemoveWormsInArea(surface, chunk_area, false, true, true, true) -- remove all non-small worms.
|
||||
ConvertEnemiesToOtherForceInArea(surface, chunk_area, ENEMY_FORCE_EASY)
|
||||
|
||||
-- Create a third area with moderately reduced enemies
|
||||
elseif (util.distance(closest_spawn.position, chunkAreaCenter) < spawn_config.safe_area.danger_radius) then
|
||||
|
||||
-- TODO: Refactor this to reduce calls to find_entities_filtered!
|
||||
ReduceEnemiesInArea(surface, chunk_area, spawn_config.safe_area.danger_reduction)
|
||||
RemoveWormsInArea(surface, chunk_area, false, false, true, true) -- remove all huge/behemoth worms.
|
||||
ConvertEnemiesToOtherForceInArea(surface, chunk_area, ENEMY_FORCE_MEDIUM)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---Convenient way to remove aliens, just provide an area
|
||||
---@param surface LuaSurface
|
||||
---@param area BoundingBox
|
||||
---@return nil
|
||||
function RemoveEnemiesInArea(surface, area)
|
||||
for _, entity in pairs(surface.find_entities_filtered { area = area, force = "enemy" }) do
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
|
||||
---Make an area safer, randomly removes enemies based on a reduction factor.
|
||||
---@param surface LuaSurface
|
||||
---@param area BoundingBox
|
||||
---@param reductionFactor integer Reduction factor divides the enemy spawns by that number. 2 = half, 3 = third, etc...
|
||||
---@return nil
|
||||
function ReduceEnemiesInArea(surface, area, reductionFactor)
|
||||
for _, entity in pairs(surface.find_entities_filtered { area = area, force = "enemy" }) do
|
||||
if (math.random(0, reductionFactor) > 0) then
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Downgrades worms in an area based on chance. 100% small would mean all worms are changed to small.
|
||||
---@param surface LuaSurface
|
||||
---@param area BoundingBox
|
||||
---@param small_percent integer ---Chance to change to small worm
|
||||
---@param medium_percent integer
|
||||
---@param big_percent integer
|
||||
---@return nil
|
||||
function DowngradeWormsInArea(surface, area, small_percent, medium_percent, big_percent)
|
||||
-- Leave out "small-worm-turret" as it's the lowest.
|
||||
local worm_types = { "medium-worm-turret", "big-worm-turret", "behemoth-worm-turret" }
|
||||
|
||||
for _, entity in pairs(surface.find_entities_filtered { area = area, name = worm_types }) do
|
||||
-- Roll a number between 0-100
|
||||
local rand_percent = math.random(0, 100)
|
||||
local worm_pos = entity.position
|
||||
local worm_name = entity.name
|
||||
|
||||
-- If number is less than small percent, change to small
|
||||
if (rand_percent <= small_percent) then
|
||||
if (not (worm_name == "small-worm-turret")) then
|
||||
entity.destroy()
|
||||
surface.create_entity { name = "small-worm-turret", position = worm_pos, force = game.forces.enemy }
|
||||
end
|
||||
|
||||
-- ELSE If number is less than medium percent, change to small
|
||||
elseif (rand_percent <= medium_percent) then
|
||||
if (not (worm_name == "medium-worm-turret")) then
|
||||
entity.destroy()
|
||||
surface.create_entity { name = "medium-worm-turret", position = worm_pos, force = game.forces.enemy }
|
||||
end
|
||||
|
||||
-- ELSE If number is less than big percent, change to small
|
||||
elseif (rand_percent <= big_percent) then
|
||||
if (not (worm_name == "big-worm-turret")) then
|
||||
entity.destroy()
|
||||
surface.create_entity { name = "big-worm-turret", position = worm_pos, force = game.forces.enemy }
|
||||
end
|
||||
|
||||
-- ELSE ignore it.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---A function to help me remove worms in an area. Yeah kind of an unecessary wrapper, but makes my life easier to remember the worm types.
|
||||
---@param surface LuaSurface
|
||||
---@param area BoundingBox
|
||||
---@param small boolean
|
||||
---@param medium boolean
|
||||
---@param big boolean
|
||||
---@param behemoth boolean
|
||||
---@return nil
|
||||
function RemoveWormsInArea(surface, area, small, medium, big, behemoth)
|
||||
local worm_types = {}
|
||||
|
||||
if (small) then
|
||||
table.insert(worm_types, "small-worm-turret")
|
||||
end
|
||||
if (medium) then
|
||||
table.insert(worm_types, "medium-worm-turret")
|
||||
end
|
||||
if (big) then
|
||||
table.insert(worm_types, "big-worm-turret")
|
||||
end
|
||||
if (behemoth) then
|
||||
table.insert(worm_types, "behemoth-worm-turret")
|
||||
end
|
||||
|
||||
-- Destroy
|
||||
if (#worm_types > 0) then
|
||||
for _, entity in pairs(surface.find_entities_filtered { area = area, name = worm_types }) do
|
||||
entity.destroy()
|
||||
end
|
||||
else
|
||||
log("RemoveWormsInArea had empty worm_types list!")
|
||||
end
|
||||
end
|
||||
|
||||
---Converts all enemies in the base enemy force in an area to easy force.
|
||||
---@param surface LuaSurface
|
||||
---@param area BoundingBox
|
||||
---@param force_name string
|
||||
---@return nil
|
||||
function ConvertEnemiesToOtherForceInArea(surface, area, force_name)
|
||||
for _, entity in pairs(surface.find_entities_filtered { area = area, force = "enemy" }) do
|
||||
entity.force = game.forces[force_name]
|
||||
end
|
||||
end
|
||||
|
||||
---Converts new enemy bases to different enemy forces if they are near to player bases.
|
||||
---@param event EventData.on_biter_base_built
|
||||
---@return nil
|
||||
function ChangeEnemySpawnersToOtherForceOnBuilt(event)
|
||||
if (not event.entity or not event.entity.position or not TableContains(ENEMY_FORCES_NAMES, event.entity.force.name)) then
|
||||
log("ChangeEnemySpawnersToOtherForceOnBuilt Unexpected entity or force?")
|
||||
return
|
||||
end
|
||||
|
||||
local enemy_pos = event.entity.position
|
||||
local surface = event.entity.surface
|
||||
local enemy_name = event.entity.name
|
||||
|
||||
if (not TableContains(ENEMY_BUILT_TYPES, enemy_name)) then
|
||||
log("ChangeEnemySpawnersToOtherForceOnBuilt Unexpected entity name? " .. enemy_name)
|
||||
return
|
||||
end
|
||||
|
||||
local closest_spawn = GetClosestUniqueSpawn(surface.name, enemy_pos)
|
||||
if (closest_spawn == nil) then return end
|
||||
|
||||
-- No enemies inside safe radius!
|
||||
if (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.safe_radius) then
|
||||
event.entity.destroy()
|
||||
|
||||
-- Warn distance should be EASY
|
||||
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.warn_radius) then
|
||||
event.entity.force = game.forces[ENEMY_FORCE_EASY]
|
||||
|
||||
-- Danger distance should be MEDIUM
|
||||
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.danger_radius) then
|
||||
event.entity.force = game.forces[ENEMY_FORCE_MEDIUM]
|
||||
|
||||
-- Otherwise make sure they are on the base enemy force (stops easy enemies from spreading out too far).
|
||||
else
|
||||
event.entity.force = game.forces["enemy"]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- I wrote this to ensure everyone gets safer spawns regardless of evolution level.
|
||||
-- This is intended to downgrade any biters/spitters spawning near player bases.
|
||||
-- I'm not sure the performance impact of this but I'm hoping it's not bad.
|
||||
---@param event EventData.on_entity_spawned|EventData.on_biter_base_built
|
||||
---@return nil
|
||||
function ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
if (not event.entity or not (event.entity.force.name == "enemy") or not event.entity.position) then
|
||||
log("ModifyBiterSpawns - Unexpected use.")
|
||||
return
|
||||
end
|
||||
|
||||
local enemy_pos = event.entity.position
|
||||
local surface = event.entity.surface
|
||||
local enemy_name = event.entity.name
|
||||
|
||||
local closest_spawn = GetClosestUniqueSpawn(surface.name, enemy_pos)
|
||||
|
||||
if (closest_spawn == nil) then
|
||||
-- log("GetClosestUniqueSpawn ERROR - None found?")
|
||||
return
|
||||
end
|
||||
|
||||
-- No enemies inside safe radius!
|
||||
if (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.safe_radius) then
|
||||
event.entity.destroy()
|
||||
|
||||
-- Warn distance is all SMALL only.
|
||||
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.warn_radius) then
|
||||
if ((enemy_name == "biter-spawner") or (enemy_name == "spitter-spawner")) then
|
||||
event.entity.force = game.forces["enemy-easy"]
|
||||
|
||||
elseif ((enemy_name == "big-biter") or (enemy_name == "behemoth-biter") or (enemy_name == "medium-biter")) then
|
||||
event.entity.destroy()
|
||||
surface.create_entity { name = "small-biter", position = enemy_pos, force = game.forces["enemy-easy"] }
|
||||
-- log("Downgraded biter close to spawn.")
|
||||
elseif ((enemy_name == "big-spitter") or (enemy_name == "behemoth-spitter") or (enemy_name == "medium-spitter")) then
|
||||
event.entity.destroy()
|
||||
surface.create_entity { name = "small-spitter", position = enemy_pos, force = game.forces["enemy-easy"] }
|
||||
-- log("Downgraded spitter close to spawn.")
|
||||
elseif ((enemy_name == "big-worm-turret") or (enemy_name == "behemoth-worm-turret") or (enemy_name == "medium-worm-turret")) then
|
||||
event.entity.destroy()
|
||||
surface.create_entity { name = "small-worm-turret", position = enemy_pos, force = game.forces["enemy-easy"] }
|
||||
-- log("Downgraded worm close to spawn.")
|
||||
end
|
||||
|
||||
-- Danger distance is MEDIUM max.
|
||||
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.surfaces_config[surface.name].spawn_config.safe_area.danger_radius) then
|
||||
if ((enemy_name == "big-biter") or (enemy_name == "behemoth-biter")) then
|
||||
event.entity.destroy()
|
||||
surface.create_entity { name = "medium-biter", position = enemy_pos, force = game.forces.enemy }
|
||||
-- log("Downgraded biter further from spawn.")
|
||||
elseif ((enemy_name == "big-spitter") or (enemy_name == "behemoth-spitter")) then
|
||||
event.entity.destroy()
|
||||
surface.create_entity { name = "medium-spitter", position = enemy_pos, force = game.forces.enemy }
|
||||
-- log("Downgraded spitter further from spawn
|
||||
elseif ((enemy_name == "big-worm-turret") or (enemy_name == "behemoth-worm-turret")) then
|
||||
event.entity.destroy()
|
||||
surface.create_entity { name = "medium-worm-turret", position = enemy_pos, force = game.forces.enemy }
|
||||
-- log("Downgraded worm further from spawn.")
|
||||
end
|
||||
end
|
||||
end
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,689 +0,0 @@
|
||||
-- shared_chests.lua
|
||||
-- Feb 2020
|
||||
-- Oarc's silly idea for a scripted item sharing solution.
|
||||
|
||||
-- Buffer size is the limit of joules/tick so multiply by 60 to get /sec.
|
||||
SHARED_ELEC_OUTPUT_BUFFER_SIZE = 1000000000
|
||||
SHARED_ELEC_INPUT_BUFFER_SIZE = 1000000001
|
||||
|
||||
SHARED_ENERGY_STARTING_VALUE = 0 -- 100GJ
|
||||
|
||||
function SharedChestInitItems()
|
||||
|
||||
global.oshared = {}
|
||||
|
||||
global.oshared.chests = {}
|
||||
global.oshared.requests = {}
|
||||
global.oshared.requests_totals = {}
|
||||
|
||||
global.oshared.electricity_inputs = {}
|
||||
global.oshared.electricity_outputs = {}
|
||||
|
||||
global.oshared.chests_combinators = {}
|
||||
global.oshared.items = {}
|
||||
|
||||
global.oshared.items['red-wire'] = 10000
|
||||
global.oshared.items['green-wire'] = 10000
|
||||
global.oshared.items['raw-fish'] = 10000
|
||||
|
||||
global.oshared.energy_stored = SHARED_ENERGY_STARTING_VALUE
|
||||
global.oshared.energy_stored_history = {start=SHARED_ENERGY_STARTING_VALUE, after_input=SHARED_ENERGY_STARTING_VALUE, after_output=SHARED_ENERGY_STARTING_VALUE}
|
||||
end
|
||||
|
||||
function SharedEnergySpawnInput(player, pos)
|
||||
|
||||
local inputElec = game.surfaces[GAME_SURFACE_NAME].create_entity{name="electric-energy-interface", position=pos, force="neutral"}
|
||||
inputElec.destructible = false
|
||||
inputElec.minable = false
|
||||
inputElec.operable = false
|
||||
inputElec.last_user = player
|
||||
|
||||
inputElec.electric_buffer_size = SHARED_ELEC_INPUT_BUFFER_SIZE
|
||||
inputElec.power_production = 0
|
||||
inputElec.power_usage = 0
|
||||
inputElec.energy = 0
|
||||
|
||||
local inputElecCombi = game.surfaces[GAME_SURFACE_NAME].create_entity{name="constant-combinator", position={x=pos.x+1, y=pos.y}, force="neutral"}
|
||||
inputElecCombi.destructible = false
|
||||
inputElecCombi.minable = false
|
||||
inputElecCombi.operable = true -- Input combi can be set by the player!
|
||||
inputElecCombi.last_user = player
|
||||
|
||||
-- Default share is 1MW
|
||||
inputElecCombi.get_or_create_control_behavior().set_signal(1,
|
||||
{signal={type="virtual", name="signal-M"},
|
||||
count=1})
|
||||
|
||||
TemporaryHelperText("Connect to electric network to contribute shared energy.", {pos.x+1.5, pos.y-1}, TICKS_PER_MINUTE*2)
|
||||
TemporaryHelperText("Use combinator to limit number of MW shared.", {pos.x+2.5, pos.y}, TICKS_PER_MINUTE*2)
|
||||
|
||||
table.insert(global.oshared.electricity_inputs, {eei=inputElec, combi=inputElecCombi})
|
||||
end
|
||||
|
||||
function SharedEnergySpawnOutput(player, pos)
|
||||
|
||||
local outputElec = game.surfaces[GAME_SURFACE_NAME].create_entity{name="electric-energy-interface", position=pos, force="neutral"}
|
||||
outputElec.destructible = false
|
||||
outputElec.minable = false
|
||||
outputElec.operable = false
|
||||
outputElec.last_user = player
|
||||
|
||||
outputElec.electric_buffer_size = SHARED_ELEC_OUTPUT_BUFFER_SIZE
|
||||
outputElec.power_production = 0
|
||||
outputElec.power_usage = 0
|
||||
outputElec.energy = 0
|
||||
|
||||
local outputElecCombi = game.surfaces[GAME_SURFACE_NAME].create_entity{name="constant-combinator", position={x=pos.x+1, y=pos.y}, force="neutral"}
|
||||
outputElecCombi.destructible = false
|
||||
outputElecCombi.minable = false
|
||||
outputElecCombi.operable = false -- Output combi is set my script!
|
||||
outputElec.last_user = player
|
||||
|
||||
TemporaryHelperText("Connect to electric network to consume shared energy.", {pos.x+1.5, pos.y-1}, TICKS_PER_MINUTE*2)
|
||||
TemporaryHelperText("Combinator outputs number of MJ currently stored.", {pos.x+2.5, pos.y}, TICKS_PER_MINUTE*2)
|
||||
|
||||
table.insert(global.oshared.electricity_outputs, {eei=outputElec, combi=outputElecCombi})
|
||||
end
|
||||
|
||||
function SharedEnergyStoreInputOnTick()
|
||||
global.oshared.energy_stored_history.start = global.oshared.energy_stored
|
||||
|
||||
for idx,input in pairs(global.oshared.electricity_inputs) do
|
||||
|
||||
-- Check for entity no longer valid:
|
||||
if (input.eei == nil) or (not input.eei.valid) or (input.combi == nil) or (not input.combi.valid) then
|
||||
global.oshared.electricity_inputs[idx] = nil
|
||||
|
||||
-- Is input at least half full, then we can start to store energy.
|
||||
elseif (input.eei.energy > (SHARED_ELEC_INPUT_BUFFER_SIZE/2)) then
|
||||
|
||||
-- Calculate the max we can share
|
||||
local max_input_allowed = input.eei.energy - (SHARED_ELEC_INPUT_BUFFER_SIZE/2)
|
||||
|
||||
-- Get the combinator limit
|
||||
local limit = 0
|
||||
local sig = input.combi.get_or_create_control_behavior().get_signal(1)
|
||||
if ((sig ~= nil) and (sig.signal ~= nil) and (sig.signal.name == "signal-M")) then
|
||||
limit = sig.count
|
||||
end
|
||||
|
||||
-- Get the minimum
|
||||
input.eei.power_usage = math.min(max_input_allowed, math.floor(limit*1000000/60))
|
||||
|
||||
global.oshared.energy_stored = global.oshared.energy_stored + input.eei.power_usage
|
||||
|
||||
-- Switch off contribution if not at least half full.
|
||||
else
|
||||
input.eei.power_usage = 0
|
||||
end
|
||||
end
|
||||
|
||||
global.oshared.energy_stored_history.after_input = global.oshared.energy_stored
|
||||
end
|
||||
|
||||
-- If there is room to distribute energy, we take shared amount split by players.
|
||||
function SharedEnergyDistributeOutputOnTick()
|
||||
|
||||
-- Share limit is total amount stored divided by outputs
|
||||
local energyShareCap = math.floor(global.oshared.energy_stored / (#global.oshared.electricity_outputs))
|
||||
|
||||
-- Iterate through and fill up outputs if they are under 50%
|
||||
for idx,output in pairs(global.oshared.electricity_outputs) do
|
||||
|
||||
-- Check for entity no longer valid:
|
||||
if (output.eei == nil) or (not output.eei.valid) or (output.combi == nil) or (not output.combi.valid) then
|
||||
global.oshared.electricity_outputs[idx] = nil
|
||||
|
||||
|
||||
else
|
||||
-- If it's not full, set production to fill (or as much as is allowed.)
|
||||
if (output.eei.energy < (SHARED_ELEC_OUTPUT_BUFFER_SIZE/2)) then
|
||||
local outBufferSpace = ((SHARED_ELEC_OUTPUT_BUFFER_SIZE/2) - output.eei.energy)
|
||||
output.eei.power_production = math.min(outBufferSpace, energyShareCap)
|
||||
global.oshared.energy_stored = global.oshared.energy_stored - math.min(outBufferSpace, energyShareCap)
|
||||
|
||||
-- Switch off if we're more than half full.
|
||||
else
|
||||
output.eei.power_production = 0
|
||||
end
|
||||
|
||||
-- Update output combinator
|
||||
output.combi.get_or_create_control_behavior().set_signal(1,
|
||||
{signal={type="virtual", name="signal-M"},
|
||||
count=clampInt32(math.floor(global.oshared.energy_stored/1000000))})
|
||||
end
|
||||
end
|
||||
|
||||
global.oshared.energy_stored_history.after_output = global.oshared.energy_stored
|
||||
end
|
||||
|
||||
-- Returns NIL or position of destroyed chest.
|
||||
function FindClosestWoodenChestAndDestroy(player)
|
||||
local target_chest = FindClosestPlayerOwnedEntity(player, "wooden-chest", 16)
|
||||
if (not target_chest) then
|
||||
player.print("Failed to find wooden-chest?")
|
||||
return nil
|
||||
end
|
||||
|
||||
if (not target_chest.get_inventory(defines.inventory.chest).is_empty()) then
|
||||
player.print("Chest is NOT empty! Please empty it and try again.")
|
||||
return nil
|
||||
end
|
||||
|
||||
local pos = target_chest.position
|
||||
if (not target_chest.destroy()) then
|
||||
player.print("ERROR - Can't remove wooden chest??")
|
||||
return nil
|
||||
end
|
||||
|
||||
return {x=math.floor(pos.x),y=math.floor(pos.y)}
|
||||
end
|
||||
|
||||
function ConvertWoodenChestToSharedChestInput(player)
|
||||
local pos = FindClosestWoodenChestAndDestroy(player)
|
||||
if (pos) then
|
||||
SharedChestsSpawnInput(player, pos)
|
||||
OarcMapFeaturePlayerCountChange(player, "special_chests", "logistic-chest-storage", 1)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ConvertWoodenChestToSharedChestOutput(player)
|
||||
local pos = FindClosestWoodenChestAndDestroy(player)
|
||||
if (pos) then
|
||||
SharedChestsSpawnOutput(player, pos)
|
||||
OarcMapFeaturePlayerCountChange(player, "special_chests", "logistic-chest-requester", 1)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ConvertWoodenChestToSharedChestCombinators(player)
|
||||
local pos = FindClosestWoodenChestAndDestroy(player)
|
||||
if (pos) then
|
||||
if (player.surface.can_place_entity{name="constant-combinator", position={pos.x,pos.y-1}}) and
|
||||
(player.surface.can_place_entity{name="constant-combinator", position={pos.x,pos.y+1}}) then
|
||||
SharedChestsSpawnCombinators(player, {x=pos.x,y=pos.y-1}, {x=pos.x,y=pos.y+1})
|
||||
return true
|
||||
else
|
||||
player.print("Failed to place the special combinators. Please check there is enough space in the surrounding tiles!")
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ConvertWoodenChestToShareEnergyInput(player)
|
||||
local pos = FindClosestWoodenChestAndDestroy(player)
|
||||
if (pos) then
|
||||
if (player.surface.can_place_entity{name="electric-energy-interface", position=pos}) and
|
||||
(player.surface.can_place_entity{name="constant-combinator", position={x=pos.x+1, y=pos.y}}) then
|
||||
SharedEnergySpawnInput(player, pos)
|
||||
OarcMapFeaturePlayerCountChange(player, "special_chests", "accumulator", 1)
|
||||
return true
|
||||
else
|
||||
player.print("Failed to place the shared energy input. Please check there is enough space in the surrounding tiles!")
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ConvertWoodenChestToShareEnergyOutput(player)
|
||||
local pos = FindClosestWoodenChestAndDestroy(player)
|
||||
if (pos) then
|
||||
if (player.surface.can_place_entity{name="electric-energy-interface", position=pos}) and
|
||||
(player.surface.can_place_entity{name="constant-combinator", position={x=pos.x+1, y=pos.y}}) then
|
||||
SharedEnergySpawnOutput(player, pos)
|
||||
OarcMapFeaturePlayerCountChange(player, "special_chests", "electric-energy-interface", 1)
|
||||
return true
|
||||
else
|
||||
player.print("Failed to place the shared energy input. Please check there is enough space in the surrounding tiles!")
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ConvertWoodenChestToWaterFill(player)
|
||||
local pos = FindClosestWoodenChestAndDestroy(player)
|
||||
if (pos) then
|
||||
if (getDistance(pos, player.position) > 2) then
|
||||
player.surface.set_tiles({[1]={name = "water", position=pos}})
|
||||
return true
|
||||
else
|
||||
player.print("Failed to place waterfill. Don't stand so close FOOL!")
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function DestroyClosestSharedChestEntity(player)
|
||||
local special_entities = game.surfaces[GAME_SURFACE_NAME].find_entities_filtered{
|
||||
name={"electric-energy-interface", "constant-combinator", "logistic-chest-storage", "logistic-chest-requester"},
|
||||
position=player.position,
|
||||
radius=16,
|
||||
force={"neutral"}}
|
||||
|
||||
if (#special_entities == 0) then
|
||||
player.print("Special entity not found? Are you close enough?")
|
||||
return
|
||||
end
|
||||
|
||||
local closest = game.surfaces[GAME_SURFACE_NAME].get_closest(player.position, special_entities)
|
||||
|
||||
if (closest) then
|
||||
if (closest.last_user and (closest.last_user ~= player)) then
|
||||
player.print("You can't remove other players chests!")
|
||||
else
|
||||
-- Subtract from feature counter...
|
||||
local name = closest.name
|
||||
if (name == "electric-energy-interface") then
|
||||
if (closest.electric_buffer_size == SHARED_ELEC_INPUT_BUFFER_SIZE) then
|
||||
OarcMapFeaturePlayerCountChange(player, "special_chests", "accumulator", -1)
|
||||
else
|
||||
OarcMapFeaturePlayerCountChange(player, "special_chests", "electric-energy-interface", -1)
|
||||
end
|
||||
elseif (name == "logistic-chest-storage") then
|
||||
OarcMapFeaturePlayerCountChange(player, "special_chests", "logistic-chest-storage", -1)
|
||||
elseif (name == "logistic-chest-requester") then
|
||||
OarcMapFeaturePlayerCountChange(player, "special_chests", "logistic-chest-requester", -1)
|
||||
end
|
||||
|
||||
closest.destroy()
|
||||
player.print("Special entity removed!")
|
||||
end
|
||||
else
|
||||
player.print("Special entity not found? Are you close enough? -- ERROR")
|
||||
end
|
||||
end
|
||||
|
||||
function SharedChestsSpawnInput(player, pos)
|
||||
|
||||
local inputChest = game.surfaces[GAME_SURFACE_NAME].create_entity{name="logistic-chest-storage", position={pos.x, pos.y}, force="neutral"}
|
||||
inputChest.destructible = false
|
||||
inputChest.minable = false
|
||||
inputChest.last_user = player
|
||||
|
||||
if global.oshared.chests == nil then
|
||||
global.oshared.chests = {}
|
||||
end
|
||||
|
||||
local chestInfoIn = {player=player.name,type="INPUT",entity=inputChest}
|
||||
table.insert(global.oshared.chests, chestInfoIn)
|
||||
|
||||
TemporaryHelperText("Place items in to share.", {pos.x+1.5, pos.y}, TICKS_PER_MINUTE*2)
|
||||
end
|
||||
|
||||
function SharedChestsSpawnOutput(player, pos, enable_example)
|
||||
|
||||
local outputChest = game.surfaces[GAME_SURFACE_NAME].create_entity{name="logistic-chest-requester", position={pos.x, pos.y}, force="neutral"}
|
||||
outputChest.destructible = false
|
||||
outputChest.minable = false
|
||||
outputChest.last_user = player
|
||||
|
||||
if (enable_example) then
|
||||
outputChest.set_request_slot({name="raw-fish", count=1}, 1)
|
||||
end
|
||||
|
||||
if global.oshared.chests == nil then
|
||||
global.oshared.chests = {}
|
||||
end
|
||||
|
||||
local chestInfoOut = {player=player.name,type="OUTPUT",entity=outputChest}
|
||||
table.insert(global.oshared.chests, chestInfoOut)
|
||||
|
||||
TemporaryHelperText("Set filters to request items.", {pos.x+1.5, pos.y}, TICKS_PER_MINUTE*2)
|
||||
end
|
||||
|
||||
|
||||
function SharedChestsSpawnCombinators(player, posCtrl, posStatus)
|
||||
|
||||
local combiCtrl = game.surfaces[GAME_SURFACE_NAME].create_entity{name="constant-combinator", position=posCtrl, force="neutral"}
|
||||
combiCtrl.destructible = false
|
||||
combiCtrl.minable = false
|
||||
combiCtrl.last_user = player
|
||||
|
||||
-- Fish as an example.
|
||||
combiCtrl.get_or_create_control_behavior().set_signal(1, {signal={type="item", name="raw-fish"}, count=1})
|
||||
|
||||
local combiStat = game.surfaces[GAME_SURFACE_NAME].create_entity{name="constant-combinator", position=posStatus, force="neutral"}
|
||||
combiStat.destructible = false
|
||||
combiStat.minable = false
|
||||
combiStat.operable = false
|
||||
combiStat.last_user = player
|
||||
|
||||
if global.oshared.chests_combinators == nil then
|
||||
global.oshared.chests_combinators = {}
|
||||
end
|
||||
|
||||
local combiPair = {player=player.name,ctrl=combiCtrl,status=combiStat}
|
||||
table.insert(global.oshared.chests_combinators, combiPair)
|
||||
|
||||
TemporaryHelperText("Set signals here to monitor item counts.", {posCtrl.x+1.5, posCtrl.y}, TICKS_PER_MINUTE*2)
|
||||
TemporaryHelperText("Receive signals here to see available items.", {posStatus.x+1.5, posStatus.y}, TICKS_PER_MINUTE*2)
|
||||
end
|
||||
|
||||
function SharedChestsUpdateCombinators()
|
||||
|
||||
if global.oshared.chests_combinators == nil then
|
||||
global.oshared.chests_combinators = {}
|
||||
end
|
||||
|
||||
for idx,combiPair in pairs(global.oshared.chests_combinators) do
|
||||
|
||||
-- Check if combinators still exist
|
||||
if (combiPair.ctrl == nil) or (combiPair.status == nil) or
|
||||
(not combiPair.ctrl.valid) or (not combiPair.status.valid) then
|
||||
global.oshared.chests_combinators[idx] = nil
|
||||
else
|
||||
|
||||
local combiCtrlBehav = combiPair.ctrl.get_or_create_control_behavior()
|
||||
local ctrlSignals = {}
|
||||
|
||||
-- Get signals on the ctrl combi:
|
||||
for i=1,combiCtrlBehav.signals_count do
|
||||
local sig = combiCtrlBehav.get_signal(i)
|
||||
if ((sig ~= nil) and (sig.signal ~= nil) and (sig.signal.type == "item")) then
|
||||
table.insert(ctrlSignals, sig.signal.name)
|
||||
end
|
||||
end
|
||||
|
||||
local combiStatBehav = combiPair.status.get_or_create_control_behavior()
|
||||
|
||||
-- Set signals on the status combi:
|
||||
for i=1,combiCtrlBehav.signals_count do
|
||||
if (ctrlSignals[i] ~= nil) then
|
||||
local availAmnt = global.oshared.items[ctrlSignals[i]]
|
||||
if availAmnt == nil then availAmnt = 0 end
|
||||
|
||||
combiStatBehav.set_signal(i, {signal={type="item", name=ctrlSignals[i]}, count=clampInt32(availAmnt)})
|
||||
else
|
||||
combiStatBehav.set_signal(i, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SharedChestUploadItem(item_name, count)
|
||||
if (not game.item_prototypes[item_name].has_flag("hidden")) then
|
||||
if (global.oshared.items[item_name] == nil) then
|
||||
global.oshared.items[item_name] = count
|
||||
else
|
||||
global.oshared.items[item_name] = global.oshared.items[item_name] + count
|
||||
end
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function SharedChestEmptyEquipment(item_stack)
|
||||
if (item_stack == nil) then
|
||||
return
|
||||
end
|
||||
|
||||
if (item_stack.grid == nil) then
|
||||
return
|
||||
end
|
||||
|
||||
local contents = item_stack.grid.get_contents()
|
||||
for item_name,count in pairs(contents) do
|
||||
SharedChestUploadItem(item_name, count)
|
||||
end
|
||||
end
|
||||
|
||||
function SharedChestUploadChest(entity)
|
||||
|
||||
local chest_inv = entity.get_inventory(defines.inventory.chest)
|
||||
if (chest_inv == nil) then return end
|
||||
if (chest_inv.is_empty()) then return end
|
||||
|
||||
local contents = chest_inv.get_contents()
|
||||
for item_name,count in pairs(contents) do
|
||||
if (game.item_prototypes[item_name].equipment_grid ~= nil) then
|
||||
local item_stack = chest_inv.find_item_stack(item_name)
|
||||
while (item_stack ~= nil) do
|
||||
SharedChestEmptyEquipment(item_stack)
|
||||
item_stack.clear()
|
||||
item_stack = chest_inv.find_item_stack(item_name)
|
||||
end
|
||||
end
|
||||
|
||||
if (SharedChestUploadItem(item_name, count)) then
|
||||
chest_inv.remove({name=item_name, count=count})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Pull all items in the deposit chests
|
||||
function SharedChestsDepositAll()
|
||||
|
||||
if global.oshared.items == nil then
|
||||
global.oshared.items = {}
|
||||
end
|
||||
|
||||
for idx,chest_info in pairs(global.oshared.chests) do
|
||||
|
||||
local chest_entity = chest_info.entity
|
||||
|
||||
-- Delete any chest that is no longer valid.
|
||||
if ((chest_entity == nil) or (not chest_entity.valid)) then
|
||||
global.oshared.chests[idx] = nil
|
||||
|
||||
-- Take inputs and store.
|
||||
elseif (chest_info.type == "INPUT") then
|
||||
SharedChestUploadChest(chest_entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Tally up requests by item.
|
||||
function SharedChestsTallyRequests()
|
||||
|
||||
-- Clear existing requests. Also serves as an init
|
||||
global.oshared.requests = {}
|
||||
global.oshared.requests_totals = {}
|
||||
|
||||
-- For each output chest.
|
||||
for idx,chestInfo in pairs(global.oshared.chests) do
|
||||
|
||||
local chestEntity = chestInfo.entity
|
||||
|
||||
-- Delete any chest that is no longer valid.
|
||||
if ((chestEntity == nil) or (not chestEntity.valid)) then
|
||||
global.oshared.chests[idx] = nil
|
||||
|
||||
elseif (chestInfo.type == "OUTPUT") then
|
||||
|
||||
-- For each request slot
|
||||
for i = 1, chestEntity.request_slot_count, 1 do
|
||||
local req = chestEntity.get_request_slot(i)
|
||||
|
||||
-- If there is a request, add the request count to our request table.
|
||||
if (req ~= nil) then
|
||||
|
||||
if global.oshared.requests[req.name] == nil then
|
||||
global.oshared.requests[req.name] = {}
|
||||
end
|
||||
|
||||
if global.oshared.requests[req.name][chestInfo.player] == nil then
|
||||
global.oshared.requests[req.name][chestInfo.player] = 0
|
||||
end
|
||||
|
||||
if global.oshared.requests_totals[req.name] == nil then
|
||||
global.oshared.requests_totals[req.name] = 0
|
||||
end
|
||||
|
||||
-- Calculate actual request to fill remainder
|
||||
local existingAmount = chestEntity.get_inventory(defines.inventory.chest).get_item_count(req.name)
|
||||
local requestAmount = math.max(req.count-existingAmount, 0)
|
||||
|
||||
-- Add the request counts
|
||||
global.oshared.requests[req.name][chestInfo.player] = global.oshared.requests[req.name][chestInfo.player] + requestAmount
|
||||
global.oshared.requests_totals[req.name] = global.oshared.requests_totals[req.name] + requestAmount
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- If demand is more than supply, limit each player's total item request to shared amount
|
||||
for reqName,reqTally in pairs(global.oshared.requests) do
|
||||
|
||||
local cap = 0
|
||||
local mustCap = false
|
||||
|
||||
-- No shared items means nothing to supply.
|
||||
if (global.oshared.items[reqName] == nil) or (global.oshared.items[reqName] == 0) then
|
||||
mustCap = true
|
||||
cap = 0
|
||||
|
||||
-- Otherwise, limit by dividing by players.
|
||||
elseif (global.oshared.requests_totals[reqName] > global.oshared.items[reqName]) then
|
||||
mustCap = true
|
||||
cap = math.floor(global.oshared.items[reqName] / TableLength(global.oshared.requests[reqName]))
|
||||
|
||||
-- In the case where we are rounding down to 0, let's bump the minimum distribution to 1.
|
||||
if (cap == 0) then
|
||||
cap = 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Limit each request to the cap.
|
||||
if mustCap then
|
||||
for player,reqCount in pairs(global.oshared.requests[reqName]) do
|
||||
if (reqCount > cap) then
|
||||
global.oshared.requests[reqName][player] = cap
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- Distribute requests based on demand
|
||||
function SharedChestsDistributeRequests()
|
||||
|
||||
-- For each output chest.
|
||||
for idx,chestInfo in pairs(global.oshared.chests) do
|
||||
if (chestInfo.type == "OUTPUT") then
|
||||
|
||||
local chestEntity = chestInfo.entity
|
||||
|
||||
-- Delete any chest that is no longer valid.
|
||||
if ((chestEntity == nil) or (not chestEntity.valid)) then
|
||||
global.oshared.chests[idx] = nil
|
||||
|
||||
-- For each request slot
|
||||
else
|
||||
for i = 1, chestEntity.request_slot_count, 1 do
|
||||
local req = chestEntity.get_request_slot(i)
|
||||
|
||||
-- If there is a request, distribute items
|
||||
if (req ~= nil) then
|
||||
|
||||
-- Make sure requests have been created.
|
||||
-- Make sure shared items exist.
|
||||
if (global.oshared.requests_totals[req.name] ~= nil) and
|
||||
(global.oshared.items[req.name] ~= nil) and
|
||||
(global.oshared.requests[req.name][chestInfo.player] ~= nil) then
|
||||
|
||||
if (global.oshared.requests[req.name][chestInfo.player] > 0)and (global.oshared.items[req.name] > 0) then
|
||||
|
||||
-- How much is already in the chest?
|
||||
local existingAmount = chestEntity.get_inventory(defines.inventory.chest).get_item_count(req.name)
|
||||
-- How much is required to fill the remainder request?
|
||||
local requestAmount = math.max(req.count-existingAmount, 0)
|
||||
-- How much is allowed based on the player's current request amount?
|
||||
local allowedAmount = math.min(requestAmount, global.oshared.requests[req.name][chestInfo.player])
|
||||
|
||||
if (allowedAmount > 0) then
|
||||
local chestInv = chestEntity.get_inventory(defines.inventory.chest)
|
||||
if chestInv.can_insert({name=req.name}) then
|
||||
|
||||
local amnt = chestInv.insert({name=req.name, count=math.min(allowedAmount, global.oshared.items[req.name])})
|
||||
global.oshared.items[req.name] = global.oshared.items[req.name] - amnt
|
||||
global.oshared.requests[req.name][chestInfo.player] = global.oshared.requests[req.name][chestInfo.player] - amnt
|
||||
global.oshared.requests_totals[req.name] = global.oshared.requests_totals[req.name] - amnt
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SharedChestsOnTick()
|
||||
|
||||
-- Every tick we share power
|
||||
SharedEnergyStoreInputOnTick()
|
||||
SharedEnergyDistributeOutputOnTick()
|
||||
|
||||
-- Every second, we check the input chests and deposit stuff.
|
||||
if ((game.tick % (60)) == 37) then
|
||||
SharedChestsDepositAll()
|
||||
end
|
||||
|
||||
-- Every second, we check the output chests for requests
|
||||
if ((game.tick % (60)) == 38) then
|
||||
SharedChestsTallyRequests()
|
||||
end
|
||||
|
||||
-- Every second, we distribute to the output chests.
|
||||
if ((game.tick % (60)) == 39) then
|
||||
SharedChestsDistributeRequests()
|
||||
end
|
||||
|
||||
-- Every second, we update our combinator status info.
|
||||
if ((game.tick % (60)) == 40) then
|
||||
SharedChestsUpdateCombinators()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function CreateSharedItemsGuiTab(tab_container, player)
|
||||
local scrollFrame = tab_container.add{type="scroll-pane",
|
||||
name="sharedItems-panel",
|
||||
direction = "vertical"}
|
||||
ApplyStyle(scrollFrame, my_shared_item_list_fixed_width_style)
|
||||
scrollFrame.horizontal_scroll_policy = "never"
|
||||
|
||||
AddLabel(scrollFrame, "share_items_info", "Place items into the [color=yellow]yellow storage chests to share[/color].\nRequest items from the [color=blue]blue requestor chests to pull out items[/color].\nTo refresh this view, click the tab again.\nShared items are accessible by [color=red]EVERYONE and all teams[/color].\nThe combinator pair allows you to 'set' item types to watch for. Set items in the top one, and connect the bottom one to a circuit network to view the current available inventory. Items with 0 amount do not generate any signal.\nThe special accumulators share energy. The top one acts as an input, the bottom is the output.", my_longer_label_style)
|
||||
|
||||
AddSpacerLine(scrollFrame)
|
||||
|
||||
-- MW charging/discharging rate. (delta change * sample rate per second)
|
||||
local energy_change_add = (global.oshared.energy_stored_history.after_input - global.oshared.energy_stored_history.start)*60/1000000
|
||||
local energy_change_sub = (((global.oshared.energy_stored_history.after_input - global.oshared.energy_stored_history.after_output)*60))/1000000
|
||||
local energy_add_str = string.format("+%.3fMW", energy_change_add)
|
||||
local energy_sub_str = string.format("-%.3fMW", energy_change_sub)
|
||||
local rate_color = "green"
|
||||
if (energy_change_add <= energy_change_sub) then
|
||||
rate_color = "red"
|
||||
elseif (energy_change_add < (energy_change_sub+10)) then
|
||||
rate_color = "orange"
|
||||
end
|
||||
|
||||
AddLabel(scrollFrame, "elec_avail_info", "[color=acid]Current electricity available: " .. string.format("%.3f", global.oshared.energy_stored/1000000) .. "MJ[/color] [color=" .. rate_color .. "](" .. energy_add_str .. " " .. energy_sub_str ..")[/color]", my_longer_label_style)
|
||||
|
||||
AddSpacerLine(scrollFrame)
|
||||
AddLabel(scrollFrame, "share_items_title_msg", "Shared Items:", my_label_header_style)
|
||||
|
||||
local sorted_items = {}
|
||||
for k in pairs(global.oshared.items) do table.insert(sorted_items, k) end
|
||||
table.sort(sorted_items)
|
||||
|
||||
for idx,itemName in pairs(sorted_items) do
|
||||
if (global.oshared.items[itemName] > 0) then
|
||||
local caption_str = "[item="..itemName.."] " .. itemName..": "..global.oshared.items[itemName]
|
||||
AddLabel(scrollFrame, itemName.."_itemlist", caption_str, my_player_list_style)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
105
lib/sharing.lua
Normal file
105
lib/sharing.lua
Normal file
@ -0,0 +1,105 @@
|
||||
-- This handles the shared power logic for the Oarc scenario.
|
||||
-- Won't work too hard on this since 2.0 might change things...
|
||||
|
||||
|
||||
STARTING_X_OFFSET_SHARING_POLE = -5
|
||||
Y_OFFSET_SHARING_POLE = 20
|
||||
|
||||
---Create and connect a pair of power poles for a new base given surface and position.
|
||||
---@param surface LuaSurface
|
||||
---@param position MapPosition
|
||||
---@return nil
|
||||
function CreateSharedPowerPolePair(surface, position)
|
||||
|
||||
if global.shared_power_poles == nil then
|
||||
---@type LuaEntity[]
|
||||
global.shared_power_poles = {}
|
||||
end
|
||||
|
||||
--Get an open sharing pole from the holding pen surface if one exists, otherwise create a new one.
|
||||
local hidden_pole = FindSharedPowerPole()
|
||||
if not hidden_pole then
|
||||
local poles_count = table_size(global.shared_power_poles)
|
||||
local new_position = { x = poles_count + STARTING_X_OFFSET_SHARING_POLE, y = Y_OFFSET_SHARING_POLE }
|
||||
hidden_pole = CreateSpecialPole(game.surfaces[HOLDING_PEN_SURFACE_NAME], new_position)
|
||||
if not hidden_pole then
|
||||
log("ERROR - Failed to create shared power poles!? " .. serpent.block(position) .. " on " .. surface.name)
|
||||
return
|
||||
end
|
||||
table.insert(global.shared_power_poles, hidden_pole)
|
||||
end
|
||||
|
||||
--Create the base pole on the new spawn area surface and connect it to the hidden pole.
|
||||
local base_pole = CreateSpecialPole(surface, position)
|
||||
if not base_pole then
|
||||
log("ERROR - Failed to create shared power poles!? " .. serpent.block(position) .. " on " .. surface.name)
|
||||
return
|
||||
end
|
||||
base_pole.connect_neighbour(hidden_pole)
|
||||
|
||||
TemporaryHelperText(
|
||||
{ "oarc-shared-power-pole-helper-txt" },
|
||||
surface,
|
||||
{position.x, position.y},
|
||||
TICKS_PER_MINUTE*2,
|
||||
"right"
|
||||
)
|
||||
end
|
||||
|
||||
---Find the first shared power pole that doesn't exceed the max number of connections.
|
||||
---@return LuaEntity?
|
||||
function FindSharedPowerPole()
|
||||
if global.shared_power_poles == nil then return nil end
|
||||
|
||||
for _,pole in pairs(global.shared_power_poles) do
|
||||
-- 5 is the hard coded engine limit and we need to leave one open for the connection to the next hidden pole.
|
||||
if pole.neighbours["copper"] and table_size(pole.neighbours["copper"]) < 4 then
|
||||
return pole
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
---Creates a special pole on the surface at the given position on the neutral force.
|
||||
---@param surface LuaSurface
|
||||
---@param position MapPosition
|
||||
---@return LuaEntity?
|
||||
function CreateSpecialPole(surface, position)
|
||||
local pole = surface.create_entity
|
||||
{
|
||||
name="oarc-linked-power",
|
||||
position=position,
|
||||
force="neutral"
|
||||
}
|
||||
pole.destructible = false
|
||||
pole.minable = false
|
||||
pole.rotatable = false
|
||||
return pole
|
||||
end
|
||||
|
||||
---Creates a special linked-chest on the surface at the given position on the neutral force.
|
||||
---@param surface LuaSurface
|
||||
---@param position MapPosition
|
||||
---@return LuaEntity?
|
||||
function CreateSharedChest(surface, position)
|
||||
local chest = surface.create_entity
|
||||
{
|
||||
name="oarc-linked-chest",
|
||||
position=position,
|
||||
force="neutral"
|
||||
}
|
||||
chest.destructible = false
|
||||
chest.minable = false
|
||||
chest.rotatable = false
|
||||
|
||||
TemporaryHelperText(
|
||||
{ "oarc-shared-chest-helper-txt" },
|
||||
surface,
|
||||
{position.x, position.y},
|
||||
TICKS_PER_MINUTE*2,
|
||||
"right"
|
||||
)
|
||||
|
||||
return chest
|
||||
end
|
52
lib/tag.lua
52
lib/tag.lua
@ -1,52 +0,0 @@
|
||||
-- tag.lua
|
||||
-- Apr 2017
|
||||
-- Allows adding play tags
|
||||
|
||||
-- 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 = "[Science!]"},
|
||||
{display_name = "[Logistics]"},
|
||||
{display_name = "[Misc]"},
|
||||
{display_name = "[Aliens]"},
|
||||
{display_name = "[Rocket]"},
|
||||
{display_name = "[AFK]"}}
|
||||
|
||||
function CreateTagGuiTab(tab_container, player)
|
||||
for i,role in ipairs(roles) do
|
||||
tab_container.add{type="button", caption=role.display_name, name=role.display_name}
|
||||
end
|
||||
if (player.admin) then
|
||||
tab_container.add{type="button", caption="[Admin]", name="admin"}
|
||||
tab_container.add{type="button", caption="[Moderator]", name="moderator"}
|
||||
end
|
||||
tab_container.add{type="button", caption="Clear", name="clear_btn"}
|
||||
end
|
||||
|
||||
function TagGuiClick(event)
|
||||
if not (event and event.element and event.element.valid) then return end
|
||||
local player = game.players[event.player_index]
|
||||
local name = event.element.name
|
||||
|
||||
if (name == "clear_btn") then
|
||||
player.tag = ""
|
||||
return
|
||||
end
|
||||
|
||||
for i,role in ipairs(roles) do
|
||||
if (name == role.display_name) then
|
||||
player.tag = role.display_name
|
||||
elseif (name == "admin") then
|
||||
player.tag = "[Admin]"
|
||||
elseif (name == "moderator") then
|
||||
player.tag = "[Moderator]"
|
||||
end
|
||||
end
|
||||
end
|
@ -1,122 +0,0 @@
|
||||
oarc-spawn-time-warning-msg=Aufgrund der Funktionsweise dieses Szenarios kann es einige Zeit dauern, bis das Land um deinen neuen Spawn herum generiert wurde. Bitte warte 10-20 Sekunden, nachdem du deinen Spawn ausgewählt hast.
|
||||
|
||||
oarc-i-understand=Ich verstehe
|
||||
oarc-spawn-options=Spawn Optionen
|
||||
|
||||
oarc-click-info-btn-help=Klick oben links auf die Schaltfläche INFO, um mehr über dieses Szenario zu erfahren! Dies ist deine einzige Chance, eine Spawn-Option zu wählen. Wähle sorgfältig...
|
||||
|
||||
oarc-vanilla-spawn=Vanilla Spawn
|
||||
oarc-default-spawn-behavior=Dies ist das standardmäßige Spawn-Verhalten eines Vanilla-Spiels. Du trittst dem Standardteam in der Mitte der Karte bei.
|
||||
|
||||
oarc-join-main-team-radio=Dem Standardteam beitreten (gemeinsame Forschung)
|
||||
oarc-create-own-team-radio=Erstelle dein eigenes Team (eigene Forschung)
|
||||
|
||||
oarc-moat-option=Umgib deinen Spawn mit einem Graben.
|
||||
|
||||
oarc-solo-spawn-near=Allein spawnen (nah)
|
||||
oarc-solo-spawn-far=Allein spawnen (fern)
|
||||
|
||||
oarc-starting-area-vanilla=Du spawnst in deinem eigenen Startbereich (wie im Vanilla-Spiel).
|
||||
oarc-vanilla-spawns-available=Es steht __1__ Vanilla-Spawn zur Verfügung.
|
||||
|
||||
oarc-starting-area-normal=Du spawnst in einem neuen Gebiet mit voreingestellten Startressourcen.
|
||||
oarc-join-someone-avail=Jemandem beitreten (__1__ verfügbar)
|
||||
oarc-join-someone-info=Du wirst in der Basis von jemand anderem spawnen. Dies erfordert, dass mindestens eine Person Zugang zu seiner Basis gewährt hat. Diese Wahl ist endgültig und du wirst später nicht mehr in der Lage sein, deinen eigenen Spawn zu erstellen.
|
||||
|
||||
oarc-no-shared-avail=Es gibt derzeit keine freigegebenen Basen, in denen man spawnen kann.
|
||||
oarc-join-check-again=Erneut prüfen
|
||||
oarc-shared-spawn-disabled=Gemeinsame Spawns sind in diesem Modus deaktiviert.
|
||||
|
||||
oarc-buddy-spawn=Buddy Spawn
|
||||
oarc-buddy-spawn-info=Das Buddy-System erfordert 2 Spieler in diesem Menü gleichzeitig, sie spawnen nebeneinander, jeder mit seinen eigenen Ressourcen.
|
||||
|
||||
oarc-max-players-shared-spawn=Wenn du deinen eigenen Spawn-Punkt erstellst, kann bis zu __1__ anderer Online-Spieler teilnehmen.
|
||||
oarc-spawn-dist-notes=Der nahe Spawn liegt zwischen __1__-__2__ Chunks von der Mitte der Karte entfernt.\nDer ferne Spawn liegt zwischen __3__-__4__ Chunks von der Mitte der Karte entfernt.\nAlleine Spawnen ist gefährlich! Du wirst kämpfen müssen, um andere Spieler erreichen zu können.
|
||||
|
||||
oarc-player-is-joining-main-force=__1__ tritt dem Standardteam bei!
|
||||
oarc-player-is-joining-near=__1__ nimmt aus naher Entfernung am Spiel teil!
|
||||
oarc-player-is-joining-far=__1__ nimmt aus großer Entfernung am Spiel teil!
|
||||
|
||||
oarc-please-wait=BITTE WARTE, BIS DEIN SPAWN-PUNKT GENERIERT WURDE!
|
||||
|
||||
oarc-looking-for-buddy=__1__ sucht nach einem Buddy.
|
||||
|
||||
oarc-avail-bases-join=Verfügbare Basen zum Beitreten:
|
||||
oarc-spawn-spots-remaining=__1__ (__2__ verbleibende Plätze)
|
||||
oarc-cancel-return-to-previous=Abbrechen (zurück zu den vorherigen Optionen)
|
||||
oarc-player-requesting-join-you=__1__ bittet darum, deiner Basis beizutreten!
|
||||
oarc-waiting-for-spawn-owner=Warte auf die Antwort des Spawn-Besitzers...
|
||||
|
||||
oarc-you-will-spawn-once-host=Du wirst spawnen, sobald der Host ja gewählt hat...
|
||||
oarc-player-cancel-join-request=__1__ hat die Anfrage zum Beitritt zu deinem Spawn abgebrochen.
|
||||
|
||||
oarc-spawn-ctrl=Spawn-Einst.
|
||||
oarc-spawn-controls=Spawn Einstellungen:
|
||||
oarc-spawn-allow-joiners=Erlaube anderen, deiner Basis beizutreten.
|
||||
|
||||
oarc-set-respawn-loc=Setze neuen Spawn-Punkt (1 Stunde Abklingzeit)
|
||||
oarc-set-respawn-loc-cooldown=Set Respawn Cooldown Remaining: __1__
|
||||
oarc-set-respawn-note=Das setzt deinen Spawn-Punkt an deine aktuelle Position.
|
||||
|
||||
oarc-select-player-join-queue=Wähle einen Spieler aus der Beitrittswarteschlange aus:
|
||||
|
||||
oarc-accept=Akzeptieren
|
||||
oarc-reject=Ablehnen
|
||||
|
||||
oarc-no-player-join-reqs=Du hast keine Anfragen von Spielern, deiner Base beizutreten.
|
||||
|
||||
oarc-start-shared-base=Neue Spieler können nun __1__'s Base beitreten!
|
||||
oarc-stop-shared-base=Neue Spieler können nicht länger der Base von __1__ beitreten!
|
||||
|
||||
oarc-spawn-point-updated=Re-Spawn Punkt aktualisiert!
|
||||
oarc-selected-player-not-wait=Der ausgewählte Spieler wartet nicht mehr auf den Beitritt!
|
||||
oarc-reject-joiner=Du lehnst __1__'s Anfrage, deiner Base beizutreten, ab.
|
||||
oarc-your-request-rejected=Deine Anfrage zum Beitritt wurde abgelehnt.
|
||||
|
||||
oarc-player-joining-base=__1__ trat der Base von __2__ bei!
|
||||
|
||||
oarc-player-left-while-joining=__1__ verließ das Spiel. Was für ein Arsch.
|
||||
|
||||
oarc-buddy-spawn-options=Buddy Spawn Optionen
|
||||
oarc-buddy-spawn-instructions=Um dies zu nutzen, vergewissere dich, dass du und dein Buddy zur gleichen Zeit in diesem Menü sind. Nur einer von euch muss die Anfrage senden. Wähle deinen Buddy aus der Liste aus (aktualisiere, wenn der Name deines Buddys nicht sichtbar ist) und wähle deine Spawn-Optionen. Klicke auf eine der Schaltflächen für die Anforderung, um die Anforderung zu senden. Der andere Buddy kann dann die Anfrage annehmen (oder ablehnen). Dies ermöglicht es euch beiden, nebeneinander zu spawnen, jeder mit seinem eigenen Spawn-Bereich. Sobald ein Buddy eine Spawn-Anfrage annimmt, ist sie endgültig!
|
||||
|
||||
oarc-buddy-select-info=Wähle zunächst einen Buddy aus der Warteliste aus. Wähle dann die Spawn-Optionen aus und sende deine Anfrage:
|
||||
|
||||
oarc-buddy-refresh=Buddy-Liste aktualisieren
|
||||
oarc-create-buddy-team=Erstelle dein eigenes Team (geteilte Entwicklung)
|
||||
|
||||
oarc-buddy-spawn-near=Erfrage Buddy-Spawn (nah)
|
||||
oarc-buddy-spawn-far=Erfrage Buddy-Spawn (fern)
|
||||
|
||||
oarc-invalid-buddy=Du hast keinen gültigen Buddy ausgewählt, versuche es noch mal.
|
||||
oarc-buddy-not-avail=Der ausgewählte Buddy ist nicht länger vefügbar, bitte versuche es erneut.
|
||||
|
||||
oarc-waiting-for-buddy=Warte auf die Antwort vom Buddy...
|
||||
oarc-wait-buddy-select-yes=Du wirst spawnen, sobald dein Buddy ja gewählt hat...
|
||||
|
||||
oarc-buddy-cancel-request=__1__ brach die Buddy-Anfrage ab!
|
||||
|
||||
oarc-buddy-requesting-from-you=__1__ fordert einen Buddy Spawn von dir an!
|
||||
|
||||
oarc-buddy-txt-main-team=Das Standardteam
|
||||
oarc-buddy-txt-new-teams=in getrennten Teams
|
||||
oarc-buddy-txt-buddy-team=ein Buddy Team
|
||||
oarc-buddy-txt-moat= umgeben von einem Graben
|
||||
oarc-buddy-txt-near=in der Nähe der Mitte der Karte!
|
||||
oarc-buddy-txt-far=weit weg von der Mitte der Karte!
|
||||
oarc-buddy-txt-would-like= möchte teilnehmen
|
||||
oarc-buddy-txt-next-to-you= neben dir
|
||||
|
||||
oarc-buddy-declined=__1__ hat deine Buddy-Anfrage abgelehnt!
|
||||
|
||||
oarc-spawn-wait=Bitte warte!
|
||||
oarc-wait-text=Dein Spawn wird gerade erstellt. Du wirst in __1__ Sekunden dorthin teleportiert!\nHalte dich bereit...
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
122
locale/en/locale-mod-settings.cfg
Normal file
122
locale/en/locale-mod-settings.cfg
Normal file
@ -0,0 +1,122 @@
|
||||
[mod-description]
|
||||
oarc-mod=This is a multiplayer mod that allows every player to create their own spawn point when they join the game. There are a lot of helpful features to ensure that new players can join at anytime in the game or even join other player's spawn areas.\n\n[color=red][font=default-bold]Please check out the github page and discord for more information and support.[/font][/color]\n\n[font=default-small]This USED to be available as a scenario, but now is only provided as a mod. To start a new game with this mod, just use the default freeplay scenario. The scenario included in this mod only provides a template to overwrite the default freeplay scenario. It also provides a way for experienced server hosts to configure settings from a file instead of through the usual mod settings. Please read the control.lua file inside the scenario for notes.[/font]
|
||||
|
||||
[mod-setting-name]
|
||||
oarc-mod-default-allow-spawning-on-other-surfaces=Default to allow spawns on other surfaces
|
||||
oarc-mod-linked-chest-size=Sharing chest capacity
|
||||
|
||||
oarc-mod-welcome-msg-title=Welcome message title
|
||||
oarc-mod-welcome-msg=Welcome message
|
||||
oarc-mod-discord-invite=Discord Invite
|
||||
|
||||
oarc-mod-enable-main-team=Enable main team
|
||||
oarc-mod-enable-separate-teams=Enable separate teams
|
||||
|
||||
oarc-mod-allow-moats-around-spawns=Allow moats around spawns
|
||||
oarc-mod-enable-moat-bridging=Moat bridges
|
||||
oarc-mod-minimum-distance-to-existing-chunks=Minimum distance to existing chunks
|
||||
oarc-mod-near-spawn-distance=Spawn minimum distance
|
||||
oarc-mod-far-spawn-distance=Spawn maximum distance
|
||||
|
||||
oarc-mod-enable-buddy-spawn=Enable buddy spawn
|
||||
oarc-mod-enable-offline-protection=Offline protection
|
||||
oarc-mod-enable-shared-team-vision=Shared team vision
|
||||
oarc-mod-enable-shared-team-chat=Shared team chat
|
||||
oarc-mod-enable-shared-spawns=Shared spawns
|
||||
oarc-mod-number-of-players-per-shared-spawn=Number of players per shared spawn
|
||||
oarc-mod-enable-friendly-fire=Enable friendly fire
|
||||
|
||||
oarc-mod-main-force-name=Main force name
|
||||
oarc-mod-default-surface=Default starting surface
|
||||
oarc-mod-enable-secondary-spawns=Enable secondary spawns
|
||||
|
||||
oarc-mod-scale-resources-around-spawns=Scale resources around spawns
|
||||
oarc-mod-modified-enemy-spawning=Scale enemies around spawns
|
||||
oarc-mod-modified-enemy-easy-evo=Easy enemy evolution
|
||||
oarc-mod-modified-enemy-medium-evo=Medium enemy evolution
|
||||
oarc-mod-minimum-online-time=Minimum online time
|
||||
oarc-mod-respawn-cooldown-min=Reset respawn point cooldown
|
||||
oarc-mod-enable-shared-power=Shared power
|
||||
oarc-mod-enable-shared-chest=Shared chest
|
||||
|
||||
oarc-mod-enable-regrowth=Regrowth (map cleanup)
|
||||
oarc-mod-enable-world-eater=World eater (map cleanup - additional)
|
||||
oarc-mod-enable-abandoned-base-cleanup=Cleanup abandoned bases
|
||||
oarc-mod-regrowth-cleanup-interval-min=Regrowth cleanup interval
|
||||
|
||||
oarc-mod-spawn-general-radius-tiles=Spawn area radius
|
||||
oarc-mod-spawn-general-moat-width-tiles=Spawn moat width
|
||||
oarc-mod-spawn-general-tree-width-tiles=Spawn tree ring width
|
||||
oarc-mod-spawn-general-enable-resources-circle-shape=Spawn resource deposits shape
|
||||
oarc-mod-spawn-general-enable-force-grass=Force spawn area grass
|
||||
oarc-mod-spawn-general-shape=Spawn area shape
|
||||
|
||||
oarc-mod-resource-placement-enabled=Starting resource auto placement
|
||||
oarc-mod-resource-placement-distance-to-edge=Starting resource distance to edge
|
||||
oarc-mod-resource-placement-angle-offset=Starting resource angle offset
|
||||
oarc-mod-resource-placement-angle-final=Starting resource angle final
|
||||
oarc-mod-resource-placement-vertical-offset=Starting resource vertical offset
|
||||
oarc-mod-resource-placement-horizontal-offset=Starting resource horizontal offset
|
||||
oarc-mod-resource-placement-linear-spacing=Starting resource linear spacing
|
||||
oarc-mod-resource-placement-size-multiplier=Starting resource size multiplier
|
||||
oarc-mod-resource-placement-amount-multiplier=Starting resource amount multiplier
|
||||
|
||||
[mod-setting-description]
|
||||
oarc-mod-default-allow-spawning-on-other-surfaces=This controls the default starting setting for whether to allow spawning on other surfaces. If enabled, by default all other surfaces will be available for players to spawn on. [color=red]If you have other mods installed that add additional surfaces, I recommend leaving this disabled. Regardless of this setting, you can configure which surfaces allow spawning using the in game settings menu.[/color]
|
||||
oarc-mod-linked-chest-size=This is the size of the shared chest that players can use to share items with other players. This is only meaningful if the shared chest feature is enabled.
|
||||
|
||||
oarc-mod-welcome-msg-title=This is the title of the welcome message that will be displayed to players when they join the game.
|
||||
oarc-mod-welcome-msg=This is the welcome message that will be displayed to players when they join the game and in the info panel. [color=red]Leave a single space to disable.[/color]
|
||||
oarc-mod-discord-invite=Place your discord invite here so players can easily copy it from the in game info panel. [color=red]Leave a single space to disable.[/color]
|
||||
|
||||
oarc-mod-enable-main-team=Allow players to join the main team. This is the default team that is created when the game starts. This lets players share research progress.\n[color=red][font=default-bold]You must enable one or both of the main team and separate team options. Otherwise it will default to main team allowed only.[/font][/color]
|
||||
oarc-mod-enable-separate-teams=Allow players to start their own teams (CO-OP only, No PVP). This lets players have their own research progress.\n[color=red][font=default-bold]You must enable one or both of the main team and separate team options. Otherwise it will default to main team allowed only.[/font][/color]
|
||||
|
||||
oarc-mod-allow-moats-around-spawns=Allow players to choose spawns with a moat around them.
|
||||
oarc-mod-enable-moat-bridging=If the spawn area is surrounded by land, the moat will have a small land bridge connecting it.
|
||||
oarc-mod-minimum-distance-to-existing-chunks=This is the minimum distance a new spawn will be created from existing chunks (even if not visible).
|
||||
oarc-mod-near-spawn-distance=The minimum distance a player can choose to spawn from the origin. This is used as a starting point for the search algorithm so is not a guaranteed distance.
|
||||
oarc-mod-far-spawn-distance=The maximum distance a player can choose to spawn from the origin. This is used as a starting point for the search algorithm so is not a guaranteed distance.
|
||||
|
||||
oarc-mod-enable-buddy-spawn=Allow spawning with a buddy. 2 players can spawn next to each other with their own starting areas.
|
||||
oarc-mod-enable-offline-protection=This inhibits enemy attacks on bases where all players are offline. Not 100% guaranteed!
|
||||
oarc-mod-enable-shared-team-vision=Makes sure all teams can see each other's map and radar coverage.
|
||||
oarc-mod-enable-shared-team-chat=All teams can see each other's chat messages.
|
||||
oarc-mod-enable-shared-spawns=Allow players to share their spawn areas for other players to join.
|
||||
oarc-mod-number-of-players-per-shared-spawn=Number of players that can join a shared spawn, including the original player.
|
||||
oarc-mod-enable-friendly-fire=Enables friendly fire. So you can shoot your chests (or run over your friends with a train). This lets you damage your OWN team.
|
||||
|
||||
oarc-mod-main-force-name=The name of the main force. This is the default team that is created when the game starts.
|
||||
oarc-mod-default-surface=The default surface that players will spawn on if they join the main team or if spawning on other surfaces is not enabled.
|
||||
oarc-mod-enable-secondary-spawns=Enabling this will provide players with a secondary spawn point when they first move to a new surface/planet. This is only applicable if the other surface is enabled for separate spawns.
|
||||
|
||||
oarc-mod-scale-resources-around-spawns=This scales resources around every spawn area so far away spawns aren't immediately next to very rich deposits.
|
||||
oarc-mod-modified-enemy-spawning=This scales the enemy spawning globally based on the allowed spawn distances to avoid every spawn being surrounded by behemoth worms.
|
||||
oarc-mod-modified-enemy-easy-evo=This is the maximum evolution that the enemies in the warning zone (closest) to a spawn will be FIXED at to ensure all players have a safe start! The warning zone area can be configured via the custom scenario.
|
||||
oarc-mod-modified-enemy-medium-evo=This is the maximum evolution that the enemies in the danger zone (next closest) to a spawn will be allowed to reach. Helps ensure players have a safe start! The danger zone area can be configured via the custom scenario.
|
||||
oarc-mod-minimum-online-time=The minimum time a player must be online before they leave, otherwise their spawn area will be cleaned up.
|
||||
oarc-mod-respawn-cooldown-min=The minimum time a player must wait before they can change their spawn point to a new location.
|
||||
oarc-mod-enable-shared-power=This allows players to share their power network with other players. It will create a special power pole at their spawn point that connects to the shared power network wirelessly.
|
||||
oarc-mod-enable-shared-chest=This gives players a special chest they can use to share items with other players. It uses the "linked-chest" item that is a hidden feature in factorio.
|
||||
|
||||
oarc-mod-enable-regrowth=Enables regrowth. This slowly removes inactive chunks over time. Any chunk with player structures will not be removed. This helps to keep the map (and file size) smaller over time.
|
||||
oarc-mod-enable-world-eater=Enables world eater. This requires regrowth. This slowly checks all chunks for a lack of player structures and entities and marks them for removal. This helps to keep the map (and file size) smaller over time. This is more of an experimental feature, use at your own risk.
|
||||
oarc-mod-enable-abandoned-base-cleanup=Abandoned bases will be cleaned up if players leave within a short time of joining. [color=red]This does NOT require regrowth or world eater to be enabled.[/color]
|
||||
oarc-mod-regrowth-cleanup-interval-min=This is the interval in minutes that the regrowth cleanup will run. Whenever a map cleanup happens, the game freezes for a second, so you don't want to run this too often! Abandoned base cleanup happens immediately when a player leaves and is not affected by this setting.
|
||||
|
||||
oarc-mod-spawn-general-radius-tiles=This is the radius of the spawn area in tiles. I would not make this much smaller then 2 chunks (64) and not too huge unless you want spawns to take a long time to generate.
|
||||
oarc-mod-spawn-general-moat-width-tiles=This is the width of the moat around the spawn area in tiles, if a moat is enabled and selected at spawn time.
|
||||
oarc-mod-spawn-general-tree-width-tiles=This is the width of the tree ring around the spawn area in tiles. It guarantees some trees near to spawn.
|
||||
oarc-mod-spawn-general-enable-resources-circle-shape=This is the shape of the starting area resource deposits.
|
||||
oarc-mod-spawn-general-enable-force-grass=Enabling this will make the entire spawn area pure grass. Disabling will use landfill as needed instead.
|
||||
oarc-mod-spawn-general-shape=This is the shape of the spawn area.
|
||||
|
||||
oarc-mod-resource-placement-enabled=You should leave this enabled unless you are manually specifying resource placements in the custom scenario!
|
||||
oarc-mod-resource-placement-distance-to-edge=This is the distance from the edge of the spawn area that resources will be placed. Only applicable for circle/octagon shaped spawns.
|
||||
oarc-mod-resource-placement-angle-offset=This is the starting angle offset (in radians) for the resource placement. At what angle (in radians) do resources start. 0 = east. 3.14 = west. Resources are placed clockwise starting at this angle. Only applicable for circle/octagon shaped spawns.
|
||||
oarc-mod-resource-placement-angle-final=This is the final angle offset (in radians) for the resource placement. At what angle (in radians) do resources end. 0 = east. 3.14 = west. Resources are placed clockwise ending at this angle. Only applicable for circle/octagon shaped spawns.
|
||||
oarc-mod-resource-placement-vertical-offset=This is the vertical offset (in tiles) for the resource placement from the top-left of the spawn. Only applicable for square shaped spawns.
|
||||
oarc-mod-resource-placement-horizontal-offset=This is the horizontal offset (in tiles) for the resource placement from the top-left of the spawn. Only applicable for square shaped spawns.
|
||||
oarc-mod-resource-placement-linear-spacing=This is the linear spacing (in tiles) between resources. Only applicable for square shaped spawns.
|
||||
oarc-mod-resource-placement-size-multiplier=This changes the size of the resource deposits. Default settings should provide close to a vanilla starting experience.
|
||||
oarc-mod-resource-placement-amount-multiplier=This changes the richness of the resource deposits. Default settings should provide close to a vanilla starting experience.
|
@ -1,53 +1,86 @@
|
||||
scenario-name=Oarc's Multiplayer Spawning Scenario
|
||||
scenario-name=OARC
|
||||
|
||||
description=This scenario allows every player to create their own spawn point when they join the game. There are a lot of helpful features to ensure that new players can join at anytime in the game or even join other players. This is a multiplayer only scenario.\n[color=red][font=default-bold]You must first create a config.lua file if one does not yet exist![/font][/color]\nIn the scenario folder, copy "example-config.lua" and name it "config.lua". Edit the file with any text editor. This is the only way to configure your scenario options and it MUST be done before starting a new game.\n[color=yellow][font=default-bold]Always start a new game when changing the config or updating the scenario![/font][/color]\nPlease visit https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn for the scenario source code, Wiki, and if you want to file any bugs. The wiki has instructions on how to best start games on dedicated/headless servers!\n[color=yellow][font=default-bold]If you got this scenario from the mod portal or by joining a server, make sure you remove any blueprint.zip files in the scenario folder![/font][/color]
|
||||
description=This mod provides a scenario that overhauls multiplayer spawning.\n[color=red][font=default-bold]Please check out the github page and discord for more information and support.[/font][/color]
|
||||
|
||||
oarc-spawn-time-warning-msg=Due to the way this scenario works, it may take some time for the land around your new spawn area to generate... Please wait for 10-20 seconds when you select your first spawn.
|
||||
oarc-scenario-info-warn-msg=This scenario allows players to create their own spawn points far from the center of the map. For more information, click the OARC button in the top left corner.
|
||||
|
||||
oarc-i-understand=I Understand
|
||||
oarc-spawn-options=Spawn Options
|
||||
|
||||
oarc-click-info-btn-help=Click the INFO button in the top left to learn more about this scenario! This is your ONLY chance to choose a spawn option. Choose carefully...
|
||||
oarc-click-info-btn-help=Click the OARC button in the top left to learn more about this scenario! This is your ONLY chance to choose a spawn option. Choose carefully...
|
||||
oarc-gui-tooltip=Click to open and close the mod gui. This provides settings and information about the mod.
|
||||
|
||||
oarc-vanilla-spawn=Vanilla Spawn
|
||||
oarc-default-spawn-behavior=This is the default spawn behavior of a vanilla game. You join the default team in the center of the map.
|
||||
|
||||
oarc-surface-select-tooltip=Select the surface you would like to spawn on. (Disabled if only one surface is available.)
|
||||
|
||||
oarc-join-main-team-radio=Join Main Team (shared research)
|
||||
oarc-join-main-team-tooltip=You will share research progress with other players on the main team.
|
||||
|
||||
oarc-create-own-team-radio=Create Your Own Team (own research tree)
|
||||
oarc-create-own-team-tooltip=You will have your own research tree and will not share research with players on the main team.
|
||||
|
||||
oarc-moat-option=Surround your spawn with a moat
|
||||
oarc-moat-option-tooltip=If you spawn near land, a ring of water will be generated around your spawn area. Depending on the mod options, there may be a small land bridge created as well. If you spawn in water already, this option has no effect.
|
||||
|
||||
oarc-solo-spawn-near=Solo Spawn (Near)
|
||||
oarc-solo-spawn-far=Solo Spawn (Far)
|
||||
oarc-solo-spawn=Create Your Own Spawn
|
||||
oarc-solo-spawn-tooltip=You will spawn in your own starting area with pre-set starting resources.
|
||||
|
||||
oarc-starting-area-vanilla=You are spawned in your own starting area (vanilla style).
|
||||
oarc-vanilla-spawns-available=There are __1__ vanilla spawns available.
|
||||
|
||||
oarc-spawn-menu-settings-info=These settings apply when creating a new spawn. They do not have any effect if you are joining a shared spawn.
|
||||
oarc-spawn-menu-settings-header=Spawn Settings
|
||||
oarc-spawn-menu-solo-header=Create Your Own Spawn
|
||||
oarc-spawn-menu-shared-header=Join Another Spawn
|
||||
oarc-spawn-menu-buddy-header=Create A Buddy Spawn
|
||||
|
||||
oarc-spawn-distance-slider-label=Distance From Map Center:
|
||||
oarc-spawn-distance-slider-tooltip=This is the minimum distance from the center of the map in chunks that you will spawn. You may spawn further away if the map has already been explored. Resources and enemies are both more abundant the further you go from the center, but you will still be guaranteed a safe starting area.
|
||||
|
||||
oarc-starting-area-normal=You are spawned in a new area, with pre-set starting resources.
|
||||
oarc-join-someone-avail=Join Someone (__1__ available)
|
||||
oarc-join-someone-info=You are spawned in someone else's base. This requires at least 1 person to have allowed access to their base. This choice is final and you will not be able to create your own spawn later.
|
||||
oarc-join-someone-dropdown-label=Select Shared Spawn:
|
||||
oarc-join-someone-dropdown-tooltip=Select a player to REQUEST to join their shared spawn area.
|
||||
oarc-join-shared-button-enable=Join (__1__ on __2__)
|
||||
oarc-join-shared-button-disable=Select A Player First!
|
||||
oarc-join-shared-button-tooltip=This will send a request to join the selected player's shared spawn area. You can cancel the request if you change your mind.
|
||||
oarc-join-someone-info=You can request to join someone else's starting area. This requires at least 1 person to have allowed access to their base and to have space for you to join.
|
||||
|
||||
oarc-no-shared-avail=There are currently no shared bases availble to spawn at.
|
||||
oarc-join-check-again=Check Again
|
||||
oarc-shared-spawn-disabled=Shared spawns are disabled in this mode.
|
||||
oarc-no-shared-spawn-selected=You have not selected a valid player to join. Please select one from the list.
|
||||
oarc-invalid-host-shared-spawn=Selected player is no longer available! Please try again.
|
||||
|
||||
oarc-buddy-spawn=Buddy Spawn
|
||||
oarc-buddy-spawn=Request Buddy Spawn
|
||||
oarc-buddy-spawn-tooltip=This will send the request to your selected buddy. They may choose to accept or deny the request. You may also cancel the request if you change your mind.
|
||||
oarc-buddy-spawn-disabled=Buddy spawns are disabled in this mode.
|
||||
oarc-buddy-spawn-info=The buddy system requires 2 players in this menu at the same time, you spawn beside each other, each with your own resources.
|
||||
|
||||
oarc-max-players-shared-spawn=If you create your own spawn point you can allow up to __1__ other online players to join.
|
||||
oarc-spawn-dist-notes=Near spawn is between __1__-__2__ chunks away from the center of the map.\nFar spawn is between __3__-__4__ chunks away from the center of the map.\nSolo spawns are dangerous! Expect a fight to reach other players.
|
||||
oarc-max-players-shared-spawn=You can allow up to __1__ other players to join you.
|
||||
oarc-spawn-dist-notes=Spawn distance is in CHUNKS from the center of the map.\nExpect a fight to reach other players.
|
||||
|
||||
oarc-player-is-joining-main-force=__1__ is joining the main force!
|
||||
oarc-player-is-joining-near=__1__ is joining the game from a distance!
|
||||
oarc-player-is-joining-far=__1__ is joining the game from a great distance!
|
||||
oarc-player-is-joining=__1__ is joining the game on __2__!
|
||||
oarc-player-started-own-team=__1__ has started their own team!
|
||||
oarc-player-no-new-teams-sorry=Sorry, no new teams can be created. You were assigned to the default team instead.
|
||||
oarc-no-ungenerated-land-error=[color=red]Failed to find ungenerated land to spawn on! Please try again. If you see this message multiple times, it could be a settings or mod conflict issue.[/color]
|
||||
oarc-buddies-are-joining=__1__ and __2__ are joining the game together on __3__!
|
||||
oarc-player-new-secondary=__1__ has started a secondary base on __2__!
|
||||
|
||||
oarc-please-wait=PLEASE WAIT WHILE YOUR SPAWN POINT IS GENERATED!
|
||||
oarc-player-left-early=Player (__1__) left the game before __2__ minutes of play time. Their spawn area has been marked for cleanup.
|
||||
oarc-generating-spawn-please-wait=Generating your spawn now, please wait...
|
||||
oarc-host-left-new-host=__1__ has left so __2__ now owns their base.
|
||||
oarc-new-owner-msg=You have been given ownership of this base!
|
||||
|
||||
|
||||
oarc-looking-for-buddy=__1__ is looking for a buddy.
|
||||
|
||||
oarc-avail-bases-join=Available Bases to Join:
|
||||
oarc-spawn-spots-remaining=__1__ (__2__ spots remaining)
|
||||
oarc-cancel-return-to-previous=Cancel (Return to Previous Options)
|
||||
oarc-cancel-button-caption=Cancel
|
||||
oarc-return-to-previous-tooltip=Return to the previous menu.
|
||||
oarc-player-requesting-join-you=__1__ is requesting to join your base!
|
||||
oarc-waiting-for-spawn-owner=Waiting for spawn owner to respond...
|
||||
|
||||
@ -55,18 +88,39 @@ oarc-you-will-spawn-once-host=You will spawn once the host selects yes...
|
||||
oarc-player-cancel-join-request=__1__ cancelled their request to join your spawn.
|
||||
|
||||
oarc-spawn-ctrl=Spawn Ctrl
|
||||
oarc-spawn-controls=Spawn Controls:
|
||||
oarc-spawn-allow-joiners=Allow others to join your base.
|
||||
oarc-primary-spawn-info-header=Primary Spawn Information
|
||||
oarc-primary-spawn-info-note=This is your home base location. If you let other players join your base, they will spawn here.
|
||||
oarc-primary-spawn-info-surface-label=Home: __1__ (x=__2__, y=__3__)
|
||||
|
||||
oarc-set-respawn-loc=Set New Respawn Location (has a cooldown)
|
||||
oarc-secondary-spawn-info-header=Secondary Spawn Information
|
||||
oarc-secondary-spawn-info-note=These are your secondary base locations.
|
||||
oarc-secondary-spawn-info-surface-label=Secondary: __1__ (x=__2__, y=__3__)
|
||||
|
||||
oarc-spawn-info-location-button=Show Map Location
|
||||
oarc-spawn-info-location-button-tooltip=Click to show the location on the map.
|
||||
|
||||
|
||||
oarc-spawn-gps-location=Location:
|
||||
oarc-shared-spawn-controls=Shared Spawn Controls
|
||||
oarc-shared-spawn-allow-joiners=Allow others to join your base.
|
||||
oarc-shared-spawn-full=Your base is full! No more players can join.
|
||||
|
||||
oarc-set-respawn-loc-header=Respawn Location
|
||||
oarc-set-respawn-loc-info-surface-label=Current Surface Respawn: __1__ (x=__2__, y=__3__)
|
||||
oarc-set-respawn-loc=Set Respawn Location
|
||||
oarc-set-respawn-loc-tooltip=This will set your respawn point to your current location. There is a cooldown before you can change it again.
|
||||
oarc-set-respawn-loc-cooldown=Set Respawn Cooldown Remaining: __1__
|
||||
oarc-set-respawn-note=This will set your respawn point to your current location.
|
||||
oarc-set-respawn-note=This will set your respawn point to your current location. Change tabs to update the timer.
|
||||
|
||||
oarc-join-queue-header=Shared Spawn Queue
|
||||
oarc-select-player-join-queue=Select a player from the join queue:
|
||||
|
||||
oarc-accept=Accept
|
||||
oarc-reject=Reject
|
||||
|
||||
oarc-enabled=enabled
|
||||
oarc-disabled=disabled
|
||||
|
||||
oarc-no-player-join-reqs=You have no players requesting to join you at this time.
|
||||
|
||||
oarc-start-shared-base=New players can now join __1__'s base!
|
||||
@ -74,20 +128,24 @@ oarc-stop-shared-base=New players can no longer join __1__'s base!
|
||||
|
||||
oarc-spawn-point-updated=Re-spawn point updated!
|
||||
oarc-selected-player-not-wait=Selected player is no longer waiting to join!
|
||||
oarc-selected-player-not-valid=Please select a valid player!
|
||||
oarc-reject-joiner=You rejected __1__'s request to join your base.
|
||||
oarc-your-request-rejected=Your request to join was rejected.
|
||||
|
||||
oarc-player-joining-base=__1__ is joining __2__'s base!
|
||||
|
||||
oarc-player-left-while-joining=__1__ left the game. What an ass.
|
||||
|
||||
oarc-buddy-select-label=Select Buddy:
|
||||
oarc-buddy-select-tooltip=Select a buddy from the list to request a buddy spawn.
|
||||
oarc-buddy-spawn-options=Buddy Spawn Options
|
||||
oarc-buddy-spawn-instructions=To use this, make sure you and your buddy are in this menu at the same time. Only one of you must send the request. Select your buddy from the list (refresh if your buddy's name is not visible) and select your spawn options. Click one of the request buttons to send the request. The other buddy can then accept (or deny) the request. This will allow you both to spawn next to each other, each with your own spawn area. Once a buddy accepts a spawn request, it is final!
|
||||
oarc-buddy-spawn-instructions=You and your buddy must be in this menu at the same time. Only one of you can send the request. Select your your spawn options first, then select your buddy from the list. Click Request Buddy Spawn. The other buddy can then accept (or deny) the request. This will allow you both to spawn next to each other, each with your own spawn area.
|
||||
|
||||
oarc-buddy-select-info=First, select a buddy from the waiting list. Then choose the spawn options and send your request:
|
||||
|
||||
oarc-buddy-spawn-request-header=Buddy Request!
|
||||
|
||||
oarc-buddy-refresh=Refresh Buddy List
|
||||
oarc-create-buddy-team=Create Your Own Buddy Team (buddy and you share research)
|
||||
oarc-create-buddy-team-tooltip=If this is enabled, you and your buddy will share research progress.
|
||||
|
||||
oarc-buddy-spawn-near=Request Buddy Spawn (Near)
|
||||
oarc-buddy-spawn-far=Request Buddy Spawn (Far)
|
||||
@ -102,16 +160,100 @@ oarc-buddy-cancel-request=__1__ cancelled their buddy request!
|
||||
|
||||
oarc-buddy-requesting-from-you=__1__ is requesting a buddy spawn from you!
|
||||
|
||||
# [BUDDY NAME] oarc-buddy-txt-would-like {team choice} oarc-buddy-txt-next-to-you {moat choice} {surface choice} {distance choice}
|
||||
oarc-buddy-txt-would-like= would like to join
|
||||
oarc-buddy-txt-main-team=the main team
|
||||
oarc-buddy-txt-new-teams=on separate teams
|
||||
oarc-buddy-txt-buddy-team=a buddy team
|
||||
oarc-buddy-txt-moat= surrounded by a moat
|
||||
oarc-buddy-txt-near=near to the center of the map!
|
||||
oarc-buddy-txt-far=far from the center of the map!
|
||||
oarc-buddy-txt-would-like= would like to join
|
||||
oarc-buddy-txt-next-to-you= next to you
|
||||
oarc-buddy-txt-moat= surrounded by a moat
|
||||
oarc-buddy-txt-surface= on __1__
|
||||
oarc-buddy-txt-distance= at a distance of __1__ chunks from the center of the map!
|
||||
|
||||
|
||||
oarc-buddy-declined=__1__ declined your buddy request!
|
||||
|
||||
oarc-spawn-wait=Please wait!
|
||||
oarc-wait-text=Your spawn is being created now.\nYou will be teleported there in __1__ seconds!\nPlease standby...
|
||||
oarc-wait-text=Your spawn is being created now.\nYou will be teleported there in __1__ seconds!\nPlease standby...
|
||||
|
||||
oarc-gui-tab-header-label=Scenario Info and Controls
|
||||
oarc-server-info-tab-title=Server Info
|
||||
oarc-spawn-ctrls-tab-title=Spawn Controls
|
||||
oarc-settings-tab-title=Settings
|
||||
oarc-mod-info-tab-title=Mod Info
|
||||
|
||||
oarc-server-info-tab-welcome-msg-title=Welcome Message
|
||||
oarc-server-info-tab-discord-invite=Discord Invite:
|
||||
oarc-server-info-tab-discord-invite-tooltip=Come join the discord (copy this invite)!
|
||||
oarc-server-info-tab-map-info-label=Map Info
|
||||
oarc-server-info-tab-server-run-time=Server Run Time: __1__
|
||||
oarc-server-info-leave-warning=If you leave within __1__ minutes of joining, your base and character will be deleted.
|
||||
oarc-server-info-admin-controls=Admin Controls
|
||||
oarc-server-info-ban-select-player=Select Player:
|
||||
oarc-server-info-button-ban-player=Ban Player
|
||||
oarc-server-info-button-restart-player=Restart Player
|
||||
|
||||
oarc-player-not-found=Player __1__ is not found?
|
||||
oarc-player-about-to-spawn=Player __1__ is about to spawn, try again later.
|
||||
oarc-player-none-selected=No player selected!
|
||||
|
||||
|
||||
oarc-settings-tab-title-mod-settings=Mod Settings
|
||||
oarc-settings-tab-admin-warning=You are an admin. Changing these settings late in the game may cause issues!\nChanging settings will not modify existing spawns. BE CAREFUL!
|
||||
oarc-settings-tab-player-warning=You are not an admin. These settings are read-only for you.
|
||||
oarc-settings-tab-description=This tab contains the same mod settings in the mod settings menu AND some additional settings that can only be changed in game.
|
||||
oarc-settings-tab-text-field-enter-tooltip=[color=red]You must press ENTER after typing in a text field to save the value![/color]
|
||||
oarc-settings-tab-title-surface=Surface Settings
|
||||
oarc-settings-tab-surface-checkbox-tooltip=Enabling this will allow custom spawn areas (the main feature of this mod) on this surface. You need at least one of these enabled for the mod to work.
|
||||
oarc-settings-tab-surface-regrowth-checkbox-tooltip=Enabling this will allow the regrowth and world eater features to work on this surface, if those are enabled.
|
||||
oarc-settings-tab-surface-column-header=Surface
|
||||
oarc-settings-tab-surface-spawning-enabled=Custom Spawns
|
||||
oarc-settings-tab-surface-regrowth-enabled=Regrowth
|
||||
|
||||
|
||||
oarc-settings-section-header-server-info=Server Info
|
||||
oarc-settings-section-header-gameplay=Gameplay
|
||||
oarc-settings-section-header-regrowth=Regrowth
|
||||
oarc-settings-section-header-general-spawn=General Spawn Area Config
|
||||
oarc-settings-section-header-resource-placement=Starting Resources Placement
|
||||
|
||||
oarc-settings-section-subheader-spawn-choices=Spawn choices
|
||||
oarc-settings-section-subheader-difficulty-scaling=Enemy and resource scaling
|
||||
oarc-settings-section-subheader-gameplay-misc=Misc
|
||||
oarc-settings-section-subheader-sharing=Sharing and co-op
|
||||
|
||||
oarc-settings-section-subheader-resource-placement-circular=For circle/octagon spawns
|
||||
oarc-settings-section-subheader-resource-placement-square=For square spawns
|
||||
|
||||
oarc-settings-section-subheader-regrowth-warning=These features can help reduce save file size but can be more UPS intensive.\n
|
||||
|
||||
oarc-shared-power-pole-helper-txt=This connects to a shared electric network for all players.
|
||||
oarc-shared-chest-helper-txt=This connects to a shared storage chest for all players.
|
||||
|
||||
oarc-mod-faq-what-is-this-mod=What is this mod?
|
||||
oarc-mod-faq-what-is-this-mod-answer=This mod overhauls multiplayer spawning. Players can create their own spawn points away from the center of the map. Players can also join other players' bases, or create buddy spawns. The mod does not change the core gameplay of Factorio, but it does change the way players start the game. The starting area is a preset area with resources, not a "natural" spawn point.
|
||||
oarc-mod-faq-other-surfaces=Can I start on a different surface/planet?
|
||||
oarc-mod-faq-other-surfaces-answer=[color=red]This feature is currently a work in progress. As such, only spawning on "nauvis" or a default nauvis-like surface will work correctly.[/color]
|
||||
oarc-mod-faq-secondary-spawns=What are secondary spawns?
|
||||
oarc-mod-faq-secondary-spawns-answer=If enabled, when a player first travels to a new surface that has custom spawning enabled, they will automatically be given a new starting area based on their primary spawn choice (the first spawn they create). [color=red]This feature is currently a work in progress. Secondary spawning on other surfaces may cause issues. Buddy and shared spawns are not implemented yet at this time.[/color]
|
||||
oarc-mod-faq-what-are-teams=What are the different team options?
|
||||
oarc-mod-faq-what-are-teams-answer=I wrote this mod for co-op play only, not pvp. Depending on the mod settings, you can either join the main team, which shares research, or create your own team, which has its own research tree. All teams/forces are friendly to each other and can communicate in chat and (optionally) share map vision. The only reason to create your own team is if you want to have your own research progress.
|
||||
oarc-mod-faq-shared-spawn=What is a shared spawn?
|
||||
oarc-mod-faq-shared-spawn-answer=Players can choose to allow other players to join their base. This allows new players to spawn in the same starting area as the host player. The host player can control who can join their base.
|
||||
oarc-mod-faq-buddy-spawn=What is a buddy spawn?
|
||||
oarc-mod-faq-buddy-spawn-answer=If you and another player are in the spawn menu at the same time, you can request a buddy spawn. This will spawn you and your buddy right next to each other on the map, each with your own starting area.
|
||||
oarc-mod-faq-regrowth=What are the regrowth and world eater features?
|
||||
oarc-mod-faq-regrowth-answer="Regrowth" helps keep the save file size down by removing inactive map chunks over time. This is useful for long-running servers with many players. No chunks with player activity will be removed. "World Eater" slowly checks every chunk to see if there are any player structures in it. This is an additional check on top of the regrowth feature that will catch any chunks that players built on, but then later removed all structures from.
|
||||
oarc-mod-faq-cleanup-abandoned=What is the cleanup abandoned bases feature?
|
||||
oarc-mod-faq-cleanup-abandoned-answer=This removes the spawn area if the player leaves within the "minimum online time" and will also delete their character. This feature is useful for keeping the map clean and the save file size down. Public servers likely want to enable this feature.
|
||||
oarc-mod-faq-offline-protection=What is the offline protection feature?
|
||||
oarc-mod-faq-offline-protection-answer=If a base has all players offline, it will be protected from enemy attacks. This is not 100% guaranteed, but it will help prevent the base from being destroyed while players are offline.
|
||||
oarc-mod-faq-shared-power=What is the shared power feature?
|
||||
oarc-mod-faq-shared-power-answer=This allows players to share their power network with other players. It will create a special power pole at their spawn point that connects to the shared power network wirelessly. It utilizes cross-surface power poles to make "wireless" connections.
|
||||
oarc-mod-faq-shared-chest=What is the shared chest feature?
|
||||
oarc-mod-faq-shared-chest-answer=This gives players a special chest at their spawn area that they can use to share items with other players. It uses the "linked-chest" item that is a hidden feature in Factorio. This allows players to share items with other players in a secure way.
|
||||
|
||||
|
||||
[entity-name]
|
||||
oarc-linked-chest=OARC Linked Chest
|
||||
oarc-linked-power=OARC Linked Power
|
@ -1,113 +0,0 @@
|
||||
oarc-spawn-time-warning-msg=En raison de la manière de fonctionnement de ce scénario, la génération de votre base peut prendre un certain temps ... Veuillez patienter 10 à 20 secondes lorsque vous apparaissez.
|
||||
|
||||
oarc-i-understand=Je comprends
|
||||
oarc-spawn-options=Options d'apparition
|
||||
|
||||
oarc-click-info-btn-help=Cliquez sur le bouton INFO en haut à gauche pour en savoir plus sur ce scénario! Ceci est votre SEULE chance de choisir une option de spawn. Choisissez soigneusement...
|
||||
|
||||
oarc-vanilla-spawn=Spawn vanillia
|
||||
oarc-default-spawn-behavior=C’est le comportement par défaut du spawn d’un jeu vanillia. Vous rejoignez l'équipe par défaut au centre de la carte.
|
||||
|
||||
oarc-join-main-team-radio=Rejoindre l'équipe principale (recherche partagée)
|
||||
oarc-create-own-team-radio=Créez votre propre équipe (propre arbre de recherche)
|
||||
|
||||
oarc-moat-option=Entourez votre base d'eau (fossé)
|
||||
|
||||
oarc-solo-spawn-near=Spawn solo (proche)
|
||||
oarc-solo-spawn-far=Spawn solo (Loin)
|
||||
|
||||
oarc-starting-area-vanilla=Vous êtes apparu dans votre propre base (style vanilla).
|
||||
oarc-vanilla-spawns-available=Il y a __1__ une base vanilla disponible.
|
||||
|
||||
oarc-starting-area-normal=Vous êtes apparu dans une nouvelle base, avec des ressources de départ prédéfinies.
|
||||
oarc-join-someone-avail=Rejoindre quelqu'un (__1__ disponible)
|
||||
oarc-join-someone-info=Vous rejoindrez la base de quelqu'un d'autre. Cela nécessite que au moins 1 personne autorise l'accès à leur base. Ce choix est définitif et vous ne pourrez pas créer votre propre zone plus tard.
|
||||
|
||||
oarc-no-shared-avail=Il n’existe actuellement aucune base partagée disponible.
|
||||
oarc-join-check-again=Vérifier à nouveau
|
||||
oarc-shared-spawn-disabled=Les spawns partagés sont désactivés dans ce mode.
|
||||
|
||||
oarc-buddy-spawn=Spawn avec un ami
|
||||
oarc-buddy-spawn-info=Se système d'amis nécessite 2 joueurs dans ce menu en même temps, vous apparaissez côte à côte, chacun avec vos propres ressources.
|
||||
|
||||
oarc-max-players-shared-spawn=Si vous créez votre propre point d'apparition, vous pouvez autoriser jusqu'à __1__ autres joueurs en ligne à rejoindre.
|
||||
oarc-spawn-dist-notes=Le point d’apparition (proche) se situe entre __1__-__2__ morceaux du centre de la carte. \nLe point d’apparition (loin) se situe entre __3__-__4__ morceaux du centre de la carte. \nLes apparitions solo sont dangereuses! Attendez-vous à un combat pour atteindre d'autres joueurs.
|
||||
|
||||
oarc-player-is-joining-main-force=__1__ rejoint l'équipe principale'!
|
||||
oarc-player-is-joining-near=__1__ rejoint le jeu à distance!
|
||||
oarc-player-is-joining-far=__1__ rejoint le jeu avec une grande distance!
|
||||
|
||||
oarc-please-wait=VEUILLEZ ATTENDRE QUE VOTRE POINT D'APPARITION SOIT GÉNÉRÉ!
|
||||
|
||||
oarc-looking-for-buddy=__1__ est à la recherche d'un copain.
|
||||
|
||||
oarc-avail-bases-join=Bases disponibles à rejoindre:
|
||||
oarc-spawn-spots-remaining=__1__ (__2__ places restantes)
|
||||
oarc-cancel-return-to-previous=Annuler (retourner aux options précédentes)
|
||||
oarc-player-requesting-join-you=__1__ demande à rejoindre votre base!
|
||||
oarc-waiting-for-spawn-owner=Attendez que le propriétaire de la base réponde ...
|
||||
|
||||
oarc-you-will-spawn-once-host=Vous apparaîtrez une fois que l'hôte aura choisi oui ...
|
||||
oarc-player-cancel-join-request=__1__ a annulé sa demande pour rejoindre votre base.
|
||||
|
||||
oarc-spawn-ctrl=Spawn Ctrl
|
||||
oarc-spawn-controls=Contrôle d'apparition':
|
||||
oarc-spawn-allow-joiners=Autoriser les autres à rejoindre votre base.
|
||||
|
||||
oarc-set-respawn-loc=Définir le nouvel emplacement de réapparition (il y a une heure d'attente pour réutiliser ce boutton)
|
||||
oarc-set-respawn-loc-cooldown=Définir le temps d'attente pour réapparaître restant: __1__
|
||||
oarc-set-respawn-note=Cela va définir votre point de réapparition à votre position actuelle.
|
||||
|
||||
oarc-select-player-join-queue=Sélectionnez un joueur dans la file d'attente:
|
||||
|
||||
oarc-accept=Accepter
|
||||
oarc-reject=Rejeter
|
||||
|
||||
oarc-no-player-join-reqs=Vous n'avez aucun joueur demandant à vous rejoindre pour le moment.
|
||||
|
||||
oarc-start-shared-base=Les nouveaux joueurs peuvent maintenant rejoindre la base de __1__!
|
||||
oarc-stop-shared-base=Les nouveaux joueurs ne peuvent plus rejoindre la base de __1__!
|
||||
|
||||
oarc-spawn-point-updated=Point de réapparition mis à jour!
|
||||
oarc-selected-player-not-wait=Le joueur sélectionné n'attend plus de rejoindre!
|
||||
oarc-reject-joiner=Vous avez rejeté la demande de __1__ pour rejoindre votre base.
|
||||
oarc-your-request-rejected=Votre demande d'adhésion a été rejetée.
|
||||
|
||||
oarc-player-joining-base=__1__ à rejoint la base de __2__!
|
||||
|
||||
oarc-player-left-while-joining=__1__ a quitté le match. Quel âne.
|
||||
|
||||
oarc-buddy-spawn-options=Options d'apparition du menu (Spawn avec un ami)
|
||||
oarc-buddy-spawn-instructions=Pour utiliser ceci, assurez-vous que votre ami et vous êtes dans ce menu en même temps. Un seul d'entre vous doit envoyer la demande. Sélectionnez votre ami dans la liste (actualisez-la si son nom n'est pas visible) et sélectionnez vos options d'apparition. Cliquez sur l'un des boutons de requête pour envoyer la requête. L'autre copain peut alors accepter (ou refuser) la demande. Cela vous permettra à tous les deux d'apparaître côte à côte, chacun avec votre propre base. Une fois que votre copain aura accepté une demande de spawn, c'est définitif!
|
||||
|
||||
oarc-buddy-select-info=D'abord, sélectionnez un ami dans la liste d'attente. Puis choisissez les options de spawn et envoyez votre demande:
|
||||
|
||||
oarc-buddy-refresh=Actualiser la liste d'amis
|
||||
oarc-create-buddy-team=Créez votre propre équipe d'amis (vous et votre ami partagez la recherche)
|
||||
|
||||
oarc-buddy-spawn-near=Demander la création de la base (proche)
|
||||
oarc-buddy-spawn-far=Demander la création de la base (loin)
|
||||
|
||||
oarc-invalid-buddy=Vous n'avez pas sélectionné de partenaire valide! Veuillez réessayer.
|
||||
oarc-buddy-not-avail=Le copain sélectionné n'est plus disponible! Veuillez réessayer.
|
||||
|
||||
oarc-waiting-for-buddy=Attendez que votre ami réponde ...
|
||||
oarc-wait-buddy-select-yes=Vous apparaîtrez une fois que votre ami aura choisi oui ...
|
||||
|
||||
oarc-buddy-cancel-request=__1__ a annulé sa demande!
|
||||
|
||||
oarc-buddy-requesting-from-you=__1__ vous demande a appraître avec lui!
|
||||
|
||||
oarc-buddy-txt-main-team=l'équipe principale
|
||||
oarc-buddy-txt-new-teams=sur des équipes séparées
|
||||
oarc-buddy-txt-buddy-team=une équipe de amis
|
||||
oarc-buddy-txt-moat=entouré d'eau (fossé)
|
||||
oarc-buddy-txt-near=proche du centre de la carte!
|
||||
oarc-buddy-txt-far=loin du centre de la carte!
|
||||
oarc-buddy-txt-would-like=aimerait rejoindre
|
||||
oarc-buddy-txt-next-to-you=à côté de toi
|
||||
|
||||
oarc-buddy-declined=__1__ a refusé votre demande d'ami!
|
||||
|
||||
oarc-spawn-wait=Patienter s'il vous plaît!
|
||||
oarc-wait-text=Votre spawn est en cours de création. \nVous serez téléporté dans __1__ secondes! \nVeuillez patienter ...
|
@ -1,117 +0,0 @@
|
||||
scenario-name=Oarc多人游戏场景。
|
||||
|
||||
description=该场景允许每个玩家在加入游戏时创建自己的生成点。这里有许多便利功能,确保新玩家可以随时加入游戏或加入其他玩家的队伍。这是一个仅限多人游戏的场景。\n[color=red][font=default-bold]首先您必须创建一个config.lua文件,如果还没有的话![/font][/color]\n在场景文件夹内,复制"example-config.lua"并将其命名为"config.lua"。用任何文本编辑器编辑该文件。这是唯一配置场景选项的方法,必须在开始新游戏之前完成。\n[color=yellow][font=default-bold]更改配置或更新场景时,请始终开始新游戏![/font][/color]\n请访问https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn获取场景源代码、Wiki以及提交任何错误报告。Wiki上有关于如何在专用/无头服务器上最佳开始游戏的说明。\n[color=yellow][font=default-bold]如果你是从模组门户或通过加入服务器得到这个场景的,确保在场景文件夹中删除任何blueprint.zip文件![/font][/color]
|
||||
|
||||
oarc-spawn-time-warning-msg=由于这个场景的运作方式,你新出生点周围的土地可能需要一些时间来生成...当你选择你的第一个生成点时,请等待10-20秒。
|
||||
|
||||
oarc-i-understand=我明白了
|
||||
oarc-spawn-options=生成选项
|
||||
|
||||
oarc-click-info-btn-help=点击左上角的信息按钮以了解更多关于这个场景的信息!这是你唯一选择生成选项的机会,请慎重选择...
|
||||
|
||||
oarc-vanilla-spawn=原版生成
|
||||
oarc-default-spawn-behavior=这是原版游戏的默认生成行为。你将加入地图中心的默认队伍。
|
||||
|
||||
oarc-join-main-team-radio=加入主队(共享科技)
|
||||
oarc-create-own-team-radio=创建你自己的队伍(独立科技树)
|
||||
|
||||
oarc-moat-option=用护城河包围你的出生点
|
||||
|
||||
oarc-solo-spawn-near=单人出生点(近)
|
||||
oarc-solo-spawn-far=单人出生点(远)
|
||||
|
||||
oarc-starting-area-vanilla=你将在自己的起始区域生成(原版风格)。
|
||||
oarc-vanilla-spawns-available=目前有 __1__ 个原版生成点可用。
|
||||
|
||||
oarc-starting-area-normal=你将在一个新出生点出生。
|
||||
oarc-join-someone-avail=加入他人(有 __1__ 个可用)
|
||||
oarc-join-someone-info=你将在别人的基地出生。这需要至少有1个人开放了他们的基地。这个选择是最终的,你将无法再次创建自己的基地。
|
||||
|
||||
oarc-no-shared-avail=当前没有可用的共享基地。
|
||||
oarc-join-check-again=再次检查
|
||||
oarc-shared-spawn-disabled=本模式中禁用了共享生成。
|
||||
|
||||
oarc-buddy-spawn=和好友一起出生
|
||||
oarc-buddy-spawn-info=好友系统需要两个玩家同时在这个菜单中,你们将彼此并肩生成,各自拥有自己的资源。
|
||||
|
||||
oarc-max-players-shared-spawn=如果你创建自己的出生基地,则最多可以允许多达 __1__ 名其他在线玩家加入。
|
||||
oarc-spawn-dist-notes=近距离生成距离地图中心 __1__-__2__ 块。\n远距离生成距离地图中心 __3__-__4__ 块。\n单人出生有危险!要开荒到达其他玩家的地方,请做好准备。
|
||||
|
||||
oarc-player-is-joining-main-force=__ 1__正在加入主要团队!
|
||||
oarc-player-is-joining-near=__1__ 从远处加入了游戏!
|
||||
oarc-player-is-joining-far=___1__ 从很远的地方加入游戏!
|
||||
|
||||
oarc-please-wait=请稍候,正在生成您的出生点!
|
||||
|
||||
oarc-looking-for-buddy=__1__正在寻找队友。
|
||||
|
||||
oarc-avail-bases-join=可加入的基地:
|
||||
oarc-spawn-spots-remaining=__1__(还剩 __2__ 个名额)
|
||||
oarc-cancel-return-to-previous=取消(返回到之前的选项)
|
||||
oarc-player-requesting-join-you=__1__ 请求加入你的基地!
|
||||
oarc-waiting-for-spawn-owner=等待基地拥有者的回应...
|
||||
|
||||
oarc-you-will-spawn-once-host=一旦对方大佬选择是,你便会加入...
|
||||
oarc-player-cancel-join-request=__1__ 取消了他加入你基地的请求。
|
||||
|
||||
oarc-spawn-ctrl=生成控制
|
||||
oarc-spawn-controls=生成控制选项:
|
||||
oarc-spawn-allow-joiners=允许其他人加入你的基地。
|
||||
|
||||
oarc-set-respawn-loc=设置新的重生点(有冷却时间)
|
||||
oarc-set-respawn-loc-cooldown=重生点冷却剩余时间:__1__
|
||||
oarc-set-respawn-note=这将把你的重生点设置为你当前的位置。
|
||||
|
||||
oarc-select-player-join-queue=从申请队列中选择一个玩家:
|
||||
|
||||
oarc-accept=欣然接受
|
||||
oarc-reject=残忍拒绝
|
||||
|
||||
oarc-no-player-join-reqs=目前没有玩家请求加入你的基地。
|
||||
|
||||
oarc-start-shared-base=新玩家现在可以加入 __1__的基地!
|
||||
oarc-stop-shared-base=新玩家不能再加入 __1__ 的基地!
|
||||
|
||||
oarc-spawn-point-updated=重生点已更新!
|
||||
oarc-selected-player-not-wait=选中的玩家不再等待加入。
|
||||
oarc-reject-joiner=您已拒绝 __1__ 加入您的基地的请求。
|
||||
oarc-your-request-rejected=您的加入请求被拒绝了。
|
||||
|
||||
oarc-player-joining-base=__1__ 正在加入 __2__ 的基地!
|
||||
|
||||
oarc-player-left-while-joining=__1__ 退出了游戏,真是无情。
|
||||
|
||||
oarc-buddy-spawn-options=好友生成选项
|
||||
oarc-buddy-spawn-instructions=要使用此功能,请确保您和您的好友同时处于此菜单中。只需其中一人发送请求即可。从列表中选择你的好友(如果看不到好友的名字,请刷新),然后选择您的出生选项。单击请求按钮发送请求。然后,另一个伙伴可以接受(或拒绝)该请求。这将使你们俩都能在彼此旁边生成,每人都有自己的出生点。一旦伙伴接受了生成请求,便不可更改!
|
||||
|
||||
oarc-buddy-select-info=首先,从待选列表中选择一个好友。然后选择生成选项并发送您的请求:
|
||||
|
||||
oarc-buddy-refresh=刷新好友列表
|
||||
oarc-create-buddy-team=创建你自己的好友团队 (好友和你共享研究)
|
||||
|
||||
oarc-buddy-spawn-near=请求和好友出生 (近)
|
||||
oarc-buddy-spawn-far=请求和好友出生 (远)
|
||||
|
||||
oarc-invalid-buddy=你尚未选择有效的好友!请再试一遍。
|
||||
oarc-buddy-not-avail=已选择的好友不再可用!请再试一遍。
|
||||
|
||||
oarc-waiting-for-buddy=正在等待好友响应...
|
||||
oarc-wait-buddy-select-yes=一旦您的好友选择是,您将会出生…
|
||||
|
||||
oarc-buddy-cancel-request=__1__ 取消了其好友请求!
|
||||
|
||||
oarc-buddy-requesting-from-you=__1__ 正在向你请求好友生成!
|
||||
|
||||
oarc-buddy-txt-main-team=默认团队
|
||||
oarc-buddy-txt-new-teams=独立团队
|
||||
oarc-buddy-txt-buddy-team=好友团队
|
||||
oarc-buddy-txt-moat=被护城河环绕
|
||||
oarc-buddy-txt-near=靠近地图中心!
|
||||
oarc-buddy-txt-far=远离地图中心!
|
||||
oarc-buddy-txt-would-like=希望加入
|
||||
oarc-buddy-txt-next-to-you=在您旁边
|
||||
|
||||
oarc-buddy-declined=__1__ 拒绝了你的好友请求!
|
||||
|
||||
oarc-spawn-wait=请稍候!
|
||||
oarc-wait-text=您的出生点正在创建中。\n您将在 __1__ 秒内被传送到那里!\n请稍候…
|
46
scenarios/OARC/control.lua
Normal file
46
scenarios/OARC/control.lua
Normal file
@ -0,0 +1,46 @@
|
||||
-- To edit this scenario, you must make a copy of it and place it in your own scenarios folder first!
|
||||
-- Do not edit the scenario provided by the mod install directly!
|
||||
|
||||
-- I provide this empty scenario to avoid the freeplay scenario extra baggage.
|
||||
-- You can use the freeplay scenario too just fine if you want.
|
||||
-- The main benefit of the scenario is that it lets you modify the any of the config during on_init of the mod.
|
||||
|
||||
-- This is where you can modify what resources spawn, how much, where, etc.
|
||||
-- Once you have a config you like, it's a good idea to save it for later use so you don't lose it if you update the
|
||||
-- scenario. I will try to avoid making breaking changes to this, but no guarantees.
|
||||
|
||||
-- To see what settings are available, look at the config_mod.lua file in the mod folder.
|
||||
|
||||
-- Check if the OARC mod is loaded. Other than that, it's an empty scenario!
|
||||
script.on_init(function(event)
|
||||
if not game.active_mods["oarc-mod"] then
|
||||
error("OARC mod not found! This scenario is intended to be run with the OARC mod!")
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
local oarc_scenario_interface =
|
||||
{
|
||||
get_scenario_settings = function()
|
||||
|
||||
---@type OarcConfig
|
||||
local modified_settings = remote.call("oarc_mod", "get_mod_settings")
|
||||
|
||||
-- Overwrite whatever settings you want here:
|
||||
-- If you provide an invalid value for a mod setting, it will error and not load the scenario.
|
||||
----------------------------------------------------------------------------------------------------------------
|
||||
modified_settings.server_info.welcome_msg_title = "THIS IS A TEMPLATE SCENARIO"
|
||||
modified_settings.server_info.welcome_msg = "This is a template scenario. You can modify the settings in the control.lua file. If you are seeing this message, you did not modify the scenario correctly."
|
||||
|
||||
modified_settings.spawn_general.shape = "circle"
|
||||
|
||||
-- Some examples of overriding surface config (which is not accessible from the mod settings!)
|
||||
modified_settings.surfaces_config["nauvis"].starting_items.player_start_items = {
|
||||
["coal"] = 1, -- You're on the naughty list!
|
||||
}
|
||||
----------------------------------------------------------------------------------------------------------------
|
||||
return modified_settings
|
||||
end
|
||||
}
|
||||
|
||||
remote.add_interface("oarc_scenario", oarc_scenario_interface)
|
Before (image error) Size: 263 KiB After (image error) Size: 263 KiB |
3
scenarios/OARC/locale/en/locale-scenario.cfg
Normal file
3
scenarios/OARC/locale/en/locale-scenario.cfg
Normal file
@ -0,0 +1,3 @@
|
||||
scenario-name=OARC-TEMPLATE
|
||||
|
||||
description=This is an EMPTY TEMPLATE scenario! It is provided as an example of how to override the default freeplay scenario if you want to. Additionally, experienced server hosts can configure all settings from a file instead of through the mod settings. Please read the control.lua file inside the scenario for notes. [color=red]To edit this scenario, you must make a copy of it and place it in your own scenarios folder first. Do not edit this scenario directly.[/color]
|
403
settings.lua
Normal file
403
settings.lua
Normal file
@ -0,0 +1,403 @@
|
||||
data:extend({
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-default-allow-spawning-on-other-surfaces",
|
||||
setting_type = "startup",
|
||||
default_value = false,
|
||||
order = "a1"
|
||||
},
|
||||
{
|
||||
type = "string-setting",
|
||||
name = "oarc-mod-main-force-name",
|
||||
setting_type = "startup",
|
||||
default_value = "Main Force",
|
||||
order = "a2"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-linked-chest-size",
|
||||
setting_type = "startup",
|
||||
default_value = 100,
|
||||
minimum_value = 1,
|
||||
maximum_value = 1000,
|
||||
order = "a3"
|
||||
},
|
||||
|
||||
{
|
||||
type = "string-setting",
|
||||
name = "oarc-mod-welcome-msg-title",
|
||||
setting_type = "runtime-global",
|
||||
default_value = "Insert Server Title Here!",
|
||||
order = "a1"
|
||||
},
|
||||
{
|
||||
type = "string-setting",
|
||||
name = "oarc-mod-welcome-msg",
|
||||
setting_type = "runtime-global",
|
||||
default_value = "Insert Server Welcome Message Here!",
|
||||
order = "a2"
|
||||
},
|
||||
{
|
||||
type = "string-setting",
|
||||
name = "oarc-mod-discord-invite",
|
||||
setting_type = "runtime-global",
|
||||
default_value = "Insert Discord Invite Here!",
|
||||
order = "a4"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-main-team",
|
||||
setting_type = "runtime-global",
|
||||
default_value = true,
|
||||
order = "b1"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-separate-teams",
|
||||
setting_type = "runtime-global",
|
||||
default_value = true,
|
||||
order = "b2"
|
||||
},
|
||||
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-allow-moats-around-spawns",
|
||||
setting_type = "runtime-global",
|
||||
default_value = true,
|
||||
order = "b4"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-moat-bridging",
|
||||
setting_type = "runtime-global",
|
||||
default_value = false,
|
||||
order = "b5"
|
||||
},
|
||||
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-minimum-distance-to-existing-chunks",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 10,
|
||||
minimum_value = 5,
|
||||
maximum_value = 25,
|
||||
order = "c1"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-near-spawn-distance",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 100,
|
||||
minimum_value = 50,
|
||||
maximum_value = 250,
|
||||
order = "c2"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-far-spawn-distance",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 500,
|
||||
minimum_value = 250,
|
||||
maximum_value = 5000,
|
||||
order = "c3"
|
||||
},
|
||||
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-buddy-spawn",
|
||||
setting_type = "runtime-global",
|
||||
default_value = true,
|
||||
order = "d1"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-offline-protection",
|
||||
setting_type = "runtime-global",
|
||||
default_value = true,
|
||||
order = "d2"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-shared-team-vision",
|
||||
setting_type = "runtime-global",
|
||||
default_value = true,
|
||||
order = "d3"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-shared-team-chat",
|
||||
setting_type = "runtime-global",
|
||||
default_value = true,
|
||||
order = "d4"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-shared-spawns",
|
||||
setting_type = "runtime-global",
|
||||
default_value = true,
|
||||
order = "d5"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-number-of-players-per-shared-spawn",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 3,
|
||||
minimum_value = 2,
|
||||
maximum_value = 10,
|
||||
order = "d6"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-friendly-fire",
|
||||
setting_type = "runtime-global",
|
||||
default_value = false,
|
||||
order = "d7"
|
||||
},
|
||||
|
||||
{
|
||||
type = "string-setting",
|
||||
name = "oarc-mod-default-surface",
|
||||
setting_type = "runtime-global",
|
||||
default_value = "nauvis",
|
||||
order = "e2"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-secondary-spawns",
|
||||
setting_type = "runtime-global",
|
||||
default_value = false,
|
||||
order = "e3"
|
||||
},
|
||||
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-scale-resources-around-spawns",
|
||||
setting_type = "runtime-global",
|
||||
default_value = true,
|
||||
order = "f1"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-modified-enemy-spawning",
|
||||
setting_type = "runtime-global",
|
||||
default_value = true,
|
||||
order = "f2"
|
||||
},
|
||||
{
|
||||
type = "double-setting",
|
||||
name = "oarc-mod-modified-enemy-easy-evo",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 0,
|
||||
minimum_value = 0,
|
||||
maximum_value = 1,
|
||||
order = "f21"
|
||||
},
|
||||
{
|
||||
type = "double-setting",
|
||||
name = "oarc-mod-modified-enemy-medium-evo",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 0.3,
|
||||
minimum_value = 0,
|
||||
maximum_value = 1,
|
||||
order = "f22"
|
||||
},
|
||||
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-minimum-online-time",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 15,
|
||||
minimum_value = 0,
|
||||
maximum_value = 60,
|
||||
order = "f3"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-respawn-cooldown-min",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 5,
|
||||
minimum_value = 0,
|
||||
maximum_value = 60,
|
||||
order = "f4"
|
||||
},
|
||||
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-shared-power",
|
||||
setting_type = "runtime-global",
|
||||
default_value = false,
|
||||
order = "f10"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-shared-chest",
|
||||
setting_type = "runtime-global",
|
||||
default_value = false,
|
||||
order = "f11"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-regrowth",
|
||||
setting_type = "runtime-global",
|
||||
default_value = false,
|
||||
order = "g1"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-world-eater",
|
||||
setting_type = "runtime-global",
|
||||
default_value = false,
|
||||
order = "g2"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-enable-abandoned-base-cleanup",
|
||||
setting_type = "runtime-global",
|
||||
default_value = true,
|
||||
order = "g3"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-regrowth-cleanup-interval-min",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 60,
|
||||
minimum_value = 15,
|
||||
maximum_value = 180,
|
||||
order = "g4"
|
||||
},
|
||||
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-spawn-general-radius-tiles",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 64,
|
||||
minimum_value = 32,
|
||||
maximum_value = 320,
|
||||
order = "h1"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-spawn-general-moat-width-tiles",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 8,
|
||||
minimum_value = 1,
|
||||
maximum_value = 32,
|
||||
order = "h2"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-spawn-general-tree-width-tiles",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 5,
|
||||
minimum_value = 1,
|
||||
maximum_value = 32,
|
||||
order = "h3"
|
||||
},
|
||||
{
|
||||
type = "string-setting",
|
||||
name = "oarc-mod-spawn-general-enable-resources-circle-shape",
|
||||
setting_type = "runtime-global",
|
||||
default_value = "circle",
|
||||
allowed_values = {"circle", "square"},
|
||||
order = "h4"
|
||||
},
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-spawn-general-enable-force-grass",
|
||||
setting_type = "runtime-global",
|
||||
default_value = false,
|
||||
order = "h5"
|
||||
}, {
|
||||
type = "string-setting",
|
||||
name = "oarc-mod-spawn-general-shape",
|
||||
setting_type = "runtime-global",
|
||||
default_value = "circle",
|
||||
allowed_values = {"circle", "octagon", "square"},
|
||||
order = "h6"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
type = "bool-setting",
|
||||
name = "oarc-mod-resource-placement-enabled",
|
||||
setting_type = "runtime-global",
|
||||
default_value = true,
|
||||
order = "i1"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-resource-placement-distance-to-edge",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 20,
|
||||
minimum_value = 0,
|
||||
maximum_value = 96,
|
||||
order = "i2"
|
||||
},
|
||||
{
|
||||
type = "double-setting",
|
||||
name = "oarc-mod-resource-placement-angle-offset",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 2.32,
|
||||
minimum_value = 0,
|
||||
maximum_value = 6.28,
|
||||
order = "i3"
|
||||
},
|
||||
{
|
||||
type = "double-setting",
|
||||
name = "oarc-mod-resource-placement-angle-final",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 4.46,
|
||||
minimum_value = 0,
|
||||
maximum_value = 6.28,
|
||||
order = "i4"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-resource-placement-vertical-offset",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 20,
|
||||
minimum_value = 0,
|
||||
maximum_value = 96,
|
||||
order = "i5"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-resource-placement-horizontal-offset",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 20,
|
||||
minimum_value = 0,
|
||||
maximum_value = 96,
|
||||
order = "i6"
|
||||
},
|
||||
{
|
||||
type = "int-setting",
|
||||
name = "oarc-mod-resource-placement-linear-spacing",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 6,
|
||||
minimum_value = 0,
|
||||
maximum_value = 32,
|
||||
order = "i7"
|
||||
},
|
||||
{
|
||||
type = "double-setting",
|
||||
name = "oarc-mod-resource-placement-size-multiplier",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 1.0,
|
||||
minimum_value = 0,
|
||||
maximum_value = 10,
|
||||
order = "i8"
|
||||
},
|
||||
{
|
||||
type = "double-setting",
|
||||
name = "oarc-mod-resource-placement-amount-multiplier",
|
||||
setting_type = "runtime-global",
|
||||
default_value = 1.0,
|
||||
minimum_value = 0,
|
||||
maximum_value = 10,
|
||||
order = "i9"
|
||||
},
|
||||
})
|
BIN
thumbnail.png
Normal file
BIN
thumbnail.png
Normal file
Binary file not shown.
After (image error) Size: 464 KiB |
Loading…
Reference in New Issue
Block a user