0bdc55c32c
ball: damages entities now, so you can kill mobs for example( except basic_robots) keypad: PROBLEM: if player doesnt move it takes another punch at same block for this function to run again, and it works normally if player moved at least one block from his previous position. it is caused by minetest bad handling of punches.
667 lines
22 KiB
Lua
667 lines
22 KiB
Lua
-- BALL: energy ball that flies around, can bounce and activate stuff
|
|
-- rnd 2016:
|
|
|
|
-- TO DO: move mode: ball just rolling around on ground without hopping, also if inside slope it would "roll down", just increased velocity in slope direction
|
|
|
|
-- SETTINGS
|
|
|
|
basic_machines.ball = {};
|
|
basic_machines.ball.maxdamage = 10; -- player health 20
|
|
basic_machines.ball.bounce_materials = { -- to be used with bounce setting 2 in ball spawner: 1: bounce in x direction, 2: bounce in z direction, otherwise it bounces in y direction
|
|
["default:wood"]=1,
|
|
["xpanes:bar_2"]=1,
|
|
["xpanes:bar_10"]=1,
|
|
["darkage:iron_bars"]=1,
|
|
["default:glass"] = 2,
|
|
};
|
|
|
|
-- END OF SETTINGS
|
|
|
|
local ballcount = {};
|
|
local function round(x)
|
|
if x < 0 then
|
|
return -math.floor(-x+0.5);
|
|
else
|
|
return math.floor(x+0.5);
|
|
end
|
|
end
|
|
|
|
|
|
local ball_spawner_update_form = function (pos)
|
|
|
|
local meta = minetest.get_meta(pos);
|
|
local x0,y0,z0;
|
|
x0=meta:get_int("x0");y0=meta:get_int("y0");z0=meta:get_int("z0"); -- direction of velocity
|
|
|
|
local energy,bounce,g,puncheable, gravity,hp,hurt,solid;
|
|
local speed = meta:get_float("speed"); -- if positive sets initial ball speed
|
|
energy = meta:get_float("energy"); -- if positive activates, negative deactivates, 0 does nothing
|
|
bounce = meta:get_int("bounce"); -- if nonzero bounces when hit obstacle, 0 gets absorbed
|
|
gravity = meta:get_float("gravity"); -- gravity
|
|
hp = meta:get_float("hp");
|
|
hurt = meta:get_float("hurt");
|
|
puncheable = meta:get_int("puncheable"); -- if 1 can be punched by players in protection, if 2 can be punched by anyone
|
|
solid = meta:get_int("solid"); -- if 1 then entity is solid - cant be walked on
|
|
|
|
local texture = meta:get_string("texture") or "basic_machines_ball.png";
|
|
local visual = meta:get_string("visual") or "sprite";
|
|
local scale = meta:get_int("scale");
|
|
|
|
local form =
|
|
"size[4.25,4.75]" .. -- width, height
|
|
"field[0.25,0.5;1,1;x0;target;"..x0.."] field[1.25,0.5;1,1;y0;;"..y0.."] field[2.25,0.5;1,1;z0;;"..z0.."]"..
|
|
"field[3.25,0.5;1,1;speed;speed;"..speed.."]"..
|
|
--speed, jump, gravity,sneak
|
|
"field[0.25,1.5;1,1;energy;energy;"..energy.."]"..
|
|
"field[1.25,1.5;1,1;bounce;bounce;".. bounce.."]"..
|
|
"field[2.25,1.5;1,1;gravity;gravity;"..gravity.."]"..
|
|
"field[3.25,1.5;1,1;puncheable;puncheable;"..puncheable.."]"..
|
|
"field[3.25,2.5;1,1;solid;solid;"..solid.."]"..
|
|
"field[0.25,2.5;1,1;hp;hp;"..hp.."]".."field[1.25,2.5;1,1;hurt;hurt;"..hurt.."]"..
|
|
"field[0.25,3.5;4,1;texture;texture;"..minetest.formspec_escape(texture).."]"..
|
|
"field[0.25,4.5;1,1;scale;scale;"..scale.."]".."field[1.25,4.5;1,1;visual;visual;"..visual.."]"..
|
|
"button_exit[3.25,4.25;1,1;OK;OK]";
|
|
|
|
|
|
|
|
if meta:get_int("admin")==1 then
|
|
local lifetime = meta:get_int("lifetime");
|
|
if lifetime <= 0 then lifetime = 20 end
|
|
form = form .. "field[2.25,2.5;1,1;lifetime;lifetime;"..lifetime.."]"
|
|
end
|
|
|
|
meta:set_string("formspec",form);
|
|
|
|
end
|
|
|
|
|
|
|
|
minetest.register_entity("basic_machines:ball",{
|
|
timer = 0,
|
|
lifetime = 20, -- how long it exists before disappearing
|
|
energy = 0, -- if negative it will deactivate stuff, positive will activate, 0 wont do anything
|
|
puncheable = 1, -- can be punched by players in protection
|
|
bounce = 0, -- 0: absorbs in block, 1 = proper bounce=lag buggy, -- to do: 2 = line of sight bounce
|
|
gravity = 0,
|
|
speed = 5, -- velocity when punched
|
|
hurt = 0, -- how much damage it does to target entity, if 0 damage disabled
|
|
owner = "",
|
|
state = false,
|
|
origin = {x=0,y=0,z=0},
|
|
lastpos = {x=0,y=0,z=0}, -- last not-colliding position
|
|
hp_max = 100,
|
|
elasticity = 0.9, -- speed gets multiplied by this after bounce
|
|
visual="sprite",
|
|
visual_size={x=.6,y=.6},
|
|
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
|
|
physical=false,
|
|
|
|
--textures={"basic_machines_ball"},
|
|
|
|
on_activate = function(self, staticdata)
|
|
self.object:set_properties({textures={"basic_machines_ball.png"}})
|
|
self.object:set_properties({visual_size = {x=1, y=1}});
|
|
self.timer = 0;self.owner = "";
|
|
self.origin = self.object:getpos();
|
|
self.lifetime = 20;
|
|
end,
|
|
|
|
get_staticdata = function(self) -- this gets called before object put in world and before it hides
|
|
if not self.state then return nil end
|
|
self.object:remove();
|
|
return nil
|
|
end,
|
|
|
|
|
|
on_punch = function (self, puncher, time_from_last_punch, tool_capabilities, dir)
|
|
if self.puncheable == 0 then return end
|
|
if self.puncheable == 1 then -- only those in protection
|
|
local name = puncher:get_player_name();
|
|
local pos = self.object:getpos();
|
|
if minetest.is_protected(pos,name) then return end
|
|
end
|
|
--minetest.chat_send_all(minetest.pos_to_string(dir))
|
|
if time_from_last_punch<0.5 then return end
|
|
local v = self.speed or 1;
|
|
|
|
local velocity = dir;
|
|
velocity.x = velocity.x*v;velocity.y = velocity.y*v;velocity.z = velocity.z*v;
|
|
self.object:setvelocity(velocity)
|
|
end,
|
|
|
|
on_step = function(self, dtime)
|
|
self.timer=self.timer+dtime
|
|
if self.timer>self.lifetime then
|
|
local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count;
|
|
self.object:remove()
|
|
return
|
|
end
|
|
|
|
if not self.state then self.state = true end
|
|
local pos=self.object:getpos()
|
|
|
|
local origin = self.origin;
|
|
|
|
local r = 30;-- maximal distance when balls disappear
|
|
local dist = math.max(math.abs(pos.x-origin.x), math.abs(pos.y-origin.y), math.abs(pos.z-origin.z));
|
|
if dist>r then -- remove if it goes too far
|
|
local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count;
|
|
self.object:remove()
|
|
return
|
|
end
|
|
|
|
local nodename = minetest.get_node(pos).name;
|
|
local walkable = false;
|
|
if nodename ~= "air" then
|
|
walkable = minetest.registered_nodes[nodename].walkable;
|
|
if nodename == "basic_machines:ball_spawner" and dist>0.5 then walkable = true end -- ball can activate spawner, just not originating one
|
|
end
|
|
if not walkable then
|
|
self.lastpos = pos
|
|
if self.hurt~=0 then -- check for coliding nearby objects
|
|
local objects = minetest.get_objects_inside_radius(pos,2);
|
|
if #objects>1 then
|
|
for _, obj in pairs(objects) do
|
|
local p = obj:getpos();
|
|
local d = math.sqrt((p.x-pos.x)^2+(p.y-pos.y)^2+(p.z-pos.z)^2);
|
|
if d>0 then
|
|
|
|
--if minetest.is_protected(p,self.owner) then return end
|
|
if math.abs(p.x)<32 and math.abs(p.y)<32 and math.abs(p.z)<32 then return end -- no damage around spawn
|
|
|
|
if obj:is_player() then --player
|
|
if obj:get_player_name()==self.owner then break end -- dont hurt owner
|
|
|
|
local hp = obj:get_hp()
|
|
local newhp = hp-self.hurt;
|
|
if newhp<=0 and boneworld and boneworld.killxp then
|
|
local killxp = boneworld.killxp[self.owner];
|
|
if killxp then
|
|
boneworld.killxp[self.owner] = killxp + 0.01;
|
|
end
|
|
end
|
|
obj:set_hp(newhp)
|
|
else -- non player
|
|
local lua_entity = obj:get_luaentity();
|
|
if lua_entity and lua_entity.itemstring then
|
|
local entname = lua_entity.itemstring;
|
|
if entname == "robot" then
|
|
self.object:remove()
|
|
return;
|
|
end
|
|
end
|
|
local hp = obj:get_hp()
|
|
local newhp = hp-self.hurt;
|
|
minetest.chat_send_player(self.owner,"#ball: target hp " .. newhp)
|
|
if newhp<=0 then obj:remove() else obj:set_hp(newhp) end
|
|
end
|
|
|
|
|
|
|
|
|
|
local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count;
|
|
self.object:remove();
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
if walkable then -- we hit a node
|
|
--minetest.chat_send_all(" hit node at " .. minetest.pos_to_string(pos))
|
|
|
|
|
|
local node = minetest.get_node(pos);
|
|
local table = minetest.registered_nodes[node.name];
|
|
if table and table.mesecons and table.mesecons.effector then -- activate target
|
|
|
|
local energy = self.energy;
|
|
if energy~=0 then
|
|
if minetest.is_protected(pos,self.owner) then return end
|
|
end
|
|
local effector = table.mesecons.effector;
|
|
|
|
self.object:remove();
|
|
|
|
if energy>0 then
|
|
if not effector.action_on then return end
|
|
effector.action_on(pos,node,16);
|
|
elseif energy<0 then
|
|
if not effector.action_off then return end
|
|
effector.action_off(pos,node,16);
|
|
end
|
|
|
|
|
|
else -- bounce ( copyright rnd, 2016 )
|
|
local bounce = self.bounce;
|
|
if self.bounce == 0 then
|
|
local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count;
|
|
self.object:remove()
|
|
return end
|
|
|
|
local n = {x=0,y=0,z=0}; -- this will be bounce normal
|
|
local v = self.object:getvelocity();
|
|
local opos = {x=round(pos.x),y=round(pos.y), z=round(pos.z)}; -- obstacle
|
|
local bpos ={ x=(pos.x-opos.x),y=(pos.y-opos.y),z=(pos.z-opos.z)}; -- boundary position on cube, approximate
|
|
|
|
if bounce == 2 then -- uses special blocks for non buggy lag proof bouncing: by default it bounces in y direction
|
|
local bounce_direction = basic_machines.ball.bounce_materials[node.name] or 0;
|
|
|
|
if bounce_direction == 0 then
|
|
if v.y>=0 then n.y = -1 else n.y = 1 end
|
|
elseif bounce_direction == 1 then
|
|
if v.x>=0 then n.x = -1 else n.x = 1 end
|
|
n.y = 0;
|
|
elseif bounce_direction == 2 then
|
|
if v.z>=0 then n.z = -1 else n.z = 1 end
|
|
n.y = 0;
|
|
end
|
|
|
|
else -- algorithm to determine bounce direction - problem: with lag its impossible to determine reliable which node was hit and which face ..
|
|
|
|
if v.x<=0 then n.x = 1 else n.x = -1 end -- possible bounce directions
|
|
if v.y<=0 then n.y = 1 else n.y = -1 end
|
|
if v.z<=0 then n.z = 1 else n.z = -1 end
|
|
|
|
local dpos = {};
|
|
|
|
dpos.x = 0.5*n.x; dpos.y = 0; dpos.z = 0; -- calculate distance to bounding surface midpoints
|
|
|
|
local d1 = (bpos.x-dpos.x)^2 + (bpos.y)^2 + (bpos.z)^2;
|
|
dpos.x = 0; dpos.y = 0.5*n.y; dpos.z = 0;
|
|
local d2 = (bpos.x)^2 + (bpos.y-dpos.y)^2 + (bpos.z)^2;
|
|
dpos.x = 0; dpos.y = 0; dpos.z = 0.5*n.z;
|
|
local d3 = (bpos.x)^2 + (bpos.y)^2 + (bpos.z-dpos.z)^2;
|
|
local d = math.min(d1,d2,d3); -- we obtain bounce direction from minimal distance
|
|
|
|
if d1==d then --x
|
|
n.y=0;n.z=0
|
|
elseif d2==d then --y
|
|
n.x=0;n.z=0
|
|
elseif d3==d then --z
|
|
n.x=0;n.y=0
|
|
end
|
|
|
|
|
|
nodename=minetest.get_node({x=opos.x+n.x,y=opos.y+n.y,z=opos.z+n.z}).name -- verify normal
|
|
walkable = nodename ~= "air";
|
|
if walkable then -- problem, nonempty node - incorrect normal, fix it
|
|
if n.x ~=0 then -- x direction is wrong, try something else
|
|
n.x=0;
|
|
if v.y>=0 then n.y = -1 else n.y = 1 end -- try y
|
|
nodename=minetest.get_node({x=opos.x+n.x,y=opos.y+n.y,z=opos.z+n.z}).name -- verify normal
|
|
walkable = nodename ~= "air";
|
|
if walkable then -- still problem, only remaining is z
|
|
n.y=0;
|
|
if v.z>=0 then n.z = -1 else n.z = 1 end
|
|
nodename=minetest.get_node({x=opos.x+n.x,y=opos.y+n.y,z=opos.z+n.z}).name -- verify normal
|
|
walkable = nodename ~= "air";
|
|
if walkable then -- messed up, just remove the ball
|
|
self.object:remove()
|
|
return
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
local elasticity = self.elasticity;
|
|
|
|
-- bounce
|
|
if n.x~=0 then
|
|
v.x=-elasticity*v.x
|
|
elseif n.y~=0 then
|
|
v.y=-elasticity*v.y
|
|
elseif n.z~=0 then
|
|
v.z=-elasticity*v.z
|
|
end
|
|
|
|
local r = 0.2
|
|
bpos = {x=pos.x+n.x*r,y=pos.y+n.y*r,z=pos.z+n.z*r}; -- point placed a bit further away from box
|
|
self.object:setpos(bpos) -- place object at last known outside point
|
|
|
|
self.object:setvelocity(v);
|
|
|
|
minetest.sound_play("default_dig_cracky", {pos=pos,gain=1.0,max_hear_distance = 8,})
|
|
|
|
end
|
|
end
|
|
return
|
|
end,
|
|
})
|
|
|
|
|
|
minetest.register_node("basic_machines:ball_spawner", {
|
|
description = "Spawns energy ball one block above",
|
|
tiles = {"basic_machines_ball.png"},
|
|
groups = {cracky=3, mesecon_effector_on = 1},
|
|
drawtype = "allfaces",
|
|
paramtype = "light",
|
|
param1=1,
|
|
walkable = false,
|
|
alpha = 150,
|
|
sounds = default.node_sound_wood_defaults(),
|
|
after_place_node = function(pos, placer)
|
|
local meta = minetest.env:get_meta(pos)
|
|
meta:set_string("owner", placer:get_player_name());
|
|
local privs = minetest.get_player_privs(placer:get_player_name()); if privs.privs then meta:set_int("admin",1) end
|
|
|
|
if privs.machines then meta:set_int("machines",1) end
|
|
|
|
meta:set_float("hurt",0);
|
|
meta:set_string("texture", "basic_machines_ball.png");
|
|
meta:set_float("hp",100);
|
|
meta:set_float("speed",5); -- if positive sets initial ball speed
|
|
meta:set_float("energy",1); -- if positive activates, negative deactivates, 0 does nothing
|
|
meta:set_int("bounce",0); -- if nonzero bounces when hit obstacle, 0 gets absorbed
|
|
meta:set_float("gravity",0); -- gravity
|
|
meta:set_int("puncheable",0); -- if 0 not puncheable, if 1 can be punched by players in protection, if 2 can be punched by anyone
|
|
meta:set_int("scale",100);
|
|
meta:set_string("visual","sprite");
|
|
ball_spawner_update_form(pos);
|
|
|
|
end,
|
|
|
|
mesecons = {effector = {
|
|
action_on = function (pos, node,ttl)
|
|
if ttl<0 then return end
|
|
|
|
local meta = minetest.get_meta(pos);
|
|
local t0 = meta:get_int("t");
|
|
local t1 = minetest.get_gametime();
|
|
local T = meta:get_int("T"); -- temperature
|
|
|
|
if t0>t1-2 then -- activated before natural time
|
|
T=T+1;
|
|
else
|
|
if T>0 then
|
|
T=T-1
|
|
if t1-t0>5 then T = 0 end
|
|
end
|
|
end
|
|
meta:set_int("T",T);
|
|
meta:set_int("t",t1); -- update last activation time
|
|
|
|
if T > 2 then -- overheat
|
|
minetest.sound_play("default_cool_lava",{pos = pos, max_hear_distance = 16, gain = 0.25})
|
|
meta:set_string("infotext","overheat: temperature ".. T)
|
|
return
|
|
end
|
|
|
|
if meta:get_int("machines")~=1 then -- no machines priv, limit ball count
|
|
local owner = meta:get_string("owner");
|
|
local count = ballcount[owner];
|
|
if not count or count<0 then count = 0 end
|
|
|
|
if count>=2 then
|
|
if t1-t0>20 then count = 0
|
|
else return
|
|
end
|
|
end
|
|
|
|
count = count + 1;
|
|
ballcount[owner]=count;
|
|
--minetest.chat_send_all("count " .. count);
|
|
end
|
|
|
|
pos.x = round(pos.x);pos.y = round(pos.y);pos.z = round(pos.z);
|
|
local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, "basic_machines:ball");
|
|
local luaent = obj:get_luaentity();
|
|
local meta = minetest.get_meta(pos);
|
|
|
|
local speed,energy,bounce,gravity,puncheable,solid;
|
|
speed = meta:get_float("speed");
|
|
energy = meta:get_float("energy"); -- if positive activates, negative deactivates, 0 does nothing
|
|
bounce = meta:get_int("bounce"); -- if nonzero bounces when hit obstacle, 0 gets absorbed
|
|
gravity = meta:get_float("gravity"); -- gravity
|
|
puncheable = meta:get_int("puncheable"); -- if 1 can be punched by players in protection, if 2 can be punched by anyone
|
|
solid = meta:get_int("solid");
|
|
|
|
if energy<0 then
|
|
obj:set_properties({textures={"basic_machines_ball.png^[colorize:blue:120"}})
|
|
end
|
|
|
|
luaent.bounce = bounce;
|
|
luaent.energy = energy;
|
|
if gravity>0 then
|
|
obj:setacceleration({x=0,y=-gravity,z=0});
|
|
end
|
|
luaent.puncheable = puncheable;
|
|
luaent.owner = meta:get_string("owner");
|
|
luaent.hurt = meta:get_float("hurt");
|
|
if solid==1 then
|
|
luaent.physical = true
|
|
end
|
|
|
|
obj:set_hp( meta:get_float("hp") );
|
|
|
|
local x0,y0,z0;
|
|
if speed>0 then luaent.speed = speed end
|
|
|
|
x0=meta:get_int("x0");y0=meta:get_int("y0");z0=meta:get_int("z0"); -- direction of velocity
|
|
if speed~=0 and (x0~=0 or y0~=0 or z0~=0) then -- set velocity direction
|
|
local velocity = {x=x0,y=y0,z=z0};
|
|
local v = math.sqrt(velocity.x^2+velocity.y^2+velocity.z^2); if v == 0 then v = 1 end
|
|
v = v / speed;
|
|
velocity.x=velocity.x/v;velocity.y=velocity.y/v;velocity.z=velocity.z/v;
|
|
obj:setvelocity(velocity);
|
|
end
|
|
|
|
if meta:get_int("admin")==1 then
|
|
luaent.lifetime = meta:get_float("lifetime");
|
|
end
|
|
|
|
|
|
local visual = meta:get_string("visual")
|
|
obj:set_properties({visual=visual});
|
|
local texture = meta:get_string("texture");
|
|
if visual=="sprite" then
|
|
obj:set_properties({textures={texture}})
|
|
elseif visual == "cube" then
|
|
obj:set_properties({textures={texture,texture,texture,texture,texture,texture}})
|
|
end
|
|
local scale = meta:get_int("scale");if scale<=0 then scale = 1 else scale = scale/100 end
|
|
obj:set_properties({visual_size = {x=scale, y=scale}});
|
|
|
|
|
|
|
|
end,
|
|
|
|
action_off = function (pos, node,ttl)
|
|
if ttl<0 then return end
|
|
pos.x = round(pos.x);pos.y = round(pos.y);pos.z = round(pos.z);
|
|
local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, "basic_machines:ball");
|
|
local luaent = obj:get_luaentity();
|
|
luaent.energy = -1;
|
|
obj:set_properties({textures={"basic_machines_ball.png^[colorize:blue:120"}})
|
|
end
|
|
}
|
|
},
|
|
|
|
on_receive_fields = function(pos, formname, fields, sender)
|
|
|
|
local name = sender:get_player_name();if minetest.is_protected(pos,name) then return end
|
|
|
|
if fields.OK then
|
|
local privs = minetest.get_player_privs(sender:get_player_name());
|
|
local meta = minetest.get_meta(pos);
|
|
local x0=0; local y0=0; local z0=0;
|
|
--minetest.chat_send_all("form at " .. dump(pos) .. " fields " .. dump(fields))
|
|
if fields.x0 then x0 = tonumber(fields.x0) or 0 end
|
|
if fields.y0 then y0 = tonumber(fields.y0) or 0 end
|
|
if fields.z0 then z0 = tonumber(fields.z0) or 0 end
|
|
if not privs.privs and (math.abs(x0)>10 or math.abs(y0)>10 or math.abs(z0) > 10) then return end
|
|
|
|
meta:set_int("x0",x0);meta:set_int("y0",y0);meta:set_int("z0",z0);
|
|
|
|
local speed,energy,bounce,gravity,puncheable,solid;
|
|
energy = meta:get_float("energy"); -- if positive activates, negative deactivates, 0 does nothing
|
|
bounce = meta:get_int("bounce"); -- if nonzero bounces when hit obstacle, 0 gets absorbed
|
|
gravity = meta:get_float("gravity"); -- gravity
|
|
puncheable = meta:get_int("puncheable"); -- if 1 can be punched by players in protection, if 2 can be punched by anyone
|
|
solid = meta:get_int("solid");
|
|
|
|
|
|
if fields.speed then
|
|
local speed = tonumber(fields.speed) or 0;
|
|
if (speed > 10 or speed < 0) and not privs.privs then return end
|
|
meta:set_float("speed", speed)
|
|
end
|
|
|
|
if fields.energy then
|
|
local energy = tonumber(fields.energy) or 1;
|
|
meta:set_float("energy", energy)
|
|
end
|
|
|
|
if fields.bounce then
|
|
local bounce = tonumber(fields.bounce) or 1;
|
|
meta:set_int("bounce",bounce)
|
|
end
|
|
|
|
if fields.gravity then
|
|
local gravity = tonumber(fields.gravity) or 1;
|
|
if (gravity<0 or gravity>30) and not privs.privs then return end
|
|
meta:set_float("gravity", gravity)
|
|
end
|
|
if fields.puncheable then
|
|
meta:set_int("puncheable", tonumber(fields.puncheable) or 0)
|
|
end
|
|
|
|
if fields.solid then
|
|
meta:set_int("solid", tonumber(fields.solid) or 0)
|
|
end
|
|
|
|
if fields.lifetime then
|
|
meta:set_int("lifetime", tonumber(fields.lifetime) or 0)
|
|
end
|
|
|
|
if fields.hurt then
|
|
meta:set_float("hurt", tonumber(fields.hurt) or 0)
|
|
end
|
|
|
|
if fields.hp then
|
|
meta:set_float("hp", math.abs(tonumber(fields.hp) or 0))
|
|
end
|
|
|
|
if fields.texture then
|
|
meta:set_string ("texture", fields.texture);
|
|
end
|
|
|
|
if fields.scale then
|
|
local scale = math.abs(tonumber(fields.scale) or 100);
|
|
if scale>1000 and not privs.privs then scale = 1000 end
|
|
meta:set_int("scale", scale)
|
|
end
|
|
|
|
if fields.visual then
|
|
local visual = fields.visual or "";
|
|
if visual~="sprite" and visual~="cube" then return end
|
|
meta:set_string ("visual", fields.visual);
|
|
end
|
|
|
|
ball_spawner_update_form(pos);
|
|
end
|
|
end,
|
|
|
|
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
|
local name = digger:get_player_name();
|
|
local inv = digger:get_inventory();
|
|
inv:remove_item("main", ItemStack("basic_machines:ball_spawner"));
|
|
local stack = ItemStack("basic_machines:ball_spell");
|
|
local meta = oldmetadata["fields"];
|
|
meta["formspec"]=nil;
|
|
stack:set_metadata(minetest.serialize(meta));
|
|
inv:add_item("main",stack);
|
|
end
|
|
|
|
})
|
|
|
|
|
|
local spelltime = {};
|
|
|
|
-- ball as magic spell user can cast
|
|
minetest.register_tool("basic_machines:ball_spell", {
|
|
description = "ball spawner",
|
|
inventory_image = "basic_machines_ball.png",
|
|
tool_capabilities = {
|
|
full_punch_interval = 2,
|
|
max_drop_level=0,
|
|
},
|
|
on_use = function(itemstack, user, pointed_thing)
|
|
|
|
local pos = user:getpos();pos.y=pos.y+1;
|
|
local meta = minetest.deserialize(itemstack:get_metadata());
|
|
if not meta then return end
|
|
local owner = meta["owner"] or "";
|
|
|
|
--if minetest.is_protected(pos,owner) then return end
|
|
|
|
local t0 = spelltime[owner] or 0;
|
|
local t1 = minetest.get_gametime();
|
|
if t1-t0<2 then return end -- too soon
|
|
spelltime[owner]=t1;
|
|
|
|
|
|
local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, "basic_machines:ball");
|
|
local luaent = obj:get_luaentity();
|
|
|
|
|
|
local speed,energy,bounce,gravity,puncheable;
|
|
speed = tonumber(meta["speed"]) or 0;
|
|
energy = tonumber(meta["energy"]) or 0; -- if positive activates, negative deactivates, 0 does nothing
|
|
bounce = tonumber(meta["bounce"]) or 0; -- if nonzero bounces when hit obstacle, 0 gets absorbed
|
|
gravity = tonumber(meta["gravity"]) or 0; -- gravity
|
|
puncheable = tonumber(meta["puncheable"]) or 0; -- if 1 can be punched by players in protection, if 2 can be punched by anyone
|
|
|
|
if energy<0 then
|
|
obj:set_properties({textures={"basic_machines_ball.png^[colorize:blue:120"}})
|
|
end
|
|
|
|
luaent.bounce = bounce;
|
|
luaent.energy = energy;
|
|
if gravity>0 then
|
|
obj:setacceleration({x=0,y=-gravity,z=0});
|
|
end
|
|
luaent.puncheable = puncheable;
|
|
luaent.owner = meta["owner"];
|
|
luaent.hurt = math.min(tonumber(meta["hurt"]),basic_machines.ball.maxdamage);
|
|
|
|
obj:set_hp( tonumber(meta["hp"]) );
|
|
|
|
local x0,y0,z0;
|
|
if speed>0 then luaent.speed = speed end
|
|
|
|
|
|
|
|
local v = user:get_look_dir();
|
|
v.x=v.x*speed;v.y=v.y*speed;v.z=v.z*speed;
|
|
obj:setvelocity(v);
|
|
|
|
|
|
if tonumber(meta["admin"])==1 then
|
|
luaent.lifetime = tonumber(meta["lifetime"]);
|
|
end
|
|
|
|
|
|
obj:set_properties({textures={meta["texture"]}})
|
|
|
|
|
|
end,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
-- minetest.register_craft({
|
|
-- output = "basic_machines:ball_spawner",
|
|
-- recipe = {
|
|
-- {"basic_machines:power_cell"},
|
|
-- {"basic_machines:keypad"}
|
|
-- }
|
|
-- }) |