diff --git a/api.lua b/api.lua index f815c8b..e859222 100644 --- a/api.lua +++ b/api.lua @@ -27,10 +27,14 @@ 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_snow = minetest.registered_aliases["mapgen_snow"] or "mcl_core:snow", + node_dirt = minetest.registered_aliases["mapgen_dirt"] or "mcl_core:dirt" } +mobs.fallback_node = mobs.node_dirt + -- localize common functions local pi = math.pi @@ -135,12 +139,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 +263,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 +282,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 +865,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 +1319,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 +2022,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 +2117,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 +2169,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 +2225,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 +2238,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 +2338,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") end + if self.fire_damage > 0 then table.insert(grps, "group:fire") end + if self.lava_damage > 0 then table.insert(grps, "group:lava") 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 +2353,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"}) -- did we find land? if lp and #lp > 0 then @@ -2372,7 +2385,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 +2497,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 +2888,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 +2946,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 +3056,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 +3419,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 +3799,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 +4121,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 +4317,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 +4565,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 diff --git a/api.txt b/api.txt index cc6514a..cf04172 100644 --- a/api.txt +++ b/api.txt @@ -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 diff --git a/crafts.lua b/crafts.lua index aa8edca..ca92c2a 100644 --- a/crafts.lua +++ b/crafts.lua @@ -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", { @@ -8,12 +27,12 @@ minetest.register_craftitem("mobs:nametag", { groups = {flammable = 2, nametag = 1} }) -if minetest.get_modpath("dye") and minetest.get_modpath("farming") then - minetest.register_craft({ - output = "mobs:nametag", - recipe = {{"default:paper", "dye:black", "farming:string"}} - }) -end +minetest.register_craft({ + output = "mobs:nametag", + recipe = { + { items.paper, items.dye_black, items.string } + } +}) -- leather minetest.register_craftitem("mobs:leather", { @@ -52,17 +71,14 @@ minetest.register_tool("mobs:lasso", { groups = {flammable = 2} }) -if minetest.get_modpath("farming") then - - minetest.register_craft({ - output = "mobs:lasso", - recipe = { - {"farming:string", "", "farming:string"}, - {"", "default:diamond", ""}, - {"farming:string", "", "farming:string"} - } - }) -end +minetest.register_craft({ + output = "mobs:lasso", + recipe = { + { items.string, "", items.string}, + { "", items.diamond, "" }, + { items.string, "", items.string } + } +}) minetest.register_alias("mobs:magic_lasso", "mobs:lasso") @@ -73,17 +89,14 @@ minetest.register_tool("mobs:net", { groups = {flammable = 2} }) -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"} - } - }) -end +minetest.register_craft({ + output = "mobs:net", + recipe = { + { items.stick, "", items.stick }, + { items.stick, "", items.stick }, + { items.string, items.stick, items.string } + } +}) -- shears (right click to shear animal) minetest.register_tool("mobs:shears", { @@ -95,8 +108,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 +123,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 +139,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 +156,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 +210,7 @@ minetest.register_craft({ output = "mobs:fence_top 12", recipe = { {"group:wood", "group:wood", "group:wood"}, - {"", "default:fence_wood", ""} + {"", items.fence_wood, ""} } }) @@ -372,9 +385,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 +405,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 } } }) diff --git a/init.lua b/init.lua index 4632238..0205b39 100644 --- a/init.lua +++ b/init.lua @@ -7,6 +7,7 @@ minetest.register_privilege("peaceful_player", { give_to_singleplayer = false }) + -- Mob API dofile(path .. "/api.lua") diff --git a/mount.lua b/mount.lua index 88cbcd0..296b63f 100644 --- a/mount.lua +++ b/mount.lua @@ -1,6 +1,20 @@ -- lib_mount by Blert2112 (edited by TenPlus1) +--[[ 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_50 = minetest.get_modpath("player_api") -- 5.x compatibility +local is_mc2 = minetest.get_modpath("mcl_mobs") -- MineClone2 compatibility local abs, cos, floor, sin, sqrt, pi = math.abs, math.cos, math.floor, math.sin, math.sqrt, math.pi @@ -75,9 +89,7 @@ end local function force_detach(player) - if not player then return end - - local attached_to = player:get_attach() + local attached_to = player and player:get_attach() if not attached_to then return @@ -85,8 +97,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 @@ -97,6 +108,9 @@ local function force_detach(player) if is_50 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 +165,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 @@ -162,6 +175,15 @@ local function find_free_pos(pos) 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 + + function mobs.attach(entity, player) entity.player_rotation = entity.player_rotation or {x = 0, y = 0, z = 0} @@ -184,6 +206,8 @@ function mobs.attach(entity, player) if is_50 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 +224,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 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 @@ -251,13 +277,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 +311,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 +324,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 +337,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 +470,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({ diff --git a/readme.MD b/readme.MD index 5366da4..f291242 100644 --- a/readme.MD +++ b/readme.MD @@ -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 diff --git a/sounds/mobs_swing.ogg b/sounds/mobs_swing.ogg index ffe6a9c..30609f0 100644 Binary files a/sounds/mobs_swing.ogg and b/sounds/mobs_swing.ogg differ diff --git a/spawner.lua b/spawner.lua index bfcdcba..4840d1c 100644 --- a/spawner.lua +++ b/spawner.lua @@ -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