diff --git a/mods/ctf_treasure/depends.txt b/mods/ctf_treasure/depends.txt new file mode 100644 index 0000000..f05833c --- /dev/null +++ b/mods/ctf_treasure/depends.txt @@ -0,0 +1,2 @@ +treasurer +default diff --git a/mods/ctf_treasure/init.lua b/mods/ctf_treasure/init.lua new file mode 100644 index 0000000..a2f69b6 --- /dev/null +++ b/mods/ctf_treasure/init.lua @@ -0,0 +1,34 @@ +--treasurer.register_treasure("firearms:m3",0.5,2,{1,2}) +--treasurer.register_treasure("firearms:bullet_12g",0.7,1,{1,10}) +--treasurer.register_treasure("firearms:m4",0.3,3,{1,2}) +--treasurer.register_treasure("firearms:bullet_556mm", 0.5, 2,{1,10}) +--treasurer.register_treasure("firearms:m9",0.2,3,{1,2}) +--treasurer.register_treasure("firearms:bullet_45",0.5,2,{1,20}) +--[[treasurer.register_treasure("firearms:awp",0.05,8,1) +treasurer.register_treasure("firearms:bullet_762mm",0.1,5,{1,22}) +]]-- + + +treasurer.register_treasure("shooter:pistol",0.5,2,{1,5}) +treasurer.register_treasure("shooter:rifle",0.1,2,{1,2}) +treasurer.register_treasure("shooter:shotgun",0.05,2,1) +treasurer.register_treasure("shooter:machine_gun",0.01,2,1) +treasurer.register_treasure("shooter:ammo",0.5,2,{1,10}) + + +treasurer.register_treasure("throwing:arrow_steel",0.1,5,{1,12}) +treasurer.register_treasure("throwing:arrow_stone",0.2,5,{1,12}) +treasurer.register_treasure("throwing:arrow_obsidian",0.3,5,{1,8}) +--treasurer.register_treasure("throwing:arrow_fire",0.1,5,{1,2}) +--treasurer.register_treasure("throwing:arrow_tnt",0.1,5,1) +treasurer.register_treasure("throwing:arrow_steel",0.2,5,{1,12}) +treasurer.register_treasure("throwing:bow_wood",0.9,5,{1,12}) +treasurer.register_treasure("throwing:longbow",0.4,5,1) +treasurer.register_treasure("throwing:bow_steel",0.1,5,1) + + + + + + + diff --git a/mods/gauges/init.lua b/mods/gauges/init.lua new file mode 100644 index 0000000..48dc176 --- /dev/null +++ b/mods/gauges/init.lua @@ -0,0 +1,43 @@ +-- Adds health bars above players. +-- Code by 4aiman, textures by Calinou. Licensed under CC0. + +local hp_bar = { + physical = false, + collisionbox = {x = 0, y = 0, z = 0}, + visual = "sprite", + textures = {"20.png"}, -- The texture is changed later in the code. + visual_size = {x = 1.5, y = 0.09375, z = 1.5}, -- Y value is (1 / 16) * 1.5. + wielder = nil, +} + +function hp_bar:on_step(dtime) + local wielder = self.wielder + if wielder == nil then + self.object:remove() + return + elseif minetest.env:get_player_by_name(wielder:get_player_name()) == nil then + self.object:remove() + return + end + hp = wielder:get_hp() + breath = wielder:get_breath() + self.object:set_properties({textures = {"health_" .. tostring(hp) .. ".png^breath_" .. tostring(breath) .. ".png",},} + ) +end + +minetest.register_entity("gauges:hp_bar", hp_bar) + +function add_HP_gauge(pl) + local pos = pl:getpos() + local ent = minetest.env:add_entity(pos, "gauges:hp_bar") + if ent ~= nil then + ent:set_attach(pl, "", {x = 0, y = 10, z = 0}, {x = 0, y = 0, z = 0}) + ent = ent:get_luaentity() + ent.wielder = pl + end +end + +if minetest.setting_getbool("health_bars") ~= false -- “If not defined or set to true then” +and minetest.setting_getbool("enable_damage") then -- Health bars only display when damage is enabled. + minetest.register_on_joinplayer(add_HP_gauge) +end diff --git a/mods/gauges/textures/breath_0.png b/mods/gauges/textures/breath_0.png new file mode 100644 index 0000000..6aeb84e Binary files /dev/null and b/mods/gauges/textures/breath_0.png differ diff --git a/mods/gauges/textures/breath_1.png b/mods/gauges/textures/breath_1.png new file mode 100644 index 0000000..5182fc8 Binary files /dev/null and b/mods/gauges/textures/breath_1.png differ diff --git a/mods/gauges/textures/breath_10.png b/mods/gauges/textures/breath_10.png new file mode 100644 index 0000000..96fe81a Binary files /dev/null and b/mods/gauges/textures/breath_10.png differ diff --git a/mods/gauges/textures/breath_11.png b/mods/gauges/textures/breath_11.png new file mode 100644 index 0000000..4b5b302 Binary files /dev/null and b/mods/gauges/textures/breath_11.png differ diff --git a/mods/gauges/textures/breath_2.png b/mods/gauges/textures/breath_2.png new file mode 100644 index 0000000..9ad96a8 Binary files /dev/null and b/mods/gauges/textures/breath_2.png differ diff --git a/mods/gauges/textures/breath_3.png b/mods/gauges/textures/breath_3.png new file mode 100644 index 0000000..dc787df Binary files /dev/null and b/mods/gauges/textures/breath_3.png differ diff --git a/mods/gauges/textures/breath_4.png b/mods/gauges/textures/breath_4.png new file mode 100644 index 0000000..f22fdac Binary files /dev/null and b/mods/gauges/textures/breath_4.png differ diff --git a/mods/gauges/textures/breath_5.png b/mods/gauges/textures/breath_5.png new file mode 100644 index 0000000..bc7e23f Binary files /dev/null and b/mods/gauges/textures/breath_5.png differ diff --git a/mods/gauges/textures/breath_6.png b/mods/gauges/textures/breath_6.png new file mode 100644 index 0000000..3396ea6 Binary files /dev/null and b/mods/gauges/textures/breath_6.png differ diff --git a/mods/gauges/textures/breath_65535.png b/mods/gauges/textures/breath_65535.png new file mode 100644 index 0000000..4b5b302 Binary files /dev/null and b/mods/gauges/textures/breath_65535.png differ diff --git a/mods/gauges/textures/breath_7.png b/mods/gauges/textures/breath_7.png new file mode 100644 index 0000000..03dbd4a Binary files /dev/null and b/mods/gauges/textures/breath_7.png differ diff --git a/mods/gauges/textures/breath_8.png b/mods/gauges/textures/breath_8.png new file mode 100644 index 0000000..6c4b40c Binary files /dev/null and b/mods/gauges/textures/breath_8.png differ diff --git a/mods/gauges/textures/breath_9.png b/mods/gauges/textures/breath_9.png new file mode 100644 index 0000000..22edb2d Binary files /dev/null and b/mods/gauges/textures/breath_9.png differ diff --git a/mods/gauges/textures/health_0.png b/mods/gauges/textures/health_0.png new file mode 100644 index 0000000..4b5b302 Binary files /dev/null and b/mods/gauges/textures/health_0.png differ diff --git a/mods/gauges/textures/health_1.png b/mods/gauges/textures/health_1.png new file mode 100644 index 0000000..7f5f05e Binary files /dev/null and b/mods/gauges/textures/health_1.png differ diff --git a/mods/gauges/textures/health_10.png b/mods/gauges/textures/health_10.png new file mode 100644 index 0000000..8c493ab Binary files /dev/null and b/mods/gauges/textures/health_10.png differ diff --git a/mods/gauges/textures/health_11.png b/mods/gauges/textures/health_11.png new file mode 100644 index 0000000..47879eb Binary files /dev/null and b/mods/gauges/textures/health_11.png differ diff --git a/mods/gauges/textures/health_12.png b/mods/gauges/textures/health_12.png new file mode 100644 index 0000000..ef54677 Binary files /dev/null and b/mods/gauges/textures/health_12.png differ diff --git a/mods/gauges/textures/health_13.png b/mods/gauges/textures/health_13.png new file mode 100644 index 0000000..62222cc Binary files /dev/null and b/mods/gauges/textures/health_13.png differ diff --git a/mods/gauges/textures/health_14.png b/mods/gauges/textures/health_14.png new file mode 100644 index 0000000..86aa26b Binary files /dev/null and b/mods/gauges/textures/health_14.png differ diff --git a/mods/gauges/textures/health_15.png b/mods/gauges/textures/health_15.png new file mode 100644 index 0000000..532a915 Binary files /dev/null and b/mods/gauges/textures/health_15.png differ diff --git a/mods/gauges/textures/health_16.png b/mods/gauges/textures/health_16.png new file mode 100644 index 0000000..9541e07 Binary files /dev/null and b/mods/gauges/textures/health_16.png differ diff --git a/mods/gauges/textures/health_17.png b/mods/gauges/textures/health_17.png new file mode 100644 index 0000000..11b4322 Binary files /dev/null and b/mods/gauges/textures/health_17.png differ diff --git a/mods/gauges/textures/health_18.png b/mods/gauges/textures/health_18.png new file mode 100644 index 0000000..aeff4d5 Binary files /dev/null and b/mods/gauges/textures/health_18.png differ diff --git a/mods/gauges/textures/health_19.png b/mods/gauges/textures/health_19.png new file mode 100644 index 0000000..fc95e7c Binary files /dev/null and b/mods/gauges/textures/health_19.png differ diff --git a/mods/gauges/textures/health_2.png b/mods/gauges/textures/health_2.png new file mode 100644 index 0000000..8faf1a0 Binary files /dev/null and b/mods/gauges/textures/health_2.png differ diff --git a/mods/gauges/textures/health_20.png b/mods/gauges/textures/health_20.png new file mode 100644 index 0000000..93d6733 Binary files /dev/null and b/mods/gauges/textures/health_20.png differ diff --git a/mods/gauges/textures/health_3.png b/mods/gauges/textures/health_3.png new file mode 100644 index 0000000..c973e01 Binary files /dev/null and b/mods/gauges/textures/health_3.png differ diff --git a/mods/gauges/textures/health_4.png b/mods/gauges/textures/health_4.png new file mode 100644 index 0000000..e463d7e Binary files /dev/null and b/mods/gauges/textures/health_4.png differ diff --git a/mods/gauges/textures/health_5.png b/mods/gauges/textures/health_5.png new file mode 100644 index 0000000..2b241c5 Binary files /dev/null and b/mods/gauges/textures/health_5.png differ diff --git a/mods/gauges/textures/health_6.png b/mods/gauges/textures/health_6.png new file mode 100644 index 0000000..e0af5f6 Binary files /dev/null and b/mods/gauges/textures/health_6.png differ diff --git a/mods/gauges/textures/health_7.png b/mods/gauges/textures/health_7.png new file mode 100644 index 0000000..35fd7f8 Binary files /dev/null and b/mods/gauges/textures/health_7.png differ diff --git a/mods/gauges/textures/health_8.png b/mods/gauges/textures/health_8.png new file mode 100644 index 0000000..1f45f7b Binary files /dev/null and b/mods/gauges/textures/health_8.png differ diff --git a/mods/gauges/textures/health_9.png b/mods/gauges/textures/health_9.png new file mode 100644 index 0000000..90abc72 Binary files /dev/null and b/mods/gauges/textures/health_9.png differ diff --git a/mods/shooter/.gitignore b/mods/shooter/.gitignore new file mode 100644 index 0000000..0d0a0f5 --- /dev/null +++ b/mods/shooter/.gitignore @@ -0,0 +1,8 @@ +## Generic ignorable patterns and files +*~ +.*.swp +*bak* +tags +*.vim +shooter.conf +crosshair.png diff --git a/mods/shooter/LICENSE.txt b/mods/shooter/LICENSE.txt new file mode 100644 index 0000000..ca3505a --- /dev/null +++ b/mods/shooter/LICENSE.txt @@ -0,0 +1,32 @@ +Minetest Mod - Simple Shooter [shooter] +======================================= + +License Source Code: 2013 Stuart Jones - LGPL v2.1 + +Additional credit for code ideas taken from other mods. + +PilzAdam [throwing] for the throwing physics +ShadowNinja [nuke] for the vm explosion routine + +License Textures: Stuart Jones - WTFPL + +Licence Models: Stuart Jones - CC-BY-SA 3.0 + +License Sounds: freesound.org + + flobert1_20070728.wav by Nonoo - Attribution 3.0 Unported (CC BY 3.0) + + shot.wav by Sergenious - Attribution 3.0 Unported (CC BY 3.0) + + GUNSHOT.WAV by erkanozan - CC0 1.0 Universal (CC0 1.0) + + winchester-rifle-cock-reload.wav by MentalSanityOff - CC0 1.0 Universal (CC0 1.0) + + trigger-with-hammer-fall.wav by Nanashi - CC0 1.0 Universal (CC0 1.0) + + woosh.wav by ReadeOnly - CC0 1.0 Universal (CC0 1.0) + + AGM-114 Hellfire Rocket Missile Launch.flac by qubodup - CC0 1.0 Universal (CC0 1.0) + + Sparkler.aif by Ned Bouhalassa - CC0 1.0 Universal (CC0 1.0) + diff --git a/mods/shooter/README.txt b/mods/shooter/README.txt new file mode 100644 index 0000000..5287bf4 --- /dev/null +++ b/mods/shooter/README.txt @@ -0,0 +1,169 @@ +Minetest Mod - Simple Shooter [shooter] +======================================= + +Mod Version: 0.5.3 + +Minetest Version: 0.4.9, 0.4.10, 0.4.11 + +Depends: default, dye, tnt, wool + +An experimental first person shooter mod that uses simple vector mathematics +to produce an accurate and server-firendly method of hit detection. + +By default this mod is configured to work only against other players in +multiplayer mode and against Simple Mobs [mobs] in singleplayer mode. + +Default configuration can be customised by adding a shooter.conf file to +the mod's main directory, see shooter.conf.example for more details. + +This is still very much a work in progress which I eventually plan to use +as the base for a 'Spades' style FPS game using the minetest engine. + +Crafting +======== + + = grey, black, red, yellow, green, cyan, blue, magenta + +A = Arrow [shooter:arrow_white] +C = Color Dye [dye:] +W = Wooden Stick [default:stick] +P = Paper [default:paper] +S = Steel Ingot [default:steel_ingot] +B = Bronze Ingot [default:bronze_ingot] +M = Mese Crystal [default:mese_crysytal] +D = Diamond [default:diamond] +R = Red Wool [wool:red] +G = Gun Powder [tnt:gunpowder] + +Crossbow: [shooter:crossbow] + ++---+---+---+ +| W | W | W | ++---+---+---+ +| W | W | | ++---+---+---+ +| W | | B | ++---+---+---+ + +White Arrow: [shooter:arrow_white] + ++---+---+---+ +| S | | | ++---+---+---+ +| | W | P | ++---+---+---+ +| | P | W | ++---+---+---+ + +Coloured Arrow: [shooter:arrow_] + ++---+---+ +| C | A | ++---+---+ + +Pistol: [shooter:pistol] + ++---+---+ +| S | S | ++---+---+ +| | M | ++---+---+ + +Rifle: [shooter:rifle] + ++---+---+---+ +| S | | | ++---+---+---+ +| | B | | ++---+---+---+ +| | M | B | ++---+---+---+ + +Shotgun: [shooter:shotgun] + ++---+---+---+ +| S | | | ++---+---+---+ +| | S | | ++---+---+---+ +| | M | B | ++---+---+---+ + +Sub Machine Gun: [shooter:machine_gun] + ++---+---+---+ +| S | S | S | ++---+---+---+ +| | B | M | ++---+---+---+ +| | B | | ++---+---+---+ + +Ammo Pack: [shooter:ammo] + ++---+---+ +| G | B | ++---+---+ + +Grappling Hook: [shooter:grapple_hook] + ++---+---+---+ +| S | S | D | ++---+---+---+ +| S | S | | ++---+---+---+ +| D | | S | ++---+---+---+ + +Grappling Hook Gun: [shooter:grapple_gun] + ++---+---+ +| S | S | ++---+---+ +| | D | ++---+---+ + +Flare: [shooter:flare] + ++---+---+ +| G | R | ++---+---+ + +Flare Gun: [shooter:flaregun] + ++---+---+---+ +| R | R | R | ++---+---+---+ +| | | S | ++---+---+---+ + +Grenade: [shooter:grenade] + ++---+---+ +| G | S | ++---+---+ + +Flare Gun: [shooter:rocket_gun] + ++---+---+---+ +| B | S | S | ++---+---+---+ +| | | D | ++---+---+---+ + +Rocket: [shooter:rocket] + ++---+---+---+ +| B | G | B | ++---+---+---+ + +Turret: [shooter:turret] + ++---+---+---+ +| B | B | S | ++---+---+---+ +| | B | S | ++---+---+---+ +| | D | | ++---+---+---+ + diff --git a/mods/shooter/crossbow.lua b/mods/shooter/crossbow.lua new file mode 100644 index 0000000..8304162 --- /dev/null +++ b/mods/shooter/crossbow.lua @@ -0,0 +1,293 @@ +SHOOTER_CROSSBOW_USES = 50 +SHOOTER_ARROW_TOOL_CAPS = {damage_groups={fleshy=2}} +SHOOTER_ARROW_LIFETIME = 180 -- 3 minutes + +minetest.register_alias("shooter:arrow", "shooter:arrow_white") +minetest.register_alias("shooter:crossbow_loaded", "shooter:crossbow_loaded_white") + +local dye_basecolors = (dye and dye.basecolors) or + {"white", "grey", "black", "red", "yellow", "green", "cyan", "blue", "magenta"} + +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 + +-- name is the overlay texture name, colour is used to select the wool texture +local function get_texture(name, colour) + return "shooter_"..name..".png^wool_"..colour..".png^shooter_"..name..".png^[makealpha:255,126,126" +end + +minetest.register_entity("shooter:arrow_entity", { + physical = false, + visual = "mesh", + mesh = "shooter_arrow.b3d", + visual_size = {x=1, y=1}, + textures = { + get_texture("arrow_uv", "white"), + }, + color = "white", + timer = 0, + lifetime = SHOOTER_ARROW_LIFETIME, + player = nil, + state = "init", + node_pos = nil, + collisionbox = {0,0,0, 0,0,0}, + stop = function(self, pos) + local acceleration = {x=0, y=-10, z=0} + if self.state == "stuck" then + pos = pos or self.object:getpos() + acceleration = {x=0, y=0, z=0} + end + if pos then + self.object:moveto(pos) + end + self.object:set_properties({ + physical = true, + collisionbox = {-1/8,-1/8,-1/8, 1/8,1/8,1/8}, + }) + self.object:setvelocity({x=0, y=0, z=0}) + self.object:setacceleration(acceleration) + end, + strike = function(self, object) + local puncher = self.player + if puncher and shooter: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) + shooter:spawn_particles(tpos, SHOOTER_EXPLOSION_TEXTURE) + object:punch(puncher, nil, SHOOTER_ARROW_TOOL_CAPS, dir) + end + end + self:stop(object:getpos()) + end, + 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 = "shooter:arrow_"..self.color + 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" + self:stop() + 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 shooter:is_valid_object(obj) then + local collisionbox = {-0.25,-1.0,-0.25, 0.25,0.8,0.25} + local offset = SHOOTER_PLAYER_OFFSET + if not obj:is_player() then + offset = SHOOTER_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 = shooter:get_intersect_pos(ray, plane, collisionbox) + if ipos then + self:strike(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" + self:stop(tpos) + shooter:play_node_sound(node, npos) + end + self.timer = 0 + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +for _, color in pairs(dye_basecolors) do + minetest.register_craftitem("shooter:arrow_"..color, { + description = color:gsub("%a", string.upper, 1).." Arrow", + inventory_image = get_texture("arrow_inv", color), + }) + minetest.register_tool("shooter:crossbow_loaded_"..color, { + description = "Crossbow", + inventory_image = get_texture("crossbow_loaded", color), + groups = {not_in_creative_inventory=1}, + on_use = function(itemstack, user, pointed_thing) + minetest.sound_play("shooter_click", {object=user}) + if not minetest.setting_getbool("creative_mode") then + itemstack:add_wear(65535/SHOOTER_CROSSBOW_USES) + end + itemstack = "shooter: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, "shooter:arrow_entity") + local ent = nil + if obj then + ent = obj:get_luaentity() + end + if ent then + ent.player = ent.player or user + ent.state = "flight" + ent.color = color + obj:set_properties({ + textures = {get_texture("arrow_uv", color)} + }) + minetest.sound_play("shooter_throw", {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 + ent:strike(pointed_thing.ref) + return itemstack + 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" + ent:stop(pos) + shooter:play_node_sound(node, npos) + end, obj, tpos, ppos) + return itemstack + end + end + obj:setacceleration({x=dir.x * -3, y=-5, z=dir.z * -3}) + end + end + return itemstack + end, + }) +end + +minetest.register_tool("shooter:crossbow", { + description = "Crossbow", + inventory_image = "shooter_crossbow.png", + on_use = function(itemstack, user, pointed_thing) + local inv = user:get_inventory() + local stack = inv:get_stack("main", user:get_wield_index() + 1) + local color = string.match(stack:get_name(), "shooter:arrow_(%a+)") + if color then + minetest.sound_play("shooter_reload", {object=user}) + if not minetest.setting_getbool("creative_mode") then + inv:remove_item("main", "shooter:arrow_"..color.." 1") + end + return "shooter:crossbow_loaded_"..color.." 1 "..itemstack:get_wear() + end + for _, color in pairs(dye_basecolors) do + if inv:contains_item("main", "shooter:arrow_"..color) then + minetest.sound_play("shooter_reload", {object=user}) + if not minetest.setting_getbool("creative_mode") then + inv:remove_item("main", "shooter:arrow_"..color.." 1") + end + return "shooter:crossbow_loaded_"..color.." 1 "..itemstack:get_wear() + end + end + minetest.sound_play("shooter_click", {object=user}) + end, +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:crossbow", + recipe = { + {"default:stick", "default:stick", "default:stick"}, + {"default:stick", "default:stick", ""}, + {"default:stick", "", "default:bronze_ingot"}, + }, + }) + minetest.register_craft({ + output = "shooter:arrow_white", + recipe = { + {"default:steel_ingot", "", ""}, + {"", "default:stick", "default:paper"}, + {"", "default:paper", "default:stick"}, + }, + }) + for _, color in pairs(dye_basecolors) do + if color ~= "white" then + minetest.register_craft({ + output = "shooter:arrow_"..color, + recipe = { + {"", "dye:"..color, "shooter:arrow_white"}, + }, + }) + end + end +end + diff --git a/mods/shooter/depends.txt b/mods/shooter/depends.txt new file mode 100644 index 0000000..d2aa8ff --- /dev/null +++ b/mods/shooter/depends.txt @@ -0,0 +1,5 @@ +default +dye +tnt +wool +mobs? diff --git a/mods/shooter/flaregun.lua b/mods/shooter/flaregun.lua new file mode 100644 index 0000000..6904d3e --- /dev/null +++ b/mods/shooter/flaregun.lua @@ -0,0 +1,147 @@ +minetest.register_craftitem("shooter:flare", { + description = "Flare", + inventory_image = "shooter_flare_inv.png", +}) + +minetest.register_node("shooter:flare_light", { + drawtype = "glasslike", + tiles = {"shooter_flare_light.png"}, + paramtype = "light", + groups = {not_in_creative_inventory=1}, + drop = "", + walkable = false, + buildable_to = true, + sunlight_propagates = true, + light_source = LIGHT_MAX, + pointable = false, +}) + +minetest.register_abm({ + nodenames = "shooter:flare_light", + interval = 5, + chance = 1, + action = function(pos, node) + local time = os.time() + local meta = minetest.get_meta(pos) + local init_time = meta:get_int("init_time") or 0 + if time > init_time + 30 then + local id = meta:get_int("particle_id") + if id then + minetest.delete_particlespawner(id) + end + minetest.remove_node(pos) + end + end, +}) + +minetest.register_entity("shooter:flare_entity", { + physical = true, + timer = 0, + visual = "cube", + visual_size = {x=1/8, y=1/8}, + textures = { + "shooter_flare.png", + "shooter_flare.png", + "shooter_flare.png", + "shooter_flare.png", + "shooter_flare.png", + "shooter_flare.png", + }, + player = nil, + collisionbox = {-1/16,-1/16,-1/16, 1/16,1/16,1/16}, + on_activate = function(self, staticdata) + if staticdata == "expired" then + self.object:remove() + end + end, + on_step = function(self, dtime) + self.timer = self.timer + dtime + if self.timer > 0.2 then + local pos = self.object:getpos() + local below = {x=pos.x, y=pos.y - 1, z=pos.z} + local node = minetest.get_node(below) + if node.name ~= "air" then + self.object:setvelocity({x=0, y=-10, z=0}) + self.object:setacceleration({x=0, y=0, z=0}) + if minetest.get_node(pos).name == "air" and + node.name ~= "default:water_source" and + node.name ~= "default:water_flowing" then + minetest.place_node(pos, {name="shooter:flare_light"}) + local meta = minetest.get_meta(pos) + meta:set_int("particle_id", id) + meta:set_int("init_time", os.time()) + pos.y = pos.y - 0.1 + local id = minetest.add_particlespawner( + 1000, 30, pos, pos, + {x=-1, y=1, z=-1}, {x=1, y=1, z=1}, + {x=2, y=-2, z=-2}, {x=2, y=-2, z=2}, + 0.1, 0.75, 1, 8, false, "shooter_flare_particle.png" + ) + local sound = minetest.sound_play("shooter_flare_burn", { + object = self.player, + loop = true, + }) + minetest.after(30, function(sound) + minetest.sound_stop(sound) + end, sound) + end + self.object:remove() + end + self.timer = 0 + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +minetest.register_tool("shooter:flaregun", { + description = "Flare Gun", + inventory_image = "shooter_flaregun.png", + on_use = function(itemstack, user, pointed_thing) + local inv = user:get_inventory() + if not inv:contains_item("main", "shooter:flare") then + minetest.sound_play("shooter_click", {object=user}) + return itemstack + end + if not minetest.setting_getbool("creative_mode") then + inv:remove_item("main", "shooter:flare 1") + itemstack:add_wear(65535/100) + end + 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, "shooter:flare_entity") + if obj then + minetest.sound_play("shooter_flare_fire", {object=obj}) + obj:setvelocity({x=dir.x * 16, y=dir.y * 16, z=dir.z * 16}) + obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3}) + obj:setyaw(yaw + math.pi) + local ent = obj:get_luaentity() + if ent then + ent.player = ent.player or user + end + end + end + return itemstack + end, +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:flare", + recipe = { + {"tnt:gunpowder", "wool:red"}, + }, + }) + minetest.register_craft({ + output = "shooter:flaregun", + recipe = { + {"wool:red", "wool:red", "wool:red"}, + {"", "", "default:steel_ingot"} + }, + }) +end + diff --git a/mods/shooter/grapple.lua b/mods/shooter/grapple.lua new file mode 100644 index 0000000..44a60b1 --- /dev/null +++ b/mods/shooter/grapple.lua @@ -0,0 +1,132 @@ +local function throw_hook(itemstack, user, vel) + local inv = user:get_inventory() + local pos = user:getpos() + local dir = user:get_look_dir() + local yaw = user:get_look_yaw() + if pos and dir and yaw then + if not minetest.setting_getbool("creative_mode") then + itemstack:add_wear(65535/100) + end + pos.y = pos.y + 1.5 + local obj = minetest.add_entity(pos, "shooter:hook") + if obj then + minetest.sound_play("shooter_throw", {object=obj}) + obj:setvelocity({x=dir.x * vel, y=dir.y * vel, z=dir.z * vel}) + obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3}) + obj:setyaw(yaw + math.pi) + local ent = obj:get_luaentity() + if ent then + ent.player = ent.player or user + ent.itemstack = itemstack + end + end + end +end + +minetest.register_entity("shooter:hook", { + physical = true, + timer = 0, + visual = "wielditem", + visual_size = {x=1/2, y=1/2}, + textures = {"shooter:grapple_hook"}, + player = nil, + itemstack = "", + collisionbox = {-1/4,-1/4,-1/4, 1/4,1/4,1/4}, + on_activate = function(self, staticdata) + self.object:set_armor_groups({fleshy=0}) + if staticdata == "expired" then + self.object:remove() + end + end, + on_step = function(self, dtime) + if not self.player then + return + end + self.timer = self.timer + dtime + if self.timer > 0.25 then + local pos = self.object:getpos() + local below = {x=pos.x, y=pos.y - 1, z=pos.z} + local node = minetest.get_node(below) + if node.name ~= "air" then + self.object:setvelocity({x=0, y=-10, z=0}) + self.object:setacceleration({x=0, y=0, z=0}) + if minetest.get_item_group(node.name, "liquid") == 0 and + minetest.get_node(pos).name == "air" then + self.player:moveto(pos) + end + if minetest.get_item_group(node.name, "lava") == 0 then + minetest.add_item(pos, self.itemstack) + end + self.object:remove() + end + self.timer = 0 + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +minetest.register_tool("shooter:grapple_hook", { + description = "Grappling Hook", + inventory_image = "shooter_hook.png", + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type ~= "nothing" then + return itemstack + end + throw_hook(itemstack, user, 14) + return "" + end, +}) + +minetest.register_tool("shooter:grapple_gun", { + description = "Grappling Gun", + inventory_image = "shooter_hook_gun.png", + on_use = function(itemstack, user, pointed_thing) + local inv = user:get_inventory() + if inv:contains_item("main", "shooter:grapple_hook") and + inv:contains_item("main", "tnt:gunpowder") then + inv:remove_item("main", "tnt:gunpowder") + minetest.sound_play("shooter_reload", {object=user}) + local stack = inv:remove_item("main", "shooter:grapple_hook") + itemstack = "shooter:grapple_gun_loaded 1 "..stack:get_wear() + else + minetest.sound_play("shooter_click", {object=user}) + end + return itemstack + end, +}) + +minetest.register_tool("shooter:grapple_gun_loaded", { + description = "Grappling Gun", + inventory_image = "shooter_hook_gun_loaded.png", + groups = {not_in_creative_inventory=1}, + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type ~= "nothing" then + return itemstack + end + minetest.sound_play("shooter_pistol", {object=user}) + itemstack = ItemStack("shooter:grapple_hook 1 "..itemstack:get_wear()) + throw_hook(itemstack, user, 20) + return "shooter:grapple_gun" + end, +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:grapple_hook", + recipe = { + {"default:steel_ingot", "default:steel_ingot", "default:diamond"}, + {"default:steel_ingot", "default:steel_ingot", ""}, + {"default:diamond", "", "default:steel_ingot"}, + }, + }) + minetest.register_craft({ + output = "shooter:grapple_gun", + recipe = { + {"", "default:steel_ingot", "default:steel_ingot"}, + {"", "", "default:diamond"}, + }, + }) +end + diff --git a/mods/shooter/grenade.lua b/mods/shooter/grenade.lua new file mode 100644 index 0000000..5dac7f8 --- /dev/null +++ b/mods/shooter/grenade.lua @@ -0,0 +1,80 @@ +minetest.register_entity("shooter:grenade_entity", { + physical = false, + timer = 0, + visual = "cube", + visual_size = {x=1/8, y=1/8}, + textures = { + "shooter_grenade.png", + "shooter_grenade.png", + "shooter_grenade.png", + "shooter_grenade.png", + "shooter_grenade.png", + "shooter_grenade.png", + }, + player = nil, + collisionbox = {0,0,0, 0,0,0}, + on_activate = function(self, staticdata) + if staticdata == "expired" then + self.object:remove() + end + end, + on_step = function(self, dtime) + self.timer = self.timer + dtime + if self.timer > 0.2 then + local pos = self.object:getpos() + local below = {x=pos.x, y=pos.y - 1, z=pos.z} + if minetest.get_node(below).name ~= "air" then + self.object:remove() + shooter:blast(pos, 1, 25, 5, self.player) + end + self.timer = 0 + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +minetest.register_tool("shooter:grenade", { + description = "Grenade", + inventory_image = "shooter_hand_grenade.png", + on_use = function(itemstack, user, pointed_thing) + if not minetest.setting_getbool("creative_mode") then + itemstack = "" + end + if pointed_thing.type ~= "nothing" then + local pointed = minetest.get_pointed_thing_position(pointed_thing) + if vector.distance(user:getpos(), pointed) < 8 then + shooter:blast(pointed, 1, 25, 5) + return + end + end + local pos = user:getpos() + local dir = user:get_look_dir() + local yaw = user:get_look_yaw() + if pos and dir then + pos.y = pos.y + 1.5 + local obj = minetest.add_entity(pos, "shooter:grenade_entity") + if obj then + obj:setvelocity({x=dir.x * 15, y=dir.y * 15, z=dir.z * 15}) + obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3}) + obj:setyaw(yaw + math.pi) + local ent = obj:get_luaentity() + if ent then + ent.player = ent.player or user + end + end + end + return itemstack + end, +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:grenade", + recipe = { + {"tnt:gunpowder", "default:steel_ingot"}, + }, + }) +end + diff --git a/mods/shooter/guns.lua b/mods/shooter/guns.lua new file mode 100644 index 0000000..d31a724 --- /dev/null +++ b/mods/shooter/guns.lua @@ -0,0 +1,127 @@ +shooter:register_weapon("shooter:pistol", { + description = "Pistol", + inventory_image = "shooter_pistol.png", + rounds = 200, + spec = { + range = 100, + step = 20, + tool_caps = {full_punch_interval=0.5, damage_groups={fleshy=2}}, + groups = {snappy=3, fleshy=3, oddly_breakable_by_hand=3}, + sound = "shooter_pistol", + particle = "shooter_cap.png", + }, +}) + +shooter:register_weapon("shooter:rifle", { + description = "Rifle", + inventory_image = "shooter_rifle.png", + rounds = 100, + spec = { + range = 200, + step = 30, + tool_caps = {full_punch_interval=1.0, damage_groups={fleshy=3}}, + groups = {snappy=3, crumbly=3, choppy=3, fleshy=2, oddly_breakable_by_hand=2}, + sound = "shooter_rifle", + particle = "shooter_bullet.png", + }, +}) + +shooter:register_weapon("shooter:shotgun", { + description = "Shotgun", + inventory_image = "shooter_shotgun.png", + rounds = 50, + spec = { + range = 50, + step = 15, + tool_caps = {full_punch_interval=1.5, damage_groups={fleshy=4}}, + groups = {cracky=3, snappy=2, crumbly=2, choppy=2, fleshy=1, oddly_breakable_by_hand=1}, + sound = "shooter_shotgun", + particle = "smoke_puff.png", + }, +}) + +shooter:register_weapon("shooter:machine_gun", { + description = "Sub Machine Gun", + inventory_image = "shooter_smgun.png", + rounds = 50, + shots = 4, + spec = { + range = 100, + step = 20, + tool_caps = {full_punch_interval=0.125, damage_groups={fleshy=2}}, + groups = {snappy=3, fleshy=3, oddly_breakable_by_hand=3}, + sound = "shooter_pistol", + particle = "shooter_cap.png", + }, +}) + +minetest.register_craftitem("shooter:ammo", { + description = "Ammo pack", + inventory_image = "shooter_ammo.png", +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:pistol 1 65535", + recipe = { + {"default:steel_ingot", "default:steel_ingot"}, + {"", "default:mese_crystal"}, + }, + }) + minetest.register_craft({ + output = "shooter:rifle 1 65535", + recipe = { + {"default:steel_ingot", "", ""}, + {"", "default:bronze_ingot", ""}, + {"", "default:mese_crystal", "default:bronze_ingot"}, + }, + }) + minetest.register_craft({ + output = "shooter:shotgun 1 65535", + recipe = { + {"default:steel_ingot", "", ""}, + {"", "default:steel_ingot", ""}, + {"", "default:mese_crystal", "default:bronze_ingot"}, + }, + }) + minetest.register_craft({ + output = "shooter:machine_gun 1 65535", + recipe = { + {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, + {"", "default:bronze_ingot", "default:mese_crystal"}, + {"", "default:bronze_ingot", ""}, + }, + }) + minetest.register_craft({ + output = "shooter:ammo", + recipe = { + {"tnt:gunpowder", "default:bronze_ingot"}, + }, + }) +end + +local rounds_update_time = 0 + +minetest.register_globalstep(function(dtime) + shooter.time = shooter.time + dtime + if shooter.time - rounds_update_time > SHOOTER_ROUNDS_UPDATE_TIME then + for i, round in ipairs(shooter.rounds) do + if shooter:process_round(round) or round.dist > round.def.range then + table.remove(shooter.rounds, i) + else + local v = vector.multiply(round.ray, round.def.step) + shooter.rounds[i].pos = vector.add(round.pos, v) + shooter.rounds[i].dist = round.dist + round.def.step + end + end + rounds_update_time = shooter.time + end + if shooter.time > 100000 then + shooter.shots = {} + rounds_update_time = 0 + shooter.reload_time = 0 + shooter.update_time = 0 + shooter.time = 0 + end +end) + diff --git a/mods/shooter/init.lua b/mods/shooter/init.lua new file mode 100644 index 0000000..1eceb96 --- /dev/null +++ b/mods/shooter/init.lua @@ -0,0 +1,26 @@ +local modpath = minetest.get_modpath(minetest.get_current_modname()) + +dofile(modpath.."/shooter.lua") + +if SHOOTER_ENABLE_CROSSBOW == true then + dofile(modpath.."/crossbow.lua") +end +if SHOOTER_ENABLE_GUNS == true then + dofile(modpath.."/guns.lua") +end +if SHOOTER_ENABLE_FLARES == true then + dofile(modpath.."/flaregun.lua") +end +if SHOOTER_ENABLE_HOOK == true then + dofile(modpath.."/grapple.lua") +end +if SHOOTER_ENABLE_GRENADES == true then + dofile(modpath.."/grenade.lua") +end +if SHOOTER_ENABLE_ROCKETS == true then + dofile(modpath.."/rocket.lua") +end +if SHOOTER_ENABLE_TURRETS == true then + dofile(modpath.."/turret.lua") +end + diff --git a/mods/shooter/models/shooter_arrow.b3d b/mods/shooter/models/shooter_arrow.b3d new file mode 100644 index 0000000..d24be3c Binary files /dev/null and b/mods/shooter/models/shooter_arrow.b3d differ diff --git a/mods/shooter/models/shooter_arrow.blend b/mods/shooter/models/shooter_arrow.blend new file mode 100644 index 0000000..4cf5f43 Binary files /dev/null and b/mods/shooter/models/shooter_arrow.blend differ diff --git a/mods/shooter/models/shooter_turret.b3d b/mods/shooter/models/shooter_turret.b3d new file mode 100644 index 0000000..8aa291c Binary files /dev/null and b/mods/shooter/models/shooter_turret.b3d differ diff --git a/mods/shooter/models/shooter_turret.blend b/mods/shooter/models/shooter_turret.blend new file mode 100644 index 0000000..c9519bd Binary files /dev/null and b/mods/shooter/models/shooter_turret.blend differ diff --git a/mods/shooter/rocket.lua b/mods/shooter/rocket.lua new file mode 100644 index 0000000..165282d --- /dev/null +++ b/mods/shooter/rocket.lua @@ -0,0 +1,113 @@ +minetest.register_craftitem("shooter:rocket", { + description = "Rocket", + stack_max = 1, + inventory_image = "shooter_rocket_inv.png", +}) + +minetest.register_entity("shooter:rocket_entity", { + physical = false, + timer = 0, + visual = "cube", + visual_size = {x=1/8, y=1/8}, + textures = { + "shooter_bullet.png", + "shooter_bullet.png", + "shooter_bullet.png", + "shooter_bullet.png", + "shooter_bullet.png", + "shooter_bullet.png", + }, + player = nil, + collisionbox = {0,0,0, 0,0,0}, + on_activate = function(self, staticdata) + if staticdata == "expired" then + self.object:remove() + end + end, + on_step = function(self, dtime) + self.timer = self.timer + dtime + if self.timer > 0.2 then + local pos = self.object:getpos() + if minetest.get_node(pos).name ~= "air" then + self.object:remove() + shooter:blast(pos, 2, 50, 7, self.player) + end + self.timer = 0 + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +minetest.register_tool("shooter:rocket_gun_loaded", { + description = "Rocket Gun", + inventory_image = "shooter_rocket_gun_loaded.png", + groups = {not_in_creative_inventory=1}, + on_use = function(itemstack, user, pointed_thing) + if not minetest.setting_getbool("creative_mode") then + itemstack:add_wear(65535/50) + end + itemstack = "shooter:rocket_gun 1 "..itemstack:get_wear() + if pointed_thing.type ~= "nothing" then + local pointed = minetest.get_pointed_thing_position(pointed_thing) + if vector.distance(user:getpos(), pointed) < 8 then + shooter:blast(pointed, 2, 50, 7) + return itemstack + end + end + 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, "shooter:rocket_entity") + if obj then + minetest.sound_play("shooter_rocket_fire", {object=obj}) + obj:setvelocity({x=dir.x * 20, y=dir.y * 20, z=dir.z * 20}) + obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3}) + obj:setyaw(yaw + math.pi) + local ent = obj:get_luaentity() + if ent then + ent.player = ent.player or user + end + end + end + return itemstack + end, +}) + +minetest.register_tool("shooter:rocket_gun", { + description = "Rocket Gun", + inventory_image = "shooter_rocket_gun.png", + on_use = function(itemstack, user, pointed_thing) + local inv = user:get_inventory() + if inv:contains_item("main", "shooter:rocket") then + minetest.sound_play("shooter_reload", {object=user}) + if not minetest.setting_getbool("creative_mode") then + inv:remove_item("main", "shooter:rocket 1") + end + itemstack = "shooter:rocket_gun_loaded 1 "..itemstack:get_wear() + else + minetest.sound_play("shooter_click", {object=user}) + end + return itemstack + end, +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:rocket_gun", + recipe = { + {"default:bronze_ingot", "default:steel_ingot", "default:steel_ingot"}, + {"", "", "default:diamond"}, + }, + }) + minetest.register_craft({ + output = "shooter:rocket", + recipe = { + {"default:bronze_ingot", "tnt:gunpowder", "default:bronze_ingot"}, + }, + }) +end + diff --git a/mods/shooter/shooter.conf.example b/mods/shooter/shooter.conf.example new file mode 100644 index 0000000..89cc064 --- /dev/null +++ b/mods/shooter/shooter.conf.example @@ -0,0 +1,81 @@ +-- Simple Shooter config example + +-- Global Constants (defaults) + +-- Enable admin super weapons +-- This lets admins shoot guns automatically after 2 seconds without munition. +SHOOTER_ADMIN_WEAPONS = false + +-- Enable node destruction with explosives +SHOOTER_ENABLE_BLASTING = true + +-- Enable Crossbow +SHOOTER_ENABLE_CROSSBOW = true + +-- Enable basic guns (Pistol, Rifle, Shotgun, Machine Gun) +SHOOTER_ENABLE_GUNS = true + +-- Enable Flare Gun +SHOOTER_ENABLE_FLARES = true + +-- Enable Grappling Hook +SHOOTER_ENABLE_HOOK = true + +-- Enable Grenades +SHOOTER_ENABLE_GRENADES = true + +-- Enable Rocket Gun +SHOOTER_ENABLE_ROCKETS = true + +-- Enable Turrret Gun +SHOOTER_ENABLE_TURRETS = true + +-- Enable Crafting +SHOOTER_ENABLE_CRAFTING = true + +-- Enable particle effects +SHOOTER_ENABLE_PARTICLE_FX = true + +-- Enable protection mod support, requires a protection mod that utilizes +-- minetest.is_protected(), tested with TenPlus1's version of [protector] +SHOOTER_ENABLE_PROTECTION = false + +-- Particle texture used when a player or entity is hit +SHOOTER_EXPLOSION_TEXTURE = "shooter_hit.png" + +-- Allow node destruction +SHOOTER_ALLOW_NODES = true + +-- Allow entities in multiplayer mode +SHOOTER_ALLOW_ENTITIES = false + +-- Allow players in multiplayer mode +SHOOTER_ALLOW_PLAYERS = true + +-- How often objects are fully reloaded +SHOOTER_OBJECT_RELOAD_TIME = 1 + +-- How often object positions are updated +SHOOTER_OBJECT_UPDATE_TIME = 0.25 + +-- How often rounds are processed +SHOOTER_ROUNDS_UPDATE_TIME = 0.4 + +-- Player collision box offset (may require adjustment for some games) +SHOOTER_PLAYER_OFFSET = {x=0, y=1, z=0} + +-- Entity collision box offset (may require adjustment for other mobs) +SHOOTER_ENTITY_OFFSET = {x=0, y=0, z=0} + +-- Shootable entities (default support for Simple Mobs) +SHOOTER_ENTITIES = { + "mobs:dirt_monster", + "mobs:stone_monster", + "mobs:sand_monster", + "mobs:tree_monster", + "mobs:sheep", + "mobs:rat", + "mobs:oerkki", + "mobs:dungeon_master", +} + diff --git a/mods/shooter/shooter.lua b/mods/shooter/shooter.lua new file mode 100644 index 0000000..6e9ae3d --- /dev/null +++ b/mods/shooter/shooter.lua @@ -0,0 +1,465 @@ +shooter = { + time = 0, + objects = {}, + rounds = {}, + shots = {}, + update_time = 0, + reload_time = 0, +} + +SHOOTER_ADMIN_WEAPONS = false +SHOOTER_ENABLE_BLASTING = false +SHOOTER_ENABLE_CROSSBOW = true +SHOOTER_ENABLE_GUNS = true +SHOOTER_ENABLE_FLARES = true +SHOOTER_ENABLE_HOOK = true +SHOOTER_ENABLE_GRENADES = true +SHOOTER_ENABLE_ROCKETS = true +SHOOTER_ENABLE_TURRETS = true +SHOOTER_ENABLE_CRAFTING = true +SHOOTER_ENABLE_PARTICLE_FX = true +SHOOTER_ENABLE_PROTECTION = false +SHOOTER_EXPLOSION_TEXTURE = "shooter_hit.png" +SHOOTER_ALLOW_NODES = true +SHOOTER_ALLOW_ENTITIES = false +SHOOTER_ALLOW_PLAYERS = true +SHOOTER_OBJECT_RELOAD_TIME = 1 +SHOOTER_OBJECT_UPDATE_TIME = 0.25 +SHOOTER_ROUNDS_UPDATE_TIME = 0.4 +SHOOTER_PLAYER_OFFSET = {x=0, y=1, z=0} +SHOOTER_ENTITY_OFFSET = {x=0, y=0, z=0} +SHOOTER_ENTITIES = {} +for k, v in pairs(minetest.registered_entities) do + if string.find(k, "^mobs") then + table.insert(SHOOTER_ENTITIES, k) + end +end + +local singleplayer = minetest.is_singleplayer() +if singleplayer then + SHOOTER_ENABLE_BLASTING = true + SHOOTER_ALLOW_ENTITIES = true + SHOOTER_ALLOW_PLAYERS = false +end + +local modpath = minetest.get_modpath(minetest.get_current_modname()) +local worldpath = minetest.get_worldpath() +local input = io.open(modpath.."/shooter.conf", "r") +if input then + dofile(modpath.."/shooter.conf") + input:close() + input = nil +end +input = io.open(worldpath.."/shooter.conf", "r") +if input then + dofile(worldpath.."/shooter.conf") + input:close() + input = nil +end + +local allowed_entities = {} +for _,v in ipairs(SHOOTER_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 shooter:spawn_particles(pos, texture) + if SHOOTER_ENABLE_PARTICLE_FX == true then + if type(texture) ~= "string" then + texture = SHOOTER_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 shooter:play_node_sound(node, pos) + local item = minetest.registered_items[node.name] + if item then + if item.sounds then + local spec = item.sounds.dug + if spec then + spec.pos = pos + minetest.sound_play(spec.name, spec) + end + end + end +end + +function shooter: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 SHOOTER_ENABLE_PROTECTION then + if minetest.is_protected(pos, def.name) then + return + end + end + 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) + shooter:play_node_sound(node, pos) + if item.tiles then + if item.tiles[1] then + shooter:spawn_particles(pos, item.tiles[1]) + end + end + break + end + end + end +end + +function shooter:is_valid_object(object) + if object then + if object:is_player() == true then + return SHOOTER_ALLOW_PLAYERS + end + if SHOOTER_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 shooter: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 shooter:process_round(round) + local target = {object=nil, distance=10000} + local p1 = round.pos + local v1 = round.ray + for _,ref in ipairs(shooter.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 = shooter: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) + shooter:spawn_particles(target.pos, SHOOTER_EXPLOSION_TEXTURE) + end + return 1 + elseif pos and SHOOTER_ALLOW_NODES == true then + shooter:punch_node(pos, round.def) + return 1 + end + elseif SHOOTER_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 + shooter:punch_node(pos, round.def) + return 1 + end + end +end + +shooter.registered_weapons = shooter.registered_weapons or {} +function shooter:register_weapon(name, def) + shooter.registered_weapons[name] = def + local shots = def.shots or 1 + local wear = math.ceil(65534 / def.rounds) + local max_wear = (def.rounds - 1) * wear + minetest.register_tool(name, { + description = def.description, + inventory_image = def.inventory_image, + on_use = function(itemstack, user, pointed_thing) + if itemstack:get_wear() < max_wear then + def.spec.name = user:get_player_name() + if shots > 1 then + local step = def.spec.tool_caps.full_punch_interval + for i = 0, step * shots, step do + minetest.after(i, function() + shooter:fire_weapon(user, pointed_thing, def.spec) + end) + end + else + shooter:fire_weapon(user, pointed_thing, def.spec) + end + itemstack:add_wear(wear) + else + local inv = user:get_inventory() + if inv then + local stack = "shooter:ammo 1" + if inv:contains_item("main", stack) then + minetest.sound_play("shooter_reload", {object=user}) + inv:remove_item("main", stack) + itemstack:replace(name.." 1 1") + else + minetest.sound_play("shooter_click", {object=user}) + end + end + end + return itemstack + end, + }) +end + +function shooter:fire_weapon(user, pointed_thing, def) + if shooter.shots[def.name] then + if shooter.time < shooter.shots[def.name] then + return + end + end + shooter.shots[def.name] = shooter.time + def.tool_caps.full_punch_interval + minetest.sound_play(def.sound, {object=user}) + local v1 = user:get_look_dir() + local p1 = user:getpos() + if not v1 or not p1 then + return + end + minetest.add_particle({x=p1.x, y=p1.y + 1.6, z=p1.z}, + vector.multiply(v1, {x=30, y=30, z=30}), + {x=0, y=0, z=0}, 0.5, 0.25, + false, def.particle + ) + if pointed_thing.type == "node" and SHOOTER_ALLOW_NODES == true then + local pos = minetest.get_pointed_thing_position(pointed_thing, false) + shooter:punch_node(pos, def) + elseif pointed_thing.type == "object" then + local object = pointed_thing.ref + if shooter:is_valid_object(object) == true then + object:punch(user, nil, def.tool_caps, v1) + local p2 = object:getpos() + local pp = get_particle_pos(p1, v1, vector.distance(p1, p2)) + pp.y = pp.y + 1.75 + shooter:spawn_particles(pp, SHOOTER_EXPLOSION_TEXTURE) + end + else + shooter:update_objects() + table.insert(shooter.rounds, { + name = def.name, + pos = vector.add(p1, {x=0, y=1.75, z=0}), + ray = v1, + dist = 0, + def = def, + }) + end +end + +function shooter:load_objects() + local objects = {} + if SHOOTER_ALLOW_PLAYERS == true then + local players = minetest.get_connected_players() + for _,player in ipairs(players) do + local pos = player:getpos() + local name = player:get_player_name() + local hp = player:get_hp() or 0 + if pos and name and hp > 0 then + table.insert(objects, { + name = name, + object = player, + pos = pos, + collisionbox = {-0.25,-1.0,-0.25, 0.25,0.8,0.25}, + offset = SHOOTER_PLAYER_OFFSET, + }) + end + end + end + if SHOOTER_ALLOW_ENTITIES == true then + for _,ref in pairs(minetest.luaentities) do + if ref.object and ref.name then + if allowed_entities[ref.name] then + local pos = ref.object:getpos() + local hp = ref.object:get_hp() or 0 + if pos and hp > 0 then + local def = minetest.registered_entities[ref.name] + table.insert(objects, { + name = ref.name, + object = ref.object, + pos = pos, + collisionbox = def.collisionbox or {0,0,0, 0,0,0}, + offset = SHOOTER_ENTITY_OFFSET, + }) + end + end + end + end + end + shooter.reload_time = shooter.time + shooter.update_time = shooter.time + shooter.objects = {} + for _,v in ipairs(objects) do + table.insert(shooter.objects, v) + end +end + +function shooter:update_objects() + if shooter.time - shooter.reload_time > SHOOTER_OBJECT_RELOAD_TIME then + shooter:load_objects() + elseif shooter.time - shooter.update_time > SHOOTER_OBJECT_UPDATE_TIME then + for i, ref in ipairs(shooter.objects) do + if ref.object then + local pos = ref.object:getpos() + if pos then + shooter.objects[i].pos = pos + end + else + table.remove(shooter.objects, i) + end + end + shooter.update_time = shooter.time + end +end + +function shooter:blast(pos, radius, fleshy, distance, user) + if not user then + return + end + local name = user:get_player_name() + local pos = vector.round(pos) + local p1 = vector.subtract(pos, radius) + local p2 = vector.add(pos, radius) + minetest.sound_play("tnt_explode", {pos=pos, gain=1}) + if SHOOTER_ALLOW_NODES and SHOOTER_ENABLE_BLASTING then + if SHOOTER_ENABLE_PROTECTION then + if not minetest.is_protected(pos, name) then + minetest.set_node(pos, {name="tnt:boom"}) + end + else + minetest.set_node(pos, {name="tnt:boom"}) + end + end + if SHOOTER_ENABLE_PARTICLE_FX == true then + minetest.add_particlespawner(50, 0.1, + p1, p2, {x=-0, y=-0, z=-0}, {x=0, y=0, z=0}, + {x=-0.5, y=5, z=-0.5}, {x=0.5, y=5, z=0.5}, + 0.1, 1, 8, 15, false, "tnt_smoke.png" + ) + end + local objects = minetest.get_objects_inside_radius(pos, distance) + for _,obj in ipairs(objects) do + if (obj:is_player() and SHOOTER_ALLOW_PLAYERS == true) or + (obj:get_luaentity() and SHOOTER_ALLOW_ENTITIES == true and + obj:get_luaentity().name ~= "__builtin:item") then + local obj_pos = obj:getpos() + local dist = vector.distance(obj_pos, pos) + local damage = (fleshy * 0.5 ^ dist) * 2 + if dist ~= 0 then + obj_pos.y = obj_pos.y + 1.7 + blast_pos = {x=pos.x, y=pos.y + 4, z=pos.z} + if minetest.line_of_sight(obj_pos, blast_pos, 1) then + obj:punch(obj, 1.0, { + full_punch_interval = 1.0, + damage_groups = {fleshy=damage}, + }) + end + end + end + end + if SHOOTER_ALLOW_NODES and SHOOTER_ENABLE_BLASTING then + local pr = PseudoRandom(os.time()) + local vm = VoxelManip() + local min, max = vm:read_from_map(p1, p2) + local area = VoxelArea:new({MinEdge=min, MaxEdge=max}) + local data = vm:get_data() + local c_air = minetest.get_content_id("air") + for z = -radius, radius do + for y = -radius, radius do + local vp = {x=pos.x - radius, y=pos.y + y, z=pos.z + z} + local vi = area:index(vp.x, vp.y, vp.z) + for x = -radius, radius do + if (x * x) + (y * y) + (z * z) <= + (radius * radius) + pr:next(-radius, radius) then + if SHOOTER_ENABLE_PROTECTION then + if not minetest.is_protected(vp, name) then + data[vi] = c_air + end + else + data[vi] = c_air + end + end + vi = vi + 1 + end + end + end + vm:set_data(data) + vm:update_liquids() + vm:write_to_map() + vm:update_map() + end +end + +if not singleplayer and SHOOTER_ADMIN_WEAPONS then + local timer = 0 + local shooting = false + minetest.register_globalstep(function(dtime) + if not shooting then + timer = timer+dtime + if timer < 2 then + return + end + timer = 0 + end + shooting = false + for _,player in pairs(minetest.get_connected_players()) do + if player:get_player_control().LMB then + local name = player:get_player_name() + if minetest.check_player_privs(name, {server=true}) then + local spec = shooter.registered_weapons[player:get_wielded_item():get_name()] + if spec then + spec = spec.spec + shooter.shots[name] = false + spec.name = name + shooter:fire_weapon(player, {}, spec) + shooting = true + end + end + end + end + end) +end diff --git a/mods/shooter/sounds/shooter_click.ogg b/mods/shooter/sounds/shooter_click.ogg new file mode 100644 index 0000000..8e60db8 Binary files /dev/null and b/mods/shooter/sounds/shooter_click.ogg differ diff --git a/mods/shooter/sounds/shooter_flare_burn.ogg b/mods/shooter/sounds/shooter_flare_burn.ogg new file mode 100644 index 0000000..68d6e10 Binary files /dev/null and b/mods/shooter/sounds/shooter_flare_burn.ogg differ diff --git a/mods/shooter/sounds/shooter_flare_fire.ogg b/mods/shooter/sounds/shooter_flare_fire.ogg new file mode 100644 index 0000000..e0c625b Binary files /dev/null and b/mods/shooter/sounds/shooter_flare_fire.ogg differ diff --git a/mods/shooter/sounds/shooter_pistol.ogg b/mods/shooter/sounds/shooter_pistol.ogg new file mode 100644 index 0000000..0d3f464 Binary files /dev/null and b/mods/shooter/sounds/shooter_pistol.ogg differ diff --git a/mods/shooter/sounds/shooter_reload.ogg b/mods/shooter/sounds/shooter_reload.ogg new file mode 100644 index 0000000..47f7245 Binary files /dev/null and b/mods/shooter/sounds/shooter_reload.ogg differ diff --git a/mods/shooter/sounds/shooter_rifle.ogg b/mods/shooter/sounds/shooter_rifle.ogg new file mode 100644 index 0000000..1ea68bf Binary files /dev/null and b/mods/shooter/sounds/shooter_rifle.ogg differ diff --git a/mods/shooter/sounds/shooter_rocket_fire.ogg b/mods/shooter/sounds/shooter_rocket_fire.ogg new file mode 100644 index 0000000..57252f3 Binary files /dev/null and b/mods/shooter/sounds/shooter_rocket_fire.ogg differ diff --git a/mods/shooter/sounds/shooter_shotgun.ogg b/mods/shooter/sounds/shooter_shotgun.ogg new file mode 100644 index 0000000..22aa8d4 Binary files /dev/null and b/mods/shooter/sounds/shooter_shotgun.ogg differ diff --git a/mods/shooter/sounds/shooter_throw.ogg b/mods/shooter/sounds/shooter_throw.ogg new file mode 100644 index 0000000..9ce9176 Binary files /dev/null and b/mods/shooter/sounds/shooter_throw.ogg differ diff --git a/mods/shooter/textures/shooter_ammo.png b/mods/shooter/textures/shooter_ammo.png new file mode 100644 index 0000000..daa348e Binary files /dev/null and b/mods/shooter/textures/shooter_ammo.png differ diff --git a/mods/shooter/textures/shooter_arrow_inv.png b/mods/shooter/textures/shooter_arrow_inv.png new file mode 100644 index 0000000..65da440 Binary files /dev/null and b/mods/shooter/textures/shooter_arrow_inv.png differ diff --git a/mods/shooter/textures/shooter_arrow_uv.png b/mods/shooter/textures/shooter_arrow_uv.png new file mode 100644 index 0000000..0225e43 Binary files /dev/null and b/mods/shooter/textures/shooter_arrow_uv.png differ diff --git a/mods/shooter/textures/shooter_bullet.png b/mods/shooter/textures/shooter_bullet.png new file mode 100644 index 0000000..c8b8e30 Binary files /dev/null and b/mods/shooter/textures/shooter_bullet.png differ diff --git a/mods/shooter/textures/shooter_cap.png b/mods/shooter/textures/shooter_cap.png new file mode 100644 index 0000000..692c1d5 Binary files /dev/null and b/mods/shooter/textures/shooter_cap.png differ diff --git a/mods/shooter/textures/shooter_crossbow.png b/mods/shooter/textures/shooter_crossbow.png new file mode 100644 index 0000000..f832c26 Binary files /dev/null and b/mods/shooter/textures/shooter_crossbow.png differ diff --git a/mods/shooter/textures/shooter_crossbow_loaded.png b/mods/shooter/textures/shooter_crossbow_loaded.png new file mode 100644 index 0000000..77dc04a Binary files /dev/null and b/mods/shooter/textures/shooter_crossbow_loaded.png differ diff --git a/mods/shooter/textures/shooter_flare.png b/mods/shooter/textures/shooter_flare.png new file mode 100644 index 0000000..98fd967 Binary files /dev/null and b/mods/shooter/textures/shooter_flare.png differ diff --git a/mods/shooter/textures/shooter_flare_inv.png b/mods/shooter/textures/shooter_flare_inv.png new file mode 100644 index 0000000..532a7df Binary files /dev/null and b/mods/shooter/textures/shooter_flare_inv.png differ diff --git a/mods/shooter/textures/shooter_flare_light.png b/mods/shooter/textures/shooter_flare_light.png new file mode 100644 index 0000000..afed6ba Binary files /dev/null and b/mods/shooter/textures/shooter_flare_light.png differ diff --git a/mods/shooter/textures/shooter_flare_particle.png b/mods/shooter/textures/shooter_flare_particle.png new file mode 100644 index 0000000..8e38165 Binary files /dev/null and b/mods/shooter/textures/shooter_flare_particle.png differ diff --git a/mods/shooter/textures/shooter_flaregun.png b/mods/shooter/textures/shooter_flaregun.png new file mode 100644 index 0000000..e654ab3 Binary files /dev/null and b/mods/shooter/textures/shooter_flaregun.png differ diff --git a/mods/shooter/textures/shooter_grenade.png b/mods/shooter/textures/shooter_grenade.png new file mode 100644 index 0000000..fd13447 Binary files /dev/null and b/mods/shooter/textures/shooter_grenade.png differ diff --git a/mods/shooter/textures/shooter_hand_grenade.png b/mods/shooter/textures/shooter_hand_grenade.png new file mode 100644 index 0000000..f0ade73 Binary files /dev/null and b/mods/shooter/textures/shooter_hand_grenade.png differ diff --git a/mods/shooter/textures/shooter_hit.png b/mods/shooter/textures/shooter_hit.png new file mode 100644 index 0000000..46d95b2 Binary files /dev/null and b/mods/shooter/textures/shooter_hit.png differ diff --git a/mods/shooter/textures/shooter_hook.png b/mods/shooter/textures/shooter_hook.png new file mode 100644 index 0000000..15311e5 Binary files /dev/null and b/mods/shooter/textures/shooter_hook.png differ diff --git a/mods/shooter/textures/shooter_hook_gun.png b/mods/shooter/textures/shooter_hook_gun.png new file mode 100644 index 0000000..3687ac4 Binary files /dev/null and b/mods/shooter/textures/shooter_hook_gun.png differ diff --git a/mods/shooter/textures/shooter_hook_gun_loaded.png b/mods/shooter/textures/shooter_hook_gun_loaded.png new file mode 100644 index 0000000..28cdb3b Binary files /dev/null and b/mods/shooter/textures/shooter_hook_gun_loaded.png differ diff --git a/mods/shooter/textures/shooter_pistol.png b/mods/shooter/textures/shooter_pistol.png new file mode 100644 index 0000000..56c821f Binary files /dev/null and b/mods/shooter/textures/shooter_pistol.png differ diff --git a/mods/shooter/textures/shooter_rifle.png b/mods/shooter/textures/shooter_rifle.png new file mode 100644 index 0000000..451ed5f Binary files /dev/null and b/mods/shooter/textures/shooter_rifle.png differ diff --git a/mods/shooter/textures/shooter_rocket_gun.png b/mods/shooter/textures/shooter_rocket_gun.png new file mode 100644 index 0000000..84738d4 Binary files /dev/null and b/mods/shooter/textures/shooter_rocket_gun.png differ diff --git a/mods/shooter/textures/shooter_rocket_gun_loaded.png b/mods/shooter/textures/shooter_rocket_gun_loaded.png new file mode 100644 index 0000000..3b2dc4d Binary files /dev/null and b/mods/shooter/textures/shooter_rocket_gun_loaded.png differ diff --git a/mods/shooter/textures/shooter_rocket_inv.png b/mods/shooter/textures/shooter_rocket_inv.png new file mode 100644 index 0000000..a96fa46 Binary files /dev/null and b/mods/shooter/textures/shooter_rocket_inv.png differ diff --git a/mods/shooter/textures/shooter_shotgun.png b/mods/shooter/textures/shooter_shotgun.png new file mode 100644 index 0000000..903223b Binary files /dev/null and b/mods/shooter/textures/shooter_shotgun.png differ diff --git a/mods/shooter/textures/shooter_smgun.png b/mods/shooter/textures/shooter_smgun.png new file mode 100644 index 0000000..98886e9 Binary files /dev/null and b/mods/shooter/textures/shooter_smgun.png differ diff --git a/mods/shooter/textures/shooter_turret_base.png b/mods/shooter/textures/shooter_turret_base.png new file mode 100644 index 0000000..fd13447 Binary files /dev/null and b/mods/shooter/textures/shooter_turret_base.png differ diff --git a/mods/shooter/textures/shooter_turret_gun.png b/mods/shooter/textures/shooter_turret_gun.png new file mode 100644 index 0000000..97e3e9f Binary files /dev/null and b/mods/shooter/textures/shooter_turret_gun.png differ diff --git a/mods/shooter/textures/shooter_turret_uv.png b/mods/shooter/textures/shooter_turret_uv.png new file mode 100644 index 0000000..c6bcde2 Binary files /dev/null and b/mods/shooter/textures/shooter_turret_uv.png differ diff --git a/mods/shooter/turret.lua b/mods/shooter/turret.lua new file mode 100644 index 0000000..f56769d --- /dev/null +++ b/mods/shooter/turret.lua @@ -0,0 +1,276 @@ +local function get_turret_entity(pos) + local entity = nil + local objects = minetest.get_objects_inside_radius(pos, 1) + for _, obj in ipairs(objects) do + local ent = obj:get_luaentity() + if ent then + if ent.name == "shooter:turret_entity" then + -- Remove duplicates + if entity then + obj:remove() + else + entity = ent + end + end + end + end + return entity +end + +minetest.register_entity("shooter:tnt_entity", { + physical = false, + timer = 0, + visual = "cube", + visual_size = {x=1/4, y=1/4}, + textures = { + "tnt_top.png", + "tnt_bottom.png", + "tnt_side.png", + "tnt_side.png", + "tnt_side.png", + "tnt_side.png", + }, + player = nil, + collisionbox = {0,0,0, 0,0,0}, + on_activate = function(self, staticdata) + if staticdata == "expired" then + self.object:remove() + end + end, + on_step = function(self, dtime) + self.timer = self.timer + dtime + if self.timer > 0.2 then + local pos = self.object:getpos() + if minetest.get_node(pos).name ~= "air" then + self.object:remove() + shooter:blast(pos, 4, 80, 10, self.player) + end + self.timer = 0 + end + end, + get_staticdata = function(self) + return "expired" + end, +}) + +minetest.register_entity("shooter:turret_entity", { + physical = true, + visual = "mesh", + mesh = "shooter_turret.b3d", + visual_size = {x=1, y=1}, + collisionbox = {-0.3, 0.5,-0.3, 0.3,1,0.3}, + textures = { + "shooter_turret_uv.png", + }, + timer = 0, + player = nil, + pitch = 40, + yaw = 0, + firing = false, + on_activate = function(self, staticdata) + self.pos = self.object:getpos() + self.yaw = self.object:getyaw() + if minetest.get_node(self.pos).name ~= "shooter:turret" then + self.object:remove() + return + end + self.object:set_animation({x=self.pitch, y=self.pitch}, 0) + self.object:set_armor_groups({fleshy=0}) + -- Remove duplicates + get_turret_entity(self.pos) + end, + on_rightclick = function(self, clicker) + if self.player == nil then + clicker:set_attach(self.object, "", {x=0,y=5,z=-8}, {x=0,y=0,z=0}) + self.player = clicker + else + self.player:set_detach() + local yaw = self.yaw + math.pi / 2 + local dir = vector.normalize({ + x = math.cos(yaw), + y = 0, + z = math.sin(yaw), + }) + local pos = vector.subtract(self.player:getpos(), dir) + minetest.after(0.2, function(player) + player:setpos(pos) + end, self.player) + self.player = nil + end + end, + on_step = function(self, dtime) + self.timer = self.timer + dtime + if self.timer < 0.1 then + return + end + if self.player then + local pitch = self.pitch + local yaw = self.object:getyaw() + local ctrl = self.player:get_player_control() + local step = 2 + if ctrl then + if ctrl.sneak then + step = 1 + if ctrl.jump then + if self.firing == false then + self:fire() + self.firing = true + end + else + self.firing = false + end + end + if ctrl.down then + pitch = pitch + 1 * step + elseif ctrl.up then + pitch = pitch - 1 * step + end + if ctrl.left then + yaw = yaw + 0.025 * step + elseif ctrl.right then + yaw = yaw - 0.025 * step + end + if pitch < 0 then + pitch = 0 + elseif pitch > 90 then + pitch = 90 + end + if self.pitch ~= pitch then + self.object:set_animation({x=pitch, y=pitch}, 0) + self.pitch = pitch + end + if self.yaw ~= yaw then + self.object:setyaw(yaw) + self.yaw = yaw + end + end + end + self.timer = 0 + end, + fire = function(self) + local meta = minetest.get_meta(self.pos) + local inv = meta:get_inventory() + if not inv then + return + end + if not inv:contains_item("main", "tnt:tnt") then + minetest.sound_play("shooter_click", {object=self.object}) + return + end + minetest.sound_play("shooter_shotgun", {object=self.object}) + if not minetest.setting_getbool("creative_mode") then + inv:remove_item("main", "tnt:tnt") + end + local pitch = math.rad(self.pitch - 40) + local len = math.cos(pitch) + local dir = vector.normalize({ + x = len * math.sin(-self.yaw), + y = math.sin(pitch), + z = len * math.cos(self.yaw), + }) + local pos = {x=self.pos.x, y=self.pos.y + 0.87, z=self.pos.z} + pos = vector.add(pos, {x=dir.x * 1.5, y=dir.y * 1.5, z=dir.z * 1.5}) + local obj = minetest.add_entity(pos, "shooter:tnt_entity") + if obj then + local ent = obj:get_luaentity() + if ent then + minetest.sound_play("shooter_rocket_fire", {object=obj}) + ent.player = self.player + obj:setyaw(self.yaw) + obj:setvelocity({x=dir.x * 20, y=dir.y * 20, z=dir.z * 20}) + obj:setacceleration({x=dir.x * -3, y=-10, z=dir.z * -3}) + end + end + if SHOOTER_ENABLE_PARTICLE_FX == true then + minetest.add_particlespawner(10, 0.1, + {x=pos.x - 1, y=pos.y - 1, z=pos.z - 1}, + {x=pos.x + 1, y=pos.y + 1, z=pos.z + 1}, + {x=0, y=0, z=0}, {x=0, y=0, z=0}, + {x=-0.5, y=-0.5, z=-0.5}, {x=0.5, y=0.5, z=0.5}, + 0.1, 1, 8, 15, false, "tnt_smoke.png" + ) + end + end +}) + +minetest.register_node("shooter:turret", { + description = "Turret Gun", + tiles = {"shooter_turret_base.png"}, + inventory_image = "shooter_turret_gun.png", + wield_image = "shooter_turret_gun.png", + drawtype = "nodebox", + paramtype = "light", + paramtype2 = "facedir", + groups = {snappy=2,choppy=2,oddly_breakable_by_hand=3}, + node_box = { + type = "fixed", + fixed = { + {-1/8, 1/8, -1/8, 1/8, 1/2, 1/8}, + {-5/16, 0, -5/16, 5/16, 1/8, 5/16}, + {-3/8, -1/2, -3/8, -1/4, 0, -1/4}, + {1/4, -1/2, 1/4, 3/8, 0, 3/8}, + {1/4, -1/2, -3/8, 3/8, 0, -1/4}, + {-3/8, -1/2, 1/4, -1/4, 0, 3/8}, + }, + }, + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("formspec", "size[8,9]".. + "list[current_name;main;2,0;4,4;]".. + "list[current_player;main;0,5;8,4;]" + ) + meta:set_string("infotext", "Turret Gun") + local inv = meta:get_inventory() + inv:set_size("main", 16) + end, + after_place_node = function(pos, placer) + local node = minetest.get_node({x=pos.x, y=pos.y + 1, z=pos.z}) + if node.name == "air" then + if not get_turret_entity(pos) then + minetest.add_entity(pos, "shooter:turret_entity") + end + end + end, + can_dig = function(pos, player) + local meta = minetest.get_meta(pos); + local inv = meta:get_inventory() + return inv:is_empty("main") + end, + after_destruct = function(pos, oldnode) + local ent = get_turret_entity(pos) + if ent then + ent.object:remove() + end + end, + mesecons = { + effector = { + action_on = function(pos, node) + local ent = get_turret_entity(pos) + if ent then + if ent.firing == false then + ent:fire() + ent.firing = true + end + end + end, + action_off = function(pos, node) + local ent = get_turret_entity(pos) + if ent then + ent.firing = false + end + end, + }, + }, +}) + +if SHOOTER_ENABLE_CRAFTING == true then + minetest.register_craft({ + output = "shooter:turret", + recipe = { + {"default:bronze_ingot", "default:bronze_ingot", "default:steel_ingot"}, + {"", "default:bronze_ingot", "default:steel_ingot"}, + {"", "default:diamond", ""}, + }, + }) +end + diff --git a/mods/throwing/README.txt b/mods/throwing/README.txt new file mode 100644 index 0000000..4d3cf61 --- /dev/null +++ b/mods/throwing/README.txt @@ -0,0 +1,112 @@ +=== THROWING ENHANCED for MINETEST === + +Inroduction: +This mod adds many bows and arrows to Minetest. +It began as a fork of PilzAdam's throwing mod with some enhancements from me. Enjoy! +Echoes91 + +How to install: +http://wiki.minetest.com/wiki/Installing_Mods + +How to use the mod: +Select a bow and place the arrows into the slot next to it; shoot with left mouse click. +Every shoot will take 1 arrow from your inventory and wears out the bow. +Select a spear and attack with left mouse click; it will be used as a melee weapon if pointing any target, otherwise it will be thrown. + +License: +This mod was originally published by Jeija and reworked by PilzAdam +Sourcecode: LGPLv2.1 (see http://www.gnu.org/licenses/lgpl-2.1.html) +Grahpics & sounds: CC-BY 3.0 (see http://creativecommons.org/licenses/by/3.0/legalcode) + + +Changelog: + +Update 1.4.1: +- Fixed spears not retaining wear +- Improved textures +- Torch arrows have light trail + +Update 1.4: +- Added spears, capable of melee and ranged attacks +- Improved arrows textures + +Update 1.3: +- Added automated arbalest, the ultimate weapon +- New arbalest texture coherent with steel color + +Update 1.2: +- Added arbalest +- Defaults initialized + +Update 1.1: +- Added crossbow +- Code shrink +- Offensive arrows go through flora's and farming's +- Small fixes + +Update 1.0: +- Definitive reload, unload and shot system based on tool metadata, new global functions, no more "throw" privilege +- New textures for loaded bows +- Fireworks arrows to celebrate! + +Update 1.0rc2: +- Fixed "compare nil with number" due to self.break not being retained +- Filled conf.example's list +- Added Royal bow + +Update 1.0rc1: +- Added longbow and removed golden bow, definitive bow set for stable release. Feature freeze +- Fixed torch arrow recipe, thanks to Excalibur Zero +- Removed config.lua, configuration now goes int throwing.config, see example + +Update 0.9.8: +- New damage calculation for offensive arrows based on arrow's speed and material. Beware that dependency is more than linear for both, so that differences between arrows will be enhanced if they're shot at higher speed. +- Fixed bug that blocked ability to shot after shooting with empty arrow stack. +- Removed annoying debug symbols + +Update 0.9.7: +- Added visual feedback for reload +- Fixed reload for players who die while reloading and for multiplayer +- Changed license for the code to LGPLv2 + +Update 0.9.6: +- Any bow and arrow is now deactivable under config.lua, which won't be overwritten +- Changed license for media to CC-BY + +Update 0.9.5: +- Added shell arrows +- Revised sounds and some textures +- General balancing of bow's statistics + +Update 0.9.4: +- New bow texture +- Made recipes coherent + +Update 0.9.3: +- Added symmetric recipes, fixed golden bow recipe +- Adjusted few parameters + +Update 0.9.2: +- Added a chance to break for many arrows, so they don't last forever and outclass any other tool +- Build and torch arrows won't build on fluids and torches any more, build arrows won't place torches +- TNT arrow digs instead if removing blocks, eventual indestructible nodes are safe +- Added golden bow with possible new bow style +- Changed the (bit OP) composite bow resistance and new recipe +- New teleport arrow recipe, cheaper but single use + +Update 0.9.1: +- Good improvement for torch arrows, now they always attach and are often turned to the right direction +- Git repository will make things nicer + +Update 0.9: +- Now bows have reload times! They depend on weight and quality, anyway no more machine-gun-style shell swarms +- Fixed build arrow behavior, now it placed and consumes the node from the slot [b]right next to the arrow[/b] or drops the item beside it if not a node; no more disappearing nor 'CONTENT_IGNORE' errors +- Code cleanup and rationalization + +Update 0.8.1: +- Fixed wrong texture reference which made some arrows get a bad color during flight. +- Now bows have different stiffness besides wear resistances, which means that they shot arrows at different initial speed and learning to hit the target will become even harder. + Get rid of the old .env: API + Added new bows and new offensive, utility and harmful arrows (these are just my categories, they're not present into the code at all). + Removed stone bow, at least as long as somebody discovers an elastic rock ;) + Non-exploding arrows won't disappear any more after hitting target. \ No newline at end of file diff --git a/mods/throwing/build_arrow.lua b/mods/throwing/build_arrow.lua new file mode 100644 index 0000000..d0cb031 --- /dev/null +++ b/mods/throwing/build_arrow.lua @@ -0,0 +1,107 @@ +minetest.register_craftitem("throwing:arrow_build", { + description = "Build Arrow", + inventory_image = "throwing_arrow_build.png", +}) + +minetest.register_node("throwing:arrow_build_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 = {"throwing_arrow_build.png", "throwing_arrow_build.png", "throwing_arrow_build_back.png", "throwing_arrow_build_front.png", "throwing_arrow_build_2.png", "throwing_arrow_build.png"}, + groups = {not_in_creative_inventory=1}, +}) + +local THROWING_ARROW_ENTITY={ + physical = false, + timer=0, + visual = "wielditem", + visual_size = {x=0.1, y=0.1}, + textures = {"throwing:arrow_build_box"}, + lastpos={}, + collisionbox = {0,0,0,0,0,0}, + node = "", +} + +THROWING_ARROW_ENTITY.on_step = function(self, dtime) + self.timer=self.timer+dtime + local pos = self.object:getpos() + local node = minetest.get_node(pos) + if not self.inventory or not self.stack then + self.object:remove() + end + + if self.timer>0.2 then + local objs = minetest.get_objects_inside_radius({x=pos.x,y=pos.y,z=pos.z}, 1) + for k, obj in pairs(objs) do + if obj:get_luaentity() ~= nil then + if obj:get_luaentity().name ~= "throwing:arrow_build_entity" and obj:get_luaentity().name ~= "__builtin:item" then + self.object:remove() + if self.inventory and self.stack and not minetest.setting_getbool("creative_mode") then + self.inventory:remove_item("main", {name=self.stack:get_name()}) + end + if self.stack then + minetest.add_item(self.lastpos, {name=self.stack:get_name()}) + end + local toughness = 0.95 + if math.random() < toughness then + minetest.add_item(self.lastpos, 'throwing:arrow_build') + else + minetest.add_item(self.lastpos, 'default:stick') + end + end + end + end + end + + if self.lastpos.x~=nil then + if node.name ~= "air" then + self.object:remove() + if self.inventory and self.stack and not minetest.setting_getbool("creative_mode") then + self.inventory:remove_item("main", {name=self.stack:get_name()}) + end + if self.stack then + if not string.find(node.name, "water") and not string.find(node.name, "lava") and not string.find(node.name, "torch") and self.stack:get_definition().type == "node" and self.stack:get_name() ~= "default:torch" then + minetest.place_node(self.lastpos, {name=self.stack:get_name()}) + else + minetest.add_item(self.lastpos, {name=self.stack:get_name()}) + end + end + minetest.add_item(self.lastpos, 'default:shovel_steel') + end + end + self.lastpos={x=pos.x, y=pos.y, z=pos.z} +end + +minetest.register_entity("throwing:arrow_build_entity", THROWING_ARROW_ENTITY) + +minetest.register_craft({ + output = 'throwing:arrow_build', + recipe = { + {'default:stick', 'default:stick', 'default:shovel_steel'}, + } +}) + +minetest.register_craft({ + output = 'throwing:arrow_build', + recipe = { + {'default:shovel_steel', 'default:stick', 'default:stick'}, + } +}) diff --git a/mods/throwing/defaults.lua b/mods/throwing/defaults.lua new file mode 100644 index 0000000..ec803ed --- /dev/null +++ b/mods/throwing/defaults.lua @@ -0,0 +1,24 @@ +DISABLE_WOODEN_BOW = false +DISABLE_LONGBOW = false +DISABLE_COMPOSITE_BOW = false +DISABLE_STEEL_BOW = false +DISABLE_ROYAL_BOW = false +DISABLE_CROSSBOW = false +DISABLE_ARBALEST = false +DISABLE_AUTOMATED_ARBALEST = false + +DISABLE_TELEPORT_ARROW = true +DISABLE_DIG_ARROW = true +DISABLE_BUILD_ARROW = true +DISABLE_TNT_ARROW = true +DISABLE_FIRE_ARROW = true +DISABLE_TORCH_ARROW = false +DISABLE_SHELL_ARROW = false + +DISABLE_FIREWORKS_BLUE_ARROW = false +DISABLE_FIREWORKS_RED_ARROW = false + +DISABLE_STONE_ARROW = false +DISABLE_STEEL_ARROW = false +DISABLE_DIAMOND_ARROW = false +DISABLE_OBSIDIAN_ARROW = false diff --git a/mods/throwing/depends.txt b/mods/throwing/depends.txt new file mode 100644 index 0000000..9f0fa34 --- /dev/null +++ b/mods/throwing/depends.txt @@ -0,0 +1,6 @@ +default +farming? +dye? +bucket? +fire? +tnt? diff --git a/mods/throwing/description.txt b/mods/throwing/description.txt new file mode 100644 index 0000000..ee25fb4 --- /dev/null +++ b/mods/throwing/description.txt @@ -0,0 +1 @@ +This mod adds many bows and arrows to Minetest. diff --git a/mods/throwing/dig_arrow.lua b/mods/throwing/dig_arrow.lua new file mode 100644 index 0000000..4e7cf9b --- /dev/null +++ b/mods/throwing/dig_arrow.lua @@ -0,0 +1,100 @@ +minetest.register_craftitem("throwing:arrow_dig", { + description = "Dig Arrow", + inventory_image = "throwing_arrow_dig.png", +}) + +minetest.register_node("throwing:arrow_dig_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 = {"throwing_arrow_dig.png", "throwing_arrow_dig.png", "throwing_arrow_dig_back.png", "throwing_arrow_dig_front.png", "throwing_arrow_dig_2.png", "throwing_arrow_dig.png"}, + groups = {not_in_creative_inventory=1}, +}) + +local THROWING_ARROW_ENTITY={ + physical = false, + timer=0, + visual = "wielditem", + visual_size = {x=0.1, y=0.1}, + textures = {"throwing:arrow_dig_box"}, + lastpos={}, + collisionbox = {0,0,0,0,0,0}, +} + +THROWING_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}, 1) + for k, obj in pairs(objs) do + if obj:get_luaentity() ~= nil then + if obj:get_luaentity().name ~= "throwing:arrow_dig_entity" and obj:get_luaentity().name ~= "__builtin:item" then + local damage = 1.5 + obj:punch(self.object, 1.0, { + full_punch_interval=1.0, + damage_groups={fleshy=damage}, + }, nil) + self.object:remove() + local toughness = 0.9 + if math.random() < toughness then + minetest.add_item(self.lastpos, 'throwing:arrow_dig') + else + minetest.add_item(self.lastpos, 'default:stick') + end + end + end + end + end + + if self.lastpos.x~=nil then + if node.name ~= "air" then + self.object:remove() + if node.diggable ~= false then + minetest.dig_node(pos) + end + local toughness = 0.65 + if math.random() < toughness then + minetest.add_item(self.lastpos, 'default:pick_steel') + else + minetest.add_item(self.lastpos, 'default:stick') + end + end + end + self.lastpos={x=pos.x, y=pos.y, z=pos.z} +end + +minetest.register_entity("throwing:arrow_dig_entity", THROWING_ARROW_ENTITY) + +minetest.register_craft({ + output = 'throwing:arrow_dig', + recipe = { + {'default:stick', 'default:stick', 'default:pick_steel'}, + } +}) + +minetest.register_craft({ + output = 'throwing:arrow_dig', + recipe = { + {'default:pick_steel', 'default:stick', 'default:stick'}, + } +}) diff --git a/mods/throwing/fire_arrow.lua b/mods/throwing/fire_arrow.lua new file mode 100644 index 0000000..9457a87 --- /dev/null +++ b/mods/throwing/fire_arrow.lua @@ -0,0 +1,125 @@ +minetest.register_craftitem("throwing:arrow_fire", { + description = "Fire Arrow", + inventory_image = "throwing_arrow_fire.png", +}) + +minetest.register_node("throwing:arrow_fire_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 = {"throwing_arrow_fire.png", "throwing_arrow_fire.png", "throwing_arrow_fire_back.png", "throwing_arrow_fire_front.png", "throwing_arrow_fire_2.png", "throwing_arrow_fire.png"}, + groups = {not_in_creative_inventory=1}, +}) + +local THROWING_ARROW_ENTITY={ + physical = false, + timer=0, + visual = "wielditem", + visual_size = {x=0.1, y=0.1}, + textures = {"throwing:arrow_fire_box"}, + lastpos={}, + collisionbox = {0,0,0,0,0,0}, +} + +THROWING_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 ~= "throwing:arrow_fire_entity" and obj:get_luaentity().name ~= "__builtin:item" then + local damage = 4 + obj:punch(self.object, 1.0, { + full_punch_interval=1.0, + damage_groups={fleshy=damage}, + }, nil) + self.object:remove() + minetest.add_item(self.lastpos, 'default:stick') + end + end + end + end + + if self.lastpos.x~=nil then + if node.name ~= "air" and node.name ~= "throwing:light" then + minetest.set_node(self.lastpos, {name="fire:basic_flame"}) + self.object:remove() + end + if math.floor(self.lastpos.x+0.5) ~= math.floor(pos.x+0.5) or math.floor(self.lastpos.y+0.5) ~= math.floor(pos.y+0.5) or math.floor(self.lastpos.z+0.5) ~= math.floor(pos.z+0.5) then + if minetest.get_node(self.lastpos).name == "throwing:light" then + minetest.remove_node(self.lastpos) + end + if minetest.get_node(pos).name == "air" then + minetest.set_node(pos, {name="throwing:light"}) + end + end + end + self.lastpos={x=pos.x, y=pos.y, z=pos.z} +end + +minetest.register_entity("throwing:arrow_fire_entity", THROWING_ARROW_ENTITY) + +minetest.register_node("throwing:light", { + drawtype = "airlike", + paramtype = "light", + sunlight_propagates = true, + tiles = {"throwing_empty.png"}, + light_source = LIGHT_MAX-4, + selection_box = { + type = "fixed", + fixed = { + {0,0,0,0,0,0} + } + }, + groups = {not_in_creative_inventory=1} +}) + +minetest.register_abm({ + nodenames = {"throwing:light"}, + interval = 10, + chance = 1, + action = function(pos, node) + minetest.remove_node(pos) + end +}) + +minetest.register_craft({ + output = 'throwing:arrow_fire 4', + recipe = { + {'default:stick', 'default:stick', 'bucket:bucket_lava'}, + }, + replacements = { + {"bucket:bucket_lava", "bucket:bucket_empty"} + } +}) + +minetest.register_craft({ + output = 'throwing:arrow_fire 4', + recipe = { + {'bucket:bucket_lava', 'default:stick', 'default:stick'}, + }, + replacements = { + {"bucket:bucket_lava", "bucket:bucket_empty"} + } +}) diff --git a/mods/throwing/fireworks_arrows.lua b/mods/throwing/fireworks_arrows.lua new file mode 100644 index 0000000..4dd38fe --- /dev/null +++ b/mods/throwing/fireworks_arrows.lua @@ -0,0 +1,195 @@ +local function throwing_register_fireworks(color, desc) + minetest.register_craftitem("throwing:arrow_fireworks_" .. color, { + description = desc .. "fireworks arrow", + inventory_image = "throwing_arrow_fireworks_" .. color .. ".png", + }) + + minetest.register_node("throwing:arrow_fireworks_" .. color .. "_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 = {"throwing_arrow_fireworks_" .. color .. ".png", "throwing_arrow_fireworks_" .. color .. ".png", "throwing_arrow_fireworks_" .. color .. "_back.png", "throwing_arrow_fireworks_" .. color .. "_front.png", "throwing_arrow_fireworks_" .. color .. "_2.png", "throwing_arrow_fireworks_" .. color .. ".png"}, + groups = {not_in_creative_inventory=1}, + }) + + local THROWING_ARROW_ENTITY={ + physical = false, + timer=0, + visual = "wielditem", + visual_size = {x=0.1, y=0.1}, + textures = {"throwing:arrow_fireworks_" .. color .. "_box"}, + lastpos={}, + collisionbox = {0,0,0,0,0,0}, + } + + local radius = 0.5 + + local function add_effects(pos, radius) + minetest.add_particlespawner({ + amount = 256, + time = 0.2, + minpos = vector.subtract(pos, radius / 2), + maxpos = vector.add(pos, radius / 2), + minvel = {x=-5, y=-5, z=-5}, + maxvel = {x=5, y=5, z=5}, + minacc = {x=0, y=-8, z=0}, + --~ maxacc = {x=-20, y=-50, z=-50}, + minexptime = 2.5, + maxexptime = 3, + minsize = 1, + maxsize = 2.5, + texture = "throwing_sparkle_" .. color .. ".png", + }) + end + + + local function boom(pos) + minetest.sound_play("throwing_firework_boom", {pos=pos, gain=1, max_hear_distance=2*64}) + if minetest.get_node(pos).name == 'air' or minetest.get_node(pos).name == 'throwing:firework_trail' then + minetest.add_node(pos, {name="throwing:firework_boom"}) + minetest.get_node_timer(pos):start(0.2) + end + add_effects(pos, radius) + end + + -- Back to the arrow + + THROWING_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.07 then + minetest.sound_play("throwing_firework_launch", {pos=pos, gain=0.8, max_hear_distance=2*64}) + end + minetest.add_particlespawner({ + amount = 16, + time = 0.1, + minpos = pos, + maxpos = pos, + minvel = {x=-5, y=-5, z=-5}, + maxvel = {x=5, y=5, z=5}, + minacc = vector.new(), + maxacc = vector.new(), + minexptime = 0.3, + maxexptime = 0.5, + minsize = 0.5, + maxsize = 1, + texture = "throwing_sparkle.png", + }) + 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 ~= "throwing:arrow_fireworks_" .. color .. "_entity" and obj:get_luaentity().name ~= "__builtin:item" then + local damage = 2 + obj:punch(self.object, 1.0, { + full_punch_interval=1.0, + damage_groups={fleshy=damage}, + }, nil) + self.object:remove() + boom(pos) + end + end + end + end + if self.timer > 2 then + self.object:remove() + boom(self.lastpos) + end + if self.lastpos.x~=nil then + if node.name ~= "air" and node.name ~= "throwing:firework_trail" then + self.object:remove() + boom(self.lastpos) + end + end + if node.name == 'air' then + minetest.add_node(pos, {name="throwing:firework_trail"}) + minetest.get_node_timer(pos):start(0.1) + end + self.lastpos={x=pos.x, y=pos.y, z=pos.z} + end + + minetest.register_entity("throwing:arrow_fireworks_" .. color .. "_entity", THROWING_ARROW_ENTITY) + + minetest.register_craft({ + output = 'throwing:arrow_fireworks_' .. color .. ' 8', + recipe = { + {'default:stick', 'tnt:gunpowder', 'dye:' .. color}, + } + }) + + minetest.register_craft({ + output = 'throwing:arrow_fireworks_' .. color .. ' 8', + recipe = { + {'dye:' .. color, 'tnt:gunpowder', 'default:stick'}, + } + }) +end + +--~ Arrows + +if not DISABLE_FIREWORKS_BLUE_ARROW then + throwing_register_fireworks('blue', 'Blue') +end + +if not DISABLE_FIREWORKS_RED_ARROW then + throwing_register_fireworks('red', 'Red') +end + +--~ Nodes + +minetest.register_node("throwing:firework_trail", { + drawtype = "airlike", + light_source = 9, + walkable = false, + drop = "", + groups = {dig_immediate=3}, + on_timer = function(pos, elapsed) + minetest.remove_node(pos) + end, +}) + +minetest.register_node("throwing:firework_boom", { + drawtype = "plantlike", + tiles = {"throwing_sparkle.png"}, + light_source = default.LIGHT_MAX, + walkable = false, + drop = "", + groups = {dig_immediate=3}, + on_timer = function(pos, elapsed) + minetest.remove_node(pos) + end, + after_destruct = function(pos, oldnode) + minetest.set_node(pos, {name="throwing:firework_light"}) + minetest.get_node_timer(pos):start(3) + end, +}) + +minetest.register_node("throwing:firework_light", { + drawtype = "airlike", + light_source = default.LIGHT_MAX, + walkable = false, + drop = "", + groups = {dig_immediate=3}, + on_timer = function(pos, elapsed) + minetest.remove_node(pos) + end, +}) diff --git a/mods/throwing/functions.lua b/mods/throwing/functions.lua new file mode 100644 index 0000000..fd72686 --- /dev/null +++ b/mods/throwing/functions.lua @@ -0,0 +1,145 @@ +--~ +--~ Shot and reload system +--~ + +local players = {} + +minetest.register_on_joinplayer(function(player) + local playerName = player:get_player_name() + players[playerName] = { + reloading=false, + } +end) + +minetest.register_on_leaveplayer(function(player) + local playerName = player:get_player_name() + players[playerName] = nil +end) + +function throwing_shoot_arrow (itemstack, player, stiffness, is_cross) + if not player then + return + end + local arrow = itemstack:get_metadata() + itemstack:set_metadata("") + player:set_wielded_item(itemstack) + local playerpos = player:getpos() + print(dump(minetest.luaentities)) + local obj = minetest.add_entity({x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, arrow) + if not obj then + minetest.chat_send_player(player:get_player_name(), "Error! Failed to create arrow.") + end + print(dump(minetest.luaentities)) + local dir = player:get_look_dir() + obj:setvelocity({x=dir.x*stiffness, y=dir.y*stiffness, z=dir.z*stiffness}) + obj:setacceleration({x=dir.x*-3, y=-8.5, z=dir.z*-3}) + obj:setyaw(player:get_look_yaw()+math.pi) + if is_cross then + minetest.sound_play("throwing_crossbow_sound", {pos=playerpos}) + else + minetest.sound_play("throwing_bow_sound", {pos=playerpos}) + end + local le = obj:get_luaentity() + if le then + le.player = player + le.inventory = player:get_inventory() + le.stack = player:get_inventory():get_stack("main", player:get_wield_index()-1) + print("le") + else + print("no le") + end + return true +end + +function throwing_unload (itemstack, player, unloaded, wear) + if itemstack:get_metadata() then + for _,arrow in ipairs(throwing_arrows) do + if itemstack:get_metadata() == arrow[2] then + if not minetest.setting_getbool("creative_mode") then + player:get_inventory():add_item("main", arrow[1]) + end + end + end + end + if wear >= 65535 then + player:set_wielded_item({}) + else + player:set_wielded_item({name=unloaded, wear=wear}) + end +end + +function throwing_reload (itemstack, player, pos, is_cross, loaded) + local playerName = player:get_player_name() + players[playerName]['reloading'] = false + if itemstack:get_name() == player:get_wielded_item():get_name() then + if (pos.x == player:getpos().x and pos.y == player:getpos().y and pos.z == player:getpos().z) or not is_cross then + local wear = itemstack:get_wear() + for _,arrow in ipairs(throwing_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 meta = arrow[2] + player:set_wielded_item({name=loaded, wear=wear, metadata=meta}) + end + end + end + end +end + +-- Bows and crossbows + +function throwing_register_bow (name, desc, scale, stiffness, reload_time, toughness, is_cross, craft) + minetest.register_tool("throwing:" .. name, { + description = desc, + inventory_image = "throwing_" .. name .. ".png", + wield_scale = scale, + stack_max = 1, + on_use = function(itemstack, user, pointed_thing) + local pos = user:getpos() + local playerName = user:get_player_name() + if not players[playerName]['reloading'] then + players[playerName]['reloading'] = true + minetest.after(reload_time, throwing_reload, itemstack, user, pos, is_cross, "throwing:" .. name .. "_loaded") + end + return itemstack + end, + }) + + minetest.register_tool("throwing:" .. name .. "_loaded", { + description = desc, + inventory_image = "throwing_" .. name .. "_loaded.png", + wield_scale = scale, + stack_max = 1, + on_use = function(itemstack, user, pointed_thing) + local wear = itemstack:get_wear() + if not minetest.setting_getbool("creative_mode") then + wear = wear + (65535/toughness) + end + local unloaded = "throwing:" .. name + throwing_shoot_arrow(itemstack, user, stiffness, is_cross) + minetest.after(0, throwing_unload, itemstack, user, unloaded, wear) + return itemstack + end, + on_drop = function(itemstack, dropper, pointed_thing) + local wear = itemstack:get_wear() + local unloaded = "throwing:" .. name + minetest.after(0, throwing_unload, itemstack, dropper, unloaded, wear) + end, + groups = {not_in_creative_inventory=1}, + }) + + minetest.register_craft({ + output = 'throwing:' .. name, + recipe = craft + }) + + minetest.register_craft({ + output = 'throwing:' .. name, + recipe = { + {craft[1][3], craft[1][2], craft[1][1]}, + {craft[2][3], craft[2][2], craft[2][1]}, + {craft[3][3], craft[3][2], craft[3][1]}, + } + }) +end diff --git a/mods/throwing/init.lua b/mods/throwing/init.lua new file mode 100644 index 0000000..be1d42a --- /dev/null +++ b/mods/throwing/init.lua @@ -0,0 +1,66 @@ +throwing_arrows = { + {"throwing:arrow_steel", "throwing:arrow_steel_entity"}, + {"throwing:arrow_stone", "throwing:arrow_stone_entity"}, + {"throwing:arrow_obsidian", "throwing:arrow_obsidian_entity"}, + {"throwing:arrow_fire", "throwing:arrow_fire_entity"}, + {"throwing:arrow_teleport", "throwing:arrow_teleport_entity"}, + {"throwing:arrow_dig", "throwing:arrow_dig_entity"}, + {"throwing:arrow_build", "throwing:arrow_build_entity"}, + {"throwing:arrow_tnt", "throwing:arrow_tnt_entity"}, + {"throwing:arrow_torch", "throwing:arrow_torch_entity"}, + {"throwing:arrow_diamond", "throwing:arrow_diamond_entity"}, + {"throwing:arrow_shell", "throwing:arrow_shell_entity"}, + {"throwing:arrow_fireworks_blue", "throwing:arrow_fireworks_blue_entity"}, + {"throwing:arrow_fireworks_red", "throwing:arrow_fireworks_red_entity"}, +} + +dofile(minetest.get_modpath("throwing").."/defaults.lua") + +local input = io.open(minetest.get_modpath("throwing").."/throwing.conf", "r") +if input then + dofile(minetest.get_modpath("throwing").."/throwing.conf") + input:close() + input = nil +end + +dofile(minetest.get_modpath("throwing").."/functions.lua") + +dofile(minetest.get_modpath("throwing").."/tools.lua") + +dofile(minetest.get_modpath("throwing").."/standard_arrows.lua") + +if minetest.get_modpath('fire') and minetest.get_modpath('bucket') and not DISABLE_FIRE_ARROW then + dofile(minetest.get_modpath("throwing").."/fire_arrow.lua") +end + +if not DISABLE_TELEPORT_ARROW then + dofile(minetest.get_modpath("throwing").."/teleport_arrow.lua") +end + +if not DISABLE_DIG_ARROW then + dofile(minetest.get_modpath("throwing").."/dig_arrow.lua") +end + +if not DISABLE_BUILD_ARROW then + dofile(minetest.get_modpath("throwing").."/build_arrow.lua") +end + +if minetest.get_modpath('fire') and minetest.get_modpath('tnt') and not DISABLE_TNT_ARROW then + dofile(minetest.get_modpath("throwing").."/tnt_arrow.lua") +end + +if not DISABLE_TORCH_ARROW then + dofile(minetest.get_modpath("throwing").."/torch_arrow.lua") +end + +if minetest.get_modpath('tnt') and not DISABLE_SHELL_ARROW then + dofile(minetest.get_modpath("throwing").."/shell_arrow.lua") +end + +if minetest.get_modpath('tnt') then + dofile(minetest.get_modpath("throwing").."/fireworks_arrows.lua") +end + +if minetest.setting_get("log_mods") then + minetest.log("action", "throwing loaded") +end diff --git a/mods/throwing/screenshot.png b/mods/throwing/screenshot.png new file mode 100644 index 0000000..32a89d1 Binary files /dev/null and b/mods/throwing/screenshot.png differ diff --git a/mods/throwing/shell_arrow.lua b/mods/throwing/shell_arrow.lua new file mode 100644 index 0000000..5267cf9 --- /dev/null +++ b/mods/throwing/shell_arrow.lua @@ -0,0 +1,118 @@ +minetest.register_craftitem("throwing:arrow_shell", { + description = "Shell arrow", + inventory_image = "throwing_arrow_shell.png", +}) + +minetest.register_node("throwing:arrow_shell_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 = {"throwing_arrow_shell.png", "throwing_arrow_shell.png", "throwing_arrow_shell_back.png", "throwing_arrow_shell_front.png", "throwing_arrow_shell_2.png", "throwing_arrow_shell.png"}, + groups = {not_in_creative_inventory=1}, +}) + +local THROWING_ARROW_ENTITY={ + physical = false, + timer=0, + visual = "wielditem", + visual_size = {x=0.1, y=0.1}, + textures = {"throwing:arrow_shell_box"}, + lastpos={}, + collisionbox = {0,0,0,0,0,0}, +} + +local radius = 1 + +local function add_effects(pos, radius) + minetest.add_particlespawner({ + amount = 8, + 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 = 0.5, + maxexptime = 1, + minsize = 0.5, + maxsize = 1, + texture = "tnt_smoke.png", + }) +end + + +local function boom(pos) + minetest.sound_play("shell_explode", {pos=pos, gain=1.5, max_hear_distance=2*64}) + minetest.set_node(pos, {name="tnt:boom"}) + minetest.get_node_timer(pos):start(0.1) + add_effects(pos, radius) +end + +-- Back to the arrow + +THROWING_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 ~= "throwing:arrow_shell_entity" and obj:get_luaentity().name ~= "__builtin:item" then + local speed = vector.length(self.object:getvelocity()) + local damage = ((speed + 5)^1.2)/10 + 12 + obj:punch(self.object, 1.0, { + full_punch_interval=1.0, + damage_groups={fleshy=damage}, + }, nil) + self.object:remove() + boom(pos) + end + end + end + end + + if self.lastpos.x~=nil then + if node.name ~= "air" and not (string.find(node.name, 'grass') and not string.find(node.name, 'dirt')) and not string.find(node.name, 'flowers:') and not string.find(node.name, 'farming:') then + self.object:remove() + boom(self.lastpos) + end + end + self.lastpos={x=pos.x, y=pos.y, z=pos.z} +end + +minetest.register_entity("throwing:arrow_shell_entity", THROWING_ARROW_ENTITY) + +minetest.register_craft({ + output = 'throwing:arrow_shell 8', + recipe = { + {'default:stick', 'tnt:gunpowder', 'default:bronze_ingot'}, + } +}) + +minetest.register_craft({ + output = 'throwing:arrow_shell 8', + recipe = { + {'default:bronze_ingot', 'tnt:gunpowder', 'default:stick'}, + } +}) diff --git a/mods/throwing/sounds/throwing_bow_sound.ogg b/mods/throwing/sounds/throwing_bow_sound.ogg new file mode 100644 index 0000000..411598c Binary files /dev/null and b/mods/throwing/sounds/throwing_bow_sound.ogg differ diff --git a/mods/throwing/sounds/throwing_crossbow_sound.ogg b/mods/throwing/sounds/throwing_crossbow_sound.ogg new file mode 100644 index 0000000..e30b1e0 Binary files /dev/null and b/mods/throwing/sounds/throwing_crossbow_sound.ogg differ diff --git a/mods/throwing/sounds/throwing_firework_boom.ogg b/mods/throwing/sounds/throwing_firework_boom.ogg new file mode 100644 index 0000000..c25b96a Binary files /dev/null and b/mods/throwing/sounds/throwing_firework_boom.ogg differ diff --git a/mods/throwing/sounds/throwing_firework_launch.ogg b/mods/throwing/sounds/throwing_firework_launch.ogg new file mode 100644 index 0000000..e6165a4 Binary files /dev/null and b/mods/throwing/sounds/throwing_firework_launch.ogg differ diff --git a/mods/throwing/sounds/throwing_shell_explode.ogg b/mods/throwing/sounds/throwing_shell_explode.ogg new file mode 100644 index 0000000..ec827f8 Binary files /dev/null and b/mods/throwing/sounds/throwing_shell_explode.ogg differ diff --git a/mods/throwing/standard_arrows.lua b/mods/throwing/standard_arrows.lua new file mode 100644 index 0000000..60674e0 --- /dev/null +++ b/mods/throwing/standard_arrows.lua @@ -0,0 +1,114 @@ +function throwing_register_arrow_standard (kind, desc, eq, toughness, craft) + minetest.register_craftitem("throwing:arrow_" .. kind, { + description = desc .. " arrow", + inventory_image = "throwing_arrow_" .. kind .. ".png", + }) + + minetest.register_node("throwing:arrow_" .. kind .. "_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 = {"throwing_arrow_" .. kind .. ".png", "throwing_arrow_" .. kind .. ".png", "throwing_arrow_" .. kind .. "_back.png", "throwing_arrow_" .. kind .. "_front.png", "throwing_arrow_" .. kind .. "_2.png", "throwing_arrow_" .. kind .. ".png"}, + groups = {not_in_creative_inventory=1}, + }) + + local THROWING_ARROW_ENTITY={ + physical = false, + timer=0, + visual = "wielditem", + visual_size = {x=0.1, y=0.1}, + textures = {"throwing:arrow_" .. kind .. "_box"}, + lastpos={}, + collisionbox = {0,0,0,0,0,0}, + } + + THROWING_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 ~= "throwing:arrow_" .. kind .. "_entity" and obj:get_luaentity().name ~= "__builtin:item" then + local speed = vector.length(self.object:getvelocity()) + local damage = ((speed + eq)^1.2)/10 + obj:punch(self.object, 1.0, { + full_punch_interval=1.0, + damage_groups={fleshy=damage}, + }, nil) + self.object:remove() + --if math.random() < toughness then + --minetest.add_item(self.lastpos, 'throwing:arrow_' .. kind) + --else + --minetest.add_item(self.lastpos, 'default:stick') + --end + end + end + end + end + + if self.lastpos.x~=nil then + if node.name ~= "air" and not (string.find(node.name, 'grass') and not string.find(node.name, 'dirt')) and not string.find(node.name, 'flowers:') and not string.find(node.name, 'farming:') then + self.object:remove() + if math.random() < toughness then + minetest.add_item(self.lastpos, 'throwing:arrow_' .. kind) + else + minetest.add_item(self.lastpos, 'default:stick') + end + end + end + self.lastpos={x=pos.x, y=pos.y, z=pos.z} + end + + minetest.register_entity("throwing:arrow_" .. kind .. "_entity", THROWING_ARROW_ENTITY) + + minetest.register_craft({ + output = 'throwing:arrow_' .. kind .. ' 16', + recipe = { + {'default:stick', 'default:stick', craft}, + } + }) + + minetest.register_craft({ + output = 'throwing:arrow_' .. kind .. ' 16', + recipe = { + {craft, 'default:stick', 'default:stick'}, + } + }) +end + +if not DISABLE_STONE_ARROW then + throwing_register_arrow_standard ('stone', 'Stone', 0, 0.88, 'group:stone') +end + +if not DISABLE_STEEL_ARROW then + throwing_register_arrow_standard ('steel', 'Steel', 5, 0.94, 'default:steel_ingot') +end + +if not DISABLE_DIAMOND_ARROW then + throwing_register_arrow_standard ('diamond', 'Diamond', 10, 0.97, 'default:diamond') +end + +if not DISABLE_OBSIDIAN_ARROW then + throwing_register_arrow_standard ('obsidian', 'Obsidian', 15, 0.88, 'default:obsidian') +end diff --git a/mods/throwing/teleport_arrow.lua b/mods/throwing/teleport_arrow.lua new file mode 100644 index 0000000..d495de3 --- /dev/null +++ b/mods/throwing/teleport_arrow.lua @@ -0,0 +1,92 @@ +minetest.register_craftitem("throwing:arrow_teleport", { + description = "Teleport Arrow", + inventory_image = "throwing_arrow_teleport.png", +}) + +minetest.register_node("throwing:arrow_teleport_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 = {"throwing_arrow_teleport.png", "throwing_arrow_teleport.png", "throwing_arrow_teleport_back.png", "throwing_arrow_teleport_front.png", "throwing_arrow_teleport_2.png", "throwing_arrow_teleport.png"}, + groups = {not_in_creative_inventory=1}, +}) + +local THROWING_ARROW_ENTITY={ + physical = false, + timer=0, + visual = "wielditem", + visual_size = {x=0.1, y=0.1}, + textures = {"throwing:arrow_teleport_box"}, + lastpos={}, + collisionbox = {0,0,0,0,0,0}, + player = "", +} + +THROWING_ARROW_ENTITY.on_step = function(self, dtime) + self.timer=self.timer+dtime + local pos = self.object:getpos() + local node = minetest.get_node(pos) + if not self.player then + self.object:remove() + end + + 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 ~= "throwing:arrow_teleport_entity" and obj:get_luaentity().name ~= "__builtin:item" then + self.object:remove() + if self.player ~= "" then + self.player:setpos(pos) + self.player:get_inventory():add_item("main", ItemStack("default:stick 2")) + end + end + end + end + end + + if self.lastpos.x~=nil then + if node.name ~= "air" then + self.object:remove() + if self.player ~= "" then + self.player:setpos(self.lastpos) + self.player:get_inventory():add_item("main", ItemStack("default:stick 2")) + end + end + end + self.lastpos={x=pos.x, y=pos.y, z=pos.z} +end + +minetest.register_entity("throwing:arrow_teleport_entity", THROWING_ARROW_ENTITY) + +minetest.register_craft({ + output = 'throwing:arrow_teleport', + recipe = { + {'default:stick', 'default:stick', 'default:mese_crystal_fragment'} + } +}) + +minetest.register_craft({ + output = 'throwing:arrow_teleport', + recipe = { + {'default:mese_crystal_fragment', 'default:stick', 'default:stick'} + } +}) diff --git a/mods/throwing/textures/throwing_arbalest.png b/mods/throwing/textures/throwing_arbalest.png new file mode 100644 index 0000000..f53e377 Binary files /dev/null and b/mods/throwing/textures/throwing_arbalest.png differ diff --git a/mods/throwing/textures/throwing_arbalest_auto.png b/mods/throwing/textures/throwing_arbalest_auto.png new file mode 100644 index 0000000..c40cd92 Binary files /dev/null and b/mods/throwing/textures/throwing_arbalest_auto.png differ diff --git a/mods/throwing/textures/throwing_arbalest_auto_loaded.png b/mods/throwing/textures/throwing_arbalest_auto_loaded.png new file mode 100644 index 0000000..f971b24 Binary files /dev/null and b/mods/throwing/textures/throwing_arbalest_auto_loaded.png differ diff --git a/mods/throwing/textures/throwing_arbalest_loaded.png b/mods/throwing/textures/throwing_arbalest_loaded.png new file mode 100644 index 0000000..09b2cc8 Binary files /dev/null and b/mods/throwing/textures/throwing_arbalest_loaded.png differ diff --git a/mods/throwing/textures/throwing_arrow_build.png b/mods/throwing/textures/throwing_arrow_build.png new file mode 100644 index 0000000..d9b4062 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_build.png differ diff --git a/mods/throwing/textures/throwing_arrow_build_2.png b/mods/throwing/textures/throwing_arrow_build_2.png new file mode 100644 index 0000000..1e178e4 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_build_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_build_back.png b/mods/throwing/textures/throwing_arrow_build_back.png new file mode 100644 index 0000000..621f258 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_build_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_build_front.png b/mods/throwing/textures/throwing_arrow_build_front.png new file mode 100644 index 0000000..25d47ce Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_build_front.png differ diff --git a/mods/throwing/textures/throwing_arrow_diamond.png b/mods/throwing/textures/throwing_arrow_diamond.png new file mode 100644 index 0000000..a70038a Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_diamond.png differ diff --git a/mods/throwing/textures/throwing_arrow_diamond_2.png b/mods/throwing/textures/throwing_arrow_diamond_2.png new file mode 100644 index 0000000..485b12f Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_diamond_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_diamond_back.png b/mods/throwing/textures/throwing_arrow_diamond_back.png new file mode 100644 index 0000000..0c92fba Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_diamond_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_diamond_front.png b/mods/throwing/textures/throwing_arrow_diamond_front.png new file mode 100644 index 0000000..6e545df Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_diamond_front.png differ diff --git a/mods/throwing/textures/throwing_arrow_dig.png b/mods/throwing/textures/throwing_arrow_dig.png new file mode 100644 index 0000000..6a1d443 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_dig.png differ diff --git a/mods/throwing/textures/throwing_arrow_dig_2.png b/mods/throwing/textures/throwing_arrow_dig_2.png new file mode 100644 index 0000000..b6d9d1b Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_dig_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_dig_back.png b/mods/throwing/textures/throwing_arrow_dig_back.png new file mode 100644 index 0000000..3bc4a60 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_dig_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_dig_front.png b/mods/throwing/textures/throwing_arrow_dig_front.png new file mode 100644 index 0000000..aa7ff2e Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_dig_front.png differ diff --git a/mods/throwing/textures/throwing_arrow_fire.png b/mods/throwing/textures/throwing_arrow_fire.png new file mode 100644 index 0000000..fd3a9d5 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_fire.png differ diff --git a/mods/throwing/textures/throwing_arrow_fire_2.png b/mods/throwing/textures/throwing_arrow_fire_2.png new file mode 100644 index 0000000..f86579a Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_fire_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_fire_back.png b/mods/throwing/textures/throwing_arrow_fire_back.png new file mode 100644 index 0000000..8a7d993 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_fire_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_fire_front.png b/mods/throwing/textures/throwing_arrow_fire_front.png new file mode 100644 index 0000000..3994257 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_fire_front.png differ diff --git a/mods/throwing/textures/throwing_arrow_fireworks_blue.png b/mods/throwing/textures/throwing_arrow_fireworks_blue.png new file mode 100644 index 0000000..71d4ec8 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_fireworks_blue.png differ diff --git a/mods/throwing/textures/throwing_arrow_fireworks_blue_2.png b/mods/throwing/textures/throwing_arrow_fireworks_blue_2.png new file mode 100644 index 0000000..51b055c Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_fireworks_blue_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_fireworks_blue_back.png b/mods/throwing/textures/throwing_arrow_fireworks_blue_back.png new file mode 100644 index 0000000..85956a6 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_fireworks_blue_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_fireworks_blue_front.png b/mods/throwing/textures/throwing_arrow_fireworks_blue_front.png new file mode 100644 index 0000000..8674cc8 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_fireworks_blue_front.png differ diff --git a/mods/throwing/textures/throwing_arrow_fireworks_red.png b/mods/throwing/textures/throwing_arrow_fireworks_red.png new file mode 100644 index 0000000..9b13fcd Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_fireworks_red.png differ diff --git a/mods/throwing/textures/throwing_arrow_fireworks_red_2.png b/mods/throwing/textures/throwing_arrow_fireworks_red_2.png new file mode 100644 index 0000000..edb0097 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_fireworks_red_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_fireworks_red_back.png b/mods/throwing/textures/throwing_arrow_fireworks_red_back.png new file mode 100644 index 0000000..8e90fc5 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_fireworks_red_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_fireworks_red_front.png b/mods/throwing/textures/throwing_arrow_fireworks_red_front.png new file mode 100644 index 0000000..8d6a629 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_fireworks_red_front.png differ diff --git a/mods/throwing/textures/throwing_arrow_obsidian.png b/mods/throwing/textures/throwing_arrow_obsidian.png new file mode 100644 index 0000000..2df7a08 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_obsidian.png differ diff --git a/mods/throwing/textures/throwing_arrow_obsidian_2.png b/mods/throwing/textures/throwing_arrow_obsidian_2.png new file mode 100644 index 0000000..abccbb3 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_obsidian_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_obsidian_back.png b/mods/throwing/textures/throwing_arrow_obsidian_back.png new file mode 100644 index 0000000..bd1232f Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_obsidian_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_obsidian_front.png b/mods/throwing/textures/throwing_arrow_obsidian_front.png new file mode 100644 index 0000000..2cbed4c Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_obsidian_front.png differ diff --git a/mods/throwing/textures/throwing_arrow_shell.png b/mods/throwing/textures/throwing_arrow_shell.png new file mode 100644 index 0000000..d100424 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_shell.png differ diff --git a/mods/throwing/textures/throwing_arrow_shell_2.png b/mods/throwing/textures/throwing_arrow_shell_2.png new file mode 100644 index 0000000..25f2b3e Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_shell_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_shell_back.png b/mods/throwing/textures/throwing_arrow_shell_back.png new file mode 100644 index 0000000..4b64eaf Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_shell_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_shell_front.png b/mods/throwing/textures/throwing_arrow_shell_front.png new file mode 100644 index 0000000..f6d51c4 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_shell_front.png differ diff --git a/mods/throwing/textures/throwing_arrow_steel.png b/mods/throwing/textures/throwing_arrow_steel.png new file mode 100644 index 0000000..9932590 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_steel.png differ diff --git a/mods/throwing/textures/throwing_arrow_steel_2.png b/mods/throwing/textures/throwing_arrow_steel_2.png new file mode 100644 index 0000000..c0401a6 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_steel_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_steel_back.png b/mods/throwing/textures/throwing_arrow_steel_back.png new file mode 100644 index 0000000..c7edf56 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_steel_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_steel_front.png b/mods/throwing/textures/throwing_arrow_steel_front.png new file mode 100644 index 0000000..ff22af0 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_steel_front.png differ diff --git a/mods/throwing/textures/throwing_arrow_stone.png b/mods/throwing/textures/throwing_arrow_stone.png new file mode 100644 index 0000000..b563af6 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_stone.png differ diff --git a/mods/throwing/textures/throwing_arrow_stone_2.png b/mods/throwing/textures/throwing_arrow_stone_2.png new file mode 100644 index 0000000..20182bc Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_stone_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_stone_back.png b/mods/throwing/textures/throwing_arrow_stone_back.png new file mode 100644 index 0000000..293334e Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_stone_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_stone_front.png b/mods/throwing/textures/throwing_arrow_stone_front.png new file mode 100644 index 0000000..8b35047 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_stone_front.png differ diff --git a/mods/throwing/textures/throwing_arrow_teleport.png b/mods/throwing/textures/throwing_arrow_teleport.png new file mode 100644 index 0000000..584735b Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_teleport.png differ diff --git a/mods/throwing/textures/throwing_arrow_teleport_2.png b/mods/throwing/textures/throwing_arrow_teleport_2.png new file mode 100644 index 0000000..56192c8 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_teleport_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_teleport_back.png b/mods/throwing/textures/throwing_arrow_teleport_back.png new file mode 100644 index 0000000..325c203 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_teleport_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_teleport_front.png b/mods/throwing/textures/throwing_arrow_teleport_front.png new file mode 100644 index 0000000..138f1cf Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_teleport_front.png differ diff --git a/mods/throwing/textures/throwing_arrow_tnt.png b/mods/throwing/textures/throwing_arrow_tnt.png new file mode 100644 index 0000000..c7ee8f1 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_tnt.png differ diff --git a/mods/throwing/textures/throwing_arrow_tnt_2.png b/mods/throwing/textures/throwing_arrow_tnt_2.png new file mode 100644 index 0000000..3b53ece Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_tnt_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_tnt_back.png b/mods/throwing/textures/throwing_arrow_tnt_back.png new file mode 100644 index 0000000..230bb18 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_tnt_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_tnt_front.png b/mods/throwing/textures/throwing_arrow_tnt_front.png new file mode 100644 index 0000000..b7252ac Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_tnt_front.png differ diff --git a/mods/throwing/textures/throwing_arrow_torch.png b/mods/throwing/textures/throwing_arrow_torch.png new file mode 100644 index 0000000..1c424e4 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_torch.png differ diff --git a/mods/throwing/textures/throwing_arrow_torch_2.png b/mods/throwing/textures/throwing_arrow_torch_2.png new file mode 100644 index 0000000..a5800ce Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_torch_2.png differ diff --git a/mods/throwing/textures/throwing_arrow_torch_back.png b/mods/throwing/textures/throwing_arrow_torch_back.png new file mode 100644 index 0000000..3b9fd11 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_torch_back.png differ diff --git a/mods/throwing/textures/throwing_arrow_torch_front.png b/mods/throwing/textures/throwing_arrow_torch_front.png new file mode 100644 index 0000000..764a263 Binary files /dev/null and b/mods/throwing/textures/throwing_arrow_torch_front.png differ diff --git a/mods/throwing/textures/throwing_bow_composite.png b/mods/throwing/textures/throwing_bow_composite.png new file mode 100644 index 0000000..630af5a Binary files /dev/null and b/mods/throwing/textures/throwing_bow_composite.png differ diff --git a/mods/throwing/textures/throwing_bow_composite_loaded.png b/mods/throwing/textures/throwing_bow_composite_loaded.png new file mode 100644 index 0000000..67bf8e5 Binary files /dev/null and b/mods/throwing/textures/throwing_bow_composite_loaded.png differ diff --git a/mods/throwing/textures/throwing_bow_royal.png b/mods/throwing/textures/throwing_bow_royal.png new file mode 100644 index 0000000..43c20f8 Binary files /dev/null and b/mods/throwing/textures/throwing_bow_royal.png differ diff --git a/mods/throwing/textures/throwing_bow_royal_loaded.png b/mods/throwing/textures/throwing_bow_royal_loaded.png new file mode 100644 index 0000000..3416f2e Binary files /dev/null and b/mods/throwing/textures/throwing_bow_royal_loaded.png differ diff --git a/mods/throwing/textures/throwing_bow_steel.png b/mods/throwing/textures/throwing_bow_steel.png new file mode 100644 index 0000000..0e1d3d1 Binary files /dev/null and b/mods/throwing/textures/throwing_bow_steel.png differ diff --git a/mods/throwing/textures/throwing_bow_steel_loaded.png b/mods/throwing/textures/throwing_bow_steel_loaded.png new file mode 100644 index 0000000..16150a6 Binary files /dev/null and b/mods/throwing/textures/throwing_bow_steel_loaded.png differ diff --git a/mods/throwing/textures/throwing_bow_wood.png b/mods/throwing/textures/throwing_bow_wood.png new file mode 100644 index 0000000..9a6c6da Binary files /dev/null and b/mods/throwing/textures/throwing_bow_wood.png differ diff --git a/mods/throwing/textures/throwing_bow_wood_loaded.png b/mods/throwing/textures/throwing_bow_wood_loaded.png new file mode 100644 index 0000000..251e643 Binary files /dev/null and b/mods/throwing/textures/throwing_bow_wood_loaded.png differ diff --git a/mods/throwing/textures/throwing_crossbow.png b/mods/throwing/textures/throwing_crossbow.png new file mode 100644 index 0000000..f8a400b Binary files /dev/null and b/mods/throwing/textures/throwing_crossbow.png differ diff --git a/mods/throwing/textures/throwing_crossbow_loaded.png b/mods/throwing/textures/throwing_crossbow_loaded.png new file mode 100644 index 0000000..5d2b0c7 Binary files /dev/null and b/mods/throwing/textures/throwing_crossbow_loaded.png differ diff --git a/mods/throwing/textures/throwing_empty.png b/mods/throwing/textures/throwing_empty.png new file mode 100644 index 0000000..6bbd554 Binary files /dev/null and b/mods/throwing/textures/throwing_empty.png differ diff --git a/mods/throwing/textures/throwing_longbow.png b/mods/throwing/textures/throwing_longbow.png new file mode 100644 index 0000000..7bdae2f Binary files /dev/null and b/mods/throwing/textures/throwing_longbow.png differ diff --git a/mods/throwing/textures/throwing_longbow_loaded.png b/mods/throwing/textures/throwing_longbow_loaded.png new file mode 100644 index 0000000..e03299a Binary files /dev/null and b/mods/throwing/textures/throwing_longbow_loaded.png differ diff --git a/mods/throwing/textures/throwing_sparkle.png b/mods/throwing/textures/throwing_sparkle.png new file mode 100644 index 0000000..432786e Binary files /dev/null and b/mods/throwing/textures/throwing_sparkle.png differ diff --git a/mods/throwing/textures/throwing_sparkle_blue.png b/mods/throwing/textures/throwing_sparkle_blue.png new file mode 100644 index 0000000..d4710a3 Binary files /dev/null and b/mods/throwing/textures/throwing_sparkle_blue.png differ diff --git a/mods/throwing/textures/throwing_sparkle_red.png b/mods/throwing/textures/throwing_sparkle_red.png new file mode 100644 index 0000000..59ec68c Binary files /dev/null and b/mods/throwing/textures/throwing_sparkle_red.png differ diff --git a/mods/throwing/throwing.conf.example b/mods/throwing/throwing.conf.example new file mode 100644 index 0000000..f0e1ba5 --- /dev/null +++ b/mods/throwing/throwing.conf.example @@ -0,0 +1,52 @@ +# You can disable or disable any bow and arrow by writing lines like these inside throwing.conf +# +# DISABLE_WOODEN_BOW = true +# +# +# DISABLE_LONGBOW = true +# +# +# DISABLE_COMPOSITE_BOW = true +# +# +# DISABLE_STEEL_BOW = true +# +# +# DISABLE_ROYAL_BOW = true +# +# +# DISABLE_CROSSBOW = true +# +# +# DISABLE_BUILD_ARROW = false +# +# +# DISABLE_DIG_ARROW = false +# +# +# DISABLE_DIAMOND_ARROW = true +# +# +# DISABLE_OBSIDIAN_ARROW = true +# +# +# DISABLE_SHELL_ARROW = true +# +# +# DISABLE_STEEL_ARROW = true +# +# +# DISABLE_STONE_ARROW = false +# +# +# DISABLE_TELEPORT_ARROW = false +# +# +# DISABLE_FIRE_ARROW = false +# +# +# DISABLE_TNT_ARROW = false +# +# +# DISABLE_TORCH_ARROW = true +# \ No newline at end of file diff --git a/mods/throwing/tnt_arrow.lua b/mods/throwing/tnt_arrow.lua new file mode 100644 index 0000000..d0a4f7d --- /dev/null +++ b/mods/throwing/tnt_arrow.lua @@ -0,0 +1,286 @@ +minetest.register_craftitem("throwing:arrow_tnt", { + description = "TNT arrow", + inventory_image = "throwing_arrow_tnt.png", +}) + +minetest.register_node("throwing:arrow_tnt_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 = {"throwing_arrow_tnt.png", "throwing_arrow_tnt.png", "throwing_arrow_tnt_back.png", "throwing_arrow_tnt_front.png", "throwing_arrow_tnt_2.png", "throwing_arrow_tnt.png"}, + groups = {not_in_creative_inventory=1}, +}) + +local THROWING_ARROW_ENTITY={ + physical = false, + timer=0, + visual = "wielditem", + visual_size = {x=0.1, y=0.1}, + textures = {"throwing:arrow_tnt_box"}, + lastpos={}, + collisionbox = {0,0,0,0,0,0}, +} + +-- TNT functions copied, would be nice to directly call them through an API... + +-- loss probabilities array (one in X will be lost) +local loss_prob = {} + +loss_prob["default:cobble"] = 3 +loss_prob["default:dirt"] = 4 + +local radius = tonumber(minetest.setting_get("tnt_radius") or 3) + +-- Fill a list with data for content IDs, after all nodes are registered +local cid_data = {} +minetest.after(0, function() + for name, def in pairs(minetest.registered_nodes) do + cid_data[minetest.get_content_id(name)] = { + name = name, + drops = def.drops, + flammable = def.groups.flammable, + } + end +end) + +local function rand_pos(center, pos, radius) + pos.x = center.x + math.random(-radius, radius) + pos.z = center.z + math.random(-radius, radius) +end + +local function eject_drops(drops, pos, radius) + local drop_pos = vector.new(pos) + for _, item in pairs(drops) do + local count = item:get_count() + local max = item:get_stack_max() + if count > max then + item:set_count(max) + end + while count > 0 do + if count < max then + item:set_count(count) + end + rand_pos(pos, drop_pos, radius) + local obj = minetest.add_item(drop_pos, item) + if obj then + obj:get_luaentity().collect = true + obj:setacceleration({x=0, y=-10, z=0}) + obj:setvelocity({x=math.random(-3, 3), y=10, + z=math.random(-3, 3)}) + end + count = count - max + end + end +end + +local function add_drop(drops, item) + item = ItemStack(item) + local name = item:get_name() + if loss_prob[name] ~= nil and math.random(1, loss_prob[name]) == 1 then + return + end + + local drop = drops[name] + if drop == nil then + drops[name] = item + else + drop:set_count(drop:get_count() + item:get_count()) + end +end + +local fire_node = {name="fire:basic_flame"} + +local function destroy(drops, pos, cid) + if minetest.is_protected(pos, "") then + return + end + local def = cid_data[cid] + if def and def.flammable then + minetest.set_node(pos, fire_node) + else + minetest.dig_node(pos) + if def then + local node_drops = minetest.get_node_drops(def.name, "") + for _, item in ipairs(node_drops) do + add_drop(drops, item) + end + end + end +end + + +local function calc_velocity(pos1, pos2, old_vel, power) + local vel = vector.direction(pos1, pos2) + vel = vector.normalize(vel) + vel = vector.multiply(vel, power) + + -- Divide by distance + 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 + +local function 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_pos = obj:getpos() + local obj_vel = obj:getvelocity() + local dist = math.max(1, vector.distance(pos, obj_pos)) + + if obj_vel ~= nil then + obj:setvelocity(calc_velocity(pos, obj_pos, + obj_vel, radius * 10)) + end + + local damage = (5 / dist) * radius + obj:set_hp(obj:get_hp() - damage) + end +end + +local function add_effects(pos, radius) + minetest.add_particlespawner({ + amount = 128, + 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, + texture = "tnt_smoke.png", + }) +end + + +local function explode(pos, radius) + 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 minp, maxp = vm:read_from_map(p1, p2) + local a = VoxelArea:new({MinEdge = minp, MaxEdge = maxp}) + local data = vm:get_data() + + local drops = {} + local p = {} + + local c_air = minetest.get_content_id("air") + local c_tnt = minetest.get_content_id("tnt:tnt") + local c_tnt_burning = minetest.get_content_id("tnt:tnt_burning") + local c_gunpowder = minetest.get_content_id("tnt:gunpowder") + local c_gunpowder_burning = minetest.get_content_id("tnt:gunpowder_burning") + local c_boom = minetest.get_content_id("tnt:boom") + local c_fire = minetest.get_content_id("fire:basic_flame") + + 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 cid = data[vi] + p.x = pos.x + x + p.y = pos.y + y + p.z = pos.z + z + if cid == c_tnt or cid == c_gunpowder then + burn(p) + elseif cid ~= c_tnt_burning and + cid ~= c_gunpowder_burning and + cid ~= c_air and + cid ~= c_fire and + cid ~= c_boom then + destroy(drops, p, cid) + end + end + vi = vi + 1 + end + end + end + + return drops +end + + +local function boom(pos) + minetest.sound_play("tnt_explode", {pos=pos, gain=1.5, max_hear_distance=2*64}) + minetest.set_node(pos, {name="tnt:boom"}) + minetest.get_node_timer(pos):start(0.5) + + local drops = explode(pos, radius) + entity_physics(pos, radius) + eject_drops(drops, pos, radius) + add_effects(pos, radius) +end + +-- Back to the arrow + +THROWING_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 ~= "throwing:arrow_tnt_entity" and obj:get_luaentity().name ~= "__builtin:item" then + self.object:remove() + boom(pos) + end + end + end + end + + if self.lastpos.x~=nil then + if node.name ~= "air" then + self.object:remove() + boom(self.lastpos) + end + end + self.lastpos={x=pos.x, y=pos.y, z=pos.z} +end + +minetest.register_entity("throwing:arrow_tnt_entity", THROWING_ARROW_ENTITY) + +minetest.register_craft({ + output = 'throwing:arrow_tnt', + recipe = { + {'default:stick', 'tnt:tnt', 'default:bronze_ingot'}, + } +}) + +minetest.register_craft({ + output = 'throwing:arrow_tnt', + recipe = { + {'default:bronze_ingot', 'tnt:tnt', 'default:stick'}, + } +}) diff --git a/mods/throwing/tools.lua b/mods/throwing/tools.lua new file mode 100644 index 0000000..57ef85c --- /dev/null +++ b/mods/throwing/tools.lua @@ -0,0 +1,63 @@ +if not DISABLE_WOODEN_BOW then + throwing_register_bow ('bow_wood', 'Wooden bow', {x=1, y=1, z=0.5}, 11, 0.8, 50, false, { + {'', 'default:stick', ''}, + {'farming:string', '', 'default:stick'}, + {'', 'default:stick', ''}, + }) +end + +if not DISABLE_LONGBOW then + throwing_register_bow ('longbow', 'Longbow', {x=1, y=2.5, z=0.5}, 17, 1.8, 100, false, { + {'farming:string', 'group:wood', ''}, + {'farming:string', '', 'group:wood'}, + {'farming:string', 'group:wood', ''}, + }) +end + +if not DISABLE_COMPOSITE_BOW then + throwing_register_bow ('bow_composite', 'Composite bow', {x=1, y=1.4, z=0.5}, 17, 1, 150, false, { + {'farming:string', 'group:wood', ''}, + {'farming:string', '', 'default:steel_ingot'}, + {'farming:string', 'group:wood', ''}, + }) +end + +if not DISABLE_STEEL_BOW then + throwing_register_bow ('bow_steel', 'Steel bow', {x=1, y=1.4, z=0.5}, 20, 1.3, 250, false, { + {'farming:string', 'default:steel_ingot', ''}, + {'farming:string', '', 'default:steel_ingot'}, + {'farming:string', 'default:steel_ingot', ''}, + }) +end + +if not DISABLE_ROYAL_BOW then + throwing_register_bow ('bow_royal', 'Royal bow', {x=1, y=1.5, z=0.5}, 18, 1.4, 750, false, { + {'farming:string', 'group:wood', 'default:diamond'}, + {'farming:string', '', 'default:gold_ingot'}, + {'farming:string', 'group:wood', 'default:diamond'}, + }) +end + +if not DISABLE_CROSSBOW then + throwing_register_bow ('crossbow', 'Crossbow', {x=1, y=1.3, z=0.5}, 28, 5, 80, true, { + {'group:wood', 'farming:string', ''}, + {'default:steel_ingot', 'farming:string', 'group:wood'}, + {'group:wood', 'farming:string', ''}, + }) +end + +if not DISABLE_ARBALEST then + throwing_register_bow ('arbalest', 'Arbalest', {x=1, y=1.3, z=0.5}, 35, 7.5, 120, true, { + {'default:steel_ingot', 'farming:string', 'default:stick'}, + {'default:steel_ingot', 'farming:string', 'default:steel_ingot'}, + {'default:steel_ingot', 'farming:string', 'default:stick'}, + }) +end + +if not DISABLE_AUTOMATED_ARBALEST then + throwing_register_bow ('arbalest_auto', 'Automated arbalest', {x=1, y=1.3, z=0.5}, 40, 3.5, 60, true, { + {'default:steel_ingot', 'farming:string', 'default:mese_crystal'}, + {'default:steel_ingot', 'farming:string', 'default:steel_ingot'}, + {'default:steel_ingot', 'farming:string', 'default:mese_crystal'}, + }) +end diff --git a/mods/throwing/torch_arrow.lua b/mods/throwing/torch_arrow.lua new file mode 100644 index 0000000..0de170a --- /dev/null +++ b/mods/throwing/torch_arrow.lua @@ -0,0 +1,118 @@ +minetest.register_craftitem("throwing:arrow_torch", { + description = "Torch Arrow", + inventory_image = "throwing_arrow_torch.png", +}) + +minetest.register_node("throwing:arrow_torch_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 = {"throwing_arrow_torch.png", "throwing_arrow_torch.png", "throwing_arrow_torch_back.png", "throwing_arrow_torch_front.png", "throwing_arrow_torch_2.png", "throwing_arrow_torch.png"}, + groups = {not_in_creative_inventory=1}, +}) + +local THROWING_ARROW_ENTITY={ + physical = false, + timer=0, + visual = "wielditem", + visual_size = {x=0.1, y=0.1}, + textures = {"throwing:arrow_torch_box"}, + lastpos={}, + collisionbox = {0,0,0,0,0,0}, + node = "", +} + +THROWING_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}, 0.5) + for k, obj in pairs(objs) do + if obj:get_luaentity() ~= nil then + if obj:get_luaentity().name ~= "throwing:arrow_torch_entity" and obj:get_luaentity().name ~= "__builtin:item" then + local damage = 0.5 + obj:punch(self.object, 1.0, { + full_punch_interval=1.0, + damage_groups={fleshy=damage}, + }, nil) + self.object:remove() + local toughness = 0.9 + if math.random() < toughness then + minetest.add_item(self.lastpos, 'throwing:arrow_torch') + else + minetest.add_item(self.lastpos, 'default:stick') + end + end + end + end + end + + if self.lastpos.x~=nil then + if node.name == 'air' then + minetest.add_node(pos, {name="throwing:torch_trail"}) + minetest.get_node_timer(pos):start(0.1) + elseif node.name ~= "air" and not string.find(node.name, "trail") then + self.object:remove() + if not string.find(node.name, "water") and not string.find(node.name, "lava") and not string.find(node.name, "torch") then + local dir=vector.direction(self.lastpos, pos) + local wall=minetest.dir_to_wallmounted(dir) + minetest.add_node(self.lastpos, {name="default:torch", param2 = wall}) + else + local toughness = 0.9 + if math.random() < toughness then + minetest.add_item(self.lastpos, 'throwing:arrow_torch') + else + minetest.add_item(self.lastpos, 'default:stick') + end + end + end + end + self.lastpos={x=pos.x, y=pos.y, z=pos.z} +end + +minetest.register_entity("throwing:arrow_torch_entity", THROWING_ARROW_ENTITY) + +minetest.register_craft({ + output = 'throwing:arrow_torch 4', + recipe = { + {'default:stick', 'default:stick', 'group:coal'}, + } +}) + +minetest.register_craft({ + output = 'throwing:arrow_torch 4', + recipe = { + {'group:coal', 'default:stick', 'default:stick'}, + } +}) + +minetest.register_node("throwing:torch_trail", { + drawtype = "airlike", + light_source = default.LIGHT_MAX-1, + walkable = false, + drop = "", + groups = {dig_immediate=3}, + on_timer = function(pos, elapsed) + minetest.remove_node(pos) + end, +}) diff --git a/mods/treasurer/GROUPS_AND_PRECIOUSNESS b/mods/treasurer/GROUPS_AND_PRECIOUSNESS new file mode 100644 index 0000000..126cfd5 --- /dev/null +++ b/mods/treasurer/GROUPS_AND_PRECIOUSNESS @@ -0,0 +1,83 @@ + +If you wish to register treasures to Treasurer, it is recommended to assign the treasurer to a Treasurer group. + +It is not possible to assign a treasure to multiple groups. If you think a treasure fits into two groups, create +two seperate treasure definitions instead. But try to keep it as an exception. + +Think of treasurer groups as categories for treasurers to put into. + +This file contains some guidelines for Treasurer groups and there are even guidelines for preciousness levels. +Keep in mind these are only guidelines. + +Treasurer suggests to use the following standard Treasurer groups: + +crafting_component: + Generic group for components in crafting recipes. If it is primarily a component for food, use raw_food instead. + Preciousness is based roughly on the preciousness of items it can create. + +fuel: + Fuel for furnaces. + Preciousness is based on burning time. + +food: + Can be eaten and restores health. + Preciousness should equal the number of hearts restored (restored HP divided by 2), but not higher than 7. + +raw_food: + Food which is not fully processed and is not (quite) ready to be eaten. + +melee_weapon: + Primarily used to damage close foes, i.e. a sword. + Preciousness is based on attack speed and damage. + +ranged_weapon: + Primarily used to damage far away foes, + Preciousness is based on attack speed, damage and range. + +tool: + A tool for other uses. + Preciousness is hard to determine; at least is should base on the number of uses. + + +minetool: + A tool to destroy blocks. Includes pickaxes, axes, shovels, … + Preciousness is based on power, number of uses and speed. + +deco: + Primarily just a decorational thing to place. + Preciousness is based on beauty, highly subjective. + +light: + Is a light source. + Preciousness is based on the brightness. For the maximum brightness (before sun brightness), preciousness should be 3. + +building_block: + A block for buildings. Includes stairs, slabs, fences and similar things. + Excludes all natural blocks. + Preciousness should be roughly based on the “cost” to craft the block. + +seed: + Seeds and saplings. + Preciousness is based on the percieved “usefulness” of what can grow from the seed. + +transport_vehicle + A vehicle to transport players and stuff, i.e. a cart or a boat. + Preciousness is hard to determine, maybe speed? + +transport_structure + A fixed structure which is neccessary for a transport vehicle to operate, i.e. rails. + Preciousness is hard to dertermine … + +ladder + A ladder. + +default: + This is the group your treasure get assigned to if you don’t specify a group. + + +Do you miss a group? Feel free to invent your own! + + +Note that Treasurer groups differ from the groups as defined by the Minetest API. These groups are handled differently. + + diff --git a/mods/treasurer/README.md b/mods/treasurer/README.md new file mode 100644 index 0000000..2ae0e77 --- /dev/null +++ b/mods/treasurer/README.md @@ -0,0 +1,246 @@ += Treasurer’s README file for Treasurer version 0.2.0 = +== Overview == +* Name: Treasurer +* Technical name: `treasurer` +* Purpose: To provide an interface for mods which want to spawn ItemStacks randomly and an interface for mods which create new items. +* Version: 0.2.0 +* Dependencies: none +* License: WTFPL + +== Introduction == +Problem: +There are a bunch of mods which have cool items but they won’t appear in the world by +themselves. +There are some mods which randomly distribute treasures into the world. Sadly, these only +distribute the items they know—which are just the items of the mod “default” most of the +time. The items of the other mods are completely missed. + +The reason for this is that the distributing mods can’t know what sort of items are available +unless they explicitly depend on the mods that defines these. Viewed the other way round, +the item-defining mods that also distribute these items into the world are limited in the +sense that they only know one means of distributing items. + +There is a gap between defining items and distributing them. Every time a mod does both, +flexibility is limited and expansion becomes difficult. + +To bridge this gap, Treasurer has been written. Treasurer makes it possible a) for mods to define +treasures without bothering _how_ these are distributed into the world and b) for mods to distribute +treasures around the world without knowledge about _what_ treasures exactly are distributed. + +== Technical side of Treasurer == +=== technical overview === +To get a working Treasurer architecture and actually get some treasures into the world, +you need: +* Treasurer +* at least one treasure registration mod +* at least one treasure spawning mod + +=== treasurer registration mod === +Firstly, there are the treasure registration mods (TRMs). The task of TRMs is to tell +Treasurer which items does it have to offer, which relative appearance probabilities these +treasures should have, how “precious” the treasure is considered (on a scale from 0 to 10) +, optionally how big the stack of items should be and optionally how much worn out it is. +TRMs must depend on at least two mods: On treasurer and on the mod or mods +where the items are defined. Technically, a TRM consists of nothing more than a long +list of “registration” function calls. While this seems trivial, the task of balancing +out probabilties and balancing out preciousness levels of treasures is not trivial +and it may take a long time to get right. + +It is strongly recommended that a TRM really does nothing +more than registering treasures (and not defining items, for example). If you want +to make your mod compatible to Treasurer, don’t change your mod, write a TRM for +it instead. + +There is an example TRM, called “`trm_default_example`”. It registers some items +of the default as treasures. Unsurprisingly, it depends on `treasurer` and `default`. + +=== treasurer spawning mods === +Secondly, there are the treasure spawning mods (TSMs). The task of a TSM is to somehow +distribute the available treasures into the world. This is also called “treasure +spawning”. How exactly the TSM spawns the treasures is completely up the TSM. But a +TSM has to request Treasurer to get some random treasures to distribute. A TSM may +optionally request to filter out treasures outside certain preciousness levels +and groups, so the result is a bit controllable and not completely random. +Treasurer can not guarantee to return the requestet amount of treasures, it may +return an empty table, for two reasons: + +* There is no TRM activated. There must be at least one to work. +* The filters filtered out everything, leaving Treasurer with an empty treasure pool +to choose from. This problem can be fixed by installing more TRMs or by balancing the +existing TRMs to cover as many preciousness levels as possible. It also may be that +the range specified by the TSM was too small. It is recommended to keep the +requested range at least of a size of 1. Treasurer does, however, guarantee that +the returned treasures are always in the requested bounds. + +A TSM has to at least depend on Treasurer. +Unlike for TRMs, it may not be a problem to also do some other stuff than just +spawning treasures if it seems feasible. You may choose to make your TSM fully +dependant on Treasure, then it won’t work without Treasurer. You may also choose +to only add an optional dependency on Treasurer. For this to work, your mod must +now select its own treasures, which of course will only come from a rather limited +pool. + +To check if the Treasurer mod is installed, you can use something like this in +your code: + +``` +if(minetest.get_modpath("treasurer")~=nil) then + -- Treasurer is installed. + -- You can call Treasurer’s functions here. +else + -- Treasurer is not installed. + -- You may write your replacement code here. + -- You can not call Treasurer’s funcitons here. +end +``` + +There are two example TSMs. The first one is a very basic one and called “`tsm_gift_example`”. +It gives a “welcome gift” (1 random treasure) to players who just joined the server +or who respawn. The preciousness and group filters are not used. It does only depend on +Treasurer. The second one is called “`tsm_chests_example`” and pretty advanced for an example. +It places chests of the mod “default” between 20 and 200 node lenghts below the water +surface and puts 1-6 random treasures into these. The lower the chest, the higher +the preciousness. It depends on treasurer and default (for the chests, of course). + +=== Recap === +TRMs define treasures, TSMs spawn them. Treasurer manages the pool of available treasures. +TRMs and TSMs do not have to know anything from each other. +TRMs and TSMs do not neccessarily have to change any line of code of other mods to function. +Treasurer depends on nothing. + +Important: It should always only be neccessary for TRMs and TSMs to depend on Treasurer. +All other mods do NOT HAVE TO and SHOULD NOT depend on Treasurer. + + + +=== Treasure attributes === +This section explains the various attributes a treasure can have. + +==== Rarity ==== +Rarity in Treasurer works in a pretty primitive way: The relative rarities of all +treasures from the treasure pool are simply all added up. The probabilitiy of one +certain treasure is then simply the rarity value divided by the sum. + +==== Preciousness ==== +How “precious” an item is, is highly subjective and also not always easy to categorize. +Preciousness in Treasurer’s terms should be therefore viewed as “utility” or as +“reward level” or “strength” or even “beauty” or whatever positive attributes you can +think of for items. See the text file `GROUPS_AND_PRECIOUSNESS` for a rough +guideline. +So, if you create a TRM and your treasure is something you want the player work +hard for, assign it a high preciousness. Everyday items that are already easy to +obtain in normal gameplay certainly deserve a lower precious than items that are +expensive to craft. +If your treasure consists of a powerful +item, assign it a high preciousness. When in doubt, try to value gameplay over +personal taste. Remember that TSMs can (and will!) filter out treasures based +on their preciousness. +For TSM authors, consider preciousness this way: If the trouble the player has +to get through to in order to obtain the treasure is high, better filter +out unprecious treasures. If your TSM distributes many treasures all over the world and these +are easy to obtain, filter out precious treasures. + +TSMs also can just completely ignore preciousness, then the given treasures base +on sheer luck. + +==== Treasurer groups ==== +Every treasure can be assigned to a group. These groups are specific to Treasurer only. +The idea is that treasures which share a common property are member of the same group. +All groups have a name by which they are identified. +For example, if there are apples, plums, pears and oranges and those items can be +eaten for health, all those treasures would be members of the group “food”. + +The group system can be used to further narrow down the treasure pool from which you +want Treasurer to return treasures. This makes it more interesting than just using +an one-dimensional preciousness scale. + +Using the groups system is entirely optional. If your TRM does not specify any group, +your treasure will be assigned to the group “default”. It is not possible for a treasure +to not belong to any group. If your TSM does not specify a group parameter, Treasurer +will use all groups. +While not using groups as a TSM may be perfectly okay, not using groups as a TRM is +not recommended, because TSM which filter by groups may “overlook” your treasure, +even if it would actually fit, simply because you didn’t assign it to a specific group. + +Note that Treasurer groups are completely distinct from Minetest’s group system. + +You can basically invent your own groups on the fly, but it is strongly recommended that you +use the groups suggested in the text file `GROUPS_AND_PRECIOUSNESS` whenever possible, for +maximum portability of your TSM. The text file also has a rough guideline for finding +appropriate values for the preciousness. + + +==== Recap ==== +Rarity determines the chance of a treasure, whereas preciousness determines +the difficulty to obtain it. Group + +== Overview of examples == +- `trm_default_example` - registers items of default mod +- `tsm_chests_example` - spawns chests (from the default mod) +- `tsm_gift_example` - gives one treasure as a “welcome gift” to joined or respawned players + +== Treasurer API documentation == +=== API documentation for treasure registration mods === +The API consists of one function, which is called “`treasurer.register_treasure`”. + +==== `treasurer.register_treasure` ==== +Registers a new treasure (this means the treasure will be ready to be spawned by treasure spawning mods). + +This function does some basic parameter checking to catch the most obvious +mistakes. If invalid parameters have been passed, the input is rejected and +the function returns false. However, it does not cover every possible +mistake, so some invalid treasures may slip through. + +Rarity does not imply preciousness. A rare treasure may not neccessarily a +very precious one. A treasure chest with scorched stuff inside may be very +rare, but it’s certainly also very unprecious. + +===== Parameters ===== +* `name`: name of resulting `ItemStack`, e.g. “`mymod:item`” +* `rarity`: rarity of treasure on a scale from 0 to 1 (inclusive). lower = rarer +* `preciousness` : subjective preciousness on a scale from 0 to 10 (inclusive). higher = more precious. +* `count`: optional value which specifies the multiplicity of the item. Default is 1. See `count` syntax help in this file. +* `wear`: optional value which specifies the wear of the item. Default is 0, which disables the wear. See `wear` syntax help in this file. +* `treasurer_groups`: an optional table of group names to assign this treasure to. If omitted, the treasure is added to the default group. + +===== Return value ===== +`true` on success, `false` on failure. + +=== data formats === +format of count type: +==== `count` ==== +A `count` can be a number or a table + +* `number`: it’s always so many times +* `{min, max}`: it’s pseudorandomly between `min` and `max` times, `math.random` will be used to chose the value +* `{min, max, prob_func}`: it’s between `min` and `max` times, and the value is given by `prob_func` (see below) + +==== `wear` ==== +Completely analogous to `count`. + +==== Format of `prob_func` function ==== +There are no parameters. + +It returns a random or pseudorandom number between 0 (inclusive) and 1 (exclusive). + +`prob_func` is entirely optional, if it’s not used, treasurer will +default to using `math.random`. You can use `prob_func` to define your own +“randomness” function, in case you don’t wish your values to be evenly +distributed. + +=== API documentation for treasure spawning mods === +The API consists of one function, called “`treasurer.select_random_treasures`”. + +==== `treasurer.select_random_treasures` ==== +Request some treasures from treasurer. + +===== Parameters ===== +* `count`: (optional) amount of treasures. If this value is `nil`, Treasurer assumes a default of 1. +* `minimal_preciousness`: (optional) don’t consider treasures with a lower preciousness. If `nil`, there’s no lower bound. +* `maximum_preciousness`: (optional) don’t consider treasures with a higher preciousness. If `nil`, there’s no upper bound. +* `treasurer_group`: (optional): Only consider treasures which are members of at least one of the members of the provided Treasurer group table. `nil` = consider all groups + + +===== Return value ===== +A table of `ItemStacks` (the requested treasures). It may be empty. + diff --git a/mods/treasurer/Treasurer_ANNOUNCEMENT b/mods/treasurer/Treasurer_ANNOUNCEMENT new file mode 100644 index 0000000..695afdf --- /dev/null +++ b/mods/treasurer/Treasurer_ANNOUNCEMENT @@ -0,0 +1,48 @@ +== Treasurer [treasurer] == + +With Treasurer I want to introduce a new way to define and distribute random treasures into the world. Treasurer itself does neither define any items nor does it attempt to distribute them. + +Instead Treasurer provides two interfaces. + +To actually get a game with treasures using Treasurer running, you need at least the following: +[list] +[*]Treasurer +[*]at least one treasure registration mod +[*]at least one treasure spawning mod +[/list] + +A treasure is basically just a fancy ItemStack. It consists of: +- name of item +- relative rarity +- a preciousness +- the amount of item (can be randomized) +- a wear (can be randomized) + + +A TRM does nothing more than telling Treasurer a bunch of treasures it has to offer. Note, however, that a TRM should really do nothing more than just that. The actual mods where the items originate from does most likely NOT have to be changed in order to register its items as treasures. TRMs are simple, + +A TSM distributes (“spawns”) treasures, where it has to ask Treasurer for some treasures. Treasurer then chooses some treasures out of the pool of available treasures and returns a list of ItemStacks. The TSM also may ask Treasurer to filter out treasures which have an either too high or too low preciousness (“preciousness” is defined by the TRMs). Be warned: Treasurer may return no treasures at all, either because no TRM is activated or no treasure that matches the filter exists. +“Spawning” here means to put the treasures into the world somehow. How a TSM does the task of spawning is up to the TSM. Treasurer + +Recap: The TSMs will spawn treasures into the world out of a pool of treasures which is offered by TRMs. Treasurer manages the pool. + +Examples for TRMs are: +- registering bronze, gold, iron ingots and possibly other items of default (see trm_default_example) +- registering bread, wheat, seeds, etc. of farming +- registering super cool item X from awesome mod Y, where X could normally only obtained by “/give” or with creative mode. +- registering the … bla bla bla, you get the idea + +Examples for TSMs are: +- let a mob drop a not mob-specific, random treasure on death +- give a random treasure as a “welcome gift” to joining and respawning players (see tsm_gift_example) +- placing chests around the world and spawning treasures into them (see tsm_chests_example) + +Some of the above example have been implemented into already working mods, which are distributed with Treasurer +as seperate mods. + + +Dependencies of Treasurer: None! +Minimal dependencies of all TRMs: treasurer and all mods that define the registered items +Minimal dependency of all TSMs: treasurer + +License: WTFPL diff --git a/mods/treasurer/depends.txt b/mods/treasurer/depends.txt new file mode 100644 index 0000000..e69de29 diff --git a/mods/treasurer/descripton.txt b/mods/treasurer/descripton.txt new file mode 100644 index 0000000..b22edfc --- /dev/null +++ b/mods/treasurer/descripton.txt @@ -0,0 +1 @@ +A framework which manages treasures which helps other mods to register and distribute treasures around the world. diff --git a/mods/treasurer/init.lua b/mods/treasurer/init.lua new file mode 100644 index 0000000..37b37b1 --- /dev/null +++ b/mods/treasurer/init.lua @@ -0,0 +1,363 @@ +--[==[ + Treasurer + - A mod for Minetest + version 0.2.0 +]==] + +--[=[ + TABLE OF CONTENTS + part 1: Initialization + part 2: Treasure API + part 3: Treasure spawning mod handling + part 4: Internal functions +]=] + +--[=[ + part 1: Initialization +]=] + +-- This creates the main table; all functions of this mod are stored in this table +treasurer = {} + +-- Table which stores all the treasures +treasurer.treasures = {} + +-- This table stores the treasures again, but this time sorted by groups +treasurer.groups = {} + +-- Groups defined by the Treasurer API +treasurer.groups.treasurer = {} +treasurer.groups.treasurer.default = {} + +-- Groups defined by the Minetest API +treasurer.groups.minetest = {} + +--[[ +format of treasure table: + treasure = { + name, -- treasure name, e.g. mymod:item + rarity, -- relative rarity on a scale from 0 to 1 (inclusive). + -- a rare treasure must not neccessarily be a precious treasure + count, -- count (see below) + preciousness, -- preciousness or “worth” of the treasure. + -- ranges from 0 (“scorched stuff”) to 10 (“diamond block”) + wear, -- wear (see below) + metadata, -- unused at the moment + + } + treasures can be nodes or items + +format of count type: + count = number -- it’s always number times + count = {min, max} -- it’s pseudorandomly between min and max times, math.random() will be used to chose the value + count = {min, max, prob_func} -- it’s between min and max times, and the value is given by prob_func (which is not neccessarily random [in the strictly mathematical sense]) + +format of wear type: + completely analogous to count type + +format of prob_func function: + prob_func = function() + --> returns a random or pseudorandom number between 0 (inclusive) and 1 (exclusive) + prob_func is entirely optional, if it’s not used, treasurer will default to math.random. + You can use prob_func to define your own random function, in case you don’t like an even + distribution + +format of treasurer_groups: + This is just a table of strings, each string stands for a group name. +]] + + +--[=[ + part 2: Treasurer API +]=] + +--[[ + treasurer.register_treasure - registers a new treasure + (this means the treasure will be ready to be spawned by treasure spawning mods. + + name: name of resulting ItemStack, e.g. “mymod:item” + rarity: rarity of treasure on a scale from 0 to 1 (inclusive). lower = rarer + preciousness: preciousness of treasure on a scale from 0 (“scorched stuff”) to 10 (“diamond block”). + count: optional value which specifies the multiplicity of the item. Default is 1. See count syntax help in this file. + wear: optional value which specifies the wear of the item. Default is 0, which disables the wear. See wear syntax help in this file. + treasurer_groups: (optional) a table of group names to assign this treasure to. If omitted, the treasure is added to the default group. + This function does some basic parameter checking to catch the most obvious mistakes. If invalid parameters have been passed, the input is rejected and the function returns false. However, it does not cover every possible mistake, so some invalid treasures may slip through. + + returns: true on success, false on failure +]] +function treasurer.register_treasure(name, rarity, preciousness, count, wear, treasurer_groups ) + --[[ We don’t trust our input, so we first check if the parameters + have the correct types and refuse to add the treasure if a + parameter is malformed. + What follows is a bunch of parameter checks. + ]] + + -- check wheather name is a string + if type(name) ~= "string" then + minetest.log("error","[treasure] I rejected a treasure because the name was of type \""..type(name).."\" instead of \"string\".") + return false + end + -- first check if rarity is even a number + if type(rarity) == "number" then + -- then check wheather the rarity lies in the allowed range + if rarity < 0 or rarity > 1 then + minetest.log("error", "[treasurer] I rejected the treasure \""..tostring(name).."\" because it’s rarity value is out of bounds. (it was "..tostring(rarity)..".)") + return false + end + else + minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had an illegal type of rarity. Given type was \""..type(rarity).."\".") + return false + end + + -- check if preciousness is even a number + if type(preciousness) == "number" then + -- then check wheather the preciousness lies in the allowed range + if preciousness < 0 or preciousness > 10 then + minetest.log("error", "[treasurer] I rejected the treasure \""..tostring(name).."\" because it’s preciousness value is out of bounds. (it was "..tostring(preciousness)..".)") + return false + end + else + minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had an illegal type of preciousness. Given type was \""..type(preciousness).."\".") + return false + end + + + -- first check if count is of a correct type + if type(count) ~= "number" and type(count) ~= "nil" and type(count) ~= "table" then + minetest.log("error", "[treasurer] I rejected the treasure \""..tostring(name).."\" because it had an illegal type of “count”. Given type was \""..type(count).."\".") + return false + end + -- if count’s a table, check if it’s format is correct + if type(count) == "table" then + if(not (type(count[1]) == "number" and type(count[2]) == "number" and (type(count[3]) == "function" or type(count[3]) == "nil"))) then + minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had a malformed table for the count parameter.") + return false + end + end + + -- now do the same for wear: + -- first check if wear is of a correct type + if type(wear) ~= "number" and type(wear) ~= "nil" and type(wear) ~= "table" then + minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had an illegal type of “wear”. Given type was \""..type(wear).."\".") + return false + end + -- if wear’s a table, check if it’s format is correct + if type(wear) == "table" then + if(not (type(wear[1]) == "number" and type(wear[2]) == "number" and (type(wear[3]) == "function" or type(wear[3]) == "nil"))) then + minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because it had a malformed table for the wear parameter.") + return false + end + end + + -- check type of treasurer_group + if type(treasurer_groups) ~= "table" and type(treasurer_groups) ~= "nil" and type(treasurer_groups) ~= "string" then + minetest.log("error","[treasurer] I rejected the treasure \""..tostring(name).."\" because the treasure_group parameter is of type "..tosting(type(treasurer_groups)).." (expected: nil, string or table).") + return false + end + + + + + --[[ End of checks. If we reached this point of the code, all checks have been passed + and we finally register the treasure.]] + + -- default count is 1 + if count == nil then count = 1 end + -- default wear is 0 + if wear == nil then wear = 0 end + local treasure = { + name = name, + rarity = rarity, + count = count, + wear = wear, + preciousness = preciousness, + metadata = "", + } + table.insert(treasurer.treasures, treasure) + + --[[ Assign treasure to Treasurer group(s) or default if not provided ]] + -- default Treasurer group is default + if treasurer_groups == nil then treasurer_groups = "default" end + + if(type(treasurer_groups) == "string") then + if(treasurer.groups.treasurer[treasurer_groups] == nil) then + treasurer.groups.treasurer[treasurer_groups] = {} + end + table.insert(treasurer.groups.treasurer[treasurer_groups], treasure) + elseif(type(treasurer_groups) == "table") then + for i=1,#treasurer_groups do + -- assign to Treasurer group (create table if it does not exist yet) + if(treasurer.groups.treasurer[treasurer_groups[i]] == nil) then + treasurer.groups.treasurer[treasurer_groups[i]] = {} + end + table.insert(treasurer.groups.treasurer[treasurer_groups[i]], treasure) + end + + end + + minetest.log("info","[treasurer] Treasure successfully registered: "..name) + return true +end + + +--[=[ + part 3: Treasure spawning mod (TSM) handling +]=] + +--[[ + treasurer.select_random_treasures - request some treasures from treasurer + parameters: + count: (optional) amount of items in the treasure. If this value is nil, treasurer assumes a default of 1. + min_preciousness: (optional) don’t consider treasures with a lower preciousness. nil = no lower limit + max_preciousness: (optional) don’t consider treasures with a higher preciousness. nil = no lower limit + treasurer_groups: (optional): Only consider treasures which are members of at least one of the members of the provided Treasurer group table. nil = consider all groups + returns: + a table of ItemStacks (the requested treasures) - may be empty + on error, it returns false +]] +function treasurer.select_random_treasures(count, min_preciousness, max_preciousness, treasurer_groups) + if #treasurer.treasures == 0 and count >= 1 then + minetest.log("info","[treasurer] I was asked to return "..count.." treasure(s) but I can’t return any because no treasure was registered to me.") + return {} + end + if count == nil then count = 1 end + local sum = 0 + local cumulate = {} + local randoms = {} + + -- copy treasures into helper table + local p_treasures = {} + if(treasurer_groups == nil) then + -- if the group filter is not used (defaul behaviour), copy all treasures + for i=1,#treasurer.treasures do + table.insert(p_treasures, treasurer.treasures[i]) + end + + -- if the group filter IS used, copy only the treasures from the said groups + elseif(type(treasurer_groups) == "string") then + if(treasurer.groups.treasurer[treasurer_groups] ~= nil) then + for i=1,#treasurer.groups.treasurer[treasurer_groups] do + table.insert(p_treasures, treasurer.groups.treasurer[treasurer_groups][i]) + end + else + minetest.log("info","[treasurer] I was asked to return "..count.." treasure(s) but I can’t return any because no treasure which fits to the given Treasurer group “"..treasurer_groups.."”.") + return {} + end + elseif(type(treasurer_groups) == "table") then + for t=1,#treasurer_groups do + if(treasurer.groups.treasurer[treasurer_groups[t]] ~= nil) then + for i=1,#treasurer.groups.treasurer[treasurer_groups[t]] do + table.insert(p_treasures, treasurer.groups.treasurer[treasurer_groups[t]][i]) + end + end + end + else + minetest.log("error","[treasurer] treasurer.select_random_treasures was called with a malformed treasurer_groups parameter!") + return false + end + + if(min_preciousness ~= nil) then + -- filter out too unprecious treasures + for t=#p_treasures,1,-1 do + if((p_treasures[t].preciousness) < min_preciousness) then + table.remove(p_treasures,t) + end + end + end + + if(max_preciousness ~= nil) then + -- filter out too precious treasures + for t=#p_treasures,1,-1 do + if(p_treasures[t].preciousness > max_preciousness) then + table.remove(p_treasures,t) + end + end + end + + for t=1,#p_treasures do + sum = sum + p_treasures[t].rarity + cumulate[t] = sum + end + for c=1,count do + randoms[c] = math.random() * sum + end + + local treasures = {} + for c=1,count do + for t=1,#p_treasures do + if randoms[c] < cumulate[t] then + table.insert(treasures, p_treasures[t]) + break + end + end + end + + local itemstacks = {} + for i=1,#treasures do + itemstacks[i] = treasurer.treasure_to_itemstack(treasures[i]) + end + if #itemstacks < count then + minetest.log("info","[treasurer] I was asked to return "..count.." treasure(s) but I could only return "..(#itemstacks)..".") + end + return itemstacks +end + +--[=[ + Part 4: internal functions +]=] + +--[[ treasurer.treasure_to_itemstack - converts a treasure table to an + ItemStack + parameter: + treasure: a treasure (see format in the head of this file) + returns: + an ItemStack +]] +function treasurer.treasure_to_itemstack(treasure) + local itemstack = {} + itemstack.name = treasure.name + itemstack.count = treasurer.determine_count(treasure) + itemstack.wear = treasurer.determine_wear(treasure) + itemstack.metadata = treasure.metadata + + return ItemStack(itemstack) +end + +--[[ + This determines the count of a treasure by taking the various different + possible types of the count value into account + This function assumes that the treasure table is valid. + returns: the count +]] +function treasurer.determine_count(treasure) + if(type(treasure.count)=="number") then + return treasure.count + else + local min,max,prob = treasure.count[1], treasure.count[2], treasure.count[3] + if(prob == nil) then + return(math.floor(min + math.random() * (max-min))) + else + return(math.floor(min + prob() * (max-min))) + end + end +end + +--[[ + This determines the wear of a treasure by taking the various different + possible types of the wear value into account. + This function assumes that the treasure table is valid. + returns: the count +]] +function treasurer.determine_wear(treasure) + if(type(treasure.wear)=="number") then + return treasure.wear + else + local min,max,prob = treasure.wear[1], treasure.wear[2], treasure.wear[3] + if(prob == nil) then + return(math.floor(min + math.random() * (max-min))) + else + return(math.floor(min + prob() * (max-min))) + end + end +end + diff --git a/mods/tsm_chests_example/depends.txt b/mods/tsm_chests_example/depends.txt new file mode 100644 index 0000000..f05833c --- /dev/null +++ b/mods/tsm_chests_example/depends.txt @@ -0,0 +1,2 @@ +treasurer +default diff --git a/mods/tsm_chests_example/description.txt b/mods/tsm_chests_example/description.txt new file mode 100644 index 0000000..5f4a35d --- /dev/null +++ b/mods/tsm_chests_example/description.txt @@ -0,0 +1 @@ +Generates some chests in the underground with random treasures. diff --git a/mods/tsm_chests_example/init.lua b/mods/tsm_chests_example/init.lua new file mode 100644 index 0000000..be0ae92 --- /dev/null +++ b/mods/tsm_chests_example/init.lua @@ -0,0 +1,224 @@ +--[[ + This is an example treasure spawning mod (TSM) for the default mod. + It needs the mods “treasurer” and “default” to work. + For an example, it is kinda advanced. + + A TSM’s task is to somehow bring treasures (which are ItemStacks) into the world. + This is also called “spawning treasures”. + How it does this task is completely free to the programmer of the TSM. + + This TSM spawns the treasures by placing chests (default:chest) between 20 and 200 node lengths below the water surface. This cau + The chests are provided by the default mod, therefore this TSM depends on the default mod. + The treasures are requested from the treasurer mod. The TSM asks the treasurer mod for some treasures. + + However, the treasurer mod comes itself with no treasures whatsoever. You need another mod which tells the treasurer what treasures to add. These mods are called “treasure registration mods” (TRMs). + For this, there is another example mod, called “trm_default_example”, which registers a bunch of items of the default mod, like default:gold_ingot. +]] + + +local chest_formspec = + "size[8,9]" .. + default.gui_bg .. + default.gui_bg_img .. + default.gui_slots .. + "list[current_name;main;0,0.3;8,4;]" .. + "list[current_player;main;0,4.85;8,1;]" .. + "list[current_player;main;0,6.08;8,3;8]" .. + "listring[current_name;main]" .. + "listring[current_player;main]" .. + default.get_hotbar_bg(0,4.85) + +local function get_locked_chest_formspec(pos) + local spos = pos.x .. "," .. pos.y .. "," .. pos.z + local formspec = + "size[8,9]" .. + default.gui_bg .. + default.gui_bg_img .. + default.gui_slots .. + "list[nodemeta:" .. spos .. ";main;0,0.3;8,4;]" .. + "list[current_player;main;0,4.85;8,1;]" .. + "list[current_player;main;0,6.08;8,3;8]" .. + "listring[nodemeta:" .. spos .. ";main]" .. + "listring[current_player;main]" .. + default.get_hotbar_bg(0,4.85) + return formspec +end + +local function has_locked_chest_privilege(meta, player) + if player:get_player_name() ~= meta:get_string("owner") then + return false + end + return true +end + +minetest.register_node(":default:chest", { + description = "Chest", + tiles = {"default_chest_top.png", "default_chest_top.png", "default_chest_side.png", + "default_chest_side.png", "default_chest_side.png", "default_chest_front.png"}, + paramtype2 = "facedir", + groups = {choppy = 2, oddly_breakable_by_hand = 2}, + legacy_facedir_simple = true, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), + allow_metadata_inventory_put = function(pos, listname, index, stack, player) + if player then + minetest.chat_send_player(player:get_player_name(), "You're not allowed to put things in chests!") + return 0 + else + --minetest.chat_send_all("Error! Non player putting things in chests") + return -1 + end + end, + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("formspec", chest_formspec) + meta:set_string("infotext", "Chest") + local inv = meta:get_inventory() + inv:set_size("main", 8*4) + end, + can_dig = function(pos,player) + local meta = minetest.get_meta(pos); + local inv = meta:get_inventory() + return inv:is_empty("main") + end, + on_metadata_inventory_move = function(pos, from_list, from_index, + to_list, to_index, count, player) + minetest.log("action", player:get_player_name() .. + " moves stuff in chest at " .. minetest.pos_to_string(pos)) + end, + on_metadata_inventory_put = function(pos, listname, index, stack, player) + minetest.log("action", player:get_player_name() .. + " moves stuff to chest at " .. minetest.pos_to_string(pos)) + end, + on_metadata_inventory_take = function(pos, listname, index, stack, player) + minetest.log("action", player:get_player_name() .. + " takes stuff from chest at " .. minetest.pos_to_string(pos)) + end, +}) + +--[[ here are some configuration variables ]] + +local chests_per_chunk = 5 -- number of chests per chunk. 15 is a bit high, an actual mod might have a lower number +local h_min = 0 -- minimum chest spawning height, relative to water_level +local h_max = 15 -- maximum chest spawning height, relative to water_level +local t_min = 2 -- minimum amount of treasures found in a chest +local t_max = 6 -- maximum amount of treasures found in a chest + +--[[ here comes the generation code + the interesting part which involes treasurer comes way below +]] +minetest.register_on_generated(function(minp, maxp, seed) + -- get the water level and convert it to a number + local water_level = minetest.setting_get("water_level") + if water_level == nil or type(water_level) ~= "number" then + water_level = 1 + else + water_level = tonumber(water_level) + end + + -- chests minimum and maximum spawn height + local height_min = water_level + h_min + local height_max = water_level + h_max + + if(maxp.y < height_min or minp.y > height_max) then + return + end + local y_min = math.max(minp.y, height_min) + local y_max = math.min(maxp.y, height_max) + local get_node = minetest.get_node + local env = minetest.env + for i=1, chests_per_chunk do + local pos = {x=math.random(minp.x,maxp.x),z=math.random(minp.z,maxp.z), y=minp.y} + + -- Find ground level + local ground = nil + local top = y_max + for y=y_max,y_min,-1 do + local p = {x=pos.x,y=y,z=pos.z} + local name = get_node(p).name + if name == "air" or name == "default:water_source" or name == "default:lava_source" then + top = y + break + end + end + for y=top,y_min,-1 do + local p = {x=pos.x,y=y,z=pos.z} + local name = get_node(p).name + if name ~= "air" and name ~= "default:water_source" and name ~= "default:lava_source" then + ground = y + break + end + end + + if ground~=nil then + local chest_pos = {x=pos.x,y=ground+1, z=pos.z} + local nn = minetest.get_node(chest_pos).name -- chest node name (before it becomes a chest) + if nn == "air" or nn == "default:water_source" then + -->>>> chest spawning starts here <<<<-- + + -- first: spawn the chest + local chest = {name = "default:chest"} + + -- secondly: rotate the chest + -- find possible faces + local xp, xm, zp, zm + xp = minetest.get_node({x=pos.x+1,y=ground+1, z=pos.z}) + xm = minetest.get_node({x=pos.x-1,y=ground+1, z=pos.z}) + zp = minetest.get_node({x=pos.x,y=ground+1, z=pos.z+1}) + zm = minetest.get_node({x=pos.x,y=ground+1, z=pos.z-1}) + + local facedirs = {} + if(xp.name=="air" or xp.name=="default:water_source") then + table.insert(facedirs, minetest.dir_to_facedir({x=-1,y=0,z=0})) + end + if(xm.name=="air" or xm.name=="default:water_source") then + + table.insert(facedirs, minetest.dir_to_facedir({x=1,y=0,z=0})) + end + if(zp.name=="air" or zp.name=="default:water_source") then + table.insert(facedirs, minetest.dir_to_facedir({x=0,y=0,z=-1})) + end + if(zm.name=="air" or zm.name=="default:water_source") then + table.insert(facedirs, minetest.dir_to_facedir({x=0,y=0,z=1})) + end + + -- choose a random face (if possible) + if(#facedirs == 0) then + minetest.set_node({x=pos.x,y=ground+1, z=pos.z+1},{name=nn}) + chest.param2 = minetest.dir_to_facedir({x=0,y=0,z=1}) + else + chest.param2 = facedirs[math.floor(math.random(#facedirs))] + end + + -- Lastly: place the chest + minetest.set_node(chest_pos,chest) + --minetest.chat_send_all("Placed chest! " .. dump(chest_pos)) + + --->>>>>>>>>> At this point we are finally going to involve Treasurer. <<<<<<<<<<<<<<-- + -- determine a random amount of treasures + local treasure_amount = math.ceil(math.random(t_min, t_max)) + + -- calculate preciousness of treasures based on height. higher = more precious + local height = math.abs(h_min) - math.abs(h_max) + local y_norm = (ground+1) - h_min + local scale = 1 - (y_norm/height) + local minp = 0 --scale*4 -- minimal preciousness: 0..4 + local maxp = 10 --scale*4+2.1 -- maximum preciousness: 2.1..6.1 + + -- now use these values to finally request the treasure(s) + local treasures = treasurer.select_random_treasures(treasure_amount,minp,maxp) + -- That’s it! + + -- Get the inventory of the chest to place the treasures in + local meta = minetest.get_meta(chest_pos) + local inv = meta:get_inventory() + + --[[ Now that we got both our treasures and the chest’s inventory, + let’s place the treasures one for one into it. ]] + for i=1,#treasures do + inv:set_stack("main",i,treasures[i]) + end + end + end + end +end)