forked from edubart/otclient
-
Notifications
You must be signed in to change notification settings - Fork 13
/
otclientrc.lua
328 lines (266 loc) · 10.5 KB
/
otclientrc.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
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
-- this file is loaded after all modules are loaded and initialized
-- you can place any custom user code here
g_logger.info('Startup done :]')
g_logger.debug("----- STEP 1 -----")
g_logger.info('OTClient Map Generator version: 5.1')
g_logger.info("prepare client to generate graphics, EXECUTE:")
g_logger.debug("prepareClient(1076, '/things/1076/items.otb', '/map.otbm', 8, 5)")
g_logger.info("'1076' - client version, OTC will load client .spr and .dat from folder 'data/things/1076'")
g_logger.info("'/things/1076/items.otb' - OTB file [from OTS /data/items/ folder], OTC will load from file 'data/things/1076/items.otb'")
g_logger.info("'/map.otbm' - MAP file [from OTS /data/world/ folder], OTC will load from file 'data/map.otbm'")
g_logger.info("8 - number of threads that will generate map images, set it to number of your CPU 'threads' or lower")
g_logger.info("5 - split map to 5 parts, if your map uses much RAM and OTC is compiled in 32-bit version, you need to load map by parts to reduce maximum RAM usage of OTC")
local clientVersion = 0
local otbPath = ''
local mapPath = ''
local isGenerating = false
local threadsToRun = 3
local areasAdded = 0
local startTime = os.time()
local lastPrintStatus = os.time()
local mapParts = {}
local mapPartsToGenerate = {}
local mapPartsCount = 0
local mapPartsCurrentId = 0
local mapImagesGenerated = 0
-- ex. prepareClient(1076, '/things/1076/items.otb', '/map.otbm')
function prepareClient(cv, op, mp, ttr, mpc)
clientVersion = cv
otbPath = op
mapPath = mp
threadsToRun = ttr or 3
mapPartsCount = mpc
g_logger.info("Loading client data... (it will freez client for FEEEEW seconds)")
g_dispatcher.scheduleEvent(prepareClient_action, 1000)
end
function prepareClient_action()
g_map.initializeMapGenerator(threadsToRun);
g_resources.makeDir('house');
g_logger.info("Loading client Tibia.dat and Tibia.spr...")
g_game.setClientVersion(clientVersion)
g_logger.info("Loading server items.otb...")
g_things.loadOtb(otbPath)
g_logger.info("Loading server map information...")
g_map.setMaxXToLoad(-1) -- do not load tiles, just save map min/max position
g_map.loadOtbm(mapPath)
g_logger.info("Loaded map positions. Minimum [X: " .. g_map.getMinPosition().x .. ", Y: " .. g_map.getMinPosition().y .. ", Z: " .. g_map.getMinPosition().z .. "] Maximum [X: " .. g_map.getMaxPosition().x .. ", Y: " .. g_map.getMaxPosition().y .. ", Z: " .. g_map.getMaxPosition().z .. "]")
g_logger.debug("Loaded client data.")
local totalTilesCount = 0
local mapTilesPerX = g_map.getMapTilesPerX()
for x, c in pairs(mapTilesPerX) do
totalTilesCount = totalTilesCount + c
end
mapParts = {}
local targetTilesCount = totalTilesCount / mapPartsCount
local currentTilesCount = 0
local currentPart = {["minXrender"] = 0}
for i = 0, 70000 do
if mapTilesPerX[i] then
currentTilesCount = currentTilesCount + mapTilesPerX[i]
currentPart.maxXrender = i
if #mapParts < mapPartsCount and currentTilesCount > targetTilesCount then
table.insert(mapParts, currentPart)
currentPart = {["minXrender"] = i}
currentTilesCount = 0
end
end
end
currentPart.maxXrender = 70000
table.insert(mapParts, currentPart)
g_logger.debug('----- MAP PARTS LIST -----')
for i, currentPart in pairs(mapParts) do
-- render +/- 8 tiles to avoid problem with calculations precision
currentPart.minXrender = math.max(0, math.floor((currentPart.minXrender - 8) / 8) * 8)
currentPart.maxXrender = math.floor((currentPart.maxXrender + 8) / 8) * 8
-- load +/- 16 tiles to be sure that all items on floors below will load
currentPart.minXload = math.max(0, math.floor((currentPart.minXrender - 16) / 8) * 8)
currentPart.maxXload = math.floor((currentPart.maxXrender + 16) / 8) * 8
print("PART " .. i .. " FROM X: " .. currentPart.minXrender .. ", TO X: " .. currentPart.maxXrender)
end
g_logger.debug('----- MAP PARTS LIST -----')
g_logger.debug('')
g_logger.debug("----- STEP 2 -----")
g_logger.info("Now just type (lower levels shadow 30%)");
g_logger.info("ALL PARTS OF MAP:")
g_logger.debug("generateMap('all', 30)");
g_logger.info("ONLY PARTS 2 AND 3 OF MAP:")
g_logger.debug("generateMap({2, 3}, 30)");
g_logger.info("")
end
function generateManager()
-- code here
if (g_map.getGeneratedAreasCount() / 1000) + 1 > areasAdded then
g_map.addAreasToGenerator(areasAdded * 1000, areasAdded * 1000 + 999)
areasAdded = areasAdded + 1
end
if lastPrintStatus ~= os.time() then
-- status here
print(math.floor(g_map.getGeneratedAreasCount() / g_map.getAreasCount() * 100) .. '%, ' .. format_int(g_map.getGeneratedAreasCount()) .. ' of ' .. format_int(g_map.getAreasCount()) .. ' images generated - PART ' .. mapPartsCurrentId .. ' OF ' .. #mapPartsToGenerate)
if g_map.getAreasCount() == g_map.getGeneratedAreasCount() then
mapImagesGenerated = mapImagesGenerated + g_map.getGeneratedAreasCount()
if mapPartsCurrentId ~= #mapPartsToGenerate then
mapPartsCurrentId = mapPartsCurrentId + 1
startMapPartGenerator()
g_dispatcher.scheduleEvent(generateManager, 100)
return
end
isGenerating = false
print('Map images generation finished.')
print(mapImagesGenerated .. ' images generated in ' .. (os.time() - startTime) .. ' seconds.')
return
end
lastPrintStatus = os.time()
end
g_dispatcher.scheduleEvent(generateManager, 100)
end
function startMapPartGenerator()
local currentMapPart = mapPartsToGenerate[mapPartsCurrentId]
g_logger.info("Set min X to load: " .. currentMapPart.minXload)
g_logger.info("Set max X to load: " .. currentMapPart.maxXload)
g_logger.info("Set min X to render: " .. currentMapPart.minXrender)
g_logger.info("Set max X to render: " .. currentMapPart.maxXrender)
g_map.setMinXToLoad(currentMapPart.minXload)
g_map.setMaxXToLoad(currentMapPart.maxXload)
g_map.setMinXToRender(currentMapPart.minXrender)
g_map.setMaxXToRender(currentMapPart.maxXrender)
g_logger.info("Loading server map part...")
g_map.loadOtbm(mapPath)
areasAdded = 0
g_map.setGeneratedAreasCount(0)
print('Starting generator (PART ' .. mapPartsCurrentId .. ' OF ' .. #mapPartsToGenerate .. '). ' .. format_int(g_map.getAreasCount()) .. ' images to generate. ' .. threadsToRun .. ' threads will generate it now. Please wait.')
end
function generateMap(mapPartsToGenerateIds, shadowPercent)
if isGenerating then
print('Generating script is already running. Cannot start another generation')
return
end
isGenerating = true
if type(mapPartsToGenerateIds) == "string" then
mapPartsToGenerateIds = {}
for i = 1, mapPartsCount do
table.insert(mapPartsToGenerateIds, i)
end
end
g_map.setShadowPercent(shadowPercent)
mapImagesGenerated = 0
-- split map into parts
mapPartsCurrentId = 1
mapPartsToGenerate = {}
for _, i in pairs(mapPartsToGenerateIds) do
table.insert(mapPartsToGenerate, mapParts[i])
end
startTime = os.time()
startMapPartGenerator()
g_dispatcher.scheduleEvent(generateManager, 1000)
end
function format_int(number)
local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
int = int:reverse():gsub("(%d%d%d)", "%1,")
return minus .. int:reverse():gsub("^,", "") .. fraction
end
partialLoading = false
houseTiles = {}
houseImageMarginSize = 5
function generateHouseImage(id)
local floors = {}
if partialLoading then
-- load part of map that contains house
local minX = 99999
local maxX = 0
for _, tilePosition in pairs(houseTiles[id]) do
local pos = tilePosition
floors[pos.z] = pos.z
if pos.x < minX then
minX = pos.x
end
if pos.x > maxX then
maxX = pos.x
end
end
g_map.setMinXToLoad(minX - (houseImageMarginSize + 16))
g_map.setMaxXToLoad(maxX + houseImageMarginSize + 16)
g_map.setMinXToRender(minX - (houseImageMarginSize + 8))
g_map.setMaxXToRender(maxX + houseImageMarginSize + 8)
g_logger.info("Loading server map part for house ID " .. id)
print(
g_map.getMinXToLoad(),
g_map.getMaxXToLoad(),
g_map.getMinXToRender(),
g_map.getMaxXToRender())
g_map.loadOtbm(mapPath)
end
g_map.drawHouse(id, houseImageMarginSize)
houseTiles[id] = nil
end
function generateHouseImages()
-- code to generate images in loop, generated images got houseTiles set to null
local anyGenerated = false
for houseId, tiles in pairs(houseTiles) do
if tiles then
generateHouseImage(houseId)
g_logger.info("Generated house image: " .. houseId)
anyGenerated = true
end
end
if anyGenerated then
g_dispatcher.scheduleEvent(generateHouseImages, 100)
else
isGenerating = false
g_logger.info("Done house image generation.")
end
end
function mapLoadManager()
if mapPartsCurrentId ~= #mapPartsToGenerate then
mapPartsCurrentId = mapPartsCurrentId + 1
local currentMapPart = mapPartsToGenerate[mapPartsCurrentId]
g_logger.info("Set min X to load: " .. currentMapPart.minXload)
g_logger.info("Set max X to load: " .. currentMapPart.maxXload)
g_logger.info("Set min X to render: " .. currentMapPart.minXrender)
g_logger.info("Set max X to render: " .. currentMapPart.maxXrender)
g_map.setMinXToLoad(currentMapPart.minXload)
g_map.setMaxXToLoad(currentMapPart.maxXload)
g_map.setMinXToRender(currentMapPart.minXrender)
g_map.setMaxXToRender(currentMapPart.maxXrender)
g_logger.info("Loading server map part " .. mapPartsCurrentId .. " of " .. #mapPartsToGenerate)
g_map.loadOtbm(mapPath)
for _, house in pairs(g_houses.getHouseList()) do
local houseId = house:getId()
if not houseTiles[houseId] then
houseTiles[houseId] = {}
end
for i, tile in pairs(house:getTiles()) do
local tileString = tile:getPosition().x .. '_' .. tile:getPosition().y .. '_' .. tile:getPosition().z
houseTiles[houseId][tileString] = tile:getPosition()
end
end
g_dispatcher.scheduleEvent(mapLoadManager, 1000)
else
g_logger.info("Map loading finished.")
g_logger.info("Starting house image generator.")
g_logger.info("CLIENT WILL FREEZ! Watch progress in house images output folder.")
g_dispatcher.scheduleEvent(generateHouseImages, 1000)
end
end
function generateHouses(shadowPercent, doPartialLoading)
if isGenerating then
print('Generating script is already running. Cannot start another generator.')
return
end
partialLoading = doPartialLoading
g_map.setShadowPercent(shadowPercent)
isGenerating = true
mapPartsCurrentId = 0
mapPartsToGenerate = {}
for i = 1, mapPartsCount do
table.insert(mapPartsToGenerate, mapParts[i])
end
g_dispatcher.scheduleEvent(mapLoadManager, 100)
end
function saveMinimap(path)
g_map.setMinXToLoad(0)
g_map.setMaxXToLoad(70000)
g_map.setMinXToRender(0)
g_map.setMaxXToRender(70000)
g_map.loadOtbm(mapPath)
g_minimap.saveOtmm(path)
end