Remove villagers mod

master
Solebull 2019-04-09 16:57:25 +02:00
parent 50b2ba52da
commit 7d09af97be
222 changed files with 14 additions and 6349 deletions

3
.gitignore vendored
View File

@ -7,3 +7,6 @@ tags
## Files related to minetest development cycle
*.patch
## Removed mods
OLDmods/*/

4
OLDmods/README.md Normal file
View File

@ -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
View File

@ -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

View File

@ -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!

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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

View File

@ -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,
})

View File

@ -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

View File

@ -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

View File

@ -1,2 +0,0 @@
-- set 'false' to not give player startup coins
villagers.startup_coins = true

View File

@ -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"
}
}

View File

@ -1,6 +0,0 @@
mg_villages
cottages?
village_gambit?
village_sandcity?
village_towntest?
farming?

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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")

View File

@ -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",
})

View File

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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,
})

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Some files were not shown because too many files have changed in this diff Show More