Add "Creatures MOB-Engine" (cme) mod.

master
AntumDeluge 2016-07-29 13:08:00 -07:00
parent bdc8da3226
commit 385df02cc5
20 changed files with 1778 additions and 0 deletions

View File

@ -22,6 +22,8 @@ The following mods are also included:
* [stairsplus][] ([zlib](mods/buildings/stairsplus/LICENSE.txt))
* crafting/
* [craftguide][] ([GPL/WTFPL](mods/crafting/craftguide/LICENSE))
* engines/
* [creatures (Creatures MOB-Engine)][cme] ([zlib/CC-BY-SA](doc/modpacks/cme/README.txt))
* farming/
* [farming_plus][] ([WTFPL](mods/farming/farming_plus/README.txt))
* lib/
@ -63,6 +65,7 @@ The following mods are also included:
[awards]: https://forum.minetest.net/viewtopic.php?t=4870
[biome_lib]: https://forum.minetest.net/viewtopic.php?f=11&t=12999
[character_creator]: https://forum.minetest.net/viewtopic.php?f=9&t=13138
[cme]: https://forum.minetest.net/viewtopic.php?t=8638
[craftguide]: https://forum.minetest.net/viewtopic.php?f=11&t=14088
[farming_plus]: https://forum.minetest.net/viewtopic.php?t=2787
[fort_spikes]: https://forum.minetest.net/viewtopic.php?t=14574

202
doc/modpacks/cme/API.txt Normal file
View File

@ -0,0 +1,202 @@
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
backface_culling = false, -- set true to enable backface culling
animations = { -- animation used if defined <optional>
idle = {#AnimationDef}, -- see #AnimationDef
... -- depends on modes (must correspond to be used);
^ supported "special modes": eat, follow, attack, death, swim, panic
},
},
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
combat = { -- specifies behavior of hostile mobs in "attack" mode
attack_damage = 1, -- how much damage deals each hit
attack_speed = 0.6, -- time in seconds between hits
attack_radius = 1.1, -- distance in blocks mob can reach to hit
search_enemy = true, -- true to search enemies to attack
search_timer = 2, -- time in seconds to search an enemy (only if none found yet)
search_radius = 12, -- radius in blocks within enemies are searched
search_type = "player", -- what enemy is being searched (see types at creatures.findTarget())
}
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
description = <desc>, -- Item description as string <optional>
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

@ -0,0 +1,20 @@
Copyright (c) 2015-2016 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.

View File

@ -0,0 +1,36 @@
Mod/Modpack Creatures
=====================
Copyright (c) 2015-2016 BlockMen <blockmen2015@gmail.com>
Version: 2.3.1
A Mod(pack) for Minetest that provides a MOB-Engine and adds several creatures to the game.
Currently included: Ghosts, Zombies, Sheep, Chicken and Oerrki.
License:
~~~~~~~~
Code(if not stated differently):
(c) Copyright 2015-2016 BlockMen; modified zlib-License
see "LICENSE.txt" for details.
Media(if not stated differently):
(c) Copyright (2014-2016) BlockMen; CC-BY-SA 3.0
see each MOB-Module for detailed informations.
Github:
~~~~~~~
https://github.com/BlockMen/cme
Forum:
~~~~~~
https://forum.minetest.net/viewtopic.php?id=8638
Changelog:
~~~~~~~~~~
see Changelog.txt

View File

@ -0,0 +1,40 @@
Version 2.1
-----------
- Added death animations
- Zombie spawners are now generated in dungeons (if they have more than 4 rooms)
- Fixed bug that hostile mobs did sometimes not attack
- Reduced sheep model size
- Tweak codestyle a bit/ added missing documentation of combat values
Version 2.2
-----------
- Added chicken
- Readded sheep spawner (by LNJ)
- Fix possible crash (reported by bbaez)
- Added descriptions to spawners
- Added option to set custom panic and swim animations
Version 2.2.1
-------------
- Fixed crash caused by not existing node
Version 2.2.2
-------------
- Eggs can be thrown to spawn chicken (rare)
- Chicken drop chicken meat and feather(s) on death
- Fixed spawn eggs being endless in singleplayer
- Fix searching for target if in panic mode
Version 2.3
-----------
- Added Oerrki
- Added fried eggs
- Fixed moveing facement being reset
- Fixed chicken model
- Fixed sneaky variable not working
- Fixed feathers being eatable
Version 2.3.1
-------------
- Added colored sheep
- Fixed Oerrki spawning times (spawns on night as intended)

View File

@ -0,0 +1,148 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015-2016 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,2 @@
default
wool

View File

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

View File

@ -0,0 +1,674 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015-2016 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.
--
-- Localizations
local rnd = math.random
local function knockback(selfOrObject, dir, old_dir, strengh)
local object = selfOrObject
if selfOrObject.mob_name then
object = selfOrObject.object
end
local current_fmd = object:get_properties().automatic_face_movement_dir or 0
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
core.after(0.4, function()
object:set_properties({automatic_face_movement_dir = current_fmd})
object:setvelocity(old_dir)
selfOrObject.falltimer = nil
if selfOrObject.stunned == true then
selfOrObject.stunned = false
if selfOrObject.can_panic == true then
selfOrObject.target = nil
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_def)
if anim_def and obj_ref then
obj_ref:set_animation({x = anim_def.start, y = anim_def.stop}, anim_def.speed, 0, anim_def.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 * rnd()
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_properties({collisionbox = 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["death"])
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 limit(value, min, max)
if value < min then
return min
end
if value > max then
return max
end
return value
end
local function calcPunchDamage(obj, actual_interval, tool_caps)
local damage = 0
if not tool_caps or not actual_interval then
return 0
end
local my_armor = obj:get_armor_groups() or {}
for group,_ in pairs(tool_caps.damage_groups) do
damage = damage + (tool_caps.damage_groups[group] or 0) * limit(actual_interval / tool_caps.full_punch_interval, 0.0, 1.0) * ((my_armor[group] or 0) / 100.0)
end
return damage or 0
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)
me:set_hp(hp)
if value < 0 then
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()
changeHP(self, calcPunchDamage(me, time_from_last_punch, tool_capabilities) * -1)
if puncher then
if self.hostile then
self.mode = "attack"
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)
end
end
end
creatures.on_rightclick = function(self, clicker)
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 modes = def.modes
local current_mode = self.mode
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 (modes[current_mode].moving_speed or 0) > 0 then
update_velocity(me, nullVec, 0)
if modes["idle"] and not (current_mode == "attack" or current_mode == "follow") then
current_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 = 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 modes["follow"] then
radius = modes["follow"].radius
end
if dist == -1 or dist > (radius or 5) then
self.target = nil
current_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 current_mode == "attack" or current_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, modes[current_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 modes["follow"]) and current_mode ~= "_run" then
local timer
if self.hostile then
timer = def.combat.search_timer or 2
elseif modes["follow"] then
timer = 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, modes["follow"].radius or 5, "player")
end
if #targets > 1 then
self.target = targets[rnd(1, #targets)]
elseif #targets == 1 then
self.target = targets[1]
end
if self.target then
if self.hostile and modes["attack"] then
current_mode = "attack"
else
local name = self.target:get_wielded_item():get_name()
if name and checkWielded(name, modes["follow"].items) == true then
current_mode = "follow"
self.modetimer = 0
else
self.target = nil
end
end
end
end
end
if current_mode == "eat" and not self.eat_node then
local nodes = modes[current_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 sn and sn.name == name then
eat_node = p
break
end
end
if not eat_node then
current_mode = "idle"
else
self.eat_node = eat_node
end
end
-- further mode handling
-- update mode
if current_mode ~= "attack" and
(current_mode == "" or self.modetimer > (modes[current_mode].duration or 4)) then
self.modetimer = 0
local new_mode = creatures.rnd(modes) or "idle"
if new_mode == "eat" and self.in_water == true then
new_mode = "idle"
end
if current_mode == "follow" and rnd(1, 10) < 3 then
new_mode = current_mode
elseif current_mode == "follow" then
-- "lock" searching a little bit
self.searchtimer = rnd(5, 8) * -1
self.target = nil
end
current_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 current_mode ~= self.last_mode then
self.last_mode = current_mode
local moving_speed = modes[current_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 >= (modes["fly"].max_height or 50) and not self.target then
self.dir.y = -0.5
else
self.dir.y = (rnd() - 0.5)
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)
local anim_def = def.model.animations[current_mode]
if self.in_water and def.model.animations["swim"] then
anim_def = def.model.animations["swim"]
end
update_animation(me, current_mode, anim_def)
end
-- update yaw
if current_mode ~= "attack" and current_mode ~= "follow" and
(modes[current_mode].update_yaw or 0) > 0 and
self.yawtimer > (modes[current_mode].update_yaw or 4) then
self.yawtimer = 0
local mod = nil
if current_mode == "_run" then
mod = me:getyaw()
end
local yaw = (getYaw(mod) + 90.0) * DEGTORAD
me:setyaw(yaw + 4.73)
local moving_speed = modes[current_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[current_mode] then
local rnd_sound = def.sounds.random[current_mode]
if not self.snd_rnd_time then
self.snd_rnd_time = rnd((rnd_sound.time_min or 5), (rnd_sound.time_max or 35))
end
if rnd_sound and self.soundtimer > self.snd_rnd_time + rnd() 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
self.mode = current_mode
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

View File

@ -0,0 +1,33 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015-2016 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")

View File

@ -0,0 +1,39 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015-2016 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",
})

View File

@ -0,0 +1,580 @@
--= Creatures MOB-Engine (cme) =--
-- Copyright (c) 2015-2016 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},
backface_culling = def.model.backface_culling or false,
collide_with_objects = def.model.collide_with_objects or true,
makes_footstep_sound = true,
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
if def.modes.panic and def.modes.panic.moving_speed then
new.moving_speed = def.modes.panic.moving_speed
end
new.update_yaw = 0.7
new_def.modes["_run"] = new
local new_anim = def.model.animations.panic
if not new_anim then
new_anim = table.copy(def.model.animations.walk)
new_anim.speed = new_anim.speed * 2
end
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 def.stats.sneaky or def.stats.can_fly then
new_def.makes_footstep_sound = false
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
-- immortal is needed to disable clientside smokepuff shit
self.object:set_armor_groups({fleshy = 100, immortal = 1})
-- 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,
catch_up = false,
action = function(pos, node, active_object_count, active_object_count_wider)
-- prevent abm-"feature"
if stopABMFlood() == true then
return
end
-- 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
return
end
end
-- position check
if spawn_def.height_limit and not inRange(spawn_def.height_limit, pos.y) then
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
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
return
else
max = spawn_def.max_number - mates_num
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
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 core.setting_getbool("creative_mode") ~= true 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 or egg_def.mob_name .. " spawn egg",
inventory_image = egg_def.texture or "creatures_spawn_egg.png",
liquids_pointable = false,
on_place = function(itemstack, placer, pointed_thing)
return 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
local llvl = core.get_node_light(p)
if not spawner_def.light or (spawner_def.light and inRange(spawner_def.light, llvl)) then
cnt = cnt + 1
core.add_entity(p, spawner_def.mob_name)
end
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.description or 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,
catch_up = false,
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

0
mods/engines/modpack.txt Normal file
View File