Compare commits

...

9 Commits

Author SHA1 Message Date
f12519cd5f dont force mc2 or faroming, proper checks of player on mount logic
* proper checks on mount logic
* do not force mc2 or farming on local items
  for craft recipes to work
* seems minimized nodes at do_states, add older ones
2023-07-28 23:05:30 -04:00
90292f9af2 Merge master of upstream https://notabug.org/TenPlus1/mobs_redo into main
* can now ride mobs redo mobs in mineclone2 (must be fine tuned)
  better MineClone2 compatibility for api, items and crafts
* harder player checks (must be in better way)
  add check to see what hit mob, player or entity for immune_to
* monsters will always attack creatura mobs,
  anything else will defend it's owner if attacked
* change mobs_swing.ogg to mono
* can now add nodes to 'runaway_from' table
* add damage_per_second nil check
* on_breed spawns child a little higher
*  remove htimer, tweak update_tag()
* add mtobjid mod to optional dependencies, stop mob count going
* added check to be sure creatura mod enabled
* if max_light_damage is 16 then use natural light
  value to damage mob
* min/max light set to 15, only kill mob with natural light
* better way to reset pause_timer with "stand" order
* pause timer reset, mobs ordered to "stand", fix knockback
* fix group attack anim reset, tidy code
* simplify yaw clamping
2023-07-28 22:13:44 -04:00
tenplus1
8201c165e7 can now ride mobs redo mobs in mineclone2 2023-07-27 12:54:46 +01:00
tenplus1
07dce8208b harden is_player checks 2023-07-26 17:43:40 +01:00
tenplus1
e2ee5c62c7 tidy compat 2023-07-26 14:15:03 +01:00
tenplus1
f31cf75b24 better MineClone2 compatibility for api, items and crafts 2023-07-26 13:43:06 +01:00
tenplus1
3106c4c859 change mobs_swing.ogg to mono 2023-07-20 12:25:27 +01:00
tenplus1
dcc702848f can now add nodes to 'runaway_from' table 2023-07-15 08:58:18 +01:00
tenplus1
cc0798b617 add check to see what hit mob, player or entity for immune_to 2023-07-02 17:17:14 +01:00
8 changed files with 169 additions and 92 deletions

101
api.lua
View File

@ -27,10 +27,15 @@ local use_cmi = minetest.global_exists("cmi")
mobs = {
mod = "redo",
version = "20230613",
version = "20230726",
intllib = S,
invis = minetest.global_exists("invisibility") and invisibility or {}
invis = minetest.global_exists("invisibility") and invisibility or {},
node_ice = "default:ice"
node_snow = minetest.registered_aliases["mapgen_snow"] or "default:snow" or "mcl_core:snow",
node_dirt = minetest.registered_aliases["mapgen_dirt"] or "default:dirt" or "mcl_core:dirt"
}
mobs.fallback_node = mobs.node_dirt
-- localize common functions
local pi = math.pi
@ -135,12 +140,6 @@ local aoc_range = tonumber(settings:get("active_block_range")) * 16
local creatura = minetest.get_modpath("creatura") and
settings:get_bool("mobs_attack_creatura") == true
-- default nodes
local node_ice = "default:ice"
local node_snowblock = "default:snowblock"
local node_snow = "default:snow"
mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "default:dirt"
mobs.mob_class = {
stepheight = 1.1,
@ -265,6 +264,15 @@ local get_distance = function(a, b)
end
-- are we a real player ?
local function is_player(player)
if player and type(player) == "userdata" and minetest.is_player(player) then
return true
end
end
-- collision function based on jordan4ibanez' open_ai mod
function mob_class:collision()
@ -275,7 +283,7 @@ function mob_class:collision()
for _,object in ipairs(minetest.get_objects_inside_radius(pos, width)) do
if object:is_player() then
if is_player(object) then
local pos2 = object:get_pos()
local vec = {x = pos.x - pos2.x, z = pos.z - pos2.z}
@ -858,7 +866,7 @@ function mob_class:item_drop()
-- was mob killed by player?
local death_by_player = self.cause_of_death
and self.cause_of_death.puncher
and self.cause_of_death.puncher:is_player()
and is_player(self.cause_of_death.puncher)
-- check for tool 'looting_level' under tool_capabilities as default, or use
-- meta string 'looting_level' if found (max looting level is 3).
@ -1312,7 +1320,7 @@ function mob_class:do_jump()
and (self.walk_chance == 0 or minetest.registered_items[self.looking_at].walkable)
and not blocked
and not self.facing_fence
and self.looking_at ~= node_snow then
and self.looking_at ~= mobs.node_snow then
local v = self.object:get_velocity()
@ -2015,7 +2023,7 @@ function mob_class:general_attack()
local ent = objs[n]:get_luaentity()
-- are we a player?
if objs[n]:is_player() then
if is_player(objs[n]) then
-- if player invisible or mob cannot attack then remove from list
if not damage_enabled
@ -2110,9 +2118,10 @@ function mob_class:do_runaway_from()
local min_dist = self.view_range + 1
local objs = minetest.get_objects_inside_radius(s, self.view_range)
-- loop through entities surrounding mob
for n = 1, #objs do
if objs[n]:is_player() then
if is_player(objs[n]) then
pname = objs[n]:get_player_name()
@ -2161,6 +2170,20 @@ function mob_class:do_runaway_from()
self.state = "runaway"
self.runaway_timer = 3
self.following = nil
return
end
-- check for nodes to runaway from
objs = minetest.find_node_near(s, self.view_range, self.runaway_from, true)
if objs then
yaw_to_pos(self, objs, 3)
self.state = "runaway"
self.runaway_timer = 3
self.following = nil
end
end
@ -2203,7 +2226,7 @@ function mob_class:follow_flop()
end
else
-- stop following player if not holding specific item or mob is horny
if self.following and self.following:is_player()
if self.following and is_player(self.following)
and (self:follow_holding(self.following) == false or self.horny) then
self.following = nil
end
@ -2216,7 +2239,7 @@ function mob_class:follow_flop()
local s = self.object:get_pos()
local p
if self.following:is_player() then
if is_player(self.following) then
p = self.following:get_pos()
elseif self.following.object then
p = self.following.object:get_pos()
@ -2316,22 +2339,13 @@ function mob_class:do_states(dtime)
if is_node_dangerous(self, self.standing_in) then
local s = self.object:get_pos()
local lp
local grps = {}
-- is there something I need to avoid?
if self.water_damage > 0
and self.lava_damage > 0 then
if self.water_damage > 0 then table.insert(grps, "group:water", "group:igniter") end
if self.fire_damage > 0 then table.insert(grps, "group:fire") end
if self.lava_damage > 0 then table.insert(grps, "group:lava", "group:igniter") end
lp = minetest.find_node_near(s, 1, {"group:water", "group:igniter"})
elseif self.water_damage > 0 then
lp = minetest.find_node_near(s, 1, {"group:water"})
elseif self.lava_damage > 0 then
lp = minetest.find_node_near(s, 1, {"group:igniter"})
end
local lp = minetest.find_node_near(s, 1, grps)
if lp then
@ -2340,7 +2354,7 @@ function mob_class:do_states(dtime)
lp = minetest.find_nodes_in_area_under_air(
{x = s.x - 5, y = s.y , z = s.z - 5},
{x = s.x + 5, y = s.y + 2, z = s.z + 5},
{"group:soil", "group:stone", "group:sand", node_ice, node_snowblock})
{"group:cracky", "group:crumbly", "group:choppy", "group:solid", "group:stone", "group:sand", node_ice, node_snowblock})
-- did we find land?
if lp and #lp > 0 then
@ -2372,7 +2386,7 @@ function mob_class:do_states(dtime)
for n = 1, #objs do
if objs[n]:is_player() then
if is_player(objs[n]) then
lp = objs[n]:get_pos()
break
end
@ -2484,7 +2498,7 @@ function mob_class:do_states(dtime)
or not self.attack
or not self.attack:get_pos()
or self.attack:get_hp() <= 0
or (self.attack:is_player()
or (is_player(self.attack)
and is_invisible(self, self.attack:get_player_name())) then
--print(" ** stop attacking **", self.name, self.health, dist, self.view_range)
@ -2875,7 +2889,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
if self.protected then
-- did player hit mob and if so is it in protected area
if hitter:is_player() then
if is_player(hitter) then
local player_name = hitter:get_player_name()
@ -2933,10 +2947,17 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
end
end
-- check if hit by player item or entity
local hit_item = weapon_def.name
if not is_player(hitter) then
hit_item = hitter:get_luaentity().name
end
-- check for tool immunity or special damage
for n = 1, #self.immune_to do
if self.immune_to[n][1] == weapon_def.name then
if self.immune_to[n][1] == hit_item then
damage = self.immune_to[n][2] or 0
@ -3036,7 +3057,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage)
local entity = hitter and hitter:get_luaentity()
-- check if arrow from same mob, if so then do no damage
if (entity and entity.name ~= self.arrow) or hitter:is_player() then
if (entity and entity.name ~= self.arrow) or is_player(hitter) then
self.health = self.health - floor(damage)
end
end
@ -3399,7 +3420,7 @@ function mob_class:mob_expire(pos, dtime)
for n = 1, #objs do
if objs[n]:is_player() then
if is_player(objs[n]) then
self.lifetimer = 20
@ -3779,7 +3800,7 @@ local function count_mobs(pos, type)
for n = 1, #objs do
if not objs[n]:is_player() then
if not is_player(objs[n]) then
ent = objs[n]:get_luaentity()
@ -4101,7 +4122,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter
for n = 1, #objs do
if objs[n]:is_player() then
if is_player(objs[n]) then
--print("--- player too close", name)
return
end
@ -4297,7 +4318,7 @@ function mobs:register_arrow(name, def)
for _,player in pairs(minetest.get_objects_inside_radius(pos, 1.0)) do
if self.hit_player and player:is_player() then
if self.hit_player and is_player(player) then
self:hit_player(player)
@ -4545,7 +4566,7 @@ end
function mobs:capture_mob(
self, clicker, chance_hand, chance_net, chance_lasso, force_take, replacewith)
if not self or not clicker:is_player() or not clicker:get_inventory() then
if not self or not is_player(clicker) or not clicker:get_inventory() then
return false
end

View File

@ -154,8 +154,8 @@ functions needed for the mob to work properly which contains the following:
'friendly_fire` when set to false, mobs will not be able to harm other
mobs of the same type with friendly fire arrows.
Defaults to true.
'runaway_from' contains a table with mob names to run away from, add
"player" to list to runaway from player also.
'runaway_from' contains a table with mob names or nodesto run away
from, add "player" to list to runaway from player also.
'ignore_invisibility' When true mob will still be able to see and attack
player even if invisible (invisibility mod only).
'blood_amount' contains the number of blood droplets to appear when

View File

@ -1,5 +1,24 @@
local S = mobs.intllib
local mc2 = minetest.get_modpath("mcl_core")
-- recipe items
local items = {
paper = (mc2 and "mcl_core:paper") or "default:paper",
dye_black = (mc2 and "mcl_dye:black") or "dye:black",
string = (mc2 and "mcl_mobitems:string") or "farming:string",
stick = (mc2 and "mcl_core:stick") or "default:stick",
diamond = (mc2 and "mcl_core:diamond") or "default:diamond",
steel_ingot = (mc2 and "mcl_core:iron_ingot") or "default:steel_ingot",
gold_block = (mc2 and "mcl_core:goldblock") or "default:goldblock",
diamond_block = (mc2 and "mcl_core:diamondblock") or "default:diamondblock",
stone = (mc2 and "mcl_core:stone") or "default:stone",
mese_crystal = (mc2 and "mcl_core:gold_ingot") or "default:mese_crystal",
wood = (mc2 and "mcl_core:wood") or "default:wood",
fence_wood = (mc2 and "group:fence_wood") or "default:fence_wood",
meat_raw = (mc2 and "mcl_mobitems:beef") or "group:food_meat_raw",
meat_cooked = (mc2 and "mcl_mobitems:cooked_beef") or "group:food_meat",
}
-- name tag
minetest.register_craftitem("mobs:nametag", {
@ -11,7 +30,9 @@ minetest.register_craftitem("mobs:nametag", {
if minetest.get_modpath("dye") and minetest.get_modpath("farming") then
minetest.register_craft({
output = "mobs:nametag",
recipe = {{"default:paper", "dye:black", "farming:string"}}
recipe = {
{ items.paper, items.dye_black, items.string }
}
})
end
@ -57,9 +78,9 @@ if minetest.get_modpath("farming") then
minetest.register_craft({
output = "mobs:lasso",
recipe = {
{"farming:string", "", "farming:string"},
{"", "default:diamond", ""},
{"farming:string", "", "farming:string"}
{ items.string, "", items.string},
{ "", items.diamond, "" },
{ items.string, "", items.string }
}
})
end
@ -78,9 +99,9 @@ if minetest.get_modpath("farming") then
minetest.register_craft({
output = "mobs:net",
recipe = {
{"group:stick", "", "group:stick"},
{"group:stick", "", "group:stick"},
{"farming:string", "group:stick", "farming:string"}
{ items.stick, "", items.stick },
{ items.stick, "", items.stick },
{ items.string, items.stick, items.string }
}
})
end
@ -95,8 +116,8 @@ minetest.register_tool("mobs:shears", {
minetest.register_craft({
output = "mobs:shears",
recipe = {
{"", "default:steel_ingot", ""},
{"", "group:stick", "default:steel_ingot"}
{ "", items.steel_ingot, "" },
{ "", items.stick, items.steel_ingot }
}
})
@ -110,9 +131,9 @@ minetest.register_craftitem("mobs:protector", {
minetest.register_craft({
output = "mobs:protector",
recipe = {
{"default:stone", "default:stone", "default:stone"},
{"default:stone", "default:goldblock", "default:stone"},
{"default:stone", "default:stone", "default:stone"}
{ items.stone, items.stone, items.stone },
{ items.stone, items.gold_block, items.stone },
{ items.stone, items.stone, items.stone }
}
})
@ -126,9 +147,9 @@ minetest.register_craftitem("mobs:protector2", {
minetest.register_craft({
output = "mobs:protector2",
recipe = {
{"mobs:protector", "default:mese_crystal", "mobs:protector"},
{"default:mese_crystal", "default:diamondblock", "default:mese_crystal"},
{"mobs:protector", "default:mese_crystal", "mobs:protector"}
{ "mobs:protector", items.mese_crystal, "mobs:protector" },
{ items.mese_crystal, items.diamond_block, items.mese_crystal },
{ "mobs:protector", items.mese_crystal, "mobs:protector" }
}
})
@ -143,8 +164,8 @@ minetest.register_craft({
output = "mobs:saddle",
recipe = {
{"mobs:leather", "mobs:leather", "mobs:leather"},
{"mobs:leather", "default:steel_ingot", "mobs:leather"},
{"mobs:leather", "default:steel_ingot", "mobs:leather"}
{"mobs:leather", items.steel_ingot, "mobs:leather"},
{"mobs:leather", items.steel_ingot, "mobs:leather"}
}
})
@ -197,7 +218,7 @@ minetest.register_craft({
output = "mobs:fence_top 12",
recipe = {
{"group:wood", "group:wood", "group:wood"},
{"", "default:fence_wood", ""}
{"", items.fence_wood, ""}
}
})
@ -372,9 +393,9 @@ minetest.register_node("mobs:meatblock", {
minetest.register_craft({
output = "mobs:meatblock",
recipe = {
{"group:food_meat", "group:food_meat", "group:food_meat"},
{"group:food_meat", "group:food_meat", "group:food_meat"},
{"group:food_meat", "group:food_meat", "group:food_meat"}
{ items.meat_cooked, items.meat_cooked, items.meat_cooked },
{ items.meat_cooked, items.meat_cooked, items.meat_cooked },
{ items.meat_cooked, items.meat_cooked, items.meat_cooked }
}
})
@ -392,9 +413,9 @@ minetest.register_node("mobs:meatblock_raw", {
minetest.register_craft({
output = "mobs:meatblock_raw",
recipe = {
{"group:food_meat_raw", "group:food_meat_raw", "group:food_meat_raw"},
{"group:food_meat_raw", "group:food_meat_raw", "group:food_meat_raw"},
{"group:food_meat_raw", "group:food_meat_raw", "group:food_meat_raw"}
{ items.meat_raw, items.meat_raw, items.meat_raw },
{ items.meat_raw, items.meat_raw, items.meat_raw },
{ items.meat_raw, items.meat_raw, items.meat_raw }
}
})

View File

@ -7,6 +7,7 @@ minetest.register_privilege("peaceful_player", {
give_to_singleplayer = false
})
-- Mob API
dofile(path .. "/api.lua")

View File

@ -1,6 +1,29 @@
-- lib_mount by Blert2112 (edited by TenPlus1)
local is_50 = minetest.get_modpath("player_api") -- 5.x compatibility
--[[ one of these is needed (for now) to ride mobs, otherwise no riding for you
if not minetest.get_modpath("default")
or not minetest.get_modpath("player_api") then
function mobs.attach() end
function mobs.detach() end
function mobs.fly() end
function mobs.drive() end
return
end
]]
local is_pa = minetest.get_modpath("player_api") -- 5.x compatibility
local is_mc2 = minetest.get_modpath("mcl_mobs") -- MineClone2 compatibility
-- are we a real player ?
local function is_player(player)
if player and type(player) == "userdata" and minetest.is_player(player) then
return true
end
end
local abs, cos, floor, sin, sqrt, pi =
math.abs, math.cos, math.floor, math.sin, math.sqrt, math.pi
@ -75,7 +98,7 @@ end
local function force_detach(player)
if not player then return end
if not is_player(player) then return end
local attached_to = player:get_attach()
@ -85,8 +108,7 @@ local function force_detach(player)
local entity = attached_to:get_luaentity()
if entity and entity.driver
and entity.driver == player then
if entity and entity.driver and entity.driver == player then
entity.driver = nil
end
@ -94,9 +116,12 @@ local function force_detach(player)
local name = player:get_player_name()
if is_50 then
if is_pa then
player_api.player_attached[name] = false
player_api.set_animation(player, "stand", 30)
elseif is_mc2 then
mcl_player.player_attached[player:get_player_name()] = false
mcl_player.player_set_animation(player, "stand", 30)
else
default.player_attached[name] = false
default.player_set_animation(player, "stand", 30)
@ -151,8 +176,7 @@ local function find_free_pos(pos)
local def = minetest.registered_nodes[node.name]
if def and not def.walkable and
def.liquidtype == "none" then
if def and not def.walkable and def.liquidtype == "none" then
return npos
end
end
@ -164,6 +188,8 @@ end
function mobs.attach(entity, player)
if not is_player(player) then return end
entity.player_rotation = entity.player_rotation or {x = 0, y = 0, z = 0}
entity.driver_attach_at = entity.driver_attach_at or {x = 0, y = 0, z = 0}
entity.driver_eye_offset = entity.driver_eye_offset or {x = 0, y = 0, z = 0}
@ -182,8 +208,10 @@ function mobs.attach(entity, player)
force_detach(player)
if is_50 then
if is_pa then
player_api.player_attached[player:get_player_name()] = true
elseif is_mc2 then
mcl_player.player_attached[player:get_player_name()] = true
else
default.player_attached[player:get_player_name()] = true
end
@ -200,10 +228,12 @@ function mobs.attach(entity, player)
minetest.after(0.2, function()
if player and player:is_player() then
if is_player(player) then
if is_50 then
if is_pa then
player_api.set_animation(player, "sit", 30)
elseif is_mc2 then
mcl_player.player_set_animation(player, "sit_mount" , 30)
else
default.player_set_animation(player, "sit", 30)
end
@ -220,7 +250,7 @@ function mobs.detach(player)
minetest.after(0.1, function()
if player and player:is_player() then
if is_player(player) then
local pos = find_free_pos(player:get_pos())
@ -251,13 +281,11 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
local ctrl = entity.driver:get_player_control()
-- move forwards
if ctrl.up then
if ctrl.up then -- move forwards
entity.v = entity.v + entity.accel * dtime
-- move backwards
elseif ctrl.down then
elseif ctrl.down then -- move backwards
if entity.max_speed_reverse == 0 and entity.v == 0 then
return
@ -287,8 +315,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
if can_fly then
-- fly up
if ctrl.jump then
if ctrl.jump then -- fly up
velo.y = velo.y + 1
@ -301,8 +328,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
if velo.y < 0 then velo.y = 0 end
end
-- fly down
if ctrl.sneak then
if ctrl.sneak then -- fly down
velo.y = velo.y - 1
@ -315,8 +341,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
if velo.y > 0 then velo.y = 0 end
end
else
-- jump
if ctrl.jump then
if ctrl.jump then -- jump
if velo.y == 0 then
velo.y = velo.y + entity.jump_height
@ -449,12 +474,10 @@ end
function mobs.fly(entity, _, speed, shoots, arrow, moving_anim, stand_anim)
local ctrl = entity.driver:get_player_control() ; if not ctrl then return end
local velo = entity.object:get_velocity()
local velo = entity.object:get_velocity() ; if not velo then return end
local dir = entity.driver:get_look_dir()
local yaw = entity.driver:get_look_horizontal() + 1.57
if not ctrl or not velo then return end
if ctrl.up then
entity.object:set_velocity({

View File

@ -37,6 +37,8 @@ Zeg9, ExeterDad and AspireMint.
* Refactored do_jump and added get_nodes function
* Many bug fixes and tweaks to improve performance
* Added 'mobs_attack_creatura' setting so that monsters can attack Creatura mobs
* Nodes can be added to 'runaway_from' table
* Better Mineclone2 compatibility with api, items and recipes
### Version 1.56

Binary file not shown.

View File

@ -1,8 +1,17 @@
local S = mobs.intllib
-- mob spawner
-- are we a real player ?
local function is_player(player)
if player and type(player) == "userdata" and minetest.is_player(player) then
return true
end
end
-- mob spawner
local spawner_default = "mobs_animal:pumba 10 15 0 0 0"
minetest.register_node("mobs:spawner", {
@ -148,7 +157,7 @@ minetest.register_abm({
for _, oir in pairs(objsp) do
if oir:is_player() then
if is_player(oir) then
in_range = 1