Initial commit.
|
@ -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"},
|
||||
},
|
||||
})
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
default
|
||||
kingdoms_meta
|
||||
ancient_world?
|
||||
tnt?
|
|
@ -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")
|
|
@ -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)
|
|
@ -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
|
|
@ -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"},
|
||||
})
|
|
@ -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
|
|
@ -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"},
|
||||
},
|
||||
})
|
|
@ -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"},
|
||||
},
|
||||
})
|
|
@ -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"},
|
||||
},
|
||||
})
|
|
@ -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
|
|
@ -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,
|
||||
},
|
||||
})
|
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 3.4 KiB |
|
@ -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
|
|
@ -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,
|
||||
})
|
||||
|
|
@ -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
|
||||
|