From a6508974a56e3b14b1e997b3c6382c2df767c70b Mon Sep 17 00:00:00 2001 From: Beha Date: Sat, 7 Jan 2017 03:07:35 -0500 Subject: [PATCH] Add turrets. --- magic/crafts.lua | 8 ++ magic/defaults.lua | 6 ++ magic/init.lua | 1 + magic/spells.lua | 5 +- magic/spells/action.lua | 3 +- magic/spells/attack.lua | 23 ++++- magic/spells/defense.lua | 3 + magic/textures/magic_turret.png | Bin 0 -> 3530 bytes magic/throwing.lua | 112 +++++++++++++++------- magic/turrets.lua | 163 ++++++++++++++++++++++++++++++++ manual.md | 3 +- 11 files changed, 285 insertions(+), 42 deletions(-) create mode 100644 magic/textures/magic_turret.png create mode 100644 magic/turrets.lua diff --git a/magic/crafts.lua b/magic/crafts.lua index bd23a04..b506e8b 100644 --- a/magic/crafts.lua +++ b/magic/crafts.lua @@ -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"}, + }, +}) diff --git a/magic/defaults.lua b/magic/defaults.lua index f5659bc..a058e18 100644 --- a/magic/defaults.lua +++ b/magic/defaults.lua @@ -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 diff --git a/magic/init.lua b/magic/init.lua index 52b75f2..eb6dde3 100644 --- a/magic/init.lua +++ b/magic/init.lua @@ -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") diff --git a/magic/spells.lua b/magic/spells.lua index 3933d2f..6f5505e 100644 --- a/magic/spells.lua +++ b/magic/spells.lua @@ -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 diff --git a/magic/spells/action.lua b/magic/spells/action.lua index 85e7e05..498fd99 100644 --- a/magic/spells/action.lua +++ b/magic/spells/action.lua @@ -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, }) diff --git a/magic/spells/attack.lua b/magic/spells/attack.lua index 6da8204..069e5cb 100644 --- a/magic/spells/attack.lua +++ b/magic/spells/attack.lua @@ -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, }) diff --git a/magic/spells/defense.lua b/magic/spells/defense.lua index 809ea8b..70e5745 100644 --- a/magic/spells/defense.lua +++ b/magic/spells/defense.lua @@ -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, diff --git a/magic/textures/magic_turret.png b/magic/textures/magic_turret.png new file mode 100644 index 0000000000000000000000000000000000000000..e62d276d724a1a932d773ada94e4063dc78fc15d GIT binary patch literal 3530 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 literal 0 HcmV?d00001 diff --git a/magic/throwing.lua b/magic/throwing.lua index 38d18d6..9c18142 100644 --- a/magic/throwing.lua +++ b/magic/throwing.lua @@ -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 diff --git a/magic/turrets.lua b/magic/turrets.lua new file mode 100644 index 0000000..044d748 --- /dev/null +++ b/magic/turrets.lua @@ -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 + diff --git a/manual.md b/manual.md index 5737d7b..6ab3401 100644 --- a/manual.md +++ b/manual.md @@ -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