251 lines
6.9 KiB
Lua
251 lines
6.9 KiB
Lua
local minetest_line_of_sight = minetest.line_of_sight
|
|
--local minetest_dir_to_yaw = minetest.dir_to_yaw
|
|
local minetest_yaw_to_dir = minetest.yaw_to_dir
|
|
local minetest_get_node = minetest.get_node
|
|
local minetest_get_item_group = minetest.get_item_group
|
|
local minetest_get_objects_inside_radius = minetest.get_objects_inside_radius
|
|
local minetest_get_node_or_nil = minetest.get_node_or_nil
|
|
local minetest_registered_nodes = minetest.registered_nodes
|
|
local minetest_get_connected_players = minetest.get_connected_players
|
|
|
|
local vector_new = vector.new
|
|
local vector_add = vector.add
|
|
local vector_multiply = vector.multiply
|
|
local vector_distance = vector.distance
|
|
|
|
local table_copy = table.copy
|
|
|
|
local math_abs = math.abs
|
|
|
|
-- default function when mobs are blown up with TNT
|
|
--[[local function do_tnt(obj, damage)
|
|
obj.object:punch(obj.object, 1.0, {
|
|
full_punch_interval = 1.0,
|
|
damage_groups = {fleshy = damage},
|
|
}, nil)
|
|
return false, true, {}
|
|
end]]
|
|
|
|
--a fast function to be able to detect only players without using objects_in_radius
|
|
mobs.detect_closest_player_within_radius = function(self, line_of_sight, radius, object_height_adder)
|
|
local pos1 = self.object:get_pos()
|
|
local players_in_area = {}
|
|
local winner_player = nil
|
|
local players_detected = 0
|
|
|
|
--get players in radius
|
|
for _,player in pairs(minetest.get_connected_players()) do
|
|
if player and player:get_hp() > 0 then
|
|
|
|
local pos2 = player:get_pos()
|
|
|
|
local distance = vector_distance(pos1,pos2)
|
|
|
|
if distance <= radius then
|
|
if line_of_sight then
|
|
--must add eye height or stuff breaks randomly because of
|
|
--seethrough nodes being a blocker (like grass)
|
|
if minetest_line_of_sight(
|
|
vector_new(pos1.x, pos1.y + object_height_adder, pos1.z),
|
|
vector_new(pos2.x, pos2.y + player:get_properties().eye_height, pos2.z)
|
|
) then
|
|
players_detected = players_detected + 1
|
|
players_in_area[player] = distance
|
|
end
|
|
else
|
|
players_detected = players_detected + 1
|
|
players_in_area[player] = distance
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--return if there's no one near by
|
|
if players_detected <= 0 then --handle negative numbers for some crazy error that could possibly happen
|
|
return nil
|
|
end
|
|
|
|
--do a default radius max
|
|
local shortest_distance = radius + 1
|
|
|
|
--sort through players and find the closest player
|
|
for player,distance in pairs(players_in_area) do
|
|
if distance < shortest_distance then
|
|
shortest_distance = distance
|
|
winner_player = player
|
|
end
|
|
end
|
|
return winner_player
|
|
end
|
|
|
|
|
|
--check if a mob needs to jump
|
|
mobs.jump_check = function(self,dtime)
|
|
|
|
local pos = self.object:get_pos()
|
|
pos.y = pos.y + 0.1
|
|
local dir = minetest_yaw_to_dir(self.yaw)
|
|
|
|
local collisionbox = self.object:get_properties().collisionbox
|
|
local radius = collisionbox[4] + 0.5
|
|
|
|
vector_multiply(dir, radius)
|
|
|
|
--only jump if there's a node and a non-solid node above it
|
|
local test_dir = vector_add(pos,dir)
|
|
|
|
local green_flag_1 = minetest_get_item_group(minetest_get_node(test_dir).name, "solid") ~= 0
|
|
|
|
test_dir.y = test_dir.y + 1
|
|
|
|
local green_flag_2 = minetest_get_item_group(minetest_get_node(test_dir).name, "solid") == 0
|
|
|
|
if green_flag_1 and green_flag_2 then
|
|
--can jump over node
|
|
return 1
|
|
elseif green_flag_1 and not green_flag_2 then
|
|
--wall in front of mob
|
|
return 2
|
|
end
|
|
--nothing to jump over
|
|
return 0
|
|
end
|
|
|
|
-- a helper function to quickly turn neutral passive mobs hostile
|
|
local turn_hostile = function(self,detected_mob)
|
|
--drop in variables for attacking (stops crash)
|
|
detected_mob.punch_timer = 0
|
|
--set to hostile
|
|
detected_mob.hostile = true
|
|
--hostile_cooldown timer is initialized here
|
|
detected_mob.hostile_cooldown_timer = detected_mob.hostile_cooldown
|
|
--set target to the same
|
|
detected_mob.attacking = self.attacking
|
|
end
|
|
|
|
--allow hostile mobs to signal to other mobs
|
|
--to switch from neutal passive to neutral hostile
|
|
mobs.group_attack_initialization = function(self)
|
|
|
|
--get basic data
|
|
local friends_list
|
|
|
|
if self.group_attack == true then
|
|
friends_list = {self.name}
|
|
else
|
|
friends_list = table_copy(self.group_attack)
|
|
end
|
|
|
|
local objects_in_area = minetest_get_objects_inside_radius(self.object:get_pos(), self.view_range)
|
|
|
|
--get the player's name
|
|
local name = self.attacking:get_player_name()
|
|
|
|
--re-use local variable
|
|
local detected_mob
|
|
|
|
--run through mobs in viewing distance
|
|
for _,object in pairs(objects_in_area) do
|
|
if object and object:get_luaentity() then
|
|
detected_mob = object:get_luaentity()
|
|
-- only alert members of same mob or friends
|
|
if detected_mob._cmi_is_mob and detected_mob.state ~= "attack" and detected_mob.owner ~= name then
|
|
if detected_mob.name == self.name then
|
|
turn_hostile(self,detected_mob)
|
|
else
|
|
for _,id in pairs(friends_list) do
|
|
if detected_mob.name == id then
|
|
turn_hostile(self,detected_mob)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--THIS NEEDS TO BE RE-IMPLEMENTED AS A GLOBAL HIT IN MOB_PUNCH!!
|
|
-- have owned mobs attack player threat
|
|
--if obj.owner == name and obj.owner_loyal then
|
|
-- do_attack(obj, self.object)
|
|
--end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- check if within physical map limits (-30911 to 30927)
|
|
-- within_limits, wmin, wmax = nil, -30913, 30928
|
|
mobs.within_limits = function(pos, radius)
|
|
local wmin, wmax
|
|
if mcl_mapgen then
|
|
if mcl_mapgen.EDGE_MIN and mcl_mapgen.EDGE_MAX then
|
|
wmin, wmax = mcl_mapgen.EDGE_MIN, mcl_mapgen.EDGE_MAX
|
|
return pos
|
|
and (pos.x - radius) > wmin and (pos.x + radius) < wmax
|
|
and (pos.y - radius) > wmin and (pos.y + radius) < wmax
|
|
and (pos.z - radius) > wmin and (pos.z + radius) < wmax
|
|
end
|
|
end
|
|
end
|
|
|
|
-- get node but use fallback for nil or unknown
|
|
mobs.node_ok = function(pos, fallback)
|
|
|
|
fallback = fallback or mobs.fallback_node
|
|
|
|
local node = minetest_get_node_or_nil(pos)
|
|
|
|
if node and minetest_registered_nodes[node.name] then
|
|
return node
|
|
end
|
|
|
|
return minetest_registered_nodes[fallback]
|
|
end
|
|
|
|
|
|
--a teleport functoin
|
|
mobs.teleport = function(self, target)
|
|
if self.do_teleport then
|
|
if self.do_teleport(self, target) == false then
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
--a function used for despawning mobs
|
|
mobs.check_for_player_within_area = function(self, radius)
|
|
local pos1 = self.object:get_pos()
|
|
if not pos1 then return end
|
|
--get players in radius
|
|
for _,player in pairs(minetest_get_connected_players()) do
|
|
if player and player:get_hp() > 0 then
|
|
local pos2 = player:get_pos()
|
|
local distance = vector_distance(pos1,pos2)
|
|
if distance < radius then
|
|
--found a player
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
--did not find a player
|
|
return false
|
|
end
|
|
|
|
|
|
--a simple helper function for mobs following
|
|
mobs.get_2d_distance = function(pos1,pos2)
|
|
pos1.y = 0
|
|
pos2.y = 0
|
|
return vector_distance(pos1, pos2)
|
|
end
|
|
|
|
-- fall damage onto solid ground
|
|
mobs.calculate_fall_damage = function(self)
|
|
if self.old_velocity and self.old_velocity.y < -7 and self.object:get_velocity().y == 0 then
|
|
local vel = self.object:get_velocity()
|
|
if vel then
|
|
local damage = math_abs(self.old_velocity.y + 7) * 2
|
|
self.pause_timer = 0.4
|
|
self.health = self.health - damage
|
|
end
|
|
end
|
|
end |