Dialogues: Re-write dialogues to allow Minetest's like registration system instead of the actual table lookup system. In general, dialogues now are registered in similar fashion as nodes, entities and others are registered in Minetest.

Relationships: Re-write to the way favorite/disliked items are defined, now allow Minetest's like registration as well.
Random data: Random data now includes registrations for gift items and dialogues.
This commit is contained in:
Hector Franqui 2017-07-13 19:01:28 -04:00
parent bf935fd091
commit e79fb91ff3
7 changed files with 1274 additions and 675 deletions

View File

@ -1,20 +1,6 @@
-------------------------------------------------------------------------------------
-- NPC dialogue code by Zorman2000 -- NPC dialogue code by Zorman2000
-- Dialogue definitions: -------------------------------------------------------------------------------------
-- TODO: Complete
-- {
-- text: "",
-- ^ The "spoken" dialogue line
-- flag:
-- ^ If the flag with the specified name has the specified value
-- then this dialogue is valid
-- {
-- name: ""
-- ^ Name of the flag
-- value:
-- ^ Expected value of the flag. A flag can be a function. In such a case, it is
-- expected the function will return this value.
-- }
-- }
npc.dialogue = {} npc.dialogue = {}
@ -37,11 +23,144 @@ npc.dialogue.dialogue_results = {
yes_no_dialogue = {} yes_no_dialogue = {}
} }
--------------------------------------------------------------------------------------- npc.dialogue.tags = {
UNISEX = "unisex",
MALE = "male",
FEMALE = "female",
-- Relationship based tags - these are one-to-one with the
-- phase names.
PHASE_1 = "phase1",
PHASE_2 = "phase2",
PHASE_3 = "phase3",
PHASE_4 = "phase4",
PHASE_5 = "phase5",
GIFT_ITEM_HINT = "gift_item_hint",
GIFT_ITEM_RESPONSE = "gift_item_response",
GIFT_ITEM_LIKED = "gift_item_liked",
GIFT_ITEM_UNLIKED = "gift_item_unliked",
-- Occupation-based tags - these are one-to-one with the
-- default occupation names
BASIC = "basic", -- Dialogues related to the basic occupation should
-- use this. As basic occupation is generic, any occupation
-- should be able to use these dialogues.
DEFAULT_FARMER = "default_farmer",
DEFAULT_COOKER = "default_cooker"
}
-- This table will contain all the registered dialogues for NPCs
npc.dialogue.registered_dialogues = {}
--------------------------------------------------------------------------------------
-- Dialogue registration functions
-- All dialogues will be registered by providing a definition.
-- A unique key will be assigned to them. The dialogue definition is the following:
-- {
-- text: "",
-- ^ The "spoken" dialogue line
-- flag:
-- ^ If the flag with the specified name has the specified value
-- then this dialogue is valid
-- {
-- name: ""
-- ^ Name of the flag
-- value:
-- ^ Expected value of the flag. A flag can be a function. In such a case, it is
-- expected the function will return this value.
-- },
-- tags = {
-- -- Tags are an array of string that allow to classify dialogues
-- -- A dialogue can have as many tags as desired and can take any form.
-- -- However, for consistency, some predefined tags can be found at
-- -- npc.dialogue.tags.
-- -- Example:
-- "phase1",
-- "any"
-- }
-- responses = {
-- -- Array of responses the player can choose. A response can be of
-- -- two types: as [1] or as [2] (see example below)
-- [1] = {
-- text = "Yes",
-- -- Text displayed to the player
-- action_type = "dialogue",
-- -- Type of action that happens when the player chooses this response.
-- -- can be "dialogue" or "function". This example shows "dialogue"
-- action = {
-- text = "It's so beautiful, and big, and large, and infinite, and..."
-- },
-- },
-- -- A table containing a dialogue. This means you can include not only
-- -- text but also flag and responses as well. Dialogues are recursive.
-- [2] = {
-- text = "No",
-- action_type = "function",
-- action = function(self, player)
-- -- A function will have access to self, which is the NPC
-- -- and the player, which is the player ObjectRef. You can
-- -- pretty much do anything here. The example here is very simple,
-- -- just sending a chat message. But you can add items to players
-- -- or to NPCs and so on.
-- minetest.chat_send_player(player:get_player_name(), "Oh, ok...")
-- end,
-- },
-- }
-- }
--------------------------------------------------------------------------------------
-- The register dialogue function will just receive the definition as
-- explained above. The unique key will be the index it gets into the
-- array when inserted.
function npc.dialogue.register_dialogue(def)
-- If def has not tags then apply the default ones
if not def.tags then
def.tags = {npc.dialogue.tags.UNISEX, npc.dialogue.tags.PHASE_1}
end
-- Insert dialogue into table
table.insert(npc.dialogue.registered_dialogues, def)
--minetest.log("Dialogues: "..dump(npc.dialogue.registered_dialogues))
end
-- This function returns a table of dialogues that meet the given
-- tags array. The keys in the table are the keys in
-- npc.dialogue.registered_dialogues, therefore you can use them to
--retrieve specific dialogues. However, it should be stored by the NPC.
function npc.dialogue.search_dialogue_by_tags(tags, find_all)
--minetest.log("Tags being searched: "..dump(tags))
local result = {}
for key, def in pairs(npc.dialogue.registered_dialogues) do
-- Check if def.tags have any of the provided tags
local tags_found = 0
--minetest.log("Tags on dialogue def: "..dump(def.tags))
for i = 1, #tags do
if npc.utils.array_contains(def.tags, tags[i]) then
tags_found = tags_found + 1
end
end
--minetest.log("Tags found: "..dump(tags_found))
-- Check if we found all tags
if find_all then
if tags_found == #tags then
-- Add result
result[key] = def
end
elseif not find_all then
if tags_found == #tags or tags_found == #def.tags then
-- Add result
result[key] = def
end
end
-- if (find_all == true and tags_found == #tags)
-- or (not find_all and tags_found == #def.tags) then
-- end
end
return result
end
--------------------------------------------------------------------------------------
-- Dialogue box definitions -- Dialogue box definitions
-- The dialogue boxes are used for the player to interact with the -- The dialogue boxes are used for the player to interact with the
-- NPC in dialogues. -- NPC in dialogues.
--------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------
-- Creates and shows a multi-option dialogue based on the number of responses -- Creates and shows a multi-option dialogue based on the number of responses
-- that the dialogue object contains -- that the dialogue object contains
function npc.dialogue.show_options_dialogue(self, function npc.dialogue.show_options_dialogue(self,
@ -67,10 +186,14 @@ function npc.dialogue.show_options_dialogue(self,
-- Create entry on options_dialogue table -- Create entry on options_dialogue table
npc.dialogue.dialogue_results.options_dialogue[player_name] = { npc.dialogue.dialogue_results.options_dialogue[player_name] = {
npc = self, npc = self,
is_married_dialogue = (dialogue.dialogue_type == npc.dialogue.dialogue_type.married), is_married_dialogue =
is_casual_trade_dialogue = (dialogue.dialogue_type == npc.dialogue.dialogue_type.casual_trade), (dialogue.dialogue_type == npc.dialogue.dialogue_type.married),
is_dedicated_trade_dialogue = (dialogue.dialogue_type == npc.dialogue.dialogue_type.dedicated_trade), is_casual_trade_dialogue =
is_custom_trade_dialogue = (dialogue.dialogue_type == npc.dialogue.dialogue_type.custom_trade), (dialogue.dialogue_type == npc.dialogue.dialogue_type.casual_trade),
is_dedicated_trade_dialogue =
(dialogue.dialogue_type == npc.dialogue.dialogue_type.dedicated_trade),
is_custom_trade_dialogue =
(dialogue.dialogue_type == npc.dialogue.dialogue_type.custom_trade),
casual_trade_type = dialogue.casual_trade_type, casual_trade_type = dialogue.casual_trade_type,
options = responses options = responses
} }
@ -104,9 +227,9 @@ function npc.dialogue.show_yes_no_dialogue(self,
minetest.show_formspec(player_name, "advanced_npc:yes_no", formspec) minetest.show_formspec(player_name, "advanced_npc:yes_no", formspec)
end end
--------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------
-- Dialogue methods -- Dialogue methods
--------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------
-- This function sets a unique response ID (made of <depth>:<response index>) to -- This function sets a unique response ID (made of <depth>:<response index>) to
-- each response that features a function. This is to be able to locate the -- each response that features a function. This is to be able to locate the
-- function easily later -- function easily later
@ -135,43 +258,79 @@ end
-- Select random dialogue objects for an NPC based on sex -- Select random dialogue objects for an NPC based on sex
-- and the relationship phase with player -- and the relationship phase with player
function npc.dialogue.select_random_dialogues_for_npc(sex, phase, favorite_items, disliked_items) function npc.dialogue.select_random_dialogues_for_npc(self, phase)
local result = { local result = {
normal = {}, normal = {},
hints = {} hints = {}
} }
local dialogues = npc.data.DIALOGUES.female local phase_tag = "phase1"
if sex == npc.MALE then if phase then
dialogues = npc.data.DIALOGUES.male phase_tag = phase
end end
dialogues = dialogues[phase]
local search_tags = {
"unisex",
self.sex,
phase_tag,
self.occupation
}
local dialogues = npc.dialogue.search_dialogue_by_tags(search_tags)
minetest.log("dialogues found by tag search: "..dump(dialogues))
-- Determine how many dialogue lines the NPC will have -- Determine how many dialogue lines the NPC will have
local number_of_dialogues = math.random(npc.dialogue.MIN_DIALOGUES, npc.dialogue.MAX_DIALOGUES) local number_of_dialogues = math.random(npc.dialogue.MIN_DIALOGUES, npc.dialogue.MAX_DIALOGUES)
for i = 1,number_of_dialogues do for i = 1,number_of_dialogues do
local dialogue_id = math.random(1, #dialogues) local dialogue_id = math.random(1, #dialogues)
result.normal[i] = dialogues[dialogue_id] result.normal[i] = dialogue_id
set_response_ids_recursively(result.normal[i], 0, dialogue_id) --set_response_ids_recursively(result.normal[i], 0, dialogue_id)
end end
-- Add item hints. -- Add item hints.
-- Favorite items
for i = 1, 2 do for i = 1, 2 do
result.hints[i] = {} local hints = npc.relationships.get_dialogues_for_gift_item(
result.hints[i].text = self.gift_data.favorite_items["fav"..tostring(i)],
npc.relationships.get_hint_for_favorite_item(favorite_items["fav"..tostring(i)], sex, phase) npc.dialogue.tags.GIFT_ITEM_HINT,
npc.dialogue.tags.GIFT_ITEM_LIKED,
self.sex,
phase_tag)
for key, value in pairs(hints) do
result.hints[i] = key
end
end end
-- Disliked items
for i = 3, 4 do for i = 3, 4 do
result.hints[i] = {} local hints = npc.relationships.get_dialogues_for_gift_item(
result.hints[i].text = self.gift_data.disliked_items["dis"..tostring(i-2)],
npc.relationships.get_hint_for_disliked_item(disliked_items["dis"..tostring(i-2)], sex) npc.dialogue.tags.GIFT_ITEM_HINT,
npc.dialogue.tags.GIFT_ITEM_UNLIKED,
self.sex)
for key, value in pairs(hints) do
result.hints[i] = key
end
end end
-- Favorite items
-- for i = 1, 2 do
-- result.hints[i] = {}
-- result.hints[i].text =
-- npc.relationships.get_hint_for_favorite_item(
-- self.gift_data.favorite_items["fav"..tostring(i)], self.sex, phase_tag)
-- end
-- -- Disliked items
-- for i = 3, 4 do
-- result.hints[i] = {}
-- result.hints[i].text =
-- npc.relationships.get_hint_for_disliked_item(
-- self.gift_data.disliked_items["dis"..tostring(i-2)], self.sex, phase_tag)
-- end
minetest.log("Dialogue results:"..dump(result))
return result return result
end end
@ -181,7 +340,10 @@ function npc.dialogue.create_custom_trade_options(self, player)
-- Create the action for each option -- Create the action for each option
local actions = {} local actions = {}
for i = 1, #self.trader_data.custom_trades do for i = 1, #self.trader_data.custom_trades do
table.insert(actions, function() npc.trade.show_custom_trade_offer(self, player, self.trader_data.custom_trades[i]) end) table.insert(actions,
function()
npc.trade.show_custom_trade_offer(self, player, self.trader_data.custom_trades[i])
end)
end end
-- Default text to be shown for dialogue prompt -- Default text to be shown for dialogue prompt
local text = npc.trade.CUSTOM_TRADES_PROMPT_TEXT local text = npc.trade.CUSTOM_TRADES_PROMPT_TEXT
@ -224,10 +386,11 @@ function npc.dialogue.start_dialogue(self, player, show_married_dialogue)
end end
local chance = math.random(1, 100) local chance = math.random(1, 100)
minetest.log("Chance: "..dump(chance))
if chance < 30 then if chance < 30 then
-- If NPC is a casual trader, show a sell or buy dialogue 30% of the time, depending -- Show trading options for casual traders
-- on the state of the casual trader. -- If NPC has custom trading options, these will be
-- shown as well with equal chance as the casual
-- buy/sell options
if self.trader_data.trader_status == npc.trade.NONE then if self.trader_data.trader_status == npc.trade.NONE then
-- Show custom trade options if available -- Show custom trade options if available
if table.getn(self.trader_data.custom_trades) > 0 then if table.getn(self.trader_data.custom_trades) > 0 then
@ -274,6 +437,11 @@ function npc.dialogue.process_dialogue(self, dialogue, player_name)
-- Freeze NPC actions -- Freeze NPC actions
npc.lock_actions(self) npc.lock_actions(self)
if type(dialogue) ~= "table" then
dialogue = npc.dialogue.registered_dialogues[dialogue]
minetest.log("Found dialogue: "..dump(dialogue))
end
-- Check if this dialogue has a flag definition -- Check if this dialogue has a flag definition
if dialogue.flag then if dialogue.flag then
-- Check if the NPC has this flag -- Check if the NPC has this flag

View File

@ -24,6 +24,7 @@ end
mobs.intllib = S mobs.intllib = S
dofile(path .. "/npc.lua") dofile(path .. "/npc.lua")
dofile(path .. "/utils.lua")
dofile(path .. "/spawner.lua") dofile(path .. "/spawner.lua")
dofile(path .. "/relationships.lua") dofile(path .. "/relationships.lua")
dofile(path .. "/dialogue.lua") dofile(path .. "/dialogue.lua")
@ -33,6 +34,10 @@ dofile(path .. "/actions/actions.lua")
dofile(path .. "/actions/places.lua") dofile(path .. "/actions/places.lua")
dofile(path .. "/actions/pathfinder.lua") dofile(path .. "/actions/pathfinder.lua")
dofile(path .. "/actions/node_registry.lua") dofile(path .. "/actions/node_registry.lua")
-- Load random data definitions
dofile(path .. "/random_data.lua") dofile(path .. "/random_data.lua")
dofile(path .. "/random_data/dialogues_data.lua")
dofile(path .. "/random_data/gift_items_data.lua")
dofile(path .. "/random_data/names_data.lua")
print (S("[Mod] Advanced NPC loaded")) print (S("[Mod] Advanced NPC loaded"))

106
npc.lua
View File

@ -148,7 +148,38 @@ local function get_random_texture(sex, age)
return filtered_textures[math.random(1,#filtered_textures)] return filtered_textures[math.random(1,#filtered_textures)]
end end
-- Choose whether NPC can have relationships. Only 30% of NPCs cannot have relationships function npc.get_random_texture_from_array(age, sex, textures)
local filtered_textures = {}
for i = 1, #textures do
local current_texture = textures[i]
-- Filter by age
if (sex == npc.MALE
and string.find(current_texture, sex)
and not string.find(current_texture, npc.FEMALE)
and ((age == npc.age.adult
and not string.find(current_texture, npc.age.child))
or (age == npc.age.child
and string.find(current_texture, npc.age.child))
)
)
or (sex == npc.FEMALE
and string.find(current_texture, sex)
and ((age == npc.age.adult
and not string.find(current_texture, npc.age.child))
or (age == npc.age.child
and string.find(current_texture, npc.age.child))
)
) then
table.insert(filtered_textures, current_texture)
end
end
return filtered_textures[math.random(1, #filtered_textures)]
end
-- Choose whether NPC can have relationships. Only 30% of NPCs
-- cannot have relationships
local function can_have_relationships(is_child) local function can_have_relationships(is_child)
-- Children can't have relationships -- Children can't have relationships
if is_child then if is_child then
@ -177,18 +208,18 @@ local function choose_spawn_items(self)
npc.add_item_to_inventory(self, npc.trade.prices.currency.tier3.string, currency_item_count) npc.add_item_to_inventory(self, npc.trade.prices.currency.tier3.string, currency_item_count)
-- For test -- For test
npc.add_item_to_inventory(self, "default:tree", 10) --npc.add_item_to_inventory(self, "default:tree", 10)
npc.add_item_to_inventory(self, "default:cobble", 10) --npc.add_item_to_inventory(self, "default:cobble", 10)
npc.add_item_to_inventory(self, "default:diamond", 2) --npc.add_item_to_inventory(self, "default:diamond", 2)
npc.add_item_to_inventory(self, "default:mese_crystal", 2) --npc.add_item_to_inventory(self, "default:mese_crystal", 2)
npc.add_item_to_inventory(self, "flowers:rose", 2) --npc.add_item_to_inventory(self, "flowers:rose", 2)
npc.add_item_to_inventory(self, "advanced_npc:marriage_ring", 2) --npc.add_item_to_inventory(self, "advanced_npc:marriage_ring", 2)
npc.add_item_to_inventory(self, "flowers:geranium", 2) --npc.add_item_to_inventory(self, "flowers:geranium", 2)
npc.add_item_to_inventory(self, "mobs:meat", 2) --npc.add_item_to_inventory(self, "mobs:meat", 2)
npc.add_item_to_inventory(self, "mobs:leather", 2) --npc.add_item_to_inventory(self, "mobs:leather", 2)
npc.add_item_to_inventory(self, "default:sword_stone", 2) --npc.add_item_to_inventory(self, "default:sword_stone", 2)
npc.add_item_to_inventory(self, "default:shovel_stone", 2) --npc.add_item_to_inventory(self, "default:shovel_stone", 2)
npc.add_item_to_inventory(self, "default:axe_stone", 2) --npc.add_item_to_inventory(self, "default:axe_stone", 2)
--minetest.log("Initial inventory: "..dump(self.inventory)) --minetest.log("Initial inventory: "..dump(self.inventory))
end end
@ -204,8 +235,6 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
ent = entity:get_luaentity() ent = entity:get_luaentity()
end end
ent.initialized = true
-- Avoid NPC to be removed by mobs_redo API -- Avoid NPC to be removed by mobs_redo API
ent.remove_ok = false ent.remove_ok = false
@ -264,13 +293,17 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
elseif child_s <= age_chance and age_chance <= child_e then elseif child_s <= age_chance and age_chance <= child_e then
selected_age = npc.age.child selected_age = npc.age.child
ent.visual_size = { ent.visual_size = {
x = 0.75, x = 0.60,
y = 0.75 y = 0.60
} }
ent.collisionbox = {-0.10,-0.50,-0.10, 0.10,0.40,0.10} ent.collisionbox = {-0.10,-0.50,-0.10, 0.10,0.40,0.10}
ent.is_child = true ent.is_child = true
-- For mobs_redo
ent.child = true ent.child = true
end end
-- Store the selected age
ent.age = selected_age
-- Set texture accordingly -- Set texture accordingly
local selected_texture = get_random_texture(selected_sex, selected_age) local selected_texture = get_random_texture(selected_sex, selected_age)
--minetest.log("Selected texture: "..dump(selected_texture)) --minetest.log("Selected texture: "..dump(selected_texture))
@ -310,7 +343,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
-- Flag that determines if NPC can have a relationship -- Flag that determines if NPC can have a relationship
ent.can_have_relationship = can_have_relationships(ent.is_child) ent.can_have_relationship = can_have_relationships(ent.is_child)
ent.infotext = "Interested in relationships: "..dump(ent.can_have_relationship) --ent.infotext = "Interested in relationships: "..dump(ent.can_have_relationship)
-- Flag to determine if NPC can receive gifts -- Flag to determine if NPC can receive gifts
ent.can_receive_gifts = ent.can_have_relationship ent.can_receive_gifts = ent.can_have_relationship
@ -322,10 +355,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
ent.is_married_to = nil ent.is_married_to = nil
-- Initialize dialogues -- Initialize dialogues
ent.dialogues = npc.dialogue.select_random_dialogues_for_npc(ent.sex, ent.dialogues = npc.dialogue.select_random_dialogues_for_npc(ent, "phase1")
"phase1",
ent.gift_data.favorite_items,
ent.gift_data.disliked_items)
-- Declare NPC inventory -- Declare NPC inventory
ent.inventory = initialize_inventory() ent.inventory = initialize_inventory()
@ -417,6 +447,9 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
enabled = true, enabled = true,
-- Lock for when executing a schedule -- Lock for when executing a schedule
lock = false, lock = false,
-- Queue of schedules executed
-- Used to calculate dependencies
temp_executed_queue = {},
-- An array of schedules, meant to be one per day at some point -- An array of schedules, meant to be one per day at some point
-- when calendars are implemented. Allows for only 7 schedules, -- when calendars are implemented. Allows for only 7 schedules,
-- one for each day of the week -- one for each day of the week
@ -452,6 +485,7 @@ function npc.initialize(entity, pos, is_lua_entity, npc_stats)
local offer2 = npc.trade.create_custom_sell_trade_offer("Do you want me to fix your mese sword?", "Fix mese sword", "Fix mese sword", "default:sword_mese", {"default:sword_mese", "default:copper_lump 10"}) local offer2 = npc.trade.create_custom_sell_trade_offer("Do you want me to fix your mese sword?", "Fix mese sword", "Fix mese sword", "default:sword_mese", {"default:sword_mese", "default:copper_lump 10"})
table.insert(ent.trader_data.custom_trades, offer2) table.insert(ent.trader_data.custom_trades, offer2)
ent.initialized = true
--minetest.log(dump(ent)) --minetest.log(dump(ent))
npc.log("INFO", "Successfully initialized NPC with name "..dump(ent.npc_name) npc.log("INFO", "Successfully initialized NPC with name "..dump(ent.npc_name)
..", sex: "..ent.sex..", is child: "..dump(ent.is_child) ..", sex: "..ent.sex..", is child: "..dump(ent.is_child)
@ -958,8 +992,8 @@ mobs:register_mob("advanced_npc:npc", {
{"npc_female1.png"}, -- female by nuttmeg20 {"npc_female1.png"}, -- female by nuttmeg20
}, },
child_texture = { child_texture = {
{"npc_baby_male1.png"}, {"npc_child_male1.png"},
{"npc_baby_female1.png"}, {"npc_child_female1.png"},
}, },
makes_footstep_sound = true, makes_footstep_sound = true,
sounds = {}, sounds = {},
@ -1162,6 +1196,21 @@ mobs:register_mob("advanced_npc:npc", {
-- Add to action queue all actions on schedule -- Add to action queue all actions on schedule
for i = 1, #schedule[time] do for i = 1, #schedule[time] do
--minetest.log("schedule[time]: "..dump(schedule[time])) --minetest.log("schedule[time]: "..dump(schedule[time]))
-- Check chance
local execution_chance = math.random(1, 100)
if not schedule[time][i].chance or
(schedule[time][i].chance and execution_chance <= schedule[time][i].chance) then
-- Check if entry has dependency on other entry
local dependencies_met = nil
if schedule[time][i].depends then
dependencies_met = npc.utils.array_is_subset_of_array(
self.schedules.temp_executed_queue,
schedule[time][i].depends)
end
-- Check for dependencies being met
if dependencies_met == nil or dependencies_met == true then
-- Add tasks
if schedule[time][i].task ~= nil then if schedule[time][i].task ~= nil then
-- Add task -- Add task
npc.add_task(self, schedule[time][i].task, schedule[time][i].args) npc.add_task(self, schedule[time][i].task, schedule[time][i].args)
@ -1172,7 +1221,16 @@ mobs:register_mob("advanced_npc:npc", {
-- Change NPC property -- Change NPC property
npc.schedule_change_property(self, schedule[time][i].property, schedule[time][i].args) npc.schedule_change_property(self, schedule[time][i].property, schedule[time][i].args)
end end
-- Backward compatibility check
if self.schedules.temp_executed_queue then
-- Add into execution queue to meet dependency
table.insert(self.schedules.temp_executed_queue, i)
end end
end
end
end
-- Clear execution queue
self.schedules.temp_executed_queue = {}
npc.log("DEBUG", "New action queue: "..dump(self.actions)) npc.log("DEBUG", "New action queue: "..dump(self.actions))
end end
end end

View File

View File

@ -0,0 +1,237 @@
------------------------------------------------------------------------------
-- Gift Items data definitions
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- PHASE 1
------------------------------------------------------------------------------
npc.relationships.register_favorite_item("default:apple", "phase1", "female", {
responses = {"Hey, I really wanted an apple, thank you!"},
hints = {"I could really do with an apple..."}
})
npc.relationships.register_favorite_item("farming:bread", "phase1", "female", {
responses = {"Thanks, you didn't have to, but thanks..."},
hints = {"Some fresh bread would be good!"}
})
npc.relationships.register_favorite_item("farming:seed_cotton", "phase1", "female", {
responses = {"Thank you, I will plant this really soon"},
hints = {"I would like to have some cotton plants around"}
})
npc.relationships.register_favorite_item("farming:seed_wheat", "phase1", "female", {
responses = {"Thank you! These seeds will make a good wheat plant!"},
hints = {"I've been thinking I should get wheat seeds"}
})
npc.relationships.register_favorite_item("flowers:rose", "phase1", "female", {
responses = {"Thanks..."},
hints = {"Red roses make a nice gift!"}
})
npc.relationships.register_favorite_item("flowers:geranium", "phase1", "female", {
responses = {"Oh, for me? Thank you!"},
hints = {"Blue geraniums are so beautiful"}
})
npc.relationships.register_favorite_item("default:clay_lump", "phase1", "female", {
responses = {"Thanks! Now, what can I do with this..."},
hints = {"If I had some clay lump, I may do some pottery"}
})
npc.relationships.register_favorite_item("mobs:meat_raw", "phase1", "female", {
responses = {"This will be great for tonight! Thanks"},
hints = {"A good dinner always have meat"}
})
npc.relationships.register_favorite_item("mobs:leather", "phase1", "female", {
responses = {"Thank you! I needed this!"},
hints = {"If only I could get some leather"}
})
npc.relationships.register_favorite_item("default:sapling", "phase1", "female", {
responses = {"Now I can plant that tree..."},
hints = {"I really would like an apple tree close by."}
})
npc.relationships.register_favorite_item("farming:cotton", "phase2", "female", {
responses = {"This is going to be very helpful, thank you!"},
hints = {"If I just had some cotton lying around..."}
})
npc.relationships.register_favorite_item("wool:white", "phase2", "female", {
responses = {"Thanks, you didn't have to, but thanks..."},
hints = {"Have you seen a sheep? I wish I had some white wool..."}
})
npc.relationships.register_favorite_item("default:apple", "phase3", "female", {
responses = {"Hey, I really wanted an apple, thank you!"},
hints = {"I could really do with an apple..."}
})
npc.relationships.register_favorite_item("farming:bread", "phase3", "female", {
responses = {"Thanks, you didn't have to, but thanks..."},
hints = {"Some fresh bread would be good!"}
})
npc.relationships.register_favorite_item("default:apple", "phase4", "female", {
responses = {"Hey, I really wanted an apple, thank you!"},
hints = {"I could really do with an apple..."}
})
npc.relationships.register_favorite_item("farming:bread", "phase4", "female", {
responses = {"Thanks, you didn't have to, but thanks..."},
hints = {"Some fresh bread would be good!"}
})
npc.relationships.register_favorite_item("default:apple", "phase5", "female", {
responses = {"Hey, I really wanted an apple, thank you!"},
hints = {"I could really do with an apple..."}
})
npc.relationships.register_favorite_item("farming:bread", "phase5", "female", {
responses = {"Thanks, you didn't have to, but thanks..."},
hints = {"Some fresh bread would be good!"}
})
npc.relationships.register_favorite_item("default:apple", "phase6", "female", {
responses = {"Hey, I really wanted an apple, thank you!"},
hints = {"I could really do with an apple..."}
})
npc.relationships.register_favorite_item("farming:bread", "phase6", "female", {
responses = {"Thanks, you didn't have to, but thanks..."},
hints = {"Some fresh bread would be good!"}
})
-- Male
npc.relationships.register_favorite_item("default:apple", "phase1", "male", {
responses = {"Good apple, thank you!"},
hints = {"I could do with an apple..."}
})
npc.relationships.register_favorite_item("farming:bread", "phase1", "male", {
responses = {"Thank you! I was hungry."},
hints = {"Some fresh bread would be good!"}
})
npc.relationships.register_favorite_item("farming:seed_cotton", "phase1", "male", {
responses = {"Thank you, I will plant this soon"},
hints = {"I would like to have some cotton plants around."}
})
npc.relationships.register_favorite_item("farming:seed_wheat", "phase1", "male", {
responses = {"Thank you! These seeds will make a good wheat plant!"},
hints = {"I've been thinking I should get wheat seeds."}
})
npc.relationships.register_favorite_item("default:wood", "phase1", "male", {
responses = {"Thanks, I needed this."},
hints = {"Some wood without having to cut a tree would be good.}"}
})
npc.relationships.register_favorite_item("default:tree", "phase1", "male", {
responses = {"Excellent to get that furnace going!"},
hints = {"I'm looking for some logs"}
})
npc.relationships.register_favorite_item("default:clay_lump", "phase1", "male", {
responses = {"Thanks! Now, what can I do with this..."},
hints = {"Now, some clay would be good."}
})
npc.relationships.register_favorite_item("mobs:meat_raw", "phase1", "male", {
responses = {"This makes a great meal. Thank you"},
hints = {"Meat is always great"},
})
npc.relationships.register_favorite_item("mobs:leather", "phase1", "male", {
responses = {"Time to tan some leathers!"},
hints = {"I have been needing leather these days."}
})
npc.relationships.register_favorite_item("default:sapling", "phase1", "male", {
responses = {"Thanks, I will plant this right now"},
hints = {"I really would like an apple tree close by."}
})
npc.relationships.register_favorite_item("default:apple", "phase2", "male", {
responses = {"Good apple, thank you!"},
hints = {"I could do with an apple..."}
})
npc.relationships.register_favorite_item("farming:bread", "phase2", "male", {
responses = {"Thank you! I was hungry."},
hints = {"Some fresh bread would be good!"}
})
npc.relationships.register_favorite_item("default:apple", "phase3", "male", {
responses = {"Good apple, thank you!"},
hints = {"I could do with an apple..."}
})
npc.relationships.register_favorite_item("farming:bread", "phase3", "male", {
responses = {"Thank you! I was hungry."},
hints = {"Some fresh bread would be good!"}
})
npc.relationships.register_favorite_item("default:apple", "phase4", "male", {
responses = {"Good apple, thank you!"},
hints = {"I could do with an apple..."}
})
npc.relationships.register_favorite_item("farming:bread", "phase4", "male", {
responses = {"Thank you! I was hungry."},
hints = {"Some fresh bread would be good!"}
})
npc.relationships.register_favorite_item("default:apple", "phase5", "male", {
responses = {"Good apple, thank you!"},
hints = {"I could do with an apple..."}
})
npc.relationships.register_favorite_item("farming:bread", "phase5", "male", {
responses = {"Thank you! I was hungry."},
hints = {"Some fresh bread would be good!"}
})
npc.relationships.register_favorite_item("default:apple", "phase6", "male", {
responses = {"Good apple, thank you!"},
hints = {"I could do with an apple..."}
})
npc.relationships.register_favorite_item("farming:bread", "phase6", "male", {
responses = {"Thank you! I was hungry."},
hints = {"Some fresh bread would be good!"}
})
-- Disliked items
-- Female
npc.relationships.register_disliked_item("default:stone", "female", {
responses = {"A stone, oh... why do you give this to me?"},
hints = {"Why would someone want a stone?"}
})
npc.relationships.register_disliked_item("default:cobble", "female", {
responses = {"Cobblestone? No, no, why?"},
hints = {"Anything worst than stone is cobblestone."}
})
-- Male
npc.relationships.register_disliked_item("default:stone", "male", {
responses = {"Good apple, thank you!"},
hints = {"I could do with an apple..."}
})
npc.relationships.register_disliked_item("default:cobble", "male", {
responses = {"Cobblestone!? Wow, you sure think a lot before giving a gift..."},
hints = {"If I really hate something, that's cobblestone!"}
})
npc.log("INFO", "Registered gift items: "..dump(npc.relationships.gift_items))
npc.log("INFO", "Registered dialogues: "..dump(npc.dialogue.registered_dialogues))

View File

View File

@ -38,6 +38,40 @@ npc.relationships.RELATIONSHIP_PHASE["phase3"] = {limit = 45}
npc.relationships.RELATIONSHIP_PHASE["phase4"] = {limit = 70} npc.relationships.RELATIONSHIP_PHASE["phase4"] = {limit = 70}
npc.relationships.RELATIONSHIP_PHASE["phase5"] = {limit = 100} npc.relationships.RELATIONSHIP_PHASE["phase5"] = {limit = 100}
npc.relationships.GIFT_ITEM_LIKED = "liked"
npc.relationships.GIFT_ITEM_DISLIKED = "disliked"
npc.relationships.GIFT_ITEM_HINT = "hint"
npc.relationships.GIFT_ITEM_RESPONSE = "response"
-- Favorite and disliked items tables
npc.relationships.gift_items = {
liked = {
female = {
["phase1"] = {},
["phase2"] = {},
["phase3"] = {},
["phase4"] = {},
["phase5"] = {},
["phase6"] = {}
},
male = {
["phase1"] = {},
["phase2"] = {},
["phase3"] = {},
["phase4"] = {},
["phase5"] = {},
["phase6"] = {}
}
},
disliked = {
female = {},
male = {}
}
}
npc.relationships.DEFAULT_RESPONSE_NO_GIFT_RECEIVE =
"Thank you, but I don't need anything for now."
-- Married NPC dialogue definition -- Married NPC dialogue definition
npc.relationships.MARRIED_NPC_DIALOGUE = { npc.relationships.MARRIED_NPC_DIALOGUE = {
text = "Hi darling!", text = "Hi darling!",
@ -90,6 +124,96 @@ function npc.relationships.get_relationship_phase_by_points(points)
end end
end end
-- Registration functions
-----------------------------------------------------------------------------
-- Items can be registered to be part of the gift system using the
-- below function. The def is the following:
-- {
-- dialogues = {
-- liked = {
-- -- ^ This is an array of the following table:
-- [1] = {dialogue_type="", sex="", text=""}
-- -- ^ dialogue_type: defines is this is a hint or a response.
-- -- valid values are: "hint", "response"
-- -- sex: valid values are: "male", female"
-- -- text: the dialogue text
-- },
-- disliked = {
-- -- ^ This is an array with the same type of tables as above
-- }
-- }
-- }
function npc.relationships.register_favorite_item(item_name, phase, sex, def)
local dialogues = {}
-- Register dialogues based on the hints and responses
-- Liked
for i = 1, #def.hints do
table.insert(dialogues, {
text = def.hints[i],
tags = {phase, item_name, sex,
npc.dialogue.tags.GIFT_ITEM_HINT, npc.dialogue.tags.GIFT_ITEM_LIKED}
})
end
for i = 1, #def.responses do
table.insert(dialogues, {
text = def.responses[i],
tags = {phase, item_name, sex,
npc.dialogue.tags.GIFT_ITEM_RESPONSE, npc.dialogue.tags.GIFT_ITEM_LIKED}
})
end
-- Register all dialogues
for i = 1, #dialogues do
npc.dialogue.register_dialogue(dialogues[i])
end
-- Insert item into table
table.insert(npc.relationships.gift_items.liked[sex][phase], item_name)
end
function npc.relationships.register_disliked_item(item_name, sex, def)
local dialogues = {}
-- Register dialogues based on the hints and responses
-- Liked
for i = 1, #def.hints do
table.insert(dialogues, {
text = def.hints[i],
tags = {item_name, sex,
npc.dialogue.tags.GIFT_ITEM_HINT, npc.dialogue.tags.GIFT_ITEM_UNLIKED}
})
end
for i = 1, #def.responses do
table.insert(dialogues, {
text = def.responses[i],
tags = {item_name, sex,
npc.dialogue.tags.GIFT_ITEM_RESPONSE, npc.dialogue.tags.GIFT_ITEM_UNLIKED}
})
end
-- Register all dialogues
for i = 1, #dialogues do
npc.dialogue.register_dialogue(dialogues[i])
end
-- Insert item into table
table.insert(npc.relationships.gift_items.disliked[sex], item_name)
end
function npc.relationships.get_dialogues_for_gift_item(item_name, dialogue_type, item_type, sex, phase)
local tags = {
[1] = item_name,
[2] = dialogue_type,
[3] = item_type,
[4] = sex
}
if phase ~= nil then
tags[5] = phase
end
minetest.log("Searching with tags: "..dump(tags))
return npc.dialogue.search_dialogue_by_tags(tags, true)
end
-- Returns the response message for a given item -- Returns the response message for a given item
function npc.relationships.get_response_for_favorite_item(item_name, sex, phase) function npc.relationships.get_response_for_favorite_item(item_name, sex, phase)
local items = npc.FAVORITE_ITEMS.female local items = npc.FAVORITE_ITEMS.female
@ -144,7 +268,7 @@ end
-- Relationship functions -- Relationship functions
--------------------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- This function selects two random items from the npc.favorite_items table -- This function selects two random items from the npc.favorite_items table
-- It checks for sex and phase for choosing the items -- It checks for sex and phase for choosing the items
@ -152,18 +276,19 @@ function npc.relationships.select_random_favorite_items(sex, phase)
local result = {} local result = {}
local items = {} local items = {}
-- Filter sex -- -- Filter sex
if sex == npc.FEMALE then -- if sex == npc.FEMALE then
items = npc.FAVORITE_ITEMS.female -- items = npc.FAVORITE_ITEMS.female
else -- else
items = npc.FAVORITE_ITEMS.male -- items = npc.FAVORITE_ITEMS.male
end -- end
-- Select the phase -- Select the phase
items = items[phase] -- items = items[phase]
items = npc.relationships.gift_items.liked[sex][phase]
result.fav1 = items[math.random(1, #items)].item result.fav1 = items[math.random(1, #items)]
result.fav2 = items[math.random(1, #items)].item result.fav2 = items[math.random(1, #items)]
return result return result
end end
@ -174,15 +299,16 @@ function npc.relationships.select_random_disliked_items(sex)
local result = {} local result = {}
local items = {} local items = {}
-- Filter sex -- -- Filter sex
if sex == npc.FEMALE then -- if sex == npc.FEMALE then
items = npc.DISLIKED_ITEMS.female -- items = npc.DISLIKED_ITEMS.female
else -- else
items = npc.DISLIKED_ITEMS.male -- items = npc.DISLIKED_ITEMS.male
end -- end
items = npc.relationships.gift_items.disliked[sex]
result.dis1 = items[math.random(1, #items)].item result.dis1 = items[math.random(1, #items)]
result.dis2 = items[math.random(1, #items)].item result.dis2 = items[math.random(1, #items)]
return result return result
end end
@ -233,10 +359,8 @@ local function update_relationship(self, clicker_name, modifier)
npc.relationships.select_random_favorite_items(self.sex, self.relationships[i].phase) npc.relationships.select_random_favorite_items(self.sex, self.relationships[i].phase)
-- Re-select dialogues per new -- Re-select dialogues per new
self.dialogues = self.dialogues =
npc.dialogue.select_random_dialogues_for_npc(self.sex, npc.dialogue.select_random_dialogues_for_npc(self,
self.relationships[i].phase, self.relationships[i].phase)
self.gift_data.favorite_items,
self.gift_data.disliked_items)
return true return true
end end
return false return false
@ -352,14 +476,22 @@ local function show_receive_gift_reaction(self, item_name, modifier, clicker_nam
-- Send message -- Send message
-- TODO: There might be an error with getting the message... -- TODO: There might be an error with getting the message...
--minetest.log("Item_name: "..dump(item_name)..", sex: "..dump(self.sex)..", phase: "..dump(phase)) --minetest.log("Item_name: "..dump(item_name)..", sex: "..dump(self.sex)..", phase: "..dump(phase))
local message_to_send = local message_to_send = npc.relationships.get_dialogues_for_gift_item(
npc.relationships.get_response_for_favorite_item(item_name, self.sex, phase) item_name,
npc.relationships.GIFT_ITEM_RESPONSE,
npc.relationships.GIFT_ITEM_LIKED,
self.sex,
phase)
npc.chat(self.npc_name, clicker_name, message_to_send) npc.chat(self.npc_name, clicker_name, message_to_send)
-- Disliked items reactions -- Disliked items reactions
elseif modifier < 0 then elseif modifier < 0 then
effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, "default_item_smoke.png") 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) --minetest.log("Item name: "..item_name..", sex: "..self.sex)
local message_to_send = npc.relationships.get_response_for_disliked_item(item_name, self.sex) local message_to_send = npc.relationships.get_dialogues_for_gift_item(
item_name,
npc.relationships.GIFT_ITEM_RESPONSE,
npc.relationships.GIFT_ITEM_DISLIKED,
self.sex)
npc.chat(self.npc_name, clicker_name, message_to_send) npc.chat(self.npc_name, clicker_name, message_to_send)
end end
@ -425,21 +557,20 @@ function npc.relationships.receive_gift(self, clicker)
else else
npc.chat(self.npc_name, clicker_name, npc.chat(self.npc_name, clicker_name,
"Dear, I feel the same as you. But maybe not yet...") "Dear, I feel the same as you. But maybe not yet...")
end end
-- Reset gift timer -- Reset gift timer
reset_gift_timer(self, clicker_name) reset_gift_timer(self, clicker_name)
return true return true
end end
-- Marriage gifts: except for disliked items, all product a 0.5 * npc.ITEM_GIFT_EFFECT -- Marriage gifts: except for disliked items, all product a 0.5 * npc.ITEM_GIFT_EFFECT
-- Disliked items cause only a -1 point effect -- Disliked items cause only a -0.5 point effect
if get_relationship_points(self, clicker_name) >= if get_relationship_points(self, clicker_name) >=
npc.relationships.RELATIONSHIP_PHASE["phase5"].limit then npc.relationships.RELATIONSHIP_PHASE["phase5"].limit then
local modifier = 0.5 * npc.ITEM_GIFT_EFFECT local modifier = 0.5 * npc.ITEM_GIFT_EFFECT
-- Check for disliked items -- Check for disliked items
if item:get_name() == self.gift_data.disliked_items.dis1 if item:get_name() == self.gift_data.disliked_items.dis1
or item:get_name() == self.gift_data.disliked_items.dis2 then or item:get_name() == self.gift_data.disliked_items.dis2 then
modifier = -1 modifier = -0.5
show_receive_gift_reaction(self, item:get_name(), modifier, clicker_name, false) show_receive_gift_reaction(self, item:get_name(), modifier, clicker_name, false)
elseif item:get_name() == self.gift_data.favorite_items.fav1 elseif item:get_name() == self.gift_data.favorite_items.fav1
or item:get_name() == self.gift_data.favorite_items.fav2 then or item:get_name() == self.gift_data.favorite_items.fav2 then