ghost added

master
Alexsandro Percy 2022-01-02 16:43:23 -03:00
parent e0c15bd050
commit 2acb87aa5c
12 changed files with 333 additions and 86 deletions

304
init.lua
View File

@ -147,6 +147,59 @@ local function zombie_brain(self)
end
end
local function ghost_brain(self)
-- vitals should be checked every step
if mobkit.timer(self,1) then lava_dmg(self,6) end
if self.hp <= 0 then
mobkit.clear_queue_high(self) -- cease all activity
mobkit.hq_die(self) -- kick the bucket
-- workaround for models bottom y being -1. Makes them blink white sometimes, why?
local props = self.object:get_properties()
props.collisionbox[2] = props.collisionbox[1]
self.object:set_properties({collisionbox=props.collisionbox})
return
end
if mobkit.timer(self,1) then -- decision making needn't happen every engine step
local prty = mobkit.get_queue_priority(self)
if prty < 50 and self.isinliquid then
mobkit.hq_liquid_recovery(self,50)
return
end
local pos=self.object:get_pos()
if prty < 20 then
local plyr=mobkit.get_nearby_player(self)
if plyr then
local pos2 = plyr:get_pos()
if prty < 5 then -- ghost not alert
if vector.distance(pos,pos2) < self.view_range/2 and
(not mobkit.is_there_yet2d(pos,minetest.yaw_to_dir(self.object:get_yaw()),pos2) or
vector.length(plyr:get_player_velocity()) > 3) then
mobkit.make_sound(self,'misc')
mobkit.hq_hunt(self,20,plyr)
if random()<=0.5 then alert(pos) end
end
else
if vector.distance(pos,pos2) < self.view_range then
mobkit.make_sound(self,'misc')
mobkit.hq_hunt(self,20,plyr)
if random()<=0.5 then alert(pos) end
end
end
end
end
if mobkit.is_queue_empty_high(self) then
zombiestrd.hq_roam(self,0)
end
end
end
local function shark_brain(self)
if mobkit.timer(self,1) then lava_dmg(self,6) end
mobkit.vitals(self)
@ -210,12 +263,19 @@ minetest.register_entity("zombiestrd:target", {
end,
})
local function check_is_inside_area(pos)
local function locate( table, value )
for i = 1, #table do
if table[i] == value then return true end
end
return false
end
local function check_is_inside_area(pos, table)
if areas then
local areasAtPos = areas:getAreasAtPos(pos)
for id, area in pairs(areasAtPos) do
--minetest.chat_send_all(dump(area.name))
if area.name == "cemetery" or area.name == "Cemetery" or area.name == "monsters" or area.name == "zbd" then
if locate( table, area.name ) then
return true
end
end
@ -223,6 +283,72 @@ local function check_is_inside_area(pos)
return false
end
local function spawn_monsters(pos, yaw, chance, distance_multiplier, monster_name, areas)
-- is the player inside the area?
if check_is_inside_area(pos, areas) then
local dir = vector.multiply(minetest.yaw_to_dir(yaw),distance_multiplier)
local pos2 = vector.add(pos,dir)
--minetest.add_entity(pos2, "zombiestrd:target") --debug target
pos2.y=pos2.y-5
local height, liquidflag = mobkit.get_terrain_height(pos2,32)
if height == nil then height = 0 end
local position_at_terrain_height = {x=pos2.x,y=height-0.01,z=pos2.z}
--force the spawn area be inside the area
local is_zombie_spawn_area = check_is_inside_area(position_at_terrain_height, areas)
if is_zombie_spawn_area then
--if height and height >= 0 and
if mobkit.nodeatpos(position_at_terrain_height).is_ground_content then
local objs = minetest.get_objects_inside_radius(pos,abr*distance_multiplier+5)
local wcnt=0
local dcnt=0
local mobname = monster_name --'zombiestrd:zombie' --myTable[ math.random( #myTable ) ]
if liquidflag then -- sharks
--[[
local spnode = mobkit.nodeatpos({x=pos2.x,y=height+0.01,z=pos2.z})
local spnode2 = mobkit.nodeatpos({x=pos2.x,y=height+1.01,z=pos2.z}) -- node above to make sure won't spawn in shallows
nodename_water = nodename_water or minetest.registered_aliases.mapgen_water_source
if spnode and spnode2 and spnode.name == nodename_water and spnode2.name == nodename_water then
for _,obj in ipairs(objs) do
if not obj:is_player() then
local entity = obj:get_luaentity()
if entity and entity.name=='zombiestrd:shark' then return end
end
end
mobname = 'zombiestrd:shark'
else
return false
end
]]--
return false
elseif height >= 0 then --zombies
for _,obj in ipairs(objs) do -- count mobs in abrange
if not obj:is_player() then
local entity = obj:get_luaentity()
if entity and entity.name:find('zombiestrd:') then
chance=chance + (1-chance)*spawn_reduction -- chance reduced for every mob in range
end
end
end
end
if chance < random() then
pos2.y = height+1.01
objs = minetest.get_objects_inside_radius(pos2,abr*distance_multiplier-2)
--[[for _,obj in ipairs(objs) do -- do not spawn if another player around
if obj:is_player() then return end
end]]--
local obj=minetest.add_entity(pos2,mobname) -- ok spawn it already damnit
return true
end
end
end --monsters_spwan_area
end
return false
end
-- spawning is too specific to be included in the api, this is an example.
-- a modder will want to refer to specific names according to games/mods they're using
-- in order for mobs not to spawn on treetops, certain biomes etc.
@ -245,66 +371,15 @@ local function spawnstep(dtime)
local distance_multiplier = 10 --16
--local dir = vector.multiply(minetest.yaw_to_dir(yaw),abr*distance_multiplier)
if areas then
-- is player inside the spawn area?
if check_is_inside_area(pos) then
local zb_area = {"cemetery","Cemetery","monsters","zbd"}
local spawned = spawn_monsters(pos, yaw, chance, distance_multiplier, 'zombiestrd:zombie', zb_area)
local dir = vector.multiply(minetest.yaw_to_dir(yaw),distance_multiplier)
local pos2 = vector.add(pos,dir)
--minetest.add_entity(pos2, "zombiestrd:target") --debug target
pos2.y=pos2.y-5
local height, liquidflag = mobkit.get_terrain_height(pos2,32)
if height == nil then height = 0 end
local position_at_terrain_height = {x=pos2.x,y=height-0.01,z=pos2.z}
if spawned == false then
chance = spawn_rate * 1.4
local ghost_area = {"cemetery","Cemetery","monsters","gtd"}
spawned = spawn_monsters(pos, yaw, chance, distance_multiplier, 'zombiestrd:ghost', ghost_area)
end
--force the spawn area be inside the area
local is_monsters_spawn_area = check_is_inside_area(position_at_terrain_height)
if is_monsters_spawn_area then
--if height and height >= 0 and
if mobkit.nodeatpos(position_at_terrain_height).is_ground_content then
local objs = minetest.get_objects_inside_radius(pos,abr*distance_multiplier+5)
local wcnt=0
local dcnt=0
local mobname = 'zombiestrd:zombie'
if liquidflag then -- sharks
--[[
local spnode = mobkit.nodeatpos({x=pos2.x,y=height+0.01,z=pos2.z})
local spnode2 = mobkit.nodeatpos({x=pos2.x,y=height+1.01,z=pos2.z}) -- node above to make sure won't spawn in shallows
nodename_water = nodename_water or minetest.registered_aliases.mapgen_water_source
if spnode and spnode2 and spnode.name == nodename_water and spnode2.name == nodename_water then
for _,obj in ipairs(objs) do
if not obj:is_player() then
local entity = obj:get_luaentity()
if entity and entity.name=='zombiestrd:shark' then return end
end
end
mobname = 'zombiestrd:shark'
else
return
end
]]--
return
elseif height >= 0 then --zombies
for _,obj in ipairs(objs) do -- count mobs in abrange
if not obj:is_player() then
local entity = obj:get_luaentity()
if entity and entity.name:find('zombiestrd:') then
chance=chance + (1-chance)*spawn_reduction -- chance reduced for every mob in range
end
end
end
end
if chance < random() then
pos2.y = height+1.01
objs = minetest.get_objects_inside_radius(pos2,abr*distance_multiplier-2)
--[[for _,obj in ipairs(objs) do -- do not spawn if another player around
if obj:is_player() then return end
end]]--
local obj=minetest.add_entity(pos2,mobname) -- ok spawn it already damnit
end
end
end --monsters_spwan_area
end -- player area
end -- end areas
end
end
@ -388,11 +463,11 @@ minetest.register_entity("zombiestrd:zombie",{
--PONCTUATION
if zombie_score[name] then
zombie_score[name] = zombie_score[name] + 1
check_prizes(puncher)
check_prizes(puncher, zombie_score[name], "zombie")
else
zombie_score[name] = 1
end
zb_savelist()
savelist()
--END PONCTUATION
else
mobkit.make_sound(self,'bodyhit')
@ -416,6 +491,111 @@ minetest.register_entity("zombiestrd:zombie",{
})
minetest.register_entity("zombiestrd:ghost",{
-- common props
physical = true,
stepheight = 0.1, --EVIL!
collide_with_objects = false,
collisionbox = {-0.25, -1, -0.25, 0.25, 0.75, 0.25},
visual = "mesh",
mesh = "zombie_normal.b3d",
textures = {"mobs_npc_ghost.png","mobs_npc_ghost2.png"},
visual_size = {x = 1, y = 1},
static_save = true,
makes_footstep_sound = false,
on_step = mobkit.stepfunc, -- required
on_activate = mobkit.actfunc, -- required
get_staticdata = mobkit.statfunc,
-- api props
springiness=0,
buoyancy = 0.75, -- portion of hitbox submerged
max_speed = 4,
jump_height = 3,
view_range = 30,
lung_capacity = 200, -- seconds
max_hp = 50,
attack={range=0.5,damage_groups={fleshy=7}},
animation = {
walk={range={x=41,y=101},speed=40,loop=true},
stand={range={x=0,y=40},speed=1,loop=true},
},
sounds = {
misc='ghost',
attack='ghost_laugh',
warn = 'angrydog',
headhit = 'splash_hit',
bodyhit = 'ghost_hit',
charge = 'ghost_charge',
},
armor_groups={immortal=100},
brainfunc = zombie_brain,
on_punch=function(self, puncher, time_from_last_punch, tool_caps, dir)
if mobkit.is_alive(self) then
-- head seeking
if type(puncher)=='userdata' and puncher:is_player() then
local name = puncher:get_player_name()
local pp = puncher:get_pos()
pp.y = pp.y + puncher:get_properties().eye_height -- pp is now camera pos
local pm, radius = get_head(self)
local look_dir = puncher:get_look_dir()
local head_dir = vector.subtract(pm,pp)
local dot = dot(look_dir,head_dir)
local p2 = {x=pp.x+look_dir.x*dot, y=pp.y+look_dir.y*dot, z=pp.z+look_dir.z*dot}
if vector.distance(pp,pm) <=2 then -- a way to decrease punch range without dependences
if mobkit.isnear3d(pm,p2,radius*0.8) and
time_from_last_punch >= tool_caps.full_punch_interval-0.01 and
tool_caps.damage_groups.fleshy > 3 then -- valid headshot
mobkit.make_sound(self,'headhit')
-- self.object:set_hp(99)
self.hp=0
--PONCTUATION
if ghost_score[name] then
ghost_score[name] = ghost_score[name] + 1
check_prizes(puncher, ghost_score[name], "ghost")
else
ghost_score[name] = 1
end
savelist()
--END PONCTUATION
else
mobkit.make_sound(self,'bodyhit')
if mobkit.is_alive(self) then
mobkit.hurt(self,tool_caps.damage_groups.fleshy or 1)
if random()<=0.3 then alert(pp) end
if mobkit.get_queue_priority(self) < 10 then
mobkit.make_sound(self,'misc')
mobkit.hq_hunt(self,10,puncher)
end
if self.hp<=0 then
--PONCTUATION
if ghost_score[name] then
ghost_score[name] = ghost_score[name] + 1
check_prizes(puncher, ghost_score[name], "ghost")
else
ghost_score[name] = 1
end
savelist()
--END PONCTUATION
end
end
end
-- kickback
local hvel = vector.multiply(look_dir,4)
self.object:set_velocity({x=hvel.x,y=max(hvel.y,1),z=hvel.z})
end
else
local hvel = vector.multiply(vector.normalize({x=dir.x,y=0,z=dir.z}),4)
self.object:set_velocity({x=hvel.x,y=2,z=hvel.z})
end
end
end
})
--[[minetest.register_entity("zombiestrd:shark",{
-- common props
physical = true,

View File

@ -1,5 +1,7 @@
local storage = minetest.get_mod_storage()
score = {}
zombie_score = {}
ghost_score = {}
local prizes = {
{250, "binoculars:binoculars", 1, "binoculars"},
@ -16,21 +18,68 @@ local prizes = {
local function openlist()
local load = storage:to_table()
zombie_score = load.fields
for count in pairs(zombie_score) do
zombie_score[count] = tonumber(zombie_score[count])
end
score = load.fields
--minetest.debug("score monsters: " .. dump(score))
if score["zombie_score"] ~= nil then
--minetest.debug("score zombie: " .. dump(score["zombie_score"]))
zombie_score = loadstring("return " .. score["zombie_score"])()
for count in pairs(zombie_score) do
zombie_score[count] = tonumber(zombie_score[count])
end
else
zombie_score = {}
end
if score["ghost_score"] ~= nil then
ghost_score = loadstring("return " .. score["ghost_score"])()
for count in pairs(ghost_score) do
ghost_score[count] = tonumber(ghost_score[count])
end
else
ghost_score = {}
end
end
-- save scoreboard
function zb_savelist()
function savelist()
storage:from_table({fields=zombie_score})
score["zombie_score"] = serializeTable(zombie_score)
score["ghost_score"] = serializeTable(ghost_score)
storage:from_table({fields=score})
--minetest.chat_send_all(dump(score))
end -- poi.save()
function serializeTable(val, name, skipnewlines, depth)
skipnewlines = skipnewlines or false
depth = depth or 0
local tmp = string.rep(" ", depth)
if name then tmp = tmp .. name .. " = " end
if type(val) == "table" then
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
for k, v in pairs(val) do
tmp = tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
end
tmp = tmp .. string.rep(" ", depth) .. "}"
elseif type(val) == "number" then
tmp = tmp .. tostring(val)
elseif type(val) == "string" then
tmp = tmp .. string.format("%q", val)
elseif type(val) == "boolean" then
tmp = tmp .. (val and "true" or "false")
else
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
end
return tmp
end
function spairs(t, order)
-- collect the keys
local keys = {}
@ -55,11 +104,11 @@ function spairs(t, order)
end
local function sortscore()
local function sortscore(score_table)
local fname = "size[5,6]"
local count = 1
for k,v in spairs(zombie_score, function(t,a,b) return t[b] < t[a] end) do
for k,v in spairs(score_table, function(t,a,b) return t[b] < t[a] end) do
--minetest.chat_send_all(count.." >>> "..k.." , "..v)
fname = fname.."label[1,"..(count*0.3)..";"..count.." >>> "..k.." , "..v.."]"
count = count + 1
@ -70,20 +119,21 @@ local function sortscore()
return fname
end
function check_prizes(user)
local name = user:get_player_name()
local inv = user:get_inventory()
for i in ipairs(prizes) do
local goal = prizes[i][1]
local nodename = prizes[i][2]
local howmuch = prizes[i][3]
local sayit = prizes[i][4]
if zombie_score[name] == goal then
minetest.chat_send_player(name, core.colorize("#FF6700", "Congratulation: You killed your "..goal.."s zombies !! Keep up the good work. "..howmuch.." "..sayit.." have been added to your inv"))
function check_prizes(user, points, monster_name)
--check_prizes(puncher, zombie_score[name], "zombie")
local name = user:get_player_name()
local inv = user:get_inventory()
for i in ipairs(prizes) do
local goal = prizes[i][1]
local nodename = prizes[i][2]
local howmuch = prizes[i][3]
local sayit = prizes[i][4]
if points == goal then
minetest.chat_send_player(name, core.colorize("#FF6700", "Congratulation: You killed your "..goal.."s "..monster_name.."s !! Keep up the good work. "..howmuch.." "..sayit.." have been added to your inv"))
inv:add_item("main", {name=nodename, count=howmuch})
end
end
end
end
minetest.register_chatcommand("zombie_score", {
@ -92,7 +142,22 @@ minetest.register_chatcommand("zombie_score", {
privs = {interact = true},
func = function(name)
local fname = sortscore()
local fname = sortscore(zombie_score)
if fname then
--minetest.chat_send_player(name, ">>> Highscore is :"..score[highscore].." by "..highscore)
minetest.show_formspec(name, "zombiestrd:the_killers", fname)
end
end,
})
minetest.register_chatcommand("ghost_score", {
params = "",
description = "Shows the best ghost killer",
privs = {interact = true},
func = function(name)
local fname = sortscore(ghost_score)
if fname then
--minetest.chat_send_player(name, ">>> Highscore is :"..score[highscore].." by "..highscore)
minetest.show_formspec(name, "zombiestrd:the_killers", fname)

BIN
sounds/ghost.1.ogg Normal file

Binary file not shown.

BIN
sounds/ghost.2.ogg Normal file

Binary file not shown.

BIN
sounds/ghost.3.ogg Normal file

Binary file not shown.

BIN
sounds/ghost.4.ogg Normal file

Binary file not shown.

BIN
sounds/ghost_charge.ogg Normal file

Binary file not shown.

BIN
sounds/ghost_hit.ogg Normal file

Binary file not shown.

BIN
sounds/ghost_laugh.ogg Normal file

Binary file not shown.

2
sounds/media.txt Normal file
View File

@ -0,0 +1,2 @@
ghost.*.ogg and gohst_charge.ogg are Creative Commons 3.0 by Videvo https://www.videvo.net/royalty-free-sound-effects/ghost/
Additional sound effects (ghost_laugh.ogg) from https://www.zapsplat.com

BIN
textures/mobs_npc_ghost.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
textures/mobs_npc_ghost2.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B