fun_caves/dungeon.lua
2016-08-07 04:51:07 -05:00

606 lines
19 KiB
Lua

local max_depth = 31000
local cells = 10
local border = 10
local cell_size = math.floor((80 - border * 2) / cells)
local cells_y = math.floor(80 / cell_size)
local dead_space = 80 - cells_y * cell_size
local dungeon_depth = -1 -- close to y / 80
local dungeon_noise = {offset = 0, scale = 1, seed = -4082, spread = {x = 7, y = 7, z = 7}, octaves = 4, persist = 1, lacunarity = 2}
if fun_caves.path then
dofile(fun_caves.path .. "/trophies.lua")
dofile(fun_caves.path .. "/tesseract.lua")
end
-- dungeon floor, rooms
newnode = fun_caves.clone_node("default:cobble")
newnode.description = "Dungeon Stone"
newnode.legacy_mineral = false
newnode.groups.dungeon = 1
minetest.register_node("fun_caves:dungeon_floor_1", newnode)
-- dungeon floor, halls
newnode = fun_caves.clone_node("default:cobble")
newnode.description = "Dungeon Stone"
newnode.legacy_mineral = false
newnode.groups.dungeon = 1
newnode.drop = 'default:cobble'
minetest.register_node("fun_caves:dungeon_floor_2", newnode)
-- dungeon walls, basic
newnode = fun_caves.clone_node("default:cobble")
newnode.description = "Dungeon Stone"
newnode.groups.dungeon = 1
newnode.drop = 'default:cobble'
minetest.register_node("fun_caves:dungeon_wall_1", newnode)
-- dungeon walls, type 2
newnode = fun_caves.clone_node("default:cobble")
newnode.description = "Dungeon Stone"
newnode.groups.dungeon = 1
newnode.drop = 'default:cobble'
minetest.register_node("fun_caves:dungeon_wall_2", newnode)
local treasures = {
{ level = 1, rarity = 10, name = 'default:apple 10', },
{ level = 1, rarity = 20, name = 'fun_caves:onion 10', },
{ level = 1, rarity = 120, name = 'default:bronze_ingot 5', },
{ level = 1, rarity = 30, name = 'default:coal_lump 10', },
{ level = 1, rarity = 85, name = 'default:copper_ingot 5', },
{ level = 1, rarity = 155, name = 'default:diamond 5', },
{ level = 1, rarity = 155, name = 'default:gold_ingot 5', },
{ level = 1, rarity = 200, name = 'default:mese_crystal', },
{ level = 1, rarity = 240, name = 'default:obsidian', },
{ level = 1, rarity = 85, name = 'default:obsidian_shard', },
{ level = 1, rarity = 450, name = 'default:pick_diamond', },
{ level = 1, rarity = 600, name = 'default:pick_mese', },
{ level = 1, rarity = 50, name = 'default:steel_ingot 5', },
{ level = 1, rarity = 450, name = 'default:sword_diamond', },
{ level = 1, rarity = 400, name = 'default:sword_mese', },
{ level = 1, rarity = 10, name = 'default:wood 10', },
{ level = 1, rarity = 400, name = 'fun_caves:aquamarine', },
{ level = 1, rarity = 400, name = 'fun_caves:constant_flame', },
{ level = 1, rarity = 400, name = 'fun_caves:coral_gem', },
{ level = 1, rarity = 400, name = 'fun_caves:garnet', },
{ level = 2, rarity = 960, name = 'fun_caves:metallic_ice', },
{ level = 2, rarity = 120, name = 'fun_caves:eternal_ice_crystal', },
{ level = 1, rarity = 480, name = 'fun_caves:moon_glass 5', },
{ level = 1, rarity = 240, name = 'fun_caves:moon_juice 5', },
{ level = 1, rarity = 400, name = 'fun_caves:moonstone', },
{ level = 1, rarity = 5000, name = 'fun_caves:philosophers_stone', },
{ level = 1, rarity = 160, name = 'fun_caves:pure_copper', },
{ level = 2, rarity = 175, name = 'fun_caves:silver_ingot 5', },
{ level = 1, rarity = 960, name = 'fun_caves:sky_iron', },
{ level = 1, rarity = 120, name = 'fun_caves:meteorite', },
{ level = 1, rarity = 5000, name = 'fun_caves:unobtainium', },
}
for name, desc in pairs(minetest.registered_items) do
if name:find('^wool:') then
treasures[#treasures+1] = { level = 1, rarity = 80, name = name..' 2' }
elseif name == 'farming:cotton' then
treasures[#treasures+1] = { level = 1, rarity = 80, name = name..' 10' }
elseif name == 'farming:flour' then
treasures[#treasures+1] = { level = 1, rarity = 80, name = name..' 10' }
end
end
if minetest.registered_entities['mobs_monster:stone_monster'] then
treasures[#treasures+1] = { level = 1, rarity = 400, name = 'mobs_monster:stone_monster', }
end
if minetest.registered_entities['mobs_monster:dungeon_master'] then
treasures[#treasures+1] = { level = 1, rarity = 400, name = 'mobs_monster:dungeon_master', }
end
local chest_formspec =
"size[8,9]" ..
default.gui_bg ..
default.gui_bg_img ..
default.gui_slots ..
"list[current_name;main;0,0.3;8,4;]" ..
"list[current_player;main;0,4.85;8,1;]" ..
"list[current_player;main;0,6.08;8,3;8]" ..
"listring[current_name;main]" ..
"listring[current_player;main]" ..
default.get_hotbar_bg(0,4.85)
local function disintigrate(minp, maxp)
if not (minp and maxp) then
return
end
minp = vector.round(minp)
maxp = vector.round(maxp)
local air = minetest.get_content_id('air')
local vm = minetest.get_voxel_manip()
if not vm then
return
end
local emin, emax = vm:read_from_map(minp, maxp)
local area = VoxelArea:new({MinEdge = emin, MaxEdge = emax})
local data = vm:get_data()
local p = {}
for z = minp.z, maxp.z do
for y = minp.y, maxp.y do
local ivm = area:index(minp.x, y, z)
for x = minp.x, maxp.x do
data[ivm] = air
ivm = ivm + 1
end
end
end
vm:set_data(data)
vm:write_to_map()
vm:update_map()
end
if fun_caves.register_status then
fun_caves.register_status({
name = 'poisoned',
during = function(player)
if not player then
return
end
local player_name = player:get_player_name()
if not player_name or player_name == '' then
return
end
local damage = 1
if fun_caves.db.status and fun_caves.db.status[player_name] and fun_caves.db.status[player_name]['poisoned'] and fun_caves.db.status[player_name]['poisoned']['damage'] then
damage = tonumber(fun_caves.db.status[player_name]['poisoned']['damage'])
end
local hp = player:get_hp()
if hp and type(hp) == 'number' then
hp = hp - damage
player:set_hp(hp)
end
end,
terminate = function(player)
if not player then
return
end
local player_name = player:get_player_name()
minetest.chat_send_player(player_name, 'Your sickness ebbs away.')
end,
})
end
local newnode = fun_caves.clone_node("default:chest")
newnode.description = "Treasure Chest"
newnode.on_construct = nil
newnode.drop = 'default:chest'
newnode.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
if not (pos and clicker) then
return
end
local meta = minetest.get_meta(pos)
if not meta then
return
end
local depth = math.min(20, math.max(1, math.ceil(pos.y / -200)))
local level = math.max(1, math.floor(pos.y / -4900))
local ready = meta:get_string('formspec')
if treasures and ready == '' then
meta:set_string("formspec", '0')
if math.random(10) == 1 and fun_caves.dungeon_spawns and #fun_caves.dungeon_spawns > 0 then
meta:set_string("formspec", '')
minetest.remove_node(pos)
local desc = fun_caves.dungeon_spawns[math.random(#fun_caves.dungeon_spawns)]
if not desc then
return
end
local mpos = table.copy(pos)
mpos.y = mpos.y + 1
local obj = minetest.add_entity(mpos, desc.name)
if not obj then
return
end
local mob = obj:get_luaentity()
if not mob then
return
end
if mob.hp_max and mob.object and mob.health and mob.damage then
local factor = 1 + (math.max(math.abs(pos.x), math.abs(pos.y), math.abs(pos.z)) / 6200)
mob.started_in_dungeon = true
factor = factor * 1.5
mob.hp_max = math.floor(mob.hp_max * factor)
mob.damage = math.floor(mob.damage * factor)
mob.health = math.floor(mob.health * factor)
mob.object:set_hp(mob.health)
mob.initial_promotion = true
check_for_death(mob)
--print('Dungeon quality '..desc.name..': '..mob.health..' health, '..mob.damage..' damage')
end
elseif math.random(20) == 1 then
local player_name = clicker:get_player_name()
if not player_name or player_name == '' or not fun_caves.set_status then
return
end
minetest.chat_send_player(player_name, minetest.colorize('#FF0000', 'You\'ve been poisoned!'))
fun_caves.set_status(player_name, 'poisoned', 2 ^ math.random(8), {damage = 1})
meta:set_string("formspec", '')
return
elseif minetest.get_modpath('tnt') and math.random(20) == 1 then
meta:set_string("formspec", '')
minetest.set_node(pos, {name = 'tnt:tnt_burning'})
local timer = minetest.get_node_timer(pos)
if timer then
timer:start(3)
end
minetest.sound_play("default_dig_crumbly", {pos = pos, gain = 0.5, max_hear_distance = 10})
return
elseif math.random(20) == 1 then
minetest.remove_node(pos)
pos = clicker:getpos()
if not pos then
return
end
disintigrate({x = pos.x - 1, y = pos.y - 12, z = pos.z - 1}, {x = pos.x + 1, y = pos.y - 1, z = pos.z + 1})
return
end
meta:set_string("formspec", chest_formspec)
local inv = meta:get_inventory()
inv:set_size("main", 8*4)
local something
for i = 1, 100 do
for j = 1, 5 * depth do
for _, tre in pairs(treasures) do
if tre.name and tre.level and tre.rarity and tre.level <= level and math.random(tre.rarity) == 1 and inv:room_for_item('main', tre.name) then
inv:add_item('main', tre.name)
something = true
end
end
end
if something then
break
end
end
end
end
minetest.register_node("fun_caves:coffer", newnode)
fun_caves.dungeon = function(minp_in, maxp_in, data, p2data, area, node, heightmap)
if not (minp_in and maxp_in and data and area and node and heightmap and type(data) == 'table' and type(heightmap) == 'table') then
return
end
local n = minetest.get_perlin(dungeon_noise):get2d({x=minp_in.x, y=minp_in.z})
if not (n and type(n) == 'number') then
return
end
if math.floor((n * 10000) % 13) ~= 1 then
return
end
local center_off = cell_size * 4 + border
if minp_in.y > -100 then
local minp, maxp = minp_in, maxp_in
local stair, avg, count = nil, 0, 0
local index = 0
for z = minp.z, maxp.z do
for x = minp.x, maxp.x do
index = index + 1
if not heightmap[index] or type(heightmap[index]) ~= 'number' then
return
end
if x > minp.x + center_off and x < maxp.x - center_off and z > minp.z + center_off and z < maxp.z - center_off and heightmap[index] >= minp.y and heightmap[index] < maxp.y - 2 then
stair = true
avg = avg + heightmap[index]
count = count + 1
end
end
end
avg = math.ceil(avg / count)
if stair then
for z = -1, (cell_size * 2) do
for x = -1, (cell_size * 2) do
local ivm = area:index(x + minp.x + center_off, minp.y - 8, z + minp.z + center_off)
for y = minp.y - 8, avg do
if y > minp.y - 4 and (x == -1 or x == (cell_size * 2) or z == -1 or z == (cell_size * 2)) then
data[ivm] = node['default:cobble']
elseif ((x == 2 or x == 9) and z > 1 and z < 10) or ((z == 2 or z == 9) and x > 1 and x < 10) then
data[ivm] = node['default:cobble']
else
local t
if z < 2 then
t = x
elseif x > 9 then
t = z + 11
elseif z > 9 then
t = 33 - x
else
t = 44 - z
end
t = math.floor(t / 44 * 10 * 2 + 0.5) / 2
if x < 2 or x > 9 or z < 2 or z > 9 then
if math.floor((y - minp.y) % 10 * 2 + 0.5) / 2 == t then
data[ivm] = node['stairs:slab_cobble']
elseif math.floor((y - minp.y) % 10 * 2 + 0.5) / 2 == t - 0.5 then
data[ivm] = node['default:cobble']
else
data[ivm] = node['air']
end
end
end
ivm = ivm + area.ystride
end
end
end
end
return true, true
end
if minp_in.y > 0 then
return
end
local minp = table.copy(minp_in)
minp.x = minp.x + border
minp.z = minp.z + border
local maxp = table.copy(maxp_in)
maxp.x = maxp.x - border
maxp.z = maxp.z - border
center_off = center_off - border
local level = math.min(6, math.ceil(maxp.y / math.floor(max_depth / -6)))
local inner_floor = node['fun_caves:dungeon_floor_1']
local outer_wall = node['fun_caves:dungeon_wall_2']
local inner_wall = node['fun_caves:dungeon_wall_1']
local treasure_count = 0
local leave_alone = {}
leave_alone[node['default:cobble']] = true
leave_alone[node['stairs:stair_cobble']] = true
leave_alone[node['stairs:slab_cobble']] = true
leave_alone['stairway'] = true
for z = minp.z - 1, maxp.z + 1 do
for y = minp.y, maxp.y + 1 do
local ivm = area:index(minp.x - 1, y, z)
for x = minp.x - 1, maxp.x + 1 do
local centered_out = (z >= minp.z + center_off - 1 and z <= maxp.z - center_off + 1 and x >= minp.x + center_off - 1 and x <= maxp.x - center_off + 1)
local centered_in = (z >= minp.z + center_off and z <= maxp.z - center_off and x >= minp.x + center_off and x <= maxp.x - center_off)
if y > maxp.y - dead_space then
if centered_out and (x == minp.x + center_off - 1 or x == maxp.x - center_off + 1 or z == minp.z + center_off - 1 or z == maxp.z - center_off + 1) then
data[ivm] = node['fun_caves:dungeon_wall_1']
elseif not centered_out and y <= maxp.y then
data[ivm] = node['default:stone']
elseif y <= maxp.y and not leave_alone[data[ivm]] then
data[ivm] = node['air']
end
elseif x == minp.x - 1 or x == maxp.x + 1 or z == minp.z - 1 or z == maxp.z + 1 then
data[ivm] = node['fun_caves:dungeon_wall_1']
elseif (y == minp.y or y == maxp.y - dead_space) and not centered_in then
data[ivm] = node['fun_caves:dungeon_floor_1']
end
ivm = ivm + 1
end
end
end
local content = {}
for cx = 0, (cells - 1) do
content[cx] = {}
for cy = 0, (cells_y - 1) do
content[cx][cy] = {}
for cz = 0, (cells - 1) do
if cy == (cells_y - 1) and (cz == 4 or cz == 5) and (cx == 4 or cx == 5) then
content[cx][cy][cz] = 'room'
elseif cy == 0 and (cz == 4 or cz == 5) and (cx == 4 or cx == 5) then
content[cx][cy][cz] = 'room'
else
content[cx][cy][cz] = '4way'
end
end
end
end
for cy = 0, (cells_y - 2) do
local x, z = math.random(1,8), math.random(0,(cells - 1))
while cy == (cells_y - 1) and (z == 4 or z == 5) and (x == 4 or x == 5) do
x, z = math.random(1,8), math.random(0,(cells - 1))
end
content[x][cy][z] = 'stair1'
content[x - 1][cy][z] = 'room'
if cy < (cells_y - 1) then
content[x][cy + 1][z] = 'room'
content[x + 1][cy + 1][z] = 'room'
end
x, z = math.random(0,(cells - 1)), math.random(1,8)
while cy == (cells_y - 1) and (z == 4 or z == 5) and (x == 4 or x == 5) do
x, z = math.random(0,(cells - 1)), math.random(1,8)
end
content[x][cy][z] = 'stair0'
content[x][cy][z - 1] = 'room'
if cy < (cells_y - 1) then
content[x][cy + 1][z] = 'room'
content[x][cy + 1][z + 1] = 'room'
end
end
for cx = 0, (cells - 1) do
for cy = 0, (cells_y - 1) do
for cz = 0, (cells - 1) do
if content[cx][cy][cz] == '4way' and math.random(2) == 1 then
content[cx][cy][cz] = 'room'
end
end
end
end
for cz = 0, (cells - 1) do
local oz = minp.z + cz * cell_size
for cy = 0, (cells_y - 1) do
local oy = minp.y + cy * cell_size
for cx = 0, (cells - 1) do
local ox = minp.x + cx * cell_size
local centered_in = ((cx == 4 or cx == 5) and (cz == 4 or cz == 5))
if content[cx][cy][cz] == 'stair0' then
for rz = 0, cell_size do
for ry = 0, 9 do
local ivm = area:index(ox, oy + ry, oz + rz)
for rx = 0, (cell_size - 1) do
if ry == rz + 1 and (rx == 2 or rx == 3) and (cy == (cells_y - 1) or rz < cell_size) then
data[ivm] = node['stairs:stair_cobble']
p2data[ivm] = 0
elseif (ry >= rz + 1 and ry <= rz + 5) and (rx == 2 or rx == 3) then
data[ivm] = 'stairway'
elseif ry == rz and (rx == 2 or rx == 3) then
data[ivm] = node['fun_caves:dungeon_floor_1']
elseif rz < cell_size and ry == 0 then
data[ivm] = node['fun_caves:dungeon_floor_1']
elseif ry < cell_size and rz < cell_size then
data[ivm] = node['fun_caves:dungeon_wall_1']
end
ivm = ivm + 1
end
end
end
elseif content[cx][cy][cz] == 'stair1' then
for rz = 0, (cell_size - 1) do
for ry = 0, 9 do
local ivm = area:index(ox, oy + ry, oz + rz)
for rx = 0, cell_size do
if ry == rx + 1 and (rz == 2 or rz == 3) and (cy == (cells_y - 1) or rx < cell_size) then
data[ivm] = node['stairs:stair_cobble']
p2data[ivm] = 1
elseif (ry >= rx + 1 and ry <= rx + 5) and (rz == 2 or rz == 3) then
data[ivm] = 'stairway'
elseif ry == rx and (rz == 2 or rz == 3) then
data[ivm] = node['fun_caves:dungeon_floor_1']
elseif rx < cell_size and ry == 0 then
data[ivm] = node['fun_caves:dungeon_floor_1']
elseif ry < cell_size and rx < cell_size then
data[ivm] = node['fun_caves:dungeon_wall_1']
end
ivm = ivm + 1
end
end
end
else
for rz = 0, (cell_size - 1) do
for ry = 0, (cell_size - 1) do
local ivm = area:index(ox, oy + ry, oz + rz)
for rx = 0, (cell_size - 1) do
if not leave_alone[data[ivm]] then
if ry == 0 and (cy > 0 or not centered_in) then
if content[cx][cy][cz] == 'room' then
local r = math.random(1000)
if r == 1 then
data[ivm] = node['fun_caves:stone_with_gold_trap']
elseif r == 2 then
data[ivm] = node['fun_caves:stone_with_coal_trap']
elseif r == 3 then
data[ivm] = node['fun_caves:stone_with_iron_trap']
elseif r == 4 then
data[ivm] = node['fun_caves:stone_with_diamond_trap']
else
data[ivm] = node['fun_caves:dungeon_floor_1']
end
else
data[ivm] = node['fun_caves:dungeon_floor_2']
end
elseif ry == (cell_size - 1) and (cy < (cells_y - 1) or not centered_in) then
data[ivm] = node['fun_caves:dungeon_floor_2']
elseif content[cx][cy][cz] == 'room' then
if ry == 1 and math.random(250) == 1 and data[ivm - area.ystride] == node['fun_caves:dungeon_floor_1'] then
data[ivm] = node['fun_caves:coffer']
else
data[ivm] = node['air']
end
elseif content[cx][cy][cz] == '4way' and ry <= 2 and (rz == 2 or rz == 3 or rx == 2 or rx == 3) and (ox + rx >= minp.x + 2 and ox + rx <= maxp.x - 2 and oz + rz >= minp.z + 2 and oz + rz <= maxp.z - 2) then
data[ivm] = node['air']
elseif ry > 0 and content[cx][cy][cz] == '4way' then
data[ivm] = node['fun_caves:dungeon_wall_1']
end
end
ivm = ivm + 1
end
end
end
end
end
end
end
for z = minp.z + 1, maxp.z - 1 do
for y = minp.y + 1, maxp.y - 1 do
local ivm = area:index(minp.x + 1, y, z)
for x = minp.x + 1, maxp.x - 1 do
local height = (y - minp.y) % cell_size
if data[ivm] == node['fun_caves:dungeon_wall_1'] and (x - minp.x) % 2 == 0 and (z - minp.z) % 2 == 0 and (height == 4 or (height == 2 and math.random(5) == 1)) then
local dir = math.random(4)
local ivm2 = ivm
local p2 = 3
if dir == 1 then
ivm2 = ivm2 + 1
elseif dir == 2 then
ivm2 = ivm2 - 1
p2 = 2
elseif dir == 3 then
ivm2 = ivm2 + area.zstride
p2 = 5
elseif dir == 4 then
ivm2 = ivm2 - area.zstride
p2 = 4
end
if data[ivm2] == node['air'] then
data[ivm2] = node['default:torch']
p2data[ivm2] = p2
end
end
ivm = ivm + 1
end
end
end
for i = 0, #data do
if data[i] == 'stairway' then
data[i] = node['air']
end
end
return true, true, true
end