Add turrets.
parent
85c807ec48
commit
a6508974a5
|
@ -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"},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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 |
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue