initial commit
This commit is contained in:
commit
833aabcd74
20
LICENSE.txt
Normal file
20
LICENSE.txt
Normal file
@ -0,0 +1,20 @@
|
||||
engine:Copyright (c) 2015-2016 BlockMen <blockmen2015@gmail.com>
|
||||
poketest:Copyright (c) 2017 MBB <MBB@webproxy.li>
|
||||
|
||||
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.
|
36
README.txt
Normal file
36
README.txt
Normal 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
|
20
creatures/LICENSE.txt
Normal file
20
creatures/LICENSE.txt
Normal 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.
|
26
creatures/README.txt
Normal file
26
creatures/README.txt
Normal file
@ -0,0 +1,26 @@
|
||||
Creatures MOB-Engine
|
||||
====================
|
||||
Copyright (c) 2015-2016 BlockMen <blockmen2015@gmail.com>
|
||||
|
||||
Version: 2.0.2
|
||||
|
||||
|
||||
This mod provides an engine, that handles the base function for MOB in Minetest.
|
||||
It offers an easy way to register MOB and allows to custom handling for the needs
|
||||
of each mob. This engine aims to be a solid base, that has a good balance between
|
||||
performance and functionality.
|
||||
See API.txt for more informations on how to use this engine for mobs.
|
||||
|
||||
|
||||
License:
|
||||
~~~~~~~~
|
||||
Code:
|
||||
(c) Copyright 2015-2016 BlockMen; modified zlib-License
|
||||
see "LICENSE.txt" for details.
|
||||
|
||||
Media(textures and other media):
|
||||
(c) Copyright (2014-2016) BlockMen; CC-BY-SA 3.0
|
||||
|
||||
Github:
|
||||
~~~~~~~
|
||||
https://github.com/BlockMen/cme/creatures
|
148
creatures/common.lua
Normal file
148
creatures/common.lua
Normal 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
|
2
creatures/depends.txt
Normal file
2
creatures/depends.txt
Normal file
@ -0,0 +1,2 @@
|
||||
default
|
||||
wool
|
1
creatures/description.txt
Normal file
1
creatures/description.txt
Normal file
@ -0,0 +1 @@
|
||||
A Mod(pack) for Minetest that provides a MOB-Engine and adds several creatures to the game.
|
674
creatures/functions.lua
Normal file
674
creatures/functions.lua
Normal 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
|
33
creatures/init.lua
Normal file
33
creatures/init.lua
Normal 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")
|
39
creatures/items.lua
Normal file
39
creatures/items.lua
Normal 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",
|
||||
})
|
580
creatures/register.lua
Normal file
580
creatures/register.lua
Normal 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
|
BIN
creatures/sounds/creatures_splash.1.ogg
Normal file
BIN
creatures/sounds/creatures_splash.1.ogg
Normal file
Binary file not shown.
BIN
creatures/sounds/creatures_splash.2.ogg
Normal file
BIN
creatures/sounds/creatures_splash.2.ogg
Normal file
Binary file not shown.
BIN
creatures/textures/creatures_flesh.png
Normal file
BIN
creatures/textures/creatures_flesh.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 441 B |
BIN
creatures/textures/creatures_meat.png
Normal file
BIN
creatures/textures/creatures_meat.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 478 B |
BIN
creatures/textures/creatures_spawn_egg.png
Normal file
BIN
creatures/textures/creatures_spawn_egg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 686 B |
BIN
creatures/textures/creatures_spawner.png
Normal file
BIN
creatures/textures/creatures_spawner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 453 B |
0
modpack.txt
Normal file
0
modpack.txt
Normal file
20
pikachu/LICENSE.txt
Normal file
20
pikachu/LICENSE.txt
Normal 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.
|
31
pikachu/README.txt
Normal file
31
pikachu/README.txt
Normal file
@ -0,0 +1,31 @@
|
||||
Chicken for Creatures MOB-Engine
|
||||
================================
|
||||
Copyright (c) 2015-2016 BlockMen <blockmen2015@gmail.com>
|
||||
|
||||
Version: 2.2
|
||||
|
||||
|
||||
Adds chicken to Minetest (requires Creatures MOB-Engine).
|
||||
Chicken spawn on dirt and grass blocks, have 5 HP and are friendly. When killed or dying
|
||||
they drop meat, which can be eaten or cooked and probably some feathers. Also they drop
|
||||
randomly eggs, which can be thrown to spawn new chicken or fried and eaten.
|
||||
|
||||
|
||||
License:
|
||||
~~~~~~~~
|
||||
Code:
|
||||
(c) Copyright 2015-2016 BlockMen; modified zlib-License
|
||||
see "LICENSE.txt" for details.
|
||||
|
||||
Media(textures and meshes/models):
|
||||
Gamit(WTFPL):
|
||||
creatures_egg.png
|
||||
everything else:
|
||||
(c) Copyright (2014-2016) BlockMen; CC-BY-SA 3.0
|
||||
|
||||
Sounds:
|
||||
- creatures_chicken*.ogg, dobroide(https://freesound.org/people/dobroide) CC BY 3.0
|
||||
|
||||
Github:
|
||||
~~~~~~~
|
||||
https://github.com/BlockMen/cme/chicken
|
2
pikachu/depends.txt
Normal file
2
pikachu/depends.txt
Normal file
@ -0,0 +1,2 @@
|
||||
default
|
||||
creatures
|
105
pikachu/init.lua
Normal file
105
pikachu/init.lua
Normal file
@ -0,0 +1,105 @@
|
||||
--= Pikachu for poketesr (cme) =--
|
||||
--engine: Copyright (c) 2015-2016 BlockMen <blockmen2015@gmail.com>
|
||||
--poketest: Copyright (c) 2017 MBB <MBB@webproxy.li>
|
||||
-- init.lua
|
||||
--
|
||||
--POKETEST USES BLOCKMEN´S CREATURES MOB ENGINE
|
||||
--
|
||||
--ENGINE:
|
||||
--
|
||||
-- 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 def = {
|
||||
-- general
|
||||
name = "poketest:pikachu",
|
||||
stats = {
|
||||
hp = 5,
|
||||
lifetime = 300, -- 5 Minutes
|
||||
can_jump = 1,
|
||||
can_swim = true,
|
||||
can_burn = true,
|
||||
can_panic = true,
|
||||
has_kockback = true,
|
||||
sneaky = true,
|
||||
},
|
||||
|
||||
|
||||
modes = {
|
||||
idle = {chance = 0.25, duration = 5, update_yaw = 3},
|
||||
idle2 = {chance = 0.69, duration = 0.8},
|
||||
pick = {chance = 0.2, duration = 2},
|
||||
walk = {chance = 0.2, duration = 5.5, moving_speed = 0.7, update_yaw = 2},
|
||||
panic = {moving_speed = 2.1},
|
||||
lay_egg = {chance = 0.01, duration = 1},
|
||||
},
|
||||
|
||||
model = {
|
||||
mesh = "poketest_pikachu.b3d",
|
||||
textures = {"poketest_pikachu.png"},
|
||||
collisionbox = {-0.25, -0.01, -0.3, 0.25, 0.45, 0.3},
|
||||
rotation = 90.0,
|
||||
|
||||
collide_with_objects = false,
|
||||
animations = {
|
||||
idle = {start = 0, stop = 1, speed = 10},
|
||||
idle2 = {start = 40, stop = 50, speed = 50},
|
||||
pick = {start = 88, stop = 134, speed = 50},
|
||||
walk = {start = 4, stop = 36, speed = 50},
|
||||
-- special modes
|
||||
swim = {start = 51, stop = 87, speed = 40},
|
||||
panic = {start = 51, stop = 87, speed = 55},
|
||||
death = {start = 135, stop = 160, speed = 28, loop = false, duration = 2.12},
|
||||
},
|
||||
},
|
||||
|
||||
sounds = {
|
||||
on_damage = {name = "poketest_pikachu_hit", gain = 0.5, distance = 10},
|
||||
on_death = {name = "poketest_pikachu_hit", gain = 0.5, distance = 10},
|
||||
swim = {name = "creatures_splash", gain = 1.0, distance = 10},
|
||||
random = {
|
||||
idle = {name = "poketest_pikachu", gain = 0.9, distance = 12, time_min = 8, time_max = 50},
|
||||
},
|
||||
},
|
||||
|
||||
spawning = {
|
||||
abm_nodes = {
|
||||
spawn_on = {"default:dirt_with_grass", "default:dirt"},
|
||||
},
|
||||
abm_interval = 55,
|
||||
abm_chance = 7800,
|
||||
max_number = 1,
|
||||
number = 1,
|
||||
light = {min = 8, max = 15},
|
||||
height_limit = {min = 0, max = 150},
|
||||
|
||||
spawn_egg = {
|
||||
description = "Pikachu Spawn-Egg",
|
||||
texture = "poketest_egg_pikachu.png",
|
||||
},
|
||||
},
|
||||
|
||||
drops = {
|
||||
{"default:meat"},
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
creatures.register_mob(def)
|
BIN
pikachu/models/poketest_pikachu.b3d
Normal file
BIN
pikachu/models/poketest_pikachu.b3d
Normal file
Binary file not shown.
BIN
pikachu/sounds/creatures_chicken.1.ogg
Normal file
BIN
pikachu/sounds/creatures_chicken.1.ogg
Normal file
Binary file not shown.
BIN
pikachu/sounds/creatures_chicken.2.ogg
Normal file
BIN
pikachu/sounds/creatures_chicken.2.ogg
Normal file
Binary file not shown.
BIN
pikachu/sounds/creatures_chicken.3.ogg
Normal file
BIN
pikachu/sounds/creatures_chicken.3.ogg
Normal file
Binary file not shown.
BIN
pikachu/sounds/creatures_chicken_hit.1.ogg
Normal file
BIN
pikachu/sounds/creatures_chicken_hit.1.ogg
Normal file
Binary file not shown.
BIN
pikachu/sounds/creatures_chicken_hit.2.ogg
Normal file
BIN
pikachu/sounds/creatures_chicken_hit.2.ogg
Normal file
Binary file not shown.
BIN
pikachu/textures/poketest_egg_pikachu.png
Normal file
BIN
pikachu/textures/poketest_egg_pikachu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 796 B |
BIN
pikachu/textures/poketest_pikachu.png
Normal file
BIN
pikachu/textures/poketest_pikachu.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 242 KiB |
Loading…
x
Reference in New Issue
Block a user