From aeb802ad409e59c62cdf394248a6f4a671ff3c49 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 17 Dec 2018 09:17:36 +0000 Subject: [PATCH 001/135] forgot to revert testing returns, oopsie! fixed! --- api.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index 749f2d3..a4a0941 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20181209", + version = "20181217", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -3729,7 +3729,7 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, minetest.chat_send_player(name, S("Not tamed!")) - return true -- false + return false end -- cannot pick up if not owner @@ -3738,7 +3738,7 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, minetest.chat_send_player(name, S("@1 is owner!", self.owner)) - return true -- false + return false end if clicker:get_inventory():room_for_item("main", mobname) then @@ -3807,10 +3807,14 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, mob_sound(self, "default_place_node_hard") + return new_stack + elseif chance ~= 0 then minetest.chat_send_player(name, S("Missed!")) mob_sound(self, "mobs_swing") + + return false end end From 487e037cc9d575ecda39077ec0f141eaa8568111 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 20 Dec 2018 11:14:10 +0000 Subject: [PATCH 002/135] added mobs:force_capture() function, api now uses metatables thanks to bell07 --- api.lua | 581 +++++++++++++++++++++++++++++++----------------------- api.txt | 5 + readme.MD | 1 + 3 files changed, 337 insertions(+), 250 deletions(-) diff --git a/api.lua b/api.lua index a4a0941..a409a99 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20181217", + version = "20181220", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -78,9 +78,75 @@ local node_snowblock = "default:snowblock" local node_snow = "default:snow" mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "default:dirt" +local mob_class = { + stepheight = 1.1, -- was 0.6 + fly_in = "air", + owner = "", + order = "", + jump_height = 4, -- was 6 + lifetimer = 180, -- 3 minutes + physical = true, + collisionbox = {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}, + visual_size = {x = 1, y = 1}, + makes_footstep_sound = false, + view_range = 5, + walk_velocity = 1, + run_velocity = 2, + light_damage = 0, + light_damage_min = 14, + light_damage_max = 15, + water_damage = 0, + lava_damage = 0, + suffocation = 2, + fall_damage = 1, + fall_speed = -10, -- must be lower than -2 (default: -10) + drops = {}, + armor = 100, + sounds = {}, + jump = true, + knock_back = true, + walk_chance = 50, + attack_chance = 5, + passive = false, + blood_amount = 5, + blood_texture = "mobs_blood.png", + shoot_offset = 0, + floats = 1, -- floats in water by default + replace_offset = 0, + timer = 0, + env_damage_timer = 0, -- only used when state = "attack" + tamed = false, + pause_timer = 0, + horny = false, + hornytimer = 0, + child = false, + gotten = false, + health = 0, + reach = 3, + htimer = 0, + docile_by_day = false, + time_of_day = 0.5, + fear_height = 0, + runaway_timer = 0, + immune_to = {}, + explosion_timer = 3, + allow_fuse_reset = true, + stop_to_explode = true, + dogshoot_count = 0, + dogshoot_count_max = 5, + dogshoot_count2_max = 5, + group_attack = false, + attack_monsters = false, + attack_animals = false, + attack_players = true, + attack_npcs = true, + facing_fence = false, + _cmi_is_mob = true, +} +local mob_class_meta = {__index = mob_class} -- play sound -local mob_sound = function(self, sound) +function mob_class:mob_sound(sound) if sound then minetest.sound_play(sound, { @@ -93,7 +159,7 @@ end -- attack player/mob -local do_attack = function(self, player) +function mob_class:do_attack(player) if self.state == "attack" then return @@ -103,7 +169,7 @@ local do_attack = function(self, player) self.state = "attack" if random(0, 100) < 90 then - mob_sound(self, self.sounds.war_cry) + self:mob_sound(self.sounds.war_cry) end end @@ -118,7 +184,7 @@ end -- collision function based on jordan4ibanez' open_ai mod -local collision = function(self) +function mob_class:collision() local pos = self.object:get_pos() local vel = self.object:get_velocity() @@ -145,13 +211,13 @@ end -- move mob in facing direction -local set_velocity = function(self, v) +function mob_class:set_velocity(v) local c_x, c_y = 0, 0 -- can mob be pushed, if so calculate direction if self.pushable then - c_x, c_y = unpack(collision(self)) + c_x, c_y = unpack(self:collision()) end -- halt mob if it has been ordered to stay @@ -170,13 +236,13 @@ local set_velocity = function(self, v) end -- global version of above function -function mobs:set_velocity(self, v) - set_velocity(self, v) +function mobs:set_velocity(entity, v) + mob_class.set_velocity(entity, v) end -- calculate mob velocity -local get_velocity = function(self) +function mob_class:get_velocity() local v = self.object:get_velocity() @@ -185,7 +251,7 @@ end -- set and return valid yaw -local set_yaw = function(self, yaw, delay) +function mob_class:set_yaw(yaw, delay) if not yaw or yaw ~= yaw then yaw = 0 @@ -205,13 +271,13 @@ local set_yaw = function(self, yaw, delay) end -- global function to set mob yaw -function mobs:yaw(self, yaw, delay) - set_yaw(self, yaw, delay) +function mobs:yaw(entity, yaw, delay) + mob_class.set_yaw(entity, yaw, delay) end -- set defined animation -local set_animation = function(self, anim) +function mob_class:set_animation(anim) if not self.animation or not anim then return end @@ -257,13 +323,13 @@ local set_animation = function(self, anim) end -- above function exported for mount.lua -function mobs:set_animation(self, anim) - set_animation(self, anim) +function mobs:set_animation(entity, anim) + mob_class.set_animation(entity, anim) end -- check line of sight (BrunoMine) -local line_of_sight = function(self, pos1, pos2, stepsize) +function mob_class:line_of_sight(pos1, pos2, stepsize) stepsize = stepsize or 1 @@ -334,7 +400,7 @@ end -- check line of sight (by BrunoMine, tweaked by Astrobe) -local NEW_line_of_sight = function(self, pos1, pos2, stepsize) +function mob_class:NEW_line_of_sight(pos1, pos2, stepsize) if not pos1 or not pos2 then return end @@ -381,14 +447,14 @@ local NEW_line_of_sight = function(self, pos1, pos2, stepsize) end -- global function -function mobs:line_of_sight(self, pos1, pos2, stepsize) +function mobs:line_of_sight(entity, pos1, pos2, stepsize) - return line_of_sight(self, pos1, pos2, stepsize) + return mob_class.line_of_sight(entity, pos1, pos2, stepsize) end -- are we flying in what we are suppose to? (taikedz) -local flight_check = function(self, pos_w) +function mob_class:flight_check(pos_w) local def = minetest.registered_nodes[self.standing_in] @@ -450,7 +516,7 @@ end -- update nametag colour -local update_tag = function(self) +function mob_class:update_tag() local col = "#00FF00" local qua = self.hp_max / 4 @@ -476,7 +542,7 @@ end -- drop items -local item_drop = function(self) +function mob_class:item_drop() -- check for nil or no drops if not self.drops or #self.drops == 0 then @@ -541,7 +607,7 @@ end -- check if mob is dead or only hurt -local check_for_death = function(self, cmi_cause) +function mob_class:check_for_death(cmi_cause) -- has health actually changed? if self.health == self.old_health and self.health > 0 then @@ -553,7 +619,7 @@ local check_for_death = function(self, cmi_cause) -- still got some health? play hurt sound if self.health > 0 then - mob_sound(self, self.sounds.damage) + self:mob_sound(self.sounds.damage) -- make sure health isn't higher than max if self.health > self.hp_max then @@ -571,7 +637,7 @@ local check_for_death = function(self, cmi_cause) self.htimer = 2 self.nametag = "♥ " .. self.health .. " / " .. self.hp_max - update_tag(self) + self:update_tag() end return false @@ -580,16 +646,16 @@ local check_for_death = function(self, cmi_cause) self.cause_of_death = cmi_cause -- drop items - item_drop(self) + self:item_drop() - mob_sound(self, self.sounds.death) + self:mob_sound(self.sounds.death) local pos = self.object:get_pos() -- execute custom death function if self.on_die then - self.on_die(self, pos) + self:on_die(pos) if use_cmi then cmi.notify_die(self.object, cmi_cause) @@ -615,8 +681,8 @@ local check_for_death = function(self, cmi_cause) self.blinktimer = 0 self.passive = true self.state = "die" - set_velocity(self, 0) - set_animation(self, "die") + self:set_velocity(0) + self:set_animation("die") minetest.after(length, function(self) @@ -642,7 +708,7 @@ end -- is mob facing a cliff -local is_at_cliff = function(self) +function mob_class:is_at_cliff() if self.fear_height == 0 then -- 0 for no falling protection! return false @@ -682,7 +748,7 @@ end -- environmental damage (water, lava, fire, light etc.) -local do_env_damage = function(self) +function mob_class:do_env_damage() -- feed/tame text timer (so mob 'full' messages dont spam chat) if self.htimer > 0 then @@ -695,7 +761,7 @@ local do_env_damage = function(self) self.nametag = self.nametag2 self.nametag2 = nil - update_tag(self) + self:update_tag() end local pos = self.object:get_pos() @@ -720,7 +786,7 @@ local do_env_damage = function(self) effect(pos, 5, "tnt_smoke.png") - if check_for_death(self, {type = "light"}) then return end + if self:check_for_death({type = "light"}) then return end end end @@ -738,7 +804,7 @@ local do_env_damage = function(self) effect(pos, 5, "bubble.png", nil, nil, 1, nil) - if check_for_death(self, {type = "environment", + if self:check_for_death({type = "environment", pos = pos, node = self.standing_in}) then return end end @@ -755,7 +821,7 @@ local do_env_damage = function(self) effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil) - if check_for_death(self, {type = "environment", + if self:check_for_death({type = "environment", pos = pos, node = self.standing_in, hot = true}) then return end end @@ -766,7 +832,7 @@ local do_env_damage = function(self) effect(pos, 5, "tnt_smoke.png") - if check_for_death(self, {type = "environment", + if self:check_for_death({type = "environment", pos = pos, node = self.standing_in}) then return end end --[[ @@ -778,16 +844,16 @@ local do_env_damage = function(self) self.health = self.health - self.suffocation - if check_for_death(self, {type = "environment", + if self:check_for_death({type = "environment", pos = pos, node = self.standing_in}) then return end end ]] - check_for_death(self, {type = "unknown"}) + self:check_for_death({type = "unknown"}) end -- jump if facing a solid node (not fences or gates) -local do_jump = function(self) +function mob_class:do_jump() if not self.jump or self.jump_height == 0 @@ -801,7 +867,7 @@ local do_jump = function(self) -- something stopping us while moving? if self.state ~= "stand" - and get_velocity(self) > 0.5 + and self:get_velocity() > 0.5 and self.object:get_velocity().y ~= 0 then return false end @@ -848,7 +914,7 @@ local do_jump = function(self) v.y = self.jump_height - set_animation(self, "jump") -- only when defined + self:set_animation("jump") -- only when defined self.object:set_velocity(v) @@ -865,8 +931,8 @@ local do_jump = function(self) end end, self, v) - if get_velocity(self) > 0 then - mob_sound(self, self.sounds.jump) + if self:get_velocity() > 0 then + self:mob_sound(self.sounds.jump) end else self.facing_fence = true @@ -882,7 +948,7 @@ local do_jump = function(self) local yaw = self.object:get_yaw() or 0 - yaw = set_yaw(self, yaw + 1.35, 8) + yaw = self:set_yaw(yaw + 1.35, 8) --print ("---- turn") self.jump_count = 0 end @@ -923,7 +989,7 @@ end -- should mob follow what I'm holding ? -local follow_holding = function(self, clicker) +function mob_class:follow_holding(clicker) if mobs.invis[clicker:get_player_name()] then return false @@ -953,7 +1019,7 @@ end -- find two animals of same type and breed if nearby and horny -local breed = function(self) +function mob_class:breed() -- child takes 240 seconds before growing into adult if self.child == true then @@ -1064,7 +1130,7 @@ local breed = function(self) if self.on_breed then -- when false skip going any further - if self.on_breed(self, ent) == false then + if self:on_breed(ent) == false then return end else @@ -1120,7 +1186,7 @@ end -- find and replace what mob is looking for (grass, wheat etc.) -local replace = function(self, pos) +function mob_class:replace(pos) if not mobs_griefing or not self.replace_rate @@ -1157,7 +1223,7 @@ local replace = function(self, pos) local on_replace_return if self.on_replace then - on_replace_return = self.on_replace(self, pos, oldnode, newnode) + on_replace_return = self:on_replace(pos, oldnode, newnode) end if on_replace_return ~= false then @@ -1168,7 +1234,7 @@ end -- check if daytime and also if mob is docile during daylight hours -local day_docile = function(self) +function mob_class:day_docile() if self.docile_by_day == false then @@ -1187,7 +1253,7 @@ local los_switcher = false local height_switcher = false -- path finding and smart mob routine by rnd, line_of_sight and other edits by Elkien3 -local smart_mobs = function(self, s, p, dist, dtime) +function mob_class:smart_mobs(s, p, dist, dtime) local s1 = self.path.lastpos @@ -1327,7 +1393,7 @@ local smart_mobs = function(self, s, p, dist, dtime) ]] self.state = "" - do_attack(self, self.attack) + self:do_attack(self.attack) -- no path found, try something else if not self.path.way then @@ -1426,11 +1492,11 @@ local smart_mobs = function(self, s, p, dist, dtime) self.path.stuck_timer = stuck_timeout - 2 -- frustration! cant find the damn path :( - mob_sound(self, self.sounds.random) + self:mob_sound(self.sounds.random) else -- yay i found path - mob_sound(self, self.sounds.war_cry) - set_velocity(self, self.walk_velocity) + self:mob_sound(self.sounds.war_cry) + self:set_velocity(self.walk_velocity) -- follow path now that it has it self.path.following = true @@ -1460,12 +1526,12 @@ end -- general attack function for all mobs ========== -local general_attack = function(self) +function mob_class:general_attack() -- return if already attacking, passive or docile during day if self.passive or self.state == "attack" - or day_docile(self) then + or self:day_docile() then return end @@ -1527,7 +1593,7 @@ local general_attack = function(self) -- choose closest player to attack that isnt self if dist ~= 0 and dist < min_dist - and line_of_sight(self, sp, p, 2) == true then + and self:line_of_sight(sp, p, 2) == true then min_dist = dist min_player = player end @@ -1535,7 +1601,7 @@ local general_attack = function(self) -- attack closest player or mob if min_player and random(1, 100) > self.attack_chance then - do_attack(self, min_player) + self:do_attack(min_player) end end @@ -1561,7 +1627,7 @@ end -- find someone to runaway from -local runaway_from = function(self) +function mob_class:do_runaway_from() if not self.runaway_from then return @@ -1611,7 +1677,7 @@ local runaway_from = function(self) -- choose closest player/mob to runaway from if dist < min_dist - and line_of_sight(self, sp, p, 2) == true then + and self:line_of_sight(sp, p, 2) == true then min_dist = dist min_player = player end @@ -1633,7 +1699,7 @@ local runaway_from = function(self) yaw = yaw + pi end - yaw = set_yaw(self, yaw, 4) + yaw = self:set_yaw(yaw, 4) self.state = "runaway" self.runaway_timer = 3 self.following = nil @@ -1642,7 +1708,7 @@ end -- follow player if owner or holding item, if fish outta water then flop -local follow_flop = function(self) +function mob_class:follow_flop() -- find player to follow if (self.follow ~= "" @@ -1681,7 +1747,7 @@ local follow_flop = function(self) -- stop following player if not holding specific item if self.following and self.following:is_player() - and follow_holding(self, self.following) == false then + and self:follow_holding(self.following) == false then self.following = nil end @@ -1719,20 +1785,20 @@ local follow_flop = function(self) if p.x > s.x then yaw = yaw + pi end - yaw = set_yaw(self, yaw, 6) + yaw = self:set_yaw(yaw, 6) -- anyone but standing npc's can move along if dist > self.reach and self.order ~= "stand" then - set_velocity(self, self.walk_velocity) + self:set_velocity(self.walk_velocity) if self.walk_chance ~= 0 then - set_animation(self, "walk") + self:set_animation("walk") end else - set_velocity(self, 0) - set_animation(self, "stand") + self:set_velocity(0) + self:set_animation("stand") end return @@ -1743,12 +1809,12 @@ 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:get_pos() - if not flight_check(self, s) then + if not self:flight_check(s) then self.state = "flop" self.object:set_velocity({x = 0, y = -5, z = 0}) - set_animation(self, "stand") + self:set_animation("stand") return elseif self.state == "flop" then @@ -1759,7 +1825,7 @@ end -- dogshoot attack switch and counter function -local dogswitch = function(self, dtime) +function mob_class:dogswitch(dtime) -- switch mode not activated if not self.dogshoot_switch @@ -1788,7 +1854,7 @@ end -- execute current state (stand, walk, run, attacks) -local do_states = function(self, dtime) +function mob_class:do_states(dtime) local yaw = self.object:get_yaw() or 0 @@ -1823,22 +1889,22 @@ local do_states = function(self, dtime) yaw = yaw + random(-0.5, 0.5) end - yaw = set_yaw(self, yaw, 8) + yaw = self:set_yaw(yaw, 8) end - set_velocity(self, 0) - set_animation(self, "stand") + self:set_velocity(0) + self:set_animation("stand") -- mobs ordered to stand stay standing if self.order ~= "stand" and self.walk_chance ~= 0 and self.facing_fence ~= true and random(1, 100) <= self.walk_chance - and is_at_cliff(self) == false then + and self:is_at_cliff() == false then - set_velocity(self, self.walk_velocity) + self:set_velocity(self.walk_velocity) self.state = "walk" - set_animation(self, "walk") + self:set_animation("walk") end elseif self.state == "walk" then @@ -1885,9 +1951,9 @@ 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, yaw, 6) - do_jump(self) - set_velocity(self, self.walk_velocity) + yaw = self:set_yaw(yaw, 6) + self:do_jump() + self:set_velocity(self.walk_velocity) else yaw = yaw + random(-0.5, 0.5) end @@ -1904,36 +1970,36 @@ local do_states = function(self, dtime) if lp.x > s.x then yaw = yaw + pi end end - yaw = set_yaw(self, yaw, 8) + yaw = self:set_yaw(yaw, 8) -- otherwise randomly turn elseif random(1, 100) <= 30 then yaw = yaw + random(-0.5, 0.5) - yaw = set_yaw(self, yaw, 8) + yaw = self:set_yaw(yaw, 8) end -- stand for great fall in front - local temp_is_cliff = is_at_cliff(self) + local temp_is_cliff = self:is_at_cliff() if self.facing_fence == true or temp_is_cliff or random(1, 100) <= 30 then - set_velocity(self, 0) + self:set_velocity(0) self.state = "stand" - set_animation(self, "stand") + self:set_animation("stand") else - set_velocity(self, self.walk_velocity) + self:set_velocity(self.walk_velocity) - if flight_check(self) + if self:flight_check() and self.animation and self.animation.fly_start and self.animation.fly_end then - set_animation(self, "fly") + self:set_animation("fly") else - set_animation(self, "walk") + self:set_animation("walk") end end @@ -1944,15 +2010,15 @@ local do_states = function(self, dtime) -- stop after 5 seconds or when at cliff if self.runaway_timer > 5 - or is_at_cliff(self) + or self:is_at_cliff() or self.order == "stand" then self.runaway_timer = 0 - set_velocity(self, 0) + self:set_velocity(0) self.state = "stand" - set_animation(self, "stand") + self:set_animation("stand") else - set_velocity(self, self.run_velocity) - set_animation(self, "walk") + self:set_velocity(self.run_velocity) + self:set_animation("walk") end -- attack routines (explode, dogfight, shoot, dogshoot) @@ -1972,8 +2038,8 @@ local do_states = function(self, dtime) -- print(" ** stop attacking **", dist, self.view_range) self.state = "stand" - set_velocity(self, 0) - set_animation(self, "stand") + self:set_velocity(0) + self:set_animation("stand") self.attack = nil self.v_start = false self.timer = 0 @@ -1994,7 +2060,7 @@ local do_states = function(self, dtime) if p.x > s.x then yaw = yaw + pi end - yaw = set_yaw(self, yaw) + yaw = self:set_yaw(yaw) local node_break_radius = self.explosion_radius or 1 local entity_damage_radius = self.explosion_damage_radius @@ -2003,19 +2069,19 @@ local do_states = function(self, dtime) -- start timer when in reach and line of sight if not self.v_start and dist <= self.reach - and line_of_sight(self, s, p, 2) then + and self:line_of_sight(s, p, 2) then self.v_start = true self.timer = 0 self.blinktimer = 0 - mob_sound(self, self.sounds.fuse) + self:mob_sound(self.sounds.fuse) -- print ("=== explosion timer started", self.explosion_timer) -- stop timer if out of reach or direct line of sight elseif self.allow_fuse_reset and self.v_start and (dist > self.reach - or not line_of_sight(self, s, p, 2)) then + or not self:line_of_sight(s, p, 2)) then self.v_start = false self.timer = 0 self.blinktimer = 0 @@ -2025,15 +2091,15 @@ local do_states = function(self, dtime) -- walk right up to player unless the timer is active if self.v_start and (self.stop_to_explode or dist < 1.5) then - set_velocity(self, 0) + self:set_velocity(0) else - set_velocity(self, self.run_velocity) + self:set_velocity(self.run_velocity) end if self.animation and self.animation.run_start then - set_animation(self, "run") + self:set_animation("run") else - set_animation(self, "walk") + self:set_animation("walk") end if self.v_start then @@ -2094,8 +2160,8 @@ local do_states = function(self, dtime) end elseif self.attack_type == "dogfight" - or (self.attack_type == "dogshoot" and dogswitch(self, dtime) == 2) - or (self.attack_type == "dogshoot" and dist <= self.reach and dogswitch(self) == 0) then + or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 2) + or (self.attack_type == "dogshoot" and dist <= self.reach and self:dogswitch() == 0) then if self.fly and dist > self.reach then @@ -2106,7 +2172,7 @@ local do_states = function(self, dtime) local p_y = floor(p2.y + 1) local v = self.object:get_velocity() - if flight_check(self, s) then + if self:flight_check(s) then if me_y < p_y then @@ -2182,7 +2248,7 @@ local do_states = function(self, dtime) if p.x > s.x then yaw = yaw + pi end - yaw = set_yaw(self, yaw) + yaw = self:set_yaw(yaw) -- move towards enemy if beyond mob reach if dist > self.reach then @@ -2191,25 +2257,25 @@ local do_states = function(self, dtime) if self.pathfinding -- only if mob has pathfinding enabled and enable_pathfinding then - smart_mobs(self, s, p, dist, dtime) + self:smart_mobs(s, p, dist, dtime) end - if is_at_cliff(self) then + if self:is_at_cliff() then - set_velocity(self, 0) - set_animation(self, "stand") + self:set_velocity(0) + self:set_animation("stand") else if self.path.stuck then - set_velocity(self, self.walk_velocity) + self:set_velocity(self.walk_velocity) else - set_velocity(self, self.run_velocity) + self:set_velocity(self.run_velocity) end if self.animation and self.animation.run_start then - set_animation(self, "run") + self:set_animation("run") else - set_animation(self, "walk") + self:set_animation("walk") end end @@ -2219,7 +2285,7 @@ local do_states = function(self, dtime) self.path.stuck_timer = 0 self.path.following = false -- not stuck anymore - set_velocity(self, 0) + self:set_velocity(0) if not self.custom_attack then @@ -2229,9 +2295,9 @@ local do_states = function(self, dtime) -- if self.double_melee_attack -- and random(1, 2) == 1 then --- set_animation(self, "punch2") +-- self:set_animation("punch2") -- else - set_animation(self, "punch") + self:set_animation("punch") -- end local p2 = p @@ -2240,10 +2306,10 @@ local do_states = function(self, dtime) p2.y = p2.y + .5 s2.y = s2.y + .5 - if line_of_sight(self, p2, s2) == true then + if self:line_of_sight(p2, s2) == true then -- play attack sound - mob_sound(self, self.sounds.attack) + self:mob_sound(self.sounds.attack) -- punch player (or what player is attached to) local attached = self.attack:get_attach() @@ -2262,14 +2328,14 @@ local do_states = function(self, dtime) self.timer = 0 - self.custom_attack(self, p) + self:custom_attack(p) end end end elseif self.attack_type == "shoot" - or (self.attack_type == "dogshoot" and dogswitch(self, dtime) == 1) - or (self.attack_type == "dogshoot" and dist > self.reach and dogswitch(self) == 0) then + or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 1) + or (self.attack_type == "dogshoot" and dist > self.reach and self:dogswitch() == 0) then p.y = p.y - .5 s.y = s.y + .5 @@ -2285,19 +2351,19 @@ local do_states = function(self, dtime) if p.x > s.x then yaw = yaw + pi end - yaw = set_yaw(self, yaw) + yaw = self:set_yaw(yaw) - set_velocity(self, 0) + self:set_velocity(0) if self.shoot_interval and self.timer > self.shoot_interval and random(1, 100) <= 60 then self.timer = 0 - set_animation(self, "shoot") + self:set_animation("shoot") -- play shoot attack sound - mob_sound(self, self.sounds.shoot_attack) + self:mob_sound(self.sounds.shoot_attack) local p = self.object:get_pos() @@ -2328,7 +2394,7 @@ end -- falling and fall damage -local falling = function(self, pos) +function mob_class:falling(pos) if self.fly or self.disable_falling then return @@ -2385,7 +2451,7 @@ local falling = function(self, pos) effect(pos, 5, "tnt_smoke.png", 1, 2, 2, nil) - if check_for_death(self, {type = "fall"}) then + if self:check_for_death({type = "fall"}) then return end end @@ -2400,7 +2466,7 @@ end local tr = minetest.get_modpath("toolranks") -- deal damage and effects when mob punched -local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) +function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) -- mob health check if self.health <= 0 then @@ -2409,7 +2475,7 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) -- custom punch function if self.do_punch - and self.do_punch(self, hitter, tflp, tool_capabilities, dir) == false then + and self:do_punch(hitter, tflp, tool_capabilities, dir) == false then return end @@ -2555,7 +2621,7 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) local hot = tool_capabilities and tool_capabilities.damage_groups and tool_capabilities.damage_groups.fire - if check_for_death(self, {type = "punch", + if self:check_for_death({type = "punch", puncher = hitter, hot = hot}) then return end @@ -2621,7 +2687,7 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) yaw = yaw + pi end - yaw = set_yaw(self, yaw, 6) + yaw = self:set_yaw(yaw, 6) self.state = "runaway" self.runaway_timer = 0 self.following = nil @@ -2639,7 +2705,7 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) -- attack whoever punched mob self.state = "" - do_attack(self, hitter) + self:do_attack(hitter) -- alert others to the attack local objs = minetest.get_objects_inside_radius(hitter:get_pos(), self.view_range) @@ -2656,12 +2722,12 @@ local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) and obj.state ~= "attack" and obj.owner ~= name and obj.name == self.name then - do_attack(obj, hitter) + obj:do_attack(hitter) end -- have owned mobs attack player threat if obj.owner == name and obj.owner_loyal then - do_attack(obj, self.object) + obj:do_attack(self.object) end end end @@ -2670,7 +2736,7 @@ end -- get entity staticdata -local mob_staticdata = function(self) +function mob_class:get_staticdata() -- remove mob when out of range unless tamed if remove_far @@ -2722,7 +2788,7 @@ end -- activate mob and reload settings -local mob_activate = function(self, staticdata, def, dtime) +function mob_class:mob_activate(staticdata, def, dtime) -- remove monsters in peaceful mode if self.type == "monster" @@ -2842,9 +2908,9 @@ local mob_activate = function(self, staticdata, def, dtime) -- set anything changed above self.object:set_properties(self) - set_yaw(self, (random(0, 360) - 180) / 180 * pi, 6) - update_tag(self) - set_animation(self, "stand") + self:set_yaw((random(0, 360) - 180) / 180 * pi, 6) + self:update_tag() + self:set_animation("stand") -- run on_spawn function if found if self.on_spawn and not self.on_spawn_run then @@ -2855,7 +2921,7 @@ local mob_activate = function(self, staticdata, def, dtime) -- run after_activate if def.after_activate then - def.after_activate(self, staticdata, def, dtime) + def:after_activate(staticdata, def, dtime) end if use_cmi then @@ -2866,7 +2932,7 @@ end -- handle mob lifetimer and expiration -local mob_expire = function(self, pos, dtime) +function mob_class:mob_expire(pos, dtime) -- when lifetimer expires remove mob (except npc and tamed) if self.type ~= "npc" @@ -2906,7 +2972,7 @@ end -- main mob function -local mob_step = function(self, dtime) +function mob_class:on_step(dtime) if use_cmi then cmi.notify_step(self.object, dtime) @@ -2934,11 +3000,11 @@ local mob_step = function(self, dtime) -- print ("standing in " .. self.standing_in) -- check for mob expiration (0.25 instead of dtime since were in a timer) - mob_expire(self, pos, 0.25) + self:mob_expire(pos, 0.25) end -- check if falling, flying, floating - falling(self, pos) + self:falling(pos) -- smooth rotation by ThomasMonroe314 @@ -2992,7 +3058,7 @@ local mob_step = function(self, dtime) if self.do_custom then -- when false skip going any further - if self.do_custom(self, dtime) == false then + if self:do_custom(dtime) == false then return end end @@ -3016,7 +3082,7 @@ local mob_step = function(self, dtime) -- mob plays random sound at times if random(1, 100) == 1 then - mob_sound(self, self.sounds.random) + self:mob_sound(self.sounds.random) end -- environmental damage timer (every 1 second) @@ -3028,33 +3094,33 @@ local mob_step = function(self, dtime) self.env_damage_timer = 0 -- check for environmental damage (water, fire, lava etc.) - do_env_damage(self) + self:do_env_damage() -- node replace check (cow eats grass etc.) - replace(self, pos) + self:replace(pos) end - general_attack(self) + self:general_attack() - breed(self) + self:breed() - follow_flop(self) + self:follow_flop() - do_states(self, dtime) + self:do_states(dtime) - do_jump(self) + self:do_jump() - runaway_from(self) + self:do_runaway_from(self) end -- default function when mobs are blown up with TNT -local do_tnt = function(obj, damage) +function mob_class:on_blast(damage) --print ("----- Damage", damage) - obj.object:punch(obj.object, 1.0, { + self.object:punch(self.object, 1.0, { full_punch_interval = 1.0, damage_groups = {fleshy = damage}, }, nil) @@ -3070,131 +3136,107 @@ function mobs:register_mob(name, def) mobs.spawning_mobs[name] = true -minetest.register_entity(name, { +minetest.register_entity(name, setmetatable({ - stepheight = def.stepheight or 1.1, -- was 0.6 + stepheight = def.stepheight, name = name, type = def.type, attack_type = def.attack_type, fly = def.fly, - fly_in = def.fly_in or "air", - owner = def.owner or "", - order = def.order or "", + fly_in = def.fly_in, + owner = def.owner, + order = def.order, on_die = def.on_die, do_custom = def.do_custom, - jump_height = def.jump_height or 4, -- was 6 + jump_height = def.jump_height, drawtype = def.drawtype, -- DEPRECATED, use rotate instead rotate = math.rad(def.rotate or 0), -- 0=front, 90=side, 180=back, 270=side2 - lifetimer = def.lifetimer or 180, -- 3 minutes + lifetimer = def.lifetimer, hp_min = max(1, (def.hp_min or 5) * difficulty), hp_max = max(1, (def.hp_max or 10) * difficulty), - physical = true, - collisionbox = def.collisionbox or {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}, + collisionbox = def.collisionbox, selectionbox = def.selectionbox or def.collisionbox, visual = def.visual, - visual_size = def.visual_size or {x = 1, y = 1}, + visual_size = def.visual_size, mesh = def.mesh, - makes_footstep_sound = def.makes_footstep_sound or false, - view_range = def.view_range or 5, - walk_velocity = def.walk_velocity or 1, - run_velocity = def.run_velocity or 2, + makes_footstep_sound = def.makes_footstep_sound, + view_range = def.view_range, + walk_velocity = def.walk_velocity, + run_velocity = def.run_velocity, damage = max(0, (def.damage or 0) * difficulty), - light_damage = def.light_damage or 0, - light_damage_min = def.light_damage_min or 14, - light_damage_max = def.light_damage_max or 15, - water_damage = def.water_damage or 0, - lava_damage = def.lava_damage or 0, - suffocation = def.suffocation or 2, - fall_damage = def.fall_damage or 1, - fall_speed = def.fall_speed or -10, -- must be lower than -2 (default: -10) - drops = def.drops or {}, - armor = def.armor or 100, + light_damage = def.light_damage, + light_damage_min = def.light_damage_min, + light_damage_max = def.light_damage_max, + water_damage = def.water_damage, + lava_damage = def.lava_damage, + suffocation = def.suffocation, + fall_damage = def.fall_damage, + fall_speed = def.fall_speed, + drops = def.drops, + armor = def.armor, on_rightclick = def.on_rightclick, arrow = def.arrow, shoot_interval = def.shoot_interval, - sounds = def.sounds or {}, + sounds = def.sounds, animation = def.animation, follow = def.follow, - jump = def.jump ~= false, - walk_chance = def.walk_chance or 50, - attack_chance = def.attack_chance or 5, - passive = def.passive or false, - knock_back = def.knock_back ~= false, - blood_amount = def.blood_amount or 5, - blood_texture = def.blood_texture or "mobs_blood.png", - shoot_offset = def.shoot_offset or 0, - floats = def.floats or 1, -- floats in water by default + jump = def.jump, + walk_chance = def.walk_chance, + attack_chance = def.attack_chance, + passive = def.passive, + knock_back = def.knock_back, + blood_amount = def.blood_amount, + blood_texture = def.blood_texture, + shoot_offset = def.shoot_offset, + floats = def.floats, replace_rate = def.replace_rate, replace_what = def.replace_what, replace_with = def.replace_with, - replace_offset = def.replace_offset or 0, + replace_offset = def.replace_offset, on_replace = def.on_replace, - timer = 0, - env_damage_timer = 0, -- only used when state = "attack" - tamed = false, - pause_timer = 0, - horny = false, - hornytimer = 0, - child = false, - gotten = false, - health = 0, - reach = def.reach or 3, - htimer = 0, + reach = def.reach, texture_list = def.textures, child_texture = def.child_texture, - docile_by_day = def.docile_by_day or false, - time_of_day = 0.5, - fear_height = def.fear_height or 0, + docile_by_day = def.docile_by_day, + fear_height = def.fear_height, runaway = def.runaway, - runaway_timer = 0, pathfinding = def.pathfinding, - immune_to = def.immune_to or {}, + immune_to = def.immune_to, explosion_radius = def.explosion_radius, explosion_damage_radius = def.explosion_damage_radius, - explosion_timer = def.explosion_timer or 3, - allow_fuse_reset = def.allow_fuse_reset ~= false, - stop_to_explode = def.stop_to_explode ~= false, + explosion_timer = def.explosion_timer, + allow_fuse_reset = def.allow_fuse_reset, + stop_to_explode = def.stop_to_explode, custom_attack = def.custom_attack, double_melee_attack = def.double_melee_attack, dogshoot_switch = def.dogshoot_switch, - dogshoot_count = 0, - dogshoot_count_max = def.dogshoot_count_max or 5, - dogshoot_count2_max = def.dogshoot_count2_max or (def.dogshoot_count_max or 5), - group_attack = def.group_attack or false, - attack_monsters = def.attacks_monsters or def.attack_monsters or false, - attack_animals = def.attack_animals or false, - attack_players = def.attack_players ~= false, - attack_npcs = def.attack_npcs ~= false, + dogshoot_count_max = def.dogshoot_count_max, + dogshoot_count2_max = def.dogshoot_count2_max or def.dogshoot_count_max, + group_attack = def.group_attack, + attack_monsters = def.attacks_monsters or def.attack_monsters, + attack_animals = def.attack_animals, + attack_players = def.attack_players, + attack_npcs = def.attack_npcs, specific_attack = def.specific_attack, runaway_from = def.runaway_from, owner_loyal = def.owner_loyal, - facing_fence = false, pushable = def.pushable, - _cmi_is_mob = true, on_spawn = def.on_spawn, - on_blast = def.on_blast or do_tnt, - - on_step = mob_step, + on_blast = def.on_blast, -- class redifinition 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) + return self:mob_activate(staticdata, def, dtime) end, - get_staticdata = function(self) - return mob_staticdata(self) - end, - -}) +}, mob_class_meta)) end -- END mobs:register_mob function @@ -3482,7 +3524,7 @@ function mobs:register_arrow(name, def) if minetest.registered_nodes[node].walkable then - self.hit_node(self, pos, node) + self:hit_node(pos, node) if self.drop == true then @@ -3506,7 +3548,7 @@ function mobs:register_arrow(name, def) if self.hit_player and player:is_player() then - self.hit_player(self, player) + self:hit_player(player) self.object:remove() ; -- print ("hit player") return end @@ -3519,7 +3561,7 @@ function mobs:register_arrow(name, def) and tostring(player) ~= self.owner_id and entity.name ~= self.object:get_luaentity().name then - self.hit_mob(self, player) + self:hit_mob(player) self.object:remove() ; --print ("hit mob") @@ -3695,6 +3737,41 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) end +-- force capture a mob if space available in inventory, or drop as spawn egg +function mobs:force_capture(self, clicker) + + -- add special mob egg with all mob information + local new_stack = ItemStack(self.name .. "_set") + + 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 + + local data_str = minetest.serialize(tmp) + + new_stack:set_metadata(data_str) + + local inv = clicker:get_inventory() + + if inv:room_for_item("main", new_stack) then + inv:add_item("main", new_stack) + else + minetest.add_item(clicker:get_pos(), new_stack) + end + + self.object:remove() + + self:mob_sound("default_place_node_hard") +end + + -- capture critter (thanks to blert2112 for idea) function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, force_take, replacewith) @@ -3768,7 +3845,7 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, end -- calculate chance.. add to inventory if successful? - if chance > 0 and random(1, 100) <= chance then + if chance and chance > 0 and random(1, 100) <= chance then -- default mob egg local new_stack = ItemStack(mobname) @@ -3805,15 +3882,19 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, self.object:remove() - mob_sound(self, "default_place_node_hard") + self:mob_sound("default_place_node_hard") return new_stack - elseif chance ~= 0 then + -- when chance above fails or set to 0, miss! + elseif chance and chance ~= 0 then minetest.chat_send_player(name, S("Missed!")) - mob_sound(self, "mobs_swing") + self:mob_sound("mobs_swing") + return false + -- when chance set to nil always return a miss (used for npc walk/follow) + elseif not chance then return false end end @@ -3854,7 +3935,7 @@ function mobs:protect(self, clicker) effect(self.object:get_pos(), 25, "mobs_protect_particle.png", 0.5, 4, 2, 15) - mob_sound(self, "mobs_spell") + self:mob_sound("mobs_spell") return true end @@ -3871,7 +3952,7 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) end -- can eat/tame with item in hand - if follow_holding(self, clicker) then + if self:follow_holding(clicker) then -- if not in creative then take item if not mobs.is_creative(clicker:get_player_name()) then @@ -3902,7 +3983,7 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) self.object:set_hp(self.health) - update_tag(self) + self:update_tag() -- make children grow quicker if self.child == true then @@ -3940,7 +4021,7 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) end -- make sound when fed so many times - mob_sound(self, self.sounds.random) + self:mob_sound(self.sounds.random) end return true @@ -4003,7 +4084,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) -- update nametag mob_obj[name].nametag = fields.name - update_tag(mob_obj[name]) + mob_obj[name]:update_tag() -- if not in creative then take item if not mobs.is_creative(name) then diff --git a/api.txt b/api.txt index cf8e89b..d6c2a95 100644 --- a/api.txt +++ b/api.txt @@ -455,6 +455,11 @@ replace with another item entirely. 'replacewith' once captured replace mob with this item instead (overrides new mob eggs with saved information) +mobs:force_capture(self, clicker) + +Same as above but does no checks, it simply captures any and all mobs and places +inside a spawn egg containing all of the mob information. + Feeding and Taming/Breeding --------------------------- diff --git a/readme.MD b/readme.MD index 68bdc42..5739aca 100644 --- a/readme.MD +++ b/readme.MD @@ -23,6 +23,7 @@ Lucky Blocks: 9 Changelog: +- 1.49- Added mobs:force_capture(self, player) function, api functions now use metatables thanks to bell07 - 1.48- Add mobs:set_velocity(self, velocity) global function - 1.47- Mob damage changes, min and max light level for damage added, ignition sources checked for lava damage - 1.46- Mobs only drop rare items when killed by player (drops.min = 0 makes them rare), code tweak, pathfinding no longer sees through walkable nodes From 78ef5608529afcd1b4bce8c213e43eff5c3216b1 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 29 Dec 2018 12:19:51 +0000 Subject: [PATCH 003/135] fix nametag bug when self.follow is nil --- api.lua | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/api.lua b/api.lua index a409a99..284c191 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20181220", + version = "20181229", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -537,7 +537,6 @@ function mob_class:update_tag() nametag = self.nametag, nametag_color = col }) - end @@ -974,6 +973,7 @@ local entity_physics = function(pos, radius) obj_pos = objs[n]:get_pos() dist = get_distance(pos, obj_pos) + if dist < 1 then dist = 1 end local damage = floor((4 / dist) * radius) @@ -3111,7 +3111,6 @@ function mob_class:on_step(dtime) self:do_jump() self:do_runaway_from(self) - end @@ -3947,12 +3946,9 @@ local mob_sta = {} -- feeding, taming and breeding (thanks blert2112) function mobs:feed_tame(self, clicker, feed_count, breed, tame) - if not self.follow then - return false - end - -- can eat/tame with item in hand - if self:follow_holding(clicker) then + if self.follow + and self:follow_holding(clicker) then -- if not in creative then take item if not mobs.is_creative(clicker:get_player_name()) then @@ -3995,6 +3991,7 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) -- feed and tame self.food = (self.food or 0) + 1 + if self.food >= feed_count then self.food = 0 @@ -4003,8 +4000,6 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) self.horny = true end - self.gotten = false - if tame then if self.tamed == false then From 0015b01834459e3fe2914aadb644e4e21c05ce67 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 7 Jan 2019 16:25:10 +0000 Subject: [PATCH 004/135] after_spawn fix... thanks GreenDiamond --- api.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index 284c191..e8e547b 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20181229", + version = "20190107", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -2921,7 +2921,7 @@ function mob_class:mob_activate(staticdata, def, dtime) -- run after_activate if def.after_activate then - def:after_activate(staticdata, def, dtime) + def:after_activate(self, staticdata, def, dtime) end if use_cmi then @@ -3294,7 +3294,8 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, end minetest.log("action", - string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc)) + string.format("[mobs] Chance setting for %s changed to %s (total: %s)", + name, chance, aoc)) end From 980a47bdab7cbe0999627c9481f7be7a0b702c29 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 7 Jan 2019 16:29:04 +0000 Subject: [PATCH 005/135] fix : to . --- api.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.lua b/api.lua index e8e547b..4ba5166 100644 --- a/api.lua +++ b/api.lua @@ -2921,7 +2921,7 @@ function mob_class:mob_activate(staticdata, def, dtime) -- run after_activate if def.after_activate then - def:after_activate(self, staticdata, def, dtime) + def.after_activate(self, staticdata, def, dtime) end if use_cmi then From d3f5ead4fa9c7125ad7a89203f0a28e4a449e6a7 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 17 Jan 2019 09:29:39 +0000 Subject: [PATCH 006/135] add new flight check function by taikedz --- api.lua | 43 +- api.lua_testspawn | 3985 --------------------------------------------- 2 files changed, 39 insertions(+), 3989 deletions(-) delete mode 100644 api.lua_testspawn diff --git a/api.lua b/api.lua index 4ba5166..447296b 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190107", + version = "20190117", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -453,8 +453,41 @@ function mobs:line_of_sight(entity, pos1, pos2, stepsize) end +function mob_class:attempt_flight_correction() + + if self:flight_check() then return true end + + -- We are not flying in what we are supposed to. + -- See if we can find intended flight medium and return to it + local pos = self.object:get_pos() + local searchnodes = self.fly_in + + if type(searchnodes) == "string" then + searchnodes = {self.fly_in} + end + + local r = 1 + local flyable_nodes = minetest.find_nodes_in_area( + {x = pos.x - r, y = pos.y - r, z = pos.z - r}, + {x = pos.x + r, y = pos.y + r, z = pos.z + r}, + searchnodes) + + if #flyable_nodes < 1 then + return false + end + + local escape_target = flyable_nodes[math.random(1,#flyable_nodes)] + local escape_direction = vector.direction(pos, escape_target) + + self.object:set_velocity( + vector.multiply(escape_direction, self.run_velocity)) + + return true +end + + -- are we flying in what we are suppose to? (taikedz) -function mob_class:flight_check(pos_w) +function mob_class:flight_check() local def = minetest.registered_nodes[self.standing_in] @@ -1808,8 +1841,10 @@ function mob_class:follow_flop() -- swimmers flop when out of their element, and swim again when back in if self.fly then + local s = self.object:get_pos() - if not self:flight_check(s) then + + if not self:attempt_flight_correction() then self.state = "flop" self.object:set_velocity({x = 0, y = -5, z = 0}) @@ -2172,7 +2207,7 @@ function mob_class:do_states(dtime) local p_y = floor(p2.y + 1) local v = self.object:get_velocity() - if self:flight_check(s) then + if self:flight_check() then if me_y < p_y then diff --git a/api.lua_testspawn b/api.lua_testspawn deleted file mode 100644 index 471df35..0000000 --- a/api.lua_testspawn +++ /dev/null @@ -1,3985 +0,0 @@ - --- Mobs Api - -mobs = {} -mobs.mod = "redo" -mobs.version = "20180623" - - --- Intllib -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP .. "/intllib.lua") -mobs.intllib = S - - --- CMI support check -local use_cmi = minetest.global_exists("cmi") - - --- Invisibility mod check -mobs.invis = {} -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 -local sin = math.sin -local cos = math.cos -local abs = math.abs -local min = math.min -local max = math.max -local atann = math.atan -local random = math.random -local floor = math.floor -local atan = function(x) - if not x or x ~= x then - --error("atan bassed NaN") - return 0 - else - return atann(x) - end -end - - --- Load settings -local damage_enabled = minetest.settings:get_bool("enable_damage") -local mobs_spawn = minetest.settings:get_bool("mobs_spawn") ~= false -local peaceful_only = minetest.settings:get_bool("only_peaceful_mobs") -local disable_blood = minetest.settings:get_bool("mobs_disable_blood") -local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false -local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false -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") ~= false -local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0 -local show_health = minetest.settings:get_bool("mob_show_health") ~= false -local max_per_block = tonumber(minetest.settings:get("max_objects_per_block") or 99) -local mob_chance_multiplier = tonumber(minetest.settings:get("mob_chance_multiplier") or 1) - --- 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.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 -local enable_pathfinding = true -local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching -local stuck_path_timeout = 10 -- how long will mob follow path before giving up - --- default nodes -local node_fire = "fire:basic_flame" -local node_permanent_flame = "fire:permanent_flame" -local node_ice = "default:ice" -local node_snowblock = "default:snowblock" -local node_snow = "default:snow" -mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "default:dirt" - - --- play sound -local mob_sound = function(self, sound) - - if sound then - minetest.sound_play(sound, { - object = self.object, - gain = 1.0, - max_hear_distance = self.sounds.distance - }) - end -end - - --- attack player/mob -local do_attack = function(self, player) - - if self.state == "attack" then - return - end - - self.attack = player - self.state = "attack" - - if random(0, 100) < 90 then - mob_sound(self, self.sounds.war_cry) - end -end - - --- move mob in facing direction -local set_velocity = function(self, v) - - -- do not move if mob has been ordered to stay - if self.order == "stand" then - self.object:setvelocity({x = 0, y = 0, z = 0}) - return - end - - local yaw = (self.object:get_yaw() or 0) + self.rotate - - self.object:setvelocity({ - x = sin(yaw) * -v, - y = self.object:getvelocity().y, - z = cos(yaw) * v - }) -end - - --- calculate mob velocity -local get_velocity = function(self) - - local v = self.object:getvelocity() - - return (v.x * v.x + v.z * v.z) ^ 0.5 -end - - --- set and return valid yaw -local set_yaw = function(self, yaw, delay) - - if not yaw or yaw ~= yaw then - yaw = 0 - end - - delay = delay or 0 - - if delay == 0 then - self.object:set_yaw(yaw) - return yaw - end - - self.target_yaw = yaw - self.delay = delay - - return self.target_yaw -end - --- global function to set mob yaw -function mobs:yaw(self, yaw, delay) - set_yaw(self, yaw, delay) -end - - --- set defined animation -local set_animation = function(self, anim) - - if not self.animation - or not anim then return end - - self.animation.current = self.animation.current or "" - - if anim == self.animation.current - or not self.animation[anim .. "_start"] - or not self.animation[anim .. "_end"] then - return - end - - self.animation.current = anim - - self.object:set_animation({ - x = self.animation[anim .. "_start"], - y = self.animation[anim .. "_end"]}, - self.animation[anim .. "_speed"] or self.animation.speed_normal or 15, - 0, self.animation[anim .. "_loop"] ~= false) -end - - --- above function exported for mount.lua -function mobs:set_animation(self, anim) - set_animation(self, anim) -end - - --- calculate distance -local get_distance = function(a, b) - - local x, y, z = a.x - b.x, a.y - b.y, a.z - b.z - - return square(x * x + y * y + z * z) -end - - --- check line of sight (BrunoMine) -local line_of_sight = function(self, pos1, pos2, stepsize) - - stepsize = stepsize or 1 - - local s, pos = minetest.line_of_sight(pos1, pos2, stepsize) - - -- normal walking and flying mobs can see you through air - if s == true then - return true - end - - -- New pos1 to be analyzed - local npos1 = {x = pos1.x, y = pos1.y, z = pos1.z} - - local r, pos = minetest.line_of_sight(npos1, pos2, stepsize) - - -- Checks the return - if r == true then return true end - - -- Nodename found - local nn = minetest.get_node(pos).name - - -- Target Distance (td) to travel - local td = get_distance(pos1, pos2) - - -- Actual Distance (ad) traveled - local ad = 0 - - -- It continues to advance in the line of sight in search of a real - -- obstruction which counts as 'normal' nodebox. - while minetest.registered_nodes[nn] - and (minetest.registered_nodes[nn].walkable == false - or minetest.registered_nodes[nn].drawtype == "nodebox") do - - -- Check if you can still move forward - if td < ad + stepsize then - return true -- Reached the target - end - - -- Moves the analyzed pos - local d = get_distance(pos1, pos2) - - npos1.x = ((pos2.x - pos1.x) / d * stepsize) + pos1.x - npos1.y = ((pos2.y - pos1.y) / d * stepsize) + pos1.y - npos1.z = ((pos2.z - pos1.z) / d * stepsize) + pos1.z - - -- NaN checks - if d == 0 - or npos1.x ~= npos1.x - or npos1.y ~= npos1.y - or npos1.z ~= npos1.z then - return false - end - - ad = ad + stepsize - - -- scan again - r, pos = minetest.line_of_sight(npos1, pos2, stepsize) - - if r == true then return true end - - -- New Nodename found - nn = minetest.get_node(pos).name - - end - - return false -end - - --- are we flying in what we are suppose to? (taikedz) -local flight_check = function(self, pos_w) - - local def = minetest.registered_nodes[self.standing_in] - - if not def then return false end -- nil check - - if type(self.fly_in) == "string" - and self.standing_in == self.fly_in then - - return true - - elseif type(self.fly_in) == "table" then - - for _,fly_in in pairs(self.fly_in) do - - if self.standing_in == 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 - - --- custom particle effects -local effect = function(pos, amount, texture, min_size, max_size, radius, gravity, glow) - - radius = radius or 2 - min_size = min_size or 0.5 - max_size = max_size or 1 - gravity = gravity or -10 - glow = glow or 0 - - minetest.add_particlespawner({ - amount = amount, - time = 0.25, - minpos = pos, - maxpos = pos, - minvel = {x = -radius, y = -radius, z = -radius}, - maxvel = {x = radius, y = radius, z = radius}, - minacc = {x = 0, y = gravity, z = 0}, - maxacc = {x = 0, y = gravity, z = 0}, - minexptime = 0.1, - maxexptime = 1, - minsize = min_size, - maxsize = max_size, - texture = texture, - glow = glow, - }) -end - - --- update nametag colour -local update_tag = function(self) - - local col = "#00FF00" - local qua = self.hp_max / 4 - - if self.health <= floor(qua * 3) then - col = "#FFFF00" - end - - if self.health <= floor(qua * 2) then - col = "#FF6600" - end - - if self.health <= floor(qua) then - col = "#FF0000" - end - - self.object:set_properties({ - nametag = self.nametag, - nametag_color = col - }) - -end - - --- drop items -local item_drop = function(self, cooked) - - -- no drops if disabled by setting - if not mobs_drop_items then return end - - -- no drops for child mobs - if self.child then return end - - local obj, item, num - local pos = self.object:get_pos() - - self.drops = self.drops or {} -- nil check - - for n = 1, #self.drops do - - if random(1, self.drops[n].chance) == 1 then - - num = random(self.drops[n].min or 1, self.drops[n].max or 1) - item = self.drops[n].name - - -- cook items when true - if cooked then - - local output = minetest.get_craft_result({ - method = "cooking", width = 1, items = {item}}) - - if output and output.item and not output.item:is_empty() then - item = output.item:get_name() - end - end - - -- add item if it exists - obj = minetest.add_item(pos, ItemStack(item .. " " .. num)) - - if obj and obj:get_luaentity() then - - obj:setvelocity({ - x = random(-10, 10) / 9, - y = 6, - z = random(-10, 10) / 9, - }) - elseif obj then - obj:remove() -- item does not exist - end - end - end - - self.drops = {} -end - - --- check if mob is dead or only hurt -local check_for_death = function(self, cause, cmi_cause) - - -- has health actually changed? - if self.health == self.old_health and self.health > 0 then - return - end - - self.old_health = self.health - - -- still got some health? play hurt sound - if self.health > 0 then - - mob_sound(self, self.sounds.damage) - - -- make sure health isn't higher than max - if self.health > self.hp_max then - self.health = self.hp_max - end - - -- backup nametag so we can show health stats - if not self.nametag2 then - self.nametag2 = self.nametag or "" - end - - if show_health - and (cmi_cause and cmi_cause.type == "punch") then - - self.htimer = 2 - self.nametag = "♥ " .. self.health .. " / " .. self.hp_max - - update_tag(self) - end - - return false - end - - -- dropped cooked item if mob died in lava - if cause == "lava" then - item_drop(self, true) - else - item_drop(self, nil) - end - - mob_sound(self, self.sounds.death) - - local pos = self.object:get_pos() - - -- execute custom death function - if self.on_die then - - self.on_die(self, pos) - - if use_cmi then - cmi.notify_die(self.object, cmi_cause) - end - - self.object:remove() - - return true - end - - -- default death function and die animation (if defined) - if self.animation - and self.animation.die_start - and self.animation.die_end then - - local frames = self.animation.die_end - self.animation.die_start - local speed = self.animation.die_speed or 15 - local length = max(frames / speed, 0) - - self.attack = nil - self.v_start = false - self.timer = 0 - self.blinktimer = 0 - self.passive = true - self.state = "die" - set_velocity(self, 0) - set_animation(self, "die") - - minetest.after(length, function(self) - - if use_cmi and self.object:get_luaentity() then - cmi.notify_die(self.object, cmi_cause) - end - - self.object:remove() - end, self) - else - - if use_cmi then - cmi.notify_die(self.object, cmi_cause) - end - - self.object:remove() - end - - effect(pos, 20, "tnt_smoke.png") - - return true -end - - --- check if within physical map limits (-30911 to 30927) -local within_limits = function(pos, radius) - - if (pos.x - radius) > -30913 - and (pos.x + radius) < 30928 - and (pos.y - radius) > -30913 - and (pos.y + radius) < 30928 - and (pos.z - radius) > -30913 - and (pos.z + radius) < 30928 then - return true -- within limits - end - - return false -- beyond limits -end - - --- is mob facing a cliff -local is_at_cliff = function(self) - - if self.fear_height == 0 then -- 0 for no falling protection! - return false - end - - 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:get_pos() - local ypos = pos.y + self.collisionbox[2] -- just above floor - - if minetest.line_of_sight( - {x = pos.x + dir_x, y = ypos, z = pos.z + dir_z}, - {x = pos.x + dir_x, y = ypos - self.fear_height, z = pos.z + dir_z} - , 1) then - - return true - end - - return false -end - - --- get node but use fallback for nil or unknown -local node_ok = function(pos, fallback) - - fallback = fallback or mobs.fallback_node - - local node = minetest.get_node_or_nil(pos) - - if node and minetest.registered_nodes[node.name] then - return node - end - - return minetest.registered_nodes[fallback] -end - - --- environmental damage (water, lava, fire, light etc.) -local do_env_damage = function(self) - - -- feed/tame text timer (so mob 'full' messages dont spam chat) - if self.htimer > 0 then - self.htimer = self.htimer - 1 - end - - -- reset nametag after showing health stats - if self.htimer < 1 and self.nametag2 then - - self.nametag = self.nametag2 - self.nametag2 = nil - - update_tag(self) - end - - local pos = self.object:get_pos() - - self.time_of_day = minetest.get_timeofday() - - -- remove mob if beyond map limits - if not within_limits(pos, 0) then - self.object:remove() - return - end - - -- 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 (minetest.get_node_light(pos) or 0) > 12 then - - self.health = self.health - self.light_damage - - effect(pos, 5, "tnt_smoke.png") - - if check_for_death(self, "light", {type = "light"}) then return end - end ---[[ - local y_level = self.collisionbox[2] - - if self.child then - y_level = self.collisionbox[2] * 0.5 - end - - -- what is mob standing in? - pos.y = pos.y + y_level + 0.25 -- foot level - self.standing_in = node_ok(pos, "air").name --- print ("standing in " .. self.standing_in) -]] - -- don't fall when on ignore, just stand still - if self.standing_in == "ignore" then - self.object:setvelocity({x = 0, y = 0, z = 0}) - end - - local nodef = minetest.registered_nodes[self.standing_in] - - pos.y = pos.y + 1 -- for particle effect position - - -- water - if self.water_damage - and nodef.groups.water then - - if self.water_damage ~= 0 then - - self.health = self.health - self.water_damage - - effect(pos, 5, "bubble.png", nil, nil, 1, nil) - - if check_for_death(self, "water", {type = "environment", - pos = pos, node = self.standing_in}) then return end - end - - -- lava or fire - elseif self.lava_damage - and (nodef.groups.lava - or self.standing_in == node_fire - or self.standing_in == node_permanent_flame) then - - if self.lava_damage ~= 0 then - - self.health = self.health - self.lava_damage - - effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil) - - if check_for_death(self, "lava", {type = "environment", - pos = pos, node = self.standing_in}) then return end - end - - -- damage_per_second node check - elseif nodef.damage_per_second ~= 0 then - - self.health = self.health - nodef.damage_per_second - - effect(pos, 5, "tnt_smoke.png") - - 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 - and nodef.groups.disable_suffocation ~= 1 - and nodef.drawtype == "normal" then - - self.health = self.health - self.suffocation - - 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 - - --- jump if facing a solid node (not fences or gates) -local do_jump = function(self) - - if not self.jump - or self.jump_height == 0 - or self.fly - or self.child - or self.order == "stand" then - return false - end - - self.facing_fence = false - - -- something stopping us while moving? - if self.state ~= "stand" - and get_velocity(self) > 0.5 - and self.object:getvelocity().y ~= 0 then - return false - end - - 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 - - local nod = node_ok(pos) - ---print ("standing on:", nod.name, pos.y) - - if minetest.registered_nodes[nod.name].walkable == false then - return false - end - - -- where is front - local dir_x = -sin(yaw) * (self.collisionbox[4] + 0.5) - local dir_z = cos(yaw) * (self.collisionbox[4] + 0.5) - - -- what is in front of mob? - local nod = node_ok({ - x = pos.x + dir_x, - y = pos.y + 0.5, - z = pos.z + dir_z - }) - - -- thin blocks that do not need to be jumped - if nod.name == node_snow then - return false - end - ---print ("in front:", nod.name, pos.y + 0.5) - - if self.walk_chance == 0 - or minetest.registered_items[nod.name].walkable then - - if not nod.name:find("fence") - and not nod.name:find("gate") then - - local v = self.object:getvelocity() - - v.y = self.jump_height - - set_animation(self, "jump") -- only when defined - - self.object:setvelocity(v) - - -- when in air move forward - minetest.after(0.3, function(self, v) - - if self.object:get_luaentity() then - - self.object:set_acceleration({ - x = v.x * 2,--1.5, - y = 0, - z = v.z * 2,--1.5 - }) - end - end, self, v) - - if get_velocity(self) > 0 then - mob_sound(self, self.sounds.jump) - end - else - self.facing_fence = true - end - - return true - end - - return false -end - - --- blast damage to entities nearby (modified from TNT mod) -local entity_physics = function(pos, radius) - - radius = radius * 2 - - local objs = minetest.get_objects_inside_radius(pos, radius) - local obj_pos, dist - - for n = 1, #objs do - - obj_pos = objs[n]:get_pos() - - dist = get_distance(pos, obj_pos) - if dist < 1 then dist = 1 end - - local damage = floor((4 / dist) * radius) - local ent = objs[n]:get_luaentity() - - -- punches work on entities AND players - objs[n]:punch(objs[n], 1.0, { - full_punch_interval = 1.0, - damage_groups = {fleshy = damage}, - }, pos) - end -end - - --- should mob follow what I'm holding ? -local follow_holding = function(self, clicker) - - if mobs.invis[clicker:get_player_name()] then - return false - end - - local item = clicker:get_wielded_item() - local t = type(self.follow) - - -- single item - if t == "string" - and item:get_name() == self.follow then - return true - - -- multiple items - elseif t == "table" then - - for no = 1, #self.follow do - - if self.follow[no] == item:get_name() then - return true - end - end - end - - return false -end - - --- find two animals of same type and breed if nearby and horny -local breed = function(self) - - -- child takes 240 seconds before growing into adult - if self.child == true then - - self.hornytimer = self.hornytimer + 1 - - if self.hornytimer > 240 then - - self.child = false - self.hornytimer = 0 - - self.object:set_properties({ - textures = self.base_texture, - mesh = self.base_mesh, - visual_size = self.base_size, - collisionbox = self.base_colbox, - selectionbox = self.base_selbox, - }) - - -- 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 - end - - -- horny animal can mate for 40 seconds, - -- afterwards horny animal cannot mate again for 200 seconds - if self.horny == true - and self.hornytimer < 240 then - - self.hornytimer = self.hornytimer + 1 - - if self.hornytimer >= 240 then - self.hornytimer = 0 - self.horny = false - end - end - - -- 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:get_pos() - - effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, "heart.png", 3, 4, 1, 0.1) - - local objs = minetest.get_objects_inside_radius(pos, 3) - local num = 0 - local ent = nil - - for n = 1, #objs do - - ent = objs[n]:get_luaentity() - - -- check for same animal with different colour - local canmate = false - - if ent then - - if ent.name == self.name then - canmate = true - else - local entname = string.split(ent.name,":") - local selfname = string.split(self.name,":") - - if entname[1] == selfname[1] then - entname = string.split(entname[2],"_") - selfname = string.split(selfname[2],"_") - - if entname[1] == selfname[1] then - canmate = true - end - end - end - end - - if ent - and canmate == true - and ent.horny == true - and ent.hornytimer <= 40 then - num = num + 1 - end - - -- found your mate? then have a baby - if num > 1 then - - self.hornytimer = 41 - ent.hornytimer = 41 - - -- spawn baby - minetest.after(5, function(self, ent) - - if not self.object:get_luaentity() then - return - end - - -- 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 = { - x = self.base_size.x * .5, - y = self.base_size.y * .5, - }, - collisionbox = { - self.base_colbox[1] * .5, - self.base_colbox[2] * .5, - self.base_colbox[3] * .5, - self.base_colbox[4] * .5, - self.base_colbox[5] * .5, - self.base_colbox[6] * .5, - }, - selectionbox = { - self.base_selbox[1] * .5, - self.base_selbox[2] * .5, - self.base_selbox[3] * .5, - self.base_selbox[4] * .5, - self.base_selbox[5] * .5, - self.base_selbox[6] * .5, - }, - }) - -- tamed and owned by parents' owner - ent2.child = true - ent2.tamed = true - ent2.owner = self.owner - end, self, ent) - - num = 0 - - break - end - end - end -end - - --- find and replace what mob is looking for (grass, wheat etc.) -local replace = function(self, pos) - - if not mobs_griefing - or not self.replace_rate - or not self.replace_what - or self.child == true - or self.object:getvelocity().y ~= 0 - or random(1, self.replace_rate) > 1 then - return - end - - local what, with, y_offset - - if type(self.replace_what[1]) == "table" then - - local num = random(#self.replace_what) - - what = self.replace_what[num][1] or "" - with = self.replace_what[num][2] or "" - y_offset = self.replace_what[num][3] or 0 - else - what = self.replace_what - with = self.replace_with or "" - y_offset = self.replace_offset or 0 - end - - pos.y = pos.y + y_offset - - if #minetest.find_nodes_in_area(pos, pos, what) > 0 then - --- print ("replace node = ".. minetest.get_node(pos).name, pos.y) - - local oldnode = {name = what} - local newnode = {name = with} - local on_replace_return - - if self.on_replace then - on_replace_return = self.on_replace(self, pos, oldnode, newnode) - end - - if on_replace_return ~= false then - - minetest.set_node(pos, {name = 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 - - --- check if daytime and also if mob is docile during daylight hours -local day_docile = function(self) - - if self.docile_by_day == false then - - return false - - elseif self.docile_by_day == true - and self.time_of_day > 0.2 - and self.time_of_day < 0.8 then - - return true - end -end - - -local los_switcher = false -local height_switcher = false - --- path finding and smart mob routine by rnd, line_of_sight and other edits by Elkien3 -local smart_mobs = function(self, s, p, dist, dtime) - - local s1 = self.path.lastpos - - local target_pos = self.attack:get_pos() - - -- is it becoming stuck? - if abs(s1.x - s.x) + abs(s1.z - s.z) < .5 then - self.path.stuck_timer = self.path.stuck_timer + dtime - else - self.path.stuck_timer = 0 - end - - self.path.lastpos = {x = s.x, y = s.y, z = s.z} - - local use_pathfind = false - local has_lineofsight = minetest.line_of_sight( - {x = s.x, y = (s.y) + .5, z = s.z}, - {x = target_pos.x, y = (target_pos.y) + 1.5, z = target_pos.z}, .2) - - -- im stuck, search for path - if not has_lineofsight then - - if los_switcher == true then - use_pathfind = true - los_switcher = false - end -- cannot see target! - else - if los_switcher == false then - - los_switcher = true - use_pathfind = false - - minetest.after(1, function(self) - - if self.object:get_luaentity() then - - if has_lineofsight then - self.path.following = false - end - end - end, self) - end -- can see target! - end - - if (self.path.stuck_timer > stuck_timeout and not self.path.following) then - - use_pathfind = true - self.path.stuck_timer = 0 - - minetest.after(1, function(self) - - if self.object:get_luaentity() then - - if has_lineofsight then - self.path.following = false - end - end - end, self) - end - - if (self.path.stuck_timer > stuck_path_timeout and self.path.following) then - - use_pathfind = true - self.path.stuck_timer = 0 - - minetest.after(1, function(self) - - if self.object:get_luaentity() then - - if has_lineofsight then - self.path.following = false - end - end - end, self) - end - - if math.abs(vector.subtract(s,target_pos).y) > self.stepheight then - - if height_switcher then - use_pathfind = true - height_switcher = false - end - else - if not height_switcher then - use_pathfind = false - height_switcher = true - end - end - - if use_pathfind then - -- lets try find a path, first take care of positions - -- since pathfinder is very sensitive - local sheight = self.collisionbox[5] - self.collisionbox[2] - - -- round position to center of node to avoid stuck in walls - -- also adjust height for player models! - s.x = floor(s.x + 0.5) --- s.y = floor(s.y + 0.5) - sheight - s.z = floor(s.z + 0.5) - - local ssight, sground = minetest.line_of_sight(s, { - x = s.x, y = s.y - 4, z = s.z}, 1) - - -- determine node above ground - if not ssight then - s.y = sground.y + 1 - end - - local p1 = self.attack:get_pos() - - p1.x = floor(p1.x + 0.5) - p1.y = floor(p1.y + 0.5) - p1.z = floor(p1.z + 0.5) - - local dropheight = 6 - if self.fear_height ~= 0 then dropheight = self.fear_height end - - self.path.way = minetest.find_path(s, p1, 16, self.stepheight, dropheight, "Dijkstra") ---[[ - -- show path using particles - if self.path.way and #self.path.way > 0 then - print ("-- path length:" .. tonumber(#self.path.way)) - for _,pos in pairs(self.path.way) do - minetest.add_particle({ - pos = pos, - velocity = {x=0, y=0, z=0}, - acceleration = {x=0, y=0, z=0}, - expirationtime = 1, - size = 4, - collisiondetection = false, - vertical = false, - texture = "heart.png", - }) - end - end -]] - - self.state = "" - do_attack(self, self.attack) - - -- no path found, try something else - if not self.path.way then - - self.path.following = false - - -- lets make way by digging/building if not accessible - if self.pathfinding == 2 and mobs_griefing then - - -- is player higher than mob? - if s.y < p1.y then - - -- build upwards - if not minetest.is_protected(s, "") then - - local ndef1 = minetest.registered_nodes[self.standing_in] - - if ndef1 and (ndef1.buildable_to or ndef1.groups.liquid) then - - minetest.set_node(s, {name = mobs.fallback_node}) - end - end - - local sheight = math.ceil(self.collisionbox[5]) + 1 - - -- assume mob is 2 blocks high so it digs above its head - s.y = s.y + sheight - - -- remove one block above to make room to jump - if not minetest.is_protected(s, "") then - - local node1 = node_ok(s, "air").name - local ndef1 = minetest.registered_nodes[node1] - - if node1 ~= "air" - and node1 ~= "ignore" - and ndef1 - and not ndef1.groups.level - and not ndef1.groups.unbreakable - and not ndef1.groups.liquid then - - minetest.set_node(s, {name = "air"}) - minetest.add_item(s, ItemStack(node1)) - - end - end - - s.y = s.y - sheight - self.object:setpos({x = s.x, y = s.y + 2, z = s.z}) - - else -- dig 2 blocks to make door toward player direction - - local yaw1 = self.object:get_yaw() + pi / 2 - local p1 = { - x = s.x + cos(yaw1), - y = s.y, - z = s.z + sin(yaw1) - } - - if not minetest.is_protected(p1, "") then - - local node1 = node_ok(p1, "air").name - local ndef1 = minetest.registered_nodes[node1] - - if node1 ~= "air" - and node1 ~= "ignore" - and ndef1 - and not ndef1.groups.level - and not ndef1.groups.unbreakable - and not ndef1.groups.liquid then - - minetest.add_item(p1, ItemStack(node1)) - minetest.set_node(p1, {name = "air"}) - end - - p1.y = p1.y + 1 - node1 = node_ok(p1, "air").name - ndef1 = minetest.registered_nodes[node1] - - if node1 ~= "air" - and node1 ~= "ignore" - and ndef1 - and not ndef1.groups.level - and not ndef1.groups.unbreakable - and not ndef1.groups.liquid then - - minetest.add_item(p1, ItemStack(node1)) - minetest.set_node(p1, {name = "air"}) - end - - end - end - end - - -- will try again in 2 second - self.path.stuck_timer = stuck_timeout - 2 - - -- frustration! cant find the damn path :( - mob_sound(self, self.sounds.random) - else - -- yay i found path - mob_sound(self, self.sounds.war_cry) - set_velocity(self, self.walk_velocity) - - -- follow path now that it has it - self.path.following = true - end - end -end - - --- specific attacks -local specific_attack = function(list, what) - - -- no list so attack default (player, animals etc.) - if list == nil then - return true - end - - -- found entity on list to attack? - for no = 1, #list do - - if list[no] == what then - return true - end - end - - return false -end - - --- monster find someone to attack -local monster_attack = function(self) - - if self.type ~= "monster" - or not damage_enabled - or creative - or self.state == "attack" - or day_docile(self) then - return - end - - local s = self.object:get_pos() - local p, sp, dist - local player, obj, min_player - local type, name = "", "" - local min_dist = self.view_range + 1 - local objs = minetest.get_objects_inside_radius(s, self.view_range) - - for n = 1, #objs do - - if objs[n]:is_player() then - - if mobs.invis[ objs[n]:get_player_name() ] then - - type = "" - else - player = objs[n] - type = "player" - name = "player" - end - else - obj = objs[n]:get_luaentity() - - if obj then - player = obj.object - type = obj.type - name = obj.name or "" - end - end - - -- find specific mob to attack, failing that attack player/npc/animal - if specific_attack(self.specific_attack, name) - and (type == "player" or type == "npc" - or (type == "animal" and self.attack_animals == true)) then - - p = player:get_pos() - sp = s - - dist = get_distance(p, s) - - -- aim higher to make looking up hills more realistic - p.y = p.y + 1 - sp.y = sp.y + 1 - - - -- choose closest player to attack - if dist < min_dist - and line_of_sight(self, sp, p, 2) == true then - min_dist = dist - min_player = player - end - end - end - - -- attack player - if min_player then - do_attack(self, min_player) - end -end - - --- npc, find closest monster to attack -local npc_attack = function(self) - - if self.type ~= "npc" - or not self.attacks_monsters - or self.state == "attack" then - return - end - - local p, sp, obj, min_player, dist - local s = self.object:get_pos() - local min_dist = self.view_range + 1 - local objs = minetest.get_objects_inside_radius(s, self.view_range) - - for n = 1, #objs do - - obj = objs[n]:get_luaentity() - - if obj and obj.type == "monster" then - - p = obj.object:get_pos() - sp = s - - dist = get_distance(p, s) - - -- aim higher to make looking up hills more realistic - p.y = p.y + 1 - sp.y = sp.y + 1 - - if dist < min_dist - and line_of_sight(self, sp, p, 2) == true then - min_dist = dist - min_player = obj.object - end - end - end - - if min_player then - do_attack(self, min_player) - end -end - - --- specific runaway -local specific_runaway = function(list, what) - - -- no list so do not run - if list == nil then - return false - end - - -- found entity on list to attack? - for no = 1, #list do - - if list[no] == what then - return true - end - end - - return false -end - - --- find someone to runaway from -local runaway_from = function(self) - - if not self.runaway_from then - return - end - - local s = self.object:get_pos() - local p, sp, dist - local player, obj, min_player - local type, name = "", "" - local min_dist = self.view_range + 1 - local objs = minetest.get_objects_inside_radius(s, self.view_range) - - for n = 1, #objs do - - if objs[n]:is_player() then - - if mobs.invis[ objs[n]:get_player_name() ] - or self.owner == objs[n]:get_player_name() then - - type = "" - else - player = objs[n] - type = "player" - name = "player" - end - else - obj = objs[n]:get_luaentity() - - if obj then - player = obj.object - type = obj.type - name = obj.name or "" - end - end - - -- find specific mob to runaway from - if name ~= "" and name ~= self.name - and specific_runaway(self.runaway_from, name) then - - p = player:get_pos() - sp = s - - -- aim higher to make looking up hills more realistic - p.y = p.y + 1 - sp.y = sp.y + 1 - - dist = get_distance(p, s) - - - -- choose closest player/mpb to runaway from - if dist < min_dist - and line_of_sight(self, sp, p, 2) == true then - min_dist = dist - min_player = player - end - end - end - - if min_player then - - local lp = player:get_pos() - local vec = { - x = lp.x - s.x, - y = lp.y - s.y, - z = lp.z - s.z - } - - local yaw = (atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate - - if lp.x > s.x then - yaw = yaw + pi - end - - yaw = set_yaw(self, yaw, 4) - self.state = "runaway" - self.runaway_timer = 3 - self.following = nil - end -end - - --- follow player if owner or holding item, if fish outta water then flop -local follow_flop = function(self) - - -- find player to follow - if (self.follow ~= "" - or self.order == "follow") - and not self.following - and self.state ~= "attack" - and self.state ~= "runaway" then - - local s = self.object:get_pos() - local players = minetest.get_connected_players() - - for n = 1, #players do - - 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] - - break - end - end - end - - if self.type == "npc" - 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 - and self.owner ~= self.following:get_player_name() then - self.following = nil - end - else - -- stop following player if not holding specific item - if self.following - and self.following:is_player() - and follow_holding(self, self.following) == false then - self.following = nil - end - - end - - -- follow that thing - if self.following then - - local s = self.object:get_pos() - local p - - if self.following:is_player() then - - p = self.following:get_pos() - - elseif self.following.object then - - p = self.following.object:get_pos() - end - - if p then - - local dist = get_distance(p, s) - - -- dont follow if out of range - if dist > self.view_range then - self.following = nil - else - local vec = { - x = p.x - s.x, - z = p.z - s.z - } - - local yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if p.x > s.x then yaw = yaw + pi end - - yaw = set_yaw(self, yaw, 6) - - -- anyone but standing npc's can move along - if dist > self.reach - and self.order ~= "stand" then - - set_velocity(self, self.walk_velocity) - - if self.walk_chance ~= 0 then - set_animation(self, "walk") - end - else - set_velocity(self, 0) - set_animation(self, "stand") - end - - return - end - end - end - - -- swimmers flop when out of their element, and swim again when back in - if self.fly then - local s = self.object:get_pos() - if not flight_check(self, s) then - - self.state = "flop" - self.object:setvelocity({x = 0, y = -5, z = 0}) - - set_animation(self, "stand") - - return - elseif self.state == "flop" then - self.state = "stand" - end - end -end - - --- dogshoot attack switch and counter function -local dogswitch = function(self, dtime) - - -- switch mode not activated - if not self.dogshoot_switch - or not dtime then - return 0 - end - - self.dogshoot_count = self.dogshoot_count + dtime - - if (self.dogshoot_switch == 1 - and self.dogshoot_count > self.dogshoot_count_max) - or (self.dogshoot_switch == 2 - and self.dogshoot_count > self.dogshoot_count2_max) then - - self.dogshoot_count = 0 - - if self.dogshoot_switch == 1 then - self.dogshoot_switch = 2 - else - self.dogshoot_switch = 1 - end - end - - return self.dogshoot_switch -end - - --- execute current state (stand, walk, run, attacks) -local do_states = function(self, dtime) - - local yaw = self.object:get_yaw() or 0 - - if self.state == "stand" then - - if random(1, 4) == 1 then - - local lp = nil - 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]:get_pos() - break - end - end - - -- look at any players nearby, otherwise turn randomly - if lp then - - local vec = { - x = lp.x - s.x, - z = lp.z - s.z - } - - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if lp.x > s.x then yaw = yaw + pi end - else - yaw = yaw + random(-0.5, 0.5) - end - - yaw = set_yaw(self, yaw, 8) - end - - set_velocity(self, 0) - set_animation(self, "stand") - - -- npc's ordered to stand stay standing - if self.type ~= "npc" - 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 - - set_velocity(self, self.walk_velocity) - self.state = "walk" - set_animation(self, "walk") - - --[[ fly up/down randomly for flying mobs - if self.fly and random(1, 100) <= self.walk_chance then - - local v = self.object:getvelocity() - local ud = random(-1, 2) / 9 - - self.object:setvelocity({x = v.x, y = ud, z = v.z}) - end--]] - end - end - - elseif self.state == "walk" then - - local s = self.object:get_pos() - local lp = nil - - -- is there something I need to avoid? - if self.water_damage > 0 - and self.lava_damage > 0 then - - lp = minetest.find_node_near(s, 1, {"group:water", "group:lava"}) - - elseif self.water_damage > 0 then - - lp = minetest.find_node_near(s, 1, {"group:water"}) - - elseif self.lava_damage > 0 then - - lp = minetest.find_node_near(s, 1, {"group:lava"}) - end - - if lp then - - -- if mob in water or lava then look for land - if (self.lava_damage - and minetest.registered_nodes[self.standing_in].groups.lava) - or (self.water_damage - and minetest.registered_nodes[self.standing_in].groups.water) then - - lp = minetest.find_node_near(s, 5, {"group:soil", "group:stone", - "group:sand", node_ice, node_snowblock}) - - -- did we find land? - if lp then - - local vec = { - x = lp.x - s.x, - z = lp.z - s.z - } - - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if lp.x > s.x then yaw = yaw + pi end - - -- look towards land and jump/move in that direction - yaw = set_yaw(self, yaw, 6) - do_jump(self) - set_velocity(self, self.walk_velocity) - else - yaw = yaw + random(-0.5, 0.5) - end - - else - - local vec = { - x = lp.x - s.x, - z = lp.z - s.z - } - - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if lp.x > s.x then yaw = yaw + pi end - end - - yaw = set_yaw(self, yaw, 8) - - -- otherwise randomly turn - elseif random(1, 100) <= 30 then - - yaw = yaw + random(-0.5, 0.5) - - yaw = set_yaw(self, yaw, 8) - end - - -- stand for great fall in front - local temp_is_cliff = is_at_cliff(self) - - if self.facing_fence == true - or temp_is_cliff - or random(1, 100) <= 30 then - - set_velocity(self, 0) - self.state = "stand" - set_animation(self, "stand") - else - set_velocity(self, self.walk_velocity) - - if flight_check(self) - and self.animation - and self.animation.fly_start - and self.animation.fly_end then - set_animation(self, "fly") - else - set_animation(self, "walk") - end - end - - -- runaway when punched - elseif self.state == "runaway" then - - self.runaway_timer = self.runaway_timer + 1 - - -- stop after 5 seconds or when at cliff - if self.runaway_timer > 5 - or is_at_cliff(self) then - self.runaway_timer = 0 - set_velocity(self, 0) - self.state = "stand" - set_animation(self, "stand") - else - set_velocity(self, self.run_velocity) - set_animation(self, "walk") - end - - -- attack routines (explode, dogfight, shoot, dogshoot) - elseif self.state == "attack" then - - -- calculate distance from mob and enemy - local s = self.object:get_pos() - local p = self.attack:get_pos() or s - local dist = get_distance(p, s) - - -- stop attacking if player invisible or out of range - if dist > self.view_range - or not self.attack - 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) - self.state = "stand" - set_velocity(self, 0) - set_animation(self, "stand") - self.attack = nil - self.v_start = false - self.timer = 0 - self.blinktimer = 0 - self.path.way = nil - - return - end - - if self.attack_type == "explode" then - - local vec = { - x = p.x - s.x, - z = p.z - s.z - } - - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if p.x > s.x then yaw = yaw + pi end - - yaw = set_yaw(self, yaw) - - local node_break_radius = self.explosion_radius or 1 - local entity_damage_radius = self.explosion_damage_radius - or (node_break_radius * 2) - - -- start timer when in reach and line of sight - if not self.v_start - and dist <= self.reach - and line_of_sight(self, s, p, 2) then - - self.v_start = true - self.timer = 0 - self.blinktimer = 0 - mob_sound(self, self.sounds.fuse) --- print ("=== explosion timer started", self.explosion_timer) - - -- stop timer if out of reach or direct line of sight - elseif self.allow_fuse_reset - and self.v_start - and (dist > self.reach - or not line_of_sight(self, s, p, 2)) then - self.v_start = false - self.timer = 0 - self.blinktimer = 0 - self.blinkstatus = false - self.object:settexturemod("") - end - - -- walk right up to player unless the timer is active - if self.v_start and (self.stop_to_explode or dist < 1.5) then - set_velocity(self, 0) - 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 - - 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 - --- print ("=== explosion timer", self.timer) - - if self.timer > self.explosion_timer then - - local pos = self.object:get_pos() - - -- dont damage anything if area protected or next to water - if minetest.find_node_near(pos, 1, {"group:water"}) - or minetest.is_protected(pos, "") then - - node_break_radius = 1 - end - - self.object:remove() - - if minetest.get_modpath("tnt") and tnt and tnt.boom - and not minetest.is_protected(pos, "") then - - tnt.boom(pos, { - radius = node_break_radius, - damage_radius = entity_damage_radius, - sound = self.sounds.explode, - }) - else - - minetest.sound_play(self.sounds.explode, { - pos = pos, - gain = 1.0, - max_hear_distance = self.sounds.distance or 32 - }) - - entity_physics(pos, entity_damage_radius) - effect(pos, 32, "tnt_smoke.png", nil, nil, node_break_radius, 1, 0) - end - - return - end - end - - elseif self.attack_type == "dogfight" - or (self.attack_type == "dogshoot" and dogswitch(self, dtime) == 2) - or (self.attack_type == "dogshoot" and dist <= self.reach and dogswitch(self) == 0) then - - if self.fly - and dist > self.reach then - - local p1 = s - local me_y = floor(p1.y) - local p2 = p - local p_y = floor(p2.y + 1) - local v = self.object:getvelocity() - - if flight_check(self, s) 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, - z = v.z - }) - 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, - z = v.z - }) - end - end - - end - - -- rnd: new movement direction - if self.path.following - and self.path.way - and self.attack_type ~= "dogshoot" then - - -- no paths longer than 50 - if #self.path.way > 50 - or dist < self.reach then - self.path.following = false - return - end - - local p1 = self.path.way[1] - - if not p1 then - self.path.following = false - return - end - - if abs(p1.x-s.x) + abs(p1.z - s.z) < 0.6 then - -- reached waypoint, remove it from queue - table.remove(self.path.way, 1) - end - - -- set new temporary target - p = {x = p1.x, y = p1.y, z = p1.z} - end - - local vec = { - x = p.x - s.x, - z = p.z - s.z - } - - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if p.x > s.x then yaw = yaw + pi end - - yaw = set_yaw(self, yaw) - - -- move towards enemy if beyond mob reach - if dist > self.reach then - - -- path finding by rnd - if self.pathfinding -- only if mob has pathfinding enabled - and enable_pathfinding then - - smart_mobs(self, s, p, dist, dtime) - end - - if is_at_cliff(self) then - - set_velocity(self, 0) - set_animation(self, "stand") - else - - if self.path.stuck then - set_velocity(self, self.walk_velocity) - 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 - end - - else -- rnd: if inside reach range - - self.path.stuck = false - self.path.stuck_timer = 0 - self.path.following = false -- not stuck anymore - - set_velocity(self, 0) - - if not self.custom_attack then - - if self.timer > 1 then - - self.timer = 0 - - if self.double_melee_attack - and random(1, 2) == 1 then - set_animation(self, "punch2") - else - set_animation(self, "punch") - end - - local p2 = p - local s2 = s - - p2.y = p2.y + .5 - s2.y = s2.y + .5 - - if line_of_sight(self, p2, s2) == true then - - -- play attack sound - mob_sound(self, self.sounds.attack) - - -- punch player (or what player is attached to) - local attached = self.attack:get_attach() - if attached then - self.attack = attached - end - self.attack:punch(self.object, 1.0, { - full_punch_interval = 1.0, - damage_groups = {fleshy = self.damage} - }, nil) - end - end - else -- call custom attack every second - if self.custom_attack - and self.timer > 1 then - - self.timer = 0 - - self.custom_attack(self, p) - end - end - end - - elseif self.attack_type == "shoot" - or (self.attack_type == "dogshoot" and dogswitch(self, dtime) == 1) - or (self.attack_type == "dogshoot" and dist > self.reach and dogswitch(self) == 0) then - - p.y = p.y - .5 - s.y = s.y + .5 - - local dist = get_distance(p, s) - local vec = { - x = p.x - s.x, - y = p.y - s.y, - z = p.z - s.z - } - - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if p.x > s.x then yaw = yaw + pi end - - yaw = set_yaw(self, yaw) - - set_velocity(self, 0) - - if self.shoot_interval - and self.timer > self.shoot_interval - and random(1, 100) <= 60 then - - self.timer = 0 - set_animation(self, "shoot") - - -- play shoot attack sound - mob_sound(self, self.sounds.shoot_attack) - - local p = self.object:get_pos() - - p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2 - - if minetest.registered_entities[self.arrow] 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 - - -- 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 - end - end -end - - --- falling and fall damage -local falling = function(self, pos) - - if self.fly then - return - end - - -- floating in water (or falling) - local v = self.object:getvelocity() - - if v.y > 0 then - - -- apply gravity when moving up - self.object:setacceleration({ - x = 0, - y = -10, - z = 0 - }) - - elseif v.y <= 0 and v.y > self.fall_speed then - - -- fall downwards at set speed - self.object:setacceleration({ - x = 0, - y = self.fall_speed, - z = 0 - }) - else - -- stop accelerating once max fall speed hit - self.object:setacceleration({x = 0, y = 0, z = 0}) - end - - -- in water then float up - if minetest.registered_nodes[self.standing_in].groups.water then - - if self.floats == 1 then - - self.object:setacceleration({ - x = 0, - y = -self.fall_speed / (max(1, v.y) ^ 8), -- 8 was 2 - z = 0 - }) - end - else - - -- fall damage onto solid ground - if self.fall_damage == 1 - and self.object:getvelocity().y == 0 then - - local d = (self.old_y or 0) - self.object:get_pos().y - - if d > 5 then - - self.health = self.health - floor(d - 5) - - effect(pos, 5, "tnt_smoke.png", 1, 2, 2, nil) - - if check_for_death(self, "fall", {type = "fall"}) then - return - end - end - - self.old_y = self.object:get_pos().y - end - end -end - - --- deal damage and effects when mob punched -local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) - - -- custom punch function - if self.do_punch then - - -- when false skip going any further - if self.do_punch(self, hitter, tflp, tool_capabilities, dir) == false then - return - end - end - - -- 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") - return - end - - -- is mob protected? - if self.protected and hitter:is_player() - 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 - - - -- weapon wear - local weapon = hitter:get_wielded_item() - local punch_interval = 1.4 - - -- calculate mob damage - local damage = 0 - local armor = self.object:get_armor_groups() or {} - local tmp - - -- quick error check incase it ends up 0 (serialize.h check test) - if tflp == 0 then - tflp = 0.2 - end - - if use_cmi then - damage = cmi.calculate_damage(self.object, hitter, tflp, tool_capabilities, dir) - else - - for group,_ in pairs( (tool_capabilities.damage_groups or {}) ) do - - tmp = tflp / (tool_capabilities.full_punch_interval or 1.4) - - if tmp < 0 then - tmp = 0.0 - elseif tmp > 1 then - tmp = 1.0 - end - - damage = damage + (tool_capabilities.damage_groups[group] or 0) - * tmp * ((armor[group] or 0) / 100.0) - end - end - - -- check for tool immunity or special damage - for n = 1, #self.immune_to do - - if self.immune_to[n][1] == weapon:get_name() then - - damage = self.immune_to[n][2] or 0 - break - - -- if "all" then no tool does damage unless it's specified in list - elseif self.immune_to[n][1] == "all" then - damage = self.immune_to[n][2] or 0 - end - end - - -- healing - if damage <= -1 then - self.health = self.health - floor(damage) - return - end - --- print ("Mob Damage is", damage) - - if use_cmi then - - local cancel = cmi.notify_punch(self.object, hitter, tflp, tool_capabilities, dir, damage) - - if cancel then return end - end - - -- add weapon 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(floor((punch_interval / 75) * 9000)) - hitter:set_wielded_item(weapon) - end - - -- only play hit sound and show blood effects if damage is 1 or over - if damage >= 1 then - - -- weapon sounds - if weapon:get_definition().sounds ~= nil then - - local s = random(0, #weapon:get_definition().sounds) - - minetest.sound_play(weapon:get_definition().sounds[s], { - object = self.object, --hitter, - max_hear_distance = 8 - }) - else - minetest.sound_play("default_punch", { - object = self.object, --hitter, - max_hear_distance = 5 - }) - end - - -- blood_particles - if self.blood_amount > 0 - and not disable_blood then - - local pos = self.object:get_pos() - - pos.y = pos.y + (-self.collisionbox[2] + self.collisionbox[5]) * .5 - - -- do we have a single blood texture or multiple? - if type(self.blood_texture) == "table" then - - local blood = self.blood_texture[random(1, #self.blood_texture)] - - effect(pos, self.blood_amount, blood, nil, nil, 1, nil) - else - effect(pos, self.blood_amount, self.blood_texture, nil, nil, 1, nil) - end - end - - -- do damage - self.health = self.health - floor(damage) - - -- exit here if dead, special item check - if weapon:get_name() == "mobs:pick_lava" then - if check_for_death(self, "lava", {type = "punch", - puncher = hitter}) then - return - end - else - if check_for_death(self, "hit", {type = "punch", - puncher = hitter}) then - return - end - end - - --[[ add healthy afterglow when hit (can cause hit lag with larger textures) - minetest.after(0.1, function() - - if not self.object:get_luaentity() then return end - - self.object:settexturemod("^[colorize:#c9900070") - - core.after(0.3, function() - self.object:settexturemod("") - end) - end) ]] - - -- knock back effect (only on full punch) - if self.knock_back - and tflp >= punch_interval then - - local v = self.object:getvelocity() - local r = 1.4 - min(punch_interval, 1.4) - local kb = r * 5 - local up = 2 - - -- if already in air then dont go up anymore when hit - if v.y > 0 - or self.fly then - up = 0 - end - - -- 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 = 0.25 - end - end -- END if damage - - -- if skittish then run away - if self.runaway == true then - - local lp = hitter:get_pos() - local s = self.object:get_pos() - local vec = { - x = lp.x - s.x, - y = lp.y - s.y, - z = lp.z - s.z - } - - local yaw = (atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate - - if lp.x > s.x then - yaw = yaw + pi - end - - yaw = set_yaw(self, yaw, 6) - self.state = "runaway" - self.runaway_timer = 0 - self.following = nil - end - - local name = hitter:get_player_name() or "" - - -- attack puncher and call other mobs for help - if self.passive == false - and self.state ~= "flop" - and self.child == false - and hitter:get_player_name() ~= self.owner - and not mobs.invis[ name ] then - - -- attack whoever punched mob - self.state = "" - do_attack(self, hitter) - - -- alert others to the attack - local objs = minetest.get_objects_inside_radius(hitter:get_pos(), self.view_range) - local obj = nil - - for n = 1, #objs do - - obj = objs[n]:get_luaentity() - - if obj then - - -- only alert members of same mob - if obj.group_attack == true - and obj.state ~= "attack" - and obj.owner ~= name - and obj.name == self.name then - do_attack(obj, hitter) - end - - -- have owned mobs attack player threat - if obj.owner == name and obj.owner_loyal then - do_attack(obj, self.object) - end - end - end - end -end - - --- get entity staticdata -local mob_staticdata = function(self) - - -- remove mob when out of range unless tamed - if remove_far - and self.remove_ok - and self.type ~= "npc" - and self.state ~= "attack" - and not self.tamed - and self.lifetimer < 20000 then - - --print ("REMOVED " .. self.name) - - self.object:remove() - - return ""-- nil - end - - self.remove_ok = true - self.attack = nil - 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 - - if use_cmi then - self.serialized_cmi_components = cmi.serialize_components(self._cmi_components) - end - - local tmp = {} - - for _,stat in pairs(self) do - - local t = type(stat) - - if t ~= "function" - and t ~= "nil" - and t ~= "userdata" - and _ ~= "_cmi_components" then - tmp[_] = self[_] - end - end - - --print('===== '..self.name..'\n'.. dump(tmp)..'\n=====\n') - return minetest.serialize(tmp) -end - - --- activate mob and reload settings -local mob_activate = function(self, staticdata, def, dtime) - - -- remove monsters in peaceful mode - if self.type == "monster" - and peaceful_only then - - self.object:remove() - - return - end - - -- load entity variables - local tmp = minetest.deserialize(staticdata) - - if tmp then - for _,stat in pairs(tmp) do - self[_] = stat - end - end - - -- select random texture, set model and size - if not self.base_texture then - - -- compatiblity with old simple mobs textures - if type(def.textures[1]) == "string" then - def.textures = {def.textures} - end - - self.base_texture = def.textures[random(1, #def.textures)] - self.base_mesh = def.mesh - self.base_size = self.visual_size - self.base_colbox = self.collisionbox - self.base_selbox = self.selectionbox - end - - -- for current mobs that dont have this set - if not self.base_selbox then - self.base_selbox = self.selectionbox or self.base_colbox - end - - -- set texture, model and size - local textures = self.base_texture - local mesh = self.base_mesh - local vis_size = self.base_size - local colbox = self.base_colbox - local selbox = self.base_selbox - - -- specific texture if gotten - if self.gotten == true - and def.gotten_texture then - textures = def.gotten_texture - end - - -- specific mesh if gotten - if self.gotten == true - and def.gotten_mesh then - mesh = def.gotten_mesh - end - - -- set child objects to half size - if self.child == true then - - vis_size = { - x = self.base_size.x * .5, - y = self.base_size.y * .5, - } - - if def.child_texture then - textures = def.child_texture[1] - end - - colbox = { - self.base_colbox[1] * .5, - self.base_colbox[2] * .5, - self.base_colbox[3] * .5, - self.base_colbox[4] * .5, - self.base_colbox[5] * .5, - self.base_colbox[6] * .5 - } - selbox = { - self.base_selbox[1] * .5, - self.base_selbox[2] * .5, - self.base_selbox[3] * .5, - self.base_selbox[4] * .5, - self.base_selbox[5] * .5, - self.base_selbox[6] * .5 - } - end - - if self.health == 0 then - self.health = random (self.hp_min, self.hp_max) - end - - -- 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 - - -- mob defaults - self.object:set_armor_groups({immortal = 1, fleshy = self.armor}) - self.old_y = self.object:get_pos().y - self.old_health = self.health - self.sounds.distance = self.sounds.distance or 10 - self.textures = textures - self.mesh = mesh - self.collisionbox = colbox - self.selectionbox = selbox - self.visual_size = vis_size - self.standing_in = "air" - - -- 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, (random(0, 360) - 180) / 180 * pi, 6) - 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) - end -end - - --- main mob function -local mob_step = function(self, dtime) - - if use_cmi then - cmi.notify_step(self.object, dtime) - end - - local pos = self.object:get_pos() - local yaw = 0 - - -- when lifetimer expires remove mob (except npc and tamed) - if self.type ~= "npc" - and not self.tamed - and self.state ~= "attack" - and remove_far ~= true - and self.lifetimer < 20000 then - - self.lifetimer = self.lifetimer - dtime - - if self.lifetimer <= 0 then - - -- only despawn away from player - local objs = minetest.get_objects_inside_radius(pos, 15) - - for n = 1, #objs do - - if objs[n]:is_player() then - - self.lifetimer = 20 - - return - end - end - --- minetest.log("action", --- S("lifetimer expired, removed @1", self.name)) - - effect(pos, 15, "tnt_smoke.png", 2, 4, 2, 0) - - self.object:remove() - - return - end - end - - -- get node at foot level every quarter second - self.node_timer = (self.node_timer or 0) + dtime - - if self.node_timer > 0.25 then - - self.node_timer = 0 - - local y_level = self.collisionbox[2] - - if self.child then - y_level = self.collisionbox[2] * 0.5 - end - - -- what is mob standing in? - self.standing_in = node_ok({ - x = pos.x, y = pos.y + y_level + 0.25, z = pos.z}, "air").name --- print ("standing in " .. self.standing_in) - end - - -- check if falling, flying, floating - falling(self, pos) - - -- smooth rotation by ThomasMonroe314 - - if self.delay and self.delay > 0 then - - local yaw = self.object:get_yaw() - - if self.delay == 1 then - yaw = self.target_yaw - else - local dif = abs(yaw - self.target_yaw) - - if yaw > self.target_yaw then - - if dif > pi then - dif = 2 * pi - dif -- need to add - yaw = yaw + dif / self.delay - else - yaw = yaw - dif / self.delay -- need to subtract - end - - elseif yaw < self.target_yaw then - - if dif > pi then - dif = 2 * pi - dif - yaw = yaw - dif / self.delay -- need to subtract - else - yaw = yaw + dif / self.delay -- need to add - end - end - - if yaw > (pi * 2) then yaw = yaw - (pi * 2) end - if yaw < 0 then yaw = yaw + (pi * 2) end - end - - self.delay = self.delay - 1 - self.object:set_yaw(yaw) - end - - -- end rotation - - -- knockback timer - if self.pause_timer > 0 then - - self.pause_timer = self.pause_timer - dtime - - return - end - - -- run custom function (defined in mob lua file) - if self.do_custom then - - -- when false skip going any further - if self.do_custom(self, dtime) == false then - return - end - 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 - if self.timer > 100 then - self.timer = 1 - end - - -- mob plays random sound at times - if random(1, 100) == 1 then - mob_sound(self, self.sounds.random) - end - - -- environmental damage timer (every 1 second) - self.env_damage_timer = self.env_damage_timer + dtime - - if (self.state == "attack" and self.env_damage_timer > 1) - or self.state ~= "attack" then - - self.env_damage_timer = 0 - - -- check for environmental damage (water, fire, lava etc.) - do_env_damage(self) - - -- node replace check (cow eats grass etc.) - replace(self, pos) - end - - monster_attack(self) - - npc_attack(self) - - breed(self) - - follow_flop(self) - - do_states(self, dtime) - - do_jump(self) - - runaway_from(self) - -end - - --- default function when mobs are blown up with TNT -local do_tnt = function(obj, damage) - - --print ("----- Damage", damage) - - obj.object:punch(obj.object, 1.0, { - full_punch_interval = 1.0, - damage_groups = {fleshy = damage}, - }, nil) - - return false, true, {} -end - - -mobs.spawning_mobs = {} - --- register mob entity -function mobs:register_mob(name, def) - - mobs.spawning_mobs[name] = true - -minetest.register_entity(name, { - - stepheight = def.stepheight or 1.1, -- was 0.6 - name = name, - type = def.type, - attack_type = def.attack_type, - fly = def.fly, - fly_in = def.fly_in or "air", - owner = def.owner or "", - order = def.order or "", - on_die = def.on_die, - do_custom = def.do_custom, - jump_height = def.jump_height or 4, -- was 6 - drawtype = def.drawtype, -- DEPRECATED, use rotate instead - rotate = math.rad(def.rotate or 0), -- 0=front, 90=side, 180=back, 270=side2 - lifetimer = def.lifetimer or 180, -- 3 minutes - hp_min = max(1, (def.hp_min or 5) * difficulty), - hp_max = max(1, (def.hp_max or 10) * difficulty), - physical = true, - collisionbox = def.collisionbox or {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}, - selectionbox = def.selectionbox or def.collisionbox, - visual = def.visual, - visual_size = def.visual_size or {x = 1, y = 1}, - mesh = def.mesh, - makes_footstep_sound = def.makes_footstep_sound or false, - view_range = def.view_range or 5, - walk_velocity = def.walk_velocity or 1, - run_velocity = def.run_velocity or 2, - damage = max(0, (def.damage or 0) * difficulty), - light_damage = def.light_damage or 0, - water_damage = def.water_damage or 0, - lava_damage = def.lava_damage or 0, - suffocation = def.suffocation or 2, - fall_damage = def.fall_damage or 1, - fall_speed = def.fall_speed or -10, -- must be lower than -2 (default: -10) - drops = def.drops or {}, - armor = def.armor or 100, - on_rightclick = def.on_rightclick, - arrow = def.arrow, - shoot_interval = def.shoot_interval, - sounds = def.sounds or {}, - animation = def.animation, - follow = def.follow, - jump = def.jump ~= false, - walk_chance = def.walk_chance or 50, - attacks_monsters = def.attacks_monsters or false, - group_attack = def.group_attack or false, - passive = def.passive or false, - knock_back = def.knock_back ~= false, - blood_amount = def.blood_amount or 5, - blood_texture = def.blood_texture or "mobs_blood.png", - shoot_offset = def.shoot_offset or 0, - floats = def.floats or 1, -- floats in water by default - replace_rate = def.replace_rate, - replace_what = def.replace_what, - replace_with = def.replace_with, - replace_offset = def.replace_offset or 0, - on_replace = def.on_replace, - timer = 0, - env_damage_timer = 0, -- only used when state = "attack" - tamed = false, - pause_timer = 0, - horny = false, - hornytimer = 0, - child = false, - gotten = false, - health = 0, - reach = def.reach or 3, - htimer = 0, - texture_list = def.textures, - child_texture = def.child_texture, - docile_by_day = def.docile_by_day or false, - time_of_day = 0.5, - fear_height = def.fear_height or 0, - runaway = def.runaway, - runaway_timer = 0, - pathfinding = def.pathfinding, - immune_to = def.immune_to or {}, - explosion_radius = def.explosion_radius, - explosion_damage_radius = def.explosion_damage_radius, - explosion_timer = def.explosion_timer or 3, - allow_fuse_reset = def.allow_fuse_reset ~= false, - stop_to_explode = def.stop_to_explode ~= false, - custom_attack = def.custom_attack, - double_melee_attack = def.double_melee_attack, - dogshoot_switch = def.dogshoot_switch, - dogshoot_count = 0, - dogshoot_count_max = def.dogshoot_count_max or 5, - dogshoot_count2_max = def.dogshoot_count2_max or (def.dogshoot_count_max or 5), - attack_animals = def.attack_animals or false, - specific_attack = def.specific_attack, - runaway_from = def.runaway_from, - 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, - - get_staticdata = function(self) - return mob_staticdata(self) - end, - -}) - -end -- END mobs:register_mob function - - --- count how many mobs of one type are inside an area -local count_mobs = function(pos, type) - - local num_type = 0 - local num_total = 0 - local objs = minetest.get_objects_inside_radius(pos, aoc_range) - - for n = 1, #objs do - - if not objs[n]:is_player() then - - local obj = objs[n]:get_luaentity() - - -- count mob type and add to total also - if obj and obj.name and obj.name == type then - - num_type = num_type + 1 - num_total = num_total + 1 - - -- add to total mobs - elseif obj and obj.name and obj.health ~= nil then - - num_total = num_total + 1 - end - end - end - - return num_type, num_total -end - - --- global functions - -function mobs:spawn_abm_check(pos, node, name) - -- global function to add additional spawn checks - -- return true to stop spawning mob -end - - -local function player_near(pos, radius) - - local objs = minetest.get_objects_inside_radius(pos, radius) - - for n = 1, #objs do - - if objs[n]:is_player() then - return true - end - end - - return false -end - - -local function daycheck(day_toggle) - - if day_toggle ~= nil then - - local tod = (minetest.get_timeofday() or 0) * 24000 - - if tod > 4500 and tod < 19500 then - - if day_toggle == false then - return false -- mob requires night - end - else - if day_toggle == true then - return false -- mob requires day - end - end - end - - return true -- mob doesn't care -end - - -local function is_protected(pos) - - if not spawn_protected - and minetest.is_protected(pos, "") then - return true -- protected area - end - - return false -- mobs can spawn -end - - -local interval = 30 -local timer = 0 -local spawning_mobs = {} - -minetest.register_globalstep(function(dtime) - - if not mobs_spawn then - return - end - - timer = timer + dtime - if timer < interval then - return - end - timer = 0 - - for _,player in ipairs(minetest.get_connected_players()) do - - if player:get_hp() > 0 then - - local pos = player:getpos() - local area, pos2, light, obj, base - - for _,mob in ipairs(spawning_mobs) do - - area = nil - - if minetest.registered_entities[mob.name] - and random(1, mob.chance) == 1 then - - area = minetest.find_nodes_in_area_under_air( - {x = pos.x - 20, y = pos.y - 20, z = pos.z - 20}, - {x = pos.x + 20, y = pos.y + 20, z = pos.z + 20}, - mob.nodes) - end - - if area and #area > 0 then - - pos2 = area[math.random(1, #area)] - base = minetest.registered_entities[mob.name].collisionbox[5] - pos2.y = pos2.y + 1 + base - - light = minetest.get_node_light(pos2) or -1 - - if pos2.y >= mob.min_height - and pos2.y <= mob.max_height - and light >= mob.min_light - and light <= mob.max_light - and daycheck(mob.day_toggle) - and minetest.find_node_near(pos2, 1, mob.neighbors) - and count_mobs(pos2, mob.name) < mob.total - and not player_near(pos2, 10) - and not is_protected(pos2) then - -print ("--- Spawned ", mob.name, minetest.pos_to_string(pos2), mob.chance) - - obj = minetest.add_entity(pos2, mob.name) - - if mob.on_spawn then - mob.on_spawn(obj:get_luaentity(), pos2) - end - else -print ("--- Cannot spawn ", mob.name) - end - end - end - end - end -end) - - -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.settings:get(name) - - if numbers then - numbers = numbers:split(",") - chance = tonumber(numbers[1]) or chance - aoc = tonumber(numbers[2]) or aoc - - if chance == 0 then - minetest.log("warning", string.format("[mobs] %s has spawning disabled", name)) - return - end - - minetest.log("action", - string.format("[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc)) - end - - -- change old chance values to be more useable by new spawn routine - if chance > 999 then - chance = max(1, chance / 1000) - end - - -- adjust for mob chance multiplier - chance = max(1, chance * mob_chance_multiplier) - - -- add mob to table for spawning with routine above - table.insert(spawning_mobs, { - name = name, - nodes = nodes, - neighbors = neighbors, - chance = chance, - min_height = min_height, - max_height = max_height, - min_light = min_light, - max_light = max_light, - total = aoc, - day_toggle = day_toggle, - on_spawn = on_spawn, - }) -end - - --- compatibility with older mob registration -function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_object_count, max_height, day_toggle) - - mobs:spawn_specific(name, nodes, {"air"}, min_light, max_light, 30, - chance, active_object_count, -31000, max_height, day_toggle) -end - - --- MarkBu's spawn function -function mobs:spawn(def) - - mobs:spawn_specific( - def.name, - def.nodes or {"group:soil", "group:stone"}, - def.neighbors or {"air"}, - def.min_light or 0, - def.max_light or 15, - def.interval or 30, - def.chance or 5000, - def.active_object_count or 1, - def.min_height or -31000, - def.max_height or 31000, - def.day_toggle, - def.on_spawn - ) -end - - --- register arrow for shoot attack -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, - textures = def.textures, - velocity = def.velocity, - hit_player = def.hit_player, - hit_node = def.hit_node, - hit_mob = def.hit_mob, - drop = def.drop or false, -- drops arrow as registered item when true - collisionbox = {0, 0, 0, 0, 0, 0}, -- remove box around arrows - timer = 0, - switch = 0, - owner_id = def.owner_id, - rotate = def.rotate, - automatic_face_movement_dir = def.rotate - and (def.rotate - (pi / 180)) or false, - - on_activate = def.on_activate, - - on_step = def.on_step or function(self, dtime) - - self.timer = self.timer + 1 - - local pos = self.object:get_pos() - - if self.switch == 0 - or self.timer > 150 - or not within_limits(pos, 0) then - - self.object:remove() ; -- print ("removed arrow") - - return - end - - -- does arrow have a tail (fireball) - if def.tail - and def.tail == 1 - and def.tail_texture then - - minetest.add_particle({ - pos = pos, - velocity = {x = 0, y = 0, z = 0}, - acceleration = {x = 0, y = 0, z = 0}, - expirationtime = def.expire or 0.25, - collisiondetection = false, - texture = def.tail_texture, - size = def.tail_size or 5, - glow = def.glow or 0, - }) - end - - if self.hit_node then - - local node = node_ok(pos).name - - if minetest.registered_nodes[node].walkable then - - 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 - - self.object:remove() ; -- print ("hit node") - - return - end - end - - if self.hit_player or self.hit_mob then - - for _,player in pairs(minetest.get_objects_inside_radius(pos, 1.0)) do - - if self.hit_player - and player:is_player() then - - self.hit_player(self, player) - self.object:remove() ; -- print ("hit player") - return - end - - local entity = player:get_luaentity() - - if entity - and self.hit_mob - and entity._cmi_is_mob == true - and tostring(player) ~= self.owner_id - and entity.name ~= self.object:get_luaentity().name then - - self.hit_mob(self, player) - - self.object:remove() ; --print ("hit mob") - - return - end - end - end - - self.lastpos = pos - end - }) -end - - --- compatibility function -function mobs:explosion(pos, radius) - local self = {sounds = {}} - self.sounds.explode = "tnt_explode" - mobs:boom(self, pos, radius) -end - - --- no damage to nodes explosion -function mobs:safe_boom(self, pos, radius) - - minetest.sound_play(self.sounds and self.sounds.explode or "tnt_explode", { - pos = pos, - gain = 1.0, - max_hear_distance = self.sounds and self.sounds.distance or 32 - }) - - entity_physics(pos, radius) - effect(pos, 32, "tnt_smoke.png", radius * 3, radius * 5, radius, 1, 0) -end - - --- make explosion with protection and tnt mod check -function mobs:boom(self, pos, radius) - - if mobs_griefing - and minetest.get_modpath("tnt") and tnt and tnt.boom - and not minetest.is_protected(pos, "") then - - tnt.boom(pos, { - radius = radius, - damage_radius = radius, - sound = self.sounds and self.sounds.explode, - explode_center = true, - }) - else - mobs:safe_boom(self, pos, radius) - end -end - - --- Register spawn eggs - --- Note: This also introduces the “spawn_egg” group: --- * spawn_egg=1: Spawn egg (generic mob, no metadata) --- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata) -function mobs:register_egg(mob, desc, background, addegg, no_creative) - - local grp = {spawn_egg = 1} - - -- do NOT add this egg to creative inventory (e.g. dungeon master) - if creative and no_creative == true then - grp.not_in_creative_inventory = 1 - end - - local invimg = background - - if addegg == 1 then - invimg = "mobs_chicken_egg.png^(" .. invimg .. - "^[mask:mobs_chicken_egg_overlay.png)" - end - - -- register new spawn egg containing mob information - minetest.register_craftitem(mob .. "_set", { - - description = S("@1 (Tamed)", desc), - inventory_image = invimg, - groups = {spawn_egg = 2, not_in_creative_inventory = 1}, - stack_max = 1, - - on_place = function(itemstack, placer, pointed_thing) - - local pos = pointed_thing.above - - -- am I clicking on something with existing on_rightclick function? - local under = minetest.get_node(pointed_thing.under) - local def = minetest.registered_nodes[under.name] - if def and def.on_rightclick then - return def.on_rightclick(pointed_thing.under, under, placer, itemstack) - end - - if pos - 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 data = itemstack:get_metadata() - local mob = minetest.add_entity(pos, mob, data) - local ent = mob:get_luaentity() - - -- set owner if not a monster - if ent.type ~= "monster" then - ent.owner = placer:get_player_name() - ent.tamed = true - end - - -- since mob is unique we remove egg once spawned - itemstack:take_item() - end - - return itemstack - end, - }) - - - -- register old stackable mob egg - minetest.register_craftitem(mob, { - - description = desc, - inventory_image = invimg, - groups = grp, - - on_place = function(itemstack, placer, pointed_thing) - - local pos = pointed_thing.above - - -- am I clicking on something with existing on_rightclick function? - local under = minetest.get_node(pointed_thing.under) - local def = minetest.registered_nodes[under.name] - if def and def.on_rightclick then - return def.on_rightclick(pointed_thing.under, under, placer, itemstack) - end - - if pos - 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() - - -- don't set owner if monster or sneak pressed - if ent.type ~= "monster" - and not placer:get_player_control().sneak then - ent.owner = placer:get_player_name() - ent.tamed = true - end - - -- if not in creative then take item - if not mobs.is_creative(placer:get_player_name()) then - itemstack:take_item() - end - end - - return itemstack - end, - }) - -end - - --- capture critter (thanks to blert2112 for idea) -function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, force_take, replacewith) - - if self.child - or not clicker:is_player() - or not clicker:get_inventory() then - return false - end - - -- get name of clicked mob - local mobname = self.name - - -- if not nil change what will be added to inventory - if replacewith then - mobname = replacewith - end - - local name = clicker:get_player_name() - local tool = clicker:get_wielded_item() - - -- are we using hand, net or lasso to pick up mob? - if tool:get_name() ~= "" - and tool:get_name() ~= "mobs:net" - and tool:get_name() ~= "mobs:lasso" then - return false - end - - -- is mob tamed? - if self.tamed == false - and force_take == false then - - minetest.chat_send_player(name, S("Not tamed!")) - - return true -- false - end - - -- cannot pick up if not owner - if self.owner ~= name - and force_take == false then - - minetest.chat_send_player(name, S("@1 is owner!", self.owner)) - - return true -- false - end - - if clicker:get_inventory():room_for_item("main", mobname) then - - -- was mob clicked with hand, net, or lasso? - local chance = 0 - - if tool:get_name() == "" then - 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:lasso" then - - chance = chance_lasso - - tool:add_wear(650) -- 100 uses - - clicker:set_wielded_item(tool) - - end - - -- calculate chance.. add to inventory if successful? - if chance > 0 and random(1, 100) <= chance then - - -- default mob egg - local new_stack = ItemStack(mobname) - - -- add special mob egg with all mob information - -- unless 'replacewith' contains new item to use - if not replacewith then - - new_stack = ItemStack(mobname .. "_set") - - 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 - - local data_str = minetest.serialize(tmp) - - new_stack:set_metadata(data_str) - end - - local inv = clicker:get_inventory() - - if inv:room_for_item("main", new_stack) then - inv:add_item("main", new_stack) - else - minetest.add_item(clicker:get_pos(), new_stack) - end - - self.object:remove() - - mob_sound(self, "default_place_node_hard") - - elseif chance ~= 0 then - minetest.chat_send_player(name, S("Missed!")) - - mob_sound(self, "mobs_swing") - end - end - - return true -end - - --- protect tamed mob with rune item -function mobs:protect(self, clicker) - - local name = clicker:get_player_name() - local tool = clicker:get_wielded_item() - - if tool:get_name() ~= "mobs:protector" then - return false - end - - if self.tamed == false then - minetest.chat_send_player(name, S("Not tamed!")) - return true -- false - end - - if self.protected == true then - minetest.chat_send_player(name, S("Already protected!")) - return true -- false - end - - 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 - - local pos = self.object:get_pos() - pos.y = pos.y + self.collisionbox[2] + 0.5 - - effect(self.object:get_pos(), 25, "mobs_protect_particle.png", 0.5, 4, 2, 15) - - mob_sound(self, "mobs_spell") - - return true -end - - -local mob_obj = {} -local mob_sta = {} - --- feeding, taming and breeding (thanks blert2112) -function mobs:feed_tame(self, clicker, feed_count, breed, tame) - - if not self.follow then - return false - end - - -- can eat/tame with item in hand - if follow_holding(self, clicker) then - - -- if not in creative then take item - if not mobs.is_creative(clicker:get_player_name()) then - - local item = clicker:get_wielded_item() - - item:take_item() - - clicker:set_wielded_item(item) - end - - -- increase health - self.health = self.health + 4 - - if self.health >= self.hp_max then - - self.health = self.hp_max - - 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 - end - - self.object:set_hp(self.health) - - update_tag(self) - - -- 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 - - if self.tamed == false then - minetest.chat_send_player(clicker:get_player_name(), - S("@1 has been tamed!", - self.name:split(":")[2])) - end - - self.tamed = true - - if not self.owner or self.owner == "" then - self.owner = clicker:get_player_name() - end - end - - -- make sound when fed so many times - mob_sound(self, self.sounds.random) - end - - return true - end - - local item = clicker:get_wielded_item() - - -- if mob has been tamed you can name it with a nametag - if item:get_name() == "mobs:nametag" - and clicker:get_player_name() == self.owner then - - local name = clicker:get_player_name() - - -- store mob and nametag stack in external variables - mob_obj[name] = self - mob_sta[name] = item - - local tag = self.nametag or "" - - minetest.show_formspec(name, "mobs_nametag", "size[8,4]" - .. default.gui_bg - .. default.gui_bg_img - .. "field[0.5,1;7.5,0;name;" .. minetest.formspec_escape(S("Enter name:")) .. ";" .. tag .. "]" - .. "button_exit[2.5,3.5;3,1;mob_rename;" .. minetest.formspec_escape(S("Rename")) .. "]") - end - - return false -end - - --- inspired by blockmen's nametag mod -minetest.register_on_player_receive_fields(function(player, formname, fields) - - -- right-clicked with nametag and name entered? - if formname == "mobs_nametag" - and fields.name - and fields.name ~= "" then - - local name = player:get_player_name() - - if not mob_obj[name] - or not mob_obj[name].object then - return - end - - -- make sure nametag is being used to name mob - local item = player:get_wielded_item() - - if item:get_name() ~= "mobs:nametag" then - return - end - - -- limit name entered to 64 characters long - if string.len(fields.name) > 64 then - fields.name = string.sub(fields.name, 1, 64) - end - - -- update nametag - mob_obj[name].nametag = fields.name - - update_tag(mob_obj[name]) - - -- if not in creative then take item - if not mobs.is_creative(name) then - - mob_sta[name]:take_item() - - player:set_wielded_item(mob_sta[name]) - end - - -- reset external variables - mob_obj[name] = nil - mob_sta[name] = nil - end -end) - - --- compatibility function for old entities to new modpack entities -function mobs:alias_mob(old_name, new_name) - - -- spawn egg - minetest.register_alias(old_name, new_name) - - -- entity - minetest.register_entity(":" .. old_name, { - - physical = false, - - on_step = function(self) - - if minetest.registered_entities[new_name] then - minetest.add_entity(self.object:get_pos(), new_name) - end - - self.object:remove() - end - }) -end From 8da3bb5cb1b59d44659cdf23114e77d8c0775433 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 24 Jan 2019 11:14:25 +0000 Subject: [PATCH 007/135] add do_stay_near function, fix annoying pathfinding sounds and toolrank wear --- api.lua | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++------ api.txt | 3 +++ 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/api.lua b/api.lua index 447296b..46748ae 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190117", + version = "20190124", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -466,17 +466,16 @@ function mob_class:attempt_flight_correction() searchnodes = {self.fly_in} end - local r = 1 local flyable_nodes = minetest.find_nodes_in_area( - {x = pos.x - r, y = pos.y - r, z = pos.z - r}, - {x = pos.x + r, y = pos.y + r, z = pos.z + r}, + {x = pos.x - 1, y = pos.y - 1, z = pos.z - 1}, + {x = pos.x + 1, y = pos.y + 1, z = pos.z + 1}, searchnodes) if #flyable_nodes < 1 then return false end - local escape_target = flyable_nodes[math.random(1,#flyable_nodes)] + local escape_target = flyable_nodes[math.random(1, #flyable_nodes)] local escape_direction = vector.direction(pos, escape_target) self.object:set_velocity( @@ -520,6 +519,57 @@ function mob_class:flight_check() end +-- if self.stay_near set then check periodically for nodes and turn to face/move +function mob_class:do_stay_near() + + if not self.stay_near then return false end + + local pos = self.object:get_pos() + local searchnodes = self.stay_near[1] + local chance = self.stay_near[2] or 10 + + if random(1, chance) > 1 then + return false + end + + if type(searchnodes) == "string" then + searchnodes = {self.stay_near[1]} + end + + local r = self.view_range + local nearby_nodes = minetest.find_nodes_in_area( + {x = pos.x - r, y = pos.y - 1, z = pos.z - r}, + {x = pos.x + r, y = pos.y + 1, z = pos.z + r}, + searchnodes) + + if #nearby_nodes < 1 then + return false + end + + local target = nearby_nodes[math.random(1, #nearby_nodes)] + local direction = vector.direction(pos, target) + + local vec = { + x = target.x - pos.x, + z = target.z - pos.z + } + + yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate + + if target.x > pos.x then + yaw = yaw + pi + end + + yaw = self:set_yaw(yaw, 4) + + self:set_animation("walk") + + self:set_velocity(self.walk_velocity) + + return true +end + + -- custom particle effects local effect = function(pos, amount, texture, min_size, max_size, radius, gravity, glow) @@ -1525,7 +1575,7 @@ function mob_class:smart_mobs(s, p, dist, dtime) self.path.stuck_timer = stuck_timeout - 2 -- frustration! cant find the damn path :( - self:mob_sound(self.sounds.random) + --self:mob_sound(self.sounds.random) else -- yay i found path self:mob_sound(self.sounds.war_cry) @@ -2604,7 +2654,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) if tr then if weapon_def.original_description then - weapon:add_wear(toolranks.new_afteruse(weapon, hitter, nil, {wear = wear})) + toolranks.new_afteruse(weapon, hitter, nil, {wear = wear}) end else weapon:add_wear(wear) @@ -3146,6 +3196,8 @@ function mob_class:on_step(dtime) self:do_jump() self:do_runaway_from(self) + + self:do_stay_near() end @@ -3255,6 +3307,7 @@ minetest.register_entity(name, setmetatable({ runaway_from = def.runaway_from, owner_loyal = def.owner_loyal, pushable = def.pushable, + stay_near = def.stay_near, on_spawn = def.on_spawn, diff --git a/api.txt b/api.txt index d6c2a95..69c7fac 100644 --- a/api.txt +++ b/api.txt @@ -44,6 +44,9 @@ functions needed for the mob to work properly which contains the following: '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". + 'stay_near' when set allows mobs the chance to stay around certain nodes. + 'nodes' string or table of nodes to stay nearby e.g. "farming:straw" + 'chance' chance of searching for above node(s), default is 10. 'runaway' if true causes animals to turn and run away when hit. 'pushable' when true mobs can be pushed by player or other mobs. 'view_range' how many nodes in distance the mob can see a player. From a1d4e9bbfcce07062ade3088e8744aab39a7c3cd Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 23 Mar 2019 18:36:22 +0000 Subject: [PATCH 008/135] add mobs:mob_reset_stick --- api.lua | 2 +- crafts.lua | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 46748ae..aebd5f6 100644 --- a/api.lua +++ b/api.lua @@ -2963,7 +2963,7 @@ function mob_class:mob_activate(staticdata, def, dtime) end if self.health == 0 then - self.health = random (self.hp_min, self.hp_max) + self.health = random(self.hp_min, self.hp_max) end -- pathfinding init diff --git a/crafts.lua b/crafts.lua index 0460f89..b3cc12a 100644 --- a/crafts.lua +++ b/crafts.lua @@ -219,3 +219,51 @@ minetest.register_craft({ recipe = "mobs:fence_top", burntime = 2, }) + +-- this tool spawns same mob and adds owner, protected, nametag info +-- then removes original entity, this is used for fixing any issues. + +minetest.register_tool(":mobs:mob_reset_stick", { + description = "Mob Reset Stick", + inventory_image = "default_stick.png^[colorize:#ff000050", + stack_max = 1, + groups = {not_in_creative_inventory = 1}, + + on_use = function(itemstack, user, pointed_thing) + + local privs = minetest.get_player_privs(user:get_player_name()) + + if pointed_thing.type ~= "object" then + return + end + + local obj = pointed_thing.ref + + if obj then + + local self = obj:get_luaentity() + local obj2 = minetest.add_entity(obj:get_pos(), self.name) + + if obj2 then + + local ent2 = obj2:get_luaentity() + + ent2.protected = self.protected + ent2.owner = self.owner + ent2.nametag = self.nametag + ent2.gotten = self.gotten + ent2.tamed = self.tamed + ent2.health = self.health + + if self.child then + obj2:set_velocity({x = 0, y = self.jump_height, z = 0}) + end + + obj2:set_properties({nametag = self.nametag}) + + obj:remove() + end + end + end, +}) + From 26a520e214201c86b6501ad529c721c0514953e3 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 28 Mar 2019 18:37:46 +0000 Subject: [PATCH 009/135] sneak and punch mob with mob reset stick to add custom texture --- crafts.lua | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/crafts.lua b/crafts.lua index b3cc12a..ffdad7d 100644 --- a/crafts.lua +++ b/crafts.lua @@ -223,6 +223,8 @@ minetest.register_craft({ -- this tool spawns same mob and adds owner, protected, nametag info -- then removes original entity, this is used for fixing any issues. +local tex_obj + minetest.register_tool(":mobs:mob_reset_stick", { description = "Mob Reset Stick", inventory_image = "default_stick.png^[colorize:#ff000050", @@ -231,15 +233,16 @@ minetest.register_tool(":mobs:mob_reset_stick", { on_use = function(itemstack, user, pointed_thing) - local privs = minetest.get_player_privs(user:get_player_name()) - if pointed_thing.type ~= "object" then return end local obj = pointed_thing.ref - if obj then + local control = user:get_player_control() + local sneak = control and control.sneak + + if obj and not sneak then local self = obj:get_luaentity() local obj2 = minetest.add_entity(obj:get_pos(), self.name) @@ -254,6 +257,7 @@ minetest.register_tool(":mobs:mob_reset_stick", { ent2.gotten = self.gotten ent2.tamed = self.tamed ent2.health = self.health + ent2.order = self.order if self.child then obj2:set_velocity({x = 0, y = self.jump_height, z = 0}) @@ -264,6 +268,56 @@ minetest.register_tool(":mobs:mob_reset_stick", { obj:remove() end end + + if obj and sneak then + + tex_obj = obj + + local name = user:get_player_name() + local tex = "" + + minetest.show_formspec(name, "mobs_texture", "size[8,4]" + .. "field[0.5,1;7.5,0;name;" + .. minetest.formspec_escape(S("Enter texture:")) .. ";" .. tex .. "]" + .. "button_exit[2.5,3.5;3,1;mob_texture_change;" + .. minetest.formspec_escape(S("Change")) .. "]") + end end, }) +minetest.register_on_player_receive_fields(function(player, formname, fields) + + -- right-clicked with nametag and name entered? + if formname == "mobs_texture" + and fields.name + and fields.name ~= "" then + + local name = player:get_player_name() + + if not tex_obj then + return + end + + -- make sure nametag is being used to name mob + local item = player:get_wielded_item() + + if item:get_name() ~= "mobs:mob_reset_stick" then + return + end + + -- limit name entered to 64 characters long + if string.len(fields.name) > 64 then + fields.name = string.sub(fields.name, 1, 64) + end + + -- update texture + local self = tex_obj:get_luaentity() + + self.base_texture = {fields.name} + + tex_obj:set_properties({textures = {fields.name}}) + + -- reset external variable + tex_obj = nil + end +end) From 55dda6da8386e78e467664343472a254b5fbfe81 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 2 Apr 2019 08:41:09 +0100 Subject: [PATCH 010/135] force defined model on activation instead of stored --- api.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api.lua b/api.lua index aebd5f6..79e3f78 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190124", + version = "20190402", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -2893,6 +2893,12 @@ function mob_class:mob_activate(staticdata, def, dtime) end end + -- force current model into mob + self.mesh = def.mesh + self.base_mesh = def.mesh + self.collisionbox = def.collisionbox + self.selectionbox = def.selectionbox + -- select random texture, set model and size if not self.base_texture then From ddbd40328556ff62679656bcecfdaa91360ea639 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 30 Apr 2019 11:27:26 +0100 Subject: [PATCH 011/135] add stand_chance and animation force flag --- api.lua | 14 ++++++++------ api.txt | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/api.lua b/api.lua index 79e3f78..3a1c346 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190402", + version = "20190430", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -106,6 +106,7 @@ local mob_class = { jump = true, knock_back = true, walk_chance = 50, + stand_chance = 30, attack_chance = 5, passive = false, blood_amount = 5, @@ -277,15 +278,15 @@ end -- set defined animation -function mob_class:set_animation(anim) +function mob_class:set_animation(anim, force) if not self.animation or not anim then return end self.animation.current = self.animation.current or "" - -- only set different animation for attacks when setting to same set - if anim ~= "punch" and anim ~= "shoot" + -- only set different animation for attacks when using same set + if force ~= true and anim ~= "punch" and anim ~= "shoot" and string.find(self.animation.current, anim) then return end @@ -2070,11 +2071,11 @@ function mob_class:do_states(dtime) if self.facing_fence == true or temp_is_cliff - or random(1, 100) <= 30 then + or random(1, 100) <= self.stand_chance then self:set_velocity(0) self.state = "stand" - self:set_animation("stand") + self:set_animation("stand", true) else self:set_velocity(self.walk_velocity) @@ -3274,6 +3275,7 @@ minetest.register_entity(name, setmetatable({ follow = def.follow, jump = def.jump, walk_chance = def.walk_chance, + stand_chance = def.stand_chance, attack_chance = def.attack_chance, passive = def.passive, knock_back = def.knock_back, diff --git a/api.txt b/api.txt index 69c7fac..03d7365 100644 --- a/api.txt +++ b/api.txt @@ -35,6 +35,7 @@ functions needed for the mob to work properly which contains the following: 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. + 'stand_chance' has a 0-100 chance value your mob will stand from walking. '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. @@ -632,7 +633,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, From 392974e835dc32e8d7589a0b0c90a508ae96dcc1 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Wed, 8 May 2019 16:14:49 +0100 Subject: [PATCH 012/135] added new line_of_sight that uses raycasting when mt5.0 is found (thanks Astrobe) --- api.lua | 46 +++++++++++++-- api.txt | 6 +- readme.MD | 171 +++++++++++++++++++++++++++--------------------------- 3 files changed, 132 insertions(+), 91 deletions(-) diff --git a/api.lua b/api.lua index 3a1c346..8979162 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190430", + version = "20190508", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -330,7 +330,7 @@ end -- check line of sight (BrunoMine) -function mob_class:line_of_sight(pos1, pos2, stepsize) +local line_of_sight = function(self, pos1, pos2, stepsize) stepsize = stepsize or 1 @@ -401,7 +401,7 @@ end -- check line of sight (by BrunoMine, tweaked by Astrobe) -function mob_class:NEW_line_of_sight(pos1, pos2, stepsize) +local new_line_of_sight = function(self, pos1, pos2, stepsize) if not pos1 or not pos2 then return end @@ -447,10 +447,46 @@ function mob_class:NEW_line_of_sight(pos1, pos2, stepsize) return false end +-- check line of sight using raycasting (thanks Astrobe) +local ray_line_of_sight = function(self, pos1, pos2) + + local ray = minetest.raycast(pos1, pos2, true, false) + local thing = ray:next() + + while thing do + + if thing.type == "object" + and thing.ref ~= self.object + and not thing.ref:is_player() then return false end + + if thing.type == "node" then + + local name = minetest.get_node(thing.under).name + + if minetest.registered_items[name].walkable then return false end + end + + thing = ray:next() + end + + return true +end + +-- detect if using minetest 5.0 by searching for permafrost node +local is_50 = minetest.registered_nodes["default:permafrost"] + +function mob_class:line_of_sight(pos1, pos2, stepsize) + + if is_50 then -- only use if minetest 5.0 is detected + return ray_line_of_sight(self, pos1, pos2) + end + + return line_of_sight(self, pos1, pos2, stepsize) +end + -- global function function mobs:line_of_sight(entity, pos1, pos2, stepsize) - - return mob_class.line_of_sight(entity, pos1, pos2, stepsize) + return mob_class.line_of_sight(pos1, pos2, stepsize) end diff --git a/api.txt b/api.txt index 03d7365..f45b818 100644 --- a/api.txt +++ b/api.txt @@ -576,7 +576,7 @@ Certain variables need to be set before using the above functions: 'self.driver_scale' sets driver scale for mobs larger than {x=1, y=1} -mobs:line_of_sight(self, pos1, pos2, stepsize) +mobs:line_of_sight(self, pos1, pos2, stepsize) [DEPRECATED] This function is for use within the mobs definition for special use cases and returns true if a mob can see the player or victim. @@ -586,6 +586,10 @@ returns true if a mob can see the player or victim. 'pos2' position of vistim or player 'stepsize' usually set to 1 +Use this instead: + + mob_class:line_of_sight(pos1, pos2, stepsize) + External Settings for "minetest.conf" ------------------------------------ diff --git a/readme.MD b/readme.MD index 5739aca..3d5ff04 100644 --- a/readme.MD +++ b/readme.MD @@ -1,85 +1,86 @@ - -MOBS REDO for MINETEST - -Built from PilzAdam's original Simple Mobs with additional mobs by KrupnoPavel, Zeg9, ExeterDad and AspireMint. - - -This mod contains the API only for adding your own mobs into the world, so please use the additional modpacks to add animals, monsters etc. - - -https://forum.minetest.net/viewtopic.php?f=11&t=9917 - - -Crafts: - - - Nametag (paper, black dye, string) can be used right-click on a tamed mob to give them a name. - - Nets can be used to right-click tamed mobs to pick them up and place inside inventory as a spawn egg. - - Magic Lasso is similar to nets but with a better chance of picking up larger mobs. - - Shears are used to right-click sheep and return 1-3 wool. - - Protection Rune lets you protect tamed mobs from harm by other players - - Mob Fence and Fence Top (to stop mobs escaping/glitching through fences) - -Lucky Blocks: 9 - - -Changelog: -- 1.49- Added mobs:force_capture(self, player) function, api functions now use metatables thanks to bell07 -- 1.48- Add mobs:set_velocity(self, velocity) global function -- 1.47- Mob damage changes, min and max light level for damage added, ignition sources checked for lava damage -- 1.46- Mobs only drop rare items when killed by player (drops.min = 0 makes them rare), code tweak, pathfinding no longer sees through walkable nodes -- 1.45- Added Fence Top to add on top of any fence to stop mobs escaping, new line_of_sight tweaked by Astrobe -- 1.44- Added ToolRanks support for swords when attacking mobs -- 1.43- Better 0.4.16 compatibility, added general attack function and settings -- 1.42- Added "all" option to immune_to table, tidied floating mobs to be less intensive -- 1.41- Mob pathfinding has been updated thanks to Elkien3 -- 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 -- 1.34- Added function to fly mob using directional movement (thanks D00Med for flying code) -- 1.33- Added functions to mount ride mobs (mobs.attach, mobs.detach, mobs.drive) many thanks to Blert2112 -- 1.32- Added new spawn check to count specific mobs AND new minetest.conf setting to chance spawn chance and numbers, added ability to protect tamed mobs -- 1.31- Added 'attack_animals' and 'specific_attack' flags for custom monster attacks, also 'mob_difficulty' .conf setting to make mobs harder. -- 1.30- Added support for invisibility mod (mobs cant attack what they cant see), tweaked and tidied code -- 1.29- Split original Mobs Redo into a modpack to make it easier to disable mob sets (animal, monster, npc) or simply use the Api itself for your own mod -- 1.28- New damage system added with ability for mob to be immune to weapons or healed by them :) -- 1.27- Added new sheep, lava flan and spawn egg textures. New Lava Pick tool smelts what you dig. New atan checking function. -- 1.26- Pathfinding feature added thanks to rnd, when monsters attack they become scary smart in finding you :) also, beehive produces honey now :) -- 1.25- Mobs no longer spawn within 12 blocks of player or despawn within same range, spawners now have player detection, Code tidy and tweak. -- 1.24- Added feature where certain animals run away when punched (runaway = true in mob definition) -- 1.23- Added mob spawner block for admin to setup spawners in-game (place and right click to enter settings) -- 1.22- Added ability to name tamed animals and npc using nametags, also npc will attack anyone who punches them apart from owner -- 1.21- Added some more error checking to reduce serialize.h error and added height checks for falling off cliffs (thanks cmdskp) -- 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 -- 1.16- Mobs follow multiple items now, Npc's can breed -- 1.15- Added Feeding/Taming/Breeding function, right-click to pick up any sheep with X mark on them and replace with new one to fix compatibility. -- 1.14- All .self variables saved in staticdata, Fixed self.health bug -- 1.13- Added capture function (thanks blert2112) chance of picking up mob with hand; net; magic lasso, replaced some .x models with newer .b3d one's -- 1.12- Added animal ownership so that players cannot steal your tamed animals -- 1.11- Added flying mobs (and swimming), fly=true and fly_in="air" or "deafult:water_source" for fishy -- 1,10- Footstep removed (use replace), explosion routine added for exploding mobs. -- 1.09- reworked breeding routine, added mob rotation value, added footstep feature, added jumping mobs with sounds feature, added magic lasso for picking up animals -- 1.08- Mob throwing attack has been rehauled so that they can damage one another, also drops and on_die function added -- 1.07- Npc's can now be set to follow player or stand by using self.order and self.owner variables -- beta- Npc mob added, kills monsters, attacks player when punched, right click with food to heal or gold lump for drop -- 1.06- Changed recovery times after breeding, and time taken to grow up (can be sped up by feeding baby animal) -- 1.05- Added ExeterDad's bunny's which can be picked up and tamed with 4 carrots from farming redo or farming_plus, also shears added to get wool from sheep and lastly Jordach/BSD's kitten -- 1.04- Added mating for sheep, cows and hogs... feed animals to make horny and hope for a baby which is half size, will grow up quick though :) -- 1.03- Added mob drop/replace feature so that chickens can drop eggs, cow/sheep can eat grass/wheat etc. -- 1.02- Sheared sheep are remembered and spawn shaven, Warthogs will attack when threatened, Api additions -- 1.01- Mobs that suffer fall damage or die in water/lava/sunlight will now drop items -- 1.0 - more work on Api so that certain mobs can float in water while some sink like a brick :) -- 0.9 - Spawn eggs added for all mobs (admin only, cannot be placed in protected areas)... Api tweaked -- 0.8 - Added sounds to monster mobs (thanks Cyberpangolin for the sfx) and also chicken sound -- 0.7 - mobs.protected switch added to api.lua, when set to 1 mobs no longer spawn in protected areas, also bug fixes -- 0.6 - Api now supports multi-textured mobs, e.g oerkki, dungeon master, rats and chickens have random skins when spawning (sheep fix TODO), also new Honey block -- 0.5 - Mobs now float in water, die from falling, and some code improvements -- 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 + +MOBS REDO for MINETEST + +Built from PilzAdam's original Simple Mobs with additional mobs by KrupnoPavel, Zeg9, ExeterDad and AspireMint. + + +This mod contains the API only for adding your own mobs into the world, so please use the additional modpacks to add animals, monsters etc. + + +https://forum.minetest.net/viewtopic.php?f=11&t=9917 + + +Crafts: + + - Nametag (paper, black dye, string) can be used right-click on a tamed mob to give them a name. + - Nets can be used to right-click tamed mobs to pick them up and place inside inventory as a spawn egg. + - Magic Lasso is similar to nets but with a better chance of picking up larger mobs. + - Shears are used to right-click sheep and return 1-3 wool. + - Protection Rune lets you protect tamed mobs from harm by other players + - Mob Fence and Fence Top (to stop mobs escaping/glitching through fences) + +Lucky Blocks: 9 + + +Changelog: +- 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found (thanks Astrobe) +- 1.49- Added mobs:force_capture(self, player) function, api functions now use metatables thanks to bell07 +- 1.48- Add mobs:set_velocity(self, velocity) global function +- 1.47- Mob damage changes, min and max light level for damage added, ignition sources checked for lava damage +- 1.46- Mobs only drop rare items when killed by player (drops.min = 0 makes them rare), code tweak, pathfinding no longer sees through walkable nodes +- 1.45- Added Fence Top to add on top of any fence to stop mobs escaping, new line_of_sight tweaked by Astrobe +- 1.44- Added ToolRanks support for swords when attacking mobs +- 1.43- Better 0.4.16 compatibility, added general attack function and settings +- 1.42- Added "all" option to immune_to table, tidied floating mobs to be less intensive +- 1.41- Mob pathfinding has been updated thanks to Elkien3 +- 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 +- 1.34- Added function to fly mob using directional movement (thanks D00Med for flying code) +- 1.33- Added functions to mount ride mobs (mobs.attach, mobs.detach, mobs.drive) many thanks to Blert2112 +- 1.32- Added new spawn check to count specific mobs AND new minetest.conf setting to chance spawn chance and numbers, added ability to protect tamed mobs +- 1.31- Added 'attack_animals' and 'specific_attack' flags for custom monster attacks, also 'mob_difficulty' .conf setting to make mobs harder. +- 1.30- Added support for invisibility mod (mobs cant attack what they cant see), tweaked and tidied code +- 1.29- Split original Mobs Redo into a modpack to make it easier to disable mob sets (animal, monster, npc) or simply use the Api itself for your own mod +- 1.28- New damage system added with ability for mob to be immune to weapons or healed by them :) +- 1.27- Added new sheep, lava flan and spawn egg textures. New Lava Pick tool smelts what you dig. New atan checking function. +- 1.26- Pathfinding feature added thanks to rnd, when monsters attack they become scary smart in finding you :) also, beehive produces honey now :) +- 1.25- Mobs no longer spawn within 12 blocks of player or despawn within same range, spawners now have player detection, Code tidy and tweak. +- 1.24- Added feature where certain animals run away when punched (runaway = true in mob definition) +- 1.23- Added mob spawner block for admin to setup spawners in-game (place and right click to enter settings) +- 1.22- Added ability to name tamed animals and npc using nametags, also npc will attack anyone who punches them apart from owner +- 1.21- Added some more error checking to reduce serialize.h error and added height checks for falling off cliffs (thanks cmdskp) +- 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 +- 1.16- Mobs follow multiple items now, Npc's can breed +- 1.15- Added Feeding/Taming/Breeding function, right-click to pick up any sheep with X mark on them and replace with new one to fix compatibility. +- 1.14- All .self variables saved in staticdata, Fixed self.health bug +- 1.13- Added capture function (thanks blert2112) chance of picking up mob with hand; net; magic lasso, replaced some .x models with newer .b3d one's +- 1.12- Added animal ownership so that players cannot steal your tamed animals +- 1.11- Added flying mobs (and swimming), fly=true and fly_in="air" or "deafult:water_source" for fishy +- 1,10- Footstep removed (use replace), explosion routine added for exploding mobs. +- 1.09- reworked breeding routine, added mob rotation value, added footstep feature, added jumping mobs with sounds feature, added magic lasso for picking up animals +- 1.08- Mob throwing attack has been rehauled so that they can damage one another, also drops and on_die function added +- 1.07- Npc's can now be set to follow player or stand by using self.order and self.owner variables +- beta- Npc mob added, kills monsters, attacks player when punched, right click with food to heal or gold lump for drop +- 1.06- Changed recovery times after breeding, and time taken to grow up (can be sped up by feeding baby animal) +- 1.05- Added ExeterDad's bunny's which can be picked up and tamed with 4 carrots from farming redo or farming_plus, also shears added to get wool from sheep and lastly Jordach/BSD's kitten +- 1.04- Added mating for sheep, cows and hogs... feed animals to make horny and hope for a baby which is half size, will grow up quick though :) +- 1.03- Added mob drop/replace feature so that chickens can drop eggs, cow/sheep can eat grass/wheat etc. +- 1.02- Sheared sheep are remembered and spawn shaven, Warthogs will attack when threatened, Api additions +- 1.01- Mobs that suffer fall damage or die in water/lava/sunlight will now drop items +- 1.0 - more work on Api so that certain mobs can float in water while some sink like a brick :) +- 0.9 - Spawn eggs added for all mobs (admin only, cannot be placed in protected areas)... Api tweaked +- 0.8 - Added sounds to monster mobs (thanks Cyberpangolin for the sfx) and also chicken sound +- 0.7 - mobs.protected switch added to api.lua, when set to 1 mobs no longer spawn in protected areas, also bug fixes +- 0.6 - Api now supports multi-textured mobs, e.g oerkki, dungeon master, rats and chickens have random skins when spawning (sheep fix TODO), also new Honey block +- 0.5 - Mobs now float in water, die from falling, and some code improvements +- 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 From 0bc62cbf7434fe9eabbad58894990e06359a8643 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 11 May 2019 14:42:53 +0100 Subject: [PATCH 013/135] typo fix --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 8979162..3f76ca5 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190508", + version = "20190511", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -486,7 +486,7 @@ end -- global function function mobs:line_of_sight(entity, pos1, pos2, stepsize) - return mob_class.line_of_sight(pos1, pos2, stepsize) + return mob_class:line_of_sight(pos1, pos2, stepsize) end From 975175e7b8da44b384cb7dab76778f4c2de21d2e Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 13 May 2019 16:07:42 +0100 Subject: [PATCH 014/135] fix mobs:line_of_sight() global function to use entity reference --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 3f76ca5..84bae97 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190511", + version = "20190513", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -486,7 +486,7 @@ end -- global function function mobs:line_of_sight(entity, pos1, pos2, stepsize) - return mob_class:line_of_sight(pos1, pos2, stepsize) + return entity:line_of_sight(pos1, pos2, stepsize) end From e4ca52fafba91d39492692dedaa3cca09de3bc55 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 20 May 2019 17:26:20 +0100 Subject: [PATCH 015/135] fix nil check --- api.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 84bae97..24d89f8 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190513", + version = "20190520", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -463,7 +463,8 @@ local ray_line_of_sight = function(self, pos1, pos2) local name = minetest.get_node(thing.under).name - if minetest.registered_items[name].walkable then return false end + if minetest.registered_items[name] + and minetest.registered_items[name].walkable then return false end end thing = ray:next() From 1de0c342832347c3b4b2d628f167d4bdd5caa551 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 24 May 2019 14:30:42 +0100 Subject: [PATCH 016/135] code tidy --- crafts.lua | 2 +- spawner.lua | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/crafts.lua b/crafts.lua index ffdad7d..b5be3cb 100644 --- a/crafts.lua +++ b/crafts.lua @@ -226,7 +226,7 @@ minetest.register_craft({ local tex_obj minetest.register_tool(":mobs:mob_reset_stick", { - description = "Mob Reset Stick", + description = S("Mob Reset Stick"), inventory_image = "default_stick.png^[colorize:#ff000050", stack_max = 1, groups = {not_in_creative_inventory = 1}, diff --git a/spawner.lua b/spawner.lua index ca75dee..16e0d11 100644 --- a/spawner.lua +++ b/spawner.lua @@ -1,7 +1,5 @@ --- intllib -local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP .. "/intllib.lua") +local S = mobs.intllib -- mob spawner From 7179dc86e6005093e87b065aedd97e44d0aa1275 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 28 May 2019 09:35:37 +0100 Subject: [PATCH 017/135] add mob check for mob_reset_stick --- crafts.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crafts.lua b/crafts.lua index b5be3cb..6512d6d 100644 --- a/crafts.lua +++ b/crafts.lua @@ -242,6 +242,7 @@ minetest.register_tool(":mobs:mob_reset_stick", { local control = user:get_player_control() local sneak = control and control.sneak + -- spawn same mob with saved stats, with random texture if obj and not sneak then local self = obj:get_luaentity() @@ -269,6 +270,7 @@ minetest.register_tool(":mobs:mob_reset_stick", { end end + -- display form to enter texture name ending in .png if obj and sneak then tex_obj = obj @@ -294,7 +296,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) local name = player:get_player_name() - if not tex_obj then + -- does mob still exist? + if not tex_obj + or not tex_obj:get_luaentity() then return end From f040ee5bc7ca33a963fd58687cddadafa1e95eb6 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 15 Jun 2019 20:37:16 +0100 Subject: [PATCH 018/135] fix nil value when mob not available --- mount.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mount.lua b/mount.lua index d1f5841..a82dfee 100644 --- a/mount.lua +++ b/mount.lua @@ -212,7 +212,8 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) end -- fix mob rotation - entity.object:set_yaw(entity.driver:get_look_horizontal() - entity.rotate) + local horz = entity.driver:get_look_horizontal() or 0 + entity.object:set_yaw(horz - entity.rotate) if can_fly then @@ -259,7 +260,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) return end - + -- set moving animation if moving_anim then mobs:set_animation(entity, moving_anim) From 06dbdb1c44a20e1cedaaa6bcd5ad1b219b2efadb Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 28 Jun 2019 20:33:23 +0100 Subject: [PATCH 019/135] disabled the new raycasting line of sight due to mob attack issues --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 24d89f8..816505b 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190520", + version = "20190628", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -474,7 +474,7 @@ local ray_line_of_sight = function(self, pos1, pos2) end -- detect if using minetest 5.0 by searching for permafrost node -local is_50 = minetest.registered_nodes["default:permafrost"] +local is_50 = nil -- minetest.registered_nodes["default:permafrost"] function mob_class:line_of_sight(pos1, pos2, stepsize) From 1a85cf7e2c3a6a6ff4619dcc9f0a83dbe16edc83 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 30 Jun 2019 10:35:34 +0100 Subject: [PATCH 020/135] mobs cannot spawn within 8 blocks of player, update api.txt --- api.lua | 4 ++-- api.txt | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/api.lua b/api.lua index 816505b..449a925 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190628", + version = "20190630", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -3509,7 +3509,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, end -- only spawn away from player - local objs = minetest.get_objects_inside_radius(pos, 10) + local objs = minetest.get_objects_inside_radius(pos, 8) for n = 1, #objs do diff --git a/api.txt b/api.txt index f45b818..9f1888b 100644 --- a/api.txt +++ b/api.txt @@ -624,8 +624,14 @@ 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 +mobs_animal:sheep 11000 +mobs_monster:sand_monster 100 + +...you can also change how many of a certain mob appear in an active mapblock by +adding a comma and then a new value e.g. + +mobs_animal:cow 8000,4 <-- 4 cows per mapblock at 8000 spawn chance +mobs_monster:dirt_monster ,20 <-- 20 dirt monsters per mapblock Rideable Horse Example Mob From 26cc611ad4ed63b2d7de755891c359f3cbf9f882 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 30 Jun 2019 12:47:28 +0100 Subject: [PATCH 021/135] dont spawn mobs if world anchor found nearby --- api.lua | 11 +++++++++++ readme.MD | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 449a925..c2344bc 100644 --- a/api.lua +++ b/api.lua @@ -3404,6 +3404,8 @@ function mobs:spawn_abm_check(pos, node, name) -- return true to stop spawning mob end +local mod_technic = minetest.get_modpath("technic") or + minetest.get_modpath("simple_anchor") function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn) @@ -3508,6 +3510,15 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, return end +-- do not spawn if world anchor block is nearby +if mod_technic +and #minetest.find_nodes_in_area( + {x = pos.x - 32, y = pos.y - 32, z = pos.z - 32}, + {x = pos.x + 32, y = pos.y + 32, z = pos.z + 32}, + {"technic:admin_anchor", "simple_anchor:loader_block"}) > 0 then + return +end + -- only spawn away from player local objs = minetest.get_objects_inside_radius(pos, 8) diff --git a/readme.MD b/readme.MD index 3d5ff04..27d552f 100644 --- a/readme.MD +++ b/readme.MD @@ -23,7 +23,7 @@ Lucky Blocks: 9 Changelog: -- 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found (thanks Astrobe) +- 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found (thanks Astrobe), dont spawn mobs if world anchor nearby (technic or simple_anchor mods) - 1.49- Added mobs:force_capture(self, player) function, api functions now use metatables thanks to bell07 - 1.48- Add mobs:set_velocity(self, velocity) global function - 1.47- Mob damage changes, min and max light level for damage added, ignition sources checked for lava damage From 38232d5ef450cb909a4e5af57a64edf7861f4284 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 30 Jun 2019 19:10:37 +0100 Subject: [PATCH 022/135] only spawn mob if player is within active area (useful for world anchors and force loaded areas) --- api.lua | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/api.lua b/api.lua index c2344bc..3004774 100644 --- a/api.lua +++ b/api.lua @@ -3374,11 +3374,13 @@ end -- END mobs:register_mob function -- count how many mobs of one type are inside an area +-- will also return true for second value if player is inside area local count_mobs = function(pos, type) local total = 0 local objs = minetest.get_objects_inside_radius(pos, aoc_range * 2) local ent + local players for n = 1, #objs do @@ -3390,10 +3392,12 @@ local count_mobs = function(pos, type) if ent and ent.name and ent.name == type then total = total + 1 end + else + players = true end end - return total + return total, players end @@ -3404,8 +3408,6 @@ function mobs:spawn_abm_check(pos, node, name) -- return true to stop spawning mob end -local mod_technic = minetest.get_modpath("technic") or - minetest.get_modpath("simple_anchor") function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn) @@ -3464,7 +3466,12 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, end -- get total number of this mob in area - local num_mob = count_mobs(pos, name) + local num_mob, is_pla = count_mobs(pos, name) + + if not is_pla then +--print ("--- no players within active area, will not spawn " .. name) + return + end if num_mob >= aoc then --print ("--- too many " .. name .. " in area", num_mob .. "/" .. aoc) @@ -3510,15 +3517,6 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, return end --- do not spawn if world anchor block is nearby -if mod_technic -and #minetest.find_nodes_in_area( - {x = pos.x - 32, y = pos.y - 32, z = pos.z - 32}, - {x = pos.x + 32, y = pos.y + 32, z = pos.z + 32}, - {"technic:admin_anchor", "simple_anchor:loader_block"}) > 0 then - return -end - -- only spawn away from player local objs = minetest.get_objects_inside_radius(pos, 8) From c7838e551774235f1998c52b18ebe9955b9e7316 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 5 Aug 2019 09:55:12 +0100 Subject: [PATCH 023/135] code tidy ' to " --- crafts.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crafts.lua b/crafts.lua index 6512d6d..16f2f9d 100644 --- a/crafts.lua +++ b/crafts.lua @@ -92,10 +92,10 @@ minetest.register_tool("mobs:shears", { }) minetest.register_craft({ - output = 'mobs:shears', + output = "mobs:shears", recipe = { - {'', 'default:steel_ingot', ''}, - {'', 'group:stick', 'default:steel_ingot'}, + {"", "default:steel_ingot", ""}, + {"", "group:stick", "default:steel_ingot"}, } }) From 2ca012e30b4a950c96b92422a4fcc5906c77010a Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 13 Aug 2019 08:29:11 +0100 Subject: [PATCH 024/135] replace deprecated settexturemod with set_texture_mod --- api.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index 3004774..6f837b2 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190630", + version = "20190813", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -2235,9 +2235,9 @@ function mob_class:do_states(dtime) self.blinktimer = 0 if self.blinkstatus then - self.object:settexturemod("") + self.object:set_texture_mod("") else - self.object:settexturemod("^[brighten") + self.object:set_texture_mod("^[brighten") end self.blinkstatus = not self.blinkstatus From d6bd538a3511bd2a1c16d41708b9e10b2027f2fd Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 23 Aug 2019 08:25:06 +0100 Subject: [PATCH 025/135] if mob standing in solid block then jump to escape --- api.lua | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 6f837b2..45432df 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190813", + version = "20190823", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {}, } @@ -3128,6 +3128,17 @@ function mob_class:on_step(dtime) x = pos.x, y = pos.y + y_level + 0.25, z = pos.z}, "air").name -- print ("standing in " .. self.standing_in) + -- if standing inside solid block then jump to escape + if minetest.registered_nodes[self.standing_in].walkable and + minetest.registered_nodes[self.standing_in].drawtype == "normal" then + + self.object:set_velocity({ + x = 0, + y = self.jump_height, + z = 0 + }) + end + -- check for mob expiration (0.25 instead of dtime since were in a timer) self:mob_expire(pos, 0.25) end From b588452deace98696c0b2e03826a554a69a47818 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 12 Sep 2019 09:44:45 +0100 Subject: [PATCH 026/135] drops can now be a function, also code tidy --- api.lua | 62 +++++++++++++++----------------- api.txt | 5 +++ crafts.lua | 94 ++++++++++++++++++++++++------------------------- lucky_block.lua | 2 +- mount.lua | 1 - spawner.lua | 3 +- 6 files changed, 82 insertions(+), 85 deletions(-) diff --git a/api.lua b/api.lua index 45432df..31ea75e 100644 --- a/api.lua +++ b/api.lua @@ -6,9 +6,9 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190823", + version = "20190912", intllib = S, - invis = minetest.global_exists("invisibility") and invisibility or {}, + invis = minetest.global_exists("invisibility") and invisibility or {} } -- creative check @@ -79,11 +79,11 @@ local node_snow = "default:snow" mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "default:dirt" local mob_class = { - stepheight = 1.1, -- was 0.6 + stepheight = 1.1, fly_in = "air", owner = "", order = "", - jump_height = 4, -- was 6 + jump_height = 4, lifetimer = 180, -- 3 minutes physical = true, collisionbox = {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}, @@ -142,8 +142,9 @@ local mob_class = { attack_players = true, attack_npcs = true, facing_fence = false, - _cmi_is_mob = true, + _cmi_is_mob = true } + local mob_class_meta = {__index = mob_class} -- play sound @@ -232,7 +233,7 @@ function mob_class:set_velocity(v) self.object:set_velocity({ x = (sin(yaw) * -v) + c_x, y = self.object:get_velocity().y, - z = (cos(yaw) * v) + c_y, + z = (cos(yaw) * v) + c_y }) end @@ -285,7 +286,7 @@ function mob_class:set_animation(anim, force) self.animation.current = self.animation.current or "" - -- only set different animation for attacks when using same set + -- only use different animation for attacks when using same set if force ~= true and anim ~= "punch" and anim ~= "shoot" and string.find(self.animation.current, anim) then return @@ -393,7 +394,6 @@ local line_of_sight = function(self, pos1, pos2, stepsize) -- New Nodename found nn = minetest.get_node(pos).name - end return false @@ -528,7 +528,7 @@ function mob_class:flight_check() local def = minetest.registered_nodes[self.standing_in] - if not def then return false end -- nil check + if not def then return false end if type(self.fly_in) == "string" and self.standing_in == self.fly_in then @@ -631,7 +631,7 @@ local effect = function(pos, amount, texture, min_size, max_size, radius, gravit minsize = min_size, maxsize = max_size, texture = texture, - glow = glow, + glow = glow }) end @@ -664,6 +664,11 @@ end -- drop items function mob_class:item_drop() + local pos = self.object:get_pos() + + -- check for drops function + self.drops = type(self.drops) == "function" and self.drops(pos) or self.drops + -- check for nil or no drops if not self.drops or #self.drops == 0 then return @@ -680,7 +685,6 @@ function mob_class:item_drop() and self.cause_of_death.puncher:is_player() or nil local obj, item, num - local pos = self.object:get_pos() for n = 1, #self.drops do @@ -713,7 +717,7 @@ function mob_class:item_drop() obj:set_velocity({ x = random(-10, 10) / 9, y = 6, - z = random(-10, 10) / 9, + z = random(-10, 10) / 9 }) elseif obj then @@ -842,8 +846,7 @@ function mob_class:is_at_cliff() if minetest.line_of_sight( {x = pos.x + dir_x, y = ypos, z = pos.z + dir_z}, - {x = pos.x + dir_x, y = ypos - self.fear_height, z = pos.z + dir_z} - , 1) then + {x = pos.x + dir_x, y = ypos - self.fear_height, z = pos.z + dir_z}, 1) then return true end @@ -1044,9 +1047,9 @@ function mob_class:do_jump() if self.object:get_luaentity() then self.object:set_acceleration({ - x = v.x * 2,--1.5, + x = v.x * 2, y = 0, - z = v.z * 2,--1.5 + z = v.z * 2 }) end end, self, v) @@ -1157,7 +1160,7 @@ function mob_class:breed() mesh = self.base_mesh, visual_size = self.base_size, collisionbox = self.base_colbox, - selectionbox = self.base_selbox, + selectionbox = self.base_selbox }) -- custom function when child grows up @@ -1272,7 +1275,7 @@ function mob_class:breed() textures = textures, visual_size = { x = self.base_size.x * .5, - y = self.base_size.y * .5, + y = self.base_size.y * .5 }, collisionbox = { self.base_colbox[1] * .5, @@ -1280,7 +1283,7 @@ function mob_class:breed() self.base_colbox[3] * .5, self.base_colbox[4] * .5, self.base_colbox[5] * .5, - self.base_colbox[6] * .5, + self.base_colbox[6] * .5 }, selectionbox = { self.base_selbox[1] * .5, @@ -1288,7 +1291,7 @@ function mob_class:breed() self.base_selbox[3] * .5, self.base_selbox[4] * .5, self.base_selbox[5] * .5, - self.base_selbox[6] * .5, + self.base_selbox[6] * .5 }, }) -- tamed and owned by parents' owner @@ -1646,7 +1649,7 @@ local specific_attack = function(list, what) end --- general attack function for all mobs ========== +-- general attack function for all mobs function mob_class:general_attack() -- return if already attacking, passive or docile during day @@ -2264,7 +2267,7 @@ function mob_class:do_states(dtime) tnt.boom(pos, { radius = node_break_radius, damage_radius = entity_damage_radius, - sound = self.sounds.explode, + sound = self.sounds.explode }) else @@ -2415,13 +2418,7 @@ function mob_class:do_states(dtime) if self.timer > 1 then self.timer = 0 - --- if self.double_melee_attack --- and random(1, 2) == 1 then --- self:set_animation("punch2") --- else - self:set_animation("punch") --- end + self:set_animation("punch") local p2 = p local s2 = s @@ -3606,8 +3603,7 @@ function mobs:spawn(def) def.min_height or -31000, def.max_height or 31000, def.day_toggle, - def.on_spawn - ) + def.on_spawn) end @@ -3667,7 +3663,7 @@ function mobs:register_arrow(name, def) collisiondetection = false, texture = def.tail_texture, size = def.tail_size or 5, - glow = def.glow or 0, + glow = def.glow or 0 }) end @@ -4191,8 +4187,6 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) local tag = self.nametag or "" minetest.show_formspec(name, "mobs_nametag", "size[8,4]" - .. default.gui_bg - .. default.gui_bg_img .. "field[0.5,1;7.5,0;name;" .. minetest.formspec_escape(S("Enter name:")) .. ";" .. tag .. "]" .. "button_exit[2.5,3.5;3,1;mob_rename;" diff --git a/api.txt b/api.txt index 9f1888b..7b84ca6 100644 --- a/api.txt +++ b/api.txt @@ -158,6 +158,11 @@ functions needed for the mob to work properly which contains the following: 'min' minimum number of items dropped, set to 0 for rare drops. 'max' maximum number of items dropped. Note: If weapon has {fire=1} damage group set then cooked items will drop. + Note2: A function can now be passed which can also return drops table, e.g. + drops = function(pos) + -- do something + return { {name = "farming:bread"}, {name = "default:dirt", chance = 2} } + end 'visual' holds the look of the mob you wish to create: 'cube' looks like a normal node diff --git a/crafts.lua b/crafts.lua index 16f2f9d..7806ca8 100644 --- a/crafts.lua +++ b/crafts.lua @@ -5,14 +5,14 @@ local S = mobs.intllib minetest.register_craftitem("mobs:nametag", { description = S("Name Tag"), inventory_image = "mobs_nametag.png", - groups = {flammable = 2}, + groups = {flammable = 2} }) if minetest.get_modpath("dye") and minetest.get_modpath("farming") then minetest.register_craft({ type = "shapeless", output = "mobs:nametag", - recipe = {"default:paper", "dye:black", "farming:string"}, + recipe = {"default:paper", "dye:black", "farming:string"} }) end @@ -20,7 +20,7 @@ end minetest.register_craftitem("mobs:leather", { description = S("Leather"), inventory_image = "mobs_leather.png", - groups = {flammable = 2}, + groups = {flammable = 2} }) -- raw meat @@ -28,7 +28,7 @@ minetest.register_craftitem("mobs:meat_raw", { description = S("Raw Meat"), inventory_image = "mobs_meat_raw.png", on_use = minetest.item_eat(3), - groups = {food_meat_raw = 1, flammable = 2}, + groups = {food_meat_raw = 1, flammable = 2} }) -- cooked meat @@ -36,21 +36,21 @@ minetest.register_craftitem("mobs:meat", { description = S("Meat"), inventory_image = "mobs_meat.png", on_use = minetest.item_eat(8), - groups = {food_meat = 1, flammable = 2}, + groups = {food_meat = 1, flammable = 2} }) minetest.register_craft({ type = "cooking", output = "mobs:meat", recipe = "mobs:meat_raw", - cooktime = 5, + cooktime = 5 }) -- lasso minetest.register_tool("mobs:lasso", { description = S("Lasso (right-click animal to put in inventory)"), inventory_image = "mobs_magic_lasso.png", - groups = {flammable = 2}, + groups = {flammable = 2} }) if minetest.get_modpath("farming") then @@ -59,7 +59,7 @@ if minetest.get_modpath("farming") then recipe = { {"farming:string", "", "farming:string"}, {"", "default:diamond", ""}, - {"farming:string", "", "farming:string"}, + {"farming:string", "", "farming:string"} } }) end @@ -70,7 +70,7 @@ minetest.register_alias("mobs:magic_lasso", "mobs:lasso") minetest.register_tool("mobs:net", { description = S("Net (right-click animal to put in inventory)"), inventory_image = "mobs_net.png", - groups = {flammable = 2}, + groups = {flammable = 2} }) if minetest.get_modpath("farming") then @@ -79,7 +79,7 @@ if minetest.get_modpath("farming") then recipe = { {"group:stick", "", "group:stick"}, {"group:stick", "", "group:stick"}, - {"farming:string", "group:stick", "farming:string"}, + {"farming:string", "group:stick", "farming:string"} } }) end @@ -88,14 +88,14 @@ end minetest.register_tool("mobs:shears", { description = S("Steel Shears (right-click to shear)"), inventory_image = "mobs_shears.png", - groups = {flammable = 2}, + groups = {flammable = 2} }) minetest.register_craft({ output = "mobs:shears", recipe = { {"", "default:steel_ingot", ""}, - {"", "group:stick", "default:steel_ingot"}, + {"", "group:stick", "default:steel_ingot"} } }) @@ -103,7 +103,7 @@ minetest.register_craft({ minetest.register_craftitem("mobs:protector", { description = S("Mob Protection Rune"), inventory_image = "mobs_protector.png", - groups = {flammable = 2}, + groups = {flammable = 2} }) minetest.register_craft({ @@ -111,7 +111,7 @@ minetest.register_craft({ recipe = { {"default:stone", "default:stone", "default:stone"}, {"default:stone", "default:goldblock", "default:stone"}, - {"default:stone", "default:stone", "default:stone"}, + {"default:stone", "default:stone", "default:stone"} } }) @@ -119,7 +119,7 @@ minetest.register_craft({ minetest.register_craftitem("mobs:saddle", { description = S("Saddle"), inventory_image = "mobs_saddle.png", - groups = {flammable = 2}, + groups = {flammable = 2} }) minetest.register_craft({ @@ -127,7 +127,7 @@ minetest.register_craft({ recipe = { {"mobs:leather", "mobs:leather", "mobs:leather"}, {"mobs:leather", "default:steel_ingot", "mobs:leather"}, - {"mobs:leather", "default:steel_ingot", "mobs:leather"}, + {"mobs:leather", "default:steel_ingot", "mobs:leather"} } }) @@ -142,38 +142,38 @@ default.register_fence("mobs:fence_wood", { type = "fixed", fixed = { {-0.5, -0.5, -0.5, 0.5, 1.9, 0.5}, - }, - }, + } + } }) -- mob fence top (has enlarged collisionbox to stop mobs getting over) - minetest.register_node("mobs:fence_top", { - description = S("Mob Fence Top"), - drawtype = "nodebox", - tiles = {"default_wood.png"}, - paramtype = "light", - is_ground_content = false, - groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2}, - sounds = default.node_sound_wood_defaults(), - node_box = { - type = "fixed", - fixed = {-0.2, -0.5, -0.2, 0.2, 0, 0.2}, - }, - collision_box = { - type = "fixed", - fixed = {-0.4, -1.5, -0.4, 0.4, 0, 0.4}, - }, - selection_box = { - type = "fixed", - fixed = {-0.4, -1.5, -0.4, 0.4, 0, 0.4}, - }, +minetest.register_node("mobs:fence_top", { + description = S("Mob Fence Top"), + drawtype = "nodebox", + tiles = {"default_wood.png"}, + paramtype = "light", + is_ground_content = false, + groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2}, + sounds = default.node_sound_wood_defaults(), + node_box = { + type = "fixed", + fixed = {-0.2, -0.5, -0.2, 0.2, 0, 0.2} + }, + collision_box = { + type = "fixed", + fixed = {-0.4, -1.5, -0.4, 0.4, 0, 0.4} + }, + selection_box = { + type = "fixed", + fixed = {-0.4, -1.5, -0.4, 0.4, 0, 0.4} + } }) minetest.register_craft({ output = "mobs:fence_top 12", recipe = { {"group:wood", "group:wood", "group:wood"}, - {"", "default:fence_wood", ""}, + {"", "default:fence_wood", ""} } }) @@ -181,43 +181,43 @@ minetest.register_craft({ minetest.register_craft({ type = "fuel", recipe = "mobs:nametag", - burntime = 3, + burntime = 3 }) minetest.register_craft({ type = "fuel", recipe = "mobs:lasso", - burntime = 7, + burntime = 7 }) minetest.register_craft({ type = "fuel", recipe = "mobs:net", - burntime = 8, + burntime = 8 }) minetest.register_craft({ type = "fuel", recipe = "mobs:leather", - burntime = 4, + burntime = 4 }) minetest.register_craft({ type = "fuel", recipe = "mobs:saddle", - burntime = 7, + burntime = 7 }) minetest.register_craft({ type = "fuel", recipe = "mobs:fence_wood", - burntime = 7, + burntime = 7 }) minetest.register_craft({ type = "fuel", recipe = "mobs:fence_top", - burntime = 2, + burntime = 2 }) -- this tool spawns same mob and adds owner, protected, nametag info @@ -284,7 +284,7 @@ minetest.register_tool(":mobs:mob_reset_stick", { .. "button_exit[2.5,3.5;3,1;mob_texture_change;" .. minetest.formspec_escape(S("Change")) .. "]") end - end, + end }) minetest.register_on_player_receive_fields(function(player, formname, fields) diff --git a/lucky_block.lua b/lucky_block.lua index ef7d59c..b823e83 100644 --- a/lucky_block.lua +++ b/lucky_block.lua @@ -13,6 +13,6 @@ if minetest.get_modpath("lucky_block") then {"dro", {"mobs:protector"}, 1}, {"dro", {"mobs:fence_wood"}, 10}, {"dro", {"mobs:fence_top"}, 12}, - {"lig"}, + {"lig"} }) end diff --git a/mount.lua b/mount.lua index a82dfee..29b80ed 100644 --- a/mount.lua +++ b/mount.lua @@ -247,7 +247,6 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) acce_y = acce_y + (acce_y * 3) + 1 end end - end end diff --git a/spawner.lua b/spawner.lua index 16e0d11..dfe20f2 100644 --- a/spawner.lua +++ b/spawner.lua @@ -68,7 +68,7 @@ minetest.register_node("mobs:spawner", { minetest.chat_send_player(name, S("Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] distance[1-20] y_offset[-10 to 10]”")) end - end, + end }) @@ -174,6 +174,5 @@ minetest.register_abm({ minetest.add_entity(pos2, mob) end end - end }) From b9ad166821a2a89f43ef0da68d655db3d3aa74dc Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 16 Nov 2019 12:52:35 +0000 Subject: [PATCH 027/135] check cliff drop every 1/4 second instead of 1 second --- api.lua | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/api.lua b/api.lua index 31ea75e..2063645 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20190912", + version = "20191116", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -586,11 +586,7 @@ function mob_class:do_stay_near() local target = nearby_nodes[math.random(1, #nearby_nodes)] local direction = vector.direction(pos, target) - - local vec = { - x = target.x - pos.x, - z = target.z - pos.z - } + local vec = {x = target.x - pos.x, z = target.z - pos.z} yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate @@ -2026,7 +2022,7 @@ function mob_class:do_states(dtime) and self.walk_chance ~= 0 and self.facing_fence ~= true and random(1, 100) <= self.walk_chance - and self:is_at_cliff() == false then + and self.at_cliff == false then self:set_velocity(self.walk_velocity) self.state = "walk" @@ -2107,10 +2103,8 @@ function mob_class:do_states(dtime) end -- stand for great fall in front - local temp_is_cliff = self:is_at_cliff() - if self.facing_fence == true - or temp_is_cliff + or self.at_cliff or random(1, 100) <= self.stand_chance then self:set_velocity(0) @@ -2136,7 +2130,7 @@ function mob_class:do_states(dtime) -- stop after 5 seconds or when at cliff if self.runaway_timer > 5 - or self:is_at_cliff() + or self.at_cliff or self.order == "stand" then self.runaway_timer = 0 self:set_velocity(0) @@ -2386,7 +2380,7 @@ function mob_class:do_states(dtime) self:smart_mobs(s, p, dist, dtime) end - if self:is_at_cliff() then + if self.at_cliff then self:set_velocity(0) self:set_animation("stand") @@ -3136,6 +3130,13 @@ function mob_class:on_step(dtime) }) end + -- check and stop if standing at cliff and fear of heights + self.at_cliff = self:is_at_cliff() + + if self.at_cliff then + self:set_velocity(0) + end + -- check for mob expiration (0.25 instead of dtime since were in a timer) self:mob_expire(pos, 0.25) end @@ -4247,6 +4248,11 @@ end) -- compatibility function for old entities to new modpack entities function mobs:alias_mob(old_name, new_name) + -- check old_name entity doesnt already exist + if minetest.registered_entities[old_name] then + return + end + -- spawn egg minetest.register_alias(old_name, new_name) From 5958270158b649d684f6450d8684d030cb17f9e8 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 9 Jan 2020 09:38:46 +0000 Subject: [PATCH 028/135] add glow setting to mob entity, update api.txt --- api.lua | 3 ++- api.txt | 59 ++++++++++++++++++++++++++++++--------------------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/api.lua b/api.lua index 2063645..b069225 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20191116", + version = "20200109", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -3290,6 +3290,7 @@ minetest.register_entity(name, setmetatable({ jump_height = def.jump_height, drawtype = def.drawtype, -- DEPRECATED, use rotate instead rotate = math.rad(def.rotate or 0), -- 0=front, 90=side, 180=back, 270=side2 + glow = def.glow, lifetimer = def.lifetimer, hp_min = max(1, (def.hp_min or 5) * difficulty), hp_max = max(1, (def.hp_max or 10) * difficulty), diff --git a/api.txt b/api.txt index 7b84ca6..10ed372 100644 --- a/api.txt +++ b/api.txt @@ -186,6 +186,7 @@ functions needed for the mob to work properly which contains the following: self.gotten is true for mobs. 'rotate' custom model rotation, 0 = front, 90 = side, 180 = back, 270 = other side. + 'glow' has mob glow without light source, 0 to 15 or nil to disable 'double_melee_attack' when true has the api choose between 'punch' and 'punch2' animations. [DEPRECATED] @@ -321,43 +322,45 @@ for each mob. Spawning Mobs in World ---------------------- + mobs:spawn({ + name = "mobs_monster:tree_monster", + nodes = {"group:leaves"}, + max_light = 7, + }) + +Spawn functions require the following settings, some of which already have a +default setting and can be omitted: + + 'name' is the name of the animal/monster + 'nodes' is a list of nodenames on that the animal/monster can + spawn on top of (defaults to {"group:dirt", "group:stone"} + 'neighbors' is a list of nodenames on that the animal/monster will + spawn beside (default is {"air"}) + 'interval' is same as in register_abm() (default is 30) + 'chance' is same as in register_abm() (default is 5000) + 'min_light' is the minimum light level (default is 0) + 'max_light' is the maximum light (default is 15) + 'min_height' is the minimum height a mob can spawn (default: -31000) + 'max_height' is the maximum height a mob can spawn (default is 31000) + 'active_object_count' number of this type of mob to spawn at one time inside + map area (default is 1) + '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. + +The older spawn functions are still active and working but have no defaults like +the mobs:spawn, so it is recommended to use the above instead. + 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 From c5b74230e5588561eddbf029ed1261c1a3e7c91c Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 12 Jan 2020 09:42:28 +0000 Subject: [PATCH 029/135] on_replace returns node names, even when groups used --- api.lua | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/api.lua b/api.lua index b069225..99590df 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200109", + version = "20200112", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -1338,17 +1338,22 @@ function mob_class:replace(pos) -- print ("replace node = ".. minetest.get_node(pos).name, pos.y) - local oldnode = {name = what} - local newnode = {name = with} - local on_replace_return - if self.on_replace then - on_replace_return = self:on_replace(pos, oldnode, newnode) + + local oldnode = what + local newnode = with + + -- convert any group: replacements to actual node name + if oldnode:find("group:") then + oldnode = minetest.get_node(pos).name + end + + if self:on_replace(pos, oldnode, newnode) == false then + return + end end - if on_replace_return ~= false then - minetest.set_node(pos, {name = with}) - end + minetest.set_node(pos, {name = with}) end end From 92ab4eeb2e362886bb6dcfbe547107aff40e4e28 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 23 Jan 2020 09:27:27 +0000 Subject: [PATCH 030/135] mob alias now transfers staticdata (thanks MoNTE48) --- api.lua | 10 +++++++--- api.txt | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/api.lua b/api.lua index 99590df..ebbdbc5 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200112", + version = "20200123", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -4267,13 +4267,17 @@ function mobs:alias_mob(old_name, new_name) physical = false, - on_activate = function(self) + on_activate = function(self, staticdata) if minetest.registered_entities[new_name] then - minetest.add_entity(self.object:get_pos(), new_name) + minetest.add_entity(self.object:get_pos(), new_name, staticdata) end self.object:remove() + end, + + get_staticdata = function(self) + return self end }) end diff --git a/api.txt b/api.txt index 10ed372..61788d0 100644 --- a/api.txt +++ b/api.txt @@ -750,7 +750,7 @@ mobs:register_mob("mob_horse:horse", { if inv:room_for_item("main", "mobs:saddle") then inv:add_item("main", "mobs:saddle") else - minetest.add_item(clicker.getpos(), "mobs:saddle") + minetest.add_item(clicker.get_pos(), "mobs:saddle") end -- attach player to horse From 68fef94ba84959f2619717e1d9070ec8ca381a8b Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 31 Jan 2020 20:24:38 +0000 Subject: [PATCH 031/135] updated api.txt --- api.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api.txt b/api.txt index 61788d0..5361a8c 100644 --- a/api.txt +++ b/api.txt @@ -420,7 +420,8 @@ 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. +This function registers a spawn egg which can be used to properly spawn in a mob. +Animals are spawned as tamed unless sneak/shift is held while spawning. '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" From 813909c86cbddc2f07f03bf3903de13cd9c51395 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Fri, 7 Feb 2020 12:43:18 +0000 Subject: [PATCH 032/135] add hard limit of 10 to mob velocity functions --- api.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api.lua b/api.lua index ebbdbc5..2ba4395 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200123", + version = "20200207", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -230,10 +230,11 @@ function mob_class:set_velocity(v) local yaw = (self.object:get_yaw() or 0) + self.rotate + -- set velocity with hard limit of 10 self.object:set_velocity({ - x = (sin(yaw) * -v) + c_x, - y = self.object:get_velocity().y, - z = (cos(yaw) * v) + c_y + x = max(-10, min((sin(yaw) * -v) + c_x, 10)), + y = max(-10, min(self.object:get_velocity().y, 10)), + z = max(-10, min((cos(yaw) * v) + c_y, 10)) }) end From 9c00616a0077aba8313d1c9d8a755b92ff908fc6 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 14 Feb 2020 11:23:20 +0000 Subject: [PATCH 033/135] chinese local added (thanks IFRFSX) --- locale/zh_CN.po | 130 ++++++++++++++++++++++++++++++++++++++++++++++++ locale/zh_TW.po | 130 ++++++++++++++++++++++++++++++++++++++++++++++++ readme.MD | 2 +- 3 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 locale/zh_CN.po create mode 100644 locale/zh_TW.po diff --git a/locale/zh_CN.po b/locale/zh_CN.po new file mode 100644 index 0000000..bd8ffa0 --- /dev/null +++ b/locale/zh_CN.po @@ -0,0 +1,130 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# IFRFSX<1079092922@qq.com>, 2020. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-02 16:48+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: api.lua +msgid "** Peaceful Mode Active - No Monsters Will Spawn" +msgstr "** 和平模式已激活——没有怪物会产生" + +#: api.lua +msgid "Mob has been protected!" +msgstr "Mob 已经被保护了!" + +#: api.lua +msgid "@1 (Tamed)" +msgstr "@1(已驯服)" + +#: api.lua +msgid "Not tamed!" +msgstr "没有驯服!" + +#: api.lua +msgid "@1 is owner!" +msgstr "@1 是主人" + +#: api.lua +msgid "Missed!" +msgstr "没抓住!" + +#: api.lua +msgid "Already protected!" +msgstr "已经被保护!" + +#: api.lua +msgid "@1 at full health (@2)" +msgstr "@1已经满血(@2)" + +#: api.lua +msgid "@1 has been tamed!" +msgstr "@1已经被驯服!" + +#: api.lua +msgid "Enter name:" +msgstr "输入名称:" + +#: api.lua +msgid "Rename" +msgstr "重新命名" + +#: crafts.lua +msgid "Name Tag" +msgstr "名称标签" + +#: crafts.lua +msgid "Leather" +msgstr "皮革" + +#: crafts.lua +msgid "Raw Meat" +msgstr "生肉" + +#: crafts.lua +msgid "Meat" +msgstr "肉" + +#: crafts.lua +msgid "Lasso (right-click animal to put in inventory)" +msgstr "套索(右键单击动物以放入物品栏)" + +#: crafts.lua +msgid "Net (right-click animal to put in inventory)" +msgstr "网(右键单击动物以放入物品栏)" + +#: crafts.lua +msgid "Steel Shears (right-click to shear)" +msgstr "钢剪(右键单击以剪切)" + +#: crafts.lua +msgid "Mob Protection Rune" +msgstr "Mob 保护符文" + +#: crafts.lua +msgid "Saddle" +msgstr "鞍" + +#: crafts.lua +msgid "Mob Fence" +msgstr "Mob 栅栏" + +#: spawner.lua +msgid "Mob Spawner" +msgstr "Mob 孵化器" + +#: spawner.lua +msgid "Mob MinLight MaxLight Amount PlayerDist" +msgstr "Mob/最小光量/最大光量/玩家距离" + +#: spawner.lua +msgid "Spawner Not Active (enter settings)" +msgstr "孵化器未使用(输入设置)" + +#: spawner.lua +msgid "Spawner Active (@1)" +msgstr "孵化器正在运转(@1)" + +#: spawner.lua +msgid "Mob Spawner settings failed!" +msgstr "Mob 孵化器设置失败!" + +#: spawner.lua +msgid "" +"Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] " +"distance[1-20] y_offset[-10 to 10]”" +msgstr "" +"语法: “物品名称 最小光亮[0-14] 最大光亮[0-14] 范围内的最大Mob数量[0 to disable] " +"距离[1-20] y_offset[-10 to 10]”" diff --git a/locale/zh_TW.po b/locale/zh_TW.po new file mode 100644 index 0000000..6350a63 --- /dev/null +++ b/locale/zh_TW.po @@ -0,0 +1,130 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# IFRFSX<1079092922@qq.com>, 2020. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-02 16:48+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: api.lua +msgid "** Peaceful Mode Active - No Monsters Will Spawn" +msgstr "** 和平模式已激活——沒有怪物會產生" + +#: api.lua +msgid "Mob has been protected!" +msgstr "Mob 已經被保護了!" + +#: api.lua +msgid "@1 (Tamed)" +msgstr "@1(已馴服)" + +#: api.lua +msgid "Not tamed!" +msgstr "沒有馴服!" + +#: api.lua +msgid "@1 is owner!" +msgstr "@1 是主人" + +#: api.lua +msgid "Missed!" +msgstr "沒抓住!" + +#: api.lua +msgid "Already protected!" +msgstr "已經被保護!" + +#: api.lua +msgid "@1 at full health (@2)" +msgstr "@1已經滿血(@2)" + +#: api.lua +msgid "@1 has been tamed!" +msgstr "@1已經被馴服!" + +#: api.lua +msgid "Enter name:" +msgstr "輸入名稱:" + +#: api.lua +msgid "Rename" +msgstr "重新命名" + +#: crafts.lua +msgid "Name Tag" +msgstr "名稱標籤" + +#: crafts.lua +msgid "Leather" +msgstr "皮革" + +#: crafts.lua +msgid "Raw Meat" +msgstr "生肉" + +#: crafts.lua +msgid "Meat" +msgstr "肉" + +#: crafts.lua +msgid "Lasso (right-click animal to put in inventory)" +msgstr "套索(右鍵單擊動物以放入物品欄)" + +#: crafts.lua +msgid "Net (right-click animal to put in inventory)" +msgstr "網(右鍵單擊動物以放入物品欄)" + +#: crafts.lua +msgid "Steel Shears (right-click to shear)" +msgstr "鋼剪(右鍵單擊以剪切)" + +#: crafts.lua +msgid "Mob Protection Rune" +msgstr "Mob 保護符文" + +#: crafts.lua +msgid "Saddle" +msgstr "鞍" + +#: crafts.lua +msgid "Mob Fence" +msgstr "Mob 柵欄" + +#: spawner.lua +msgid "Mob Spawner" +msgstr "Mob 孵化器" + +#: spawner.lua +msgid "Mob MinLight MaxLight Amount PlayerDist" +msgstr "Mob/最小光量/最大光量/玩家距離" + +#: spawner.lua +msgid "Spawner Not Active (enter settings)" +msgstr "孵化器未使用(輸入設置)" + +#: spawner.lua +msgid "Spawner Active (@1)" +msgstr "孵化器正在運轉(@1)" + +#: spawner.lua +msgid "Mob Spawner settings failed!" +msgstr "Mob 孵化器設置失敗!" + +#: spawner.lua +msgid "" +"Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] " +"distance[1-20] y_offset[-10 to 10]”" +msgstr "" +"語法: “物品名稱 最小光亮[0-14] 最大光亮[0-14] 範圍內的最大Mob數量[0 to disable] " +"距離[1-20] y_offset[-10 to 10]”" diff --git a/readme.MD b/readme.MD index 27d552f..0f92f5e 100644 --- a/readme.MD +++ b/readme.MD @@ -23,7 +23,7 @@ Lucky Blocks: 9 Changelog: -- 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found (thanks Astrobe), dont spawn mobs if world anchor nearby (technic or simple_anchor mods) +- 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found (thanks Astrobe), dont spawn mobs if world anchor nearby (technic or simple_anchor mods), chinese local added - 1.49- Added mobs:force_capture(self, player) function, api functions now use metatables thanks to bell07 - 1.48- Add mobs:set_velocity(self, velocity) global function - 1.47- Mob damage changes, min and max light level for damage added, ignition sources checked for lava damage From 62bd960e38d08d54103321370c2cbb96b415c276 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Wed, 26 Feb 2020 21:28:04 +0000 Subject: [PATCH 034/135] added nil check for velocity --- api.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 2ba4395..bf46d7c 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200207", + version = "20200226", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -230,6 +230,9 @@ function mob_class:set_velocity(v) local yaw = (self.object:get_yaw() or 0) + self.rotate + -- nil check for velocity + v = v or 0 + -- set velocity with hard limit of 10 self.object:set_velocity({ x = max(-10, min((sin(yaw) * -v) + c_x, 10)), From 0156e04ebd67318a5b323ed622085eee1827a276 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 27 Feb 2020 17:15:51 +0000 Subject: [PATCH 035/135] another just-in-case nil check --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index bf46d7c..9b6bd4c 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200226", + version = "20200227", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -236,7 +236,7 @@ function mob_class:set_velocity(v) -- set velocity with hard limit of 10 self.object:set_velocity({ x = max(-10, min((sin(yaw) * -v) + c_x, 10)), - y = max(-10, min(self.object:get_velocity().y, 10)), + y = max(-10, min((self.object:get_velocity().y or 0), 10)), z = max(-10, min((cos(yaw) * v) + c_y, 10)) }) end From ad5b444b657b5f7b9b7fe56a2ccb381eb2392b6e Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 6 Mar 2020 13:17:18 +0000 Subject: [PATCH 036/135] added do_env_damage flags when hurt and nil check when damaged --- api.lua | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/api.lua b/api.lua index 9b6bd4c..1c2bab2 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200227", + version = "20200306", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -735,7 +735,7 @@ function mob_class:check_for_death(cmi_cause) -- has health actually changed? if self.health == self.old_health and self.health > 0 then - return + return false end self.old_health = self.health @@ -894,7 +894,7 @@ function mob_class:do_env_damage() -- remove mob if standing inside ignore node if self.standing_in == "ignore" then self.object:remove() - return + return true end -- is mob light sensative, or scared of the dark :P @@ -909,7 +909,7 @@ function mob_class:do_env_damage() effect(pos, 5, "tnt_smoke.png") - if self:check_for_death({type = "light"}) then return end + if self:check_for_death({type = "light"}) then return true end end end @@ -928,7 +928,7 @@ function mob_class:do_env_damage() effect(pos, 5, "bubble.png", nil, nil, 1, nil) if self:check_for_death({type = "environment", - pos = pos, node = self.standing_in}) then return end + pos = pos, node = self.standing_in}) then return true end end -- lava or fire or ignition source @@ -944,8 +944,8 @@ function mob_class:do_env_damage() effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil) - if self:check_for_death({type = "environment", - pos = pos, node = self.standing_in, hot = true}) then return end + if self:check_for_death({type = "environment", pos = pos, + node = self.standing_in, hot = true}) then return true end end -- damage_per_second node check @@ -956,7 +956,7 @@ function mob_class:do_env_damage() effect(pos, 5, "tnt_smoke.png") if self:check_for_death({type = "environment", - pos = pos, node = self.standing_in}) then return end + pos = pos, node = self.standing_in}) then return true end end --[[ --- suffocation inside solid node @@ -968,7 +968,7 @@ function mob_class:do_env_damage() self.health = self.health - self.suffocation if self:check_for_death({type = "environment", - pos = pos, node = self.standing_in}) then return end + pos = pos, node = self.standing_in}) then return true end end ]] self:check_for_death({type = "unknown"}) @@ -3241,7 +3241,7 @@ function mob_class:on_step(dtime) self.env_damage_timer = 0 -- check for environmental damage (water, fire, lava etc.) - self:do_env_damage() + if self:do_env_damage() then return end -- node replace check (cow eats grass etc.) self:replace(pos) From 9f5e6eb85fbf2c772603ca2d36e046dc384c8772 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 6 Mar 2020 13:23:53 +0000 Subject: [PATCH 037/135] added missing return value to do_env_damage --- api.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 1c2bab2..e51ccd3 100644 --- a/api.lua +++ b/api.lua @@ -971,7 +971,7 @@ function mob_class:do_env_damage() pos = pos, node = self.standing_in}) then return true end end ]] - self:check_for_death({type = "unknown"}) + return self:check_for_death({type = "unknown"}) end From ff4bb540e18c1161e6f7b9121079130b1092ced1 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 6 Mar 2020 19:43:35 +0000 Subject: [PATCH 038/135] on_blast returns no damage, knockback or items --- api.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api.lua b/api.lua index e51ccd3..692e172 100644 --- a/api.lua +++ b/api.lua @@ -3273,7 +3273,8 @@ function mob_class:on_blast(damage) damage_groups = {fleshy = damage}, }, nil) - return false, true, {} + -- return no damage, no knockback, no item drops, mob api handles all + return false, false, {} end From dc3ed1dfe378a2f59ff7ff9736f939d10cecfd35 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 12 Mar 2020 20:36:23 +0000 Subject: [PATCH 039/135] add nil checks --- api.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 692e172..d3d8176 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200306", + version = "20200312", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -234,9 +234,11 @@ function mob_class:set_velocity(v) v = v or 0 -- set velocity with hard limit of 10 + local vel = self.object:get_velocity() + self.object:set_velocity({ x = max(-10, min((sin(yaw) * -v) + c_x, 10)), - y = max(-10, min((self.object:get_velocity().y or 0), 10)), + y = max(-10, min((vel and vel.y or 0), 10)), z = max(-10, min((cos(yaw) * v) + c_y, 10)) }) end @@ -838,6 +840,11 @@ function mob_class:is_at_cliff() return false end + -- if object no longer exists then return + if not self.object:get_luaentity() then + return false + end + 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) From 9c60e9633653bf23aa9086f6f85a03c88079c14f Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Thu, 26 Mar 2020 19:33:16 +0000 Subject: [PATCH 040/135] add value for nil yaw --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index d3d8176..cf4086a 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200312", + version = "20200326", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -3164,7 +3164,7 @@ function mob_class:on_step(dtime) if self.delay and self.delay > 0 then - local yaw = self.object:get_yaw() + local yaw = self.object:get_yaw() or 0 if self.delay == 1 then yaw = self.target_yaw From 610cef16f4b21d9f64b986dcd598791f0279f4d4 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 6 Apr 2020 09:30:50 +0100 Subject: [PATCH 041/135] add some sanity checks --- api.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/api.lua b/api.lua index cf4086a..86e51d7 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200326", + version = "20200406", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -254,6 +254,8 @@ function mob_class:get_velocity() local v = self.object:get_velocity() + if not v then return 0 end + return (v.x * v.x + v.z * v.z) ^ 0.5 end @@ -3115,7 +3117,10 @@ function mob_class:on_step(dtime) end local pos = self.object:get_pos() - local yaw = 0 + local yaw = self.object:get_yaw() + + -- early warning check, if no yaw then no entity, skip rest of function + if not yaw then return end -- get node at foot level every quarter second self.node_timer = (self.node_timer or 0) + dtime @@ -3164,8 +3169,6 @@ function mob_class:on_step(dtime) if self.delay and self.delay > 0 then - local yaw = self.object:get_yaw() or 0 - if self.delay == 1 then yaw = self.target_yaw else From 46342881248c42007345799663b0f1fb24c5927a Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 7 Apr 2020 08:36:57 +0100 Subject: [PATCH 042/135] add falling sanity check --- api.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 86e51d7..a019c79 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200406", + version = "20200407", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -2535,6 +2535,9 @@ function mob_class:falling(pos) -- floating in water (or falling) local v = self.object:get_velocity() + -- sanity check + if not v then return end + if v.y > 0 then -- apply gravity when moving up From 92c899e6a45b4ed352a4b7fc05957cceaf51b3ec Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 7 Apr 2020 08:41:41 +0100 Subject: [PATCH 043/135] mob egg sanity checks --- api.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api.lua b/api.lua index a019c79..088d513 100644 --- a/api.lua +++ b/api.lua @@ -3846,6 +3846,8 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) local mob = minetest.add_entity(pos, mob, data) local ent = mob:get_luaentity() + if not ent then return end -- sanity check + -- set owner if not a monster if ent.type ~= "monster" then ent.owner = placer:get_player_name() @@ -3891,6 +3893,8 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) local mob = minetest.add_entity(pos, mob) local ent = mob:get_luaentity() + if not ent then return end -- sanity check + -- don't set owner if monster or sneak pressed if ent.type ~= "monster" and not placer:get_player_control().sneak then From dde9896a1a0e03905f3de30bad3471f84e0f05a8 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Sat, 11 Apr 2020 17:46:07 +0100 Subject: [PATCH 044/135] add sanity check to do_jump --- api.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 088d513..e38c04b 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200407", + version = "20200411", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -1007,6 +1007,9 @@ function mob_class:do_jump() local pos = self.object:get_pos() local yaw = self.object:get_yaw() + -- sanity check + if not yaw then return false end + -- what is mob standing on? pos.y = pos.y + self.collisionbox[2] - 0.2 From 9b0ff171018383e4870087da98f7f64dab2c1353 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Sat, 11 Apr 2020 17:51:15 +0100 Subject: [PATCH 045/135] add sanity check to do_punch --- api.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api.lua b/api.lua index e38c04b..4778d5c 100644 --- a/api.lua +++ b/api.lua @@ -2783,6 +2783,10 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) and tflp >= punch_interval then local v = self.object:get_velocity() + + -- sanity check + if not v then return end + local kb = damage or 1 local up = 2 From bff313267be54b0b7796cbe4d7f24d551fb0a5f2 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 12 Apr 2020 12:14:42 +0100 Subject: [PATCH 046/135] re-enable raycasting line of sight (should work now) --- api.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api.lua b/api.lua index 4778d5c..71ccd9f 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200411", + version = "20200412", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -463,9 +463,9 @@ local ray_line_of_sight = function(self, pos1, pos2) while thing do - if thing.type == "object" - and thing.ref ~= self.object - and not thing.ref:is_player() then return false end +-- if thing.type == "object" +-- and thing.ref ~= self.object +-- and not thing.ref:is_player() then return false end if thing.type == "node" then @@ -482,7 +482,7 @@ local ray_line_of_sight = function(self, pos1, pos2) end -- detect if using minetest 5.0 by searching for permafrost node -local is_50 = nil -- minetest.registered_nodes["default:permafrost"] +local is_50 = minetest.registered_nodes["default:permafrost"] function mob_class:line_of_sight(pos1, pos2, stepsize) From f2cc0446b0249b6ea352081453d1b07a604897f5 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 26 Apr 2020 21:16:46 +0100 Subject: [PATCH 047/135] cant attack if running away --- api.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 71ccd9f..e5f06b4 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200412", + version = "20200426", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -1671,6 +1671,7 @@ function mob_class:general_attack() -- return if already attacking, passive or docile during day if self.passive + or self.state == "runaway" or self.state == "attack" or self:day_docile() then return From a934d80631b6580fc8b1dd5379d9186d5162bbfb Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 27 Apr 2020 13:17:20 +0100 Subject: [PATCH 048/135] added some node checks and damage returns --- api.lua | 105 +++++++++++++++++++++++++++++++++++++++--------------- readme.MD | 3 +- 2 files changed, 78 insertions(+), 30 deletions(-) diff --git a/api.lua b/api.lua index e5f06b4..1aa36f3 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200426", + version = "20200427", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -835,6 +835,42 @@ function mob_class:check_for_death(cmi_cause) end +-- get node but use fallback for nil or unknown +local node_ok = function(pos, fallback) + + fallback = fallback or mobs.fallback_node + + local node = minetest.get_node_or_nil(pos) + + if node and minetest.registered_nodes[node.name] then + return node + end + + return minetest.registered_nodes[fallback] +end + + +-- Returns true is node can deal damage to self +local is_node_dangerous = function(self, nodename) + + if self.water_damage > 0 + and minetest.get_item_group(nodename, "water") ~= 0 then + return true + end + + if self.lava_damage > 0 + and minetest.get_item_group(nodename, "igniter") ~= 0 then + return true + end + + if minetest.registered_nodes[nodename].damage_per_second > 0 then + return true + end + + return false +end + + -- is mob facing a cliff function mob_class:is_at_cliff() @@ -853,32 +889,29 @@ function mob_class:is_at_cliff() local pos = self.object:get_pos() local ypos = pos.y + self.collisionbox[2] -- just above floor - if minetest.line_of_sight( + local free_fall, blocker = minetest.line_of_sight( {x = pos.x + dir_x, y = ypos, z = pos.z + dir_z}, - {x = pos.x + dir_x, y = ypos - self.fear_height, z = pos.z + dir_z}, 1) then + {x = pos.x + dir_x, y = ypos - self.fear_height, z = pos.z + dir_z}) + -- check for straight drop, drop onto danger or walkable node + if free_fall then return true + else + local bnode = node_ok(blocker) + + if is_node_dangerous(self, bnode.name) then + return true + else + local def = minetest.registered_nodes[bnode.name] + + return (not def and def.walkable) + end end return false end --- get node but use fallback for nil or unknown -local node_ok = function(pos, fallback) - - fallback = fallback or mobs.fallback_node - - local node = minetest.get_node_or_nil(pos) - - if node and minetest.registered_nodes[node.name] then - return node - end - - return minetest.registered_nodes[fallback] -end - - -- environmental damage (water, lava, fire, light etc.) function mob_class:do_env_damage() @@ -1032,6 +1065,18 @@ function mob_class:do_jump() z = pos.z + dir_z }) + -- what is above and in front? + local nodt = node_ok({ + x = pos.x + dir_x, + y = pos.y + 1.5, + z = pos.z + dir_z + }) + + -- is there space to jump up? + if minetest.registered_nodes[nodt.name].walkable == true then + return false + end + -- thin blocks that do not need to be jumped if nod.name == node_snow then return false @@ -1043,7 +1088,8 @@ function mob_class:do_jump() or minetest.registered_items[nod.name].walkable then if not nod.name:find("fence") - and not nod.name:find("gate") then + and not nod.name:find("gate") + and not nod.name:find("wall") then local v = self.object:get_velocity() @@ -1071,6 +1117,8 @@ function mob_class:do_jump() end else self.facing_fence = true + + return false end -- if we jumped against a block/wall 4 times then turn @@ -2060,7 +2108,7 @@ function mob_class:do_states(dtime) if self.water_damage > 0 and self.lava_damage > 0 then - lp = minetest.find_node_near(s, 1, {"group:water", "group:lava"}) + lp = minetest.find_node_near(s, 1, {"group:water", "group:igniter"}) elseif self.water_damage > 0 then @@ -2068,16 +2116,13 @@ function mob_class:do_states(dtime) elseif self.lava_damage > 0 then - lp = minetest.find_node_near(s, 1, {"group:lava"}) + lp = minetest.find_node_near(s, 1, {"group:igniter"}) end if lp then - -- if mob in water or lava then look for land - if (self.lava_damage - and minetest.registered_nodes[self.standing_in].groups.lava) - or (self.water_damage - and minetest.registered_nodes[self.standing_in].groups.water) then + -- if mob in dangerous node then look for land + if is_node_dangerous(self, self.standing_in) then lp = minetest.find_node_near(s, 5, {"group:soil", "group:stone", "group:sand", node_ice, node_snowblock}) @@ -2591,7 +2636,7 @@ function mob_class:falling(pos) effect(pos, 5, "tnt_smoke.png", 1, 2, 2, nil) if self:check_for_death({type = "fall"}) then - return + return true end end @@ -3173,8 +3218,10 @@ function mob_class:on_step(dtime) self:mob_expire(pos, 0.25) end - -- check if falling, flying, floating - self:falling(pos) + -- check if falling, flying, floating and return if player died + if self:falling(pos) then + return + end -- smooth rotation by ThomasMonroe314 diff --git a/readme.MD b/readme.MD index 0f92f5e..cb26894 100644 --- a/readme.MD +++ b/readme.MD @@ -23,7 +23,8 @@ Lucky Blocks: 9 Changelog: -- 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found (thanks Astrobe), dont spawn mobs if world anchor nearby (technic or simple_anchor mods), chinese local added +- 1.51 - Added some node checks for dangerous nodes, jumping and falling (thx for idea wuzzy) +- 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found, (thanks Astrobe), dont spawn mobs if world anchor nearby (technic or simple_anchor mods), chinese local added - 1.49- Added mobs:force_capture(self, player) function, api functions now use metatables thanks to bell07 - 1.48- Add mobs:set_velocity(self, velocity) global function - 1.47- Mob damage changes, min and max light level for damage added, ignition sources checked for lava damage From 064b59f01deed167d9ab4cb98160d54402e2a27e Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Wed, 29 Apr 2020 15:01:21 +0100 Subject: [PATCH 049/135] Added few sanity checks, higher pitch sounds for child mobs --- api.lua | 97 ++++++++++++++++++++++++++++++++++++++----------------- api.txt | 2 ++ mount.lua | 31 +++++++++++------- 3 files changed, 90 insertions(+), 40 deletions(-) diff --git a/api.lua b/api.lua index 1aa36f3..ed0a984 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200427", + version = "20200429", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -150,12 +150,21 @@ local mob_class_meta = {__index = mob_class} -- play sound function mob_class:mob_sound(sound) + local pitch = 1.0 + + -- higher pitch for a child + if self.child then pitch = pitch * 1.5 end + + -- a little random pitch to be different + pitch = pitch + math.random(-10, 10) * 0.005 + if sound then minetest.sound_play(sound, { object = self.object, gain = 1.0, - max_hear_distance = self.sounds.distance - }) + max_hear_distance = self.sounds.distance, + pitch = pitch + }, true) end end @@ -215,6 +224,12 @@ end -- move mob in facing direction function mob_class:set_velocity(v) + -- halt mob if it has been ordered to stay + if self.order == "stand" then + self.object:set_velocity({x = 0, y = 0, z = 0}) + return + end + local c_x, c_y = 0, 0 -- can mob be pushed, if so calculate direction @@ -222,12 +237,6 @@ function mob_class:set_velocity(v) c_x, c_y = unpack(self:collision()) end - -- halt mob if it has been ordered to stay - if self.order == "stand" then - self.object:set_velocity({x = 0, y = 0, z = 0}) - return - end - local yaw = (self.object:get_yaw() or 0) + self.rotate -- nil check for velocity @@ -668,6 +677,12 @@ end -- drop items function mob_class:item_drop() + -- no drops if disabled by setting + if not mobs_drop_items then return end + + -- no drops for child mobs + if self.child then return end + local pos = self.object:get_pos() -- check for drops function @@ -678,12 +693,6 @@ function mob_class:item_drop() return end - -- no drops if disabled by setting - if not mobs_drop_items then return end - - -- no drops for child mobs - if self.child then return end - -- was mob killed by player? local death_by_player = self.cause_of_death and self.cause_of_death.puncher and self.cause_of_death.puncher:is_player() or nil @@ -742,12 +751,17 @@ function mob_class:check_for_death(cmi_cause) return false end + local damaged = self.health < self.old_health + self.old_health = self.health -- still got some health? play hurt sound if self.health > 0 then - self:mob_sound(self.sounds.damage) + -- only play hurt sound if damaged + if damaged then + self:mob_sound(self.sounds.damage) + end -- make sure health isn't higher than max if self.health > self.hp_max then @@ -1117,8 +1131,6 @@ function mob_class:do_jump() end else self.facing_fence = true - - return false end -- if we jumped against a block/wall 4 times then turn @@ -1510,6 +1522,10 @@ function mob_class:smart_mobs(s, p, dist, dtime) minetest.after(1, function(self) + if not self.object:get_luaentity() then + return + end + if self.object:get_luaentity() then if has_lineofsight then @@ -1560,7 +1576,15 @@ function mob_class:smart_mobs(s, p, dist, dtime) local dropheight = 6 if self.fear_height ~= 0 then dropheight = self.fear_height end - self.path.way = minetest.find_path(s, p1, 16, self.stepheight, dropheight, "Dijkstra") + local jumpheight = 0 + if self.jump and self.jump_height >= 4 then + jumpheight = min(math.ceil(self.jump_height / 4), 4) + elseif self.stepheight > 0.5 then + jumpheight = 1 + end + + self.path.way = minetest.find_path(s, p1, 16, jumpheight, + dropheight, "Dijkstra") --[[ -- show path using particles @@ -1680,8 +1704,9 @@ function mob_class:smart_mobs(s, p, dist, dtime) -- will try again in 2 second self.path.stuck_timer = stuck_timeout - 2 - -- frustration! cant find the damn path :( - --self:mob_sound(self.sounds.random) + elseif s.y < p1.y and (not self.fly) then + self:do_jump() --add jump to pathfinding + self.path.following = true else -- yay i found path self:mob_sound(self.sounds.war_cry) @@ -1901,8 +1926,7 @@ end function mob_class:follow_flop() -- find player to follow - if (self.follow ~= "" - or self.order == "follow") + if (self.follow ~= "" or self.order == "follow") and not self.following and self.state ~= "attack" and self.state ~= "runaway" then @@ -2342,7 +2366,7 @@ function mob_class:do_states(dtime) effect(pos, 32, "tnt_smoke.png", nil, nil, node_break_radius, 1, 0) end - return + return true end end @@ -2772,12 +2796,12 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) minetest.sound_play(weapon_def.sounds[s], { object = self.object, max_hear_distance = 8 - }) + }, true) else minetest.sound_play("default_punch", { object = self.object, max_hear_distance = 5 - }) + }, true) end -- blood_particles @@ -3321,7 +3345,7 @@ function mob_class:on_step(dtime) self:follow_flop() - self:do_states(dtime) + if self:do_states(dtime) then return end self:do_jump() @@ -3702,6 +3726,7 @@ function mobs:register_arrow(name, def) hit_player = def.hit_player, hit_node = def.hit_node, hit_mob = def.hit_mob, + hit_object = def.hit_object, drop = def.drop or false, -- drops arrow as registered item when true collisionbox = def.collisionbox or {0, 0, 0, 0, 0, 0}, timer = 0, @@ -3723,7 +3748,8 @@ function mobs:register_arrow(name, def) local pos = self.object:get_pos() if self.switch == 0 - or self.timer > 150 then + or self.timer > 150 + or within_limits(pos, 0) then self.object:remove() ; -- print ("removed arrow") @@ -3796,6 +3822,19 @@ function mobs:register_arrow(name, def) return end + + if entity + and self.hit_object + and (not entity._cmi_is_mob) + and tostring(player) ~= self.owner_id + and entity.name ~= self.object:get_luaentity().name then + + self:hit_object(player) + + self.object:remove(); -- print ("hit object") + + return + end end end @@ -3821,7 +3860,7 @@ function mobs:safe_boom(self, pos, radius) pos = pos, gain = 1.0, max_hear_distance = self.sounds and self.sounds.distance or 32 - }) + }, true) entity_physics(pos, radius) diff --git a/api.txt b/api.txt index 5361a8c..ec33ec6 100644 --- a/api.txt +++ b/api.txt @@ -399,6 +399,8 @@ This function registers a arrow for mobs with the attack type shoot. '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_object' a function that is called when the arrow hits an object; + this function 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 diff --git a/mount.lua b/mount.lua index 29b80ed..63c8509 100644 --- a/mount.lua +++ b/mount.lua @@ -131,7 +131,7 @@ function mobs.attach(entity, player) local rot_view = 0 if entity.player_rotation.y == 90 then - rot_view = math.pi/2 + rot_view = math.pi / 2 end attach_at = entity.driver_attach_at @@ -151,11 +151,13 @@ function mobs.attach(entity, player) } }) - minetest.after(0.2, function() - default.player_set_animation(player, "sit" , 30) - end) + minetest.after(0.2, function(name) + local player = minetest.get_player_by_name(name) + if player then + default.player_set_animation(player, "sit" , 30) + end + end, player:get_player_name()) - --player:set_look_yaw(entity.object:get_yaw() - rot_view) player:set_look_horizontal(entity.object:get_yaw() - rot_view) end @@ -168,11 +170,18 @@ function mobs.detach(player, offset) local pos = player:get_pos() - pos = {x = pos.x + offset.x, y = pos.y + 0.2 + offset.y, z = pos.z + offset.z} + pos = { + x = pos.x + offset.x, + y = pos.y + 0.2 + offset.y, + z = pos.z + offset.z + } - minetest.after(0.1, function() - player:set_pos(pos) - end) + minetest.after(0.1, function(name, pos) + local player = minetest.get_player_by_name(name) + if player then + player:set_pos(pos) + end + end, player:get_player_name(), pos) end @@ -290,7 +299,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) -- Set position, velocity and acceleration local p = entity.object:get_pos() - local new_velo = {x = 0, y = 0, z = 0} + local new_velo local new_acce = {x = 0, y = -9.8, z = 0} p.y = p.y - 0.5 @@ -315,7 +324,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) minetest.sound_play("default_punch", { object = entity.object, max_hear_distance = 5 - }) + }, true) entity.object:punch(entity.object, 1.0, { full_punch_interval = 1.0, From 8b25ab4988059a8a3199c5f67d71ab79c92052a9 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Wed, 29 Apr 2020 20:15:35 +0100 Subject: [PATCH 050/135] add 'keep_flying' flag and random vertical flight changes --- api.lua | 24 ++++++++++++++++++------ api.txt | 1 + 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/api.lua b/api.lua index ed0a984..f17f4fe 100644 --- a/api.lua +++ b/api.lua @@ -508,9 +508,9 @@ function mobs:line_of_sight(entity, pos1, pos2, stepsize) end -function mob_class:attempt_flight_correction() +function mob_class:attempt_flight_correction(override) - if self:flight_check() then return true end + if self:flight_check() and override ~= true then return true end -- We are not flying in what we are supposed to. -- See if we can find intended flight medium and return to it @@ -534,7 +534,7 @@ function mob_class:attempt_flight_correction() local escape_direction = vector.direction(pos, escape_target) self.object:set_velocity( - vector.multiply(escape_direction, self.run_velocity)) + vector.multiply(escape_direction, 1)) --self.run_velocity)) return true end @@ -2191,6 +2191,12 @@ function mob_class:do_states(dtime) yaw = yaw + random(-0.5, 0.5) yaw = self:set_yaw(yaw, 8) + + -- for flying/swimming mobs randomly move up and down also + if self.fly_in + and not self.following then + self:attempt_flight_correction(true) + end end -- stand for great fall in front @@ -2198,9 +2204,14 @@ function mob_class:do_states(dtime) or self.at_cliff or random(1, 100) <= self.stand_chance then - self:set_velocity(0) - self.state = "stand" - self:set_animation("stand", true) + -- don't stand if mob flies and keep_flying set + if (self.fly and not self.keep_flying) + or not self.fly then + + self:set_velocity(0) + self.state = "stand" + self:set_animation("stand", true) + end else self:set_velocity(self.walk_velocity) @@ -3385,6 +3396,7 @@ minetest.register_entity(name, setmetatable({ attack_type = def.attack_type, fly = def.fly, fly_in = def.fly_in, + keep_flying = def.keep_flying, owner = def.owner, order = def.order, on_die = def.on_die, diff --git a/api.txt b/api.txt index ec33ec6..5b54699 100644 --- a/api.txt +++ b/api.txt @@ -45,6 +45,7 @@ functions needed for the mob to work properly which contains the following: '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". + 'keep_flying' when true mobs like birds no longer stop and stand. 'stay_near' when set allows mobs the chance to stay around certain nodes. 'nodes' string or table of nodes to stay nearby e.g. "farming:straw" 'chance' chance of searching for above node(s), default is 10. From f11b774c95683cc4df3b10c94feb26d524b330c9 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 30 Apr 2020 09:33:46 +0100 Subject: [PATCH 051/135] tweaked jumping and turning when blocked in front --- api.lua | 46 +++++++++++++++++++++------------------------- readme.MD | 2 +- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/api.lua b/api.lua index f17f4fe..d2c38bf 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200429", + version = "20200430", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -1086,20 +1086,14 @@ function mob_class:do_jump() z = pos.z + dir_z }) - -- is there space to jump up? - if minetest.registered_nodes[nodt.name].walkable == true then - return false - end - - -- thin blocks that do not need to be jumped - if nod.name == node_snow then - return false - end - --print ("in front:", nod.name, pos.y + 0.5) +--print ("in front above:", nodt.name, pos.y + 1.5) - if self.walk_chance == 0 - or minetest.registered_items[nod.name].walkable then + -- jump if standing on solid node (not snow) and not blocked above + if (self.walk_chance == 0 + or minetest.registered_items[nod.name].walkable) + and minetest.registered_nodes[nodt.name].walkable ~= true + and nod.name ~= node_snow then if not nod.name:find("fence") and not nod.name:find("gate") @@ -1129,27 +1123,29 @@ function mob_class:do_jump() if self:get_velocity() > 0 then self:mob_sound(self.sounds.jump) end + + return true else self.facing_fence = true end + end - -- if we jumped against a block/wall 4 times then turn - if self.object:get_velocity().x ~= 0 - or self.object:get_velocity().z ~= 0 then + -- if blocked against a block/wall for 5 counts then turn + if not self.following + and (self.object:get_velocity().x == 0 + or self.object:get_velocity().z == 0) then - self.jump_count = (self.jump_count or 0) + 1 + self.jump_count = (self.jump_count or 0) + 1 --print ("----", self.jump_count) - if self.jump_count == 4 then + if self.jump_count > 4 then - local yaw = self.object:get_yaw() or 0 + local yaw = self.object:get_yaw() or 0 + local turn = random(0, 2) + 1.35 - yaw = self:set_yaw(yaw + 1.35, 8) ---print ("---- turn") - self.jump_count = 0 - end + yaw = self:set_yaw(yaw + turn, 12) +--print ("---- turn", turn) + self.jump_count = 0 end - - return true end return false diff --git a/readme.MD b/readme.MD index cb26894..ae6dfb2 100644 --- a/readme.MD +++ b/readme.MD @@ -23,7 +23,7 @@ Lucky Blocks: 9 Changelog: -- 1.51 - Added some node checks for dangerous nodes, jumping and falling (thx for idea wuzzy) +- 1.51 - Added some node checks for dangerous nodes, jumping and falling tweaks (thx for idea wuzzy) - 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found, (thanks Astrobe), dont spawn mobs if world anchor nearby (technic or simple_anchor mods), chinese local added - 1.49- Added mobs:force_capture(self, player) function, api functions now use metatables thanks to bell07 - 1.48- Add mobs:set_velocity(self, velocity) global function From e4198b2a21e95a33709dfe6dc7836e96852f69ec Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 30 Apr 2020 11:51:44 +0100 Subject: [PATCH 052/135] only force turn when actually blocked --- api.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index d2c38bf..4c10ed1 100644 --- a/api.lua +++ b/api.lua @@ -1086,13 +1086,15 @@ function mob_class:do_jump() z = pos.z + dir_z }) + local blocked = minetest.registered_nodes[nodt.name].walkable + --print ("in front:", nod.name, pos.y + 0.5) --print ("in front above:", nodt.name, pos.y + 1.5) -- jump if standing on solid node (not snow) and not blocked above if (self.walk_chance == 0 or minetest.registered_items[nod.name].walkable) - and minetest.registered_nodes[nodt.name].walkable ~= true + and not blocked and nod.name ~= node_snow then if not nod.name:find("fence") @@ -1132,8 +1134,7 @@ function mob_class:do_jump() -- if blocked against a block/wall for 5 counts then turn if not self.following - and (self.object:get_velocity().x == 0 - or self.object:get_velocity().z == 0) then + and (self.facing_fence or blocked) then self.jump_count = (self.jump_count or 0) + 1 --print ("----", self.jump_count) From 32e8d069f76ff2c8ca2c745127826ff5fa78b032 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 1 May 2020 10:29:12 +0100 Subject: [PATCH 053/135] enable suffocation, add area check when spawning, code tidy and tweaks --- api.lua | 124 ++++++++++++++++++++++++++++++++++++++---------------- api.txt | 3 +- readme.MD | 2 +- 3 files changed, 90 insertions(+), 39 deletions(-) diff --git a/api.lua b/api.lua index 4c10ed1..0945f82 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200430", + version = "20200501", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -594,8 +594,7 @@ function mob_class:do_stay_near() local r = self.view_range local nearby_nodes = minetest.find_nodes_in_area( {x = pos.x - r, y = pos.y - 1, z = pos.z - r}, - {x = pos.x + r, y = pos.y + 1, z = pos.z + r}, - searchnodes) + {x = pos.x + r, y = pos.y + 1, z = pos.z + r}, searchnodes) if #nearby_nodes < 1 then return false @@ -622,20 +621,22 @@ end -- custom particle effects -local effect = function(pos, amount, texture, min_size, max_size, radius, gravity, glow) +local effect = function( + pos, amount, texture, min_size, max_size, radius, gravity, glow, fall) radius = radius or 2 min_size = min_size or 0.5 max_size = max_size or 1 gravity = gravity or -10 glow = glow or 0 + fall = fall and 0 or -radius minetest.add_particlespawner({ amount = amount, time = 0.25, minpos = pos, maxpos = pos, - minvel = {x = -radius, y = -radius, z = -radius}, + minvel = {x = -radius, y = fall, z = -radius}, maxvel = {x = radius, y = radius, z = radius}, minacc = {x = 0, y = gravity, z = 0}, maxacc = {x = 0, y = gravity, z = 0}, @@ -1014,19 +1015,20 @@ function mob_class:do_env_damage() if self:check_for_death({type = "environment", pos = pos, node = self.standing_in}) then return true end end ---[[ + --- suffocation inside solid node - if self.suffocation ~= 0 - and nodef.walkable == true - and nodef.groups.disable_suffocation ~= 1 - and nodef.drawtype == "normal" then + if (self.suffocation ~= 0) + and (nodef.walkable == nil or nodef.walkable == true) + and (nodef.collision_box == nil or nodef.collision_box.type == "regular") + and (nodef.node_box == nil or nodef.node_box.type == "regular") + and (nodef.groups.disable_suffocation ~= 1) then self.health = self.health - self.suffocation - if self:check_for_death({type = "environment", + if self:check_for_death({type = "suffocation", pos = pos, node = self.standing_in}) then return true end end -]] + return self:check_for_death({type = "unknown"}) end @@ -2764,7 +2766,8 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) -- print ("Mob Damage is", damage) if use_cmi - and cmi.notify_punch(self.object, hitter, tflp, tool_capabilities, dir, damage) then + and cmi.notify_punch( + self.object, hitter, tflp, tool_capabilities, dir, damage) then return end @@ -2816,18 +2819,23 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) if not disable_blood and self.blood_amount > 0 then local pos = self.object:get_pos() + local blood = self.blood_texture + local amount = self.blood_amount pos.y = pos.y + (-self.collisionbox[2] + self.collisionbox[5]) * .5 + -- lots of damage = more blood :) + if damage > 10 then + amount = self.blood_amount * 2 + end + -- do we have a single blood texture or multiple? if type(self.blood_texture) == "table" then - - local blood = self.blood_texture[random(1, #self.blood_texture)] - - effect(pos, self.blood_amount, blood, nil, nil, 1, nil) - else - effect(pos, self.blood_amount, self.blood_texture, nil, nil, 1, nil) + blood = self.blood_texture[random(1, #self.blood_texture)] end + + effect(pos, amount, blood, 1, 2, 1.75, nil, nil, true) + end -- do damage @@ -2928,7 +2936,8 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) self:do_attack(hitter) -- alert others to the attack - local objs = minetest.get_objects_inside_radius(hitter:get_pos(), self.view_range) + local objs = minetest.get_objects_inside_radius( + hitter:get_pos(), self.view_range) local obj = nil for n = 1, #objs do @@ -3638,6 +3647,13 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, return end + -- mobs cannot spawn in protected areas when enabled + if not spawn_protected + and minetest.is_protected(pos, "") then +--print ("--- inside protected area", name) + return + end + -- only spawn away from player local objs = minetest.get_objects_inside_radius(pos, 8) @@ -3649,32 +3665,67 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, end end - -- do we have enough height clearance to spawn mob? + -- do we have enough space to spawn mob? (thanks wuzzy) local ent = minetest.registered_entities[name] - local height = max(1, math.ceil( - (ent.collisionbox[5] or 0.25) - - (ent.collisionbox[2] or -0.25) - 1)) + local width_x = max(1, + math.ceil(ent.collisionbox[4] - ent.collisionbox[1])) + local min_x, max_x - for n = 0, height do + if width_x % 2 == 0 then + max_x = math.floor(width_x / 2) + min_x = -(max_x - 1) + else + max_x = math.floor(width_x / 2) + min_x = -max_x + end - local pos2 = {x = pos.x, y = pos.y + n, z = pos.z} + local width_z = max(1, + math.ceil(ent.collisionbox[6] - ent.collisionbox[3])) + local min_z, max_z + + if width_z % 2 == 0 then + max_z = math.floor(width_z / 2) + min_z = -(max_z - 1) + else + max_z = math.floor(width_z / 2) + min_z = -max_z + end + + local max_y = max(0, + math.ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1) + + for y = 0, max_y do + for x = min_x, max_x do + for z = min_z, max_z do + + local pos2 = { + x = pos.x + x, + y = pos.y + y, + z = pos.z + z} + + if minetest.registered_nodes[ + node_ok(pos2).name].walkable == true then + +--print ("--- not enough space to spawn", name) - if minetest.registered_nodes[node_ok(pos2).name].walkable == true then ---print ("--- inside block", name, node_ok(pos2).name) return end end - - -- mobs cannot spawn in protected areas when enabled - if not spawn_protected - and minetest.is_protected(pos, "") then ---print ("--- inside protected area", name) - return + end end - -- spawn mob half block higher than ground + -- spawn mob 1/2 node above ground pos.y = pos.y + 0.5 + -- tweak X/Z spawn pos + if width_x % 2 == 0 then + pos.x = pos.x + 0.5 + end + + if width_z % 2 == 0 then + pos.z = pos.z + 0.5 + end + local mob = minetest.add_entity(pos, name) --[[ print ("[mobs] Spawned " .. name .. " at " @@ -3757,8 +3808,7 @@ function mobs:register_arrow(name, def) local pos = self.object:get_pos() if self.switch == 0 - or self.timer > 150 - or within_limits(pos, 0) then + or self.timer > 150 then self.object:remove() ; -- print ("removed arrow") diff --git a/api.txt b/api.txt index 5b54699..840de8e 100644 --- a/api.txt +++ b/api.txt @@ -68,7 +68,8 @@ functions needed for the mob to work properly which contains the following: level is between the min and max values below 'light_damage_min' minimum light value when mob is affected (default: 14) 'light_damage_max' maximum light value when mob is affected (default: 15) - 'suffocation' when true causes mobs to suffocate inside solid blocks. + 'suffocation' when > 0 mobs will suffocate inside solid blocks and will be + hurt by the value given every second (0 to disable). '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 diff --git a/readme.MD b/readme.MD index ae6dfb2..73d68e0 100644 --- a/readme.MD +++ b/readme.MD @@ -23,7 +23,7 @@ Lucky Blocks: 9 Changelog: -- 1.51 - Added some node checks for dangerous nodes, jumping and falling tweaks (thx for idea wuzzy) +- 1.51 - Added some node checks for dangerous nodes, jumping and falling tweaks, spawn area check (thx for idea wuzzy), re-enabled mob suffocation - 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found, (thanks Astrobe), dont spawn mobs if world anchor nearby (technic or simple_anchor mods), chinese local added - 1.49- Added mobs:force_capture(self, player) function, api functions now use metatables thanks to bell07 - 1.48- Add mobs:set_velocity(self, velocity) global function From 928f8c5479d774a6b3e80feb7d9a6ae1732624ca Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 1 May 2020 18:48:07 +0100 Subject: [PATCH 054/135] never spawn mobs if player within 12 nodes --- api.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 0945f82..1b7947e 100644 --- a/api.lua +++ b/api.lua @@ -3655,7 +3655,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, end -- only spawn away from player - local objs = minetest.get_objects_inside_radius(pos, 8) + local objs = minetest.get_objects_inside_radius(pos, 12) for n = 1, #objs do From f98d769e15f9b7e01b91115b1522b3e325237cec Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 1 May 2020 19:30:29 +0100 Subject: [PATCH 055/135] added 'mob_nospawn_range' setting --- api.lua | 6 ++++-- api.txt | 1 + readme.MD | 2 +- settingtypes.txt | 3 +++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index 1b7947e..816d20e 100644 --- a/api.lua +++ b/api.lua @@ -52,6 +52,7 @@ local remove_far = minetest.settings:get_bool("remove_far_mobs") ~= false local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0 local show_health = minetest.settings:get_bool("mob_show_health") ~= false local max_per_block = tonumber(minetest.settings:get("max_objects_per_block") or 99) +local mob_nospawn_range = tonumber(minetest.settings:get("mob_nospawn_range") or 12) local mob_chance_multiplier = tonumber(minetest.settings:get("mob_chance_multiplier") or 1) -- Peaceful mode message so players will know there are no monsters @@ -3654,8 +3655,9 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, return end - -- only spawn away from player - local objs = minetest.get_objects_inside_radius(pos, 12) + -- only spawn a set distance away from player + local objs = minetest.get_objects_inside_radius( + pos, mob_nospawn_range) for n = 1, #objs do diff --git a/api.txt b/api.txt index 840de8e..06ba531 100644 --- a/api.txt +++ b/api.txt @@ -632,6 +632,7 @@ External Settings for "minetest.conf" 'mobs_griefing' when false mobs cannot break blocks when using either pathfinding level 2, replace functions or mobs:boom function. + 'mob_nospawn_range' Minimum range a mob can spawn near player (def: 12) 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 diff --git a/readme.MD b/readme.MD index 73d68e0..614bab9 100644 --- a/readme.MD +++ b/readme.MD @@ -23,7 +23,7 @@ Lucky Blocks: 9 Changelog: -- 1.51 - Added some node checks for dangerous nodes, jumping and falling tweaks, spawn area check (thx for idea wuzzy), re-enabled mob suffocation +- 1.51 - Added some node checks for dangerous nodes, jumping and falling tweaks, spawn area check (thx for idea wuzzy), re-enabled mob suffocation, add 'mob_nospawn_range' setting - 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found, (thanks Astrobe), dont spawn mobs if world anchor nearby (technic or simple_anchor mods), chinese local added - 1.49- Added mobs:force_capture(self, player) function, api functions now use metatables thanks to bell07 - 1.48- Add mobs:set_velocity(self, velocity) global function diff --git a/settingtypes.txt b/settingtypes.txt index 0821437..938827c 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -27,3 +27,6 @@ mob_chance_multiplier (Mob chance multiplier) float 1.0 # When false Mob no longer drop items when killed mobs_drop_items (Mob drops) bool true + +#....Sets minimum distance around player that mobs cannot spawn +mob_nospawn_range (Mob no-spawn range) float 12.0 From 00e8ac0850ee4bcf1fdb284ee75e4e315574fedd Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Sat, 2 May 2020 19:28:13 +0100 Subject: [PATCH 056/135] added failsafe for suffocation value (true, false) instead of value --- api.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index 816d20e..93025c6 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200501", + version = "20200502", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -1018,13 +1018,20 @@ function mob_class:do_env_damage() end --- suffocation inside solid node - if (self.suffocation ~= 0) + if (self.suffocation and self.suffocation ~= 0) and (nodef.walkable == nil or nodef.walkable == true) and (nodef.collision_box == nil or nodef.collision_box.type == "regular") and (nodef.node_box == nil or nodef.node_box.type == "regular") and (nodef.groups.disable_suffocation ~= 1) then - self.health = self.health - self.suffocation + local damage + if self.suffocation == true then + damage = 2 + else + damage = (self.suffocation or 2) + end + + self.health = self.health - damage if self:check_for_death({type = "suffocation", pos = pos, node = self.standing_in}) then return true end From 99fe3d0ce18db03f2953eaed25f871dfd955bef4 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 4 May 2020 20:45:43 +0100 Subject: [PATCH 057/135] missed local --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 93025c6..e4dc387 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200502", + version = "20200504", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -605,7 +605,7 @@ function mob_class:do_stay_near() local direction = vector.direction(pos, target) local vec = {x = target.x - pos.x, z = target.z - pos.z} - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate + local yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate if target.x > pos.x then yaw = yaw + pi From 9928b0e35e75fea9ab2d8028c21e7923112f687b Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 5 May 2020 21:32:24 +0100 Subject: [PATCH 058/135] add nil check to replace function --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index e4dc387..4e5de0b 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200504", + version = "20200505", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -1423,7 +1423,7 @@ function mob_class:replace(pos) if self.on_replace then - local oldnode = what + local oldnode = what or "" local newnode = with -- convert any group: replacements to actual node name From 18906fafebdb626198e3224b90feec96a446378e Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Wed, 6 May 2020 10:10:50 +0100 Subject: [PATCH 059/135] code tweaks and tidy, hopefully fixed remove_far feature --- api.lua | 54 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/api.lua b/api.lua index 4e5de0b..df7e68c 100644 --- a/api.lua +++ b/api.lua @@ -2973,7 +2973,7 @@ end -- get entity staticdata -function mob_class:get_staticdata() +function mob_class:mob_staticdata() -- remove mob when out of range unless tamed if remove_far @@ -2983,11 +2983,12 @@ function mob_class:get_staticdata() and not self.tamed and self.lifetimer < 20000 then - --print ("REMOVED " .. self.name) +-- print ("REMOVED " .. self.name) self.object:remove() - return ""-- nil + --return "" -- nil + return minetest.serialize({remove_ok = true, static_save = true}) end self.remove_ok = true @@ -3002,7 +3003,8 @@ function mob_class:get_staticdata() end if use_cmi then - self.serialized_cmi_components = cmi.serialize_components(self._cmi_components) + self.serialized_cmi_components = cmi.serialize_components( + self._cmi_components) end local tmp = {} @@ -3132,8 +3134,19 @@ function mob_class:mob_activate(staticdata, def, dtime) self.path.following = false -- currently following path? self.path.stuck_timer = 0 -- if stuck for too long search for path + -- Armor groups + -- immortal=1 because we use custom health + -- handling (using "health" property) + local armor + if type(self.armor) == "table" then + armor = table.copy(self.armor) + armor.immortal = 1 + else + armor = {immortal = 1, fleshy = self.armor} + end + self.object:set_armor_groups(armor) + -- mob defaults - self.object:set_armor_groups({immortal = 1, fleshy = self.armor}) self.old_y = self.object:get_pos().y self.old_health = self.health self.sounds.distance = self.sounds.distance or 10 @@ -3155,6 +3168,11 @@ function mob_class:mob_activate(staticdata, def, dtime) self:update_tag() self:set_animation("stand") + -- set 5.x flag to remove monsters when map area unloaded + if remove_far and self.type == "monster" then + self.static_save = false + end + -- run on_spawn function if found if self.on_spawn and not self.on_spawn_run then if self.on_spawn(self) then @@ -3168,7 +3186,8 @@ function mob_class:mob_activate(staticdata, def, dtime) end if use_cmi then - self._cmi_components = cmi.activate_components(self.serialized_cmi_components) + self._cmi_components = cmi.activate_components( + self.serialized_cmi_components) cmi.notify_activate(self.object, dtime) end end @@ -3506,6 +3525,10 @@ minetest.register_entity(name, setmetatable({ return self:mob_activate(staticdata, def, dtime) end, + get_staticdata = function(self) + return self:mob_staticdata(self) + end, + }, mob_class_meta)) end -- END mobs:register_mob function @@ -3564,12 +3587,13 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, aoc = tonumber(numbers[2]) or aoc if chance == 0 then - minetest.log("warning", string.format("[mobs] %s has spawning disabled", name)) + minetest.log("warning", + string.format("[mobs] %s has spawning disabled", name)) return end - minetest.log("action", - string.format("[mobs] Chance setting for %s changed to %s (total: %s)", + minetest.log("action", string.format( + "[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc)) end @@ -3807,7 +3831,8 @@ function mobs:register_arrow(name, def) on_activate = def.on_activate, - on_punch = def.on_punch or function(self, hitter, tflp, tool_capabilities, dir) + on_punch = def.on_punch or function( + self, hitter, tflp, tool_capabilities, dir) end, on_step = def.on_step or function(self, dtime) @@ -3855,7 +3880,8 @@ function mobs:register_arrow(name, def) self.lastpos = (self.lastpos or pos) - minetest.add_item(self.lastpos, self.object:get_luaentity().name) + minetest.add_item(self.lastpos, + self.object:get_luaentity().name) end self.object:remove() ; -- print ("hit node") @@ -3992,7 +4018,8 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) local under = minetest.get_node(pointed_thing.under) local def = minetest.registered_nodes[under.name] if def and def.on_rightclick then - return def.on_rightclick(pointed_thing.under, under, placer, itemstack) + return def.on_rightclick( + pointed_thing.under, under, placer, itemstack) end if pos @@ -4040,7 +4067,8 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) local under = minetest.get_node(pointed_thing.under) local def = minetest.registered_nodes[under.name] if def and def.on_rightclick then - return def.on_rightclick(pointed_thing.under, under, placer, itemstack) + return def.on_rightclick( + pointed_thing.under, under, placer, itemstack) end if pos From 8ce8490e78826803098f5c54357ccbe6f963ea9c Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Wed, 6 May 2020 21:21:20 +0100 Subject: [PATCH 060/135] code tweak and tidy --- api.lua | 84 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/api.lua b/api.lua index df7e68c..b4dbcbc 100644 --- a/api.lua +++ b/api.lua @@ -299,8 +299,7 @@ end -- set defined animation function mob_class:set_animation(anim, force) - if not self.animation - or not anim then return end + if not self.animation or not anim then return end self.animation.current = self.animation.current or "" @@ -378,10 +377,9 @@ local line_of_sight = function(self, pos1, pos2, stepsize) local ad = 0 -- It continues to advance in the line of sight in search of a real - -- obstruction which counts as 'normal' nodebox. + -- obstruction which counts as 'walkable' nodebox. while minetest.registered_nodes[nn] and (minetest.registered_nodes[nn].walkable == false) do --- or minetest.registered_nodes[nn].drawtype == "nodebox") do -- Check if you can still move forward if td < ad + stepsize then @@ -444,10 +442,9 @@ local new_line_of_sight = function(self, pos1, pos2, stepsize) local nn = minetest.get_node(pos).name -- It continues to advance in the line of sight in search of a real - -- obstruction which counts as 'normal' nodebox. + -- obstruction which counts as 'walkable' nodebox. while minetest.registered_nodes[nn] and (minetest.registered_nodes[nn].walkable == false) do --- or minetest.registered_nodes[nn].drawtype == "nodebox") do npos1 = vector.add(npos1, stepv) @@ -679,11 +676,8 @@ end -- drop items function mob_class:item_drop() - -- no drops if disabled by setting - if not mobs_drop_items then return end - - -- no drops for child mobs - if self.child then return end + -- no drops if disabled by setting or mob is child + if not mobs_drop_items or self.child then return end local pos = self.object:get_pos() @@ -719,7 +713,7 @@ function mob_class:item_drop() end end - -- only drop rare items (drops.min=0) if killed by player + -- only drop rare items (drops.min = 0) if killed by player if death_by_player then obj = minetest.add_item(pos, ItemStack(item .. " " .. num)) @@ -810,7 +804,7 @@ function mob_class:check_for_death(cmi_cause) return true end - -- default death function and die animation (if defined) + -- check for custom death function and die animation if self.animation and self.animation.die_start and self.animation.die_end then @@ -909,22 +903,21 @@ function mob_class:is_at_cliff() {x = pos.x + dir_x, y = ypos, z = pos.z + dir_z}, {x = pos.x + dir_x, y = ypos - self.fear_height, z = pos.z + dir_z}) - -- check for straight drop, drop onto danger or walkable node + -- check for straight drop if free_fall then return true - else - local bnode = node_ok(blocker) - - if is_node_dangerous(self, bnode.name) then - return true - else - local def = minetest.registered_nodes[bnode.name] - - return (not def and def.walkable) - end end - return false + local bnode = node_ok(blocker) + + -- will we drop onto dangerous node? + if is_node_dangerous(self, bnode.name) then + return true + end + + local def = minetest.registered_nodes[bnode.name] + + return (not def and def.walkable) end @@ -951,7 +944,9 @@ function mob_class:do_env_damage() -- remove mob if standing inside ignore node if self.standing_in == "ignore" then + self.object:remove() + return true end @@ -976,8 +971,7 @@ function mob_class:do_env_damage() pos.y = pos.y + 1 -- for particle effect position -- water - if self.water_damage - and nodef.groups.water then + if self.water_damage and nodef.groups.water then if self.water_damage ~= 0 then @@ -989,12 +983,8 @@ function mob_class:do_env_damage() pos = pos, node = self.standing_in}) then return true end end - -- lava or fire or ignition source - elseif self.lava_damage - and nodef.groups.igniter then --- and (nodef.groups.lava --- or self.standing_in == node_fire --- or self.standing_in == node_permanent_flame) then + -- ignition source (fire or lava) + elseif self.lava_damage and nodef.groups.igniter then if self.lava_damage ~= 0 then @@ -1025,6 +1015,7 @@ function mob_class:do_env_damage() and (nodef.groups.disable_suffocation ~= 1) then local damage + if self.suffocation == true then damage = 2 else @@ -1147,14 +1138,14 @@ function mob_class:do_jump() and (self.facing_fence or blocked) then self.jump_count = (self.jump_count or 0) + 1 ---print ("----", self.jump_count) + if self.jump_count > 4 then local yaw = self.object:get_yaw() or 0 local turn = random(0, 2) + 1.35 yaw = self:set_yaw(yaw + turn, 12) ---print ("---- turn", turn) + self.jump_count = 0 end end @@ -1460,11 +1451,11 @@ end local los_switcher = false local height_switcher = false --- path finding and smart mob routine by rnd, line_of_sight and other edits by Elkien3 +-- path finding and smart mob routine by rnd, +-- line_of_sight and other edits by Elkien3 function mob_class:smart_mobs(s, p, dist, dtime) local s1 = self.path.lastpos - local target_pos = self.attack:get_pos() -- is it becoming stuck? @@ -1563,7 +1554,6 @@ function mob_class:smart_mobs(s, p, dist, dtime) -- round position to center of node to avoid stuck in walls -- also adjust height for player models! s.x = floor(s.x + 0.5) --- s.y = floor(s.y + 0.5) - sheight s.z = floor(s.z + 0.5) local ssight, sground = minetest.line_of_sight(s, { @@ -1581,9 +1571,11 @@ function mob_class:smart_mobs(s, p, dist, dtime) p1.z = floor(p1.z + 0.5) local dropheight = 6 + if self.fear_height ~= 0 then dropheight = self.fear_height end local jumpheight = 0 + if self.jump and self.jump_height >= 4 then jumpheight = min(math.ceil(self.jump_height / 4), 4) elseif self.stepheight > 0.5 then @@ -2040,6 +2032,7 @@ function mob_class:follow_flop() self:set_animation("stand") return + elseif self.state == "flop" then self.state = "stand" end @@ -2153,10 +2146,19 @@ function mob_class:do_states(dtime) if lp then -- if mob in dangerous node then look for land - if is_node_dangerous(self, self.standing_in) then + if not is_node_dangerous(self, self.standing_in) then - lp = minetest.find_node_near(s, 5, {"group:soil", "group:stone", - "group:sand", node_ice, node_snowblock}) +-- lp = minetest.find_node_near(s, 5, {"group:soil", "group:stone", +-- "group:sand", node_ice, node_snowblock}) + + lp = minetest.find_nodes_in_area_under_air( + {s.x - 5, s.y - 5, s.z - 5}, + {s.x + 5, s.y + 5, s.z + 5}, + {"group:soil", "group:stone", "group:sand", + node_ice, node_snowblock}) + + -- select position of random block to climb onto + lp = #lp > 0 and lp[random(#lp)] -- did we find land? if lp then From bd06ad8d6ed1965c570bdce0384947e1cfd1aa12 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 7 May 2020 11:15:04 +0100 Subject: [PATCH 061/135] added pos_to_yaw function, added fence function check, cody tidy --- api.lua | 179 +++++++++++++++++------------------------------------ crafts.lua | 8 +++ 2 files changed, 66 insertions(+), 121 deletions(-) diff --git a/api.lua b/api.lua index b4dbcbc..3c0ff5b 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200505", + version = "20200508", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -572,6 +572,29 @@ function mob_class:flight_check() end +-- turn mob to face position +local yaw_to_pos = function(self, target, rot) + + rot = rot or 0 + + local pos = self.object:get_pos() + local vec = {x = target.x - pos.x, z = target.z - pos.z} + local yaw = (atan(vec.z / vec.x) + rot + pi / 2) - self.rotate + + if target.x > pos.x then + yaw = yaw + pi + end + + yaw = self:set_yaw(yaw, 6) + + return yaw +end + +function mobs:turn_to_pos(self, target, rot) + return turn_to_pos(self, target, rot) +end + + -- if self.stay_near set then check periodically for nodes and turn to face/move function mob_class:do_stay_near() @@ -598,17 +621,7 @@ function mob_class:do_stay_near() return false end - local target = nearby_nodes[math.random(1, #nearby_nodes)] - local direction = vector.direction(pos, target) - local vec = {x = target.x - pos.x, z = target.z - pos.z} - - local yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if target.x > pos.x then - yaw = yaw + pi - end - - yaw = self:set_yaw(yaw, 4) + yaw_to_pos(self, nearby_nodes[random(#nearby_nodes)]) self:set_animation("walk") @@ -1900,20 +1913,8 @@ function mob_class:do_runaway_from() if min_player then - local lp = player:get_pos() - local vec = { - x = lp.x - s.x, - y = lp.y - s.y, - z = lp.z - s.z - } + yaw_to_pos(self, min_player:get_pos(), 3) - local yaw = (atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate - - if lp.x > s.x then - yaw = yaw + pi - end - - yaw = self:set_yaw(yaw, 4) self.state = "runaway" self.runaway_timer = 3 self.following = nil @@ -1989,16 +1990,7 @@ function mob_class:follow_flop() if dist > self.view_range then self.following = nil else - local vec = { - x = p.x - s.x, - z = p.z - s.z - } - - local yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if p.x > s.x then yaw = yaw + pi end - - yaw = self:set_yaw(yaw, 6) + yaw_to_pos(self, p) -- anyone but standing npc's can move along if dist > self.reach @@ -2078,7 +2070,7 @@ function mob_class:do_states(dtime) if random(1, 4) == 1 then - local lp = nil + local lp local s = self.object:get_pos() local objs = minetest.get_objects_inside_radius(s, 3) @@ -2092,15 +2084,7 @@ function mob_class:do_states(dtime) -- look at any players nearby, otherwise turn randomly if lp then - - local vec = { - x = lp.x - s.x, - z = lp.z - s.z - } - - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if lp.x > s.x then yaw = yaw + pi end + yaw = yaw_to_pos(self, lp) else yaw = yaw + random(-0.5, 0.5) end @@ -2148,12 +2132,9 @@ function mob_class:do_states(dtime) -- if mob in dangerous node then look for land if not is_node_dangerous(self, self.standing_in) then --- lp = minetest.find_node_near(s, 5, {"group:soil", "group:stone", --- "group:sand", node_ice, node_snowblock}) - lp = minetest.find_nodes_in_area_under_air( - {s.x - 5, s.y - 5, s.z - 5}, - {s.x + 5, s.y + 5, s.z + 5}, + {s.x - 5, s.y - 1, s.z - 5}, + {s.x + 5, s.y + 2, s.z + 5}, {"group:soil", "group:stone", "group:sand", node_ice, node_snowblock}) @@ -2163,33 +2144,13 @@ function mob_class:do_states(dtime) -- did we find land? if lp then - local vec = { - x = lp.x - s.x, - z = lp.z - s.z - } + yaw = yaw_to_pos(self, lp) - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if lp.x > s.x then yaw = yaw + pi end - - -- look towards land and jump/move in that direction - yaw = self:set_yaw(yaw, 6) self:do_jump() self:set_velocity(self.walk_velocity) else yaw = yaw + random(-0.5, 0.5) end - - else - - local vec = { - x = lp.x - s.x, - z = lp.z - s.z - } - - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if lp.x > s.x then yaw = yaw + pi end end yaw = self:set_yaw(yaw, 8) @@ -2267,7 +2228,8 @@ function mob_class:do_states(dtime) 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" self:set_velocity(0) self:set_animation("stand") @@ -2282,21 +2244,15 @@ function mob_class:do_states(dtime) if self.attack_type == "explode" then - local vec = { - x = p.x - s.x, - z = p.z - s.z - } - - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if p.x > s.x then yaw = yaw + pi end - - yaw = self:set_yaw(yaw) + yaw = yaw_to_pos(self, p) local node_break_radius = self.explosion_radius or 1 local entity_damage_radius = self.explosion_damage_radius or (node_break_radius * 2) + -- look a little higher to fix raycast + s.y = s.y + 0.5 ; p.y = p.y + 0.5 + -- start timer when in reach and line of sight if not self.v_start and dist <= self.reach @@ -2306,13 +2262,16 @@ function mob_class:do_states(dtime) self.timer = 0 self.blinktimer = 0 self:mob_sound(self.sounds.fuse) --- print ("=== explosion timer started", self.explosion_timer) + +--print ("=== explosion timer started", self.explosion_timer) -- stop timer if out of reach or direct line of sight elseif self.allow_fuse_reset and self.v_start - and (dist > self.reach - or not self:line_of_sight(s, p, 2)) then + and (dist > self.reach or not self:line_of_sight(s, p, 2)) then + +--print ("=== explosion timer stopped") + self.v_start = false self.timer = 0 self.blinktimer = 0 @@ -2351,7 +2310,7 @@ function mob_class:do_states(dtime) self.blinkstatus = not self.blinkstatus end --- print ("=== explosion timer", self.timer) +--print ("=== explosion timer", self.timer) if self.timer > self.explosion_timer then @@ -2470,16 +2429,7 @@ function mob_class:do_states(dtime) p = {x = p1.x, y = p1.y, z = p1.z} end - local vec = { - x = p.x - s.x, - z = p.z - s.z - } - - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if p.x > s.x then yaw = yaw + pi end - - yaw = self:set_yaw(yaw) + yaw = yaw_to_pos(self, p) -- move towards enemy if beyond mob reach if dist > self.reach then @@ -2560,23 +2510,21 @@ function mob_class:do_states(dtime) elseif self.attack_type == "shoot" or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 1) - or (self.attack_type == "dogshoot" and dist > self.reach and self:dogswitch() == 0) then + or (self.attack_type == "dogshoot" and dist > self.reach and + self:dogswitch() == 0) then p.y = p.y - .5 s.y = s.y + .5 local dist = get_distance(p, s) + local vec = { x = p.x - s.x, y = p.y - s.y, z = p.z - s.z } - yaw = (atan(vec.z / vec.x) + pi / 2) - self.rotate - - if p.x > s.x then yaw = yaw + pi end - - yaw = self:set_yaw(yaw) + yaw = yaw_to_pos(self, p) self:set_velocity(0) @@ -2773,7 +2721,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) return end --- print ("Mob Damage is", damage) +--print ("Mob Damage is", damage) if use_cmi and cmi.notify_punch( @@ -2912,20 +2860,8 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) and self.order ~= "stand" then local lp = hitter:get_pos() - local s = self.object:get_pos() - local vec = { - x = lp.x - s.x, - y = lp.y - s.y, - z = lp.z - s.z - } + local yaw = yaw_to_pos(self, lp, 3) - local yaw = (atan(vec.z / vec.x) + 3 * pi / 2) - self.rotate - - if lp.x > s.x then - yaw = yaw + pi - end - - yaw = self:set_yaw(yaw, 6) self.state = "runaway" self.runaway_timer = 0 self.following = nil @@ -2985,11 +2921,10 @@ function mob_class:mob_staticdata() and not self.tamed and self.lifetimer < 20000 then --- print ("REMOVED " .. self.name) +--print ("REMOVED " .. self.name) self.object:remove() - --return "" -- nil return minetest.serialize({remove_ok = true, static_save = true}) end @@ -3023,7 +2958,8 @@ function mob_class:mob_staticdata() end end - --print('===== '..self.name..'\n'.. dump(tmp)..'\n=====\n') +--print('===== '..self.name..'\n'.. dump(tmp)..'\n=====\n') + return minetest.serialize(tmp) end @@ -3264,7 +3200,8 @@ function mob_class:on_step(dtime) -- what is mob standing in? self.standing_in = node_ok({ x = pos.x, y = pos.y + y_level + 0.25, z = pos.z}, "air").name --- print ("standing in " .. self.standing_in) + +--print ("standing in " .. self.standing_in) -- if standing inside solid block then jump to escape if minetest.registered_nodes[self.standing_in].walkable and @@ -3404,7 +3341,7 @@ end -- default function when mobs are blown up with TNT function mob_class:on_blast(damage) - --print ("----- Damage", damage) +--print ("----- Damage", damage) self.object:punch(self.object, 1.0, { full_punch_interval = 1.0, diff --git a/crafts.lua b/crafts.lua index 7806ca8..890fe21 100644 --- a/crafts.lua +++ b/crafts.lua @@ -131,6 +131,10 @@ minetest.register_craft({ } }) + +-- make sure we can register fences +if default.register_fence then + -- mob fence (looks like normal fence but collision is 2 high) default.register_fence("mobs:fence_wood", { description = S("Mob Fence"), @@ -177,6 +181,9 @@ minetest.register_craft({ } }) +end + + -- items that can be used as fuel minetest.register_craft({ type = "fuel", @@ -220,6 +227,7 @@ minetest.register_craft({ burntime = 2 }) + -- this tool spawns same mob and adds owner, protected, nametag info -- then removes original entity, this is used for fixing any issues. From f32b69a65460fd5fe3338a8c563622d6969d97e7 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 7 May 2020 11:35:29 +0100 Subject: [PATCH 062/135] rename global function --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 3c0ff5b..250f9e9 100644 --- a/api.lua +++ b/api.lua @@ -590,8 +590,8 @@ local yaw_to_pos = function(self, target, rot) return yaw end -function mobs:turn_to_pos(self, target, rot) - return turn_to_pos(self, target, rot) +function mobs:yaw_to_pos(self, target, rot) + return yaw_to_pos(self, target, rot) end From f576947d41a72ead29720d4a524f0ac78a796b81 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 8 May 2020 20:33:41 +0100 Subject: [PATCH 063/135] add nil check for mob eggs --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 250f9e9..593049e 100644 --- a/api.lua +++ b/api.lua @@ -3972,7 +3972,7 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) local data = itemstack:get_metadata() local mob = minetest.add_entity(pos, mob, data) - local ent = mob:get_luaentity() + local ent = mob and mob:get_luaentity() if not ent then return end -- sanity check @@ -4020,7 +4020,7 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) pos.y = pos.y + 1 local mob = minetest.add_entity(pos, mob) - local ent = mob:get_luaentity() + local ent = mob and mob:get_luaentity() if not ent then return end -- sanity check From 831a5fc888fe323bc9aac9293dad1e925e1a4ece Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Mon, 11 May 2020 12:43:28 +0100 Subject: [PATCH 064/135] update italian locale (thanks Hamlet) --- locale/it.po | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/locale/it.po b/locale/it.po index a439f6d..a40c33f 100644 --- a/locale/it.po +++ b/locale/it.po @@ -8,19 +8,19 @@ msgstr "" "Project-Id-Version: Italian locale file for the Mobs Redo module\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-02 16:48+0200\n" -"PO-Revision-Date: 2017-08-18 12:18+0100\n" -"Last-Translator: H4mlet \n" +"PO-Revision-Date: 2020-05-11 13:33+0200\n" +"Last-Translator: Hamlet \n" "Language-Team: \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.6.10\n" +"X-Generator: Poedit 2.2.1\n" #: api.lua msgid "** Peaceful Mode Active - No Monsters Will Spawn" -msgstr "" +msgstr "** Modalità pacifica attiva - non comparirà nessun mostro" #: api.lua msgid "Mob has been protected!" @@ -28,23 +28,23 @@ msgstr "Il mob è stato protetto!" #: api.lua msgid "@1 (Tamed)" -msgstr "@1 (Addomesticat*)" +msgstr "@1 (Addomesticato)" #: api.lua msgid "Not tamed!" -msgstr "Non addomesticat*!" +msgstr "Non addomesticato!" #: api.lua msgid "@1 is owner!" -msgstr "Proprietari* @1!" +msgstr "Il padrone è @1!" #: api.lua msgid "Missed!" -msgstr "Mancat*!" +msgstr "Mancato!" #: api.lua msgid "Already protected!" -msgstr "Già protett*!" +msgstr "Già protetto!" #: api.lua msgid "@1 at full health (@2)" @@ -52,7 +52,7 @@ msgstr "@1 in piena salute (@2)" #: api.lua msgid "@1 has been tamed!" -msgstr "@1 è stat* addomesticat*!" +msgstr "@1 è stato addomesticato!" #: api.lua msgid "Enter name:" @@ -60,7 +60,7 @@ msgstr "Inserire il nome:" #: api.lua msgid "Rename" -msgstr "Rinominare" +msgstr "Rinomina" #: crafts.lua msgid "Name Tag" @@ -100,7 +100,7 @@ msgstr "Sella" #: crafts.lua msgid "Mob Fence" -msgstr "" +msgstr "Recinzione per mob" #: spawner.lua msgid "Mob Spawner" @@ -128,4 +128,4 @@ msgid "" "distance[1-20] y_offset[-10 to 10]”" msgstr "" "Sintassi: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 per " -"disabilitare] distance[1-20] y_offset[-10 to 10]”" +"disabilitare] distance[1-20] y_offset[-10 fino a 10]”" From b610a81a588f21ee930a01bd59c35a850875a303 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Wed, 13 May 2020 08:12:56 +0100 Subject: [PATCH 065/135] fixed table search issue for replacing nodes, added 'group_helper' for group attacks --- api.lua | 14 +++++++++----- api.txt | 3 +++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/api.lua b/api.lua index 593049e..9a3d47d 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200508", + version = "20200513", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -1430,8 +1430,9 @@ function mob_class:replace(pos) local oldnode = what or "" local newnode = with - -- convert any group: replacements to actual node name - if oldnode:find("group:") then + -- pass actual node name when using table or groups + if type(oldnode) == "table" + or oldnode:find("group:") then oldnode = minetest.get_node(pos).name end @@ -2892,11 +2893,13 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) if obj and obj._cmi_is_mob then - -- only alert members of same mob + -- only alert members of same mob and assigned helper if obj.group_attack == true and obj.state ~= "attack" and obj.owner ~= name - and obj.name == self.name then + and (obj.name == self.name + or obj.name == self.group_helper) then + obj:do_attack(hitter) end @@ -3440,6 +3443,7 @@ minetest.register_entity(name, setmetatable({ dogshoot_count_max = def.dogshoot_count_max, dogshoot_count2_max = def.dogshoot_count2_max or def.dogshoot_count_max, group_attack = def.group_attack, + group_helper = def.group_helper, attack_monsters = def.attacks_monsters or def.attack_monsters, attack_animals = def.attack_animals, attack_players = def.attack_players, diff --git a/api.txt b/api.txt index 06ba531..85a41d9 100644 --- a/api.txt +++ b/api.txt @@ -89,6 +89,9 @@ functions needed for the mob to work properly which contains the following: punches when nearby. 'group_attack' when true has same mob type grouping together to attack offender. + 'group_helper' string containing mob name that attacks alongside + current mob when group attacking. + mob is attacking in groups. '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. From 53cc10a16fffcbf221bcb23ec49dab24d5f55cf3 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Fri, 15 May 2020 13:26:34 +0100 Subject: [PATCH 066/135] Code tweak and tidy, mob counter attempt --- api.lua | 398 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 249 insertions(+), 149 deletions(-) diff --git a/api.lua b/api.lua index 9a3d47d..ba92e60 100644 --- a/api.lua +++ b/api.lua @@ -6,15 +6,21 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200513", + version = "20200515", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } +-- mob table and limit (not active) +local active_mobs = 0 +local active_limit = 99 + + -- creative check local creative_cache = minetest.settings:get_bool("creative_mode") function mobs.is_creative(name) - return creative_cache or minetest.check_player_privs(name, {creative = true}) + return creative_cache or minetest.check_player_privs(name, + {creative = true}) end @@ -68,7 +74,7 @@ local aoc_range = tonumber(minetest.settings:get("active_block_range")) * 16 -- pathfinding settings local enable_pathfinding = true -local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching +local stuck_timeout = 3 -- how long before stuck mod starts searching local stuck_path_timeout = 10 -- how long will mob follow path before giving up -- default nodes @@ -337,7 +343,8 @@ function mob_class:set_animation(anim, force) self.object:set_animation({ x = self.animation[anim .. "_start"], y = self.animation[anim .. "_end"]}, - self.animation[anim .. "_speed"] or self.animation.speed_normal or 15, + self.animation[anim .. "_speed"] or + self.animation.speed_normal or 15, 0, self.animation[anim .. "_loop"] ~= false) end @@ -468,18 +475,16 @@ local ray_line_of_sight = function(self, pos1, pos2) local ray = minetest.raycast(pos1, pos2, true, false) local thing = ray:next() - while thing do - --- if thing.type == "object" --- and thing.ref ~= self.object --- and not thing.ref:is_player() then return false end + while thing do -- thing.type, thing.ref if thing.type == "node" then local name = minetest.get_node(thing.under).name if minetest.registered_items[name] - and minetest.registered_items[name].walkable then return false end + and minetest.registered_items[name].walkable then + return false + end end thing = ray:next() @@ -521,8 +526,7 @@ function mob_class:attempt_flight_correction(override) local flyable_nodes = minetest.find_nodes_in_area( {x = pos.x - 1, y = pos.y - 1, z = pos.z - 1}, - {x = pos.x + 1, y = pos.y + 1, z = pos.z + 1}, - searchnodes) + {x = pos.x + 1, y = pos.y + 1, z = pos.z + 1}, searchnodes) if #flyable_nodes < 1 then return false @@ -595,7 +599,7 @@ function mobs:yaw_to_pos(self, target, rot) end --- if self.stay_near set then check periodically for nodes and turn to face/move +-- if stay near set then check periodically for nodes and turn towards them function mob_class:do_stay_near() if not self.stay_near then return false end @@ -632,8 +636,8 @@ end -- custom particle effects -local effect = function( - pos, amount, texture, min_size, max_size, radius, gravity, glow, fall) +local effect = function(pos, amount, texture, min_size, max_size, + radius, gravity, glow, fall) radius = radius or 2 min_size = min_size or 0.5 @@ -695,7 +699,8 @@ function mob_class:item_drop() local pos = self.object:get_pos() -- check for drops function - self.drops = type(self.drops) == "function" and self.drops(pos) or self.drops + self.drops = type(self.drops) == "function" + and self.drops(pos) or self.drops -- check for nil or no drops if not self.drops or #self.drops == 0 then @@ -703,7 +708,8 @@ function mob_class:item_drop() end -- was mob killed by player? - local death_by_player = self.cause_of_death and self.cause_of_death.puncher + local death_by_player = self.cause_of_death + and self.cause_of_death.puncher and self.cause_of_death.puncher:is_player() or nil local obj, item, num @@ -752,6 +758,23 @@ function mob_class:item_drop() end +-- remove mob and descrease counter +local remove_mob = function(self, decrease) + + self.object:remove() + + if decrease then + +-- active_mobs = active_mobs - 1 + + if active_mobs < 0 then + active_mobs = 0 + end + end +--print("-- active mobs: " .. active_mobs .. " / " .. active_limit) +end + + -- check if mob is dead or only hurt function mob_class:check_for_death(cmi_cause) @@ -812,7 +835,7 @@ function mob_class:check_for_death(cmi_cause) cmi.notify_die(self.object, cmi_cause) end - self.object:remove() + remove_mob(self, true) return true end @@ -841,7 +864,8 @@ function mob_class:check_for_death(cmi_cause) cmi.notify_die(self.object, cmi_cause) end - self.object:remove() + remove_mob(self, true) + end, self) else @@ -849,7 +873,7 @@ function mob_class:check_for_death(cmi_cause) cmi.notify_die(self.object, cmi_cause) end - self.object:remove() + remove_mob(self, true) end effect(pos, 20, "tnt_smoke.png") @@ -955,10 +979,10 @@ function mob_class:do_env_damage() self.time_of_day = minetest.get_timeofday() - -- remove mob if standing inside ignore node + -- halt mob if standing inside ignore node if self.standing_in == "ignore" then - self.object:remove() + self.object:set_velocity(0) return true end @@ -975,7 +999,9 @@ function mob_class:do_env_damage() effect(pos, 5, "tnt_smoke.png") - if self:check_for_death({type = "light"}) then return true end + if self:check_for_death({type = "light"}) then + return true + end end end @@ -993,7 +1019,9 @@ function mob_class:do_env_damage() effect(pos, 5, "bubble.png", nil, nil, 1, nil) if self:check_for_death({type = "environment", - pos = pos, node = self.standing_in}) then return true end + pos = pos, node = self.standing_in}) then + return true + end end -- ignition source (fire or lava) @@ -1006,7 +1034,9 @@ function mob_class:do_env_damage() effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil) if self:check_for_death({type = "environment", pos = pos, - node = self.standing_in, hot = true}) then return true end + node = self.standing_in, hot = true}) then + return true + end end -- damage_per_second node check @@ -1017,7 +1047,9 @@ function mob_class:do_env_damage() effect(pos, 5, "tnt_smoke.png") if self:check_for_death({type = "environment", - pos = pos, node = self.standing_in}) then return true end + pos = pos, node = self.standing_in}) then + return true + end end --- suffocation inside solid node @@ -1038,7 +1070,9 @@ function mob_class:do_env_damage() self.health = self.health - damage if self:check_for_death({type = "suffocation", - pos = pos, node = self.standing_in}) then return true end + pos = pos, node = self.standing_in}) then + return true + end end return self:check_for_death({type = "unknown"}) @@ -1076,7 +1110,7 @@ function mob_class:do_jump() local nod = node_ok(pos) ---print ("standing on:", nod.name, pos.y) +--print("standing on:", nod.name, pos.y) if minetest.registered_nodes[nod.name].walkable == false then return false @@ -1102,8 +1136,8 @@ function mob_class:do_jump() local blocked = minetest.registered_nodes[nodt.name].walkable ---print ("in front:", nod.name, pos.y + 0.5) ---print ("in front above:", nodt.name, pos.y + 1.5) +--print("in front:", nod.name, pos.y + 0.5) +--print("in front above:", nodt.name, pos.y + 1.5) -- jump if standing on solid node (not snow) and not blocked above if (self.walk_chance == 0 @@ -1326,6 +1360,15 @@ function mob_class:breed() self.hornytimer = 41 ent.hornytimer = 41 + -- have we reached active mob limit + if active_mobs >= active_limit then + minetest.chat_send_player(self.owner, + S("Active Mob Limit Reached!") + .. " (" .. active_mobs + .. " / " .. active_limit .. ")") + return + end + -- spawn baby minetest.after(5, function(self, ent) @@ -1348,6 +1391,8 @@ function mob_class:breed() local ent2 = mob:get_luaentity() local textures = self.base_texture +-- active_mobs = active_mobs + 1 + -- using specific child texture (if found) if self.child_texture then textures = self.child_texture[1] @@ -1423,7 +1468,7 @@ function mob_class:replace(pos) if #minetest.find_nodes_in_area(pos, pos, what) > 0 then --- print ("replace node = ".. minetest.get_node(pos).name, pos.y) +-- print("replace node = ".. minetest.get_node(pos).name, pos.y) if self.on_replace then @@ -1602,7 +1647,7 @@ function mob_class:smart_mobs(s, p, dist, dtime) --[[ -- show path using particles if self.path.way and #self.path.way > 0 then - print ("-- path length:" .. tonumber(#self.path.way)) + print("-- path length:" .. tonumber(#self.path.way)) for _,pos in pairs(self.path.way) do minetest.add_particle({ pos = pos, @@ -1774,7 +1819,7 @@ function mob_class:general_attack() -- are we a player? if objs[n]:is_player() then - -- if player invisible or mob not setup to attack then remove from list + -- if player invisible or mob cannot attack then remove from list if self.attack_players == false or (self.owner and self.type ~= "monster") or mobs.invis[objs[n]:get_player_name()] @@ -2264,14 +2309,14 @@ function mob_class:do_states(dtime) self.blinktimer = 0 self:mob_sound(self.sounds.fuse) ---print ("=== explosion timer started", self.explosion_timer) +--print("=== explosion timer started", self.explosion_timer) -- stop timer if out of reach or direct line of sight elseif self.allow_fuse_reset and self.v_start and (dist > self.reach or not self:line_of_sight(s, p, 2)) then ---print ("=== explosion timer stopped") +--print("=== explosion timer stopped") self.v_start = false self.timer = 0 @@ -2311,7 +2356,7 @@ function mob_class:do_states(dtime) self.blinkstatus = not self.blinkstatus end ---print ("=== explosion timer", self.timer) +--print("=== explosion timer", self.timer) if self.timer > self.explosion_timer then @@ -2324,7 +2369,7 @@ function mob_class:do_states(dtime) node_break_radius = 1 end - self.object:remove() + remove_mob(self, true) if minetest.get_modpath("tnt") and tnt and tnt.boom and not minetest.is_protected(pos, "") then @@ -2343,7 +2388,9 @@ function mob_class:do_states(dtime) }) entity_physics(pos, entity_damage_radius) - effect(pos, 32, "tnt_smoke.png", nil, nil, node_break_radius, 1, 0) + + effect(pos, 32, "tnt_smoke.png", nil, nil, + node_break_radius, 1, 0) end return true @@ -2643,7 +2690,7 @@ end local tr = minetest.get_modpath("toolranks") -- deal damage and effects when mob punched -function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) +function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage) -- mob health check if self.health <= 0 then @@ -2658,14 +2705,19 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) -- error checking when mod profiling is enabled if not tool_capabilities then - minetest.log("warning", "[mobs] Mod profiling enabled, damage not enabled") + minetest.log("warning", + "[mobs] Mod profiling enabled, damage not enabled") return end -- is mob protected? if self.protected and hitter:is_player() - 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!")) + 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 @@ -2710,20 +2762,20 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) damage = self.immune_to[n][2] or 0 break - -- if "all" then no tool does damage unless it's specified in list + -- if "all" then no tools deal damage unless it's specified in list elseif self.immune_to[n][1] == "all" then damage = self.immune_to[n][2] or 0 end end +--print("Mob Damage is", damage) + -- healing if damage <= -1 then self.health = self.health - floor(damage) - return + return true end ---print ("Mob Damage is", damage) - if use_cmi and cmi.notify_punch( self.object, hitter, tflp, tool_capabilities, dir, damage) then @@ -2781,7 +2833,8 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) local blood = self.blood_texture local amount = self.blood_amount - pos.y = pos.y + (-self.collisionbox[2] + self.collisionbox[5]) * .5 + pos.y = pos.y + (-self.collisionbox[2] + + self.collisionbox[5]) * .5 -- lots of damage = more blood :) if damage > 10 then @@ -2806,10 +2859,10 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) if self:check_for_death({type = "punch", puncher = hitter, hot = hot}) then - return + return true end - --[[ add healthy afterglow when hit (can cause hit lag with larger textures) + --[[ add healthy afterglow when hit (causes lag with large textures) minetest.after(0.1, function() if not self.object:get_luaentity() then return end @@ -2924,9 +2977,9 @@ function mob_class:mob_staticdata() and not self.tamed and self.lifetimer < 20000 then ---print ("REMOVED " .. self.name) +--print("REMOVED " .. self.name) - self.object:remove() + remove_mob(self, true) return minetest.serialize({remove_ok = true, static_save = true}) end @@ -2937,8 +2990,7 @@ function mob_class:mob_staticdata() self.state = "stand" -- used to rotate older mobs - if self.drawtype - and self.drawtype == "side" then + if self.drawtype and self.drawtype == "side" then self.rotate = math.rad(90) end @@ -2971,14 +3023,21 @@ end function mob_class:mob_activate(staticdata, def, dtime) -- remove monsters in peaceful mode - if self.type == "monster" - and peaceful_only then + if self.type == "monster" and peaceful_only then - self.object:remove() + remove_mob(self, true) return end + -- remove mob if not tamed and mob total reached + if active_mobs >= active_limit and not self.tamed then + + remove_mob(self) +--print("-- mob limit reached, removing " .. self.name) + return + end + -- load entity variables local tmp = minetest.deserialize(staticdata) @@ -2988,6 +3047,13 @@ function mob_class:mob_activate(staticdata, def, dtime) end end + -- add currently spawned mobs to total + -- this is buggy as it doubles count when mobs unloaded and reloaded + if self.standing_in then +-- active_mobs = active_mobs + 1 +--print("-- active mobs: " .. active_mobs .. " / " .. active_limit) + end + -- force current model into mob self.mesh = def.mesh self.base_mesh = def.mesh @@ -3002,7 +3068,8 @@ function mob_class:mob_activate(staticdata, def, dtime) def.textures = {def.textures} end - self.base_texture = def.textures and def.textures[random(1, #def.textures)] + self.base_texture = def.textures and + def.textures[random(1, #def.textures)] self.base_mesh = def.mesh self.base_size = self.visual_size self.base_colbox = self.collisionbox @@ -3022,14 +3089,12 @@ function mob_class:mob_activate(staticdata, def, dtime) local selbox = self.base_selbox -- specific texture if gotten - if self.gotten == true - and def.gotten_texture then + if self.gotten == true and def.gotten_texture then textures = def.gotten_texture end -- specific mesh if gotten - if self.gotten == true - and def.gotten_mesh then + if self.gotten == true and def.gotten_mesh then mesh = def.gotten_mesh end @@ -3046,21 +3111,14 @@ function mob_class:mob_activate(staticdata, def, dtime) end colbox = { - self.base_colbox[1] * .5, - self.base_colbox[2] * .5, - self.base_colbox[3] * .5, - self.base_colbox[4] * .5, - self.base_colbox[5] * .5, - self.base_colbox[6] * .5 - } + self.base_colbox[1] * .5, self.base_colbox[2] * .5, + self.base_colbox[3] * .5, self.base_colbox[4] * .5, + self.base_colbox[5] * .5, self.base_colbox[6] * .5} + selbox = { - self.base_selbox[1] * .5, - self.base_selbox[2] * .5, - self.base_selbox[3] * .5, - self.base_selbox[4] * .5, - self.base_selbox[5] * .5, - self.base_selbox[6] * .5 - } + self.base_selbox[1] * .5, self.base_selbox[2] * .5, + self.base_selbox[3] * .5, self.base_selbox[4] * .5, + self.base_selbox[5] * .5, self.base_selbox[6] * .5} end if self.health == 0 then @@ -3075,15 +3133,14 @@ function mob_class:mob_activate(staticdata, def, dtime) self.path.following = false -- currently following path? self.path.stuck_timer = 0 -- if stuck for too long search for path - -- Armor groups - -- immortal=1 because we use custom health - -- handling (using "health" property) + -- Armor groups (immortal = 1 for custom damage handling) local armor if type(self.armor) == "table" then armor = table.copy(self.armor) - armor.immortal = 1 +-- armor.immortal = 1 else - armor = {immortal = 1, fleshy = self.armor} +-- armor = {immortal = 1, fleshy = self.armor} + armor = {fleshy = self.armor} end self.object:set_armor_groups(armor) @@ -3166,7 +3223,7 @@ function mob_class:mob_expire(pos, dtime) effect(pos, 15, "tnt_smoke.png", 2, 4, 2, 0) - self.object:remove() + remove_mob(self, true) return end @@ -3175,7 +3232,23 @@ end -- main mob function -function mob_class:on_step(dtime) +function mob_class:on_step(dtime, moveresult) + + --[[ moveresult contains this for physical mobs + { + touching_ground = boolean, + collides = boolean, + standing_on_object = boolean, + collisions = { + { + type = string, -- "node" or "object", + axis = string, -- "x", "y" or "z" + node_pos = vector, -- if type is "node" + object = ObjectRef, -- if type is "object" + old_velocity = vector, + new_velocity = vector, + }} + }]] if use_cmi then cmi.notify_step(self.object, dtime) @@ -3204,11 +3277,12 @@ function mob_class:on_step(dtime) self.standing_in = node_ok({ x = pos.x, y = pos.y + y_level + 0.25, z = pos.z}, "air").name ---print ("standing in " .. self.standing_in) +--print("standing in " .. self.standing_in) -- if standing inside solid block then jump to escape - if minetest.registered_nodes[self.standing_in].walkable and - minetest.registered_nodes[self.standing_in].drawtype == "normal" then + if minetest.registered_nodes[self.standing_in].walkable + and minetest.registered_nodes[self.standing_in].drawtype + == "normal" then self.object:set_velocity({ x = 0, @@ -3224,7 +3298,7 @@ function mob_class:on_step(dtime) self:set_velocity(0) end - -- check for mob expiration (0.25 instead of dtime since were in a timer) + -- has mob expired (0.25 instead of dtime since were in a timer) self:mob_expire(pos, 0.25) end @@ -3234,7 +3308,6 @@ function mob_class:on_step(dtime) end -- smooth rotation by ThomasMonroe314 - if self.delay and self.delay > 0 then if self.delay == 1 then @@ -3269,8 +3342,6 @@ function mob_class:on_step(dtime) self.object:set_yaw(yaw) end - -- end rotation - -- knockback timer if self.pause_timer > 0 then @@ -3344,7 +3415,7 @@ end -- default function when mobs are blown up with TNT function mob_class:on_blast(damage) ---print ("----- Damage", damage) +--print("-- blast damage", damage) self.object:punch(self.object, 1.0, { full_punch_interval = 1.0, @@ -3378,7 +3449,7 @@ minetest.register_entity(name, setmetatable({ do_custom = def.do_custom, jump_height = def.jump_height, drawtype = def.drawtype, -- DEPRECATED, use rotate instead - rotate = math.rad(def.rotate or 0), -- 0=front, 90=side, 180=back, 270=side2 + rotate = math.rad(def.rotate or 0), -- 0=front 90=side 180=back 270=side2 glow = def.glow, lifetimer = def.lifetimer, hp_min = max(1, (def.hp_min or 5) * difficulty), @@ -3514,7 +3585,7 @@ end function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, - interval, chance, aoc, min_height, max_height, day_toggle, on_spawn) + interval, chance, aoc, min_height, max_height, day_toggle, on_spawn) -- Do mobs spawn at all? if not mobs_spawn then @@ -3550,12 +3621,19 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, chance = max(1, (chance * mob_chance_multiplier)), catch_up = false, - action = function(pos, node, active_object_count, active_object_count_wider) + action = function(pos, node, active_object_count, + active_object_count_wider) -- is mob actually registered? if not mobs.spawning_mobs[name] or not minetest.registered_entities[name] then ---print ("--- mob doesn't exist", name) +--print("--- mob doesn't exist", name) + return + end + + -- are we over active mob limit + if active_mobs >= active_limit then +--print("--- active mob limit reached", active_mobs, active_limit) return end @@ -3574,12 +3652,12 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, local num_mob, is_pla = count_mobs(pos, name) if not is_pla then ---print ("--- no players within active area, will not spawn " .. name) +--print("--- no players within active area, will not spawn " .. name) return end if num_mob >= aoc then ---print ("--- too many " .. name .. " in area", num_mob .. "/" .. aoc) +--print("--- too many " .. name .. " in area", num_mob .. "/" .. aoc) return end @@ -3591,13 +3669,13 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, if tod > 4500 and tod < 19500 then -- daylight, but mob wants night if day_toggle == false then ---print ("--- mob needs night", name) +--print("--- mob needs night", name) return end else -- night time but mob wants day if day_toggle == true then ---print ("--- mob needs day", name) +--print("--- mob needs day", name) return end end @@ -3609,7 +3687,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, -- are we spawning within height limits? if pos.y > max_height or pos.y < min_height then ---print ("--- height limits not met", name, pos.y) +--print("--- height limits not met", name, pos.y) return end @@ -3618,14 +3696,14 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, if not light or light > max_light or light < min_light then ---print ("--- light limits not met", name, light) +--print("--- light limits not met", name, light) return end -- mobs cannot spawn in protected areas when enabled if not spawn_protected and minetest.is_protected(pos, "") then ---print ("--- inside protected area", name) +--print("--- inside protected area", name) return end @@ -3636,7 +3714,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, for n = 1, #objs do if objs[n]:is_player() then ---print ("--- player too close", name) +--print("--- player too close", name) return end end @@ -3681,9 +3759,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, if minetest.registered_nodes[ node_ok(pos2).name].walkable == true then - ---print ("--- not enough space to spawn", name) - +--print("--- not enough space to spawn", name) return end end @@ -3703,11 +3779,13 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, end local mob = minetest.add_entity(pos, name) ---[[ - print ("[mobs] Spawned " .. name .. " at " - .. minetest.pos_to_string(pos) .. " on " - .. node.name .. " near " .. neighbors[1]) -]] + +-- active_mobs = active_mobs + 1 + +-- 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() @@ -3764,7 +3842,7 @@ function mobs:register_arrow(name, def) hit_mob = def.hit_mob, hit_object = def.hit_object, drop = def.drop or false, -- drops arrow as registered item when true - collisionbox = def.collisionbox or {0, 0, 0, 0, 0, 0}, + collisionbox = def.collisionbox or {-.1, -.1, -.1, .1, .1, .1}, timer = 0, switch = 0, owner_id = def.owner_id, @@ -3784,18 +3862,15 @@ function mobs:register_arrow(name, def) local pos = self.object:get_pos() - if self.switch == 0 - or self.timer > 150 then + if self.switch == 0 or self.timer > 150 then - self.object:remove() ; -- print ("removed arrow") + self.object:remove() ; -- print("removed arrow") return end -- does arrow have a tail (fireball) - if def.tail - and def.tail == 1 - and def.tail_texture then + if def.tail and def.tail == 1 and def.tail_texture then minetest.add_particle({ pos = pos, @@ -3827,21 +3902,23 @@ function mobs:register_arrow(name, def) self.object:get_luaentity().name) end - self.object:remove() ; -- print ("hit node") + self.object:remove() ; -- print("hit node") return end end - if self.hit_player or self.hit_mob then + if self.hit_player or self.hit_mob or self.hit_object then - for _,player in pairs(minetest.get_objects_inside_radius(pos, 1.0)) do + for _,player in pairs( + minetest.get_objects_inside_radius(pos, 1.0)) do - if self.hit_player - and player:is_player() then + if self.hit_player and player:is_player() then self:hit_player(player) - self.object:remove() ; -- print ("hit player") + + self.object:remove() ; -- print("hit player") + return end @@ -3855,7 +3932,7 @@ function mobs:register_arrow(name, def) self:hit_mob(player) - self.object:remove() ; --print ("hit mob") + self.object:remove() ; --print("hit mob") return end @@ -3868,7 +3945,7 @@ function mobs:register_arrow(name, def) self:hit_object(player) - self.object:remove(); -- print ("hit object") + self.object:remove(); -- print("hit object") return end @@ -3883,10 +3960,7 @@ end -- compatibility function function mobs:explosion(pos, radius) - - local self = {sounds = {explode = "tnt_explode"}} - - mobs:boom(self, pos, radius) + mobs:boom({sounds = {explode = "tnt_explode"}}, pos, radius) end @@ -3957,10 +4031,12 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) local pos = pointed_thing.above - -- am I clicking on something with existing on_rightclick function? + -- does existing on_rightclick function exist? local under = minetest.get_node(pointed_thing.under) local def = minetest.registered_nodes[under.name] + if def and def.on_rightclick then + return def.on_rightclick( pointed_thing.under, under, placer, itemstack) end @@ -3980,6 +4056,8 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) if not ent then return end -- sanity check +-- active_mobs = active_mobs + 1 + -- set owner if not a monster if ent.type ~= "monster" then ent.owner = placer:get_player_name() @@ -4006,10 +4084,12 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) local pos = pointed_thing.above - -- am I clicking on something with existing on_rightclick function? + -- does existing on_rightclick function exist? local under = minetest.get_node(pointed_thing.under) local def = minetest.registered_nodes[under.name] + if def and def.on_rightclick then + return def.on_rightclick( pointed_thing.under, under, placer, itemstack) end @@ -4021,6 +4101,15 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) return end + -- have we reached active mob limit + if active_mobs >= active_limit then + minetest.chat_send_player(placer:get_player_name(), + S("Active Mob Limit Reached!") + .. " (" .. active_mobs + .. " / " .. active_limit .. ")") + return + end + pos.y = pos.y + 1 local mob = minetest.add_entity(pos, mob) @@ -4028,6 +4117,8 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) if not ent then return end -- sanity check +-- active_mobs = active_mobs + 1 + -- don't set owner if monster or sneak pressed if ent.type ~= "monster" and not placer:get_player_control().sneak then @@ -4057,10 +4148,10 @@ function mobs:force_capture(self, clicker) local tmp = {} for _,stat in pairs(self) do + local t = type(stat) - if t ~= "function" - and t ~= "nil" - and t ~= "userdata" then + + if t ~= "function" and t ~= "nil" and t ~= "userdata" then tmp[_] = self[_] end end @@ -4077,15 +4168,15 @@ function mobs:force_capture(self, clicker) minetest.add_item(clicker:get_pos(), new_stack) end - self.object:remove() - self:mob_sound("default_place_node_hard") + + remove_mob(self, true) end -- capture critter (thanks to blert2112 for idea) -function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, - force_take, replacewith) +function mobs:capture_mob(self, clicker, chance_hand, chance_net, + chance_lasso, force_take, replacewith) if self.child or not clicker:is_player() @@ -4112,8 +4203,7 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, end -- is mob tamed? - if self.tamed == false - and force_take == false then + if self.tamed == false and force_take == false then minetest.chat_send_player(name, S("Not tamed!")) @@ -4121,8 +4211,7 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, end -- cannot pick up if not owner - if self.owner ~= name - and force_take == false then + if self.owner ~= name and force_take == false then minetest.chat_send_player(name, S("@1 is owner!", self.owner)) @@ -4170,7 +4259,9 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, local tmp = {} for _,stat in pairs(self) do + local t = type(stat) + if t ~= "function" and t ~= "nil" and t ~= "userdata" then @@ -4191,7 +4282,7 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, minetest.add_item(clicker:get_pos(), new_stack) end - self.object:remove() + remove_mob(self, true) self:mob_sound("default_place_node_hard") @@ -4199,12 +4290,14 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, chance_lasso, -- when chance above fails or set to 0, miss! elseif chance and chance ~= 0 then + minetest.chat_send_player(name, S("Missed!")) self:mob_sound("mobs_swing") + return false - -- when chance set to nil always return a miss (used for npc walk/follow) + -- when chance is nil always return a miss (used for npc walk/follow) elseif not chance then return false end @@ -4244,7 +4337,8 @@ function mobs:protect(self, clicker) local pos = self.object:get_pos() pos.y = pos.y + self.collisionbox[2] + 0.5 - effect(self.object:get_pos(), 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) self:mob_sound("mobs_spell") @@ -4348,9 +4442,11 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) local tag = self.nametag or "" - minetest.show_formspec(name, "mobs_nametag", "size[8,4]" + minetest.show_formspec(name, "mobs_nametag", + "size[8,4]" .. "field[0.5,1;7.5,0;name;" - .. minetest.formspec_escape(S("Enter name:")) .. ";" .. tag .. "]" + .. minetest.formspec_escape(S("Enter name:")) + .. ";" .. tag .. "]" .. "button_exit[2.5,3.5;3,1;mob_rename;" .. minetest.formspec_escape(S("Rename")) .. "]") end @@ -4425,10 +4521,14 @@ function mobs:alias_mob(old_name, new_name) on_activate = function(self, staticdata) if minetest.registered_entities[new_name] then - minetest.add_entity(self.object:get_pos(), new_name, staticdata) + + minetest.add_entity(self.object:get_pos(), + new_name, staticdata) + else +-- active_mobs = active_mobs - 1 end - self.object:remove() + remove_mob(self) end, get_staticdata = function(self) From 85faeea604a6a9f03a258072749896b91745bf36 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Sat, 16 May 2020 10:44:38 +0100 Subject: [PATCH 067/135] added 'mob_active_limit' to limit mobs in game --- api.lua | 76 ++++++++++++++++++++++-------------------------- api.txt | 1 + readme.MD | 2 ++ settingtypes.txt | 5 +++- 4 files changed, 41 insertions(+), 43 deletions(-) diff --git a/api.lua b/api.lua index ba92e60..0783c5c 100644 --- a/api.lua +++ b/api.lua @@ -6,16 +6,11 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200515", + version = "20200516", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } --- mob table and limit (not active) -local active_mobs = 0 -local active_limit = 99 - - -- creative check local creative_cache = minetest.settings:get_bool("creative_mode") function mobs.is_creative(name) @@ -59,7 +54,11 @@ local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0 local show_health = minetest.settings:get_bool("mob_show_health") ~= false local max_per_block = tonumber(minetest.settings:get("max_objects_per_block") or 99) local mob_nospawn_range = tonumber(minetest.settings:get("mob_nospawn_range") or 12) -local mob_chance_multiplier = tonumber(minetest.settings:get("mob_chance_multiplier") or 1) +local active_limit = tonumber(minetest.settings:get("mob_active_limit") or 0) +local mob_chance_multiplier = + tonumber(minetest.settings:get("mob_chance_multiplier") or 1) +local active_mobs = 0 + -- Peaceful mode message so players will know there are no monsters if peaceful_only then @@ -763,9 +762,9 @@ local remove_mob = function(self, decrease) self.object:remove() - if decrease then + if decrease and active_limit > 0 then --- active_mobs = active_mobs - 1 + active_mobs = active_mobs - 1 if active_mobs < 0 then active_mobs = 0 @@ -1361,7 +1360,7 @@ function mob_class:breed() ent.hornytimer = 41 -- have we reached active mob limit - if active_mobs >= active_limit then + if active_limit > 0 and active_mobs >= active_limit then minetest.chat_send_player(self.owner, S("Active Mob Limit Reached!") .. " (" .. active_mobs @@ -1391,8 +1390,6 @@ function mob_class:breed() local ent2 = mob:get_luaentity() local textures = self.base_texture --- active_mobs = active_mobs + 1 - -- using specific child texture (if found) if self.child_texture then textures = self.child_texture[1] @@ -2969,6 +2966,13 @@ end -- get entity staticdata function mob_class:mob_staticdata() + -- this handles mob count for mobs activated, unloaded, reloaded + if active_limit > 0 and self.active_toggle then + active_mobs = active_mobs + self.active_toggle + self.active_toggle = -self.active_toggle +--print("-- staticdata", active_mobs, active_limit, self.active_toggle) + end + -- remove mob when out of range unless tamed if remove_far and self.remove_ok @@ -3005,9 +3009,7 @@ function mob_class:mob_staticdata() local t = type(stat) - if t ~= "function" - and t ~= "nil" - and t ~= "userdata" + if t ~= "function" and t ~= "nil" and t ~= "userdata" and _ ~= "_cmi_components" then tmp[_] = self[_] end @@ -3022,6 +3024,20 @@ end -- activate mob and reload settings function mob_class:mob_activate(staticdata, def, dtime) + -- if dtime == 0 then entity has just been created + -- anything higher means it is respawning (thanks SorceryKid) + if dtime == 0 and active_limit > 0 then + self.active_toggle = 1 + end + + -- remove mob if not tamed and mob total reached + if active_limit > 0 and active_mobs >= active_limit and not self.tamed then + + remove_mob(self) +--print("-- mob limit reached, removing " .. self.name) + return + end + -- remove monsters in peaceful mode if self.type == "monster" and peaceful_only then @@ -3030,14 +3046,6 @@ function mob_class:mob_activate(staticdata, def, dtime) return end - -- remove mob if not tamed and mob total reached - if active_mobs >= active_limit and not self.tamed then - - remove_mob(self) ---print("-- mob limit reached, removing " .. self.name) - return - end - -- load entity variables local tmp = minetest.deserialize(staticdata) @@ -3047,13 +3055,6 @@ function mob_class:mob_activate(staticdata, def, dtime) end end - -- add currently spawned mobs to total - -- this is buggy as it doubles count when mobs unloaded and reloaded - if self.standing_in then --- active_mobs = active_mobs + 1 ---print("-- active mobs: " .. active_mobs .. " / " .. active_limit) - end - -- force current model into mob self.mesh = def.mesh self.base_mesh = def.mesh @@ -3101,10 +3102,7 @@ function mob_class:mob_activate(staticdata, def, dtime) -- set child objects to half size if self.child == true then - vis_size = { - x = self.base_size.x * .5, - y = self.base_size.y * .5, - } + vis_size = {x = self.base_size.x * .5, y = self.base_size.y * .5} if def.child_texture then textures = def.child_texture[1] @@ -3632,7 +3630,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, end -- are we over active mob limit - if active_mobs >= active_limit then + if active_limit > 0 and active_mobs >= active_limit then --print("--- active mob limit reached", active_mobs, active_limit) return end @@ -3780,8 +3778,6 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, local mob = minetest.add_entity(pos, name) --- active_mobs = active_mobs + 1 - -- print("[mobs] Spawned " .. name .. " at " -- .. minetest.pos_to_string(pos) .. " on " -- .. node.name .. " near " .. neighbors[1]) @@ -4056,8 +4052,6 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) if not ent then return end -- sanity check --- active_mobs = active_mobs + 1 - -- set owner if not a monster if ent.type ~= "monster" then ent.owner = placer:get_player_name() @@ -4102,7 +4096,7 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) end -- have we reached active mob limit - if active_mobs >= active_limit then + if active_limit > 0 and active_mobs >= active_limit then minetest.chat_send_player(placer:get_player_name(), S("Active Mob Limit Reached!") .. " (" .. active_mobs @@ -4117,8 +4111,6 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) if not ent then return end -- sanity check --- active_mobs = active_mobs + 1 - -- don't set owner if monster or sneak pressed if ent.type ~= "monster" and not placer:get_player_control().sneak then diff --git a/api.txt b/api.txt index 85a41d9..32afaa7 100644 --- a/api.txt +++ b/api.txt @@ -636,6 +636,7 @@ External Settings for "minetest.conf" pathfinding level 2, replace functions or mobs:boom function. 'mob_nospawn_range' Minimum range a mob can spawn near player (def: 12) + 'mob_active_limit' Number of active mobs in game, 0 for unlimited 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 diff --git a/readme.MD b/readme.MD index 614bab9..6a85f50 100644 --- a/readme.MD +++ b/readme.MD @@ -23,6 +23,8 @@ Lucky Blocks: 9 Changelog: +- 1.52 - Added 'mob_active_limit' in settings to set number of mobs in game +(default is 0 for unlimited) - 1.51 - Added some node checks for dangerous nodes, jumping and falling tweaks, spawn area check (thx for idea wuzzy), re-enabled mob suffocation, add 'mob_nospawn_range' setting - 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found, (thanks Astrobe), dont spawn mobs if world anchor nearby (technic or simple_anchor mods), chinese local added - 1.49- Added mobs:force_capture(self, player) function, api functions now use metatables thanks to bell07 diff --git a/settingtypes.txt b/settingtypes.txt index 938827c..75bf111 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -28,5 +28,8 @@ mob_chance_multiplier (Mob chance multiplier) float 1.0 # When false Mob no longer drop items when killed mobs_drop_items (Mob drops) bool true -#....Sets minimum distance around player that mobs cannot spawn +# Sets minimum distance around player that mobs cannot spawn mob_nospawn_range (Mob no-spawn range) float 12.0 + +# Sets maximum number of active mobs in game (0 for unlimited) +mob_active_limit (Mob Active Limit) float 0 From 233cd431a8f05c912106258567969a467eec3d3d Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Sat, 16 May 2020 11:14:28 +0100 Subject: [PATCH 068/135] update readme --- readme.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.MD b/readme.MD index 6a85f50..12869c4 100644 --- a/readme.MD +++ b/readme.MD @@ -24,7 +24,7 @@ Lucky Blocks: 9 Changelog: - 1.52 - Added 'mob_active_limit' in settings to set number of mobs in game -(default is 0 for unlimited) +(default is 0 for unlimited), removed {immortal} from mob armor - 1.51 - Added some node checks for dangerous nodes, jumping and falling tweaks, spawn area check (thx for idea wuzzy), re-enabled mob suffocation, add 'mob_nospawn_range' setting - 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found, (thanks Astrobe), dont spawn mobs if world anchor nearby (technic or simple_anchor mods), chinese local added - 1.49- Added mobs:force_capture(self, player) function, api functions now use metatables thanks to bell07 From 3a17c471ae2b63454c6b9c5d65e7f23571f902e1 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Sat, 16 May 2020 14:43:03 +0100 Subject: [PATCH 069/135] added 'texture_mods' to mob definition to add overlay textures --- api.lua | 12 ++++++++++-- api.txt | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 0783c5c..69b120b 100644 --- a/api.lua +++ b/api.lua @@ -94,6 +94,7 @@ local mob_class = { physical = true, collisionbox = {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}, visual_size = {x = 1, y = 1}, + texture_mods = "", makes_footstep_sound = false, view_range = 5, walk_velocity = 1, @@ -2345,9 +2346,12 @@ function mob_class:do_states(dtime) self.blinktimer = 0 if self.blinkstatus then - self.object:set_texture_mod("") +-- self.object:set_texture_mod("") + self.object:set_texture_mod(self.texture_mods) else - self.object:set_texture_mod("^[brighten") +-- self.object:set_texture_mod("^[brighten") + self.object:set_texture_mod(self.texture_mods + .. "^[brighten") end self.blinkstatus = not self.blinkstatus @@ -3164,6 +3168,9 @@ function mob_class:mob_activate(staticdata, def, dtime) self:update_tag() self:set_animation("stand") + -- apply any texture mods + self.object:set_texture_mod(self.texture_mods) + -- set 5.x flag to remove monsters when map area unloaded if remove_far and self.type == "monster" then self.static_save = false @@ -3495,6 +3502,7 @@ minetest.register_entity(name, setmetatable({ on_replace = def.on_replace, reach = def.reach, texture_list = def.textures, + texture_mods = def.texture_mods or "", child_texture = def.child_texture, docile_by_day = def.docile_by_day, fear_height = def.fear_height, diff --git a/api.txt b/api.txt index 32afaa7..7f806e6 100644 --- a/api.txt +++ b/api.txt @@ -185,6 +185,8 @@ functions needed for the mob to work properly which contains the following: '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. + 'texture_mods' holds a string which overlays a texture on top of the + mob texture e.g. "^saddle.png" '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 From 351fce8ee82c328d977fa07b7f4218b4ff0f8c43 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 19 May 2020 21:07:54 +0100 Subject: [PATCH 070/135] fix velocity and nil check --- api.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index 69b120b..87858a7 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200516", + version = "20200519", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -982,7 +982,7 @@ function mob_class:do_env_damage() -- halt mob if standing inside ignore node if self.standing_in == "ignore" then - self.object:set_velocity(0) + self.object:set_velocity({x = 0, y = 0, z = 0}) return true end @@ -1438,11 +1438,14 @@ end -- find and replace what mob is looking for (grass, wheat etc.) function mob_class:replace(pos) + local vel = self.object:get_velocity() + if not vel then return end + if not mobs_griefing or not self.replace_rate or not self.replace_what or self.child == true - or self.object:get_velocity().y ~= 0 + or vel.y ~= 0 or random(1, self.replace_rate) > 1 then return end From d125d0a5dbad76881de869fac621ea1d0c28561a Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 21 May 2020 20:38:08 +0100 Subject: [PATCH 071/135] tweak and tidy code (thanks MoNTE48) --- api.lua | 119 ++++++++++++++++++++++++++-------------------------- crafts.lua | 6 +-- spawner.lua | 14 +++---- 3 files changed, 69 insertions(+), 70 deletions(-) diff --git a/api.lua b/api.lua index 87858a7..8fe9c87 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200519", + version = "20200521", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -27,9 +27,11 @@ local cos = math.cos local abs = math.abs local min = math.min local max = math.max -local atann = math.atan local random = math.random local floor = math.floor +local ceil = math.ceil +local rad = math.rad +local atann = math.atan local atan = function(x) if not x or x ~= x then --error("atan bassed NaN") @@ -47,7 +49,6 @@ local peaceful_only = minetest.settings:get_bool("only_peaceful_mobs") local disable_blood = minetest.settings:get_bool("mobs_disable_blood") local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false -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") ~= false local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0 @@ -163,7 +164,7 @@ function mob_class:mob_sound(sound) if self.child then pitch = pitch * 1.5 end -- a little random pitch to be different - pitch = pitch + math.random(-10, 10) * 0.005 + pitch = pitch + random(-10, 10) * 0.005 if sound then minetest.sound_play(sound, { @@ -532,7 +533,7 @@ function mob_class:attempt_flight_correction(override) return false end - local escape_target = flyable_nodes[math.random(1, #flyable_nodes)] + local escape_target = flyable_nodes[random(#flyable_nodes)] local escape_direction = vector.direction(pos, escape_target) self.object:set_velocity( @@ -608,7 +609,7 @@ function mob_class:do_stay_near() local searchnodes = self.stay_near[1] local chance = self.stay_near[2] or 10 - if random(1, chance) > 1 then + if random(chance) > 1 then return false end @@ -716,7 +717,7 @@ function mob_class:item_drop() for n = 1, #self.drops do - if random(1, self.drops[n].chance) == 1 then + if random(self.drops[n].chance) == 1 then num = random(self.drops[n].min or 0, self.drops[n].max or 1) item = self.drops[n].name @@ -1315,11 +1316,12 @@ function mob_class:breed() 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) + effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, + "heart.png", 3, 4, 1, 0.1) local objs = minetest.get_objects_inside_radius(pos, 3) local num = 0 - local ent = nil + local ent for n = 1, #objs do @@ -1333,12 +1335,12 @@ function mob_class:breed() if ent.name == self.name then canmate = true else - local entname = string.split(ent.name,":") - local selfname = string.split(self.name,":") + local entname = ent.name:split(":") + local selfname = self.name:split(":") if entname[1] == selfname[1] then - entname = string.split(entname[2],"_") - selfname = string.split(selfname[2],"_") + entname = entname[2]:split("_") + selfname = selfname[2]:split("_") if entname[1] == selfname[1] then canmate = true @@ -1446,7 +1448,7 @@ function mob_class:replace(pos) or not self.replace_what or self.child == true or vel.y ~= 0 - or random(1, self.replace_rate) > 1 then + or random(self.replace_rate) > 1 then return end @@ -1606,10 +1608,9 @@ function mob_class:smart_mobs(s, p, dist, dtime) end end + -- lets try find a path, first take care of positions + -- since pathfinder is very sensitive if use_pathfind then - -- lets try find a path, first take care of positions - -- since pathfinder is very sensitive - local sheight = self.collisionbox[5] - self.collisionbox[2] -- round position to center of node to avoid stuck in walls -- also adjust height for player models! @@ -1637,7 +1638,8 @@ function mob_class:smart_mobs(s, p, dist, dtime) local jumpheight = 0 if self.jump and self.jump_height >= 4 then - jumpheight = min(math.ceil(self.jump_height / 4), 4) + jumpheight = min(ceil(self.jump_height / 4), 4) + elseif self.stepheight > 0.5 then jumpheight = 1 end @@ -1689,7 +1691,7 @@ function mob_class:smart_mobs(s, p, dist, dtime) end end - local sheight = math.ceil(self.collisionbox[5]) + 1 + local sheight = ceil(self.collisionbox[5]) + 1 -- assume mob is 2 blocks high so it digs above its head s.y = s.y + sheight @@ -1821,7 +1823,8 @@ function mob_class:general_attack() if objs[n]:is_player() then -- if player invisible or mob cannot attack then remove from list - if self.attack_players == false + if not damage_enabled + or self.attack_players == false or (self.owner and self.type ~= "monster") or mobs.invis[objs[n]:get_player_name()] or not specific_attack(self.specific_attack, "player") then @@ -1874,7 +1877,7 @@ function mob_class:general_attack() end -- attack closest player or mob - if min_player and random(1, 100) > self.attack_chance then + if min_player and random(100) > self.attack_chance then self:do_attack(min_player) end end @@ -2061,8 +2064,6 @@ function mob_class:follow_flop() -- swimmers flop when out of their element, and swim again when back in if self.fly then - local s = self.object:get_pos() - if not self:attempt_flight_correction() then self.state = "flop" @@ -2115,7 +2116,7 @@ function mob_class:do_states(dtime) if self.state == "stand" then - if random(1, 4) == 1 then + if random(4) == 1 then local lp local s = self.object:get_pos() @@ -2146,7 +2147,7 @@ function mob_class:do_states(dtime) if self.order ~= "stand" and self.walk_chance ~= 0 and self.facing_fence ~= true - and random(1, 100) <= self.walk_chance + and random(100) <= self.walk_chance and self.at_cliff == false then self:set_velocity(self.walk_velocity) @@ -2157,7 +2158,7 @@ function mob_class:do_states(dtime) elseif self.state == "walk" then local s = self.object:get_pos() - local lp = nil + local lp -- is there something I need to avoid? if self.water_damage > 0 @@ -2203,7 +2204,7 @@ function mob_class:do_states(dtime) yaw = self:set_yaw(yaw, 8) -- otherwise randomly turn - elseif random(1, 100) <= 30 then + elseif random(100) <= 30 then yaw = yaw + random(-0.5, 0.5) @@ -2219,7 +2220,7 @@ function mob_class:do_states(dtime) -- stand for great fall in front if self.facing_fence == true or self.at_cliff - or random(1, 100) <= self.stand_chance then + or random(100) <= self.stand_chance then -- don't stand if mob flies and keep_flying set if (self.fly and not self.keep_flying) @@ -2273,7 +2274,8 @@ function mob_class:do_states(dtime) or not self.attack or not self.attack:get_pos() or self.attack:get_hp() <= 0 - or (self.attack:is_player() and mobs.invis[ self.attack:get_player_name() ]) then + or (self.attack:is_player() + and mobs.invis[ self.attack:get_player_name() ]) then --print(" ** stop attacking **", dist, self.view_range) @@ -2323,7 +2325,7 @@ function mob_class:do_states(dtime) self.timer = 0 self.blinktimer = 0 self.blinkstatus = false - self.object:settexturemod("") + self.object:set_texture_mod("") end -- walk right up to player unless the timer is active @@ -2540,9 +2542,11 @@ function mob_class:do_states(dtime) -- punch player (or what player is attached to) local attached = self.attack:get_attach() + if attached then self.attack = attached end + self.attack:punch(self.object, 1.0, { full_punch_interval = 1.0, damage_groups = {fleshy = self.damage} @@ -2568,8 +2572,6 @@ function mob_class:do_states(dtime) p.y = p.y - .5 s.y = s.y + .5 - local dist = get_distance(p, s) - local vec = { x = p.x - s.x, y = p.y - s.y, @@ -2582,7 +2584,7 @@ function mob_class:do_states(dtime) if self.shoot_interval and self.timer > self.shoot_interval - and random(1, 100) <= 60 then + and random(100) <= 60 then self.timer = 0 self:set_animation("shoot") @@ -2847,7 +2849,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage) -- do we have a single blood texture or multiple? if type(self.blood_texture) == "table" then - blood = self.blood_texture[random(1, #self.blood_texture)] + blood = self.blood_texture[random(#self.blood_texture)] end effect(pos, amount, blood, 1, 2, 1.75, nil, nil, true) @@ -2871,10 +2873,11 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage) if not self.object:get_luaentity() then return end - self.object:settexturemod("^[colorize:#c9900070") + self.object:set_texture_mod("^[colorize:#c9900070") - core.after(0.3, function() - self.object:settexturemod("") + minetest.after(0.3, function() + if not self.object:get_luaentity() then return end + self.object:set_texture_mod(self.texture_mods) end) end) ]] @@ -2942,7 +2945,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage) -- alert others to the attack local objs = minetest.get_objects_inside_radius( hitter:get_pos(), self.view_range) - local obj = nil + local obj for n = 1, #objs do @@ -3002,7 +3005,7 @@ function mob_class:mob_staticdata() -- used to rotate older mobs if self.drawtype and self.drawtype == "side" then - self.rotate = math.rad(90) + self.rotate = rad(90) end if use_cmi then @@ -3077,7 +3080,7 @@ function mob_class:mob_activate(staticdata, def, dtime) end self.base_texture = def.textures and - def.textures[random(1, #def.textures)] + def.textures[random(#def.textures)] self.base_mesh = def.mesh self.base_size = self.visual_size self.base_colbox = self.collisionbox @@ -3385,7 +3388,7 @@ function mob_class:on_step(dtime, moveresult) end -- mob plays random sound at times - if random(1, 100) == 1 then + if random(100) == 1 then self:mob_sound(self.sounds.random) end @@ -3457,7 +3460,7 @@ minetest.register_entity(name, setmetatable({ do_custom = def.do_custom, jump_height = def.jump_height, drawtype = def.drawtype, -- DEPRECATED, use rotate instead - rotate = math.rad(def.rotate or 0), -- 0=front 90=side 180=back 270=side2 + rotate = rad(def.rotate or 0), -- 0=front 90=side 180=back 270=side2 glow = def.glow, lifetimer = def.lifetimer, hp_min = max(1, (def.hp_min or 5) * difficulty), @@ -3731,31 +3734,31 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, -- do we have enough space to spawn mob? (thanks wuzzy) local ent = minetest.registered_entities[name] local width_x = max(1, - math.ceil(ent.collisionbox[4] - ent.collisionbox[1])) + ceil(ent.collisionbox[4] - ent.collisionbox[1])) local min_x, max_x if width_x % 2 == 0 then - max_x = math.floor(width_x / 2) + max_x = floor(width_x / 2) min_x = -(max_x - 1) else - max_x = math.floor(width_x / 2) + max_x = floor(width_x / 2) min_x = -max_x end local width_z = max(1, - math.ceil(ent.collisionbox[6] - ent.collisionbox[3])) + ceil(ent.collisionbox[6] - ent.collisionbox[3])) local min_z, max_z if width_z % 2 == 0 then - max_z = math.floor(width_z / 2) + max_z = floor(width_z / 2) min_z = -(max_z - 1) else - max_z = math.floor(width_z / 2) + max_z = floor(width_z / 2) min_z = -max_z end local max_y = max(0, - math.ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1) + ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1) for y = 0, max_y do for x = min_x, max_x do @@ -4015,7 +4018,7 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) local grp = {spawn_egg = 1} -- do NOT add this egg to creative inventory (e.g. dungeon master) - if creative and no_creative == true then + if no_creative == true then grp.not_in_creative_inventory = 1 end @@ -4058,8 +4061,8 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) pos.y = pos.y + 1 local data = itemstack:get_metadata() - local mob = minetest.add_entity(pos, mob, data) - local ent = mob and mob:get_luaentity() + local smob = minetest.add_entity(pos, mob, data) + local ent = smob and smob:get_luaentity() if not ent then return end -- sanity check @@ -4117,8 +4120,8 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) pos.y = pos.y + 1 - local mob = minetest.add_entity(pos, mob) - local ent = mob and mob:get_luaentity() + local smob = minetest.add_entity(pos, mob) + local ent = smob and smob:get_luaentity() if not ent then return end -- sanity check @@ -4248,7 +4251,7 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, end -- calculate chance.. add to inventory if successful? - if chance and chance > 0 and random(1, 100) <= chance then + if chance and chance > 0 and random(100) <= chance then -- default mob egg local new_stack = ItemStack(mobname) @@ -4481,8 +4484,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end -- limit name entered to 64 characters long - if string.len(fields.name) > 64 then - fields.name = string.sub(fields.name, 1, 64) + if fields.name:len() > 64 then + fields.name = fields.name:sub(1, 64) end -- update nametag @@ -4527,8 +4530,6 @@ function mobs:alias_mob(old_name, new_name) minetest.add_entity(self.object:get_pos(), new_name, staticdata) - else --- active_mobs = active_mobs - 1 end remove_mob(self) diff --git a/crafts.lua b/crafts.lua index 890fe21..73d14da 100644 --- a/crafts.lua +++ b/crafts.lua @@ -302,8 +302,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) and fields.name and fields.name ~= "" then - local name = player:get_player_name() - -- does mob still exist? if not tex_obj or not tex_obj:get_luaentity() then @@ -318,8 +316,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end -- limit name entered to 64 characters long - if string.len(fields.name) > 64 then - fields.name = string.sub(fields.name, 1, 64) + if fields.name:len() > 64 then + fields.name = fields.name:sub(1, 64) end -- update texture diff --git a/spawner.lua b/spawner.lua index dfe20f2..8940850 100644 --- a/spawner.lua +++ b/spawner.lua @@ -19,7 +19,8 @@ minetest.register_node("mobs:spawner", { -- text entry formspec meta:set_string("formspec", - "field[text;" .. S("Mob MinLight MaxLight Amount PlayerDist") .. ";${command}]") + "field[text;" .. S("Mob MinLight MaxLight Amount PlayerDist") + .. ";${command}]") meta:set_string("infotext", S("Spawner Not Active (enter settings)")) meta:set_string("command", spawner_default) end, @@ -115,10 +116,10 @@ minetest.register_abm({ -- check objects inside 9x9 area around spawner local objs = minetest.get_objects_inside_radius(pos, 9) local count = 0 - local ent = nil + local ent -- count mob objects of same type in area - for k, obj in ipairs(objs) do + for _, obj in ipairs(objs) do ent = obj:get_luaentity() @@ -136,9 +137,9 @@ minetest.register_abm({ if pla > 0 then local in_range = 0 - local objs = minetest.get_objects_inside_radius(pos, pla) + local objsp = minetest.get_objects_inside_radius(pos, pla) - for _,oir in pairs(objs) do + for _, oir in pairs(objsp) do if oir:is_player() then @@ -157,8 +158,7 @@ minetest.register_abm({ -- find air blocks within 5 nodes of spawner local air = minetest.find_nodes_in_area( {x = pos.x - 5, y = pos.y + yof, z = pos.z - 5}, - {x = pos.x + 5, y = pos.y + yof, z = pos.z + 5}, - {"air"}) + {x = pos.x + 5, y = pos.y + yof, z = pos.z + 5}, {"air"}) -- spawn in random air block if air and #air > 0 then From 9dd4ac770f13f7ac07155bee2eb00c0683fb46b4 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Mon, 25 May 2020 20:58:38 +0100 Subject: [PATCH 072/135] fix tnt explosion causing mobs to attack themselves --- api.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 8fe9c87..883e6dc 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200521", + version = "20200525", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -1222,7 +1222,8 @@ local entity_physics = function(pos, radius) local ent = objs[n]:get_luaentity() -- punches work on entities AND players - objs[n]:punch(objs[n], 1.0, { +-- objs[n]:punch(objs[n], 1.0, { + objs[n]:punch(nil, 1.0, { full_punch_interval = 1.0, damage_groups = {fleshy = damage}, }, pos) From d5a1fce680be160a0f5b4cdf8801fa520605e236 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Wed, 27 May 2020 09:10:59 +0100 Subject: [PATCH 073/135] tweak flame particles when burning --- api.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api.lua b/api.lua index 883e6dc..c2b6d4d 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200525", + version = "20200527", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -988,6 +988,9 @@ function mob_class:do_env_damage() return true end + -- particle appears at random mob height + pos.y = pos.y + random(self.collisionbox[2], self.collisionbox[5]) + -- is mob light sensative, or scared of the dark :P if self.light_damage ~= 0 then @@ -1008,8 +1011,6 @@ function mob_class:do_env_damage() local nodef = minetest.registered_nodes[self.standing_in] - pos.y = pos.y + 1 -- for particle effect position - -- water if self.water_damage and nodef.groups.water then @@ -1032,7 +1033,7 @@ function mob_class:do_env_damage() self.health = self.health - self.lava_damage - effect(pos, 5, "fire_basic_flame.png", nil, nil, 1, nil) + effect(pos, 15, "fire_basic_flame.png", 1, 5, 1, 0.2, 15, true) if self:check_for_death({type = "environment", pos = pos, node = self.standing_in, hot = true}) then From d04f144b7ccffbc02901025914788058f0aeb07c Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Thu, 28 May 2020 16:21:20 +0100 Subject: [PATCH 074/135] revert previous nil punch to mob object --- api.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index c2b6d4d..678541a 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200527", + version = "20200528", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -1223,8 +1223,8 @@ local entity_physics = function(pos, radius) local ent = objs[n]:get_luaentity() -- punches work on entities AND players --- objs[n]:punch(objs[n], 1.0, { - objs[n]:punch(nil, 1.0, { + objs[n]:punch(objs[n], 1.0, { +-- objs[n]:punch(nil, 1.0, { full_punch_interval = 1.0, damage_groups = {fleshy = damage}, }, pos) From ad116aa70ecabcac319c045b6a2277a26744217a Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 9 Jun 2020 13:13:15 +0100 Subject: [PATCH 075/135] make sure mob doesn't attack itself --- api.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 678541a..e42d44c 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200528", + version = "20200609", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -2938,7 +2938,8 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage) and self.child == false and self.attack_players == true and hitter:get_player_name() ~= self.owner - and not mobs.invis[ name ] then + and not mobs.invis[ name ] + and self.object ~= hitter then -- attack whoever punched mob self.state = "" From 83e91808536dd39e5c6a989bc1838b83e8e11d00 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Tue, 16 Jun 2020 20:53:05 +0100 Subject: [PATCH 076/135] fix pos_to_yaw missing rot --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index e42d44c..ea409fd 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200609", + version = "20200616", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -590,7 +590,7 @@ local yaw_to_pos = function(self, target, rot) yaw = yaw + pi end - yaw = self:set_yaw(yaw, 6) + yaw = self:set_yaw(yaw, rot) return yaw end From 4962d44be940e8a1cb90a5b3ada9714b650f16ad Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Fri, 19 Jun 2020 11:25:37 +0100 Subject: [PATCH 077/135] tweak staticdata checks --- api.lua | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/api.lua b/api.lua index ea409fd..e3edf57 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200616", + version = "20200619", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -3022,7 +3022,9 @@ function mob_class:mob_staticdata() local t = type(stat) - if t ~= "function" and t ~= "nil" and t ~= "userdata" + if t ~= "function" + and t ~= "nil" + and t ~= "userdata" and _ ~= "_cmi_components" then tmp[_] = self[_] end @@ -3063,8 +3065,16 @@ function mob_class:mob_activate(staticdata, def, dtime) local tmp = minetest.deserialize(staticdata) if tmp then + for _,stat in pairs(tmp) do - self[_] = stat + + local t = type(stat) + + if t ~= "function" + and t ~= "nil" + and t ~= "userdata" then + self[_] = stat + end end end @@ -3945,7 +3955,7 @@ function mobs:register_arrow(name, def) self:hit_mob(player) - self.object:remove() ; --print("hit mob") + self.object:remove() ; --print("hit mob") return end @@ -3958,7 +3968,7 @@ function mobs:register_arrow(name, def) self:hit_object(player) - self.object:remove(); -- print("hit object") + self.object:remove() ; -- print("hit object") return end @@ -4144,7 +4154,6 @@ function mobs:register_egg(mob, desc, background, addegg, no_creative) return itemstack end, }) - end @@ -4160,7 +4169,9 @@ function mobs:force_capture(self, clicker) local t = type(stat) - if t ~= "function" and t ~= "nil" and t ~= "userdata" then + if t ~= "function" + and t ~= "nil" + and t ~= "userdata" then tmp[_] = self[_] end end From 3b8c4bc070bec8953457f6f8b4147fc20c52154b Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Fri, 19 Jun 2020 11:33:33 +0100 Subject: [PATCH 078/135] optimize loops --- api.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/api.lua b/api.lua index e3edf57..6145faf 100644 --- a/api.lua +++ b/api.lua @@ -3016,11 +3016,11 @@ function mob_class:mob_staticdata() self._cmi_components) end - local tmp = {} + local tmp, t = {} for _,stat in pairs(self) do - local t = type(stat) + t = type(stat) if t ~= "function" and t ~= "nil" @@ -3066,9 +3066,11 @@ function mob_class:mob_activate(staticdata, def, dtime) if tmp then + local t + for _,stat in pairs(tmp) do - local t = type(stat) + t = type(stat) if t ~= "function" and t ~= "nil" @@ -4163,11 +4165,11 @@ function mobs:force_capture(self, clicker) -- add special mob egg with all mob information local new_stack = ItemStack(self.name .. "_set") - local tmp = {} + local tmp, t = {} for _,stat in pairs(self) do - local t = type(stat) + t = type(stat) if t ~= "function" and t ~= "nil" @@ -4276,11 +4278,11 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, new_stack = ItemStack(mobname .. "_set") - local tmp = {} + local tmp, t = {} for _,stat in pairs(self) do - local t = type(stat) + t = type(stat) if t ~= "function" and t ~= "nil" From 245128f9e6a1a1d2d0cf2e9aa3e765acdddee090 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Fri, 19 Jun 2020 16:07:29 +0100 Subject: [PATCH 079/135] tweak custom attack function --- api.lua | 15 ++++----------- api.txt | 3 ++- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/api.lua b/api.lua index 6145faf..d34242a 100644 --- a/api.lua +++ b/api.lua @@ -1224,7 +1224,6 @@ local entity_physics = function(pos, radius) -- punches work on entities AND players objs[n]:punch(objs[n], 1.0, { --- objs[n]:punch(nil, 1.0, { full_punch_interval = 1.0, damage_groups = {fleshy = damage}, }, pos) @@ -2524,9 +2523,11 @@ function mob_class:do_states(dtime) self:set_velocity(0) - if not self.custom_attack then + if self.timer > 1 then - if self.timer > 1 then + -- no custom attack or custom attack returns true to continue + if not self.custom_attack + or self:custom_attack(self, p) == true then self.timer = 0 self:set_animation("punch") @@ -2555,14 +2556,6 @@ function mob_class:do_states(dtime) }, nil) end end - else -- call custom attack every second - if self.custom_attack - and self.timer > 1 then - - self.timer = 0 - - self:custom_attack(p) - end end end diff --git a/api.txt b/api.txt index 7f806e6..fb846b6 100644 --- a/api.txt +++ b/api.txt @@ -291,7 +291,8 @@ enhance mob functionality and have them do many interesting things: 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). + melee attack, parameters are (self, to_attack) and if true + is returned normal attack function continued. 'on_die' a function that is called when mob is killed (self, pos) 'do_custom' a custom function that is called every tick while mob is active and which has access to all of the self.* variables From ec122aa6deea57e465ca5e9a8682bbc6b2930f92 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Sat, 20 Jun 2020 10:07:32 +0100 Subject: [PATCH 080/135] add particle effect global function, arrow lifetime, api.txt update --- api.lua | 22 ++++++++++++++++++---- api.txt | 25 +++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/api.lua b/api.lua index d34242a..0e66aec 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200619", + version = "20200620", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -645,7 +645,14 @@ local effect = function(pos, amount, texture, min_size, max_size, max_size = max_size or 1 gravity = gravity or -10 glow = glow or 0 - fall = fall and 0 or -radius + + if fall == true then + fall = 0 + elseif fall == false then + fall = radius + else + fall = -radius + end minetest.add_particlespawner({ amount = amount, @@ -665,6 +672,12 @@ local effect = function(pos, amount, texture, min_size, max_size, }) end +function mobs:effect(pos, amount, texture, min_size, max_size, + radius, gravity, glow, fall) + + effect(pos, amount, texture, min_size, max_size, radius, gravity, glow, fall) +end + -- update nametag colour function mob_class:update_tag() @@ -3862,6 +3875,7 @@ function mobs:register_arrow(name, def) drop = def.drop or false, -- drops arrow as registered item when true collisionbox = def.collisionbox or {-.1, -.1, -.1, .1, .1, .1}, timer = 0, + lifetime = def.lifetime or 4.5, switch = 0, owner_id = def.owner_id, rotate = def.rotate, @@ -3876,11 +3890,11 @@ function mobs:register_arrow(name, def) on_step = def.on_step or function(self, dtime) - self.timer = self.timer + 1 + self.timer = self.timer + dtime local pos = self.object:get_pos() - if self.switch == 0 or self.timer > 150 then + if self.switch == 0 or self.timer > self.lifetime then self.object:remove() ; -- print("removed arrow") diff --git a/api.txt b/api.txt index fb846b6..ee7cef0 100644 --- a/api.txt +++ b/api.txt @@ -293,7 +293,8 @@ enhance mob functionality and have them do many interesting things: 'custom_attack' when set this function is called instead of the normal mob melee attack, parameters are (self, to_attack) and if true is returned normal attack function continued. - 'on_die' a function that is called when mob is killed (self, pos) + 'on_die' a function that is called when mob is killed (self, pos), also + has access to self.cause_of_death table. '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 @@ -387,6 +388,24 @@ true the mob will not spawn. 'name' is the name of the animal/monster +Particle Effects +---------------- + +mobs:effect(pos, amount, texture, min_size, max_size, radius, gravity, glow, fall) + +This function provides a quick way to spawn particles as an effect. + + 'pos' center position of particle effect. + 'amount' how many particles. + 'texture' texture filename to use for effect. + 'min_size' smallest particle size. + 'max_size' largest particle size. + 'radius' how far particles spread outward from center. + 'gravity' gravity applied to particles once they spawn. + 'glow' number between 1 and 15 for glowing particles. + 'fall' when true particles fall, false has them rising, nil has them scatter. + + Making Arrows ------------- @@ -422,7 +441,9 @@ This function registers a arrow for mobs with the attack type shoot. 'on_step' is a custom function when arrow is active, nil for default. 'on_punch' is a custom function when arrow is punched, nil by default - 'collisionbox' is hitbox table for arrow, {0,0,0,0,0,0} by default. + 'collisionbox' is hitbox table for arrow, {-.1,-.1,-.1,.1,.1,.1} by default. + 'lifetime' contains float value for how many seconds arrow exists in + world before being removed (default is 4.5 seconds). Spawn Eggs From e63b1b478eeb30ad73598a2b86ad9bda1cb45b5f Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Mon, 22 Jun 2020 21:24:06 +0100 Subject: [PATCH 081/135] add mobs:add_mob() function --- api.lua | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- api.txt | 12 +++++++ 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index 0e66aec..8e51049 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200620", + version = "20200622", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -3464,7 +3464,7 @@ mobs.spawning_mobs = {} -- register mob entity function mobs:register_mob(name, def) - mobs.spawning_mobs[name] = true + mobs.spawning_mobs[name] = {} minetest.register_entity(name, setmetatable({ @@ -3611,6 +3611,106 @@ end -- global functions +function mobs:add_mob(pos, def) + + -- is mob actually registered? + if not mobs.spawning_mobs[def.name] + or not minetest.registered_entities[def.name] then +--print("--- mob doesn't exist", def.name) + return + end + + -- are we over active mob limit + if active_limit > 0 and active_mobs >= active_limit then +--print("--- active mob limit reached", active_mobs, active_limit) + return + end + + -- get total number of this mob in area + local num_mob, is_pla = count_mobs(pos, def.name) + + if not is_pla then +--print("--- no players within active area, will not spawn " .. def.name) + return + end + + local aoc = mobs.spawning_mobs[def.name] + and mobs.spawning_mobs[def.name].aoc or 1 + + if def.ignore_count ~= true and num_mob >= aoc then +--print("--- too many " .. def.name .. " in area", num_mob .. "/" .. aoc) + return + end + + local mob = minetest.add_entity(pos, def.name) + +--print("[mobs] Spawned " .. def.name .. " at " .. minetest.pos_to_string(pos)) + + local ent = mob:get_luaentity() + + if not ent then +--print("[mobs] entity not found " .. def.name) + return false + end + + if def.child then + + local textures = ent.base_texture + + -- using specific child texture (if found) + if ent.child_texture then + textures = ent.child_texture[1] + end + + -- and resize to half height + mob:set_properties({ + textures = textures, + visual_size = { + x = ent.base_size.x * .5, + y = ent.base_size.y * .5 + }, + collisionbox = { + ent.base_colbox[1] * .5, + ent.base_colbox[2] * .5, + ent.base_colbox[3] * .5, + ent.base_colbox[4] * .5, + ent.base_colbox[5] * .5, + ent.base_colbox[6] * .5 + }, + selectionbox = { + ent.base_selbox[1] * .5, + ent.base_selbox[2] * .5, + ent.base_selbox[3] * .5, + ent.base_selbox[4] * .5, + ent.base_selbox[5] * .5, + ent.base_selbox[6] * .5 + }, + }) + + ent.child = true + end + + if def.owner then + ent.tamed = true + ent.owner = def.owner + end + + if def.nametag then + + -- limit name entered to 64 characters long + if def.nametag:len() > 64 then + def.nametag = def.nametag:sub(1, 64) + end + + ent.nametag = def.nametag + + ent:update_tag() + end + + return true +end + + function mobs:spawn_abm_check(pos, node, name) -- global function to add additional spawn checks -- return true to stop spawning mob @@ -3621,7 +3721,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval, chance, aoc, min_height, max_height, day_toggle, on_spawn) -- Do mobs spawn at all? - if not mobs_spawn then + if not mobs_spawn or not mobs.spawning_mobs[name] then return end @@ -3645,6 +3745,8 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, end + mobs.spawning_mobs[name].aoc = aoc + minetest.register_abm({ label = name .. " spawning", diff --git a/api.txt b/api.txt index ee7cef0..0a5841e 100644 --- a/api.txt +++ b/api.txt @@ -328,6 +328,18 @@ for each mob. 'self.nametag' contains the name of the mob which it can show above +Adding Mobs in World +-------------------- + + mobs:add_mob(pos, { + name = "mobs_animal:chicken", + child = true, + owner = "singleplayer", + nametag = "Bessy", + ignore_count = true -- ignores mob count per map area + }) + + Spawning Mobs in World ---------------------- From dfdd55848d6da32412a2df9745fc415b9b084cbe Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 25 Jun 2020 09:33:59 +0100 Subject: [PATCH 082/135] mobs:add_mob() returns object spawned or false if not --- api.lua | 4 ++-- api.txt | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 8e51049..ea630bc 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200622", + version = "20200625", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -3707,7 +3707,7 @@ function mobs:add_mob(pos, def) ent:update_tag() end - return true + return ent end diff --git a/api.txt b/api.txt index 0a5841e..e96d07b 100644 --- a/api.txt +++ b/api.txt @@ -339,6 +339,8 @@ Adding Mobs in World ignore_count = true -- ignores mob count per map area }) +Returns false if mob could not be added, returns mob object if spawned ok. + Spawning Mobs in World ---------------------- From b17a5bfe77c82e11fd9effcc9f9f05fb3cb25d6e Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 28 Jun 2020 14:35:51 +0100 Subject: [PATCH 083/135] mobs move slower in liquids --- api.lua | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/api.lua b/api.lua index ea630bc..7e6f0f9 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200625", + version = "20200628", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -206,7 +206,6 @@ end function mob_class:collision() local pos = self.object:get_pos() - local vel = self.object:get_velocity() local x, z = 0, 0 local width = -self.collisionbox[1] + self.collisionbox[4] + 0.5 @@ -234,7 +233,9 @@ function mob_class:set_velocity(v) -- halt mob if it has been ordered to stay if self.order == "stand" then + self.object:set_velocity({x = 0, y = 0, z = 0}) + return end @@ -253,11 +254,25 @@ function mob_class:set_velocity(v) -- set velocity with hard limit of 10 local vel = self.object:get_velocity() - self.object:set_velocity({ - x = max(-10, min((sin(yaw) * -v) + c_x, 10)), - y = max(-10, min((vel and vel.y or 0), 10)), - z = max(-10, min((cos(yaw) * v) + c_y, 10)) - }) + local new_vel = { + x = (sin(yaw) * -v) + c_x, + y = vel.y, + z = (cos(yaw) * v) + c_y + } + + -- check if standing in water and use liquid viscocity to slow mob + local visc = minetest.registered_nodes[self.standing_in].liquid_viscosity + + -- assume any mob not flying will slow down in water as mobs flying in air + -- wont be near water, and mobs flying in water will swim at full speed + if not self.flying_in and (visc and visc > 0) then + + new_vel.x = new_vel.x ~= 0 and new_vel.x / visc or 0 + new_vel.y = new_vel.y ~= 0 and new_vel.y / visc or 0 + new_vel.z = new_vel.z ~= 0 and new_vel.z / visc or 0 + end + + self.object:set_velocity(new_vel) end -- global version of above function From 48bfa284d0b25ca9dbd6b25ca83810ef766d3bb3 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 28 Jun 2020 14:51:37 +0100 Subject: [PATCH 084/135] fix typo self.flying_in to self.fly_in --- api.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 7e6f0f9..2aff851 100644 --- a/api.lua +++ b/api.lua @@ -265,7 +265,7 @@ function mob_class:set_velocity(v) -- assume any mob not flying will slow down in water as mobs flying in air -- wont be near water, and mobs flying in water will swim at full speed - if not self.flying_in and (visc and visc > 0) then + if not self.fly_in and (visc and visc > 0) then new_vel.x = new_vel.x ~= 0 and new_vel.x / visc or 0 new_vel.y = new_vel.y ~= 0 and new_vel.y / visc or 0 From d65b80fa51478bdc03b7c022f258c07f9d828091 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 28 Jun 2020 20:36:38 +0100 Subject: [PATCH 085/135] fluid viscocity checks standing node against fly_in table/string --- api.lua | 28 +++++++++++++++++++++++++--- readme.MD | 4 ++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/api.lua b/api.lua index 2aff851..d6ac467 100644 --- a/api.lua +++ b/api.lua @@ -228,6 +228,28 @@ function mob_class:collision() end +-- check string against another string or table +local check_for = function(look_for, look_inside) + + if type(look_inside) == "string" and look_inside == look_for then + + return true + + elseif type(look_inside) == "table" then + + for _, str in pairs(look_inside) do + + if str == look_for then + + return true + end + end + end + + return false +end + + -- move mob in facing direction function mob_class:set_velocity(v) @@ -263,9 +285,9 @@ function mob_class:set_velocity(v) -- check if standing in water and use liquid viscocity to slow mob local visc = minetest.registered_nodes[self.standing_in].liquid_viscosity - -- assume any mob not flying will slow down in water as mobs flying in air - -- wont be near water, and mobs flying in water will swim at full speed - if not self.fly_in and (visc and visc > 0) then + -- if mob standing inside fluid with viscocity which isn't in self.fly_in + -- then slow down mob accordingly + if visc and visc > 0 and not check_for(self.standing_in, self.fly_in)then new_vel.x = new_vel.x ~= 0 and new_vel.x / visc or 0 new_vel.y = new_vel.y ~= 0 and new_vel.y / visc or 0 diff --git a/readme.MD b/readme.MD index 12869c4..d6982e8 100644 --- a/readme.MD +++ b/readme.MD @@ -23,8 +23,8 @@ Lucky Blocks: 9 Changelog: -- 1.52 - Added 'mob_active_limit' in settings to set number of mobs in game -(default is 0 for unlimited), removed {immortal} from mob armor +- 1.52 - Added 'mob_active_limit' in settings to set number of mobs in game +(default is 0 for unlimited), removed {immortal} from mob armor, fluid viscocity slows mobs - 1.51 - Added some node checks for dangerous nodes, jumping and falling tweaks, spawn area check (thx for idea wuzzy), re-enabled mob suffocation, add 'mob_nospawn_range' setting - 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found, (thanks Astrobe), dont spawn mobs if world anchor nearby (technic or simple_anchor mods), chinese local added - 1.49- Added mobs:force_capture(self, player) function, api functions now use metatables thanks to bell07 From 296fad85ca596c34b850133ea00a0db917d9e1c1 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 28 Jun 2020 20:46:57 +0100 Subject: [PATCH 086/135] simplify string/table searches --- api.lua | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/api.lua b/api.lua index d6ac467..98c52e9 100644 --- a/api.lua +++ b/api.lua @@ -287,7 +287,7 @@ function mob_class:set_velocity(v) -- if mob standing inside fluid with viscocity which isn't in self.fly_in -- then slow down mob accordingly - if visc and visc > 0 and not check_for(self.standing_in, self.fly_in)then + if visc and visc > 0 and not check_for(self.standing_in, self.fly_in) then new_vel.x = new_vel.x ~= 0 and new_vel.x / visc or 0 new_vel.y = new_vel.y ~= 0 and new_vel.y / visc or 0 @@ -587,20 +587,9 @@ function mob_class:flight_check() if not def then return false end - if type(self.fly_in) == "string" - and self.standing_in == self.fly_in then - + -- are we standing inside what we should be to fly/swim ? + if check_for(self.standing_in, self.fly_in) then return true - - elseif type(self.fly_in) == "table" then - - for _,fly_in in pairs(self.fly_in) do - - if self.standing_in == fly_in then - - return true - end - end end -- stops mobs getting stuck inside stairs and plantlike nodes @@ -1289,22 +1278,10 @@ function mob_class:follow_holding(clicker) end local item = clicker:get_wielded_item() - local t = type(self.follow) - -- single item - if t == "string" - and item:get_name() == self.follow then + -- are we holding an item mob can follow ? + if check_for(item:get_name(), self.follow) then return true - - -- multiple items - elseif t == "table" then - - for no = 1, #self.follow do - - if self.follow[no] == item:get_name() then - return true - end - end end return false From ee8c19ce217bc9b1df8920a75bca3a581c22b4e2 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 29 Jun 2020 15:00:32 +0100 Subject: [PATCH 087/135] tweak mob falling, floating, jumping and fluid viscosity --- api.lua | 115 ++++++++++++++++++++++++++------------------------------ 1 file changed, 54 insertions(+), 61 deletions(-) diff --git a/api.lua b/api.lua index 98c52e9..6d2eafd 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200628", + version = "20200629", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -273,6 +273,16 @@ function mob_class:set_velocity(v) -- nil check for velocity v = v or 0 + -- check if standing in liquid with max viscosity of 7 + local visc = min(minetest.registered_nodes[self.standing_in].liquid_viscosity, 7) + + -- only slow mob trying to move while inside a viscous fluid that + -- they aren't meant to be in (fish in water, spiders in cobweb etc) + if v > 0 and visc and visc > 0 + and not check_for(self.standing_in, self.fly_in) then + v = v / (visc + 1) + end + -- set velocity with hard limit of 10 local vel = self.object:get_velocity() @@ -282,18 +292,6 @@ function mob_class:set_velocity(v) z = (cos(yaw) * v) + c_y } - -- check if standing in water and use liquid viscocity to slow mob - local visc = minetest.registered_nodes[self.standing_in].liquid_viscosity - - -- if mob standing inside fluid with viscocity which isn't in self.fly_in - -- then slow down mob accordingly - if visc and visc > 0 and not check_for(self.standing_in, self.fly_in) then - - new_vel.x = new_vel.x ~= 0 and new_vel.x / visc or 0 - new_vel.y = new_vel.y ~= 0 and new_vel.y / visc or 0 - new_vel.z = new_vel.z ~= 0 and new_vel.z / visc or 0 - end - self.object:set_velocity(new_vel) end @@ -1146,14 +1144,8 @@ function mob_class:do_jump() -- sanity check if not yaw then return false end - -- 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 + -- we can only jump if standing on solid node + if minetest.registered_nodes[self.standing_on].walkable == false then return false end @@ -1161,22 +1153,22 @@ function mob_class:do_jump() local dir_x = -sin(yaw) * (self.collisionbox[4] + 0.5) local dir_z = cos(yaw) * (self.collisionbox[4] + 0.5) + -- set y_pos to base of mob + pos.y = pos.y + self.collisionbox[2] + -- what is in front of mob? local nod = node_ok({ - x = pos.x + dir_x, - y = pos.y + 0.5, - z = pos.z + dir_z + x = pos.x + dir_x, y = pos.y + 0.5, z = pos.z + dir_z }) -- what is above and in front? local nodt = node_ok({ - x = pos.x + dir_x, - y = pos.y + 1.5, - z = pos.z + dir_z + x = pos.x + dir_x, y = pos.y + 1.5, z = pos.z + dir_z }) local blocked = minetest.registered_nodes[nodt.name].walkable +--print("standing on:", self.standing_on, pos.y - 0.25) --print("in front:", nod.name, pos.y + 0.5) --print("in front above:", nodt.name, pos.y + 1.5) @@ -2379,10 +2371,10 @@ function mob_class:do_states(dtime) self.blinktimer = 0 if self.blinkstatus then --- self.object:set_texture_mod("") + self.object:set_texture_mod(self.texture_mods) else --- self.object:set_texture_mod("^[brighten") + self.object:set_texture_mod(self.texture_mods .. "^[brighten") end @@ -2594,11 +2586,7 @@ function mob_class:do_states(dtime) p.y = p.y - .5 s.y = s.y + .5 - local vec = { - x = p.x - s.x, - y = p.y - s.y, - z = p.z - s.z - } + local vec = {x = p.x - s.x, y = p.y - s.y, z = p.z - s.z} yaw = yaw_to_pos(self, p) @@ -2655,39 +2643,33 @@ function mob_class:falling(pos) -- sanity check if not v then return end - if v.y > 0 then + local fall_speed = -10 -- gravity - -- apply gravity when moving up - self.object:set_acceleration({ - x = 0, - y = -10, - z = 0 - }) - - elseif v.y <= 0 and v.y > self.fall_speed then - - -- fall downwards at set speed - self.object:set_acceleration({ - x = 0, - y = self.fall_speed, - z = 0 - }) - else - -- stop accelerating once max fall speed hit - self.object:set_acceleration({x = 0, y = 0, z = 0}) + -- don't exceed mob fall speed + if v.y < self.fall_speed then + fall_speed = self.fall_speed end - -- in water then float up - if self.standing_in - and minetest.registered_nodes[self.standing_in].groups.water then + -- in water then use liquid viscosity for float/sink speed + if (self.standing_in + and minetest.registered_nodes[self.standing_in].groups.liquid) --water) + or (self.standing_on + and minetest.registered_nodes[self.standing_in].groups.liquid) then -- water) then + + local visc = min( + minetest.registered_nodes[self.standing_in].liquid_viscosity, 7) if self.floats == 1 then - self.object:set_acceleration({ - x = 0, - y = -self.fall_speed / (max(1, v.y) ^ 8), -- 8 was 2 - z = 0 - }) + -- floating up + if visc > 0 then + fall_speed = max(1, v.y) / (visc + 1) + end + else + -- sinking down + if visc > 0 then + fall_speed = -(max(1, v.y) / (visc + 1)) + end end else @@ -2711,6 +2693,13 @@ function mob_class:falling(pos) self.old_y = self.object:get_pos().y end end + + -- fall at set speed + self.object:set_acceleration({ + x = 0, + y = fall_speed, + z = 0 + }) end @@ -3197,6 +3186,7 @@ function mob_class:mob_activate(staticdata, def, dtime) self.selectionbox = selbox self.visual_size = vis_size self.standing_in = "air" + self.standing_on = "air" -- check existing nametag if not self.nametag then @@ -3323,6 +3313,9 @@ function mob_class:on_step(dtime, moveresult) self.standing_in = node_ok({ x = pos.x, y = pos.y + y_level + 0.25, z = pos.z}, "air").name + self.standing_on = node_ok({ + x = pos.x, y = pos.y + y_level - 0.25, z = pos.z}, "air").name + --print("standing in " .. self.standing_in) -- if standing inside solid block then jump to escape From d20ea74890b2543202e1ae9d38ac2ce945ea265f Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Wed, 1 Jul 2020 14:43:17 +0100 Subject: [PATCH 088/135] fix runaway player nil --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 6d2eafd..39466b1 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200629", + version = "20200701", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -1963,8 +1963,8 @@ function mob_class:do_runaway_from() if name ~= "" and name ~= self.name and specific_runaway(self.runaway_from, name) then - p = player:get_pos() sp = s + p = player and player:get_pos() or s -- aim higher to make looking up hills more realistic p.y = p.y + 1 From edf04690098597c0bb7b319b8ccd6a9923b7986d Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Wed, 1 Jul 2020 15:14:31 +0100 Subject: [PATCH 089/135] tidy up spawner formspec --- locale/template.pot | 6 +++++- spawner.lua | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/locale/template.pot b/locale/template.pot index af31440..fa32c0f 100644 --- a/locale/template.pot +++ b/locale/template.pot @@ -106,13 +106,17 @@ msgid "Mob Spawner" msgstr "" #: spawner.lua -msgid "Mob MinLight MaxLight Amount PlayerDist" +msgid "(mob name) (min light) (max light) (amount) (player distance) (Y offset)" msgstr "" #: spawner.lua msgid "Spawner Not Active (enter settings)" msgstr "" +#@ spawner.lua +msgid "Command:" +msgstr "" + #: spawner.lua msgid "Spawner Active (@1)" msgstr "" diff --git a/spawner.lua b/spawner.lua index 8940850..da86301 100644 --- a/spawner.lua +++ b/spawner.lua @@ -3,7 +3,7 @@ local S = mobs.intllib -- mob spawner -local spawner_default = "mobs_animal:pumba 10 15 0 0" +local spawner_default = "mobs_animal:pumba 10 15 0 0 0" minetest.register_node("mobs:spawner", { tiles = {"mob_spawner.png"}, @@ -17,10 +17,17 @@ minetest.register_node("mobs:spawner", { local meta = minetest.get_meta(pos) + -- setup formspec + local head = S("(mob name) (min light) (max light) (amount)" + .. " (player distance) (Y offset)") + -- text entry formspec meta:set_string("formspec", - "field[text;" .. S("Mob MinLight MaxLight Amount PlayerDist") + "size[9,3.5]" + .. "label[0.15,0.5;" .. minetest.formspec_escape(head) .. "]" + .. "field[1,2.5;7.5,0.8;text;" .. S("Command:") .. ";${command}]") + meta:set_string("infotext", S("Spawner Not Active (enter settings)")) meta:set_string("command", spawner_default) end, @@ -54,11 +61,11 @@ minetest.register_node("mobs:spawner", { local pla = tonumber(comm[5]) -- player distance (0 to disable) local yof = tonumber(comm[6]) or 0 -- Y offset to spawn mob - if mob and mob ~= "" and mobs.spawning_mobs[mob] == true + if mob and mob ~= "" and mobs.spawning_mobs[mob] and num and num >= 0 and num <= 10 and mlig and mlig >= 0 and mlig <= 15 and xlig and xlig >= 0 and xlig <= 15 - and pla and pla >=0 and pla <= 20 + and pla and pla >= 0 and pla <= 20 and yof and yof > -10 and yof < 10 then meta:set_string("command", fields.text) @@ -67,7 +74,7 @@ minetest.register_node("mobs:spawner", { else minetest.chat_send_player(name, S("Mob Spawner settings failed!")) minetest.chat_send_player(name, - S("Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] distance[1-20] y_offset[-10 to 10]”")) + S("Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”")) end end }) From 804f8b9a0c8308371a1918ce94094fb5ff122bc7 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Fri, 3 Jul 2020 15:46:50 +0100 Subject: [PATCH 090/135] nil check during mob detatch in mount.lua --- mount.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mount.lua b/mount.lua index 63c8509..d60413a 100644 --- a/mount.lua +++ b/mount.lua @@ -84,7 +84,7 @@ local function force_detach(player) local entity = attached_to:get_luaentity() - if entity.driver + if entity and entity.driver and entity.driver == player then entity.driver = nil From 81dd3d75c0383071cd84f40d599d71d8aec78d1c Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Fri, 17 Jul 2020 20:58:52 +0100 Subject: [PATCH 091/135] add randomly_turn setting to mob definition --- api.lua | 7 ++++--- api.txt | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index 39466b1..1f379f7 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200701", + version = "20200717", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -2136,7 +2136,7 @@ function mob_class:do_states(dtime) if self.state == "stand" then - if random(4) == 1 then + if self.randomly_turn and random(4) == 1 then local lp local s = self.object:get_pos() @@ -2224,7 +2224,7 @@ function mob_class:do_states(dtime) yaw = self:set_yaw(yaw, 8) -- otherwise randomly turn - elseif random(100) <= 30 then + elseif self.randomly_turn and random(100) <= 30 then yaw = yaw + random(-0.5, 0.5) @@ -3564,6 +3564,7 @@ minetest.register_entity(name, setmetatable({ owner_loyal = def.owner_loyal, pushable = def.pushable, stay_near = def.stay_near, + randomly_turn = def.randomly_turn ~= false, on_spawn = def.on_spawn, diff --git a/api.txt b/api.txt index e96d07b..6a9bfd0 100644 --- a/api.txt +++ b/api.txt @@ -38,6 +38,8 @@ functions needed for the mob to work properly which contains the following: 'stand_chance' has a 0-100 chance value your mob will stand from walking. 'walk_chance' has a 0-100 chance value your mob will walk from standing, set to 0 for jumping mobs only. + 'randomly_turn' if set to false then mob will not turn to face player or + randomly turn while walking or standing. 'jump' when true allows your mob to jump updwards. 'jump_height' holds the height your mob can jump, 0 to disable jumping. 'stepheight' height of a block that your mob can easily walk up onto, From 903e81bdabdebc880624b5ce3cbeea63eb79dab7 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Fri, 17 Jul 2020 21:12:43 +0100 Subject: [PATCH 092/135] another nil check for entity --- api.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 1f379f7..fa8148e 100644 --- a/api.lua +++ b/api.lua @@ -1013,7 +1013,7 @@ function mob_class:do_env_damage() self:update_tag() end - local pos = self.object:get_pos() + local pos = self.object:get_pos() ; if not pos then return end self.time_of_day = minetest.get_timeofday() From f033cd401c13ca6e39df23ef3c1187b9bd5d64b3 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Sat, 18 Jul 2020 20:39:49 +0100 Subject: [PATCH 093/135] add nil check to general_attack --- api.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.lua b/api.lua index fa8148e..f1aeaac 100644 --- a/api.lua +++ b/api.lua @@ -1831,7 +1831,7 @@ function mob_class:general_attack() return end - local s = self.object:get_pos() + local s = self.object:get_pos() ; if not s then return end local objs = minetest.get_objects_inside_radius(s, self.view_range) -- remove entities we aren't interested in From 4814f53885123355b4c2cbb0e0300955e9227c41 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Mon, 20 Jul 2020 07:27:55 +0100 Subject: [PATCH 094/135] nil check for get_distance --- api.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.lua b/api.lua index f1aeaac..db1820b 100644 --- a/api.lua +++ b/api.lua @@ -195,7 +195,7 @@ end -- calculate distance local get_distance = function(a, b) - +if not a or not b then return 50 end -- nil check local x, y, z = a.x - b.x, a.y - b.y, a.z - b.z return square(x * x + y * y + z * z) From c4f56f4204818a6c524cbe5dfd40fd86aa894e80 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Mon, 20 Jul 2020 07:32:24 +0100 Subject: [PATCH 095/135] nill check for follow_flop --- api.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.lua b/api.lua index db1820b..dbbe374 100644 --- a/api.lua +++ b/api.lua @@ -2001,7 +2001,7 @@ function mob_class:follow_flop() and self.state ~= "attack" and self.state ~= "runaway" then - local s = self.object:get_pos() + local s = self.object:get_pos() ; if not s then return end local players = minetest.get_connected_players() for n = 1, #players do From ec3c6fb518c6e3814166b61474a1b0ac409da2ba Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Mon, 20 Jul 2020 07:36:39 +0100 Subject: [PATCH 096/135] update api.txt --- api.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.txt b/api.txt index 6a9bfd0..9dea80a 100644 --- a/api.txt +++ b/api.txt @@ -31,7 +31,7 @@ functions needed for the mob to work properly which contains the following: '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, + 'passive' when false 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. From 12715707807af02e71e1971a50cd40127d5b4ae2 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 20 Jul 2020 18:10:08 +0100 Subject: [PATCH 097/135] nil check in do_states --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index dbbe374..c7b510b 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200717", + version = "20200720", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -2132,7 +2132,7 @@ end -- execute current state (stand, walk, run, attacks) function mob_class:do_states(dtime) - local yaw = self.object:get_yaw() or 0 + local yaw = self.object:get_yaw() ; if not yaw then return end if self.state == "stand" then From 469e35d6e09b098627f525dfb00709243cfe0585 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Thu, 23 Jul 2020 20:13:15 +0100 Subject: [PATCH 098/135] tidy api --- api.lua | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/api.lua b/api.lua index c7b510b..0ebeedd 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200720", + version = "20200723", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -195,7 +195,9 @@ end -- calculate distance local get_distance = function(a, b) -if not a or not b then return 50 end -- nil check + +-- if not a or not b then return 50 end -- nil check + local x, y, z = a.x - b.x, a.y - b.y, a.z - b.z return square(x * x + y * y + z * z) @@ -812,6 +814,11 @@ local remove_mob = function(self, decrease) --print("-- active mobs: " .. active_mobs .. " / " .. active_limit) end +-- global function for removing mobs +function mobs:remove(self, decrease) + remove_mob(self, decrease) +end + -- check if mob is dead or only hurt function mob_class:check_for_death(cmi_cause) @@ -1538,7 +1545,9 @@ local height_switcher = false function mob_class:smart_mobs(s, p, dist, dtime) local s1 = self.path.lastpos - local target_pos = self.attack:get_pos() +-- local target_pos = self.attack:get_pos() +local target_pos = p + -- is it becoming stuck? if abs(s1.x - s.x) + abs(s1.z - s.z) < .5 then @@ -1602,10 +1611,6 @@ function mob_class:smart_mobs(s, p, dist, dtime) minetest.after(1, function(self) - if not self.object:get_luaentity() then - return - end - if self.object:get_luaentity() then if has_lineofsight then @@ -1687,7 +1692,10 @@ function mob_class:smart_mobs(s, p, dist, dtime) ]] self.state = "" + +if self.attack then self:do_attack(self.attack) +end -- no path found, try something else if not self.path.way then @@ -1785,12 +1793,16 @@ function mob_class:smart_mobs(s, p, dist, dtime) -- will try again in 2 second self.path.stuck_timer = stuck_timeout - 2 - elseif s.y < p1.y and (not self.fly) then + elseif s.y < p1.y and (not self.fly) then self:do_jump() --add jump to pathfinding self.path.following = true else -- yay i found path +if self.attack then self:mob_sound(self.sounds.war_cry) +else + self:mob_sound(self.sounds.random) +end self:set_velocity(self.walk_velocity) -- follow path now that it has it @@ -2284,12 +2296,12 @@ function mob_class:do_states(dtime) -- attack routines (explode, dogfight, shoot, dogshoot) elseif self.state == "attack" then - -- calculate distance from mob and enemy + -- get mob and enemy positions and distance between local s = self.object:get_pos() - local p = self.attack:get_pos() or s - local dist = get_distance(p, s) + local p = self.attack:get_pos() + local dist = p and get_distance(p, s) or 500 - -- stop attacking if player invisible or out of range + -- stop attacking if player out of range or invisible if dist > self.view_range or not self.attack or not self.attack:get_pos() @@ -2425,7 +2437,8 @@ function mob_class:do_states(dtime) elseif self.attack_type == "dogfight" or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 2) - or (self.attack_type == "dogshoot" and dist <= self.reach and self:dogswitch() == 0) then + or (self.attack_type == "dogshoot" and dist <= self.reach + and self:dogswitch() == 0) then if self.fly and dist > self.reach then @@ -2472,7 +2485,6 @@ function mob_class:do_states(dtime) }) end end - end -- rnd: new movement direction From cb465559a1836f6f52f2ab4f4225574622234f89 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Sat, 25 Jul 2020 10:52:29 +0100 Subject: [PATCH 099/135] code tweak and tidy --- api.lua | 135 ++++++++++++++++++++------------------------------------ api.txt | 9 ++++ 2 files changed, 56 insertions(+), 88 deletions(-) diff --git a/api.lua b/api.lua index 0ebeedd..791cd48 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200723", + version = "20200725", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -230,7 +230,7 @@ function mob_class:collision() end --- check string against another string or table +-- check if string exists in another string or table local check_for = function(look_for, look_inside) if type(look_inside) == "string" and look_inside == look_for then @@ -291,8 +291,7 @@ function mob_class:set_velocity(v) local new_vel = { x = (sin(yaw) * -v) + c_x, y = vel.y, - z = (cos(yaw) * v) + c_y - } + z = (cos(yaw) * v) + c_y} self.object:set_velocity(new_vel) end @@ -353,9 +352,9 @@ function mob_class:set_animation(anim, force) return end - -- check for more than one animation local num = 0 + -- check for more than one animation (max 4) for n = 1, 4 do if self.animation[anim .. n .. "_start"] @@ -388,7 +387,7 @@ end -- above function exported for mount.lua function mobs:set_animation(entity, anim) - mob_class.set_animation(entity, anim) + entity.set_animation(entity, anim) end @@ -750,7 +749,7 @@ function mob_class:item_drop() -- was mob killed by player? local death_by_player = self.cause_of_death and self.cause_of_death.puncher - and self.cause_of_death.puncher:is_player() or nil + and self.cause_of_death.puncher:is_player() local obj, item, num @@ -970,12 +969,11 @@ function mob_class:is_at_cliff() return false end - -- if object no longer exists then return - if not self.object:get_luaentity() then - return false - end - + -- get yaw but if nil returned object no longer exists local yaw = self.object:get_yaw() + + if not yaw then return false end + local dir_x = -sin(yaw) * (self.collisionbox[4] + 0.5) local dir_z = cos(yaw) * (self.collisionbox[4] + 0.5) local pos = self.object:get_pos() @@ -1035,24 +1033,6 @@ function mob_class:do_env_damage() -- particle appears at random mob height pos.y = pos.y + random(self.collisionbox[2], self.collisionbox[5]) - -- is mob light sensative, or scared of the dark :P - if self.light_damage ~= 0 then - - local light = minetest.get_node_light(pos) or 0 - - if light >= self.light_damage_min - and light <= self.light_damage_max then - - self.health = self.health - self.light_damage - - effect(pos, 5, "tnt_smoke.png") - - if self:check_for_death({type = "light"}) then - return true - end - end - end - local nodef = minetest.registered_nodes[self.standing_in] -- water @@ -1098,6 +1078,24 @@ function mob_class:do_env_damage() end end + -- is mob light sensative, or scared of the dark :P + if self.light_damage ~= 0 then + + local light = minetest.get_node_light(pos) or 0 + + if light >= self.light_damage_min + and light <= self.light_damage_max then + + self.health = self.health - self.light_damage + + effect(pos, 5, "tnt_smoke.png") + + if self:check_for_death({type = "light"}) then + return true + end + end + end + --- suffocation inside solid node if (self.suffocation and self.suffocation ~= 0) and (nodef.walkable == nil or nodef.walkable == true) @@ -1545,8 +1543,7 @@ local height_switcher = false function mob_class:smart_mobs(s, p, dist, dtime) local s1 = self.path.lastpos --- local target_pos = self.attack:get_pos() -local target_pos = p + local target_pos = p -- is it becoming stuck? @@ -1693,9 +1690,9 @@ local target_pos = p self.state = "" -if self.attack then - self:do_attack(self.attack) -end + if self.attack then + self:do_attack(self.attack) + end -- no path found, try something else if not self.path.way then @@ -1785,7 +1782,6 @@ end minetest.add_item(p1, ItemStack(node1)) minetest.set_node(p1, {name = "air"}) end - end end end @@ -1798,11 +1794,12 @@ end self.path.following = true else -- yay i found path -if self.attack then - self:mob_sound(self.sounds.war_cry) -else - self:mob_sound(self.sounds.random) -end + if self.attack then + self:mob_sound(self.sounds.war_cry) + else + self:mob_sound(self.sounds.random) + end + self:set_velocity(self.walk_velocity) -- follow path now that it has it @@ -1812,26 +1809,6 @@ end end --- specific attacks -local specific_attack = function(list, what) - - -- no list so attack default (player, animals etc.) - if list == nil then - return true - end - - -- found entity on list to attack? - for no = 1, #list do - - if list[no] == what then - return true - end - end - - return false -end - - -- general attack function for all mobs function mob_class:general_attack() @@ -1859,7 +1836,8 @@ function mob_class:general_attack() or self.attack_players == false or (self.owner and self.type ~= "monster") or mobs.invis[objs[n]:get_player_name()] - or not specific_attack(self.specific_attack, "player") then + or (self.specific_attack + and not check_for("player", self.specific_attack)) then objs[n] = nil --print("- pla", n) end @@ -1872,7 +1850,8 @@ function mob_class:general_attack() or (not self.attack_animals and ent.type == "animal") or (not self.attack_monsters and ent.type == "monster") or (not self.attack_npcs and ent.type == "npc") - or not specific_attack(self.specific_attack, ent.name) then + or (self.specific_attack + and not check_for(ent.name, self.specific_attack)) then objs[n] = nil --print("- mob", n, self.name, ent.name) end @@ -1915,26 +1894,6 @@ function mob_class:general_attack() end --- specific runaway -local specific_runaway = function(list, what) - - -- no list so do not run - if list == nil then - return false - end - - -- found entity on list to attack? - for no = 1, #list do - - if list[no] == what then - return true - end - end - - return false -end - - -- find someone to runaway from function mob_class:do_runaway_from() @@ -1973,7 +1932,7 @@ function mob_class:do_runaway_from() -- find specific mob to runaway from if name ~= "" and name ~= self.name - and specific_runaway(self.runaway_from, name) then + and (self.runaway_from and check_for(name, self.runaway_from)) then sp = s p = player and player:get_pos() or s @@ -2506,7 +2465,7 @@ function mob_class:do_states(dtime) return end - if abs(p1.x-s.x) + abs(p1.z - s.z) < 0.6 then + if abs(p1.x - s.x) + abs(p1.z - s.z) < 0.6 then -- reached waypoint, remove it from queue table.remove(self.path.way, 1) end @@ -2664,9 +2623,9 @@ function mob_class:falling(pos) -- in water then use liquid viscosity for float/sink speed if (self.standing_in - and minetest.registered_nodes[self.standing_in].groups.liquid) --water) + and minetest.registered_nodes[self.standing_in].groups.liquid) or (self.standing_on - and minetest.registered_nodes[self.standing_in].groups.liquid) then -- water) then + and minetest.registered_nodes[self.standing_in].groups.liquid) then local visc = min( minetest.registered_nodes[self.standing_in].liquid_viscosity, 7) @@ -2928,7 +2887,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage) dir = dir or {x = 0, y = 0, z = 0} -- use tool knockback value or default - kb = tool_capabilities.damage_groups["knockback"] or kb -- (kb * 1.5) + kb = tool_capabilities.damage_groups["knockback"] or kb self.object:set_velocity({ x = dir.x * kb, diff --git a/api.txt b/api.txt index 9dea80a..c26334f 100644 --- a/api.txt +++ b/api.txt @@ -344,6 +344,15 @@ Adding Mobs in World Returns false if mob could not be added, returns mob object if spawned ok. +Removing Mob from World +----------------------- + + mobs:remove(self, decrease) + +Removes mob 'self' from the world and if 'decrease' is true then the mob counter +will also be decreased by one. + + Spawning Mobs in World ---------------------- From 565c0851eb22e28eeb157755745c2a56d9be64b6 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Mon, 27 Jul 2020 19:17:07 +0100 Subject: [PATCH 100/135] nil check for self.attack --- api.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 791cd48..795e02b 100644 --- a/api.lua +++ b/api.lua @@ -6,7 +6,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200725", + version = "20200727", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -2257,7 +2257,7 @@ function mob_class:do_states(dtime) -- get mob and enemy positions and distance between local s = self.object:get_pos() - local p = self.attack:get_pos() + local p = self.attack and self.attack:get_pos() local dist = p and get_distance(p, s) or 500 -- stop attacking if player out of range or invisible @@ -3701,6 +3701,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, -- Do mobs spawn at all? if not mobs_spawn or not mobs.spawning_mobs[name] then +--print ("--- spawning not registered for " .. name) return end From 81b5e40d09bd8a593b74e108e2852ec797e11527 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Fri, 14 Aug 2020 08:36:04 +0100 Subject: [PATCH 101/135] update french translation (thanks dacmot) --- locale/fr.po | 23 +++++++++++++++-------- locale/template.pot | 4 ++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/locale/fr.po b/locale/fr.po index 25b920c..53b5f9f 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -3,20 +3,19 @@ # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # +#, fuzzy msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-29 09:13+0200\n" -"PO-Revision-Date: 2017-07-29 09:20+0200\n" +"PO-Revision-Date: 2020-08-13 21:20+0500\n" +"Last-Translator: Olivier Dragon \n" "Language-Team: \n" +"Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.8.12\n" -"Last-Translator: fat115 \n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" -"Language: fr\n" #: api.lua msgid "** Peaceful Mode Active - No Monsters Will Spawn" @@ -64,7 +63,7 @@ msgstr "Renommer" #: crafts.lua msgid "Name Tag" -msgstr "Étiquette pour collier" +msgstr "Étiquette de collier" #: crafts.lua msgid "Leather" @@ -102,18 +101,26 @@ msgstr "Selle" msgid "Mob Fence" msgstr "Clôture à animaux" +#: crafts.lua +msgid "Mob Fence Top" +msgstr "Haut de clôture à animaux" + #: spawner.lua msgid "Mob Spawner" msgstr "Générateur de mob" #: spawner.lua -msgid "Mob MinLight MaxLight Amount PlayerDist" -msgstr "Mob MinLumière MaxLumière Quantité DistanceJoueur" +msgid "(mob name) (min light) (max light) (amount) (player distance) (Y offset)" +msgstr "(Nom) (MinLumière) (MaxLumière) (Quantité) (Distance du Joueur) (Décalage en Y)" #: spawner.lua msgid "Spawner Not Active (enter settings)" msgstr "Générateur non actif (entrez les paramètres)" +#: spawner.lua +msgid "Command:" +msgstr "Commande:" + #: spawner.lua msgid "Spawner Active (@1)" msgstr "Générateur actif (@1)" diff --git a/locale/template.pot b/locale/template.pot index fa32c0f..a625ee3 100644 --- a/locale/template.pot +++ b/locale/template.pot @@ -101,6 +101,10 @@ msgstr "" msgid "Mob Fence" msgstr "" +#: crafts.lua +msgid "Mob Fence Top" +msgstr "" + #: spawner.lua msgid "Mob Spawner" msgstr "" From ae62b1410d76d550b4590886edfc71239a7460de Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Wed, 19 Aug 2020 12:07:44 +0100 Subject: [PATCH 102/135] update api.txt --- api.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api.txt b/api.txt index c26334f..73718e0 100644 --- a/api.txt +++ b/api.txt @@ -199,7 +199,8 @@ functions needed for the mob to work properly which contains the following: 'double_melee_attack' when true has the api choose between 'punch' and 'punch2' animations. [DEPRECATED] - 'animation' holds a table containing animation names and settings for use with mesh models: + '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. From 9f02777f14b3c915e59ec81592558c4cbb375b01 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 20 Aug 2020 20:29:34 +0100 Subject: [PATCH 103/135] re-add nil check for get_distance function --- api.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 795e02b..7505491 100644 --- a/api.lua +++ b/api.lua @@ -196,7 +196,7 @@ end -- calculate distance local get_distance = function(a, b) --- if not a or not b then return 50 end -- nil check + if not a or not b then return 50 end -- nil check local x, y, z = a.x - b.x, a.y - b.y, a.z - b.z From 497ff7241f7f3c1d783603d0cb17d96a331c1b55 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 25 Aug 2020 09:44:00 +0100 Subject: [PATCH 104/135] update translation detection --- api.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index 7505491..90ca429 100644 --- a/api.lua +++ b/api.lua @@ -1,12 +1,15 @@ --- Intllib and CMI support check +-- Load support for intllib. local MP = minetest.get_modpath(minetest.get_current_modname()) -local S, NS = dofile(MP .. "/intllib.lua") +local S = minetest.get_translator and minetest.get_translator("mobs_redo") or + dofile(MP .. "/intllib.lua") + +-- CMI support check local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200727", + version = "20200825", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } From 477148962fc852a0ee3dee2e4b4ba4912dd82787 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 25 Aug 2020 09:55:23 +0100 Subject: [PATCH 105/135] update intllib.lua --- intllib.lua | 46 ++-------------------------------------------- 1 file changed, 2 insertions(+), 44 deletions(-) diff --git a/intllib.lua b/intllib.lua index 6669d72..adb0f88 100644 --- a/intllib.lua +++ b/intllib.lua @@ -1,45 +1,3 @@ +-- Support for the old multi-load method +dofile(minetest.get_modpath("intllib").."/init.lua") --- Fallback functions for when `intllib` is not installed. --- Code released under Unlicense . - --- Get the latest version of this file at: --- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua - -local function format(str, ...) - local args = { ... } - local function repl(escape, open, num, close) - if escape == "" then - local replacement = tostring(args[tonumber(num)]) - if open == "" then - replacement = replacement..close - end - return replacement - else - return "@"..open..num..close - end - end - return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl)) -end - -local gettext, ngettext -if minetest.get_modpath("intllib") then - if intllib.make_gettext_pair then - -- New method using gettext. - gettext, ngettext = intllib.make_gettext_pair() - else - -- Old method using text files. - gettext = intllib.Getter() - end -end - --- Fill in missing functions. - -gettext = gettext or function(msgid, ...) - return format(msgid, ...) -end - -ngettext = ngettext or function(msgid, msgid_plural, n, ...) - return format(n==1 and msgid or msgid_plural, ...) -end - -return gettext, ngettext From 39002cf4e750a593ef543c955d6f3d8c2ff5d3c7 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 29 Aug 2020 09:35:23 +0100 Subject: [PATCH 106/135] mt5.x translations added/updated (thanks dacmot) --- locale/mobs.de_DE.tr | 34 ++++++++++++++++++++++++++++++++++ locale/mobs.en.tr | 34 ++++++++++++++++++++++++++++++++++ locale/mobs.es.tr | 34 ++++++++++++++++++++++++++++++++++ locale/mobs.fr.tr | 34 ++++++++++++++++++++++++++++++++++ locale/mobs.it.tr | 34 ++++++++++++++++++++++++++++++++++ locale/mobs.ms.tr | 34 ++++++++++++++++++++++++++++++++++ locale/mobs.pt.tr | 34 ++++++++++++++++++++++++++++++++++ locale/mobs.ru.tr | 34 ++++++++++++++++++++++++++++++++++ locale/mobs.tr.tr | 34 ++++++++++++++++++++++++++++++++++ locale/mobs.zh_CN.tr | 34 ++++++++++++++++++++++++++++++++++ locale/mobs.zh_TW.tr | 34 ++++++++++++++++++++++++++++++++++ 11 files changed, 374 insertions(+) create mode 100644 locale/mobs.de_DE.tr create mode 100644 locale/mobs.en.tr create mode 100644 locale/mobs.es.tr create mode 100644 locale/mobs.fr.tr create mode 100644 locale/mobs.it.tr create mode 100644 locale/mobs.ms.tr create mode 100644 locale/mobs.pt.tr create mode 100644 locale/mobs.ru.tr create mode 100644 locale/mobs.tr.tr create mode 100644 locale/mobs.zh_CN.tr create mode 100644 locale/mobs.zh_TW.tr diff --git a/locale/mobs.de_DE.tr b/locale/mobs.de_DE.tr new file mode 100644 index 0000000..f11b665 --- /dev/null +++ b/locale/mobs.de_DE.tr @@ -0,0 +1,34 @@ +# textdomain:mobs +#** Peaceful Mode Active - No Monsters Will Spawn= +@1 (Tamed)=@1 (Gezähmt) +@1 at full health (@2)=@1 bei voller Gesundheit (@2) +@1 has been tamed!=@1 wurde gezähmt! +@1 is owner!=@1 ist der Besitzer! +#Active Mob Limit Reached!= +Already protected!=Bereits geschützt! +#Change= +#Command:= +Enter name:=Namen eingeben: +#Enter texture:= +Lasso (right-click animal to put in inventory)=Lasso (Rechtsklick auf Tier, um es zu nehmen) +Leather=Leder +Meat=Fleisch +Missed!=Daneben! +Mob Fence=Kreaturen Zaun +#Mob Fence Top= +Mob Protection Rune=Kreaturschutzrune +#Mob Reset Stick= +#Mob Spawner= +Mob Spawner settings failed!=Kreaturenspawner-Einstellungen gescheitert! +Mob has been protected!=Kreatur wurde geschützt! +Name Tag=Namensschild +Net (right-click animal to put in inventory)=Netz (Rechtsklick auf Tier, um es zu nehmen) +Not tamed!=Nicht gezähmt! +Raw Meat=Rohes Fleisch +Rename=Umbenennen +Saddle=Sattel +Spawner Active (@1)=Spawner aktiv (@1) +Spawner Not Active (enter settings)=Nicht aktiv (Einstellungen eingeben) +Steel Shears (right-click to shear)=Stahlschere (Rechtsklick zum Scheren) +#Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”= +#lifetimer expired, removed @1= diff --git a/locale/mobs.en.tr b/locale/mobs.en.tr new file mode 100644 index 0000000..fab62b7 --- /dev/null +++ b/locale/mobs.en.tr @@ -0,0 +1,34 @@ +# textdomain:mobs +#** Peaceful Mode Active - No Monsters Will Spawn= +#@1 (Tamed)= +#@1 at full health (@2)= +#@1 has been tamed!= +#@1 is owner!= +#Active Mob Limit Reached!= +#Already protected!= +#Change= +#Command:= +#Enter name:= +#Enter texture:= +#Lasso (right-click animal to put in inventory)= +#Leather= +#Meat= +#Missed!= +#Mob Fence= +#Mob Fence Top= +#Mob Protection Rune= +#Mob Reset Stick= +#Mob Spawner= +#Mob Spawner settings failed!= +#Mob has been protected!= +#Name Tag= +#Net (right-click animal to put in inventory)= +#Not tamed!= +#Raw Meat= +#Rename= +#Saddle= +#Spawner Active (@1)= +#Spawner Not Active (enter settings)= +#Steel Shears (right-click to shear)= +#Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”= +#lifetimer expired, removed @1= diff --git a/locale/mobs.es.tr b/locale/mobs.es.tr new file mode 100644 index 0000000..29ee934 --- /dev/null +++ b/locale/mobs.es.tr @@ -0,0 +1,34 @@ +# textdomain:mobs +#** Peaceful Mode Active - No Monsters Will Spawn= +@1 (Tamed)=@1 (Domesticado) +@1 at full health (@2)=@1 con salud llena (@2) +@1 has been tamed!=@1 ha sido domesticado! +@1 is owner!=@1 es el dueño! +#Active Mob Limit Reached!= +Already protected!=Ya está protegido! +#Change= +#Command:= +Enter name:=Ingrese nombre: +#Enter texture:= +Lasso (right-click animal to put in inventory)=Lazo (click derecho en animal para colocar en inventario) +Leather=Cuero +Meat=Carne +Missed!=Perdido! +#Mob Fence= +#Mob Fence Top= +Mob Protection Rune=Runa de protección de Mob +#Mob Reset Stick= +#Mob Spawner= +Mob Spawner settings failed!=Configuracion de generador de Mob falló! +Mob has been protected!=El mob ha sido protegido! +Name Tag=Nombrar etiqueta +Net (right-click animal to put in inventory)=Red (click derecho en animal para colocar en inventario) +Not tamed!=No domesticado! +Raw Meat=Carne cruda +Rename=Renombrar +Saddle=Montura +Spawner Active (@1)=Generador activo (@1) +Spawner Not Active (enter settings)=Generador no activo (ingrese config) +Steel Shears (right-click to shear)=Tijera de acero (click derecho para esquilar) +#Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”= +#lifetimer expired, removed @1= diff --git a/locale/mobs.fr.tr b/locale/mobs.fr.tr new file mode 100644 index 0000000..3e137fb --- /dev/null +++ b/locale/mobs.fr.tr @@ -0,0 +1,34 @@ +# textdomain:mobs +** Peaceful Mode Active - No Monsters Will Spawn=** Mode pacifique activé - aucun monstre ne sera généré +@1 (Tamed)=@1 (apprivoisé) +@1 at full health (@2)=@1 est en pleine forme (@2) +@1 has been tamed!=@1 a été apprivoisé ! +@1 is owner!=Appartient à @1 ! +Active Mob Limit Reached!=Limite atteinte du nombre des êtres vivants actifs ! +Already protected!=Déjà protégé ! +Change=Changer +Command:=Commande : +Enter name:=Saisissez un nom : +Enter texture:=Saisissez une texture : +Lasso (right-click animal to put in inventory)=Lasso (clic droit sur l'animal pour le mettre dans l'inventaire) +Leather=Cuir +Meat=Viande +Missed!=Raté ! +Mob Fence= Clôture à animaux +Mob Fence Top=Haut de clôture à animaux +Mob Protection Rune=Rune de protection des animaux +Mob Reset Stick=Baguette de réinitialisation des êtres vivants +Mob Spawner=Créateur d'êtres vivants +Mob Spawner settings failed!=Échec des paramètres du créateur d'être vivants ! +Mob has been protected!=L'animal a été protégé ! +Name Tag=Étiquette de collier +Net (right-click animal to put in inventory)=Filet (clic droit sur l'animal pour le mettre dans l'inventaire) +Not tamed!=Non-apprivoisé ! +Raw Meat=Viande crue +Rename=Renommer +Saddle=Selle +Spawner Active (@1)=Créateur actif (@1) +Spawner Not Active (enter settings)=Créateur non actif (entrez les paramètres) +Steel Shears (right-click to shear)=Ciseaux à laine (clic droit pour tondre) +Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”=Syntaxe : «name min_lumière[0-14] max_lumière[0-14] max_être_vivant_dans_région[0 pour désactiver] distance_joueur[1-20] décalage_y[-10 to 10]» +lifetimer expired, removed @1=Être immortel expiré ; @1 retiré diff --git a/locale/mobs.it.tr b/locale/mobs.it.tr new file mode 100644 index 0000000..6b5edd9 --- /dev/null +++ b/locale/mobs.it.tr @@ -0,0 +1,34 @@ +# textdomain:mobs +** Peaceful Mode Active - No Monsters Will Spawn=** Modalità pacifica attiva - non comparirà nessun mostro +@1 (Tamed)=@1 (Addomesticato) +@1 at full health (@2)=@1 in piena salute (@2) +@1 has been tamed!=@1 è stato addomesticato! +@1 is owner!=Il padrone è @1! +#Active Mob Limit Reached!= +Already protected!=Già protetto! +#Change= +#Command:= +Enter name:=Inserire il nome: +#Enter texture:= +Lasso (right-click animal to put in inventory)=Lazo (click di destro per mettere l'animale nell'inventario) +Leather=Pelle +Meat=Carne +Missed!=Mancato! +Mob Fence=Recinzione per mob +#Mob Fence Top= +Mob Protection Rune=Runa di protezione per mob +#Mob Reset Stick= +#Mob Spawner= +Mob Spawner settings failed!=Impostazioni del generatore di mob fallite! +Mob has been protected!=Il mob è stato protetto! +Name Tag=Targhetta +Net (right-click animal to put in inventory)=Rete (click destro per mettere l'animale nell'inventario) +Not tamed!=Non addomesticato! +Raw Meat=Carne cruda +Rename=Rinomina +Saddle=Sella +Spawner Active (@1)=Generatore attivo (@1) +Spawner Not Active (enter settings)=Generatore inattivo (inserire le impostazioni) +Steel Shears (right-click to shear)=Cesoie d'acciaio (click destro per tosare) +#Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”= +#lifetimer expired, removed @1= diff --git a/locale/mobs.ms.tr b/locale/mobs.ms.tr new file mode 100644 index 0000000..f79e2fb --- /dev/null +++ b/locale/mobs.ms.tr @@ -0,0 +1,34 @@ +# textdomain:mobs +** Peaceful Mode Active - No Monsters Will Spawn=** Mod Aman Diaktifkan - Tiada Raksasa Akan Muncul +@1 (Tamed)=@1 (Jinak) +@1 at full health (@2)=Mata kesihatan @1 telah penuh (@2) +@1 has been tamed!=@1 telah dijinakkan! +@1 is owner!=Ini hak milik @1! +#Active Mob Limit Reached!= +Already protected!=Telah dilindungi! +#Change= +#Command:= +Enter name:=Masukkan nama: +#Enter texture:= +Lasso (right-click animal to put in inventory)=Tanjul (klik-kanan haiwan untuk masukkan ke inventori) +Leather=Kulit +Meat=Daging Bakar +Missed!=Terlepas! +Mob Fence=Pagar Mob +#Mob Fence Top= +Mob Protection Rune=Rune Perlindungan Mob +#Mob Reset Stick= +#Mob Spawner= +Mob Spawner settings failed!=Penetapan Pewujud Mob gagal! +Mob has been protected!=Mob telah pun dilindungi! +Name Tag=Tanda Nama +Net (right-click animal to put in inventory)=Jaring (klik-kanan haiwan untuk masukkan ke inventori) +Not tamed!=Belum dijinakkan! +Raw Meat=Daging Mentah +Rename=Namakan semula +Saddle=Pelana +Spawner Active (@1)=Pewujud Mob Aktif (@1) +Spawner Not Active (enter settings)=Pewujud Mob Tidak Aktif (masukkan tetapan) +Steel Shears (right-click to shear)=Ketam Keluli (klik-kanan untuk mengetam bulu biri-biri) +#Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”= +#lifetimer expired, removed @1= diff --git a/locale/mobs.pt.tr b/locale/mobs.pt.tr new file mode 100644 index 0000000..b62e989 --- /dev/null +++ b/locale/mobs.pt.tr @@ -0,0 +1,34 @@ +# textdomain:mobs +#** Peaceful Mode Active - No Monsters Will Spawn= +#@1 (Tamed)= +@1 at full health (@2)=@1 em plena saude (@2) +@1 has been tamed!=@1 foi domesticado! +@1 is owner!=Dono @1! +#Active Mob Limit Reached!= +#Already protected!= +#Change= +#Command:= +Enter name:=Insira um nome: +#Enter texture:= +Lasso (right-click animal to put in inventory)=Laço (clique-direito no animal para por no inventario) +Leather=Couro +Meat=Carne +Missed!=Faltou! +#Mob Fence= +#Mob Fence Top= +#Mob Protection Rune= +#Mob Reset Stick= +#Mob Spawner= +Mob Spawner settings failed!=Configuraçao de Spawnador do Mob falhou! +#Mob has been protected!= +Name Tag=Etiqueta +Net (right-click animal to put in inventory)=Net (clique-direito no animal para por no inventario) +Not tamed!=Indomesticado! +Raw Meat=Carne crua +Rename=Renomear +#Saddle= +Spawner Active (@1)=Spawnador Ativo (@1) +Spawner Not Active (enter settings)=Spawnador Inativo (configurar) +Steel Shears (right-click to shear)=Tesoura de Aço (clique-direito para tosquiar) +#Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”= +#lifetimer expired, removed @1= diff --git a/locale/mobs.ru.tr b/locale/mobs.ru.tr new file mode 100644 index 0000000..f7c201a --- /dev/null +++ b/locale/mobs.ru.tr @@ -0,0 +1,34 @@ +# textdomain:mobs +** Peaceful Mode Active - No Monsters Will Spawn=** Мирный модус активирован - монстры не спаунятся +@1 (Tamed)=@1 (Прирученный) +@1 at full health (@2)=@1 при полном здоровье (@2) +@1 has been tamed!=@1 приручен +@1 is owner!=@1 владелец +#Active Mob Limit Reached!= +Already protected!=Уже защищен! +#Change= +#Command:= +Enter name:=Введите имя: +#Enter texture:= +Lasso (right-click animal to put in inventory)=Лассо (Правый клик - положить животное в инвентарь) +Leather=Кожа +Meat=Мясо +Missed!=Промазал! +Mob Fence=Забор от мобов +#Mob Fence Top= +Mob Protection Rune=Защитная руна мобов +#Mob Reset Stick= +#Mob Spawner= +Mob Spawner settings failed!=Настройки спаунера моба провалились +Mob has been protected!=Моб защищен! +Name Tag=Новый тэг +Net (right-click animal to put in inventory)=Сеть (Правый клик - положить животное в инвентарь) +Not tamed!=Не прирученный +Raw Meat=Сырое мясо +Rename=Переименовать +Saddle=Седло +Spawner Active (@1)=Активные спаунер (@1) +Spawner Not Active (enter settings)=Спаунер не активен (введите настройки) +Steel Shears (right-click to shear)=Ножницы (Правый клик - подстричь) +#Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”= +#lifetimer expired, removed @1= diff --git a/locale/mobs.tr.tr b/locale/mobs.tr.tr new file mode 100644 index 0000000..6e28706 --- /dev/null +++ b/locale/mobs.tr.tr @@ -0,0 +1,34 @@ +# textdomain:mobs +#** Peaceful Mode Active - No Monsters Will Spawn= +#@1 (Tamed)= +@1 at full health (@2)=@1 tam canında (@2) +@1 has been tamed!=@1 tamamen evcilleştirilmiştir! +@1 is owner!=Sahibi @1! +#Active Mob Limit Reached!= +#Already protected!= +#Change= +#Command:= +Enter name:=İsim gir: +#Enter texture:= +Lasso (right-click animal to put in inventory)=Kement (hayvana sağ tıklayarak envantere koy) +Leather=Deri +Meat=Et +Missed!=Kaçırdın! +Mob Fence=Canavar Yaratıcı +#Mob Fence Top= +#Mob Protection Rune= +#Mob Reset Stick= +#Mob Spawner= +Mob Spawner settings failed!=Yaratıcı ayarları uygulanamadı. +#Mob has been protected!= +Name Tag=İsim etiketi +Net (right-click animal to put in inventory)=Ağ (hayvana sağ tıklayarak envantere koy) +Not tamed!=Evcil değil! +Raw Meat=Çiğ et +Rename=Yeniden adlandır +#Saddle= +Spawner Active (@1)=Yaratıcı aktif (@1) +Spawner Not Active (enter settings)=Yaratıcı aktif değil (ayarlara gir) +Steel Shears (right-click to shear)=Çelik makas (sağ tıklayarak kes) +#Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”= +#lifetimer expired, removed @1= diff --git a/locale/mobs.zh_CN.tr b/locale/mobs.zh_CN.tr new file mode 100644 index 0000000..fe7556c --- /dev/null +++ b/locale/mobs.zh_CN.tr @@ -0,0 +1,34 @@ +# textdomain:mobs +** Peaceful Mode Active - No Monsters Will Spawn=** 和平模式已激活——没有怪物会产生 +@1 (Tamed)=@1(已驯服) +@1 at full health (@2)=@1已经满血(@2) +@1 has been tamed!=@1已经被驯服! +@1 is owner!=@1 是主人 +#Active Mob Limit Reached!= +Already protected!=已经被保护! +#Change= +#Command:= +Enter name:=输入名称: +#Enter texture:= +Lasso (right-click animal to put in inventory)=套索(右键单击动物以放入物品栏) +Leather=皮革 +Meat=肉 +Missed!=没抓住! +Mob Fence=Mob 栅栏 +#Mob Fence Top= +Mob Protection Rune=Mob 保护符文 +#Mob Reset Stick= +#Mob Spawner= +Mob Spawner settings failed!=Mob 孵化器设置失败! +Mob has been protected!=Mob 已经被保护了! +Name Tag=名称标签 +Net (right-click animal to put in inventory)=网(右键单击动物以放入物品栏) +Not tamed!=没有驯服! +Raw Meat=生肉 +Rename=重新命名 +Saddle=鞍 +Spawner Active (@1)=孵化器正在运转(@1) +Spawner Not Active (enter settings)=孵化器未使用(输入设置) +Steel Shears (right-click to shear)=钢剪(右键单击以剪切) +#Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”= +#lifetimer expired, removed @1= diff --git a/locale/mobs.zh_TW.tr b/locale/mobs.zh_TW.tr new file mode 100644 index 0000000..89bed26 --- /dev/null +++ b/locale/mobs.zh_TW.tr @@ -0,0 +1,34 @@ +# textdomain:mobs +** Peaceful Mode Active - No Monsters Will Spawn=** 和平模式已激活——沒有怪物會產生 +@1 (Tamed)=@1(已馴服) +@1 at full health (@2)=@1已經滿血(@2) +@1 has been tamed!=@1已經被馴服! +@1 is owner!=@1 是主人 +#Active Mob Limit Reached!= +Already protected!=已經被保護! +#Change= +#Command:= +Enter name:=輸入名稱: +#Enter texture:= +Lasso (right-click animal to put in inventory)=套索(右鍵單擊動物以放入物品欄) +Leather=皮革 +Meat=肉 +Missed!=沒抓住! +Mob Fence=Mob 柵欄 +#Mob Fence Top= +Mob Protection Rune=Mob 保護符文 +#Mob Reset Stick= +#Mob Spawner= +Mob Spawner settings failed!=Mob 孵化器設置失敗! +Mob has been protected!=Mob 已經被保護了! +Name Tag=名稱標籤 +Net (right-click animal to put in inventory)=網(右鍵單擊動物以放入物品欄) +Not tamed!=沒有馴服! +Raw Meat=生肉 +Rename=重新命名 +Saddle=鞍 +Spawner Active (@1)=孵化器正在運轉(@1) +Spawner Not Active (enter settings)=孵化器未使用(輸入設置) +Steel Shears (right-click to shear)=鋼剪(右鍵單擊以剪切) +#Syntax: “name min_light[0-14] max_light[0-14] max_mobs_in_area[0 to disable] player_distance[1-20] y_offset[-10 to 10]”= +#lifetimer expired, removed @1= From 499d43a9aad463039e642a6bc81a9380b079111b Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 4 Sep 2020 13:59:14 +0100 Subject: [PATCH 107/135] added 'on_map_load' setting to mobs:spawn function to spawn mob in newly loaded areas of map only --- api.lua | 348 +++++++++++++++++++++++++++++------------------------- api.txt | 3 + readme.MD | 3 +- 3 files changed, 192 insertions(+), 162 deletions(-) diff --git a/api.lua b/api.lua index 90ca429..e6031bf 100644 --- a/api.lua +++ b/api.lua @@ -9,7 +9,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200825", + version = "20200904", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -3699,8 +3699,8 @@ function mobs:spawn_abm_check(pos, node, name) end -function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, - interval, chance, aoc, min_height, max_height, day_toggle, on_spawn) +function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval, + chance, aoc, min_height, max_height, day_toggle, on_spawn, map_load) -- Do mobs spawn at all? if not mobs_spawn or not mobs.spawning_mobs[name] then @@ -3730,186 +3730,211 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, mobs.spawning_mobs[name].aoc = aoc - minetest.register_abm({ + local spawn_action = function(pos, node, active_object_count, + active_object_count_wider) - label = name .. " spawning", - nodenames = nodes, - neighbors = neighbors, - interval = interval, - chance = max(1, (chance * mob_chance_multiplier)), - catch_up = false, + -- use instead of abm's chance setting when using lbm + if map_load and random(max(1, (chance * mob_chance_multiplier))) > 1 then + return + end - action = function(pos, node, active_object_count, - active_object_count_wider) + -- use instead of abm's neighbor setting when using lbm + if map_load and not minetest.find_node_near(pos, 1, neighbors) then +--print("--- lbm neighbors not found") + return + end - -- is mob actually registered? - if not mobs.spawning_mobs[name] - or not minetest.registered_entities[name] then + -- is mob actually registered? + if not mobs.spawning_mobs[name] + or not minetest.registered_entities[name] then --print("--- mob doesn't exist", name) - return - end + return + end - -- are we over active mob limit - if active_limit > 0 and active_mobs >= active_limit then + -- are we over active mob limit + if active_limit > 0 and active_mobs >= active_limit then --print("--- active mob limit reached", active_mobs, active_limit) - return - end + return + end - -- additional custom checks for spawning mob - if mobs:spawn_abm_check(pos, node, name) == true then - return - end + -- additional custom checks for spawning mob + if mobs:spawn_abm_check(pos, node, name) == true then + return + end - -- do not spawn if too many entities in area - if active_object_count_wider >= max_per_block then + -- do not spawn if too many entities in area + if active_object_count_wider + and active_object_count_wider >= max_per_block then --print("--- too many entities in area", active_object_count_wider) - return - end + return + end - -- get total number of this mob in area - local num_mob, is_pla = count_mobs(pos, name) + -- get total number of this mob in area + local num_mob, is_pla = count_mobs(pos, name) - if not is_pla then + if not is_pla then --print("--- no players within active area, will not spawn " .. name) - return - end + return + end - if num_mob >= aoc then + if num_mob >= aoc then --print("--- too many " .. name .. " in area", num_mob .. "/" .. aoc) - return - end + return + end -- if toggle set to nil then ignore day/night check - if day_toggle ~= nil then + if day_toggle ~= nil then - local tod = (minetest.get_timeofday() or 0) * 24000 + local tod = (minetest.get_timeofday() or 0) * 24000 - if tod > 4500 and tod < 19500 then - -- daylight, but mob wants night - if day_toggle == false then + if tod > 4500 and tod < 19500 then + -- daylight, but mob wants night + if day_toggle == false then --print("--- mob needs night", name) - return - end - else - -- night time but mob wants day - if day_toggle == true then + return + end + else + -- night time but mob wants day + if day_toggle == true then --print("--- mob needs day", name) - return - end - end - end - - -- spawn above node - pos.y = pos.y + 1 - - -- are we spawning within height limits? - if pos.y > max_height - or pos.y < min_height then ---print("--- height limits not met", name, pos.y) - return - end - - -- are light levels ok? - local light = minetest.get_node_light(pos) - if not light - or light > max_light - or light < min_light then ---print("--- light limits not met", name, light) - return - end - - -- mobs cannot spawn in protected areas when enabled - if not spawn_protected - and minetest.is_protected(pos, "") then ---print("--- inside protected area", name) - return - end - - -- only spawn a set distance away from player - local objs = minetest.get_objects_inside_radius( - pos, mob_nospawn_range) - - for n = 1, #objs do - - if objs[n]:is_player() then ---print("--- player too close", name) return end end - - -- do we have enough space to spawn mob? (thanks wuzzy) - local ent = minetest.registered_entities[name] - local width_x = max(1, - ceil(ent.collisionbox[4] - ent.collisionbox[1])) - local min_x, max_x - - if width_x % 2 == 0 then - max_x = floor(width_x / 2) - min_x = -(max_x - 1) - else - max_x = floor(width_x / 2) - min_x = -max_x - end - - local width_z = max(1, - ceil(ent.collisionbox[6] - ent.collisionbox[3])) - local min_z, max_z - - if width_z % 2 == 0 then - max_z = floor(width_z / 2) - min_z = -(max_z - 1) - else - max_z = floor(width_z / 2) - min_z = -max_z - end - - local max_y = max(0, - ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1) - - for y = 0, max_y do - for x = min_x, max_x do - for z = min_z, max_z do - - local pos2 = { - x = pos.x + x, - y = pos.y + y, - z = pos.z + z} - - if minetest.registered_nodes[ - node_ok(pos2).name].walkable == true then ---print("--- not enough space to spawn", name) - return - end - end - end - end - - -- spawn mob 1/2 node above ground - pos.y = pos.y + 0.5 - - -- tweak X/Z spawn pos - if width_x % 2 == 0 then - pos.x = pos.x + 0.5 - end - - if width_z % 2 == 0 then - pos.z = pos.z + 0.5 - end - - 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 end - }) + + -- spawn above node + pos.y = pos.y + 1 + + -- are we spawning within height limits? + if pos.y > max_height + or pos.y < min_height then +--print("--- height limits not met", name, pos.y) + return + end + + -- are light levels ok? + local light = minetest.get_node_light(pos) + if not light + or light > max_light + or light < min_light then +--print("--- light limits not met", name, light) + return + end + + -- mobs cannot spawn in protected areas when enabled + if not spawn_protected + and minetest.is_protected(pos, "") then +--print("--- inside protected area", name) + return + end + + -- only spawn a set distance away from player + local objs = minetest.get_objects_inside_radius(pos, mob_nospawn_range) + + for n = 1, #objs do + + if objs[n]:is_player() then +--print("--- player too close", name) + return + end + end + + -- do we have enough space to spawn mob? (thanks wuzzy) + local ent = minetest.registered_entities[name] + local width_x = max(1, ceil(ent.collisionbox[4] - ent.collisionbox[1])) + local min_x, max_x + + if width_x % 2 == 0 then + max_x = floor(width_x / 2) + min_x = -(max_x - 1) + else + max_x = floor(width_x / 2) + min_x = -max_x + end + + local width_z = max(1, ceil(ent.collisionbox[6] - ent.collisionbox[3])) + local min_z, max_z + + if width_z % 2 == 0 then + max_z = floor(width_z / 2) + min_z = -(max_z - 1) + else + max_z = floor(width_z / 2) + min_z = -max_z + end + + local max_y = max(0, ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1) + + for y = 0, max_y do + for x = min_x, max_x do + for z = min_z, max_z do + + local pos2 = { + x = pos.x + x, + y = pos.y + y, + z = pos.z + z} + + if minetest.registered_nodes[node_ok(pos2).name].walkable == true then +--print("--- not enough space to spawn", name) + return + end + end + end + end + + -- spawn mob 1/2 node above ground + pos.y = pos.y + 0.5 + + -- tweak X/Z spawn pos + if width_x % 2 == 0 then + pos.x = pos.x + 0.5 + end + + if width_z % 2 == 0 then + pos.z = pos.z + 0.5 + end + + 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 + on_spawn(mob:get_luaentity(), pos) + end + end + + + -- are we registering an abm or lbm? + if map_load == true then + + minetest.register_lbm({ + name = name .. "_spawning", + label = name .. " spawning", + nodenames = nodes, + run_at_every_load = false, + + action = function(pos, node) + spawn_action(pos, node) + end + }) + + else + + minetest.register_abm({ + label = name .. " spawning", + nodenames = nodes, + neighbors = neighbors, + interval = interval, + chance = max(1, (chance * mob_chance_multiplier)), + catch_up = false, + + action = function(pos, node, active_object_count, active_object_count_wider) + spawn_action(pos, node, active_object_count, active_object_count_wider) + end + }) + end end @@ -3922,7 +3947,7 @@ function mobs:register_spawn(name, nodes, max_light, min_light, chance, end --- MarkBu's spawn function +-- MarkBu's spawn function (USE this one please) function mobs:spawn(def) mobs:spawn_specific( @@ -3937,7 +3962,8 @@ function mobs:spawn(def) def.min_height or -31000, def.max_height or 31000, def.day_toggle, - def.on_spawn) + def.on_spawn, + def.on_map_load) end diff --git a/api.txt b/api.txt index 73718e0..14ee000 100644 --- a/api.txt +++ b/api.txt @@ -383,6 +383,9 @@ default setting and can be omitted: anytime 'on_spawn' is a custom function which runs after mob has spawned and gives self and pos values. + 'on_map_load' when true mobs will have a chance of spawning only + when new areas of map are loaded, interval will not be + used. The older spawn functions are still active and working but have no defaults like the mobs:spawn, so it is recommended to use the above instead. diff --git a/readme.MD b/readme.MD index d6982e8..1960dc0 100644 --- a/readme.MD +++ b/readme.MD @@ -23,7 +23,8 @@ Lucky Blocks: 9 Changelog: -- 1.52 - Added 'mob_active_limit' in settings to set number of mobs in game +- 1.53 - Added 'on_map_load' settings to mobs:spawn so that mobs will only spawn when new areas of map are loaded. +- 1.52 - Added 'mob_active_limit' in settings to set number of mobs in game, (default is 0 for unlimited), removed {immortal} from mob armor, fluid viscocity slows mobs - 1.51 - Added some node checks for dangerous nodes, jumping and falling tweaks, spawn area check (thx for idea wuzzy), re-enabled mob suffocation, add 'mob_nospawn_range' setting - 1.50 - Added new line_of_sight function that uses raycasting if mt5.0 is found, (thanks Astrobe), dont spawn mobs if world anchor nearby (technic or simple_anchor mods), chinese local added From c6dffd1390b3bbfc3e807e7ce84c22426d6782e8 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 4 Sep 2020 14:00:53 +0100 Subject: [PATCH 108/135] hide spawning mobs message in terminal --- api.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index e6031bf..89704e0 100644 --- a/api.lua +++ b/api.lua @@ -3896,9 +3896,9 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter local mob = minetest.add_entity(pos, name) - print("[mobs] Spawned " .. name .. " at " - .. minetest.pos_to_string(pos) .. " on " - .. node.name .. " near " .. neighbors[1]) +-- print("[mobs] Spawned " .. name .. " at " +-- .. minetest.pos_to_string(pos) .. " on " +-- .. node.name .. " near " .. neighbors[1]) if on_spawn then on_spawn(mob:get_luaentity(), pos) From 89523195d5d868131ca01e9d956f06d0588b70b3 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 5 Sep 2020 08:59:09 +0100 Subject: [PATCH 109/135] fix typo in api.txt --- api.lua | 1 - api.txt | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index 89704e0..2b29479 100644 --- a/api.lua +++ b/api.lua @@ -3725,7 +3725,6 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter minetest.log("action", string.format( "[mobs] Chance setting for %s changed to %s (total: %s)", name, chance, aoc)) - end mobs.spawning_mobs[name].aoc = aoc diff --git a/api.txt b/api.txt index 14ee000..fdbd1dd 100644 --- a/api.txt +++ b/api.txt @@ -701,8 +701,8 @@ mobs_monster:sand_monster 100 ...you can also change how many of a certain mob appear in an active mapblock by adding a comma and then a new value e.g. -mobs_animal:cow 8000,4 <-- 4 cows per mapblock at 8000 spawn chance -mobs_monster:dirt_monster ,20 <-- 20 dirt monsters per mapblock +mobs_animal:cow = 8000,4 <-- 4 cows per mapblock at 8000 spawn chance +mobs_monster:dirt_monster = ,20 <-- 20 dirt monsters per mapblock Rideable Horse Example Mob From bd8be0905a3eeafc3bcce3f6a58a713080c12e2e Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 5 Sep 2020 18:15:21 +0100 Subject: [PATCH 110/135] fix nametag return --- api.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 2b29479..7b4883f 100644 --- a/api.lua +++ b/api.lua @@ -9,7 +9,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200904", + version = "20200905", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -4588,6 +4588,8 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) .. ";" .. tag .. "]" .. "button_exit[2.5,3.5;3,1;mob_rename;" .. minetest.formspec_escape(S("Rename")) .. "]") + + return true end return false From 3009da0efed67358776a72543344cc6211a6c4d9 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 15 Sep 2020 13:20:29 +0100 Subject: [PATCH 111/135] add meat block and recipe --- crafts.lua | 21 +++++++++++++++++++++ textures/mobs_meat_bottom.png | Bin 0 -> 253 bytes textures/mobs_meat_side.png | Bin 0 -> 271 bytes textures/mobs_meat_top.png | Bin 0 -> 287 bytes 4 files changed, 21 insertions(+) create mode 100644 textures/mobs_meat_bottom.png create mode 100644 textures/mobs_meat_side.png create mode 100644 textures/mobs_meat_top.png diff --git a/crafts.lua b/crafts.lua index 73d14da..5f9b23d 100644 --- a/crafts.lua +++ b/crafts.lua @@ -331,3 +331,24 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) tex_obj = nil end end) + + +-- Meat Block (thanks to painterlypack.net for allowing me to use these textures) +minetest.register_node("mobs:meatblock", { + description = S("Meat Block"), + tiles = {"mobs_meat_top.png", "mobs_meat_bottom.png", "mobs_meat_side.png"}, + paramtype2 = "facedir", + groups = {choppy = 1, oddly_breakable_by_hand = 1, flammable = 2}, + sounds = default.node_sound_leaves_defaults(), + on_place = minetest.rotate_node +}) + +minetest.register_craft({ + output = "mobs:meatblock", + type = "shapeless", + recipe = { + "group:food_meat", "group:food_meat", "group:food_meat", + "group:food_meat", "group:food_meat", "group:food_meat", + "group:food_meat", "group:food_meat", "group:food_meat" + } +}) diff --git a/textures/mobs_meat_bottom.png b/textures/mobs_meat_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..351c16fae4a55f57bcfb262fc13a9285afe45e8b GIT binary patch literal 253 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv5AX?bjTdGpmSE_WW9YMISme($ zHJV{{sQSVbt~CkjYw~4wmpLDq+4=wff2NM2PN0^Fo-U3d9M^eIZ{%u95NUhpKRv8o zG0Jwn%Z-XYg|qd}#R6|##TPt!@Q%0R$o}WI?p#nU3EjERMqegtiFToYr0jf=CBNVI z>IxruKQsC=&)@Uek*(Zj^Bvblaiy>SdxYoX-7;QX=P#EQE#Z7}v3>ITtdi%>!E&Yw za_eTz7W}f-P-n)^B|6jY8g@jRb1qupFsbhcvs{LJ;x>t%2B6~@JYD@<);T3K0RT*H BW3B)I literal 0 HcmV?d00001 diff --git a/textures/mobs_meat_side.png b/textures/mobs_meat_side.png new file mode 100644 index 0000000000000000000000000000000000000000..10dac0810cac63f7da0a2f36beec815dfa884b90 GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Wq?nJYrHT+u>?b}97CTy!-N2- z=|NKSL!=f3t51z)SRJaqFokPPg8I5l^-Y=1Yw~4wmpLDq*?DwH=l}oz%jVjO0u7ny z>Eak7aaq>iQ|Pb(hjWm)kI=1W|NGZ|ILg**rnDl+R70ej<3olfyN8+bqp!9{TQUyx zH~#h!JK&S)otI{6$P(FfJil_e=)L2Qe_CFeY_UOCVEU>vE51oT{y6PaOPRY2v)$5Vd`E_Uqa=cK`Y7v{)t! TbR2`HtDnm{r-UW|jk0Gh literal 0 HcmV?d00001 diff --git a/textures/mobs_meat_top.png b/textures/mobs_meat_top.png new file mode 100644 index 0000000000000000000000000000000000000000..0058e6a4de95d2c2e79fdc81dfb085a097cdf5df GIT binary patch literal 287 zcmV+)0pR|LP) zq5uE?J((Kt0001^w=*)+QN8~~bFiSnql2Anu zO$pg5Nl0OT`k_2y)@JN)54CHAI+|~9-$Nr7*|JtcKF107QY_aGA;SE+$6Y3GGLAsG zy$lCqHqc2Qo8L}R=N(67xyJZx!s Date: Wed, 16 Sep 2020 12:04:03 +0100 Subject: [PATCH 112/135] Fix on_punch damage returns (thanks OgelGames) --- api.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/api.lua b/api.lua index 7b4883f..8d2c123 100644 --- a/api.lua +++ b/api.lua @@ -9,7 +9,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200905", + version = "20200916", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -2685,20 +2685,20 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage) -- mob health check if self.health <= 0 then - return + return true end -- custom punch function if self.do_punch and self:do_punch(hitter, tflp, tool_capabilities, dir) == false then - return + return true end -- error checking when mod profiling is enabled if not tool_capabilities then minetest.log("warning", "[mobs] Mod profiling enabled, damage not enabled") - return + return true end -- is mob protected? @@ -2709,7 +2709,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage) minetest.chat_send_player(hitter:get_player_name(), S("Mob has been protected!")) - return + return true end local weapon = hitter:get_wielded_item() @@ -2770,7 +2770,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage) if use_cmi and cmi.notify_punch( self.object, hitter, tflp, tool_capabilities, dir, damage) then - return + return true end -- add weapon wear @@ -2875,7 +2875,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir, damage) local v = self.object:get_velocity() -- sanity check - if not v then return end + if not v then return true end local kb = damage or 1 local up = 2 From b6fb1948e868f90c95cf77a52974a7d4819ada41 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 19 Sep 2020 15:17:38 +0100 Subject: [PATCH 113/135] meatblocks can be eaten for 20 hp --- crafts.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crafts.lua b/crafts.lua index 5f9b23d..ae8251e 100644 --- a/crafts.lua +++ b/crafts.lua @@ -340,7 +340,8 @@ minetest.register_node("mobs:meatblock", { paramtype2 = "facedir", groups = {choppy = 1, oddly_breakable_by_hand = 1, flammable = 2}, sounds = default.node_sound_leaves_defaults(), - on_place = minetest.rotate_node + on_place = minetest.rotate_node, + on_use = minetest.item_eat(20), }) minetest.register_craft({ From f14050ad3cf52f1168d7011d4ad5fa28bae41b77 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Wed, 23 Sep 2020 20:35:04 +0100 Subject: [PATCH 114/135] nil check for do_stay_near --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 8d2c123..9e3ae25 100644 --- a/api.lua +++ b/api.lua @@ -9,7 +9,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200916", + version = "20200923", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -637,7 +637,7 @@ function mob_class:do_stay_near() local searchnodes = self.stay_near[1] local chance = self.stay_near[2] or 10 - if random(chance) > 1 then + if not pos or random(chance) > 1 then return false end From f7a1b27ea405b454085f0e7c6e690dbc55b7b303 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 1 Oct 2020 12:12:12 +0100 Subject: [PATCH 115/135] add nil check to do_runaway_from --- api.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 9e3ae25..21a2b7e 100644 --- a/api.lua +++ b/api.lua @@ -9,7 +9,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20200923", + version = "20201001", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -1904,7 +1904,7 @@ function mob_class:do_runaway_from() return end - local s = self.object:get_pos() + local s = self.object:get_pos() ; if not s then return end local p, sp, dist, pname local player, obj, min_player, name local min_dist = self.view_range + 1 From 7f4d473feef7ab6df20d00e5ceaba8335efd463a Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 3 Oct 2020 09:34:38 +0100 Subject: [PATCH 116/135] Localize a few functions (thanks MoNTE48) --- api.lua | 88 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/api.lua b/api.lua index 21a2b7e..9e955d9 100644 --- a/api.lua +++ b/api.lua @@ -1,4 +1,3 @@ - -- Load support for intllib. local MP = minetest.get_modpath(minetest.get_current_modname()) local S = minetest.get_translator and minetest.get_translator("mobs_redo") or @@ -9,19 +8,11 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20201001", + version = "20201003", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } --- creative check -local creative_cache = minetest.settings:get_bool("creative_mode") -function mobs.is_creative(name) - return creative_cache or minetest.check_player_privs(name, - {creative = true}) -end - - -- localize math functions local pi = math.pi local square = math.sqrt @@ -43,24 +34,36 @@ local atan = function(x) return atann(x) end end +local table_copy = table.copy +local table_remove = table.remove +local vadd = vector.add +local vdirection = vector.direction +local vmultiply = vector.multiply +local vsubtract = vector.subtract +local settings = minetest.settings +-- creative check +local creative_cache = minetest.settings:get_bool("creative_mode") +function mobs.is_creative(name) + return creative_cache or minetest.check_player_privs(name, + {creative = true}) +end -- Load settings -local damage_enabled = minetest.settings:get_bool("enable_damage") -local mobs_spawn = minetest.settings:get_bool("mobs_spawn") ~= false -local peaceful_only = minetest.settings:get_bool("only_peaceful_mobs") -local disable_blood = minetest.settings:get_bool("mobs_disable_blood") -local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false -local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false -local spawn_protected = minetest.settings:get_bool("mobs_spawn_protected") ~= false -local remove_far = minetest.settings:get_bool("remove_far_mobs") ~= false -local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0 -local show_health = minetest.settings:get_bool("mob_show_health") ~= false -local max_per_block = tonumber(minetest.settings:get("max_objects_per_block") or 99) -local mob_nospawn_range = tonumber(minetest.settings:get("mob_nospawn_range") or 12) -local active_limit = tonumber(minetest.settings:get("mob_active_limit") or 0) -local mob_chance_multiplier = - tonumber(minetest.settings:get("mob_chance_multiplier") or 1) +local damage_enabled = settings:get_bool("enable_damage") +local mobs_spawn = settings:get_bool("mobs_spawn") ~= false +local peaceful_only = settings:get_bool("only_peaceful_mobs") +local disable_blood = settings:get_bool("mobs_disable_blood") +local mobs_drop_items = settings:get_bool("mobs_drop_items") ~= false +local mobs_griefing = settings:get_bool("mobs_griefing") ~= false +local spawn_protected = settings:get_bool("mobs_spawn_protected") ~= false +local remove_far = settings:get_bool("remove_far_mobs") ~= false +local difficulty = tonumber(settings:get("mob_difficulty")) or 1.0 +local show_health = settings:get_bool("mob_show_health") ~= false +local max_per_block = tonumber(settings:get("max_objects_per_block") or 99) +local mob_nospawn_range = tonumber(settings:get("mob_nospawn_range") or 12) +local active_limit = tonumber(settings:get("mob_active_limit") or 0) +local mob_chance_multiplier = tonumber(settings:get("mob_chance_multiplier") or 1) local active_mobs = 0 @@ -73,7 +76,7 @@ if peaceful_only then end -- calculate aoc range for mob count -local aoc_range = tonumber(minetest.settings:get("active_block_range")) * 16 +local aoc_range = tonumber(settings:get("active_block_range")) * 16 -- pathfinding settings local enable_pathfinding = true @@ -470,7 +473,7 @@ local new_line_of_sight = function(self, pos1, pos2, stepsize) stepsize = stepsize or 1 - local stepv = vector.multiply(vector.direction(pos1, pos2), stepsize) + local stepv = vmultiply(vdirection(pos1, pos2), stepsize) local s, pos = minetest.line_of_sight(pos1, pos2, stepsize) @@ -493,7 +496,7 @@ local new_line_of_sight = function(self, pos1, pos2, stepsize) while minetest.registered_nodes[nn] and (minetest.registered_nodes[nn].walkable == false) do - npos1 = vector.add(npos1, stepv) + npos1 = vadd(npos1, stepv) if get_distance(npos1, pos2) < stepsize then return true end @@ -573,10 +576,10 @@ function mob_class:attempt_flight_correction(override) end local escape_target = flyable_nodes[random(#flyable_nodes)] - local escape_direction = vector.direction(pos, escape_target) + local escape_direction = vdirection(pos, escape_target) self.object:set_velocity( - vector.multiply(escape_direction, 1)) --self.run_velocity)) + vmultiply(escape_direction, 1)) --self.run_velocity)) return true end @@ -1028,7 +1031,7 @@ function mob_class:do_env_damage() -- halt mob if standing inside ignore node if self.standing_in == "ignore" then - self.object:set_velocity({x = 0, y = 0, z = 0}) + self.object:set_velocity({x = 0, y = 0, z = 0}) return true end @@ -1620,7 +1623,7 @@ function mob_class:smart_mobs(s, p, dist, dtime) end, self) end - if abs(vector.subtract(s,target_pos).y) > self.stepheight then + if abs(vsubtract(s,target_pos).y) > self.stepheight then if height_switcher then use_pathfind = true @@ -2470,7 +2473,7 @@ function mob_class:do_states(dtime) if abs(p1.x - s.x) + abs(p1.z - s.z) < 0.6 then -- reached waypoint, remove it from queue - table.remove(self.path.way, 1) + table_remove(self.path.way, 1) end -- set new temporary target @@ -3142,7 +3145,7 @@ function mob_class:mob_activate(staticdata, def, dtime) -- Armor groups (immortal = 1 for custom damage handling) local armor if type(self.armor) == "table" then - armor = table.copy(self.armor) + armor = table_copy(self.armor) -- armor.immortal = 1 else -- armor = {immortal = 1, fleshy = self.armor} @@ -3184,7 +3187,7 @@ function mob_class:mob_activate(staticdata, def, dtime) -- 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 + self.on_spawn_run = true -- if true, set flag to run once only end end @@ -3709,7 +3712,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter end -- chance/spawn number override in minetest.conf for registered mob - local numbers = minetest.settings:get(name) + local numbers = settings:get(name) if numbers then numbers = numbers:split(",") @@ -4580,14 +4583,15 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) mob_sta[name] = item local tag = self.nametag or "" + local esc = minetest.formspec_escape minetest.show_formspec(name, "mobs_nametag", - "size[8,4]" - .. "field[0.5,1;7.5,0;name;" - .. minetest.formspec_escape(S("Enter name:")) - .. ";" .. tag .. "]" - .. "button_exit[2.5,3.5;3,1;mob_rename;" - .. minetest.formspec_escape(S("Rename")) .. "]") + "size[8,4]" .. + "field[0.5,1;7.5,0;name;" .. + esc(S("Enter name:")) .. + ";" .. tag .. "]" .. + "button_exit[2.5,3.5;3,1;mob_rename;" .. + esc(S("Rename")) .. "]") return true end From d12576f0c84bd993572ea11fe4b76de38e47e502 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 29 Oct 2020 10:46:59 +0000 Subject: [PATCH 117/135] mobs:can_spawn function added to check free space to spawn mob --- api.lua | 129 +++++++++++++++++++++++++++++++------------------------- api.txt | 7 +++ 2 files changed, 79 insertions(+), 57 deletions(-) diff --git a/api.lua b/api.lua index 9e955d9..e4c64ef 100644 --- a/api.lua +++ b/api.lua @@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20201003", + version = "20201029", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -3594,6 +3594,68 @@ local count_mobs = function(pos, type) end +-- do we have enough space to spawn mob? (thanks wuzzy) +local can_spawn = function(pos, name) + + local ent = minetest.registered_entities[name] + local width_x = max(1, ceil(ent.collisionbox[4] - ent.collisionbox[1])) + local min_x, max_x + + if width_x % 2 == 0 then + max_x = floor(width_x / 2) + min_x = -(max_x - 1) + else + max_x = floor(width_x / 2) + min_x = -max_x + end + + local width_z = max(1, ceil(ent.collisionbox[6] - ent.collisionbox[3])) + local min_z, max_z + + if width_z % 2 == 0 then + max_z = floor(width_z / 2) + min_z = -(max_z - 1) + else + max_z = floor(width_z / 2) + min_z = -max_z + end + + local max_y = max(0, ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1) + local pos2 + + for y = 0, max_y do + for x = min_x, max_x do + for z = min_z, max_z do + + pos2 = {x = pos.x + x, y = pos.y + y, z = pos.z + z} + + if minetest.registered_nodes[node_ok(pos2).name].walkable == true then + return nil + end + end + end + end + + -- spawn mob 1/2 node above ground + pos.y = pos.y + 0.5 + + -- tweak X/Z spawn pos + if width_x % 2 == 0 then + pos.x = pos.x + 0.5 + end + + if width_z % 2 == 0 then + pos.z = pos.z + 0.5 + end + + return pos +end + +function mobs:can_spawn(pos, name) + return can_spawn(pos, name) +end + + -- global functions function mobs:add_mob(pos, def) @@ -3841,69 +3903,22 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter end end - -- do we have enough space to spawn mob? (thanks wuzzy) - local ent = minetest.registered_entities[name] - local width_x = max(1, ceil(ent.collisionbox[4] - ent.collisionbox[1])) - local min_x, max_x + -- returns position if we have enough space to spawn mob + pos = can_spawn(pos, name) - if width_x % 2 == 0 then - max_x = floor(width_x / 2) - min_x = -(max_x - 1) - else - max_x = floor(width_x / 2) - min_x = -max_x - end + if pos then - local width_z = max(1, ceil(ent.collisionbox[6] - ent.collisionbox[3])) - local min_z, max_z - - if width_z % 2 == 0 then - max_z = floor(width_z / 2) - min_z = -(max_z - 1) - else - max_z = floor(width_z / 2) - min_z = -max_z - end - - local max_y = max(0, ceil(ent.collisionbox[5] - ent.collisionbox[2]) - 1) - - for y = 0, max_y do - for x = min_x, max_x do - for z = min_z, max_z do - - local pos2 = { - x = pos.x + x, - y = pos.y + y, - z = pos.z + z} - - if minetest.registered_nodes[node_ok(pos2).name].walkable == true then ---print("--- not enough space to spawn", name) - return - end - end - end - end - - -- spawn mob 1/2 node above ground - pos.y = pos.y + 0.5 - - -- tweak X/Z spawn pos - if width_x % 2 == 0 then - pos.x = pos.x + 0.5 - end - - if width_z % 2 == 0 then - pos.z = pos.z + 0.5 - end - - local mob = minetest.add_entity(pos, name) + 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 - on_spawn(mob:get_luaentity(), pos) + if on_spawn then + on_spawn(mob:get_luaentity(), pos) + end + else +--print("--- not enough space to spawn", name) end end diff --git a/api.txt b/api.txt index fdbd1dd..e81b750 100644 --- a/api.txt +++ b/api.txt @@ -660,6 +660,13 @@ Use this instead: mob_class:line_of_sight(pos1, pos2, stepsize) +mobs:can_spawn(pos, name) + +This function checks the surrounding area at [pos] to see if there is enough empty +space to spawn mob [name], if so then a new position is returned for use, +otherwise nil is returned. + + External Settings for "minetest.conf" ------------------------------------ From bdea826b7c30900745539d52423d11a24f21683d Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 15 Nov 2020 18:23:52 +0000 Subject: [PATCH 118/135] add additional death checks --- api.lua | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/api.lua b/api.lua index e4c64ef..72947ab 100644 --- a/api.lua +++ b/api.lua @@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20201029", + version = "20201115", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -877,7 +877,7 @@ function mob_class:check_for_death(cmi_cause) local pos = self.object:get_pos() -- execute custom death function - if self.on_die then + if pos and self.on_die then self:on_die(pos) @@ -897,7 +897,7 @@ function mob_class:check_for_death(cmi_cause) local frames = self.animation.die_end - self.animation.die_start local speed = self.animation.die_speed or 15 - local length = max(frames / speed, 0) + local length = max((frames / speed), 0) self.attack = nil self.v_start = false @@ -910,23 +910,28 @@ function mob_class:check_for_death(cmi_cause) minetest.after(length, function(self) - if use_cmi and self.object:get_luaentity() then - cmi.notify_die(self.object, cmi_cause) + if self.object:get_luaentity() then + + if use_cmi then + cmi.notify_die(self.object, cmi_cause) + end + + remove_mob(self, true) end - - remove_mob(self, true) - end, self) - else + + return true + + elseif pos then -- otherwise remove mod and show particle effect if use_cmi then cmi.notify_die(self.object, cmi_cause) end remove_mob(self, true) - end - effect(pos, 20, "tnt_smoke.png") + effect(pos, 20, "tnt_smoke.png") + end return true end From a4d29183836a60a62b1b2bb342c662815b442fe9 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 30 Nov 2020 14:43:49 +0000 Subject: [PATCH 119/135] add mob_area_spawn setting --- api.lua | 26 +++++++++++++++++++++++--- api.txt | 3 +++ settingtypes.txt | 3 +++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index 72947ab..6484457 100644 --- a/api.lua +++ b/api.lua @@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20201115", + version = "20201130", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -58,6 +58,7 @@ local mobs_drop_items = settings:get_bool("mobs_drop_items") ~= false local mobs_griefing = settings:get_bool("mobs_griefing") ~= false local spawn_protected = settings:get_bool("mobs_spawn_protected") ~= false local remove_far = settings:get_bool("remove_far_mobs") ~= false +local mob_area_spawn = settings:get_bool("mob_area_spawn") local difficulty = tonumber(settings:get("mob_difficulty")) or 1.0 local show_health = settings:get_bool("mob_show_health") ~= false local max_per_block = tonumber(settings:get("max_objects_per_block") or 99) @@ -3908,8 +3909,27 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter end end - -- returns position if we have enough space to spawn mob - pos = can_spawn(pos, name) + -- should we check mob area for obstructions ? + if mob_area_spawn ~= true then + + -- do we have enough height clearance to spawn mob? + local ent = minetest.registered_entities[name] + local height = max(1, math.ceil( + (ent.collisionbox[5] or 0.25) - (ent.collisionbox[2] or -0.25) - 1)) + + for n = 0, height do + + local pos2 = {x = pos.x, y = pos.y + n, z = pos.z} + + if minetest.registered_nodes[node_ok(pos2).name].walkable == true then +--print ("--- inside block", name, node_ok(pos2).name) + return + end + end + else + -- returns position if we have enough space to spawn mob + pos = can_spawn(pos, name) + end if pos then diff --git a/api.txt b/api.txt index e81b750..d78fd98 100644 --- a/api.txt +++ b/api.txt @@ -697,6 +697,9 @@ External Settings for "minetest.conf" function. 'mob_nospawn_range' Minimum range a mob can spawn near player (def: 12) 'mob_active_limit' Number of active mobs in game, 0 for unlimited + 'mob_area_spawn' When true will check surrounding area the size of the + mob for obstructions before spawning, otherwise it + defaults to checking the height of the mob only. 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 diff --git a/settingtypes.txt b/settingtypes.txt index 75bf111..49d2443 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -33,3 +33,6 @@ mob_nospawn_range (Mob no-spawn range) float 12.0 # Sets maximum number of active mobs in game (0 for unlimited) mob_active_limit (Mob Active Limit) float 0 + +# Enables area check when spawning mobs +mob_area_spawn (Mob Area Spawn) bool false From 4a6518eebae4bc1c5b76b5d00ee51bbe5b3299f3 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Wed, 2 Dec 2020 15:14:11 +0000 Subject: [PATCH 120/135] add additional nil backups for set_velocity --- api.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api.lua b/api.lua index 6484457..1aaaa7d 100644 --- a/api.lua +++ b/api.lua @@ -277,10 +277,10 @@ function mob_class:set_velocity(v) c_x, c_y = unpack(self:collision()) end - local yaw = (self.object:get_yaw() or 0) + self.rotate + local yaw = (self.object:get_yaw() or 0) + (self.rotate or 0) -- nil check for velocity - v = v or 0 + v = v or 0.01 -- check if standing in liquid with max viscosity of 7 local visc = min(minetest.registered_nodes[self.standing_in].liquid_viscosity, 7) @@ -292,8 +292,8 @@ function mob_class:set_velocity(v) v = v / (visc + 1) end - -- set velocity with hard limit of 10 - local vel = self.object:get_velocity() + -- set velocity + local vel = self.object:get_velocity() or 0 local new_vel = { x = (sin(yaw) * -v) + c_x, From 1dfd7e1af1a1e896ccd8982845aa86dbe14e3b63 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 5 Dec 2020 12:06:34 +0000 Subject: [PATCH 121/135] simplified breeding function, added some settings --- api.lua | 61 ++++++++++++++++++++++++++++++++++--------------------- api.txt | 8 ++++++++ readme.MD | 1 + 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/api.lua b/api.lua index 1aaaa7d..3dbc0ea 100644 --- a/api.lua +++ b/api.lua @@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20201130", + version = "20201205", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -829,6 +829,11 @@ end -- check if mob is dead or only hurt function mob_class:check_for_death(cmi_cause) + -- We dead already + if self.state == "die" then + return true + end + -- has health actually changed? if self.health == self.old_health and self.health > 0 then return false @@ -906,6 +911,9 @@ function mob_class:check_for_death(cmi_cause) self.blinktimer = 0 self.passive = true self.state = "die" + self.object:set_properties({ + pointable = false, collide_with_objects = false + }) self:set_velocity(0) self:set_animation("die") @@ -1296,16 +1304,20 @@ function mob_class:follow_holding(clicker) return false end +-- Thanks Wuzzy for the following editable settings +local HORNY_TIME = 30 +local HORNY_AGAIN_TIME = 300 +local CHILD_GROW_TIME = 60 * 20 -- 20 minutes -- find two animals of same type and breed if nearby and horny function mob_class:breed() - -- child takes 240 seconds before growing into adult + -- child takes a long time before growing into adult if self.child == true then self.hornytimer = self.hornytimer + 1 - if self.hornytimer > 240 then + if self.hornytimer > CHILD_GROW_TIME then self.child = false self.hornytimer = 0 @@ -1334,14 +1346,14 @@ function mob_class:breed() return end - -- horny animal can mate for 40 seconds, - -- afterwards horny animal cannot mate again for 200 seconds + -- horny animal can mate for HORNY_TIME seconds, + -- afterwards horny animal cannot mate again for HORNY_AGAIN_TIME seconds if self.horny == true - and self.hornytimer < 240 then + and self.hornytimer < HORNY_TIME + HORNY_AGAIN_TIME then self.hornytimer = self.hornytimer + 1 - if self.hornytimer >= 240 then + if self.hornytimer >= HORNY_TIME + HORNY_AGAIN_TIME then self.hornytimer = 0 self.horny = false end @@ -1349,7 +1361,7 @@ function mob_class:breed() -- find another same animal who is also horny and mate if nearby if self.horny == true - and self.hornytimer <= 40 then + and self.hornytimer <= HORNY_TIME then local pos = self.object:get_pos() @@ -1357,7 +1369,6 @@ function mob_class:breed() "heart.png", 3, 4, 1, 0.1) local objs = minetest.get_objects_inside_radius(pos, 3) - local num = 0 local ent for n = 1, #objs do @@ -1386,18 +1397,20 @@ function mob_class:breed() end end - if ent + -- found another similar horny animal that isn't self? + if ent and ent.object ~= self.object and canmate == true and ent.horny == true - and ent.hornytimer <= 40 then - num = num + 1 - end + and ent.hornytimer <= HORNY_TIME then - -- found your mate? then have a baby - if num > 1 then + local pos2 = ent.object:get_pos() - self.hornytimer = 41 - ent.hornytimer = 41 + -- Have mobs face one another + yaw_to_pos(self, pos2) + yaw_to_pos(ent, self.object:get_pos()) + + self.hornytimer = HORNY_TIME + 1 + ent.hornytimer = HORNY_TIME + 1 -- have we reached active mob limit if active_limit > 0 and active_mobs >= active_limit then @@ -1465,8 +1478,6 @@ function mob_class:breed() ent2.owner = self.owner end, self, ent) - num = 0 - break end end @@ -2011,10 +2022,11 @@ function mob_class:follow_flop() self.following = nil end else - -- stop following player if not holding specific item + -- stop following player if not holding specific item or mob is horny if self.following and self.following:is_player() - and self:follow_holding(self.following) == false then + and (self:follow_holding(self.following) == false + or self.horny) then self.following = nil end @@ -4572,8 +4584,11 @@ function mobs:feed_tame(self, clicker, feed_count, breed, tame) -- make children grow quicker if self.child == true then - self.hornytimer = self.hornytimer + 20 - +-- self.hornytimer = self.hornytimer + 20 + -- deduct 10% of the time to adulthood + self.hornytimer = self.hornytimer + ( + (CHILD_GROW_TIME - self.hornytimer) * 0.1) +print ("====", self.hornytimer) return true end diff --git a/api.txt b/api.txt index d78fd98..6c09a1f 100644 --- a/api.txt +++ b/api.txt @@ -329,6 +329,14 @@ for each mob. '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 + 'self.state' Current mob state. + "stand": no movement (except turning around) + "walk": walk or move around aimlessly + "attack": chase and attack enemy + "runaway": flee from target + "flop": bounce around aimlessly + (for swimming mobs that have stranded) + "die": during death Adding Mobs in World diff --git a/readme.MD b/readme.MD index 1960dc0..1382810 100644 --- a/readme.MD +++ b/readme.MD @@ -23,6 +23,7 @@ Lucky Blocks: 9 Changelog: +- 1.54 - Simplified animal breeding function, added editable settings (thanks Wuzzy), Child mobs now take 20 mins to grow up, reverted to simple mob spawning with setting to use area checks. - 1.53 - Added 'on_map_load' settings to mobs:spawn so that mobs will only spawn when new areas of map are loaded. - 1.52 - Added 'mob_active_limit' in settings to set number of mobs in game, (default is 0 for unlimited), removed {immortal} from mob armor, fluid viscocity slows mobs From 9489ff6965f0d1432e4fe61259cf6ed4319d2583 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 6 Dec 2020 08:55:17 +0000 Subject: [PATCH 122/135] add option to spin mob when playing death animation --- api.lua | 4 +++- api.txt | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 3dbc0ea..530a852 100644 --- a/api.lua +++ b/api.lua @@ -904,6 +904,7 @@ function mob_class:check_for_death(cmi_cause) local frames = self.animation.die_end - self.animation.die_start local speed = self.animation.die_speed or 15 local length = max((frames / speed), 0) + local rot = self.animation.die_rotate and 5 self.attack = nil self.v_start = false @@ -912,7 +913,8 @@ function mob_class:check_for_death(cmi_cause) self.passive = true self.state = "die" self.object:set_properties({ - pointable = false, collide_with_objects = false + pointable = false, collide_with_objects = false, + automatic_rotate = rot, }) self:set_velocity(0) self:set_animation("die") diff --git a/api.txt b/api.txt index 6c09a1f..e8014ed 100644 --- a/api.txt +++ b/api.txt @@ -226,6 +226,7 @@ functions needed for the mob to work properly which contains the following: 'die_end' 'die_speed' 'die_loop' when set to false stops the animation looping. + 'die_rotate' if true mob spins during death animation. Using '_loop = false' setting will stop any of the above animations from looping. From bf32a09e5eda81d9e54a9d2368dffb4d71f53674 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sun, 6 Dec 2020 09:53:02 +0000 Subject: [PATCH 123/135] on_flop and air_damage added --- api.lua | 27 ++++++++++++++++++++++++++- api.txt | 4 ++++ readme.MD | 2 +- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index 530a852..d219850 100644 --- a/api.lua +++ b/api.lua @@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20201205", + version = "20201206", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -112,6 +112,7 @@ local mob_class = { light_damage_max = 15, water_damage = 0, lava_damage = 0, + air_damage = 0, suffocation = 2, fall_damage = 1, fall_speed = -10, -- must be lower than -2 (default: -10) @@ -1100,6 +1101,19 @@ function mob_class:do_env_damage() end end + -- air damage + if self.air_damage ~= 0 and self.standing_in == "air" then + + self.health = self.health - self.air_damage + + effect(pos, 3, "bubble.png", 1, 1, 1, 0.2) + + if self:check_for_death({type = "environment", + pos = pos, node = self.standing_in}) then + return true + end + end + -- is mob light sensative, or scared of the dark :P if self.light_damage ~= 0 then @@ -2084,6 +2098,15 @@ function mob_class:follow_flop() if not self:attempt_flight_correction() then self.state = "flop" + + -- do we have a custom on_flop function? + if self.on_flop then + + if self:on_flop(self) then + return + end + end + self.object:set_velocity({x = 0, y = -5, z = 0}) self:set_animation("stand") @@ -3482,6 +3505,7 @@ minetest.register_entity(name, setmetatable({ owner = def.owner, order = def.order, on_die = def.on_die, + on_flop = def.on_flop, do_custom = def.do_custom, jump_height = def.jump_height, drawtype = def.drawtype, -- DEPRECATED, use rotate instead @@ -3505,6 +3529,7 @@ minetest.register_entity(name, setmetatable({ light_damage_max = def.light_damage_max, water_damage = def.water_damage, lava_damage = def.lava_damage, + air_damage = def.air_damage, suffocation = def.suffocation, fall_damage = def.fall_damage, fall_speed = def.fall_speed, diff --git a/api.txt b/api.txt index e8014ed..7001891 100644 --- a/api.txt +++ b/api.txt @@ -64,6 +64,7 @@ functions needed for the mob to work properly which contains the following: 'fall_damage' when true causes falling to inflict damage. 'water_damage' holds the damage per second infliced to mobs when standing in water. + 'air_damage' holds damage per second inflicted to mob when standing in air. 'lava_damage' holds the damage per second inflicted to mobs when standing in lava or fire or an ignition source. 'light_damage' holds the damage per second inflicted to mobs when light @@ -299,6 +300,9 @@ enhance mob functionality and have them do many interesting things: is returned normal attack function continued. 'on_die' a function that is called when mob is killed (self, pos), also has access to self.cause_of_death table. + 'on_flop' function called when flying or swimmimng mob is no longer in + air/water, (self) paramater and return true to skip the built + in api flop feature. '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 diff --git a/readme.MD b/readme.MD index 1382810..67cc12b 100644 --- a/readme.MD +++ b/readme.MD @@ -23,7 +23,7 @@ Lucky Blocks: 9 Changelog: -- 1.54 - Simplified animal breeding function, added editable settings (thanks Wuzzy), Child mobs now take 20 mins to grow up, reverted to simple mob spawning with setting to use area checks. +- 1.54 - Simplified animal breeding function, added editable settings (thanks Wuzzy), Child mobs now take 20 mins to grow up, reverted to simple mob spawning with setting to use area checks, on_flop added, air_damage added. - 1.53 - Added 'on_map_load' settings to mobs:spawn so that mobs will only spawn when new areas of map are loaded. - 1.52 - Added 'mob_active_limit' in settings to set number of mobs in game, (default is 0 for unlimited), removed {immortal} from mob armor, fluid viscocity slows mobs From 00ac9efc8c029afdc38557886ada4f1b7a434fae Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 2 Jan 2021 20:31:01 +0000 Subject: [PATCH 124/135] fix global catch sound (thanks SwissalpS) --- api.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api.lua b/api.lua index d219850..a00b16e 100644 --- a/api.lua +++ b/api.lua @@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20201206", + version = "20210102", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -4503,10 +4503,10 @@ function mobs:capture_mob(self, clicker, chance_hand, chance_net, minetest.add_item(clicker:get_pos(), new_stack) end - remove_mob(self, true) - self:mob_sound("default_place_node_hard") + remove_mob(self, true) + return new_stack -- when chance above fails or set to 0, miss! From 76ee3d0b51d2bde979d09ca6aebb6f342949a096 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Mon, 4 Jan 2021 12:28:17 +0000 Subject: [PATCH 125/135] add peaceful player privs and settings (thanks sfence) --- api.lua | 23 +++++++++++++++++++++-- init.lua | 6 ++++++ readme.MD | 1 + settingtypes.txt | 3 +++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index a00b16e..d91b508 100644 --- a/api.lua +++ b/api.lua @@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20210102", + version = "20210104", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -65,6 +65,7 @@ local max_per_block = tonumber(settings:get("max_objects_per_block") or 99) local mob_nospawn_range = tonumber(settings:get("mob_nospawn_range") or 12) local active_limit = tonumber(settings:get("mob_active_limit") or 0) local mob_chance_multiplier = tonumber(settings:get("mob_chance_multiplier") or 1) +local peaceful_player_enabled = settings:get_bool("enable_peaceful_player") local active_mobs = 0 @@ -1848,6 +1849,23 @@ function mob_class:smart_mobs(s, p, dist, dtime) end +-- peaceful player privilege support +local function is_peaceful_player(player) + + if peaceful_player_enabled then + + local player_name = player:get_player_name() + + if player_name + and minetest.check_player_privs(player_name, "peaceful_player") then + return true + end + end + + return false +end + + -- general attack function for all mobs function mob_class:general_attack() @@ -1920,7 +1938,8 @@ function mob_class:general_attack() -- choose closest player to attack that isnt self if dist ~= 0 and dist < min_dist - and self:line_of_sight(sp, p, 2) == true then + and self:line_of_sight(sp, p, 2) == true + and not is_peaceful_player(player) then min_dist = dist min_player = player end diff --git a/init.lua b/init.lua index f63fb16..518d50d 100644 --- a/init.lua +++ b/init.lua @@ -1,6 +1,12 @@ local path = minetest.get_modpath("mobs") +-- Peaceful player privilege +minetest.register_privilege("peaceful_player", { + description = "Prevents Mobs Redo mobs from attacking player", + give_to_singleplayer = false +}) + -- Mob API dofile(path .. "/api.lua") diff --git a/readme.MD b/readme.MD index 67cc12b..d646984 100644 --- a/readme.MD +++ b/readme.MD @@ -23,6 +23,7 @@ Lucky Blocks: 9 Changelog: +- 1.55 - Add 'peaceful_player' privelage and setting so mobs don't attack specific players (thanks sfence) - 1.54 - Simplified animal breeding function, added editable settings (thanks Wuzzy), Child mobs now take 20 mins to grow up, reverted to simple mob spawning with setting to use area checks, on_flop added, air_damage added. - 1.53 - Added 'on_map_load' settings to mobs:spawn so that mobs will only spawn when new areas of map are loaded. - 1.52 - Added 'mob_active_limit' in settings to set number of mobs in game, diff --git a/settingtypes.txt b/settingtypes.txt index 49d2443..9a9100c 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -36,3 +36,6 @@ mob_active_limit (Mob Active Limit) float 0 # Enables area check when spawning mobs mob_area_spawn (Mob Area Spawn) bool false + +# Enable peaceful player attack prevention +enable_peaceful_player (Mobs do not attack peaceful player without reason) bool false From 55eb893a9ab028c18ce961eefc66be22c26e8859 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 7 Jan 2021 10:42:10 +0000 Subject: [PATCH 126/135] simple swimming fix to stop floaters --- api.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index d91b508..f4b44de 100644 --- a/api.lua +++ b/api.lua @@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20210104", + version = "20210107", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -572,7 +572,8 @@ function mob_class:attempt_flight_correction(override) local flyable_nodes = minetest.find_nodes_in_area( {x = pos.x - 1, y = pos.y - 1, z = pos.z - 1}, - {x = pos.x + 1, y = pos.y + 1, z = pos.z + 1}, searchnodes) + {x = pos.x + 1, y = pos.y + 0, z = pos.z + 1}, searchnodes) + -- pos.y + 0 hopefully fixes floating swimmers if #flyable_nodes < 1 then return false From 622abd1486a5de5050805b46dc7c643ab34443ec Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Fri, 8 Jan 2021 10:16:33 +0000 Subject: [PATCH 127/135] hopefully fixed death animation crash when player away --- api.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/api.lua b/api.lua index f4b44de..d132793 100644 --- a/api.lua +++ b/api.lua @@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20210107", + version = "20210108", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -910,6 +910,7 @@ function mob_class:check_for_death(cmi_cause) local rot = self.animation.die_rotate and 5 self.attack = nil + self.following = nil self.v_start = false self.timer = 0 self.blinktimer = 0 @@ -917,7 +918,7 @@ function mob_class:check_for_death(cmi_cause) self.state = "die" self.object:set_properties({ pointable = false, collide_with_objects = false, - automatic_rotate = rot, + automatic_rotate = rot, static_save = false }) self:set_velocity(0) self:set_animation("die") @@ -3074,6 +3075,7 @@ function mob_class:mob_staticdata() if t ~= "function" and t ~= "nil" and t ~= "userdata" + and _ ~= "object" and _ ~= "_cmi_components" then tmp[_] = self[_] end @@ -3326,6 +3328,8 @@ function mob_class:on_step(dtime, moveresult) }} }]] +if self.state == "die" then return end ---------------- + if use_cmi then cmi.notify_step(self.object, dtime) end From 754321541aa7c7ba49219e1b525a00ea69002e28 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 14 Jan 2021 10:28:18 +0000 Subject: [PATCH 128/135] fix mob spawn height (thanks OgelGames) --- api.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/api.lua b/api.lua index d132793..2659681 100644 --- a/api.lua +++ b/api.lua @@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20210108", + version = "20210114", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -3705,9 +3705,6 @@ local can_spawn = function(pos, name) end end - -- spawn mob 1/2 node above ground - pos.y = pos.y + 0.5 - -- tweak X/Z spawn pos if width_x % 2 == 0 then pos.x = pos.x + 0.5 @@ -3972,15 +3969,15 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter end end + local ent = minetest.registered_entities[name] + -- should we check mob area for obstructions ? if mob_area_spawn ~= true then -- do we have enough height clearance to spawn mob? - local ent = minetest.registered_entities[name] - local height = max(1, math.ceil( - (ent.collisionbox[5] or 0.25) - (ent.collisionbox[2] or -0.25) - 1)) + local height = max(0, ent.collisionbox[5] - ent.collisionbox[2]) - for n = 0, height do + for n = 0, floor(height) do local pos2 = {x = pos.x, y = pos.y + n, z = pos.z} @@ -3996,6 +3993,9 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter if pos then + -- adjust for mob collision box + pos.y = pos.y + (ent.collisionbox[2] * -1) - 0.4 + local mob = minetest.add_entity(pos, name) -- print("[mobs] Spawned " .. name .. " at " From ad2ccc589588cf99848ee75f48cf7173a5493a15 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 14 Jan 2021 19:24:40 +0000 Subject: [PATCH 129/135] add a few compatibility groups to mob items --- crafts.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crafts.lua b/crafts.lua index ae8251e..c137191 100644 --- a/crafts.lua +++ b/crafts.lua @@ -5,7 +5,7 @@ local S = mobs.intllib minetest.register_craftitem("mobs:nametag", { description = S("Name Tag"), inventory_image = "mobs_nametag.png", - groups = {flammable = 2} + groups = {flammable = 2, nametag = 1} }) if minetest.get_modpath("dye") and minetest.get_modpath("farming") then @@ -20,7 +20,7 @@ end minetest.register_craftitem("mobs:leather", { description = S("Leather"), inventory_image = "mobs_leather.png", - groups = {flammable = 2} + groups = {flammable = 2, leather = 1} }) -- raw meat @@ -119,7 +119,7 @@ minetest.register_craft({ minetest.register_craftitem("mobs:saddle", { description = S("Saddle"), inventory_image = "mobs_saddle.png", - groups = {flammable = 2} + groups = {flammable = 2, saddle = 1} }) minetest.register_craft({ From 2d1befacb33cfdefbe42bc9ae3df0aa48c1fb68c Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Tue, 19 Jan 2021 20:37:42 +0000 Subject: [PATCH 130/135] nil check --- api.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.lua b/api.lua index 2659681..1d9e9af 100644 --- a/api.lua +++ b/api.lua @@ -563,7 +563,7 @@ function mob_class:attempt_flight_correction(override) -- We are not flying in what we are supposed to. -- See if we can find intended flight medium and return to it - local pos = self.object:get_pos() + local pos = self.object:get_pos() ; if not pos then return true end local searchnodes = self.fly_in if type(searchnodes) == "string" then From 425cadeb519ad1d7ae415fcf1ca71aeca74526aa Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 21 Jan 2021 09:44:31 +0000 Subject: [PATCH 131/135] improve mount.lua (changes from lib_mount :) --- mount.lua | 195 ++++++++++++++++++++++++------------------------------ 1 file changed, 88 insertions(+), 107 deletions(-) diff --git a/mount.lua b/mount.lua index d60413a..550bf3e 100644 --- a/mount.lua +++ b/mount.lua @@ -1,9 +1,7 @@ - -- lib_mount by Blert2112 (edited by TenPlus1) -local enable_crash = false -local crash_threshold = 6.5 -- ignored if enable_crash=false - +local abs, cos, floor, sin, sqrt, pi = + math.abs, math.cos, math.floor, math.sin, math.sqrt, math.pi ------------------------------------------------------------------------------ -- @@ -11,7 +9,6 @@ local crash_threshold = 6.5 -- ignored if enable_crash=false -- local node_ok = function(pos, fallback) - fallback = fallback or mobs.fallback_node local node = minetest.get_node_or_nil(pos) @@ -25,7 +22,6 @@ end local function node_is(pos) - local node = node_ok(pos) if node.name == "air" then @@ -49,33 +45,30 @@ end local function get_sign(i) - i = i or 0 if i == 0 then return 0 else - return i / math.abs(i) + return i / abs(i) end end local function get_velocity(v, yaw, y) - - local x = -math.sin(yaw) * v - local z = math.cos(yaw) * v + local x = -sin(yaw) * v + local z = cos(yaw) * v return {x = x, y = y, z = z} end local function get_v(v) - return math.sqrt(v.x * v.x + v.z * v.z) + return sqrt(v.x * v.x + v.z * v.z) end local function force_detach(player) - local attached_to = player:get_attach() if not attached_to then @@ -86,21 +79,19 @@ local function force_detach(player) if entity and entity.driver and entity.driver == player then - entity.driver = nil end player:set_detach() - default.player_attached[player:get_player_name()] = false + player_api.player_attached[player:get_player_name()] = false player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0}) - default.player_set_animation(player, "stand" , 30) - player:set_properties({visual_size = {x = 1, y = 1} }) + player_api.set_animation(player, "stand", 30) + player:set_properties({visual_size = {x = 1, y = 1}}) end ------------------------------------------------------------------------------- - minetest.register_on_leaveplayer(function(player) force_detach(player) end) @@ -119,10 +110,37 @@ end) ------------------------------------------------------------------------------- +-- Just for correct detaching +local function find_free_pos(pos) + local check = { + {x = 1, y = 0, z = 0}, + {x = 1, y = 1, z = 0}, + {x = -1, y = 0, z = 0}, + {x = -1, y = 1, z = 0}, + {x = 0, y = 0, z = 1}, + {x = 0, y = 1, z = 1}, + {x = 0, y = 0, z = -1}, + {x = 0, y = 1, z = -1} + } + + for _, c in pairs(check) do + local npos = {x = pos.x + c.x, y = pos.y + c.y, z = pos.z + c.z} + local node = minetest.get_node_or_nil(npos) + if node and node.name then + local def = minetest.registered_nodes[node.name] + if def and not def.walkable and + def.liquidtype == "none" then + return npos + end + end + end + + return pos +end + +------------------------------------------------------------------------------- + function mobs.attach(entity, player) - - local attach_at, eye_offset = {}, {} - entity.player_rotation = entity.player_rotation or {x = 0, y = 0, z = 0} entity.driver_attach_at = entity.driver_attach_at or {x = 0, y = 0, z = 0} entity.driver_eye_offset = entity.driver_eye_offset or {x = 0, y = 0, z = 0} @@ -131,17 +149,17 @@ function mobs.attach(entity, player) local rot_view = 0 if entity.player_rotation.y == 90 then - rot_view = math.pi / 2 + rot_view = pi / 2 end - attach_at = entity.driver_attach_at - eye_offset = entity.driver_eye_offset + local attach_at = entity.driver_attach_at + local eye_offset = entity.driver_eye_offset entity.driver = player force_detach(player) player:set_attach(entity.object, "", attach_at, entity.player_rotation) - default.player_attached[player:get_player_name()] = true + player_api.player_attached[player:get_player_name()] = true player:set_eye_offset(eye_offset, {x = 0, y = 0, z = 0}) player:set_properties({ @@ -151,46 +169,36 @@ function mobs.attach(entity, player) } }) - minetest.after(0.2, function(name) - local player = minetest.get_player_by_name(name) - if player then - default.player_set_animation(player, "sit" , 30) + minetest.after(0.2, function() + if player and player:is_player() then + player_api.set_animation(player, "sit", 30) end - end, player:get_player_name()) + end) player:set_look_horizontal(entity.object:get_yaw() - rot_view) end -function mobs.detach(player, offset) - +function mobs.detach(player) force_detach(player) - default.player_set_animation(player, "stand" , 30) - - local pos = player:get_pos() - - pos = { - x = pos.x + offset.x, - y = pos.y + 0.2 + offset.y, - z = pos.z + offset.z - } - - minetest.after(0.1, function(name, pos) - local player = minetest.get_player_by_name(name) - if player then + minetest.after(0.1, function() + if player and player:is_player() then + local pos = find_free_pos(player:get_pos()) + pos.y = pos.y + 0.5 player:set_pos(pos) end - end, player:get_player_name(), pos) + end) end function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) + local yaw = entity.object:get_yaw() or 0 - local rot_steer, rot_view = math.pi/2, 0 + local rot_view = 0 if entity.player_rotation.y == 90 then - rot_steer, rot_view = 0, math.pi/2 + rot_view = pi / 2 end local acce_y = 0 @@ -200,19 +208,14 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) -- process controls if entity.driver then - ---print ("---velo", get_v(velo)) - local ctrl = entity.driver:get_player_control() -- move forwards if ctrl.up then - entity.v = entity.v + entity.accel / 10 -- move backwards elseif ctrl.down then - if entity.max_speed_reverse == 0 and entity.v == 0 then return end @@ -220,12 +223,24 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) entity.v = entity.v - entity.accel / 10 end - -- fix mob rotation - local horz = entity.driver:get_look_horizontal() or 0 + -- mob rotation + local horz + if entity.alt_turn == true then + horz = yaw + + if ctrl.left then + horz = horz + 0.05 + + elseif ctrl.right then + horz = horz - 0.05 + end + else + horz = entity.driver:get_look_horizontal() or 0 + end + entity.object:set_yaw(horz - entity.rotate) if can_fly then - -- fly up if ctrl.jump then velo.y = velo.y + 1 @@ -245,9 +260,7 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) velo.y = velo.y + 0.1 if velo.y > 0 then velo.y = 0 end end - else - -- jump if ctrl.jump then @@ -261,7 +274,6 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) -- if not moving then set animation and return if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then - if stand_anim then mobs:set_animation(entity, stand_anim) end @@ -280,7 +292,6 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) entity.v = entity.v - 0.02 * s if s ~= get_sign(entity.v) then - entity.object:set_velocity({x = 0, y = 0, z = 0}) entity.v = 0 return @@ -293,14 +304,15 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) max_spd = entity.max_speed_forward end - if math.abs(entity.v) > max_spd then + if abs(entity.v) > max_spd then entity.v = entity.v - get_sign(entity.v) end -- Set position, velocity and acceleration local p = entity.object:get_pos() - local new_velo - local new_acce = {x = 0, y = -9.8, z = 0} + if not p then return end + + local new_acce = {x = 0, y = -9.81, z = 0} p.y = p.y - 0.5 @@ -308,23 +320,18 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) local v = entity.v if ni == "air" then - if can_fly == true then new_acce.y = 0 end - elseif ni == "liquid" or ni == "lava" then - if ni == "lava" and entity.lava_damage ~= 0 then - entity.lava_counter = (entity.lava_counter or 0) + dtime if entity.lava_counter > 1 then - minetest.sound_play("default_punch", { object = entity.object, max_hear_distance = 5 - }, true) + }) entity.object:punch(entity.object, 1.0, { full_punch_interval = 1.0, @@ -335,14 +342,12 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) end end - if entity.terrain_type == 2 - or entity.terrain_type == 3 then - + local terrain_type = entity.terrain_type + if terrain_type == 2 or terrain_type == 3 then new_acce.y = 0 p.y = p.y + 1 if node_is(p) == "liquid" then - if velo.y >= 5 then velo.y = 5 elseif velo.y < 0 then @@ -351,9 +356,11 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) new_acce.y = 5 end else - if math.abs(velo.y) < 1 then + if abs(velo.y) < 1 then local pos = entity.object:get_pos() - pos.y = math.floor(pos.y) + 0.5 + if not pos then return end + + pos.y = floor(pos.y) + 0.5 entity.object:set_pos(pos) velo.y = 0 end @@ -363,46 +370,22 @@ function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) end end - new_velo = get_velocity(v, entity.object:get_yaw() - rot_view, velo.y) + local new_velo = get_velocity(v, yaw - rot_view, velo.y) new_acce.y = new_acce.y + acce_y entity.object:set_velocity(new_velo) entity.object:set_acceleration(new_acce) - -- CRASH! - if enable_crash then - - local intensity = entity.v2 - v - - if intensity >= crash_threshold then - ---print("----------- crash", intensity) - - entity.object:punch(entity.object, 1.0, { - full_punch_interval = 1.0, - damage_groups = {fleshy = intensity} - }, nil) - - end - end - entity.v2 = v end -- directional flying routine by D00Med (edited by TenPlus1) - -function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim) - +function mobs.fly(entity, _, speed, shoots, arrow, moving_anim, stand_anim) local ctrl = entity.driver:get_player_control() local velo = entity.object:get_velocity() local dir = entity.driver:get_look_dir() 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 - - if entity.player_rotation.y == 90 then - rot_steer, rot_view = 0, math.pi / 2 - end if ctrl.up then entity.object:set_velocity({ @@ -414,7 +397,7 @@ function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim) elseif ctrl.down then entity.object:set_velocity({ x = -dir.x * speed, - y = dir.y * speed + 2, + y = dir.y * speed + 2, z = -dir.z * speed }) @@ -422,11 +405,10 @@ function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim) entity.object:set_velocity({x = 0, y = -2, z = 0}) end - entity.object:set_yaw(yaw + math.pi + math.pi / 2 - entity.rotate) + entity.object:set_yaw(yaw + pi + pi / 2 - entity.rotate) -- firing arrows if ctrl.LMB and ctrl.sneak and shoots then - local pos = entity.object:get_pos() local obj = minetest.add_entity({ x = pos.x + 0 + dir.x * 2.5, @@ -438,8 +420,8 @@ 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_horizontal() - obj:set_yaw(yaw + math.pi / 2) + yaw = entity.driver:get_look_horizontal() + obj:set_yaw(yaw + pi / 2) obj:set_velocity(vec) else obj:remove() @@ -448,7 +430,6 @@ function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim) -- change animation if stopped if velo.x == 0 and velo.y == 0 and velo.z == 0 then - mobs:set_animation(entity, stand_anim) else -- moving animation From 10053e05a90d0155cbc83080ef5517cf12efcd70 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 21 Jan 2021 10:18:00 +0000 Subject: [PATCH 132/135] update mod.conf info --- mod.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mod.conf b/mod.conf index f3a3ad7..31a27ed 100644 --- a/mod.conf +++ b/mod.conf @@ -1 +1,4 @@ name = mobs +depends = default +optional_depends = tnt, dye, farming, invisibility, intllib, lucky_block, cmi, toolranks +description = Adds a mob api for mods to add animals or monsters etc. From 10e4dd92a045203420c87c3de1db25bfe4633d34 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 21 Jan 2021 11:10:17 +0000 Subject: [PATCH 133/135] spawner spawns mob in it's own medium (air, water etc.) --- spawner.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/spawner.lua b/spawner.lua index da86301..bfcdcba 100644 --- a/spawner.lua +++ b/spawner.lua @@ -23,9 +23,9 @@ minetest.register_node("mobs:spawner", { -- text entry formspec meta:set_string("formspec", - "size[9,3.5]" + "size[10,3.5]" .. "label[0.15,0.5;" .. minetest.formspec_escape(head) .. "]" - .. "field[1,2.5;7.5,0.8;text;" .. S("Command:") + .. "field[1,2.5;8.5,0.8;text;" .. S("Command:") .. ";${command}]") meta:set_string("infotext", S("Spawner Not Active (enter settings)")) @@ -162,10 +162,17 @@ minetest.register_abm({ end end + -- set medium mob usually spawns in (defaults to air) + local reg = minetest.registered_entities[mob].fly_in + + if not reg or type(reg) == "string" then + reg = {(reg or "air")} + end + -- find air blocks within 5 nodes of spawner local air = minetest.find_nodes_in_area( {x = pos.x - 5, y = pos.y + yof, z = pos.z - 5}, - {x = pos.x + 5, y = pos.y + yof, z = pos.z + 5}, {"air"}) + {x = pos.x + 5, y = pos.y + yof, z = pos.z + 5}, reg) -- spawn in random air block if air and #air > 0 then From c158e84e28c4cb2ee5a4168f1a0418ee50a40d8d Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Wed, 3 Feb 2021 21:29:26 +0000 Subject: [PATCH 134/135] stop grown child mobs sinking into blocks below --- api.lua | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/api.lua b/api.lua index 1d9e9af..568d21f 100644 --- a/api.lua +++ b/api.lua @@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20210114", + version = "20210203", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -1354,11 +1354,15 @@ function mob_class:breed() self.on_grown(self) else -- jump when fully grown so as not to fall into ground - self.object:set_velocity({ - x = 0, - y = self.jump_height, - z = 0 - }) +-- self.object:set_velocity({ +-- x = 0, +-- y = self.jump_height, +-- z = 0 +-- }) + local pos = self.object:get_pos() ; if not pos then return end + local ent = self.object:get_luaentity() + pos.y = pos.y + (ent.collisionbox[2] * -1) - 0.4 + self.object:set_pos(pos) end end @@ -1458,6 +1462,8 @@ function mob_class:breed() effect(pos, 15, "tnt_smoke.png", 1, 2, 2, 15, 5) end + pos.y = pos.y + 0.5 -- spawn child a little higher + local mob = minetest.add_entity(pos, self.name) local ent2 = mob:get_luaentity() local textures = self.base_texture From 11e1d52cfb9d6bc4e55bf7b00054559829d74039 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Sat, 6 Feb 2021 17:10:50 +0000 Subject: [PATCH 135/135] replace minetest 5.0 check --- api.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/api.lua b/api.lua index 568d21f..c1ea969 100644 --- a/api.lua +++ b/api.lua @@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi") mobs = { mod = "redo", - version = "20210203", + version = "20210206", intllib = S, invis = minetest.global_exists("invisibility") and invisibility or {} } @@ -539,12 +539,10 @@ local ray_line_of_sight = function(self, pos1, pos2) return true end --- detect if using minetest 5.0 by searching for permafrost node -local is_50 = minetest.registered_nodes["default:permafrost"] function mob_class:line_of_sight(pos1, pos2, stepsize) - if is_50 then -- only use if minetest 5.0 is detected + if minetest.raycast then -- only use if minetest 5.0 is detected return ray_line_of_sight(self, pos1, pos2) end