Add turrets.

master
Beha 2017-01-07 03:07:35 -05:00
parent 85c807ec48
commit a6508974a5
11 changed files with 285 additions and 42 deletions

View File

@ -68,3 +68,11 @@ minetest.register_craft({
{"default:bronzeblock", "magic:rage_essence", "magic:control_essence"},
},
})
minetest.register_craft({
output = "magic:turret",
recipe = {
{"magic:rage_essence", "magic:control_essence"},
{"default:steelblock", "group:spellbinding"},
},
})

View File

@ -9,3 +9,9 @@ magic.config.mana_fast_regen_threshold = 18
-- Fast regeneration multiplier.
magic.config.mana_fast_regen = 2
-- Search radius of a missile turret.
magic.config.turret_missile_radius = 24
-- Block radius of a defense turret.
magic.config.turret_shield_radius = 5

View File

@ -31,6 +31,7 @@ domodfile("crafts.lua")
domodfile("timegens.lua")
domodfile("crystals.lua")
domodfile("spells.lua")
domodfile("turrets.lua")
domodfile("spells/action.lua")
domodfile("spells/attack.lua")

View File

@ -34,8 +34,11 @@ function magic.damage_obj(obj, g)
local heldstack = obj:get_wielded_item()
local def = minetest.registered_items[heldstack:get_name()]
local remove = false
if def.original.protects then
if (def.groups.spell or 0) > 0 and def.original.protects then
for k,protect in pairs(def.original.protects) do
if not groups[k] then
break
end
if def.original.harmful then
if not magic.require_energy(obj, def.original.cost) then
break

View File

@ -70,9 +70,10 @@ magic.register_spell("magic:spell_ice", {
emblem = "action",
speed = 20,
cost = 8,
element = "cold",
hit_node = drop_ice,
hit_object = function(self, pos, obj)
magic.damage_obj(obj, {cold = 4})
magic.damage_obj(obj, {cold = (self.was_near_turret > 0 and 2 or 4)})
return true
end,
})

View File

@ -6,6 +6,8 @@ magic.register_spell("magic:spell_fire", {
emblem = "attack",
speed = 30,
cost = 2,
allow_turret = true,
element = "fire",
hit_node = function(self, pos, last_empty_pos)
local flammable = minetest.get_item_group(minetest.get_node(pos).name, "flammable")
local puts_out = minetest.get_item_group(minetest.get_node(pos).name, "puts_out_fire")
@ -16,6 +18,8 @@ magic.register_spell("magic:spell_fire", {
if flammable > 0 then
minetest.set_node(pos, {name = "fire:basic_flame"})
return true
elseif magic.missile_passable(pos) then
return false
elseif last_empty_pos then
minetest.set_node(last_empty_pos, {name = "fire:basic_flame"})
return true
@ -23,7 +27,7 @@ magic.register_spell("magic:spell_fire", {
return false
end,
hit_object = function(self, pos, obj)
magic.damage_obj(obj, {fire = 4})
magic.damage_obj(obj, {fire = (self.was_near_turret > 0 and 2 or 4)})
return true
end,
})
@ -47,6 +51,9 @@ if rawget(_G, 'tnt') and tnt.boom then
-- This spell can travel through water.
return false
end
if magic.missile_passable(pos) then
return false
end
tnt.boom(pos, {
radius = 3,
damage_radius = 5,
@ -61,10 +68,16 @@ if rawget(_G, 'tnt') and tnt.boom then
speed = 15,
cost = 6,
gravity = 0.5,
element = "fire",
hit_node = hit_node,
hit_object = function(self, pos, obj)
return hit_node(self, pos)
end,
near_turret = function(self, pos, spell)
if spell.protects and spell.protects.fire and magic.use_turrent_spell(pos) then
return true
end
end,
})
minetest.register_craft({
output = "magic:spell_bomb",
@ -82,8 +95,10 @@ magic.register_spell("magic:spell_dart", {
emblem = "attack",
speed = 60,
cost = 1,
element = "fleshy",
allow_turret = true,
hit_object = function(self, pos, obj)
magic.damage_obj(obj, {fleshy = 2})
magic.damage_obj(obj, {fleshy = (self.was_near_turret > 0 and 1 or 2)})
return true
end,
})
@ -103,8 +118,10 @@ magic.register_spell("magic:spell_missile", {
emblem = "attack",
speed = 50,
cost = 1,
element = "magic",
allow_turret = true,
hit_object = function(self, pos, obj)
magic.damage_obj(obj, {magic = 1, fire = 1})
magic.damage_obj(obj, {magic = (self.was_near_turret > 0 and 0.5 or 1), fire = (self.was_near_turret > 0 and 0.5 or 1)})
return true
end,
})

View File

@ -5,6 +5,7 @@ magic.register_spell("magic:spell_dark_shield", {
color = "#222",
emblem = "defense",
cost = 1,
allow_turret = true,
protects = {
fire = {
max = 4,
@ -26,6 +27,7 @@ magic.register_spell("magic:spell_white_shield", {
color = "#DDD",
emblem = "defense",
cost = 1,
allow_turret = true,
protects = {
magic = {
max = 4,
@ -48,6 +50,7 @@ magic.register_spell("magic:spell_solid_shield", {
color = "#AA0",
emblem = "defense",
cost = 2,
allow_turret = true,
protects = {
fleshy = {
max = 4,

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -62,6 +62,14 @@ local function rayIter(pos, dir, range)
end
-- END COPIED
function magic.missile_passable(pos)
local def = minetest.registered_nodes[minetest.get_node(pos).name]
if not def.walkable and def.buildable_to then
return true
end
return false
end
function magic.register_missile(name, texture, def, item_def)
def.hit_object = def.hit_object or function(self, pos, obj)
@ -73,9 +81,16 @@ function magic.register_missile(name, texture, def, item_def)
end
def.hit_node = def.hit_node or function(self, pos, last_empty_pos)
if magic.missile_passable(pos) then
return false
end
return true
end
def.near_turret = def.near_turret or function(self, pos, spell)
return false
end
def.is_passthrough_node = def.is_passthrough_node or function(self, pos, node)
return node.name == "air"
end
@ -89,6 +104,7 @@ function magic.register_missile(name, texture, def, item_def)
textures = {texture},
lastpos={},
lastair = nil,
was_near_turret = 0,
collisionbox = {0,0,0,0,0,0},
}
@ -105,6 +121,7 @@ function magic.register_missile(name, texture, def, item_def)
elseif def.is_passthrough_node(self, vector.add(pos, {x=0, y=2, z=0}), minetest.get_node(vector.add(pos, {x=0, y=2, z=0}))) then
self.lastair = vector.add(pos, {x=0, y=2, z=0})
end
return
end
if self.timer > TIMEOUT then
@ -115,11 +132,12 @@ function magic.register_missile(name, texture, def, item_def)
local line = {
start = self.lastpos,
finish = pos,
middle = {
x = (self.lastpos.x + pos.x) / 2,
y = (self.lastpos.y + pos.y) / 2,
z = (self.lastpos.z + pos.z) / 2,
},
}
line.middle = {
x = (line.start.x + line.finish.x) / 2,
y = (line.start.y + line.finish.y) / 2,
z = (line.start.z + line.finish.z) / 2,
}
local Hit = {x=0, y=0, z=0};
@ -166,11 +184,11 @@ function magic.register_missile(name, texture, def, item_def)
end
local function CheckLineNear(line, pos, distance)
local nx = 0.5
local nx = 0.25
if line.finish.x < line.start.x then nx = -nx end
local ny = 0.5
local ny = 0.25
if line.finish.y < line.start.y then ny = -ny end
local nz = 0.5
local nz = 0.25
if line.finish.z < line.start.z then nz = -nz end
for x=line.start.x,line.finish.x,nx do
@ -186,31 +204,22 @@ function magic.register_missile(name, texture, def, item_def)
return false
end
local objs = minetest.get_objects_inside_radius(line.middle, (math.ceil(vector.distance(line.middle, line.start)) + math.ceil(vector.distance(line.middle, line.finish)) * 2) + 6)
for k, obj in pairs(objs) do
local bb = obj:get_properties().collisionbox
-- If bb collides with line...
local b1 = vector.add(obj:getpos(), vector.multiply({x=bb[1], y=bb[2], z=bb[3]}, 1.5))
local b2 = vector.add(obj:getpos(), vector.multiply({x=bb[4], y=bb[5], z=bb[6]}, 1.5))
if CheckLineBox(b1, b2, line.start, line.finish) or CheckLineNear(line, obj:getpos(), 1) then
if obj:get_luaentity() ~= nil then
if obj:get_luaentity().name ~= name and not NO_HIT_ENTS[obj:get_luaentity().name] then
if def.hit_object(self, obj:getpos(), obj) then
self.object:remove()
end
end
elseif obj:is_player() then
local can = true
if self.timer > 0.5 or not self.player or obj:get_player_name() ~= self.player:get_player_name() then
if def.hit_player(self, obj:getpos(), obj) then
self.object:remove()
end
end
end
end
for _,pos in ipairs(kingdoms.utils.find_nodes_by_area(pos, magic.config.turret_shield_radius, {"magic:turret"})) do
if self.kingdom == minetest.get_meta(pos):get_string("kingdom.id") then
break
end
local turret_spell = magic.get_turret_spell(pos)
if turret_spell.protects and turret_spell.protects[def.element] and (self.was_near_turret > 1 or magic.use_turrent_spell(pos)) then
self.was_near_turret = self.was_near_turret + 1
end
if def.near_turret(self, pos, turret_spell) then
self.object:remove()
return
end
end
local hitnode = nil
local willremove = false
for pos in rayIter(line.start, self.object:getvelocity(), vector.distance(line.start, line.finish)) do
local node = minetest.get_node(pos)
@ -225,9 +234,45 @@ function magic.register_missile(name, texture, def, item_def)
if hitnode then
if def.hit_node(self, hitnode, self.lastair) then
self.object:remove()
willremove = true
end
end
local objs = minetest.get_objects_inside_radius(line.middle, (math.ceil(vector.distance(line.middle, line.start)) + math.ceil(vector.distance(line.middle, line.finish)) * 2) + 6)
for k, obj in pairs(objs) do
local bb = obj:get_properties().collisionbox
local pp = vector.add(obj:getpos(), {x=0, y=0.5, z=0})
-- If bb collides with line...
local b1 = vector.add(pp, vector.multiply({x=bb[1], y=bb[2], z=bb[3]}, 1.5))
local b2 = vector.add(pp, vector.multiply({x=bb[4], y=bb[5], z=bb[6]}, 1.5))
if willremove and vector.distance(obj:getpos(), line.start) > vector.distance(hitnode, line.start) then
break
end
if CheckLineBox(b1, b2, line.start, line.finish) or CheckLineNear(line, pp, 1) then
if obj:get_luaentity() ~= nil then
if obj:get_luaentity().name ~= name and not NO_HIT_ENTS[obj:get_luaentity().name] then
if def.hit_object(self, obj:getpos(), obj) then
self.object:remove()
return
end
end
elseif obj:is_player() then
local can = true
if self.timer > 0.5 or not self.player or obj:get_player_name() ~= self.player:get_player_name() then
if def.hit_player(self, obj:getpos(), obj) then
self.object:remove()
return
end
end
end
end
end
if willremove then
self.object:remove()
return
end
if self.particletimer > 0.05 then
minetest.add_particle({
pos = pos,
@ -252,11 +297,8 @@ function magic.register_missile(name, texture, def, item_def)
obj:setvelocity({x=dir.x*def.speed, y=dir.y*def.speed, z=dir.z*def.speed})
obj:setacceleration({x=0, y=-8.5*(def.gravity or 0), z=0})
obj:setyaw(player:get_look_yaw()+math.pi)
if obj:get_luaentity() then
obj:get_luaentity().player = player
else
obj:remove()
end
obj:get_luaentity().player = player
obj:get_luaentity().kingdom = kingdoms.player.kingdom(player:get_player_name()) and kingdoms.player.kingdom(player:get_player_name()).id or nil
itemstack:take_item()
return itemstack
end

163
magic/turrets.lua Normal file
View File

@ -0,0 +1,163 @@
local function update_formspec(pos)
local meta = minetest.get_meta(pos)
local name = meta:get_string("spell_name")
name = (name ~= "") and name or "N/A"
local count = meta:get_int("spell_count")
meta:set_string("formspec", ([[
size[8,6]
label[0,0.1;Input spells. Current: ]]..tostring(name).." qty "..tostring(count)..[[]
button[0,1;8,1;setp_%d;Only Players (Current: %s)]
list[context;input;7,0;1,1;]
list[current_player;main;0,2;8,4;]
]]):format(meta:get_int("onlyplayers"), (meta:get_int("onlyplayers") == 1) and "yes" or "no"))
end
minetest.register_node("magic:turret", {
description = "Turret",
tiles = {"magic_turret.png"},
groups = {cracky = 1, kingdom_infotext = 1},
on_place = function(itemstack, placer, pointed_thing)
if not placer or pointed_thing.type ~= "node" then
return itemstack
end
local kingdom = kingdoms.player.kingdom(placer:get_player_name())
if not kingdom then
minetest.chat_send_player(placer:get_player_name(), "You cannot place a turret if you are not a member of a kingdom.")
return itemstack
end
return minetest.item_place(itemstack, placer, pointed_thing)
end,
after_place_node = function(pos, placer)
local kingdom = kingdoms.player.kingdom(placer:get_player_name())
local meta = minetest.get_meta(pos)
meta:set_string("kingdom.id", kingdom.id)
meta:set_string("spell_name", "")
meta:set_int("spell_count", 0)
meta:get_inventory():set_size("input", 8)
update_formspec(pos)
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
meta:get_inventory():set_list("input", {})
meta:set_string("spell_name", stack:get_name())
meta:set_int("spell_count", meta:get_int("spell_count") + stack:get_count())
meta:set_int("spell_max", stack:get_stack_max())
meta:set_int("onlyplayers", 0)
update_formspec(pos)
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
return 0
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
if listname ~= "input" then return 0 end
if not minetest.registered_items[stack:get_name()].original or not minetest.registered_items[stack:get_name()].original.allow_turret then
minetest.chat_send_player(player:get_player_name(), "You must place turret-enabled spells in this device.")
return 0
end
if stack:get_name() ~= meta:get_string("spell_name") and meta:get_int("spell_count") > 0 then
minetest.chat_send_player(player:get_player_name(), "This turret still is holding "..meta:get_string("spell_name"))
return 0
end
if not kingdoms.player.canpos(pos, player:get_player_name(), "devices") then
minetest.chat_send_player(player:get_player_name(), "You cannot use this device.")
return 0
end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
return 0
end,
on_destruct = function(pos)
local meta = minetest.get_meta(pos)
if meta:get_string("spell_name") ~= "" then
local count = meta:get_int("spell_count")
local needed = math.ceil(count / meta:get_int("spell_max"))
for i=1,needed do
minetest.add_item(pos, meta:get_string("spell_name").." "..tostring(math.min(meta:get_int("spell_max"), count)))
count = count - meta:get_int("spell_max")
end
end
end,
on_receive_fields = function(pos, formname, fields, player)
if not kingdoms.player.canpos(pos, player:get_player_name(), "devices") then
minetest.chat_send_player(player:get_player_name(), "You cannot use this device.")
return 0
end
local meta = minetest.get_meta(pos)
if fields.setp_0 then
meta:set_int("onlyplayers", 1)
elseif fields.setp_1 then
meta:set_int("onlyplayers", 0)
end
update_formspec(pos)
end,
})
minetest.register_abm({
nodenames = {"magic:turret"},
interval = 3,
chance = 1,
action = function(pos, node)
local meta = minetest.get_meta(pos)
if not kingdoms.db.kingdoms[meta:get_string("kingdom.id")] then
return
end
local name = meta:get_string("spell_name")
local count = meta:get_int("spell_count")
if count <= 0 then return end
local def = minetest.registered_items[name].original
if def.type == "missile" then
local closest = nil
local objs = minetest.get_objects_inside_radius(pos, magic.config.turret_missile_radius)
for _,obj in pairs(objs) do
local ok = true
if obj:get_luaentity() ~= nil and meta:get_int("onlyplayers") == 0 then
if NO_HIT_ENTS[obj:get_luaentity().name] then
ok = false
end
elseif obj:is_player() then
if kingdoms.player.kingdom(obj:get_player_name()) and kingdoms.player.kingdom(obj:get_player_name()).id == meta:get_string("kingdom.id") then
ok = false
end
end
if ok and (not closest or vector.distance(obj:getpos(), pos) < vector.distance(closest:getpos(), pos)) then
closest = obj
end
end
if closest then
local dir = vector.normalize{x=closest:getpos().x - pos.x, y=(closest:getpos().y + 0.5) - pos.y, z=closest:getpos().z - pos.z}
local mobj = minetest.add_entity(pos, name.."_missile")
mobj:setvelocity({x=dir.x*def.speed, y=dir.y*def.speed, z=dir.z*def.speed})
mobj:setacceleration({x=0, y=-8.5*(def.gravity or 0), z=0})
mobj:get_luaentity().kingdom = meta:get_string("kingdom.id")
meta:set_int("spell_count", count - 1)
update_formspec(pos)
end
end
end,
})
function magic.get_turret_spell(pos)
local meta = minetest.get_meta(pos)
local name = meta:get_string("spell_name")
local count = meta:get_int("spell_count")
if count <= 0 then return end
return minetest.registered_items[name].original
end
function magic.use_turret_spell(pos)
local meta = minetest.get_meta(pos)
local name = meta:get_string("spell_name")
local count = meta:get_int("spell_count")
if count <= 0 then return false end
meta:set_int("spell_count", count - 1)
update_formspec(pos)
return true
end

View File

@ -16,9 +16,8 @@ These claims cannot overlap with other claims, and must be spaced with at least
Defending a neutral area, or even preventing enemies from entering your clamied area, is enhanced beyond personal battle by the addition of several nodes:
* (TODO) Turrets, which ward off enemies by blasting spells at them.
* Turrets, which, after a missile or shield spell is inserted, will ward off enemies by blasting spells at them and cancel or subdue enemy spells.
* Materializers and Materialized Walls. The Walls act like hard stone, but when a Materializer is placed by them they will level from 1 to 4 over time. A level 4 node, when dug, will revert to a level 3 instead of vanishing, and so on down to a level 1. They will also absorb explosions, preventing damage inside them.
* (TODO) Spell Wards, which prevent destructive or disruptive spells from entering your territory.
## Destroying a Corestone