From 487e037cc9d575ecda39077ec0f141eaa8568111 Mon Sep 17 00:00:00 2001 From: TenPlus1 Date: Thu, 20 Dec 2018 11:14:10 +0000 Subject: [PATCH] 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