-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathcontrol.lua
250 lines (217 loc) · 9.5 KB
/
control.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
local util = require("__AbandonedRuins__/utilities")
local spawning = require("__AbandonedRuins__/spawning")
---@type table<string, RuinSet>
local ruin_sets = {}
ruin_sets.base = require("__AbandonedRuins__/ruins/base_ruin_set")
local on_entity_force_changed_event = script.generate_event_name()
local function spawn_chances()
local smallChance = settings.global["ruins-small-ruin-chance"].value
local mediumChance = settings.global["ruins-medium-ruin-chance"].value
local largeChance = settings.global["ruins-large-ruin-chance"].value
local sumChance = smallChance + mediumChance + largeChance
local totalChance = math.min(sumChance, 1)
-- now compute cumulative distribution of conditional probabilities for
-- spawn_type given a spawn occurs.
local smallThreshold = smallChance / sumChance * totalChance
local mediumThreshold = mediumChance / sumChance * totalChance + smallThreshold
local largeThreshold = largeChance / sumChance * totalChance + mediumThreshold
global.spawn_table = {small = smallThreshold, medium = mediumThreshold, large = largeThreshold}
end
local function init()
util.set_enemy_force_cease_fire(util.get_enemy_force(), not settings.global["AbandonedRuins-enemy-not-cease-fire"].value)
spawn_chances()
if global.spawn_ruins == nil then
global.spawn_ruins = true
end
global.ruin_queue = global.ruin_queue or {}
if not global.excluded_surfaces then
global.excluded_surfaces = {
["beltlayer"] = true,
["pipelayer"] = true,
["Factory floor"] = true, -- factorissimo
["ControlRoom"] = true -- mobile factory
}
end
end
script.on_init(init)
script.on_configuration_changed(init)
script.on_event(defines.events.on_runtime_mod_setting_changed, init)
script.on_event(defines.events.on_force_created,
function()
-- Sets up the diplomacy for all forces, not just the newly created one.
util.set_enemy_force_diplomacy(util.get_enemy_force(), not settings.global["AbandonedRuins-enemy-not-cease-fire"].value)
end
)
script.on_event(defines.events.on_tick,
function(event)
---@type RuinQueueItem[]
local ruins = global.ruin_queue[event.tick]
if not ruins then return end
for _, ruin in pairs(ruins) do
spawning.spawn_random_ruin(ruin_sets[settings.global["AbandonedRuins-set"].value][ruin.size], util.ruin_half_sizes[ruin.size], ruin.center, ruin.surface)
end
global.ruin_queue[event.tick] = nil
end
)
-- This delays ruin spawning to the next tick. This is done because on_chunk_generated may be called before other mods have a chance to do the remote call for the ruin set:
-- ThisMod_onInit -> SomeOtherMod_generatesChunks -> ThisMod_onChunkGenerated (ruin is queued) -> RuinPack_onInit (ruin set remote call) -> ThisMod_OnTick (ruin set is used)
---@param tick uint
---@param ruin RuinQueueItem
local function queue_ruin(tick, ruin)
local processing_tick = tick + 1
if not global.ruin_queue[processing_tick] then
global.ruin_queue[processing_tick] = {}
end
table.insert(global.ruin_queue[processing_tick], ruin)
end
---@param size number
---@param min_distance number
---@param center MapPosition
---@param surface LuaSurface
---@param tick uint
local function try_ruin_spawn(size, min_distance, center, surface, tick)
min_distance = min_distance * util.ruin_min_distance_multiplier[size]
if math.abs(center.x) < min_distance and math.abs(center.y) < min_distance then return end -- too close to spawn
-- random variance so they aren't always chunk aligned
local variance = -(util.ruin_half_sizes[size] * 0.75) + 12 -- 4 -> 9, 8 -> 6, 16 -> 0. Was previously 4 -> 10, 8 -> 5, 16 -> 0
if variance > 0 then
center.x = center.x + math.random(-variance, variance)
center.y = center.y + math.random(-variance, variance)
end
queue_ruin(tick, {size = size, center = center, surface = surface})
end
script.on_event(defines.events.on_chunk_generated,
function (e)
if global.spawn_ruins == false then return end -- ruin spawning is disabled
if util.str_contains_any_from_table(e.surface.name, global.excluded_surfaces) then return end
local center = util.get_center_of_chunk(e.position)
local min_distance = settings.global["ruins-min-distance-from-spawn"].value
local spawn_type = math.random()
if spawn_type <= global.spawn_table["small"] then
try_ruin_spawn("small", min_distance, center, e.surface, e.tick)
elseif spawn_type <= global.spawn_table["medium"] then
try_ruin_spawn("medium", min_distance, center, e.surface, e.tick)
elseif spawn_type <= global.spawn_table["large"] then
try_ruin_spawn("large", min_distance, center, e.surface, e.tick)
end
end
)
script.on_event({defines.events.on_player_selected_area, defines.events.on_player_alt_selected_area}, function(event)
if event.item ~= "AbandonedRuins-claim" then return end
local neutral_force = game.forces["neutral"]
local claimants_force = game.get_player(event.player_index).force
for _, entity in pairs(event.entities) do
if entity.valid and entity.force == neutral_force then
entity.force = claimants_force
if entity.valid then
script.raise_event(on_entity_force_changed_event, {entity = entity, force = neutral_force})
end
end
end
if event.name == defines.events.on_player_alt_selected_area then
local remnants = event.surface.find_entities_filtered{area = event.area, type = {"corpse", "rail-remnants"}}
for _, remnant in pairs(remnants) do
remnant.destroy({raise_destroy = true})
end
end
end)
remote.add_interface("AbandonedRuins",
{
get_on_entity_force_changed_event = function() return on_entity_force_changed_event end,
-- The event contains:
---@class on_entity_force_changed_event_data:EventData
---@field entity LuaEntity The entity that had its force changed.
---@field force LuaForce The entity that had its force changed.
-- The current force can be gotten from event.entity.
-- This is raised after the force is changed.
-- Mod event subscription explanation can be found lower in this file.
-- Set whether ruins should be spawned at all
---@param spawn_ruins boolean
set_spawn_ruins = function(spawn_ruins)
assert(type(spawn_ruins) == "boolean",
"Remote call parameter to set_spawn_ruins for AbandonedRuins must be a boolean value."
)
global.spawn_ruins = spawn_ruins
end,
-- Get whether ruins should be spawned at all
---@return boolean
get_spawn_ruins = function() return global.spawn_ruins end,
-- Any surface whose name contains this string will not have ruins generated on it.
---@param name string
exclude_surface = function(name)
assert(type(name) == "string",
"Remote call parameter to exclude_surface for AbandonedRuins must be a string value."
)
global.excluded_surfaces[name] = true
end,
-- You excluded a surface at some earlier point but you don't want it excluded anymore.
---@param name string
reinclude_surface = function(name)
assert(type(name) == "string",
"Remote call parameter to reinclude_surface for AbandonedRuins must be a string value."
)
global.excluded_surfaces[name] = nil
end,
-- !! ALWAYS call this in on_load and on_init. !!
-- !! The ruins sets are not save/loaded. !!
-- The ruins should have the sizes given in util.ruin_half_sizes, e.g. ruins in the small_ruins array should be 8x8 tiles.
-- See also: docs/ruin_sets.md
---@param name string
---@param small_ruins Ruin[]
---@param medium_ruins Ruin[]
---@param large_ruins Ruin[]
add_ruin_set = function(name, small_ruins, medium_ruins, large_ruins)
assert(small_ruins and next(small_ruins))
assert(medium_ruins and next(medium_ruins))
assert(large_ruins and next(large_ruins))
ruin_sets[name] = {}
ruin_sets[name].small = small_ruins
ruin_sets[name].medium = medium_ruins
ruin_sets[name].large = large_ruins
end,
-- !! The ruins sets are not save/loaded. !!
-- returns {small = {<array of ruins>}, medium = {<array of ruins>}, large = {<array of ruins>}}
---@param name string
---@return RuinSet
get_ruin_set = function(name)
return ruin_sets[name]
end,
-- !! The ruins sets are not save/loaded. !!
-- returns {small = {<array of ruins>}, medium = {<array of ruins>}, large = {<array of ruins>}}
---@return RuinSet
get_current_ruin_set = function()
return ruin_sets[settings.global["AbandonedRuins-set"].value]
end
})
--[[ How to: Subscribe to mod events
Basics: Get the event id from a remote interface. Subscribe to the event in on_init and on_load.
Example:
script.on_load(function()
if remote.interfaces["AbandonedRuins"] then
script.on_event(remote.call("AbandonedRuins", "get_on_entity_force_changed_event"),
---@param event on_entity_force_changed_event_data
function(event)
-- An entity changed force, let's handle that
local entity = event.entity
local old_force = event.force
local new_force = entity.force
-- handle the force change
game.print("old: " .. old_force.name .. " new: " .. new_force.name)
end)
end
end)
script.on_init(function()
if remote.interfaces["AbandonedRuins"] then
script.on_event(remote.call("AbandonedRuins", "get_on_entity_force_changed_event"),
---@param event on_entity_force_changed_event_data
function(event)
-- An entity changed force, let's handle that
local entity = event.entity
local old_force = event.force
local new_force = entity.force
-- handle the force change
game.print("old: " .. old_force.name .. " new: " .. new_force.name)
end)
end
end)
--]]