Complete rewrite and rename to Creatures MOB-Engine

master
BlockMen 2015-10-08 16:59:54 +02:00
parent b3d5b5c63a
commit 87ab8787a6
53 changed files with 1790 additions and 13902 deletions

188
API.txt Normal file
View File

@ -0,0 +1,188 @@
Creatures MOB-Engine API
------------------------
creatures.register_mob(#Creature definition)
-registers a mob at MOB-Engine; returns true when sucessfull
creatures.rnd(chance_table)
-returns a weighted random table element; chance_sum of table must be 1
^ example: creatures.rnd({elem1 = {chance = 0.7}, {elem2 = {chance = 0.3}})
creatures.compare_pos(pos1, pos2)
-returns true if pos1 == pos2
creatures.findTarget(search_obj, pos, radius, search_type, mob_name, xray, no_count)
-returns table of found objects (as ObjectRef) and boolean player_near
^ search_obj is searching object; can be nil
^ pos is starting position for search radius
^ radius for searching in blocks/node
^ search_type that specifies returned object requirements
^ "all" -- returns every object except dropped Items
^ "hostile" -- returns every object(creature) that has hostile setting or is player
^ ignores "mob_type" if specified
^ "nonhostile" -- returns every object that is not hostile or player
^ "player" -- returns all players
^ "mates" -- returns all objects(creatures) that are of same kind
^ requires "mob_type" specifies
^ mob_type specifies creature that is ignored or searched, depending on search_type
^ xray allows searching through blocks/nodes (default == false)
^ no_count skips collecting loop and returns just the boolean player_near
^ table is empty
creatures.dropItems(pos, drops)
-drops items at position pos
^ pos where to drop Items
^ drops table in #ItemDrops format
#ItemDrops
----------
{
{
<Itemname>, -- e.g. "default:wood"
<amount>, -- either a <number> or table in format {min = <number>, max = <number>}; optional
<rarity> -- "chance = <value>": <value> between 0.0 and 1.0
},
}
Example:
Will drop with a chance of 30% 1 to 3 items of type "default:wood"
and with a chance of 100% 2 items of type "default:stone"
{
{"default:wood", {min = 1, max = 3}, chance = 0.3},
{"default:stone", 2}
}
#Creature definition
--------------------
{
name = "", -- e.g. "creatures:sheep"
stats = {
hp = 1, -- 1 HP = "1/2 player heart"
hostile = false, -- is mob hostile (required for mode "attack") <optional>
lifetime = 300, -- after which time mob despawns, in seconds <optional>
dies_when_tamed = false, -- stop despawn when tamed <optional>
can_jump = 1, -- height in nodes <optional>
can_swim = false, -- can mob swim or will it drown <optional>
can_fly = false, -- allows to fly (requires mode "fly") and disable step sounds <optional>
can_burn = false, -- takes damage of lava <optional>
can_panic = false, -- runs fast around when hit (requires mode "walk") <optional>
has_falldamage = false, -- deals damage if falling more than 3 blocks <optional>
has_kockback = false, -- get knocked back when hit <optional>
sneaky = false, -- disables step sounds <optional>
light = {min, max}, -- which light level will burn creature (requires can_burn = true) <optional>
},
modes = {
idle = {chance = <part of 1.0>, duration = <time>, moving_speed = <number>, update_yaw = <yawtime>},
^ chance -- number between 0.0 and 1.0 (!!NOTE: sum of all modes MUST be 1.0!!)
^ if chance is 0 then mode is not chosen automatically
^ duration -- time in seconds until the next mode is chosen (depending on chance)
^ moving_speed -- moving speed(flying/walking) <optional>
^ update_yaw -- timer in seconds until the looking dir is changed <optional>
^ if moving_speed > 0 then the moving direction is also changed
-- special modes
attack = {<same as above>}
follow = {<same as above>, radius = <number>, timer = <time>, items = <table>},
^ same as above -- all possible values like specified above
^ radius -- search distance in blocks/nodes for player
^ timer -- time in seconds between each check for player
^ items -- table of items to make mob follow in format {<Itemname>, <Itemname>}; e.g. {"farming:wheat"}
eat = {<same as above>, nodes = <table>},
^ same as above -- all possible values like specified above
^ items -- eatable nodes in format {<Itemname>, <Itemname>}; e.g. {"default:dirt_with_grass"}
},
model = {
mesh = "creatures_sheep.x", -- mesh name; see Minetest Documentation for supported filetypes
textures = {"creatures_sheep.png"}, -- table of textures; see Minetest Documentation
collisionbox = <NodeBox>, -- defines mesh collision box; see Minetest Documentation
rotation = 0.0, -- sets rotation offset when moving
animations = { -- animation used if defined <optional>
idle = {#AnimationDef}, -- see #AnimationDef
... -- depends on modes (must correspond to be used); supported "special modes": eat, follow, attack
},
},
sounds = {
on_damage = {#SoundDef}, -- see #SoundDef <optional>
on_death = {#SoundDef}, -- see #SoundDef <optional>
swim = {#SoundDef}, -- see #SoundDef <optional>
random = { -- depends on mode <optional>
idle = {#SoundDef}, -- <optional>
... -- depends on modes (must correspond to be used); supports "special modes": eat, follow, attack
},
},
drops = {#ItemDrops}, -- see #ItemDrops definition <optional>
^ can also be a function; receives "self" reference
spawning = { -- defines spawning in world <optional>
abm_nodes = {
spawn_on = {<table>}, -- on what nodes mob can spawn <optional>
^ table -- nodes and groups in table format; e.g. {"group:stone", "default:stone"}
neighbors = {}, -- what node should be neighbors to spawnnode <optional>
^ can be nil or table as above; "air" is forced always as neighbor
},
abm_interval = <interval>, -- time in seconds until Minetest tries to find a node with set specs
abm_chance = <chance>, -- chance is 1/<chance>
max_number = <number>, -- maximum mobs of this kind per mapblock(16x16x16)
number = <amount>, -- how many mobs are spawned if found suitable spawn position
^ amount -- number or table {min = <value>, max = <value>}
time_range = <range>, -- time range in time of day format (0-24000) <optional>
^ range -- table {min = <value>, max = <value>}
light = <range>, -- min and max lightvalue at spawn position <optional>
^ range -- table {min = <value>, max = <value>}
height_limit = <range>, -- min and max height (world Y coordinate) <optional>
^ range -- table {min = <value>, max = <value>}
spawn_egg = { -- is set a spawn_egg is added to creative inventory <optional>
description = <desc>, -- Item description as string
texture = <name>, -- texture name as string
},
spawner = { -- is set a spawner_node is added to creative inventory <optional>
range = <number>, -- defines an area (in blocks/nodes) within mobs are spawned
number = <number>, -- maxmimum number of mobs spawned in area defined via range
light = <range>, -- min and max lightvalue at spawn position <optional>
^ range -- table {min = <value>, max = <value>}
}
},
on_rightclick = func(self, clicker) -- called when mob is rightclicked
^ prevents default action when returns boolean true
on_punch = func(self, puncher) -- called when mob is punched (puncher can be nil)
^ prevents default action when returns boolean true
on_step = func(self, dtime) -- called each server step
^ prevents default action when returns boolean true
on_activate = func(self, staticdata) -- called when mob (re-)actived
^ Note: staticdata is deserialized by MOB-Engine (including costum values)
get_staticdata = func(self) -- called when mob is punched (puncher can be nil)
^ must return a table to save mob data (serialization is done by MOB-Engine)
^ e.g:
return {
costum_mob_data = self.my_value,
}
}
#AnimationDef {
start = 0, -- animation start frame
stop = 80, -- animation end frame
speed = 15, -- animation speed
loop = true, -- if false, animation if just played once <optional>
duration = 1 -- only supported in "death"-Animation, sets time the animation needs until mob is removed <optional>
}
#SoundDef {
name = <name>, -- sound name as string; see Minetest documentation
gain = 1.0, -- sound gain; see Minetest documentation
distance = <number>, -- hear distance in blocks/nodes <optional>
time_min = <time> -- minimum time in seconds between sounds (random only) <optional>
time_max = <time> -- maximum time in seconds between sounds (random only) <optional>
}

View File

@ -1,121 +1,36 @@
Minetest mod "Creatures"
=======================
by BlockMen (c) 2014 - 2015
Creatures MOB-Engine
====================
Copyright (c) 2015 BlockMen <blockmen2015@gmail.com>
Version: 1.1.5 Beta
About
~~~~~
This mod adds 2 hostile and 1 friendly mob to Minetest, so far zombies, ghosts and sheeps.
Zombies can spawn to every day-time in the world as long there is not to much light.
So you will find some in caves, dark forests and ofc a lot at night. If they notice you they will attack.
Zombies have 20 HP (like players) and drop rotten flesh randomly.
Ghosts only spawn at night-time. Also they don't spawn underground and are a bit more rare than zombies.
They are flying in the world and attack you aswell if they notice you.
Ghosts have 12 HP and don't drop any items atm (might be changed if i have an idea what they could drop).
Sheeps spawn only at day-time and are friendly mobs. They remain around 5 minutes in the world unless there
are other sheeps around, then there is no fixed limit. If there is grass (dirt with grass) they eat the grass
and get new wool that way. They will follow you if you have Wheat in your hand.
Sheeps have 8 HP and drop 1-2 wool when punched or 2 wool when using shears. They need to eat grass until they can produce new wool.
They can't harm you in your house (in case there is no door open). If it becomes day both mobs will take damage
by the sunlight, so they will die after a while.
Version: 2.0 Beta
Notice: Weapons and tools get damaged when hitting a zombie or ghost. The wearout is calculated on the damage amout
of the tools/weapons. The more damage they can do that longer they can be used.
Example:
- Diamond Sword: 1500 uses
- Wooden Sword: 30 uses
Crafting Shears:
----------- steel ingot
steel ingot stick
A Mod(pack) for Minetest that provides a MOB-Engine and adds several creatures to the game.
Currently included: Ghosts, Zombies, Sheep.
License of source code:
-----------------------
(c) Copyright BlockMen (2014-2015), modified zlib-License
License:
~~~~~~~~
Code(if not stated differently):
(c) Copyright 2015 BlockMen; modified zlib-License
see "LICENSE.txt" for details.
Media(if not stated differently):
(c) Copyright (2014-2015) BlockMen; CC-BY-SA 3.0
see each MOB-Module for detailed informations.
Licenses of sounds
------------------
following sounds are created by Under7dude (freesound.org)
- creatures_zombie.1.ogg, CC0
- creatures_zombie.2.ogg, CC0
- creatures_zombie.3.ogg, CC0
- creatures_zombie_death.ogg, CC0
following sounds are created by confusion_music (freesound.org)
- creatures_sheep.1.ogg, CC-BY 3.0
- creatures_sheep.2.ogg, CC-BY 3.0
following sound is created by Yuval (freesound.org)
- creatures_sheep.3.ogg, CC-BY 3.0
following sound is created by SmartWentCody (freesound.org)
- creatures_shears.ogg, CC-BY 3.0
Github:
~~~~~~~
https://github.com/BlockMen/creatures_mob_engine
License of all other Media (textures, meshes/models, sounds):
-------------------------------------------------------------
(c) Copyright BlockMen (2014-2015), CC BY-SA 3.0
Forum:
~~~~~~
https://forum.minetest.net/viewtopic.php?id=8638
Changelog:
----------
# 1.0.1
- fixed incompatibility with pyramids mod
# 1.1
- new mob: sheep
- fixed crash caused by unknown node
- fixed spawning, added spawn limit
- fixed weapon & tool damage
- tweaked and restructured code
- ghosts only spawn on grass and desert-sand blocks
- ghosts have now 12 HP (instead 15 HP)
- zombies don't jump over fences anymore
# 1.1.1
- Zombies have also a maximum lifetime now
- Use line_of_sight() befor attacking
- Fix different crashes
- Experimental spawning control
# 1.1.2
- Sheeps follow you when holding wheat
- Added Shears (rightclick)
- Items are only droped (without player action) with builtin_mod
# 1.1.3
- Fixed crash caused by spawning control
- Prevent the mobs flood once and for all
- Drop items aswell when settings define max lifetime
# 1.1.4
- Fixed mobs flood (yes, really!)
- Sheep drop raw flesh on death
- Sheep are not stuck that often in valleys; spawning less
- Improved jumping of zombies and sheep
# 1.1.5
- Fixed global variable (by vitaminx)
- Added description.txt and screenshot (by ExcaliburZero)
- Fixed sounds (zombie and sheep)
- Using [colorize modifier instead of texture overlays
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
~~~~~~~~~~
see Changelog.txt

20
creatures/LICENSE.txt Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2015 BlockMen <blockmen2015@gmail.com>
This software is provided 'as-is', without any express or implied warranty. In no
event will the authors be held liable for any damages arising from the use of
this software.
Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to the
following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation is required.
2. Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

121
creatures/README.txt Normal file
View File

@ -0,0 +1,121 @@
Minetest mod "Creatures"
=======================
by BlockMen (c) 2014 - 2015
Version: 1.1.5 Beta
About
~~~~~
This mod adds 2 hostile and 1 friendly mob to Minetest, so far zombies, ghosts and sheeps.
Zombies can spawn to every day-time in the world as long there is not to much light.
So you will find some in caves, dark forests and ofc a lot at night. If they notice you they will attack.
Zombies have 20 HP (like players) and drop rotten flesh randomly.
Ghosts only spawn at night-time. Also they don't spawn underground and are a bit more rare than zombies.
They are flying in the world and attack you aswell if they notice you.
Ghosts have 12 HP and don't drop any items atm (might be changed if i have an idea what they could drop).
Sheeps spawn only at day-time and are friendly mobs. They remain around 5 minutes in the world unless there
are other sheeps around, then there is no fixed limit. If there is grass (dirt with grass) they eat the grass
and get new wool that way. They will follow you if you have Wheat in your hand.
Sheeps have 8 HP and drop 1-2 wool when punched or 2 wool when using shears. They need to eat grass until they can produce new wool.
They can't harm you in your house (in case there is no door open). If it becomes day both mobs will take damage
by the sunlight, so they will die after a while.
Notice: Weapons and tools get damaged when hitting a zombie or ghost. The wearout is calculated on the damage amout
of the tools/weapons. The more damage they can do that longer they can be used.
Example:
- Diamond Sword: 1500 uses
- Wooden Sword: 30 uses
Crafting Shears:
----------- steel ingot
steel ingot stick
License of source code:
-----------------------
(c) Copyright BlockMen (2014-2015), modified zlib-License
see "LICENSE.txt" for details.
Licenses of sounds
------------------
following sounds are created by Under7dude (freesound.org)
- creatures_zombie.1.ogg, CC0
- creatures_zombie.2.ogg, CC0
- creatures_zombie.3.ogg, CC0
- creatures_zombie_death.ogg, CC0
following sounds are created by confusion_music (freesound.org)
- creatures_sheep.1.ogg, CC-BY 3.0
- creatures_sheep.2.ogg, CC-BY 3.0
following sound is created by Yuval (freesound.org)
- creatures_sheep.3.ogg, CC-BY 3.0
following sound is created by SmartWentCody (freesound.org)
- creatures_shears.ogg, CC-BY 3.0
License of all other Media (textures, meshes/models, sounds):
-------------------------------------------------------------
(c) Copyright BlockMen (2014-2015), CC BY-SA 3.0
Changelog:
----------
# 1.0.1
- fixed incompatibility with pyramids mod
# 1.1
- new mob: sheep
- fixed crash caused by unknown node
- fixed spawning, added spawn limit
- fixed weapon & tool damage
- tweaked and restructured code
- ghosts only spawn on grass and desert-sand blocks
- ghosts have now 12 HP (instead 15 HP)
- zombies don't jump over fences anymore
# 1.1.1
- Zombies have also a maximum lifetime now
- Use line_of_sight() befor attacking
- Fix different crashes
- Experimental spawning control
# 1.1.2
- Sheeps follow you when holding wheat
- Added Shears (rightclick)
- Items are only droped (without player action) with builtin_mod
# 1.1.3
- Fixed crash caused by spawning control
- Prevent the mobs flood once and for all
- Drop items aswell when settings define max lifetime
# 1.1.4
- Fixed mobs flood (yes, really!)
- Sheep drop raw flesh on death
- Sheep are not stuck that often in valleys; spawning less
- Improved jumping of zombies and sheep
# 1.1.5
- Fixed global variable (by vitaminx)
- Added description.txt and screenshot (by ExcaliburZero)
- Fixed sounds (zombie and sheep)
- Using [colorize modifier instead of texture overlays
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

148
creatures/common.lua Normal file
View File

@ -0,0 +1,148 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015 BlockMen <blockmen2015@gmail.com>
--
-- common.lua
--
-- This software is provided 'as-is', without any express or implied warranty. In no
-- event will the authors be held liable for any damages arising from the use of
-- this software.
--
-- Permission is granted to anyone to use this software for any purpose, including
-- commercial applications, and to alter it and redistribute it freely, subject to the
-- following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software in a
-- product, an acknowledgment in the product documentation is required.
-- 2. Altered source versions must be plainly marked as such, and must not
-- be misrepresented as being the original software.
-- 3. This notice may not be removed or altered from any source distribution.
--
-- constants
nullVec = {x = 0, y = 0, z = 0}
DEGTORAD = math.pi / 180.0
-- common functions
function creatures.rnd(table, errval)
if not errval then
errval = false
end
local res = 1000000000
local rn = math.random(0, res - 1)
local retval = nil
local psum = 0
for s,w in pairs(table) do
psum = psum + ((tonumber(w) or w.chance or 0) * res)
if psum > rn then
retval = s
break
end
end
return retval
end
function throw_error(msg)
core.log("error", "#Creatures: ERROR: " .. msg)
end
function creatures.compare_pos(pos1, pos2)
if not pos1 or not pos2 then
return
end
if pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z then
return false
end
return true
end
function creatures.findTarget(search_obj, pos, radius, search_type, ignore_mob, xray, no_count)
local player_near = false
local mobs = {}
for _,obj in ipairs(core.get_objects_inside_radius(pos, radius)) do
if obj ~= search_obj then
if xray or core.line_of_sight(pos, obj:getpos()) == true then
local is_player = obj:is_player()
if is_player then
player_near = true
if no_count == true then
return {}, true
end
end
local entity = obj:get_luaentity()
local isItem = (entity and entity.name == "__builtin:item") or false
local ignore = (entity and entity.mob_name == ignore_mob and search_type ~= "mates") or false
if search_type == "all" then
if not isItem and not ignore then
table.insert(mobs, obj)
end
elseif search_type == "hostile" then
if not ignore and (entity and entity.hostile == true) or is_player then
table.insert(mobs, obj)
end
elseif search_type == "nonhostile" then
if entity and not entity.hostile and not isItem and not ignore then
table.insert(mobs, obj)
end
elseif search_type == "player" then
if is_player then
table.insert(mobs, obj)
end
elseif search_type == "mate" then
if not isItem and (entity and entity.mob_name == ignore_mob) then
table.insert(mobs, obj)
end
end
end
end --for
end
return mobs,player_near
end
function creatures.dropItems(pos, drops)
if not pos or not drops then
return
end
-- convert drops table
local tab = {}
for _,elem in pairs(drops) do
local name = tostring(elem[1])
local v = elem[2]
local chance = elem.chance
local amount = ""
-- check if drops depending on defined chance
if name and chance then
local ct = {}
ct[name] = chance
ct["_fake"] = 1 - chance
local res = creatures.rnd(ct)
if res == "_fake" then
name = nil
end
end
-- get amount
if name and v then
if type(v) == "table" then
amount = math.random(v.min or 1, v.max or 1) or 1
elseif type(v) == "number" then
amount = v
end
if amount > 0 then
amount = " " .. amount
end
end
if name then
local obj = core.add_item(pos, name .. amount)
if not obj then
throw_error("Could not drop item '" .. name .. amount .. "'")
end
end
end
end

View File

@ -0,0 +1 @@
A Mod(pack) for Minetest that provides a MOB-Engine and adds several creatures to the game.

644
creatures/functions.lua Normal file
View File

@ -0,0 +1,644 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015 BlockMen <blockmen2015@gmail.com>
--
-- functions.lua
--
-- This software is provided 'as-is', without any express or implied warranty. In no
-- event will the authors be held liable for any damages arising from the use of
-- this software.
--
-- Permission is granted to anyone to use this software for any purpose, including
-- commercial applications, and to alter it and redistribute it freely, subject to the
-- following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software in a
-- product, an acknowledgment in the product documentation is required.
-- 2. Altered source versions must be plainly marked as such, and must not
-- be misrepresented as being the original software.
-- 3. This notice may not be removed or altered from any source distribution.
--
local function knockback(selfOrObject, dir, old_dir, strengh)
local object = selfOrObject
if selfOrObject.mob_name then
object = selfOrObject.object
end
object:set_properties({automatic_face_movement_dir = false})
object:setvelocity(vector.add(old_dir, {x = dir.x * strengh, y = 3.5, z = dir.z * strengh}))
old_dir.y = 0---10
core.after(0.4, function()
object:set_properties({automatic_face_movement_dir = -90.0})
object:setvelocity(old_dir)
selfOrObject.falltimer = nil
if selfOrObject.stunned == true then
selfOrObject.stunned = false
if selfOrObject.can_panic == true then
selfOrObject.mode = "_run"
selfOrObject.modetimer = 0
end
end
end)
end
local function on_hit(me)
core.after(0.1, function()
me:settexturemod("^[colorize:#c4000099")
end)
core.after(0.5, function()
me:settexturemod("")
end)
end
local hasMoved = creatures.compare_pos
local function getDir(pos1, pos2)
local retval
if pos1 and pos2 then
retval = {x = pos2.x - pos1.x, y = pos2.y - pos1.y, z = pos2.z - pos1.z}
end
return retval
end
local function getDistance(vec, fly_offset)
if not vec then
return -1
end
if fly_offset then
vec.y = vec.y + fly_offset
end
return math.sqrt((vec.x)^2 + (vec.y)^2 + (vec.z)^2)
end
local findTarget = creatures.findTarget
local function update_animation(obj_ref, mode, anim_defs)
local anim = nil
for mn,range in pairs(anim_defs) do
if tostring(mn) == mode then
anim = range
break
end
end
if anim and obj_ref then
obj_ref:set_animation({x = anim.start, y = anim.stop}, anim.speed, 0, anim.loop)
end
end
local function update_velocity(obj_ref, dir, speed, add)
local velo = obj_ref:getvelocity()
if not dir.y then
dir.y = velo.y/speed
end
local new_velo = {x = dir.x * speed, y = dir.y * speed or velo.y , z = dir.z * speed}
if add then
new_velo = vector.add(velo, new_velo)
end
obj_ref:setvelocity(new_velo)
end
local function getYaw(dirOrYaw)
local yaw = 360 * math.random()
if dirOrYaw and type(dirOrYaw) == "table" then
yaw = math.atan(dirOrYaw.z / dirOrYaw.x) + math.pi^2 - 2
if dirOrYaw.x > 0 then
yaw = yaw + math.pi
end
elseif dirOrYaw and type(dirOrYaw) == "number" then
-- here could be a value based on given yaw
end
return yaw
end
local dropItems = creatures.dropItems
local function killMob(me, def)
if not def then
if me then
me:remove()
end
end
local pos = me:getpos()
me:setvelocity(nullVec)
me:set_hp(0)
if def.sounds and def.sounds.on_death then
local death_snd = def.sounds.on_death
core.sound_play(death_snd.name, {pos = pos, max_hear_distance = death_snd.distance or 5, gain = death_snd.gain or 1})
end
if def.model.animations.death then
local dur = def.model.animations.death.duration or 0.5
update_animation(me, "death", def.model.animations)
core.after(dur, function()
me:remove()
end)
else
me:remove()
end
if def.drops then
if type(def.drops) == "function" then
def.drops(me:get_luaentity())
else
dropItems(pos, def.drops)
end
end
end
local function onDamage(self, hp)
local me = self.object
local def = core.registered_entities[self.mob_name]
hp = hp or me:get_hp()
if hp <= 0 then
self.stunned = true
killMob(me, def)
else
on_hit(me) -- red flashing
if def.sounds and def.sounds.on_damage then
local dmg_snd = def.sounds.on_damage
core.sound_play(dmg_snd.name, {pos = me:getpos(), max_hear_distance = dmg_snd.distance or 5, gain = dmg_snd.gain or 1})
end
end
end
local function changeHP(self, value)
local me = self.object
local hp = me:get_hp()
hp = hp + math.floor(value)
if value < 0 then
me:set_hp(hp)
onDamage(self, hp)
end
end
local function checkWielded(wielded, itemList)
for s,w in pairs(itemList) do
if w == wielded then
return true
end
end
return false
end
local tool_uses = {0, 30, 110, 150, 280, 300, 500, 1000}
local function addWearout(player, tool_def)
if not core.setting_getbool("creative_mode") then
local item = player:get_wielded_item()
if tool_def and tool_def.damage_groups and tool_def.damage_groups.fleshy then
local uses = tool_uses[tool_def.damage_groups.fleshy] or 0
if uses > 0 then
local wear = 65535/uses
item:add_wear(wear)
player:set_wielded_item(item)
end
end
end
end
local function spawnParticles(...)
end
if core.setting_getbool("creatures_enable_particles") == true then
spawnParticles = function(pos, velocity, texture_str)
local vel = vector.multiply(velocity, 0.5)
vel.y = 0
core.add_particlespawner({
amount = 8,
time = 1,
minpos = vector.add(pos, -0.7),
maxpos = vector.add(pos, 0.7),
minvel = vector.add(vel, {x = -0.1, y = -0.01, z = -0.1}),
maxvel = vector.add(vel, {x = 0.1, y = 0, z = 0.1}),
minacc = vector.new(),
maxacc = vector.new(),
minexptime = 0.8,
maxexptime = 1,
minsize = 1,
maxsize = 2.5,
texture = texture_str,
})
end
end
-- --
-- Default entity functions
-- --
creatures.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
if self.stunned == true then
return
end
local me = self.object
local mypos = me:getpos()
if puncher then
if self.hostile then
self.target = puncher
end
if time_from_last_punch >= 0.45 and self.stunned == false then
if self.has_kockback == true then
local v = me:getvelocity()
v.y = 0
if not self.can_fly then
me:setacceleration({x = 0, y = -15, z = 0})
end
knockback(self, dir, v, 5)
self.stunned = true
end
-- add wearout to weapons/tools
addWearout(puncher, tool_capabilities)
onDamage(self)
end
end
end
creatures.on_rightclick = function(self, clicker)
-- do sth
core.chat_send_all("Hey, i got clicked!")
end
creatures.on_step = function(self, dtime)
-- first get the relevant specs; exit if we don't know anything (1-3ms)
local def = core.registered_entities[self.mob_name]
if not def then
throw_error("Can't load creature-definition")
return
end
-- timer updates
self.lifetimer = self.lifetimer + dtime
self.modetimer = self.modetimer + dtime
self.soundtimer = self.soundtimer + dtime
self.yawtimer = self.yawtimer + dtime
self.nodetimer = self.nodetimer + dtime
self.followtimer = self.followtimer + dtime
if self.envtimer then
self.envtimer = self.envtimer + dtime
end
if self.falltimer then
self.falltimer = self.falltimer + dtime
end
if self.searchtimer then
self.searchtimer = self.searchtimer + dtime
end
if self.attacktimer then
self.attacktimer = self.attacktimer + dtime
end
if self.swimtimer then
self.swimtimer = self.swimtimer + dtime
end
-- main
if self.stunned == true then
return
end
if self.lifetimer > def.stats.lifetime and not (self.mode == "attack" and self.target) then
self.lifetimer = 0
if not self.tamed or (self.tamed and def.stats.dies_when_tamed) then
killMob(self.object, def)
end
end
-- localize some things
local me = self.object
local current_pos = me:getpos()
current_pos.y = current_pos.y + 0.5
local moved = hasMoved(current_pos, self.last_pos) or false
local fallen = false
-- Update pos and current node if necessary
if moved == true or not self.last_pos then
-- for falldamage
if self.has_falldamage and self.last_pos and not self.in_water then
local dist = math.abs(current_pos.y - self.last_pos.y)
if dist > 0 then
self.fall_dist = self.fall_dist - dist
if not self.falltimer then
self.falltimer = 0
end
end
end
self.last_pos = current_pos
if self.nodetimer > 0.2 then
self.nodetimer = 0
local current_node = core.get_node_or_nil(current_pos)
self.last_node = current_node
if def.stats.light then
local wtime = core.get_timeofday()
local llvl = core.get_node_light({x = current_pos.x, y = current_pos.y + 0.5, z = current_pos.z}) or 0
self.last_llvl = llvl
end
end
else
if (def.modes[self.mode].moving_speed or 0) > 0 then
update_velocity(me, nullVec, 0)
if def.modes["idle"] and not (self.mode == "attack" or self.mode == "follow") then
self.mode = "idle"
self.modetimer = 0
end
end
if self.fall_dist < 0 then
fallen = true
end
end
if fallen then
local falltime = tonumber(self.falltimer) or 0
local dist = math.abs(self.fall_dist) or 0
self.falltimer = 0
self.fall_dist = 0
fallen = false
local damage = 0
if dist > 3 and not self.in_water and falltime/dist < 0.2 then
damage = dist - 3
end
-- damage by calced value
if damage > 0 then
changeHP(self, damage * -1)
end
end
-- special mode handling
-- check distance to target
if self.target and self.followtimer > 0.6 then
self.followtimer = 0
local p2 = self.target:getpos()
local dir = getDir(current_pos, p2)
local offset
if self.can_fly then
offset = def.modes["fly"].target_offset
end
local dist = getDistance(dir, offset)
local radius
if self.hostile and def.combat then
radius = def.combat.search_radius
elseif def.modes["follow"] then
radius = def.modes["follow"].radius
end
if dist == -1 or dist > (radius or 5) then
self.target = nil
self.mode = ""
elseif dist > -1 and self.hostile and dist < def.combat.attack_radius then
-- attack
if self.attacktimer > def.combat.attack_speed then
self.attacktimer = 0
if core.line_of_sight(current_pos, p2) == true then
self.target:punch(me, 1.0, {
full_punch_interval = def.combat.attack_speed,
damage_groups = {fleshy = def.combat.attack_damage}
})
end
update_velocity(me, self.dir, 0)
end
else
if self.mode == "attack" or self.mode == "follow" then
self.dir = vector.normalize(dir)
me:setyaw(getYaw(dir))
if self.in_water then
self.dir.y = me:getvelocity().y
end
update_velocity(me, self.dir, def.modes[self.mode].moving_speed or 0)
end
end
end
-- search a target (1-2ms)
if not self.target and ((self.hostile and def.combat.search_enemy) or def.modes["follow"]) then
local timer
if self.hostile then
timer = def.combat.search_timer or 2
elseif def.modes["follow"] then
timer = def.modes["follow"].timer
end
if self.searchtimer > (timer or 4) then
self.searchtimer = 0
local targets = {}
if self.hostile then
targets = findTarget(me, current_pos, def.combat.search_radius, def.combat.search_type, def.combat.search_xray)
else
targets = findTarget(me, current_pos, def.modes["follow"].radius or 5, "player")
end
if #targets > 1 then
self.target = targets[math.random(1, #targets)]
elseif #targets == 1 then
self.target = targets[1]
end
if self.target then
if self.hostile and def.modes["attack"] then
self.mode = "attack"
else
local name = self.target:get_wielded_item():get_name()
if name and checkWielded(name, def.modes["follow"].items) == true then
self.mode = "follow"
self.modetimer = 0
else
self.target = nil
end
end
end
end
end
if self.mode == "eat" and not self.eat_node then
local nodes = def.modes[self.mode].nodes
local p = {x = current_pos.x, y = current_pos.y - 1, z = current_pos.z}
local sn = core.get_node_or_nil(p)
local eat_node
for _,name in pairs(nodes) do
if name == self.last_node.name then
eat_node = current_pos
break
elseif name == sn.name then
eat_node = p
break
end
end
if not eat_node then
self.mode = "idle"
else
self.eat_node = eat_node
end
end
-- further mode handling
-- update mode
if self.mode ~= "attack" and
(self.mode == "" or self.modetimer > (def.modes[self.mode].duration or 4)) then
self.modetimer = 0
local new_mode = creatures.rnd(def.modes) or "idle"
if new_mode == "eat" and self.in_water == true then
new_mode = "idle"
end
if self.mode == "follow" and math.random(1, 10) < 3 then
new_mode = self.mode
elseif self.mode == "follow" then
-- "lock" searching a little bit
self.searchtimer = math.random(5, 8) * -1
self.target = nil
end
self.mode = new_mode
-- change eaten node when mode changes
if self.eat_node then
local n = core.get_node_or_nil(self.eat_node)
local nnn = n.name
local def = core.registered_nodes[n.name]
local sounds
if def then
if def.drop and type(def.drop) == "string" then
nnn = def.drop
elseif not def.walkable then
nnn = "air"
end
end
if nnn and nnn ~= n.name and core.registered_nodes[nnn] then
core.set_node(self.eat_node, {name = nnn})
if not sounds then
sounds = def.sounds
end
if sounds and sounds.dug then
core.sound_play(sounds.dug, {pos = self.eat_node, max_hear_distance = 5, gain = 1})
end
end
self.eat_node = nil
end
end
-- mode has changes, do things
if self.mode ~= self.last_mode then
self.last_mode = self.mode
local moving_speed = def.modes[self.mode].moving_speed or 0
if moving_speed > 0 then
local yaw = (getYaw(me:getyaw()) + 90.0) * DEGTORAD
me:setyaw(yaw + 4.73)
self.dir = {x = math.cos(yaw), y = 0, z = math.sin(yaw)}
if self.can_fly then
if current_pos.y >= (def.modes["fly"].max_height or 50) and not self.target then
self.dir.y = -0.5
else
self.dir.y = (math.random() - 0.5) --*0.1
end
end
-- reduce speed in water
if self.in_water == true then
moving_speed = moving_speed * 0.7
end
else
self.dir = nullVec
end
update_velocity(me, self.dir, moving_speed)
update_animation(me, self.mode, def.model.animations)
end
-- update yaw
if self.mode ~= "attack" and self.mode ~= "follow" and
(def.modes[self.mode].update_yaw or 0) > 0 and
self.yawtimer > (def.modes[self.mode].update_yaw or 4) then
self.yawtimer = 0
local mod = nil
if self.mode == "_run" then
mod = me:getyaw()
end
local yaw = (getYaw(mod) + 90.0) * DEGTORAD
me:setyaw(yaw + 4.73)
local moving_speed = def.modes[self.mode].moving_speed or 0
if moving_speed > 0 then
self.dir = {x = math.cos(yaw), y = nil, z = math.sin(yaw)}
update_velocity(me, self.dir, moving_speed)
end
end
--swim
if self.can_swim and self.swimtimer > 0.8 and self.last_node then
self.swimtimer = 0
local name = self.last_node.name
if name then
if name == "default:water_source" then
self.air_cnt = 0
local vel = me:getvelocity()
update_velocity(me, {x = vel.x, y = 0.9, z = vel.z}, 1)
me:setacceleration({x = 0, y = -1.2, z = 0})
self.in_water = true
-- play swimming sounds
if def.sounds and def.sounds.swim then
local swim_snd = def.sounds.swim
core.sound_play(swim_snd.name, {pos = current_pos, gain = swim_snd.gain or 1, max_hear_distance = swim_snd.distance or 10})
end
spawnParticles(current_pos, vel, "bubble.png")
else
self.air_cnt = self.air_cnt + 1
if self.in_water == true and self.air_cnt > 5 then
self.in_water = false
if not self.can_fly then
me:setacceleration({x = 0, y = -15, z = 0})
end
end
end
end
end
-- Add damage when drowning or in lava
if self.env_damage and self.envtimer > 1 and self.last_node then
self.envtimer = 0
local name = self.last_node.name
if not self.can_swim and name == "default:water_source" then
changeHP(self, -1)
elseif self.can_burn then
if name == "fire:basic_flame" or name == "default:lava_source" then
changeHP(self, -4)
end
end
-- add damage when light is too bright or too dark
local tod = core.get_timeofday() * 24000
if self.last_llvl and self.can_burn and self.last_llvl > (def.stats.light.max or 15) and tod < 18000 then
changeHP(self, -1)
elseif self.last_llvl and self.last_llvl < (def.stats.light.min or 0) then
changeHP(self, -2)
end
end
-- Random sounds
if def.sounds and def.sounds.random[self.mode] then
local rnd_sound = def.sounds.random[self.mode]
if not self.snd_rnd_time then
self.snd_rnd_time = math.random((rnd_sound.time_min or 5), (rnd_sound.time_max or 35))
end
if rnd_sound and self.soundtimer > self.snd_rnd_time + math.random() then
self.soundtimer = 0
self.snd_rnd_time = nil
core.sound_play(rnd_sound.name, {pos = current_pos, gain = rnd_sound.gain or 1, max_hear_distance = rnd_sound.distance or 30})
end
end
end
creatures.get_staticdata = function(self)
return {
hp = self.object:get_hp(),
mode = self.mode,
tamed = self.tamed,
modetimer = self.modetimer,
lifetimer = self.lifetimer,
soundtimer = self.soundtimer,
fall_dist = self.fall_dist,
in_water = self.in_water,
}
end

33
creatures/init.lua Normal file
View File

@ -0,0 +1,33 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015 BlockMen <blockmen2015@gmail.com>
--
-- init.lua
--
-- This software is provided 'as-is', without any express or implied warranty. In no
-- event will the authors be held liable for any damages arising from the use of
-- this software.
--
-- Permission is granted to anyone to use this software for any purpose, including
-- commercial applications, and to alter it and redistribute it freely, subject to the
-- following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software in a
-- product, an acknowledgment in the product documentation is required.
-- 2. Altered source versions must be plainly marked as such, and must not
-- be misrepresented as being the original software.
-- 3. This notice may not be removed or altered from any source distribution.
--
creatures = {}
local modpath = core.get_modpath("creatures")
-- API and common functions
dofile(modpath .."/common.lua")
dofile(modpath .."/functions.lua")
dofile(modpath .."/register.lua")
-- Common items
dofile(modpath .."/items.lua")

39
creatures/items.lua Normal file
View File

@ -0,0 +1,39 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015 BlockMen <blockmen2015@gmail.com>
--
-- items.lua
--
-- This software is provided 'as-is', without any express or implied warranty. In no
-- event will the authors be held liable for any damages arising from the use of
-- this software.
--
-- Permission is granted to anyone to use this software for any purpose, including
-- commercial applications, and to alter it and redistribute it freely, subject to the
-- following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software in a
-- product, an acknowledgment in the product documentation is required.
-- 2. Altered source versions must be plainly marked as such, and must not
-- be misrepresented as being the original software.
-- 3. This notice may not be removed or altered from any source distribution.
--
core.register_craftitem("creatures:flesh", {
description = "Flesh",
inventory_image = "creatures_flesh.png",
on_use = core.item_eat(2),
})
core.register_craftitem("creatures:meat", {
description = "Cooked Meat",
inventory_image = "creatures_meat.png",
on_use = core.item_eat(4),
})
core.register_craft({
type = "cooking",
output = "creatures:meat",
recipe = "creatures:flesh",
})

574
creatures/register.lua Normal file
View File

@ -0,0 +1,574 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015 BlockMen <blockmen2015@gmail.com>
--
-- register.lua
--
-- This software is provided 'as-is', without any express or implied warranty. In no
-- event will the authors be held liable for any damages arising from the use of
-- this software.
--
-- Permission is granted to anyone to use this software for any purpose, including
-- commercial applications, and to alter it and redistribute it freely, subject to the
-- following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software in a
-- product, an acknowledgment in the product documentation is required.
-- 2. Altered source versions must be plainly marked as such, and must not
-- be misrepresented as being the original software.
-- 3. This notice may not be removed or altered from any source distribution.
--
local allow_hostile = core.setting_getbool("only_peaceful_mobs") ~= true
local function translate_def(def)
local new_def = {
physical = true,
visual = "mesh",
stepheight = 0.6, -- ensure we get over slabs/stairs
automatic_face_movement_dir = def.model.rotation or 0.0,
mesh = def.model.mesh,
textures = def.model.textures,
collisionbox = def.model.collisionbox or {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
visual_size = def.model.scale or {x = 1, y = 1},
stats = def.stats,
model = def.model,
sounds = def.sounds,
combat = def.combat,
modes = {},
drops = def.drops,
}
-- Tanslate modes to better accessable format
for mn,def in pairs(def.modes) do
local name = tostring(mn)
if name ~= "update_time" then
new_def.modes[name] = def
--if name == "attack" then new_def.modes[name].chance = 0 end
end
end
-- insert special mode "_run" which is used when in panic
if def.stats.can_panic then
if def.modes.walk then
local new = table.copy(new_def.modes["walk"])
new.chance = 0
new.duration = 3
new.moving_speed = new.moving_speed * 2
new.update_yaw = 0.7
new_def.modes["_run"] = new
local new_anim = table.copy(def.model.animations.walk)
new_anim.speed = new_anim.speed * 2 --= {start = 81, stop = 100, speed = 15},
new_def.model.animations._run = new_anim
end
end
if def.stats.can_jump and type(def.stats.can_jump) == "number" then
if def.stats.can_jump > 0 then
new_def.stepheight = def.stats.can_jump + 0.1
end
end
if not def.stats.can_fly or not def.stats.sneaky then
new_def.makes_footstep_sound = true
end
new_def.get_staticdata = function(self)
local main_tab = creatures.get_staticdata(self)
-- is own staticdata function defined? If so, merge results
if def.get_staticdata then
local data = def.get_staticdata(self)
if data and type(data) == "table" then
for s,w in pairs(data) do
main_tab[s] = w
end
end
end
-- return data serialized
return core.serialize(main_tab)
end
new_def.on_activate = function(self, staticdata)
-- Add everything we need as basis for the engine
self.mob_name = def.name
self.hp = def.stats.hp
self.hostile = def.stats.hostile
self.mode = ""
self.stunned = false -- if knocked back or hit do nothing else
self.has_kockback = def.stats.has_kockback
self.has_falldamage = def.stats.has_falldamage
self.can_swim = def.stats.can_swim
self.can_fly = def.stats.can_fly
self.can_burn = def.stats.can_burn
self.can_panic = def.stats.can_panic == true and def.modes.walk ~= nil
--self.is_tamed = nil
--self.target = nil
self.dir = {x = 0, z = 0}
--self.last_pos = nil (was nullVec)
--self.last_node = nil
--self.last_llvl = 0
self.fall_dist = 0
self.air_cnt = 0
-- Timers
self.lifetimer = 0
self.modetimer = math.random()--0
self.soundtimer = math.random()
self.nodetimer = 2 -- ensure we get the first step
self.yawtimer = math.random() * 2--0
self.followtimer = 0
if self.can_swim then
self.swimtimer = 2 -- ensure we get the first step
-- self.in_water = nil
end
if self.hostile then
self.attacktimer = 0
end
if self.hostile or def.modes.follow then
self.searchtimer = 0
end
if self.can_burn or not def.stats.can_swim or self.has_falldamage then
self.env_damage = true
self.envtimer = 0
end
-- Other things
if staticdata then
local tab = core.deserialize(staticdata)
if tab and type(tab) == "table" then
for s,w in pairs(tab) do
self[tostring(s)] = w
end
end
end
-- check we got a valid mode
if not new_def.modes[self.mode] or (new_def.modes[self.mode].chance or 0) <= 0 then
self.mode = "idle"
end
if not self.can_fly then
if not self.in_water then
self.object:setacceleration({x = 0, y = -15, z = 0})
end
end
-- check if falling and set velocity only 0 when not falling
if self.fall_dist == 0 then
self.object:setvelocity(nullVec)
end
self.object:set_hp(self.hp)
if not core.setting_getbool("enable_damage") then
self.hostile = false
end
-- call custom on_activate if defined
if def.on_activate then
def.on_activate(self, staticdata)
end
end
new_def.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
if def.on_punch and def.on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir) == true then
return
end
creatures.on_punch(self, puncher, time_from_last_punch, tool_capabilities, dir)
end
new_def.on_rightclick = function(self, clicker)
if def.on_rightclick and def.on_rightclick(self, clicker) == true then
return
end
creatures.on_rightclick(self, clicker)
end
new_def.on_step = function(self, dtime)
if def.on_step and def.on_step(self, dtime) == true then
return
end
creatures.on_step(self, dtime)
end
return new_def
end
function creatures.register_mob(def) -- returns true if sucessfull
if not def or not def.name then
throw_error("Can't register mob. No name or Definition given.")
return false
end
local mob_def = translate_def(def)
core.register_entity(":" .. def.name, mob_def)
-- register spawn
if def.spawning and not (def.stats.hostile and not allow_hostile) then
local spawn_def = def.spawning
spawn_def.mob_name = def.name
spawn_def.mob_size = def.model.collisionbox
if creatures.register_spawn(spawn_def) ~= true then
throw_error("Couldn't register spawning for '" .. def.name .. "'")
end
if spawn_def.spawn_egg then
local egg_def = def.spawning.spawn_egg
egg_def.mob_name = def.name
egg_def.box = def.model.collisionbox
creatures.register_egg(egg_def)
end
if spawn_def.spawner then
local spawner_def = def.spawning.spawner
spawner_def.mob_name = def.name
spawner_def.range = spawner_def.range or 4
spawner_def.number = spawner_def.number or 6
spawner_def.model = def.model
creatures.register_spawner(spawner_def)
end
end
return true
end
local function inRange(min_max, value)
if not value or not min_max or not min_max.min or not min_max.max then
return false
end
if (value >= min_max.min and value <= min_max.max) then
return true
end
return false
end
local function checkSpace(pos, height)
for i = 0, height do
local n = core.get_node_or_nil({x = pos.x, y = pos.y + i, z = pos.z})
if not n or n.name ~= "air" then
return false
end
end
return true
end
local time_taker = 0
local function step(tick)
core.after(tick, step, tick)
time_taker = time_taker + tick
end
step(0.5)
local function stopABMFlood()
if time_taker == 0 then
return true
end
time_taker = 0
end
local function groupSpawn(pos, mob, group, nodes, range, max_loops)
local cnt = 0
local cnt2 = 0
local nodes = core.find_nodes_in_area({x = pos.x - range, y = pos.y - range, z = pos.z - range},
{x = pos.x + range, y = pos.y, z = pos.z + range}, nodes)
local number = #nodes - 1
if max_loops and type(max_loops) == "number" then
number = max_loops
end
while cnt < group and cnt2 < number do
cnt2 = cnt2 + 1
local p = nodes[math.random(1, number)]
p.y = p.y + 1
if checkSpace(p, mob.size) == true then
cnt = cnt + 1
core.add_entity(p, mob.name)
end
end
if cnt < group then
return false
end
end
function creatures.register_spawn(spawn_def)
if not spawn_def or not spawn_def.abm_nodes then
throw_error("No valid definition for given.")
return false
end
if not spawn_def.abm_nodes.neighbors then
spawn_def.abm_nodes.neighbors = {}
end
table.insert(spawn_def.abm_nodes.neighbors, "air")
core.register_abm({
nodenames = spawn_def.abm_nodes.spawn_on,
neighbors = spawn_def.abm_nodes.neighbors,
interval = spawn_def.abm_interval or 44,
chance = spawn_def.abm_chance or 7000,
action = function(pos, node, active_object_count, active_object_count_wider)
-- prevent abm-"feature"
if stopABMFlood() then
return
end
-- if time_taker == 0 then
-- return
-- end
-- time_taker = 0
-- time check
local tod = core.get_timeofday() * 24000
if spawn_def.time_range then
local wanted_res = false
local range = table.copy(spawn_def.time_range)
if range.min > range.max and range.min <= tod then
wanted_res = true
end
if inRange(range, tod) == wanted_res then
print(spawn_def.mob_name .. " - tod" .. dump(spawn_def.time_range) .. " " .. dump(tod))
return
end
end
-- position check
if spawn_def.height_limit and not inRange(spawn_def.height_limit, pos.y) then
print("height")
return
end
-- light check
pos.y = pos.y + 1
local llvl = core.get_node_light(pos)
if spawn_def.light and not inRange(spawn_def.light, llvl) then
print("light")
return
end
-- creature count check
local max
if active_object_count_wider > (spawn_def.max_number or 1) then
local mates_num = #creatures.findTarget(nil, pos, 16, "mate", spawn_def.mob_name, true)
if (mates_num or 0) >= spawn_def.max_number then
-- print("too much")
return
else
max = spawn_def.max_number - mates_num
-- return
end
end
-- ok everything seems fine, spawn creature
local height_min = (spawn_def.mob_size[5] or 2) - (spawn_def.mob_size[2] or 0)
height_min = math.ceil(height_min)
local number = 0
if type(spawn_def.number) == "table" then
number = math.random(spawn_def.number.min, spawn_def.number.max)
else
number = spawn_def.number or 1
end
if max and number > max then
number = max
end
if number > 1 then
groupSpawn(pos, {name = spawn_def.mob_name, size = height_min}, number, spawn_def.abm_nodes.spawn_on, 5)
else
-- space check
if not checkSpace(pos, height_min) then
return
end
core.add_entity(pos, spawn_def.mob_name)
end
print("spawned " .. spawn_def.mob_name .. " " .. number .. " ToD:"..tod)
end,
})
return true
end
local function eggSpawn(itemstack, placer, pointed_thing, egg_def)
if pointed_thing.type == "node" then
local pos = pointed_thing.above
pos.y = pos.y + 0.5
local height = (egg_def.box[5] or 2) - (egg_def.box[2] or 0)
if checkSpace(pos, height) == true then
core.add_entity(pos, egg_def.mob_name)
if not core.setting_getbool("creative_mode") then
itemstack:take_item()
end
end
return itemstack
end
end
function creatures.register_egg(egg_def)
if not egg_def or not egg_def.mob_name or not egg_def.box then
throw_error("Can't register Spawn-Egg. Not enough parameters given.")
return false
end
core.register_craftitem(":" .. egg_def.mob_name .. "_spawn_egg", {
description = egg_def.description,
inventory_image = egg_def.texture,
liquids_pointable = false,
on_place = function(itemstack, placer, pointed_thing)
eggSpawn(itemstack, placer, pointed_thing, egg_def)
end,
})
return true
end
local function makeSpawnerEntiy(mob_name, model)
core.register_entity(":" .. mob_name .. "_spawner_dummy", {
hp_max = 1,
physical = false,
collide_with_objects = false,
collisionbox = nullVec,
visual = "mesh",
visual_size = {x = 0.42, y = 0.42},
mesh = model.mesh,
textures = model.textures,
makes_footstep_sound = false,
automatic_rotate = math.pi * -2.9,
mob_name = "_" .. mob_name .. "_dummy",
on_activate = function(self)
self.timer = 0
self.object:setvelocity(nullVec)
self.object:setacceleration(nullVec)
self.object:set_armor_groups({immortal = 1})
--self.object:set_bone_position("Root", nullVec, {x=45,y=0,z=0})
end,
on_step = function(self, dtime)
self.timer = self.timer + dtime
if self.timer > 30 then
self.timer = 0
local n = core.get_node_or_nil(self.object:getpos())
if n and n.name and n.name ~= mob_name .. "_spawner" then
self.object:remove()
end
end
end
})
end
local function spawnerSpawn(pos, spawner_def)
local mates = creatures.findTarget(nil, pos, spawner_def.range, "mate", spawner_def.mob_name, true) or {}
if #mates >= spawner_def.number then
return false
end
local number_max = spawner_def.number - #mates
local rh = math.floor(spawner_def.range/2)
local area = {
min = {x = pos.x - rh, y=pos.y - rh, z = pos.z - rh},
max = {x = pos.x + rh, y=pos.y + rh - spawner_def.height - 1, z = pos.z + rh}
}
local height = area.max.y - area.min.y
local cnt = 0
for i = 0, height do
if cnt >= number_max then
break
end
local p = {x = math.random(area.min.x, area.max.x), y = area.min.y + i, z = math.random(area.min.z, area.max.z)}
local n = core.get_node_or_nil(p)
if n and n.name then
local walkable = core.registered_nodes[n.name].walkable or false
p.y = p.y + 1
if walkable and checkSpace(p, spawner_def.height) == true then
cnt = cnt + 1
core.add_entity(p, spawner_def.mob_name)
end
end
end
end
local spawner_timers = {}
function creatures.register_spawner(spawner_def)
if not spawner_def or not spawner_def.mob_name or not spawner_def.model then
throw_error("Can't register Spawn-Egg. Not enough parameters given.")
return false
end
makeSpawnerEntiy(spawner_def.mob_name, spawner_def.model)
core.register_node(":" .. spawner_def.mob_name .. "_spawner", {
description = spawner_def.mob_name .. " spawner",
paramtype = "light",
tiles = {"creatures_spawner.png"},
is_ground_content = true,
drawtype = "glasslike",
groups = {cracky = 1, level = 1},
drop = "",
on_construct = function(pos)
pos.y = pos.y - 0.3
core.add_entity(pos, spawner_def.mob_name .. "_spawner_dummy")
end,
on_destruct = function(pos)
for _,obj in ipairs(core.get_objects_inside_radius(pos, 1)) do
local entity = obj:get_luaentity()
if obj and entity and entity.mob_name == "_" .. spawner_def.mob_name .. "_dummy" then
obj:remove()
end
end
end
})
local box = spawner_def.model.collisionbox
local height = (box[5] or 2) - (box[2] or 0)
spawner_def.height = height
if spawner_def.player_range and type(spawner_def.player_range) == "number" then
core.register_abm({
nodenames = {spawner_def.mob_name .. "_spawner"},
interval = 2,
chance = 1,
action = function(pos)
local id = core.pos_to_string(pos)
if not spawner_timers[id] then
spawner_timers[id] = os.time()
end
local time_from_last_call = os.time() - spawner_timers[id]
local mobs,player_near = creatures.findTarget(nil, pos, spawner_def.player_range, "player", nil, true, true)
if player_near == true and time_from_last_call > 10 and (math.random(1, 5) == 1 or (time_from_last_call ) > 27) then
spawner_timers[id] = os.time()
spawnerSpawn(pos, spawner_def)
end
end
})
else
core.register_abm({
nodenames = {spawner_def.mob_name .. "_spawner"},
interval = 10,
chance = 3,
action = function(pos)
spawnerSpawn(pos, spawner_def)
end
})
end
return true
end

View File

Before

Width:  |  Height:  |  Size: 246 KiB

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

View File

Before

Width:  |  Height:  |  Size: 453 B

After

Width:  |  Height:  |  Size: 453 B

View File

@ -1 +0,0 @@
This mod adds several creatures to Minetest, including zombies, ghosts, and sheep.

290
ghost.lua
View File

@ -1,290 +0,0 @@
local g_chillaxin_speed = 2
local g_animation_speed = 10
local g_mesh = "creatures_mob.x"
local g_texture = {"creatures_ghost.png"}
local g_hp = 12
local g_drop = ""
local g_player_radius = 14
local g_hit_radius = 1
creatures.g_ll = 7
local g_sound_normal = "creatures_ghost"
local g_sound_hit = "creatures_ghost_hit"
local g_sound_dead = "creatures_ghost_death"
creatures.g_spawn_nodes = {"default:dirt_with_grass","default:desert_sand"}
local function g_get_animations()
return {
walk_START = 168,
walk_END = 187
}
end
function g_hit(self)
local sound = g_sound_hit
if self.object:get_hp() < 1 then sound = g_sound_dead end
minetest.sound_play(sound, {pos = self.object:getpos(), max_hear_distance = 10, loop = false, gain = 0.4})
self.object:settexturemod("^[colorize:#c4000099")
self.can_punch = false
minetest.after(0.4, function()
self.can_punch = true
self.object:settexturemod("")
end)
end
function g_init_visuals(self)
prop = {
mesh = g_mesh,
textures = g_texture,
}
self.object:set_properties(prop)
end
GHOST_DEF = {
physical = true,
collisionbox = {-0.3, -0.5, -0.3, 0.3, 0.75, 0.3},
visual = "mesh",
visual_size = {x=1, y=1},
mesh = g_mesh,
textures = g_texture,
makes_footstep_sound = false,
npc_anim = 0,
timer = 0,
turn_timer = 0,
vec = 0,
yaw = 0,
yawwer = 0,
dist = 0,
state = 1,
can_punch = true,
dead = false,
jump_timer = 0,
last_pos = {x=0,y=0,z=0},
punch_timer = 0,
sound_timer = 0,
attacker = "",
attacking_timer = 0,
mob_name = "ghost"
}
GHOST_DEF.on_activate = function(self)
g_init_visuals(self)
self.anim = g_get_animations()
self.object:set_animation({x=self.anim.stand_START,y=self.anim.stand_END}, g_animation_speed, 0)
self.npc_anim = ANIM_STAND
self.object:setacceleration({x=0,y=0,z=0})
self.state = 1
self.object:set_hp(g_hp)
self.object:set_armor_groups({fleshy=130})
self.last_pos = {x=0,y=0,z=0}
self.can_punch = true
self.dead = false
self.dist = math.random(0,1)
end
GHOST_DEF.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
if not self.can_punch then return end
local my_pos = self.object:getpos()
if puncher ~= nil then
self.attacker = puncher
if time_from_last_punch >= 0.45 then
g_hit(self)
local v = self.object:getvelocity()
self.direction = {x=v.x, y=v.y, z=v.z}
self.punch_timer = 0
self.object:setvelocity({x=dir.x*g_chillaxin_speed,y=0,z=dir.z*g_chillaxin_speed})
if self.state == 1 then
self.state = 8
elseif self.state >= 2 then
self.state = 9
end
-- add wear to sword/tool
creatures.add_wear(puncher, tool_capabilities)
end
end
if self.object:get_hp() < 1 then
local obj = minetest.env:add_item(my_pos, g_drop.." "..math.random(0,3))
end
end
GHOST_DEF.on_step = function(self, dtime)
if self.dead then return end
self.timer = self.timer + 0.01
self.turn_timer = self.turn_timer + 0.01
self.jump_timer = self.jump_timer + 0.01
self.punch_timer = self.punch_timer + 0.01
self.attacking_timer = self.attacking_timer + 0.01
self.sound_timer = self.sound_timer + 0.01
local current_pos = self.object:getpos()
local current_node = minetest.env:get_node(current_pos)
if self.time_passed == nil then
self.time_passed = 0
end
-- death
if self.object:get_hp() < 1 then
self.object:set_hp(0)
self.attacker = ""
self.state = 0
self.dead = true
minetest.sound_play(g_sound_dead, {pos = current_pos, max_hear_distance = 10 , gain = 0.5})
self.object:set_animation({x=self.anim.lay_START,y=self.anim.lay_END}, g_animation_speed, 0)
minetest.after(1, function()
self.object:remove()
local obj = minetest.env:add_item(current_pos, g_drop.." "..math.random(0,3))
end)
end
-- die when in water, lava or sunlight
local wtime = minetest.env:get_timeofday()
local ll = minetest.env:get_node_light({x=current_pos.x,y=current_pos.y+1,z=current_pos.z}) or 0
local nn = nil
if current_node ~= nil then nn = current_node.name end
if nn ~= nil and nn == "default:water_source" or
nn == "default:water_flowing" or
nn == "default:lava_source" or
nn == "default:lava_flowing" or
(wtime > 0.2 and wtime < 0.805 and current_pos.y > 0 and ll > 11) then
self.sound_timer = self.sound_timer + dtime
if self.sound_timer >= 0.8 then
local damage = 5
if ll > 11 then damage = 2 end
self.sound_timer = 0
self.object:set_hp(self.object:get_hp()-damage)
g_hit(self)
end
else
self.time_passed = 0
end
-- update moving state every 1 or 2 seconds
if self.state < 3 then
if self.timer > 0.2 then
if self.attacker == "" then
self.state = math.random(1,2)
else self.state = 1 end
self.timer = 0
end
end
-- play random sound
if self.sound_timer > math.random(5,35) then
minetest.sound_play(g_sound_normal, {pos = current_pos, max_hear_distance = 10, gain = 0.6})
self.sound_timer = 0
end
-- after knocked back
if self.state >= 8 then
if self.punch_timer > 0.15 then
if self.state == 9 then
self.object:setvelocity({x=self.direction.x*g_chillaxin_speed,y=0,z=self.direction.z*g_chillaxin_speed})
self.state = 2
elseif self.state == 8 then
self.object:setvelocity({x=0,y=0,z=0})
self.state = 1
end
end
end
--STANDING
if self.state == 1 then
self.yawwer = true
self.attacker = ""
-- seach for players
for _,object in ipairs(minetest.env:get_objects_inside_radius(current_pos, g_player_radius)) do
if object:is_player() then
self.yawwer = false
NPC = current_pos
PLAYER = object:getpos()
self.vec = {x=PLAYER.x-NPC.x, y=PLAYER.y-NPC.y, z=PLAYER.z-NPC.z}
self.yaw = math.atan(self.vec.z/self.vec.x)+math.pi^2
if PLAYER.x > NPC.x then
self.yaw = self.yaw + math.pi
end
self.yaw = self.yaw - 2
self.object:setyaw(self.yaw)
self.attacker = object
end
end
if self.attacker == "" and self.turn_timer > math.random(1,4) then
self.yaw = 360 * math.random()
self.object:setyaw(self.yaw)
self.turn_timer = 0
self.direction = {x = math.sin(self.yaw)*-1, y = 0, z = math.cos(self.yaw)}
end
self.object:setvelocity({x=0,y=self.object:getvelocity().y,z=0})
if self.npc_anim ~= creatures.ANIM_STAND then
self.anim = g_get_animations()
self.object:set_animation({x=self.anim.stand_START,y=self.anim.stand_END}, g_animation_speed, 0)
self.npc_anim = creatures.ANIM_STAND
end
if self.attacker ~= "" then
self.direction = {x = math.sin(self.yaw)*-1, y = 0, z = math.cos(self.yaw)}
self.state = 2
end
end
-- WALKING
if self.state == 2 then
if self.direction ~= nil then
self.object:setvelocity({x=self.direction.x*g_chillaxin_speed,y=self.object:getvelocity().y,z=self.direction.z*g_chillaxin_speed})
end
if self.turn_timer > math.random(1,4) and not self.attacker then
self.yaw = 360 * math.random()
self.object:setyaw(self.yaw)
self.turn_timer = 0
self.direction = {x = math.sin(self.yaw)*-1, y = 0, z = math.cos(self.yaw)}
end
if self.npc_anim ~= creatures.ANIM_WALK then
self.anim = g_get_animations()
self.object:set_animation({x=self.anim.walk_START,y=self.anim.walk_END}, g_animation_speed, 0)
self.npc_anim = creatures.ANIM_WALK
end
--jump
if self.direction ~= nil and self.attacker ~= "" then
if self.jump_timer > 0.1 then
self.jump_timer = 0
local p1 = current_pos
if not p1 then return end
local me_y = math.floor(p1.y)
local p2 = self.attacker:getpos()
if not p2 then return end
local p_y = math.floor(p2.y+3-self.dist)
if me_y < p_y then
self.object:setvelocity({x=self.object:getvelocity().x,y=1*g_chillaxin_speed,z=self.object:getvelocity().z})
elseif me_y > p_y then
self.object:setvelocity({x=self.object:getvelocity().x,y=-1*g_chillaxin_speed,z=self.object:getvelocity().z})
else
self.object:setvelocity({x=self.object:getvelocity().x,y=0,z=self.object:getvelocity().z})
end
end
end
if self.attacker == "" then
self.object:setvelocity({x=self.object:getvelocity().x,y=0,z=self.object:getvelocity().z})
end
if self.attacker ~= "" and minetest.setting_getbool("enable_damage") then
local s = current_pos
local attacker_pos = self.attacker:getpos() or nil
if attacker_pos == nil then return end
local p = attacker_pos
p.y = p.y+3-self.dist
self.direction = {x = math.sin(self.yaw)*-1, y = 0, z = math.cos(self.yaw)}
if (s ~= nil and p ~= nil) then
local dist = ((p.x-s.x)^2 + (p.y-s.y)^2 + (p.z-s.z)^0.5)^2
creatures.attack(self, current_pos, attacker_pos, dist, g_hit_radius)
end
end
end
end
minetest.register_entity("creatures:ghost", GHOST_DEF)

209
init.lua
View File

@ -1,209 +0,0 @@
creatures = {}
-- Max number of mobs per mapblock
creatures.zombie_max = 3
creatures.ghost_max = 1
creatures.sheep_max = 4
creatures.ANIM_STAND = 1
creatures.ANIM_SIT = 2
creatures.ANIM_LAY = 3
creatures.ANIM_WALK = 4
creatures.ANIM_EAT = 5
creatures.ANIM_RUN = 6
-- Drop items (when not killed by player) only if items get removed
creatures.drop_on_death = false
local remove_items = minetest.setting_get("remove_items")
local remove_items_2 = minetest.setting_get("item_entity_ttl")
if minetest.get_modpath("builtin_item") ~= nil and ((remove_items ~= nil and tonumber(remove_items) > 0) or
(remove_items_2 ~= nil and tonumber(remove_items_2) > 0)) then
creatures.drop_on_death = true
end
-- spawning controls (experimental)
creatures.spawned = {}
local spawn_day = 600
local tod = 0
local absolute_mob_max = 50
local function timer(tick)
tod = tod + 1
if tod > spawn_day then
tod = 0
creatures.spawned = nil
creatures.spawned = {}
end
minetest.after(tick, timer, tick)
end
timer(1)
local tool_uses = {0, 30, 110, 150, 280, 300, 500, 1000}
-- helping functions
function creatures.spawn(pos, number, mob, limit, range, abs_max)
if not pos or not number or not mob then return end
if number < 1 then return end
if limit == nil then limit = 1 end
if range == nil then range = 10 end
if abs_max == nil then abs_max = absolute_mob_max end
local m_name = string.sub(mob,11)
local spawned = creatures.spawned[m_name]
if not spawned then spawned = 0 end
local res,mobs,player_near = creatures.find_mates(pos, m_name, range)
for i=1,number do
local x = 1/math.random(1,3)
local z = 1/math.random(1,3)
local p = {x=pos.x+x,y=pos.y,z=pos.z+z}
if mobs+i <= limit and spawned+i < abs_max then
local obj = minetest.add_entity(p, mob)
if obj then
creatures.spawned[m_name] = spawned + 1
minetest.log("action", "Spawned "..mob.." at ("..pos.x..","..pos.y..","..pos.z..")")
end
end
end
end
function creatures.add_wear(player, def)
if not minetest.setting_getbool("creative_mode") then
local item = player:get_wielded_item()
if def and def.damage_groups and def.damage_groups.fleshy then
local uses = tool_uses[def.damage_groups.fleshy] or 0
if uses > 0 then
local wear = 65535/uses
item:add_wear(wear)
player:set_wielded_item(item)
end
end
end
end
function creatures.drop(pos, items, dir)
if dir == nil then
dir = {x=1,y=1,z=1}
end
for _,item in ipairs(items) do
for i=1,item.count do
local x = 1/math.random(1,5)*dir.x--math.random(0, 6)/3 - 0.5*dir.x
local z = 1/math.random(1,5)*dir.z--math.random(0, 6)/3 - 0.5*dir.z
local p = {x=pos.x+x,y=pos.y,z=pos.z+z}
local node = minetest.get_node_or_nil(p)
if node == nil or not node.name or node.name ~= "air" then
p = pos
end
local obj = minetest.add_item(p, {name=item.name})
end
end
end
function creatures.find_mates(pos, name, radius)
local player_near = false
local mobs = 0
local res = false
for _,obj in ipairs(minetest.get_objects_inside_radius(pos, radius)) do
if obj:is_player() then
player_near = true
else
local entity = obj:get_luaentity()
if entity and entity.mob_name and entity.mob_name == name then
mobs = mobs + 1
res = true
end
end
end
return res,mobs,player_near
end
function creatures.compare_pos(pos1,pos2)
if pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z then
return true
end
return false
end
function creatures.attack(self, pos1, pos2, dist, range)
if not self then return end
if not pos1 or not pos2 then return end
if minetest.line_of_sight(pos1,pos2) ~= true then
return
end
if dist < range and self.attacking_timer > 0.6 then
self.attacker:punch(self.object, 1.0, {
full_punch_interval=1.0,
damage_groups = {fleshy=1}
})
self.attacking_timer = 0
end
end
function creatures.jump(self, pos, jump_y, timer)
if not self or not pos then return end
if self.direction ~= nil then
if self.jump_timer > timer then
self.jump_timer = 0
local p = {x=pos.x + self.direction.x,y=pos.y,z=pos.z + self.direction.z}-- pos
local n = minetest.get_node_or_nil(p)
p.y = p.y+1
local n2 = minetest.get_node_or_nil(p)
local def = nil
local def2 = nil
if n and n.name then
def = minetest.registered_items[n.name]
end
if n2 and n2.name then
def2 = minetest.registered_items[n2.name]
end
if def and def.walkable and def2 and not def2.walkable and not def.groups.fences and n.name ~= "default:fence_wood" then--
self.object:setvelocity({x=self.object:getvelocity().x*2.2,y=jump_y,z=self.object:getvelocity().z*2.2})
end
end
end
end
function creatures.follow(self, items, radius)
local current_pos = self.object:getpos()
-- seach for players
for _,object in ipairs(minetest.env:get_objects_inside_radius(current_pos, radius)) do
if object:is_player() then
local item = object:get_wielded_item()
local item_name = item:get_name()
if item and item_name and item_name ~= "" then
local quit = true
for _,food in ipairs(items) do
if food.name == item_name then
quit = false
end
end
if quit then return end
NPC = current_pos
PLAYER = object:getpos()
self.vec = {x=PLAYER.x-NPC.x, y=PLAYER.y-NPC.y, z=PLAYER.z-NPC.z}
self.yaw = math.atan(self.vec.z/self.vec.x)+math.pi^2
if PLAYER.x > NPC.x then
self.yaw = self.yaw + math.pi
end
self.yaw = self.yaw - 2
self.object:setyaw(self.yaw)
self.feeder = object
end
end
end
end
-- hostile mobs
dofile(minetest.get_modpath("creatures").."/ghost.lua")
dofile(minetest.get_modpath("creatures").."/zombie.lua")
-- friendly mobs
dofile(minetest.get_modpath("creatures").."/sheep.lua")
-- spawning
dofile(minetest.get_modpath("creatures").."/spawn.lua")
dofile(minetest.get_modpath("creatures").."/spawners.lua")
-- other stuff
dofile(minetest.get_modpath("creatures").."/items.lua")

View File

@ -1,91 +0,0 @@
-- shears
minetest.register_tool("creatures:shears", {
description = "Shears",
inventory_image = "creatures_shears.png",
})
minetest.register_craft({
output = 'creatures:shears',
recipe = {
{'','default:steel_ingot'},
{'default:steel_ingot','default:stick'},
}
})
-- drop items
minetest.register_craftitem("creatures:flesh", {
description = "Flesh",
inventory_image = "creatures_flesh.png",
on_use = minetest.item_eat(2),
})
minetest.register_craftitem("creatures:rotten_flesh", {
description = "Rotten Flesh",
inventory_image = "creatures_rotten_flesh.png",
on_use = minetest.item_eat(1),
})
-- food
minetest.register_craftitem("creatures:meat", {
description = "Cooked Meat",
inventory_image = "creatures_meat.png",
on_use = minetest.item_eat(4),
})
minetest.register_craft({
type = "cooking",
output = "creatures:meat",
recipe = "creatures:flesh",
})
-- spawn-eggs
minetest.register_craftitem("creatures:zombie_spawn_egg", {
description = "Zombie spawn-egg",
inventory_image = "creatures_egg_zombie.png",
liquids_pointable = false,
stack_max = 99,
on_place = function(itemstack, placer, pointed_thing)
if pointed_thing.type == "node" then
local p = pointed_thing.above
p.y = p.y+1
creatures.spawn(p, 1, "creatures:zombie", 1, 1)
if not minetest.setting_getbool("creative_mode") then itemstack:take_item() end
return itemstack
end
end,
})
minetest.register_craftitem("creatures:ghost_spawn_egg", {
description = "Ghost spawn-egg",
inventory_image = "creatures_egg_ghost.png",
liquids_pointable = false,
stack_max = 99,
on_place = function(itemstack, placer, pointed_thing)
if pointed_thing.type == "node" then
local p = pointed_thing.above
p.y = p.y+0.5
creatures.spawn(p, 1, "creatures:ghost", 1, 1)
if not minetest.setting_getbool("creative_mode") then itemstack:take_item() end
return itemstack
end
end,
})
minetest.register_craftitem("creatures:sheep_spawn_egg", {
description = "Sheep spawn-egg",
inventory_image = "creatures_egg_sheep.png",
liquids_pointable = false,
stack_max = 99,
on_place = function(itemstack, placer, pointed_thing)
if pointed_thing.type == "node" then
local p = pointed_thing.above
p.y = p.y+0.5
creatures.spawn(p, 1, "creatures:sheep", 1, 1)
if not minetest.setting_getbool("creative_mode") then itemstack:take_item() end
return itemstack
end
end,
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

0
modpack.txt Normal file
View File

394
sheep.lua
View File

@ -1,394 +0,0 @@
local s_chillaxin_speed = 1.5
local s_animation_speed = 15
local s_mesh = "creatures_sheep.x"
local s_texture = {"creatures_sheep.png"}
local s_hp = 8
local s_life_max = 80 --~5min
local s_drop = "wool:white"
local s_drop2 = "creatures:flesh"
local s_player_radius = 14
local s_sound_normal = "creatures_sheep"
local s_sound_hit = "creatures_sheep"
local s_sound_dead = "creatures_sheep"
local s_sound_shears = "creatures_shears"
creatures.s_spawn_nodes = {"default:dirt_with_grass"}
local function s_get_animations()
return {
stand_START = 0,
stand_END = 80,
walk_START = 81,
walk_END = 100,
eat_START = 107,
eat_END = 185
}
end
local function eat_cn(self, pos)
self.object:setvelocity({x=0,y=-20,z=0})
local p = {x=pos.x,y=pos.y-1,z=pos.z}
local n = minetest.get_node(p) or nil
if n and n.name and n.name == "default:dirt_with_grass" then
self.object:set_animation({x=self.anim.eat_START,y=self.anim.eat_END}, s_animation_speed, 0)
self.npc_anim = creatures.ANIM_EAT
self.timer = 0
minetest.after(3.5, function()
self.has_wool = true
self.state = 1
s_update_visuals(self)
minetest.set_node(p,{name="default:dirt"})
end)
end
end
function s_hit(self)
local sound = s_sound_hit
if self.object:get_hp() < 1 then
sound = s_sound_dead
end
minetest.sound_play(sound, {pos = self.object:getpos(), max_hear_distance = 10, loop = false, gain = 0.4})
s_update_visuals(self, "^[colorize:#c4000099")
self.can_punch = false
minetest.after(0.4, function()
s_update_visuals(self)
end)
end
function s_update_visuals(self, hit)
self.txture = {"creatures_sheep.png" .. (hit or "")}
if not self.has_wool then
self.txture = {"creatures_sheep_shaved.png" .. (hit or "")}
end
local prop = {
mesh = s_mesh,
textures = self.txture,
}
self.object:set_properties(prop)
end
SHEEP_DEF = {
physical = true,
collisionbox = {-0.4, -0.01, -0.6, 0.4, 0.9, 0.4},
visual = "mesh",
visual_size = {x=1, y=1},
mesh = s_mesh,
textures = s_texture,
txture = s_texture,
makes_footstep_sound = true,
npc_anim = 0,
lifetime = 0,
timer = 0,
turn_timer = 0,
vec = 0,
yaw = 0,
yawwer = 0,
has_wool = true,
state = 1,
can_punch = true,
dead = false,
jump_timer = 0,
last_pos = {x=0,y=0,z=0},
punch_timer = 0,
sound_timer = 0,
feeder = "",
mob_name = "sheep"
}
SHEEP_DEF.get_staticdata = function(self)
return minetest.serialize({
itemstring = self.itemstring,
timer = self.timer,
txture = self.txture,
has_wool = self.has_wool,
lifetime = self.lifetime,
})
end
SHEEP_DEF.on_activate = function(self, staticdata, dtime_s)
self.txture = s_texture
s_update_visuals(self)
self.anim = s_get_animations()
self.object:set_animation({x=self.anim.stand_START,y=self.anim.stand_END}, s_animation_speed, 0)
self.npc_anim = ANIM_STAND
self.object:setacceleration({x=0,y=-20,z=0})
self.object:setyaw(self.object:getyaw()+((math.random(0,90)-45)/45*math.pi))
self.lastpos = self.object:getpos()
self.state = 1
self.object:set_hp(s_hp)
self.object:set_armor_groups({fleshy=130})
self.can_punch = true
self.dead = false
self.has_wool = true
self.lifetime = 0
self.feeder = ""
if staticdata then
local tmp = minetest.deserialize(staticdata)
if tmp and tmp.timer then
self.timer = tmp.timer
end
if tmp and tmp.has_wool ~= nil then
self.has_wool = tmp.has_wool
end
if tmp and tmp.lifetime ~= nil then
self.lifetime = tmp.lifetime
end
if not self.has_wool then
s_update_visuals(self)
end
end
end
SHEEP_DEF.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
if not self.can_punch then return end
self.feeder = ""
self.timer = 0
if puncher ~= nil then
if time_from_last_punch >= 0.2 then --0.45
s_hit(self)
local v = self.object:getvelocity()
self.direction = {x=v.x, y=v.y, z=v.z}
self.punch_timer = 0
self.object:setvelocity({x=dir.x*2.5,y=5.2,z=dir.z*2.5})
self.state = 9
-- add wear to sword/tool
creatures.add_wear(puncher, tool_capabilities)
end
local my_pos = self.object:getpos()
my_pos.y = my_pos.y + 0.4
-- drop 1-2 whool when punched
if self.has_wool then
self.has_wool = false
creatures.drop(my_pos, {{name=s_drop, count=math.random(1,2)}}, dir)
end
if self.object:get_hp() < 1 then
creatures.drop(my_pos, {{name=s_drop2, count=1}}, dir)
end
end
end
SHEEP_DEF.on_rightclick = function(self, clicker)
if not clicker or not self.has_wool then
return
end
local item = clicker:get_wielded_item()
local name = item:get_name()
if item and name and name == "creatures:shears" then
local my_pos = self.object:getpos()
minetest.sound_play(s_sound_shears, {pos = my_pos, max_hear_distance = 10, gain = 1})
my_pos.y = my_pos.y + 0.4
self.has_wool = false
s_update_visuals(self)
creatures.drop(my_pos, {{name=s_drop, count=2}})
if not minetest.setting_getbool("creative_mode") then
item:add_wear(65535/100)
clicker:set_wielded_item(item)
end
end
end
SHEEP_DEF.on_step = function(self, dtime)
if self.dead then return end
if self.lifetime == nil then self.lifetime = 0 end
self.timer = self.timer + 0.01
self.lifetime = self.lifetime + 0.01
self.turn_timer = self.turn_timer + 0.01
self.jump_timer = self.jump_timer + 0.01
self.punch_timer = self.punch_timer + 0.01
self.sound_timer = self.sound_timer + 0.01
local current_pos = self.object:getpos()
local current_node = minetest.env:get_node(current_pos)
-- death
if self.object:get_hp() < 1 then
self.object:setvelocity({x=0,y=-20,z=0})
self.object:set_hp(0)
self.state = 0
self.dead = true
minetest.sound_play(s_sound_dead, {pos = current_pos, max_hear_distance = 10, gain = 0.9})
self.object:set_animation({x=self.anim.lay_START,y=self.anim.lay_END}, s_animation_speed, 0)
minetest.after(0.5, function()
if creatures.drop_on_death then
local drop = {{name=s_drop2, count=1}}
if self.has_wool then
drop[2] = {name=s_drop, count=math.random(1,2)}
end
creatures.drop(current_pos, drop, dir)
end
self.object:remove()
end)
end
-- die if old and alone
if self.lifetime > s_life_max then
if creatures.find_mates(current_pos, "sheep", 15) then
self.lifetime = 0
else
self.object:set_hp(0)
self.state = 0
self.dead = true
self.object:remove()
return
end
end
-- die when in water, lava
local wtime = minetest.env:get_timeofday()
local ll = minetest.env:get_node_light({x=current_pos.x,y=current_pos.y+1,z=current_pos.z}) or 0
local nn = nil
if current_node ~= nil then nn = current_node.name end
if nn ~= nil and nn == "default:water_source" or
nn == "default:water_flowing" or
nn == "default:lava_source" or
nn == "default:lava_flowing" then
self.sound_timer = self.sound_timer + 0.1
if self.sound_timer >= 0.8 then
local damage = 2
self.sound_timer = 0
self.object:set_hp(self.object:get_hp()-damage)
s_hit(self)
end
end
if self.state < 0 then
return
end
-- update moving state depending on current state
if self.state < 4 then
if self.timer > 4/self.state then
self.timer = 0
--local new = math.random(1,3)
--if self.state == 3 then new = 1 end
--if self.feeder == "" then new = 5 end
self.state = 5--new
--s_update_visuals(self)
end
elseif self.state == 4 and self.timer > 1.5 then
self.state = 2
self.timer = 0
elseif self.state == 5 then
local new = math.random(1,3)
if self.state == 3 then new = 1 end
if self.feeder ~= "" then new = 5 end
self.state = new
self.timer = 0
end
-- play random sound
local num = tonumber(self.lifetime/2) or 35
if num < 6 then num = 6 end
if self.sound_timer > self.timer + math.random(5, num) then
minetest.sound_play(s_sound_normal, {pos = current_pos, max_hear_distance = 10, gain = 0.7})
self.sound_timer = 0
end
-- after knocked back
if self.state >= 8 then
if self.punch_timer > 0.15 then
if self.state == 9 then
self.object:setvelocity({x=self.direction.x*s_chillaxin_speed,y=-20,z=self.direction.z*s_chillaxin_speed})
self.state = 4
self.punch_timer = 0
elseif self.state == 8 then
self.object:setvelocity({x=0,y=-20,z=0})
self.state = 1
end
self.can_punch = true
end
end
--STANDING
if self.state == 1 then
self.yawwer = true
if self.turn_timer > math.random(1,4) then
local last = self.yaw
self.yaw = last + math.random(-0.5,1)
if self.yaw > 22 or self.yaw < -17 then self.yaw = 0 end
self.object:setyaw(self.yaw)
self.turn_timer = 0
self.direction = {x = math.sin(self.yaw)*-1, y = -20, z = math.cos(self.yaw)}
end
self.object:setvelocity({x=0,y=self.object:getvelocity().y,z=0})
if self.npc_anim ~= creatures.ANIM_STAND then
self.anim = s_get_animations()
self.object:set_animation({x=self.anim.stand_START,y=self.anim.stand_END}, s_animation_speed, 0)
self.npc_anim = creatures.ANIM_STAND
end
end
-- stop walking when not moving
if self.state == 2 and creatures.compare_pos(self.object:getpos(),self.lastpos) and self.jump_timer <= 0.2 then
self.state = 1
end
-- CHECK FEEDER
if self.state == 5 then
self.feeder = ""
creatures.follow(self, {{name="farming:wheat"}}, 8)
if self.feeder ~= "" then
self.direction = {x = math.sin(self.yaw)*-1, y = -20, z = math.cos(self.yaw)}
self.state = 2
else
local new = math.random(1,3)
if self.state == 3 then new = 1 end
self.state = new
end
end
-- WALKING
if self.state == 2 or self.state == 4 then
self.lastpos = self.object:getpos()
local speed = 1
local anim = creatures.ANIM_WALK
if self.state == 4 then
speed = 2.2
anim = creatures.ANIM_RUN
end
if self.feeder ~= "" then
--use this for following weed, etc
--self.direction = {x = math.sin(self.yaw)*-1, y = -20, z = math.cos(self.yaw)}
self.state = 5
end
if self.direction ~= nil then
self.object:setvelocity({x=self.direction.x*s_chillaxin_speed*speed,y=self.object:getvelocity().y,z=self.direction.z*s_chillaxin_speed*speed})
end
if (self.turn_timer > math.random(0.8,2)) or (self.state == 4 and self.turn_timer > 0.2) then
if self.state == 2 then
local last = self.yaw
self.yaw = last + math.random(-1,0.5)
if self.yaw > 22 or self.yaw < -17 then self.yaw = 0 end
else
self.yaw = 360 * math.random()
end
self.object:setyaw(self.yaw)
self.turn_timer = 0
self.direction = {x = math.sin(self.yaw)*-1, y = -20, z = math.cos(self.yaw)}
end
if self.npc_anim ~= anim then
self.object:set_animation({x=self.anim.walk_START,y=self.anim.walk_END}, s_animation_speed*speed, 0)
self.npc_anim = anim
end
--jump
creatures.jump(self, current_pos, 7.7, 0.2)
end
-- EATING
if self.state == 3 then
self.state = -1 -- deactivate most while eating
eat_cn(self, current_pos)
end
end
minetest.register_entity("creatures:sheep", SHEEP_DEF)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

101
spawn.lua
View File

@ -1,101 +0,0 @@
local max_mobs_sum = creatures.zombie_max + creatures.ghost_max + creatures.sheep_max - 1
-- hostile mobs
if not minetest.setting_getbool("only_peaceful_mobs") then
-- zombie
minetest.register_abm({
nodenames = creatures.z_spawn_nodes,
neighbors = {"air"},
interval = 40.0,
chance = 7600,
action = function(pos, node, active_object_count, active_object_count_wider)
-- check per mapblock max (max per creature is done by .spawn())
if active_object_count_wider > max_mobs_sum then
return
end
local n = minetest.get_node_or_nil(pos)
--if n and n.name and n.name ~= "default:stone" and math.random(1,4)>3 then return end
pos.y = pos.y+1
local ll = minetest.env:get_node_light(pos)
local wtime = minetest.env:get_timeofday()
if not ll then
return
end
if ll >= creatures.z_ll then
return
end
if ll < -1 then
return
end
if minetest.env:get_node(pos).name ~= "air" then
return
end
pos.y = pos.y+1
if minetest.env:get_node(pos).name ~= "air" then
return
end
creatures.spawn(pos, 1, "creatures:zombie", 2, 20)
end})
-- ghost
minetest.register_abm({
nodenames = creatures.g_spawn_nodes,
neighbors = {"air"},
interval = 44.0,
chance = 8350,
action = function(pos, node, active_object_count, active_object_count_wider)
-- check per mapblock max (max per creature is done by .spawn())
if active_object_count_wider > max_mobs_sum then
return
end
if pos.y < 0 then return end
pos.y = pos.y+1
local ll = minetest.env:get_node_light(pos)
local wtime = minetest.env:get_timeofday()
if not ll then
return
end
if ll >= creatures.g_ll then
return
end
if ll < -1 then
return
end
if minetest.env:get_node(pos).name ~= "air" then
return
end
pos.y = pos.y+1
if minetest.env:get_node(pos).name ~= "air" then
return
end
if (wtime > 0.2 and wtime < 0.805) then
return
end
creatures.spawn(pos, 1, "creatures:ghost", 2, 35)
end})
end
-- peaceful
minetest.register_abm({
nodenames = creatures.s_spawn_nodes,
neighbors = {"air"},
interval = 55.0,
chance = 7800,
action = function(pos, node, active_object_count, active_object_count_wider)
-- check per mapblock max (max per creature is done by .spawn())
if active_object_count_wider > max_mobs_sum then
return
end
if pos.y < 0 then return end
pos.y = pos.y+1
local ll = minetest.env:get_node_light(pos) or 0
if ll < 14 then return end
local wtime = minetest.env:get_timeofday()
if minetest.env:get_node(pos).name ~= "air" then
return
end
if (wtime < 0.2 and wtime > 0.805) then
return
end
if math.random(1,10) > 8 then return end
creatures.spawn(pos, 1, "creatures:sheep", 4, 50, 30)
end})

View File

@ -1,94 +0,0 @@
function creatures.register_spawner(mob,size,offset,mesh,texture,range,max,max_ll,min_ll,day_only)
local DUMMY = {
hp_max = 1,
physical = true,
collisionbox = {0,0,0,0,0,0},
visual = "mesh",
visual_size = size,
mesh = mesh,
textures = texture,
makes_footstep_sound = false,
timer = 0,
automatic_rotate = math.pi * -2.9,
m_name = "dummy"
}
DUMMY.on_activate = function(self)
self.object:setvelocity({x=0, y=0, z=0})
self.object:setacceleration({x=0, y=0, z=0})
self.object:set_armor_groups({immortal=1})
end
DUMMY.on_step = function(self, dtime)
self.timer = self.timer + 0.01
local n = minetest.get_node_or_nil(self.object:getpos())
if self.timer > 1 then
if n and n.name and n.name ~= "creatures:"..mob.."_spawner" then
self.object:remove()
end
end
end
DUMMY.on_punch = function(self, hitter)
end
minetest.register_entity("creatures:dummy_"..mob, DUMMY)
-- node
minetest.register_node("creatures:"..mob.."_spawner", {
description = mob.." spawner",
paramtype = "light",
tiles = {"creatures_spawner.png"},
is_ground_content = true,
drawtype = "allfaces",
groups = {cracky=1,level=1},
drop = "",
on_construct = function(pos)
pos.y = pos.y + offset
minetest.env:add_entity(pos,"creatures:dummy_"..mob)
end,
on_destruct = function(pos)
for _,obj in ipairs(minetest.env:get_objects_inside_radius(pos, 1)) do
if not obj:is_player() then
if obj ~= nil and obj:get_luaentity().m_name == "dummy" then
obj:remove()
end
end
end
end
})
--abm
minetest.register_abm({
nodenames = {"creatures:"..mob.."_spawner"},
interval = 2.0,
chance = 20,
action = function(pos, node, active_object_count, active_object_count_wider)
local res,player_near = false
local mobs = 0
res,mobs,player_near = creatures.find_mates(pos, mob, range)
if player_near then
if mobs < max then
pos.x = pos.x+1
local p = minetest.find_node_near(pos, 5, {"air"})
local ll = minetest.env:get_node_light(p)
local wtime = minetest.env:get_timeofday()
if not ll then return end
if ll > max_ll then return end
if ll < min_ll then return end
if minetest.env:get_node(p).name ~= "air" then return end
p.y = p.y+1
if minetest.env:get_node(p).name ~= "air" then return end
if not day_only then
if (wtime > 0.2 and wtime < 0.805) and pos.y > 0 then return end
end
p.y = p.y-1
creatures.spawn(p, 1, "creatures:"..mob,range,max)
end
end
end })
end
-- spawner
creatures.register_spawner("zombie",{x=0.42,y=0.42},0.08,"creatures_mob.x",{"creatures_zombie.png"},17,6,7,-1,false)
creatures.register_spawner("sheep",{x=0.42,y=0.42},-0.3,"creatures_sheep.x",{"creatures_sheep.png"},17,6,18,10,true)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 718 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

View File

@ -1,340 +0,0 @@
local z_chillaxin_speed = 1.5
local z_animation_speed = 15
local z_mesh = "creatures_mob.x"
local z_texture = {"creatures_zombie.png"}
local z_hp = 20
local z_drop = "creatures:rotten_flesh"
local z_life_max = 80 --~5min
local z_player_radius = 14
local z_hit_radius = 1.4
creatures.z_ll = 7
local z_sound_normal = "creatures_zombie"
local z_sound_hit = "creatures_zombie_hit"
local z_sound_dead = "creatures_zombie_death"
creatures.z_spawn_nodes = {"default:dirt_with_grass","default:stone","default:dirt","default:desert_sand"}
creatures.z_spawner_range = 17
creatures.z_spawner_max_mobs = 6
local function z_get_animations()
return {
stand_START = 0,
stand_END = 79,
lay_START = 162,
lay_END = 166,
walk_START = 168,
walk_END = 188,
-- not used
sit_START = 81,
sit_END = 160
}
end
function z_hit(self)
local sound = z_sound_hit
if self.object:get_hp() < 1 then sound = z_sound_dead end
minetest.sound_play(sound, {pos = self.object:getpos(), max_hear_distance = 10, loop = false, gain = 0.4})
self.object:settexturemod("^[colorize:#c4000099")
self.can_punch = false
minetest.after(0.4, function()
self.can_punch = true
self.object:settexturemod("")
end)
end
function z_init_visuals(self)
prop = {
mesh = z_mesh,
textures = z_texture,
}
self.object:set_properties(prop)
end
ZOMBIE_DEF = {
physical = true,
collisionbox = {-0.25, -1, -0.3, 0.25, 0.75, 0.3},
visual = "mesh",
visual_size = {x=1, y=1},
mesh = z_mesh,
textures = z_texture,
makes_footstep_sound = true,
npc_anim = 0,
lifetime = 0,
timer = 0,
turn_timer = 0,
vec = 0,
yaw = 0,
yawwer = 0,
state = 1,
can_punch = true,
dead = false,
jump_timer = 0,
last_pos = {x=0,y=0,z=0},
punch_timer = 0,
sound_timer = 0,
attacker = "",
attacking_timer = 0,
mob_name = "zombie"
}
ZOMBIE_DEF.get_staticdata = function(self)
return minetest.serialize({
itemstring = self.itemstring,
timer = self.timer,
lifetime = self.lifetime,
})
end
ZOMBIE_DEF.on_activate = function(self, staticdata, dtime_s)
z_init_visuals(self)
self.anim = z_get_animations()
self.object:set_animation({x=self.anim.stand_START,y=self.anim.stand_END}, z_animation_speed, 0)
self.npc_anim = ANIM_STAND
self.object:setacceleration({x=0,y=-20,z=0})
self.state = 1
self.object:set_hp(z_hp)
self.object:set_armor_groups({fleshy=130})
self.last_pos = {x=0,y=0,z=0}
self.can_punch = true
self.dead = false
self.lifetime = 0
if staticdata then
local tmp = minetest.deserialize(staticdata)
if tmp and tmp.timer then
self.timer = tmp.timer
end
if tmp and tmp.lifetime ~= nil then
self.lifetime = tmp.lifetime
end
end
end
ZOMBIE_DEF.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
if not self.can_punch then return end
local my_pos = self.object:getpos()
if puncher ~= nil then
self.attacker = puncher
if time_from_last_punch >= 0.45 then
z_hit(self)
local v = self.object:getvelocity()
self.direction = {x=v.x, y=v.y, z=v.z}
self.punch_timer = 0
self.object:setvelocity({x=dir.x*z_chillaxin_speed,y=5,z=dir.z*z_chillaxin_speed})
if self.state == 1 then
self.state = 8
elseif self.state >= 2 then
self.state = 9
end
-- add wear to sword/tool
creatures.add_wear(puncher, tool_capabilities)
end
end
if self.object:get_hp() < 1 then
creatures.drop(my_pos, {{name=z_drop, count=math.random(0,2)}}, dir)
end
end
ZOMBIE_DEF.on_step = function(self, dtime)
if self.dead then return end
self.timer = self.timer + 0.01
self.lifetime = self.lifetime + 0.01
self.turn_timer = self.turn_timer + 0.01
self.jump_timer = self.jump_timer + 0.01
self.punch_timer = self.punch_timer + 0.01
self.attacking_timer = self.attacking_timer + 0.01
self.sound_timer = self.sound_timer + 0.01
local current_pos = self.object:getpos()
local current_node = minetest.env:get_node_or_nil(current_pos)
if self.time_passed == nil then
self.time_passed = 0
end
-- death
if self.object:get_hp() < 1 then
self.object:setvelocity({x=0,y=-20,z=0})
self.object:set_hp(0)
self.attacker = ""
self.state = 0
self.dead = true
minetest.sound_play(z_sound_dead, {pos = current_pos, max_hear_distance = 10, gain = 0.9})
self.object:set_animation({x=self.anim.lay_START,y=self.anim.lay_END}, z_animation_speed, 0)
minetest.after(1, function()
self.object:remove()
if self.object:get_hp() < 1 and creatures.drop_on_death then
creatures.drop(current_pos, {{name=z_drop, count=math.random(0,2)}})
end
end)
end
-- die if old
if self.lifetime > z_life_max then
self.object:set_hp(0)
self.state = 0
self.dead = true
self.object:remove()
return
end
-- die when in water, lava or sunlight
local wtime = minetest.env:get_timeofday()
local ll = minetest.env:get_node_light({x=current_pos.x,y=current_pos.y+1,z=current_pos.z}) or 0
local nn = nil
if current_node ~= nil then nn = current_node.name end
if nn ~= nil and nn == "default:water_source" or
nn == "default:water_flowing" or
nn == "default:lava_source" or
nn == "default:lava_flowing" or
(wtime > 0.2 and wtime < 0.805 and current_pos.y > 0 and ll > 11) then
self.sound_timer = self.sound_timer + dtime
if self.sound_timer >= 0.8 then
local damage = 5
if ll > 11 then damage = 2 end
self.sound_timer = 0
self.object:set_hp(self.object:get_hp()-damage)
z_hit(self)
end
else
self.time_passed = 0
end
-- update moving state every 0.5 or 1 second
if self.state < 3 then
if self.timer > 0.2 then
if self.attacker == "" then
self.state = math.random(1,2)
else self.state = 5 end
self.timer = 0
end
end
-- play random sound
if self.sound_timer > math.random(5,35) then
minetest.sound_play(z_sound_normal, {pos = current_pos, max_hear_distance = 10, gain = 0.7})
self.sound_timer = 0
end
-- after knocked back
if self.state >= 8 then
if self.punch_timer > 0.15 then
if self.state == 9 then
self.object:setvelocity({x=self.direction.x*z_chillaxin_speed,y=-20,z=self.direction.z*z_chillaxin_speed})
self.state = 2
elseif self.state == 8 then
self.object:setvelocity({x=0,y=-20,z=0})
self.state = 1
end
end
end
--STANDING
if self.state == 1 then
self.yawwer = true
self.attacker = ""
-- seach for players
for _,object in ipairs(minetest.env:get_objects_inside_radius(current_pos, z_player_radius)) do
if object:is_player() then
self.yawwer = false
NPC = current_pos
PLAYER = object:getpos()
self.vec = {x=PLAYER.x-NPC.x, y=PLAYER.y-NPC.y, z=PLAYER.z-NPC.z}
self.yaw = math.atan(self.vec.z/self.vec.x)+math.pi^2
if PLAYER.x > NPC.x then
self.yaw = self.yaw + math.pi
end
self.yaw = self.yaw - 2
self.object:setyaw(self.yaw)
self.attacker = object
end
end
if self.attacker == "" and self.turn_timer > math.random(1,4) then
self.yaw = 360 * math.random()
self.object:setyaw(self.yaw)
self.turn_timer = 0
self.direction = {x = math.sin(self.yaw)*-1, y = -20, z = math.cos(self.yaw)}
end
self.object:setvelocity({x=0,y=self.object:getvelocity().y,z=0})
if self.npc_anim ~= creatures.ANIM_STAND then
self.anim = z_get_animations()
self.object:set_animation({x=self.anim.stand_START,y=self.anim.stand_END}, z_animation_speed, 0)
self.npc_anim = creatures.ANIM_STAND
end
if self.attacker ~= "" then
self.direction = {x = math.sin(self.yaw)*-1, y = -20, z = math.cos(self.yaw)}
self.state = 2
end
end
--UPDATE DIR
if self.state == 5 then
self.yawwer = true
self.attacker = ""
-- seach for players
for _,object in ipairs(minetest.env:get_objects_inside_radius(current_pos, z_player_radius)) do
if object:is_player() then
self.yawwer = false
NPC = current_pos
PLAYER = object:getpos()
self.vec = {x=PLAYER.x-NPC.x, y=PLAYER.y-NPC.y, z=PLAYER.z-NPC.z}
self.yaw = math.atan(self.vec.z/self.vec.x)+math.pi^2
if PLAYER.x > NPC.x then
self.yaw = self.yaw + math.pi
end
self.yaw = self.yaw - 2
self.object:setyaw(self.yaw)
self.attacker = object
end
end
if self.attacker ~= "" then
self.direction = {x = math.sin(self.yaw)*-1, y = -20, z = math.cos(self.yaw)}
self.state = 2
else
self.state =1
end
end
-- WALKING
if self.state == 2 then
if self.attacker ~= "" then
self.direction = {x=math.sin(self.yaw)*-1, y=-20, z=math.cos(self.yaw)}
end
if self.direction ~= nil then
self.object:setvelocity({x=self.direction.x*z_chillaxin_speed,y=self.object:getvelocity().y,z=self.direction.z*z_chillaxin_speed})
end
if self.turn_timer > math.random(1,4) and not self.attacker then
self.yaw = 360 * math.random()
self.object:setyaw(self.yaw)
self.turn_timer = 0
self.direction = {x=math.sin(self.yaw)*-1, y=-20, z=math.cos(self.yaw)}
end
if self.npc_anim ~= creatures.ANIM_WALK then
self.npc_anim = creatures.ANIM_WALK
self.object:set_animation({x=self.anim.walk_START,y=self.anim.walk_END}, z_animation_speed, 0)
end
--jump
local p = current_pos
p.y = p.y-0.5
creatures.jump(self, p, 7.4, 0.25)
if self.attacker ~= "" and minetest.setting_getbool("enable_damage") then
local s = current_pos
local attacker_pos = self.attacker:getpos() or nil
if attacker_pos == nil then return end
local p = attacker_pos
if (s ~= nil and p ~= nil) then
local dist = ((p.x-s.x)^2 + (p.y-s.y)^2 + (p.z-s.z)^2)^0.5
creatures.attack(self, current_pos, attacker_pos, dist, z_hit_radius)
end
end
end
end
minetest.register_entity("creatures:zombie", ZOMBIE_DEF)