-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathentity.lua
212 lines (202 loc) · 8.39 KB
/
entity.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
--local bc = better_commands
local S = minetest.get_translator(minetest.get_current_modname())
---Gets the name of an entity
---@param obj minetest.ObjectRef|vector.Vector
---@param no_id? boolean
---@return string
function better_commands.get_entity_name(obj, no_id, no_format)
if not obj.is_player then
return S("Command Block")
end
if obj:is_player() then
local player_name = obj:get_player_name()
if no_format then return player_name end
return better_commands.format_name(obj:get_player_name())
else
local luaentity = obj.get_luaentity and obj:get_luaentity()
if luaentity then
if no_id then
return luaentity._nametag or luaentity.nametag or ""
else
local name = luaentity._nametag or luaentity.nametag
if (not name) or name == "" then
name = luaentity.name
if name == "__builtin:item" then
local stack = ItemStack(luaentity.itemstring)
return stack:get_short_description()
elseif name == "__builtin:falling_node" then
local stack = ItemStack(luaentity.node.name)
if not stack:is_known() then return S("Unknown Falling Node") end
return S("Falling @1", stack:get_short_description())
end
return luaentity.description or better_commands.entity_names[name] or name
else
return name
end
end
else
return S("???")
end
end
end
---Gets an entity's current rotation
---@param obj minetest.ObjectRef|vector.Vector
---@return vector.Vector
function better_commands.get_entity_rotation(obj)
if obj.is_player and obj:is_player() then
return {x = obj:get_look_vertical(), y = obj:get_look_horizontal(), z = 0}
elseif obj.get_rotation then
return obj:get_rotation()
else
return vector.zero()
end
end
---Sets an entity's rotation
---@param obj minetest.ObjectRef|any
---@param rotation vector.Vector
function better_commands.set_entity_rotation(obj, rotation)
if not obj.is_player then return end
if obj:is_player() then
obj:set_look_vertical(rotation.x)
obj:set_look_horizontal(rotation.y)
elseif obj.set_rotation then
obj:set_rotation(rotation)
end
end
---Takes an object and a position, returns the rotation at which the object points at the position
---@param obj minetest.ObjectRef|vector.Vector
---@param pos vector.Vector
---@return vector.Vector
function better_commands.point_at_pos(obj, pos)
local obj_pos = obj.get_pos and obj:get_pos() or obj
if obj:is_player() then
obj_pos.y = obj_pos.y + obj:get_properties().eye_height
end
---@diagnostic disable-next-line: param-type-mismatch
local result = vector.dir_to_rotation(vector.direction(obj_pos, pos))
result.x = -result.x -- no clue why this is necessary
return result
end
---Completes a context table
---@param name string The name of the player to use as context.executor if not supplied
---@param context? table The context table to complete (optional)
---@return contextTable?
function better_commands.complete_context(name, context)
if not context then context = {} end
context.executor = context.executor or minetest.get_player_by_name(name)
if not context.executor then minetest.log("error", "Missing executor") return end
context.pos = context.pos or context.executor:get_pos()
context.rot = context.rot or better_commands.get_entity_rotation(context.executor)
--context.anchor = context.anchor or "feet"
context.origin = context.origin or name
return context
end
function better_commands.entity_from_alias(alias, list)
if minetest.registered_entities[alias] then return alias end
local entities = better_commands.unique_entities[alias]
if not entities then return end
if list then return entities end
return entities[math.random(1, #entities)]
end
---Handles Vector2 (yaw/pitch) rotation in split_param
---@param base_rot vector.Vector
---@param split_param splitParam
---@param i integer
---@param no_relative boolean?
---@return vector.Vector?
---@return string?
function better_commands.handle_vector2_rot(base_rot, split_param, i, no_relative)
base_rot = table.copy(base_rot)
if split_param[i] then
if not split_param[i+1] then
return nil, S("Missing second rotation coordinate")
end
if split_param[i].type == "number" then
base_rot.y = math.rad(tonumber(split_param[i][3]) or 0)
elseif split_param[i].type == "relative" and not no_relative then
base_rot.y = base_rot.y+math.rad(tonumber(split_param[i][3]:sub(2,-1)) or 0)
else
return nil, S("Invalid rotation coordinate @1", split_param[i][3])
end
if split_param[i+1].type == "number" then
base_rot.x = math.rad(tonumber(split_param[i+1][3]) or 0)
elseif split_param[i+1].type == "relative" and not no_relative then
base_rot.x = base_rot.x+math.rad(tonumber(split_param[i+1][3]:sub(2,-1)) or 0)
else
return nil, S("Invalid rotation coordinate @1", split_param[i+1][3])
end
return base_rot
end
return base_rot
end
---Handles rotation in various commands
---@param context contextTable
---@param victim minetest.ObjectRef|vector.Vector
---@param split_param splitParam[]
---@param i integer
---@return vector.Vector? result
---@return string? err
---@nodiscard
function better_commands.get_tp_rot(context, victim, split_param, i)
local victim_rot = table.copy(context.rot)
if split_param[i] then
local yaw_pitch
local facing
if split_param[i].type == "number" or split_param[i].type == "relative" then
yaw_pitch = true
elseif split_param[i].type == "relative" then
yaw_pitch = true
elseif split_param[i][3] == "facing" then
facing = true
else
return nil, S("Invalid rotation")
end
if yaw_pitch then
return better_commands.handle_vector2_rot(victim_rot, split_param, i)
elseif facing and split_param[i+1] then
if split_param[i+1].type == "selector" then
local targets, err = better_commands.parse_selector(split_param[i+1], context, true)
if err or not targets then return nil, err end
local target_pos = targets[1].is_player and targets[1]:get_pos() or targets[1]
---@diagnostic disable-next-line: param-type-mismatch
victim_rot = better_commands.point_at_pos(victim, target_pos)
elseif split_param[i+1][3] == "entity" and split_param[i+2].type == "selector" then
local targets, err = better_commands.parse_selector(split_param[i+2], context, true)
if err or not targets then return nil, err end
local target_pos = targets[1].is_player and targets[1]:get_pos() or targets[1]
---@diagnostic disable-next-line: param-type-mismatch
victim_rot = better_commands.point_at_pos(victim, target_pos)
else
local target_pos, err = better_commands.parse_pos(split_param, i+1, context)
if err or not target_pos then return nil, err end
victim_rot = better_commands.point_at_pos(victim, target_pos)
end
end
if yaw_pitch or facing then
return victim_rot
end
end
return victim_rot
end
---Gets a player's gamemode
---@param player minetest.Player
---@return string?
function better_commands.get_gamemode(player)
if player.is_player and player:is_player() then
local gamemode
if better_commands.mcl then
gamemode = mcl_gamemode.get_gamemode(player)
else
gamemode = minetest.is_creative_enabled(player:get_player_name()) and "creative" or "survival"
end
return gamemode
end
end
better_commands.gamemode_aliases = {
[0] = "survival",
[1] = "creative",
["0"] = "survival", -- not sure whether these are necessary
["1"] = "creative",
s = "survival",
c = "creative",
}