11 Commits

Author SHA1 Message Date
4c0e2b574a Temporary fix for male textures issue. 2017-08-11 10:44:57 -04:00
bf935fd091 Dialogues: Fix bug with NPCs being frozen if 'Esc' button was pressed to exit dialogue 2017-06-29 19:01:03 -04:00
6141af11aa Relationships: Increase gift timer and relationship decrease timer to more usual values.
Fixed small bug on giving unliked items.
2017-06-27 18:39:13 -04:00
e70888c3e5 Fix bug with 'ignore' nodes when some map areas are not fully loaded but NPC try to perform actions on them. 2017-06-27 18:38:28 -04:00
cc56446206 NPC: (WIP) Schedule properties support adding one or multiple items to NPC inventory, and support taking one item from NPC inventory.
Attempt to fix child textures issue (2).
2017-06-21 21:07:36 -04:00
7110c49b42 Spawner: Avoid multiple spawning by not replacing the mg_villages:plotmarker, but just override its definition.
NPC: (WIP) Support change properties on schedules.
2017-06-21 07:16:11 -04:00
fd4cec0d63 Spawner: Assign sits, furnaces and storage nodes to spawned NPCs.
Small basic schedule change: NPCs now sit from 12-1 on whatever sit they 'own'.
Fixed /restore_plotmarkers not clearing all metadata.
NPC: Attempted to add a fix for the children growing on their own (due to mobs_redo).
2017-06-19 21:03:24 -04:00
3df43ab580 Spawner: Assign sits, furnaces and storage nodes to spawned NPCs.
Small basic schedule change: NPCs now sit from 12-1 on whatever sit they 'own'.
Fixed /restore_plotmarkers not clearing all metadata.
NPC: Attempted to add a fix for the children growing on their own (due to mobs_redo).
2017-06-19 20:54:26 -04:00
c19ea70242 Spawner: Add clearing of 'replaced' string to /restore_plotmarkers 2017-06-18 11:19:33 -04:00
a3b428fe14 Fix README.txt and restore correct spawning frequency. 2017-06-17 13:21:24 -04:00
4814c16ba0 Remove debug.txt 2017-06-17 13:00:54 -04:00
9 changed files with 382 additions and 411403 deletions

View File

@ -27,7 +27,7 @@ License
__advanced_npc__ is Copyright (C) 2016-2017 Hector Franqui (zorman2000), licensed under the GPLv3 license. See `license.txt` for details.
The `pathfinder.lua` file contains code slighlty modified from the [pathfinder mod](https://github.com/Yonaba/Jumper) by MarkBu, which is licensed as WTFPL. See `actions/pathfinder.lua` for details.
The `pathfinder.lua` file contains code slighlty modified from the [pathfinder mod](https://github.com/MarkuBu/pathfinder) by MarkBu, which is licensed as WTFPL. See `actions/pathfinder.lua` for details.
Current NPC textures are from mobs_redo mod.
The following textures are by Zorman2000:

View File

@ -604,6 +604,10 @@ function npc.actions.use_bed(self, args)
if action == npc.actions.const.beds.LAY then
-- Get position
-- Error here due to ignore. Need to come up with better solution
if node.name == "ignore" then
return
end
local bed_pos = npc.actions.nodes.beds[node.name].get_lay_pos(pos, dir)
-- Sit down on bed, rotate to correct direction
npc.add_action(self, npc.actions.cmd.SIT, {pos=bed_pos, dir=(node.param2 + 2) % 4})
@ -611,6 +615,10 @@ function npc.actions.use_bed(self, args)
npc.add_action(self, npc.actions.cmd.LAY, {})
else
-- Calculate position to get up
-- Error here due to ignore. Need to come up with better solution
if node.name == "ignore" then
return
end
local bed_pos_y = npc.actions.nodes.beds[node.name].get_lay_pos(pos, dir).y
local bed_pos = {x = pos.x, y = bed_pos_y, z = pos.z}
-- Sit up
@ -662,6 +670,10 @@ function npc.actions.use_sittable(self, args)
if action == npc.actions.const.sittable.SIT then
-- Calculate position depending on bench
-- Error here due to ignore. Need to come up with better solution
if node.name == "ignore" then
return
end
local sit_pos = npc.actions.nodes.sittable[node.name].get_sit_pos(pos, node.param2)
-- Sit down on bench/chair/stairs
npc.add_action(self, npc.actions.cmd.SIT, {pos=sit_pos, dir=(node.param2 + 2) % 4})

View File

@ -153,7 +153,11 @@ local function walkable(node, exceptions)
if node.name == "cottages:wood_flat" then
is_mg_villages_ceiling = true
end
if node ~= nil and node.name ~= nil and not minetest.registered_nodes[node.name].walkable then
if node ~= nil
and node.name ~= nil
and node.name ~= "ignore"
and minetest.registered_nodes[node.name]
and not minetest.registered_nodes[node.name].walkable then
return false
elseif is_openable then
return false

View File

@ -22,8 +22,10 @@ npc.places.nodes = {
},
SITTABLE_TYPE = {
"cottages:bench",
-- Currently commented out since some NPCs
-- were sitting at stairs that are actually staircases
-- TODO: Register other stair types
"stairs:stair_wood"
--"stairs:stair_wood"
},
STORAGE_TYPE = {
"default:chest",
@ -56,7 +58,16 @@ npc.places.PLACE_TYPE = {
PRIMARY = "bed_primary"
},
SITTABLE = {
PRIMARY = "sit_primary"
PRIMARY = "sit_primary",
SHARED = "sit_shared"
},
FURNACE = {
PRIMARY = "furnace_primary",
SHARED = "furnace_shared"
},
STORAGE = {
PRIMARY = "storage_primary",
SHARED = "storage_shared"
},
OPENABLE = {
HOME_ENTRANCE_DOOR = "home_entrance_door"
@ -68,27 +79,51 @@ npc.places.PLACE_TYPE = {
}
}
function npc.places.add_public(self, place_name, place_type, pos, access_node)
--minetest.log("Place name: "..dump(place_name)..", type: "..dump(place_type))
function npc.places.add_shared(self, place_name, place_type, pos, access_node)
self.places_map[place_name] = {type=place_type, pos=pos, access_node=access_node or pos, status="shared"}
end
-- Adds a specific node to the NPC places, and modifies the
-- node metadata to identify the NPC as the owner. This allows
-- other NPCs to avoid to take this as their own.
function npc.places.add_owned(self, place_name, place_type, pos, access_node)
-- Get node metadata
--local meta = minetest.get_meta(pos)
-- Check if it is owned by an NPC?
--if meta:get_string("npc_owner") == "" then
-- Set owned by NPC
--meta:set_string("npc_owner", self.npc_id)
-- Add place to list
self.places_map[place_name] = {type=place_type, pos=pos, access_node=access_node or pos, status="owned"}
--npc.places.add_public(self, place_name, place_type, pos)
return true
--end
--return false
end
function npc.places.add_unowned_accessible_place(self, nodes, place_type)
for i = 1, #nodes do
-- Check if node has owner
if nodes[i].owner == "" then
-- If node has no owner, check if it is accessible
local empty_nodes = npc.places.find_node_orthogonally(
nodes[i].node_pos, {"air"}, 0)
-- Check if node is accessible
if #empty_nodes > 0 then
-- Set owner to this NPC
nodes[i].owner = self.npc_id
-- Assign node to NPC
npc.places.add_owned(self, place_type, place_type,
nodes[i].node_pos, empty_nodes[1].pos)
npc.log("DEBUG", "Added node at "..minetest.pos_to_string(nodes[i].node_pos)
.." to NPC "..dump(self.npc_name))
break
end
end
end
end
function npc.places.add_shared_accessible_place(self, nodes, place_type)
for i = 1, #nodes do
-- Check of not adding same owned sit
if nodes[i].owner ~= self.npc_id then
-- Check if it is accessible
local empty_nodes = npc.places.find_node_orthogonally(
nodes[i].node_pos, {"air"}, 0)
-- Check if bed is accessible
if #empty_nodes > 0 then
-- Assign node to NPC
npc.places.add_shared(self, place_type..dump(i),
place_type, nodes[i].node_pos, empty_nodes[1].pos)
end
end
end
end
function npc.places.get_by_type(self, place_type)

411240
debug.txt

File diff suppressed because it is too large Load Diff

View File

@ -45,10 +45,10 @@ npc.dialogue.dialogue_results = {
-- Creates and shows a multi-option dialogue based on the number of responses
-- that the dialogue object contains
function npc.dialogue.show_options_dialogue(self,
dialogue,
dismiss_option_label,
player_name)
local responses = dialogue.responses
dialogue,
dismiss_option_label,
player_name)
local responses = dialogue.responses
local options_length = table.getn(responses) + 1
local formspec_height = (options_length * 0.7) + 0.4
local formspec = "size[7,"..tostring(formspec_height).."]"
@ -81,22 +81,22 @@ end
-- This function is used for showing a yes/no dialogue formspec
function npc.dialogue.show_yes_no_dialogue(self,
prompt,
positive_answer_label,
positive_callback,
negative_answer_label,
negative_callback,
player_name)
positive_answer_label,
positive_callback,
negative_answer_label,
negative_callback,
player_name)
npc.lock_actions(self)
npc.lock_actions(self)
local formspec = "size[7,3]"..
"label[0.5,0.1;"..prompt.."]"..
"button_exit[0.5,1.15;6,0.5;yes_option;"..positive_answer_label.."]"..
"button_exit[0.5,1.95;6,0.5;no_option;"..negative_answer_label.."]"
"label[0.5,0.1;"..prompt.."]"..
"button_exit[0.5,1.15;6,0.5;yes_option;"..positive_answer_label.."]"..
"button_exit[0.5,1.95;6,0.5;no_option;"..negative_answer_label.."]"
-- Create entry into responses table
npc.dialogue.dialogue_results.yes_no_dialogue[player_name] = {
npc = self,
npc = self,
yes_callback = positive_callback,
no_callback = negative_callback
}
@ -418,8 +418,8 @@ minetest.register_on_player_receive_fields(function (player, formname, fields)
if fields then
local player_response = npc.dialogue.dialogue_results.yes_no_dialogue[player_name]
-- Unlock queue, reset action timer and unfreeze NPC.
npc.unlock_actions(player_response.npc)
-- Unlock queue, reset action timer and unfreeze NPC.
npc.unlock_actions(player_response.npc)
if fields.yes_option then
player_response.yes_callback()
@ -437,11 +437,11 @@ minetest.register_on_player_receive_fields(function (player, formname, fields)
-- Get player response
local player_response = npc.dialogue.dialogue_results.options_dialogue[player_name]
-- Check if the player hit the negative option
if fields["exit"] then
-- Unlock queue, reset action timer and unfreeze NPC.
npc.unlock_actions(player_response.npc)
end
-- Check if the player hit the negative option or esc button
if fields["exit"] or fields["quit"] == "true" then
-- Unlock queue, reset action timer and unfreeze NPC.
npc.unlock_actions(player_response.npc)
end
for i = 1, #player_response.options do
local button_label = "opt"..tostring(i)
@ -476,36 +476,36 @@ minetest.register_on_player_receive_fields(function (player, formname, fields)
.responses[player_response.options[i].response_id]
.action(player_response.npc, player)
end
return
elseif player_response.is_dedicated_trade_dialogue == true then
-- Get the functions for a dedicated trader prompt
npc.trade.DEDICATED_TRADER_PROMPT
.responses[player_response.options[i].response_id]
.action(player_response.npc, player)
return
elseif player_response.is_custom_trade_dialogue == true then
-- Functions for a custom trade should be available from the same dialogue
-- object as it is created in memory
minetest.log("Player response: "..dump(player_response.options[i]))
player_response.options[i].action(player_response.npc, player)
else
-- Get dialogues for sex and phase
local dialogues = npc.data.DIALOGUES[player_response.npc.sex][phase]
return
elseif player_response.is_dedicated_trade_dialogue == true then
-- Get the functions for a dedicated trader prompt
npc.trade.DEDICATED_TRADER_PROMPT
.responses[player_response.options[i].response_id]
.action(player_response.npc, player)
return
elseif player_response.is_custom_trade_dialogue == true then
-- Functions for a custom trade should be available from the same dialogue
-- object as it is created in memory
minetest.log("Player response: "..dump(player_response.options[i]))
player_response.options[i].action(player_response.npc, player)
else
-- Get dialogues for sex and phase
local dialogues = npc.data.DIALOGUES[player_response.npc.sex][phase]
minetest.log("Object: "..dump(dialogues[player_response.options[i].dialogue_id]))
local response = get_response_object_by_id_recursive(dialogues[player_response.options[i].dialogue_id], 0, player_response.options[i].response_id)
minetest.log("Found: "..dump(response))
minetest.log("Object: "..dump(dialogues[player_response.options[i].dialogue_id]))
local response = get_response_object_by_id_recursive(dialogues[player_response.options[i].dialogue_id], 0, player_response.options[i].response_id)
minetest.log("Found: "..dump(response))
-- Execute function
response.action(player_response.npc, player)
-- Execute function
response.action(player_response.npc, player)
-- Execute function
-- dialogues[player_response.options[i].dialogue_id]
-- .responses[player_response.options[i].response_id]
-- .action(player_response.npc, player)
-- Execute function
-- dialogues[player_response.options[i].dialogue_id]
-- .responses[player_response.options[i].response_id]
-- .action(player_response.npc, player)
-- Unlock queue, reset action timer and unfreeze NPC.
npc.unlock_actions(player_response.npc)
-- Unlock queue, reset action timer and unfreeze NPC.
npc.unlock_actions(player_response.npc)
end
end

111
npc.lua
View File

@ -44,14 +44,14 @@ npc.action_state = {
npc.log_level = {
INFO = true,
WARNING = false,
WARNING = true,
ERROR = true,
DEBUG = false
}
npc.texture_check = {
timer = 0,
interval = 0
interval = 2
}
---------------------------------------------------------------------------------------
@ -149,9 +149,9 @@ local function get_random_texture(sex, age)
end
-- Choose whether NPC can have relationships. Only 30% of NPCs cannot have relationships
local function can_have_relationships(age)
local function can_have_relationships(is_child)
-- Children can't have relationships
if not age then
if is_child then
return false
end
local chance = math.random(1,10)
@ -264,8 +264,8 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
elseif child_s <= age_chance and age_chance <= child_e then
selected_age = npc.age.child
ent.visual_size = {
x = 0.5,
y = 0.5
x = 0.75,
y = 0.75
}
ent.collisionbox = {-0.10,-0.50,-0.10, 0.10,0.40,0.10}
ent.is_child = true
@ -310,6 +310,11 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
-- Flag that determines if NPC can have a relationship
ent.can_have_relationship = can_have_relationships(ent.is_child)
ent.infotext = "Interested in relationships: "..dump(ent.can_have_relationship)
-- Flag to determine if NPC can receive gifts
ent.can_receive_gifts = ent.can_have_relationship
-- Initialize relationships object
ent.relationships = {}
@ -455,6 +460,24 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
ent.object:set_properties(ent)
end
---------------------------------------------------------------------------------------
-- Trading functions
---------------------------------------------------------------------------------------
function npc.generate_trade_list_from_inventory(self)
local list = {}
for i = 1, #self.inventory do
list[npc.get_item_name(self.inventory[i])] = {}
end
self.trader_data.trade_list.both = list
end
function npc.set_trading_status(self, status)
-- Set status
self.trader_data.trader_status = status
-- Re-generate trade offers
npc.trade.generate_trade_offers_by_status(self)
end
---------------------------------------------------------------------------------------
-- Inventory functions
---------------------------------------------------------------------------------------
@ -743,6 +766,14 @@ npc.schedule_types = {
["date_based"] = "date_based"
}
npc.schedule_properties = {
put_item = "put_item",
put_multiple_items = "put_multiple_items",
take_item = "take_item",
trader_status = "trader_status",
can_receive_gifts = "can_receive_gifts"
}
local function get_time_in_hours()
return minetest.get_timeofday() * 24
end
@ -861,6 +892,41 @@ function npc.delete_schedule_entry(self, schedule_type, date, time)
end
end
function npc.schedule_change_property(self, property, args)
if property == npc.schedule_properties.trader_status then
-- Get status from args
local status = args.status
-- Set status to NPC
npc.set_trading_status(self, status)
elseif property == npc.schedule_properties.put_item then
local itemstring = args.itemstring
-- Add item
npc.add_item_to_inventory_itemstring(self, itemstring)
elseif property == npc.schedule_properties.put_multiple_items then
local itemlist = args.itemlist
for i = 1, #itemlist do
local itemlist_entry = itemlist[i]
local current_itemstring = itemlist[i].name
if itemlist_entry.random == true then
current_itemstring = current_itemstring
.." "..dump(math.random(itemlist_entry.min, itemlist_entry.max))
else
current_itemstring = current_itemstring.." "..tostring(itemlist_entry.count)
end
-- Add item to inventory
npc.add_item_to_inventory_itemstring(self, current_itemstring)
end
elseif property == npc.schedule_properties.take_item then
local itemstring = args.itemstring
-- Add item
npc.take_item_from_inventory_itemstring(self, itemstring)
elseif property == npc.schedule_properties.can_receive_gifts then
local value = args.can_receive_gifts
-- Set status
self.can_receive_gifts = value
end
end
---------------------------------------------------------------------------------------
-- NPC Definition
---------------------------------------------------------------------------------------
@ -884,12 +950,17 @@ mobs:register_mob("advanced_npc:npc", {
drawtype = "front",
textures = {
{"npc_male1.png"},
--{"npc_male2.png"},
--{"npc_male3.png"},
--{"npc_male4.png"},
--{"npc_male5.png"},
--{"npc_male6.png"},
{"npc_female1.png"}, -- female by nuttmeg20
},
child_texture = {
{"npc_baby_male1.png"},
{"npc_baby_female1.png"},
},
{"npc_baby_female1.png"},
},
makes_footstep_sound = true,
sounds = {},
-- Added walk chance
@ -934,16 +1005,14 @@ mobs:register_mob("advanced_npc:npc", {
local item = clicker:get_wielded_item()
local name = clicker:get_player_name()
--self.child = true
--self.textures = {"mobs_npc_child_male1.png"}
--self.base_texture = "mobs_npc_child_male1.png"
--self.object:set_properties(self)
npc.log("DEBUG", "Right-clicked NPC: "..dump(self))
-- Receive gift or start chat. If player has no item in hand
-- then it is going to start chat directly
if self.can_have_relationship and item:to_table() ~= nil then
minetest.log("self.can_have_relationship: "..dump(self.can_have_relationship)..", self.can_receive_gifts: "..dump(self.can_receive_gifts)..", table: "..dump(item:to_table()))
if self.can_have_relationship
and self.can_receive_gifts
and item:to_table() ~= nil then
-- Get item name
local item = minetest.registered_items[item:get_name()]
local item_name = item.description
@ -980,13 +1049,19 @@ mobs:register_mob("advanced_npc:npc", {
-- NPC is initialized, check other variables
-- Check child texture issues
if self.is_child then
-- Check texture
npc.texture_check.timer = npc.texture_check.timer + dtime
if npc.texture_check.timer > npc.texture_check.interval then
-- Reset timer
npc.texture_check.timer = 0
-- Set hornytimer to zero every 60 seconds so that children
-- don't grow automatically
self.hornytimer = 0
-- Set correct textures
self.texture = {self.selected_texture}
self.base_texture = {self.selected_texture}
self.object:set_properties(self)
npc.log("WARNING", "Corrected textures on NPC child "..dump(self.npc_name))
-- Set interval to large interval so this code isn't called frequently
npc.texture_check.interval = 60
end
@ -1086,12 +1161,16 @@ mobs:register_mob("advanced_npc:npc", {
npc.log("DEBUG", "Adding actions to action queue")
-- Add to action queue all actions on schedule
for i = 1, #schedule[time] do
if schedule[time][i].action == nil then
--minetest.log("schedule[time]: "..dump(schedule[time]))
if schedule[time][i].task ~= nil then
-- Add task
npc.add_task(self, schedule[time][i].task, schedule[time][i].args)
else
elseif schedule[time][i].action ~= nil then
-- Add action
npc.add_action(self, schedule[time][i].action, schedule[time][i].args)
elseif schedule[time][i].property ~= nil then
-- Change NPC property
npc.schedule_change_property(self, schedule[time][i].property, schedule[time][i].args)
end
end
npc.log("DEBUG", "New action queue: "..dump(self.actions))

View File

@ -27,8 +27,8 @@ npc.relationships = {}
npc.relationships.ITEM_GIFT_EFFECT = 2.5
-- Expected values for these are 720 each respectively
npc.relationships.GIFT_TIMER_INTERVAL = 2
npc.relationships.RELATIONSHIP_DECREASE_TIMER_INTERVAL = 60
npc.relationships.GIFT_TIMER_INTERVAL = 360
npc.relationships.RELATIONSHIP_DECREASE_TIMER_INTERVAL = 720
npc.relationships.RELATIONSHIP_PHASE = {}
-- Define phases
@ -360,7 +360,7 @@ local function show_receive_gift_reaction(self, item_name, modifier, clicker_nam
effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, "default_item_smoke.png")
--minetest.log("Item name: "..item_name..", sex: "..self.sex)
local message_to_send = npc.relationships.get_response_for_disliked_item(item_name, self.sex)
npc.name(self.npc_name, clicker_name, message_to_send)
npc.chat(self.npc_name, clicker_name, message_to_send)
end
end

View File

@ -88,16 +88,72 @@ local function get_basic_schedule()
-- Allow mobs_redo wandering
[3] = {action = npc.actions.cmd.FREEZE, args = {freeze = false}}
},
-- Noon actions: go inside the house
-- This will be executed around 12 PM MTG time
noon_actions = {
-- Walk to a sittable node
[1] = {task = npc.actions.cmd.WALK_TO_POS, args = {
end_pos = {place_type=npc.places.PLACE_TYPE.SITTABLE.PRIMARY, use_access_node=true},
walkable = {"cottages:bench"}
}
},
-- Sit on the node
[2] = {task = npc.actions.cmd.USE_SITTABLE, args = {
pos = npc.places.PLACE_TYPE.SITTABLE.PRIMARY,
action = npc.actions.const.sittable.SIT
}
},
-- Stay put into place
[3] = {action = npc.actions.cmd.FREEZE, args = {freeze = true}}
},
-- Afternoon actions: go inside the house
-- This will be executed around 1 PM MTG time
afternoon_actions = {
-- Get up of the sit
[1] = {task = npc.actions.cmd.USE_SITTABLE, args = {
pos = npc.places.PLACE_TYPE.SITTABLE.PRIMARY,
action = npc.actions.const.sittable.GET_UP
}
},
-- Give NPC money to buy from player
[2] = {property = npc.schedule_properties.put_multiple_items, args = {
itemlist = {
{name="default:iron_lump", random=true, min=2, max=4}
}
}
},
-- Change trader status to "trader"
[3] = {property = npc.schedule_properties.trader_status, args = {
status = npc.trade.TRADER
}
},
[4] = {property = npc.schedule_properties.can_receive_gifts, args = {
value = true
}
},
-- Allow mobs_redo wandering
[5] = {action = npc.actions.cmd.FREEZE, args = {freeze = false}}
},
-- Afternoon actions: go inside the house
-- This will be executed around 6 PM MTG time
afternoon_actions = {
late_afternoon_actions = {
-- Change trader status to "none"
[1] = {property = npc.schedule_properties.trader_status, args = {
status = npc.trade.NONE
}
},
-- Enable gift receiving again
[2] = {property = npc.schedule_properties.can_receive_gifts, args = {
can_receive_gifts = true
}
},
-- Get inside home
[1] = {task = npc.actions.cmd.WALK_TO_POS, args = {
[3] = {task = npc.actions.cmd.WALK_TO_POS, args = {
end_pos = npc.places.PLACE_TYPE.OTHER.HOME_INSIDE,
walkable = {}}
},
-- Allow mobs_redo wandering
[2] = {action = npc.actions.cmd.FREEZE, args = {freeze = false}}
[4] = {action = npc.actions.cmd.FREEZE, args = {freeze = false}}
},
-- Evening actions: walk to bed and use it.
-- This will be executed around 10 PM MTG time
@ -164,6 +220,7 @@ function spawner.scan_area(pos1, pos2)
result.bed_type = spawner.get_nodes_by_type(start_pos, end_pos, npc.places.nodes.BED_TYPE)
result.sittable_type = spawner.get_nodes_by_type(start_pos, end_pos, npc.places.nodes.SITTABLE_TYPE)
-- Filter out
result.furnace_type = spawner.get_nodes_by_type(start_pos, end_pos, npc.places.nodes.FURNACE_TYPE)
result.storage_type = spawner.get_nodes_by_type(start_pos, end_pos, npc.places.nodes.STORAGE_TYPE)
result.openable_type = spawner.get_nodes_by_type(start_pos, end_pos, npc.places.nodes.OPENABLE_TYPE)
@ -190,49 +247,77 @@ function spawner.assign_places(self, pos)
local node_data = minetest.deserialize(meta:get_string("node_data"))
-- Assign plotmarker
npc.places.add_public(self, npc.places.PLACE_TYPE.OTHER.HOME_PLOTMARKER,
npc.places.add_shared(self, npc.places.PLACE_TYPE.OTHER.HOME_PLOTMARKER,
npc.places.PLACE_TYPE.OTHER.HOME_PLOTMARKER, pos)
-- Assign entrance door and related locations
if entrance ~= nil and entrance.node_pos ~= nil then
npc.places.add_public(self, npc.places.PLACE_TYPE.OPENABLE.HOME_ENTRANCE_DOOR, npc.places.PLACE_TYPE.OPENABLE.HOME_ENTRANCE_DOOR, entrance.node_pos)
npc.places.add_shared(self, npc.places.PLACE_TYPE.OPENABLE.HOME_ENTRANCE_DOOR, npc.places.PLACE_TYPE.OPENABLE.HOME_ENTRANCE_DOOR, entrance.node_pos)
-- Find the position inside and outside the door
local entrance_inside = npc.places.find_node_behind_door(entrance.node_pos)
local entrance_outside = npc.places.find_node_in_front_of_door(entrance.node_pos)
-- Assign these places to NPC
npc.places.add_public(self, npc.places.PLACE_TYPE.OTHER.HOME_INSIDE, npc.places.PLACE_TYPE.OTHER.HOME_INSIDE, entrance_inside)
npc.places.add_public(self, npc.places.PLACE_TYPE.OTHER.HOME_OUTSIDE, npc.places.PLACE_TYPE.OTHER.HOME_OUTSIDE, entrance_outside)
npc.places.add_shared(self, npc.places.PLACE_TYPE.OTHER.HOME_INSIDE, npc.places.PLACE_TYPE.OTHER.HOME_INSIDE, entrance_inside)
npc.places.add_shared(self, npc.places.PLACE_TYPE.OTHER.HOME_OUTSIDE, npc.places.PLACE_TYPE.OTHER.HOME_OUTSIDE, entrance_outside)
end
-- Assign beds
if #node_data.bed_type > 0 then
-- Find unowned bed
for i = 1, #node_data.bed_type do
-- Check if bed has owner
--minetest.log("Node: "..dump(node_data.bed_type[i]))
if node_data.bed_type[i].owner == "" then
-- If bed has no owner, check if it is accessible
local empty_nodes = npc.places.find_node_orthogonally(
node_data.bed_type[i].node_pos, {"air"}, 0)
-- Check if bed is accessible
if #empty_nodes > 0 then
-- Set owner to this NPC
node_data.bed_type[i].owner = self.npc_id
-- Assign node to NPC
npc.places.add_owned(self, npc.places.PLACE_TYPE.BED.PRIMARY,
npc.places.PLACE_TYPE.BED.PRIMARY, node_data.bed_type[i].node_pos, empty_nodes[1].pos)
-- Store changes to node_data
meta:set_string("node_data", minetest.serialize(node_data))
npc.log("DEBUG", "Added bed at "..minetest.pos_to_string(node_data.bed_type[i].node_pos)
.." to NPC "..dump(self.npc_name))
break
end
end
end
-- Assign a specific sittable node to a NPC.
npc.places.add_unowned_accessible_place(self, node_data.bed_type,
npc.places.PLACE_TYPE.BED.PRIMARY)
-- Store changes to node_data
meta:set_string("node_data", minetest.serialize(node_data))
end
-- Assign sits
if #node_data.sittable_type > 0 then
-- Check if there are same or more amount of sits as beds
if #node_data.sittable_type >= #node_data.bed_type then
-- Assign a specific sittable node to a NPC.
npc.places.add_unowned_accessible_place(self, node_data.sittable_type,
npc.places.PLACE_TYPE.SITTABLE.PRIMARY)
-- Store changes to node_data
meta:set_string("node_data", minetest.serialize(node_data))
end
-- Add all sits to places as shared since NPC should be able to sit
-- at any accessible sit
npc.places.add_shared_accessible_place(self, node_data.sittable_type,
npc.places.PLACE_TYPE.SITTABLE.SHARED)
end
-- Assign furnaces
if #node_data.furnace_type > 0 then
-- Check if there are same or more amount of furnace as beds
if #node_data.furnace_type >= #node_data.bed_type then
-- Assign a specific furnace node to a NPC.
npc.places.add_unowned_accessible_place(self, node_data.furnace_type,
npc.places.PLACE_TYPE.FURNACE.PRIMARY)
-- Store changes to node_data
meta:set_string("node_data", minetest.serialize(node_data))
end
-- Add all furnaces to places as shared since NPC should be able to use
-- any accessible furnace
npc.places.add_shared_accessible_place(self, node_data.furnace_type,
npc.places.PLACE_TYPE.FURNACE.SHARED)
end
-- Assign storage nodes
if #node_data.storage_type > 0 then
-- Check if there are same or more amount of storage as beds
if #node_data.storage_type >= #node_data.bed_type then
-- Assign a specific storage node to a NPC.
npc.places.add_unowned_accessible_place(self, node_data.storage_type,
npc.places.PLACE_TYPE.STORAGE.PRIMARY)
-- Store changes to node_data
meta:set_string("node_data", minetest.serialize(node_data))
end
-- Add all storage-types to places as shared since NPC should be able
-- to use other storage nodes as well.
npc.places.add_shared_accessible_place(self, node_data.storage_type,
npc.places.PLACE_TYPE.STORAGE.SHARED)
end
--local plot_info = minetest.deserialize(meta:get_string("plot_info"))
--minetest.log("Plot info:"..dump(plot_info))
npc.log("DEBUG", "Places for NPC "..self.npc_name..": "..dump(self.places_map))
-- Make NPC go into their house
@ -253,8 +338,14 @@ function spawner.assign_schedules(self, pos)
-- Add schedule entry for morning actions
npc.add_schedule_entry(self, npc.schedule_types.generic, 0, 8, nil, basic_schedule.morning_actions)
-- Add schedule entry for noon actions
npc.add_schedule_entry(self, npc.schedule_types.generic, 0, 12, nil, basic_schedule.noon_actions)
-- Add schedule entry for afternoon actions
npc.add_schedule_entry(self, npc.schedule_types.generic, 0, 18, nil, basic_schedule.afternoon_actions)
npc.add_schedule_entry(self, npc.schedule_types.generic, 0, 13, nil, basic_schedule.afternoon_actions)
-- Add schedule entry for late afternoon actions
npc.add_schedule_entry(self, npc.schedule_types.generic, 0, 18, nil, basic_schedule.late_afternoon_actions)
-- Add schedule entry for evening actions
npc.add_schedule_entry(self, npc.schedule_types.generic, 0, 22, nil, basic_schedule.evening_actions)
@ -348,17 +439,15 @@ end
-- This function takes care of calculating how many NPCs will be spawn
function spawner.calculate_npc_spawning(pos)
-- Check node
local node = minetest.get_node(pos)
if node.name ~= "advanced_npc:plotmarker_auto_spawner" then
return
end
-- Check node metadata
local meta = minetest.get_meta(pos)
if meta:get_string("replaced") ~= "true" then
return
end
-- Get nodes for this building
local node_data = minetest.deserialize(meta:get_string("node_data"))
if node_data == nil then
npc.log("ERROR", "Mis-configured advanced_npc:plotmarker_auto_spawner at position: "..minetest.pos_to_string(pos))
npc.log("ERROR", "Mis-configured mg_villages:plotmarker at position: "..minetest.pos_to_string(pos))
return
end
-- Check number of beds
@ -463,15 +552,16 @@ end
-- Also, the building is scanned for NPC-usable nodes and the amount
-- of NPCs to spawn and the interval is calculated.
function spawner.replace_mg_villages_plotmarker(pos)
-- Check if it is already replaced
-- if minetest.get_node(pos).name == "advanced_npc:plotmarker_auto_spawner" then
-- return
-- end
-- Get the meta at the current position
local meta = minetest.get_meta(pos)
local village_id = meta:get_string("village_id")
local plot_nr = meta:get_int("plot_nr")
local infotext = meta:get_string("infotext")
-- Check for nil values above
if (not village_id or (village and village == ""))
or (not plot_nr or (plot_nr and plot_nr == 0)) then
return
end
-- Following line from mg_villages mod, protection.lua
local btype = mg_villages.all_villages[village_id].to_add_data.bpos[plot_nr].btype
local building_data = mg_villages.BUILDINGS[btype]
@ -483,7 +573,7 @@ function spawner.replace_mg_villages_plotmarker(pos)
npc.log("INFO", "Replacing mg_villages:plotmarker at "..minetest.pos_to_string(pos))
-- Replace the plotmarker for auto-spawner
minetest.set_node(pos, {name="advanced_npc:plotmarker_auto_spawner"})
--minetest.set_node(pos, {name="advanced_npc:plotmarker_auto_spawner"})
-- Store old plotmarker metadata again
meta:set_string("village_id", village_id)
meta:set_int("plot_nr", plot_nr)
@ -531,9 +621,10 @@ function spawner.replace_mg_villages_plotmarker(pos)
meta:set_string("npc_stats", minetest.serialize(npc_stats))
-- Set replaced
meta:set_string("replaced", "true")
-- Calculate how many NPCs will spawn
spawner.calculate_npc_spawning(pos)
-- Stop searching for building type
break
end
end
end
@ -545,19 +636,19 @@ if minetest.get_modpath("mg_villages") ~= nil then
-- Node registration
-- This node is currently a slightly modified mg_villages:plotmarker
-- TODO: Change formspec to a more detailed one.
minetest.register_node("advanced_npc:plotmarker_auto_spawner", {
description = "Automatic NPC Spawner",
drawtype = "nodebox",
tiles = {"default_stone.png"},
paramtype = "light",
paramtype2 = "facedir",
node_box = {
type = "fixed",
fixed = {
{-0.5+2/16, -0.5, -0.5+2/16, 0.5-2/16, -0.5+2/16, 0.5-2/16},
--{-0.5+0/16, -0.5, -0.5+0/16, 0.5-0/16, -0.5+0/16, 0.5-0/16},
}
},
minetest.override_item("mg_villages:plotmarker", {
-- description = "Automatic NPC Spawner",
-- drawtype = "nodebox",
-- tiles = {"default_stone.png"},
-- paramtype = "light",
-- paramtype2 = "facedir",
-- node_box = {
-- type = "fixed",
-- fixed = {
-- {-0.5+2/16, -0.5, -0.5+2/16, 0.5-2/16, -0.5+2/16, 0.5-2/16},
-- --{-0.5+0/16, -0.5, -0.5+0/16, 0.5-0/16, -0.5+0/16, 0.5-0/16},
-- }
-- },
walkable = false,
groups = {cracky=3,stone=2},
@ -569,7 +660,7 @@ if minetest.get_modpath("mg_villages") ~= nil then
--minetest.log("First-floor beds: "..dump(spawner.filter_first_floor_nodes(nodedata.bed_type, pos)))
--local entrance = npc.places.find_entrance_from_openable_nodes(nodedata.openable_type, pos)
--minetest.log("Found entrance: "..dump(entrance))
minetest.log("Replaced: "..dump(minetest.get_meta(pos):get_string("replaced")))
-- for i = 1, #nodedata.bed_type do
-- nodedata.bed_type[i].owner = ""
-- end
@ -580,22 +671,22 @@ if minetest.get_modpath("mg_villages") ~= nil then
return mg_villages.plotmarker_formspec( pos, nil, {}, clicker )
end,
on_receive_fields = function(pos, formname, fields, sender)
return mg_villages.plotmarker_formspec( pos, formname, fields, sender );
end,
-- on_receive_fields = function(pos, formname, fields, sender)
-- return mg_villages.plotmarker_formspec( pos, formname, fields, sender );
-- end,
on_timer = function(pos, elapsed)
npc.spawner.spawn_npc(pos)
end,
-- protect against digging
can_dig = function(pos, player)
local meta = minetest.get_meta(pos);
if (meta and meta:get_string("village_id") ~= "" and meta:get_int("plot_nr") and meta:get_int("plot_nr") > 0 ) then
return false;
end
return true;
end
-- can_dig = function(pos, player)
-- local meta = minetest.get_meta(pos);
-- if (meta and meta:get_string("village_id") ~= "" and meta:get_int("plot_nr") and meta:get_int("plot_nr") > 0 ) then
-- return false;
-- end
-- return true;
-- end
})
-- LBM Registration
@ -621,7 +712,7 @@ if minetest.get_modpath("mg_villages") ~= nil then
label = "Replace mg_villages:plotmarker with Advanced NPC auto spawners",
nodenames = {"mg_villages:plotmarker"},
interval = 10,--npc.spawner.replacement_interval,
chance = 1, --5,
chance = 1,--5,
catch_up = true,
action = function(pos, node, active_object_count, active_object_count_wider)
-- Check if replacement is needed
@ -632,14 +723,14 @@ if minetest.get_modpath("mg_villages") ~= nil then
if npc.spawner.replace_activated then
-- Replace mg_villages:plotmarker
spawner.replace_mg_villages_plotmarker(pos)
-- Set NPCs to spawn
spawner.calculate_npc_spawning(pos)
end
end
})
end
--minetest.register_alias_force("mg_villages:plotmarker", )
-- Chat commands to manage spawners
minetest.register_chatcommand("restore_plotmarkers", {
description = "Replaces all advanced_npc:plotmarker_auto_spawner with mg_villages:plotmarker in the specified radius.",
@ -662,7 +753,7 @@ minetest.register_chatcommand("restore_plotmarkers", {
local start_pos = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}
local end_pos = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
local nodes = minetest.find_nodes_in_area_under_air(start_pos, end_pos,
{"advanced_npc:plotmarker_auto_spawner"})
{"mg_villages:plotmarker"})
-- Check if we have nodes to replace
minetest.chat_send_player(name, "Found "..dump(#nodes).." nodes to replace...")
if #nodes == 0 then
@ -670,13 +761,10 @@ minetest.register_chatcommand("restore_plotmarkers", {
end
-- Replace all nodes
for i = 1, #nodes do
--minetest.log(dump(nodes[i]))
local meta = minetest.get_meta(nodes[i])
local village_id = meta:get_string("village_id")
local plot_nr = meta:get_int("plot_nr")
local infotext = meta:get_string("infotext")
-- Replace node
minetest.set_node(nodes[i], {name="mg_villages:plotmarker"})
-- Set metadata
meta = minetest.get_meta(nodes[i])
meta:set_string("village_id", village_id)
@ -686,6 +774,7 @@ minetest.register_chatcommand("restore_plotmarkers", {
meta:set_string("node_data", nil)
meta:set_string("npcs", nil)
meta:set_string("npc_stats", nil)
meta:set_string("replaced", "false")
end
minetest.chat_send_player(name, "Finished replacement of "..dump(#nodes).." auto-spawners successfully")
end