Remove villagers mod
|
@ -7,3 +7,6 @@ tags
|
|||
|
||||
## Files related to minetest development cycle
|
||||
*.patch
|
||||
|
||||
## Removed mods
|
||||
OLDmods/*/
|
|
@ -0,0 +1,4 @@
|
|||
Here are removed mods and why. I do not add them to git but keep
|
||||
their name for reference :
|
||||
|
||||
villagers : not needed and suspected to slow down chests
|
13
ROADMAP
|
@ -235,16 +235,17 @@ It's really fast. May be used on a website, to show the actual map.
|
|||
- [ ] Rebuild minetest with master
|
||||
- Don't forget to issue a *git pull* before
|
||||
- [ ] May also test with the stable-0.4 branch
|
||||
**** Next stream test
|
||||
**** [fix_SlowChestOpening] Next stream test
|
||||
- [X] New texture pack Pixel Perfection
|
||||
- [X] Replaced welcome_popup with server_news
|
||||
- [X] Now hoe can be created from right
|
||||
- [X] New server craft : FIXED
|
||||
2019-03-10 19:24:41: ERROR[Main]: ServerError: AsyncErr: ServerThread::run Lua: Runtime error from mod '' in callback luaentity_Step(): Runtime error from mod '' in callback item_OnPlace(): ...nbru/.minetest/games/minetest-pvp/mods/creative/init.lua:43: attempt to index local 'placer' (a n
|
||||
2019-03-10 19:24:41: ERROR[Main]: il value)
|
||||
2019-03-10 19:24:41: ERROR[Main]: stack traceback:
|
||||
2019-03-10 19:24:41: ERROR[Main]: /mods/creative/init.lua:43: in function 'callback'
|
||||
|
||||
- [ ] SlowChestOpening test
|
||||
Moving unwanted mods to OLDmods
|
||||
- [ ] First test with no mod at all
|
||||
- [ ] remove mods until
|
||||
Start with villagers
|
||||
|
||||
**** DONE Improve minetestmapper
|
||||
*Should show factions*
|
||||
- [X] Clone this project and create a gitlab project as minetestmapper-factions
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
VILLAGERS
|
||||
|
||||
BY: ErrorNull
|
||||
DESCRIPTION: Villagers for Minetest
|
||||
VERSION: 0.17
|
||||
LICENCE: WTFPL
|
||||
|
||||
REQUIRES: Sokomine's mg_villages mod.
|
||||
Github - https://github.com/Sokomine/mg_villages
|
||||
Forum - https://forum.minetest.net/viewtopic.php?f=9&t=13588
|
||||
|
||||
COINS:
|
||||
This mod primarily uses coins for trading. Ten silver coins equal 1 gold coin. A new player will start with a few coins - if that setting was not disabled from config.lua. To get more coins, look for villagers of the following job types that will trade you coins for certain items: ore_seller, potterer, saddler, stoneminer, servant, and schoolteacher. Use the '/villagers list' command below to quickly find some traders while in a village. Also, villagers with job types of "major" can exchange silver coins for gold coins and vice versa.
|
||||
|
||||
CHAT COMMAND:
|
||||
/villagers list
|
||||
Displays a formspec that lists all villagers currently spawned nearby that has a job title. Villagers with no job title are not shown. In addition to some details about each trader, there is a GO button that will teleport you to that trader. This tool is meant to for testing while this mod is WIP, and will be restricted upon official release. The following details are shown for each trader:
|
||||
- Village: village type that the trader resides in
|
||||
- Plot: Plot number that the trader initially spawned in
|
||||
- Bed: Bed number (or spawn sequence#) of the trader
|
||||
- Name: Trader's name
|
||||
- Title: Trader's job title
|
||||
- Origin: x and z coordinates of villager's initial spawn point
|
||||
- Current: The currentx and z coordinates of villager
|
||||
- Walked: How many meters the villager has walked
|
||||
- Dist: Distance the villager is from the player
|
||||
GO button: Teleports player to the trader/villager
|
||||
REFRESH button: Updates numbers for Current, Walked and Dist to the present moment.
|
||||
|
||||
UN-SPAWNED VILLAGERS?
|
||||
Some villagers may not appear immediately when a player spawns or teleports directly into a new village. If this happens, walk (or fly) about 40 blocks away from the village, then walk back and the villager should appear and remain as a permanent resident of the village.
|
||||
|
||||
NOTE ABOUT 'GRASSHUT' VILLAGE TYPE:
|
||||
This is a village type that is available when the very nice 'cottages' mod (https://forum.minetest.net/viewtopic.php?t=5120) is installed with 'mg_villages' mod - both by Sokomine. At this time, my villagers mod do not support the GRASSHUT village type because it requires Mossmanikin's 'dryplants' mod which depends on Venessa's 'biome_lib' mod. Venessa's biome_lib mod and her beautiful mods 'plantlife_modpack' and 'moretrees' are amazing mods that make the world look so much more lush and realistic. However, in my experience so far with biome_lib and other mods that depend on it, it becomes fairly processing intensive and detracks from my goal of having a lightweight, server and multiplayer friendly villagers mod. If you have a fast computer and primarily play singleplayer, then I would highly recommend installing Venessa's plantlife and moretrees mods for sure!
|
|
@ -1,91 +0,0 @@
|
|||
TODO:
|
||||
- add support for farming redo in trading goods
|
||||
- finish details trade items for remaining job titles
|
||||
- add support for 3d armor in trading goods
|
||||
|
||||
- redo /villagers spawn chat command into formspec instead
|
||||
- remove dependancy on mg_villagers mod.
|
||||
- make unique chat for 'single' houses and for region type
|
||||
|
||||
-- use the nodemetadata to track how many mystery and simplejob traders will spawn..
|
||||
-- then can spawn mystery trader and coin exchange trader
|
||||
- create mystery merchants that sell only one valueable items at a time (only one in each village)
|
||||
|
||||
- add 'rosetta stone' item. if player does not hold in inventory, speeach from native and desert
|
||||
-- villagers will not display in a legible format
|
||||
|
||||
- add check for unsupported mods. do not load villager's mod if unsupported mods are present
|
||||
- minetest.after removing chat bubbles when many are in screen.
|
||||
|
||||
- prevent manual spawning if in water
|
||||
- villagers in snowy region will offer some snow for free
|
||||
- create custom main dialogue for traders
|
||||
- make metalsmith use only mineral and metal related dialogue from 'game facts'
|
||||
- make chat dialogue based on scm type and not building type anymore
|
||||
-- for now, only librarians, smith, tower gaurds, and house residents give random game facts.
|
||||
- slightly reduce the chance for smalltalk chat for each villager
|
||||
- make item stock based on the item type: expensive items have less stock, but bigger villages have more stock.
|
||||
|
||||
- make villagers who spawn in homes with multiple inhabitants follow these patterns:
|
||||
-- 1 inhab: a) adult male b) adult female c) old male d) old female
|
||||
-- 2 inhab: a) adult male and female b) old male and female c) more to come
|
||||
-- 3 inhab: a) adult male and female and young b) old male and female and young
|
||||
-- 4 inhab: a) adult male and female and two young b) old male and female and adult female and young
|
||||
-- 5 inhab: a) adult male and female and three young b) old male and female and adult male and female and young
|
||||
|
||||
- make custom dialogue also based on village type. Ex: huts in medieval are larger and have beds and huts in cornernote do not.
|
||||
|
||||
- make the village region type (hot, cold, normal, native, desert) not based purely on the village type, but instead
|
||||
-- based on the surrounding blocks and village tree types from the village spawn point.
|
||||
|
||||
- allow villagers to open/close and walk through doors and gates
|
||||
- while villager is walking and attempting to chat, show chat bubble 'pardon me', 'excuse me', 'sorry', 'coming through', 'walking here', etc.
|
||||
- due to many more spawned villagers matching each bed, make most of these villagers walk the roads instead of randomly wandering around the plot
|
||||
|
||||
- add new variable 'vDoorFound' and 'vDoorPosition' that stores the position of a found door as verifyPath() executes
|
||||
- after each walk action, check if vDoorFound is true contains a position and currPos + 2 nodes forward doesn't hit maxDistance, then set action to USEDOOR
|
||||
- USEDOOR: villager opens door, walks forward two nodes, and closes door, then sets action to STAND.
|
||||
|
||||
- have villagers appear on bed laying down at night
|
||||
- make villagers that have no beds lay on the ground at night with a sleeping mat (maybe next to a tree or building)
|
||||
|
||||
- have guards wield a shield and/or weapon
|
||||
- make tower villagers appear atop tower
|
||||
|
||||
- have farmers/field workers plant seedlings if they encounter bare soil
|
||||
- make field villagers stay near their fields and not wander off
|
||||
- villager wields the tool depending on what item digging - shovel for snow, hoe for crops and soil, etc
|
||||
|
||||
- add unique chat dialogue for remaining villager types
|
||||
- add better clothing textures for 'church' villager types
|
||||
- add clothing textures that match old aged villagers
|
||||
- make church villagers look for and stay inside the church
|
||||
|
||||
- add support for weather mod
|
||||
- have villagers say something different depending on weather
|
||||
|
||||
- when adding more hair textures start using v1.8 type
|
||||
- add probability for adult and old males to have beards
|
||||
- add probability for gloves in cold regions
|
||||
- add more jacket styles
|
||||
- add more female short sleeved, long sleeved and sleeveless shirt styles
|
||||
- add different dress type of males (currently using slightly modified texture of female full dress)
|
||||
- add more full dress styles for females
|
||||
|
||||
- tower guard spawn atop the tower too!
|
||||
- free roaming villagers that just walk the streets and chat
|
||||
- villagers climb stairs and villagers sit on furniture
|
||||
- support for Farming Redo items for trading
|
||||
|
||||
- allow villagers to kill mobs
|
||||
- allow villagers to get 'knocked out' (but not die) from attackes
|
||||
- knocked out villagers will automatically recover and stand back up after some time
|
||||
- cannot trade with a knocked out villager
|
||||
- knocked out villager can only respond like 'uuuhg', 'ouch', 'help', etc when knocked out
|
||||
- player can right-click on villager with a healing item to help recovery
|
||||
|
||||
ISSUES/BUGS:
|
||||
|
||||
- player who initiated a chat or trade might have chat bubble stick when
|
||||
-- another player chats same villager
|
||||
- on very rare occasions villagers seen to walk diagonally
|
|
@ -1,757 +0,0 @@
|
|||
|
||||
-- ====================================== VILLAGER ACTIONS ================================================
|
||||
-- ========================================================================================================
|
||||
|
||||
-- SUPPORTING FUNCTIONS --
|
||||
|
||||
-- verify weather current target position is a valid spot for villager to walk
|
||||
function targetClear(self)
|
||||
|
||||
local pos = {x=self.vTargetPos.x, y=self.vTargetPos.y, z=self.vTargetPos.z}
|
||||
local nodenames
|
||||
|
||||
--check vertical pos at villager's lowerbody
|
||||
nodenames = villagers.getNodeName({x=pos.x, y=pos.y, z=pos.z})
|
||||
if villagers.log then io.write("\n - "..nodenames[2]) end
|
||||
if minetest.registered_nodes[nodenames[1]].walkable then
|
||||
if villagers.log then io.write("[fail] ") end
|
||||
return false
|
||||
else
|
||||
if villagers.log then io.write("[OK] ") end
|
||||
end
|
||||
|
||||
--check vertical pos at villager's upperbody
|
||||
nodenames = villagers.getNodeName({x=pos.x, y=pos.y+1, z=pos.z})
|
||||
if villagers.log then io.write(nodenames[2]) end
|
||||
if minetest.registered_nodes[nodenames[1]].walkable then
|
||||
if villagers.log then io.write("[fail] ") end
|
||||
return false
|
||||
else
|
||||
if villagers.log then io.write("[OK] ") end
|
||||
end
|
||||
|
||||
--check vertical pos below villager's feet
|
||||
nodenames = villagers.getNodeName({x=pos.x, y=pos.y-1, z=pos.z})
|
||||
local nodename = nodenames[2]
|
||||
if villagers.log then io.write(nodenames[2]) end
|
||||
if string.find(nodename, "WATER") or string.find(nodename, "LAVA") or string.find(nodename, "AIR") then
|
||||
if villagers.log then io.write("[fail] ") end
|
||||
return false
|
||||
else
|
||||
if villagers.log then io.write("[OK] ") end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function turnYaw(self, yaw_index, turnRight)
|
||||
local new_yaw_index
|
||||
if turnRight then
|
||||
new_yaw_index = yaw_index + 1
|
||||
if new_yaw_index > 8 then new_yaw_index = 1 end
|
||||
else
|
||||
new_yaw_index = yaw_index - 1
|
||||
if new_yaw_index < 1 then new_yaw_index = 8 end
|
||||
end
|
||||
|
||||
local new_yaw = villagers.YAWS[new_yaw_index]
|
||||
self.vFacingDirection = villagers.DIRECTIONS[new_yaw_index]
|
||||
|
||||
self.vYaw = new_yaw
|
||||
self.object:set_yaw(new_yaw)
|
||||
end
|
||||
|
||||
local function posHasObject(pos)
|
||||
local objects = minetest.get_objects_inside_radius(pos, 0.8)
|
||||
local object_count = #objects
|
||||
local return_bool
|
||||
if object_count > 0 then
|
||||
|
||||
for n = 1, #objects do
|
||||
if objects[n]:is_player() then
|
||||
if villagers.log then io.write("foundPlayer["..objects[n]:get_player_name().."] ") end
|
||||
else
|
||||
local luaentity = objects[n]:get_luaentity()
|
||||
if luaentity.vName then
|
||||
if villagers.log then io.write("foundVillager["..luaentity.vName.."] ") end
|
||||
else
|
||||
if villagers.log then io.write("foundEntity ") end
|
||||
end
|
||||
local pos = objects[n]:get_pos()
|
||||
if villagers.log then io.write("("..pos.x..","..pos.z..") ") end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return_bool = true
|
||||
else
|
||||
if villagers.log then io.write("targetPosNoEntity ") end
|
||||
return_bool = false
|
||||
end
|
||||
return return_bool
|
||||
end
|
||||
|
||||
-- MAIN VILLAGER ACTIONS
|
||||
|
||||
-- stand idle
|
||||
function villagers.standVillager(self)
|
||||
if villagers.log then io.write("stand() ") end
|
||||
self.vAction = "STAND"
|
||||
|
||||
-- skip remaining code if villager already standing
|
||||
if self.vAction == "STAND" then
|
||||
return
|
||||
end
|
||||
|
||||
self.object:set_animation(
|
||||
{x=self.animation["stand_start"], y=self.animation["stand_end"]},
|
||||
self.animation_speed + math.random(10)
|
||||
)
|
||||
|
||||
-- replace turn preference from 'right' to 'left' or vice versa
|
||||
-- after a string of consecutive turns
|
||||
if self.vTurnPreference == "right" then
|
||||
self.vTurnPreference = "left"
|
||||
else
|
||||
self.vTurnPreference = "right"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- turn left or right by 45 degrees
|
||||
local function turnVillager(self)
|
||||
if villagers.log then io.write("turn() ") end
|
||||
self.vAction = "TURN"
|
||||
|
||||
local face_dir = self.vFacingDirection
|
||||
if self.vTurnDirection == "right" then
|
||||
if face_dir == "N" then turnYaw(self, 1, true)
|
||||
elseif face_dir == "NE" then turnYaw(self, 2, true)
|
||||
elseif face_dir == "E" then turnYaw(self, 3, true)
|
||||
elseif face_dir == "SE" then turnYaw(self, 4, true)
|
||||
elseif face_dir == "S" then turnYaw(self, 5, true)
|
||||
elseif face_dir == "SW" then turnYaw(self, 6, true)
|
||||
elseif face_dir == "W" then turnYaw(self, 7, true)
|
||||
elseif face_dir == "NW" then turnYaw(self, 8, true)
|
||||
end
|
||||
else
|
||||
if face_dir == "N" then turnYaw(self, 1)
|
||||
elseif face_dir == "NE" then turnYaw(self, 2)
|
||||
elseif face_dir == "E" then turnYaw(self, 3)
|
||||
elseif face_dir == "SE" then turnYaw(self, 4)
|
||||
elseif face_dir == "S" then turnYaw(self, 5)
|
||||
elseif face_dir == "SW" then turnYaw(self, 6)
|
||||
elseif face_dir == "W" then turnYaw(self, 7)
|
||||
elseif face_dir == "NW" then turnYaw(self, 8)
|
||||
end
|
||||
end
|
||||
|
||||
if villagers.log then io.write("nowFacing="..self.vFacingDirection.." ") end
|
||||
end
|
||||
|
||||
local function walkVillagerEnd(self)
|
||||
if villagers.log then io.write(string.upper(self.vName).." ") end
|
||||
|
||||
-- stop villager and show standing anim. villager should
|
||||
-- now be standing at or near the target position
|
||||
self.object:setvelocity({x=0,y=0,z=0})
|
||||
self.object:set_animation(
|
||||
{x=self.animation["stand_start"], y=self.animation["stand_end"]},
|
||||
self.animation_speed + math.random(10)
|
||||
)
|
||||
self.vAction = "STAND"
|
||||
|
||||
-- update total distance travelled by villager
|
||||
local travelDistance = vector.distance(self.vPos, {x=self.vTargetPos.x, y=self.vPos.y, z=self.vTargetPos.z})
|
||||
self.vTotalDistance = self.vTotalDistance + travelDistance
|
||||
|
||||
-- teleport villager to exact target position to makeup for any
|
||||
-- discrepencies in movement time and distance
|
||||
self.vPos.x = self.vTargetPos.x
|
||||
self.vPos.z = self.vTargetPos.z
|
||||
self.object:set_pos(self.vPos)
|
||||
|
||||
-- update villager pathfinding parameters for next walk attempt
|
||||
local newOriginDist = villagers.round(vector.distance(self.vOriginPos, self.vPos),1)
|
||||
self.vOriginDistance = newOriginDist
|
||||
|
||||
if newOriginDist < self.vOriginDistMax then
|
||||
|
||||
else
|
||||
self.vAction = "TURNBACK"
|
||||
if villagers.log then io.write("DistMaxed set_vAction=TURNBACK ") end
|
||||
end
|
||||
if villagers.log then io.write("originDist="..newOriginDist.." ") end
|
||||
|
||||
end
|
||||
|
||||
-- walk forward a specific number of blocks
|
||||
local function walkVillager(self)
|
||||
if villagers.log then io.write("walk() ") end
|
||||
self.vAction = "WALKING"
|
||||
|
||||
-- calculate velocity and direction
|
||||
local facing = self.vFacingDirection
|
||||
local x_velocity = villagers.NODE_AREA[facing][1]
|
||||
local z_velocity = villagers.NODE_AREA[facing][2]
|
||||
|
||||
--move villager in the direction of target position
|
||||
self.object:setvelocity({x=x_velocity,y=0,z=z_velocity})
|
||||
|
||||
--show walking animation
|
||||
self.object:set_animation(
|
||||
{x=self.animation["walk_start"], y=self.animation["walk_end"]},
|
||||
self.animation_speed + math.random(10)
|
||||
)
|
||||
|
||||
local distance = villagers.round(vector.distance(self.vPos, {x=self.vTargetPos.x, y=self.vPos.y, z=self.vTargetPos.z}),1)
|
||||
|
||||
--after 'distance' seconds, stop villager movement
|
||||
minetest.after(distance, function()
|
||||
if villagers.log then io.write("\n ** MTAFTER.WALK-ENDED("..distance..") ") end
|
||||
walkVillagerEnd(self)
|
||||
if villagers.log then io.write("MTAFTER_END **") end
|
||||
end)
|
||||
|
||||
if villagers.log then io.write("mtAfterScheduled runIn: "..distance.."sec ") end
|
||||
end
|
||||
|
||||
local function digVillager(self)
|
||||
local log = false
|
||||
if log then io.write("dig() ") end
|
||||
|
||||
-- skip remaining code if villager already standing
|
||||
if self.vAction == "DIG" then
|
||||
self.vSavepoints.digVillager = nil
|
||||
return
|
||||
end
|
||||
self.vAction = "DIG"
|
||||
|
||||
self.object:set_animation(
|
||||
{x=self.animation["dig_start"], y=self.animation["dig_end"]},
|
||||
self.animation_speed + math.random(10)
|
||||
)
|
||||
|
||||
self.vSoundHandle = minetest.sound_play(
|
||||
"default_dig_crumbly",
|
||||
{object = self.object, loop = true, gain = 0.2, max_hear_distance = 8}
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
local function replaceNode(self)
|
||||
local log = false
|
||||
if log then io.write("replace() ") end
|
||||
self.vAction = "REPLACE"
|
||||
|
||||
local dugNodeData = villagers.getFacingNodeInfo(self, 3)
|
||||
local dugPosition = dugNodeData[1]
|
||||
local dugNodeName = dugNodeData[2]
|
||||
local dugNodeNickname = dugNodeData[3]
|
||||
|
||||
-- snow
|
||||
if dugNodeNickname == "SNOW" then
|
||||
minetest.remove_node(dugPosition)
|
||||
|
||||
-- grass
|
||||
elseif string.find(dugNodeNickname, "GRASS_") then
|
||||
if dugNodeNickname == "GRASS_1" then
|
||||
minetest.remove_node(dugPosition)
|
||||
else
|
||||
minetest.set_node(dugPosition, {name = "default:grass_1"})
|
||||
end
|
||||
|
||||
-- cotton
|
||||
elseif dugNodeNickname == "COTTON_8" then
|
||||
minetest.set_node(dugPosition, {name = "farming:cotton_1"})
|
||||
|
||||
-- wheat
|
||||
elseif dugNodeNickname == "WHEAT_8" then
|
||||
minetest.set_node(dugPosition, {name = "farming:wheat_1"})
|
||||
|
||||
elseif string.find(dugNodeNickname, "FLOWER_") then
|
||||
minetest.set_node(dugPosition, {name = "default:grass_1"})
|
||||
end
|
||||
|
||||
minetest.sound_stop(self.vSoundHandle)
|
||||
minetest.sound_play("default_dig_snappy", {pos = dugPosition, gain = 0.4, max_hear_distance = 8} )
|
||||
|
||||
-- show standing anim for the rest of the action cycle
|
||||
self.object:set_animation(
|
||||
{x=self.animation["stand_start"], y=self.animation["stand_end"]},
|
||||
self.animation_speed + math.random(10)
|
||||
)
|
||||
|
||||
self.vAction = "STAND"
|
||||
end
|
||||
|
||||
-- stand still while verifying path in front and taking note any obstructions
|
||||
local function verifyPath(self, forced_step_distance, walking_back)
|
||||
if villagers.log then io.write("verify() ") end
|
||||
|
||||
-- if villager has despawned
|
||||
if self == nil then return end
|
||||
|
||||
-- verifyPath() called from turnBack()
|
||||
if walking_back then
|
||||
self.vAction = "WALKBACK"
|
||||
|
||||
-- verifyPath() called via normal action cycle
|
||||
else
|
||||
self.vAction = "VERIFY"
|
||||
end
|
||||
|
||||
local remainingDistance
|
||||
if forced_step_distance then
|
||||
-- 'forced_step_distance' param is typicalled passed when verifyPath()
|
||||
-- is manually executed by turnBack() when walk distance is maxed and
|
||||
-- only want villager to walk forward 1 block/node
|
||||
remainingDistance = forced_step_distance
|
||||
else
|
||||
remainingDistance = math.random(math.floor(self.vOriginDistMax - self.vOriginDistance))
|
||||
end
|
||||
|
||||
local possibleWalkDistance = 0
|
||||
local facingDeadEnd = false
|
||||
|
||||
if villagers.log then
|
||||
local currPos = self.object:get_pos()
|
||||
io.write("getPos("..currPos.x..","..currPos.z..") ")
|
||||
io.write("vPos("..self.vPos.x..","..self.vPos.z..") ")
|
||||
io.write("goalDist="..remainingDistance.." ")
|
||||
end
|
||||
|
||||
-- re-initialize targetPos to player's current x,z coordinates
|
||||
self.vTargetPos.x = self.vPos.x
|
||||
self.vTargetPos.z = self.vPos.z
|
||||
|
||||
for i = 1, remainingDistance do
|
||||
--check a column of 3 nodes that is in front of villager. if encounter liquid,
|
||||
--cliff or block node, break loop and save the target pos reached so far
|
||||
|
||||
-- set targetPos to one node forward
|
||||
self.vTargetPos.x = self.vTargetPos.x + villagers.NODE_AREA[self.vFacingDirection][1]
|
||||
self.vTargetPos.z = self.vTargetPos.z + villagers.NODE_AREA[self.vFacingDirection][2]
|
||||
|
||||
if targetClear(self) then
|
||||
if posHasObject(self.vTargetPos) then
|
||||
|
||||
-- reset targetPos back to the previous node
|
||||
self.vTargetPos.x = self.vTargetPos.x - villagers.NODE_AREA[self.vFacingDirection][1]
|
||||
self.vTargetPos.z = self.vTargetPos.z - villagers.NODE_AREA[self.vFacingDirection][2]
|
||||
if i == 1 then
|
||||
facingDeadEnd = true
|
||||
end
|
||||
break
|
||||
else
|
||||
possibleWalkDistance = possibleWalkDistance + 1
|
||||
end
|
||||
else
|
||||
-- reset targetPos back to the previous node
|
||||
self.vTargetPos.x = self.vTargetPos.x - villagers.NODE_AREA[self.vFacingDirection][1]
|
||||
self.vTargetPos.z = self.vTargetPos.z - villagers.NODE_AREA[self.vFacingDirection][2]
|
||||
if i == 1 then
|
||||
facingDeadEnd = true
|
||||
end
|
||||
break -- break from for loop
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if facingDeadEnd then
|
||||
|
||||
-- villager is trying to walk back but is apparently
|
||||
-- blocked by an entity. do not turn villager, but
|
||||
-- wait for next cycle and verifyPath() and attempt
|
||||
-- walking forward again.
|
||||
if walking_back then
|
||||
if villagers.log then io.write("walkBackBlocked waitAnotherCycle " ) end
|
||||
|
||||
-- an entity is blocking the node directly in front
|
||||
-- of villager. simply have villager turn
|
||||
else
|
||||
if villagers.log then io.write("frontNodeBlocked ") end
|
||||
turnVillager(self)
|
||||
end
|
||||
|
||||
elseif possibleWalkDistance > 0 then
|
||||
self.vAction = "WALK"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- turn villager 180 degrees and walk random distance back
|
||||
local function turnBack(self)
|
||||
if villagers.log then io.write("turnBack() ") end
|
||||
self.vAction = "TURNBACK"
|
||||
|
||||
local face_dir = self.vFacingDirection
|
||||
local new_facing_dir
|
||||
|
||||
if face_dir == "N" then
|
||||
new_facing_dir = "S"
|
||||
self.vYaw = villagers.YAWS[5]
|
||||
elseif face_dir == "E" then
|
||||
new_facing_dir = "W"
|
||||
self.vYaw = villagers.YAWS[7]
|
||||
elseif face_dir == "S" then
|
||||
new_facing_dir = "N"
|
||||
self.vYaw = villagers.YAWS[1]
|
||||
elseif face_dir == "W" then
|
||||
new_facing_dir = "E"
|
||||
self.vYaw = villagers.YAWS[3]
|
||||
end
|
||||
|
||||
-- update yaw to 180 degrees from prior yaw
|
||||
self.object:set_yaw(self.vYaw)
|
||||
self.vFacingDirection = new_facing_dir
|
||||
|
||||
-- if villager despawns before executing below minetest.after() code
|
||||
-- then setting vAction to 'WALKBACK' ensures villager resumes
|
||||
-- walking back 1 node upon respawn via verifyPath(self, 1, true)
|
||||
self.vAction = "WALKBACK"
|
||||
|
||||
minetest.after(1.0, function()
|
||||
if villagers.log then io.write("\n ** mtAfter.walkBack(1) ") end
|
||||
verifyPath(self, 1, true)
|
||||
if villagers.log then io.write("MTAFTER_END **") end
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
local function randomAct(self)
|
||||
local log = false
|
||||
if log then io.write("randomAct() ") end
|
||||
|
||||
local face_dir = self.vFacingDirection
|
||||
if (face_dir == "N" or face_dir == "E" or face_dir == "S" or face_dir == "W") then
|
||||
if log then io.write(face_dir.." ") end
|
||||
local targetNode = villagers.getFacingNodeInfo(self)
|
||||
local tNodeName = targetNode[2]
|
||||
local tNodeNickname = targetNode[3]
|
||||
local tNodeDrawtype = minetest.registered_nodes[tNodeName].drawtype
|
||||
|
||||
if log then io.write(tNodeDrawtype.." "..tNodeNickname.." plot="..self.vType.." ") end
|
||||
|
||||
-- air, ladders/signs, and torches
|
||||
if tNodeDrawtype == "airlike" or tNodeDrawtype == "signlike" or tNodeDrawtype == "torchlike" then
|
||||
-- villager can walk further distance from origin
|
||||
if self.vOriginDistance < self.vOriginDistMax then
|
||||
local random_num = math.random(5)
|
||||
if random_num == 1 then turnVillager(self)
|
||||
elseif random_num == 2 then villagers.standVillager(self)
|
||||
else verifyPath(self) end
|
||||
|
||||
else turnBack(self) end
|
||||
|
||||
-- plants or crops
|
||||
-- only farmers and field workers will walk into fields and and dig crops
|
||||
elseif tNodeDrawtype == "plantlike" then
|
||||
if tNodeNickname == "COTTON_8" then
|
||||
if self.vType == "field" or self.vType == "farm_tiny" or self.vType == "farm_full" then
|
||||
self.vDigging = "cotton"
|
||||
digVillager(self)
|
||||
else
|
||||
local random_num = math.random(4)
|
||||
if random_num == 1 then verifyPath(self)
|
||||
elseif random_num == 2 then villagers.standVillager(self)
|
||||
else turnVillager(self) end
|
||||
end
|
||||
|
||||
elseif tNodeNickname == "WHEAT_8" then
|
||||
if self.vType == "field" or self.vType == "farm_tiny" or self.vType == "farm_full" then
|
||||
self.vDigging = "wheat"
|
||||
digVillager(self)
|
||||
else
|
||||
local random_num = math.random(4)
|
||||
if random_num == 1 then verifyPath(self)
|
||||
elseif random_num == 2 then villagers.standVillager(self)
|
||||
else turnVillager(self) end
|
||||
end
|
||||
|
||||
elseif tNodeNickname == "COTTON_1" or tNodeNickname == "WHEAT_1" then
|
||||
if self.vType == "field" or self.vType == "farm_tiny" or self.vType == "farm_full" then
|
||||
verifyPath(self, 1)
|
||||
else
|
||||
if math.random(2) == 1 then turnVillager(self)
|
||||
else villagers.standVillager(self) end
|
||||
end
|
||||
|
||||
elseif tNodeNickname == "GRASS_1" or tNodeNickname == "GRASS_5" then
|
||||
local randomNum = math.random(6)
|
||||
if randomNum == 1 then turnVillager(self)
|
||||
elseif randomNum == 2 then villagers.standVillager(self)
|
||||
elseif randomNum == 3 then verifyPath(self)
|
||||
else
|
||||
self.vDigging = "grass"
|
||||
digVillager(self)
|
||||
end
|
||||
|
||||
elseif string.find(tNodeNickname, "FLOWER_") then
|
||||
self.vDigging = "flower"
|
||||
digVillager(self)
|
||||
|
||||
else
|
||||
local randomNum = math.random(3)
|
||||
if randomNum == 1 then villagers.standVillager(self)
|
||||
elseif randomNum == 2 then verifyPath(self)
|
||||
else turnVillager(self) end
|
||||
end
|
||||
|
||||
-- all other solid nodes
|
||||
elseif tNodeNickname == "SNOW" then
|
||||
if math.random(3) == 1 then
|
||||
villagers.standVillager(self)
|
||||
else
|
||||
self.vDigging = "snow"
|
||||
|
||||
digVillager(self)
|
||||
end
|
||||
|
||||
-- target node is a node block or common obstruction
|
||||
else turnVillager(self) end
|
||||
|
||||
-- facing direction is NE, SE, SW, NW
|
||||
else turnVillager(self) end
|
||||
|
||||
end
|
||||
|
||||
|
||||
local function animateVillager(self)
|
||||
local log = false
|
||||
if log then
|
||||
--io.write("\nANIM ")
|
||||
io.write(string.upper("\n"..self.vName).." ")
|
||||
end
|
||||
|
||||
|
||||
local current_action = self.vAction
|
||||
if current_action == "STAND" then -- tested ok!
|
||||
randomAct(self) -- randomly do another action
|
||||
|
||||
elseif current_action == "TURN" then -- tested ok!
|
||||
randomAct(self) -- randomly do another action
|
||||
|
||||
elseif current_action == "DIG" then -- tested ok!
|
||||
replaceNode(self)
|
||||
|
||||
-- if villager despawns while digging
|
||||
elseif current_action == "RESUMEDIG" then -- tested ok!
|
||||
digVillager(self)
|
||||
|
||||
elseif current_action == "WALK" then -- tested OK!
|
||||
walkVillager(self)
|
||||
|
||||
elseif current_action == "WALKING" then -- tested ok!
|
||||
if log then io.write("walking.. ") end
|
||||
|
||||
elseif current_action == "TURNBACK" then -- test ok!
|
||||
turnBack(self)
|
||||
|
||||
elseif current_action == "WALKBACK" then
|
||||
verifyPath(self, 1, true)
|
||||
|
||||
elseif current_action == "ENDCHAT" then
|
||||
if log then io.write("chatEnding.. ") end
|
||||
|
||||
else
|
||||
if log then io.write("error vAction="..current_action.." ") end
|
||||
end
|
||||
|
||||
if log then io.write("ANIM_END") end
|
||||
end
|
||||
|
||||
|
||||
function villagers.on_leftclick(self, clicker, time_from_last_punch)
|
||||
self.object:set_hp(15) -- don't let villager die from punches, for now........
|
||||
|
||||
-- prevent chat if punched by non-player entity (mobs, weapons, etc.)
|
||||
if not clicker:is_player() then
|
||||
--print("## Punched by non-player. No chat.")
|
||||
return
|
||||
end
|
||||
|
||||
-- debugging
|
||||
if villagers.log3 then print("\n## "..self.vTextureString.."\n") end
|
||||
|
||||
local current_action = self.vAction
|
||||
if villagers.log or villagers.log2 then io.write("vAction="..current_action.." ") end
|
||||
|
||||
if current_action == "STAND" or current_action == "TURN" or current_action == "WALK" or
|
||||
current_action == "DIG" or current_action == "RESUMEDIG" then
|
||||
|
||||
villagers.chatVillager(self, clicker)
|
||||
|
||||
elseif current_action == "ENDCHAT" or current_action == "ENDTRADE" then
|
||||
local message_text = villagers.chat.busy[math.random(#villagers.chat.busy)]
|
||||
villagers.showMessageBubble(self, clicker, message_text, "FRONT", 1.5)
|
||||
|
||||
elseif current_action == "CHAT" then
|
||||
if self.vChatting == clicker:get_player_name() then
|
||||
|
||||
-- if player constantly click spams villager
|
||||
-- villager will not be able to 'understand' player
|
||||
if time_from_last_punch < 0.8 then
|
||||
villagers.showMessageBubble(self, clicker, villagers.chat.spam[math.random(#villagers.chat.spam)], "FRONT", 1)
|
||||
else
|
||||
villagers.chatVillager(self, clicker)
|
||||
end
|
||||
else
|
||||
local message_text = "I'm chatting with someone\nOne moment.."
|
||||
villagers.showMessageBubble(self, clicker, message_text, "FRONT", 2)
|
||||
end
|
||||
|
||||
elseif current_action == "TRADE" then
|
||||
local message_text = "I'm trading with "..string.upper(self.vTrading).."\nOne moment.."
|
||||
villagers.showMessageBubble(self, clicker, message_text, "FRONT", 2)
|
||||
|
||||
-- villager will not chat if currently performing the
|
||||
-- below actions
|
||||
else
|
||||
local current_action = self.vAction
|
||||
if current_action == "WALKING" then
|
||||
local message_text = villagers.chat.walking[math.random(#villagers.chat.walking)]
|
||||
villagers.showMessageBubble(self, clicker, message_text, "FRONT", 1.5)
|
||||
elseif current_action == "WALKBACK" then
|
||||
local message_text = villagers.chat.walkback[math.random(#villagers.chat.walkback)]
|
||||
villagers.showMessageBubble(self, clicker, message_text, "FRONT", 1.5)
|
||||
elseif current_action == "TURNBACK" then
|
||||
local message_text = villagers.chat.busy[math.random(#villagers.chat.busy)]
|
||||
villagers.showMessageBubble(self, clicker, message_text, "FRONT", 1.5)
|
||||
else
|
||||
print("## ERROR Invalid current_action: "..current_action)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function villagers.on_rightclick(self, clicker)
|
||||
|
||||
local current_action = self.vAction
|
||||
if villagers.log then io.write("vAction="..current_action.." ") end
|
||||
|
||||
if current_action == "STAND" or current_action == "TURN" or current_action == "WALK" or
|
||||
current_action == "DIG" or current_action == "RESUMEDIG" then
|
||||
|
||||
if self.vSell == "none" then
|
||||
villagers.chatVillager(self, clicker, 3)
|
||||
else
|
||||
villagers.tradeVillager(self, clicker)
|
||||
villagers.chatVillager(self, clicker, 1)
|
||||
end
|
||||
|
||||
elseif current_action == "ENDTRADE" or current_action == "ENDCHAT" then
|
||||
local message_text = villagers.chat.busy[math.random(#villagers.chat.busy)]
|
||||
villagers.showMessageBubble(self, clicker, message_text, "FRONT", 1.5)
|
||||
|
||||
-- when player right click's on a villager who is already
|
||||
-- trading with another player
|
||||
elseif current_action == "TRADE" then
|
||||
local message_text = "I'm trading with "..string.upper(self.vTrading).."\nOne moment.."
|
||||
villagers.showMessageBubble(self, clicker, message_text, "FRONT", 2)
|
||||
|
||||
elseif current_action == "CHAT" then
|
||||
|
||||
if self.vChatting == clicker:get_player_name() then
|
||||
|
||||
self.vChatting = nil
|
||||
if self.vSell == "none" then
|
||||
villagers.endVillagerChat(self, clicker, 3)
|
||||
|
||||
else
|
||||
villagers.chatVillager(self, clicker, 1)
|
||||
villagers.tradeVillager(self, clicker)
|
||||
end
|
||||
|
||||
else
|
||||
local message_text = "I'm chatting with someone\nOne moment.."
|
||||
villagers.showMessageBubble(self, clicker, message_text, "FRONT", 2)
|
||||
end
|
||||
|
||||
-- villager will not chat if currently performing the
|
||||
-- below actions
|
||||
else
|
||||
local current_action = self.vAction
|
||||
if current_action == "WALKING" then
|
||||
local message_text = villagers.chat.walking[math.random(#villagers.chat.walking)]
|
||||
villagers.showMessageBubble(self, clicker, message_text, "FRONT", 1.5)
|
||||
elseif current_action == "WALKBACK" then
|
||||
local message_text = villagers.chat.walkback[math.random(#villagers.chat.walkback)]
|
||||
villagers.showMessageBubble(self, clicker, message_text, "FRONT", 1.5)
|
||||
elseif current_action == "TURNBACK" then
|
||||
local message_text = villagers.chat.busy[math.random(#villagers.chat.busy)]
|
||||
villagers.showMessageBubble(self, clicker, message_text, "FRONT", 1.5)
|
||||
else
|
||||
print("## ERROR Invalid current_action: "..current_action)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function villagers.on_step(self, dtime)
|
||||
|
||||
self.vTimer = self.vTimer + dtime
|
||||
if self.vChatting then
|
||||
if self.vTimer > 1 then
|
||||
|
||||
self.vTimer = 0
|
||||
|
||||
local player = minetest.get_player_by_name(self.vChatting)
|
||||
if player then
|
||||
|
||||
if self.vChatReady then
|
||||
-- villager maintains yaw direction toward player
|
||||
villagers.turnToPlayer(self, player)
|
||||
|
||||
local distance = vector.distance(self.vPos, player:getpos())
|
||||
|
||||
local currVillagerLookDir = villagers.round(self.object:get_yaw(),3)
|
||||
local currPlayerLookDir = villagers.round(player:get_look_horizontal(),3)
|
||||
if currVillagerLookDir < 0 then
|
||||
currVillagerLookDir = currVillagerLookDir + (3.141*2)
|
||||
end
|
||||
|
||||
-- player moved away from villager while chatting
|
||||
-- villager assumes player no longer wants to chat
|
||||
if distance > self.vInitialChatDistance then
|
||||
villagers.endVillagerChat(self, player, 1)
|
||||
end
|
||||
|
||||
else
|
||||
--io.write("#NOTREADY# ")
|
||||
end
|
||||
|
||||
|
||||
-- when player is no longer in-game while chatting/trading
|
||||
-- with villager, then end the chat.
|
||||
else
|
||||
-- load previous yaw value
|
||||
self.vYaw = self.vYawSaved
|
||||
|
||||
-- resume previous action
|
||||
minetest.after(2, function()
|
||||
self.object:set_yaw(self.vYaw)
|
||||
animateVillager(self)
|
||||
end)
|
||||
|
||||
self.vChatting = nil
|
||||
end
|
||||
|
||||
end
|
||||
elseif self.vTrading then
|
||||
if self.vTimer > 1 then
|
||||
self.vTimer = 0
|
||||
--io.write("\n"..self.vName.." trading ")
|
||||
end
|
||||
else
|
||||
if self.vTimer > self.vActionFrequency then
|
||||
self.vTimer = 0
|
||||
animateVillager(self)
|
||||
--io.write("EndC")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,152 +0,0 @@
|
|||
minetest.register_on_newplayer(function(ObjectRef)
|
||||
if villagers.startup_coins == false then return end
|
||||
local log = false
|
||||
|
||||
if log then print("## NEW PLAYER SPAWNED: "..ObjectRef:get_player_name()) end
|
||||
local items = {
|
||||
"villagers:coins "..math.random(20,30),
|
||||
"villagers:coins_gold "..math.random(1,5)
|
||||
}
|
||||
local player_inv = minetest.get_inventory({type="player", name=ObjectRef:get_player_name()})
|
||||
|
||||
-- add some startup coins
|
||||
for i=1, #items do
|
||||
local itemstring = items[i]
|
||||
local stack = ItemStack(itemstring)
|
||||
if not player_inv:room_for_item("main", stack) then
|
||||
minetest.env:add_item(ObjectRef:get_pos(), itemstring)
|
||||
else
|
||||
local leftover_items = player_inv:add_item("main", stack)
|
||||
minetest.env:add_item(ObjectRef:get_pos(), leftover_items)
|
||||
end
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
local log = false
|
||||
if log then
|
||||
io.write("\nonReceivFields() ")
|
||||
io.write("formname="..formname.." ")
|
||||
io.write("fields:"..dump(fields).."\n")
|
||||
end
|
||||
|
||||
if player == nil then print("\n## ERROR Player = NIL ##") return end
|
||||
if formname == nil then print("\n## ERROR formname = NIL ##") return end
|
||||
|
||||
local formname_data = string.split(formname, "|")
|
||||
local form_type = formname_data[1]
|
||||
|
||||
if form_type == "villagers:trade" then
|
||||
|
||||
-- obtain villager entity 'self' given the villager ID .. 'vID'
|
||||
local villager_id = formname_data[2]
|
||||
local self = villagers.getEntity(villager_id)
|
||||
|
||||
if fields == nil then print("\n## ERROR fields = NIL ##") return
|
||||
elseif fields.quit then
|
||||
villagers.endVillagerTrading(self, player)
|
||||
else
|
||||
local key_data
|
||||
for k,v in pairs(fields) do
|
||||
key_data = k
|
||||
end
|
||||
|
||||
if log then print("key_data="..key_data) end
|
||||
local fields_data = string.split(key_data, "|")
|
||||
local villager_name = fields_data[1]
|
||||
local item_name = fields_data[2]
|
||||
local item_quant = fields_data[3]
|
||||
local cost_name = fields_data[4]
|
||||
local cost_quant = fields_data[5]
|
||||
local item_stock = fields_data[6]
|
||||
local inv_quant = fields_data[7]
|
||||
local row_index = tonumber(fields_data[8])
|
||||
|
||||
if log then
|
||||
io.write("vName="..villager_name.." ")
|
||||
io.write("item_name="..item_name.." ")
|
||||
io.write("item_quant="..item_quant.." ")
|
||||
io.write("cost_name="..cost_name.." ")
|
||||
io.write("cost_quant="..cost_quant.." ")
|
||||
io.write("item_stock="..item_stock.." ")
|
||||
io.write("inv_quant="..inv_quant.." ")
|
||||
io.write("row_index="..row_index.." ")
|
||||
end
|
||||
|
||||
local player_name = player:get_player_name()
|
||||
local player_pos = player:get_pos()
|
||||
local player_inv = minetest.get_inventory({type="player", name=player_name})
|
||||
|
||||
-- remove cost items from player inv
|
||||
local stack
|
||||
stack = ItemStack(cost_name.." "..cost_quant)
|
||||
player_inv:remove_item("main", stack)
|
||||
|
||||
-- add item to player inventory
|
||||
stack = ItemStack(item_name.." "..item_quant)
|
||||
if not player_inv:room_for_item("main", stack) then
|
||||
-- throw item at player's feet
|
||||
minetest.env:add_item(player_pos, item_name)
|
||||
else
|
||||
local leftover = player_inv:add_item("main", stack)
|
||||
end
|
||||
|
||||
if log then
|
||||
print("## self.vSell: "..minetest.serialize(self.vSell).." ")
|
||||
end
|
||||
|
||||
-- remove stock from villager
|
||||
self.vSell[row_index][7] = item_stock - item_quant
|
||||
|
||||
-- ensure to show dialogue recognizing player traded
|
||||
-- instead of dialogue that player just cancelled the trade.
|
||||
self.vTraded = true
|
||||
|
||||
minetest.sound_play("coins", {pos = player_pos, gain = 0.4, max_hear_distance = 8} )
|
||||
minetest.show_formspec(player_name, "villagers:trade|"..self.vID, villagers.getTradingFormspec(self, player_name))
|
||||
|
||||
end
|
||||
|
||||
elseif form_type == "villagers:list" then
|
||||
local log = false
|
||||
|
||||
if log then io.write("\n## FIELDS: "..minetest.serialize(fields).." ") end
|
||||
|
||||
local button_data
|
||||
for key,value in pairs(fields) do
|
||||
button_data = key
|
||||
end
|
||||
|
||||
local form_data = string.split(button_data, "_")
|
||||
local form_action = form_data[1]
|
||||
|
||||
if log then io.write("form_action="..form_action.." ") end
|
||||
|
||||
if fields == nil then
|
||||
if log then io.write("\n## ERROR fields = NIL ##") end
|
||||
return
|
||||
|
||||
elseif form_action == "exit" or form_action == "quit" then
|
||||
if log then io.write("\n## EXITED FROM VILLAGER LIST ##") end
|
||||
|
||||
elseif form_action == "teleport" then
|
||||
local teleport_destination = form_data[2]
|
||||
player:set_pos(minetest.string_to_pos(teleport_destination))
|
||||
if log then io.write("\n## TELEPORTED TO VILLAGER: "..teleport_destination.." ##") end
|
||||
|
||||
elseif form_action == "refresh" then
|
||||
if log then io.write("\n## REFRESHED VILLAGER LIST ##") end
|
||||
minetest.show_formspec(player:get_player_name(), "villagers:list", villagers.getVillagerListFormspec(player))
|
||||
|
||||
else
|
||||
if log then io.write("\n## ERROR unexpected field: "..minetest.serialize(fields)) end
|
||||
end
|
||||
|
||||
else
|
||||
io.write("No Action - Unrecognized formname: "..formname.." ")
|
||||
end
|
||||
|
||||
end)
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
v0.17 Hotfix
|
||||
- removed references to bucket:bucket_empty to fix NIL errors
|
||||
- villager spawn y-pos now based on village y-pos
|
||||
- /villagers list command only shows villagers with trade items
|
||||
- trade items offered by villagers depend on surrounding region
|
||||
- village_ruins mod *seems* compatible now (more testing needed)
|
||||
|
||||
v0.17
|
||||
[added]
|
||||
- Added item description to trading formspec
|
||||
- Added initial coins for player
|
||||
-- you can disable from config.lug file
|
||||
- Villager 'goodbye' dialogue after trading depends
|
||||
-- changes if player actually trading something
|
||||
|
||||
[improved]
|
||||
- Improved looks of trading formspec
|
||||
- Cleaned up code for villager trading
|
||||
|
||||
|
||||
v0.16
|
||||
[fixed]
|
||||
- Eliminated more NIL errors that crash the game
|
||||
- Stop any ongoing digging anim by villager when trading
|
||||
|
||||
[added]
|
||||
- Young children villagers tell you they don't know much about trading
|
||||
- New chat command to list all nearby traders and teleport to them
|
||||
- Created node metadata formspec at village pos that shows any 'soft' errors
|
||||
|
||||
[improved]
|
||||
- Re-worked entire spawning algorithm to use LBMs!
|
||||
-- if some villagers do not spawn immediately on the mob spawner,
|
||||
-- unload/reload map block by walking away about 40m and returning
|
||||
- Calculate 'region' type based on surrounding nodes at village position
|
||||
- Villagers adopt attributes from mg_villagers: gender, age, and job title!
|
||||
- Trader items/goods are now determined by the villager's job title,
|
||||
-- and is no longer determined by building/schem type.
|
||||
|
||||
|
||||
v0.15
|
||||
[fixed]
|
||||
- removed crash on non-players punching villagers
|
||||
|
||||
[added]
|
||||
- completed main trading formspec
|
||||
- traders will show **trader** on their infospec
|
||||
- added coins as the main form of buying goods
|
||||
- completed cost table based on coins for common items villagers will trade
|
||||
- 'quest' traders who give ingots for a large number of items
|
||||
- 'simplejob' traders who give a coin for small amount of resources
|
||||
- if player tries to chat/trade while villager is busy (walking, turning about, etc)
|
||||
-- villager will say 'pardon', 'excuse me', etc.
|
||||
- added lots of gameplay facts as dialogue that all villagers may say
|
||||
|
||||
[improved]
|
||||
- formspec shows goods based on village and plot schem type
|
||||
- formspec shows correct values for cost, stock, etc.
|
||||
- only certain villagers trade (farmers, smiths, barkeeps, etc)
|
||||
- can trade while in the middle of chatting now
|
||||
- improved chat bubble interaction a bit
|
||||
- villager tells player they are busy if player tries to chat or trade while another player is doing so first
|
||||
- added <village> and <schem> parameter to chat command
|
||||
- organized all code into their own lua files
|
||||
- infospec on villager has useful info (and some not)
|
||||
|
||||
|
||||
v0.14
|
||||
[improved]
|
||||
- if village generates aritificial snow, villagers spawn as cold region inhabitants (light skinned and warmer clothing)
|
||||
- for each residental building (home, hut, tavern, etc), one villager will spawn for each bed that exists
|
||||
- these villagers will now spawn on the convenient mob spawners (thanks Sokomine!)
|
||||
- other villagers still spawn next to the various non-residental buildings (forge, library, bakery, sawmill, church, etc)
|
||||
[added]
|
||||
- chat command to manually spawn a villager at current position (instructions above)
|
||||
|
||||
v0.13
|
||||
[fixed]
|
||||
- white chat bubble image no longer disappears on rare occasions while chatting
|
||||
- some chat dialogue no longer appear twice in succession
|
||||
- moved existing dialogue to villager types that make more sense: from secular to library, and trader to shop
|
||||
[improved]
|
||||
- redo and optimize code for dynamic body customization (skin, eyes, hair, etc)
|
||||
- redo and optimize code for dynamic clothing customization (shirts, pants, dresses, boots, etc)
|
||||
- reworked colorization for all body and clothing textures
|
||||
- moved all lua constants for names, plots, colors, and dialogue into its own lua file
|
||||
- tweaked relationship between village types and region type
|
||||
[added]
|
||||
- unique texture for priests (though it's not that great for now), which are villagers that spawn by churches.
|
||||
- unique 'got to go' message after villager has spoken through their 'main' dialgue
|
||||
- color variation between body skin colors of villagers among different regions types *
|
||||
- support for cottage, village_towntest, and village_gambit building mods
|
||||
- custom dialgue for 'baker', 'mill', 'trader', 'hut', and 'secular'
|
||||
|
||||
v0.12
|
||||
[fixed]
|
||||
- digging of field/garden crops only allowed by farmers and field workers
|
||||
- walking on newly planted field/garden only allowed by farmers and field workers
|
||||
- 'traveler' villagers only spawn from 'empty' plot types now and no longer also from 'deco'
|
||||
[improved]
|
||||
- tweaked chatbubble positioning when trading
|
||||
- shifted eyes down a few pixels to better match hair position
|
||||
- optimized some code regarding hair auto-generating and chat dialogue while trading
|
||||
[added]
|
||||
- old aged male villagers have gray hair (or bald) and facial hair
|
||||
- old aged female villagers have gray hair
|
||||
- old aged villagers of either genders might wear eye glasses
|
||||
- while villager is digging, chat dialogue reflects what is it they are digging (wheat, cotton, grass, snow)
|
||||
- custom chat dialogue for allmende and deco villager types
|
||||
|
||||
v0.11
|
||||
[fixed]
|
||||
- prevent 'traveler' villager types from being young/child physical size
|
||||
- fix NIL dialogue error when chatting with certain 'farmer' villagers
|
||||
- allow chat when villager is about to walk and when villager is digging
|
||||
[improved]
|
||||
- custom dialogue when player chats while villager is about to walk or while digging
|
||||
- alert messages now include villager's name
|
||||
- tweaked chat text to better fit width of the chat bubble
|
||||
[added]
|
||||
- all travelers now wear straps with either backpack or sword on their backs
|
|
@ -1,171 +0,0 @@
|
|||
local function validateVillageParameters(parameter, type, village)
|
||||
local log = false
|
||||
if log then io.write("\nvalidateParams() ") end
|
||||
|
||||
if type == "region" then
|
||||
if log then io.write("for REGION ") end
|
||||
if parameter == nil then
|
||||
if log then io.write("is NIL ") end
|
||||
return "normal"
|
||||
else
|
||||
for i=1, #villagers.REGIONS do
|
||||
if villagers.REGIONS[i] == parameter then
|
||||
if log then io.write("is VALID ") end
|
||||
return parameter
|
||||
end
|
||||
end
|
||||
if log then io.write("notFound setTo=NORMAL ") end
|
||||
return "normal"
|
||||
end
|
||||
|
||||
elseif type == "village" then
|
||||
if log then io.write("for VILLAGE ") end
|
||||
if parameter == nil then
|
||||
if log then io.write("is NIL ") end
|
||||
return "nore"
|
||||
else
|
||||
for i=1, #villagers.VILLAGES do
|
||||
if villagers.VILLAGES[i] == parameter then
|
||||
if log then io.write("is VALID ") end
|
||||
return parameter
|
||||
end
|
||||
end
|
||||
if log then io.write("notFound setTo=NORE ") end
|
||||
return "nore"
|
||||
end
|
||||
|
||||
elseif type == "plot" then
|
||||
if log then io.write("for PLOT ") end
|
||||
if parameter == nil then
|
||||
if log then io.write("is NIL ") end
|
||||
return "empty"
|
||||
else
|
||||
for i=1, #villagers.BUILDINGS do
|
||||
if villagers.BUILDINGS[i] == parameter then
|
||||
if log then io.write("is VALID ") end
|
||||
return parameter
|
||||
end
|
||||
end
|
||||
if log then io.write("notFound setTo=EMPTY ") end
|
||||
return "empty"
|
||||
end
|
||||
|
||||
elseif type == "schem" then
|
||||
if log then io.write("for SCHEM ") end
|
||||
local schems_available = villagers.SCHEMS[village]
|
||||
local random_schem = schems_available[math.random(#schems_available)]
|
||||
if parameter == nil then
|
||||
if log then io.write("is NIL ") end
|
||||
return random_schem
|
||||
else
|
||||
for i=1, #schems_available do
|
||||
if schems_available[i] == parameter then
|
||||
if log then io.write("is VALID ") end
|
||||
return parameter
|
||||
end
|
||||
end
|
||||
if log then io.write("notFound setTo="..random_schem.." ") end
|
||||
return random_schem
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function villagers.getVillagerListFormspec(player)
|
||||
local log = false
|
||||
|
||||
local entities = {}
|
||||
for _, self in pairs(minetest.luaentities) do
|
||||
if self.vSell ~= "none" then
|
||||
table.insert(entities, self)
|
||||
end
|
||||
end
|
||||
local count = #entities
|
||||
if log then print(" villager count: "..count) end
|
||||
|
||||
local width_form = 13.0
|
||||
|
||||
local h_labels = 1
|
||||
local h_row = 0.7
|
||||
local h_exit_button = 1
|
||||
local h_between = 0.5 -- between label and list, and between list and exit button
|
||||
|
||||
local height_form = h_labels + (h_row * count) + h_exit_button
|
||||
|
||||
if log then print("formspec width="..width_form.." height="..height_form) end
|
||||
|
||||
-- GUI related stuff
|
||||
--local bg = "bgcolor[#080808BB;true]"
|
||||
local bg_image = "background[0,0;0,0;gui_formbg.png;true]"
|
||||
|
||||
local formspec =
|
||||
-- gui background attributes
|
||||
"size["..width_form..","..height_form.."]"..bg_image..
|
||||
|
||||
-- header row
|
||||
"label[0.3,0;Village]"..
|
||||
"label[1.8,0;Plot]"..
|
||||
"label[2.5,0;Bed]"..
|
||||
"label[3.2,0;Name]"..
|
||||
"label[5.2,0;Title]"..
|
||||
"label[7.3,0;Origin]"..
|
||||
"label[8.3.,0;Current]"..
|
||||
"label[9.4,0;Walked]"..
|
||||
"label[10.6,0;Dist]"
|
||||
--"label[11.5,0;Action]"
|
||||
|
||||
-- main villager list
|
||||
if count > 0 then
|
||||
for i = 1, count do
|
||||
local self = entities[i]
|
||||
local villager_name = self.vName
|
||||
formspec = formspec..
|
||||
"label[0.3,"..(h_row * i)..";"..self.vVillage.."]"..
|
||||
"label[1.8,"..(h_row * i)..";"..self.vPlot.."]"..
|
||||
"label[2.5,"..(h_row * i)..";"..self.vBed.."]"..
|
||||
"label[3.2,"..(h_row * i)..";"..villager_name.."]"..
|
||||
"label[5.2,"..(h_row * i)..";"..self.vTitle.."]"..
|
||||
"label[7.3,"..(h_row * i)..";"..self.vOriginPos.x..","..self.vOriginPos.z.."]"..
|
||||
"label[8.3,"..(h_row * i)..";"..self.vPos.x..","..self.vPos.z.."]"..
|
||||
"label[9.6,"..(h_row * i)..";"..self.vTotalDistance.."]"..
|
||||
"label[10.6,"..(h_row * i)..";"..villagers.round(vector.distance(self.vPos, player:get_pos()), 1).."]"
|
||||
|
||||
local teleport_data = "teleport_"..minetest.pos_to_string(self.vPos)
|
||||
formspec = formspec.."button[11.5,"..(h_row * i)..";1,0.5;"..teleport_data..";go]"
|
||||
end
|
||||
|
||||
else
|
||||
formspec = formspec.. "label[5.5,0.5;(no villagers nearby)]"
|
||||
end
|
||||
|
||||
formspec = formspec.. "button_exit[4.5,"..(height_form - 1)..";2,1;exit;Exit]"..
|
||||
"button[6.5,"..(height_form - 1)..";2,1;refresh;refresh]"
|
||||
|
||||
return formspec
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- manually spawn villager via chat command. mostly for testing.
|
||||
minetest.register_chatcommand("villagers", {
|
||||
params = "<command>",
|
||||
description = "Villager Tools",
|
||||
privs = {},
|
||||
func = function(name, param)
|
||||
local log = false
|
||||
local params = string.split(param, " ")
|
||||
local command_type = params[1]
|
||||
local player = minetest.get_player_by_name(name)
|
||||
|
||||
if command_type == "list" then
|
||||
if log then print("\n## SHOWING VILAGERS LIST ##") end
|
||||
local player = minetest.get_player_by_name(name)
|
||||
minetest.show_formspec(name, "villagers:list", villagers.getVillagerListFormspec(player))
|
||||
|
||||
else
|
||||
if command_type == nil then command_type = "NIL" end
|
||||
minetest.chat_send_player(name, "Invalid command: "..command_type)
|
||||
end
|
||||
|
||||
end,
|
||||
})
|
|
@ -1,470 +0,0 @@
|
|||
|
||||
-- ==================================== CHAT BUBBLES AND DIALOGUE =========================================
|
||||
-- ========================================================================================================
|
||||
-- show alert box messages
|
||||
function villagers.showMessageBubble(self, player, message_text, message_location, clear_timer)
|
||||
local player_name = player:get_player_name()
|
||||
local table_data = minetest.serialize(self.vHudIds[player_name])
|
||||
|
||||
if villagers.log2 then
|
||||
io.write("showBubble() ")
|
||||
io.write("self.vHudIds["..player_name.."]:"..table_data.." ")
|
||||
end
|
||||
|
||||
-- player is chatting with a villager
|
||||
if self then
|
||||
|
||||
local x_offset, message_position, message_align, bubble_texture
|
||||
local y_offset
|
||||
|
||||
if message_location == "FRONT" then
|
||||
x_offset = 0
|
||||
y_offset = -300
|
||||
bubble_texture = "bubble_villager_front.png"
|
||||
message_position = { x = 0.5, y = 1.0 }
|
||||
message_align = {x=0, y=0}
|
||||
elseif message_location == "FRONTRIGHT" then
|
||||
x_offset = -200
|
||||
y_offset = -150
|
||||
bubble_texture = "bubble_villager_frontright.png"
|
||||
message_position = { x = 1.0, y = 1.0 }
|
||||
message_align = {x=1, y=0}
|
||||
elseif message_location == "RIGHT" then
|
||||
x_offset = -200
|
||||
y_offset = -150
|
||||
bubble_texture = "bubble_villager_right.png"
|
||||
message_position = { x = 1.0, y = 1.0 }
|
||||
message_align = {x=1, y=0}
|
||||
elseif message_location == "BEHINDRIGHT" then
|
||||
x_offset = -200
|
||||
y_offset = -150
|
||||
bubble_texture = "bubble_villager_behindright.png"
|
||||
message_position = { x = 1.0, y = 1.0 }
|
||||
message_align = {x=1, y=0}
|
||||
elseif message_location == "FRONTLEFT" then
|
||||
x_offset = 200
|
||||
y_offset = -150
|
||||
bubble_texture = "bubble_villager_frontleft.png"
|
||||
message_position = { x = 0.0, y = 1.0 }
|
||||
message_align = {x=-1, y=0}
|
||||
elseif message_location == "LEFT" then
|
||||
x_offset = 200
|
||||
y_offset = -150
|
||||
bubble_texture = "bubble_villager_left.png"
|
||||
message_position = { x = 0.0, y = 1.0 }
|
||||
message_align = {x=-1, y=0}
|
||||
elseif message_location == "BEHINDLEFT" then
|
||||
x_offset = 200
|
||||
y_offset = -150
|
||||
bubble_texture = "bubble_villager_behindleft.png"
|
||||
message_position = { x = 0.0, y = 1.0 }
|
||||
message_align = {x=-1, y=0}
|
||||
elseif message_location == "BEHIND" then
|
||||
x_offset = 0
|
||||
y_offset = -150
|
||||
bubble_texture = "bubble_villager_behind.png"
|
||||
message_position = { x = 0.5, y = 1.0 }
|
||||
message_align = {x=0, y=0}
|
||||
|
||||
--[[ --formspec cuts into chatbubble. figure out later
|
||||
elseif message_location == "TRADINGBYE" then
|
||||
x_offset = 0
|
||||
y_offset = -240
|
||||
bubble_texture = "bubble_villager_front.png"
|
||||
message_position = { x = 0.5, y = 1.0 }
|
||||
message_align = {x=0, y=0}
|
||||
--]]
|
||||
elseif message_location == "TRADINGBYE" then
|
||||
x_offset = 0
|
||||
y_offset = -240
|
||||
bubble_texture = "bubble_villager_front.png"
|
||||
message_position = { x = 0.5, y = 1.0 }
|
||||
message_align = {x=0, y=0}
|
||||
else
|
||||
--io.write("ERROR - Invalid message_location="..message_location.." ")
|
||||
return
|
||||
end
|
||||
|
||||
local nameAndTitleString = "~ "..self.vName.." ~"
|
||||
|
||||
-- show chat bubble
|
||||
local chat_box_id = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
scale = { x = 1, y = 1 },
|
||||
position = message_position,
|
||||
text = bubble_texture,
|
||||
offset = {x=0+x_offset, y=y_offset},
|
||||
})
|
||||
|
||||
--show villager's name within chat bubble
|
||||
local chat_name_id = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
position = message_position,
|
||||
text = nameAndTitleString,
|
||||
number = 0x666666,
|
||||
offset = {x=0+x_offset, y=y_offset-25}
|
||||
})
|
||||
|
||||
--show main text within chat bubble
|
||||
local chat_text_id = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
position = message_position,
|
||||
text = message_text,
|
||||
number = 0x000000,
|
||||
offset = {x=0+x_offset, y=y_offset+15}
|
||||
})
|
||||
|
||||
local villager_id = self.vID
|
||||
local player_name = player:get_player_name()
|
||||
if self.vHudIds[player_name] == nil then self.vHudIds[player_name] = {} end
|
||||
|
||||
table.insert(self.vHudIds[player_name], 1, {chat_box_id, chat_name_id, chat_text_id})
|
||||
minetest.after(clear_timer, function()
|
||||
--io.write("\n## MINETEST.AFTER()!! deleting chat IDs..")
|
||||
villagers.removeTextHud(self, player)
|
||||
end)
|
||||
|
||||
if villagers.log2 then
|
||||
--io.write("newHudIds:"..dump(self.vHudIds.chats.timed[villager_id]).." ")
|
||||
local table_data = minetest.serialize(self.vHudIds[player_name])
|
||||
io.write("self.vHudIds["..player_name.."]:"..table_data.." ")
|
||||
end
|
||||
|
||||
-- play bubble pop sound at the villager's position
|
||||
minetest.sound_play("pop", {pos = self.vPos} )
|
||||
|
||||
-- player is receiving a notification from this mod
|
||||
else
|
||||
local vertical_position = 0.0
|
||||
local texture_filename = "bubble_notify.png"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local function yawToDegrees(yaw)
|
||||
|
||||
local degrees_value
|
||||
local PI_VALUE = 3.141
|
||||
local MULTIPLIER = 180 / PI_VALUE
|
||||
|
||||
-- convert yaw to value ranging from 2*PI to zero
|
||||
if yaw < 0 then
|
||||
yaw = (PI_VALUE * 2) + yaw
|
||||
end
|
||||
|
||||
yaw = villagers.round(yaw, 3)
|
||||
|
||||
if yaw == 0 or yaw == 6.282 then
|
||||
degrees_value = 0
|
||||
|
||||
-- range from 0 to 179 degrees
|
||||
elseif yaw > PI_VALUE then
|
||||
degrees_value = 180 - ((yaw - PI_VALUE) * MULTIPLIER)
|
||||
|
||||
-- range from 181 to 359 degrees
|
||||
elseif yaw < PI_VALUE then
|
||||
degrees_value = 180 + ((PI_VALUE - yaw) * MULTIPLIER)
|
||||
|
||||
-- is exactly South
|
||||
elseif yaw == PI_VALUE then
|
||||
degrees_value = 180
|
||||
|
||||
else
|
||||
--io.write("ERROR - Invalid yaw="..yaw.." ")
|
||||
degrees_value = 0
|
||||
end
|
||||
|
||||
degrees_value = villagers.round(degrees_value,1)
|
||||
return degrees_value
|
||||
end
|
||||
|
||||
local function getLookDirection(yaw)
|
||||
local degrees = yawToDegrees(yaw)
|
||||
local lookDirection
|
||||
if degrees > 337.5 or degrees <= 22.5 then lookDirection = 1
|
||||
elseif degrees > 22.5 and degrees <= 67.5 then lookDirection = 2
|
||||
elseif degrees > 67.5 and degrees <= 112.5 then lookDirection = 3
|
||||
elseif degrees > 112.5 and degrees <= 157.5 then lookDirection = 4
|
||||
elseif degrees > 157.5 and degrees <= 202.5 then lookDirection = 5
|
||||
elseif degrees > 202.5 and degrees <= 247.5 then lookDirection = 6
|
||||
elseif degrees > 247.5 and degrees <= 292.5 then lookDirection = 7
|
||||
elseif degrees > 292.5 and degrees <= 337.5 then lookDirection = 8
|
||||
else lookDirection = 1
|
||||
end
|
||||
return lookDirection
|
||||
end
|
||||
|
||||
local ORIENTATIONS = {
|
||||
{ "BEHIND", "BEHINDRIGHT", "RIGHT", "FRONTRIGHT", "FRONT", "FRONTLEFT", "LEFT", "BEHINDLEFT" },
|
||||
{ "BEHINDLEFT", "BEHIND", "BEHINDRIGHT", "RIGHT", "FRONTRIGHT", "FRONT", "FRONTLEFT", "LEFT" },
|
||||
{ "LEFT", "BEHINDLEFT", "BEHIND", "BEHINDRIGHT", "RIGHT", "FRONTRIGHT", "FRONT", "FRONTLEFT" },
|
||||
{ "FRONTLEFT", "LEFT", "BEHINDLEFT", "BEHIND", "BEHINDRIGHT", "RIGHT", "FRONTRIGHT", "FRONT" },
|
||||
{ "FRONT", "FRONTLEFT", "LEFT", "BEHINDLEFT", "BEHIND", "BEHINDRIGHT", "RIGHT", "FRONTRIGHT" },
|
||||
{ "FRONTRIGHT", "FRONT", "FRONTLEFT", "LEFT", "BEHINDLEFT", "BEHIND", "BEHINDRIGHT", "RIGHT" },
|
||||
{ "RIGHT", "FRONTRIGHT", "FRONT", "FRONTLEFT", "LEFT", "BEHINDLEFT", "BEHIND", "BEHINDRIGHT" },
|
||||
{ "BEHINDRIGHT", "RIGHT", "FRONTRIGHT", "FRONT", "FRONTLEFT", "LEFT", "BEHINDLEFT", "BEHIND" }
|
||||
}
|
||||
|
||||
local function getChatOrientation(self, player)
|
||||
local villagerDir = getLookDirection(self.object:get_yaw())
|
||||
local playerDir = getLookDirection(player:get_look_horizontal())
|
||||
return ORIENTATIONS[villagerDir][playerDir]
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--[[
|
||||
self: villager lua entity
|
||||
chat_texts: table that contains one or more strings of chat dialogue
|
||||
script_type: corresponds to the lua entity variables 'vScriptHi', 'vScriptBye', 'vScriptMain'
|
||||
--]]
|
||||
function villagers.getRandomChatText(self, chat_texts, script_type, chat_text_count)
|
||||
--print("villagers.getRandomChatText("..script_type.." "..chat_text_count..") ")
|
||||
|
||||
-- all of villager's chat text has been used up (via table.remove) so need to
|
||||
-- reload from saved. this ensures the villager keeps the same initial set of
|
||||
-- chat texts, but it will be in random order upon each reload.
|
||||
-- ** this situation never occurs for HI, BYE, and smalltalk chat tests **
|
||||
if self[script_type] then
|
||||
for i=1, #self[script_type.."Saved"] do
|
||||
local text_string = self[script_type.."Saved"][i]
|
||||
table.insert(self[script_type], text_string)
|
||||
end
|
||||
|
||||
-- first time villager object is activated. initialize with chat text and save
|
||||
-- a copy that set for reload later as player 'uses up' those chat taxts when chatting
|
||||
else
|
||||
self[script_type] = {}
|
||||
self[script_type.."Saved"] = {}
|
||||
for i=1, chat_text_count do
|
||||
local text_string = table.remove(chat_texts, math.random(#chat_texts))
|
||||
|
||||
-- add dynamic customization to villagers main chat text, not HI, BYE, or smalltalk
|
||||
-- this is mostly for testing and may be removed later
|
||||
if script_type == "vScriptMain" then
|
||||
text_string = string.gsub(text_string, "VILLAGER_NAME", self.vName)
|
||||
text_string = string.gsub(text_string, "BUILDING_TYPE", self.vType)
|
||||
text_string = string.gsub(text_string, "AGE", self.vAge)
|
||||
text_string = string.gsub(text_string, "GENDER", self.vGender)
|
||||
end
|
||||
|
||||
table.insert(self[script_type], text_string)
|
||||
table.insert(self[script_type.."Saved"], text_string)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function villagers.endVillagerChat(self, player, end_type)
|
||||
if villagers.log2 then io.write("endChat() end_type="..end_type.." ") end
|
||||
|
||||
local message_location = getChatOrientation(self, player)
|
||||
|
||||
local goodbyes
|
||||
|
||||
-- normal 'goodbye' dialogues variations when player has walked away
|
||||
if end_type == 1 then
|
||||
goodbyes = self.vScriptBye
|
||||
|
||||
-- remove current chat bubble the villager has displayed
|
||||
villagers.removeTextHud(self, player)
|
||||
|
||||
-- display the goodbye message bubble
|
||||
villagers.showMessageBubble(self, player, goodbyes[math.random(#goodbyes)], message_location, 1)
|
||||
|
||||
-- villager went through all 'main' dialogue
|
||||
-- so then ended the chat with player 'got to go!'
|
||||
elseif end_type == 2 then
|
||||
goodbyes = self.vScriptGtg
|
||||
villagers.showMessageBubble(self, player, goodbyes[math.random(#goodbyes)], message_location, 2)
|
||||
|
||||
-- villager has nothing to trade, so 'got to go'
|
||||
elseif end_type == 3 then
|
||||
if self.vAge == "young" then
|
||||
goodbyes = villagers.chat.trade.none_young
|
||||
else
|
||||
goodbyes = villagers.chat.trade.none
|
||||
end
|
||||
|
||||
villagers.showMessageBubble(self, player, goodbyes[math.random(#goodbyes)], message_location, 2.5)
|
||||
|
||||
else
|
||||
print("## ERROR Invalid end_type: "..end_type.." ##")
|
||||
end
|
||||
|
||||
-- load previous yaw value
|
||||
self.vYaw = self.vYawSaved
|
||||
|
||||
-- resume previous look direction
|
||||
minetest.after(2, function()
|
||||
self.object:set_yaw(self.vYaw)
|
||||
|
||||
-- player had initiated tradeing while villager was
|
||||
-- about to walk or dig. now continue with that action.
|
||||
if self.vWalkReady then
|
||||
self.vAction = "WALK"
|
||||
self.vWalkReady = false
|
||||
elseif self.vDigReady then
|
||||
self.vAction = "RESUMEDIG"
|
||||
self.vDigReady = false
|
||||
else
|
||||
self.vAction = "STAND"
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
self.vChatting = nil
|
||||
self.vAction = "ENDCHAT"
|
||||
end
|
||||
|
||||
function villagers.chatVillager(self, player, chat_type)
|
||||
local log = false
|
||||
if log then io.write("chat() ") end
|
||||
|
||||
-- if player initiated chat when villager was about to start
|
||||
-- walking or digging. save this state to continue action once chat ends
|
||||
if self.vAction == "WALK" then
|
||||
if log then io.write("villagerAboutToWalk ") end
|
||||
self.vWalkReady = true
|
||||
elseif self.vAction == "DIG" then
|
||||
if log then io.write("villagerAboutToDig ") end
|
||||
self.vDigReady = true
|
||||
minetest.sound_stop(self.vSoundHandle)
|
||||
self.object:set_animation(
|
||||
{x=self.animation["stand_start"], y=self.animation["stand_end"]},
|
||||
self.animation_speed + math.random(10)
|
||||
)
|
||||
elseif self.vAction == "RESUMEDIG" then
|
||||
if log then io.write("villagerAboutToResumeDig ") end
|
||||
self.vDigReady = true
|
||||
end
|
||||
|
||||
self.vAction = "CHAT"
|
||||
|
||||
local player_name = player:get_player_name()
|
||||
|
||||
-- show a quick greeting bubble for when player right clicks
|
||||
-- on villager for trading
|
||||
if chat_type == 1 then
|
||||
if log then io.write("startTrading ") end
|
||||
|
||||
--formspec cuts into chatbubble. figure out later
|
||||
--villagers.showMessageBubble(self, player, villagers.chat.trade.hi[math.random(#villagers.chat.trade.hi)], "TRADING", 4)
|
||||
|
||||
-- player ended trading with villager, so villager says custom trading goodbyes
|
||||
elseif chat_type == 2 then
|
||||
if log then io.write("playerEndedTrade vTraded="..tostring(self.vTraded).." ") end
|
||||
local bye_text
|
||||
if self.vTraded then
|
||||
bye_text = villagers.chat.trade.bye_buy
|
||||
villagers.showMessageBubble(self, player, bye_text[math.random(#bye_text)], "TRADINGBYE", 2)
|
||||
self.vTraded = false
|
||||
else
|
||||
bye_text = villagers.chat.trade.bye
|
||||
villagers.showMessageBubble(self, player, bye_text[math.random(#bye_text)], "TRADINGBYE", 2)
|
||||
end
|
||||
|
||||
-- villager has no items to trade, and will state as such and say they got to go
|
||||
elseif chat_type == 3 then
|
||||
if log then io.write("VillageNothingToSell ") end
|
||||
|
||||
self.vChatting = player_name
|
||||
self.vYawSaved = self.vYaw
|
||||
|
||||
-- make villager face player
|
||||
villagers.turnToPlayer(self, player)
|
||||
|
||||
-- end chat with villager saying nothing to sell
|
||||
villagers.endVillagerChat(self, player, 3)
|
||||
|
||||
-- player continuing ongoing dialogue with villager
|
||||
elseif self.vChatting then
|
||||
if log then io.write("continuingConversation ") end
|
||||
local next_dialogue
|
||||
|
||||
-- pop/remove one random dialogue from 'smalltalk' table
|
||||
local random_num = math.random(6)
|
||||
if random_num == 1 then
|
||||
local dialogue_count = #self.vScriptSmalltalk
|
||||
if log then io.write("getDialogue:smalltalk fromRemain="..dialogue_count.." ") end
|
||||
if dialogue_count == 0 then
|
||||
local full_dialgue_table = villagers.copytable(villagers.chat.smalltalk)
|
||||
villagers.getRandomChatText(self, full_dialgue_table, "vScriptSmalltalk", 3)
|
||||
if log then io.write("tableReloaded:smalltalk ") end
|
||||
end
|
||||
|
||||
next_dialogue = table.remove(self.vScriptSmalltalk, math.random(dialogue_count))
|
||||
villagers.showMessageBubble(self, player, next_dialogue, "FRONT", 3)
|
||||
if log then io.write("dialogueDisplayedOK remain="..#self.vScriptSmalltalk.." ") end
|
||||
|
||||
-- pop/remove one random dialogue from 'mainchat' table
|
||||
elseif random_num > 3 then
|
||||
local dialogue_count = #self.vScriptMain
|
||||
if log then io.write("getDialogue:main fromRemain="..dialogue_count.." ") end
|
||||
if dialogue_count == 0 then
|
||||
local full_dialgue_table = villagers.copytable(villagers.chat[self.vType].mainchat)
|
||||
villagers.getRandomChatText(self, full_dialgue_table, "vScriptMain", 3)
|
||||
if log then io.write("tableReloaded:main ") end
|
||||
villagers.endVillagerChat(self, player, 2)
|
||||
if log then io.write("chatEnded ") end
|
||||
else
|
||||
next_dialogue = table.remove(self.vScriptMain, math.random(dialogue_count))
|
||||
villagers.showMessageBubble(self, player, next_dialogue, "FRONT", 3)
|
||||
if log then io.write("dialogueDisplayedOK remain="..#self.vScriptMain.." ") end
|
||||
end
|
||||
|
||||
else
|
||||
local dialogue_count = #self.vScriptGameFacts
|
||||
if log then io.write("getDialogue:gamefacts fromRemain="..dialogue_count.." ") end
|
||||
if dialogue_count == 0 then
|
||||
local full_dialgue_table = villagers.copytable(villagers.chat.gamefacts)
|
||||
villagers.getRandomChatText(self, full_dialgue_table, "vScriptGameFacts", 2)
|
||||
if log then io.write("tableReloaded:gamefacts ") end
|
||||
end
|
||||
|
||||
next_dialogue = table.remove(self.vScriptGameFacts, math.random(dialogue_count))
|
||||
villagers.showMessageBubble(self, player, next_dialogue, "FRONT", 3)
|
||||
if log then io.write("dialogueDisplayedOK remain="..#self.vScriptGameFacts.." ") end
|
||||
end
|
||||
|
||||
-- player starting new conversation with villager
|
||||
else
|
||||
if log then io.write("\nnewConversation ") end
|
||||
|
||||
self.vChatting = player_name
|
||||
self.vYawSaved = self.vYaw
|
||||
|
||||
-- make villager face player
|
||||
villagers.turnToPlayer(self, player)
|
||||
|
||||
-- save initial distance between player and villager. if player
|
||||
-- surpasses this distance, villager will end chat.
|
||||
self.vInitialChatDistance = vector.distance(self.vPos, player:getpos())
|
||||
|
||||
local greeting_text
|
||||
|
||||
-- if player chats while villager was about to walk
|
||||
if self.vWalkReady then
|
||||
if log then io.write("villagerAboutToWalk ") end
|
||||
greeting_text = villagers.chat.walk[math.random(#villagers.chat.walk)]
|
||||
|
||||
-- if player chats while villager was digging
|
||||
elseif self.vDigReady then
|
||||
if log then io.write("villagerDigging ") end
|
||||
greeting_text = villagers.chat.dig[self.vDigging][math.random(#villagers.chat.dig[self.vDigging])]
|
||||
|
||||
-- show one of the random default greetings for this villager type
|
||||
else
|
||||
if log then io.write("showDefaultHI ") end
|
||||
local greetings = self.vScriptHi
|
||||
greeting_text = greetings[math.random(#greetings)]
|
||||
end
|
||||
|
||||
villagers.showMessageBubble(self, player, greeting_text, "FRONT", 3)
|
||||
|
||||
end
|
||||
|
||||
if log then io.write("\n") end
|
||||
end
|
|
@ -1,77 +0,0 @@
|
|||
local enable_coin_drop = minetest.settings:get_bool("coin_drop")
|
||||
if enable_coin_drop == nil then enable_coin_drop = true end
|
||||
|
||||
local coin_nodes = { "default:dirt", "default:dirt_with_grass", "default:dirt_with_dry_grass", "default:dirt_with_rainforest_litter", "default:dirt_with_snow" }
|
||||
local ethereal_found = false
|
||||
|
||||
for i, name in ipairs(minetest.get_modnames()) do
|
||||
if name == "ethereal" then ethereal_found = true
|
||||
end
|
||||
end
|
||||
|
||||
-- coin findings in dirty nodes :P
|
||||
local function coin_drop_items(node)
|
||||
local set_dirt = "default:dirt"
|
||||
|
||||
-- special treatment for "ethereal:dry_dirt" because it has no drop value, so it gets one :)
|
||||
if node == "ethereal:dry_dirt" then set_dirt = node end
|
||||
|
||||
return { { items = {"villagers:coins_gold", set_dirt}, rarity = 50 }, { items = {"villagers:coins", set_dirt}, rarity = 30 } }
|
||||
end
|
||||
|
||||
if enable_coin_drop then
|
||||
|
||||
minetest.log("[Villagers] hiding coins in dirt, please wait!")
|
||||
|
||||
-- ethereal support
|
||||
if ethereal_found == true then
|
||||
minetest.log("adding dirt from ethereal")
|
||||
|
||||
table.insert(coin_nodes, "ethereal:bamboo_dirt")
|
||||
table.insert(coin_nodes, "ethereal:cold_dirt")
|
||||
table.insert(coin_nodes, "ethereal:crystal_dirt")
|
||||
table.insert(coin_nodes, "ethereal:dry_dirt")
|
||||
table.insert(coin_nodes, "ethereal:fiery_dirt")
|
||||
table.insert(coin_nodes, "ethereal:grey_dirt")
|
||||
table.insert(coin_nodes, "ethereal:grove_dirt")
|
||||
table.insert(coin_nodes, "ethereal:jungle_dirt")
|
||||
table.insert(coin_nodes, "ethereal:mushroom_dirt")
|
||||
table.insert(coin_nodes, "ethereal:prairie_dirt")
|
||||
end
|
||||
|
||||
for n = 1, #coin_nodes do
|
||||
local item_def = minetest.registered_items[coin_nodes[n]]
|
||||
|
||||
if item_def ~= nil then
|
||||
local debug = ""
|
||||
local set_max_items = 1
|
||||
|
||||
-- getting drop-item-table
|
||||
local new_items = coin_drop_items(coin_nodes[n])
|
||||
|
||||
-- default drop = "string", build a table and include the default drop at last position
|
||||
if type(item_def.drop) == "string" or coin_nodes[n] == "ethereal:dry_dirt" then
|
||||
debug = " --> creating new table"
|
||||
local insert_drop = item_def.drop
|
||||
|
||||
-- special treatment for "ethereal:dry_dirt" because it has no drop value, so it gets one :)
|
||||
if coin_nodes[n] == "ethereal:dry_dirt" then insert_drop = coin_nodes[n] end
|
||||
|
||||
table.insert(new_items, { items = { insert_drop } })
|
||||
|
||||
elseif type(item_def.drop) == "table" then
|
||||
debug = " --> modifying existing table"
|
||||
set_max_items = item_def.drop.max_items
|
||||
for idx = 1, #item_def.drop.items do table.insert(new_items, item_def.drop.items[idx]) end
|
||||
end
|
||||
|
||||
local set_item_drop = {
|
||||
max_items = set_max_items,
|
||||
items = new_items
|
||||
}
|
||||
|
||||
minetest.log(coin_nodes[n] .. " has coins now!" .. debug)
|
||||
minetest.override_item(coin_nodes[n], { drop = set_item_drop })
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,2 +0,0 @@
|
|||
-- set 'false' to not give player startup coins
|
||||
villagers.startup_coins = true
|
|
@ -1,139 +0,0 @@
|
|||
-- ============================================ CONSTANTS =================================================
|
||||
-- ========================================================================================================
|
||||
|
||||
villagers.YAWS = { 0, -0.785, -1.571, -2.356, 3.141, 2.356, 1.571, 0.785}
|
||||
villagers.DIRECTIONS = { "N", "NE", "E", "SE", "S", "SW", "W", "NW"}
|
||||
villagers.DEGREES_TO_YAW = {
|
||||
[0] = 0, [45] = -0.785, [90] = -1.571, [135] = -2.356, [180] = 3.141,
|
||||
[225] = 2.356, [270] = 1.571, [315] = 0.785, [360] = 0
|
||||
}
|
||||
|
||||
-- used to help calculate the x and z positions of nodes that are adjacent to villager
|
||||
villagers.NODE_AREA = {
|
||||
["NW"]={-1,1}, ["N"]={0,1}, ["NE"]={1,1}, ["W"]={-1,0}, ["C"]={0,0},
|
||||
["E"]={1,0}, ["SW"]={-1,-1}, ["S"]={0,-1}, ["SE"]={1,-1}
|
||||
}
|
||||
|
||||
villagers.REGIONS = { "hot", "cold", "normal", "navtive", "desert" }
|
||||
|
||||
villagers.VILLAGES = {
|
||||
"tent", "charachoal", "claytrader", "lumberjack", "logcabin", "nore",
|
||||
"medieval", "gambit", "taoki", "cornernote", "sandcity",
|
||||
"chateau", "single", "tower"
|
||||
}
|
||||
|
||||
villagers.BUILDINGS = {
|
||||
"allmende", "bakery", "bench", "chateau", "church", "deco", "empty",
|
||||
"farm_full", "farm_tiny", "field", "forge", "fountain", "house", "hut",
|
||||
"library", "lumberjack", "mill", "pasture", "pit", "sawmill", "school",
|
||||
"secular", "shed", "shop", "spawn", "tavern", "tent", "tower", "trader",
|
||||
"village_square", "wagon", "well", "castle", "park", "cementry", "lamp",
|
||||
"hotel", "pub", "stable", "horsestable"
|
||||
}
|
||||
|
||||
villagers.SCHEMS = {
|
||||
tent = {
|
||||
"tent_tiny_1", "tent_tiny_2", "tent_big_1", "tent_big_2",
|
||||
"tent_medium_1", "tent_medium_2", "tent_medium_3",
|
||||
"tent_medium_4", "tent_open_1",
|
||||
"tent_open_2", "tent_open_3",
|
||||
"tent_open_big_1", "tent_open_big_2", "tent_open_big_3",
|
||||
"empty_1", "empty_2", "empty_3", "empty_4", "empty_5",
|
||||
"empty_16x32_2_90", "empty_32x32_2_90"
|
||||
},
|
||||
charachoal = {
|
||||
"charachoal_hut", "charachoal_hill",
|
||||
"empty_1", "empty_2", "empty_3", "empty_4", "empty_5",
|
||||
"empty_16x32_2_90", "empty_32x32_2_90"
|
||||
},
|
||||
claytrader = {
|
||||
"trader_clay_1", "trader_clay_2", "trader_clay_3",
|
||||
"trader_clay_4", "trader_clay_5", "clay_pit_1",
|
||||
"clay_pit_2", "clay_pit_3", "clay_pit_4", "clay_pit_5",
|
||||
"empty_1", "empty_2", "empty_3", "empty_4", "empty_5",
|
||||
"empty_16x32_2_90", "empty_32x32_2_90"
|
||||
},
|
||||
lumberjack = {
|
||||
"lumberjack_1", "lumberjack_2", "lumberjack_3", "lumberjack_4",
|
||||
"lumberjack_5", "lumberjack_6", "lumberjack_7", "lumberjack_8",
|
||||
"lumberjack_9", "lumberjack_10", "lumberjack_11", "lumberjack_12",
|
||||
"lumberjack_13", "lumberjack_14", "lumberjack_15", "lumberjack_16",
|
||||
"lumberjack_school", "lumberjack_stable", "lumberjack_pub_1",
|
||||
"lumberjack_church_1", "lumberjack_hotel_1", "lumberjack_shop_1",
|
||||
"lumberjack_sawmill_1",
|
||||
"empty_1", "empty_2", "empty_3", "empty_4", "empty_5",
|
||||
"empty_16x32_2_90", "empty_32x32_2_90"
|
||||
},
|
||||
logcabin = {
|
||||
"allmende_3_90", "logcabin1", "logcabin2", "logcabin3", "logcabin4",
|
||||
"logcabin5", "logcabin6", "logcabin7", "logcabin8", "logcabin9",
|
||||
"logcabin10", "logcabin11", "logcabinpub1", "logcabinpub2", "logcabinpub3",
|
||||
"empty_1", "empty_2", "empty_3", "empty_4", "empty_5",
|
||||
"empty_16x32_2_90", "empty_32x32_2_90"
|
||||
},
|
||||
nore = {
|
||||
"house_1_0", "wheat_field", "cotton_field", "lamp", "well",
|
||||
"fountain", "small_house_1_0", "house_with_garden_1_0", "church_1_0",
|
||||
"tower_1_0", "forge_1_0", "library_1_0", "inn_1_0", "pub_1_0",
|
||||
"empty_1", "empty_2", "empty_3", "empty_4", "empty_5",
|
||||
"empty_16x32_2_90", "empty_32x32_2_90"
|
||||
},
|
||||
medieval = {
|
||||
"church_1", "forge_1", "mill_1", "watermill_1", "hut_1", "hut_2",
|
||||
"farm_full_1", "farm_full_2", "farm_full_3", "farm_full_4", "farm_full_5",
|
||||
"farm_full_6", "farm_tiny_1", "farm_tiny_2", "farm_tiny_3", "farm_tiny_4",
|
||||
"farm_tiny_5", "farm_tiny_6", "farm_tiny_7", "taverne_1", "taverne_2",
|
||||
"taverne_3", "taverne_4", "well_1", "well_2", "well_3", "well_4",
|
||||
"well_5", "well_6", "well_7", "well_8", "allmende_3_90", "tree_place_1",
|
||||
"tree_place_2", "tree_place_3", "tree_place_4", "tree_place_5", "tree_place_6",
|
||||
"tree_place_7", "tree_place_8", "tree_place_9", "tree_place_10", "wagon_1",
|
||||
"wagon_2", "wagon_3", "wagon_4", "wagon_5", "wagon_6", "wagon_7", "wagon_8",
|
||||
"wagon_9", "wagon_10", "wagon_11", "wagon_12", "bench_1", "bench_2",
|
||||
"bench_3", "bench_4", "shed_1", "shed_2", "shed_3", "shed_4", "shed_5",
|
||||
"shed_6", "shed_7", "shed_8", "shed_9", "shed_10", "shed_11", "shed_12",
|
||||
"weide_1", "weide_2", "weide_3", "weide_4", "weide_5", "weide_6", "field_1",
|
||||
"field_2", "field_3", "field_4", "baking_house_1", "baking_house_2",
|
||||
"baking_house_3", "baking_house_4", "house_medieval_fancy_1_90",
|
||||
"cow_shed_1_270", "shed_with_forge_v2_1_0",
|
||||
"empty_1", "empty_2", "empty_3", "empty_4", "empty_5",
|
||||
"empty_16x32_2_90", "empty_32x32_2_90"
|
||||
},
|
||||
gambit = {
|
||||
"gambit_church_1_0_180", "gambit_cementry_0_180", "gambit_field_1_1_90",
|
||||
"gambit_forge_1_2_270", "gambit_fountain_1_1_90", "gambit_house_1_0_0",
|
||||
"gambit_lamp_0_270", "gambit_library_hotel_0_180", "gambit_pub_1_0_0",
|
||||
"gambit_shed_open_chests_2_0", "gambit_shop_0_90", "gambit_shop_large_0_0",
|
||||
"gambit_stable_1_2_90", "gambit_tower_1_0_270"
|
||||
},
|
||||
taoki = {
|
||||
"allmende_3_90", "default_town_farm", "default_town_house_large_1",
|
||||
"default_town_house_large_2", "default_town_house_medium",
|
||||
"default_town_house_small", "default_town_house_tiny_1",
|
||||
"default_town_house_tiny_2", "default_town_house_tiny_3", "default_town_park",
|
||||
"default_town_tower", "default_town_well", "default_town_fountain",
|
||||
"empty_1", "empty_2", "empty_3", "empty_4", "empty_5",
|
||||
"empty_16x32_2_90", "empty_32x32_2_90"
|
||||
},
|
||||
cornernote = {
|
||||
"towntest_ACDC_house_0_270", "towntest_cornernote_hut1_1_0", "towntest_cornernote_hut2_1_90",
|
||||
"towntest_cornernote_hut3_1_180", "towntest_cornernote_fortress_4_0",
|
||||
"towntest_cornernote_tower_1_90", "towntest_cornernote_turret_1_90",
|
||||
"towntest_irksomeduck_manor_0_90", "towntest_kddekadenz_barn1_1_90",
|
||||
"towntest_kddekadenz_barn2_1_90", "towntest_kddekadenz_castle_3_90",
|
||||
"towntest_kddekadenz_factory_1_90", "towntest_kddekadenz_gazebo_0_90",
|
||||
"towntest_kddekadenz_home1_1_90", "towntest_kddekadenz_home2_1_90",
|
||||
"towntest_kddekadenz_windmill_0_90", "towntest_Nanuk_chapel_1_180",
|
||||
"towntest_Nanuk_lavabeacon_0_90", "towntest_Nanuk_well_0_90",
|
||||
"towntest_VanessaE_home1_1_0", "towntest_VanessaE_home2_0_0"
|
||||
},
|
||||
sandcity = {
|
||||
"sandcity_tiny_1_1_270", "sandcity_tiny_2_1_270", "sandcity_tiny_3_1_270",
|
||||
"sandcity_tiny_4_1_270", "sandcity_small_1_1_270", "sandcity_small_2_1_270",
|
||||
"sandcity_small_3_1_270", "sandcity_small_4_1_270", "sandcity_small_5_1_270",
|
||||
"sandcity_meeting_small_1_1_270", "sandcity_ap_tower_1_1_270",
|
||||
"sandcity_ap_tower_2_1_270", "sandcity_ap_tower_3_1_270", "sandcity_ap_tower_4_1_270",
|
||||
"sandcity_ap_tower_5_1_270", "sandcity_ap_tower_6_1_270", "sandcity_ap_tower_7_1_270",
|
||||
"sandcity_ap_mixed_1_1_270", "sandcity_ap_mixed_2_1_270", "sandcity_ap_tiny_3_1_270",
|
||||
"sandcity_ap_tiny_3b_1_270", "sandcity_small_2_1_270", "sandcity_small_3_1_270"
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
mg_villages
|
||||
cottages?
|
||||
village_gambit?
|
||||
village_sandcity?
|
||||
village_towntest?
|
||||
farming?
|
|
@ -1,149 +0,0 @@
|
|||
-- ============================================ HELPER FUNCTIONS ==========================================
|
||||
-- ========================================================================================================
|
||||
|
||||
-- round a decimal number to any number of decimal points
|
||||
-- or round to nearest whole number if decimal_places is omitted
|
||||
function villagers.round(float_number, decimal_places)
|
||||
local multiplier
|
||||
if decimal_places then multiplier = 10^decimal_places
|
||||
else multiplier = 1 end
|
||||
return math.floor(float_number * multiplier + 0.5) / multiplier
|
||||
end
|
||||
|
||||
function villagers.copytable(source_table)
|
||||
local new_table = {}
|
||||
for i = 1, #source_table do
|
||||
table.insert(new_table, source_table[i])
|
||||
end
|
||||
return new_table
|
||||
end
|
||||
|
||||
-- returns the full registered nodename (ex. default:dirt) as well as
|
||||
-- an all-caps shortened version of the nodename (ex. DIRT) at given position
|
||||
function villagers.getNodeName(pos)
|
||||
local node_name = minetest.get_node({x=pos.x,y=pos.y,z=pos.z}).name
|
||||
local node_nickname
|
||||
local name_table = string.split(node_name, ":")
|
||||
if #name_table > 1 then
|
||||
node_nickname = name_table[2]
|
||||
else
|
||||
node_nickname = name_table[1]
|
||||
end
|
||||
return {node_name, string.upper(node_nickname)}
|
||||
end
|
||||
|
||||
-- get direction (N, NE, E, SE, S, SW, W, NW) from yaw
|
||||
-- yaw can be radians or degrees (0-360)
|
||||
function villagers.getDirectionFromYaw(yaw)
|
||||
local direction
|
||||
if yaw == 0 then direction = "N"
|
||||
elseif yaw == -0.785 or yaw == 45 then direction = "NE"
|
||||
elseif yaw == -1.571 or yaw == 90 then direction = "E"
|
||||
elseif yaw == -2.356 or yaw == 135 then direction = "SE"
|
||||
elseif yaw == 3.141 or yaw == 180 then direction = "S"
|
||||
elseif yaw == 2.356 or yaw == 225 then direction = "SW"
|
||||
elseif yaw == 1.571 or yaw == 270 then direction = "W"
|
||||
elseif yaw == 0.785 or yaw == 315 then direction = "NW"
|
||||
else
|
||||
print("\n## ERROR invalid yaw="..yaw)
|
||||
end
|
||||
|
||||
return direction
|
||||
end
|
||||
|
||||
function villagers.getFacingNodeInfo(self)
|
||||
local facing_dir = self.vFacingDirection
|
||||
local dug_pos = {
|
||||
x=self.vPos.x + villagers.NODE_AREA[facing_dir][1],
|
||||
y=self.vTargetHeight,
|
||||
z=self.vPos.z + villagers.NODE_AREA[facing_dir][2]
|
||||
}
|
||||
local node_names = villagers.getNodeName(dug_pos)
|
||||
return {dug_pos, node_names[1], node_names[2]}
|
||||
end
|
||||
|
||||
-- makes villager turn and face toward player's direction
|
||||
function villagers.turnToPlayer(self, player)
|
||||
local entityPos = self.object:getpos()
|
||||
local playerPos = player:getpos()
|
||||
local dx = entityPos.x - playerPos.x
|
||||
local dz = playerPos.z - entityPos.z
|
||||
self.object:set_yaw(math.atan2(dx, dz))
|
||||
end
|
||||
|
||||
function villagers.removeTextHud(self, player)
|
||||
|
||||
local hud_id_table = self.vHudIds[player:get_player_name()]
|
||||
if #hud_id_table > 0 then
|
||||
if villagers.log2 then io.write("Removing Text Hud IDs: ") end
|
||||
local hud_id_data
|
||||
hud_id_data = table.remove(hud_id_table)
|
||||
for i = 1, #hud_id_data do
|
||||
local hud_id = hud_id_data[i]
|
||||
player:hud_remove(hud_id)
|
||||
--io.write(hud_id.." ")
|
||||
end
|
||||
else
|
||||
if villagers.log2 then io.write("NoTextHudsShowing doNothing ") end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- return villager entity object when provided the entity 'vID'
|
||||
function villagers.getEntity(villager_id)
|
||||
for _,luaentity in pairs(minetest.luaentities) do
|
||||
if luaentity.vID == nil then
|
||||
print("\n## ERROR luaentity.vID = NIL ##")
|
||||
elseif luaentity.vID == villager_id then
|
||||
return luaentity
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function villagers.showAlert(self, player, alert_text, timer)
|
||||
|
||||
if self.vHudIds.alert_text then
|
||||
player:hud_remove(self.vHudIds.alert_text)
|
||||
self.vHudIds.alert_text = nil
|
||||
end
|
||||
|
||||
if self.vHudIds.alert_box then
|
||||
player:hud_remove(self.vHudIds.alert_box)
|
||||
self.vHudIds.alert_box = nil
|
||||
end
|
||||
|
||||
if timer == nil then timer = 3 end
|
||||
|
||||
-- show alert box
|
||||
local alert_box_id = player:hud_add({
|
||||
hud_elem_type = "image",
|
||||
scale = { x = 1, y = 1 },
|
||||
position = { x = 0.5, y = 0.0 },
|
||||
text = "alert_box.png",
|
||||
offset = {x=-30, y=50},
|
||||
})
|
||||
|
||||
--show text within alert box
|
||||
local alert_text_id = player:hud_add({
|
||||
hud_elem_type = "text",
|
||||
position = { x = 0.5, y = 0.0 },
|
||||
|
||||
text = alert_text,
|
||||
number = 0xFFFFFF,
|
||||
offset = {x=-30, y=50}
|
||||
})
|
||||
|
||||
if villagers.log2 then io.write("alertBox hudIDs saved ") end
|
||||
|
||||
table.insert(self.vHudIds, 1, {alert_box_id, alert_text_id})
|
||||
minetest.after(timer, function()
|
||||
io.write("\n## MINETEST.AFTER()!! deleting alert text IDs..")
|
||||
villagers.removeTextHud(self, player)
|
||||
end)
|
||||
|
||||
if villagers.log2 then
|
||||
io.write("newTextIds:"..dump(self.vHudIds.chats.timed[villager_id]).." ")
|
||||
end
|
||||
|
||||
|
||||
end
|
|
@ -1,20 +0,0 @@
|
|||
villagers = {}
|
||||
villagers.mods = {}
|
||||
|
||||
if minetest.get_modpath("cottages") then villagers.mods["cottages"] = true end
|
||||
if minetest.get_modpath("farming") then villagers.mods["farming"] = true end
|
||||
|
||||
local modpaths = minetest.get_modpath("villagers")
|
||||
dofile(modpaths.."/config.lua")
|
||||
dofile(modpaths.."/constants.lua")
|
||||
dofile(modpaths.."/items.lua")
|
||||
dofile(modpaths.."/coins.lua")
|
||||
dofile(modpaths.."/functions.lua")
|
||||
dofile(modpaths.."/attributes.lua")
|
||||
dofile(modpaths.."/chatting.lua")
|
||||
dofile(modpaths.."/actions.lua")
|
||||
dofile(modpaths.."/dialogue.lua")
|
||||
dofile(modpaths.."/trading.lua")
|
||||
dofile(modpaths.."/spawn.lua")
|
||||
dofile(modpaths.."/callbacks.lua")
|
||||
dofile(modpaths.."/chat_commands.lua")
|
|
@ -1,9 +0,0 @@
|
|||
minetest.register_craftitem("villagers:coins", {
|
||||
description = "Silver Coin",
|
||||
inventory_image = "coins.png",
|
||||
})
|
||||
|
||||
minetest.register_craftitem("villagers:coins_gold", {
|
||||
description = "Gold Coin",
|
||||
inventory_image = "coins_g.png",
|
||||
})
|
|
@ -1,5 +0,0 @@
|
|||
#Give startup-coins
|
||||
give_startup_coins (Give startup-coins) bool true
|
||||
|
||||
#Coin-drop in dirt
|
||||
coin_drop (Coin drop) bool true
|
|
@ -1,907 +0,0 @@
|
|||
function getRegionFromArea(pos, radius, errors)
|
||||
local log = false
|
||||
local log2 = false --show all nodes gathered
|
||||
|
||||
local RADIUS_MAX = 30
|
||||
local origin_pos = {x=pos.x, y=pos.y, z=pos.z}
|
||||
local origin_pos_str = minetest.pos_to_string(origin_pos)
|
||||
|
||||
-- debug output
|
||||
if log then
|
||||
io.write("\n ")
|
||||
io.write("origin_pos"..origin_pos_str.." ")
|
||||
io.write("radius="..radius.." ")
|
||||
end
|
||||
|
||||
if radius > RADIUS_MAX then
|
||||
if log then io.write("radiusTooLarge radiusIsNow="..RADIUS_MAX.." ") end
|
||||
radius = RADIUS_MAX
|
||||
end
|
||||
|
||||
if log then io.write("GettingNodes.. ")end
|
||||
|
||||
--gather stats of the surrounding nodes at this village position
|
||||
--to determine what region (cold, hot, normal, native, desert) this
|
||||
--village should be correspond to
|
||||
local nodenames = {}
|
||||
|
||||
-- seach each of the 8 directions N, NE, E, SE, S, SW, W, NW
|
||||
for dir_index = 1, 8 do
|
||||
local search_dir = villagers.DIRECTIONS[dir_index]
|
||||
|
||||
if log then io.write("\n "..search_dir..": ") end
|
||||
|
||||
-- gather name of every odd node staring at 1 node
|
||||
-- from the origin_pos to radius
|
||||
local radius_index = 1
|
||||
local loc = {x=0, y=origin_pos.y, z=0}
|
||||
local nodename
|
||||
while radius_index < radius do
|
||||
loc.x = origin_pos.x + (villagers.NODE_AREA[search_dir][1] * radius_index)
|
||||
loc.z = origin_pos.z + (villagers.NODE_AREA[search_dir][2] * radius_index)
|
||||
nodename = villagers.getNodeName(loc)[2]
|
||||
|
||||
if log then io.write(nodename) end
|
||||
if nodename == "ROAD" or
|
||||
nodename == "DIRT" or
|
||||
nodename == "FELDWEG" or
|
||||
nodename == "STONE" or
|
||||
nodename == "STONEBRICK" or
|
||||
nodename == "COBBLE" or
|
||||
nodename == "AIR" or
|
||||
nodename == "WATER_SOURCE" or
|
||||
nodename == "IGNORE" then
|
||||
if log then io.write("[skipped] ") end
|
||||
else
|
||||
if log then io.write(", ") end
|
||||
table.insert(nodenames, nodename)
|
||||
end
|
||||
|
||||
radius_index = radius_index + 2
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if log then
|
||||
io.write("\n ## nodesGathered("..#nodenames.."): ")
|
||||
for i = 1, #nodenames do
|
||||
io.write(nodenames[i].." ")
|
||||
end
|
||||
end
|
||||
|
||||
-- count re-occuring nodenames and sort from highest occurrance to lowest
|
||||
local rated_nodenames = {}
|
||||
local popped_name
|
||||
--rated_nodenames[popped_name] = 1 --record the first occurance
|
||||
|
||||
if log then io.write("\nratingNodes.. ") end
|
||||
while #nodenames >= 0 do
|
||||
popped_name = table.remove(nodenames)
|
||||
if log then
|
||||
io.write("\n nodenamesSize="..#nodenames.." ")
|
||||
io.write("popped="..popped_name.." ")
|
||||
end
|
||||
|
||||
local match_found = false
|
||||
for key,value in pairs(rated_nodenames) do
|
||||
if log then io.write("\n '"..key.."="..value.."' ") end
|
||||
if key == popped_name then
|
||||
match_found = true
|
||||
rated_nodenames[key] = rated_nodenames[key] + 1
|
||||
if log then io.write("[MATCH] set v="..rated_nodenames[key].." ") end
|
||||
break
|
||||
else
|
||||
if log then io.write("[noMatch] loopAgain.. ") end
|
||||
end
|
||||
end
|
||||
if not match_found then
|
||||
if log then io.write("endOf 'rated_nodenames' withNoMatch savedAsNew poppingNextNode.. ") end
|
||||
rated_nodenames[popped_name] = 1
|
||||
end
|
||||
if #nodenames == 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if log2 then
|
||||
io.write("\n\n## rated_nodenames: ")
|
||||
for k,v in pairs(rated_nodenames) do
|
||||
io.write(k.."="..v.." ")
|
||||
end
|
||||
io.write("\n")
|
||||
end
|
||||
|
||||
-- identify the nodename that occurred the most
|
||||
if log then io.write("\n getHighestRatedNode.. ") end
|
||||
local top_count = 0
|
||||
local top_nodename
|
||||
for key,value in pairs(rated_nodenames) do
|
||||
if log then
|
||||
io.write("\n '"..key.."="..value.."' ")
|
||||
io.write("top_count="..top_count.." ")
|
||||
end
|
||||
if value > top_count then
|
||||
top_count = value
|
||||
top_nodename = key
|
||||
if log then io.write("higherValue! setTopCount="..top_count.." setTopNode="..top_nodename.." ") end
|
||||
else
|
||||
if log then io.write("noChange ") end
|
||||
end
|
||||
end
|
||||
if log then io.write("\n ## top_nodename = "..top_nodename.." count="..top_count.." ") end
|
||||
|
||||
local region_type
|
||||
--tent, claytrader, lumberjack, log cabin, nore, medieval, taoki, cornernote
|
||||
-- assign region type based on the top node found
|
||||
-- region types: cold, hot, normal, native, desert
|
||||
if top_nodename == "DIRT_WITH_SNOW" then
|
||||
region_type = "cold"
|
||||
elseif top_nodename == "SNOWBLOCK" then
|
||||
region_type = "cold"
|
||||
elseif top_nodename == "ICE" then
|
||||
region_type = "cold"
|
||||
elseif top_nodename == "DIRT_WITH_DRY_GRASS" then
|
||||
region_type = "hot"
|
||||
elseif top_nodename == "DIRT_WITH_GRASS" then
|
||||
region_type = "normal"
|
||||
elseif top_nodename == "DIRT_WITH_RAINFOREST_LITTER" then
|
||||
region_type = "native"
|
||||
elseif top_nodename == "SOIL_WET" then
|
||||
region_type = "native"
|
||||
elseif top_nodename == "SAND" or
|
||||
top_nodename == "DESERT_SAND" or
|
||||
top_nodename == "SILVER_SAND" or
|
||||
top_nodename == "DESERT_STONE" or
|
||||
top_nodename == "SANDSTONE" then
|
||||
if math.random(2) == 1 then
|
||||
region_type = "hot"
|
||||
else
|
||||
region_type = "desert"
|
||||
end
|
||||
else
|
||||
io.write(" #ERROR IN getRegionFromArea()# ")
|
||||
local error_message = "Unknown value for top_nodename = "..top_nodename.." "..
|
||||
"Falling back to region=NORMAL."
|
||||
table.insert(errors, error_message)
|
||||
region_type = "normal"
|
||||
end
|
||||
return region_type
|
||||
end
|
||||
|
||||
|
||||
|
||||
minetest.register_lbm({
|
||||
label = "Spawn Villager",
|
||||
name = "villagers:spawn_villager",
|
||||
nodenames = {"mg_villages:mob_spawner"},
|
||||
run_at_every_load = true,
|
||||
action = function(pos, node)
|
||||
local log = false
|
||||
|
||||
if log then io.write("\n## LBM ## ") end
|
||||
|
||||
local spawn_meta = minetest.get_meta(pos)
|
||||
local meta_table = spawn_meta:to_table()
|
||||
local plot_num = tonumber(meta_table.fields.plot_nr)
|
||||
local bed_num = tonumber(meta_table.fields.bed_nr)
|
||||
|
||||
if spawn_meta:get_int("success") == 1 then
|
||||
if log then io.write("** ALREADY SPAWNED ** Plot #"..plot_num.." Bed #"..bed_num) end
|
||||
return
|
||||
end
|
||||
|
||||
-- track any errors. for use by formspec in
|
||||
-- node metadata located at the village position
|
||||
local errors = {}
|
||||
|
||||
local village_id = meta_table.fields.village_id
|
||||
local village_data = mg_villages.all_villages[village_id]
|
||||
if village_data == nil then
|
||||
return
|
||||
end
|
||||
local village_pos = {x=village_data.vx, y=village_data.vh, z=village_data.vz}
|
||||
local village_pos_str = minetest.pos_to_string(village_pos)
|
||||
local village_radius = village_data.vs
|
||||
local village_type = village_data.village_type
|
||||
local village_snow = village_data.artificial_snow
|
||||
if village_snow == nil then village_snow = "NIL" end
|
||||
|
||||
if log then io.write(string.upper(village_type)..village_pos_str.." ") end
|
||||
|
||||
-- Get region type data if it was already calculated
|
||||
-- and saved from a previous LBM call. Region data is
|
||||
-- saved in node metadata at village position.
|
||||
local village_meta = minetest.get_meta(village_pos)
|
||||
local region = village_meta:get_string("region")
|
||||
local note = ""
|
||||
if region == "" then
|
||||
note = "*"
|
||||
-- certain village types directly determine the region type
|
||||
-- without needing to examine surrounding node data
|
||||
if village_type == "sandcity" then region = "desert"
|
||||
elseif village_type == "claytrader" then region = "desert"
|
||||
elseif village_type == "charachoal" then region = "native"
|
||||
elseif village_type == "gambit" then region = "desert"
|
||||
elseif village_snow == 1 then region = "cold"
|
||||
|
||||
-- remaining village types will have region type
|
||||
-- based upon the surrounding types of nodes
|
||||
else
|
||||
region = getRegionFromArea(village_pos, village_radius, errors)
|
||||
note = "**"
|
||||
end
|
||||
village_meta:set_string("region", region)
|
||||
end
|
||||
|
||||
if log then io.write("size="..village_radius.." snow="..village_snow.." region="..region..note.." ") end
|
||||
|
||||
--[[ currently unused
|
||||
local village_num = village_data.nr
|
||||
local village_height = village_data.optimal_height
|
||||
local village_is_single = village_data.is_single_house
|
||||
local village_name = village_data.name
|
||||
--]]
|
||||
|
||||
local plot_data = village_data.to_add_data.bpos[plot_num]
|
||||
local plot_pos = {x = plot_data.x, y = plot_data.y, z = plot_data.z}
|
||||
local plot_pos_str = minetest.pos_to_string(plot_pos)
|
||||
local sizex = plot_data.bsizex
|
||||
local sizez = plot_data.bsizez
|
||||
local rotate = plot_data.brotate
|
||||
local side = plot_data.side
|
||||
if side == nil then side = "NIL" end
|
||||
|
||||
if log then io.write("Plot #"..plot_num.." "..plot_pos_str.." ") end
|
||||
|
||||
local b_type_num = plot_data.btype
|
||||
local building_data = mg_villages.BUILDINGS[b_type_num]
|
||||
local b_type = building_data.typ
|
||||
local b_schem = building_data.scm
|
||||
local b_sizex = building_data.sizex
|
||||
local b_sizez = building_data.sizez
|
||||
local b_sizey = building_data.ysize
|
||||
local b_inhab = building_data.inh
|
||||
local b_bedcount = building_data.bed_count
|
||||
local b_bedlist = building_data.bed_list -- table
|
||||
local b_nodenames = building_data.nodenames -- table
|
||||
|
||||
if b_inhab == nil then b_inhab = "NIL" end
|
||||
if b_sizex == nil then b_sizex = "NIL" end
|
||||
if b_sizey == nil then b_sizey = "NIL" end
|
||||
if b_sizez == nil then b_sizez = "NIL" end
|
||||
if type(b_schem) == "table" then b_schem = "n/a" end
|
||||
if b_type == nil then b_type = "NIL" end
|
||||
if b_bedcount == nil then b_bedcount = "NIL" end
|
||||
|
||||
local beds_data_count = 0
|
||||
for k,v in pairs(plot_data.beds) do
|
||||
beds_data_count = beds_data_count + 1
|
||||
end
|
||||
|
||||
if log then
|
||||
io.write(string.upper(b_type).."["..b_schem.."] inh="..b_inhab.." ")
|
||||
io.write("bedcount="..b_bedcount.." beds_data_count="..beds_data_count.." ")
|
||||
end
|
||||
--if log then io.write("plot_data.beds["..bed_num.."]: "..minetest.serialize(plot_data.beds).." ") end
|
||||
if bed_num > beds_data_count then
|
||||
if log then io.write(" #ERROR IN bed_num vs beds_data_count# ") end
|
||||
local error_message = "to_add_data.bpos(meta.fields.plot_nr="..plot_num..")(meta.fields.bed_nr="..bed_num..") NIL. "..
|
||||
"BUILDINGS(btype).bed_count="..b_bedcount.." and inh="..b_inhab..". Wait next cycle.."
|
||||
table.insert(errors, error_message)
|
||||
return
|
||||
end
|
||||
|
||||
local beds_data = plot_data.beds[bed_num]
|
||||
local bed_pos = {x=beds_data.x, y=beds_data.y, z=beds_data.z}
|
||||
local bed_pos_str = minetest.pos_to_string(bed_pos)
|
||||
|
||||
local gen = beds_data.generation
|
||||
local age = beds_data.age
|
||||
local gender = beds_data.gender
|
||||
if gender == "m" then gender = "male" else gender = "female" end
|
||||
|
||||
--get NAME and save to 'vName' object custom field
|
||||
local villager_name
|
||||
if region == "native" then villager_name = villagers.getVillagerName(gender, region)
|
||||
elseif region == "desert" then villager_name = villagers.getVillagerName(gender, region)
|
||||
else
|
||||
if age == "young" then villager_name = villagers.getVillagerName(gender, age)
|
||||
else villager_name = villagers.getVillagerName(gender) end
|
||||
end
|
||||
|
||||
local title = beds_data.title
|
||||
local works_at = beds_data.works_at
|
||||
local unique = beds_data.uniq
|
||||
local owns_data = beds_data.owns
|
||||
|
||||
if log then
|
||||
pos.y = village_data.vh + 1.5
|
||||
io.write("\nVillager: "..string.upper(villager_name).." "..minetest.pos_to_string(pos).." ")
|
||||
io.write("gender="..gender.." age="..age.." gen="..gen.." bed#"..bed_num.." "..bed_pos_str.." ")
|
||||
if title then io.write("*"..string.upper(title).."* ") end
|
||||
if works_at then io.write("@ plot #"..works_at.." ") end
|
||||
if unique then io.write("unique="..unique.." ") end
|
||||
if owns_data then
|
||||
io.write("OWNS PLOT(S) "..minetest.serialize(owns_data).." ")
|
||||
end
|
||||
end
|
||||
|
||||
local worker_data = village_data.to_add_data.bpos[plot_num].worker
|
||||
|
||||
if worker_data then
|
||||
--local lives_at = worker_data.lives_at
|
||||
local work_as = worker_data.work_as
|
||||
local titl = worker_data.title
|
||||
local uniq = worker_data.uniq
|
||||
if log then
|
||||
if work_as then io.write("Work Info: job="..work_as.." ") end
|
||||
if titl then io.write("titl="..titl.." ") end
|
||||
if uniq then io.write("uniq="..uniq.." ") end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------
|
||||
-- SPAWN THE ACTUAL VILLAGER! --
|
||||
-------------------------------
|
||||
|
||||
-- 'pos' is currently location of mg_Villages mob spawner.
|
||||
-- Ensure villager spawns one node above this position.
|
||||
pos.y = village_data.vh + 1.25
|
||||
local objectRef = minetest.add_entity(pos, "villagers:villager")
|
||||
local self = objectRef:get_luaentity()
|
||||
|
||||
if log then io.write("\n ** SPAWNING VILLAGER ** ") end
|
||||
|
||||
self.vName = villager_name
|
||||
self.vGender = gender
|
||||
if log then io.write(self.vGender.." ") end
|
||||
|
||||
if gen == 1 then age = "young"
|
||||
elseif gen == 2 then age = "adult"
|
||||
else age = "old" end
|
||||
self.vAge = age
|
||||
if log then io.write(self.vAge.." ") end
|
||||
|
||||
-- assigned trading goods to villagers who
|
||||
-- possess the appropriate title
|
||||
if title == nil then
|
||||
-- these are home/hut dwellers
|
||||
self.vSell = "none"
|
||||
elseif villagers.GOODS[title] == nil then
|
||||
self.vSell = "none"
|
||||
table.insert(errors, "villagers.GOODS: Unknown job title '"..title.."' for plot#"..plot_num.." bed#"..bed_num.." villager.")
|
||||
else
|
||||
-- getTradeInventory() can also return "none" for job titles
|
||||
-- that fail probability check of trading items
|
||||
self.vSell = villagers.getTradeInventory(title, region, plot_num, bed_num, errors)
|
||||
end
|
||||
self.vTitle = title
|
||||
|
||||
--get TEXTURE, VISUAL SIZE, and COLLISION BOX and apply it to corresponding entity properties
|
||||
local newTexture, newSize, newCollisionBox = villagers.getVillagerAppearance(b_type, region, gender, age)
|
||||
objectRef:set_properties({textures={newTexture}})
|
||||
objectRef:set_properties({visual_size={x=newSize,y=newSize}})
|
||||
objectRef:set_properties({collisionbox=newCollisionBox})
|
||||
|
||||
local final_pos = objectRef:getpos()
|
||||
self.vSpawnHeight = final_pos.y
|
||||
self.vTargetHeight = self.vSpawnHeight - 0.5
|
||||
|
||||
-- set final position that takes into account calc'd
|
||||
-- collision_box that was based on dynamic visual_size
|
||||
final_pos.y = final_pos.y
|
||||
objectRef:setpos(final_pos)
|
||||
self.vPos = {x=final_pos.x,y=final_pos.y,z=final_pos.z}
|
||||
self.vOriginPos = {x=final_pos.x,y=final_pos.y,z=final_pos.z}
|
||||
self.vTargetPos = {x=final_pos.x, y=self.vTargetHeight, z=final_pos.z}
|
||||
|
||||
-- randomly set how often in seconds that this
|
||||
-- villager will check to do a different action
|
||||
self.vActionFrequency = math.random(4,6)
|
||||
|
||||
--overlay applied entity new properties to custom fields
|
||||
local prop = objectRef:get_properties()
|
||||
self.vTexture = prop.textures[1]
|
||||
self.vSize = prop.visual_size
|
||||
self.vBox = prop.collisionbox
|
||||
|
||||
self.vRegion = region
|
||||
self.vVillage = village_type
|
||||
self.vPlot = plot_num
|
||||
self.vType = b_type
|
||||
self.vSchem = b_schem
|
||||
self.vBed = bed_num
|
||||
|
||||
-- use for villager trading formspec callbacks and for chat commands
|
||||
self.vID = final_pos.x.."_"..final_pos.y.."_"..final_pos.z
|
||||
|
||||
-- copy chat dialogue scripts from villagers.chat global var
|
||||
-- and save it into villager entity for more quicker access
|
||||
local hi_script, bye_script, main_script
|
||||
if villagers.chat[b_type] == nil then
|
||||
if log then io.write(" #ERROR IN LOADING Chat Scripts# ") end
|
||||
local error_message = "villagers.chat('"..b_type.."') = NIL for "..
|
||||
"plot#"..plot_num.." bed#"..bed_num.." villager. Fallback to default scripts."
|
||||
table.insert(errors, error_message)
|
||||
hi_script = { "I am error.", "My chat failed", "Potato."}
|
||||
bye_script = { "Blah.", "Kill me.", "Chimichangas." }
|
||||
main_script = { "My building type is unknown:\nBUILDING_TYPE",
|
||||
"I have nothing meaningful to\nbecause of a chat setup error.",
|
||||
"My chat dialogue setup failed.\nRight click at "..village_pos_str.."\nfor more details."
|
||||
}
|
||||
else
|
||||
hi_script = villagers.copytable(villagers.chat[b_type].greetings)
|
||||
bye_script = villagers.copytable(villagers.chat[b_type].goodbyes)
|
||||
main_script = villagers.copytable(villagers.chat[b_type].mainchat)
|
||||
end
|
||||
villagers.getRandomChatText(self, hi_script, "vScriptHi", 3)
|
||||
villagers.getRandomChatText(self, bye_script, "vScriptBye", 3)
|
||||
villagers.getRandomChatText(self, main_script, "vScriptMain", 3)
|
||||
villagers.getRandomChatText(self, villagers.copytable(villagers.chat.gtg), "vScriptGtg", 3)
|
||||
villagers.getRandomChatText(self, villagers.copytable(villagers.chat.smalltalk), "vScriptSmalltalk", 3)
|
||||
villagers.getRandomChatText(self, villagers.copytable(villagers.chat.gamefacts), "vScriptGameFacts", 2)
|
||||
|
||||
-- set door position. currently same a spawn position, but will update soon
|
||||
-- to action door position obtained from mg_villages parameters
|
||||
self.vDoorPos = {x=final_pos.x, y=final_pos.y, z=final_pos.z}
|
||||
|
||||
-- setup infotext
|
||||
local infotext_string = ""
|
||||
|
||||
if self.vTitle == "guest" then
|
||||
infotext_string = infotext_string..string.upper(self.vName).." (guest)\n"
|
||||
elseif self.vTitle == nil then
|
||||
infotext_string = infotext_string..string.upper(self.vName).."\n"
|
||||
else
|
||||
infotext_string = infotext_string.."TRADER ("..self.vTitle..")\n"..self.vName.." "
|
||||
end
|
||||
infotext_string = infotext_string..self.vAge.." "..self.vGender.."\n"..
|
||||
"Person #"..self.vBed.." from Plot #"..self.vPlot
|
||||
|
||||
|
||||
-- save infotext to villager object
|
||||
objectRef:set_properties({infotext = infotext_string })
|
||||
|
||||
-- set YAW
|
||||
local yaw = villagers.YAWS[math.random(8)]
|
||||
objectRef:set_yaw(yaw)
|
||||
self.vYaw = yaw
|
||||
self.vFacingDirection = villagers.getDirectionFromYaw(yaw)
|
||||
|
||||
-- default to standing animation
|
||||
self.object:set_animation(
|
||||
{x=self.animation["stand_start"], y=self.animation["stand_end"]},
|
||||
self.animation_speed + math.random(10)
|
||||
)
|
||||
|
||||
-- save texture string for debug output
|
||||
self.vTextureString = newTexture
|
||||
|
||||
|
||||
|
||||
-- save any found errors into the node metadata field 'errors'
|
||||
if #errors > 0 then
|
||||
if log then io.write("\n "..#errors.." ERRORS DETECTED: "..minetest.serialize(errors).." ") end
|
||||
local prev_errors = village_meta:get_string("errors")
|
||||
if prev_errors == "" then
|
||||
if log then io.write("\n noPrevErrors insertingNewErrors ") end
|
||||
village_meta:set_string("errors", minetest.serialize(errors))
|
||||
else
|
||||
if log then io.write("\n prevErros: "..prev_errors.." ") end
|
||||
local all_errors = {}
|
||||
for i = 1, #errors do table.insert(all_errors, errors[i]) end
|
||||
|
||||
local prev_err_table = minetest.deserialize(prev_errors)
|
||||
for i = 1, #prev_err_table do table.insert(all_errors, prev_err_table[i]) end
|
||||
|
||||
local all_err_str = minetest.serialize(all_errors)
|
||||
if log then io.write("\n combinedErrors: "..all_err_str.." ") end
|
||||
village_meta:set_string("errors", all_err_str)
|
||||
end
|
||||
if log then io.write("\n meta_errors: "..village_meta:get_string("errors").." ") end
|
||||
|
||||
else
|
||||
if log then io.write("\n No Errors detected.\n") end
|
||||
end
|
||||
|
||||
|
||||
-- save any changes (error messages) to this 'village formspec'
|
||||
--io.write("\n Compiling village formspec... ")
|
||||
local village_formspec = "size[12,7]background[0,0;0,0;gui_formbg.png;true]label[0,0;"..
|
||||
"type: "..string.upper(village_type).." "..
|
||||
village_pos_str.."\n"..
|
||||
"name: "..string.upper(village_data.name).."\n"..
|
||||
"region: "..string.upper(region).."]"
|
||||
|
||||
local saved_errors = village_meta:get_string("errors")
|
||||
if saved_errors == "" then
|
||||
--io.write("noErrorsFound ")
|
||||
village_formspec = village_formspec.."label[0,1.5;(No Errors)]"
|
||||
else
|
||||
--io.write("\n errorsFound: "..saved_errors.." ")
|
||||
local error_string = ""
|
||||
local saved_err_table = minetest.deserialize(saved_errors)
|
||||
for i = 1, #saved_err_table do
|
||||
error_string = error_string.."- "..saved_err_table[i].."\n"
|
||||
end
|
||||
village_formspec = village_formspec.."label[0,1.5;"..error_string.."]"
|
||||
end
|
||||
village_meta:set_string("formspec", village_formspec)
|
||||
--io.write("\n ##Village Formspec: "..village_formspec.." ")
|
||||
|
||||
-- set infotext for village formspec
|
||||
village_meta:set_string("infotext", "Village Information\n(right click to view)")
|
||||
|
||||
-- indicate that this mob spawner location has now successfully
|
||||
-- spawned a village. processing of this mob spawner will
|
||||
-- be skipped the next time the LBM at this pos executes
|
||||
spawn_meta:set_int("success", 1)
|
||||
|
||||
--io.write("\n")
|
||||
end
|
||||
})
|
||||
|
||||
-------------------------
|
||||
-- ENTITY REGISTRATION --
|
||||
-------------------------
|
||||
minetest.register_entity("villagers:villager", {
|
||||
|
||||
-- Utilized Object Proerties
|
||||
hp_max = 15,
|
||||
collisionbox = {-0.25,-1.00,-0.25, 0.25,0.75,0.25},
|
||||
physical = true,
|
||||
weight = 5,
|
||||
visual = "mesh",
|
||||
visual_size = {x=1.0, y=1.0},
|
||||
mesh = "character.b3d",
|
||||
textures = {"character.png"},
|
||||
animation = {
|
||||
stand_start = 0, stand_end = 79,
|
||||
walk_start = 168, walk_end = 187,
|
||||
dig_start = 189, dig_end = 198,
|
||||
walkdig_start = 200, walkdig_end = 219
|
||||
},
|
||||
animation_speed = 25,
|
||||
infotext = "[uninitialized villager]",
|
||||
|
||||
-- custom fields
|
||||
vName = "no-name",
|
||||
vGender = "no-gender",
|
||||
vAge = "adult",
|
||||
vTexture = "character.png",
|
||||
vSize = 1,
|
||||
vBox = {-0.25,-1.00,-0.25, 0.25,0.75,0.25},
|
||||
vYaw = 0,
|
||||
vYawSaved = 0,
|
||||
vTimer = 0,
|
||||
vAction = "STAND",
|
||||
vDigging = nil,
|
||||
vActionFrequency = 1, --rate in seconds villager updates action
|
||||
vInitialChatDistance = 0,
|
||||
vHudIds = {},
|
||||
vSoundHandle = nil,
|
||||
vDespawned = nil,
|
||||
|
||||
-- homeplace
|
||||
vRegion = "UNASSIGNED",
|
||||
vVillage = "UNASSIGNED",
|
||||
vPlot = nil,
|
||||
vType = "UNASSIGNED",
|
||||
vSchem = "UNASSIGNED",
|
||||
vBed = nil,
|
||||
vUnknown = {},
|
||||
|
||||
-- pathfinding
|
||||
vPos = {x=0,y=0,z=0},
|
||||
vFacingDirection = "N",
|
||||
vOriginPos = {x=0,y=0,z=0},
|
||||
vOriginDistance = 0,
|
||||
vOriginDistMax = 10,
|
||||
vTotalDistance = 0,
|
||||
vTargetPos = {x=0,y=0,z=0},
|
||||
vSpawnHeight = 0,
|
||||
vTargetHeight = 0,
|
||||
vTurnPreference = "right",
|
||||
vWalkReady = false,
|
||||
vDigReady = false,
|
||||
vBedPos = nil,
|
||||
vDoorPos = nil,
|
||||
vJobPos = nil,
|
||||
|
||||
-- chatting
|
||||
vChatting = nil,
|
||||
vChatReady = true,
|
||||
vScriptHi = nil,
|
||||
vScriptHiSaved = nil,
|
||||
vScriptBye = nil,
|
||||
vScriptByeSaved = nil,
|
||||
vScriptGtg = nil,
|
||||
vScriptGtgSaved = nil,
|
||||
vScriptMain = nil,
|
||||
vScriptMainSaved = nil,
|
||||
vScriptSmalltalk = nil,
|
||||
vScriptSmalltalkSaved = nil,
|
||||
vScriptSmalltalk = nil,
|
||||
vScriptSmalltalkSaved = nil,
|
||||
vScriptGameFacts = nil,
|
||||
vScriptGameFactsSaved = nil,
|
||||
|
||||
-- trading
|
||||
vID = "UNASSIGNED",
|
||||
vIsTrader = false,
|
||||
vTrading = nil,
|
||||
vNodeMetaPos = {x=0,y=0,z=0},
|
||||
vBuy = {},
|
||||
vSell = {},
|
||||
vTitle = nil,
|
||||
vTraded = false,
|
||||
|
||||
-- debugging
|
||||
vTextureString = nil,
|
||||
|
||||
on_activate = function(self, staticdata, dtime_s)
|
||||
local log = false
|
||||
if log then io.write("\nACTIVATE ") end
|
||||
|
||||
-- perform default action, whichi is standing idle animation
|
||||
villagers.standVillager(self)
|
||||
|
||||
if staticdata ~= "" then
|
||||
if log then io.write("(existing) ") end
|
||||
local data = minetest.deserialize(staticdata)
|
||||
self.object:set_properties({textures={data.vTexture}})
|
||||
self.object:set_properties({visual_size=data.vSize})
|
||||
self.object:set_properties({collisionbox=data.vBox})
|
||||
self.object:set_properties({infotext=data.vInfo})
|
||||
self.object:set_properties({hp_max=data.vHP})
|
||||
self.object:setpos(data.vPos)
|
||||
self.object:set_yaw(data.vYaw)
|
||||
|
||||
self.vName = data.vName
|
||||
self.vAge = data.vAge
|
||||
self.vTexture = data.vTexture
|
||||
self.vSize = data.vSize
|
||||
self.vBox = data.vBox
|
||||
self.vYaw = data.vYaw
|
||||
self.vYawSaved = data.vYawSaved
|
||||
self.vGender = data.vGender
|
||||
self.vAction = data.vAction
|
||||
self.vDigging = data.vDigging
|
||||
self.vActionFrequency = data.vActionFrequency
|
||||
self.vInitialChatDistance = 0
|
||||
self.vHudIds = data.vHudIds
|
||||
self.vSoundHandle = data.vSoundHandle
|
||||
self.vDespawned = nil
|
||||
|
||||
-- homeplace
|
||||
self.vRegion = data.vRegion
|
||||
self.vVillage = data.vVillage
|
||||
self.vPlot = data.vPlot
|
||||
self.vType = data.vType
|
||||
self.vSchem = data.vSchem
|
||||
self.vBed = data.vBed
|
||||
self.vUnknown = data.vUnknown
|
||||
|
||||
-- pathfinding
|
||||
self.vPos = data.vPos
|
||||
self.vFacingDirection = data.vFacingDirection
|
||||
self.vOriginPos = data.vOriginPos
|
||||
self.vOriginDistance = data.vOriginDistance
|
||||
self.vOriginDistMax = data.vOriginDistMax
|
||||
self.vTotalDistance = data.vTotalDistance
|
||||
self.vTargetPos = data.vTargetPos
|
||||
self.vSpawnHeight = data.vSpawnHeight
|
||||
self.vTargetHeight = data.vTargetHeight
|
||||
self.vTurnPreference = data.vTurnPreference
|
||||
self.vWalkReady = false
|
||||
self.vDigReady = false
|
||||
self.vBedPos = data.vBedPos
|
||||
self.vDoorPos = data.vDoorPos
|
||||
self.vJobPos = data.vJobPos
|
||||
|
||||
-- chatting
|
||||
self.vChatting = nil
|
||||
self.vChatReady = true
|
||||
self.vScriptHi = data.vScriptHi
|
||||
self.vScriptHiSaved = data.vScriptHiSaved
|
||||
self.vScriptBye = data.vScriptBye
|
||||
self.vScriptByeSaved = data.vScriptByeSaved
|
||||
self.vScriptGtg = data.vScriptGtg
|
||||
self.vScriptGtgSaved = data.vScriptGtgSaved
|
||||
self.vScriptMain = data.vScriptMain
|
||||
self.vScriptMainSaved = data.vScriptMainSaved
|
||||
self.vScriptSmalltalk = data.vScriptSmalltalk
|
||||
self.vScriptSmalltalkSaved = data.vScriptSmalltalkSaved
|
||||
self.vScriptGameFacts = data.vScriptGameFacts
|
||||
self.vScriptGameFactsSaved = data.vScriptGameFactsSaved
|
||||
|
||||
-- trading
|
||||
self.vID = data.vID
|
||||
self.vIsTrader = data.vIsTrader
|
||||
self.vTrading = nil
|
||||
self.vNodeMetaPos = data.vNodeMetaPos
|
||||
self.vBuy = data.vBuy
|
||||
self.vSell = data.vSell
|
||||
self.vTitle = data.vTitle
|
||||
self.vTraded = false
|
||||
|
||||
-- debugging
|
||||
self.vTextureString = data.vTextureString
|
||||
|
||||
if log then io.write(string.upper(self.vName).." ") end
|
||||
|
||||
if self.vDespawned then
|
||||
if log then io.write("vDespawned="..tostring(self.vDespawned).." ") end
|
||||
else
|
||||
if log then io.write("vDespawned=NIL ") end
|
||||
end
|
||||
|
||||
local prior_saved_action = self.vAction
|
||||
if prior_saved_action == "STAND" then
|
||||
if log then io.write("loaded="..prior_saved_action.." ") end
|
||||
elseif prior_saved_action == "TURN" then
|
||||
if log then io.write("loaded="..prior_saved_action.." ") end
|
||||
elseif prior_saved_action == "DIG" then
|
||||
if log then io.write("loaded="..prior_saved_action.." ") end
|
||||
if log then io.write("set_vAction=RESUMEDIG ") end
|
||||
self.vAction = "RESUMEDIG"
|
||||
elseif prior_saved_action == "REPLACE" then
|
||||
if log then io.write("loaded="..prior_saved_action.." ") end
|
||||
if log then io.write("set_vAction=RESUMEDIG ") end
|
||||
self.vAction = "RESUMEDIG"
|
||||
elseif prior_saved_action == "WALK" then
|
||||
if log then io.write("loaded="..prior_saved_action.." ") end
|
||||
elseif prior_saved_action == "WALKING" then
|
||||
if log then io.write("loaded="..prior_saved_action.." ") end
|
||||
if log then io.write("set_vAction=WALK ") end
|
||||
self.vAction = "WALK"
|
||||
self.object:setvelocity({x=0,y=0,z=0})
|
||||
elseif prior_saved_action == "TURNBACK" then
|
||||
if log then io.write("loaded="..prior_saved_action.." ") end
|
||||
elseif prior_saved_action == "WALKBACK" then
|
||||
if log then io.write("loaded="..prior_saved_action.." ") end
|
||||
elseif prior_saved_action == "CHAT" or prior_saved_action == "ENDCHAT" then
|
||||
if log then io.write("loaded="..prior_saved_action.." ") end
|
||||
if log then io.write("set_vAction=STAND ") end
|
||||
self.vAction = "STAND"
|
||||
elseif prior_saved_action == "TRADE" or prior_saved_action == "ENDTRADE" then
|
||||
if log then io.write("loaded="..prior_saved_action.." ") end
|
||||
if log then io.write("set_vAction=STAND ") end
|
||||
self.vAction = "STAND"
|
||||
else
|
||||
if log then io.write("ERROR vAction="..prior_saved_action.." ") end
|
||||
end
|
||||
|
||||
else
|
||||
if log then io.write("(new) "..string.upper(self.vName).." ") end
|
||||
end
|
||||
|
||||
if log then io.write("onActivateEND. ") end
|
||||
|
||||
end,
|
||||
|
||||
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
|
||||
villagers.on_leftclick(self, puncher, time_from_last_punch)
|
||||
end,
|
||||
|
||||
on_rightclick = function(self, clicker) villagers.on_rightclick(self, clicker) end,
|
||||
|
||||
on_step = function(self, dtime) villagers.on_step(self, dtime) end,
|
||||
|
||||
get_staticdata = function(self)
|
||||
local log = false
|
||||
if log then
|
||||
io.write("\ngetStatic() ")
|
||||
end
|
||||
|
||||
if self.vDespawned == false then
|
||||
if log then
|
||||
io.write("DESPAWNED plot #"..self.vPlot.." person #"..self.vBed.." "..self.vName.." "..string.upper(self.vVillage).." ")
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
self.vDespawned = false
|
||||
if log then io.write(" SPAWNED ") end
|
||||
|
||||
-- villager is spawning for first time and not
|
||||
-- completely initialized yet
|
||||
if self.vID == "UNASSIGNED" then
|
||||
|
||||
-- this is an existing villager re-spawning
|
||||
else
|
||||
|
||||
end
|
||||
|
||||
-- show standing animation while waiting
|
||||
-- for the first action cycle to start
|
||||
self.object:set_animation(
|
||||
{x=self.animation["stand_start"], y=self.animation["stand_end"]},
|
||||
self.animation_speed + math.random(10)
|
||||
)
|
||||
end
|
||||
|
||||
-- save all custom fields
|
||||
local objProps = self.object:get_properties()
|
||||
local villager_data = {
|
||||
vInfo = objProps.infotext,
|
||||
vHP = objProps.hp_max,
|
||||
|
||||
vName = self.vName,
|
||||
vAge = self.vAge,
|
||||
vTexture = self.vTexture,
|
||||
vSize = self.vSize,
|
||||
vBox = self.vBox,
|
||||
vYaw = self.vYaw,
|
||||
vYawSaved = self.vYawSaved,
|
||||
vGender = self.vGender,
|
||||
vTimer = self.vTimer,
|
||||
vAction = self.vAction,
|
||||
vDigging = self.vDigging,
|
||||
vActionFrequency = self.vActionFrequency,
|
||||
vInitialChatDistance = 0,
|
||||
vHudIds = self.vHudIds,
|
||||
vSoundHandle = self.vSoundHandle,
|
||||
vDespawned = self.vDespawned,
|
||||
|
||||
-- homeplace
|
||||
vRegion = self.vRegion,
|
||||
vVillage = self.vVillage,
|
||||
vPlot = self.vPlot,
|
||||
vType = self.vType,
|
||||
vSchem = self.vSchem,
|
||||
vBed = self.vBed,
|
||||
vUnknown = self.vUnknown,
|
||||
|
||||
-- pathfinding
|
||||
vPos = self.vPos,
|
||||
vFacingDirection = self.vFacingDirection,
|
||||
vOriginPos = self.vOriginPos,
|
||||
vOriginDistance = self.vOriginDistance,
|
||||
vOriginDistMax = self.vOriginDistMax,
|
||||
vTotalDistance = self.vTotalDistance,
|
||||
vTargetPos = self.vTargetPos,
|
||||
vSpawnHeight = self.vSpawnHeight,
|
||||
vTargetHeight = self.vTargetHeight,
|
||||
vTurnPreference = self.vTurnPreference,
|
||||
vWalkReady = false,
|
||||
vDigReady = false,
|
||||
vBedPos = self.vBed,
|
||||
vDoorPos = self.vDoor,
|
||||
vJobPos = self.vDoor,
|
||||
|
||||
-- chatting
|
||||
vChatting = nil,
|
||||
vChatReady = true,
|
||||
vScriptHi = self.vScriptHi,
|
||||
vScriptHiSaved = self.vScriptHiSaved,
|
||||
vScriptBye = self.vScriptBye,
|
||||
vScriptByeSaved = self.vScriptByeSaved,
|
||||
vScriptGtg = self.vScriptGtg,
|
||||
vScriptGtgSaved = self.vScriptGtgSaved,
|
||||
vScriptMain = self.vScriptMain,
|
||||
vScriptMainSaved = self.vScriptMainSaved,
|
||||
vScriptSmalltalk = self.vScriptSmalltalk,
|
||||
vScriptSmalltalkSaved = self.vScriptSmalltalkSaved,
|
||||
vScriptGameFacts = self.vScriptGameFacts,
|
||||
vScriptGameFactsSaved = self.vScriptGameFactsSaved,
|
||||
|
||||
-- trading
|
||||
vID = self.vID,
|
||||
vIsTrader = self.vIsTrader,
|
||||
vTrading = nil,
|
||||
vNodeMetaPos = self.vNodeMetaPos,
|
||||
vBuy = self.vBuy,
|
||||
vSell = self.vSell,
|
||||
vTitle = self.vTitle,
|
||||
vTraded = false,
|
||||
|
||||
-- debugging
|
||||
vTextureString = self.vTextureString,
|
||||
|
||||
}
|
||||
|
||||
return minetest.serialize(villager_data)
|
||||
end,
|
||||
|
||||
})
|
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB |