diff --git a/mods/ENTITIES/mobs/api.lua b/mods/ENTITIES/mobs/api.lua index fb8f7e3cc..584d86c4b 100644 --- a/mods/ENTITIES/mobs/api.lua +++ b/mods/ENTITIES/mobs/api.lua @@ -1,9 +1,9 @@ --- Mobs Api (4th August 2017) +-- Mobs Api mobs = {} mobs.mod = "redo" -mobs.version = "20170804" +mobs.version = "20171018" -- Intllib @@ -15,13 +15,21 @@ mobs.intllib = S -- CMI support check local use_cmi = minetest.global_exists("cmi") + -- Invisibility mod check mobs.invis = {} -if rawget(_G, "invisibility") then +if minetest.global_exists("invisibility") then mobs.invis = invisibility end +-- creative check +local creative_mode_cache = minetest.settings:get_bool("creative_mode") +function mobs.is_creative(name) + return creative_mode_cache or minetest.check_player_privs(name, {creative = true}) +end + + -- localize math functions local pi = math.pi local square = math.sqrt @@ -44,19 +52,27 @@ end -- Load settings -local damage_enabled = minetest.setting_getbool("enable_damage") -local peaceful_only = minetest.setting_getbool("only_peaceful_mobs") -local disable_blood = minetest.setting_getbool("mobs_disable_blood") -local creative = minetest.setting_getbool("creative_mode") -local spawn_protected = minetest.setting_getbool("mobs_spawn_protected") ~= false -local remove_far = minetest.setting_getbool("remove_far_mobs") -local difficulty = tonumber(minetest.setting_get("mob_difficulty")) or 1.0 +local damage_enabled = minetest.settings:get_bool("enable_damage") +local peaceful_only = minetest.settings:get_bool("only_peaceful_mobs") +local disable_blood = minetest.settings:get_bool("mobs_disable_blood") +local creative = minetest.settings:get_bool("creative_mode") +local spawn_protected = minetest.settings:get_bool("mobs_spawn_protected") ~= false +local remove_far = minetest.settings:get_bool("remove_far_mobs") +local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0 local show_health = false -local max_per_block = tonumber(minetest.setting_get("max_objects_per_block") or 99) +local max_per_block = tonumber(minetest.settings:get("max_objects_per_block") or 99) + +-- Peaceful mode message so players will know there are no monsters +if peaceful_only then + minetest.register_on_joinplayer(function(player) + minetest.chat_send_player(player:get_player_name(), + S("** Peaceful Mode Active - No Monsters Will Spawn")) + end) +end -- calculate aoc range for mob count -local aosrb = tonumber(minetest.setting_get("active_object_send_range_blocks")) -local abr = tonumber(minetest.setting_get("active_block_range")) +local aosrb = tonumber(minetest.settings:get("active_object_send_range_blocks")) +local abr = tonumber(minetest.settings:get("active_block_range")) local aoc_range = max(aosrb, abr) * 16 -- pathfinding settings @@ -104,7 +120,7 @@ end -- move mob in facing direction local set_velocity = function(self, v) - local yaw = (self.object:getyaw() or 0) + self.rotate + local yaw = (self.object:get_yaw() or 0) + self.rotate self.object:setvelocity({ x = sin(yaw) * -v, @@ -114,7 +130,7 @@ local set_velocity = function(self, v) end --- get overall speed of mob +-- calculate mob velocity local get_velocity = function(self) local v = self.object:getvelocity() @@ -123,7 +139,7 @@ local get_velocity = function(self) end --- set yaw +-- set and return valid yaw local set_yaw = function(self, yaw) if not yaw or yaw ~= yaw then @@ -166,7 +182,7 @@ function mobs:set_animation(self, anim) end --- this is a faster way to calculate distance +-- calculate distance local get_distance = function(a, b) local x, y, z = a.x - b.x, a.y - b.y, a.z - b.z @@ -252,8 +268,10 @@ local flight_check = function(self, pos_w) local nod = self.standing_in local def = minetest.registered_nodes[nod] + if not def then return false end -- nil check + if type(self.fly_in) == "string" - and (nod == self.fly_in or def.liquid_alternative_flowing ~= "") then + and nod == self.fly_in then return true @@ -261,18 +279,25 @@ local flight_check = function(self, pos_w) for _,fly_in in pairs(self.fly_in) do - if nod == fly_in or def.liquid_alternative_flowing ~= "" then + if nod == fly_in then return true end end end + -- stops mobs getting stuck inside stairs and plantlike nodes + if def.drawtype ~= "airlike" + and def.drawtype ~= "liquid" + and def.drawtype ~= "flowingliquid" then + return true + end + return false end --- particle effects +-- custom particle effects local effect = function(pos, amount, texture, min_size, max_size, radius, gravity, glow) radius = radius or 2 @@ -333,7 +358,7 @@ local item_drop = function(self, cooked) if self.child then return end local obj, item, num - local pos = self.object:getpos() + local pos = self.object:get_pos() self.drops = self.drops or {} -- nil check @@ -411,6 +436,7 @@ local check_for_death = function(self, cause, cmi_cause) return false end + -- dropped cooked item if mob died in lava if cause == "lava" then item_drop(self, true) else @@ -419,7 +445,7 @@ local check_for_death = function(self, cause, cmi_cause) mob_sound(self, self.sounds.death) - local pos = self.object:getpos() + local pos = self.object:get_pos() -- execute custom death function if self.on_die then @@ -499,10 +525,10 @@ local is_at_cliff = function(self) return false end - local yaw = self.object:getyaw() + local yaw = self.object:get_yaw() local dir_x = -sin(yaw) * (self.collisionbox[4] + 0.5) local dir_z = cos(yaw) * (self.collisionbox[4] + 0.5) - local pos = self.object:getpos() + local pos = self.object:get_pos() local ypos = pos.y + self.collisionbox[2] -- just above floor if minetest.line_of_sight( @@ -549,7 +575,7 @@ local do_env_damage = function(self) update_tag(self) end - local pos = self.object:getpos() + local pos = self.object:get_pos() self.time_of_day = minetest.get_timeofday() @@ -559,11 +585,11 @@ local do_env_damage = function(self) return end - -- daylight above ground + -- bright light harms mob if self.light_damage ~= 0 - and pos.y > 0 - and self.time_of_day > 0.2 - and self.time_of_day < 0.8 +-- and pos.y > 0 +-- and self.time_of_day > 0.2 +-- and self.time_of_day < 0.8 and (minetest.get_node_light(pos) or 0) > 12 then self.health = self.health - self.light_damage @@ -633,7 +659,7 @@ local do_env_damage = function(self) if check_for_death(self, "dps", {type = "environment", pos = pos, node = self.standing_in}) then return end end - +--[[ --- suffocation inside solid node if self.suffocation ~= 0 and nodef.walkable == true @@ -645,7 +671,7 @@ local do_env_damage = function(self) if check_for_death(self, "suffocation", {type = "environment", pos = pos, node = self.standing_in}) then return end end - +]] check_for_death(self, "", {type = "unknown"}) end @@ -660,6 +686,8 @@ local do_jump = function(self) return false end + self.facing_fence = false + -- something stopping us while moving? if self.state ~= "stand" and get_velocity(self) > 0.5 @@ -667,8 +695,8 @@ local do_jump = function(self) return false end - local pos = self.object:getpos() - local yaw = self.object:getyaw() + local pos = self.object:get_pos() + local yaw = self.object:get_yaw() -- what is mob standing on? pos.y = pos.y + self.collisionbox[2] - 0.2 @@ -699,20 +727,24 @@ local do_jump = function(self) --print ("in front:", nod.name, pos.y + 0.5) - if (minetest.registered_items[nod.name].walkable - and not nod.name:find("fence") - and not nod.name:find("gate")) - or self.walk_chance == 0 then + if self.walk_chance == 0 + or minetest.registered_items[nod.name].walkable then - local v = self.object:getvelocity() + if not nod.name:find("fence") + and not nod.name:find("gate") then - v.y = self.jump_height + local v = self.object:getvelocity() - set_animation(self, "jump") -- only when defined + v.y = self.jump_height - self.object:setvelocity(v) + set_animation(self, "jump") -- only when defined - mob_sound(self, self.sounds.jump) + self.object:setvelocity(v) + + mob_sound(self, self.sounds.jump) + else + self.facing_fence = true + end return true end @@ -731,7 +763,7 @@ local entity_physics = function(pos, radius) for n = 1, #objs do - obj_pos = objs[n]:getpos() + obj_pos = objs[n]:get_pos() dist = get_distance(pos, obj_pos) if dist < 1 then dist = 1 end @@ -743,7 +775,7 @@ local entity_physics = function(pos, radius) objs[n]:punch(objs[n], 1.0, { full_punch_interval = 1.0, damage_groups = {fleshy = damage}, - }, pos) -- was nil + }, pos) end end @@ -798,12 +830,17 @@ local breed = function(self) collisionbox = self.base_colbox, }) - -- jump when fully grown so not to fall into ground - self.object:setvelocity({ - x = 0, - y = self.jump_height, - z = 0 - }) + -- custom function when child grows up + if self.on_grown then + self.on_grown(self) + else + -- jump when fully grown so as not to fall into ground + self.object:setvelocity({ + x = 0, + y = self.jump_height, + z = 0 + }) + end end return @@ -822,11 +859,11 @@ local breed = function(self) end end - -- find another same animal who is also horny and mate if close enough + -- find another same animal who is also horny and mate if nearby if self.horny == true and self.hornytimer <= 40 then - local pos = self.object:getpos() + local pos = self.object:get_pos() effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, "heart.png", 3, 4, 1, 0.1) @@ -876,14 +913,27 @@ local breed = function(self) -- spawn baby minetest.after(5, function() + -- custom breed function + if self.on_breed then + + -- when false skip going any further + if self.on_breed(self, ent) == false then + return + end + else + effect(pos, 15, "tnt_smoke.png", 1, 2, 2, 15, 5) + end + local mob = minetest.add_entity(pos, self.name) local ent2 = mob:get_luaentity() local textures = self.base_texture + -- using specific child texture (if found) if self.child_texture then textures = self.child_texture[1] end + -- and resize to half height mob:set_properties({ textures = textures, visual_size = { @@ -899,6 +949,7 @@ local breed = function(self) self.base_colbox[6] * .5, }, }) + -- tamed and owned by parents' owner ent2.child = true ent2.tamed = true ent2.owner = self.owner @@ -1021,7 +1072,7 @@ local smart_mobs = function(self, s, p, dist, dtime) s.y = sground.y + 1 end - local p1 = self.attack:getpos() + local p1 = self.attack:get_pos() p1.x = floor(p1.x + 0.5) p1.y = floor(p1.y + 0.5) @@ -1094,7 +1145,7 @@ local smart_mobs = function(self, s, p, dist, dtime) else -- dig 2 blocks to make door toward player direction - local yaw1 = self.object:getyaw() + pi / 2 + local yaw1 = self.object:get_yaw() + pi / 2 local p1 = { x = s.x + cos(yaw1), y = s.y, @@ -1185,7 +1236,7 @@ local monster_attack = function(self) return end - local s = self.object:getpos() + local s = self.object:get_pos() local p, sp, dist local player, obj, min_player local type, name = "", "" @@ -1219,8 +1270,8 @@ local monster_attack = function(self) and (type == "player" or type == "npc" or (type == "animal" and self.attack_animals == true)) then - s = self.object:getpos() - p = player:getpos() + s = self.object:get_pos() + p = player:get_pos() sp = s -- aim higher to make looking up hills more realistic @@ -1259,7 +1310,7 @@ local npc_attack = function(self) end local p, sp, obj, min_player - local s = self.object:getpos() + local s = self.object:get_pos() local min_dist = self.view_range + 1 local objs = minetest.get_objects_inside_radius(s, self.view_range) @@ -1269,7 +1320,7 @@ local npc_attack = function(self) if obj and obj.type == "monster" then - p = obj.object:getpos() + p = obj.object:get_pos() dist = get_distance(p, s) @@ -1296,12 +1347,12 @@ local follow_flop = function(self) and self.state ~= "attack" and self.state ~= "runaway" then - local s = self.object:getpos() + local s = self.object:get_pos() local players = minetest.get_connected_players() for n = 1, #players do - if get_distance(players[n]:getpos(), s) < self.view_range + if get_distance(players[n]:get_pos(), s) < self.view_range and not mobs.invis[ players[n]:get_player_name() ] then self.following = players[n] @@ -1335,16 +1386,16 @@ local follow_flop = function(self) -- follow that thing if self.following then - local s = self.object:getpos() + local s = self.object:get_pos() local p if self.following:is_player() then - p = self.following:getpos() + p = self.following:get_pos() elseif self.following.object then - p = self.following.object:getpos() + p = self.following.object:get_pos() end if p then @@ -1387,7 +1438,7 @@ local follow_flop = function(self) -- swimmers flop when out of their element, and swim again when back in if self.fly then - local s = self.object:getpos() + local s = self.object:get_pos() if not flight_check(self, s) then self.state = "flop" @@ -1442,13 +1493,13 @@ local do_states = function(self, dtime) if random(1, 4) == 1 then local lp = nil - local s = self.object:getpos() + local s = self.object:get_pos() local objs = minetest.get_objects_inside_radius(s, 3) for n = 1, #objs do if objs[n]:is_player() then - lp = objs[n]:getpos() + lp = objs[n]:get_pos() break end end @@ -1465,8 +1516,6 @@ local do_states = function(self, dtime) if lp.x > s.x then yaw = yaw + pi end else --- yaw = (random(0, 360) - 180) / 180 * pi - yaw = yaw + random(-0.5, 0.5) end @@ -1481,6 +1530,7 @@ local do_states = function(self, dtime) or self.order ~= "stand" then if self.walk_chance ~= 0 + and self.facing_fence ~= true and random(1, 100) <= self.walk_chance and is_at_cliff(self) == false then @@ -1501,7 +1551,7 @@ local do_states = function(self, dtime) elseif self.state == "walk" then - local s = self.object:getpos() + local s = self.object:get_pos() local lp = nil -- is there something I need to avoid? @@ -1542,13 +1592,11 @@ local do_states = function(self, dtime) if lp.x > s.x then yaw = yaw + pi end - -- look towards land and jump/move in that direction - yaw = set_yaw(self.object, yaw) - do_jump(self) - set_velocity(self, self.walk_velocity) + -- look towards land and jump/move in that direction + yaw = set_yaw(self.object, yaw) + do_jump(self) + set_velocity(self, self.walk_velocity) else --- yaw = (random(0, 360) - 180) / 180 * pi - yaw = yaw + random(-0.5, 0.5) end @@ -1569,9 +1617,6 @@ local do_states = function(self, dtime) -- otherwise randomly turn elseif random(1, 100) <= 30 then - --yaw = random() * 2 * pi --- yaw = (random(0, 360) - 180) / 180 * pi - yaw = yaw + random(-0.5, 0.5) yaw = set_yaw(self.object, yaw) @@ -1580,7 +1625,8 @@ local do_states = function(self, dtime) -- stand for great fall in front local temp_is_cliff = is_at_cliff(self) - if temp_is_cliff + if self.facing_fence == true + or temp_is_cliff or random(1, 100) <= 30 then set_velocity(self, 0) @@ -1620,18 +1666,18 @@ local do_states = function(self, dtime) elseif self.state == "attack" then -- calculate distance from mob and enemy - local s = self.object:getpos() - local p = self.attack:getpos() or s + local s = self.object:get_pos() + local p = self.attack:get_pos() or s local dist = get_distance(p, s) -- stop attacking if player or out of range if dist > self.view_range or not self.attack - or not self.attack:getpos() + or not self.attack:get_pos() or self.attack:get_hp() <= 0 or (self.attack:is_player() and mobs.invis[ self.attack:get_player_name() ]) then - --print(" ** stop attacking **", dist, self.view_range) +-- print(" ** stop attacking **", dist, self.view_range) self.state = "stand" set_velocity(self, 0) set_animation(self, "stand") @@ -1656,29 +1702,28 @@ local do_states = function(self, dtime) yaw = set_yaw(self.object, yaw) - if dist > self.reach then + -- start timer when inside reach + if dist < self.reach and not self.v_start then + self.v_start = true + self.timer = 0 + self.blinktimer = 0 +-- print ("=== explosion timer started", self.explosion_timer) + end - if not self.v_start then - - self.v_start = true - set_velocity(self, self.run_velocity) - self.timer = 0 - self.blinktimer = 0 - else - self.timer = 0 - self.blinktimer = 0 - - set_velocity(self, self.run_velocity) - end - - if self.animation and self.animation.run_start then - set_animation(self, "run") - else - set_animation(self, "walk") - end - else + -- walk right up to player when timer active + if dist < 1.5 and self.v_start then set_velocity(self, 0) - set_animation(self, "punch") + else + set_velocity(self, self.run_velocity) + end + + if self.animation and self.animation.run_start then + set_animation(self, "run") + else + set_animation(self, "walk") + end + + if self.v_start then self.timer = self.timer + dtime self.blinktimer = (self.blinktimer or 0) + dtime @@ -1696,9 +1741,11 @@ local do_states = function(self, dtime) self.blinkstatus = not self.blinkstatus end - if self.timer > 3 then +-- print ("=== explosion timer", self.timer) - local pos = self.object:getpos() + if self.timer > self.explosion_timer then + + local pos = self.object:get_pos() local radius = self.explosion_radius or 1 local damage_radius = radius @@ -1941,16 +1988,17 @@ local do_states = function(self, dtime) -- play shoot attack sound mob_sound(self, self.sounds.shoot_attack) - local p = self.object:getpos() + local p = self.object:get_pos() p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2 - local obj = minetest.add_entity(p, self.arrow) - local ent = obj:get_luaentity() + if minetest.registered_entities[self.arrow] then - if ent then + local obj = minetest.add_entity(p, self.arrow) + local ent = obj:get_luaentity() local amount = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) ^ 0.5 local v = ent.velocity or 1 -- or set to default + ent.switch = 1 ent.owner_id = tostring(self.object) -- add unique owner id to arrow @@ -1961,8 +2009,6 @@ local do_states = function(self, dtime) vec.z = vec.z * (v / amount) obj:setvelocity(vec) - else - obj:remove() -- arrow entity does not exist end end end @@ -2020,7 +2066,7 @@ local falling = function(self, pos) if self.fall_damage == 1 and self.object:getvelocity().y == 0 then - local d = (self.old_y or 0) - self.object:getpos().y + local d = (self.old_y or 0) - self.object:get_pos().y if d > 5 then @@ -2033,7 +2079,7 @@ local falling = function(self, pos) end end - self.old_y = self.object:getpos().y + self.old_y = self.object:get_pos().y end end end @@ -2042,11 +2088,20 @@ end -- deal damage and effects when mob punched local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) - -- mob health check - if self.health <= 0 then - return + -- custom punch function + if self.do_punch then + + -- when false skip going any further + if self.do_punch(self, hitter, tflp, tool_caps, dir) == false then + return + end end + -- mob health check +-- if self.health <= 0 then +-- return +-- end + -- error checking when mod profiling is enabled if not tool_capabilities then minetest.log("warning", "[mobs] Mod profiling enabled, damage not enabled") @@ -2055,7 +2110,7 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) -- is mob protected? if self.protected and hitter:is_player() - and minetest.is_protected(self.object:getpos(), hitter:get_player_name()) then + and minetest.is_protected(self.object:get_pos(), hitter:get_player_name()) then minetest.chat_send_player(hitter:get_player_name(), S("Mob has been protected!")) return end @@ -2140,12 +2195,12 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) local s = random(0, #weapon:get_definition().sounds) minetest.sound_play(weapon:get_definition().sounds[s], { - object = hitter, + object = self.object, --hitter, max_hear_distance = 8 }) else minetest.sound_play("default_punch", { - object = hitter, + object = self.object, --hitter, max_hear_distance = 5 }) end @@ -2154,7 +2209,7 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) if self.blood_amount > 0 and not disable_blood then - local pos = self.object:getpos() + local pos = self.object:get_pos() pos.y = pos.y + (-self.collisionbox[2] + self.collisionbox[5]) * .5 @@ -2204,21 +2259,28 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) -- direction error check dir = dir or {x = 0, y = 0, z = 0} + -- check if tool already has specific knockback value + if tool_capabilities.damage_groups["knockback"] then + kb = tool_capabilities.damage_groups["knockback"] + else + kb = kb * 1.5 + end + self.object:setvelocity({ x = dir.x * kb, y = up, z = dir.z * kb }) - self.pause_timer = r + self.pause_timer = 0.25 end end -- END if damage -- if skittish then run away if self.runaway == true then - local lp = hitter:getpos() - local s = self.object:getpos() + local lp = hitter:get_pos() + local s = self.object:get_pos() local vec = { x = lp.x - s.x, y = lp.y - s.y, @@ -2251,7 +2313,7 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) do_attack(self, hitter) -- alert others to the attack - local objs = minetest.get_objects_inside_radius(hitter:getpos(), self.view_range) + local objs = minetest.get_objects_inside_radius(hitter:get_pos(), self.view_range) local obj = nil for n = 1, #objs do @@ -2331,8 +2393,9 @@ end -- activate mob and reload settings local mob_activate = function(self, staticdata, def, dtime) - -- remove monsters in peaceful mode, or when no data - if (self.type == "monster" and peaceful_only) then + -- remove monsters in peaceful mode + if self.type == "monster" + and peaceful_only then self.object:remove() @@ -2406,17 +2469,17 @@ local mob_activate = function(self, staticdata, def, dtime) self.health = random (self.hp_min, self.hp_max) end - -- rnd: pathfinding init + -- pathfinding init self.path = {} self.path.way = {} -- path to follow, table of positions self.path.lastpos = {x = 0, y = 0, z = 0} self.path.stuck = false self.path.following = false -- currently following path? self.path.stuck_timer = 0 -- if stuck for too long search for path - -- end init + -- mob defaults self.object:set_armor_groups({immortal = 1, fleshy = self.armor}) - self.old_y = self.object:getpos().y + self.old_y = self.object:get_pos().y self.old_health = self.health self.sounds.distance = self.sounds.distance or 10 self.textures = textures @@ -2425,12 +2488,29 @@ local mob_activate = function(self, staticdata, def, dtime) self.visual_size = vis_size self.standing_in = "" + -- check existing nametag + if not self.nametag then + self.nametag = def.nametag + end + -- set anything changed above self.object:set_properties(self) set_yaw(self.object, (random(0, 360) - 180) / 180 * pi) update_tag(self) set_animation(self, "stand") + -- run on_spawn function if found + if self.on_spawn and not self.on_spawn_run then + if self.on_spawn(self) then + self.on_spawn_run = true -- if true, set flag to run once only + end + end + + -- run after_activate + if def.after_activate then + def.after_activate(self, staticdata, def, dtime) + end + if use_cmi then self._cmi_components = cmi.activate_components(self.serialized_cmi_components) cmi.notify_activate(self.object, dtime) @@ -2445,7 +2525,7 @@ local mob_step = function(self, dtime) cmi.notify_step(self.object, dtime) end - local pos = self.object:getpos() + local pos = self.object:get_pos() local yaw = 0 -- when lifetimer expires remove mob (except npc and tamed) @@ -2490,10 +2570,6 @@ local mob_step = function(self, dtime) self.pause_timer = self.pause_timer - dtime - if self.pause_timer < 1 then - self.pause_timer = 0 - end - return end @@ -2625,7 +2701,6 @@ minetest.register_entity(name, { attacks_monsters = def.attacks_monsters or false, group_attack = def.group_attack or false, passive = def.passive or false, - recovery_time = def.recovery_time or 0.5, knock_back = def.knock_back or 3, blood_amount = def.blood_amount or 5, blood_texture = def.blood_texture or "mobs_blood.png", @@ -2657,6 +2732,7 @@ minetest.register_entity(name, { pathfinding = def.pathfinding, immune_to = def.immune_to or {}, explosion_radius = def.explosion_radius, + explosion_timer = def.explosion_timer or 3, custom_attack = def.custom_attack, double_melee_attack = def.double_melee_attack, dogshoot_switch = def.dogshoot_switch, @@ -2666,14 +2742,23 @@ minetest.register_entity(name, { attack_animals = def.attack_animals or false, specific_attack = def.specific_attack, owner_loyal = def.owner_loyal, + facing_fence = false, _cmi_is_mob = true, + on_spawn = def.on_spawn, + on_blast = def.on_blast or do_tnt, on_step = mob_step, + do_punch = def.do_punch, + on_punch = mob_punch, + on_breed = def.on_breed, + + on_grown = def.on_grown, + on_activate = function(self, staticdata, dtime) return mob_activate(self, staticdata, def, dtime) end, @@ -2724,7 +2809,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn) -- chance/spawn number override in minetest.conf for registered mob - local numbers = minetest.setting_get(name) + local numbers = minetest.settings:get(name) if numbers then numbers = numbers:split(",") @@ -2838,20 +2923,24 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, -- spawn mob half block higher than ground pos.y = pos.y - 0.5 - local mob = minetest.add_entity(pos, name) + if minetest.registered_entities[name] then - if mob and mob:get_luaentity() then --- print ("[mobs] Spawned " .. name .. " at " --- .. minetest.pos_to_string(pos) .. " on " --- .. node.name .. " near " .. neighbors[1]) - if on_spawn and not on_spawn(mob, pos) then - return + local mob = minetest.add_entity(pos, name) +--[[ + print ("[mobs] Spawned " .. name .. " at " + .. minetest.pos_to_string(pos) .. " on " + .. node.name .. " near " .. neighbors[1]) +]] + if on_spawn then + + local ent = mob:get_luaentity() + + on_spawn(ent, pos) end else minetest.log("warning", string.format("[mobs] %s failed to spawn at %s", name, minetest.pos_to_string(pos))) end - end }) end @@ -2910,13 +2999,13 @@ function mobs:register_arrow(name, def) automatic_face_movement_dir = def.rotate and (def.rotate - (pi / 180)) or false, - on_activate = def.on_activate or nil, + on_activate = def.on_activate, on_step = def.on_step or function(self, dtime) self.timer = self.timer + 1 - local pos = self.object:getpos() + local pos = self.object:get_pos() if self.switch == 0 or self.timer > 150 @@ -3020,6 +3109,7 @@ function mobs:boom(self, pos, radius) radius = radius, damage_radius = radius, sound = self.sounds.explode, + explode_center = true, }) else minetest.sound_play(self.sounds.explode, { @@ -3077,25 +3167,24 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) and within_limits(pos, 0) and not minetest.is_protected(pos, placer:get_player_name()) then + if not minetest.registered_entities[mob] then + return + end + pos.y = pos.y + 1 local mob = minetest.add_entity(pos, mob) local ent = mob:get_luaentity() - if not ent then - mob:remove() - return - end - + -- don't set owner if monster or sneak pressed if ent.type ~= "monster" and not placer:get_player_control().sneak then - -- set owner and tame if not monster ent.owner = placer:get_player_name() ent.tamed = true end -- if not in creative then take item - if not creative then + if not mobs.is_creative(placer:get_player_name()) then itemstack:take_item() end end @@ -3211,7 +3300,7 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, if inv:room_for_item("main", new_stack) then inv:add_item("main", new_stack) else - minetest.add_item(clicker:getpos(), new_stack) + minetest.add_item(clicker:get_pos(), new_stack) end self.object:remove() @@ -3250,18 +3339,17 @@ function mobs:protect(self, clicker) return true -- false end - if not creative then + if not mobs.is_creative(clicker:get_player_name()) then tool:take_item() -- take 1 protection rune clicker:set_wielded_item(tool) end self.protected = true --- minetest.chat_send_player(name, S("Protected!")) - local pos = self.object:getpos() + local pos = self.object:get_pos() pos.y = pos.y + self.collisionbox[2] + 0.5 - effect(self.object:getpos(), 25, "mobs_protect_particle.png", 0.5, 4, 2, 15) + effect(self.object:get_pos(), 25, "mobs_protect_particle.png", 0.5, 4, 2, 15) mob_sound(self, "mobs_spell") @@ -3283,7 +3371,7 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) if follow_holding(self, clicker) then -- if not in creative then take item - if not creative then + if not mobs.is_creative(clicker:get_player_name()) then local item = clicker:get_wielded_item() @@ -3301,9 +3389,11 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) if self.htimer < 1 then +--[[ minetest.chat_send_player(clicker:get_player_name(), S("@1 at full health (@2)", self.name:split(":")[2], tostring(self.health))) +]] self.htimer = 5 end @@ -3408,7 +3498,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) update_tag(mob_obj[name]) -- if not in creative then take item - if not creative then + if not mobs.is_creative(name) then mob_sta[name]:take_item() @@ -3436,9 +3526,11 @@ function mobs:alias_mob(old_name, new_name) on_step = function(self) - local pos = self.object:getpos() + local pos = self.object:get_pos() - minetest.add_entity(pos, new_name) + if minetest.registered_entities[new_name] then + minetest.add_entity(pos, new_name) + end self.object:remove() end diff --git a/mods/ENTITIES/mobs/api.txt b/mods/ENTITIES/mobs/api.txt index 90a5966e9..3ec690893 100644 --- a/mods/ENTITIES/mobs/api.txt +++ b/mods/ENTITIES/mobs/api.txt @@ -1,5 +1,5 @@ -MOB API (13th July 2017) +MOB API (18th October 2017) The mob api is a function that can be called on by other mods to add new animals or monsters into minetest. @@ -30,12 +30,13 @@ This functions registers a new mob as a Minetest entity. 'specific_attack' has a table of entity names that monsters can attack {"player", "mobs_animal:chicken"} 'hp_min' minimum health 'hp_max' maximum health (mob health is randomly selected between both) + 'nametag' string containing name of mob to display above entity 'physical' same is in minetest.register_entity() 'collisionbox' same is in minetest.register_entity() 'visual' same is in minetest.register_entity() 'visual_size' same is in minetest.register_entity() 'textures' same is in minetest.register_entity() - although you can add multiple lines for random textures {{"texture1.png"},{"texture2.png"}}, + although you can add multiple lines for random textures {{"texture1.png"},{"texture2.png"}}, 'gotten_texture' alt. texture for when self.gotten value is set to true (used for shearing sheep) 'child_texture' texture of mod for when self.child is set to true 'mesh' same is in minetest.register_entity() @@ -74,9 +75,7 @@ This functions registers a new mob as a Minetest entity. 'fall_damage' will mob be hurt when falling from height 'fall_speed' maximum falling velocity of mob (default is -10 and must be below -2) 'fear_height' when mob walks near a drop then anything over this value makes it stop and turn back (default is 0 to disable) - 'on_die' a function that is called when the mob is killed the parameters are (self, pos) 'floats' 1 to float in water, 0 to sink - 'on_rightclick' its same as in minetest.register_entity() 'pathfinding' set to 1 for mobs to use pathfinder feature to locate player, set to 2 so they can build/break also (only works with dogfight attack) 'attack_type' the attack type of a monster 'dogfight' follows player in range and attacks when in reach @@ -88,8 +87,8 @@ This functions registers a new mob as a Minetest entity. 'dogshoot_count2_max' number of seconds before switching back to shoot mode. 'custom_attack' when set this function is called instead of the normal mob melee attack, parameters are (self, to_attack) 'double_melee_attack' if false then api will choose randomly between 'punch' and 'punch2' attack animations - 'on_blast' is called when an explosion happens near mob when using TNT functions, parameters are (object, damage) and returns (do_damage, do_knockback, drops) 'explosion_radius' radius of explosion attack (defaults to 1) + 'explosion_timer' number of seconds before mob explodes while still inside view range. 'arrow' if the attack_type is "shoot" or "dogshoot" then the entity name of a pre-defined arrow is required, see below for arrow definition. 'shoot_interval' the minimum shoot interval 'shoot_offset' +/- value to position arrow/fireball when fired @@ -105,6 +104,19 @@ This functions registers a new mob as a Minetest entity. 'explode' sound when exploding 'distance' maximum distance sounds are heard from (default is 10) +Custom mob functions inside mob registry: + + 'on_die' a function that is called when the mob is killed the parameters are (self, pos) + 'on_rightclick' its same as in minetest.register_entity() + 'on_blast' is called when an explosion happens near mob when using TNT functions, parameters are (object, damage) and returns (do_damage, do_knockback, drops) + 'on_spawn' is a custom function that runs on mob spawn with 'self' as variable, return true at end of function to run only once. + 'after_activate' is a custom function that runs once mob has been activated with the paramaters (self, staticdata, def, dtime) + 'on_breed' called when two similar mobs breed, paramaters are (parent1, parent2) objects, return false to stop child from being resized and owner/tamed flags and child textures being applied. + 'on_grown' is called when a child mob has grown up, only paramater is (self). + 'do_punch' called when mob is punched with paramaters (self, hitter, time_from_last_punch, tool_capabilities, direction), return false to stop punch damage and knockback from taking place. + + + Mobs can look for specific nodes as they walk and replace them to mimic eating. 'replace_what' group if items to replace e.g. {"farming:wheat_8", "farming:carrot_8"} @@ -135,6 +147,7 @@ can be added to the mob definition under pre-defined mob animation names like: 'fly_start', 'fly_end', 'fly_speed' when mob flies 'punch_start', 'punch_end', 'punch_speed' when mob attacks 'punch2_start', 'punch2_end', 'punch2_speed' when mob attacks (alternative) + 'shoot_start', 'shoot_end', shoot_speed' when mob shoots 'die_start', 'die_end', 'die_speed' when mob dies '*_loop' bool value to determine if any set animation loops e.g (die_loop = false) defaults to true if not set @@ -346,7 +359,7 @@ mobs:register_mob("mob_horse:horse", { visual_size = {x = 1.20, y = 1.20}, mesh = "mobs_horse.x", collisionbox = {-0.4, -0.01, -0.4, 0.4, 1.25, 0.4}, - animation = { + animation = { speed_normal = 15, speed_run = 30, stand_start = 25, diff --git a/mods/ENTITIES/mobs/api_new.txt b/mods/ENTITIES/mobs/api_new.txt new file mode 100644 index 000000000..15b87a3a4 --- /dev/null +++ b/mods/ENTITIES/mobs/api_new.txt @@ -0,0 +1,684 @@ + +Mobs Redo API (last updated 18th Oct 2017) +========================================== + +Welcome to the world of mobs in minetest and hopefully an easy guide to defining +your own mobs and having them appear in your worlds. + + +Registering Mobs +---------------- + +To register a mob and have it ready for use requires the following function: + + mobs:register_mob(name, definition) + +The 'name' of a mob usually starts with the mod name it's running from followed +by it's own name e.g. + + "mobs_monster:sand_monster" or "mymod:totally_awesome_beast" + +... and the 'definition' is a table which holds all of the settings and +functions needed for the mob to work properly which contains the following: + + 'nametag' contains the name which is shown above mob. + 'type' holds the type of mob that inhabits your world e.g. + "animal" usually docile and walking around. + "monster" attacks player or npc on sight. + "npc" walk around and will defend themselves if hit first, they + kill monsters. + 'hp_min' has the minimum health value the mob can spawn with. + 'hp_max' has the maximum health value the mob can spawn with. + 'armor' holds strength of mob, 100 is normal, lower is more powerful + and needs more hits and better weapons to kill. + 'passive' when true allows animals to defend themselves when hit, + otherwise they amble onwards. + 'walk_velocity' is the speed that your mob can walk around. + 'run_velocity' is the speed your mob can run with, usually when attacking. + 'walk_chance' has a 0-100 chance value your mob will walk from standing, + set to 0 for jumping mobs only. + 'jump' when true allows your mob to jump updwards. + 'jump_height' holds the height your mob can jump, 0 to disable jumping. + 'step_height' height of a block that your mob can easily walk up onto. + 'fly' when true allows your mob to fly around instead of walking. + 'fly_in' holds the node name that the mob flies (or swims) around + in e.g. "air" or "default:water_source". + 'runaway' if true causes animals to turn and run away when hit. + 'view_range' how many nodes in distance the mob can see a player. + 'reach' how many nodes in distance a mob can attack a player while + standing. + 'damage' how many health points the mob does to a player or another + mob when melee attacking. + 'knockback' when true has mobs falling backwards when hit, the greater + the damage the more they move back. + 'fear_height' is how high a cliff or edge has to be before the mob stops + walking, 0 to turn off height fear. + 'fall_speed' has the maximum speed the mob can fall at, default is -10. + 'fall_damage' when true causes falling to inflict damage. + 'water_damage' holds the damage per second infliced to mobs when standing in + water. + 'lava_damage' holds the damage per second inflicted to mobs when standing + in lava or fire. + 'light_damage' holds the damage per second inflicted to mobs when it's too + bright (above 13 light). + 'suffocation' when true causes mobs to suffocate inside solid blocks. + 'floats' when set to 1 mob will float in water, 0 has them sink. + 'follow' mobs follow player when holding any of the items which appear + on this table, the same items can be fed to a mob to tame or + breed e.g. {"farming:wheat", "default:apple"} + + 'reach' is how far the mob can attack player when standing + nearby, default is 3 nodes. + 'docile_by_day' when true has mobs wandering around during daylight + hours and only attacking player at night or when + provoked. + 'attacks_monsters' when true has npc's attacking monsters or not. + 'attack_animal' when true will have monsters attacking animals. + 'owner_loyal' when true will have tamed mobs attack anything player + punches when nearby. + 'group_attack' when true has same mob type grouping together to attack + offender. + 'attack_type' tells the api what a mob does when attacking the player + or another mob: + 'dogfight' is a melee attack when player is within mob reach. + 'shoot' has mob shoot pre-defined arrows at player when inside + view_range. + 'dogshoot' has melee attack when inside reach and shoot attack + when inside view_range. + 'explode' causes mob to explode when inside reach. + 'explosion_radius' has the radius of the explosion which defaults to 1. + 'explosion_timer' number of seconds before mob explodes while still + inside view range. + 'arrow' holds the pre-defined arrow object to shoot when + attacking. + 'dogshoot_switch' allows switching between attack types by using timers + (1 for shoot, 2 for dogfight) + 'dogshoot_count_max' contains how many seconds before switching from + dogfight to shoot. + 'dogshoot_count_max2' contains how many seconds before switching from shoot + to dogfight. + 'shoot_interval' has the number of seconds between shots. + 'shoot_offset' holds the y position added as to where the + arrow/fireball appears on mob. + 'specific_attack' has a table of entity names that mob can also attack + e.g. {"player", "mobs_animal:chicken"}. + 'blood_amount' contains the number of blood droplets to appear when + mob is hit. + 'blood_texture' has the texture name to use for droplets e.g. + "mobs_blood.png". + 'pathfinding' set to 1 for mobs to use pathfinder feature to locate + player, set to 2 so they can build/break also (only + works with dogfight attack). + 'immune_to' is a table that holds specific damage when being hit by + certain items e.g. + {"default:sword_wood", 0} -- causes no damage. + {"default:gold_lump", -10} -- heals by 10 health points. + {"default:coal_block", 20} -- 20 damage when hit on head with coal blocks. + + 'makes_footstep_sound' when true you can hear mobs walking. + 'sounds' this is a table with sounds of the mob + 'distance' maximum distance sounds can be heard, default is 10. + 'random' random sound that plays during gameplay. + 'war_cry' what you hear when mob starts to attack player. + 'attack' what you hear when being attacked. + 'shoot_attack' sound played when mob shoots. + 'damage' sound heard when mob is hurt. + 'death' played when mob is killed. + 'jump' played when mob jumps. + 'explode' sound played when mob explodes. + + 'drops' table of items that are dropped when mob is killed, fields are: + 'name' name of item to drop. + 'chance' chance of drop, 1 for always, 2 for 1-in-2 chance etc. + 'min' minimum number of items dropped. + 'max' maximum number of items dropped. + + 'visual' holds the look of the mob you wish to create: + 'cube' looks like a normal node + 'sprite' sprite which looks same from all angles. + 'upright_sprite' flat model standing upright. + 'wielditem' how it looks when player holds it in hand. + 'mesh' uses separate object file to define mob. + 'visual_size' has the size of the mob, defaults to {x = 1, y = 1} + 'collision_box' has the box in which mob can be interacted with e.g. + {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5} + 'textures' holds a table list of textures to be used for mob, or you + could use multiple lists inside another table for random + selection e.g. { {"texture1.png"}, {"texture2.png"} } + 'child_texture' holds the texture table for when baby mobs are used. + 'gotten_texture' holds the texture table for when self.gotten value is + true, used for milking cows or shearing sheep. + 'mesh' holds the name of the external object used for mob model + e.g. "mobs_cow.b3d" + 'gotten_mesh" holds the name of the external object used for when + self.gotten is true for mobs. + 'rotate' custom model rotation, 0 = front, 90 = side, 180 = back, + 270 = other side. + 'double_melee_attack' when true has the api choose between 'punch' and + 'punch2' animations. + + 'animation' holds a table containing animation names and settings for use with mesh models: + 'stand_start' start frame for when mob stands still. + 'stand_end' end frame of stand animation. + 'stand_speed' speed of animation in frames per second. + 'walk_start' when mob is walking around. + 'walk_end' + 'walk_speed' + 'run_start' when a mob runs or attacks. + 'run_end' + 'run_speed' + 'fly_start' when a mob is flying. + 'fly_end' + 'fly_speed' + 'punch_start' when a mob melee attacks. + 'punch_end' + 'punch_speed' + 'punch2_start' alternative melee attack animation. + 'punch2_end' + 'punch2_speed' + 'shoot_start' shooting animation. + 'shoot_end' + 'shoot_speed' + 'die_start' death animation + 'die_end' + 'die_speed' + 'die_loop' when set to false stops the animation looping. + + Using '_loop = false' setting will stop any of the above animations from + looping. + + 'speed_normal' is used for animation speed for compatibility with some + older mobs. + + +Node Replacement +---------------- + +Mobs can look around for specific nodes as they walk and replace them to mimic +eating. + + 'replace_what' group of items to replace e.g. + {"farming:wheat_8", "farming:carrot_8"} + or you can use the specific options of what, with and + y offset by using this instead: + { + {"group:grass", "air", 0}, + {"default:dirt_with_grass", "default:dirt", -1} + } + 'replace_with' replace with what e.g. "air" or in chickens case "mobs:egg" + 'replace_rate' how random should the replace rate be (typically 10) + 'replace_offset' +/- value to check specific node to replace + + 'on_replace(self, pos, oldnode, newnode)' is called when mob is about to + replace a node. + 'self' ObjectRef of mob + 'pos' Position of node to replace + 'oldnode' Current node + 'newnode' What the node will become after replacing + + If false is returned, the mob will not replace the node. + + By default, replacing sets self.gotten to true and resets the object + properties. + + +Custom Definition Functions +--------------------------- + +Along with the above mob registry settings we can also use custom functions to +enhance mob functionality and have them do many interesting things: + + 'on_die' a function that is called when the mob is killed the + parameters are (self, pos) + 'on_rightclick' its same as in minetest.register_entity() + 'on_blast' is called when an explosion happens near mob when using TNT + functions, parameters are (object, damage) and returns + (do_damage, do_knockback, drops) + 'on_spawn' is a custom function that runs on mob spawn with 'self' as + variable, return true at end of function to run only once. + 'after_activate' is a custom function that runs once mob has been activated + with these paramaters (self, staticdata, def, dtime) + 'on_breed' called when two similar mobs breed, paramaters are + (parent1, parent2) objects, return false to stop child from + being resized and owner/tamed flags and child textures being + applied. Function itself must spawn new child mob. + 'on_grown' is called when a child mob has grown up, only paramater is + (self). + 'do_punch' called when mob is punched with paramaters (self, hitter, + time_from_last_punch, tool_capabilities, direction), return + false to stop punch damage and knockback from taking place. + 'custom_attack' when set this function is called instead of the normal mob + melee attack, parameters are (self, to_attack). + 'on_die' a function that is called when mob is killed + 'do_custom' a custom function that is called every tick while mob is + active and which has access to all of the self.* variables + e.g. (self.health for health or self.standing_in for node + status), return with 'false' to skip remainder of mob API. + + +Internal Variables +------------------ + +The mob api also has some preset variables and functions that it will remember +for each mob. + + 'self.health' contains current health of mob (cannot exceed + self.hp_max) + 'self.texture_list' contains list of all mob textures + 'self.child_texture' contains mob child texture when growing up + 'self.base_texture' contains current skin texture which was randomly + selected from textures list + 'self.gotten' this is used for obtaining milk from cow and wool from + sheep + 'self.horny' when animal fed enough it is set to true and animal can + breed with same animal + 'self.hornytimer' background timer that controls breeding functions and + mob childhood timings + 'self.child' used for when breeding animals have child, will use + child_texture and be half size + 'self.owner' string used to set owner of npc mobs, typically used for + dogs + 'self.order' set to "follow" or "stand" so that npc will follow owner + or stand it's ground + 'self.nametag' contains the name of the mob which it can show above + + +Spawning Mobs in World +---------------------- + +mobs:register_spawn(name, nodes, max_light, min_light, chance, + active_object_count, max_height, day_toggle) + +mobs:spawn_specfic(name, nodes, neighbors, min_light, max_light, interval, + chance, active_object_count, min_height, max_height, day_toggle, on_spawn) + +These functions register a spawn algorithm for the mob. Without this function +the call the mobs won't spawn. + + 'name' is the name of the animal/monster + 'nodes' is a list of nodenames on that the animal/monster can + spawn on top of + 'neighbors' is a list of nodenames on that the animal/monster will + spawn beside (default is {"air"} for + mobs:register_spawn) + 'max_light' is the maximum of light + 'min_light' is the minimum of light + 'interval' is same as in register_abm() (default is 30 for + mobs:register_spawn) + 'chance' is same as in register_abm() + 'active_object_count' number of this type of mob to spawn at one time inside + map area + 'min_height' is the minimum height the mob can spawn + 'max_height' is the maximum height the mob can spawn + 'day_toggle' true for day spawning, false for night or nil for + anytime + 'on_spawn' is a custom function which runs after mob has spawned + and gives self and pos values. + +A simpler way to handle mob spawns has been added with the mobs:spawn(def) +command which uses above names to make settings clearer: + + mobs:spawn({name = "mobs_monster:tree_monster", + nodes = {"group:leaves"}, + max_light = 7, + }) + + +For each mob that spawns with this function is a field in mobs.spawning_mobs. +It tells if the mob should spawn or not. Default is true. So other mods can +only use the API of this mod by disabling the spawning of the default mobs in +this mod. + + +Making Arrows +------------- + +mobs:register_arrow(name, definition) + +This function registers a arrow for mobs with the attack type shoot. + + 'name' is the name of the arrow + 'definition' is a table with the following values: + 'visual' same is in minetest.register_entity() + 'visual_size' same is in minetest.register_entity() + 'textures' same is in minetest.register_entity() + 'velocity' the velocity of the arrow + 'drop' if set to true any arrows hitting a node will drop as item + 'hit_player' a function that is called when the arrow hits a player; + this function should hurt the player, the parameters are + (self, player) + 'hit_mob' a function that is called when the arrow hits a mob; + this function should hurt the mob, the parameters are + (self, player) + 'hit_node' a function that is called when the arrow hits a node, the + parameters are (self, pos, node) + 'tail' when set to 1 adds a trail or tail to mob arrows + 'tail_texture' texture string used for above effect + 'tail_size' has size for above texture (defaults to between 5 and 10) + 'expire' contains float value for how long tail appears for + (defaults to 0.25) + 'glow' has value for how brightly tail glows 1 to 10 (default is + 0 for no glow) + 'rotate' integer value in degrees to rotate arrow + 'on_step' is a custom function when arrow is active, nil for + default. + + +Spawn Eggs +---------- + +mobs:register_egg(name, description, background, addegg, no_creative) + +This function registers a spawn egg which can be used by admin to properly spawn in a mob. + + 'name' this is the name of your new mob to spawn e.g. "mob:sheep" + 'description' the name of the new egg you are creating e.g. "Spawn Sheep" + 'background' the texture displayed for the egg in inventory + 'addegg' would you like an egg image in front of your texture (1 = yes, + 0 = no) + 'no_creative' when set to true this stops spawn egg appearing in creative + mode for destructive mobs like Dungeon Masters. + + +Explosion Function +------------------ + +mobs:explosion(pos, radius) -- DEPRECATED!!! use mobs:boom() instead + +mobs:boom(self, pos, radius) + 'self' mob entity + 'pos' centre position of explosion + 'radius' radius of explosion (typically set to 3) + +This function generates an explosion which removes nodes in a specific radius +and damages any entity caught inside the blast radius. Protection will limit +node destruction but not entity damage. + + +Capturing Mobs +-------------- + +mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, + force_take, replacewith) + +This function is generally called inside the on_rightclick section of the mob +api code, it provides a chance of capturing the mob by hand, using the net or +lasso items, and can also have the player take the mob by force if tamed and +replace with another item entirely. + + 'self' mob information + 'clicker' player information + 'chance_hand' chance of capturing mob by hand (1 to 100) 0 to disable + 'chance_net' chance of capturing mob using net (1 to 100) 0 to disable + 'chance_lasso' chance of capturing mob using magic lasso (1 to 100) 0 to + disable + 'force_take' take mob by force, even if tamed (true or false) + 'replacewith' once captured replace mob with this item instead (overrides + new mob eggs with saved information) + + +Feeding and Taming/Breeding +--------------------------- + +mobs:feed_tame(self, clicker, feed_count, breed, tame) + +This function allows the mob to be fed the item inside self.follow be it apple, +wheat or whatever a set number of times and be tamed or bred as a result. +Will return true when mob is fed with item it likes. + + 'self' mob information + 'clicker' player information + 'feed_count' number of times mob must be fed to tame or breed + 'breed' true or false stating if mob can be bred and a child created + afterwards + 'tame' true or false stating if mob can be tamed so player can pick + them up + + +Protecting Mobs +--------------- + +mobs:protect(self, clicker) + +This function can be used to right-click any tamed mob with mobs:protector item, +this will protect the mob from harm inside of a protected area from other +players. Will return true when mob right-clicked with mobs:protector item. + + 'self' mob information + 'clicker' player information + + +Riding Mobs +----------- + +Mobs can now be ridden by players and the following shows its functions and +usage: + + +mobs:attach(self, player) + +This function attaches a player to the mob so it can be ridden. + + 'self' mob information + 'player' player information + + +mobs:detach(player, offset) + +This function will detach the player currently riding a mob to an offset +position. + + 'player' player information + 'offset' position table containing offset values + + +mobs:drive(self, move_animation, stand_animation, can_fly, dtime) + +This function allows an attached player to move the mob around and animate it at +same time. + + 'self' mob information + 'move_animation' string containing movement animation e.g. "walk" + 'stand_animation' string containing standing animation e.g. "stand" + 'can_fly' if true then jump and sneak controls will allow mob to fly + up and down + 'dtime' tick time used inside drive function + + +mobs:fly(self, dtime, speed, can_shoot, arrow_entity, move_animation, stand_animation) + +This function allows an attached player to fly the mob around using directional +controls. + + 'self' mob information + 'dtime' tick time used inside fly function + 'speed' speed of flight + 'can_shoot' true if mob can fire arrow (sneak and left mouse button + fires) + 'arrow_entity' name of arrow entity used for firing + 'move_animation' string containing name of pre-defined animation e.g. "walk" + or "fly" etc. + 'stand_animation' string containing name of pre-defined animation e.g. + "stand" or "blink" etc. + +Note: animation names above are from the pre-defined animation lists inside mob +registry without extensions. + + +mobs:set_animation(self, name) + +This function sets the current animation for mob, defaulting to "stand" if not +found. + + 'self' mob information + 'name' name of animation + + +Certain variables need to be set before using the above functions: + + 'self.v2' toggle switch used to define below values for the + first time + 'self.max_speed_forward' max speed mob can move forward + 'self.max_speed_reverse' max speed mob can move backwards + 'self.accel' acceleration speed + 'self.terrain_type' integer containing terrain mob can walk on + (1 = water, 2 or 3 = land) + 'self.driver_attach_at' position offset for attaching player to mob + 'self.driver_eye_offset' position offset for attached player view + 'self.driver_scale' sets driver scale for mobs larger than {x=1, y=1} + + +External Settings for "minetest.conf" +------------------------------------ + + 'enable_damage' if true monsters will attack players (default is true) + 'only_peaceful_mobs' if true only animals will spawn in game (default is + false) + 'mobs_disable_blood' if false blood effects appear when mob is hit (default + is false) + 'mobs_spawn_protected' if set to false then mobs will not spawn in protected + areas (default is true) + 'remove_far_mobs' if true then mobs that are outside players visual + range will be removed (default is false) + 'mobname' can change specific mob chance rate (0 to disable) and + spawn number e.g. mobs_animal:cow = 1000,5 + 'mob_difficulty' sets difficulty level (health and hit damage + multiplied by this number), defaults to 1.0. + 'mob_show_health' if false then punching mob will not show health status + (true by default) + +Players can override the spawn chance for each mob registered by adding a line +to their minetest.conf file with a new value, the lower the value the more each +mob will spawn e.g. + +mobs_animal:sheep_chance 11000 +mobs_monster:sand_monster_chance 100 + + +Rideable Horse Example Mob +-------------------------- + +mobs:register_mob("mob_horse:horse", { + type = "animal", + visual = "mesh", + visual_size = {x = 1.20, y = 1.20}, + mesh = "mobs_horse.x", + collisionbox = {-0.4, -0.01, -0.4, 0.4, 1.25, 0.4}, + animation = { + speed_normal = 15, + speed_run = 30, + stand_start = 25, + stand_end = 75, + walk_start = 75, + walk_end = 100, + run_start = 75, + run_end = 100, + }, + textures = { + {"mobs_horse.png"}, + {"mobs_horsepeg.png"}, + {"mobs_horseara.png"} + }, + fear_height = 3, + runaway = true, + fly = false, + walk_chance = 60, + view_range = 5, + follow = {"farming:wheat"}, + passive = true, + hp_min = 12, + hp_max = 16, + armor = 200, + lava_damage = 5, + fall_damage = 5, + water_damage = 1, + makes_footstep_sound = true, + drops = { + {name = "mobs:meat_raw", chance = 1, min = 2, max = 3} + }, + sounds = { + random = "horse_neigh.ogg", + damage = "horse_whinney.ogg", + }, + + do_custom = function(self, dtime) + + -- set needed values if not already present + if not self.v2 then + self.v2 = 0 + self.max_speed_forward = 6 + self.max_speed_reverse = 2 + self.accel = 6 + self.terrain_type = 3 + self.driver_attach_at = {x = 0, y = 20, z = -2} + self.driver_eye_offset = {x = 0, y = 3, z = 0} + self.driver_scale = {x = 1, y = 1} + end + + -- if driver present allow control of horse + if self.driver then + + mobs.drive(self, "walk", "stand", false, dtime) + + return false -- skip rest of mob functions + end + + return true + end, + + on_die = function(self, pos) + + -- drop saddle when horse is killed while riding + -- also detach from horse properly + if self.driver then + minetest.add_item(pos, "mobs:saddle") + mobs.detach(self.driver, {x = 1, y = 0, z = 1}) + end + + end, + + on_rightclick = function(self, clicker) + + -- make sure player is clicking + if not clicker or not clicker:is_player() then + return + end + + -- feed, tame or heal horse + if mobs:feed_tame(self, clicker, 10, true, true) then + return + end + + -- make sure tamed horse is being clicked by owner only + if self.tamed and self.owner == clicker:get_player_name() then + + local inv = clicker:get_inventory() + + -- detatch player already riding horse + if self.driver and clicker == self.driver then + + mobs.detach(clicker, {x = 1, y = 0, z = 1}) + + -- add saddle back to inventory + if inv:room_for_item("main", "mobs:saddle") then + inv:add_item("main", "mobs:saddle") + else + minetest.add_item(clicker.getpos(), "mobs:saddle") + end + + -- attach player to horse + elseif not self.driver + and clicker:get_wielded_item():get_name() == "mobs:saddle" then + + self.object:set_properties({stepheight = 1.1}) + mobs.attach(self, clicker) + + -- take saddle from inventory + inv:remove_item("main", "mobs:saddle") + end + end + + -- used to capture horse with magic lasso + mobs:capture_mob(self, clicker, 0, 0, 80, false, nil) + end +}) diff --git a/mods/ENTITIES/mobs/mount.lua b/mods/ENTITIES/mobs/mount.lua index 050fbb5da..09df15870 100644 --- a/mods/ENTITIES/mobs/mount.lua +++ b/mods/ENTITIES/mobs/mount.lua @@ -155,8 +155,8 @@ function mobs.attach(entity, player) mcl_player.player_set_animation(player, "sit" , 30) end) - --player:set_look_yaw(entity.object:getyaw() - rot_view) - player:set_look_horizontal(entity.object:getyaw() - rot_view) + --player:set_look_yaw(entity.object:get_yaw() - rot_view) + player:set_look_horizontal(entity.object:get_yaw() - rot_view) end @@ -166,7 +166,7 @@ function mobs.detach(player, offset) mcl_player.player_set_animation(player, "stand" , 30) - local pos = player:getpos() + local pos = player:get_pos() pos = {x = pos.x + offset.x, y = pos.y + 0.2 + offset.y, z = pos.z + offset.z} @@ -212,7 +212,6 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) end -- fix mob rotation --- entity.object:setyaw(entity.driver:get_look_yaw() - entity.rotate) entity.object:setyaw(entity.driver:get_look_horizontal() - entity.rotate) if can_fly then @@ -290,7 +289,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) end -- Set position, velocity and acceleration - local p = entity.object:getpos() + local p = entity.object:get_pos() local new_velo = {x = 0, y = 0, z = 0} local new_acce = {x = 0, y = -9.8, z = 0} @@ -344,7 +343,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) end else if math.abs(velo.y) < 1 then - local pos = entity.object:getpos() + local pos = entity.object:get_pos() pos.y = math.floor(pos.y) + 0.5 entity.object:setpos(pos) velo.y = 0 @@ -355,7 +354,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) end end - new_velo = get_velocity(v, entity.object:getyaw() - rot_view, velo.y) + new_velo = get_velocity(v, entity.object:get_yaw() - rot_view, velo.y) new_acce.y = new_acce.y + acce_y entity.object:setvelocity(new_velo) @@ -389,7 +388,6 @@ function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim) local ctrl = entity.driver:get_player_control() local velo = entity.object:getvelocity() local dir = entity.driver:get_look_dir() --- local yaw = entity.driver:get_look_yaw() local yaw = entity.driver:get_look_horizontal() + 1.57 -- offset fix between old and new commands local rot_steer, rot_view = math.pi / 2, 0 @@ -420,7 +418,7 @@ function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim) -- firing arrows if ctrl.LMB and ctrl.sneak and shoots then - local pos = entity.object:getpos() + local pos = entity.object:get_pos() local obj = minetest.add_entity({ x = pos.x + 0 + dir.x * 2.5, y = pos.y + 1.5 + dir.y, @@ -431,7 +429,6 @@ function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim) ent.switch = 1 -- for mob specific arrows ent.owner_id = tostring(entity.object) -- so arrows dont hurt entity you are riding local vec = {x = dir.x * 6, y = dir.y * 6, z = dir.z * 6} --- local yaw = entity.driver:get_look_yaw() local yaw = entity.driver:get_look_horizontal() obj:setyaw(yaw + math.pi / 2) obj:setvelocity(vec) diff --git a/mods/ENTITIES/mobs/readme.MD b/mods/ENTITIES/mobs/readme.MD index ae879e562..92ae3ba68 100644 --- a/mods/ENTITIES/mobs/readme.MD +++ b/mods/ENTITIES/mobs/readme.MD @@ -22,6 +22,9 @@ Lucky Blocks: 9 Changelog: +- 1.40- Updated to use newer functions, requires Minetest 0.4.16+ to work. +- 1.39- Added 'on_breed', 'on_grown' and 'do_punch' custom functions per mob +- 1.38- Better entity checking, nametag setting and on_spawn function added to mob registry, tweaked light damage - 1.37- Added support for Raymoo's CMI (common mob interface) mod: https://forum.minetest.net/viewtopic.php?f=9&t=15448 - 1.36- Death check added, if mob dies in fire/lava/with lava pick then drops are cooked - 1.35- Added owner_loyal flag for owned mobs to attack player enemies, also fixed group_attack