864 lines
27 KiB
Lua
864 lines
27 KiB
Lua
smart_villages.animation_frames = {
|
|
STAND = { x= 0, y= 79, },
|
|
LAY = { x=162, y=166, },
|
|
WALK = { x=168, y=187, },
|
|
MINE = { x=189, y=198, },
|
|
WALK_MINE = { x=200, y=219, },
|
|
SIT = { x= 81, y=160, },
|
|
}
|
|
|
|
smart_villages.registered_villagers = {}
|
|
|
|
smart_villages.registered_jobs = {}
|
|
|
|
smart_villages.registered_eggs = {}
|
|
|
|
-- smart_villages.is_job reports whether a item is a job item by the name.
|
|
function smart_villages.is_job(item_name)
|
|
if smart_villages.registered_jobs[item_name] then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- smart_villages.is_villager reports whether a name is villager's name.
|
|
function smart_villages.is_villager(name)
|
|
if smart_villages.registered_villagers[name] then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
|
|
-- smart_villages.villager represents a table that contains common methods
|
|
-- for villager object.
|
|
-- this table must be contains by a metatable.__index of villager self tables.
|
|
-- minetest.register_entity set initial properties as a metatable.__index, so
|
|
-- this table's methods must be put there.
|
|
smart_villages.villager = {}
|
|
|
|
-- smart_villages.villager.get_inventory returns a inventory of a villager.
|
|
function smart_villages.villager:get_inventory()
|
|
return minetest.get_inventory {
|
|
type = "detached",
|
|
name = self.inventory_name,
|
|
}
|
|
end
|
|
|
|
-- smart_villages.villager.get_job_name returns a name of a villager's current job.
|
|
function smart_villages.villager:get_job_name()
|
|
local inv = self:get_inventory()
|
|
return inv:get_stack("job", 1):get_name()
|
|
end
|
|
|
|
-- smart_villages.villager.get_job returns a villager's current job definition.
|
|
function smart_villages.villager:get_job()
|
|
local name = self:get_job_name()
|
|
if name ~= "" then
|
|
return smart_villages.registered_jobs[name]
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- smart_villages.villager.get_nearest_player returns a player object who
|
|
-- is the nearest to the villager.
|
|
function smart_villages.villager:get_nearest_player(range_distance)
|
|
local player, min_distance = nil, range_distance
|
|
local position = self.object:getpos()
|
|
|
|
local all_objects = minetest.get_objects_inside_radius(position, range_distance)
|
|
for _, object in pairs(all_objects) do
|
|
if object:is_player() then
|
|
local player_position = object:getpos()
|
|
local distance = vector.distance(position, player_position)
|
|
|
|
if distance < min_distance then
|
|
min_distance = distance
|
|
player = object
|
|
end
|
|
end
|
|
end
|
|
return player
|
|
end
|
|
|
|
-- woriking_villages.villager.get_nearest_item_by_condition returns the position of
|
|
-- an item that returns true for the condition
|
|
function smart_villages.villager:get_nearest_item_by_condition(cond, range_distance)
|
|
local max_distance=range_distance
|
|
if type(range_distance) == "table" then
|
|
max_distance=math.max(math.max(range_distance.x,range_distance.y),range_distance.z)
|
|
end
|
|
local item = nil
|
|
local min_distance = max_distance
|
|
local position = self.object:getpos()
|
|
|
|
local all_objects = minetest.get_objects_inside_radius(position, max_distance)
|
|
for _, object in pairs(all_objects) do
|
|
if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then
|
|
local found_item = ItemStack(object:get_luaentity().itemstring):to_table()
|
|
if found_item then
|
|
if cond(found_item) then
|
|
local item_position = object:getpos()
|
|
local distance = vector.distance(position, item_position)
|
|
|
|
if distance < min_distance then
|
|
min_distance = distance
|
|
item = object
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return item;
|
|
end
|
|
|
|
-- smart_villages.villager.get_front returns a position in front of the villager.
|
|
function smart_villages.villager:get_front()
|
|
local direction = self:get_look_direction()
|
|
if math.abs(direction.x) >= 0.5 then
|
|
if direction.x > 0 then direction.x = 1 else direction.x = -1 end
|
|
else
|
|
direction.x = 0
|
|
end
|
|
|
|
if math.abs(direction.z) >= 0.5 then
|
|
if direction.z > 0 then direction.z = 1 else direction.z = -1 end
|
|
else
|
|
direction.z = 0
|
|
end
|
|
|
|
--direction.y = direction.y - 1
|
|
|
|
return vector.add(vector.round(self.object:getpos()), direction)
|
|
end
|
|
|
|
-- smart_villages.villager.get_front_node returns a node that exists in front of the villager.
|
|
function smart_villages.villager:get_front_node()
|
|
local front = self:get_front()
|
|
return minetest.get_node(front)
|
|
end
|
|
|
|
-- smart_villages.villager.get_back returns a position behind the villager.
|
|
function smart_villages.villager:get_back()
|
|
local direction = self:get_look_direction()
|
|
if math.abs(direction.x) >= 0.5 then
|
|
if direction.x > 0 then direction.x = -1
|
|
else direction.x = 1 end
|
|
else
|
|
direction.x = 0
|
|
end
|
|
|
|
if math.abs(direction.z) >= 0.5 then
|
|
if direction.z > 0 then direction.z = -1
|
|
else direction.z = 1 end
|
|
else
|
|
direction.z = 0
|
|
end
|
|
|
|
--direction.y = direction.y - 1
|
|
|
|
return vector.add(vector.round(self.object:getpos()), direction)
|
|
end
|
|
|
|
-- smart_villages.villager.get_back_node returns a node that exists behind the villager.
|
|
function smart_villages.villager:get_back_node()
|
|
local back = self:get_back()
|
|
return minetest.get_node(back)
|
|
end
|
|
|
|
-- smart_villages.villager.get_look_direction returns a normalized vector that is
|
|
-- the villagers's looking direction.
|
|
function smart_villages.villager:get_look_direction()
|
|
local yaw = self.object:getyaw()
|
|
return vector.normalize{x = -math.sin(yaw), y = 0.0, z = math.cos(yaw)}
|
|
end
|
|
|
|
-- smart_villages.villager.set_animation sets the villager's animation.
|
|
-- this method is wrapper for self.object:set_animation.
|
|
function smart_villages.villager:set_animation(frame)
|
|
self.object:set_animation(frame, 15, 0)
|
|
if frame == smart_villages.animation_frames.LAY then
|
|
local dir = self:get_look_direction()
|
|
local dirx = math.abs(dir.x)*0.5
|
|
local dirz = math.abs(dir.z)*0.5
|
|
self.object:set_properties({collisionbox={-0.5-dirx, 0, -0.5-dirz, 0.5+dirx, 0.5, 0.5+dirz}})
|
|
else
|
|
self.object:set_properties({collisionbox={-0.25, 0, -0.25, 0.25, 1.75, 0.25}})
|
|
end
|
|
end
|
|
|
|
-- smart_villages.villager.set_yaw_by_direction sets the villager's yaw
|
|
-- by a direction vector.
|
|
function smart_villages.villager:set_yaw_by_direction(direction)
|
|
self.object:setyaw(math.atan2(direction.z, direction.x) - math.pi / 2)
|
|
end
|
|
|
|
-- smart_villages.villager.get_wield_item_stack returns the villager's wield item's stack.
|
|
function smart_villages.villager:get_wield_item_stack()
|
|
local inv = self:get_inventory()
|
|
return inv:get_stack("wield_item", 1)
|
|
end
|
|
|
|
-- smart_villages.villager.set_wield_item_stack sets villager's wield item stack.
|
|
function smart_villages.villager:set_wield_item_stack(stack)
|
|
local inv = self:get_inventory()
|
|
inv:set_stack("wield_item", 1, stack)
|
|
end
|
|
|
|
-- smart_villages.villager.add_item_to_main add item to main slot.
|
|
-- and returns leftover.
|
|
function smart_villages.villager:add_item_to_main(stack)
|
|
local inv = self:get_inventory()
|
|
return inv:add_item("main", stack)
|
|
end
|
|
|
|
-- smart_villages.villager.move_main_to_wield moves itemstack from main to wield.
|
|
-- if this function fails then returns false, else returns true.
|
|
function smart_villages.villager:move_main_to_wield(pred)
|
|
local inv = self:get_inventory()
|
|
local main_size = inv:get_size("main")
|
|
|
|
for i = 1, main_size do
|
|
local stack = inv:get_stack("main", i)
|
|
if pred(stack:get_name()) then
|
|
local wield_stack = inv:get_stack("wield_item", 1)
|
|
inv:set_stack("wield_item", 1, stack)
|
|
inv:set_stack("main", i, wield_stack)
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- smart_villages.villager.is_named reports the villager is still named.
|
|
function smart_villages.villager:is_named()
|
|
return self.nametag ~= ""
|
|
end
|
|
|
|
-- smart_villages.villager.has_item_in_main reports whether the villager has item.
|
|
function smart_villages.villager:has_item_in_main(pred)
|
|
local inv = self:get_inventory()
|
|
local stacks = inv:get_list("main")
|
|
|
|
for _, stack in ipairs(stacks) do
|
|
local itemname = stack:get_name()
|
|
if pred(itemname) then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
-- smart_villages.villager.change_direction change direction to destination and velocity vector.
|
|
function smart_villages.villager:change_direction(destination)
|
|
local position = self.object:getpos()
|
|
local direction = vector.subtract(destination, position)
|
|
direction.y = 0
|
|
local velocity = vector.multiply(vector.normalize(direction), 1.5)
|
|
|
|
self.object:setvelocity(velocity)
|
|
self:set_yaw_by_direction(direction)
|
|
end
|
|
|
|
-- smart_villages.villager.change_direction_randomly change direction randonly.
|
|
function smart_villages.villager:change_direction_randomly()
|
|
local direction = {
|
|
x = math.random(0, 5) * 2 - 5,
|
|
y = 0,
|
|
z = math.random(0, 5) * 2 - 5,
|
|
}
|
|
local velocity = vector.multiply(vector.normalize(direction), 1.5)
|
|
self.object:setvelocity(velocity)
|
|
self:set_yaw_by_direction(direction)
|
|
self:set_animation(smart_villages.animation_frames.WALK)
|
|
end
|
|
|
|
-- smart_villages.villager.get_timer get the value of a counter.
|
|
function smart_villages.villager:get_timer(timerId)
|
|
return self.time_counters[timerId]
|
|
end
|
|
|
|
-- smart_villages.villager.set_timer set the value of a counter.
|
|
function smart_villages.villager:set_timer(timerId,value)
|
|
assert(type(value)=="number","timers need to be countable")
|
|
self.time_counters[timerId]=value
|
|
end
|
|
|
|
-- smart_villages.villager.clear_timers set all counters to 0.
|
|
function smart_villages.villager:clear_timers()
|
|
for timerId,_ in pairs(self.time_counters) do
|
|
self.time_counters[timerId] = 0
|
|
end
|
|
end
|
|
|
|
-- smart_villages.villager.count_timer count a counter up by 1.
|
|
function smart_villages.villager:count_timer(timerId)
|
|
if not self.time_counters[timerId] then
|
|
minetest.log("info","timer \""..timerId.."\" was not initialized")
|
|
self.time_counters[timerId] = 0
|
|
end
|
|
self.time_counters[timerId] = self.time_counters[timerId] + 1
|
|
end
|
|
|
|
-- smart_villages.villager.count_timers count all counters up by 1.
|
|
function smart_villages.villager:count_timers()
|
|
for id, counter in pairs(self.time_counters) do
|
|
self.time_counters[id] = counter + 1
|
|
end
|
|
end
|
|
|
|
-- smart_villages.villager.timer_exceeded if a timer exceeds the limit it will be reset and true is returned
|
|
function smart_villages.villager:timer_exceeded(timerId,limit)
|
|
if self:get_timer(timerId)>=limit then
|
|
self:set_timer(timerId,0)
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
-- smart_villages.villager.update_infotext updates the infotext of the villager.
|
|
function smart_villages.villager:update_infotext()
|
|
local infotext = ""
|
|
local job_name = self:get_job()
|
|
|
|
if job_name ~= nil then
|
|
job_name = job_name.description
|
|
infotext = infotext .. job_name .. "\n"
|
|
else
|
|
infotext = infotext .. "this villager is inactive\nNo job\n"
|
|
end
|
|
infotext = infotext .. "[Owner] : " .. self.owner_name
|
|
if self.pause=="resting" then
|
|
infotext = infotext .. "\nthis villager is resting"
|
|
elseif self.pause=="sleeping" then
|
|
infotext = infotext .. "\nthis villager is sleeping"
|
|
elseif self.pause=="active" then
|
|
infotext = infotext .. "\nthis villager is active"
|
|
end
|
|
self.object:set_properties{infotext = infotext}
|
|
end
|
|
|
|
-- smart_villages.villager.is_near checks if the villager is withing the radius of a position
|
|
function smart_villages.villager:is_near(pos, distance)
|
|
local p = self.object:getpos()
|
|
p.y = p.y + 0.5
|
|
return vector.distance(p, pos) < distance
|
|
end
|
|
|
|
--smart_villages.villager.handle_obstacles(ignore_fence,ignore_doors)
|
|
--if the villager hits a walkable he wil jump
|
|
--if ignore_fence is false and the villager hits a door he opens it
|
|
--if ignore_fence is false the villager will not jump over fences
|
|
function smart_villages.villager:handle_obstacles(ignore_fence,ignore_doors)
|
|
local velocity = self.object:getvelocity()
|
|
--local inside_node = minetest.get_node(self.object:getpos())
|
|
--if string.find(inside_node.name,"doors:door") and not ignore_doors then
|
|
-- self:change_direction(vector.round(self.object:getpos()))
|
|
--end
|
|
if velocity.y == 0 then
|
|
local front_node = self:get_front_node()
|
|
local above_node = self:get_front()
|
|
above_node = vector.add(above_node,{x=0,y=1,z=0})
|
|
above_node = minetest.get_node(above_node)
|
|
if minetest.get_item_group(front_node.name, "fence") > 0 and not(ignore_fence) then
|
|
self:change_direction_randomly()
|
|
elseif string.find(front_node.name,"doors:door") and not(ignore_doors) then
|
|
local door = doors.get(self:get_front())
|
|
door:open()
|
|
elseif minetest.registered_nodes[front_node.name].walkable
|
|
and not(minetest.registered_nodes[above_node.name].walkable) then
|
|
|
|
self.object:setvelocity{x = velocity.x, y = 6, z = velocity.z}
|
|
end
|
|
if not ignore_doors then
|
|
local back_pos = self:get_back()
|
|
if string.find(minetest.get_node(back_pos).name,"doors:door") then
|
|
local door = doors.get(back_pos)
|
|
door:close()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- smart_villages.villager.pickup_item pickup items placed and put it to main slot.
|
|
function smart_villages.villager:pickup_item()
|
|
local pos = self.object:getpos()
|
|
local radius = 1.0
|
|
local all_objects = minetest.get_objects_inside_radius(pos, radius)
|
|
|
|
for _, obj in ipairs(all_objects) do
|
|
if not obj:is_player() and obj:get_luaentity() and obj:get_luaentity().itemstring then
|
|
local itemstring = obj:get_luaentity().itemstring
|
|
local stack = ItemStack(itemstring)
|
|
if stack and stack:to_table() then
|
|
local name = stack:to_table().name
|
|
|
|
if minetest.registered_items[name] ~= nil then
|
|
local inv = self:get_inventory()
|
|
local leftover = inv:add_item("main", stack)
|
|
|
|
minetest.add_item(obj:getpos(), leftover)
|
|
obj:get_luaentity().itemstring = ""
|
|
obj:remove()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- smart_villages.villager.is_active check if the villager is paused.
|
|
function smart_villages.villager:is_active()
|
|
return self.pause == "active"
|
|
end
|
|
|
|
dofile(smart_villages.modpath.."/async_actions.lua") --load states
|
|
|
|
function smart_villages.villager:set_state(id) --deprecated
|
|
if id == "idle" then
|
|
print("the idle state is deprecated")
|
|
elseif id == "goto_dest" then
|
|
print("use self:go_to(pos) instead of self:set_state(\"goto\")")
|
|
self:go_to(self.destination)
|
|
elseif id == "job" then
|
|
print("the job state is not nessecary anymore")
|
|
elseif id == "dig_target" then
|
|
print("use self:dig(pos) instead of self:set_state(\"dig_target\")")
|
|
self:dig(self.target)
|
|
elseif id == "place_wield" then
|
|
print("use self:place(itemname,pos) instead of self:set_state(\"place_wield\")")
|
|
local wield_stack = self:get_wield_item_stack()
|
|
self:place(wield_stack:get_name(),self.target)
|
|
end
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
|
|
-- smart_villages.manufacturing_data represents a table that contains manufacturing data.
|
|
-- this table's keys are product names, and values are manufacturing numbers
|
|
-- that has been already manufactured.
|
|
smart_villages.manufacturing_data = (function()
|
|
local file_name = minetest.get_worldpath() .. "/smart_villages_data"
|
|
|
|
minetest.register_on_shutdown(function()
|
|
local file = io.open(file_name, "w")
|
|
file:write(minetest.serialize(smart_villages.manufacturing_data))
|
|
file:close()
|
|
end)
|
|
|
|
local file = io.open(file_name, "r")
|
|
if file ~= nil then
|
|
local data = file:read("*a")
|
|
file:close()
|
|
return minetest.deserialize(data)
|
|
end
|
|
return {}
|
|
end) ()
|
|
|
|
--------------------------------------------------------------------
|
|
|
|
-- register empty item entity definition.
|
|
-- this entity may be hold by villager's hands.
|
|
do
|
|
minetest.register_craftitem("smart_villages:dummy_empty_craftitem", {
|
|
wield_image = "smart_villages_dummy_empty_craftitem.png",
|
|
})
|
|
|
|
local function on_activate(self)
|
|
-- attach to the nearest villager.
|
|
local all_objects = minetest.get_objects_inside_radius(self.object:getpos(), 0.1)
|
|
for _, obj in ipairs(all_objects) do
|
|
local luaentity = obj:get_luaentity()
|
|
|
|
if smart_villages.is_villager(luaentity.name) then
|
|
self.object:set_attach(obj, "Arm_R", {x = 0.065, y = 0.50, z = -0.15}, {x = -45, y = 0, z = 0})
|
|
self.object:set_properties{textures={"smart_villages:dummy_empty_craftitem"}}
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
local function on_step(self)
|
|
local all_objects = minetest.get_objects_inside_radius(self.object:getpos(), 0.1)
|
|
for _, obj in ipairs(all_objects) do
|
|
local luaentity = obj:get_luaentity()
|
|
|
|
if smart_villages.is_villager(luaentity.name) then
|
|
local stack = luaentity:get_wield_item_stack()
|
|
|
|
if stack:get_name() ~= self.itemname then
|
|
if stack:is_empty() then
|
|
self.itemname = ""
|
|
self.object:set_properties{textures={"smart_villages:dummy_empty_craftitem"}}
|
|
else
|
|
self.itemname = stack:get_name()
|
|
self.object:set_properties{textures={self.itemname}}
|
|
end
|
|
end
|
|
return
|
|
end
|
|
end
|
|
-- if cannot find villager, delete empty item.
|
|
self.object:remove()
|
|
return
|
|
end
|
|
|
|
minetest.register_entity("smart_villages:dummy_item", {
|
|
hp_max = 1,
|
|
visual = "wielditem",
|
|
visual_size = {x = 0.025, y = 0.025},
|
|
collisionbox = {0, 0, 0, 0, 0, 0},
|
|
physical = false,
|
|
textures = {"air"},
|
|
on_activate = on_activate,
|
|
on_step = on_step,
|
|
itemname = "",
|
|
})
|
|
end
|
|
|
|
---------------------------------------------------------------------
|
|
|
|
-- smart_villages.register_job registers a definition of a new job.
|
|
function smart_villages.register_job(job_name, def, recipe)
|
|
smart_villages.registered_jobs[job_name] = def
|
|
minetest.register_tool(job_name, {
|
|
stack_max = 1,
|
|
description = def.description,
|
|
inventory_image = def.inventory_image,
|
|
})
|
|
if recipe ~= nil then
|
|
minetest.register_craft({
|
|
output = job_name,
|
|
recipe = recipe
|
|
})
|
|
end
|
|
end
|
|
|
|
-- smart_villages.register_egg registers a definition of a new egg.
|
|
function smart_villages.register_egg(egg_name, def)
|
|
smart_villages.registered_eggs[egg_name] = def
|
|
|
|
minetest.register_tool(egg_name, {
|
|
description = def.description,
|
|
inventory_image = def.inventory_image,
|
|
stack_max = 1,
|
|
|
|
on_use = function(itemstack, user, pointed_thing)
|
|
if pointed_thing.above ~= nil and def.product_name ~= nil then
|
|
-- set villager's direction.
|
|
local new_villager = minetest.add_entity(pointed_thing.above, def.product_name)
|
|
new_villager:get_luaentity():set_yaw_by_direction(
|
|
vector.subtract(user:getpos(), new_villager:getpos())
|
|
)
|
|
new_villager:get_luaentity().owner_name = user:get_player_name()
|
|
new_villager:get_luaentity():update_infotext()
|
|
|
|
itemstack:take_item()
|
|
return itemstack
|
|
end
|
|
return nil
|
|
end,
|
|
})
|
|
end
|
|
|
|
-- smart_villages.register_villager registers a definition of a new villager.
|
|
function smart_villages.register_villager(product_name, def)
|
|
smart_villages.registered_villagers[product_name] = def
|
|
|
|
-- initialize manufacturing number of a new villager.
|
|
if smart_villages.manufacturing_data[product_name] == nil then
|
|
smart_villages.manufacturing_data[product_name] = 0
|
|
end
|
|
|
|
-- create_inventory creates a new inventory, and returns it.
|
|
local function create_inventory(self)
|
|
self.inventory_name = self.product_name .. "_" .. tostring(self.manufacturing_number)
|
|
local inventory = minetest.create_detached_inventory(self.inventory_name, {
|
|
on_put = function(_, listname, _, stack) --inv, listname, index, stack, player
|
|
if listname == "job" then
|
|
local job_name = stack:get_name()
|
|
local job = smart_villages.registered_jobs[job_name]
|
|
if type(job.on_start)=="function" then
|
|
job.on_start(self)
|
|
self.job_thread = coroutine.create(job.on_step)
|
|
elseif type(job.jobfunc)=="function" then
|
|
self.job_thread = coroutine.create(job.jobfunc)
|
|
end
|
|
self:update_infotext()
|
|
end
|
|
end,
|
|
|
|
allow_put = function(_, listname, _, stack) --inv, listname, index, stack, player
|
|
-- only jobs can put to a job inventory.
|
|
if listname == "main" then
|
|
return stack:get_count()
|
|
elseif listname == "job" and smart_villages.is_job(stack:get_name()) then
|
|
return stack:get_count()
|
|
elseif listname == "wield_item" then
|
|
return 0
|
|
end
|
|
return 0
|
|
end,
|
|
|
|
on_take = function(_, listname, _, stack) --inv, listname, index, stack, player
|
|
if listname == "job" then
|
|
local job_name = stack:get_name()
|
|
local job = smart_villages.registered_jobs[job_name]
|
|
self.time_counters = {}
|
|
if job then
|
|
if type(job.on_stop)=="function" then
|
|
job.on_stop(self)
|
|
elseif type(job.jobfunc)=="function" then
|
|
self.job_thread = false
|
|
end
|
|
end
|
|
self:update_infotext()
|
|
end
|
|
end,
|
|
|
|
allow_take = function(_, listname, _, stack) --inv, listname, index, stack, player
|
|
if listname == "wield_item" then
|
|
return 0
|
|
end
|
|
return stack:get_count()
|
|
end,
|
|
|
|
on_move = function(inv, from_list, _, to_list, to_index)
|
|
--inv, from_list, from_index, to_list, to_index, count, player
|
|
if to_list == "job" or from_list == "job" then
|
|
local job_name = inv:get_stack(to_list, to_index):get_name()
|
|
local job = smart_villages.registered_jobs[job_name]
|
|
|
|
if to_list == "job" then
|
|
if type(job.on_start)=="function" then
|
|
job.on_start(self)
|
|
self.job_thread = coroutine.create(job.on_step)
|
|
elseif type(job.jobfunc)=="function" then
|
|
self.job_thread = coroutine.create(job.jobfunc)
|
|
end
|
|
elseif from_list == "job" then
|
|
if type(job.on_stop)=="function" then
|
|
job.on_stop(self)
|
|
elseif type(job.jobfunc)=="function" then
|
|
self.job_thread = false
|
|
end
|
|
end
|
|
|
|
self:update_infotext()
|
|
end
|
|
end,
|
|
|
|
allow_move = function(inv, from_list, from_index, to_list, _, count)
|
|
--inv, from_list, from_index, to_list, to_index, count, player
|
|
if to_list == "wield_item" then
|
|
return 0
|
|
end
|
|
|
|
if to_list == "main" then
|
|
return count
|
|
elseif to_list == "job" and smart_villages.is_job(inv:get_stack(from_list, from_index):get_name()) then
|
|
return count
|
|
end
|
|
|
|
return 0
|
|
end,
|
|
})
|
|
|
|
inventory:set_size("main", 16)
|
|
inventory:set_size("job", 1)
|
|
inventory:set_size("wield_item", 1)
|
|
|
|
return inventory
|
|
end
|
|
|
|
-- on_activate is a callback function that is called when the object is created or recreated.
|
|
local function on_activate(self, staticdata)
|
|
-- parse the staticdata, and compose a inventory.
|
|
if staticdata == "" then
|
|
self.product_name = product_name
|
|
self.manufacturing_number = smart_villages.manufacturing_data[product_name]
|
|
smart_villages.manufacturing_data[product_name] = smart_villages.manufacturing_data[product_name] + 1
|
|
create_inventory(self)
|
|
|
|
-- attach dummy item to new villager.
|
|
minetest.add_entity(self.object:getpos(), "smart_villages:dummy_item")
|
|
else
|
|
-- if static data is not empty string, this object has beed already created.
|
|
local data = minetest.deserialize(staticdata)
|
|
|
|
self.product_name = data["product_name"]
|
|
self.manufacturing_number = data["manufacturing_number"]
|
|
self.nametag = data["nametag"]
|
|
self.owner_name = data["owner_name"]
|
|
self.pause = data["pause"]
|
|
|
|
local inventory = create_inventory(self)
|
|
for list_name, list in pairs(data["inventory"]) do
|
|
inventory:set_list(list_name, list)
|
|
end
|
|
end
|
|
|
|
self:update_infotext()
|
|
|
|
self.object:set_nametag_attributes{
|
|
text = self.nametag
|
|
}
|
|
|
|
self.object:setvelocity{x = 0, y = 0, z = 0}
|
|
self.object:setacceleration{x = 0, y = -10, z = 0}
|
|
|
|
local job = self:get_job()
|
|
if job ~= nil then
|
|
if type(job.on_start)=="function" then
|
|
job.on_start(self)
|
|
self.job_thread = coroutine.create(job.on_step)
|
|
elseif type(job.jobfunc)=="function" then
|
|
self.job_thread = coroutine.create(job.jobfunc)
|
|
end
|
|
if self.pause == "resting" then
|
|
if type(job.on_pause)=="function" then
|
|
job.on_pause(self)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- get_staticdata is a callback function that is called when the object is destroyed.
|
|
local function get_staticdata(self)
|
|
local inventory = self:get_inventory()
|
|
local data = {
|
|
["product_name"] = self.product_name,
|
|
["manufacturing_number"] = self.manufacturing_number,
|
|
["nametag"] = self.nametag,
|
|
["owner_name"] = self.owner_name,
|
|
["inventory"] = {},
|
|
["pause"] = self.pause,
|
|
}
|
|
|
|
-- set lists.
|
|
for list_name, list in pairs(inventory:get_lists()) do
|
|
data["inventory"][list_name] = {}
|
|
|
|
for i, item in ipairs(list) do
|
|
data["inventory"][list_name][i] = item:to_string()
|
|
end
|
|
end
|
|
|
|
return minetest.serialize(data)
|
|
end
|
|
|
|
-- on_step is a callback function that is called every delta times.
|
|
local function on_step(self, dtime)
|
|
--upate old pause state
|
|
if self.pause==true then
|
|
self.pause="resting"
|
|
elseif self.pause == false then
|
|
self.pause="active"
|
|
end
|
|
|
|
--[[ if owner didn't login, the villager does nothing.
|
|
if not minetest.get_player_by_name(self.owner_name) then
|
|
return
|
|
end--]]
|
|
|
|
-- pickup surrounding item.
|
|
self:pickup_item()
|
|
|
|
if self.pause ~= "active" and self.pause ~= "sleeping" then
|
|
return
|
|
end
|
|
|
|
local job = self:get_job()
|
|
if not job then return end
|
|
if not self.job_thread and job.on_step then
|
|
job.on_start(self)
|
|
self.job_thread = coroutine.create(job.on_step)
|
|
end
|
|
if coroutine.status(self.job_thread) == "dead" then
|
|
if job.jobfunc then
|
|
self.job_thread = coroutine.create(job.jobfunc)
|
|
else
|
|
self.job_thread = coroutine.create(job.on_step)
|
|
end
|
|
end
|
|
if coroutine.status(self.job_thread) == "suspended" then
|
|
local state, err = coroutine.resume(self.job_thread, self, dtime)
|
|
if state == false then
|
|
error("error in job_thread " .. err)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- on_rightclick is a callback function that is called when a player right-click them.
|
|
local function on_rightclick(self, clicker)
|
|
local wielded_stack = clicker:get_wielded_item()
|
|
if wielded_stack:get_name() == "smart_villages:commanding_sceptre"
|
|
and clicker:get_player_name() == self.owner_name then
|
|
|
|
smart_villages.forms.show_inv_formspec(self, clicker:get_player_name())
|
|
else
|
|
smart_villages.forms.show_talking_formspec(self, clicker:get_player_name())
|
|
end
|
|
self:update_infotext()
|
|
end
|
|
|
|
-- on_punch is a callback function that is called when a player punch then.
|
|
local function on_punch()--self, puncher, time_from_last_punch, tool_capabilities, dir
|
|
--TODO: aggression
|
|
end
|
|
|
|
-- register a definition of a new villager.
|
|
|
|
local villager_def = table.copy(smart_villages.villager)
|
|
-- basic initial properties
|
|
villager_def.hp_max = def.hp_max
|
|
villager_def.weight = def.weight
|
|
villager_def.mesh = def.mesh
|
|
villager_def.textures = def.textures
|
|
|
|
villager_def.physical = true
|
|
villager_def.visual = "mesh"
|
|
villager_def.visual_size = {x = 1, y = 1}
|
|
villager_def.collisionbox = {-0.25, 0, -0.25, 0.25, 1.75, 0.25}
|
|
villager_def.is_visible = true
|
|
villager_def.makes_footstep_sound = true
|
|
villager_def.infotext = ""
|
|
villager_def.nametag = ""
|
|
|
|
-- extra initial properties
|
|
villager_def.pause = "active"
|
|
villager_def.state = "job"
|
|
villager_def.job_thread = false
|
|
villager_def.product_name = ""
|
|
villager_def.manufacturing_number = -1
|
|
villager_def.owner_name = ""
|
|
villager_def.time_counters = {}
|
|
villager_def.destination = vector.new(0,0,0)
|
|
|
|
-- callback methods
|
|
villager_def.on_activate = on_activate
|
|
villager_def.on_step = on_step
|
|
villager_def.on_rightclick = on_rightclick
|
|
villager_def.on_punch = on_punch
|
|
villager_def.get_staticdata = get_staticdata
|
|
|
|
-- home methods
|
|
villager_def.get_home = smart_villages.get_home
|
|
villager_def.has_home = smart_villages.is_valid_home
|
|
|
|
|
|
minetest.register_entity(product_name, villager_def)
|
|
|
|
-- register villager egg.
|
|
smart_villages.register_egg(product_name .. "_egg", {
|
|
description = product_name .. " egg",
|
|
inventory_image = def.egg_image,
|
|
product_name = product_name,
|
|
})
|
|
end
|
|
function smart_villages.random_texture(...)
|
|
math.randomseed(os.time())
|
|
local args = { ... }
|
|
return args[math.random(1, #args)]
|
|
-- body
|
|
end |