Initial commit

This commit is contained in:
ShadowNinja 2014-07-11 18:29:35 -04:00
commit 05e77c4453
27 changed files with 2558 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*~

32
README.md Normal file
View File

@ -0,0 +1,32 @@
Nuke mod by ShadowNinja
=======================
Nuke is a fast explosives mod for Minetest 0.4. It adds several variands of
TNT, and a (very unpolished) missle. It is also configurable and usable on
servers, due to it's optional privilege requirements.
Configuration
-------------
Nuke stores it's configuration in `nuke.conf` in the world directory.
It contains the following settings:
* `mese_radius` (24) - Explosion radius of mese TNT.
* `iron_radius` (12) - Explosion radius of iron TNT.
* `tnt_radius` (3) - Explosion radius of TNT.
* `missle_radius` (16) - Explosion radius of the missle.
* `missle_misfire_radius` (5) - The explosion radius of the missle when it
misfires (hits something before reaching it's max altitude).
* `fancy` (true) - Boolean flag enabling fancy effects like particles.
* `unprivileged_detonation` (false) - Boolean flag for whether detonation is
allowed without the `pyrotechnic` privilege. Allows activating nukes
with mesecons and burning nodes (fire, lava).
Licenses
--------
* Code: LGPLv3+ by ShadowNinja.
* Sounds: Unknown
* Iron and mese TNT textures: CC-BY-SA 3.0 by sfan5
* Everything else: CC-BY-SA 4.0 by ShadowNinja

85
api.lua Normal file
View File

@ -0,0 +1,85 @@
-- Create some tables first so that they're only created once
local mesecon_def = {effector = {
rules = {
{x=0, y=1, z=-1},
{x=0, y=0, z=-1},
{x=0, y=-1, z=-1},
{x=0, y=1, z=1},
{x=0, y=-1, z=1},
{x=0, y=0, z=1},
{x=1, y=0, z=0},
{x=1, y=1, z=0},
{x=1, y=-1, z=0},
{x=-1, y=1, z=0},
{x=-1, y=-1, z=0},
{x=-1, y=0, z=0},
{x=0, y=-1, z=0},
{x=0, y=1, z=0},
{x=0, y=2, z=0},
},
action_on = function(pos, node)
if nuke:can_detonate() then
nuke:ignite(pos, node.name)
end
end,
}}
local node_groups = {dig_immediate = 3, mesecon = 2, falling_node=1}
local entity_groups = {punch_operable = 1}
local sounds = default.node_sound_stone_defaults()
local collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}
local function on_punch(pos, node, player)
local stack = player:get_wielded_item()
local player_name = player:get_player_name()
if stack:get_name() == "default:torch" and
nuke:can_detonate(player_name) then
nuke:ignite(pos, node.name)
end
end
function nuke:register_nuke(name, description, radius, tiles)
minetest.register_node(name, {
tiles = tiles,
description = description,
sounds = sounds,
groups = node_groups,
on_punch = on_punch,
mesecons = mesecon_def,
})
local e = self.entity
minetest.register_entity(name, {
textures = tiles,
radius = radius,
physical = true,
collisionbox = collisionbox,
visual = "cube",
groups = entity_groups,
health = 10,
timer = 1,
blinktimer = 0,
blinkstatus = true,
on_activate = e.on_activate,
on_step = e.on_step,
on_punch = e.on_punch,
get_staticdata = e.get_staticdata,
smoke_spawner = false,
})
self.cid_names[minetest.get_content_id(name)] = name
if nuke:can_detonate() then
minetest.register_abm({
nodenames = {name},
neighbors = {"group:igniter"},
interval = 1,
chance = 1,
action = function(pos, node)
self:ignite(pos, node.name)
end
})
end
end

21
config.lua Normal file
View File

@ -0,0 +1,21 @@
local worldpath = minetest.get_worldpath()
nuke.config = Settings(worldpath..DIR_DELIM.."nuke.conf")
local defaults = {
missle_radius = "16",
missle_misfire_radius = "5",
mese_radius = "24",
iron_radius = "12",
tnt_radius = "3",
fancy = "true",
unprivileged_detonation = "false",
}
local config = nuke.config
for k, v in pairs(defaults) do
if config:get(k) == nil then
config:set(k, v)
end
end

55
definitions.lua Normal file
View File

@ -0,0 +1,55 @@
-- Convenience function
function nuke:get_tiles(name)
local side = name.."_side.png"
return {name.."_top.png", name.."_bottom.png",
side, side, side, side}
end
-- Mese nuke
minetest.register_craft({
output = "nuke:mese 3",
recipe = {
{"nuke:iron", "default:mese_crystal", "nuke:iron"},
{"default:mese_crystal", "nuke:iron", "default:mese_crystal"},
{"nuke:iron", "default:mese_crystal", "nuke:iron"}
}
})
nuke:register_nuke("nuke:mese",
"Mese nuke",
tonumber(nuke.config:get("mese_radius")),
nuke:get_tiles("nuke_mese"))
-- Iron nuke
minetest.register_craft({
output = "nuke:iron 3",
recipe = {
{"nuke:tnt", "default:steel_ingot", "nuke:tnt"},
{"default:steel_ingot", "nuke:tnt", "default:steel_ingot"},
{"nuke:tnt", "default:steel_ingot", "nuke:tnt"}
}
})
nuke:register_nuke("nuke:iron",
"Iron nuke",
tonumber(nuke.config:get("iron_radius")),
nuke:get_tiles("nuke_iron"))
-- Normal TNT
minetest.register_craft({
output = 'nuke:tnt 3',
recipe = {
{"default:coal_lump", "default:sand", "default:coal_lump"},
{"default:sand", "default:coal_lump", "default:sand"},
{"default:coal_lump", "default:sand", "default:coal_lump"}
}
})
nuke:register_nuke("nuke:tnt",
"TNT",
tonumber(nuke.config:get("tnt_radius")),
nuke:get_tiles("default_tnt"))

2
depends.txt Normal file
View File

@ -0,0 +1,2 @@
default

91
entity.lua Normal file
View File

@ -0,0 +1,91 @@
nuke.entity = {}
function nuke.entity:on_activate(staticdata)
local o = self.object
o:setvelocity({x=0, y=3, z=0})
o:setacceleration({x=0, y=-5, z=0})
o:settexturemod("^[brighten")
if nuke.config:get_bool("fancy") then
local pos = o:getpos()
local min_pos = vector.new(pos)
min_pos.x = min_pos.x - 0.2
min_pos.y = min_pos.y + 0.5
min_pos.z = min_pos.z - 0.2
local max_pos = vector.new(pos)
max_pos.x = max_pos.x + 0.2
max_pos.y = max_pos.y + 0.5
max_pos.z = max_pos.z + 0.2
-- add_particlespawner silently fails in entity callbacks, so
-- use minetest.after to call it later.
minetest.after(0, function()
if self.smoke_spawner == false then
self.smoke_spawner = minetest.add_particlespawner({
amount = 512,
time = 10,
minpos = min_pos,
maxpos = max_pos,
minvel = {x=-1, y=1, z=-1},
maxvel = {x=1, y=4, z=1},
minacc = vector.new(),
maxacc = vector.new(),
minexptime = 0.2,
maxexptime = 0.4,
minsize = 2,
maxsize = 3,
collisiondetection = false,
texture = "nuke_smoke_dark.png",
})
end
end)
end
end
function nuke.entity:on_step(dtime)
local o = self.object
self.timer = self.timer + dtime
self.blinktimer = self.blinktimer + (dtime * self.timer)
if self.blinktimer > 1 then
self.blinktimer = self.blinktimer - 1
o:settexturemod(self.blinkstatus and "" or "^[brighten")
self.blinkstatus = not self.blinkstatus
end
if self.timer < 10 then
return
end
-- Explode
local pos = vector.round(o:getpos())
local node = minetest.get_node(pos)
-- Cause entity physics even if we are put out.
-- This isn't very realistic but it allows for cannons.
o:remove()
minetest.sound_play("nuke_explode",
{pos = pos, gain = 1.0, max_hear_distance = 16})
nuke:entity_physics(pos, self.radius)
if minetest.get_item_group(node.name, "puts_out_fire") <= 0 then
nuke:explode(pos, self.radius)
end
if nuke.config:get_bool("fancy") then
nuke:effects(pos, self.radius)
end
end
function nuke.entity:on_punch(hitter)
self.object:remove()
hitter:get_inventory():add_item("main", self.name)
if self.smoke_spawner then
minetest.delete_particlespawner(self.smoke_spawner)
end
-- For add_particlespawner hack to detect if we've been removed
self.smoke_spawner = nil
end
function nuke.entity:get_staticdata()
if self.smoke_spawner then
minetest.delete_particlespawner(self.smoke_spawner)
end
-- For add_particlespawner hack to detect if we've been removed
self.smoke_spawner = nil
end

19
init.lua Normal file
View File

@ -0,0 +1,19 @@
-- Nuke mod by ShadowNinja
-- Based on the nuke mod by sfan5
nuke = {}
nuke.cid_names = {}
minetest.register_privilege("pyrotechnic", {
description = "Can detonate nukes",
})
local modpath = minetest.get_modpath("nuke") .. DIR_DELIM
dofile(modpath .. "config.lua")
dofile(modpath .. "entity.lua")
dofile(modpath .. "api.lua")
dofile(modpath .. "internal.lua")
dofile(modpath .. "definitions.lua")
dofile(modpath .. "missles.lua")

121
internal.lua Normal file
View File

@ -0,0 +1,121 @@
local priv_set = {pyrotechnic=true}
function nuke:can_detonate(player_name)
if self.config:get_bool("unprivileged_detonation") or
(player_name and minetest.check_player_privs(
player_name, priv_set)) then
return true
end
return false
end
function nuke:ignite(pos, name)
minetest.dig_node(pos)
minetest.sound_play("nuke_ignite",
{pos = pos, gain = 1.0, max_hear_distance = 10})
return minetest.add_entity(pos, name)
end
function nuke:calc_velocity(pos1, pos2, old_vel, power)
local vel = vector.direction(pos1, pos2)
vel = vector.normalize(vel)
vel = vector.multiply(vel, power * 10)
-- Divide by distanve
local dist = vector.distance(pos1, pos2)
dist = math.max(dist, 1)
vel = vector.divide(vel, dist)
-- Add old velocity
vel = vector.add(vel, old_vel)
return vel
end
-- Entity physics
function nuke:entity_physics(pos, radius)
-- Make the damage radius larger than the destruction radius
radius = radius * 2
local objs = minetest.get_objects_inside_radius(pos, radius)
for _, obj in pairs(objs) do
local obj_vel = obj:getvelocity()
local obj_pos = obj:getpos()
local dist = vector.distance(pos, obj_pos)
local damage = (4 / math.max(dist, 1)) * radius
obj:set_hp(obj:get_hp() - damage)
-- Ignore velocity calculation for entities exactly at our
-- position (us) and entities without velocity
-- (non-LuaEntitySAO).
if dist ~= 0 and obj_vel ~= nil then
obj:setvelocity(nuke:calc_velocity(pos, obj_pos,
obj_vel, radius))
end
end
end
function nuke:effects(pos, radius)
minetest.add_particlespawner({
amount = math.min(128 * radius / 2, 4096),
time = 1,
minpos = vector.subtract(pos, radius / 2),
maxpos = vector.add(pos, radius / 2),
minvel = {x=-20, y=-20, z=-20},
maxvel = {x=20, y=20, z=20},
minacc = vector.new(),
maxacc = vector.new(),
minexptime = 1,
maxexptime = 3,
minsize = 8,
maxsize = 16,
collisiondetection = true,
texture = "nuke_smoke_light.png",
})
end
function nuke:explode(pos, radius)
local start = os.clock()
local pos = vector.round(pos)
local vm = VoxelManip()
local pr = PseudoRandom(os.time())
local p1 = vector.subtract(pos, radius)
local p2 = vector.add(pos, radius)
local MinEdge, MaxEdge = vm:read_from_map(p1, p2)
local a = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
local data = vm:get_data()
local cid_names = nuke.cid_names
local p = {}
local c_air = minetest.get_content_id("air")
for z = -radius, radius do
for y = -radius, radius do
local vi = a:index(pos.x - radius, pos.y + y, pos.z + z)
for x = -radius, radius do
if (x * x) + (y * y) + (z * z) <=
(radius * radius) + pr:next(-radius, radius) then
local name = cid_names[data[vi]]
if name then
p.x = pos.x + x
p.y = pos.y + y
p.z = pos.z + z
self:ignite(p, name)
end
data[vi] = c_air
end
vi = vi + 1
end
end
end
vm:set_data(data)
vm:update_liquids()
vm:write_to_map()
vm:update_map()
print("Nuke exploded in "..(os.clock() - start).." seconds")
end

210
missles.lua Normal file
View File

@ -0,0 +1,210 @@
local radius = assert(tonumber(nuke.config:get("missle_radius")),
"missle_radius must be convertible to a number")
local misfire_radius = assert(tonumber(nuke.config:get("missle_misfire_radius")),
"missle_misfire_radius must be convertible to a number")
local function get_missle(pos)
for _, o in pairs(minetest.get_objects_inside_radius(pos, 1)) do
local e = o:get_luaentity()
if e and e.name == "nuke:missle" then
return e
end
end
end
local function launch_missle(pos, strike_pos)
local e = get_missle(pos)
if not e then return end
e.state = 1
e.origin = pos
e.strike_pos = strike_pos
local ppos = vector.new(pos)
ppos.y = ppos.y + 2
minetest.add_particlespawner({
amount = 128,
time = 1,
minpos = ppos,
maxpos = ppos,
minvel = {x=-5, y=-10, z=-5},
maxvel = {x=5, y=0, z=5},
minacc = vector.new(),
maxacc = vector.new(),
minexptime = 1,
maxexptime = 3,
minsize = 4,
maxsize = 6,
collisiondetection = true,
texture = "nuke_smoke_dark.png",
})
end
local function get_controller_formspec(strike_pos)
return "size[4,1.5]"
.."field[0.3,0.5;4,1;pos;Position to strike;"
..minetest.formspec_escape(minetest.pos_to_string(strike_pos)).."]"
.."button_exit[0,1;2,1;save;Save]"
.."button_exit[2,1;2,1;fire;Fire!]"
end
minetest.register_entity("nuke:missle", {
textures = {"nuke_missle.png"},
visual = "mesh",
mesh = "nuke_missle.x",
visual_size = {x=3, y=3, z=3},
phisical = true,
collisionbox = {-0.5, -0.5, -0.5, 0.5, 4, 0.5},
state = 0,
--origin = vector.new(),
on_activate = function(self, static_data)
if static_data == "" then return end
for k, v in pairs(minetest.deserialize(static_data)) do
self[k] = v
end
end,
on_step = function(self, dtime)
local o = self.object
local pos = vector.round(o:getpos())
local node = minetest.get_node(pos)
if self.state == 0 then
if not vector.equals(self.object:getvelocity(), {x=0, y=0, z=0}) then
-- We are in standby but moving, probably got unloaded
-- Drop ourselves
minetest.add_item(pos, "nuke:missle")
assert(false)
end
return
elseif self.state == 1 then
o:setacceleration({x=0, y=10, z=0})
if pos.y >= self.origin.y + 100 then
local dir = vector.direction(pos, self.strike_pos)
dir = vector.normalize(dir)
dir = vector.multiply(dir, 20)
o:setvelocity(dir)
o:setacceleration({x=0, y=0, z=0})
self.state = 2
end
if node.name ~= "air" and pos.y >= self.origin.y + 2 then
nuke:explode(pos, misfire_radius)
o:remove()
end
elseif self.state == 2 then
if node.name ~= "air" then
nuke:explode(pos, radius)
o:remove()
end
end
end,
on_punch = function(self, player)
player:get_inventory():add_item("main", "nuke:missle")
end,
get_staticdata = function(self)
if self.state == 0 then
return ""
end
return minetest.serialize({
state = self.state,
origin = self.origin,
strike_pos = self.strike_pos,
})
end,
})
minetest.register_node("nuke:missle_controller", {
tiles = {"nuke_missle_controller_top.png", "nuke_missle_controller.png",
"nuke_missle_controller.png", "nuke_missle_controller.png",
"nuke_missle_controller.png", "nuke_missle_controller.png"},
groups = {cracky=1},
paramtype = "light",
paramtype2 = "facedir",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.3, -0.5, -0.3, 0.3, 1, 0.3}, -- Base
{-0.5, 1, -0.5, 0.5, 1.2, 0.5}, -- Top
{-0.5, 1.2, 0, 0.5, 1.4, 0.5}, -- Half top
}
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local pos_str = minetest.pos_to_string(pos)
meta:set_string("strikepos", pos_str)
meta:set_string("infotext", "Missle controller")
meta:set_string("formspec", get_controller_formspec(pos))
end,
on_receive_fields = function(pos, formname, fields, player)
if not fields.pos then return end
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local strike_pos = minetest.string_to_pos(fields.pos)
if not strike_pos then return end
local player_name = player:get_player_name()
if vector.distance(pos, strike_pos) > 500 then
minetest.chat_send_player(player_name, "Strike position is too far.")
return
end
meta:set_string("strikepos", minetest.pos_to_string(strike_pos))
meta:set_string("formspec", get_controller_formspec(strike_pos))
if fields.fire then
if not nuke:can_detonate(player_name) then
minetest.chat_send_player(player_name, "You can't detonate nukes!")
return
end
local dir = minetest.facedir_to_dir(node.param2)
dir = vector.multiply(dir, 2) -- The launcher is two nodes behind us
local launcher_pos = vector.add(pos, dir)
launch_missle(launcher_pos, strike_pos)
end
end,
})
minetest.register_node("nuke:missle_launcher", {
tiles = {"nuke_missle_launcher.png"},
groups = {cracky=1},
paramtype = "light",
paramtype2 = "facedir",
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
--{-1, -0.3, -0.2, -0.8, 4, 0.2}, -- Left bar
--{0.8, -0.3, -0.2, 1, 4, 0.2}, -- Right bar
{-0.2, -0.3, 0.8, 0.2, 4, 1}, -- Back bar
{-0.4, 3.8, 0.2, -0.2, 4, 1}, -- Left holder
{0.2, 3.8, 0.2, 0.4, 4, 1}, -- Right holder
{-1, -0.5, -1, 1, -0.3, 1}, -- Base
}
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", "Missle launcher")
end,
on_rightclick = function(pos, node, clicker, itemstack)
local meta = minetest.get_meta(pos)
if get_missle(pos) then
return itemstack
end
if itemstack:get_name() == "nuke:missle" then
itemstack:take_item()
minetest.add_entity(pos, "nuke:missle")
end
return itemstack
end,
after_dig_node = function(pos, node, meta, player)
local e = get_missle(pos)
if e then
e.object:remove()
player:get_inventory():add_item("main", "nuke:missle")
end
end,
})
minetest.register_craftitem("nuke:missle", {
description = "Missle",
inventory_image = "nuke_missle_wield.png",
wield_image = "nuke_missle_wield.png",
stack_max = 1,
})

1920
models/nuke_missle.x Normal file

File diff suppressed because it is too large Load Diff

BIN
sounds/nuke_explode.ogg Normal file

Binary file not shown.

BIN
sounds/nuke_ignite.ogg Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

BIN
textures/nuke_iron_side.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

BIN
textures/nuke_iron_top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 B

BIN
textures/nuke_mese_side.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 B

BIN
textures/nuke_mese_top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

BIN
textures/nuke_missle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B