-
Notifications
You must be signed in to change notification settings - Fork 8
/
Templates.lua
223 lines (153 loc) · 6.69 KB
/
Templates.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
-- Templates.lua
-- Implements the various bits needed for templating ("/gal template" command) to work
local g_Templates = {};
--- Returns the relative Y-coord of the highest non-air block in the area
function GetAreaHeight(a_Area)
local SizeZ = a_Area:GetSizeZ();
local SizeX = a_Area:GetSizeX();
local AirBlock = E_BLOCK_AIR;
for y = a_Area:GetSizeY() - 1, 0, -1 do
for z = 0, SizeZ - 1 do
for x = 0, SizeX - 1 do
if (a_Area:GetRelBlockType(x, y, z) ~= AirBlock) then
return y;
end
end
end
end
return 0;
end
--- Sends the current templating status, along with hints about what to do next, to the player
function SendTemplatingStatus(a_Player)
assert(a_Player)
assert(IsPlayerTemplating(a_Player))
local PlayerTemplate = g_Templates[a_Player:GetWorld():GetName()][a_Player:GetUniqueID()];
local msg = "You are templating with the export file set to '" .. PlayerTemplate.FileName .. "'. Left-click on a block to (re)set the first corner. Right-click on a block to (re)set the second corner. ";
if (PlayerTemplate.FirstPoint and PlayerTemplate.SecondPoint) then
-- Both points are assigned, output coords:
local DiffX = math.abs(PlayerTemplate.FirstPoint.x - PlayerTemplate.SecondPoint.x) + 1;
local DiffZ = math.abs(PlayerTemplate.FirstPoint.z - PlayerTemplate.SecondPoint.z) + 1;
msg =
"The current template is set, first corner at {" .. PlayerTemplate.FirstPoint.x .. ", " ..
PlayerTemplate.FirstPoint.z .. "}, second corner at {" .. PlayerTemplate.SecondPoint.x .. ", " ..
PlayerTemplate.SecondPoint.z .. "}. The area size is " .. DiffX .. " x " .. DiffZ ..
" blocks. You can now use " .. cChatColor.Green .. g_Config.CommandPrefix .. " template export" ..
cChatColor.White .. " to export the area, or left-click or right-click to re-adjust the coordinates. ";
end
msg =
msg .. "You can also use " .. cChatColor.Green .. g_Config.CommandPrefix .. " template cancel" ..
cChatColor.White .. " to cancel templating and return to normal gameplay.";
a_Player:SendMessage(msg);
end
--- Returns true if the player currently is in the templating mode
function IsPlayerTemplating(a_Player)
assert(a_Player ~= nil);
local WorldTemplates = g_Templates[a_Player:GetWorld():GetName()];
return (WorldTemplates ~= nil) and (WorldTemplates[a_Player:GetUniqueID()] ~= nil);
end
--- Starts templating for the specified player
function BeginTemplating(a_Player, a_FileName)
assert(a_Player ~= nil);
assert(a_FileName ~= nil);
assert(not(IsPlayerTemplating(a_Player)));
local WorldName = a_Player:GetWorld():GetName();
if (g_Templates[WorldName] == nil) then
g_Templates[WorldName] = {};
end
g_Templates[WorldName][a_Player:GetUniqueID()] =
{
PlayerName = a_Player:GetName();
PlayerID = a_Player:GetUniqueID();
WorldName = WorldName;
FileName = a_FileName;
};
return true;
end
--- Cancels templating for the specified player
function CancelTemplating(a_Player)
assert(a_Player ~= nil);
assert(IsPlayerTemplating(a_Player));
g_Templates[a_Player:GetWorld():GetName()][a_Player:GetUniqueID()] = nil;
end
--- Exports the template created by the specified player.
-- Returns true if successful, false and error message if not
function ExportTemplate(a_Player)
assert(a_Player ~= nil);
assert(IsPlayerTemplating(a_Player));
local PlayerTemplate = g_Templates[a_Player:GetWorld():GetName()][a_Player:GetUniqueID()];
assert(PlayerTemplate ~= nil);
if ((PlayerTemplate.FirstPoint == nil) or (PlayerTemplate.SecondPoint == nil)) then
return false, "One of the corners haven't been set.";
end
-- Read the coords, sort them:
local MinX = PlayerTemplate.FirstPoint.x;
local MaxX = PlayerTemplate.SecondPoint.x;
if (MinX > MaxX) then
MinX, MaxX = MaxX, MinX; -- swap them
end
local MinZ = PlayerTemplate.FirstPoint.z;
local MaxZ = PlayerTemplate.SecondPoint.z;
if (MinZ > MaxZ) then
MinZ, MaxZ = MaxZ, MinZ; -- swap them
end
-- Read the template from the world:
local BlockArea = cBlockArea();
if not(BlockArea:Read(a_Player:GetWorld(), MinX, MaxX, 0, 255, MinZ, MaxZ, cBlockArea.baTypes + cBlockArea.baMetas)) then
return false, "Cannot read the template data.";
end
-- Crop the area up to the max height:
local AreaHeight = GetAreaHeight(BlockArea);
local CroppedArea = cBlockArea();
CroppedArea:Create(MaxX - MinX + 1, AreaHeight + 1, MaxZ - MinZ + 1, cBlockArea.baTypes + cBlockArea.baMetas);
CroppedArea:Merge(BlockArea, 0, 0, 0, cBlockArea.msOverwrite);
-- Save to file:
LOG("Saving to file '" .. PlayerTemplate.FileName .. "'...");
if not(CroppedArea:SaveToSchematicFile(PlayerTemplate.FileName)) then
return false, "Cannot save schematic";
end
return true, "Template has been exported to '" .. PlayerTemplate.FileName .. "'.";
end
--- Called when a player left-clicks
-- If the player is templating, processes the click and returns true
-- Returns false if the player is not templating and normal click handling should continue
function HandleTemplatingLeftClick(a_Player, a_BlockX, a_BlockZ)
local WorldTemplates = g_Templates[a_Player:GetWorld():GetName()];
if (WorldTemplates == nil) then
-- The player is not templating (there's no templating in this world)
-- At least initialize the g_Templates[] for the current world
g_Templates[a_Player:GetWorld():GetName()] = {};
return false;
end
-- If the player is not templating, return control to the normal handler:
local PlayerTemplate = WorldTemplates[a_Player:GetUniqueID()];
if (PlayerTemplate == nil) then
return false;
end
-- The player is templating, (re-)set the first point:
PlayerTemplate.FirstPoint = {x = a_BlockX, z = a_BlockZ};
a_Player:SendMessage("First corner set to {" .. a_BlockX .. ", " .. a_BlockZ .. "}.");
SendTemplatingStatus(a_Player);
return true;
end
--- Called when a player right-clicks
-- If the player is templating, processes the click and returns true
-- Returns false if the player is not templating and normal click handling should continue
function HandleTemplatingRightClick(a_Player, a_BlockX, a_BlockZ)
local WorldTemplates = g_Templates[a_Player:GetWorld():GetName()];
if (WorldTemplates == nil) then
-- The player is not templating (there's no templating in this world)
-- At least initialize the g_Templates[] for the current world
g_Templates[a_Player:GetWorld():GetName()] = {};
return false;
end
-- If the player is not templating, return control to the normal handler:
local PlayerTemplate = WorldTemplates[a_Player:GetUniqueID()];
if (PlayerTemplate == nil) then
return false;
end
-- The player is templating, (re-)set the second point:
PlayerTemplate.SecondPoint = {x = a_BlockX, z = a_BlockZ};
a_Player:SendMessage("Second corner set to {" .. a_BlockX .. ", " .. a_BlockZ .. "}.");
SendTemplatingStatus(a_Player);
return true;
end