forked from Squamousness/random-code-stuff
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstonereplace.lua
More file actions
197 lines (171 loc) · 6.2 KB
/
stonereplace.lua
File metadata and controls
197 lines (171 loc) · 6.2 KB
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
-- stonereplace.lua
-- replaces the generic INORGANIC:NONE placeholder material on
-- site-generated buildings (roads, monastery walls/floors, town constructions)
-- with PLASTCRETE_ID_NULL.
--
-- Runs automatically on map load via ln_autoload; also re-scans nearby blocks
-- periodically as the player walks, so constructions that load in during normal
-- (non-fast-travel) movement are caught.
-- stonereplace enable
-- stonereplace disable
-- stonereplace status
local INITIAL_DELAY_TICKS = 120
local SCAN_INTERVAL_TICKS = 150
local SCAN_RADIUS = 96
---------------------------------------------------------------------------
-- CORE LOGIC
---------------------------------------------------------------------------
-- The placeholder is mat_type=0, mat_index=-1 ("inorganic, no specific stone").
local PLACEHOLDER_MAT_TYPE = 0
local PLACEHOLDER_MAT_INDEX = -1
local TARGET_ID = "INORGANIC:PLASTCRETE_ID_NULL"
local S = rawget(_G, "__stonereplace_state")
if not S then
S = {
watcher_enabled = false,
scan_gen = 0,
init_gen = 0,
target_mat = nil,
last_scan_x = nil,
last_scan_y = nil,
scan_cursor = 0,
}
_G.__stonereplace_state = S
end
local function find_target_mat()
if S.target_mat then return S.target_mat end
local m = dfhack.matinfo.find(TARGET_ID)
if m then S.target_mat = m end
return m
end
-- silent: if true, only prints when replacements were made (for periodic calls).
local function run_replacement(silent)
local m = find_target_mat()
if not m then
dfhack.printerr(("[stonereplace] %s not found in loaded raws."):format(TARGET_ID))
return 0
end
if not silent then
print(("[stonereplace] Target: %s at raws index %d"):format(TARGET_ID, m.index))
end
local count = 0
local constructions = df.global.world.event.constructions
local total = #constructions
for i = S.scan_cursor, total - 1 do
local c = constructions[i]
if c.mat_type == PLACEHOLDER_MAT_TYPE and c.mat_index == PLACEHOLDER_MAT_INDEX then
c.mat_index = m.index
c.item_type = df.item_type.BLOCKS
count = count + 1
end
end
S.scan_cursor = total
if not silent or count > 0 then
print(("[stonereplace] Replaced %d constructions."):format(count))
end
return count
end
---------------------------------------------------------------------------
-- WATCHER (periodic proximity re-scan)
---------------------------------------------------------------------------
local function scan_tick(gen)
if not S.watcher_enabled then return end
if gen ~= S.scan_gen then return end
if not dfhack.isMapLoaded() then return end
local delay = SCAN_INTERVAL_TICKS
local adv = dfhack.world and dfhack.world.getAdventurer and dfhack.world.getAdventurer()
if adv and adv.pos and S.last_scan_x then
local dx = adv.pos.x - S.last_scan_x
local dy = adv.pos.y - S.last_scan_y
if dx*dx + dy*dy > (SCAN_RADIUS * SCAN_RADIUS / 4) then
delay = 30
end
end
if adv and adv.pos then
S.last_scan_x = adv.pos.x
S.last_scan_y = adv.pos.y
end
local n = run_replacement(true)
-- Fort mode: player-built constructions use real materials, so after ruins.lua
-- finishes at embark, no new placeholders ever appear. Stop permanently once
-- two consecutive zero-replacement scans confirm all placeholders are fixed.
-- Two scans required to survive the ruins.lua / stonereplace timing overlap at startup.
if dfhack.world.isFortressMode() then
if n == 0 then
S.fort_zero_scans = (S.fort_zero_scans or 0) + 1
else
S.fort_zero_scans = 0
end
if S.fort_zero_scans >= 2 then
S.watcher_enabled = false
print("[stonereplace] Fort mode: all placeholders replaced — watcher stopped.")
return
end
end
dfhack.timeout(delay, "ticks", function() scan_tick(gen) end)
end
local function start_watcher()
if S.watcher_enabled then return end
S.watcher_enabled = true
S.fort_zero_scans = 0
S.scan_gen = S.scan_gen + 1
scan_tick(S.scan_gen)
end
local function stop_watcher()
S.watcher_enabled = false
S.scan_gen = S.scan_gen + 1
end
local function schedule_initial()
S.init_gen = S.init_gen + 1
local gen = S.init_gen
dfhack.timeout(INITIAL_DELAY_TICKS, "ticks", function()
if gen ~= S.init_gen then return end
run_replacement(false)
end)
end
---------------------------------------------------------------------------
-- ENABLE / DISABLE / STATUS
---------------------------------------------------------------------------
local function enable()
S.scan_cursor = 0
S.target_mat = nil
print("[stonereplace] Running stone replacement...")
run_replacement(false)
start_watcher()
schedule_initial()
end
local function disable()
stop_watcher()
print("[stonereplace] Disabled.")
end
local function status()
local m = find_target_mat()
local total = #df.global.world.event.constructions
print(("[stonereplace] watcher=%s cursor=%d/%d"):format(tostring(S.watcher_enabled), S.scan_cursor or 0, total))
print(("[stonereplace] Placeholder: mat_type=%d, mat_index=%d"):format(PLACEHOLDER_MAT_TYPE, PLACEHOLDER_MAT_INDEX))
print(("[stonereplace] Target: %s — %s"):format(TARGET_ID, m and ("index " .. m.index) or "NOT FOUND"))
end
---------------------------------------------------------------------------
-- ARGUMENT PARSING
---------------------------------------------------------------------------
do
S.target_mat = nil
S.scan_cursor = 0
local runtime = -dfhack.getQueryPerformanceCounter()
run_replacement(false)
runtime = runtime + dfhack.getQueryPerformanceCounter()
runtime = 1.0 * runtime / dfhack.getQueryPerformanceFrequency()
print(string.format("stonereplace.lua 'force' runtime %0.9f seconds.", runtime))
return
end
local args = { ... }
local command = args[1] or "enable"
if command == "enable" then
enable()
elseif command == "disable" then
disable()
elseif command == "status" then
status()
else
print("[stonereplace] Usage: stonereplace [enable|disable|status]")
end