diff --git a/arrow.lua b/arrow.lua deleted file mode 100644 index c135f5f..0000000 --- a/arrow.lua +++ /dev/null @@ -1,92 +0,0 @@ -minetest.register_craftitem("castle:arrow", { - description = "Arrow", - inventory_image = "castle_arrow.png", -}) - -minetest.register_node("castle:arrow_box", { - drawtype = "nodebox", - node_box = { - type = "fixed", - fixed = { - -- Shaft - {-6.5/17, -1.5/17, -1.5/17, 6.5/17, 1.5/17, 1.5/17}, - --Spitze - {-4.5/17, 2.5/17, 2.5/17, -3.5/17, -2.5/17, -2.5/17}, - {-8.5/17, 0.5/17, 0.5/17, -6.5/17, -0.5/17, -0.5/17}, - --Federn - {6.5/17, 1.5/17, 1.5/17, 7.5/17, 2.5/17, 2.5/17}, - {7.5/17, -2.5/17, 2.5/17, 6.5/17, -1.5/17, 1.5/17}, - {7.5/17, 2.5/17, -2.5/17, 6.5/17, 1.5/17, -1.5/17}, - {6.5/17, -1.5/17, -1.5/17, 7.5/17, -2.5/17, -2.5/17}, - - {7.5/17, 2.5/17, 2.5/17, 8.5/17, 3.5/17, 3.5/17}, - {8.5/17, -3.5/17, 3.5/17, 7.5/17, -2.5/17, 2.5/17}, - {8.5/17, 3.5/17, -3.5/17, 7.5/17, 2.5/17, -2.5/17}, - {7.5/17, -2.5/17, -2.5/17, 8.5/17, -3.5/17, -3.5/17}, - } - }, - tiles = {"castle_arrow.png", "castle_arrow.png", "castle_arrow_back.png", "castle_arrow_front.png", "castle_arrow_2.png", "castle_arrow.png"}, - groups = {not_in_creative_inventory=1}, -}) - -local castle_ARROW_ENTITY={ - physical = false, - timer=0, - visual = "wielditem", - visual_size = {x=0.1, y=0.1}, - textures = {"castle:arrow_box"}, - lastpos={}, - collisionbox = {0,0,0,0,0,0}, -} - -castle_ARROW_ENTITY.on_step = function(self, dtime) - self.timer=self.timer+dtime - local pos = self.object:getpos() - local node = minetest.get_node(pos) - - if self.timer>0.2 then - local objs = minetest.get_objects_inside_radius({x=pos.x,y=pos.y,z=pos.z}, 2) - for k, obj in pairs(objs) do - if obj:get_luaentity() ~= nil then - if obj:get_luaentity().name ~= "castle:arrow_entity" and obj:get_luaentity().name ~= "__builtin:item" then - local damage = 3 - obj:punch(self.object, 1.0, { - full_punch_interval=1.0, - groupcaps={ - fleshy={times={[1]=1/(damage-2), [2]=1/(damage-1), [3]=1/damage}}, - snappy={times={[1]=1/(damage-2), [2]=1/(damage-1), [3]=1/damage}}, - } - }, nil) - self.object:remove() - end - else - local damage = 3 - obj:punch(self.object, 1.0, { - full_punch_interval=1.0, - groupcaps={ - fleshy={times={[1]=1/(damage-2), [2]=1/(damage-1), [3]=1/damage}}, - snappy={times={[1]=1/(damage-2), [2]=1/(damage-1), [3]=1/damage}}, - } - }, nil) - self.object:remove() - end - end - end - - if self.lastpos.x~=nil then - if node.name ~= "air" then - minetest.add_item(self.lastpos, 'castle:arrow') - self.object:remove() - end - end - self.lastpos={x=pos.x, y=pos.y, z=pos.z} -end - -minetest.register_entity("castle:arrow_entity", castle_ARROW_ENTITY) - -minetest.register_craft({ - output = 'castle:arrow 16', - recipe = { - {'default:stick', 'default:stick', 'default:steel_ingot'}, - } -}) diff --git a/crossbow.lua b/crossbow.lua index 43f033d..c752779 100644 --- a/crossbow.lua +++ b/crossbow.lua @@ -1,43 +1,431 @@ -arrows = { - {"castle:arrow", "castle:arrow_entity"}, +--[[ +Minetest Mod - Simple Shooter [shooter] 0.5.3 +======================================= + +License Source Code: 2013 Stuart Jones - LGPL v2.1 + +License Textures: Stuart Jones - WTFPL + +Licence Models: Stuart Jones - CC-BY-SA 3.0 + +License Sounds: freesound.org + +--]] +local crossbow={} + +minetest.register_alias("crossbow", "castle:crossbow") +minetest.register_alias("bolt", "castle:crossbow_bolt") + +CROSSBOW_USES = 300 +CROSSBOW_BOLT_TOOL_CAPS = {damage_groups={fleshy=4}} +CROSSBOW_BOLT_LIFETIME = 60-- 1 minute +CROSSBOW_ENABLE_PARTICLE_FX = false +CROSSBOW_ENABLE_PROTECTION = true +CROSSBOW_EXPLOSION_TEXTURE = "castle_crossbow_hit.png" +CROSSBOW_ALLOW_NODES = true +CROSSBOW_ALLOW_ENTITIES = true +CROSSBOW_ALLOW_PLAYERS = true +CROSSBOW_PLAYER_OFFSET = {x=0, y=1, z=0} +CROSSBOW_ENTITY_OFFSET = {x=0, y=0, z=0} +CROSSBOW_ENTITIES = { +"mobs:chicken", +"mobs:cow", +"mobs:dirt_monster", +"mobs:dungeon_master", +"mobs:goat", +"mobs:mese_monster", +"mobs:npc", +"mobs:oerkki", +"mobs:pig", +"mobs:pumba", +"mobs:rat", +"mobs:rhino", +"mobs:sand_monster", +"mobs:sheep", +"mobs:spider", +"mobs:stone_monster", +"mobs:tree_monster", } -local castle_shoot_arrow = function(itemstack, player) - for _,arrow in ipairs(arrows) do - if player:get_inventory():get_stack("main", player:get_wield_index()+1):get_name() == arrow[1] then - if not minetest.setting_getbool("creative_mode") then - player:get_inventory():remove_item("main", arrow[1]) - end - local playerpos = player:getpos() - local obj = minetest.add_entity({x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, arrow[2]) - local dir = player:get_look_dir() - obj:setvelocity({x=dir.x*19, y=dir.y*19, z=dir.z*19}) - obj:setacceleration({x=dir.x*-3, y=-10, z=dir.z*-3}) - obj:setyaw(player:get_look_yaw()+math.pi) - minetest.sound_play("castle_sound", {pos=playerpos}) - if obj:get_luaentity().player == "" then - obj:get_luaentity().player = player - end - obj:get_luaentity().node = player:get_inventory():get_stack("main", 1):get_name() - return true +if minetest.is_singleplayer() == true then + CROSSBOW_ALLOW_ENTITIES = true + CROSSBOW_ALLOW_PLAYERS = true +end + +local allowed_entities = {} +for _,v in ipairs(CROSSBOW_ENTITIES) do + allowed_entities[v] = 1 +end + +local function get_dot_product(v1, v2) + return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z +end + +local function get_particle_pos(p, v, d) + return vector.add(p, vector.multiply(v, {x=d, y=d, z=d})) +end + +function crossbow:spawn_particles(pos, texture) + if CROSSBOW_ENABLE_PARTICLE_FX == true then + if type(texture) ~= "string" then + texture = CROSSBOW_EXPLOSION_TEXTURE + end + local spread = {x=0.1, y=0.1, z=0.1} + minetest.add_particlespawner(15, 0.3, + vector.subtract(pos, spread), vector.add(pos, spread), + {x=-1, y=1, z=-1}, {x=1, y=2, z=1}, + {x=-2, y=-2, z=-2}, {x=2, y=-2, z=2}, + 0.1, 0.75, 1, 2, false, texture + ) + end +end + +function crossbow:punch_node(pos, def) + local node = minetest.get_node(pos) + if not node then + return + end + local item = minetest.registered_items[node.name] + if not item then + return + end + if CROSSBOW_ENABLE_PROTECTION then + if minetest.is_protected(pos, def.name) then + return end end - return false -end -minetest.register_tool("castle:crossbow", { - description = "Crossbow", - inventory_image = "castle_crossbow.png", - stack_max = 1, - on_use = function(itemstack, user, pointed_thing) - if castle_shoot_arrow(item, user, pointed_thing) then - if not minetest.setting_getbool("creative_mode") then - itemstack:add_wear(65535/200) + if item.groups then + for k, v in pairs(def.groups) do + local level = item.groups[k] or 0 + if level >= v then + minetest.remove_node(pos) + if item.tiles then + if item.tiles[1] then + crossbow:spawn_particles(pos, item.tiles[1]) + end + end + break end end - return itemstack + end +end + +function crossbow:is_valid_object(object) + if object then + if object:is_player() == true then + return CROSSBOW_ALLOW_PLAYERS + end + if CROSSBOW_ALLOW_ENTITIES == true then + local luaentity = object:get_luaentity() + if luaentity then + if luaentity.name then + if allowed_entities[luaentity.name] then + return true + end + end + end + end + end +end + +function crossbow:get_intersect_pos(ray, plane, collisionbox) + local v = vector.subtract(ray.pos, plane.pos) + local r1 = get_dot_product(v, plane.normal) + local r2 = get_dot_product(ray.dir, plane.normal) + if r2 ~= 0 then + local t = -(r1 / r2) + local td = vector.multiply(ray.dir, {x=t, y=t, z=t}) + local pt = vector.add(ray.pos, td) + local pd = vector.subtract(pt, plane.pos) + if math.abs(pd.x) < collisionbox[4] and + math.abs(pd.y) < collisionbox[5] and + math.abs(pd.z) < collisionbox[6] then + return pt + end + end +end + +function crossbow:process_round(round) + local target = {object=nil, distance=10000} + local p1 = round.pos + local v1 = round.ray + for _,ref in ipairs(castle.objects) do + local p2 = vector.add(ref.pos, ref.offset) + if p1 and p2 and ref.name ~= round.name then + local d = vector.distance(p1, p2) + if d < round.def.step and d < target.distance then + local ray = {pos=p1, dir=v1} + local plane = {pos=p2, normal={x=-1, y=0, z=-1}} + local pos = crossbow:get_intersect_pos(ray, plane, ref.collisionbox) + if pos then + target.object = ref.object + target.pos = pos + target.distance = d + end + end + end + end + if target.object and target.pos then + local success, pos = minetest.line_of_sight(p1, target.pos, 1) + if success then + local user = minetest.get_player_by_name(round.name) + if user then + target.object:punch(user, nil, round.def.tool_caps, v1) + crossbow:spawn_particles(target.pos, CROSSBOW_EXPLOSION_TEXTURE) + end + return 1 + elseif pos and CROSSBOW_ALLOW_NODES == true then + crossbow:punch_node(pos, round.def) + return 1 + end + elseif CROSSBOW_ALLOW_NODES == true then + local d = round.def.step + local p2 = vector.add(p1, vector.multiply(v1, {x=d, y=d, z=d})) + local success, pos = minetest.line_of_sight(p1, p2, 1) + if pos then + crossbow:punch_node(pos, round.def) + return 1 + end + end +end + +local function get_animation_frame(dir) + local angle = math.atan(dir.y) + local frame = 90 - math.floor(angle * 360 / math.pi) + if frame < 1 then + frame = 1 + elseif frame > 180 then + frame = 180 + end + return frame +end + +local function get_target_pos(p1, p2, dir, offset) + local d = vector.distance(p1, p2) - offset + local td = vector.multiply(dir, {x=d, y=d, z=d}) + return vector.add(p1, td) +end + +local function punch_object(puncher, object) + if puncher and crossbow:is_valid_object(object) then + if puncher ~= object then + local dir = puncher:get_look_dir() + local p1 = puncher:getpos() + local p2 = object:getpos() + local tpos = get_target_pos(p1, p2, dir, 0) + crossbow:spawn_particles(tpos, CROSSBOW_EXPLOSION_TEXTURE) + object:punch(puncher, nil, CROSSBOW_BOLT_TOOL_CAPS, dir) + end + end +end + +local function stop_crossbow_bolt(object, pos, stuck) + local acceleration = {x=0, y=-10, z=0} + if stuck == true then + pos = pos or object:getpos() + acceleration = {x=0, y=0, z=0} + object:moveto(pos) + end + object:set_properties({ + physical = true, + collisionbox = {-1/8,-1/8,-1/8, 1/8,1/8,1/8}, + }) + object:setvelocity({x=0, y=0, z=0}) + object:setacceleration(acceleration) +end + +minetest.register_craftitem("castle:crossbow_bolt", { + description = "Bolt", + stack_max = 20, + inventory_image = "castle_crossbow_bolt_inv.png", +}) + +minetest.register_entity("castle:crossbow_bolt_entity", { + physical = false, + visual = "mesh", + mesh = "castle_crossbow_bolt.b3d", + visual_size = {x=1.0, y=1.0}, + textures = { + "castle_crossbow_bolt_uv.png" + }, + timer = 0, + lifetime = CROSSBOW_BOLT_LIFETIME, + player = nil, + state = "init", + node_pos = nil, + collisionbox = {0,0,0, 0,0,0}, + on_activate = function(self, staticdata) + self.object:set_armor_groups({immortal=1}) + if staticdata == "expired" then + self.object:remove() + end + end, + on_punch = function(self, puncher) + if puncher then + if puncher:is_player() then + local stack = "castle:crossbow_bolt" + local inv = puncher:get_inventory() + if inv:room_for_item("main", stack) then + inv:add_item("main", stack) + self.object:remove() + end + end + end + end, + on_step = function(self, dtime) + if self.state == "init" then + return + end + self.timer = self.timer + dtime + self.lifetime = self.lifetime - dtime + if self.lifetime < 0 then + self.object:remove() + return + elseif self.state == "dropped" then + return + elseif self.state == "stuck" then + if self.timer > 1 then + if self.node_pos then + local node = minetest.get_node(self.node_pos) + if node.name then + local item = minetest.registered_items[node.name] + if item then + if not item.walkable then + self.state = "dropped" + stop_crossbow_bolt(self.object) + return + end + end + end + end + self.timer = 0 + end + return + end + if self.timer > 0.2 then + local pos = self.object:getpos() + local dir = vector.normalize(self.object:getvelocity()) + local frame = get_animation_frame(dir) + self.object:set_animation({x=frame, y=frame}, 0) + local objects = minetest.get_objects_inside_radius(pos, 5) + for _,obj in ipairs(objects) do + if crossbow:is_valid_object(obj) then + local collisionbox = {-0.25,-1.0,-0.25, 0.25,0.8,0.25} + local offset = CROSSBOW_PLAYER_OFFSET + if not obj:is_player() then + offset = CROSSBOW_ENTITY_OFFSET + local ent = obj:get_luaentity() + if ent then + local def = minetest.registered_entities[ent.name] + collisionbox = def.collisionbox or collisionbox + end + end + local opos = vector.add(obj:getpos(), offset) + local ray = {pos=pos, dir=dir} + local plane = {pos=opos, normal={x=-1, y=0, z=-1}} + local ipos = crossbow:get_intersect_pos(ray, plane, collisionbox) + if ipos then + punch_object(self.player, obj) + end + end + end + local p = vector.add(pos, vector.multiply(dir, {x=5, y=5, z=5})) + local _, npos = minetest.line_of_sight(pos, p, 1) + if npos then + local node = minetest.get_node(npos) + local tpos = get_target_pos(pos, npos, dir, 0.66) + self.node_pos = npos + self.state = "stuck" + stop_crossbow_bolt(self.object, tpos, true) + minetest.sound_play("castle_crossbow_bolt", {gain = 0.08, max_hear_distance = 2}) + end + self.timer = 0 + end + end, + get_staticdata = function(self) + return "expired" end, }) + minetest.register_tool("castle:crossbow_loaded", { + description = "Crossbow", + inventory_image = "castle_crossbow_loaded.png", + groups = {not_in_creative_inventory=1}, + on_use = function(itemstack, user, pointed_thing) + minetest.sound_play("castle_crossbow_click", {object=user}) + if not minetest.setting_getbool("creative_mode") then + itemstack:add_wear(65535/CROSSBOW_USES) + end + itemstack = "castle:crossbow 1 "..itemstack:get_wear() + local pos = user:getpos() + local dir = user:get_look_dir() + local yaw = user:get_look_yaw() + if pos and dir and yaw then + pos.y = pos.y + 1.5 + local obj = minetest.add_entity(pos, "castle:crossbow_bolt_entity") + local ent = nil + if obj then + ent = obj:get_luaentity() + end + if ent then + obj:set_properties({ + textures = {"castle_crossbow_bolt_uv.png"} + }) + minetest.sound_play("castle_crossbow_shoot", {object=obj}) + local frame = get_animation_frame(dir) + obj:setyaw(yaw + math.pi) + obj:set_animation({x=frame, y=frame}, 0) + obj:setvelocity({x=dir.x * 14, y=dir.y * 14, z=dir.z * 14}) + if pointed_thing.type ~= "nothing" then + local ppos = minetest.get_pointed_thing_position(pointed_thing, false) + local _, npos = minetest.line_of_sight(pos, ppos, 1) + if npos then + ppos = npos + pointed_thing.type = "node" + end + if pointed_thing.type == "object" then + punch_object(user, pointed_thing.ref) + elseif pointed_thing.type == "node" then + local node = minetest.get_node(ppos) + local tpos = get_target_pos(pos, ppos, dir, 0.66) + minetest.after(0.2, function(object, pos, npos) + ent.node_pos = npos + ent.state = "stuck" + stop_crossbow_bolt(object, pos, true) + minetest.sound_play("castle_crossbow_bolt", {gain = 0.08, max_hear_distance = 2}) + end, obj, tpos, ppos) + return itemstack + end + end + obj:setacceleration({x=dir.x * -3, y=-5, z=dir.z * -3}) + ent.player = ent.player or user + ent.state = "flight" + end + end + return itemstack + end, + }) + +minetest.register_tool("castle:crossbow", { + description = "Crossbow", + inventory_image = "castle_crossbow_inv.png", + on_use = function(itemstack, user, pointed_thing) + local inv = user:get_inventory() +if inv:contains_item("main", "castle:crossbow_bolt") then + minetest.sound_play("castle_crossbow_reload", {object=user}) + if not minetest.setting_getbool("creative_mode") then + inv:remove_item("main", "castle:crossbow_bolt 1") + end + return "castle:crossbow_loaded 1 "..itemstack:get_wear() + end + minetest.sound_play("castle_crossbow_click", {object=user}) + end, +}) + +----------- +--Crafting +----------- + minetest.register_craft({ output = 'castle:crossbow', recipe = { @@ -46,3 +434,10 @@ minetest.register_craft({ {'', 'default:stick', ''}, } }) + +minetest.register_craft({ + output = "castle:crossbow_bolt 6", + recipe = { + {'default:stick', 'default:stick', 'default:steel_ingot'}, + } +}) \ No newline at end of file diff --git a/init.lua b/init.lua index 10ec90f..5235d52 100644 --- a/init.lua +++ b/init.lua @@ -7,6 +7,7 @@ dofile(minetest.get_modpath("castle").."/shields_decor.lua") dofile(minetest.get_modpath("castle").."/murder_hole.lua") dofile(minetest.get_modpath("castle").."/orbs.lua") dofile(minetest.get_modpath("castle").."/rope.lua") +dofile(minetest.get_modpath("castle").."/crossbow.lua") minetest.register_node("castle:stonewall", { description = "Castle Wall", diff --git a/models/LICENSE.txt b/models/LICENSE.txt new file mode 100644 index 0000000..7c019e6 --- /dev/null +++ b/models/LICENSE.txt @@ -0,0 +1,10 @@ +License Textures: Stuart Jones - WTFPL + +-castle_crossbow_bolt_inv.png +-castle_crossbow_bolt_uv.png +-castle_crossbow_hit.png + +Licence Models: Stuart Jones - CC-BY-SA 3.0 + +-castle_crossbow_bolt.b3d +-castle_crossbow_bolt.blend \ No newline at end of file diff --git a/models/castle_crossbow_bolt.b3d b/models/castle_crossbow_bolt.b3d new file mode 100644 index 0000000..d24be3c Binary files /dev/null and b/models/castle_crossbow_bolt.b3d differ diff --git a/models/castle_crossbow_bolt.blend b/models/castle_crossbow_bolt.blend new file mode 100644 index 0000000..4cf5f43 Binary files /dev/null and b/models/castle_crossbow_bolt.blend differ diff --git a/models/castle_crossbow_bolt_uv.png b/models/castle_crossbow_bolt_uv.png new file mode 100644 index 0000000..b7d29c5 Binary files /dev/null and b/models/castle_crossbow_bolt_uv.png differ diff --git a/sounds/LICENSE.txt b/sounds/LICENSE.txt new file mode 100644 index 0000000..94f412c --- /dev/null +++ b/sounds/LICENSE.txt @@ -0,0 +1,21 @@ +License Sounds +------------------ + +(From Simple Shooter mod by Stuart Jones) +-castle_crossbow_click.ogg +-castle_crossbow_shoot.ogg +-castle_reload.ogg + +Author : freesound.org +License : Attribution 3.0 Unported (CC BY 3.0) +CC0 1.0 Universal (CC0 1.0) + +------------------ + +(From Minetest Game:default mod) +-default_wood_footstep.1.ogg +(renamed to : castle_crossbow_bolt.ogg) +Author : Mito551 +License : (CC BY-SA) + +------------------ \ No newline at end of file diff --git a/sounds/castle_crossbow_bolt.ogg b/sounds/castle_crossbow_bolt.ogg new file mode 100644 index 0000000..34f63a1 Binary files /dev/null and b/sounds/castle_crossbow_bolt.ogg differ diff --git a/sounds/castle_crossbow_click.ogg b/sounds/castle_crossbow_click.ogg new file mode 100644 index 0000000..8e60db8 Binary files /dev/null and b/sounds/castle_crossbow_click.ogg differ diff --git a/sounds/castle_crossbow_shoot.ogg b/sounds/castle_crossbow_shoot.ogg new file mode 100644 index 0000000..9ce9176 Binary files /dev/null and b/sounds/castle_crossbow_shoot.ogg differ diff --git a/sounds/castle_reload.ogg b/sounds/castle_reload.ogg new file mode 100644 index 0000000..47f7245 Binary files /dev/null and b/sounds/castle_reload.ogg differ diff --git a/sounds/throwing_sound.ogg b/sounds/throwing_sound.ogg deleted file mode 100644 index c8911e5..0000000 Binary files a/sounds/throwing_sound.ogg and /dev/null differ diff --git a/textures/castle_arrow.png b/textures/castle_arrow.png deleted file mode 100644 index 9b72ee9..0000000 Binary files a/textures/castle_arrow.png and /dev/null differ diff --git a/textures/castle_arrow_2.png b/textures/castle_arrow_2.png deleted file mode 100644 index b5980d0..0000000 Binary files a/textures/castle_arrow_2.png and /dev/null differ diff --git a/textures/castle_arrow_back.png b/textures/castle_arrow_back.png deleted file mode 100644 index d680d88..0000000 Binary files a/textures/castle_arrow_back.png and /dev/null differ diff --git a/textures/castle_arrow_front.png b/textures/castle_arrow_front.png deleted file mode 100644 index 828a486..0000000 Binary files a/textures/castle_arrow_front.png and /dev/null differ diff --git a/textures/castle_crossbow.png b/textures/castle_crossbow.png deleted file mode 100644 index e14c45c..0000000 Binary files a/textures/castle_crossbow.png and /dev/null differ diff --git a/textures/castle_crossbow_bolt_inv.png b/textures/castle_crossbow_bolt_inv.png new file mode 100644 index 0000000..87cd847 Binary files /dev/null and b/textures/castle_crossbow_bolt_inv.png differ diff --git a/textures/castle_crossbow_hit.png b/textures/castle_crossbow_hit.png new file mode 100644 index 0000000..0a2b2f3 Binary files /dev/null and b/textures/castle_crossbow_hit.png differ diff --git a/textures/castle_crossbow_inv.png b/textures/castle_crossbow_inv.png new file mode 100644 index 0000000..39bcf07 Binary files /dev/null and b/textures/castle_crossbow_inv.png differ diff --git a/textures/castle_crossbow_loaded.png b/textures/castle_crossbow_loaded.png new file mode 100644 index 0000000..ca2dca7 Binary files /dev/null and b/textures/castle_crossbow_loaded.png differ