diff --git a/mods/mobs/README.txt b/mods/mobs/README.txt index 43fb832f..29fb7b39 100755 --- a/mods/mobs/README.txt +++ b/mods/mobs/README.txt @@ -28,6 +28,7 @@ This mod contains the following additions: Changelog: +1.20- Error checking added to remove bad mobs, out of map limit mobs and stop serialize.h error 1.19- Chickens now drop egg items instead of placing the egg, also throwing eggs result in 1/8 chance of spawning chick 1.18- Added docile_by_day flag so that monsters will not attack automatically during daylight hours unless hit first 1.17- Added 'dogshoot' attack type, shoots when out of reach, melee attack when in reach, also api tweaks and self.reach added @@ -57,4 +58,4 @@ beta- Npc mob added, kills monsters, attacks player when punched, right click wi 0.4 - Dungeon Masters and Mese Monsters have much better aim due to shoot_offset, also they can both shoot through nodes that aren't walkable (flowers, grass etc) plus new sheep sound :) 0.3 - Added LOTT's Spider mob, made Cobwebs, added KPavel's Bee with Honey and Beehives (made texture), Warthogs now have sound and can be tamed, taming of shaved sheep or milked cow with 8 wheat so it will not despawn, many bug fixes :) 0.2 - Cooking bucket of milk into cheese now returns empty bucket -0.1 - Initial Release \ No newline at end of file +0.1 - Initial Release diff --git a/mods/mobs/api.lua b/mods/mobs/api.lua index bf035410..a94f71a6 100755 --- a/mods/mobs/api.lua +++ b/mods/mobs/api.lua @@ -1,4 +1,4 @@ --- Mobs Api (4th November 2015) +-- Mobs Api (6th December 2015) mobs = {} mobs.mod = "redo" @@ -19,6 +19,7 @@ do_attack = function(self, player) if math.random(0,100) < 90 and self.sounds.war_cry then + minetest.sound_play(self.sounds.war_cry,{ object = self.object, max_hear_distance = self.sounds.distance @@ -32,17 +33,15 @@ end set_velocity = function(self, v) - v = (v or 0) + local x = 0 + local z = 0 - if self.drawtype - and self.drawtype == "side" then - self.rotate = math.rad(90) + if v and v ~= 0 then + local yaw = (self.object:getyaw() + self.rotate) or 0 + x = math.sin(yaw) * -v + z = math.cos(yaw) * v end - local yaw = self.object:getyaw() + self.rotate - local x = math.sin(yaw) * -v - local z = math.cos(yaw) * v - self.object:setvelocity({ x = x, y = self.object:getvelocity().y, @@ -138,6 +137,7 @@ function check_for_death(self) -- return if no change local hp = self.object:get_hp() + if hp == self.health then return false end @@ -150,25 +150,30 @@ function check_for_death(self) self.health = hp if self.sounds.damage then + minetest.sound_play(self.sounds.damage,{ pos = pos, gain = 1.0, max_hear_distance = self.sounds.distance }) end + return false end -- drop items when dead local obj + for _,drop in ipairs(self.drops) do if math.random(1, drop.chance) == 1 then + obj = minetest.add_item(pos, ItemStack(drop.name .. " " .. math.random(drop.min, drop.max))) if obj then + obj:setvelocity({ x = math.random(-1, 1), y = 6, @@ -180,6 +185,7 @@ function check_for_death(self) -- play death sound if self.sounds.death then + minetest.sound_play(self.sounds.death,{ pos = pos, gain = 1.0, @@ -193,6 +199,7 @@ function check_for_death(self) end self.object:remove() + return true end @@ -214,7 +221,7 @@ end -- environmental damage (water, lava, fire, light) do_env_damage = function(self) - -- feed/tame text timer (so mob full messages dont spam chat) + -- feed/tame text timer (so mob 'full' messages dont spam chat) if self.htimer > 0 then self.htimer = self.htimer - 1 end @@ -253,7 +260,9 @@ do_env_damage = function(self) -- water if self.water_damage ~= 0 and nodef.groups.water then + self.object:set_hp(self.object:get_hp() - self.water_damage) + effect(pos, 5, "bubble.png") end @@ -261,9 +270,10 @@ do_env_damage = function(self) if self.lava_damage ~= 0 and (nodef.groups.lava or nod.name == "fire:basic_flame" - or nod.name == "fire:eternal_flame" or nod.name == "fire:permanent_flame") then + self.object:set_hp(self.object:get_hp() - self.lava_damage) + effect(pos, 5, "fire_basic_flame.png") end end @@ -280,77 +290,56 @@ do_jump = function(self) end local pos = self.object:getpos() + + -- what is mob standing on? pos.y = (pos.y + self.collisionbox[2]) - 0.2 + local nod = node_ok(pos) --print ("standing on:", nod.name, pos.y) - if minetest.registered_nodes[nod.name].walkable == false then + if minetest.registered_nodes[nod.name].walkable == false + or not self.direction then return end - if self.direction then + -- what is in front of mob? + local nod = node_ok({ + x = pos.x + self.direction.x, + y = pos.y + 0.5, + z = pos.z + self.direction.z + }) - local nod = node_ok({ - x = pos.x + self.direction.x, - y = pos.y + 0.5, - z = pos.z + self.direction.z - }) + -- thin blocks that do not need to be jumped + if nod.name == "default:snow" then + return + end ---print ("in front:", nod.name, pos.y) +--print ("in front:", nod.name, pos.y + 0.5) - if minetest.registered_items[nod.name].walkable - and not nod.name:find("fence") - or self.walk_chance == 0 then + if minetest.registered_items[nod.name].walkable + and not nod.name:find("fence") + or self.walk_chance == 0 then - local v = self.object:getvelocity() - v.y = self.jump_height + 1 - v.x = v.x * 2.2 - v.z = v.z * 2.2 + local v = self.object:getvelocity() - self.object:setvelocity(v) + v.y = self.jump_height + 1 + v.x = v.x * 2.2 + v.z = v.z * 2.2 - if self.sounds.jump then - minetest.sound_play(self.sounds.jump, { - pos = pos, - gain = 1.0, - max_hear_distance = self.sounds.distance - }) - end + self.object:setvelocity(v) + + if self.sounds.jump then + + minetest.sound_play(self.sounds.jump, { + pos = pos, + gain = 1.0, + max_hear_distance = self.sounds.distance + }) end end end --- check if POS is in mobs field of view -in_fov = function(self, pos) - - local yaw = self.object:getyaw() + self.rotate - local vx = math.sin(yaw) - local vz = math.cos(yaw) - local ds = math.sqrt(vx ^ 2 + vz ^ 2) - local ps = math.sqrt(pos.x ^ 2 + pos.z ^ 2) - local d = {x = vx / ds, z = vz / ds} - local p = {x = pos.x / ps, z = pos.z / ps} - local an = (d.x * p.x) + (d.z * p.z) - - if math.deg(math.acos(an)) > (self.fov / 2) then - return false - end - - return true -end - -function calc_velocity(pos1, pos2, old_vel, power) - local vel = vector.direction(pos1, pos2) - vel = vector.normalize(vel) - vel = vector.multiply(vel, power) - local dist = vector.distance(pos1, pos2) - dist = math.max(dist, 1) - vel = vector.divide(vel, dist) - vel = vector.add(vel, old_vel) - return vel -end - -- blast damage to entities nearby (modified from TNT mod) function entity_physics(pos, radius, self) --/MFF (Crabman|06/23/2015)add self to use punch function @@ -409,7 +398,9 @@ function follow_holding(self, clicker) -- multiple items elseif t == "table" then + for no = 1, #self.follow do + if self.follow[no] == item:get_name() then return true end @@ -466,6 +457,7 @@ local function breed(self) and self.hornytimer <= 40 then local pos = self.object:getpos() + effect({x = pos.x, y = pos.y + 1, z = pos.z}, 4, "heart.png") local ents = minetest.get_objects_inside_radius(pos, 3) @@ -512,7 +504,7 @@ local function breed(self) ent.hornytimer = 41 -- spawn baby - minetest.after(7, function(dtime) + minetest.after(5, function(dtime) local mob = minetest.add_entity(pos, self.name) local ent2 = mob:get_luaentity() @@ -562,12 +554,19 @@ function replace(self, pos) if self.replace_what and self.object:getvelocity().y == 0 and #minetest.find_nodes_in_area(pos, pos, self.replace_what) > 0 then + minetest.set_node(pos, {name = self.replace_with}) + + -- when cow/sheep eats grass, replace wool and milk + if self.gotten == true then + self.gotten = false + self.object:set_properties(self) + end end end end --- chceck if daytime and if mob is docile during daylight hours +-- check if daytime and also if mob is docile during daylight hours function day_docile(self) if self.docile_by_day == false then @@ -643,8 +642,6 @@ minetest.register_entity(name, { replace_offset = def.replace_offset or 0, timer = 0, env_damage_timer = 0, -- only used when state = "attack" - attack = {player = nil, dist = nil}, - state = "stand", tamed = false, pause_timer = 0, horny = false, @@ -672,23 +669,26 @@ minetest.register_entity(name, { self.lifetimer = self.lifetimer - dtime if self.lifetimer <= 0 then + minetest.log("action", "lifetimer expired, removed " .. self.name) + effect(pos, 15, "tnt_smoke.png") + self.object:remove() + return end end - -- node replace check (chicken lays egg, cow eats grass etc.) - replace(self, pos) - if not self.fly then + -- floating in water (or falling) local v = self.object:getvelocity() -- going up then apply gravity if v.y > 0.1 then + self.object:setacceleration({ x = 0, y = self.fall_speed, @@ -698,7 +698,9 @@ minetest.register_entity(name, { -- in water then float up if minetest.registered_nodes[node_ok(pos).name].groups.water then + if self.floats == 1 then + self.object:setacceleration({ x = 0, y = -self.fall_speed / (math.max(1, v.y) ^ 2), @@ -720,12 +722,16 @@ minetest.register_entity(name, { local d = self.old_y - self.object:getpos().y if d > 5 then + self.object:set_hp(self.object:get_hp() - math.floor(d - 5)) + effect(pos, 5, "tnt_smoke.png") + if check_for_death(self) then return end end + self.old_y = self.object:getpos().y end end @@ -733,19 +739,25 @@ minetest.register_entity(name, { -- knockback timer if self.pause_timer > 0 then + self.pause_timer = self.pause_timer - dtime + if self.pause_timer < 1 then self.pause_timer = 0 end + return end -- attack timer self.timer = self.timer + dtime + if self.state ~= "attack" then + if self.timer < 1 then return end + self.timer = 0 end -- never go over 100 @@ -753,9 +765,13 @@ minetest.register_entity(name, { self.timer = 1 end + -- node replace check (cow eats grass etc.) + replace(self, pos) + -- mob plays random sound at times if self.sounds.random and math.random(1, 100) <= 1 then + minetest.sound_play(self.sounds.random, { object = self.object, max_hear_distance = self.sounds.distance @@ -767,16 +783,22 @@ minetest.register_entity(name, { if self.state == "attack" and self.env_damage_timer > 1 then + self.env_damage_timer = 0 + do_env_damage(self) + -- custom function (defined in mob lua file) if self.do_custom then self.do_custom(self) end elseif self.state ~= "attack" then + self.env_damage_timer = 0 + do_env_damage(self) + -- custom function if self.do_custom then self.do_custom(self) @@ -800,6 +822,7 @@ minetest.register_entity(name, { for _,oir in ipairs(minetest.get_objects_inside_radius(s, self.view_range)) do if oir:is_player() then + player = oir type = "player" else @@ -812,6 +835,7 @@ minetest.register_entity(name, { if type == "player" or type == "npc" then + s = self.object:getpos() p = player:getpos() sp = s @@ -820,7 +844,8 @@ minetest.register_entity(name, { dist = ((p.x - s.x) ^ 2 + (p.y - s.y) ^ 2 + (p.z - s.z) ^ 2) ^ 0.5 if dist < self.view_range then - -- and self.in_fov(self,p) then + -- field of view check goes here + -- choose closest player to attack if minetest.line_of_sight(sp, p, 2) == true and dist < min_dist then @@ -848,15 +873,18 @@ minetest.register_entity(name, { local p, dist --MFF local obj = nil - for _, oir in pairs(minetest.get_objects_inside_radius(s,self.view_range)) do + for _, oir in pairs(minetest.get_objects_inside_radius(s, self.view_range)) do obj = oir:get_luaentity() if obj and obj.type == "monster" then + -- attack monster p = obj.object:getpos() + dist = ((p.x - s.x) ^ 2 + (p.y - s.y) ^ 2 + (p.z - s.z) ^ 2) ^ 0.5 + if dist < min_dist then min_dist = dist min_player = obj.object @@ -869,7 +897,7 @@ minetest.register_entity(name, { end end - -- breed mobs and grow children + -- breed and grow children breed(self) -- find player to follow @@ -877,11 +905,15 @@ minetest.register_entity(name, { or self.order == "follow") and not self.following and self.state ~= "attack" then + local s, p, dist + for _,player in pairs(minetest.get_connected_players()) do + s = self.object:getpos() p = player:getpos() dist = ((p.x - s.x) ^ 2 + (p.y - s.y) ^ 2 + (p.z - s.z) ^ 2) ^ 0.5 + if dist < self.view_range then self.following = player break @@ -893,6 +925,7 @@ minetest.register_entity(name, { and self.order == "follow" and self.state ~= "attack" and self.owner ~= "" then + -- npc stop following player if not owner if self.following and self.owner @@ -910,12 +943,16 @@ minetest.register_entity(name, { -- follow that thing if self.following then + local s = self.object:getpos() local p if self.following:is_player() then + p = self.following:getpos() + elseif self.following.object then + p = self.following.object:getpos() end @@ -928,28 +965,36 @@ minetest.register_entity(name, { self.following = nil else local vec = {x = p.x - s.x, y = p.y - s.y, z = p.z - s.z} + yaw = (math.atan(vec.z / vec.x) + pi / 2) - self.rotate + if p.x > s.x then yaw = yaw + pi end + self.object:setyaw(yaw) -- anyone but standing npc's can move along if dist > self.reach and self.order ~= "stand" then + if (self.jump and get_velocity(self) <= 0.5 and self.object:getvelocity().y == 0) or (self.object:getvelocity().y == 0 and self.jump_chance > 0) then + self.direction = { x = math.sin(yaw) * -1, y = 0, z = math.cos(yaw) } + do_jump(self) end + set_velocity(self, self.walk_velocity) + if self.walk_chance ~= 0 then set_animation(self, "walk") end @@ -957,6 +1002,7 @@ minetest.register_entity(name, { set_velocity(self, 0) set_animation(self, "stand") end + return end end @@ -970,8 +1016,11 @@ minetest.register_entity(name, { local s = self.object:getpos() if self.type == "npc" then + local o = minetest.get_objects_inside_radius(self.object:getpos(), 3) + for _,o in ipairs(o) do + if o:is_player() then lp = o:getpos() break @@ -981,14 +1030,18 @@ minetest.register_entity(name, { -- look at any players nearby, otherwise turn randomly if lp then + local vec = {x = lp.x - s.x, y = lp.y - s.y, z = lp.z - s.z} + yaw = (math.atan(vec.z / vec.x) + pi / 2) - self.rotate + if lp.x > s.x then yaw = yaw + pi end else yaw = self.object:getyaw() + ((math.random(0, 360) - 180) / 180 * pi) end + self.object:setyaw(yaw) end @@ -998,12 +1051,14 @@ minetest.register_entity(name, { -- npc's ordered to stand stay standing if self.type == "npc" and self.order == "stand" then + set_velocity(self, 0) self.state = "stand" set_animation(self, "stand") else if self.walk_chance ~= 0 and math.random(1, 100) <= self.walk_chance then + set_velocity(self, self.walk_velocity) self.state = "walk" set_animation(self, "walk") @@ -1019,35 +1074,48 @@ minetest.register_entity(name, { if self.fly and self.fly_in == "default:water_source" and not lp then + print ("out of water") + set_velocity(self, 0) + -- change to undefined state so nothing more happens self.state = "flop" set_animation(self, "stand") + return end -- if water nearby then turn away if lp then + local vec = {x = lp.x - s.x, y = lp.y - s.y, z = lp.z - s.z} + yaw = math.atan(vec.z / vec.x) + 3 * pi / 2 - self.rotate + if lp.x > s.x then yaw = yaw + pi end + self.object:setyaw(yaw) + -- otherwise randomly turn elseif math.random(1, 100) <= 30 then - self.object:setyaw(self.object:getyaw() + ((math.random(0, 360) - 180) / 180 * pi)) + + yaw = self.object:getyaw() + ((math.random(0, 360) - 180) / 180 * pi) + self.object:setyaw(yaw) end -- jump when walking comes to a halt if self.jump and get_velocity(self) <= 0.5 and self.object:getvelocity().y == 0 then + self.direction = { x = math.sin(yaw) * -1, y = 0, z = math.cos(yaw) } + do_jump(self) end @@ -1055,6 +1123,7 @@ minetest.register_entity(name, { set_animation(self, "walk") if math.random(1, 100) <= 30 then + set_velocity(self, 0) self.state = "stand" set_animation(self, "stand") @@ -1073,6 +1142,7 @@ minetest.register_entity(name, { or not self.attack or not self.attack:getpos() or self.attack:get_hp() <= 0 then + --print(" ** stop attacking **", dist, self.view_range) self.state = "stand" set_velocity(self, 0) @@ -1081,20 +1151,26 @@ minetest.register_entity(name, { self.v_start = false self.timer = 0 self.blinktimer = 0 + return end if self.attack_type == "explode" then local vec = {x = p.x - s.x, y = p.y - s.y, z = p.z - s.z} + yaw = math.atan(vec.z / vec.x) + pi / 2 - self.rotate + if p.x > s.x then yaw = yaw + pi end + self.object:setyaw(yaw) if dist > self.reach then + if not self.v_start then + self.v_start = true set_velocity(self, self.run_velocity) self.timer = 0 @@ -1102,48 +1178,69 @@ minetest.register_entity(name, { else self.timer = 0 self.blinktimer = 0 + if get_velocity(self) <= 0.5 and self.object:getvelocity().y == 0 then + local v = self.object:getvelocity() v.y = 5 self.object:setvelocity(v) end + set_velocity(self, self.run_velocity) end + set_animation(self, "run") else set_velocity(self, 0) self.timer = self.timer + dtime self.blinktimer = (self.blinktimer or 0) + dtime + if self.blinktimer > 0.2 then + self.blinktimer = 0 + if self.blinkstatus then self.object:settexturemod("") else self.object:settexturemod("^[brighten") end + self.blinkstatus = not self.blinkstatus end + if self.timer > 3 then - local pos = vector.round(self.object:getpos()) + + local pos = self.object:getpos() + -- hurt player/mobs caught in blast area entity_physics(pos, 3, self) --/MFF (Crabman|06/23/2015)add self to use punch function + if minetest.find_node_near(pos, 1, {"group:water"}) or minetest.is_protected(pos, "") then + if self.sounds.explode then + minetest.sound_play(self.sounds.explode, { pos = pos, gain = 1.0, max_hear_distance = 16 }) end + self.object:remove() + effect(pos, 15, "tnt_smoke.png", 5) + return end + pos.y = pos.y - 1 + mobs:explosion(pos, 2, 0, 1, self.sounds.explode) + self.object:remove() + return end end @@ -1165,12 +1262,15 @@ minetest.register_entity(name, { if nod.name == self.fly_in then if me_y < p_y then + self.object:setvelocity({ x = v.x, y = 1 * self.walk_velocity, z = v.z }) + elseif me_y > p_y then + self.object:setvelocity({ x = v.x, y = -1 * self.walk_velocity, @@ -1179,12 +1279,15 @@ minetest.register_entity(name, { end else if me_y < p_y then + self.object:setvelocity({ x = v.x, y = 0.01, z = v.z }) + elseif me_y > p_y then + self.object:setvelocity({ x = v.x, y = -0.01, @@ -1197,49 +1300,65 @@ minetest.register_entity(name, { -- end fly bit local vec = {x = p.x - s.x, y = p.y - s.y, z = p.z - s.z} + yaw = (math.atan(vec.z / vec.x) + pi / 2) - self.rotate + if p.x > s.x then yaw = yaw + pi end + self.object:setyaw(yaw) -- move towards enemy if beyond mob reach - -- set reach for each mob (default is 3) if dist > self.reach then + -- jump attack if (self.jump and get_velocity(self) <= 0.5 and self.object:getvelocity().y == 0) or (self.object:getvelocity().y == 0 and self.jump_chance > 0) then + self.direction = { x = math.sin(yaw) * -1, y = 0, z = math.cos(yaw) } + do_jump(self) end + set_velocity(self, self.run_velocity) set_animation(self, "run") + else + set_velocity(self, 0) set_animation(self, "punch") + if self.timer > 1 then + self.timer = 0 + local p2 = p local s2 = s + p2.y = p2.y + 1.5 s2.y = s2.y + 1.5 + if minetest.line_of_sight(p2, s2) == true then + -- play attack sound if self.sounds.attack then + minetest.sound_play(self.sounds.attack, { object = self.object, max_hear_distance = self.sounds.distance }) end + -- punch player - self.attack:punch(self.object, 1.0, { + self.attack:punch(self.object, 1.0, { full_punch_interval = 1.0, damage_groups = {fleshy = self.damage} }, nil) @@ -1252,23 +1371,29 @@ minetest.register_entity(name, { p.y = p.y - .5 s.y = s.y + .5 + local dist = ((p.x - s.x) ^ 2 + (p.y - s.y) ^ 2 + (p.z - s.z) ^ 2) ^ 0.5 local vec = {x = p.x - s.x, y = p.y - s.y, z = p.z - s.z} + yaw = (math.atan(vec.z / vec.x) + pi / 2) - self.rotate + if p.x > s.x then yaw = yaw + pi end + self.object:setyaw(yaw) set_velocity(self, 0) if self.shoot_interval and self.timer > self.shoot_interval and math.random(1, 100) <= 60 then + self.timer = 0 set_animation(self, "punch") -- play shoot attack sound if self.sounds.shoot_attack then + minetest.sound_play(self.sounds.shoot_attack, { object = self.object, max_hear_distance = self.sounds.distance @@ -1276,15 +1401,19 @@ minetest.register_entity(name, { end local p = self.object:getpos() + p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2 + local obj = minetest.add_entity(p, self.arrow) local amount = (vec.x ^ 2 + vec.y ^ 2 + vec.z ^ 2) ^ 0.5 local v = obj:get_luaentity().velocity + -- offset makes shoot aim accurate vec.y = vec.y + self.shoot_offset vec.x = vec.x * v / amount vec.y = vec.y * v / amount vec.z = vec.z * v / amount + obj:setvelocity(vec) end end @@ -1297,7 +1426,9 @@ minetest.register_entity(name, { -- remove monsters if playing on peaceful if self.type == "monster" and peaceful_only then + self.object:remove() + return end @@ -1307,16 +1438,20 @@ minetest.register_entity(name, { local tmp = minetest.deserialize(staticdata) if tmp then + for _,stat in pairs(tmp) do self[_] = stat end end else + self.object:remove() + return end -- select random texture, set model and size if not self.base_texture then + self.base_texture = def.textures[math.random(1, #def.textures)] self.base_mesh = def.mesh self.base_size = self.visual_size @@ -1376,6 +1511,7 @@ minetest.register_entity(name, { self.mesh = mesh self.collisionbox = colbox self.visual_size = vis_size + -- set anything changed above self.object:set_properties(self) end, @@ -1383,9 +1519,14 @@ minetest.register_entity(name, { get_staticdata = function(self) -- remove mob when out of range unless tamed - if mobs.remove and self.remove_ok and not self.tamed then - print ("REMOVED", self.remove_ok, self.name) + if mobs.remove + and self.remove_ok + and not self.tamed then + + --print ("REMOVED", self.remove_ok, self.name) + self.object:remove() + return nil end @@ -1394,30 +1535,49 @@ minetest.register_entity(name, { self.following = nil self.state = "stand" + -- used to rotate older mobs + if self.drawtype + and self.drawtype == "side" then + self.rotate = math.rad(90) + end + local tmp = {} for _,stat in pairs(self) do + local t = type(stat) + if t ~= 'function' and t ~= 'nil' and t ~= 'userdata' then tmp[_] = self[_] end end + -- print('===== '..self.name..'\n'.. dump(tmp)..'\n=====\n') return minetest.serialize(tmp) end, on_punch = function(self, hitter, tflp, tool_capabilities, dir) + -- no punch punch spamming + if tflp < 0.45 then + return + end + -- weapon wear hitter:set_detach() --MFF (crabman|27/7/2015) anti usebug, immortal if attached local weapon = hitter:get_wielded_item() - local punch_interval = tool_capabilities.full_punch_interval or 1.4 + local punch_interval = 1.4 - if weapon:get_definition().tool_capabilities ~= nil then - local wear = math.floor((punch_interval / 75) * 9000) - weapon:add_wear(wear) + if tool_capabilities then + punch_interval = tool_capabilities.full_punch_interval or 1.4 + end + + if weapon:get_definition() + and weapon:get_definition().tool_capabilities then + + weapon:add_wear(math.floor((punch_interval / 75) * 9000)) hitter:set_wielded_item(weapon) end @@ -1445,30 +1605,24 @@ minetest.register_entity(name, { -- blood_particles if self.blood_amount > 0 and not disable_blood then + local pos = self.object:getpos() + pos.y = pos.y + (-self.collisionbox[2] + self.collisionbox[5]) / 2 + effect(pos, self.blood_amount, self.blood_texture) end -- knock back effect if self.knock_back > 0 then - local kb = self.knock_back - local r = self.recovery_time local v = self.object:getvelocity() - - if tflp < punch_interval then - - if kb > 0 then - kb = kb * (tflp / punch_interval) - end - - r = r * (tflp / punch_interval) - end + local r = 1.4 - math.min(punch_interval, 1.4) + local kb = r * 5 self.object:setvelocity({ x = dir.x * kb, - y = 0, + y = 2, z = dir.z * kb }) @@ -1515,16 +1669,19 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, -- chance override in minetest.conf for registered mob local new_chance = tonumber(minetest.setting_get(name .. "_chance")) + if new_chance ~= nil then chance = new_chance print ("[Mobs Redo] Chance setting for " .. name .. " is now " .. chance) end minetest.register_abm({ + nodenames = nodes, neighbors = neighbors, interval = interval, chance = chance, + --catch_up = false, action = function(pos, node, _, active_object_count_wider) @@ -1566,10 +1723,17 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, return end - -- spawn mob half block higher then ground + -- spawn mob half block higher than ground pos.y = pos.y - 0.5 - minetest.add_entity(pos, name) - --print ("Spawned "..name.." at "..minetest.pos_to_string(pos).." on "..node.name.." near "..neighbors[1]) + + local mob = minetest.add_entity(pos, name) + --local ent = mob:get_luaentity() + + if mob then + print ("Spawned "..name.." at "..minetest.pos_to_string(pos).." on "..node.name.." near "..neighbors[1]) + else + print (name.." failed to spawn at "..minetest.pos_to_string(pos)) + end end }) @@ -1582,27 +1746,30 @@ function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_o chance, active_object_count, -31000, max_height) end +-- set content id's +local c_air = minetest.get_content_id("air") +local c_ignore = minetest.get_content_id("ignore") +local c_obsidian = minetest.get_content_id("default:obsidian") +local c_brick = minetest.get_content_id("default:obsidianbrick") +local c_chest = minetest.get_content_id("default:chest_locked") + -- explosion (cannot break protected or unbreakable nodes) function mobs:explosion(pos, radius, fire, smoke, sound) + radius = radius or 0 fire = fire or 0 smoke = smoke or 0 - local pos = vector.round(pos) - local vm = VoxelManip() - local minp, maxp = vm:read_from_map(vector.subtract(pos, radius), vector.add(pos, radius)) - local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) - local data = vm:get_data() - local p = {} - local c_air = minetest.get_content_id("air") - local c_ignore = minetest.get_content_id("ignore") - local c_obsidian = minetest.get_content_id("default:obsidian") - local c_brick = minetest.get_content_id("default:obsidianbrick") - local c_chest = minetest.get_content_id("default:chest_locked") + -- if area protected or near map limits then no blast damage + if minetest.is_protected(pos, "") + or not within_limits(pos, radius) then + return + end -- explosion sound if sound and sound ~= "" then + minetest.sound_play(sound, { pos = pos, gain = 1.0, @@ -1610,11 +1777,13 @@ function mobs:explosion(pos, radius, fire, smoke, sound) }) end - -- if area protected or near map limits then no blast damage - if minetest.is_protected(pos, "") - or not within_limits(pos, radius) then - return - end + pos = vector.round(pos) -- voxelmanip doesn't work properly unless pos is rounded ?!?! + + local vm = VoxelManip() + local minp, maxp = vm:read_from_map(vector.subtract(pos, radius), vector.add(pos, radius)) + local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) + local data = vm:get_data() + local p = {} for z = -radius, radius do for y = -radius, radius do @@ -1624,6 +1793,7 @@ function mobs:explosion(pos, radius, fire, smoke, sound) p.x = pos.x + x p.y = pos.y + y p.z = pos.z + z + if p.y >= -19600 --MFF and data[vi] ~= c_air and data[vi] ~= c_ignore @@ -1637,17 +1807,19 @@ function mobs:explosion(pos, radius, fire, smoke, sound) and minetest.get_item_group(n, "nether") == 0 then -- if chest then drop items inside if n == "default:chest" - or n == "3dchest:chest" then + or n == "3dchest:chest" + or n == "bones:bones" then local meta = minetest.get_meta(p) local inv = meta:get_inventory() - for i = 1, 32 do + for i = 1, inv:get_size("main") do local m_stack = inv:get_stack("main", i) local obj = minetest.add_item(p, m_stack) if obj then + obj:setvelocity({ x = math.random(-2, 2), y = 7, @@ -1661,17 +1833,20 @@ function mobs:explosion(pos, radius, fire, smoke, sound) if fire > 0 and (minetest.registered_nodes[n].groups.flammable or math.random(1, 100) <= 30) then + minetest.set_node(p, {name = "fire:basic_flame"}) else - minetest.remove_node(p) - end + minetest.set_node(p, {name = "air"}) - if smoke > 0 then - effect(p, 2, "tnt_smoke.png", 5) + if smoke > 0 then + effect(p, 2, "tnt_smoke.png", 5) + end end end end + vi = vi + 1 + end end end @@ -1683,6 +1858,7 @@ function mobs:register_arrow(name, def) if not name or not def then return end -- errorcheck minetest.register_entity(name, { + physical = false, visual = def.visual, visual_size = def.visual_size, @@ -1703,7 +1879,9 @@ function mobs:register_arrow(name, def) if self.timer > 150 or not within_limits(pos, 0) then + self.object:remove() ; -- print ("removed arrow") + return end @@ -1716,8 +1894,11 @@ function mobs:register_arrow(name, def) self.hit_node(self, pos, node) if self.drop == true then + pos.y = pos.y + 1 + self.lastpos = (self.lastpos or pos) + minetest.add_item(self.lastpos, self.object:get_luaentity().name) end @@ -1749,11 +1930,14 @@ function mobs:register_arrow(name, def) and player:get_luaentity().name ~= "signs:text" then self.hit_mob(self, player) + self.object:remove() ; -- print ("hit mob") + return end end end + self.lastpos = pos end }) @@ -1769,6 +1953,7 @@ function mobs:register_egg(mob, desc, background, addegg) end minetest.register_craftitem(mob, { + description = desc, inventory_image = invimg, @@ -1822,14 +2007,18 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, -- is mob tamed? if self.tamed == false and force_take == false then + minetest.chat_send_player(name, "Not tamed!") + return end -- cannot pick up if not owner if self.owner ~= name and force_take == false then + minetest.chat_send_player(name, self.owner.." is owner!") + return end @@ -1843,13 +2032,19 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, chance = chance_hand elseif tool:get_name() == "mobs:net" then + chance = chance_net + tool:add_wear(4000) -- 17 uses + clicker:set_wielded_item(tool) elseif tool:get_name() == "mobs:magic_lasso" then + chance = chance_lasso + tool:add_wear(650) -- 100 uses + clicker:set_wielded_item(tool) end @@ -1858,7 +2053,9 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, -- calculate chance.. add to inventory if successful? if math.random(1, 100) <= chance then + clicker:get_inventory():add_item("main", mobname) + self.object:remove() else minetest.chat_send_player(name, "Missed!") @@ -1877,42 +2074,58 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) -- take item if not minetest.setting_getbool("creative_mode") then + local item = clicker:get_wielded_item() + item:take_item() + clicker:set_wielded_item(item) end -- heal health - local hp = self.object:get_hp() - hp = hp + 4 + local hp = self.object:get_hp() + 4 + if hp >= self.hp_max then + hp = self.hp_max + if self.htimer < 1 then + minetest.chat_send_player(clicker:get_player_name(), self.name:split(":")[2] .. " at full health (" .. tostring(hp) .. ")") + self.htimer = 5 end end + self.object:set_hp(hp) self.health = hp -- make children grow quicker if self.child == true then + self.hornytimer = self.hornytimer + 20 + return true end -- feed and tame self.food = (self.food or 0) + 1 if self.food == feed_count then + self.food = 0 + if breed and self.hornytimer == 0 then self.horny = true end + self.gotten = false + if tame then + self.tamed = true + if not self.owner or self.owner == "" then self.owner = clicker:get_player_name() end @@ -1920,12 +2133,14 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) -- make sound when fed so many times if self.sounds.random then + minetest.sound_play(self.sounds.random, { object = self.object, max_hear_distance = self.sounds.distance }) end end + return true else return false diff --git a/mods/mobs/bee.lua b/mods/mobs/bee.lua index c6352ceb..d4388dc3 100755 --- a/mods/mobs/bee.lua +++ b/mods/mobs/bee.lua @@ -49,7 +49,7 @@ mobs:register_mob("mobs:bee", { end, }) -- spawn on group:flowers between 4 and 20 light, 1 in 5000 chance, 1 bee in area up to 31000 in height -mobs:spawn_specific("mobs:bee", {"group:flower"}, {"air"}, 4, 20, 30, 5000, 1, -31000, 31000, true) +mobs:spawn_specific("mobs:bee", {"group:flower"}, {"air"}, 4, 20, 30, 5000, 2, -31000, 31000, true) -- register spawn egg mobs:register_egg("mobs:bee", "Bee", "mobs_bee_inv.png", 1) diff --git a/mods/mobs/bunny.lua b/mods/mobs/bunny.lua index dccf6359..b03b374e 100755 --- a/mods/mobs/bunny.lua +++ b/mods/mobs/bunny.lua @@ -44,9 +44,9 @@ mobs:register_mob("mobs:bunny", { }, -- follows carrot from farming redo follow = {"farming:carrot", "farming_plus:carrot_item"}, - view_range = 10, + view_range = 8, -- eat carrots - replace_rate = 80, + replace_rate = 10, replace_what = {"farming:carrot_7", "farming:carrot_8", "farming_plus:carrot"}, replace_with = "air", -- right click to pick up rabbit @@ -73,5 +73,5 @@ mobs:register_mob("mobs:bunny", { attack_type = "dogfight", damage = 5, }) -mobs:spawn_specific("mobs:bunny", {"default:dirt_with_grass"}, {"air"}, 8, 20, 30, 10000, 1, -31000, 31000, true) +mobs:spawn_specific("mobs:bunny", {"default:dirt_with_grass"}, {"air"}, 8, 20, 30, 10000, 2, -31000, 31000, true) mobs:register_egg("mobs:bunny", "Bunny", "mobs_bunny_inv.png", 1) diff --git a/mods/mobs/chicken.lua b/mods/mobs/chicken.lua index 7329ffca..8fb21e6d 100755 --- a/mods/mobs/chicken.lua +++ b/mods/mobs/chicken.lua @@ -7,7 +7,9 @@ mobs:register_mob("mobs:chicken", { -- is it aggressive passive = true, -- health & armor - hp_min = 4, hp_max = 8, armor = 200, + hp_min = 5, + hp_max = 10, + armor = 200, -- textures and model collisionbox = {-0.3, -0.75, -0.3, 0.3, 0.1, 0.3}, visual = "mesh", @@ -53,7 +55,7 @@ mobs:register_mob("mobs:chicken", { }, -- follows wheat follow = {"farming:seed_wheat", "farming:seed_cotton"}, - view_range = 8, + view_range = 5, on_rightclick = function(self, clicker) mobs:feed_tame(self, clicker, 8, true, true) @@ -74,7 +76,7 @@ mobs:register_mob("mobs:chicken", { end, }) -- spawn on default or bamboo grass between 8 and 20 light, 1 in 10000 change, 1 chicken in area up to 31000 in height -mobs:spawn_specific("mobs:chicken", {"default:dirt_with_grass"}, {"air"}, 8, 20, 30, 10000, 1, -31000, 31000, true) +mobs:spawn_specific("mobs:chicken", {"default:dirt_with_grass"}, {"air"}, 8, 20, 30, 10000, 2, -31000, 31000, true) -- register spawn egg mobs:register_egg("mobs:chicken", "Chicken", "mobs_chicken_inv.png", 1) -- egg entity diff --git a/mods/mobs/cow.lua b/mods/mobs/cow.lua index 4b4cfc17..18b02009 100755 --- a/mods/mobs/cow.lua +++ b/mods/mobs/cow.lua @@ -48,15 +48,20 @@ mobs:register_mob("mobs:cow", { light_damage = 0, -- model animation animation = { - speed_normal = 15, speed_run = 15, - stand_start = 0, stand_end = 30, - walk_start = 35, walk_end = 65, - run_start = 105, run_end = 135, - punch_start = 70, punch_end = 100, + speed_normal = 15, + speed_run = 15, + stand_start = 0, + stand_end = 30, + walk_start = 35, + walk_end = 65, + run_start = 105, + run_end = 135, + punch_start = 70, + punch_end = 100, }, follow = "farming:wheat", - view_range = 8, - replace_rate = 50, + view_range = 7, + replace_rate = 10, replace_what = {"default:grass_3", "default:grass_4", "default:grass_5", "farming:wheat_8"}, replace_with = "air", on_rightclick = function(self, clicker) @@ -87,7 +92,7 @@ mobs:register_mob("mobs:cow", { }) -- spawn on default;green;prairie grass between 0 and 20 light, 1 in 11000 chance, 1 cow in area up to 31000 in height -mobs:spawn_specific("mobs:cow", {"default:dirt_with_grass"}, {"air"}, 8, 20, 30, 10000, 1, -31000, 31000, true) +mobs:spawn_specific("mobs:cow", {"default:dirt_with_grass"}, {"air"}, 8, 20, 30, 10000, 2, -31000, 31000, true) -- register spawn egg mobs:register_egg("mobs:cow", "Cow", "mobs_cow_inv.png", 1) diff --git a/mods/mobs/ent.lua b/mods/mobs/ent.lua old mode 100644 new mode 100755 diff --git a/mods/mobs/sheep.lua b/mods/mobs/sheep.lua index 84aeac67..160f5c19 100755 --- a/mods/mobs/sheep.lua +++ b/mods/mobs/sheep.lua @@ -54,9 +54,9 @@ for _, col in ipairs(all_colours) do walk_start = 81, walk_end = 100, }, follow = {"farming:wheat", "default:grass_5"}, - view_range = 10, + view_range = 8, -- replace grass/wheat with air (eat) - replace_rate = 50, + replace_rate = 10, replace_what = {"default:grass_3", "default:grass_4", "default:grass_5", "farming:wheat_8"}, replace_with = "air", -- right click sheep to shear sheep and get wood, feed 8 wheat for wool to grow back @@ -143,7 +143,7 @@ for _, col in ipairs(all_colours) do end -mobs:spawn_specific("mobs:sheep_white", {"default:dirt_with_grass"}, {"air"}, 8, 20, 30, 10000, 1, -31000, 31000, true) +mobs:spawn_specific("mobs:sheep_white", {"default:dirt_with_grass"}, {"air"}, 8, 20, 30, 10000, 2, -31000, 31000, true) -- compatibility (item and entity) minetest.register_alias("mobs:sheep", "mobs:sheep_white")