parent
1fe7676bcc
commit
ce9f06d38e
|
@ -34,10 +34,10 @@ Here are some of the other highlights of the Mobs Lite engine:
|
|||
changing their course.
|
||||
|
||||
* Mobs can be randomly spawned in the vicinity of players, thus relieving the overhead of
|
||||
ABM-based spawners.
|
||||
costly ABM-based spawners.
|
||||
|
||||
* Specialized Timekeeper class ensures efficient dispatching of mob-related callbacks for
|
||||
each globalstep cycle.
|
||||
* Timekeeper helper class ensures efficient dispatching of mob-related callbacks for the
|
||||
appropriate server cycle.
|
||||
|
||||
* And of course, much much more!
|
||||
|
||||
|
|
10
animals.lua
10
animals.lua
|
@ -56,6 +56,10 @@ mobs.register_mob( "mobs:kitten", {
|
|||
["mobs:meat_raw"] = "follow",
|
||||
["mobs:meat"] = "follow",
|
||||
},
|
||||
watch_spawnitems = {
|
||||
["mobs:meat_raw"] = "follow",
|
||||
["mobs:meat"] = "follow",
|
||||
},
|
||||
watch_players = { },
|
||||
|
||||
hunger = 5,
|
||||
|
@ -149,6 +153,10 @@ mobs.register_mob( "mobs:rat", {
|
|||
["mobs:meat_raw"] = "follow",
|
||||
["mobs:meat"] = "follow",
|
||||
},
|
||||
watch_spawnitems = {
|
||||
["mobs:meat_raw"] = "follow",
|
||||
["mobs:meat"] = "follow",
|
||||
},
|
||||
watch_players = { },
|
||||
|
||||
hp_max = 2,
|
||||
|
@ -229,6 +237,7 @@ mobs.register_mob( "mobs:hare", {
|
|||
["default:apple"] = "follow",
|
||||
["default:orange"] = "follow",
|
||||
},
|
||||
watch_spawnitems = { },
|
||||
watch_players = { },
|
||||
|
||||
hp_max = 4,
|
||||
|
@ -320,6 +329,7 @@ mobs.register_mob( "mobs:chicken", {
|
|||
["default:apple"] = "follow",
|
||||
["default:orange"] = "follow",
|
||||
},
|
||||
watch_spawnitems = { },
|
||||
watch_players = { },
|
||||
|
||||
hunger = 10,
|
||||
|
|
153
init.lua
153
init.lua
|
@ -9,8 +9,11 @@
|
|||
|
||||
mobs = { }
|
||||
|
||||
local source_list = { }
|
||||
local connected_players = { }
|
||||
local registry = {
|
||||
players = { },
|
||||
avatars = { },
|
||||
spawnitems = { },
|
||||
}
|
||||
|
||||
world_gravity = -10
|
||||
liquid_density = 0.5
|
||||
|
@ -124,8 +127,8 @@ local function node_locator( pos, size, time, color )
|
|||
if is_debug then
|
||||
minetest.add_particle( {
|
||||
pos = pos,
|
||||
velocity = { x=0, y=0, z=0 },
|
||||
acceleration = { x=0, y=0, z=0 },
|
||||
vel = { x=0, y=0, z=0 },
|
||||
acc = { x=0, y=0, z=0 },
|
||||
exptime = time + 4,
|
||||
size = size,
|
||||
collisiondetection = false,
|
||||
|
@ -201,25 +204,49 @@ end
|
|||
|
||||
--------------------
|
||||
|
||||
minetest.register_on_joinplayer( function( player )
|
||||
local name = player:get_player_name( )
|
||||
|
||||
connected_players[ name ] = player -- maintain our own lookup table for efficiency
|
||||
end )
|
||||
|
||||
minetest.register_on_leaveplayer( function( player, is_timeout )
|
||||
local name = player:get_player_name( )
|
||||
|
||||
-- delete all target references (if applicable)
|
||||
for id, this in pairs( source_list ) do
|
||||
for id, this in pairs( registry.avatars ) do
|
||||
if this.target == player then
|
||||
this:reset_target( )
|
||||
end
|
||||
end
|
||||
|
||||
connected_players[ name ] = nil
|
||||
registry.players[ name ] = nil
|
||||
end )
|
||||
|
||||
minetest.register_on_joinplayer( function( player )
|
||||
local name = player:get_player_name( )
|
||||
registry.players[ name ] = player
|
||||
end )
|
||||
|
||||
--------------------
|
||||
|
||||
local builtin_item = minetest.registered_entities[ "__builtin:item" ]
|
||||
|
||||
builtin_item.old_on_activate = builtin_item.on_activate
|
||||
builtin_item.old_set_item = builtin_item.set_item
|
||||
|
||||
builtin_item.set_item = function ( self, itemstring )
|
||||
self:old_set_item( itemstring )
|
||||
self.item_name = ItemStack( self.itemstring ):get_name( )
|
||||
end
|
||||
|
||||
builtin_item.on_activate = function ( self, staticdata, dtime, id )
|
||||
self:old_on_activate( staticdata, dtime )
|
||||
registry.spawnitems[ id ] = self.object
|
||||
end
|
||||
|
||||
builtin_item.on_deactivate = function ( self, id )
|
||||
for id, this in pairs( registry.avatars ) do
|
||||
if this.target == self.object then
|
||||
this:reset_target( )
|
||||
end
|
||||
end
|
||||
registry.spawnitems[ id ] = nil
|
||||
end
|
||||
|
||||
--------------------
|
||||
|
||||
local globaltimer = Timekeeper( { } )
|
||||
|
@ -254,6 +281,7 @@ mobs.register_mob = function ( name, def )
|
|||
attack_range = def.attack_range,
|
||||
escape_range = def.escape_range,
|
||||
follow_range = def.follow_range,
|
||||
pickup_range = def.pickup_range,
|
||||
sneak_velocity = def.sneak_velocity,
|
||||
walk_velocity = def.walk_velocity,
|
||||
run_velocity = def.run_velocity,
|
||||
|
@ -276,6 +304,7 @@ mobs.register_mob = function ( name, def )
|
|||
shoot_chance = def.shoot_chance,
|
||||
weapon_params = def.weapon_params,
|
||||
watch_wielditems = def.watch_wielditems,
|
||||
watch_spawnitems = def.watch_spawnitems,
|
||||
watch_players = def.watch_players,
|
||||
sounds = def.sounds,
|
||||
animation = def.animation,
|
||||
|
@ -389,24 +418,24 @@ mobs.register_mob = function ( name, def )
|
|||
-- sensory processing --
|
||||
|
||||
check_suspect = function ( self, target, elapsed )
|
||||
local player_name = target:get_player_name( )
|
||||
local wielditem_name = target:get_wielded_item( ):get_name( )
|
||||
local suspect = self.watch_players[ player_name ] or self.watch_wielditems[ wielditem_name ]
|
||||
local entity = target:get_luaentity( )
|
||||
local suspect
|
||||
|
||||
if not entity then
|
||||
local player_name = target:get_player_name( )
|
||||
local item_name = target:get_wielded_item( ):get_name( )
|
||||
suspect = self.watch_players[ player_name ] or self.watch_wielditems[ item_name ]
|
||||
elseif entity.name == "__builtin:item" then
|
||||
suspect = self.watch_spawnitems[ entity.item_name ]
|
||||
end
|
||||
|
||||
if type( suspect ) == "function" then
|
||||
return suspect( self, player_name, wielditem_name, elapsed or 0.0 )
|
||||
return suspect( self, target, elapsed or 0.0 )
|
||||
else
|
||||
return suspect
|
||||
end
|
||||
end,
|
||||
|
||||
is_starving = function ( self )
|
||||
local hunger = self.hunger_noise:get2d( { x = self.timeout, y = 0 } )
|
||||
|
||||
-- offset of -1 is never hungry, offset of 1 is always hungry
|
||||
return hunger > -self.hunger_offset
|
||||
end,
|
||||
|
||||
is_paranoid = function ( self, target_pos )
|
||||
local length = get_vector_length( self.pos, target_pos )
|
||||
local this = self.alertness
|
||||
|
@ -436,6 +465,48 @@ mobs.register_mob = function ( name, def )
|
|||
end
|
||||
end,
|
||||
|
||||
-- utility functions --
|
||||
|
||||
is_starving = function ( self )
|
||||
local hunger = self.hunger_noise:get2d( { x = self.timeout, y = 0 } )
|
||||
|
||||
-- offset of -1 is never hungry, offset of 1 is always hungry
|
||||
return hunger > -self.hunger_offset
|
||||
end,
|
||||
|
||||
iterate_registry = function ( self, radius, height, classes )
|
||||
local length = radius * radius
|
||||
local class_idx = 1
|
||||
local key
|
||||
|
||||
local function is_inside_area( obj )
|
||||
-- perform fast length-squared distance check
|
||||
local target_pos = obj:get_pos( )
|
||||
local a = self.pos.x - target_pos.x
|
||||
local b = self.pos.z - target_pos.z
|
||||
|
||||
return a * a + b * b < length
|
||||
end
|
||||
|
||||
return function ( )
|
||||
while classes[ class_idx ] do
|
||||
local obj
|
||||
|
||||
key, obj = next( classes[ class_idx ], key )
|
||||
if obj then
|
||||
if obj:get_hp( ) > 0 and is_inside_area( obj ) then
|
||||
return obj
|
||||
end
|
||||
else
|
||||
class_idx = class_idx + 1
|
||||
key = nil
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
|
||||
fire_weapon = function ( self, target_pos )
|
||||
local params = self.weapon_params
|
||||
|
||||
|
@ -723,10 +794,6 @@ mobs.register_mob = function ( name, def )
|
|||
if self.speed > 0 then
|
||||
self:set_speed( 0 )
|
||||
self:set_animation( "stand" )
|
||||
|
||||
-- if self.on_wielditem( self, self.target )
|
||||
-- if self.target:get_entity_name( ) then
|
||||
-- end
|
||||
end
|
||||
else
|
||||
if self.speed == 0 then
|
||||
|
@ -961,25 +1028,25 @@ mobs.register_mob = function ( name, def )
|
|||
end
|
||||
|
||||
-- when not upset, seek out food or prey at random intervals
|
||||
for _, player in pairs( connected_players ) do
|
||||
local player_pos = player:getpos( )
|
||||
for obj in self:iterate_registry( 30, 30, { registry.players, registry.spawnitems } ) do
|
||||
local target_pos = obj:get_pos( )
|
||||
|
||||
if not player:get_attach( ) and self:is_paranoid( player_pos ) then
|
||||
if random( 10 ) <= self.fear_factor and player:get_hp( ) > 0 then
|
||||
if self:is_paranoid( target_pos ) then
|
||||
if random( 10 ) <= self.fear_factor and obj:is_player( ) and not obj:get_attach( ) then
|
||||
if self.type == "monster" then
|
||||
self:set_attack_state( player )
|
||||
self:set_attack_state( obj )
|
||||
return
|
||||
else
|
||||
self:set_escape_state( player )
|
||||
self:set_escape_state( obj )
|
||||
return
|
||||
end
|
||||
elseif self.type == "animal" then
|
||||
local state = self:check_suspect( player )
|
||||
local state = self:check_suspect( obj )
|
||||
if state == "escape" then
|
||||
self:set_escape_state( player )
|
||||
self:set_escape_state( obj )
|
||||
return
|
||||
elseif state == "follow" then
|
||||
self:set_follow_state( player )
|
||||
self:set_follow_state( obj )
|
||||
return
|
||||
end
|
||||
end
|
||||
|
@ -991,7 +1058,7 @@ mobs.register_mob = function ( name, def )
|
|||
|
||||
on_step = function( self, dtime, pos, rot, new_vel, old_vel, move_result )
|
||||
self.pos = pos
|
||||
self.yaw = rot.Y
|
||||
self.yaw = rot.y
|
||||
self.new_vel = new_vel
|
||||
self.move_result = move_result
|
||||
|
||||
|
@ -1037,7 +1104,7 @@ mobs.register_mob = function ( name, def )
|
|||
end,
|
||||
|
||||
on_activate = function ( self, staticdata, dtime_s, id )
|
||||
source_list[ id ] = self
|
||||
registry.avatars[ id ] = self
|
||||
|
||||
self.object:set_armor_groups( { fleshy = self.armor } )
|
||||
self:set_acceleration_vert( self.gravity )
|
||||
|
@ -1071,7 +1138,7 @@ mobs.register_mob = function ( name, def )
|
|||
end,
|
||||
|
||||
on_deactivate = function ( self, id )
|
||||
source_list[ id ] = nil
|
||||
registry.avatars[ id ] = nil
|
||||
end,
|
||||
|
||||
get_staticdata = function ( self )
|
||||
|
@ -1179,8 +1246,8 @@ mobs.register_spawn_near = function ( name, def )
|
|||
|
||||
globaltimer.shift( 0.5 )
|
||||
globaltimer.start( 10, name, function ( )
|
||||
for player_name, player in pairs( connected_players ) do
|
||||
local player_pos = player:getpos( )
|
||||
for player_name, player in pairs( registry.players ) do
|
||||
local player_pos = player:get_pos( )
|
||||
|
||||
if random( chance ) == 1 and is_area_safe == safe_area:containsp( player_pos ) then
|
||||
local positions = minetest.find_nodes_in_area_under_air(
|
||||
|
@ -1330,5 +1397,5 @@ dofile( minetest.get_modpath( "mobs" ) .. "/weapons.lua" )
|
|||
-- compatibility for Minetest S3 engine
|
||||
|
||||
if not vector.origin then
|
||||
dofile( minetest.get_modpath( "mobs" ) .. "/compatibility.lua" )
|
||||
dofile( minetest.get_modpath( "mobs" ) .. "/compatibility.lua" )
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue