mods that hopefully work for a TechAge-based pack
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
Ancient Bones
|
||||
=====
|
||||
Adds randomly generated bones with some "loot" to generated dungeons.
|
||||
Only works if dungeons are actually enabled in mapgen flags.
|
||||
|
||||
Based on the vanilla minetest game's dungeon_loot mod, with the bones textures
|
||||
from the bones mod.
|
||||
The pool of possible loot has more tools than the normal chests.
|
||||
|
||||
License information can be found in license.txt
|
||||
|
||||
Authors of source code
|
||||
----------------------
|
||||
Original dungeon_loot mod by sfan5 (MIT)
|
||||
Modified by karlexceed
|
||||
|
||||
Authors of media (textures)
|
||||
---------------------------
|
||||
All textures: paramat (CC BY-SA 3.0)
|
||||
@@ -0,0 +1,85 @@
|
||||
-- ancient_bones/ancient_bones.lua
|
||||
|
||||
-- Load support for MT game translation.
|
||||
local S = core.get_translator("ancient_bones")
|
||||
|
||||
ancient_bones_object = {}
|
||||
|
||||
local bones_formspec =
|
||||
"size[8,9]" ..
|
||||
"list[current_name;main;0,0.3;8,4;]" ..
|
||||
"list[current_player;main;0,4.85;8,1;]" ..
|
||||
"list[current_player;main;0,6.08;8,3;8]" ..
|
||||
"listring[current_name;main]" ..
|
||||
"listring[current_player;main]" ..
|
||||
default.get_hotbar_bg(0,4.85)
|
||||
|
||||
local bones_def = {
|
||||
description = S("Ancient Bones"),
|
||||
tiles = {
|
||||
"bones_top.png^[transform2",
|
||||
"bones_bottom.png",
|
||||
"bones_side.png",
|
||||
"bones_side.png",
|
||||
"bones_rear.png",
|
||||
"bones_front.png"
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
groups = {dig_immediate = 2},
|
||||
sounds = default.node_sound_gravel_defaults(),
|
||||
|
||||
on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
local meta = core.get_meta(pos)
|
||||
if meta:get_inventory():is_empty("main") then
|
||||
local inv = player:get_inventory()
|
||||
if inv:room_for_item("main", {name = "bones:bones"}) then
|
||||
inv:add_item("main", {name = "bones:bones"})
|
||||
else
|
||||
core.add_item(pos, "bones:bones")
|
||||
end
|
||||
core.remove_node(pos)
|
||||
end
|
||||
end,
|
||||
|
||||
on_punch = function(pos, node, player)
|
||||
local inv = core.get_meta(pos):get_inventory()
|
||||
local player_inv = player:get_inventory()
|
||||
local has_space = true
|
||||
|
||||
for i = 1, inv:get_size("main") do
|
||||
local stk = inv:get_stack("main", i)
|
||||
if player_inv:room_for_item("main", stk) then
|
||||
inv:set_stack("main", i, nil)
|
||||
player_inv:add_item("main", stk)
|
||||
else
|
||||
has_space = false
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- remove bones if player emptied them
|
||||
if has_space then
|
||||
if player_inv:room_for_item("main", {name = "bones:bones"}) then
|
||||
player_inv:add_item("main", {name = "bones:bones"})
|
||||
else
|
||||
core.add_item(pos,"bones:bones")
|
||||
end
|
||||
core.remove_node(pos)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
function ancient_bones_object.register_ancient_bones(d)
|
||||
local def = table.copy(d)
|
||||
def.on_construct = function(pos)
|
||||
local meta = core.get_meta(pos)
|
||||
meta:set_string("infotext", S("Ancient Bones"))
|
||||
meta:set_string("formspec", bones_formspec)
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("main", 8*4)
|
||||
end
|
||||
|
||||
core.register_node("ancient_bones:ancient_bones", def)
|
||||
end
|
||||
|
||||
ancient_bones_object.register_ancient_bones(bones_def)
|
||||
@@ -0,0 +1,9 @@
|
||||
ancient_bones = {}
|
||||
|
||||
|
||||
ancient_bones.modpath = core.get_modpath("ancient_bones")
|
||||
dofile(ancient_bones.modpath .. "/settings.lua")
|
||||
dofile(ancient_bones.modpath .. "/ancient_bones.lua")
|
||||
|
||||
dofile(ancient_bones.modpath .. "/loot.lua")
|
||||
dofile(ancient_bones.modpath .. "/mapgen.lua")
|
||||
@@ -0,0 +1,9 @@
|
||||
The code is licensed under MIT License (MIT): https://spdx.org/licenses/MIT.html
|
||||
|
||||
Based on original code from:
|
||||
dungeon_loot by sfan5 (MIT)
|
||||
bones by PilzAdam and Various Minetest developers and contributors (MIT)
|
||||
|
||||
The art is licensed under CC-BY-SA-3.0: https://spdx.org/licenses/CC-BY-SA-3.0.html
|
||||
|
||||
The textures are copied from the bones mod, made by paramat (CC BY-SA 3.0)
|
||||
@@ -0,0 +1,66 @@
|
||||
-- Loot from the `default` mod is registered here,
|
||||
-- with the rest being registered in the respective mods
|
||||
|
||||
ancient_bones.registered_loot = {
|
||||
-- various items
|
||||
{name = "default:stick", chance = 0.6, count = {3, 8}},
|
||||
{name = "default:flint", chance = 0.4, count = {1, 3}},
|
||||
|
||||
-- farming / consumable
|
||||
{name = "default:apple", chance = 0.4, count = {1, 4}},
|
||||
{name = "default:cactus", chance = 0.4, count = {1, 4},
|
||||
types = {"sandstone", "desert"}},
|
||||
|
||||
-- minerals
|
||||
{name = "default:coal_lump", chance = 0.9, count = {1, 12}},
|
||||
{name = "default:gold_ingot", chance = 0.5},
|
||||
{name = "default:steel_ingot", chance = 0.4, count = {1, 6}},
|
||||
{name = "default:mese_crystal", chance = 0.1, count = {2, 3}},
|
||||
|
||||
-- tools
|
||||
{name = "default:sword_wood", chance = 0.6},
|
||||
{name = "default:sword_steel", chance = 0.3},
|
||||
{name = "default:sword_diamond", chance = 0.05},
|
||||
{name = "default:pick_stone", chance = 0.4},
|
||||
{name = "default:pick_steel", chance = 0.2},
|
||||
{name = "default:pick_diamond", chance = 0.05},
|
||||
{name = "default:axe_stone", chance = 0.4},
|
||||
{name = "default:axe_steel", chance = 0.2},
|
||||
{name = "default:axe_diamond", chance = 0.05},
|
||||
|
||||
-- natural materials
|
||||
{name = "default:sand", chance = 0.8, count = {4, 32}, y = {-64, 32768},
|
||||
types = {"normal"}},
|
||||
{name = "default:desert_sand", chance = 0.8, count = {4, 32}, y = {-64, 32768},
|
||||
types = {"sandstone"}},
|
||||
{name = "default:desert_cobble", chance = 0.8, count = {4, 32},
|
||||
types = {"desert"}},
|
||||
{name = "default:snow", chance = 0.8, count = {8, 64}, y = {-64, 32768},
|
||||
types = {"ice"}},
|
||||
{name = "default:dirt", chance = 0.6, count = {2, 16}, y = {-64, 32768},
|
||||
types = {"normal", "sandstone", "desert"}},
|
||||
{name = "default:obsidian", chance = 0.25, count = {1, 3}, y = {-32768, -512}},
|
||||
{name = "default:mese", chance = 0.15, y = {-32768, -512}},
|
||||
}
|
||||
|
||||
function ancient_bones.register(t)
|
||||
if t.name ~= nil then
|
||||
t = {t} -- single entry
|
||||
end
|
||||
for _, loot in ipairs(t) do
|
||||
table.insert(ancient_bones.registered_loot, loot)
|
||||
end
|
||||
end
|
||||
|
||||
function ancient_bones._internal_get_loot(pos_y, dungeontype)
|
||||
-- filter by y pos and type
|
||||
local ret = {}
|
||||
for _, l in ipairs(ancient_bones.registered_loot) do
|
||||
if l.y == nil or (pos_y >= l.y[1] and pos_y <= l.y[2]) then
|
||||
if l.types == nil or table.indexof(l.types, dungeontype) ~= -1 then
|
||||
table.insert(ret, l)
|
||||
end
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
@@ -0,0 +1,173 @@
|
||||
core.set_gen_notify({dungeon = true, temple = true})
|
||||
|
||||
local function noise3d_integer(noise, pos)
|
||||
return math.abs(math.floor(noise:get_3d(pos) * 0x7fffffff))
|
||||
end
|
||||
|
||||
local function random_sample(rand, list, count)
|
||||
local ret = {}
|
||||
for n = 1, count do
|
||||
local idx = rand:next(1, #list)
|
||||
table.insert(ret, list[idx])
|
||||
table.remove(list, idx)
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local function find_walls(cpos, is_temple)
|
||||
local is_wall = function(node)
|
||||
return node.name ~= "air" and node.name ~= "ignore"
|
||||
end
|
||||
|
||||
local dirs = {{x=1, z=0}, {x=-1, z=0}, {x=0, z=1}, {x=0, z=-1}}
|
||||
local get_node = core.get_node
|
||||
|
||||
local ret = {}
|
||||
local mindist = {x=0, z=0}
|
||||
local min = function(a, b) return a ~= 0 and math.min(a, b) or b end
|
||||
for _, dir in ipairs(dirs) do
|
||||
for i = 1, 9 do -- 9 = max room size / 2
|
||||
local pos = vector.add(cpos, {x=dir.x*i, y=0, z=dir.z*i})
|
||||
|
||||
-- continue in that direction until we find a wall-like node
|
||||
local node = get_node(pos)
|
||||
if is_wall(node) then
|
||||
local front_below = vector.subtract(pos, {x=dir.x, y=1, z=dir.z})
|
||||
local above = vector.add(pos, {x=0, y=1, z=0})
|
||||
|
||||
-- check that it:
|
||||
--- is at least 2 nodes high (not a staircase)
|
||||
--- has a floor
|
||||
if is_wall(get_node(front_below)) and is_wall(get_node(above)) then
|
||||
table.insert(ret, {pos = pos, facing = {x=-dir.x, y=0, z=-dir.z}})
|
||||
if dir.z == 0 then
|
||||
mindist.x = min(mindist.x, i-1)
|
||||
else
|
||||
mindist.z = min(mindist.z, i-1)
|
||||
end
|
||||
end
|
||||
-- abort even if it wasn't a wall cause something is in the way
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local biome = core.get_biome_data(cpos)
|
||||
biome = biome and core.get_biome_name(biome.biome) or ""
|
||||
local type = "normal"
|
||||
if is_temple or biome:find("desert") == 1 then
|
||||
type = "desert"
|
||||
elseif biome:find("sandstone_desert") == 1 then
|
||||
type = "sandstone"
|
||||
elseif biome:find("icesheet") == 1 then
|
||||
type = "ice"
|
||||
end
|
||||
|
||||
return {
|
||||
walls = ret,
|
||||
size = {x=mindist.x*2, z=mindist.z*2},
|
||||
type = type,
|
||||
}
|
||||
end
|
||||
|
||||
local function populate_bones(pos, rand, dungeontype)
|
||||
--core.chat_send_all("bones placed at " .. core.pos_to_string(pos) .. " [" .. dungeontype .. "]")
|
||||
--core.add_node(vector.add(pos, {x=0, y=1, z=0}), {name="default:torch", param2=1})
|
||||
|
||||
local item_list = ancient_bones._internal_get_loot(pos.y, dungeontype)
|
||||
-- take random (partial) sample of all possible items
|
||||
local sample_n = math.min(#item_list, ancient_bones.config.stacks_per_bones)
|
||||
item_list = random_sample(rand, item_list, sample_n)
|
||||
|
||||
-- apply chances / randomized amounts and collect resulting items
|
||||
local items = {}
|
||||
for _, loot in ipairs(item_list) do
|
||||
if rand:next(0, 1000) / 1000 <= loot.chance then
|
||||
local itemdef = core.registered_items[loot.name]
|
||||
local amount = 1
|
||||
if loot.count ~= nil then
|
||||
amount = rand:next(loot.count[1], loot.count[2])
|
||||
end
|
||||
|
||||
if not itemdef then
|
||||
core.log("warning", "Registered loot item " .. loot.name .. " does not exist")
|
||||
elseif itemdef.tool_capabilities then
|
||||
for n = 1, amount do
|
||||
local wear = rand:next(0.20 * 65535, 0.75 * 65535) -- 20% to 75% wear
|
||||
table.insert(items, ItemStack({name = loot.name, wear = wear}))
|
||||
end
|
||||
elseif itemdef.stack_max == 1 then
|
||||
-- not stackable, add separately
|
||||
for n = 1, amount do
|
||||
table.insert(items, loot.name)
|
||||
end
|
||||
else
|
||||
table.insert(items, ItemStack({name = loot.name, count = amount}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- place items at random places in bones
|
||||
local inv = core.get_meta(pos):get_inventory()
|
||||
local listsz = inv:get_size("main")
|
||||
assert(listsz >= #items)
|
||||
for _, item in ipairs(items) do
|
||||
local index = rand:next(1, listsz)
|
||||
if inv:get_stack("main", index):is_empty() then
|
||||
inv:set_stack("main", index, item)
|
||||
else
|
||||
inv:add_item("main", item) -- space occupied, just put it anywhere
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
core.register_on_generated(function(minp, maxp, blockseed)
|
||||
local gennotify = core.get_mapgen_object("gennotify")
|
||||
local poslist = gennotify["dungeon"] or {}
|
||||
local n_dungeons = #poslist
|
||||
-- Add MGv6 desert temples to the list too
|
||||
for _, entry in ipairs(gennotify["temple"] or {}) do
|
||||
table.insert(poslist, entry)
|
||||
end
|
||||
if #poslist == 0 then return end
|
||||
|
||||
local noise = core.get_perlin(10115, 4, 0.5, 1)
|
||||
local rand = PcgRandom(noise3d_integer(noise, poslist[1]))
|
||||
|
||||
local candidates = {}
|
||||
-- process at most 8 rooms to keep runtime of this predictable
|
||||
local num_process = math.min(#poslist, 8)
|
||||
for i = 1, num_process do
|
||||
local room = find_walls(poslist[i], i > n_dungeons)
|
||||
-- skip small rooms and everything that doesn't at least have 3 walls
|
||||
if math.min(room.size.x, room.size.z) >= 4 and #room.walls >= 3 then
|
||||
table.insert(candidates, room)
|
||||
end
|
||||
end
|
||||
|
||||
local num_bones = rand:next(ancient_bones.config.min_bones, ancient_bones.config.max_bones)
|
||||
num_bones = math.min(#candidates, num_bones)
|
||||
local rooms = random_sample(rand, candidates, num_bones)
|
||||
|
||||
for _, room in ipairs(rooms) do
|
||||
-- choose place somewhere in front of any of the walls
|
||||
local wall = room.walls[rand:next(1, #room.walls)]
|
||||
local v, vi -- vector / axis that runs alongside the wall
|
||||
if wall.facing.x ~= 0 then
|
||||
v, vi = {x=0, y=0, z=1}, "z"
|
||||
else
|
||||
v, vi = {x=1, y=0, z=0}, "x"
|
||||
end
|
||||
local bonespos = vector.add(wall.pos, wall.facing)
|
||||
local off = rand:next(-room.size[vi]/2 + 1, room.size[vi]/2 - 1)
|
||||
bonespos = vector.add(bonespos, vector.multiply(v, off))
|
||||
|
||||
if core.get_node(bonespos).name == "air" then
|
||||
-- make it face inwards to the room
|
||||
local facedir = core.dir_to_facedir(vector.multiply(wall.facing, -1))
|
||||
core.add_node(bonespos, {name = "ancient_bones:ancient_bones", param2 = facedir})
|
||||
populate_bones(bonespos, PcgRandom(noise3d_integer(noise, bonespos)), room.type)
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,5 @@
|
||||
name = ancient_bones
|
||||
description = Adds bones with loot to dungeons
|
||||
depends = default, bones
|
||||
title = Ancient Bones
|
||||
author = karlexceed
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 233 KiB |
@@ -0,0 +1,26 @@
|
||||
ancient_bones.config = {}
|
||||
|
||||
local function setting(name, tp, default)
|
||||
local full_name = "ancient_bones." .. name
|
||||
local value
|
||||
|
||||
value = tonumber(core.settings:get(full_name))
|
||||
local v, other = default:match("^(%S+) (.+)")
|
||||
default = value == nil and tonumber(other and v or default)
|
||||
|
||||
if value == nil then
|
||||
value = default
|
||||
assert(default ~= nil, "Cannot parse default for " .. full_name)
|
||||
end
|
||||
|
||||
ancient_bones.config[name] = value
|
||||
end
|
||||
|
||||
local file = io.open(ancient_bones.modpath .. "/settingtypes.txt", "r")
|
||||
for line in file:lines() do
|
||||
local name, tp, value = line:match("^ancient_bones%.(%S+) %(.*%) (%S+) (.*)")
|
||||
if value then
|
||||
setting(name, tp, value)
|
||||
end
|
||||
end
|
||||
file:close()
|
||||
@@ -0,0 +1,8 @@
|
||||
# Minimum bones: The minimum number of bones per mapgen chunk
|
||||
ancient_bones.min_bones (Minimum bones count) int 0
|
||||
|
||||
# Maximum bones: The maximum number of bones per mapgen chunk
|
||||
ancient_bones.max_bones (Maximum bones count) int 2
|
||||
|
||||
# Maximum loot stacks: The maximum number of loot stacks per bones (32 max)
|
||||
ancient_bones.stacks_per_bones (Maximum loot stacks count) int 8
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 349 B |
Binary file not shown.
|
After Width: | Height: | Size: 339 B |
Binary file not shown.
|
After Width: | Height: | Size: 341 B |
Binary file not shown.
|
After Width: | Height: | Size: 338 B |
Binary file not shown.
|
After Width: | Height: | Size: 333 B |
Reference in New Issue
Block a user