2021-04-23 01:12:02 +02:00
|
|
|
local minetest_after = minetest.after
|
|
|
|
local minetest_sound_play = minetest.sound_play
|
2021-04-23 23:10:44 +02:00
|
|
|
local minetest_dir_to_yaw = minetest.dir_to_yaw
|
2021-04-23 01:12:02 +02:00
|
|
|
|
2021-04-23 02:07:30 +02:00
|
|
|
local math_floor = math.floor
|
|
|
|
local math_min = math.min
|
|
|
|
local math_random = math.random
|
2021-04-23 01:12:02 +02:00
|
|
|
|
2021-04-21 18:42:34 +02:00
|
|
|
local vector_direction = vector.direction
|
2021-04-23 02:07:30 +02:00
|
|
|
local vector_multiply = vector.multiply
|
2021-04-21 16:53:20 +02:00
|
|
|
|
2021-04-19 03:22:39 +02:00
|
|
|
mobs.feed_tame = function(self)
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2021-04-15 21:04:55 +02:00
|
|
|
-- Code to execute before custom on_rightclick handling
|
|
|
|
local on_rightclick_prefix = function(self, clicker)
|
2021-04-19 03:22:39 +02:00
|
|
|
|
2021-04-15 21:04:55 +02:00
|
|
|
local item = clicker:get_wielded_item()
|
|
|
|
|
|
|
|
-- Name mob with nametag
|
|
|
|
if not self.ignores_nametag and item:get_name() == "mcl_mobs:nametag" then
|
|
|
|
|
|
|
|
local tag = item:get_meta():get_string("name")
|
|
|
|
if tag ~= "" then
|
|
|
|
if string.len(tag) > MAX_MOB_NAME_LENGTH then
|
|
|
|
tag = string.sub(tag, 1, MAX_MOB_NAME_LENGTH)
|
|
|
|
end
|
|
|
|
self.nametag = tag
|
|
|
|
|
|
|
|
update_tag(self)
|
|
|
|
|
|
|
|
if not mobs.is_creative(clicker:get_player_name()) then
|
|
|
|
item:take_item()
|
|
|
|
clicker:set_wielded_item(item)
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2021-04-21 17:50:22 +02:00
|
|
|
-- I have no idea what this does
|
2021-04-15 21:04:55 +02:00
|
|
|
mobs.create_mob_on_rightclick = function(on_rightclick)
|
|
|
|
return function(self, clicker)
|
2021-04-23 02:25:58 +02:00
|
|
|
--don't allow rightclicking dead mobs
|
|
|
|
if self.health <= 0 then
|
|
|
|
return
|
|
|
|
end
|
2021-04-15 21:04:55 +02:00
|
|
|
local stop = on_rightclick_prefix(self, clicker)
|
|
|
|
if (not stop) and (on_rightclick) then
|
|
|
|
on_rightclick(self, clicker)
|
|
|
|
end
|
|
|
|
end
|
2021-04-21 16:23:51 +02:00
|
|
|
end
|
|
|
|
|
2021-04-21 17:50:22 +02:00
|
|
|
|
2021-04-21 16:23:51 +02:00
|
|
|
-- deal damage and effects when mob punched
|
2021-04-21 16:53:20 +02:00
|
|
|
mobs.mob_punch = function(self, hitter, tflp, tool_capabilities, dir)
|
|
|
|
|
2021-04-23 02:25:58 +02:00
|
|
|
--don't do anything if the mob is already dead
|
|
|
|
if self.health <= 0 then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2021-04-24 00:34:20 +02:00
|
|
|
--neutral passive mobs switch to neutral hostile
|
|
|
|
if self.neutral then
|
|
|
|
--drop in variables for attacking (stops crash)
|
|
|
|
self.attacking = hitter
|
|
|
|
self.punch_timer = 0
|
|
|
|
self.hostile = true
|
|
|
|
--hostile_cooldown timer is initialized here
|
|
|
|
self.hostile_cooldown_timer = self.hostile_cooldown
|
|
|
|
|
|
|
|
--initialize the group attack (check for other mobs in area, make them neutral hostile)
|
|
|
|
if self.group_attack then
|
|
|
|
mobs.group_attack_initialization(self)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-04-23 23:10:44 +02:00
|
|
|
--turn skittish mobs away and RUN
|
|
|
|
if self.skittish then
|
2021-04-21 17:50:22 +02:00
|
|
|
|
2021-04-23 23:10:44 +02:00
|
|
|
self.state = "run"
|
2021-04-21 17:50:22 +02:00
|
|
|
|
2021-04-23 23:10:44 +02:00
|
|
|
self.run_timer = 5 --arbitrary 5 seconds
|
2021-04-21 17:50:22 +02:00
|
|
|
|
2021-04-23 23:10:44 +02:00
|
|
|
local pos1 = self.object:get_pos()
|
|
|
|
pos1.y = 0
|
|
|
|
local pos2 = hitter:get_pos()
|
|
|
|
pos2.y = 0
|
|
|
|
|
|
|
|
|
|
|
|
local dir = vector_direction(pos2,pos1)
|
|
|
|
|
|
|
|
local yaw = minetest_dir_to_yaw(dir)
|
|
|
|
|
|
|
|
self.yaw = yaw
|
2021-04-21 16:53:20 +02:00
|
|
|
end
|
|
|
|
|
2021-04-21 16:23:51 +02:00
|
|
|
|
|
|
|
-- custom punch function
|
|
|
|
if self.do_punch then
|
|
|
|
-- when false skip going any further
|
|
|
|
if self.do_punch(self, hitter, tflp, tool_capabilities, dir) == false then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-04-23 02:20:56 +02:00
|
|
|
--don't do damage until pause timer resets
|
|
|
|
if self.pause_timer > 0 then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2021-04-23 01:12:02 +02:00
|
|
|
|
2021-04-21 16:23:51 +02:00
|
|
|
-- error checking when mod profiling is enabled
|
|
|
|
if not tool_capabilities then
|
2021-04-23 01:12:02 +02:00
|
|
|
minetest.log("warning", "[mobs_mc] Mod profiling enabled, damage not enabled")
|
2021-04-21 16:23:51 +02:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2021-04-23 01:12:02 +02:00
|
|
|
local is_player = hitter:is_player()
|
2021-04-21 16:23:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
-- punch interval
|
|
|
|
local weapon = hitter:get_wielded_item()
|
2021-04-23 02:20:56 +02:00
|
|
|
|
2021-04-21 16:23:51 +02:00
|
|
|
local punch_interval = 1.4
|
|
|
|
|
|
|
|
-- exhaust attacker
|
|
|
|
if mod_hunger and is_player then
|
|
|
|
mcl_hunger.exhaust(hitter:get_player_name(), mcl_hunger.EXHAUST_ATTACK)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- calculate mob damage
|
|
|
|
local damage = 0
|
|
|
|
local armor = self.object:get_armor_groups() or {}
|
|
|
|
local tmp
|
|
|
|
|
2021-04-23 02:20:56 +02:00
|
|
|
--calculate damage groups
|
2021-04-23 01:12:02 +02:00
|
|
|
for group,_ in pairs( (tool_capabilities.damage_groups or {}) ) do
|
2021-04-23 02:20:56 +02:00
|
|
|
damage = damage + (tool_capabilities.damage_groups[group] or 0) * ((armor[group] or 0) / 100.0)
|
2021-04-21 16:23:51 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
if weapon then
|
|
|
|
local fire_aspect_level = mcl_enchanting.get_enchantment(weapon, "fire_aspect")
|
|
|
|
if fire_aspect_level > 0 then
|
|
|
|
mcl_burning.set_on_fire(self.object, fire_aspect_level * 4)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- check for tool immunity or special damage
|
|
|
|
for n = 1, #self.immune_to do
|
|
|
|
if self.immune_to[n][1] == weapon:get_name() then
|
|
|
|
damage = self.immune_to[n][2] or 0
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- healing
|
|
|
|
if damage <= -1 then
|
|
|
|
self.health = self.health - math_floor(damage)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
if tool_capabilities then
|
|
|
|
punch_interval = tool_capabilities.full_punch_interval or 1.4
|
|
|
|
end
|
|
|
|
|
|
|
|
-- add weapon wear manually
|
|
|
|
-- Required because we have custom health handling ("health" property)
|
2021-04-23 02:07:30 +02:00
|
|
|
--minetest_is_creative_enabled("") ~= true --removed for now
|
|
|
|
if tool_capabilities then
|
2021-04-21 16:23:51 +02:00
|
|
|
if tool_capabilities.punch_attack_uses then
|
|
|
|
-- Without this delay, the wear does not work. Quite hacky ...
|
|
|
|
minetest_after(0, function(name)
|
|
|
|
local player = minetest.get_player_by_name(name)
|
|
|
|
if not player then return end
|
|
|
|
local weapon = hitter:get_wielded_item(player)
|
|
|
|
local def = weapon:get_definition()
|
|
|
|
if def.tool_capabilities and def.tool_capabilities.punch_attack_uses then
|
|
|
|
local wear = math_floor(65535/tool_capabilities.punch_attack_uses)
|
|
|
|
weapon:add_wear(wear)
|
|
|
|
hitter:set_wielded_item(weapon)
|
|
|
|
end
|
|
|
|
end, hitter:get_player_name())
|
|
|
|
end
|
|
|
|
end
|
2021-04-23 04:08:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
--if player is falling multiply damage by 1.5
|
|
|
|
--critical hit
|
|
|
|
if hitter:get_velocity().y < 0 then
|
|
|
|
damage = damage * 1.5
|
|
|
|
mobs.critical_effect(self)
|
|
|
|
end
|
2021-04-21 16:23:51 +02:00
|
|
|
|
|
|
|
|
|
|
|
-- only play hit sound and show blood effects if damage is 1 or over; lower to 0.1 to ensure armor works appropriately.
|
|
|
|
if damage >= 0.1 then
|
|
|
|
|
2021-04-23 22:22:34 +02:00
|
|
|
minetest_sound_play("default_punch", {
|
|
|
|
object = self.object,
|
|
|
|
max_hear_distance = 16
|
|
|
|
}, true)
|
2021-04-21 16:23:51 +02:00
|
|
|
|
|
|
|
-- do damage
|
|
|
|
self.health = self.health - damage
|
|
|
|
|
2021-04-23 23:44:49 +02:00
|
|
|
|
|
|
|
--0.4 seconds until you can hurt the mob again
|
|
|
|
self.pause_timer = 0.4
|
|
|
|
|
|
|
|
--don't do knockback from a rider
|
|
|
|
for _,obj in pairs(self.object:get_children()) do
|
|
|
|
if obj == hitter then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-04-23 02:20:56 +02:00
|
|
|
-- knock back effect
|
|
|
|
local velocity = self.object:get_velocity()
|
|
|
|
|
|
|
|
--2d direction
|
|
|
|
local pos1 = self.object:get_pos()
|
|
|
|
pos1.y = 0
|
|
|
|
local pos2 = hitter:get_pos()
|
|
|
|
pos2.y = 0
|
2021-04-21 16:23:51 +02:00
|
|
|
|
2021-04-23 02:20:56 +02:00
|
|
|
local dir = vector.direction(pos2,pos1)
|
2021-04-23 02:07:30 +02:00
|
|
|
|
2021-04-23 02:20:56 +02:00
|
|
|
local up = 3
|
2021-04-21 16:23:51 +02:00
|
|
|
|
2021-04-23 02:20:56 +02:00
|
|
|
-- if already in air then dont go up anymore when hit
|
|
|
|
if velocity.y ~= 0 then
|
|
|
|
up = 0
|
|
|
|
end
|
2021-04-21 16:23:51 +02:00
|
|
|
|
2021-04-23 02:07:30 +02:00
|
|
|
|
2021-04-23 02:20:56 +02:00
|
|
|
--0.75 for perfect distance to not be too easy, and not be too hard
|
|
|
|
local multiplier = 0.75
|
2021-04-21 16:23:51 +02:00
|
|
|
|
2021-04-23 02:20:56 +02:00
|
|
|
-- check if tool already has specific knockback value
|
|
|
|
local knockback_enchant = mcl_enchanting.get_enchantment(hitter:get_wielded_item(), "knockback")
|
|
|
|
if knockback_enchant and knockback_enchant > 0 then
|
|
|
|
multiplier = knockback_enchant + 1 --(starts from 1, 1 would be no change)
|
|
|
|
end
|
2021-04-21 16:23:51 +02:00
|
|
|
|
2021-04-23 23:29:23 +02:00
|
|
|
--do this to sure you can punch a mob back when
|
|
|
|
--it's coming for you
|
2021-04-23 23:27:08 +02:00
|
|
|
if self.hostile then
|
|
|
|
multiplier = multiplier + 2
|
2021-04-23 23:29:23 +02:00
|
|
|
end
|
2021-04-23 02:07:30 +02:00
|
|
|
|
2021-04-23 02:20:56 +02:00
|
|
|
dir = vector_multiply(dir,multiplier)
|
2021-04-23 02:07:30 +02:00
|
|
|
|
2021-04-23 02:20:56 +02:00
|
|
|
dir.y = up
|
2021-04-21 16:23:51 +02:00
|
|
|
|
2021-04-23 23:27:08 +02:00
|
|
|
--add the velocity
|
|
|
|
self.object:add_velocity(dir)
|
2021-04-21 16:23:51 +02:00
|
|
|
|
2021-04-23 02:20:56 +02:00
|
|
|
end
|
2021-04-21 16:23:51 +02:00
|
|
|
end
|
2021-04-21 18:42:34 +02:00
|
|
|
|
|
|
|
--do internal per mob projectile calculations
|
|
|
|
mobs.shoot_projectile = function(self)
|
|
|
|
|
|
|
|
local pos1 = self.object:get_pos()
|
|
|
|
--add mob eye height
|
|
|
|
pos1.y = pos1.y + self.eye_height
|
|
|
|
|
|
|
|
local pos2 = self.attacking:get_pos()
|
|
|
|
--add player eye height
|
|
|
|
pos2.y = pos2.y + self.attacking:get_properties().eye_height
|
|
|
|
|
|
|
|
--get direction
|
|
|
|
local dir = vector_direction(pos1,pos2)
|
|
|
|
|
|
|
|
--call internal shoot_arrow function
|
|
|
|
self.shoot_arrow(self,pos1,dir)
|
|
|
|
end
|