code:
bweapons api provides two public functions: bweapons.register_weapon() and bweapons.register_ammo().
bweapons.register_ammo() is not required (actual required ammo is specified in weapon definition), however it was defined for simplicity of registering ammo.

Automatic weapons are currently not implemented.
All fields can be omited (except for mandatory ones), in that case they will either default to some value or a specific feature will be disabled
Many features will only be enabled if a corresponding field was specified (for example if you omit a "flare" field, a muzzle flash wont be shown,
same for all other visuals and sounds). All features are supposed to work together, so there's a big room for experimentation.

The following example contains all possible fields. Many fields are not required or used together
(for example there's no point in defining projectile options if your weapon is hitscan).
Main weapon registration function, with definition supplied in form of table:

bweapons.register_weapon({
    --Mandatory fields
    name = "my_weapons_pack:rifle",                                     --Tool ID
    description = "Rifle",                                              --Name shown in inventory
    texture = "my_weapons_pack_rifle.png",                              --Texture

    --Ammo or energy
    has_durability = true,                                              --Master switch for tool durability. If false, it will not deplete its durability and can only use mana or ammo
    uses = 128,                                                         --Amount of uses until the tool breaks/needs to be recharged by technic or repaired
    requires_technic = false,                                           --If true, weapon durability is expressed as technic charge. Requires "technic"
    technic_charge = 350000,                                            --Maximum charge that the weapon holds, in technic EU
    custom_charge = false,                                              --Item doesn't break when its durability is depleted, and cannot be used until charged/repaired. Has no effect
                                                                        --if technic_powered is true (since this is default behavior in technic mod anyway). Also prevents items from being
                                                                        --Repaired with technic workshop
    mana_per_use = 50,                                                  --If defined, the tool requires this much mana per use. Requires "mana"
    ammo_type = "my_weapons_pack:rifle_round",                          --If defined, the tool requires this type of ammo to function
    ammo_per_shot = 1,                                                  --How much ammo should be used per shot
    return_item = "my_weapons_pack:round_case",                         --Adds an item to the user inventory, if defined. If no space is left, the item is dropped on the ground instead

    --Repair
    repair_with = "default:wood",                                      --Combine the tool with this item on crafting grid (shapeless) to repair it by amount of uses specified below
    repair_uses = 8,                                                    --Amount of uses to repair with "repair with" option
    tool_repair = false,                                                --Allows to repair two items of the same type by combining them on the crafting grid

    --Drop on hit
    drop = "default:stone",                                             --Drop something on successful hit. If penetration is enabled, drops at the last trace stop
    drop_chance = 0.8,                                                  --Chance of drop in range from 0 to 1 (1 is always drop, 0 is never)

    --Hitscan configuration
    hitscan = true,                                                     --Instantly trace shots to the target, use in weapons like firearms, lasers, etc
    distance = 100,                                                     --Distance in nodes in which hitscan traces will connect to the target
    penetration = 2,                                                    --How many nodes/objects hitscan trace will go through before stopping (damaging each one and triggering on_hit())

    --Weapon stats
    liquids_stop = false,                                               --Should hitscan traces or projectiles travel through liquid nodes
    damage = 15,                                                        --Damage per projectile/hitscan trace
    shot_amount = 1,                                                    --Total amount of projectiles or hitscan traces per shot (multiple can be used for shotguns, salvos, etc)
    spread = 0.02,                                                      --Random deviation of direction of every particle or hitscan trace from center of the screen
    cooldown = 2,                                                       --Period of time in seconds after which you can fire the weapon again
    spawn_distance = 1,                                                 --Distance in direction of player view at which projectile spawns or hitscan traces start
    aoe = false,                                                        --Enable Area Of Effect Damage
    aoe_radius = 5,                                                     --Radius at which damage is inflicted, damage is calculated linearly, with highest at the center
                                                                        --and lowest at the edge of radius

    --Visuals
    flare = "tnt_boom.png",                                             --Texture briefly displayed when player fires a weapon
    flare_size = 10,                                                    --Size of fire flare
    flare_glow = true,                                                  --Should fire flare glow
    hit_flare = "tnt_boom.png",                                         --Texture of a big particle spawned upon hit
    hit_flare_size = 20,                                                --Size of hit flare
    hit_flare_glow = true,                                              --Should hit flare glow
    hit_particle = "tnt_smoke.png",                                     --Texture of particles to emit when hit something
    hit_particle_glow = false,                                          --Should hit particles glow
    hit_particle_amount = 32,                                           --Amount of hit particles to spawn
    hit_particle_size = 2,                                              --Maximum visual size of hit particles. Minimum size is lowest integer of maximum size divided by two.
    hit_particle_velocity = 2,                                          --Starting velocity of hit particles
    hit_particle_gravity = -10,                                         --Acceleration on vertical axis of hit particles
    trail_particle = "my_weapons_pack_projectile_trail.png",            --Texture of projectile trail particles
    trail_particle_distance = 0.5,                                      --Distance between each trail particle with hitscan mode. No effect in projectile mode.
    trail_particle_velocity = 1,                                        --Random velocity of each particle in trail
    trail_particle_gravity = 0,                                         --Vertical acceleration of each particle in trail
    trail_particle_size = 1,                                            --Maximum size of each trail particle. Minimum size is trail_particle_size/2
    trail_particle_amount = 4,                                          --Amount of particles to emit each server step (with projectile) or hitscan trail particle distance
    trail_particle_displacement = 0,                                    --Random displacement of each particle from trail center
    trail_particle_glow = true,                                         --Should trail particles glow

    --Sounds
    fire_sound = "my_weapons_pack_fire_sound",                          --Sound when firing a weapon
    fire_sound_gain = 1,                                                --How loud the fire sound should be
    reload_sound = "my_weapons_pack_reload_sound",                      --Sound when weapon is cooling down/not enough ammo or charge
    reload_sound_gain = 0.25,                                           --How loud the reload sound should be
    hit_sound = "my_weapons_pack_hit_sound",                            --Sound, emitted at every place where projectile/hitscan trace hits something
    hit_sound_gain = 0.25,                                              --How loud the hit sound should be

    --Projectile configuration
    projectile_speed = 15,                                              --Starting velocity of projectile
    projectile_gravity = -10,                                           --Acceleration on vertical axis of projectile
    projectile_timeout = 35,                                            --Timeout in server ticks after which projectile dissapears, calling on_timeout()

    --Projectile visuals
    projectile_texture = "my_weapons_pack_projectile.png",              --Texture of projectiile
    projectile_glow = true,                                             --Should projectile visual glow
    projectile_visual_size = 1,                                         --Size of projectile visual

    --Custom functions
    on_fire = function(itemstack, user, pointed_thing)
            print("Fired weapon")
        end,                                                           --Custom function called on weapon fire

    on_hit = function (self, target)
            print("Hit something")
        end,                                                            --Custom function called on hitscan/projectile hit. With hitscan, self provides a user entity,
                                                                        --with projectile self provides a projectile entity. Depending on occasion, target may reference a node or an object

    on_timeout = function (self)
            print("Projectile timeout")
        end,                                                            --Custom function called on a projectile timeout. Self refers to the projectile

    --Recipes
    shapeless_recipe = false,                                           --Should the recipe be shapeless
    recipe={
        {
            {'moreores:tin_ingot',            'technic:diamond_drill_head', 'moreores:tin_ingot'},
            {'technic:stainless_steel_ingot', 'technic:motor',              'technic:stainless_steel_ingot'},
            {'',                              'technic:red_energy_crystal', 'default:copper_ingot'}
        },
        {
            {'moreores:tin_ingot',            'technic:diamond_drill_head', 'moreores:tin_ingot'},
            {'technic:stainless_steel_ingot', 'technic:tin_ingot',              'technic:stainless_steel_ingot'},
            {'',                              'technic:red_energy_crystal', 'default:copper_ingot'}
        }
    },                                                                  --Recipes to be registered for this weapon. Table may contain multiple recipes
    replacements = {{"mesecraft_bucket:bucket_water", "mesecraft_bucket:bucket_empty"}},    --Replacements for recipe items
})

Ammo registration function, with definition supplied in form of table:

bweapons.register_ammo({
    --Mandatory
    name = "my_weapons_pack:rifle_round",                               --Ammo ID
    description = "Pistol Round",                                       --Inventory name
    texture = "bweapons_firearms_pack_pistol_round.png",                --Inventory texture

    --Optional
    recipe={
        {
            {'moreores:tin_ingot',            'technic:diamond_drill_head', 'moreores:tin_ingot'},
            {'technic:stainless_steel_ingot', 'technic:motor',              'technic:stainless_steel_ingot'},
            {'',                              'technic:red_energy_crystal', 'default:copper_ingot'}
        },
        {
            {'moreores:tin_ingot',            'technic:diamond_drill_head', 'moreores:tin_ingot'},
            {'technic:stainless_steel_ingot', 'technic:tin_ingot',              'technic:stainless_steel_ingot'},
            {'',                              'technic:red_energy_crystal', 'default:copper_ingot'}
        }
    },                                                                  --Recipes to be registered for this ammo. Table may contain multiple recipes
    amount = 1,                                                         --Amount of ammo to be given after crafting
})

Copy of functions above without comments and whitespace, for easy copy-pasting:

bweapons.register_weapon({
    name = "my_weapons_pack:rifle",
    description = "Rifle",
    texture = "my_weapons_pack_rifle.png",
    has_durability = true,
    uses = 128,
    requires_technic = false,
    technic_charge = 350000,
    custom_charge = false,
    mana_per_use = 50,
    ammo_type = "my_weapons_pack:rifle_round",
    ammo_per_shot = 1,
    return_item = "my_weapons_pack:round_case",
    repair_with = "default:wood",
    repair_uses = 8,
    tool_repair = false,
    drop = "default:stone",
    drop_chance = 0.8,
    hitscan = true,
    distance = 100,
    penetration = 2,
    liquids_stop = false,
    damage = 15,
    shot_amount = 1,
    spread = 0.02,
    cooldown = 2,
    spawn_distance = 1,
    aoe = false,
    aoe_radius = 5,
    flare = "tnt_boom.png",
    flare_size = 10,
    flare_glow = true,
    hit_flare = "tnt_boom.png",
    hit_flare_size = 20,
    hit_flare_glow = true,
    hit_particle = "tnt_smoke.png",
    hit_particle_glow = false,
    hit_particle_amount = 32,
    hit_particle_size = 2,
    hit_particle_velocity = 2,
    hit_particle_gravity = -10,
    trail_particle = "my_weapons_pack_projectile_trail.png",
    trail_particle_distance = 0.5,
    trail_particle_velocity = 1,
    trail_particle_gravity = 0,
    trail_particle_size = 1,
    trail_particle_amount = 4,
    trail_particle_displacement = 0,
    trail_particle_glow = true,
    fire_sound = "my_weapons_pack_fire_sound",
    fire_sound_gain = 1,
    reload_sound = "my_weapons_pack_reload_sound",
    reload_sound_gain = 0.25,
    hit_sound = "my_weapons_pack_hit_sound",
    hit_sound_gain = 0.25,
    projectile_speed = 15,
    projectile_gravity = -10,
    projectile_timeout = 35,
    projectile_texture = "my_weapons_pack_projectile.png",
    projectile_glow = true,
    projectile_visual_size = 1,
    on_fire = function(itemstack, user, pointed_thing)
            print("Fired weapon")
        end,
    on_hit = function (self, target)
            print("Hit something")
        end,
    on_timeout = function (self)
            print("Projectile timeout")
        end,
    recipe={
        {
            {'moreores:tin_ingot',            'technic:diamond_drill_head', 'moreores:tin_ingot'},
            {'technic:stainless_steel_ingot', 'technic:motor',              'technic:stainless_steel_ingot'},
            {'',                              'technic:red_energy_crystal', 'default:copper_ingot'}
        },
        {
            {'moreores:tin_ingot',            'technic:diamond_drill_head', 'moreores:tin_ingot'},
            {'technic:stainless_steel_ingot', 'technic:tin_ingot',              'technic:stainless_steel_ingot'},
            {'',                              'technic:red_energy_crystal', 'default:copper_ingot'}
        }
    },
})

bweapons.register_ammo({
    name = "my_weapons_pack:rifle_round",
    description = "Rifle Round",
    texture = "bweapons_firearms_pack_pistol_round.png",
    recipe={
        {
            {'moreores:tin_ingot',            'technic:diamond_drill_head', 'moreores:tin_ingot'},
            {'technic:stainless_steel_ingot', 'technic:motor',              'technic:stainless_steel_ingot'},
            {'',                              'technic:red_energy_crystal', 'default:copper_ingot'}
        },
        {
            {'moreores:tin_ingot',            'technic:diamond_drill_head', 'moreores:tin_ingot'},
            {'technic:stainless_steel_ingot', 'technic:tin_ingot',              'technic:stainless_steel_ingot'},
            {'',                              'technic:red_energy_crystal', 'default:copper_ingot'}
        }
    },
    amount = 1,
})


code:
local path = minetest.get_modpath("bweapons_api")
dofile(path .. "/api.lua")

repo_name: korkut/MeseCraft

license: mit


code:
name = bweapons_api
author = ClockGen
description = An API for easy creation of various weapons with many configurable options
depends = default
optional_depends = technic, mana


code:
# Combine player and projectile velocity
bweapons_combine_velocity (Add player velocity to projectile velocity) bool false

# Global bweapons damage multiplier
bweapons_damage_multiplier (Bweapons damage multiplier) float 1

# Distance from projectile to which raycast is performed each server step
bweapons_projectile_raycast_distance (Projectile raycast distance) float 0.5 0 5


code:
bweapons.register_ammo({
    name = "bweapons_bows_pack:arrow",
    description = "Arrow",
    texture = "bweapons_bows_pack_arrow.png",
    recipe = {
        {
            {'', 'default:steel_ingot', ''},
            {'', 'default:stick', ''},
            {'', 'default:paper', ''},
        },
    },
    amount = 8,
})

bweapons.register_ammo({
    name = "bweapons_bows_pack:bolt",
    description = "Bolt",
    texture = "bweapons_bows_pack_bolt.png",
    recipe = {
        {
            {'', 'default:steel_ingot', ''},
            {'', 'default:steel_ingot', ''},
            {'', 'default:paper', ''},
        },
    },
    amount = 8,
})


code:
local path = minetest.get_modpath("bweapons_bows_pack")
dofile(path .. "/weapons.lua")
dofile(path .. "/ammo.lua")


code:
name = bweapons_bows_pack
description = Part of bweapons_modpack, providing bows
author = ClockGen
depends = default, bweapons_api
optional_depends = technic


code:
bweapons.register_weapon({
    name = "bweapons_bows_pack:wooden_bow",
    description = "Wooden Bow",
    texture = "bweapons_bows_pack_wooden_bow.png",
    has_durability = true,
    uses = 128,
    ammo_type = "bweapons_bows_pack:arrow",
    tool_repair = true,
    damage = 10,
    spread = 0.05,
    cooldown = 1,
    hit_flare = "tnt_smoke.png",
    hit_flare_size = 2,
    hit_flare_glow = false,
    hit_particle = "tnt_smoke.png",
    hit_particle_glow = false,
    hit_particle_amount = 32,
    hit_particle_size = 2,
    hit_particle_velocity = 3,
    hit_particle_gravity = -10,
    fire_sound = "bweapons_bows_pack_longbow_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_bows_pack_longbow_reload",
    hit_sound = "bweapons_bows_pack_arrow_hit",
    hit_sound_gain = 0.5,
    projectile_speed = 30,
    projectile_gravity = -6,
    projectile_dampening = 0,
    projectile_timeout = 35,
    projectile_texture = "bweapons_bows_pack_arrow_entity.png",
    projectile_glow = false,
    projectile_visual_size = 0.4,
    drop = "bweapons_bows_pack:arrow",
    drop_chance = 0.5,
    recipe={
        {
            {'', 'group:wood', 'farming:string'},
            {'group:wood', '', 'farming:string'},
            {'', 'group:wood', 'farming:string'}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_bows_pack:crossbow",
    description = "Crossbow",
    texture = "bweapons_bows_pack_crossbow.png",
    has_durability = true,
    uses = 256,
    ammo_type = "bweapons_bows_pack:bolt",
    tool_repair = true,
    damage = 15,
    spread = 0.02,
    cooldown = 1.5,
    hit_flare = "tnt_smoke.png",
    hit_flare_size = 2,
    hit_flare_glow = false,
    hit_particle = "tnt_smoke.png",
    hit_particle_glow = false,
    hit_particle_amount = 32,
    hit_particle_size = 2,
    hit_particle_velocity = 3,
    hit_particle_gravity = -10,
    fire_sound = "bweapons_bows_pack_crossbow_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_bows_pack_crossbow_reload",
    hit_sound = "bweapons_bows_pack_bolt_hit",
    hit_sound_gain = 0.5,
    projectile_speed = 30,
    projectile_gravity = -6,
    projectile_dampening = 0,
    projectile_timeout = 35,
    projectile_texture = "bweapons_bows_pack_bolt_entity.png",
    projectile_glow = false,
    projectile_visual_size = 0.4,
    drop = "bweapons_bows_pack:bolt",
    drop_chance = 0.85,
    recipe={
        {
            {'default:steel_ingot', 'group:wood', 'farming:string'},
            {'group:wood', 'group:wood', ''},
            {'farming:string', '', 'default:steel_ingot'}
        },
    },
})


code:
bweapons.register_ammo({
    name = "bweapons_firearms_pack:pistol_round",
    description = "Pistol Round",
    texture = "bweapons_firearms_pack_pistol_round.png",
    recipe = {
        {
            {'', 'technic:lead_ingot', ''},
            {'', 'tnt:gunpowder', ''},
            {'', 'technic:brass_ingot', ''},
        },
    },
    amount = 8,
})

bweapons.register_ammo({
    name = "bweapons_firearms_pack:rifle_round",
    description = "Rifle Round",
    texture = "bweapons_firearms_pack_rifle_round.png",
    recipe = {
        {
            {'', 'technic:lead_ingot', ''},
            {'technic:stainless_steel_ingot', 'tnt:gunpowder', 'technic:stainless_steel_ingot'},
            {'', 'technic:brass_ingot', ''},
        }
    },
    amount = 8,
})

bweapons.register_ammo({
    name = "bweapons_firearms_pack:shotgun_shell",
    description = "Shotgun Shell",
    texture = "bweapons_firearms_pack_shotgun_shell.png",
    recipe = {
        {
            {'', 'technic:lead_dust', ''},
            {'basic_materials:plastic_sheet', 'tnt:gunpowder', 'basic_materials:plastic_sheet'},
            {'', 'technic:brass_ingot', ''},
        }
    },
    amount = 8,
})

bweapons.register_ammo({
    name = "bweapons_firearms_pack:grenade",
    description = "Grenade",
    texture = "bweapons_firearms_pack_grenade.png",
    recipe = {
        {
            {'technic:coal_dust', 'technic:lead_ingot', 'technic:coal_dust'},
            {'tnt:gunpowder', 'tnt:gunpowder', 'tnt:gunpowder'},
            {'technic:brass_ingot', 'technic:brass_ingot', 'technic:brass_ingot'},
        }
    },
    amount = 4,
})


code:
local path = minetest.get_modpath("bweapons_firearms_pack")

dofile(path .. "/weapons.lua")
dofile(path .. "/ammo.lua")


code:
name = bweapons_firearms_pack
description = Part of bweapons_modpack, providing various firearms
author = ClockGen
depends = default, bweapons_api
optional_depends = technic, basic_materials, tnt


code:
bweapons.register_weapon({
    name = "bweapons_firearms_pack:pistol",
    description = "Pistol",
    texture = "bweapons_firearms_pack_pistol.png",
    has_durability = true,
    uses = 96,
    ammo_type = "bweapons_firearms_pack:pistol_round",
    tool_repair = true,
    hitscan = true,
    distance = 50,
    damage = 4,
    shot_amount = 1,
    spread = 0.05,
    cooldown = 0.3,
    flare = "tnt_boom.png",
    flare_size = 8,
    flare_glow = true,
    hit_flare = "tnt_boom.png",
    hit_flare_size = 4,
    hit_flare_glow = true,
    hit_particle = "tnt_smoke.png",
    hit_particle_size = 2,
    hit_particle_velocity = 2,
    hit_particle_gravity = -10,
    fire_sound = "bweapons_firearms_pack_pistol_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_firearms_pack_pistol_reload",
    hit_sound = "bweapons_firearms_pack_ricochet",
    hit_sound_gain = 1,
    recipe = {
        {
            {'', 'technic:stainless_steel_ingot', ''},
            {'', 'technic:carbon_plate', 'technic:stainless_steel_ingot'},
            {'', '', 'group:wood'}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_firearms_pack:shotgun",
    description = "Pump-Action Shotgun",
    texture = "bweapons_firearms_pack_shotgun.png",
    has_durability = true,
    uses = 128,
    ammo_type = "bweapons_firearms_pack:shotgun_shell",
    tool_repair = true,
    hitscan = true,
    distance = 35,
    damage = 5,
    shot_amount = 5,
    spread = 0.2,
    cooldown = 1.5,
    flare = "tnt_boom.png",
    flare_size = 12,
    flare_glow = true,
    hit_flare = "tnt_boom.png",
    hit_flare_size = 4,
    hit_flare_glow = true,
    hit_particle = "tnt_smoke.png",
    hit_particle_size = 2,
    hit_particle_velocity = 2,
    hit_particle_gravity = -10,
    fire_sound = "bweapons_firearms_pack_shotgun_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_firearms_pack_shotgun_reload",
    hit_sound = "bweapons_firearms_pack_ricochet",
    hit_sound_gain = 1,
    recipe = {
        {
            {'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot', ''},
            {'group:wood', 'technic:carbon_plate', 'technic:stainless_steel_ingot'},
            {'', 'group:wood', 'group:wood'}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_firearms_pack:double_barrel",
    description = "Double-Barreled Shotgun",
    texture = "bweapons_firearms_pack_double_barrel_shotgun.png",
    has_durability = true,
    uses = 96,
    ammo_type = "bweapons_firearms_pack:shotgun_shell",
    ammo_per_shot = 2,
    tool_repair = true,
    hitscan = true,
    distance = 35,
    damage = 5,
    shot_amount = 10,
    spread = 0.35,
    cooldown = 3,
    flare = "tnt_boom.png",
    flare_size = 15,
    flare_glow = true,
    hit_flare = "tnt_boom.png",
    hit_flare_size = 4,
    hit_flare_glow = true,
    hit_particle = "tnt_smoke.png",
    hit_particle_size = 2,
    hit_particle_velocity = 2,
    hit_particle_gravity = -10,
    fire_sound = "bweapons_firearms_pack_double_barrel_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_firearms_pack_shotgun_reload",
    hit_sound = "bweapons_firearms_pack_ricochet",
    hit_sound_gain = 1,
    recipe = {
        {
            {'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot', 'group:wood'},
            {'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot', 'technic:composite_plate'},
            {'', 'group:wood', 'group:wood'}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_firearms_pack:rifle",
    description = "Rifle",
    texture = "bweapons_firearms_pack_hunting_rifle.png",
    has_durability = true,
    uses = 128,
    ammo_type = "bweapons_firearms_pack:rifle_round",
    tool_repair = true,
    hitscan = true,
    distance = 100,
    penetration = 2,
    damage = 17,
    shot_amount = 1,
    spread = 0.02,
    cooldown = 2,
    flare = "tnt_boom.png",
    flare_size = 12,
    flare_glow = true,
    hit_flare = "tnt_boom.png",
    hit_flare_size = 4,
    hit_flare_glow = true,
    hit_particle = "tnt_smoke.png",
    hit_particle_size = 2,
    hit_particle_velocity = 2,
    hit_particle_gravity = -10,
    fire_sound = "bweapons_firearms_pack_rifle_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_firearms_pack_pistol_reload",
    hit_sound = "bweapons_firearms_pack_ricochet",
    hit_sound_gain = 1,
    recipe = {
        {
            {'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot', ''},
            {'technic:composite_plate', 'group:wood', 'technic:stainless_steel_ingot'},
            {'', 'group:wood', 'group:wood'}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_firearms_pack:grenade_launcher",
    description = "Grenade Launcher",
    texture = "bweapons_firearms_pack_grenade_launcher.png",
    has_durability = true,
    uses = 64,
    ammo_type = "bweapons_firearms_pack:grenade",
    tool_repair = true,
    damage = 10,
    spread = 0.1,
    cooldown = 3,
    aoe = true,
    aoe_radius = 4,
    hit_flare = "tnt_boom.png",
    hit_flare_size = 50,
    hit_flare_glow = true,
    hit_particle = "tnt_smoke.png",
    hit_particle_glow = false,
    hit_particle_amount = 256,
    hit_particle_size = 4,
    hit_particle_velocity = 7,
    hit_particle_gravity = -10,
    trail_particle = "tnt_smoke.png",
    trail_particle_size = 0.5,
    trail_particle_displacement = 0.1,
    trail_particle_amount = 4,
    fire_sound = "bweapons_firearms_pack_grenade_launcher_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_firearms_pack_pistol_reload",
    projectile_speed = 20,
    projectile_gravity = -10,
    projectile_timeout = 50,
    projectile_texture = "bweapons_firearms_pack_grenade_ent.png",
    projectile_visual_size = 0.25,
    on_hit = function(self, target)
        tnt.boom(target.intersection_point, {radius=2})
    end,
    recipe = {
        {
            {'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot', 'technic:composite_plate'},
            {'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot', 'technic:composite_plate'},
            {'technic:coal_dust', 'group:wood', 'group:wood'}
        },
    },
})


code:
bweapons.register_ammo({
    name = "bweapons_hitech_pack:rail_slug",
    description = "Depleted Uranium Slug",
    texture = "bweapons_hitech_pack_rail_gun_slug.png",
    recipe={
        {
            {'', 'technic:uranium0_ingot', ''},
            {'', 'technic:uranium0_ingot', ''},
            {'', 'default:steel_ingot', ''}
        },
    },
    amount = 1,
})

bweapons.register_ammo({
    name = "bweapons_hitech_pack:missile",
    description = "Missile",
    texture = "bweapons_hitech_pack_missile.png",
    recipe={
        {
            {'technic:carbon_plate', 'technic:silicon_wafer', 'technic:carbon_plate'},
            {'tnt:gunpowder', 'tnt:gunpowder', 'tnt:gunpowder'},
            {'technic:stainless_steel_ingot', 'technic:coal_dust', 'technic:stainless_steel_ingot'}
        },
    },
    amount = 1,
})


code:
local path = minetest.get_modpath("bweapons_hitech_pack")

dofile(path .. "/weapons.lua")
dofile(path .. "/ammo.lua")


code:
name = bweapons_hitech_pack
author = ClockGen
description = Part of bweapons_modpack, providing hitech, technic-powered weapons
depends = default, bweapons_api


code:
bweapons.register_weapon({
    name = "bweapons_hitech_pack:missile_launcher",
    description = "Missile Launcher",
    texture = "bweapons_hitech_pack_missile_launcher.png",
    has_durability = true,
    uses = 64,
    ammo_type = "bweapons_hitech_pack:missile",
    spread = 0.05,
    cooldown = 1,
    flare = "tnt_boom.png",
    flare_size = 10,
    flare_glow = true,
    hit_flare = "tnt_boom.png",
    hit_flare_size = 4,
    hit_flare_glow = true,
    trail_particle = "tnt_smoke.png",
    trail_particle_velocity = 1,
    trail_particle_gravity = 6,
    trail_particle_size = 3,
    trail_particle_amount = 4,
    trail_particle_displacement = 0.25,
    trail_particle_glow = false,
    fire_sound = "bweapons_hitech_pack_missile_launcher_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_hitech_pack_missile_launcher_reload",
    reload_sound_gain = 0.25,
    projectile_speed = 30,
    projectile_gravity = 0,
    projectile_timeout = 50,
    projectile_texture = "bweapons_hitech_pack_missile_entity.png",
    projectile_glow = true,
    projectile_visual_size = 0.5,
    on_hit = function (self, target)
        tnt.boom(target.intersection_point, {radius=3})
        end,
    on_timeout = function (self)
        tnt.boom(self.previous_pos, {radius=3})
        end,
    recipe={
        {
            {'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot', 'technic:composite_plate'},
            {'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot', 'technic:composite_plate'},
            {'', '', 'technic:composite_plate'}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_hitech_pack:particle_gun",
    description = "Particle Gun",
    texture = "bweapons_hitech_pack_particle_gun.png",
    has_durability = true,
    uses = 64,
    requires_technic = false,
    technic_charge = 100000,
    hitscan = true,
    distance = 40,
    damage = 8,
    spread = 0.08,
    cooldown = 0.25,
    flare = "bweapons_hitech_pack_particle_gun_flare.png",
    flare_size = 10,
    flare_glow = true,
    hit_flare = "bweapons_hitech_pack_particle_hit_flare.png",
    hit_flare_size = 25,
    hit_flare_glow = true,
    hit_particle = "bweapons_hitech_pack_particle_gun_trail_particle.png",
    hit_particle_glow = true,
    hit_particle_amount = 32,
    hit_particle_size = 10,
    hit_particle_velocity = 2,
    hit_particle_gravity = -8,
    trail_particle = "bweapons_hitech_pack_particle_gun_trail_particle.png",
    trail_particle_distance = 1,
    trail_particle_velocity = 0.25,
    trail_particle_gravity = 0,
    trail_particle_size = 6,
    trail_particle_amount = 1,
    trail_particle_displacement = 0,
    trail_particle_glow = true,
    fire_sound = "bweapons_hitech_pack_particle_gun_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_hitech_pack_laser_gun_reload",
    reload_sound_gain = 0.25,
    hit_sound = "bweapons_hitech_pack_impact",
    hit_sound_gain = 0.15,
    recipe={
        {
            {'default:diamond', 'technic:doped_silicon_wafer', 'technic:red_energy_crystal'},
            {'technic:hv_transformer', 'technic:hv_transformer', 'technic:stainless_steel_ingot'},
            {'', '', 'technic:stainless_steel_ingot'}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_hitech_pack:laser_gun",
    description = "Laser Gun",
    texture = "bweapons_hitech_pack_laser_gun.png",
    has_durability = true,
    uses = 128,
    requires_technic = false,
--    technic_charge = 350000,
    hitscan = true,
    distance = 100,
    damage = 15,
    spread = 0.01,
    cooldown = 1.25,
    flare = "bweapons_hitech_pack_laser_gun_flare.png",
    flare_size = 10,
    flare_glow = true,
    hit_flare = "bweapons_hitech_pack_laser_gun_hit_flare.png",
    hit_flare_size = 20,
    hit_flare_glow = true,
    hit_particle = "bweapons_hitech_pack_sparks.png",
    hit_particle_glow = true,
    hit_particle_amount = 32,
    hit_particle_size = 10,
    hit_particle_velocity = 4,
    hit_particle_gravity = -10,
    trail_particle = "bweapons_hitech_pack_laser_gun_trail.png",
    trail_particle_distance = 1,
    trail_particle_velocity = 0.25,
    trail_particle_gravity = 0,
    trail_particle_size = 3,
    trail_particle_amount = 2,
    trail_particle_displacement = 0,
    trail_particle_glow = true,
    fire_sound = "bweapons_hitech_pack_laser_gun_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_hitech_pack_laser_gun_reload",
    reload_sound_gain = 0.25,
    hit_sound = "bweapons_hitech_pack_impact",
    hit_sound_gain = 0.15,
    recipe={
        {
            {'default:mese', 'default:obsidian_glass', 'technic:green_energy_crystal'},
            {'technic:hv_transformer', 'technic:hv_transformer', 'technic:composite_plate'},
            {'', '', 'technic:stainless_steel_ingot'}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_hitech_pack:plasma_gun",
    description = "" ..core.colorize("#35cdff","Plasma Gun\n") ..core.colorize("#FFFFFF", "Phased plasma rifle in the 40-watt range."),
    texture = "bweapons_hitech_pack_plasma_gun.png",
    has_durability = true,
    uses = 96,
    requires_technic = false,
--    technic_charge = 500000,
    damage = 10,
    shot_amount = 5,
    spread = 0.18,
    cooldown = 1.5,
    flare = "bweapons_hitech_pack_plasma_gun_flare.png",
    flare_size = 10,
    flare_glow = true,
    hit_flare = "bweapons_hitech_pack_plasma_gun_hit_flare.png",
    hit_flare_size = 20,
    hit_flare_glow = true,
    hit_particle = "bweapons_hitech_pack_plasma_gun_hit_particle.png",
    hit_particle_glow = true,
    hit_particle_amount = 32,
    hit_particle_size = 10,
    hit_particle_velocity = 4,
    hit_particle_gravity = -10,
    trail_particle = "bweapons_hitech_pack_plasma_gun_projectile_trail.png",
    trail_particle_velocity = 0.5,
    trail_particle_gravity = 0,
    trail_particle_size = 4,
    trail_particle_amount = 2,
    trail_particle_displacement = 0.1,
    trail_particle_glow = true,
    fire_sound = "bweapons_hitech_pack_plasma_gun_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_hitech_pack_laser_gun_reload",
    reload_sound_gain = 0.25,
    hit_sound = "bweapons_hitech_pack_impact",
    hit_sound_gain = 0.1,
    projectile_speed = 30,
    projectile_gravity = 0,
    projectile_timeout = 25,
    projectile_texture = "bweapons_hitech_pack_plasma_gun_projectile.png",
    projectile_glow = true,
    projectile_visual_size = 0.5,
    recipe={
        {
            {'default:diamondblock', 'default:obsidian_glass', 'technic:green_energy_crystal'},
            {'technic:hv_transformer', 'technic:hv_transformer', 'technic:composite_plate'},
            {'', '', 'technic:stainless_steel_ingot'}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_hitech_pack:rail_gun",
    description = "Rail Gun",
    texture = "bweapons_hitech_pack_rail_gun.png",
    has_durability = true,
    uses = 64,
    requires_technic = false,
--    technic_charge = 500000,
    ammo_type = "bweapons_hitech_pack:rail_slug",
    hitscan = true,
    distance = 150,
    penetration = 10,
    liquids_stop = false,
    damage = 20,
    spread = 0.01,
    cooldown = 3.5,
    flare = "bweapons_hitech_pack_rail_gun_flare.png",
    flare_size = 15,
    flare_glow = true,
    hit_flare = "tnt_boom.png",
    hit_flare_size = 25,
    hit_flare_glow = true,
    trail_particle = "bweapons_hitech_pack_rail_gun_trail.png",
    trail_particle_velocity = 1.5,
    trail_particle_gravity = 1,
    trail_particle_size = 4,
    trail_particle_amount = 4,
    trail_particle_displacement = 0,
    trail_particle_glow = true,
    fire_sound = "bweapons_hitech_pack_rail_gun_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_hitech_pack_missile_launcher_reload",
    reload_sound_gain = 0.25,
    on_hit = function (self, target)
            tnt.boom(target.intersection_point, {radius=1})
        end,
    recipe={
        {
            {'technic:copper_coil', 'technic:copper_coil', 'technic:composite_plate'},
            {'technic:hv_transformer', 'technic:hv_transformer', 'technic:blue_energy_crystal'},
            {'technic:copper_coil', 'technic:copper_coil', 'technic:composite_plate'}
        },
    },
})


code:
local path = minetest.get_modpath("bweapons_magic_pack")
dofile(path .. "/weapons.lua")


code:
name = bweapons_magic_pack
author = ClockGen
description = Part of bweapons_modpack, providing magical weapons
depends = bweapons_api, default, magic_materials


code:
local fireball_blacklist = {
    "default:water_source",
    "default:river_water_source",
    "default:water_flowing",
    "default:river_water_flowing",
}

bweapons.register_weapon({
    name = "bweapons_magic_pack:tome_fireball",
    description = "Tome of Fireball",
    texture = "bweapons_magic_pack_tome_fireball.png",
    mana_per_use = 35,
    liquids_stop = true,
    damage = 15,
    shot_amount = 1,
    spread = 0.035,
    cooldown = 1.5,
    aoe = true,
    aoe_radius = 2,
    flare = "bweapons_magic_pack_fireball_flare.png",
    flare_size = 10,
    flare_glow = true,
    hit_flare = "bweapons_magic_pack_fireball_hit_flare.png",
    hit_flare_size = 20,
    hit_flare_glow = true,
    hit_particle = "bweapons_magic_pack_fireball_hit_particle.png",
    hit_particle_glow = true,
    hit_particle_amount = 32,
    hit_particle_size = 10,
    hit_particle_velocity = 7,
    hit_particle_gravity = -8,
    trail_particle = "bweapons_magic_pack_fireball_trail.png",
    trail_particle_velocity = 0.4,
    trail_particle_gravity = 1,
    trail_particle_size = 6,
    trail_particle_amount = 4,
    trail_particle_displacement = 0.6,
    trail_particle_glow = true,
    fire_sound = "bweapons_magic_pack_fireball_fire",
    fire_sound_gain = 0.75,
    reload_sound = "bweapons_magic_pack_reload",
    reload_sound_gain = 0.25,
    hit_sound = "bweapons_magic_pack_fireball_hit",
    hit_sound_gain = 0.75,
    projectile_speed = 27,
    projectile_gravity = 1.5,
    projectile_timeout = 35,
    projectile_texture = "bweapons_magic_pack_fireball_projectile.png",
    projectile_glow = true,
    projectile_visual_size = 1.15,
    on_hit = function (projectile, target)
        if target.type == "node" then
            local target_pos = minetest.get_pointed_thing_position(target, false)
            local node = minetest.get_node_or_nil(target_pos)
            if minetest.is_protected(target_pos, projectile.owner:get_player_name()) then return end
            for _,v in pairs(fireball_blacklist) do
                if node and node.name == v then return end
            end
            local air_pos = minetest.find_node_near(target_pos, 2, "air", true)
            if air_pos ~= nil then
                if minetest.is_protected(air_pos, projectile.owner:get_player_name()) then return end
                local delta = {x = target_pos.x - air_pos.x, y = target_pos.y - air_pos.y, z = target_pos.z - air_pos.z}
                local rotation = minetest.dir_to_wallmounted(delta)
                minetest.set_node(air_pos, {name = "fire:basic_flame", nil, param2 = rotation})
            end
        end
    end,
    recipe = {
        {
            {"", "magic_materials:fire_rune", ""},
            {"", "magic_materials:enchanted_book", ""},
            {"", "", ""}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_magic_pack:tome_iceshard",
    description = "Tome of Ice Shard",
    texture = "bweapons_magic_pack_tome_iceshard.png",
    mana_per_use = 40,
    liquids_stop = true,
    damage = 20,
    shot_amount = 1,
    spread = 0.02,
    cooldown = 1.5,
    aoe = true,
    aoe_radius = 4,
    flare = "bweapons_magic_pack_iceshard_flare.png",
    flare_size = 10,
    flare_glow = true,
    hit_flare = "bweapons_magic_pack_iceshard_hit_flare.png",
    hit_flare_size = 25,
    hit_flare_glow = true,
    hit_particle = "bweapons_magic_pack_iceshard_hit_particle.png",
    hit_particle_glow = true,
    hit_particle_amount = 48,
    hit_particle_size = 8,
    hit_particle_velocity = 8,
    hit_particle_gravity = -8,
    trail_particle = "bweapons_magic_pack_iceshard_trail.png",
    trail_particle_velocity = 0.5,
    trail_particle_gravity = -7,
    trail_particle_size = 4,
    trail_particle_amount = 6,
    trail_particle_displacement = 0.6,
    trail_particle_glow = true,
    fire_sound = "bweapons_magic_pack_iceshard_fire",
    fire_sound_gain = 0.75,
    reload_sound = "bweapons_magic_pack_reload",
    reload_sound_gain = 0.25,
    hit_sound = "bweapons_magic_pack_iceshard_hit",
    hit_sound_gain = 0.75,
    projectile_speed = 30,
    projectile_gravity = -6,
    projectile_timeout = 35,
    projectile_texture = "bweapons_magic_pack_iceshard_projectile.png",
    projectile_glow = true,
    projectile_visual_size = 1.15,
    on_hit = function (projectile, target)
        if target.type == "node" then
            local target_pos = minetest.get_pointed_thing_position(target, false)
            local node = minetest.get_node_or_nil(target_pos)
            if not node then return end
            if minetest.is_protected(target_pos, projectile.owner:get_player_name()) then return end
            if node.name == "default:water_source" or node.name == "default:river_water_source" then
                minetest.set_node(target_pos, {name = "default:ice"})
            elseif node.name == "default:lava_source" then
                minetest.set_node(target_pos, {name = "default:obsidian"})
            elseif node.name == "default:lava_flowing" then
                minetest.set_node(target_pos, {name = "default:stone"})
            end
        end
    end,
    recipe = {
        {
            {"", "magic_materials:ice_rune", ""},
            {"", "magic_materials:enchanted_book", ""},
            {"", "", ""}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_magic_pack:tome_electrosphere",
    description = "Tome of Electrosphere",
    texture = "bweapons_magic_pack_tome_electrosphere.png",
    mana_per_use = 37,
    liquids_stop = true,
    damage = 18,
    shot_amount = 1,
    spread = 0.02,
    cooldown = 1.5,
    aoe = true,
    aoe_radius = 3,
    flare = "bweapons_magic_pack_electrosphere_flare.png",
    flare_size = 10,
    flare_glow = true,
    hit_flare = "bweapons_magic_pack_electrosphere_hit_flare.png",
    hit_flare_size = 35,
    hit_flare_glow = true,
    hit_particle = "bweapons_magic_pack_electrosphere_hit_particle.png",
    hit_particle_glow = true,
    hit_particle_amount = 48,
    hit_particle_size = 12,
    hit_particle_velocity = 8,
    hit_particle_gravity = 3,
    trail_particle = "bweapons_magic_pack_electrosphere_trail.png",
    trail_particle_velocity = 0,
    trail_particle_gravity = 1,
    trail_particle_size = 5,
    trail_particle_amount = 6,
    trail_particle_displacement = 0.9,
    trail_particle_glow = true,
    fire_sound = "bweapons_magic_pack_electrosphere_fire",
    fire_sound_gain = 1.25,
    reload_sound = "bweapons_magic_pack_reload",
    reload_sound_gain = 0.25,
    hit_sound = "bweapons_magic_pack_electrosphere_hit",
    hit_sound_gain = 1.25,
    projectile_speed = 22,
    projectile_gravity = 0,
    projectile_timeout = 45,
    projectile_texture = "bweapons_magic_pack_electrosphere_projectile.png",
    projectile_glow = true,
    projectile_visual_size = 1.3,
    on_hit = function (projectile, target)
        if target.type == "node" then
            local target_pos = minetest.get_pointed_thing_position(target, false)
            local node = minetest.get_node_or_nil(target_pos)
            if not node then return end
            if minetest.is_protected(target_pos, projectile.owner:get_player_name()) then return end
            if node.name == "default:sand" or node.name == "default:silver_sand" or node.name == "default:desert_sand" then
                minetest.set_node(target_pos, {name = "default:glass"})
            end
        end
    end,
    recipe = {
        {
            {"", "magic_materials:storm_rune", ""},
            {"", "magic_materials:enchanted_book", ""},
            {"", "", ""}
        }
    },
})

bweapons.register_weapon({
    name = "bweapons_magic_pack:staff_magic",
    description = "Staff of Energy",
    texture = "bweapons_magic_pack_staff_magic.png",
    has_durability = true,
    uses = 32,
    custom_charge = true,
    repair_with = "magic_materials:februm_crystal",
    repair_uses = 2,
    liquids_stop = true,
    damage = 27,
    shot_amount = 1,
    spread = 0.02,
    cooldown = 2,
    aoe = true,
    aoe_radius = 6,
    flare = "bweapons_magic_pack_magic_flare.png",
    flare_size = 20,
    flare_glow = true,
    hit_flare = "bweapons_magic_pack_magic_hit_flare.png",
    hit_flare_size = 40,
    hit_flare_glow = true,
    hit_particle = "bweapons_magic_pack_magic_hit_particle.png",
    hit_particle_glow = true,
    hit_particle_amount = 48,
    hit_particle_size = 12,
    hit_particle_velocity = 8,
    hit_particle_gravity = -1,
    trail_particle = "bweapons_magic_pack_magic_trail.png",
    trail_particle_velocity = 2,
    trail_particle_gravity = 0,
    trail_particle_size = 6,
    trail_particle_amount = 8,
    trail_particle_displacement = 1,
    trail_particle_glow = true,
    fire_sound = "bweapons_magic_pack_magic_fire",
    fire_sound_gain = 0.75,
    reload_sound = "bweapons_magic_pack_reload",
    reload_sound_gain = 0.25,
    hit_sound = "bweapons_magic_pack_magic_hit",
    hit_sound_gain = 1,
    projectile_speed = 30,
    projectile_gravity = -7,
    projectile_timeout = 35,
    projectile_texture = "bweapons_magic_pack_magic_projectile.png",
    projectile_glow = true,
    projectile_visual_size = 2,
    recipe = {
        {
            {"", "magic_materials:energy_rune", ""},
            {"", "magic_materials:enchanted_staff", ""},
            {"", "default:diamond", ""}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_magic_pack:staff_light",
    description = "Staff of Light",
    texture = "bweapons_magic_pack_staff_light.png",
    has_durability = true,
    uses = 32,
    custom_charge = true,
    repair_with = "magic_materials:februm_crystal",
    repair_uses = 2,
    damage = 22,
    shot_amount = 1,
    spread = 0.02,
    cooldown = 2,
    aoe = true,
    aoe_radius = 4,
    flare = "bweapons_magic_pack_light_flare.png",
    flare_size = 20,
    flare_glow = true,
    hit_flare = "bweapons_magic_pack_light_hit_flare.png",
    hit_flare_size = 30,
    hit_flare_glow = true,
    hit_particle = "bweapons_magic_pack_light_hit_particle.png",
    hit_particle_glow = true,
    hit_particle_amount = 48,
    hit_particle_size = 4,
    hit_particle_velocity = 10,
    hit_particle_gravity = 1,
    trail_particle = "bweapons_magic_pack_light_trail.png",
    trail_particle_velocity = 0.3,
    trail_particle_gravity = -0.3,
    trail_particle_size = 8,
    trail_particle_amount = 4,
    trail_particle_displacement = 0.9,
    trail_particle_glow = true,
    fire_sound = "bweapons_magic_pack_light_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_magic_pack_reload",
    reload_sound_gain = 0.25,
    hit_sound = "bweapons_magic_pack_light_hit",
    hit_sound_gain = 1.25,
    projectile_speed = 23,
    projectile_gravity = 1.5,
    projectile_timeout = 35,
    projectile_texture = "bweapons_magic_pack_light_projectile.png",
    projectile_glow = true,
    projectile_visual_size = 3,
    recipe = {
        {
            {"", "magic_materials:light_rune", ""},
            {"", "magic_materials:enchanted_staff", ""},
            {"", "default:gold_ingot", ""}
        },
    },
})

bweapons.register_weapon({
    name = "bweapons_magic_pack:staff_void",
    description = "Staff of Void",
    texture = "bweapons_magic_pack_staff_void.png",
    has_durability = true,
    uses = 32,
    custom_charge = true,
    repair_with = "magic_materials:februm_crystal",
    repair_uses = 2,
    liquids_stop = true,
    damage = 25,
    shot_amount = 1,
    spread = 0.02,
    cooldown = 2,
    aoe = true,
    aoe_radius = 5,
    flare = "bweapons_magic_pack_void_flare.png",
    flare_size = 20,
    flare_glow = true,
    hit_flare = "bweapons_magic_pack_void_hit_flare.png",
    hit_flare_size = 30,
    hit_flare_glow = true,
    hit_particle = "bweapons_magic_pack_void_hit_particle.png",
    hit_particle_glow = true,
    hit_particle_amount = 48,
    hit_particle_size = 12,
    hit_particle_velocity = 10,
    hit_particle_gravity = 1,
    trail_particle = "bweapons_magic_pack_void_trail.png",
    trail_particle_velocity = 1,
    trail_particle_gravity = 1,
    trail_particle_size = 6,
    trail_particle_amount = 6,
    trail_particle_displacement = 0.5,
    trail_particle_glow = true,
    fire_sound = "bweapons_magic_pack_void_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_magic_pack_reload",
    reload_sound_gain = 0.25,
    hit_sound = "bweapons_magic_pack_void_hit",
    hit_sound_gain = 1.25,
    projectile_speed = 22,
    projectile_gravity = 0,
    projectile_timeout = 45,
    projectile_texture = "bweapons_magic_pack_void_projectile.png",
    projectile_glow = true,
    projectile_visual_size = 1.5,
    on_hit = function (projectile, target)
        if target.type == "node" then
            local target_pos = minetest.get_pointed_thing_position(target, false)
            local node = minetest.get_node_or_nil(target_pos)
            if not node then return end
            if minetest.is_protected(target_pos, projectile.owner:get_player_name()) then return end
            if minetest.registered_nodes[node.name].diggable == false then return end
--            minetest.set_node(target_pos, {name = "air"})
        end
    end,
    recipe = {
        {
            {"", "magic_materials:void_rune", ""},
            {"", "magic_materials:enchanted_staff", ""},
            {"", "default:obsidian", ""}
        },
    },
})


code:
local path = minetest.get_modpath("bweapons_utility_pack")
dofile(path .. "/weapons.lua")


code:
name = bweapons_utility_pack
author = ClockGen
description = Part of bweapons_modpack, providing utility weapons
depends = default, bweapons_api
optional_depends = vines


code:
bweapons.register_weapon({
    name = "bweapons_utility_pack:torch_bow",
    description = "Torch Bow",
    texture = "bweapons_utility_pack_torch_bow.png",
    has_durability = true,
    uses = 128,
    ammo_type = "default:torch",
    tool_repair = true,
    liquids_stop = true,
    damage = 5,
    spread = 0.08,
    cooldown = 2,
    hit_flare = "tnt_smoke.png",
    hit_flare_size = 2,
    hit_flare_glow = false,
    hit_particle = "bweapons_utility_pack_torch_bow_hit_particle.png",
    hit_particle_glow = true,
    hit_particle_amount = 16,
    hit_particle_size = 4,
    hit_particle_velocity = 3,
    hit_particle_gravity = -10,
    trail_particle = "bweapons_utility_pack_torch_bow_trail.png",
    trail_particle_size = 4,
    trail_particle_displacement = 0.15,
    trail_particle_amount = 2,
    trail_particle_velocity = 1.5,
    trail_particle_gravity = 2,
    trail_particle_glow = true,
    fire_sound = "bweapons_bows_pack_longbow_fire",
    fire_sound_gain = 1,
    reload_sound = "bweapons_bows_pack_longbow_reload",
    hit_sound = "bweapons_bows_pack_arrow_hit",
    hit_sound_gain = 0.5,
    projectile_speed = 30,
    projectile_gravity = -7.5,
    projectile_dampening = 0,
    projectile_timeout = 35,
    projectile_texture = "bweapons_utility_pack_torch_entity.png",
    projectile_glow = true,
    projectile_visual_size = 1,
    on_hit = function (projectile, target)
        if target.type == "node" then
            local target_pos = minetest.get_pointed_thing_position(target, false)
            local node = minetest.get_node_or_nil(target_pos)
            if node and not minetest.registered_nodes[node.name].walkable then return end
            if minetest.is_protected(target_pos, projectile.owner:get_player_name()) then return end
            local air_pos = minetest.find_node_near(target_pos, 2, "air", true)
            if air_pos ~= nil then
                local delta = {x = target_pos.x - air_pos.x, y = target_pos.y - air_pos.y, z = target_pos.z - air_pos.z}
                local rotation = minetest.dir_to_wallmounted(delta)
                local nodename
                if rotation == 0 then
                    nodename = "default:torch_ceiling"
                elseif rotation == 1 then
                    nodename = "default:torch"
                else
                    nodename = "default:torch_wall"
                end
                minetest.set_node(air_pos, {name = nodename, nil, param2 = rotation})
            end
        end
    end,
    recipe={
        {
            {'fire:flint_and_steel', 'group:wood', 'farming:string'},
            {'group:wood', '', 'farming:string'},
            {'default:steel_ingot', 'group:wood', 'farming:string'}
        },
    },
})


code: A weapons modpack aiming at providing a complete, technologic and magic progression of ranged weapons, along with an API to easily make your own ones.


code:
CRAFTING:

hive_wild:
	naturally occurring in apple trees, remove the leaves above the
	 hive to make it drop

bee hive:
	wood	wood		wood
	stick	hive_wild	stick
	stick	wood		stick

empty bee hive: (bees will occupy it after a few minutes)
	wood	wood	wood
	stick	paper	stick
	stick	wood	stick

honeycomb:
	punch a bee hive (or wild bee hive)... take too much and the
	 bees will sting you.

wax/honey:
	cook comb -> wax x2
	craft comb -> honey x2

candle:
	wax	string	wax

wall mount candle:
	candle
	ingot

candelabra:
	candle	candle	candle
	ingot	ingot	ingot		<- must be 3 of the same ore

ingot can be any of: steel, silver, bronze, copper, gold

candles (all types) are not lit when placed, punch with a torch to light
 them. punch them with anything but a torch to snuff them out


code:
--[[
--==========================================
-- FreeGamers Candles Version -- FreeGamers.org
-- Original Candles mod by darkrose
-- Copyright (C) Lisa Milne 2013 <lisa@ltmnet.com>
-- Code: GPL version 2
-- http://www.gnu.org/licenses/>
--==========================================
--]]

screwdriver = screwdriver or {}

local candles = {}

candles.types = {
	{
		unlit = "candles:candle",
		lit = "candles:candle_lit",
		name = "Candle",
		ingot = nil,
		image = "candles_candle"
	},
	{
		unlit = "candles:candle_floor_silver",
		lit = "candles:candle_floor_silver_lit",
		name = "Silver Candle Stick",
		ingot = "moreores:silver_ingot",
		image = "candles_candle_silver"
	},
	{
		unlit = "candles:candle_floor_gold",
		lit = "candles:candle_floor_gold_lit",
		name = "Gold Candle Stick",
		ingot = "default:gold_ingot",
		image = "candles_candle_gold"
	},
	{
		unlit = "candles:candle_floor_bronze",
		lit = "candles:candle_floor_bronze_lit",
		name = "Bronze Candle Stick",
		ingot = "default:bronze_ingot",
		image = "candles_candle_bronze"
	},
	{
		unlit = "candles:candle_wall_silver",
		lit = "candles:candle_wall_silver_lit",
		name = "Silver Wall-Mount Candle",
		ingot = "moreores:silver_ingot",
		image = "candles_candle_silver"
	},
	{
		unlit = "candles:candle_wall_gold",
		lit = "candles:candle_wall_gold_lit",
		name = "Gold Wall-Mount Candle",
		ingot = "default:gold_ingot",
		image = "candles_candle_gold"
	},
	{
		unlit = "candles:candle_wall_bronze",
		lit = "candles:candle_wall_bronze_lit",
		name = "Bronze Wall-Mount Candle",
		ingot = "default:bronze_ingot",
		image = "candles_candle_bronze"
	},
	{
		unlit = "candles:candelabra_silver",
		lit = "candles:candelabra_silver_lit",
		name = "Silver Candelebra",
		ingot = "moreores:silver_ingot",
		image = "candles_candelabra_silver"
	},
	{
		unlit = "candles:candelabra_gold",
		lit = "candles:candelabra_gold_lit",
		name = "Gold Candelebra",
		ingot = "default:gold_ingot",
		image = "candles_candelabra_gold"
	},
	{
		unlit = "candles:candelabra_bronze",
		lit = "candles:candelabra_bronze_lit",
		name = "Bronze Candelebra",
		ingot = "default:bronze_ingot",
		image = "candles_candelabra_bronze"
	},
}

candles.find_lit = function(name)
	for i,n in pairs(candles.types) do
		if n.unlit == name then
			return n.lit
		end
	end
	return nil
end

candles.find_unlit = function(name)
	for i,n in pairs(candles.types) do
		if n.lit == name then
			return n.unlit
		end
	end
	return nil
end

candles.light1 = function(pos, node, puncher)
	if not puncher or not node then
		return
	end
	local wield = puncher:get_wielded_item()
	if not wield then
		return
	end
	wield = wield:get_name()
	if wield and wield == "default:torch" then
		local litname = candles.find_lit(node.name)
		minetest.env:add_node(pos,{name=litname, param1=node.param1, param2=node.param2})
	local p1 = {x=pos.x, y=pos.y+1, z=pos.z}
	local n1 = minetest.get_node(p1)
	if n1.name == "air" then
			minetest.add_node(p1, {name="candles:candle_flame"})
		end
		end
	end

candles.light2 = function(pos, node, puncher)
	if not puncher or not node then
		return
	end
	local wield = puncher:get_wielded_item()
	if not wield then
		return
	end
	wield = wield:get_name()
	if wield and wield == "default:torch" then
		local litname = candles.find_lit(node.name)
		minetest.env:add_node(pos,{name=litname, param1=node.param1, param2=node.param2})
		end
	end

candles.light3 = function(pos, node, puncher)
	if not puncher or not node then
		return
	end
	local wield = puncher:get_wielded_item()
	if not wield then
		return
	end
	wield = wield:get_name()
	if wield and wield == "default:torch" then
		local litname = candles.find_lit(node.name)
		minetest.env:add_node(pos,{name=litname, param1=node.param1, param2=node.param2})
	local p1 = {x=pos.x, y=pos.y+1, z=pos.z}
	local n1 = minetest.get_node(p1)
	if n1.name == "air" then
			minetest.add_node(p1, {name="candles:candelabra_flame", param2 = 1})
		end
		end
	end

	candles.snuff1 = function(pos, node, puncher)
	if not puncher or not node then
		return
	end
	local wield = puncher:get_wielded_item()
	if not wield then
		return
	end
	wield = wield:get_name()
	if not wield or wield ~= "default:torch" then
		local unlitname = candles.find_unlit(node.name)
		minetest.env:add_node(pos,{name=unlitname, param1=node.param1, param2=node.param2})

	local p1 = {x=pos.x, y=pos.y+1, z=pos.z}
	local n1 = minetest.get_node(p1)
	if n1.name == "candles:candle_flame"then
			minetest.remove_node(p1, {name="candles:candle_flame"})
		end
	end
	end

	candles.snuff2 = function(pos, node, puncher)
	if not puncher or not node then
		return
	end
	local wield = puncher:get_wielded_item()
	if not wield then
		return
	end
	wield = wield:get_name()
	if not wield or wield ~= "default:torch" then
		local unlitname = candles.find_unlit(node.name)
		minetest.env:add_node(pos,{name=unlitname, param1=node.param1, param2=node.param2})
		end
		end

	candles.snuff3 = function(pos, node, puncher)
	if not puncher or not node then
		return
	end
	local wield = puncher:get_wielded_item()
	if not wield then
		return
	end
	wield = wield:get_name()
	if not wield or wield ~= "default:torch" then
		local unlitname = candles.find_unlit(node.name)
		minetest.env:add_node(pos,{name=unlitname, param1=node.param1, param2=node.param2})

	local p1 = {x=pos.x, y=pos.y+1, z=pos.z}
	local n1 = minetest.get_node(p1)
	if n1.name == "candles:candelabra_flame"then
			minetest.remove_node(p1, {name="candles:candelabra_flame"})
		end
	end
	end

candles.create_wall = function(ctype)
	minetest.register_node(ctype.unlit, {
		description = ctype.name,
		tiles = {ctype.image.."_top.png",ctype.image.."_bottom.png",ctype.image..".png"},
		drawtype = "nodebox",
		paramtype = "light",
		paramtype2 = "facedir",
		groups = {crumbly=3,oddly_breakable_by_hand=1},
		on_punch = candles.light1,
		sunlight_propagates = true,
		walkable = false,
		sounds = default.node_sound_metal_defaults({
			dug = {name = "default_dig_crumbly", gain = 1.0},
			dig = {name = "default_dig_crumbly", gain = 0.1},
			}
		),
		on_rotate = screwdriver.rotate_simple,
		node_box = {
			type = "fixed",
			fixed = {
				{-0.125, 0.0625, -0.125, 0.125, 0.125, 0.125},
				{-0.0625, -0.1875, -0.0625, 0.0625, 0.0625, 0.0625},
				{-0.125, -0.25, -0.125, 0.125, -0.125, 0.125},
				{-0.0625, -0.3125, -0.0625, 0.375, -0.25, 0.0625},
				{0.4375, -0.4375, -0.1875, 0.5, -0.125, 0.1875},
				{0.3125, -0.375, -0.125, 0.5, -0.1875, 0.125},
				{-0.0625, 0.125, -0.0625, 0.0625, 0.5, 0.0625},
			},
		},
		selection_box = {
			type = "fixed",
			fixed = {-0.1875, -0.5, -0.25, 0.5, 0.5, 0.25},
		},
		on_place = function(itemstack, placer, pointed_thing)
			local above = pointed_thing.above
			local under = pointed_thing.under
			local dir = {x = under.x - above.x,
				     y = under.y - above.y,
				     z = under.z - above.z}

			local wdir = minetest.dir_to_wallmounted(dir)
			local fdir = minetest.dir_to_facedir(dir)

			if wdir == 0 or wdir == 1 then
				return itemstack
			else
				fdir = fdir-1
				if fdir < 0 then
					fdir = 3
				end
				minetest.env:add_node(above, {name = itemstack:get_name(), param2 = fdir})
				itemstack:take_item()
				return itemstack
			end
		end
	})

	minetest.register_node(ctype.lit, {
		description = ctype.name,
		tiles = {ctype.image.."_top.png",ctype.image.."_bottom.png",ctype.image.."_lit.png"},
		drawtype = "nodebox",
		paramtype = "light",
		paramtype2 = "facedir",
		groups = {crumbly=3,oddly_breakable_by_hand=1, not_in_creative_inventory = 1},
		on_punch = candles.snuff1,
		sunlight_propagates = true,
		walkable = false,
		sounds = default.node_sound_metal_defaults({
			dug = {name = "default_dig_crumbly", gain = 1.0},
			dig = {name = "default_dig_crumbly", gain = 0.1},
			}
		),
		on_rotate = screwdriver.rotate_simple,
		drop = ctype.unlit,
		node_box = {
			type = "fixed",
			fixed = {
				{-0.125, 0.0625, -0.125, 0.125, 0.125, 0.125},
				{-0.0625, -0.1875, -0.0625, 0.0625, 0.0625, 0.0625},
				{-0.125, -0.25, -0.125, 0.125, -0.125, 0.125},
				{-0.0625, -0.3125, -0.0625, 0.375, -0.25, 0.0625},
				{0.4375, -0.4375, -0.1875, 0.5, -0.125, 0.1875},
				{0.3125, -0.375, -0.125, 0.5, -0.1875, 0.125},
				{-0.0625, 0.125, -0.0625, 0.0625, 0.5, 0.0625},
			},
		},
		selection_box = {
			type = "fixed",
			fixed = {-0.1875, -0.5, -0.25, 0.5, 0.5, 0.25},
		},
		can_dig = function(pos,player)
			return false
		end,
	})
	minetest.register_craft({
		output = ctype.unlit,
		recipe = {
			{"","candles:candle",""},
			{"",ctype.ingot,ctype.ingot},
		}
	})
end

candles.create_floor= function(ctype)
	minetest.register_node(ctype.unlit, {
		description = ctype.name,
		tiles = {ctype.image.."_top.png",ctype.image.."_bottom.png",ctype.image..".png"},
		drawtype = "nodebox",
		paramtype = "light",
		paramtype2 = "facedir",
		groups = {crumbly=3,oddly_breakable_by_hand=1},
		on_punch = candles.light1,
		sunlight_propagates = true,
		walkable = false,
		sounds = default.node_sound_metal_defaults({
			dug = {name = "default_dig_crumbly", gain = 1.0},
			dig = {name = "default_dig_crumbly", gain = 0.1},
			}
		),
		on_rotate = screwdriver.rotate_simple,
		node_box = {
			type = "fixed",
			fixed = {
				{-0.0625, -0.375, -0.0625, 0.0625, 0.5, 0.0625},
				{-0.125, 0.0625, -0.125, 0.125, 0.125, 0.125},
				{-0.1875, -0.5, -0.1875, 0.1875, -0.4375, 0.1875},
				{-0.125, -0.4375, -0.125, 0.125, -0.375, 0.125},
			},
		},
		selection_box = {
			type = "fixed",
			fixed = {
				{-0.1875, -0.5, -0.1875, 0.1875, 0.5, 0.1875},
			},
		},
				on_place = function(itemstack, placer, pointed_thing)
			local above = pointed_thing.above
			local under = pointed_thing.under
			local dir = {x = under.x - above.x,
				     y = under.y - above.y,
				     z = under.z - above.z}

			local wdir = minetest.dir_to_wallmounted(dir)
			local fdir = minetest.dir_to_facedir(dir)

			if wdir == 1 then
				minetest.env:add_node(above, {name = itemstack:get_name(), param2 = fdir})
				itemstack:take_item()
			end
			return itemstack
		end
	})

	minetest.register_node(ctype.lit, {
		description = ctype.name,
		tiles = {ctype.image.."_top.png",ctype.image.."_bottom.png",ctype.image.."_lit.png"},
		drawtype = "nodebox",
		paramtype = "light",
		paramtype2 = "facedir",
		groups = {crumbly=3,oddly_breakable_by_hand=1, not_in_creative_inventory = 1},
		on_punch = candles.snuff1,
		sunlight_propagates = true,
		walkable = false,
		sounds = default.node_sound_metal_defaults({
			dug = {name = "default_dig_crumbly", gain = 1.0},
			dig = {name = "default_dig_crumbly", gain = 0.1},
			}
		),
		on_rotate = screwdriver.rotate_simple,
		drop = ctype.unlit,
		node_box = {
			type = "fixed",
			fixed = {
				{-0.0625, -0.375, -0.0625, 0.0625, 0.5, 0.0625},
				{-0.125, 0.0625, -0.125, 0.125, 0.125, 0.125},
				{-0.1875, -0.5, -0.1875, 0.1875, -0.4375, 0.1875},
				{-0.125, -0.4375, -0.125, 0.125, -0.375, 0.125},
			},
		},
		selection_box = {
			type = "fixed",
			fixed = {
				{-0.1875, -0.5, -0.1875, 0.1875, 0.5, 0.1875},
			},
		},
		can_dig = function(pos,player)
			return false
		end,
	})
	minetest.register_craft({
		output = ctype.unlit,
		recipe = {
			{"candles:candle"},
			{ctype.ingot},
		}
	})
end

candles.create_candelabra = function(ctype)
	minetest.register_node(ctype.unlit, {
		description = ctype.name,
		tiles = {ctype.image.."_top.png",ctype.image.."_bottom.png",ctype.image..".png"},
		drawtype = "nodebox",
		paramtype = "light",
		paramtype2 = "facedir",
		groups = {crumbly=3,oddly_breakable_by_hand=1},
		on_punch = candles.light3,
		sunlight_propagates = true,
		walkable = false,
		sounds = default.node_sound_metal_defaults({
			dug = {name = "default_dig_crumbly", gain = 1.0},
			dig = {name = "default_dig_crumbly", gain = 0.1},
			}
		),
		on_rotate = screwdriver.rotate_simple,
		node_box = {
			type = "fixed",
			fixed = {
				{-0.0625, -0.375, -0.0625, 0.0625, 0.5, 0.0625},
				{-0.0625, 0.0625, -0.4375, 0.0625, 0.125, 0.4375},
				{-0.1875, -0.5, -0.1875, 0.1875, -0.4375, 0.1875},
				{-0.125, -0.4375, -0.125, 0.125, -0.375, 0.125},
				{-0.125, 0.125, 0.25, 0.125, 0.1875, 0.5},
				{-0.125, 0.125, -0.5, 0.125, 0.1875, -0.25},
				{-0.125, 0.125, -0.125, 0.125, 0.1875, 0.125},
				{-0.0625, 0.1875, 0.3125, 0.0625, 0.5, 0.4375},
				{-0.0625, 0.125, -0.4375, 0.0625, 0.5, -0.3125},
				{-0.4375, 0.0625, -0.0625, 0.4375, 0.125, 0.0625},
				{0.25, 0.125, -0.125, 0.5, 0.1875, 0.125},
				{0.3125, 0.1875, -0.0625, 0.4375, 0.5, 0.0625},
				{-0.5, 0.125, -0.125, -0.25, 0.1875, 0.125},
				{-0.4375, 0.1875, -0.0625, -0.3125, 0.5, 0.0625},
			},
		},
		selection_box = {
			type = "fixed",
			fixed = {-0.45, -0.45, -0.45, 0.45, 0.45, 0.45},
		},
		on_place = function(itemstack, placer, pointed_thing)
			local above = pointed_thing.above
			local under = pointed_thing.under
			local dir = {x = under.x - above.x,
				     y = under.y - above.y,
				     z = under.z - above.z}

			local wdir = minetest.dir_to_wallmounted(dir)
			local fdir = minetest.dir_to_facedir(dir)

			if wdir == 1 then
				minetest.env:add_node(above, {name = itemstack:get_name(), param2 = fdir})
				itemstack:take_item()
			end
			return itemstack
		end
	})

	minetest.register_node(ctype.lit, {
		description = ctype.name,
		tiles = {ctype.image.."_top.png",ctype.image.."_bottom.png",ctype.image.."_lit.png"},
		drawtype = "nodebox",
		paramtype = "light",
		paramtype2 = "facedir",
		groups = {crumbly=3,oddly_breakable_by_hand=1, not_in_creative_inventory = 1},
		on_punch = candles.snuff3,
		sunlight_propagates = true,
		walkable = false,
		sounds = default.node_sound_metal_defaults({
			dug = {name = "default_dig_crumbly", gain = 1.0},
			dig = {name = "default_dig_crumbly", gain = 0.1},
			}
		),
		on_rotate = screwdriver.rotate_simple,
		drop = ctype.unlit,
		node_box = {
			type = "fixed",
			fixed = {
				{-0.0625, -0.375, -0.0625, 0.0625, 0.5, 0.0625},
				{-0.0625, 0.0625, -0.4375, 0.0625, 0.125, 0.4375},
				{-0.1875, -0.5, -0.1875, 0.1875, -0.4375, 0.1875},
				{-0.125, -0.4375, -0.125, 0.125, -0.375, 0.125},
				{-0.125, 0.125, 0.25, 0.125, 0.1875, 0.5},
				{-0.125, 0.125, -0.5, 0.125, 0.1875, -0.25},
				{-0.125, 0.125, -0.125, 0.125, 0.1875, 0.125},
				{-0.0625, 0.1875, 0.3125, 0.0625, 0.5, 0.4375},
				{-0.0625, 0.125, -0.4375, 0.0625, 0.5, -0.3125},
				{-0.4375, 0.0625, -0.0625, 0.4375, 0.125, 0.0625},
				{0.25, 0.125, -0.125, 0.5, 0.1875, 0.125},
				{0.3125, 0.1875, -0.0625, 0.4375, 0.5, 0.0625},
				{-0.5, 0.125, -0.125, -0.25, 0.1875, 0.125},
				{-0.4375, 0.1875, -0.0625, -0.3125, 0.5, 0.0625},
			},
		},
		selection_box = {
			type = "fixed",
			fixed = {-0.45, -0.45, -0.45, 0.45, 0.45, 0.45},
		},
		can_dig = function(pos,player)
			return false
		end,
	})
	minetest.register_craft({
		output = ctype.unlit,
		recipe = {
			{"candles:candle","candles:candle","candles:candle"},
			{ctype.ingot,ctype.ingot,ctype.ingot},
		}
	})
end

minetest.register_node("candles:candle", {
	description = "Candle",
	drawtype = "plantlike",
	tiles = {"candles_candle.png"},
	paramtype = "light",
	groups = {crumbly=3,oddly_breakable_by_hand=1},
	on_punch = candles.light2,
	sunlight_propagates = true,
	walkable = false,
	selection_box = {
		type = "fixed",
		fixed = {-0.1875, -0.5, -0.1875, 0.1875, 0.125, 0.1875},
	},
	on_place = function(itemstack, placer, pointed_thing)
		local above = pointed_thing.above
		local under = pointed_thing.under
		local dir = {x = under.x - above.x,
			     y = under.y - above.y,
			     z = under.z - above.z}

		local wdir = minetest.dir_to_wallmounted(dir)

		if wdir == 1 then
			minetest.env:add_node(above, {name = "candles:candle"})
			itemstack:take_item()
		end
		return itemstack
	end
})

minetest.register_node("candles:candle_lit", {
	description = "Candle",
	drawtype = "plantlike",
	tiles = {
		{name="candles_candle_lit.png", animation={type="vertical_frames", aspect_w=32, aspect_h=32, length=1}},
	},
	paramtype = "light",
	groups = {crumbly=3,oddly_breakable_by_hand=1, not_in_creative_inventory = 1},
	on_punch = candles.snuff2,
	sunlight_propagates = true,
	walkable = false,
	light_source = 11,
	drop = "candles:candle",
	selection_box = {
		type = "fixed",
		fixed = {-0.1875, -0.5, -0.1875, 0.1875, 0.125, 0.1875},
	},
	can_dig = function(pos,player)
		return false
	end,
})

minetest.register_node("candles:candle_flame", {
	drawtype = "plantlike",
	tiles = {
		{
			name = "candles_candle_flame.png",
			animation = {
				type = "vertical_frames",
				aspect_w = 16,
				aspect_h = 16,
				length = 1
			},
		},
	},
	paramtype = "light",
	light_source = 12,
	walkable = false,
	buildable_to = false,
	pointable = false,
	sunlight_propagates = true,
	damage_per_second = 1,
	groups = {torch = 1, dig_immediate = 3, not_in_creative_inventory = 1},
	drop = "",
	can_dig = function(pos,player)
			return false
		end,
})

minetest.register_node("candles:candelabra_flame", {
	drawtype = "plantlike",
		tiles = {
		{
			name = "candles_candelabra_flame.png",
			animation = {
				type = "vertical_frames",
				aspect_w = 16,
				aspect_h = 16,
				length = 1
			},
		},
	},
	paramtype = "light",
	paramtype2 = "meshoptions",
	place_param2 = 1,
	light_source = 14,
	walkable = false,
	buildable_to = false,
	pointable = false,
	sunlight_propagates = true,
	damage_per_second = 1,
	groups = {torch = 1, dig_immediate = 3, not_in_creative_inventory = 1},
	drop = "",
	can_dig = function(pos,player)
		return false
	end,
	})

for i,n in pairs(candles.types) do
   if n.ingot then
		if string.find(n.unlit,"candle_wall") then
		candles.create_wall(n)
		end
		if string.find(n.unlit,"candelabra") then
		candles.create_candelabra(n)
		end
		if string.find(n.unlit,"candle_floor") then
		candles.create_floor(n)
		end
	end
end

-----------------
-- Chandelier
-----------------
minetest.register_node("candles:gold_chandelier", {
	description = "Gold Chandelier",
	drawtype = "plantlike",
	paramtype = "light",
	sunlight_propagates = true,
	tiles = {{
		name = "candles_gold_chandelier_animated.png",
		animation = {
			type = "vertical_frames",
			aspect_w = 16,
			aspect_h = 16,
			length = 2.5
		}
	}},
	wield_image = "candles_gold_chandelier.png",
	inventory_image = "candles_gold_chandelier.png",
	selection_box = {
		type = "fixed",
		fixed = {-0.375, -0.5, -0.375, 0.375, 0.5, 0.375}
	},
	walkable = false,
	groups = {dig_immediate = 2},
	light_source = 10,
	groups = {cracky = 2, oddly_breakable_by_hand = 3},
	sounds = default.node_sound_glass_defaults(),
})

minetest.register_node("candles:gold_chandelier_unlit", {
	description = "Unlit Gold Chandelier",
	drawtype = "plantlike",
	paramtype = "light",
	sunlight_propagates = true,
	tiles = {"candles_gold_chandelier_unlit.png"},
	selection_box = {
		type = "fixed",
		fixed = {-0.375, -0.5, -0.375, 0.375, 0.5, 0.375}
	},
	walkable = false,
	groups = {dig_immediate = 2}
})

minetest.register_craft({
	output = "candles:gold_chandelier",
	recipe = {
		{"", "default:gold_ingot", ""},
		{"default:torch", "default:gold_ingot", "default:torch"}
	}
})

minetest.register_craft({
	output = "candles:gold_chandelier_unlit",
	recipe = {
		{"candles:candle", "default:gold_ingot", "candles:candle"},
		{"", "default:gold_ingot", ""},
		{"candles:candle", "", "candles:candle"}
	}
})

------------------
-- Craft Recipes
------------------
minetest.register_craft({
	output = "candles:wax 2", --Palm Wax???   :)
	recipe = {
		{"default:jungleleaves", "default:jungleleaves", "default:jungleleaves"},
		{"default:jungleleaves", "default:jungleleaves", "default:jungleleaves"},
		{"default:jungleleaves", "default:jungleleaves", "default:jungleleaves"},
	}
})

minetest.register_craft({
	output = "candles:candle",
	recipe = {
		{"bees:wax", "farming:cotton", "bees:wax"},
	}
})


code:
name = candles
description = Adds simple 3d candle light sources.
depends = default, moreores, farming, vessels
optional_depends = bees


code:
=====================

#Minetest mod  "Candles" by darkrose

##Copyright (C) Lisa Milne 2013 <lisa@ltmnet.com>

modified for  the "Church Modpack"

Licensed under GPL2.0 or later,

see http://www.gnu.org/licenses/gpl-2.0.html

=====================

Church Mod Forum Topic :

https://forum.minetest.net/viewtopic.php?f=9&t=16029&sid=65c913208fd877b0d021969ad86cd0d4#p242311

=====================

depends :
- default
- moreores
- farming
- vessels
- mobs?

=====================

![Preview](https://raw.githubusercontent.com/Napiophelios/church/master/church_candles/screenshot.png)

Textures by Napiophelios

License: CC-BY-SA 3.0

=====================


code:
# Carpets mod for minetest

This mod adds 15 colorized wool carpets to the minetest

![](https://github.com/bell07/minetest-carpets/blob/master/screenshot.png)

Maintained version, based on https://forum.minetest.net/viewtopic.php?t=15459 and https://forum.minetest.net/viewtopic.php?f=11&t=11637

License: LGPL-2.1+.

The mod provides an api to register more carpets using template nodes
Example:
```
carpets.register('default:dirt') -- registers carpet looks like dirt
```


code:
local carpet_proto = {
	drawtype = "nodebox",
	paramtype = "light",
	paramtype2 = "facedir",
	node_box = {
		type = "fixed",
		fixed = {-0.5, -0.5, -0.5, 0.5, -0.45, 0.5}
	},
	groups = {
		snappy = 2,
		flammable = 3,
		oddly_breakable_by_hand = 3,
		choppy = 2,
		carpet = 1,
	}
}

-- Register the carpet and recipe using material
--    material - already registered material item the textures and sounds applied from
--    def - optional additional - or overriding data passed to minetest.register_node()
carpets = {}

function carpets.register(material, def)
	local node = table.copy(carpet_proto)
	if def then
		for k,v in pairs(def) do
			node[k] = v
		end
	end

	local material_def = minetest.registered_nodes[material]
	node.description = node.description or material_def.description.." Carpet"
	node.sounds = table.copy(node.sounds or material_def.sounds or default.node_sound_defaults())
	node.groups = table.copy(node.groups)

	if node.tiles then
		node.tiles = table.copy(node.tiles)
	elseif material_def.tiles[6] then
		node.tiles = {material_def.tiles[6]}
	else
		node.tiles = table.copy(material_def.tiles)
	end

	local name = "carpet:" .. (node.name or material:gsub(":", "_"))
	node.name = nil

	minetest.register_node(":" .. name, node)

	minetest.register_craft({
		output = name .. " 32",
		recipe = {
			{"group:wool", "group:wool", "group:wool"},
			{"group:wool", material, "group:wool"}
		}
	})
end


code:
local modpath = minetest.get_modpath(minetest.get_current_modname())
dofile(modpath .. "/api.lua")

if minetest.get_modpath("wool") then
	local nodenames = {
		"wool:black",
		"wool:blue",
		"wool:brown",
		"wool:cyan",
		"wool:dark_green",
		"wool:dark_grey",
		"wool:green",
		"wool:grey",
		"wool:magenta",
		"wool:orange",
		"wool:pink",
		"wool:red",
		"wool:violet",
		"wool:white",
		"wool:yellow",
	}
	for _, nodename in ipairs(nodenames) do
		carpets.register(nodename)
	end
end


code:
name = carpets
author =
description = Adds carpets to MeseCraft.
depends =
optional_depends = wool


code:
=-=-=-=-=-=-=-=-=-=

Castles Mod
by: Philipbenr And DanDuncombe
Edited by: TenPlus1

=-=-=-=-=-=-=-=-=-=

Licence: GPLv3

http://www.gnu.org/licenses/gpl.html

=-=-=-=-=-=-=-=-=-=

This is a mod all about creating castles and castle dungeons. Many of the nodes are used for the outer-walls or dungeons.

=-=-=-=-=-=-=-=-=-=

Contains as of now:

--Walls, Corner-walls,
--Castlestone Stairs, Slabs, and Pillars
--Jailbars (removed)
--Hides
--Arrowslits
--Rubble (for between walls)
--Doors
--and more!

=-=-=-=-=-=-=-=-=-=


code:
default
doors
farming


code:
name = castle
depends = default, doors, farming


code:
local pillar = {
	{"stonebrick", "Stonebrick", "default_stone_brick", "default:stonebrick"},
	{"stone", "Stone", "default_stone", "default:stone"},
	{"sandstone", "Sandstone", "default_sandstone", "default:sandstone"},
	{"desertstone", "Desert Stone", "default_desert_stone", "default:desert_stone"},
	{"obsidian", "Obsidian", "default_obsidian", "default:obsidian"},
	{"dungeonstone", "Dungeon Stone", "castle_dungeon_stone", "castle:dungeon_stone"},
}

if minetest.get_modpath("ethereal") then
	pillar[7] = {"mushroomtrunk", "Mushroom Trunk", "mushroom_trunk", "ethereal:mushroom_trunk"}
	pillar[8] = {"icebrick", "Ice Brick", "brick_ice", "ethereal:icebrick"}
end

local grp = {cracky = 3}

for n = 1, #pillar do

	minetest.register_node("castle:pillars_"..pillar[n][1].."_bottom", {
		drawtype = "nodebox",
		description = pillar[n][2].." Pillar Base",
		tiles = {pillar[n][3]..".png"},
		groups = grp,
		sounds = default.node_sound_defaults(),
		paramtype = "light",
		paramtype2 = "facedir",
		node_box = {
			type = "fixed",
			fixed = {
				{-0.500000,-0.500000,-0.500000,0.500000,-0.375000,0.500000},
				{-0.375000,-0.375000,-0.375000,0.375000,-0.125000,0.375000},
				{-0.250000,-0.125000,-0.250000,0.250000,0.500000,0.250000},
			},
		},
	})

	minetest.register_craft({
		output = "castle:pillars_"..pillar[n][1].."_bottom 4",
		recipe = {
		{"",pillar[n][4],""},
		{"",pillar[n][4],""},
		{pillar[n][4],pillar[n][4],pillar[n][4]} },
	})

	minetest.register_node("castle:pillars_"..pillar[n][1].."_top", {
		drawtype = "nodebox",
		description = pillar[n][2].." Pillar Top",
		tiles = {pillar[n][3]..".png"},
		groups = grp,
		sounds = default.node_sound_defaults(),
		paramtype = "light",
		paramtype2 = "facedir",
		node_box = {
			type = "fixed",
			fixed = {
				{-0.500000,0.312500,-0.500000,0.500000,0.500000,0.500000},
				{-0.375000,0.062500,-0.375000,0.375000,0.312500,0.375000},
				{-0.250000,-0.500000,-0.250000,0.250000,0.062500,0.250000},
			},
		},
	})

	minetest.register_craft({
		output = "castle:pillars_"..pillar[n][1].."_top 4",
		recipe = {
		{pillar[n][4],pillar[n][4],pillar[n][4]},
		{"",pillar[n][4],""},
		{"",pillar[n][4],""} },
	})

	minetest.register_node("castle:pillars_"..pillar[n][1].."_middle", {
		drawtype = "nodebox",
		description = pillar[n][2].." Pillar Middle",
		tiles = {pillar[n][3]..".png"},
		groups = grp,
		sounds = default.node_sound_defaults(),
		paramtype = "light",
		paramtype2 = "facedir",
		node_box = {
			type = "fixed",
			fixed = {
				{-0.250000,-0.500000,-0.250000,0.250000,0.500000,0.250000},
			},
		},
	})

	minetest.register_craft({
		output = "castle:pillars_"..pillar[n][1].."_middle 4",
		recipe = {
		{pillar[n][4],pillar[n][4]},
		{pillar[n][4],pillar[n][4]},
		{pillar[n][4],pillar[n][4]} },
	})

	grp = {cracky = 3, not_in_craft_guide = 0}
end


code:
minetest.register_node("castle:dungeon_stone", {
	description = "Dungeon Stone",
	drawtype = "normal",
	tiles = {"castle_dungeon_stone.png"},
	groups = {cracky = 2},
	paramtype = "light",
	sounds = default.node_sound_stone_defaults(),
})

minetest.register_craft({
	type = "shapeless",
	output = "castle:dungeon_stone",
	recipe = {"default:stonebrick", "default:obsidian"},
})

minetest.register_node("castle:crate", {
	description = "Crate",
	drawtype = "normal",
	tiles = {
		"castle_crate_top.png", "castle_crate_top.png",
		"castle_crate.png", "castle_crate.png",
		"castle_crate.png", "castle_crate.png"},
	groups = {choppy = 3},
	paramtype = "light",
	sounds = default.node_sound_wood_defaults(),

	on_construct = function(pos)

		local meta = minetest.get_meta(pos)

		meta:set_string("formspec", "size[8,9]"
			.. default.gui_bg
			.. default.gui_bg_img
			.. default.gui_slots
			.. "list[current_name;main;0,1;8,4;]"
			.. "list[current_player;main;0,5;8,4;]"
			.. "listring[]")

		meta:set_string("infotext", "Crate")

		local inv = meta:get_inventory()

		inv:set_size("main", 8 * 3)
	end,

	can_dig = function(pos,player)

		local meta = minetest.get_meta(pos)
		local inv = meta:get_inventory()

		return inv:is_empty("main")
	end,
})

minetest.register_craft({
	output = "castle:crate",
	recipe = {
		{"group:wood", "group:wood", "group:wood"},
		{"group:wood", "default:steel_ingot", "group:wood"},
	}
})


code:
CBlocks (Coloured Blocks) mod for Minetest

This mod adds coloured wood and stonebrick blocks to the game without the need for any additional textures.  To craft place 2 wood or stonebrick blocks and then 1 coloured dye.

https://forum.minetest.net/viewtopic.php?f=9&t=13303

Change log:

- 0.1 - Initial release
- 0.2 - Added coloured glass and fixed violet
- 0.3 - Added stairsplus and stairs mod support

Lucky Blocks: 4


code:
local colours = {
	{"black",      "Black",      "#000000b0"},
	{"blue",       "Blue",       "#015dbb70"},
	{"brown",      "Brown",      "#a78c4570"},
	{"cyan",       "Cyan",       "#01ffd870"},
	{"dark_green", "Dark Green", "#005b0770"},
	{"dark_grey",  "Dark Grey",  "#303030b0"},
	{"green",      "Green",      "#61ff0170"},
	{"grey",       "Grey",       "#5b5b5bb0"},
	{"magenta",    "Magenta",    "#ff05bb70"},
	{"orange",     "Orange",     "#ff840170"},
	{"pink",       "Pink",       "#ff65b570"},
	{"red",        "Red",        "#ff000070"},
	{"violet",     "Violet",     "#2000c970"},
	{"white",      "White",      "#abababc0"},
	{"yellow",     "Yellow",     "#e3ff0070"},
}

local stairs_mod = minetest.get_modpath("stairs")
local stairsplus_mod = minetest.get_modpath("moreblocks")
	and minetest.global_exists("stairsplus")

local function cblocks_stairs(nodename, def)

	minetest.register_node(nodename, def)

	if stairs_mod or stairsplus_mod then

		local mod, name = nodename:match("(.*):(.*)")

		for groupname, value in pairs(def.groups) do

			if groupname ~= "cracky"
			and groupname ~= "choppy"
			and groupname ~="flammable"
			and groupname ~="crumbly"
			and groupname ~="snappy" then
				def.groups.groupname = nil
			end
		end

		if stairsplus_mod then

			stairsplus:register_all(mod, name, nodename, {
				description = def.description,
				tiles = def.tiles,
				groups = def.groups,
				sounds = def.sounds,
			})
--[[
		elseif stairs_mod and stairs.mod then

			stairs.register_all(name, nodename,
				def.groups,
				def.tiles,
				def.description,
				def.sounds,
				def.alpha
			)
]]
		elseif stairs_mod and not stairs.mod then

			stairs.register_stair_and_slab(name, nodename,
				def.groups,
				def.tiles,
				("%s Stair"):format(def.description),
				("%s Slab"):format(def.description),
				def.sounds
			)
		end
	end
end

for i = 1, #colours, 1 do

-- wood

cblocks_stairs("cblocks:wood_" .. colours[i][1], {
	description = colours[i][2] .. " Wooden Planks",
	tiles = {"default_wood.png^[colorize:" .. colours[i][3]},
	paramtype = "light",
	is_ground_content = false,
	groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 3, wood = 1},
	sounds = default.node_sound_wood_defaults(),
})

minetest.register_craft({
	output = "cblocks:wood_".. colours[i][1] .. " 2",
	recipe = {
		{"group:wood","group:wood", "dye:" .. colours[i][1]},
	}
})

-- stone brick

cblocks_stairs("cblocks:stonebrick_" .. colours[i][1], {
	description = colours[i][2] .. " Stone Brick",
	tiles = {"default_stone_brick.png^[colorize:" .. colours[i][3]},
	paramtype = "light",
	is_ground_content = false,
	groups = {cracky = 2, stone = 1},
	sounds = default.node_sound_stone_defaults(),
})

minetest.register_craft({
	output = "cblocks:stonebrick_".. colours[i][1] .. " 2",
	recipe = {
		{"default:stonebrick","default:stonebrick", "dye:" .. colours[i][1]},
	}
})

-- glass (no stairs because they dont support transparant nodes)

minetest.register_node("cblocks:glass_" .. colours[i][1], {
	description = colours[i][2] .. " Glass",
	tiles = {"cblocks.png^[colorize:" .. colours[i][3]},
	drawtype = "glasslike",
	paramtype = "light",
	sunlight_propagates = true,
	use_texture_alpha = "blend",
	is_ground_content = false,
	groups = {cracky = 3, oddly_breakable_by_hand = 3},
	sounds = default.node_sound_glass_defaults(),
})

minetest.register_craft({
	output = "cblocks:glass_".. colours[i][1] .. " 2",
	recipe = {
		{"default:glass","default:glass", "dye:" .. colours[i][1]},
	}
})

end

-- add lucky blocks
if minetest.get_modpath("lucky_block") then
lucky_block:add_blocks({
	{"dro", {"cblocks:wood_"}, 10, true},
	{"dro", {"cblocks:stonebrick_"}, 10, true},
	{"dro", {"cblocks:glass_"}, 10, true},
	{"exp"},
})
end

print ("[MOD] Cblocks loaded")


code:
name = cblocks
author = TenPlus1
description = Adds coloured wood, glass and stone blocks.
depends = default
optional_depends = lucky_block, stairs, moreblocks


code:
# ccompass

This minetest mod adds a calibratable compass to the minetest. Original mod [here](https://forum.minetest.net/viewtopic.php?f=11&t=3785)

  - License: Code: MIT, textures: CC BY-SA, sound: CC0.
  - Dependencies to other mods: none
  - Forum: https://forum.minetest.net/viewtopic.php?f=9&t=17881

## For Players:
 <TODO: nice screenshot>

1. Craft the compass as before using the crafting recipe.
   The new compass points to the origin / Zero point and is already usable.
2. Punch the compass to a compatible node (all by default) and enter the target name
3. Now this compass leads you allways to this location. You can give it away to allow other users to find this place.
4. Punch a teleport compatible node (by default mese block) to teleport back to the calibrated place

## For server owners:
The mod support the next settings:

    static_spawnpoint - use this position instead 0,0,0 as initial target
    ccompass_recalibrate (enabled by default): If disabled each compass can be calibrated one time only
    ccompass_restrict_target (Disabled by default): If enabled, only specific nodes are allowed for calibration (usable with any type waypoint nodes for example)
    ccompass_restrict_target_nodes: List of technical node names allowed for compass calibration, separated by ','
    ccompass_aliasses: If enabled the compas:* items will be aliased to the ccompass:* items for compatibility
    ccompass_teleport_nodes: List of technical node names that triggers the teleport to destination, separated by ','

##  For developers:
1. It is possible to change compass settings from other mods by changing values in global table ccompass. So it is possible for example to add a waypoint node to the target-nodes by

```
	ccompass.recalibrate = true
	ccompass.restrict_target = true
	ccompass.restrict_target_nodes["schnitzeljagd:waypoint"] = true
	ccompass.teleport_nodes["default:diamondblock"] = true
```

2. The pointed node metadata will be checked for "waypoint_name" attribute. It this attribute is set, the calibration screen take this string as proposal. This can be used for a game specific calibration node. To get it working working just set in node definition something like

```
          after_place_node = function(pos, placer)
             local meta = minetest.get_meta(pos)
             meta:set_string("waypoint_name", "the unique and wunderfull place")
             meta:set_string("waypoint_pos", minetest.pos_to_string(target_pos)) -- if an other position should be the target instead of the node position
             meta:set_string("waypoint_skip_namechange", "skip") -- do not ask for the waypoint name
          end,
```

3. It is possible to create pre-calibrated compasses trough other mods. Just write the position to the Itemstack meta:

```
    stack:get_meta():set_string("target_pos", minetest.pos_to_string(pos))
```

    Recalibration related to a user should be done by function call
```
    local stack = ItemStack("ccompass:0")
    ccompass.set_target(stack, {
             target_pos_string = minetest.pos_to_string(pos),
             target_name = waypoint_name,
             playername = player:get_player_name()
    })
```

4. Each time the compass is updated, a hook is called, if defined in other mod. The hook is usefull to implement wear or any other compass manipulation logic.
```
    function ccompass.usage_hook(compass_stack, player)
        --do anything with compass_stack
        return modified_compass_stack
    end
```


code:
-- compass configuration interface - adjustable from other mods or minetest.conf settings
ccompass = {}

-- default target to static_spawnpoint or 0/0/0
ccompass.default_target = minetest.setting_get_pos("static_spawnpoint") or {x=0, y=0, z=0}

-- Re-calibration allowed
ccompass.recalibrate = minetest.settings:get_bool("ccompass_recalibrate")
if ccompass.recalibrate == nil then
	ccompass.recalibrate = false
end

-- Target restriction
ccompass.restrict_target = minetest.settings:get_bool("ccompass_restrict_target")
ccompass.restrict_target_nodes = {}
local nodes_setting = minetest.settings:get("ccompass_restrict_target_nodes")
if nodes_setting then
	nodes_setting:gsub("[^,]+", function(z)
		ccompass.restrict_target_nodes[z] = true
	end)
end
-- Teleport targets
ccompass.teleport_nodes = {}
local teleport_nodes_setting = minetest.settings:get("ccompass_teleport_nodes")
if teleport_nodes_setting then
	teleport_nodes_setting:gsub("[^,]+", function(z)
		ccompass.teleport_nodes[z] = false
	end)
else
	ccompass.teleport_nodes["default:mese"] = false
end

if minetest.settings:get_bool("ccompass_aliasses") then
	minetest.register_alias("compass:0", "ccompass:0")
	minetest.register_alias("compass:1", "ccompass:1")
	minetest.register_alias("compass:2", "ccompass:2")
	minetest.register_alias("compass:3", "ccompass:3")
	minetest.register_alias("compass:4", "ccompass:4")
	minetest.register_alias("compass:5", "ccompass:5")
	minetest.register_alias("compass:6", "ccompass:6")
	minetest.register_alias("compass:7", "ccompass:7")
	minetest.register_alias("compass:8", "ccompass:8")
	minetest.register_alias("compass:9", "ccompass:9")
	minetest.register_alias("compass:10", "ccompass:10")
	minetest.register_alias("compass:11", "ccompass:11")
end

-- set a position to the compass stack
function ccompass.set_target(stack, param)
	param = param or {}
	-- param.target_pos_string
	-- param.target_name
	-- param.playername

	local meta=stack:get_meta()
	meta:set_string("target_pos", param.target_pos_string)
	if param.target_name == "" then
		meta:set_string("description", "Magic Compass to "..param.target_pos_string)
	else
		meta:set_string("description", "Magic Compass to "..param.target_name)
	end

	if param.playername then
		local player = minetest.get_player_by_name(param.playername)
		minetest.chat_send_player(param.playername, "Calibration done to "..param.target_name.." "..param.target_pos_string)
		minetest.sound_play({ name = "ccompass_calibrate", gain = 1 }, { pos = player:getpos(), max_hear_distance = 3 })
	end
end

-- Get compass target
local function get_destination(player, stack)
	local posstring = stack:get_meta():get_string("target_pos")
	if posstring ~= "" then
		return minetest.string_to_pos(posstring)
	else
		return ccompass.default_target
	end
end

local function teleport_above(playername, target, counter)
	local player = minetest.get_player_by_name(playername)
	if not player then
		return
	end

	for i = (counter or 1), 160 do
		local nodename = minetest.get_node(target).name
		if nodename == "ignore" then
			minetest.emerge_area(target, target)
			minetest.after(0.1, teleport_above, playername, target, i)
			return
		end

		if nodename ~= 'air' then
			target.y = target.y + 1
		else
			break
		end
	end
	player:setpos(target)
	return
end

-- get right image number for players compas
local function get_compass_stack(player, stack)
	local target = get_destination(player, stack)
	local pos = player:getpos()
	local dir = player:get_look_horizontal()
	local angle_north = math.deg(math.atan2(target.x - pos.x, target.z - pos.z))
	if angle_north < 0 then
		angle_north = angle_north + 360
	end
	local angle_dir = math.deg(dir)
	local angle_relative = (angle_north + angle_dir) % 360
	local compass_image = math.floor((angle_relative/22.5) + 0.5)%16

	-- create new stack with metadata copied
	local metadata = stack:get_meta():to_table()

	local newstack = ItemStack("ccompass:"..compass_image)
	if metadata then
		newstack:get_meta():from_table(metadata)
	end
	if ccompass.usage_hook then
		newstack = ccompass.usage_hook(newstack, player) or newstack
	end
	return newstack
end

-- Calibrate compass on pointed_thing
local function on_use_function(itemstack, player, pointed_thing)
	-- possible only on nodes
	if pointed_thing.type ~= "node" then --support nodes only for destination
		minetest.chat_send_player(player:get_player_name(), "Calibration can be done on nodes only")
		return
	end

	local nodepos = minetest.get_pointed_thing_position(pointed_thing)
	local node = minetest.get_node(nodepos)

	-- Do teleport to target
	if ccompass.teleport_nodes[node.name] then
		teleport_above(player:get_player_name(), get_destination(player, itemstack))
		return
	end

	-- recalibration allowed?
	if not ccompass.recalibrate then
		local destination = itemstack:get_meta():get_string("target_pos")
		if destination ~= "" then
			minetest.chat_send_player(player:get_player_name(), "Compass already calibrated")
			return
		end
	end

	-- target nodes restricted?
	if ccompass.restrict_target then
		if not ccompass.restrict_target_nodes[node.name] then
			minetest.chat_send_player(player:get_player_name(), "Calibration on this node not possible")
			return
		end
	end

	-- check if waypoint name set in target node
	local nodepos_string = minetest.pos_to_string(nodepos)
	local nodemeta = minetest.get_meta(nodepos)
	local waypoint_name = nodemeta:get_string("waypoint_name")
	local waypoint_pos = nodemeta:get_string("waypoint_pos")
	local skip_namechange = nodemeta:get_string("waypoint_skip_namechange")
	local itemmeta=itemstack:get_meta()

	if waypoint_pos and waypoint_pos ~= "" then
		nodepos_string = waypoint_pos
	end

	if skip_namechange ~= "" then
		ccompass.set_target(itemstack, {
				target_pos_string = nodepos_string,
				target_name = waypoint_name,
				playername = player:get_player_name()
			})
	else
		-- show the formspec to player
		itemmeta:set_string("tmp_target_pos", nodepos_string) --just save temporary
		minetest.show_formspec(player:get_player_name(), "ccompass",
				"size[10,2.5]" ..
				"field[1,1;8,1;name;Destination name:;"..waypoint_name.."]"..
				"button_exit[0.7,2;3,1;cancel;Cancel]"..
				"button_exit[3.7,2;5,1;ok;Calibrate]" )
	end
	return itemstack
end

-- Process the calibration using entered data
minetest.register_on_player_receive_fields(function(player, formname, fields)
	if formname == "ccompass" and fields.name and (fields.ok or fields.key_enter) then
		local stack=player:get_wielded_item()
		local meta=stack:get_meta()
		ccompass.set_target(stack, {
				target_pos_string = meta:get_string("tmp_target_pos"),
				target_name = fields.name,
				playername = player:get_player_name()
			})
		meta:set_string("tmp_target_pos", "")
		player:set_wielded_item(stack)
	end
end)

-- update inventory
minetest.register_globalstep(function(dtime)
	for i,player in ipairs(minetest.get_connected_players()) do
		if player:get_inventory() then
			for i,stack in ipairs(player:get_inventory():get_list("main")) do
				if i > 8 then
					break
				end
				if string.sub(stack:get_name(), 0, 9) == "ccompass:" then
					player:get_inventory():set_stack("main", i, get_compass_stack(player, stack))
				end
			end
		end
	end
end)

-- register items
for i = 0, 15 do
	local image = "ccompass_16_"..i..".png"
	local groups = {}
	if i > 0 then
		groups.not_in_creative_inventory = 1
	end
	minetest.register_tool("ccompass:"..i, {
		description = "Magic Compass",
		inventory_image = image,
		wield_image = image,
		groups = groups,
		on_use = on_use_function,
	})
end

minetest.register_craft({
	output = 'ccompass:0',
	recipe = {
		{'', 'default:steel_ingot', ''},
		{'default:steel_ingot', 'default:mese', 'default:steel_ingot'},
		{'', 'default:steel_ingot', ''}
	}
})


code:
name = ccompass
description = Adds a calibratable compass to Minetest.