advanced_npc/npc.lua

542 lines
16 KiB
Lua
Executable File

local S = mobs.intllib
-- Advanced NPC by Zorman2000
-- Based on original NPC by Tenplus1
npc = {}
-- Constants
npc.FEMALE = "female"
npc.MALE = "male"
npc.ITEM_GIFT_EFFECT = 2.5
npc.RELATIONSHIP_PHASE1_LIMIT = 10
npc.RELATIONSHIP_PHASE2_LIMIT = 25
npc.RELATIONSHIP_PHASE3_LIMIT = 45
npc.RELATIONSHIP_PHASE4_LIMIT = 70
npc.RELATIONSHIP_PHASE5_LIMIT = 100
npc.FAVORITE_ITEMS = {
female = {
child = {
"farming:cotton",
"farming:bread",
"default:apple",
"default:gold_ingot",
"default:steel_ingot",
"default:diamond"
},
adult = {
"default:apple",
"farming:bread",
"mobs:meat",
"default:pick_steel",
"default:shovel_steel",
"default:sword_steel"
}
},
male = {
child = {
"farming:cotton",
"farming:bread",
"default:apple",
"default:gold_ingot",
"default:steel_ingot",
"default:diamond"
},
adult = {
"default:apple",
"farming:bread",
"mobs:meat",
"default:pick_steel",
"default:shovel_steel",
"default:sword_steel"
}
}
}
-- TODO: Complete responses for female and males, both adult and child
npc.GIFT_RESPONSES = {
female = {
child = {
},
adult = {
{
phase1 = "Thank you!",
phase2 = "It is very appreciated! Thanks!",
phase3 = "Thank you! You definetely are special...",
phase4 = "Awww, you are so great!",
phase5 = "Oh, so cute! Thank you! I love you!",
phase6 = "Thank you my dear! You are the greatest husband!"
},
{
phase1 = "Thank you!",
phase2 = "It is very appreciated! Thanks!",
phase3 = "Thank you! You definetely are special...",
phase4 = "Awww, you are so great!",
phase5 = "Oh, so cute! Thank you! I love you!",
phase6 = "Thank you my dear! You are the greatest husband!"
},
{
phase1 = "Thank you!",
phase2 = "It is very appreciated! Thanks!",
phase3 = "Thank you! You definetely are special...",
phase4 = "Awww, you are so great!",
phase5 = "Oh, so cute! Thank you! I love you!",
phase6 = "Thank you my dear! You are the greatest husband!"
},
{
phase1 = "Thank you!",
phase2 = "It is very appreciated! Thanks!",
phase3 = "Thank you! You definetely are special...",
phase4 = "Awww, you are so great!",
phase5 = "Oh, so cute! Thank you! I love you!",
phase6 = "Thank you my dear! You are the greatest husband!"
},
{
phase1 = "Thank you!",
phase2 = "It is very appreciated! Thanks!",
phase3 = "Thank you! You definetely are special...",
phase4 = "Awww, you are so great!",
phase5 = "Oh, so cute! Thank you! I love you!",
phase6 = "Thank you my dear! You are the greatest husband!"
}
}
},
male = {
}
}
mobs.npc_drops = {
"default:pick_steel", "mobs:meat", "default:sword_steel",
"default:shovel_steel", "farming:bread", "bucket:bucket_water"
}
-- General functions
-- Gets name of player or NPC
local function get_entity_name(entity)
if entity:is_player() then
return entity:get_player_name()
else
return entity:get_luaentity().nametag
end
end
-- Returns the item "wielded" by player or NPC
-- TODO: Implement NPC
local function get_entity_wielded_item(entity)
if entity:is_player() then
return entity:get_wielded_item()
end
end
-- Functions on right click
---------------------------------------------------------------------------------------
-- Gift and relationship system
---------------------------------------------------------------------------------------
-- Each NPCs has 2 favorite and 2 disliked items. These items are chosen at spawn
-- time and will be re-chosen when the age changes (from child to adult, for example).
-- The items are chosen from the npc.FAVORITE_ITEMS table, and depends on sex and age.
-- A player, via right-click, or another NPC, can gift an item to a NPC. In the case
-- of the player, the player will give one of the currently wielded item. Gifts can be
-- given only once per some time period, the NPC will reject the given item if still
-- the period isn't over.
-- If the NPC is neutral on the item (meanining it's neither favorite or disliked), it
-- is possible it will not accept it, and the relationship the giver has with the NPC
-- will be unchanged.
-- In the other hand, if the item given its a favorite, the relationship points the NPC
-- has with giver will increase by a given amount, depending on favoriteness. Favorite 1
-- will increase the relationship by 2 * npc.ITEM_GIFT_EFFECT, and favorite 2 only by
-- npc.ITEM_GIFT_EFFECT. Similarly, if the item given is a disliked item, the NPC will
-- not take it, and its relationship points with the giver will decrease by 2 or 1 times
-- npc.ITEM_GIFT_EFFECT.
-- Relationship functions
---------------------------------------------------------------------------------------
-- Creates a relationship with a given player or NPC
local function create_relationship(self, clicker_name)
local count = #self.relationships
self.relationships[count + 1] = {
name = clicker_name,
points = 0
}
end
-- Returns a relationship points
local function get_relationship_points(self, clicker_name)
for i = 1, #self.relationships do
if self.relationships[i].name == clicker_name then
return self.relationships[i].points
end
end
return nil
end
-- Updates relationship with given points
local function update_relationship(self, clicker_name, modifier)
for i = 1, #self.relationships do
if self.relationships[i].name == clicker_name then
self.relationships[i].points = self.relationships[i].points + modifier
return
end
end
end
-- Checks if a relationship with given player or NPC exists
local function check_relationship_exists(self, clicker_name)
for i = 1, #self.relationships do
if self.relationships[i].name == clicker_name then
return true
end
end
return false
end
-- Gifts functions
---------------------------------------------------------------------------------------
-- This function selects two random items from the npc.favorite_items table
-- It checks both for age and for sex for choosing the items
local function select_random_favorite_items(sex, is_child)
local result = {}
local items = {}
-- Filter sex
if sex == npc.FEMALE then
items = npc.FAVORITE_ITEMS.female
else
items = npc.FAVORITE_ITEMS.male
end
-- Filter age
if is_child then
items = items.child
else
items = items.adult
end
result.fav1 = items[math.random(1, #items)]
result.fav2 = items[math.random(1, #items)]
return result
end
-- Displays message and hearts depending on relationship level
local function show_receive_gift_reaction(self, clicker_name)
local points = get_relationship_points(self, clicker_name)
local chat_messages = {}
if self.sex == npc.FEMALE then
if self.child == true then
-- TODO: Implement child responses
else
chat_messages = npc.GIFT_RESPONSES.female.adult[1]
minetest.log(dump(chat_messages))
end
end
local pos = self.object:getpos()
local message_to_send = ""
-- Positive relationship reactions
if points >= 0 then
if points < npc.RELATIONSHIP_PHASE1_LIMIT then
message_to_send = chat_messages.phase1
elseif points < npc.RELATIONSHIP_PHASE2_LIMIT then
message_to_send = chat_messages.phase2
elseif points < npc.RELATIONSHIP_PHASE3_LIMIT then
message_to_send = chat_messages.phase3
effect({x = pos.x, y = pos.y + 1, z = pos.z}, 2, "heart.png")
elseif points < npc.RELATIONSHIP_PHASE4_LIMIT then
message_to_send = chat_messages.phase4
effect({x = pos.x, y = pos.y + 1, z = pos.z}, 4, "heart.png")
elseif points < npc.RELATIONSHIP_PHASE5_LIMIT then
message_to_send = chat_messages.phase5
effect({x = pos.x, y = pos.y + 1, z = pos.z}, 6, "heart.png")
-- This will show when players are married
elseif points > npc.RELATIONSHIP_PHASE5_LIMIT then
message_to_send = chat_messages.phase6
effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, "heart.png")
end
-- Send message
minetest.chat_send_player(clicker_name, message_to_send)
-- Relationship is in negative state
elseif points < 0 then
end
end
-- Receive gift function; applies relationship points as explained above
-- Also, creates a relationship object if not present
local function receive_gift(self, clicker)
-- Return if clicker is not offering an item
local item = get_entity_wielded_item(clicker)
if item:get_name() == "" then return false end
-- Get clicker name
local clicker_name = get_entity_name(clicker)
-- If NPC received a gift, then reject any more gifts for now
if self.gift_data.gift_timer_value < self.gift_data.gift_interval then
minetest.chat_send_player(clicker_name, "Thanks, but I don't need anything for now")
return false
end
-- Create relationship if it doesn't exists
if check_relationship_exists(self, clicker_name) == false then
create_relationship(self, clicker_name)
end
-- If NPC is ready for marriage, do no accept anything else but the ring,
-- and that with only a certain chance. The self.owner is to whom is married
-- this NPC... he he.
minetest.log(get_relationship_points(self, clicker_name))
if get_relationship_points(self, clicker_name) >= npc.RELATIONSHIP_PHASE5_LIMIT
and self.owner ~= clicker_name
and item:get_name() ~= "advanced_npc:marriage_ring" then
minetest.chat_send_player(clicker_name, "Thank you my love, but I think that you have given me")
minetest.chat_send_player(clicker_name, "enough gifts for now. Maybe we should go a step further")
self.gift_data.gift_timer_value = 0
return true
elseif get_relationship_points(self, clicker_name) >= npc.RELATIONSHIP_PHASE5_LIMIT
and item:get_name() == "advanced_npc:marriage_ring" then
-- If the player/entity is offering a marriage ring, then NPC will accept with a 50%
-- chance to marry the clicker
local receive_chance = math.random(1, 10)
-- Receive ring and get married
if receive_chance < 6 then
minetest.chat_send_player(clicker_name, "Oh, oh you make me so happy! Yes! I will marry you!")
-- Get ring
item:take_item()
clicker:set_wielded_item(item)
-- Show marriage reaction
local pos = self.object:getpos()
effect({x = pos.x, y = pos.y + 1, z = pos.z}, 20, "heart.png", 4)
-- Give 100 points, so NPC is really happy on marriage
update_relationship(self, clicker_name, 100)
-- This sets the married state, for now. Hehe
self.owner = clicker_name
self.gift_data.gift_timer_value = 0
return true
-- Reject ring for now
else
minetest.chat_send_player(clicker_name, "Dear, I feel the same as you. But maybe not yet...")
self.gift_data.gift_timer_value = 0
return true
end
end
-- Modifies relationship depending on given item
local modifier = 0
local take = true
local show_reaction = false
if item:get_name() == self.gift_data.favorite_items.fav1 then
modifier = 2 * npc.ITEM_GIFT_EFFECT
show_reaction = true
elseif item:get_name() == self.gift_data.favorite_items.fav2 then
modifier = npc.ITEM_GIFT_EFFECT
show_reaction = true
else
-- If item is not a favorite or a dislike, then receive chance
-- if 70%
local receive_chance = math.random(1,10)
if receive_chance < 7 then
minetest.chat_send_player(clicker_name, "Thanks. I will find some use for this.")
else
minetest.chat_send_player(clicker_name, "Thank you, but no, I have no use for this.")
take = false
end
show_reaction = false
end
-- Take item if NPC accepted it
if take == true then
item:take_item()
clicker:set_wielded_item(item)
end
-- Show NPC reaction to gift
if show_reaction == true then
show_receive_gift_reaction(self, clicker_name)
end
-- Update relationship status
update_relationship(self, clicker_name, modifier)
minetest.log(dump(self))
self.gift_data.gift_timer_value = 0
return true
end
-- Chat functions
local function start_chat(self, clicker)
local name = get_entity_name(clicker)
-- Married player can tell NPC to follow or to stay at a given place
-- TODO: Improve this. There should be a dialogue box for this
if self.owner and self.owner == name then
if self.order == "follow" then
self.order = "stand"
minetest.chat_send_player(name, S("Ok dear, I will wait here for you."))
else
self.order = "follow"
minetest.chat_send_player(name, S("Let's go honey!"))
end
end
end
mobs:register_mob("advanced_npc:npc", {
type = "npc",
passive = false,
damage = 3,
attack_type = "dogfight",
attacks_monsters = true,
-- Added group attack
group_attack = true,
--pathfinding = true,
pathfinding = 1,
hp_min = 10,
hp_max = 20,
armor = 100,
collisionbox = {-0.35,-1.0,-0.35, 0.35,0.8,0.35},
visual = "mesh",
mesh = "character.b3d",
drawtype = "front",
textures = {
{"mobs_npc_male1.png"},
{"mobs_npc_female1.png"}, -- female by nuttmeg20
},
child_texture = {
{"mobs_npc_baby_male1.png"}, -- derpy baby by AmirDerAssassine
},
makes_footstep_sound = true,
sounds = {},
-- Added walk chance
walk_chance = 30,
-- Added stepheight
stepheight = 1,
walk_velocity = 2,
run_velocity = 3,
jump = true,
drops = {
{name = "default:wood", chance = 1, min = 1, max = 3},
{name = "default:apple", chance = 2, min = 1, max = 2},
{name = "default:axe_stone", chance = 5, min = 1, max = 1},
},
water_damage = 0,
lava_damage = 2,
light_damage = 0,
--follow = {"farming:bread", "mobs:meat", "default:diamond"},
view_range = 15,
owner = "",
order = "follow",
--order = "stand",
fear_height = 3,
animation = {
speed_normal = 30,
speed_run = 30,
stand_start = 0,
stand_end = 79,
walk_start = 168,
walk_end = 187,
run_start = 168,
run_end = 187,
punch_start = 200,
punch_end = 219,
},
on_rightclick = function(self, clicker)
local item = clicker:get_wielded_item()
local name = clicker:get_player_name()
minetest.log(dump(self))
-- Receive gift or start chat
if receive_gift(self, clicker) == false then
start_chat(self, clicker)
end
end,
})
-- This function checks for "female" text on the texture name
local function is_female_texture(textures)
for i = 1, #textures do
if string.find(textures[i], "female") ~= nil then
return true
end
end
return false
end
local function npc_spawn(self, pos)
minetest.log("Spawning new NPC:")
local ent = self:get_luaentity()
ent.nametag = "Kio"
-- Determine sex based on textures
if (is_female_texture(ent.base_texture)) then
ent.sex = npc.FEMALE
else
ent.sex = npc.MALE
end
-- Initialize all gift data
ent.gift_data = {
-- Choose favorite items
favorite_items = select_random_favorite_items(ent.sex, ent.child),
-- How frequent can the NPC receive a gift
gift_interval = 10,
-- Current timer count since last gift
gift_timer_value = 0
}
-- Timer function for gifts
ent.on_step = function(self, dtime)
if self.gift_data.gift_timer_value < self.gift_data.gift_interval then
self.gift_data.gift_timer_value = self.gift_data.gift_timer_value + dtime
minetest.log(dump(self.gift_data.gift_timer_value))
end
end
-- Initialize relationships object
ent.relationships = {}
minetest.log(dump(ent))
end
-- Spawn
mobs:spawn({
name = "advanced_npc:npc",
nodes = {"default:stone"},
min_light = 3,
active_object_count = 1,
interval = 5,
chance = 1,
--max_height = 0,
on_spawn = npc_spawn,
})
mobs:register_egg("advanced_npc:npc", S("Npc"), "default_brick.png", 1)
-- compatibility
mobs:alias_mob("mobs:npc", "advanced_npc:npc")
-- Marriage ring
minetest.register_craftitem("advanced_npc:marriage_ring", {
description = S("Marriage Ring"),
inventory_image = "diamond_ring.png",
})
-- Marriage ring craft recipe
minetest.register_craft({
output = "advanced_npc:marriage_ring",
recipe = { {"", "", ""},
{"", "default:diamond", ""},
{"", "default:gold_ingot", ""} },
})