Mods in den Spieleordner reingeschoben. So richtig tief.
This commit is contained in:
parent
b4b6c08f4f
commit
f7bc25a670
1674 changed files with 56056 additions and 530 deletions
361
mods/leads/util.lua
Normal file
361
mods/leads/util.lua
Normal file
|
@ -0,0 +1,361 @@
|
|||
--[[
|
||||
Leads — Adds leads for transporting animals to Minetest.
|
||||
Copyright © 2023, Silver Sandstone <@SilverSandstone@craftodon.social>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
|
||||
|
||||
--- Generic utility functions.
|
||||
-- @module util
|
||||
|
||||
|
||||
leads.util = {};
|
||||
|
||||
leads.util.rng = PseudoRandom(0x4C656164);
|
||||
|
||||
local has_objectuuids = minetest.get_modpath('objectuuids') ~= nil;
|
||||
|
||||
|
||||
--- Checks if the object is a mob.
|
||||
-- @param object [ObjectRef] The object to check.
|
||||
-- @return [boolean] true if the object is a mob.
|
||||
function leads.util.is_mob(object)
|
||||
local entity = object:get_luaentity();
|
||||
if not entity then
|
||||
return false;
|
||||
end;
|
||||
|
||||
-- Explicitly marked as an animal:
|
||||
local result = entity._leads_is_mob or entity._leads_is_animal;
|
||||
if result ~= nil then
|
||||
return result;
|
||||
end;
|
||||
|
||||
-- Mobs (Redo) and Repixture:
|
||||
if entity.health then
|
||||
return true;
|
||||
end;
|
||||
|
||||
-- Creatura:
|
||||
if entity._creatura_mob then
|
||||
return true;
|
||||
end;
|
||||
|
||||
-- Exile:
|
||||
if entity.hp and (entity.max_health or entity.max_hp) then
|
||||
return true;
|
||||
end;
|
||||
|
||||
return false;
|
||||
end;
|
||||
|
||||
leads.util.is_animal = leads.util.is_mob; -- Deprecated alias.
|
||||
|
||||
|
||||
--- Tiles a texture to the specified size.
|
||||
-- @param texture [string] The texture to tile.
|
||||
-- @param src_width [integer] The input texture's width.
|
||||
-- @param src_height [integer] The input texture's height.
|
||||
-- @param out_width [integer] The resulting texture's width.
|
||||
-- @param out_height [integer] The resulting texture's height.
|
||||
-- @return [string] A texture string.
|
||||
function leads.util.tile_texture(texture, src_width, src_height, out_width, out_height)
|
||||
texture = leads.util.escape_texture(('(%s)^[resize:%dx%d'):format(texture, src_width, src_height));
|
||||
local parts = {'[combine:', out_width, 'x', out_height};
|
||||
local y = 0;
|
||||
while y < out_height do
|
||||
local x = 0;
|
||||
while x < out_width do
|
||||
table.insert(parts, (':%d,0=%s'):format(x, texture));
|
||||
x = x + src_width;
|
||||
end;
|
||||
y = y + src_height;
|
||||
end;
|
||||
return table.concat(parts, '');
|
||||
end;
|
||||
|
||||
|
||||
--- Escapes a texture for use with [combine.
|
||||
-- @param texture [string] A texture string.
|
||||
-- @return [string] An escaped texture string.
|
||||
function leads.util.escape_texture(texture)
|
||||
return string.gsub(texture, '[\\^:]', function(char) return '\\' .. char; end);
|
||||
end;
|
||||
|
||||
|
||||
--- Serialises the identity (not the state) of an object reference.
|
||||
-- @param obj [ObjectRef|nil] The object to serialise.
|
||||
-- @return [table|nil] A table identifying the object, or nil if the reference is invalid.
|
||||
function leads.util.serialise_objref(obj)
|
||||
if not obj then
|
||||
return nil;
|
||||
end;
|
||||
|
||||
local result = {pos = obj:get_pos()};
|
||||
|
||||
if has_objectuuids then
|
||||
result.uuid = objectuuids.get_uuid(obj);
|
||||
end;
|
||||
|
||||
if minetest.is_player(obj) then
|
||||
result.player_name = obj:get_player_name();
|
||||
else
|
||||
local entity = obj:get_luaentity();
|
||||
if not entity then
|
||||
return nil;
|
||||
end;
|
||||
result.name = entity.name;
|
||||
end;
|
||||
|
||||
return result;
|
||||
end;
|
||||
|
||||
|
||||
--- Deserialises an object ID previously returned from `serialise_objref()`, trying to identify the original object.
|
||||
-- @param id [table|nil] A table identifying an object.
|
||||
-- @return [ObjectRef|nil] An object matching the ID, or nil if no such object was found.
|
||||
function leads.util.deserialise_objref(id)
|
||||
if not id then
|
||||
return nil;
|
||||
end;
|
||||
|
||||
-- Objects are identified by UUID where possible:
|
||||
if has_objectuuids and id.uuid then
|
||||
return objectuuids.get_object_by_uuid(id.uuid);
|
||||
end;
|
||||
|
||||
-- Without UUIDs, players are identified by name:
|
||||
if id.player_name then
|
||||
return minetest.get_player_by_name(id.player_name);
|
||||
end;
|
||||
|
||||
-- Minetest doesn't provide any way to persistently identify Lua entities,
|
||||
-- so the best we can do is look for an entity with the correct name near
|
||||
-- the saved position.
|
||||
if not id.pos then
|
||||
return nil;
|
||||
end;
|
||||
local pos = vector.new(id.pos);
|
||||
|
||||
local range = 3;
|
||||
local range_min = pos:offset(-range, -range, -range);
|
||||
local range_max = pos:offset( range, range, range);
|
||||
local objects = minetest.get_objects_in_area(range_min, range_max);
|
||||
local best_object = nil;
|
||||
local best_distance = math.huge;
|
||||
for __, object in ipairs(objects) do
|
||||
local entity = object:get_luaentity();
|
||||
if entity and (id.name == nil or entity.name == id.name) then
|
||||
local distance = object:get_pos():distance(pos);
|
||||
if distance <= 0.0 then
|
||||
return object;
|
||||
elseif distance < best_distance then
|
||||
best_distance = distance;
|
||||
best_object = object;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
return best_object;
|
||||
end;
|
||||
|
||||
|
||||
--- Checks if two objrefs refer to the same object, which may be a player or entity.
|
||||
-- @param obj1 [ObjectRef|nil] The first object to compare.
|
||||
-- @param obj2 [ObjectRef|nil] The second object to compare.
|
||||
-- @return [boolean] true if obj1 and obj2 reference the same object.
|
||||
function leads.util.is_same_object(obj1, obj2)
|
||||
if not (obj1 and obj2) then
|
||||
return false;
|
||||
end;
|
||||
|
||||
local obj1_is_player = minetest.is_player(obj1);
|
||||
local obj2_is_player = minetest.is_player(obj2);
|
||||
if obj1_is_player ~= obj2_is_player then
|
||||
return false;
|
||||
end;
|
||||
|
||||
if obj1_is_player then
|
||||
return obj1:get_player_name() == obj2:get_player_name();
|
||||
else
|
||||
return obj1:get_luaentity() == obj2:get_luaentity();
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
--- Returns the relative attachment position for the specified object.
|
||||
-- @param object [ObjectRef|nil] The player or entity to check.
|
||||
-- @return [vector] The attachment offset as a vector relative to the object's origin.
|
||||
function leads.util.get_attach_offset(object)
|
||||
local properties = object and object:get_properties();
|
||||
if not properties then
|
||||
return vector.zero();
|
||||
end;
|
||||
local hitbox = (properties.physical and properties.collisionbox) or (properties.pointable and properties.selectionbox) or {};
|
||||
local bottom = hitbox[2] or 0;
|
||||
local top = hitbox[5] or 0;
|
||||
return vector.new(0, (bottom + top) / 2, 0);
|
||||
end;
|
||||
|
||||
|
||||
--- Finds the first item available for crafting.
|
||||
-- @param ... [string] Any number of prefixed node/item IDs.
|
||||
-- @return [string|nil] One of the specified IDs, or nil.
|
||||
function leads.util.first_available_item(...)
|
||||
for __, name in ipairs{...} do
|
||||
if name == '' or string.match(name, '^group:.*') or minetest.registered_items[name] then
|
||||
return name;
|
||||
end;
|
||||
end;
|
||||
return nil;
|
||||
end;
|
||||
|
||||
|
||||
--- Returns a string describing an object, for debugging.
|
||||
-- @param object [ObjectRef] An object reference.
|
||||
-- @return [string] A string describing the object.
|
||||
function leads.util.describe_object(object)
|
||||
if minetest.is_player(object) then
|
||||
return ('[Player %q]'):format(object:get_player_name());
|
||||
end;
|
||||
|
||||
local entity = object:get_luaentity();
|
||||
if entity then
|
||||
return ('[LuaEntity %q]'):format(entity.name);
|
||||
end;
|
||||
|
||||
return '[Unknown object]';
|
||||
end;
|
||||
|
||||
|
||||
--- Prevents the player from interacting for some time.
|
||||
-- @param name [string] The name of the player.
|
||||
-- @param time [number] How long to block interactions, in seconds.
|
||||
function leads.util.block_player_interaction(name, time)
|
||||
local function _callback()
|
||||
leads.interaction_blockers[name] = nil;
|
||||
end;
|
||||
|
||||
local old_timer = leads.interaction_blockers[name];
|
||||
if old_timer then
|
||||
old_timer:cancel();
|
||||
end;
|
||||
|
||||
leads.interaction_blockers[name] = minetest.after(time, _callback);
|
||||
end;
|
||||
|
||||
|
||||
--- Figures out the type of an object.
|
||||
-- @param object [ObjectRef] The object to check.
|
||||
-- @return [ObjectType] The type of the object.
|
||||
function leads.util.get_object_type(object)
|
||||
-- Check player:
|
||||
if minetest.is_player(object) then
|
||||
return leads.ObjectType.PLAYER;
|
||||
end;
|
||||
|
||||
-- Get entity:
|
||||
local entity = object:get_luaentity();
|
||||
if not entity then
|
||||
return leads.ObjectType.OTHER;
|
||||
end;
|
||||
|
||||
-- Custom type override:
|
||||
local override = entity._leads_type or leads.custom_object_types[entity.name];
|
||||
if override then
|
||||
return override;
|
||||
end;
|
||||
|
||||
-- Get entity definition:
|
||||
local def = minetest.registered_entities[entity.name];
|
||||
if not def then
|
||||
return leads.ObjectType.OTHER;
|
||||
end;
|
||||
|
||||
-- Check Creatura (assumed to be animals):
|
||||
if entity._creatura_mob then
|
||||
return leads.ObjectType.ANIMAL;
|
||||
end;
|
||||
|
||||
-- Check Mobs API type:
|
||||
if def.type == 'animal' then
|
||||
return leads.ObjectType.ANIMAL;
|
||||
elseif def.type == 'monster' then
|
||||
return leads.ObjectType.MONSTER;
|
||||
elseif def.type == 'npc' then
|
||||
return leads.ObjectType.NPC;
|
||||
end;
|
||||
|
||||
return leads.ObjectType.OTHER;
|
||||
end;
|
||||
|
||||
|
||||
--- Gets the owner of an object.
|
||||
-- @param object [ObjectRef] The object to check.
|
||||
-- @return [string] The owner's name, or '' for unowned.
|
||||
function leads.util.get_object_owner(object)
|
||||
local entity = object:get_luaentity();
|
||||
if not entity then
|
||||
return '';
|
||||
end;
|
||||
|
||||
return entity.owner or '';
|
||||
end;
|
||||
|
||||
|
||||
--- Calculates the mass of a player or entity.
|
||||
-- @param object [ObjectRef] The object to check.
|
||||
-- @return [number] The object's mass, in an abstract unit.
|
||||
function leads.util.get_object_mass(object)
|
||||
local entity = object:get_luaentity();
|
||||
|
||||
local mass = entity and entity._leads_mass;
|
||||
if mass then
|
||||
return mass;
|
||||
end;
|
||||
|
||||
local density = entity and entity._leads_density or 1;
|
||||
local properties = object:get_properties() or {};
|
||||
local hitbox = properties.collisionbox or properties.selectionbox;
|
||||
if not hitbox then
|
||||
return density;
|
||||
end;
|
||||
|
||||
local width = math.abs(hitbox[4] - hitbox[1]);
|
||||
local height = math.abs(hitbox[5] - hitbox[2]);
|
||||
local depth = math.abs(hitbox[6] - hitbox[3]);
|
||||
return width * height * depth * density;
|
||||
end;
|
||||
|
||||
|
||||
--- Clamps a value within the specified range.
|
||||
-- @param value [number] The value to clamp.
|
||||
-- @param min [number] The lower bound.
|
||||
-- @param max [number] The upper bound.
|
||||
-- @return [number] A number between lower and upper.
|
||||
function leads.util.clamp(value, min, max)
|
||||
if value < min then
|
||||
return min;
|
||||
elseif value > max then
|
||||
return max;
|
||||
else
|
||||
return value;
|
||||
end;
|
||||
end;
|
Loading…
Add table
Add a link
Reference in a new issue