Initial commit.

master
Beha 2017-02-09 22:10:10 -05:00
commit a37fea1360
30 changed files with 1739 additions and 0 deletions

78
crafts.lua Normal file
View File

@ -0,0 +1,78 @@
function magic.register_crystal_craft(def)
minetest.register_craft({
type = "cooking",
output = "magic:"..def.name.."_essence",
recipe = "magic:crystal_"..def.name,
cooktime = 6,
})
minetest.register_craft({
type = "shapeless",
output = "magic:crystal_"..def.name,
recipe = {"magic:"..def.name.."_essence", "group:spellbinding", "group:stone"}
})
minetest.register_craft({
type = "shapeless",
output = "magic:"..def.name.."_essence 3",
recipe = {"magic:concentrated_"..def.name.."_essence"}
})
minetest.register_craft({
type = "shapeless",
output = "magic:concentrated_"..def.name.."_essence",
recipe = {"magic:"..def.name.."_essence", "magic:"..def.name.."_essence", "magic:"..def.name.."_essence"}
})
minetest.register_craft({
type = "shapeless",
output = "magic:crystal_"..def.name.." 9",
recipe = {"magic:concentrated_crystal_"..def.name}
})
minetest.register_craft({
output = "magic:concentrated_crystal_"..def.name,
recipe = {
{"magic:crystal_"..def.name, "magic:crystal_"..def.name, "magic:crystal_"..def.name},
{"magic:crystal_"..def.name, "magic:crystal_"..def.name, "magic:crystal_"..def.name},
{"magic:crystal_"..def.name, "magic:crystal_"..def.name, "magic:crystal_"..def.name},
},
})
if def.fuel then
minetest.register_craft({
type = "fuel",
recipe = "magic:crystal_"..def.name,
burntime = def.fuel,
})
end
end
minetest.register_craft({
type = "shapeless",
output = "magic:null_essence 4",
recipe = {"group:minor_essence", "default:gold_ingot", "group:minor_essence"},
})
minetest.register_craft({
output = "magic:nightcall",
recipe = {
{"magic:area_essence", "group:spellbinding", ""},
{"default:bronzeblock", "magic:calm_essence", "magic:control_essence"},
},
})
minetest.register_craft({
output = "magic:daypull",
recipe = {
{"magic:area_essence", "group:spellbinding", ""},
{"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"},
},
})

259
crystals.lua Normal file
View File

@ -0,0 +1,259 @@
magic.crystals = {
{
name = "rage",
desc = "Rage",
color = "#A00",
light = 10,
fuel = 500,
ores = {
{
rarity = 4 * 4 * 4,
clust_num_ores = 1,
clust_size = 1,
wherein = "default:lava_source",
y_max = -64,
},
},
},
{
name = "solidity",
desc = "Solidity",
color = "#AA0",
light = 4,
},
{
name = "area",
desc = "Area",
color = "#033",
light = 8,
},
{
name = "warp",
desc = "Warp",
color = "#0CC",
light = 13,
rarity = 0.5,
},
{
name = "control",
desc = "Control",
color = "#707",
light = 7,
},
{
name = "vitality",
desc = "Vitality",
color = "#0F0",
light = 12,
ores = {
{
rarity = 18 * 18 * 18,
clust_num_ores = 1,
clust_size = 1,
wherein = "default:dirt",
},
},
},
{
name = "calm",
desc = "Calm",
color = "#00F",
light = 5,
ores = {
{
rarity = 4 * 4 * 4,
clust_num_ores = 1,
clust_size = 1,
wherein = "default:water_source",
y_max = -128,
},
{
rarity = 16 * 16 * 16,
clust_num_ores = 1,
clust_size = 1,
wherein = "default:water_source",
y_min = -128,
y_max = -8,
},
},
},
{
name = "day",
desc = "Day",
color = "#FFF",
light = 14,
fuel = 350,
nodefgen = true,
ores = {
{
rarity = 15 * 15 * 15,
clust_num_ores = 3,
clust_size = 2,
y_min = 400,
wherein = "air"
},
},
},
{
name = "night",
desc = "Night",
color = "#000",
light = 0,
nodefgen = true,
ores = {
{
rarity = 15 * 15 * 15,
clust_num_ores = 3,
clust_size = 2,
y_min = 400,
wherein = "air"
},
},
},
}
minetest.register_craftitem("magic:null_essence", {
description = "Null Essence",
inventory_image = "magic_essence.png",
})
function magic.register_crystal(def, nocraft)
minetest.register_node("magic:crystal_"..def.name, {
description = def.desc.." Crystal",
drawtype = "glasslike",
tiles = {"magic_crystal.png^[colorize:"..def.color..":"..tostring(0xCC)},
groups = {cracky = 2, not_in_creative_inventory = (def.hidecrystal and 1 or 0)},
light_source = def.light or 7,
sunlight_propagates = true,
use_texture_alpha = true,
paramtype = "light",
sounds = default.node_sound_stone_defaults(),
})
minetest.register_node("magic:concentrated_crystal_"..def.name, {
description = "Compressed "..def.desc.." Crystal",
tiles = {"magic_concentrated_crystal.png^[colorize:"..def.color..":"..tostring(0xCC)},
is_ground_content = false,
drawtype = "glasslike",
light_source = def.light or 7,
sunlight_propagates = true,
use_texture_alpha = true,
paramtype = "light",
groups = {cracky = 2, not_in_creative_inventory = (def.hidecrystal and 1 or 0)},
sounds = default.node_sound_stone_defaults(),
})
if rawget(_G, 'ancient_world') then
ancient_world.register_item("magic:concentrated_crystal_"..def.name, (4 / #magic.crystals) * (def.rarity or 1))
end
minetest.register_craftitem("magic:"..def.name.."_essence", {
description = def.desc.." Essence",
inventory_image = "magic_essence.png^[colorize:"..def.color..":"..tostring(0xCC),
groups = {minor_essence = 1},
})
minetest.register_craftitem("magic:concentrated_"..def.name.."_essence", {
description = "Concentrated "..def.desc.." Essence",
inventory_image = "magic_concentrated_essence.png^[colorize:"..def.color..":"..tostring(0xCC),
})
local ndefd = {
ore_type = "scatter",
ore = "magic:crystal_"..def.name,
wherein = "default:stone",
clust_num_ores = 1,
clust_size = 1,
y_min = -31000,
y_max = 31000,
}
if def.ores then
for _,oredef in ipairs(def.ores) do
local ndef = table.copy(ndefd)
ndef.clust_scarcity = oredef.rarity * #magic.crystals * (def.rarity or 1)
for k,v in pairs(oredef) do
ndef[k] = v
end
minetest.register_ore(ndef)
end
end
if not def.nodefgen then
local ores = {
-- 30k belt.
{
rarity = 8 * 8 * 8,
clust_num_ores = 4,
clust_size = 3,
y_max = -30000,
y_min = -30008,
},
-- 20k belt.
{
rarity = 9 * 9 * 9,
clust_num_ores = 4,
clust_size = 3,
y_max = -20000,
y_min = -20016,
},
-- 10k belt.
{
rarity = 10 * 10 * 10,
clust_num_ores = 4,
clust_size = 3,
y_max = -10000,
y_min = -10025,
},
{
rarity = 12 * 12 * 12,
clust_num_ores = 4,
clust_size = 2,
y_max = -2048,
},
{
rarity = 13 * 13 * 13,
clust_num_ores = 3,
clust_size = 2,
y_max = -1024,
y_min = -2048,
},
{
rarity = 14 * 14 * 14,
clust_num_ores = 3,
clust_size = 2,
y_max = -512,
y_min = -1024,
},
{
rarity = 16 * 16 * 16,
clust_num_ores = 3,
clust_size = 2,
y_max = -64,
y_min = -512,
},
{
rarity = 14 * 14 * 14,
clust_num_ores = 3,
clust_size = 2,
y_min = 50,
},
}
for _,oredef in ipairs(ores) do
local ndef = table.copy(ndefd)
ndef.clust_scarcity = oredef.rarity * #magic.crystals * (def.rarity or 1)
for k,v in pairs(oredef) do
ndef[k] = v
end
minetest.register_ore(ndef)
end
end
if not nocraft then
magic.register_crystal_craft(def)
end
end
for _,def in ipairs(magic.crystals) do
magic.register_crystal(def)
end

20
defaults.lua Normal file
View File

@ -0,0 +1,20 @@
-- Maximum mana.
magic.config.max_mana = 20
-- Seconds to regnerate one mana point. (Faster if mana_fast_regen_threshold applies.)
magic.config.mana_regen = 6
-- If player HP is >= this, mana will regenerate faster.
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
-- Delay before actually teleporting.
magic.config.teleportation_delay = 5
-- Enable short teleports.
magic.config.enable_short_teleports = true

4
depends.txt Normal file
View File

@ -0,0 +1,4 @@
default
kingdoms_meta
ancient_world?
tnt?

40
init.lua Normal file
View File

@ -0,0 +1,40 @@
-- Function to execute more files.
local modpath = minetest.get_modpath("magic")
local function domodfile(f)
dofile(modpath .. '/' .. f)
end
-- Mod namespace.
magic = {}
magic.config = kingdoms.config_table("magic")
magic.log = kingdoms.log_function("magic")
domodfile("defaults.lua")
minetest.override_item("default:mese", {
groups = {cracky = 1, level = 2, major_spellbinding = 1}
})
domodfile("mana.lua")
domodfile("throwing.lua")
domodfile("mapgen.lua")
domodfile("crafts.lua")
domodfile("timegens.lua")
domodfile("crystals.lua")
domodfile("spells.lua")
domodfile("potions.lua")
domodfile("turrets.lua")
domodfile("spells/action.lua")
domodfile("spells/attack.lua")
domodfile("spells/defense.lua")
domodfile("spells/teleportation.lua")
if rawget(_G, "ancient_world") then
domodfile("structures.lua")
end
magic.log("action", "Loaded.")
kingdoms.mod_ready("magic")

79
mana.lua Normal file
View File

@ -0,0 +1,79 @@
magic.manadb = {}
local huds = {}
local timer = 0
kingdoms.at_mod_load("kingdoms", function()
kingdoms.db.magic = kingdoms.db.magic or {}
kingdoms.db.magic.mana = kingdoms.db.magic.mana or {}
magic.manadb = kingdoms.db.magic.mana
end)
minetest.register_globalstep(function(dtime)
timer = timer + dtime
if timer < 0.1 then return end
timer = 0
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
local p = magic.manadb[name]
p.mana = math.min(p.max_mana, p.mana + (dtime / magic.config.mana_regen) * (player:get_hp() >= magic.config.mana_fast_regen_threshold and magic.config.mana_fast_regen or 1 ))
local hud = huds[name]
if not hud then
hud = {}
huds[name] = hud
hud.id = player:hud_add({
name = "mana",
hud_elem_type = "statbar",
position = {x = 0.5, y = 1},
size = {x = 24, y = 24},
text = "magic_essence.png^[colorize:#00A:200",
number = p.mana,
alignment = {x = -1, y = -1},
offset = {x = -266, y = -110 - 24},
max = 0,
})
hud.oldvalue = p.mana
return
elseif hud.oldvalue ~= p.mana then
player:hud_change(hud.id, "number", p.mana)
hud.oldvalue = p.mana
end
end
end)
function magic.require_mana(player, cost, message)
local p = magic.manadb[player:get_player_name()]
if not p then return false end
if p.mana < cost then
if message then minetest.chat_send_player(player:get_player_name(), "You do not have enough mana.") end
return false
end
p.mana = math.max(0, p.mana - cost)
return true
end
function magic.require_energy(player, cost, message)
local p = magic.manadb[player:get_player_name()]
if not p then return false end
if magic.require_mana(player, cost) then
return true
end
local newcost = cost - p.mana
p.mana = 0
if player:get_hp() <= newcost then
if message then minetest.chat_send_player(player:get_player_name(), "You do not have enough health.") end
return false
end
player:set_hp(player:get_hp() - newcost)
return true
end
minetest.register_on_joinplayer(function(player)
magic.manadb[player:get_player_name()] = magic.manadb[player:get_player_name()] or {
mana = magic.config.max_mana,
}
magic.manadb[player:get_player_name()].max_mana = magic.config.max_mana
end)
minetest.register_on_leaveplayer(function(player)
huds[player:get_player_name()] = nil
end)

13
mapgen.lua Normal file
View File

@ -0,0 +1,13 @@
function magic.register_mapgen()
-- Vitality crystals form in warm forests.
minetest.register_decoration({
deco_type = "simple",
place_on = {"default:dirt_with_grass"},
sidelen = 80,
fill_ratio = 0.00025,
biomes = {"rainforest", "deciduous_forest", "coniferous_forest"},
y_min = 1,
y_max = 31000,
decoration = "magic:crystal_vitality",
})
end

81
potions.lua Normal file
View File

@ -0,0 +1,81 @@
function magic.register_potion(name, def)
local item_def = {
description = def.description,
drawtype = "plantlike",
tiles = {"vessels_glass_bottle.png^[colorize:"..def.color..":200"},
inventory_image = "vessels_glass_bottle.png^[colorize:"..def.color..":200",
wield_image = "vessels_glass_bottle.png^[colorize:"..def.color..":200",
paramtype = "light",
is_ground_content = false,
walkable = false,
selection_box = {
type = "fixed",
fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25}
},
groups = {vessel = 1, dig_immediate = 3, attached_node = 1},
sounds = default.node_sound_glass_defaults(),
}
local function docost(player)
if def.harmful then
return magic.require_energy(player, def.cost, true)
else
return magic.require_mana(player, def.cost, true)
end
end
if def.on_use then
function item_def.on_use(itemstack, player)
if def.cost then
if not docost(player) then return end
end
if def.on_use(itemstack, player) then
if player:get_inventory():room_for_item("main", "vessels:glass_bottle") then
player:get_inventory():add_item("main", "vessels:glass_bottle")
else
minetest.add_item(pos, "vessels:glass_bottle")
end
itemstack:take_item()
end
return itemstack
end
end
minetest.register_node(name, item_def)
end
magic.register_potion("magic:water_bottle", {
description = "Bottle of Water",
color = "#02A",
})
minetest.register_craft({
output = "magic:water_bottle",
type = "shapeless",
recipe = {"vessels:glass_bottle", "bucket:bucket_water"},
replacements = {{"bucket:bucket_water", "bucket:bucket_empty"}},
})
magic.register_potion("magic:purified_water_bottle", {
description = "Purified Bottle of Water",
color = "#23B",
})
minetest.register_craft({
type = "cooking",
output = "magic:purified_water_bottle",
recipe = "magic:water_bottle",
cooktime = 5,
})
magic.register_potion("magic:minor_mana_potion", {
description = "Minor Mana Restoration Potion",
color = "#00F",
on_use = function(itemstack, player)
magic.require_mana(player, -2)
return true
end,
})
minetest.register_craft({
output = "magic:minor_mana_potion",
type = "shapeless",
recipe = {"magic:water_bottle", "flowers:geranium"},
})

Binary file not shown.

Binary file not shown.

Binary file not shown.

84
spells.lua Normal file
View File

@ -0,0 +1,84 @@
function magic.register_spell(name, def)
local item_def = {
description = def.description..(" (%d)"):format(def.cost),
inventory_image = "magic_essence.png^[colorize:"..def.color..":"..tostring(0xCC).."^magic_emblem_"..def.emblem..".png",
groups = def.groups or {spell = 1},
original = def,
}
local function docost(player)
-- If the spell is harmful, it will dip into player health when mana runs out.
if def.harmful then
return magic.require_energy(player, def.cost, true)
else
return magic.require_mana(player, def.cost, true)
end
end
if def.type == "missile" then
local f = magic.register_missile(name.."_missile", item_def.inventory_image, def, item_def)
function item_def.on_use(itemstack, player, pointed_thing)
if not docost(player) then return end
return f(itemstack, player, pointed_thing)
end
elseif def.type == "action" then
function item_def.on_use(itemstack, player, pointed_thing)
if not docost(player) then return end
if def.on_use(itemstack, player, pointed_thing) then
itemstack:take_item()
end
return itemstack
end
elseif def.type == "shield" then
-- magic.damage_obj handles shields.
else
error("Unknown spell type: "..def.type)
end
minetest.register_craftitem(name, item_def)
end
-- Convert all damage to fleshy.
function magic.damage_obj(obj, g)
local groups = table.copy(g)
-- If the target is a player, apply shields.
if obj:is_player() then
local heldstack = obj:get_wielded_item()
local def = minetest.registered_items[heldstack:get_name()]
local remove = false
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
end
else
if not magic.require_mana(obj, def.original.cost) then
break
end
end
remove = true
groups[k] = math.max(0, math.min(protect.max, groups[k] * protect.factor))
end
end
if remove then
heldstack:take_item()
obj:set_wielded_item(heldstack)
end
end
local x = 0
local armor = obj:get_armor_groups()
for k,v in pairs(groups) do
local factor = 1
if k ~= 'fleshy' then
factor = (armor.fleshy or 100) / 100
end
local delta = (v / factor)
x = x + delta
end
obj:punch(obj, 1.0, {full_punch_interval=1.0, damage_groups={fleshy=x}, nil})
-- Magic damage has a chance to drain mana (or deal extra damage if the target doesn't have enough mana).
if obj:is_player() and groups.magic and groups.magic > 0 then
magic.require_energy(obj, math.random(0, math.max(1, math.ceil(groups.magic / 3))))
end
end

71
spells/action.lua Normal file
View File

@ -0,0 +1,71 @@
-- Create a small explosion of flowing water.
local function drop_water(self, pos)
local water = "default:water_flowing"
local limit = 5
-- Put out fires first.
local positions = kingdoms.utils.shuffled(kingdoms.utils.find_nodes_by_area(pos, 3, {"fire:basic_flame"}))
for _,p in ipairs(positions) do
limit = limit - 1
minetest.set_node(p, {name=water})
if limit <= 0 then
break
end
end
limit = math.max(limit, 3)
-- A smaller air radius, avoiding travel through thick walls.
positions = kingdoms.utils.shuffled(kingdoms.utils.find_nodes_by_area(pos, 1, {"air"}))
for _,p in ipairs(positions) do
limit = limit - 1
minetest.set_node(p, {name=water})
if limit <= 0 then
break
end
end
return true
end
magic.register_spell("magic:spell_water", {
description = "Water Spell",
type = "missile",
color = "#00F",
emblem = "action",
speed = 20,
cost = 4,
gravity = 0.25,
hit_node = drop_water,
hit_object = drop_water,
})
minetest.register_craft({
output = "magic:spell_water 2",
recipe = {
{"magic:calm_essence", "group:minor_spellbinding", "magic:area_essence"},
},
})
magic.register_spell("magic:spell_heal_minor", {
description = "Minor Healing Spell",
type = "missile",
color = "#CCF",
emblem = "action",
speed = 30,
cost = 4,
allow_turret = true,
friendly_turret = true,
check_object = function(obj, ok)
if obj:get_hp() >= 20 then
return false
end
return ok
end,
hit_object = function(self, pos, obj)
magic.damage_obj(obj, {
healing = (self.was_near_turret > 0 and -1 or -2),
})
end,
})
minetest.register_craft({
output = "magic:spell_heal_minor 3",
recipe = {
{"magic:calm_essence", "group:minor_spellbinding", "magic:day_essence"},
},
})

176
spells/attack.lua Normal file
View File

@ -0,0 +1,176 @@
-- The fireball, ignites flames and deals fire damage.
magic.register_spell("magic:spell_fire", {
description = "Fire Spell",
type = "missile",
color = "#F00",
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")
if puts_out > 0 then
-- No chance of a fire starting here.
return true
end
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
end
return false
end,
hit_object = function(self, pos, obj)
magic.damage_obj(obj, {fire = (self.was_near_turret > 0 and 2 or 4)})
return true
end,
})
minetest.register_craft({
output = "magic:spell_fire",
recipe = {
{"magic:rage_essence", "group:minor_spellbinding"},
},
})
minetest.register_craft({
type = "fuel",
recipe = "magic:spell_fire",
burntime = 550,
})
-- The bomb, creates a TNT-style explosion at the contact point.
if rawget(_G, 'tnt') and tnt.boom then
local hit_node = function(self, pos, last_empty_pos)
local puts_out = minetest.get_item_group(minetest.get_node(pos).name, "puts_out_fire")
if puts_out > 0 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,
})
return true
end
magic.register_spell("magic:spell_bomb", {
description = "Bomb Spell",
type = "missile",
color = "#FA0",
emblem = "attack",
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",
recipe = {
{"magic:spell_fire", "group:minor_spellbinding", "magic:area_essence"},
},
})
end
-- A weak but cheap dart.
magic.register_spell("magic:spell_dart", {
description = "Dart Spell",
type = "missile",
color = "#333",
emblem = "attack",
speed = 60,
cost = 1,
element = "fleshy",
allow_turret = true,
hit_object = function(self, pos, obj)
magic.damage_obj(obj, {fleshy = (self.was_near_turret > 0 and 1 or 2)})
return true
end,
})
minetest.register_craft({
output = "magic:spell_dart 6",
recipe = {
{"magic:area_essence", "magic:solidity_essence"},
{"group:minor_spellbinding", "group:stone"},
},
})
-- A weak dart that deals armor-bypassing magic and fire damage.
magic.register_spell("magic:spell_missile", {
description = "Missile Spell",
type = "missile",
color = "#04F",
emblem = "attack",
speed = 50,
cost = 1,
element = "magic",
allow_turret = true,
hit_object = function(self, pos, obj)
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,
})
minetest.register_craft({
output = "magic:spell_missile 2",
recipe = {
{"magic:rage_essence", "magic:day_essence", "group:minor_spellbinding"},
},
})
local function drop_ice(self, pos)
self.firsthit = self.firsthit or pos
if minetest.get_node(pos).name ~= "default:water_source" and minetest.get_node(pos).name ~= "default:water_flowing" then
return true
else
if vector.distance(self.firsthit, pos) >= 16 then
return true
end
end
local limit = 6
local positions = kingdoms.utils.shuffled(kingdoms.utils.find_nodes_by_area_under_air(pos, 4, {"default:water_source"}))
for _,p in ipairs(positions) do
limit = limit - 1
minetest.set_node(p, {name="default:ice"})
if limit <= 0 then
break
end
end
end
-- Convert water sources to ice.
magic.register_spell("magic:spell_ice", {
description = "Ice Spell",
type = "missile",
color = "#08B",
emblem = "attack",
speed = 20,
cost = 8,
element = "cold",
hit_node = drop_ice,
hit_object = function(self, pos, obj)
magic.damage_obj(obj, {cold = (self.was_near_turret > 0 and 2 or 4)})
return true
end,
})
minetest.register_craft({
output = "magic:spell_ice 2",
recipe = {
{"magic:concentrated_night_essence", "magic:calm_essence", ""},
{"group:minor_spellbinding", "magic:area_essence", "magic:solidity_essence"},
},
})

66
spells/defense.lua Normal file
View File

@ -0,0 +1,66 @@
-- Absorb rage damage.
magic.register_spell("magic:spell_dark_shield", {
description = "Dark Shield Spell",
type = "shield",
color = "#222",
emblem = "defense",
cost = 1,
allow_turret = true,
protects = {
fire = {
max = 4,
factor = 0.5,
},
},
})
minetest.register_craft({
output = "magic:spell_dark_shield 9",
recipe = {
{"group:minor_spellbinding", "magic:night_essence"},
},
})
-- Refract magic damage.
magic.register_spell("magic:spell_white_shield", {
description = "White Shield Spell",
type = "shield",
color = "#DDD",
emblem = "defense",
cost = 1,
allow_turret = true,
protects = {
magic = {
max = 4,
factor = 0.5,
},
},
harmful = true,
})
minetest.register_craft({
output = "magic:spell_white_shield 9",
recipe = {
{"group:minor_spellbinding", "magic:day_essence"},
},
})
-- Block fleshy damage.
magic.register_spell("magic:spell_solid_shield", {
description = "Solid Shield Spell",
type = "shield",
color = "#AA0",
emblem = "defense",
cost = 2,
allow_turret = true,
protects = {
fleshy = {
max = 4,
factor = 0.5,
},
},
})
minetest.register_craft({
output = "magic:spell_solid_shield 9",
recipe = {
{"group:minor_spellbinding", "magic:solidity_essence"},
},
})

130
spells/teleportation.lua Normal file
View File

@ -0,0 +1,130 @@
local teleporting = {}
minetest.register_globalstep(function(dtime)
for _, player in pairs(minetest.get_connected_players()) do
local name = player:get_player_name()
local tp = teleporting[name]
if tp then
tp.timer = tp.timer + dtime
if vector.distance(player:getpos(), tp.start) > 0.1 then
magic.cancel_teleportation(name)
elseif tp.timer >= tp.delay then
minetest.registered_items[tp.item].original.go(player)
teleporting[name] = nil
end
end
end
end)
function magic.start_teleportation(player, item, delay)
local name = player:get_player_name()
if teleporting[name] then
magic.cancel_teleportation(name)
end
teleporting[name] = {
timer = 0,
delay = delay,
item = item,
start = player:getpos(),
}
minetest.chat_send_player(name, "Teleportation will occur in "..tostring(delay).." seconds. Remain still.")
end
function magic.cancel_teleportation(name)
minetest.chat_send_player(name, "Teleportation has been canceled.")
teleporting[name] = nil
end
minetest.register_on_leaveplayer(function(player)
magic.cancel_teleportation(player:get_player_name())
end)
magic.register_spell("magic:spell_teleport_spawn", {
description = "Spawn Teleportation Spell",
type = "action",
color = "#0A0",
emblem = "action",
cost = 15,
on_use = function(itemstack, player)
magic.start_teleportation(player, itemstack:get_name(), magic.config.teleportation_delay)
return true
end,
go = function(player)
if not kingdoms.db.servercorestone then
minetest.chat_send_player(player:get_player_name(), "There is no destination.")
return
end
player:setpos(vector.add(kingdoms.db.servercorestone, {x=0, y=1, z=0}))
end,
})
minetest.register_craft({
output = "magic:spell_teleport_spawn",
recipe = {
{"magic:concentrated_warp_essence", "magic:control_essence"},
{"group:minor_spellbinding", "default:sapling"},
},
})
magic.register_spell("magic:spell_teleport_kingdom", {
description = "Kingdom Teleportation Spell",
type = "action",
color = "#FA0",
emblem = "action",
cost = 15,
on_use = function(itemstack, player)
magic.start_teleportation(player, itemstack:get_name(), magic.config.teleportation_delay)
return true
end,
go = function(player)
local kingdom = kingdoms.player.kingdom(player:get_player_name())
if not kingdom or not kingdom.corestone.pos then
minetest.chat_send_player(player:get_player_name(), "There is no destination.")
return
end
player:setpos(vector.add(kingdom.corestone.pos, {x=0, y=1, z=0}))
end,
})
minetest.register_craft({
output = "magic:spell_teleport_kingdom",
recipe = {
{"magic:concentrated_warp_essence", "magic:control_essence"},
{"group:minor_spellbinding", "default:junglesapling"},
},
})
if magic.config.enable_short_teleports then
magic.register_spell("magic:spell_short_teleport", {
description = "Short Teleportation Spell",
type = "missile",
color = "#F0F",
emblem = "action",
speed = 10,
cost = 7,
gravity = 0.35,
forceload = true,
near_turret = function(self, pos, spell)
if spell.protects and spell.protects.magic and magic.use_turrent_spell(pos) then
return true
end
end,
hit_node = function(self, pos, last_empty_pos)
local akingdom = kingdoms.bypos(last_empty_pos)
if self.player then
local pkingdom = kingdoms.player.kingdom(self.player:get_player_name())
if akingdom and (not pkingdom or pkingdom.id ~= akingdom.id) then
minetest.chat_send_player(self.player:get_player_name(), "You cannot teleport there.")
return true
end
self.player:setpos(last_empty_pos)
end
return true
end,
hit_object = function() return false end
})
minetest.register_craft({
output = "magic:spell_short_teleport",
recipe = {
{"magic:concentrated_warp_essence", "magic:control_essence"},
{"group:minor_spellbinding", ""},
},
})
end

47
structures.lua Normal file
View File

@ -0,0 +1,47 @@
ancient_world.register("magic:magic_hovel_1", {
schematic = minetest.get_modpath("magic") .. "/schematics/magic_hovel_1.mts",
type = "decoration",
limit_y = {
max = -1024,
min = -31000,
},
on = {"default:stone"},
random_replacements = {
["ancient_world:placeholder_1"] = {"magic:nightcall", "magic:daypull"},
},
})
ancient_world.register("magic:magic_hovel_2", {
schematic = minetest.get_modpath("magic") .. "/schematics/magic_hovel_2.mts",
type = "decoration",
on = {"default:dirt_with_grass"},
offset = {
x = 0,
y = -3,
z = 0,
},
random_replacements = {
["ancient_world:placeholder_1"] = true,
},
})
ancient_world.register("magic:underground_lab_1", {
schematic = minetest.get_modpath("magic") .. "/schematics/underground_lab_1.mts",
type = "decoration",
limit_y = {
max = -512,
min = -31000,
},
offset = {
x = 0,
y = -16,
z = 0,
},
on = {"default:stone"},
replacements = {
["ancient_world:placeholder_2"] = "air",
},
random_replacements = {
["ancient_world:placeholder_1"] = true,
},
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
textures/magic_crystal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
textures/magic_daypull.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
textures/magic_essence.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
textures/magic_turret.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

313
throwing.lua Normal file
View File

@ -0,0 +1,313 @@
-- Throwing movement physics.
NO_HIT_ENTS = {
["__builtin:item"] = true,
["itemframes:item"] = true,
["xdecor:f_item"] = true,
}
local TIMEOUT = 300
-- ORIGINALLY COPIED FROM technic, under LGPL v2
-- BEGIN COPIED
local scalar = vector.scalar or vector.dot or function(v1, v2)
return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z
end
local function biggest_of_vec(vec)
if vec.x < vec.y then
if vec.y < vec.z then
return "z"
end
return "y"
end
if vec.x < vec.z then
return "z"
end
return "x"
end
local function rayIter(pos, dir, range)
-- make a table of possible movements
local step = {}
for i in pairs(pos) do
local v = math.sign(dir[i])
if v ~= 0 then
step[i] = v
end
end
local p
return function()
if not p then
-- avoid skipping the first position
p = vector.round(pos)
return vector.new(p)
end
-- find the position which has the smallest distance to the line
local choose = {}
local choosefit = vector.new()
for i in pairs(step) do
choose[i] = vector.new(p)
choose[i][i] = choose[i][i] + step[i]
choosefit[i] = scalar(vector.normalize(vector.subtract(choose[i], pos)), dir)
end
p = choose[biggest_of_vec(choosefit)]
if vector.distance(pos, p) <= range then
return vector.new(p)
end
end
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)
return true
end
def.hit_player = def.hit_player or function(self, pos, obj)
return def.hit_object(self, pos, obj)
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
local ent_def = {
physical = false,
hp_max = math.ceil(def.cost / 2),
timer=0,
particletimer = 0,
visual = "sprite",
visual_size = {x=0.4, y=0.4},
textures = {texture},
lastpos={},
lastair = nil,
was_near_turret = 0,
collisionbox = {0,0,0,0,0,0},
}
ent_def.on_step = function(self, dtime)
self.timer=self.timer+dtime
self.particletimer = self.particletimer + dtime
local pos = self.object:getpos()
if self.lastpos.x == nil then
self.lastpos = pos
if def.is_passthrough_node(self, pos, minetest.get_node(pos).name) then
self.lastair = pos
elseif def.is_passthrough_node(self, vector.add(pos, {x=0, y=1, z=0}), minetest.get_node(vector.add(pos, {x=0, y=1, z=0}))) then
self.lastair = vector.add(pos, {x=0, y=1, z=0})
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
self.object:remove()
return
end
local line = {
start = self.lastpos,
finish = pos,
}
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};
local function GetIntersection(fDst1, fDst2, P1, P2)
if ( (fDst1 * fDst2) >= 0.0) then return nil end
if ( fDst1 == fDst2) then return nil end
Hit = vector.multiply(vector.add(P1, vector.subtract(P2, P1)), ( -fDst1/(fDst2-fDst1) ));
return true
end
local function InBox(H, B1, B2, Axis)
if ( Axis==1 and H.z > B1.z and H.z < B2.z and H.y > B1.y and H.y < B2.y) then return true; end
if ( Axis==2 and H.z > B1.z and H.z < B2.z and H.x > B1.x and H.x < B2.x) then return true; end
if ( Axis==3 and H.x > B1.x and H.x < B2.x and H.y > B1.y and H.y < B2.y) then return true; end
return false;
end
local function CheckLineBox( B1, B2, L1, L2)
if (L2.x < B1.x and L1.x < B1.x) then return false end
if (L2.x > B2.x and L1.x > B2.x) then return false end
if (L2.y < B1.y and L1.y < B1.y) then return false end
if (L2.y > B2.y and L1.y > B2.y) then return false end
if (L2.z < B1.z and L1.z < B1.z) then return false end
if (L2.z > B2.z and L1.z > B2.z) then return false end
if (L1.x > B1.x and L1.x < B2.x and
L1.y > B1.y and L1.y < B2.y and
L1.z > B1.z and L1.z < B2.z)
then
Hit = L1;
return true
end
if ( (GetIntersection( L1.x-B1.x, L2.x-B1.x, L1, L2) and InBox( Hit, B1, B2, 1 ))
or (GetIntersection( L1.y-B1.y, L2.y-B1.y, L1, L2) and InBox( Hit, B1, B2, 2 ))
or (GetIntersection( L1.z-B1.z, L2.z-B1.z, L1, L2) and InBox( Hit, B1, B2, 3 ))
or (GetIntersection( L1.x-B2.x, L2.x-B2.x, L1, L2) and InBox( Hit, B1, B2, 1 ))
or (GetIntersection( L1.y-B2.y, L2.y-B2.y, L1, L2) and InBox( Hit, B1, B2, 2 ))
or (GetIntersection( L1.z-B2.z, L2.z-B2.z, L1, L2) and InBox( Hit, B1, B2, 3 )))
then
return true
end
return false;
end
local function CheckLineNear(line, pos, distance)
local nx = 0.5
if line.finish.x < line.start.x then nx = -nx end
local ny = 0.5
if line.finish.y < line.start.y then ny = -ny end
local nz = 0.5
if line.finish.z < line.start.z then nz = -nz end
for x=line.start.x,line.finish.x,nx do
for y=line.start.y,line.finish.y,ny do
for z=line.start.z,line.finish.z,nz do
if vector.distance({x=x, y=y, z=z}, pos) <= distance then
return true
end
end
end
end
return false
end
if def.forceload then
for pos in rayIter(line.start, vector.normalize(self.object:getvelocity()), math.max(64, vector.distance(line.start, line.finish) * 2)) do
kingdoms.utils.load_pos(pos)
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
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
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)
if node.name == "ignore" then
return
elseif def.is_passthrough_node(self, pos, node) then
self.lastair = pos
else
hitnode = pos
break
end
end
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,
velocity = {x=math.random()-0.5, y=math.random()-0.5, z=math.random()-0.5},
acceleration = {x=0, y=-1, z=0},
expirationtime = 1.5,
size = math.random() * 4,
texture = "smoke_puff.png^[transform" .. math.random(0, 7),
})
self.particletimer = 0
end
self.lastpos={x=pos.x, y=pos.y, z=pos.z}
end
minetest.register_entity(name, ent_def)
return function(itemstack, player, pointed_thing)
local playerpos = player:getpos()
local obj = minetest.add_entity({x=playerpos.x,y=playerpos.y+1.4,z=playerpos.z}, name)
local dir = player:get_look_dir()
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_horizontal()+math.pi)
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
end

106
timegens.lua Normal file
View File

@ -0,0 +1,106 @@
minetest.register_node("magic:nightcall", {
description = "Nightcall",
tiles = {"magic_nightcall.png"},
groups = {cracky = 1},
after_place_node = function(pos)
local meta = minetest.get_meta(pos)
meta:get_inventory():set_size("input", 1)
meta:get_inventory():set_size("output", 6)
meta:set_string("formspec", [[
size[8,7]
label[0,0.1;Input null essences during the night to produce night essences. Requires mana.]
list[context;input;0,1;1,1;]
list[context;output;2,1;6,1;]
list[current_player;main;0,3;8,4;]
]])
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
meta:get_inventory():set_list("input", {})
local leftover = meta:get_inventory():add_item("output", "magic:night_essence "..tostring(stack:get_count()))
minetest.add_item(vector.add(pos, {x=0, y=1, z=0}), leftover)
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 tod = minetest.get_timeofday()
if listname ~= "input" then return 0 end
if stack:get_name() ~= "magic:null_essence" then
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
if not (tod < 0.2 or tod > 0.805) then
minetest.chat_send_player(player:get_player_name(), "It is not night.")
return 0
end
if not magic.require_mana(player, stack:get_count() / 10, true) then
return 0
end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, 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
return stack:get_count()
end,
})
minetest.register_node("magic:daypull", {
description = "Daypull",
tiles = {"magic_daypull.png"},
groups = {cracky = 1},
after_place_node = function(pos)
local meta = minetest.get_meta(pos)
meta:get_inventory():set_size("input", 1)
meta:get_inventory():set_size("output", 6)
meta:set_string("formspec", [[
size[8,7]
label[0,0.1;Input null essences during the day to produce day essences. Requires mana.]
list[context;input;0,1;1,1;]
list[context;output;2,1;6,1;]
list[current_player;main;0,3;8,4;]
]])
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
meta:get_inventory():set_list("input", {})
local leftover = meta:get_inventory():add_item("output", "magic:day_essence "..tostring(stack:get_count()))
minetest.add_item(vector.add(pos, {x=0, y=1, z=0}), leftover)
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 tod = minetest.get_timeofday()
if listname ~= "input" then return 0 end
if stack:get_name() ~= "magic:null_essence" then
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
if tod < 0.2 or tod > 0.805 then
minetest.chat_send_player(player:get_player_name(), "It is not day.")
return 0
end
if not magic.require_mana(player, stack:get_count() / 10, true) then
return 0
end
return stack:get_count()
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, 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
return stack:get_count()
end,
})

172
turrets.lua Normal file
View File

@ -0,0 +1,172 @@
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 target 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 def.friendly_turret then
if not obj:is_player() or not kingdoms.player.is_friendly(meta:get_string("kingdom.id"), obj:get_player_name()) then
ok = false
end
else
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.is_friendly(meta:get_string("kingdom.id"), obj:get_player_name()) then
ok = false
end
end
end
if def.check_object then
ok = def.check_object(obj, ok)
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 + closest:get_properties().collisionbox[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