diff --git a/cannon.lua b/cannon.lua index 175f186..facb3e7 100644 --- a/cannon.lua +++ b/cannon.lua @@ -5,7 +5,7 @@ local S = utils.S local cannon_force = 20 local min_pitch = -20 -local max_pitch = 50 +local max_pitch = 70 local min_rotation = -60 local max_rotation = 60 @@ -298,6 +298,7 @@ local function process_controller_input (pos, input) local pitch = input.pitch * -180 / math.pi local node_rot = vector.dir_to_rotation (minetest.facedir_to_dir ((node.param2 + 2) % 4)) local rot = (input.yaw - node_rot.y) * 180 / math.pi + local sensitivity = (meta:get_string ("sensitive") == "true" and 3) or 1 while rot > 180 do rot = rot - 360 @@ -311,8 +312,8 @@ local function process_controller_input (pos, input) rot = -rot end - set_barrel_pitch (pos, pitch * 3) - set_barrel_rotation (pos, rot * 3) + set_barrel_pitch (pos, pitch * sensitivity) + set_barrel_rotation (pos, rot * sensitivity) if input.dig then fire_cannon (pos) @@ -323,15 +324,19 @@ end -local function get_formspec () +local function get_formspec (pos) + local meta = minetest.get_meta (pos) + local sensitive = (meta and meta:get_string ("sensitive")) or "false" + return "formspec_version[3]\n".. "size[11.75,10.75;true]\n".. "field[1.0,1.0;4.0,0.8;channel;Channel;${channel}]\n".. "button[5.5,1.0;2.0,0.8;setchannel;Set]\n".. "button[8.5,1.0;2.0,0.8;hide;Hide]\n".. - "field[1.0,3.0;4.0,0.8;controller;Controller;${controller}]\n".. - "button[5.5,3.0;2.0,0.8;setcontroller;Set]\n".. + "field[1.0,2.6;4.0,0.8;controller;Controller;${controller}]\n".. + "button[5.5,2.6;2.0,0.8;setcontroller;Set]\n".. + "checkbox[1.3,3.8;sensitive;Sensitive;"..sensitive.."]\n".. "list[context;main;9.0,2.75;1,1;]\n".. "list[current_player;main;1.0,5.0;8,4;]\n".. "listring[]" @@ -365,6 +370,7 @@ local function on_construct (pos) if barrel then set_barrel_rotation (pos, 0) set_barrel_pitch (pos, 0) + barrel:set_armor_groups ({ immortal = 1 }) end minetest.set_node (blank_pos, { name = "lwcomponents:cannon_blank" }) @@ -392,14 +398,16 @@ end local function after_place_node (pos, placer, itemstack, pointed_thing) local meta = minetest.get_meta (pos) + meta:set_string ("sensitive", "true") meta:set_string ("inventory", "{ main = { } }") - meta:set_string ("formspec", get_formspec ()) local inv = meta:get_inventory () inv:set_size ("main", 1) inv:set_width ("main", 1) + meta:set_string ("formspec", get_formspec (pos)) + -- If return true no item is taken from itemstack return false end @@ -542,6 +550,15 @@ local function on_receive_fields (pos, formname, fields, sender) meta:set_string ("formspec", "") end end + + if fields.sensitive ~= nil then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("sensitive", fields.sensitive) + meta:set_string ("formspec", get_formspec (pos)) + end + end end @@ -660,29 +677,26 @@ local function on_rightclick (pos, node, clicker, itemstack, pointed_thing) if hz == 0.5 and hy >= -0.5 and hy <= 0.2 then local angle = get_barrel_angle (pos) - if hx >= -0.5 and hx <= -0.25 and hy >= -0.25 and hy <= -0.0625 then - -- left - set_barrel_rotation (pos, angle.y + inc) - - elseif hx >= 0.25 and hx <= 0.5 and hy >= -0.25 and hy <= -0.0625 then - -- right - set_barrel_rotation (pos, angle.y - inc) - - elseif hx >= -0.125 and hx <= 0.125 and hy >= 0.0 and hy <= 0.1875 then - -- up - set_barrel_pitch (pos, angle.x + inc) - - elseif hx >= -0.125 and hx <= 0.125 and hy >= -0.5 and hy <= -0.3125 then - -- down - set_barrel_pitch (pos, angle.x - inc) - - elseif hx >= -0.125 and hx <= 0.125 and hy >= -0.25 and hy <= -0.0625 then - -- fire - fire_cannon (pos) - + if angle then + if hx >= -0.5 and hx <= -0.25 and hy >= -0.25 and hy <= -0.0625 then + -- left + set_barrel_rotation (pos, angle.y + inc) + elseif hx >= 0.25 and hx <= 0.5 and hy >= -0.25 and hy <= -0.0625 then + -- right + set_barrel_rotation (pos, angle.y - inc) + elseif hx >= -0.125 and hx <= 0.125 and hy >= 0.0 and hy <= 0.1875 then + -- up + set_barrel_pitch (pos, angle.x + inc) + elseif hx >= -0.125 and hx <= 0.125 and hy >= -0.5 and hy <= -0.3125 then + -- down + set_barrel_pitch (pos, angle.x - inc) + elseif hx >= -0.125 and hx <= 0.125 and hy >= -0.25 and hy <= -0.0625 then + -- fire + fire_cannon (pos) + end end else - meta:set_string ("formspec", get_formspec ()) + meta:set_string ("formspec", get_formspec (pos)) end end end @@ -839,6 +853,8 @@ minetest.register_node("lwcomponents:cannon_blank", { drop = "", groups = { not_in_creative_inventory = 1 }, paramtype = "light", + -- unaffected by explosions + on_blast = function() end, }) @@ -858,6 +874,8 @@ minetest.register_node("lwcomponents:cannon_blank_fire", { drop = "", groups = { not_in_creative_inventory = 1 }, paramtype = "light", + -- unaffected by explosions + on_blast = function() end, }) @@ -877,7 +895,7 @@ minetest.register_node("lwcomponents:cannon", { type = "fixed", fixed = { { -0.09, 0, -0.09, 0.09, 0.5, 0.09 }, - { -0.5, -0.1875, -0.5, 0.5, 0.125, 0.5 }, + { -0.5, -0.25, -0.5, 0.5, 0.125, 0.5 }, { -0.4375, -0.1875, -0.4375, 0.4375, 0.1875, 0.5 }, { -0.5, -0.5, 0.3125, 0.5, 0.125, 0.5 }, { -0.5, -0.5, -0.5, -0.3125, 0.125, -0.3125 }, @@ -939,7 +957,7 @@ minetest.register_node("lwcomponents:cannon_locked", { type = "fixed", fixed = { { -0.09, 0, -0.09, 0.09, 0.5, 0.09 }, - { -0.5, -0.1875, -0.5, 0.5, 0.125, 0.5 }, + { -0.5, -0.25, -0.5, 0.5, 0.125, 0.5 }, { -0.4375, -0.1875, -0.4375, 0.4375, 0.1875, 0.5 }, { -0.5, -0.5, 0.3125, 0.5, 0.125, 0.5 }, { -0.5, -0.5, -0.5, -0.3125, 0.125, -0.3125 }, @@ -1023,6 +1041,10 @@ minetest.register_entity ("lwcomponents:cannon_barrel", { on_punch = function (self, puncher, time_from_last_punch, tool_capabilities, dir) return true end, + + on_blast = function (self, damage) + return false, false, nil + end, }) diff --git a/cannon_shell.lua b/cannon_shell.lua new file mode 100644 index 0000000..4da4f4c --- /dev/null +++ b/cannon_shell.lua @@ -0,0 +1,293 @@ +local utils = ... +local S = utils.S + + + +--[[ +on_step info + +info.touching_ground = bool +info.standing_on_object = bool +info.collides = bool + + +info.collisions[n].type = "node" +info.collisions[n].node_pos = vector +info.collisions[n].old_velocity = vector +info.collisions[n].now_velocity = vector +info.collisions[n].axis = "x" | "y" | "z" - axis hit + +or + +info.collisions[n].type = "object" +info.collisions[n].object = userdata +info.collisions[n].old_velocity = vector +info.collisions[n].now_velocity = vector +info.collisions[n].axis = "x" | "y" | "z" - axis hit +]] + + + +local function get_adjacent_node (collision_info, spawn_pos) + if vector.equals (collision_info.node_pos, spawn_pos) then + return collision_info.node_pos + end + + local adj = { x = 0, y = 0, z = 0 } + + if collision_info.axis == "x" then + adj.x = (collision_info.old_velocity.x > 0 and -1) or 1 + elseif collision_info.axis == "y" then + adj.y = (collision_info.old_velocity.y > 0 and -1) or 1 + elseif collision_info.axis == "z" then + adj.z = (collision_info.old_velocity.z > 0 and -1) or 1 + end + + local pos = vector.new (collision_info.node_pos) + local node = utils.get_far_node (pos) + local def = minetest.registered_nodes[node and node.name or nil] + + while (node and node.name ~= "air") and (def and not def.buildable_to) do + local next_pos = vector.add (pos, adj) + + if vector.equals (next_pos, spawn_pos) then + return pos + end + + pos = next_pos + node = utils.get_far_node (pos) + def = minetest.registered_nodes[node and node.name or nil] + end + + return pos +end + + + +local function register_shell (name, description, texture, inventory_image, + stack_max, shell_speed, explode_func) + + minetest.register_entity (name.."_entity", { + initial_properties = { + physical = true, + collide_with_objects = true, + collisionbox = { -0.25, -0.125, -0.25, 0.25, 0.125, 0.25 }, + pointable = false, + visual_size = { x = 0.7, y = 0.7, z = 0.7 }, + visual = "mesh", + mesh = "lwcomponents_shell.obj", + textures = { texture }, + use_texture_alpha = false, + is_visible = true, + makes_footstep_sound = false, + automatic_face_movement_dir = false, + automatic_face_movement_max_rotation_per_sec = false, + automatic_rotate = 0, + backface_culling = true, + damage_texture_modifier = "", + glow = 0, + static_save = false, + shaded = true, + show_on_minimap = false, + }, + + on_activate = function (self, staticdata, dtime_s) + if not self.spawn_pos then + self.spawn_pos = vector.new (self.object:get_pos ()) + end + + if not self.time_lived then + self.time_lived = 0 + end + + if not self.shell_speed then + self.shell_speed = shell_speed + end + + self.staticdata = staticdata + end, + + get_staticdata = function (self) + return self.staticdata + end, + + on_step = function (self, dtime, info) + local explode_pos = nil + + self.object:set_rotation (vector.dir_to_rotation (self.object:get_velocity ())) + + if self.time_lived then + self.time_lived = self.time_lived + dtime + + if self.time_lived > self.shell_speed then + self.object:remove () + + return + end + end + + if info.collides then + --For each collision that was found in reverse order + for i = #info.collisions, 1, -1 do + local c = info.collisions[i] + + if c.type == "node" then + local node = utils.get_far_node (c.node_pos) + + if node and node.name ~= "air" then + local def = minetest.registered_nodes[node.name] + + if def and def.walkable then + -- adjacent for explosion + explode_pos = get_adjacent_node (c, self.spawn_pos) + +--minetest.log ("action", "Shell on node "..node.name.." at "..minetest.pos_to_string (explode_pos).. + --" node at "..minetest.pos_to_string (c.node_pos)) + + break + end + end + + if not explode_pos then + self.object:set_velocity (c.old_velocity) + end + + elseif c.type == "object" then + -- explode at this pos + if c.object:get_armor_groups ().immortal then + self.object:set_velocity (c.old_velocity) + else + explode_pos = vector.new (c.object:get_pos ()) + +--minetest.log ("action", "Shell on entity "..c.object:get_luaentity ().name.." at "..minetest.pos_to_string (explode_pos)) + + break + end + end + end + end + + if explode_pos then + self.object:remove () + + explode_func (explode_pos) + end + end, + + on_punch = function (self, puncher, time_from_last_punch, tool_capabilities, dir) + return true + end, + }) + + minetest.register_craftitem (name, { + description = description, + short_description = description, + groups = { }, + inventory_image = inventory_image, + wield_image = inventory_image, + stack_max = stack_max, + }) + + + lwcomponents.register_spawner (name, + function (spawn_pos, itemstack, owner, spawner_pos, spawner_dir, force) + if not itemstack:is_empty() then + local def = minetest.registered_entities[name.."_entity"] + + if def then + local obj = minetest.add_entity (spawn_pos, name.."_entity") + + if obj then + obj:set_armor_groups ({ immortal = 1 }) + obj:set_acceleration ({ x = 0, y = -9.81, z = 0 }) + obj:set_rotation (vector.dir_to_rotation (vector.multiply (spawner_dir, shell_speed))) + obj:set_velocity (vector.multiply (spawner_dir, shell_speed)) + + local luaent = obj:get_luaentity () + + if luaent then + luaent.spawn_pos = { x = spawn_pos.x, y = spawn_pos.y, z = spawn_pos.z } + luaent.time_lived = 0 + luaent.shell_speed = shell_speed + end + + return obj, false + end + end + end + + return nil, false + end) +end + + +register_shell ("lwcomponents:cannon_shell", + S("Shell"), + "lwcannon_shell.png", + "lwcannon_shell_item.png", + 99, + 25, + function (pos) + utils.boom (pos, + 2, -- node_radius + 70, -- node_chance in 100 + 2, -- fire_radius + 5, -- fire_chance in 100 + 4, -- entity_radius + 20, -- entity_damage + false, -- disable_drops + nil, -- node_filter + false, -- burn_all + nil) -- sound + end) + + +register_shell ("lwcomponents:cannon_soft_shell", + S("Soft Shell"), + "lwcannon_soft_shell.png", + "lwcannon_soft_shell_item.png", + 99, + 25, + function (pos) + utils.boom (pos, + 2, -- node_radius + 50, -- node_chance in 100 + 2, -- fire_radius + 5, -- fire_chance in 100 + 4, -- entity_radius + 20, -- entity_damage + false, -- disable_drops + { + buildable_to = true, + buildable_to_undefined = false, + }, -- node_filter + false, -- burn_all + nil) -- sound + end) + + +if minetest.global_exists ("fire") then +register_shell ("lwcomponents:cannon_fire_shell", + S("Fire Shell"), + "lwcannon_fire_shell.png", + "lwcannon_fire_shell_item.png", + 99, + 25, + function (pos) + utils.boom (pos, + 2, -- node_radius + 0, -- node_chance in 100 + 2, -- fire_radius + 70, -- fire_chance in 100 + 4, -- entity_radius + 20, -- entity_damage + false, -- disable_drops + nil, -- node_filter + true, -- burn_all + nil) -- sound + end) +end + + + +-- diff --git a/change.log b/change.log index c6cb695..9ebed6d 100644 --- a/change.log +++ b/change.log @@ -67,3 +67,10 @@ v0.1.10 v0.1.11 * Fix to breakers (?). * Added position aiming to cannons. + + +v0.1.12 +* Added sensitivity option for game controller in cannons. +* Added cannon shells. +* Fixed bug in utils.is_creative. +* Increased cannon pitch to -20 to 70. diff --git a/crafting.lua b/crafting.lua index 0ef2fa8..a14f484 100644 --- a/crafting.lua +++ b/crafting.lua @@ -23,6 +23,35 @@ minetest.register_craft( { }) +minetest.register_craft( { + output = "lwcomponents:cannon_shell 10", + recipe = { + { "default:steel_ingot", "default:steel_ingot" }, + { "", "default:coalblock" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:cannon_soft_shell 10", + recipe = { + { "default:steel_ingot", "default:steel_ingot" }, + { "default:copper_lump", "default:coalblock" }, + }, +}) + + +if minetest.global_exists ("fire") then +minetest.register_craft( { + output = "lwcomponents:cannon_fire_shell 10", + recipe = { + { "default:steel_ingot", "default:steel_ingot" }, + { "default:iron_lump", "default:coalblock" }, + }, +}) +end + + if utils.digilines_supported or utils.mesecon_supported then diff --git a/explode.lua b/explode.lua new file mode 100644 index 0000000..eacf0a3 --- /dev/null +++ b/explode.lua @@ -0,0 +1,512 @@ +local utils = ... +local S = utils.S + + + + +local explode = { } + + + +if minetest.global_exists ("fire") then + + +explode.fire_supported = true + + +function explode.set_fire (pos, burn_all) + local node = utils.get_far_node (pos) + + if not node then + return + end + + if node.name ~= "air" then + local def = minetest.registered_nodes[node.name] + + if not def or not def.buildable_to then + return + end + end + + local dirs = + { + { x = 0, y = -1, z = 0 }, + { x = -1, y = 0, z = 0 }, + { x = 0, y = 0, z = -1 }, + { x = 1, y = 0, z = 0 }, + { x = 0, y = 0, z = 1 } + } + + for i = 1, #dirs do + node = utils.get_far_node (vector.add (pos, dirs[i])) + + if node and node.name ~= "air" and node.name ~= "fire:basic_flame" then + local def = minetest.registered_nodes[node.name] + + if def and def.liquidtype == "none" then + if (def.groups and def.groups.flammable) or burn_all then + minetest.set_node (pos, { name = "fire:basic_flame" }) + + return + end + end + end + end +end + + +else + + +explode.fire_supported = false + + +function explode.set_fire (pos, burn_all) +end + + +end + + + +local function is_same_item (stack1, stack2) + local copy1 = ItemStack (stack1) + local copy2 = ItemStack (stack2) + + if copy1 and copy2 then + copy1:set_count (1) + copy2:set_count (1) + + if copy1:to_string () == copy2:to_string () then + return true + end + end + + return false +end + + + +local function dig_node (pos, toolname) + local node = utils.get_far_node (pos) + local dig = false + local drops = nil + + if toolname == true then + dig = true + toolname = nil + end + + if node and node.name ~= "air" then + local def = utils.find_item_def (node.name) + + if not dig then + if def and def.can_dig then + local result, can_dig = pcall (def.can_dig, pos) + + dig = ((not result) or (result and (can_dig == nil or can_dig == true))) + else + dig = true + end + end + + if dig then + local items = minetest.get_node_drops (node, toolname) + + if items then + drops = { } + + for i = 1, #items do + drops[i] = ItemStack (items[i]) + end + + if def and def.preserve_metadata then + def.preserve_metadata (pos, node, minetest.get_meta (pos), drops) + end + end + + minetest.remove_node (pos) + end + end + + return drops +end + + + +local function add_drops (drops, drop) + if drops and drop then + for i = 1, #drop do + local item = ItemStack (drop[i]) + + if item and not item:is_empty () then + local existing = drops[item:get_name ()] + + if existing and is_same_item (item, existing) then + existing:set_count (existing:get_count () + item:get_count ()) + else + drops[item:get_name ()] = item + end + end + end + end +end + + + +local function explode_node (pos, dig_chance, intensity, drops, filter) + if not utils.is_protected (pos, nil) then + dig_chance = math.min (math.max (dig_chance, 0), 100) + + if math.random (100) <= dig_chance then + local node = utils.get_far_node (pos) + local blasted = false + + if node and node.name ~= "air" then + local def = minetest.registered_nodes[node.name] + + if def then + if def.diggable == false then + return false + end + + for k, v in pairs (filter) do + if def[k] == nil then + if filter[k.."_undefined"] == false then + return false + end + elseif def[k] ~= v then + return false + end + end + + if def.on_blast then + def.on_blast (pos, intensity) + blasted = true + end + end + + if not blasted then + local drop = dig_node (pos, true) + + add_drops (drops, drop) + end + + minetest.check_for_falling ({ x = pos.x, y = pos.y + 1, z = pos.z }) + + return true + end + end + end + + return false +end + + + +local function burn_node (pos, fire_chance, burn_all) + if not utils.is_protected (pos, nil) then + fire_chance = math.min (math.max (fire_chance, 0), 100) + + if math.random (100) <= fire_chance then + explode.set_fire (pos, burn_all) + end + end +end + + + +local function entity_is_drop (obj) + return obj.get_luaentity and obj:get_luaentity () and + obj:get_luaentity ().name and + obj:get_luaentity ().name == "__builtin:item" +end + + + +local function explode_entities (pos, radius, damage, drops) + local objs = minetest.get_objects_inside_radius (pos, radius) + + for _, obj in pairs (objs) do + local obj_pos = obj:get_pos () + local dir = vector.direction (pos, obj_pos) + local dist = vector.length (vector.subtract (obj_pos, pos)) + local vel = vector.multiply (dir, ((radius + 1) - dist) / (radius + 1) * damage * 5) + + if entity_is_drop (obj) then + obj:add_velocity (vel) + + elseif not obj:get_armor_groups ().immortal then + + local ent_damage = ((radius - dist) / radius * damage / 2) + (damage / 2) + local reason = { type = "set_hp", from = "lwcomponents" } + + if obj:is_player() then + obj:add_velocity (vel) + + obj:set_hp (obj:get_hp() - ent_damage, reason) + + else + local luaobj = obj:get_luaentity() + + -- object might have disappeared somehow + if luaobj then + local do_damage = true + local do_knockback = true + local entity_drops = {} + local objdef = minetest.registered_entities[luaobj.name] + + if objdef and objdef.on_blast then + do_damage, do_knockback, entity_drops = objdef.on_blast (luaobj, ent_damage) + end + + if do_knockback then + obj:add_velocity (vel) + end + + if do_damage then + obj:set_hp (obj:get_hp() - ent_damage, reason) + end + + add_drops (drops, entity_drops) + end + end + end + end +end + + + +local function spray_drops (pos, drops, damage) + local max_vel = damage * 2.5 + + for k, stack in pairs (drops) do + local vel = + { + x = math.random (max_vel) - (max_vel / 2), + y = math.random (max_vel) - (max_vel / 2), + z = math.random (max_vel) - (max_vel / 2) + } + + local drop = minetest.add_item (pos, stack) + + if drop then + drop:set_velocity (vel) + end + end +end + + + +local function add_effects (pos, radius, drops) + minetest.add_particle ({ + pos = pos, + velocity = vector.new (), + acceleration = vector.new (), + expirationtime = 0.4, + size = 30, -- radius * 10, + collisiondetection = false, + vertical = false, + texture = "lwcomponents_boom.png", + glow = 15, + }) + + minetest.add_particlespawner ({ + amount = 64, + time = 0.5, + minpos = vector.subtract (pos, radius / 2), + maxpos = vector.add (pos, radius / 2), + minvel = {x = -10, y = -10, z = -10}, + maxvel = {x = 10, y = 10, z = 10}, + minacc = vector.new (), + maxacc = vector.new (), + minexptime = 1, + maxexptime = 2.5, + minsize = 9, -- radius * 3, + maxsize = 15, -- radius * 5, + texture = "lwcomponents_smoke.png", + }) + + -- we just dropped some items. Look at the items entities and pick + -- one of them to use as texture + local texture = "lwcomponents_blast.png" --fallback texture + local node + local most = 0 + + if drops then + for name, stack in pairs (drops) do + local count = stack:get_count() + if count > most then + most = count + local def = minetest.registered_nodes[name] + if def then + node = { name = name } + end + if def and def.tiles and def.tiles[1] then + if type (def.tiles[1]) == "table" then + texture = def.tiles[1].name or "lwcomponents_blast.png" + elseif type (def.tiles[1]) == "string" then + texture = def.tiles[1] + end + end + end + end + end + + minetest.add_particlespawner ({ + amount = 64, + time = 0.1, + minpos = vector.subtract (pos, radius / 2), + maxpos = vector.add (pos, radius / 2), + minvel = {x = -3, y = 0, z = -3}, + maxvel = {x = 3, y = 5, z = 3}, + minacc = {x = 0, y = -10, z = 0}, + maxacc = {x = 0, y = -10, z = 0}, + minexptime = 0.8, + maxexptime = 2.0, + minsize = 1, -- radius * 0.33, + maxsize = 3, -- radius, + texture = texture, + -- ^ only as fallback for clients without support for `node` parameter + node = node, + collisiondetection = true, + }) +end + + + +function utils.boom (pos, -- center of explosion + node_radius, node_chance, -- radius and chance in 100 + fire_radius, fire_chance, -- radius and chance in 100 + entity_radius, entity_damage, -- radius and max damage applied + disable_drops, -- true to disable drops + node_filter, -- node filter table as { buildable_to = true, buildable_to_undefined = false, ... } + burn_all, -- true to set fire to anything, otherwise only flammable + sound) -- sound on blast, if nil plays default + + pos = vector.round (pos) + node_radius = math.floor (node_radius or 1) + fire_radius = math.floor (fire_radius or node_radius) + entity_radius = math.floor (entity_radius or node_radius * 2) + node_chance = node_chance or 80 + fire_chance = fire_chance or 30 + entity_damage = math.floor (entity_damage or entity_radius) + disable_drops = disable_drops == true + node_filter = node_filter or { } + burn_all = burn_all == true + sound = sound or "lwcannon" + + local drops = { } + local effects_radius = (node_radius > 0 and node_radius) or entity_radius + local center_free = false + + if not utils.is_protected (pos, nil) then + local center_node = utils.get_far_node (pos) + + if not node or node.name == "air" then + center_free = true + end + end + + if node_radius > 0 and node_chance > 0 then + local extents = node_radius * 2 + + for y = -extents, extents, 1 do + for z = -extents, extents, 1 do + for x = -extents, extents, 1 do + local node_pos = { x = x + pos.x, y = y + pos.y, z = z + pos.z } + local length = vector.length ({ x = x, y = y, z = z }) + + if node_chance > 0 and length <= node_radius then + if explode_node (node_pos, node_chance, 1.0, drops, node_filter) then + if vector.equals (pos, node_pos) then + center_free = true + end + end + end + end + end + end + end + + if fire_radius > 0 and fire_chance > 0 then + local extents = fire_radius * 2 + + for y = -extents, extents, 1 do + for z = -extents, extents, 1 do + for x = -extents, extents, 1 do + local node_pos = { x = x + pos.x, y = y + pos.y, z = z + pos.z } + local length = vector.length ({ x = x, y = y, z = z }) + + if fire_chance > 0 and length <= fire_radius then + burn_node (node_pos, fire_chance, burn_all) + end + end + end + end + end + + minetest.sound_play (sound, + { + pos = pos, + gain = 2.5, + max_hear_distance = math.min (effects_radius * 20, 128) + }, + true) + + if center_free then + minetest.set_node (pos, { name = "lwcomponents:boom" }) + end + + explode_entities (pos, entity_radius, entity_damage, drops) + + if not disable_drops then + spray_drops (pos, drops, entity_damage) + end + + add_effects (pos, effects_radius, drops) + + + minetest.log ("action", "A Shell explosion occurred at " .. minetest.pos_to_string (pos) .. + " with radius " .. entity_radius) +end + + + +minetest.register_node ("lwcomponents:boom", { + description = S("Boom"), + drawtype = "airlike", + tiles = { "lwcomponents_boom.png" }, + inventory_image = "lwcomponents_boom.png", + wield_image = "lwcomponents_boom.png", + light_source = default.LIGHT_MAX, + use_texture_alpha = "blend", + sunlight_propagates = true, + walkable = false, + pointable = false, + diggable = false, + climbable = false, + buildable_to = true, + floodable = true, + is_ground_content = false, + drop = "", + paramtype = "light", + param1 = 255, + post_effect_color = { a = 128, r = 255, g = 0, b = 0 }, + groups = { dig_immediate = 3, not_in_creative_inventory = 1 }, + on_construct = function (pos) + minetest.get_node_timer (pos):start (0.5) + end, + on_timer = function (pos, elapsed) + minetest.remove_node (pos) + + return false + end, + -- unaffected by explosions + on_blast = function() end, +}) + + + +-- diff --git a/init.lua b/init.lua index 615c3aa..79388a4 100644 --- a/init.lua +++ b/init.lua @@ -1,4 +1,4 @@ -local version = "0.1.11" +local version = "0.1.12" local mod_storage = minetest.get_mod_storage () @@ -15,8 +15,9 @@ end local utils = { } local modpath = minetest.get_modpath ("lwcomponents") -loadfile (modpath.."/utils.lua") (utils, mod_storage) loadfile (modpath.."/settings.lua") (utils) +loadfile (modpath.."/utils.lua") (utils, mod_storage) +loadfile (modpath.."/explode.lua") (utils) loadfile (modpath.."/api.lua") (utils) utils.connections = loadfile (modpath.."/connections.lua") () loadfile (modpath.."/dropper.lua") (utils) @@ -32,6 +33,7 @@ loadfile (modpath.."/deployer.lua") (utils) loadfile (modpath.."/fan.lua") (utils) loadfile (modpath.."/conduit.lua") (utils, mod_storage) loadfile (modpath.."/cannon.lua") (utils) +loadfile (modpath.."/cannon_shell.lua") (utils) loadfile (modpath.."/extras.lua") (utils) loadfile (modpath.."/digiswitch.lua") (utils) loadfile (modpath.."/movefloor.lua") (utils) diff --git a/license.txt b/license.txt index d0d0c73..3d36d6b 100644 --- a/license.txt +++ b/license.txt @@ -69,7 +69,10 @@ public domain. player button images derived from mesecons button image. -cannon firing sound from tnt, released under CC BY-SA 3.0 and CC0 1.0 +cannon firing and explosion sound from tnt (TumeniNodes/steveygos93), +released under CC0 1.0 (originally from https://freesound.org/s/80401/) + +boom image from tnt, released under CC BY-SA 3.0. All other media, or media not covered by a licence, is licensed Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) diff --git a/readme.txt b/readme.txt index 5816552..e87b97c 100644 --- a/readme.txt +++ b/readme.txt @@ -13,7 +13,7 @@ CC BY-SA 3.0 Version ======= -0.1.11 +0.1.12 Minetest Version @@ -63,7 +63,7 @@ Various components for mesecons and digilines. * Hologram, projects a hologram above the hologram node. * Fan, blows any entity, player or drop in front of the fan. * Conduit, connected in a circuit to move items. -* Cannon, shoots an item on command with directional aiming. +* Cannon, shoots an item on command with directional aiming (plus 3 shells). * Digiswitch, digilines controlled mesecons power. * Movefloor, similar to vertical mesecons movestone. * Solid color conductor blocks, same as Solid Color Block but also mesecons diff --git a/screenshot.png b/screenshot.png index 1be6ff7..68aa146 100644 Binary files a/screenshot.png and b/screenshot.png differ diff --git a/utils.lua b/utils.lua index 645682c..cc9a5b5 100644 --- a/utils.lua +++ b/utils.lua @@ -321,7 +321,7 @@ function utils.is_creative (player) if player and player:is_player () then return minetest.is_creative_enabled (player:get_player_name ()) or - minetest.check_player_privs (placer, "creative") + minetest.check_player_privs (player, "creative") end return false