ghost added
parent
e0c15bd050
commit
2acb87aa5c
304
init.lua
304
init.lua
|
@ -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,
|
||||
|
|
113
scoreboard.lua
113
scoreboard.lua
|
@ -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)
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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
|
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 736 B |
Loading…
Reference in New Issue