311 lines
9.3 KiB
Lua
311 lines
9.3 KiB
Lua
local MODPATH = minetest.get_modpath(minetest.get_current_modname())
|
|
local BUILDER_REQ_MATERIALS = minetest.setting_getbool("creative_mode") == false
|
|
local MAX_SPEED = 5
|
|
local MAX_POS = 1000
|
|
local DEFAULT_NODE = {name="air"}
|
|
local SCHEMS = {"basic_hut.we"}
|
|
local INSTABUILD_PATH = minetest.get_modpath("instabuild")
|
|
if INSTABUILD_PATH then
|
|
for _,v in ipairs({"factory.we", "large_warehouse.we", "small_farm.we", "tall_tower.we",
|
|
"large_farm.we", "mansion.we", "small_house.we", "large_house.we", "modern_house.we",
|
|
"small_hut.we", "large_hut.we", "short_tower.we", "small_warehouse.we"}) do
|
|
table.insert(SCHEMS, v)
|
|
end
|
|
end
|
|
|
|
local function reset_build(self)
|
|
self.var.nodedata = {}
|
|
self.var.nodelist = {}
|
|
self.metadata.index = nil
|
|
self.metadata.schematic = nil
|
|
self.metadata.build_pos = nil
|
|
self.metadata.building = false
|
|
end
|
|
|
|
local function get_registered_nodename(name)
|
|
if string.find(name, "^doors") then
|
|
name = name:gsub("_[tb]_[12]", "")
|
|
elseif string.find(name, "^stairs") then
|
|
name = name:gsub("upside_down", "")
|
|
elseif string.find(name, "^farming") then
|
|
name = name:gsub("_%d", "")
|
|
end
|
|
return name
|
|
end
|
|
|
|
local function load_schematic(self, filename)
|
|
local input = nil
|
|
if INSTABUILD_PATH then
|
|
input = io.open(INSTABUILD_PATH.."/models/"..filename, "r")
|
|
end
|
|
if not input then
|
|
input = io.open(MODPATH.."/schems/"..filename, "r")
|
|
end
|
|
if input then
|
|
local data = minetest.deserialize(input:read('*all'))
|
|
io.close(input)
|
|
table.sort(data, function(a,b)
|
|
if a.y == b.y then
|
|
if a.z == b.z then
|
|
return a.x > b.x
|
|
end
|
|
return a.z > b.z
|
|
end
|
|
return a.y > b.y
|
|
end)
|
|
local sorted = {}
|
|
local pos = {x=0, y=0, z=0}
|
|
while #data > 0 do
|
|
local index = 1
|
|
local min_pos = {x=MAX_POS, y=MAX_POS, z=MAX_POS}
|
|
for i,v in ipairs(data) do
|
|
if v.y < min_pos.y or vector.distance(pos, v) < vector.distance(pos, min_pos) then
|
|
min_pos = v
|
|
index = i
|
|
end
|
|
end
|
|
local node = data[index]
|
|
table.insert(sorted, node)
|
|
table.remove(data, index)
|
|
pos = {x=node.x, y=node.y, z=node.z}
|
|
end
|
|
self.var.nodedata = {}
|
|
self.var.nodelist = {}
|
|
for i,v in ipairs(sorted) do
|
|
if v.name and v.param1 and v.param2 and v.x and v.y and v.z then
|
|
local node = {name=v.name, param1=v.param1, param2=v.param2}
|
|
local pos = vector.add(self.metadata.build_pos, {x=v.x, y=v.y, z=v.z})
|
|
local name = get_registered_nodename(v.name)
|
|
if minetest.registered_items[name] then
|
|
self.metadata.inventory[name] = self.metadata.inventory[name] or 0
|
|
self.var.nodelist[name] = self.var.nodelist[name] or 0
|
|
self.var.nodelist[name] = self.var.nodelist[name] + 1
|
|
else
|
|
node = DEFAULT_NODE
|
|
end
|
|
self.var.nodedata[i] = {pos=pos, node=node}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function show_build_form(self, player_name)
|
|
local nodelist = {}
|
|
for k,v in pairs(self.var.nodelist) do
|
|
if string.find(k, "^doors") then
|
|
v = v * 0.5
|
|
end
|
|
if self.metadata.inventory[k] then
|
|
v = v - self.metadata.inventory[k]
|
|
end
|
|
if v < 0 then
|
|
v = 0
|
|
end
|
|
if minetest.registered_items[k].description ~= "" then
|
|
k = minetest.registered_items[k].description
|
|
end
|
|
table.insert(nodelist, k.." ("..v..")")
|
|
end
|
|
local materials = table.concat(nodelist, ",") or ""
|
|
local title = self.metadata.schematic:gsub("%.we","")
|
|
local button_build = "button_exit[5.0,1.0;3.0,0.5;build_start;Begin Build]"
|
|
if self.metadata.index then
|
|
button_build = "button_exit[5.0,1.0;3.0,0.5;build_resume;Resume Build]"
|
|
end
|
|
local formspec = "size[8,9]"
|
|
.."label[3.0,0.0;Project: "..title.."]"
|
|
.."textlist[0.0,1.0;4.0,3.5;inv_sel;"..materials..";"..self.var.selected..";]"
|
|
.."list[current_player;main;0.0,5.0;8.0,4.0;]"
|
|
..button_build
|
|
if BUILDER_REQ_MATERIALS == true then
|
|
formspec = formspec.."list[detached:npcf_"..self.npc_id..";input;6.0,3.5;1,1;]"
|
|
end
|
|
if self.owner == player_name then
|
|
formspec = formspec.."button_exit[5.0,2.0;3.0,0.5;build_cancel;Cancel Build]"
|
|
end
|
|
npcf:show_formspec(player_name, self.npc_id, formspec)
|
|
end
|
|
|
|
local function get_speed(distance)
|
|
local speed = distance * 0.5
|
|
if speed > MAX_SPEED then
|
|
speed = MAX_SPEED
|
|
end
|
|
return speed
|
|
end
|
|
|
|
npcf:register_npc("npcf_builder:npc" ,{
|
|
description = "Builder NPC",
|
|
textures = {"npcf_builder_skin.png"},
|
|
metadata = {
|
|
schematic = nil,
|
|
inventory = {},
|
|
index = nil,
|
|
build_pos = nil,
|
|
building = false,
|
|
},
|
|
var = {
|
|
selected = "",
|
|
nodelist = {},
|
|
nodedata = {},
|
|
last_pos = {},
|
|
},
|
|
stepheight = 1.1,
|
|
inventory_image = "npcf_builder_inv.png",
|
|
on_construct = function(self)
|
|
self.metadata.building = false
|
|
self.object:setvelocity({x=0, y=0, z=0})
|
|
self.object:setacceleration({x=0, y=-10, z=0})
|
|
if self.metadata.schematic and self.metadata.build_pos then
|
|
load_schematic(self, self.metadata.schematic)
|
|
end
|
|
end,
|
|
on_activate = function(self)
|
|
local inv = minetest.create_detached_inventory("npcf_"..self.npc_id, {
|
|
on_put = function(inv, listname, index, stack, player)
|
|
local player_name = player:get_player_name()
|
|
local item = stack:get_name()
|
|
if player_name and self.metadata.inventory[item] then
|
|
self.metadata.inventory[item] = self.metadata.inventory[item] + stack:get_count()
|
|
inv:remove_item("input", stack)
|
|
show_build_form(self, player_name)
|
|
end
|
|
end,
|
|
})
|
|
inv:set_size("input", 1)
|
|
end,
|
|
on_rightclick = function(self, clicker)
|
|
local player_name = clicker:get_player_name()
|
|
if self.owner == player_name then
|
|
if not self.metadata.schematic then
|
|
local schemlist = table.concat(SCHEMS, ",") or ""
|
|
local formspec = "size[6,5]"
|
|
.."textlist[0.0,0.0;5.0,4.0;schemlist;"..schemlist..";;]"
|
|
.."button_exit[5.0,4.5;1.0,0.5;;Ok]"
|
|
npcf:show_formspec(player_name, self.npc_id, formspec)
|
|
return
|
|
elseif self.metadata.building == true then
|
|
self.metadata.building = false
|
|
return
|
|
end
|
|
end
|
|
if self.metadata.schematic and self.metadata.building == false then
|
|
show_build_form(self, player_name)
|
|
end
|
|
end,
|
|
on_step = function(self, dtime)
|
|
if self.timer > 1 then
|
|
self.timer = 0
|
|
if not self.owner then
|
|
return
|
|
end
|
|
local pos = self.object:getpos()
|
|
local yaw = self.object:getyaw()
|
|
local state = NPCF_ANIM_STAND
|
|
local speed = 0
|
|
if self.metadata.building == true then
|
|
local nodedata = self.var.nodedata[self.metadata.index]
|
|
pos.y = math.floor(pos.y)
|
|
local acceleration = {x=0, y=-10, z=0}
|
|
if pos.y < nodedata.pos.y then
|
|
if self.object:getacceleration().y > 0 and self.var.last_pos.y == pos.y then
|
|
self.object:setpos({x=pos.x, y=nodedata.pos.y + 1.5, z=pos.z})
|
|
acceleration = {x=0, y=0, z=0}
|
|
else
|
|
acceleration = {x=0, y=0.1, z=0}
|
|
end
|
|
end
|
|
self.var.last_pos = pos
|
|
self.object:setacceleration(acceleration)
|
|
yaw = npcf:get_face_direction(pos, nodedata.pos)
|
|
local distance = vector.distance(pos, nodedata.pos)
|
|
if distance < 4 then
|
|
if minetest.registered_items[nodedata.node.name].sounds then
|
|
local soundspec = minetest.registered_items[nodedata.node.name].sounds.place
|
|
if soundspec then
|
|
soundspec.pos = pos
|
|
minetest.sound_play(soundspec.name, soundspec)
|
|
end
|
|
end
|
|
minetest.add_node(nodedata.pos, nodedata.node)
|
|
local door_top = string.find(nodedata.node.name, "^doors+_t_[12]$")
|
|
if BUILDER_REQ_MATERIALS == true and not door_top then
|
|
local name = get_registered_nodename(nodedata.node.name)
|
|
if self.metadata.inventory[name] > 0 then
|
|
self.metadata.inventory[name] = self.metadata.inventory[name] - 1
|
|
self.var.selected = ""
|
|
else
|
|
self.metadata.building = false
|
|
state = NPCF_ANIM_STAND
|
|
speed = 0
|
|
local i = 0
|
|
for k,v in pairs(self.var.nodelist) do
|
|
i = i + 1
|
|
if k == name then
|
|
self.var.selected = i
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
self.metadata.index = self.metadata.index + 1
|
|
if self.metadata.index > #self.var.nodedata then
|
|
reset_build(self)
|
|
end
|
|
state = NPCF_ANIM_WALK_MINE
|
|
speed = 1
|
|
else
|
|
state = NPCF_ANIM_WALK
|
|
speed = get_speed(distance)
|
|
end
|
|
elseif vector.equals(pos, self.origin.pos) == false then
|
|
self.object:setacceleration({x=0, y=-10, z=0})
|
|
yaw = npcf:get_face_direction(pos, self.origin.pos)
|
|
local distance = vector.distance(pos, self.origin.pos)
|
|
if distance > 1 then
|
|
speed = get_speed(distance)
|
|
state = NPCF_ANIM_WALK
|
|
else
|
|
self.object:setpos(self.origin.pos)
|
|
yaw = self.origin.yaw
|
|
end
|
|
end
|
|
self.object:setvelocity(npcf:get_walk_velocity(speed, self.object:getvelocity().y, yaw))
|
|
self.object:setyaw(yaw)
|
|
npcf:set_animation(self, state)
|
|
end
|
|
end,
|
|
on_receive_fields = function(self, fields, sender)
|
|
local player_name = sender:get_player_name()
|
|
if self.owner == player_name then
|
|
if fields.schemlist then
|
|
local id = tonumber(string.match(fields.schemlist, "%d+"))
|
|
if id then
|
|
if SCHEMS[id] then
|
|
local pos = {
|
|
x=math.ceil(self.origin.pos.x) + 1,
|
|
y=math.floor(self.origin.pos.y),
|
|
z=math.ceil(self.origin.pos.z) + 1
|
|
}
|
|
self.metadata.schematic = SCHEMS[id]
|
|
self.metadata.build_pos = pos
|
|
load_schematic(self, self.metadata.schematic)
|
|
end
|
|
end
|
|
elseif fields.build_cancel then
|
|
reset_build(self)
|
|
end
|
|
end
|
|
if fields.build_start then
|
|
for i,v in ipairs(self.var.nodedata) do
|
|
minetest.remove_node(v.pos)
|
|
end
|
|
self.metadata.index = 1
|
|
self.metadata.building = true
|
|
elseif fields.build_resume then
|
|
self.metadata.building = true
|
|
end
|
|
end,
|
|
})
|
|
|