Codebase list minetest-mod-mobs-redo / fresh-snapshots/main
New upstream snapshot. Debian Janitor 1 year, 3 months ago
18 changed file(s) with 340 addition(s) and 268 deletion(s). Raw diff Collapse all Expand all
0 unused_args = false
1
2 read_globals = {
3 "minetest",
4 "lucky_block",
5 "intllib",
6 "vector",
7 "table",
8 "invisibility",
9 "cmi",
10 "toolranks",
11 "pathfinder",
12 "tnt",
13 "ItemStack"
14 }
15
16 globals = {
17 "mobs",
18 "player_api",
19 "default"
20 }
21
22 ignore = {
23 "431", -- Shadowing an upvalue
24 "432", -- Shadowing an upvalue argument
25 }
+219
-232
api.lua less more
0 local MP = minetest.get_modpath(minetest.get_current_modname())
1
20 -- Check for translation method
31 local S
42 if minetest.get_translator ~= nil then
75 if minetest.get_modpath("intllib") then
86 dofile(minetest.get_modpath("intllib") .. "/init.lua")
97 if intllib.make_gettext_pair then
10 gettext, ngettext = intllib.make_gettext_pair() -- new gettext method
8 S = intllib.make_gettext_pair() -- new gettext method
119 else
12 gettext = intllib.Getter() -- old text file method
13 end
14 S = gettext
10 S = intllib.Getter() -- old text file method
11 end
1512 else -- boilerplate function
1613 S = function(str, ...)
1714 local args = {...}
2724
2825 mobs = {
2926 mod = "redo",
30 version = "20220314",
27 version = "20221213",
3128 intllib = S,
3229 invis = minetest.global_exists("invisibility") and invisibility or {}
3330 }
4441 local floor = math.floor
4542 local ceil = math.ceil
4643 local rad = math.rad
44 local deg = math.deg
4745 local atann = math.atan
4846 local atan = function(x)
4947 if not x or x ~= x then
5452 end
5553 local table_copy = table.copy
5654 local table_remove = table.remove
57 local vadd = vector.add
5855 local vdirection = vector.direction
5956 local vmultiply = vector.multiply
6057 local vsubtract = vector.subtract
7976 local remove_far = settings:get_bool("remove_far_mobs") ~= false
8077 local mob_area_spawn = settings:get_bool("mob_area_spawn")
8178 local difficulty = tonumber(settings:get("mob_difficulty")) or 1.0
82 local show_health = settings:get_bool("mob_show_health") ~= false
8379 local max_per_block = tonumber(settings:get("max_objects_per_block") or 99)
8480 local mob_nospawn_range = tonumber(settings:get("mob_nospawn_range") or 12)
8581 local active_limit = tonumber(settings:get("mob_active_limit") or 0)
105101 local stuck_path_timeout = 5 -- how long will mob follow path before giving up
106102
107103 -- default nodes
108 local node_fire = "fire:basic_flame"
109 local node_permanent_flame = "fire:permanent_flame"
104 --local node_fire = "fire:basic_flame"
105 --local node_permanent_flame = "fire:permanent_flame"
110106 local node_ice = "default:ice"
111107 local node_snowblock = "default:snowblock"
112108 local node_snow = "default:snow"
109
113110 mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "default:dirt"
114111
115 local mob_class = {
112 mobs.mob_class = {
116113 stepheight = 1.1,
117114 fly_in = "air",
118115 owner = "",
183180 _cmi_is_mob = true
184181 }
185182
183 local mob_class = mobs.mob_class -- Compatibility
186184 local mob_class_meta = {__index = mob_class}
187185
188186
189187 -- play sound
190188 function mob_class:mob_sound(sound)
191189
192 local pitch = 1.0
193
194 -- higher pitch for a child
195 if self.child then pitch = pitch * 1.5 end
196
197 -- a little random pitch to be different
198 pitch = pitch + random(-10, 10) * 0.005
199
200190 if sound then
191
192 -- higher pitch for a child
193 local pitch = self.child and 1.5 or 1.0
194
195 -- a little random pitch to be different
196 pitch = pitch + random(-10, 10) * 0.005
197
201198 minetest.sound_play(sound, {
202199 object = self.object,
203200 gain = 1.0,
218215 self.attack = player
219216 self.state = "attack"
220217
221 if random(0, 100) < 90 then
218 if random(100) < 90 then
222219 self:mob_sound(self.sounds.war_cry)
223220 end
224221 end
420417 self.object:set_animation({
421418 x = self.animation[anim .. "_start"],
422419 y = self.animation[anim .. "_end"]},
423 self.animation[anim .. "_speed"] or
424 self.animation.speed_normal or 15,
420 self.animation[anim .. "_speed"] or self.animation.speed_normal or 15,
425421 0, self.animation[anim .. "_loop"] ~= false)
426422 end
427423
435431
436432 stepsize = stepsize or 1
437433
438 local s, pos = minetest.line_of_sight(pos1, pos2, stepsize)
434 local s = minetest.line_of_sight(pos1, pos2, stepsize)
439435
440436 -- normal walking and flying mobs can see you through air
441437 if s == true then
462458 -- It continues to advance in the line of sight in search of a real
463459 -- obstruction which counts as 'walkable' nodebox.
464460 while minetest.registered_nodes[nn]
465 and (minetest.registered_nodes[nn].walkable == false) do
461 and minetest.registered_nodes[nn].walkable == false do
466462
467463 -- Check if you can still move forward
468464 if td < ad + stepsize then
499495 end
500496
501497
502 -- check line of sight (by BrunoMine, tweaked by Astrobe)
503 local new_line_of_sight = function(self, pos1, pos2, stepsize)
504
505 if not pos1 or not pos2 then return end
506
507 stepsize = stepsize or 1
508
509 local stepv = vmultiply(vdirection(pos1, pos2), stepsize)
510
511 local s, pos = minetest.line_of_sight(pos1, pos2, stepsize)
512
513 -- normal walking and flying mobs can see you through air
514 if s == true then return true end
515
516 -- New pos1 to be analyzed
517 local npos1 = {x = pos1.x, y = pos1.y, z = pos1.z}
518
519 local r, pos = minetest.line_of_sight(npos1, pos2, stepsize)
520
521 -- Checks the return
522 if r == true then return true end
523
524 -- Nodename found
525 local nn = minetest.get_node(pos).name
526
527 -- It continues to advance in the line of sight in search of a real
528 -- obstruction which counts as 'walkable' nodebox.
529 while minetest.registered_nodes[nn]
530 and (minetest.registered_nodes[nn].walkable == false) do
531
532 npos1 = vadd(npos1, stepv)
533
534 if get_distance(npos1, pos2) < stepsize then return true end
535
536 -- scan again
537 r, pos = minetest.line_of_sight(npos1, pos2, stepsize)
538
539 if r == true then return true end
540
541 -- New Nodename found
542 nn = minetest.get_node(pos).name
543 end
544
545 return false
546 end
547
548498 -- check line of sight using raycasting (thanks Astrobe)
549499 local ray_line_of_sight = function(self, pos1, pos2)
550500
610560 local escape_target = flyable_nodes[random(#flyable_nodes)]
611561 local escape_direction = vdirection(pos, escape_target)
612562
613 self.object:set_velocity(
614 vmultiply(escape_direction, 1))
563 self.object:set_velocity(vmultiply(escape_direction, 1))
615564
616565 return true
617566 end
749698 local CHILD_GROW_TIME = 60 * 20 -- 20 minutes
750699
751700
752 -- update nametag colour
701 -- update nametag and infotext
753702 function mob_class:update_tag()
754703
755704 local col = "#00FF00"
783732
784733 end
785734
735 if self.protected then
736 if self.protected == 2 then
737 text = text .. "\nProtection: Level 2"
738 else
739 text = text .. "\nProtection: Level 1"
740 end
741 end
742
786743 self.infotext = "Health: " .. self.health .. " / " .. self.hp_max
787 .. (self.owner == "" and "" or "\n" .. "Owner: " .. self.owner)
744 .. (self.owner == "" and "" or "\nOwner: " .. self.owner)
788745 .. text
789746
790747 -- set changes
805762 local pos = self.object:get_pos()
806763
807764 -- check for drops function
808 self.drops = type(self.drops) == "function"
809 and self.drops(pos) or self.drops
765 self.drops = type(self.drops) == "function" and self.drops(pos) or self.drops
810766
811767 -- check for nil or no drops
812768 if not self.drops or #self.drops == 0 then
818774 and self.cause_of_death.puncher
819775 and self.cause_of_death.puncher:is_player()
820776
777 -- check for tool 'looting_level' under tool_capabilities as default, or use
778 -- meta string 'looting_level' if found (max looting level is 3).
779 local looting = 0
780
781 if death_by_player then
782
783 local wield_stack = self.cause_of_death.puncher:get_wielded_item()
784 local wield_name = wield_stack:get_name()
785 local wield_stack_meta = wield_stack:get_meta()
786 local item_def = minetest.registered_items[wield_name]
787 local item_looting = item_def and item_def.tool_capabilities and
788 item_def.tool_capabilities.looting_level or 0
789
790 looting = tonumber(wield_stack_meta:get_string("looting_level")) or item_looting
791 looting = min(looting, 3)
792 end
793
794 --print("--- looting level", looting)
795
821796 local obj, item, num
822797
823798 for n = 1, #self.drops do
840815
841816 -- only drop rare items (drops.min = 0) if killed by player
842817 if death_by_player or self.drops[n].min ~= 0 then
843 obj = minetest.add_item(pos, ItemStack(item .. " " .. num))
818 obj = minetest.add_item(pos, ItemStack(item .. " " .. (num + looting)))
844819 end
845820
846821 if obj and obj:get_luaentity() then
913888 self.health = self.hp_max
914889 end
915890
916 -- backup nametag so we can show health stats
917 -- if not self.nametag2 then
918 -- self.nametag2 = self.nametag or ""
919 -- end
920
921 -- if show_health
922 -- and (cmi_cause and cmi_cause.type == "punch") then
923
924 -- self.htimer = 2
925 -- self.nametag = "♥ " .. self.health .. " / " .. self.hp_max
926 self:update_tag()
927 -- end
891 self:update_tag()
928892
929893 return false
930894 end
1008972 -- get node but use fallback for nil or unknown
1009973 local node_ok = function(pos, fallback)
1010974
1011 fallback = fallback or mobs.fallback_node
1012
1013975 local node = minetest.get_node_or_nil(pos)
1014976
1015977 if node and minetest.registered_nodes[node.name] then
1016978 return node
1017979 end
1018980
1019 return minetest.registered_nodes[fallback]
981 return minetest.registered_nodes[(fallback or mobs.fallback_node)]
1020982 end
1021983
1022984
10531015 -- is mob facing a cliff
10541016 function mob_class:is_at_cliff()
10551017
1056 if self.fear_height == 0 then -- 0 for no falling protection!
1018 if self.driver or self.fear_height == 0 then -- 0 for no falling protection!
10571019 return false
10581020 end
10591021
10761038 return true
10771039 end
10781040
1079 local bnode = node_ok(blocker)
1041 local bnode = node_ok(blocker, "air")
10801042
10811043 -- will we drop onto dangerous node?
10821044 if is_node_dangerous(self, bnode.name) then
10921054 -- environmental damage (water, lava, fire, light etc.)
10931055 function mob_class:do_env_damage()
10941056
1095 -- feed/tame text timer (so mob 'full' messages dont spam chat)
1096 if self.htimer > 0 then
1097 self.htimer = self.htimer - 1
1098 end
1099
1100 -- reset nametag after showing health stats
1101 -- if self.htimer < 1 and self.nametag2 then
1102
1103 -- self.nametag = self.nametag2
1104 -- self.nametag2 = nil
1105
1106 self:update_tag()
1107 -- end
1057 self:update_tag()
11081058
11091059 local pos = self.object:get_pos() ; if not pos then return end
11101060
12731223 -- set y_pos to base of mob
12741224 pos.y = pos.y + self.collisionbox[2]
12751225
1276 -- what is in front of mob?
1277 local nod = node_ok({
1278 x = pos.x + dir_x, y = pos.y + 0.5, z = pos.z + dir_z
1279 })
1280
1281 -- what is above and in front?
1282 local nodt = node_ok({
1283 x = pos.x + dir_x, y = pos.y + 1.5, z = pos.z + dir_z
1284 })
1285
1226 -- what is in front of mob and above?
1227 local nod = node_ok({x = pos.x + dir_x, y = pos.y + 0.5, z = pos.z + dir_z})
1228 local nodt = node_ok({x = pos.x + dir_x, y = pos.y + 1.5, z = pos.z + dir_z})
12861229 local blocked = minetest.registered_nodes[nodt.name].walkable
12871230
12881231 -- are we facing a fence or wall
12891232 if nod.name:find("fence") or nod.name:find("gate") or nod.name:find("wall") then
12901233 self.facing_fence = true
12911234 end
1235
12921236 --[[
12931237 print("on: " .. self.standing_on
12941238 .. ", front: " .. nod.name
12971241 .. ", fence: " .. (self.facing_fence and "yes" or "no")
12981242 )
12991243 ]]
1244
1245 -- if mob can leap then remove blockages and let them try
1246 if self.can_leap == true then
1247 blocked = false
1248 self.facing_fence = false
1249 end
1250
13001251 -- jump if standing on solid node (not snow) and not blocked
13011252 if (self.walk_chance == 0 or minetest.registered_items[nod.name].walkable)
13021253 and not blocked and not self.facing_fence and nod.name ~= node_snow then
13411292 local yaw = self.object:get_yaw() or 0
13421293 local turn = random(0, 2) + 1.35
13431294
1344 yaw = self:set_yaw(yaw + turn, 12)
1295 self:set_yaw(yaw + turn, 12)
13451296
13461297 self.jump_count = 0
13471298 end
13681319 if dist < 1 then dist = 1 end
13691320
13701321 local damage = floor((4 / dist) * radius)
1371 local ent = objs[n]:get_luaentity()
13721322
13731323 -- punches work on entities AND players
13741324 objs[n]:punch(objs[n], 1.0, {
13751325 full_punch_interval = 1.0,
1376 damage_groups = {fleshy = damage},
1326 damage_groups = {fleshy = damage}
13771327 }, pos)
13781328 end
13791329 end
14391389 self.object:set_pos(pos)
14401390
14411391 -- jump slightly when fully grown so as not to fall into ground
1442 self.object:set_velocity({x = 0, y = 0.5, z = 0 })
1392 self.object:set_velocity({x = 0, y = 2, z = 0 })
14431393 end
14441394 end
14451395
14671417
14681418 local pos = self.object:get_pos()
14691419
1470 effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8,
1471 "heart.png", 3, 4, 1, 0.1)
1420 effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, "heart.png", 3, 4, 1, 0.1)
14721421
14731422 local objs = minetest.get_objects_inside_radius(pos, 3)
14741423 local ent
14891438 local selfname = self.name:split(":")
14901439
14911440 if entname[1] == selfname[1] then
1441
14921442 entname = entname[2]:split("_")
14931443 selfname = selfname[2]:split("_")
14941444
15761526 self.base_selbox[4] * .5,
15771527 self.base_selbox[5] * .5,
15781528 self.base_selbox[6] * .5
1579 },
1529 }
15801530 })
15811531 -- tamed and owned by parents' owner
15821532 ent2.child = true
16521602 function mob_class:day_docile()
16531603
16541604 if self.docile_by_day == false then
1655
16561605 return false
1657
16581606 elseif self.docile_by_day == true
16591607 and self.time_of_day > 0.2
16601608 and self.time_of_day < 0.8 then
1661
16621609 return true
16631610 end
16641611 end
17031650
17041651
17051652 local pathfinder_mod = minetest.get_modpath("pathfinder")
1653
17061654 -- path finding and smart mob routine by rnd,
17071655 -- line_of_sight and other edits by Elkien3
17081656 function mob_class:smart_mobs(s, p, dist, dtime)
17501698 end -- can see target!
17511699 end
17521700
1753 if (self.path.stuck_timer > stuck_timeout and not self.path.following) then
1701 if self.path.stuck_timer > stuck_timeout and not self.path.following then
17541702
17551703 use_pathfind = true
17561704 self.path.stuck_timer = 0
17661714 end, self)
17671715 end
17681716
1769 if (self.path.stuck_timer > stuck_path_timeout and self.path.following) then
1717 if self.path.stuck_timer > stuck_path_timeout and self.path.following then
17701718
17711719 use_pathfind = true
17721720 self.path.stuck_timer = 0
17821730 end, self)
17831731 end
17841732
1785 if abs(vsubtract(s,target_pos).y) > self.stepheight then
1733 if abs(vsubtract(s, target_pos).y) > self.stepheight then
17861734
17871735 if height_switcher then
17881736 use_pathfind = true
18441792 print("-- path length:" .. tonumber(#self.path.way))
18451793
18461794 for _,pos in pairs(self.path.way) do
1795
18471796 minetest.add_particle({
1848 pos = pos,
1849 velocity = {x=0, y=0, z=0},
1850 acceleration = {x=0, y=0, z=0},
1851 expirationtime = 1,
1852 size = 4,
1853 collisiondetection = false,
1854 vertical = false,
1855 texture = "heart.png",
1797 pos = pos,
1798 velocity = {x=0, y=0, z=0},
1799 acceleration = {x=0, y=0, z=0},
1800 expirationtime = 1,
1801 size = 4,
1802 collisiondetection = false,
1803 vertical = false,
1804 texture = "heart.png",
18561805 })
18571806 end
18581807 end
19481897 -- peaceful player privilege support
19491898 local function is_peaceful_player(player)
19501899
1900 -- main setting enabled
19511901 if peaceful_player_enabled then
1952
1953 local player_name = player:get_player_name()
1954
1955 if player_name
1956 and minetest.check_player_privs(player_name, "peaceful_player") then
1957 return true
1958 end
1902 return true
1903 end
1904
1905 local player_name = player:get_player_name()
1906
1907 -- player priv enabled
1908 if player_name
1909 and minetest.check_player_privs(player_name, "peaceful_player") then
1910 return true
19591911 end
19601912
19611913 return false
21312083
21322084 for n = 1, #players do
21332085
2134 if players[n] and get_distance(players[n]:get_pos(), s) < self.view_range
2135 and not is_invisible(self, players[n]:get_player_name()) then
2086 if players[n]
2087 and not is_invisible(self, players[n]:get_player_name())
2088 and get_distance(players[n]:get_pos(), s) < self.view_range then
21362089
21372090 self.following = players[n]
21382091
21892142 yaw_to_pos(self, p)
21902143
21912144 -- anyone but standing npc's can move along
2192 if dist > self.reach
2145 if dist >= self.reach
21932146 and self.order ~= "stand" then
21942147
21952148 self:set_velocity(self.walk_velocity)
2149 self.follow_stop = nil
21962150
21972151 if self.walk_chance ~= 0 then
21982152 self:set_animation("walk")
22002154 else
22012155 self:set_velocity(0)
22022156 self:set_animation("stand")
2157 self.follow_stop = true
22032158 end
22042159
22052160 return
22692224
22702225 local yaw = self.object:get_yaw() ; if not yaw then return end
22712226
2272 if self.state == "stand" then
2227 if self.state == "stand" and not self.follow_stop then
22732228
22742229 if self.randomly_turn and random(4) == 1 then
22752230
22922247 yaw = yaw + random(-0.5, 0.5)
22932248 end
22942249
2295 yaw = self:set_yaw(yaw, 8)
2250 self:set_yaw(yaw, 8)
22962251 end
22972252
22982253 self:set_velocity(0)
23562311 end
23572312 end
23582313
2359 yaw = self:set_yaw(yaw, 8)
2314 self:set_yaw(yaw, 8)
23602315
23612316 -- otherwise randomly turn
23622317 elseif self.randomly_turn and random(100) <= 30 then
23632318
23642319 yaw = yaw + random(-0.5, 0.5)
23652320
2366 yaw = self:set_yaw(yaw, 8)
2321 self:set_yaw(yaw, 8)
23672322
23682323 -- for flying/swimming mobs randomly move up and down also
23692324 if self.fly_in
23882343 else
23892344 self:set_velocity(self.walk_velocity)
23902345
2346 -- figure out which animation to use while in motion
23912347 if self:flight_check()
23922348 and self.animation
23932349 and self.animation.fly_start
23942350 and self.animation.fly_end then
2395 self:set_animation("fly")
2351
2352 local on_ground = minetest.registered_nodes[self.standing_on].walkable
2353 local in_water = minetest.registered_nodes[self.standing_in].groups.water
2354
2355 if on_ground and in_water then
2356 self:set_animation("fly")
2357 elseif on_ground then
2358 self:set_animation("walk")
2359 else
2360 self:set_animation("fly")
2361 end
23962362 else
23972363 self:set_animation("walk")
23982364 end
24482414
24492415 if self.attack_type == "explode" then
24502416
2451 yaw = yaw_to_pos(self, p)
2417 yaw_to_pos(self, p)
24522418
24532419 local node_break_radius = self.explosion_radius or 1
24542420 local entity_damage_radius = self.explosion_damage_radius
25102476 self.object:set_texture_mod(self.texture_mods)
25112477 else
25122478
2513 self.object:set_texture_mod(self.texture_mods
2514 .. "^[brighten")
2479 self.object:set_texture_mod(self.texture_mods .. "^[brighten")
25152480 end
25162481
25172482 self.blinkstatus = not self.blinkstatus
25322497
25332498 remove_mob(self, true)
25342499
2535 if minetest.get_modpath("tnt") and tnt and tnt.boom
2536 and not minetest.is_protected(pos, "") then
2537
2538 tnt.boom(pos, {
2539 radius = node_break_radius,
2540 damage_radius = entity_damage_radius,
2541 sound = self.sounds.explode
2542 })
2543 else
2544
2545 minetest.sound_play(self.sounds.explode, {
2546 pos = pos,
2547 gain = 1.0,
2548 max_hear_distance = self.sounds.distance or 32
2549 })
2550
2551 entity_physics(pos, entity_damage_radius)
2552
2553 effect(pos, 32, "tnt_smoke.png", nil, nil,
2554 node_break_radius, 1, 0)
2555 end
2500 mobs:boom(self, pos, entity_damage_radius, node_break_radius)
25562501
25572502 return true
25582503 end
26382583 p = {x = p1.x, y = p1.y, z = p1.z}
26392584 end
26402585
2641 yaw = yaw_to_pos(self, p)
2586 yaw_to_pos(self, p)
26422587
26432588 -- move towards enemy if beyond mob reach
2644 if dist > self.reach then
2589 if dist > (self.reach + (self.reach_ext or 0)) then
26452590
26462591 -- path finding by rnd
26472592 if self.pathfinding -- only if mob has pathfinding enabled
26532598 -- distance padding to stop spinning mob
26542599 local pad = abs(p.x - s.x) + abs(p.z - s.z)
26552600
2601 self.reach_ext = 0 -- extended ready off by default
2602
26562603 if self.at_cliff or pad < 0.2 then
26572604
2605 -- when on top of player extend reach slightly so player can
2606 -- still be attacked.
2607 self.reach_ext = 0.8
26582608 self:set_velocity(0)
26592609 self:set_animation("stand")
26602610 else
27272677
27282678 local vec = {x = p.x - s.x, y = p.y - s.y, z = p.z - s.z}
27292679
2730 yaw = yaw_to_pos(self, p)
2680 yaw_to_pos(self, p)
27312681
27322682 self:set_velocity(0)
27332683
28872837
28882838 local weapon = hitter:get_wielded_item()
28892839 local weapon_def = weapon:get_definition() or {}
2890 local punch_interval = 1.4
28912840
28922841 -- calculate mob damage
28932842 local damage = 0
29492898 end
29502899
29512900 -- add weapon wear
2952 punch_interval = tool_capabilities.full_punch_interval or 1.4
2901 local punch_interval = tool_capabilities.full_punch_interval or 1.4
29532902
29542903 -- toolrank support
29552904 local wear = floor((punch_interval / 75) * 9000)
29762925
29772926 -- select tool use sound if found, or fallback to default
29782927 local snd = weapon_def.sound and weapon_def.sound.use
2979 or "default_punch"
2928 or "mobs_punch"
29802929
29812930 minetest.sound_play(snd, {object = self.object, max_hear_distance = 8}, true)
29822931
30122961 if self:check_for_death({type = "punch", puncher = hitter, hot = hot}) then
30132962 return true
30142963 end
3015 end -- END if damage
2964 end
30162965
30172966 -- knock back effect (only on full punch)
30182967 if self.knock_back and tflp >= punch_interval then
30472996 and self.order ~= "stand" then
30482997
30492998 local lp = hitter:get_pos()
3050 local yaw = yaw_to_pos(self, lp, 3)
2999 yaw = yaw_to_pos(self, lp, 3)
30513000
30523001 self.state = "runaway"
30533002 self.runaway_timer = 0
31553104 end
31563105 end
31573106
3158 --print('===== '..self.name..'\n'.. dump(tmp)..'\n=====\n')
3159
31603107 return minetest.serialize(tmp)
31613108 end
31623109
32853232 if type(self.armor) == "table" then
32863233 armor = table_copy(self.armor)
32873234 else
3288 armor = {fleshy = self.armor} -- immortal = 1
3235 armor = {fleshy = self.armor, immortal = 1}
32893236 end
32903237 self.object:set_armor_groups(armor)
32913238
33333280 end
33343281
33353282 if use_cmi then
3336 self._cmi_components = cmi.activate_components(
3337 self.serialized_cmi_components)
3283 self._cmi_components = cmi.activate_components(self.serialized_cmi_components)
33383284 cmi.notify_activate(self.object, dtime)
33393285 end
33403286 end
33673313 end
33683314 end
33693315
3370 -- minetest.log("action",
3371 -- S("lifetimer expired, removed @1", self.name))
3316 -- minetest.log("action", S("lifetimer expired, removed @1", self.name))
33723317
33733318 effect(pos, 15, "tnt_smoke.png", 2, 4, 2, 0)
33743319
33953340 -- early warning check, if no yaw then no entity, skip rest of function
33963341 if not yaw then return end
33973342
3398 -- get node at foot level every quarter second
33993343 self.node_timer = (self.node_timer or 0) + dtime
34003344
3345 -- get nodes above and below foot level every 1/4 second
34013346 if self.node_timer > 0.25 then
34023347
34033348 self.node_timer = 0
34193364
34203365 -- if standing inside solid block then jump to escape
34213366 if minetest.registered_nodes[self.standing_in].walkable
3422 and minetest.registered_nodes[self.standing_in].drawtype
3423 == "normal" then
3424
3425 self.object:set_velocity({
3426 x = 0,
3427 y = self.jump_height,
3428 z = 0
3429 })
3367 and minetest.registered_nodes[self.standing_in].drawtype == "normal" then
3368
3369 self.object:set_velocity({
3370 x = 0,
3371 y = self.jump_height,
3372 z = 0
3373 })
34303374 end
34313375
34323376 -- check and stop if standing at cliff and fear of heights
34563400 if yaw > self.target_yaw then
34573401
34583402 if dif > pi then
3459 dif = 2 * pi - dif -- need to add
3460 yaw = yaw + dif / self.delay
3403 dif = 2 * pi - dif
3404 yaw = yaw + dif / self.delay -- need to add
34613405 else
34623406 yaw = yaw - dif / self.delay -- need to subtract
34633407 end
35943538 on_flop = def.on_flop,
35953539 do_custom = def.do_custom,
35963540 jump_height = def.jump_height,
3541 can_leap = def.can_leap,
35973542 drawtype = def.drawtype, -- DEPRECATED, use rotate instead
35983543 rotate = rad(def.rotate or 0), -- 0=front 90=side 180=back 270=side2
35993544 glow = def.glow,
36783623 stay_near = def.stay_near,
36793624 randomly_turn = def.randomly_turn ~= false,
36803625 ignore_invisibility = def.ignore_invisibility,
3626 messages = def.messages,
36813627
36823628 on_spawn = def.on_spawn,
36833629
36953641
36963642 get_staticdata = function(self)
36973643 return self:mob_staticdata(self)
3698 end,
3644 end
36993645
37003646 }, mob_class_meta))
37013647
37923738 -- global functions
37933739
37943740 function mobs:add_mob(pos, def)
3741
3742 -- nil check
3743 if not pos or not def then
3744 --print("--- no position or definition given")
3745 return
3746 end
37953747
37963748 -- is mob actually registered?
37973749 if not mobs.spawning_mobs[def.name]
38643816 ent.base_selbox[4] * .5,
38653817 ent.base_selbox[5] * .5,
38663818 ent.base_selbox[6] * .5
3867 },
3819 }
38683820 })
38693821
38703822 ent.child = true
42804232 end
42814233
42824234
4283 -- compatibility function
4235 -- compatibility function (deprecated)
42844236 function mobs:explosion(pos, radius)
4285 mobs:boom({sounds = {explode = "tnt_explode"}}, pos, radius)
4237 mobs:boom({sounds = {explode = "tnt_explode"}}, pos, radius, radius, "tnt_smoke.png")
42864238 end
42874239
42884240
42894241 -- no damage to nodes explosion
4290 function mobs:safe_boom(self, pos, radius)
4242 function mobs:safe_boom(self, pos, radius, texture)
42914243
42924244 minetest.sound_play(self.sounds and self.sounds.explode or "tnt_explode", {
42934245 pos = pos,
42974249
42984250 entity_physics(pos, radius)
42994251
4300 effect(pos, 32, "tnt_smoke.png", radius * 3, radius * 5, radius, 1, 0)
4252 effect(pos, 32, texture, radius * 3, radius * 5, radius, 1, 0)
43014253 end
43024254
43034255
43044256 -- make explosion with protection and tnt mod check
4305 function mobs:boom(self, pos, radius)
4257 function mobs:boom(self, pos, radius, damage_radius, texture)
43064258
43074259 if mobs_griefing
43084260 and minetest.get_modpath("tnt") and tnt and tnt.boom
43104262
43114263 tnt.boom(pos, {
43124264 radius = radius,
4313 damage_radius = radius,
4265 damage_radius = damage_radius,
43144266 sound = self.sounds and self.sounds.explode,
4315 explode_center = true
4267 explode_center = true,
4268 tiles = {(texture or "tnt_smoke.png")}
43164269 })
43174270 else
4318 mobs:safe_boom(self, pos, radius)
4271 mobs:safe_boom(self, pos, radius, texture)
43194272 end
43204273 end
43214274
43894342 end
43904343
43914344 return itemstack
4392 end,
4345 end
43934346 })
43944347
43954348
44514404 end
44524405
44534406 return itemstack
4454 end,
4407 end
44554408 })
44564409 end
44574410
44704423
44714424 if t ~= "function"
44724425 and t ~= "nil"
4473 and t ~= "userdata" then
4426 and t ~= "userdata"
4427 and _ ~= "object"
4428 and _ ~= "_cmi_components" then
44744429 tmp[_] = self[_]
44754430 end
44764431 end
44974452 function mobs:capture_mob(self, clicker, chance_hand, chance_net,
44984453 chance_lasso, force_take, replacewith)
44994454
4500 if not self --self.child
4455 if not self
45014456 or not clicker:is_player()
45024457 or not clicker:get_inventory() then
45034458 return false
45844539
45854540 if t ~= "function"
45864541 and t ~= "nil"
4587 and t ~= "userdata" then
4542 and t ~= "userdata"
4543 and _ ~= "object"
4544 and _ ~= "_cmi_components" then
45884545 tmp[_] = self[_]
45894546 end
45904547 end
46664623
46674624 pos.y = pos.y + self.collisionbox[2] + 0.5
46684625
4669 effect(self.object:get_pos(), 25, "mobs_protect_particle.png",
4670 0.5, 4, 2, 15)
4626 effect(self.object:get_pos(), 25, "mobs_protect_particle.png", 0.5, 4, 2, 15)
46714627
46724628 self:mob_sound("mobs_spell")
46734629
47014657 if self.health >= self.hp_max then
47024658
47034659 self.health = self.hp_max
4704
4705 -- if self.htimer < 1 then
4706
4707 -- minetest.chat_send_player(clicker:get_player_name(),
4708 -- S("@1 at full health (@2)",
4709 -- self.name:split(":")[2], tostring(self.health)))
4710
4711 -- self.htimer = 5
4712 -- end
47134660 end
47144661
47154662 self.object:set_hp(self.health)
47174664 -- make children grow quicker
47184665 if self.child == true then
47194666
4720 -- self.hornytimer = self.hornytimer + 20
47214667 -- deduct 10% of the time to adulthood
47224668 self.hornytimer = math.floor(self.hornytimer + (
47234669 (CHILD_GROW_TIME - self.hornytimer) * 0.1))
48564802 end)
48574803
48584804
4859 -- compatibility function for old entities to new modpack entities
4805 -- compatibility function for old mobs entities to new mobs_redo modpack
48604806 function mobs:alias_mob(old_name, new_name)
48614807
48624808 -- check old_name entity doesnt already exist
48874833 end
48884834 })
48894835 end
4836
4837
4838 -- admin command to remove untamed mobs around players
4839 minetest.register_chatcommand("clear_mobs", {
4840 params = "<text>",
4841 description = "Remove untamed mobs from around players.",
4842 privs = {server = true},
4843
4844 func = function (name, param)
4845
4846 local count = 0
4847
4848 for _, player in pairs(minetest.get_connected_players()) do
4849
4850 if player then
4851
4852 local pos = player:get_pos()
4853
4854 local objs = minetest.get_objects_inside_radius(pos, 28)
4855
4856 for _, obj in pairs(objs) do
4857
4858 if obj then
4859
4860 local ent = obj:get_luaentity()
4861
4862 -- only remove mobs redo mobs that are not tamed
4863 if ent and ent._cmi_is_mob and ent.tamed ~= true then
4864
4865 remove_mob(ent, true)
4866
4867 count = count + 1
4868 end
4869 end
4870 end
4871 end
4872 end
4873
4874 minetest.chat_send_player(name, S("@1 mobs removed.", count))
4875 end
4876 })
33
44 Welcome to the world of mobs in minetest and hopefully an easy guide to defining
55 your own mobs and having them appear in your worlds.
6
7
8 Quick Note
9 ----------
10
11 Since the mobs redo api checks for nodes around the mob to function, it relies on a
12 default node incase anything goes wrong, so in the default game this is default:dirt
13 but for any custom game please make sure the following line is registered with your
14 preferred dirt node of choice:
15
16 minetest.register_alias("mapgen_dirt", "mymod:my_dirt_node")
617
718
819 Registering Mobs
4152 randomly turn while walking or standing.
4253 'jump' when true allows your mob to jump updwards.
4354 'jump_height' holds the height your mob can jump, 0 to disable jumping.
55 'can_leap' when true obstacles like fences or pits wont stop a mob
56 from trying to jump out.
4457 'stepheight' height of a block that your mob can easily walk up onto,
4558 defaults to 1.1.
4659 'fly' when true allows your mob to fly around instead of walking.
222235 'fly_start' when a mob is flying.
223236 'fly_end'
224237 'fly_speed'
238 'jump_start' when a mob is jumping
239 'jump_end'
240 'jump_speed'
225241 'punch_start' when a mob melee attacks.
226242 'punch_end'
227243 'punch_speed'
414430 mobs:register_spawn(name, nodes, max_light, min_light, chance,
415431 active_object_count, max_height, day_toggle)
416432
417 mobs:spawn_specfic(name, nodes, neighbors, min_light, max_light, interval,
433 mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, interval,
418434 chance, active_object_count, min_height, max_height, day_toggle, on_spawn)
419435
420436 A simpler way to handle mob spawns has been added with the mobs:spawn(def)
520536
521537 mobs:explosion(pos, radius) -- DEPRECATED!!! use mobs:boom() instead
522538
523 mobs:boom(self, pos, radius)
539 mobs:boom(self, pos, radius, damage_radius, texture)
524540 'self' mob entity
525541 'pos' centre position of explosion
526542 'radius' radius of explosion (typically set to 3)
543 'damage_radius' radius of damage around explosion
544 'texture' particle texture during explosion, defaults to "tnt_smoke.png"
527545
528546 This function generates an explosion which removes nodes in a specific radius
529547 and damages any entity caught inside the blast radius. Protection will limit
694712
695713 This function returns true if the node name given is harmful to the mob (mob_object),
696714 it is mainly used when a mob is near a node it has to avoid.
715
716
717 Looting Level
718 -------------
719
720 If a tool is used with 'looting_level' defined under tool_capabilities then mobs can drop
721 extra items per level up to a maximum of 3 levels. 'looting_level' can also be read from
722 the tools own meta to override the default.
697723
698724
699725 External Settings for "minetest.conf"
714740 spawn number e.g. mobs_animal:cow = 1000,5
715741 'mob_difficulty' sets difficulty level (health and hit damage
716742 multiplied by this number), defaults to 1.0.
717 'mob_show_health' if false then punching mob will not show health status
718 (true by default)
719743 'mob_chance_multiplier' multiplies chance of all mobs spawning and can be set
720744 to 0.5 to have mobs spawn more or 2.0 to spawn less.
721745 e.g. 1 in 7000 * 0.5 = 1 in 3500 so better odds of
5252 })
5353
5454 if minetest.get_modpath("farming") then
55
5556 minetest.register_craft({
5657 output = "mobs:lasso",
5758 recipe = {
7273 })
7374
7475 if minetest.get_modpath("farming") then
76
7577 minetest.register_craft({
7678 output = "mobs:net",
7779 recipe = {
147149
148150
149151 -- make sure we can register fences
150 if minetest.get_modpath("default") and default.register_fence then
152 local mod_def = minetest.get_modpath("default")
153
154 if mod_def and default.register_fence then
151155
152156 -- mob fence (looks like normal fence but collision is 2 high)
153157 default.register_fence("mobs:fence_wood", {
155159 texture = "default_wood.png",
156160 material = "default:fence_wood",
157161 groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2},
158 sounds = default.node_sound_wood_defaults(),
162 sounds = mod_def and default.node_sound_wood_defaults(),
159163 collision_box = {
160164 type = "fixed",
161165 fixed = {
173177 paramtype = "light",
174178 is_ground_content = false,
175179 groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 2},
176 sounds = default.node_sound_wood_defaults(),
180 sounds = mod_def and default.node_sound_wood_defaults(),
177181 node_box = {
178182 type = "fixed",
179183 fixed = {-0.2, -0.5, -0.2, 0.2, 0, 0.2}
243247
244248 -- this tool spawns same mob and adds owner, protected, nametag info
245249 -- then removes original entity, this is used for fixing any issues.
250 -- also holding sneak while punching mob lets you change texture name.
246251
247252 local tex_obj
248253
352357 end)
353358
354359
355 -- Meat Block (thanks to painterlypack.net for allowing me to use these textures)
360 -- Meat Block
356361 minetest.register_node("mobs:meatblock", {
357362 description = S("Meat Block"),
358363 tiles = {"mobs_meat_top.png", "mobs_meat_bottom.png", "mobs_meat_side.png"},
359364 paramtype2 = "facedir",
360365 groups = {choppy = 1, oddly_breakable_by_hand = 1, flammable = 2},
361 sounds = default and default.node_sound_leaves_defaults(),
366 sounds = mod_def and default.node_sound_leaves_defaults(),
362367 on_place = minetest.rotate_node,
363368 on_use = minetest.item_eat(20)
364369 })
365370
366371 minetest.register_craft({
367372 output = "mobs:meatblock",
368 -- type = "shapeless",
369373 recipe = {
370374 {"group:food_meat", "group:food_meat", "group:food_meat"},
371375 {"group:food_meat", "group:food_meat", "group:food_meat"},
372376 {"group:food_meat", "group:food_meat", "group:food_meat"}
373377 }
374378 })
379
380 -- Meat Block (raw)
381 minetest.register_node("mobs:meatblock_raw", {
382 description = S("Raw Meat Block"),
383 tiles = {"mobs_meat_raw_top.png", "mobs_meat_raw_bottom.png", "mobs_meat_raw_side.png"},
384 paramtype2 = "facedir",
385 groups = {choppy = 1, oddly_breakable_by_hand = 1, flammable = 2},
386 sounds = mod_def and default.node_sound_leaves_defaults(),
387 on_place = minetest.rotate_node,
388 on_use = minetest.item_eat(20)
389 })
390
391 minetest.register_craft({
392 output = "mobs:meatblock_raw",
393 recipe = {
394 {"group:food_meat_raw", "group:food_meat_raw", "group:food_meat_raw"},
395 {"group:food_meat_raw", "group:food_meat_raw", "group:food_meat_raw"},
396 {"group:food_meat_raw", "group:food_meat_raw", "group:food_meat_raw"}
397 }
398 })
399
400 minetest.register_craft({
401 type = "cooking",
402 output = "mobs:meatblock",
403 recipe = "mobs:meatblock_raw",
404 cooktime = 30
405 })
0 minetest-mod-mobs-redo (20210923+git20220314.1.fb42be6-1) UNRELEASED; urgency=low
0 minetest-mod-mobs-redo (20210923+git20221213.0.86b6a1a-1) UNRELEASED; urgency=low
11
22 * New upstream snapshot.
3 * New upstream snapshot.
34
4 -- Debian Janitor <janitor@jelmer.uk> Sat, 19 Mar 2022 14:44:49 -0000
5 -- Debian Janitor <janitor@jelmer.uk> Mon, 19 Dec 2022 06:21:30 -0000
56
67 minetest-mod-mobs-redo (20210923-1) unstable; urgency=medium
78
1818 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1919 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2020 THE SOFTWARE.
21
22
23 Textures under CC0 license by TenPlus1
24
25
26 ShadowNinja (CC BY-SA 3.0):
27 tnt_smoke.png
28
29 mobs_swing.ogg by qubodup (CC0)
30 - http://freesound.org/people/qubodup/sounds/60012/
31
32 mobs_spell.ogg by littlerobotsoundfactory (CC0)
33 - http://freesound.org/people/LittleRobotSoundFactory/sounds/270396/
34
35 mobs_punch.ogg by Merrick079 (CC0)
36 - https://freesound.org/people/Merrick079/sounds/566436/
253253 -- move forwards
254254 if ctrl.up then
255255
256 entity.v = entity.v + entity.accel / 10
256 entity.v = entity.v + entity.accel * dtime
257257
258258 -- move backwards
259259 elseif ctrl.down then
262262 return
263263 end
264264
265 entity.v = entity.v - entity.accel / 10
265 entity.v = entity.v - entity.accel * dtime
266266 end
267267
268268 -- mob rotation
295295
296296 elseif velo.y > 0 then
297297
298 velo.y = velo.y - 0.1
298 velo.y = velo.y - dtime
299299
300300 if velo.y < 0 then velo.y = 0 end
301301 end
309309
310310 elseif velo.y < 0 then
311311
312 velo.y = velo.y + 0.1
312 velo.y = velo.y + dtime
313313
314314 if velo.y > 0 then velo.y = 0 end
315315 end
354354 end
355355
356356 -- enforce speed limit forward and reverse
357 local max_spd = entity.max_speed_reverse
358
359 if get_sign(entity.v) >= 0 then
360 max_spd = entity.max_speed_forward
361 end
362
363 if abs(entity.v) > max_spd then
364 entity.v = entity.v - get_sign(entity.v)
357 if entity.v > entity.max_speed_forward then
358 entity.v = entity.max_speed_forward
359 elseif entity.v < -entity.max_speed_reverse then
360 entity.v = -entity.max_speed_reverse
365361 end
366362
367363 -- Set position, velocity and acceleration
369365
370366 if not p then return end
371367
372 local new_acce = {x = 0, y = -9.81, z = 0}
368 local new_acce = {x = 0, y = entity.fall_speed, z = 0}
373369
374370 p.y = p.y - 0.5
375371
495491
496492 ent.switch = 1 -- for mob specific arrows
497493 ent.owner_id = tostring(entity.object) -- so arrows dont hurt entity you are riding
494
498495 local vec = {x = dir.x * 6, y = dir.y * 6, z = dir.z * 6}
499496
500497 yaw = entity.driver:get_look_horizontal()
2222
2323
2424 Changelog:
25 - 1.56 - Added arrow_override function to mob definition to tweak arrow entity settings, tamed monsters no longer despawn when outside loaded map area.
25 - 1.56 - Added arrow_override function to mob definition to tweak arrow entity settings, tamed monsters no longer despawn when outside loaded map area, 'looting_level' can be read from tool definition or tool meta to add extra drops when mob killed.
2626 - 1.55 - Add 'peaceful_player' privelage and setting so mobs don't attack specific players (thanks sfence), add support for MarkBu's pathfinder mod, remove need for default mod
2727 - 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.
2828 - 1.53 - Added 'on_map_load' settings to mobs:spawn so that mobs will only spawn when new areas of map are loaded.
2121 # Sets Mob difficulty level by multiplying punch damage
2222 mob_difficulty (Mob difficulty) float 1.0
2323
24 # If disabled health status no longer appears above Mob when punched
25 mob_show_health (Show Mob health) bool true
26
2724 # Contains a value used to multiply Mob spawn values
2825 mob_chance_multiplier (Mob chance multiplier) float 1.0
2926
sounds/default_punch.ogg less more
Binary diff not shown
+0
-7
sounds/license.txt less more
0 Creative Commons sounds from Freesound.org
1
2 mobs_swing.ogg by qubodup
3 - http://freesound.org/people/qubodup/sounds/60012/
4
5 mobs_spell.ogg by littlerobotsoundfactory
6 - http://freesound.org/people/LittleRobotSoundFactory/sounds/270396/
Binary diff not shown
Binary diff not shown