ADD Goblins

master
daret 2020-11-07 23:10:17 +01:00
parent 3bcd69ff64
commit bbfdaca91f
87 changed files with 4011 additions and 0 deletions

101
mods/mobs/goblins/README.md Normal file
View File

@ -0,0 +1,101 @@
# [goblins] Goblins (Mobs Redo addon) [mod] for Minetest
* forum: https://forum.minetest.net/viewtopic.php?f=9&t=13004
* code :https://github.com/FreeLikeGNU/goblins
* Minetest Content DB: https://content.minetest.net/packages/FreeLikeGNU/goblins/
This mod adds several Goblins (and Goblin Dogs) to Minetest that should spawn near ore deposits or lairs underground.
* Goblins dig caves, destroy torches, create lairs, set traps, cultivate mushrooms and some are aggressive.
* Gobdogs will roam caves, bury bones and other items in soft terrain, eat meats. Some Gobdogs are aggressive!
* Basic trading with Goblins - the more you trade with a goblin, the more likely you will get things in return!
* Goblins have a territorial framework for location based interactions, if you trade with more Goblins of a territory
your trades will be easier!
* Harming goblins will negatively affect your trade with all gobins in a territory!
* If you brandish weapons (including axes) around a goblin it may decide to attack!
* Goblins and Gobdogs will defend each other. You can have them defend other mobs by simply
adding to:
`on_spawn = function(self)`
the following:
`self.groups = {"goblin_friend", "gobdog_friend"}`
in your own mobs definition!
* There are many settings now accessible from the minetest menu -> "settings" tab -> "all settings" -> "mods" -> "goblins" list!
these can also be defined in the settingtypes.txt
* Goblin and Gobdog spawning is now configured from goblins_spawning.lua (at least until there is a way to easily change these with the setting menu :P )
* A basic and optional (enabled in minetest setting menu) HUD is available
* tested with Minetest 5.2 and 5.30(dev)
## Required Mods:
* Mobs Redo by TenPlus1 API as of version 20200516: to run
* https://forum.minetest.net/viewtopic.php?f=9&t=9917
* Mobs Redo git repository https://notabug.org/TenPlus1/mobs_redo
## Optional Mods:
* Ambience Lite by TenPlus1 API
* https://notabug.org/TenPlus1/ambience
* Hunger NG by Linuxdirk
* https://forum.minetest.net/viewtopic.php?t=19664
* https://gitlab.com/4w/hunger_ng
* Bonemeal by TenPlus1
* https://forum.minetest.net/viewtopic.php?t=16446
* https://notabug.org/TenPlus1/bonemeal
## Licenses of Source Media Files:
* goblins_goblin.b3d and goblins_goblin.blend
* Copyright 2015 by Francisco "FreeLikeGNU" Athens Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
* http://creativecommons.org/licenses/by-sa/3.0/
* above meshes based on character from minetest_game
* by MirceaKitsune (WTFPL)
* https://github.com/minetest/minetest_game/blob/master/mods/default/README.txt#L71
* goblins_goblins*.png files and goblins_goblin.xcf files
* Copyright 2015,2020 by Francisco "FreeLikeGNU" Athens Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
* http://creativecommons.org/licenses/by-sa/3.0/
* goblins_goblin_dog.b3d and goblins_goblin_dog*.blend
* Copyright 2016,2020 by Francisco "FreeLikeGNU" Athens Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
* http://creativecommons.org/licenses/by-sa/3.0/
* goblins_goblin_dog*.png files and goblins_goblin_dog*_.xcf files
* Copyright 2015 by Francisco "FreeLikeGNU" Athens Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
* http://creativecommons.org/licenses/by-sa/3.0/
## Additional source and content credits:
* goblin mushrooms from "flowers" mod source code:
* Originally by Ironzorg (MIT) and VanessaE (MIT)
* Various Minetest developers and contributors (MIT)
* mushrooms.xcf Copyright Francisco Athens (CC BY-SA 3.0) 2020
* goblins_mushroom_brown.png, goblins_mushroom_brown2.png goblins_mushroom_brown3.png goblins_mushroom_brown4.png Copyright Francisco Athens (CC BY-SA 3.0) 2020
## Sound files by:
* artisticdude http://opengameart.org/content/goblins-sound-pack (CC0-license)
* Ogrebane http://opengameart.org/content/monster-sound-pack-volume-1 (CC0-license)
* goblins_ambient_underground: 232685__julius-galla__atmosphere-cave-loop (CC-BY-SA)
* https://freesound.org/people/julius_galla/sounds/232685/
* goblins_goblin_trap: LittleRobotSoundFactory (CC-BY-SA)
* https://freesound.org/people/LittleRobotSoundFactory/
* goblins_goblin_breathing: spookymodem
* https://freesound.org/people/spookymodem/ (CC-0)
* goblins_goblin_dog_ sounds:
* delphidebrain Jazz the Dog Howl & Bark (CC-BY-SA)
* https://freesound.org/people/delphidebrain/sounds/236027/
* Glitchedtones Dog Shih Tzu Growling 06.wav (CC-BY-SA)
* https://freesound.org/people/Glitchedtones/sounds/372533/
* Super thanks to duane-r for his work on nasty traps and tunneling https://github.com/duane-r
* Thanks to Napiophelios for the goblin king skin
* https://forum.minetest.net/viewtopic.php?f=9&t=13004#p186921
* goblins_goblin_king.png
* License: Creative Commons (CC-BY-SA-3.0 SummerFeilds TP
Thanks to orbea for adding Hunger NG and Bonemeal mod support and bugfixes!
* https://github.com/orbea
Thanks to TenPlus1 for keeping the Mobs_Redo going!
Thanks to rubenwardy for awesome help and the Minetest ContentDB
Thanks to everyone in the Minetest forums and IRC for just being great!
GET MINETEST: https://www.minetest.net/

View File

@ -0,0 +1,178 @@
local function variance(min,max)
local target = math.random(min,max) / 100
--print(target)
return target
end
local gob_name_parts = goblins.gob_name_parts
local goblins_spawning = goblins.spawning
goblins.gobdog_types = {
gobdog = {
owner_loyal = true,
attack_npcs = false,
attack_monsters = false,
group_attack = true,
attack_players = true,
},
gobdog_aggro = {
description ="Dire Gobdog",
lore = "Dire Gobdogs are sensitive to light and are very territorial",
type = "monster",
group_attack = true,
owner_loyal = true,
attack_npcs = false,
attack_monsters = false,
attack_players = true,
spawning = goblins_spawning.gobdog_aggro
},
}
-------------
-- Gobdog Template
------------
goblins.gobdog_template = {
description ="Gobdog",
lore = "Gobdogs are not canids but goblins that have mutated somehow, fortunately they do not share the hunger and size of the mythical werewolf.",
type = "npc",
attack_type = "dogfight",
group_attack = true,
reach = 2,
damage = 1,
hp_min = 2,
hp_max = 5,
armor = 100,
stepheight = 1.2,
walk_velocity = 2,
run_velocity = 4,
jump = true,
jump_height = 6,
pushable = true,
knock_back = true,
view_range = 15,
water_damage = 0,
lava_damage = 5,
light_damage = 0,
fear_height = 4,
floats = 1,
--glow = 1,
pathfinding = 1,
stay_near = {
"group:water", 20,
"group:sand", 20,
"group:soil", 20,
"default:mossycobble", 10,
"group:meat" ,2,
"goblins:goblins_goblin_bone_meaty"},
collisionbox = {-0.45, -0.01, -0.45, 0.45, 0.85, 0.45},
visual = "mesh",
mesh = "goblins_goblin_dog.b3d",
textures = {
{"goblins_goblin_dog.png"},
},
makes_footstep_sound = true,
sounds = {
random = "goblins_goblin_dog_ambient_cave",
war_cry = "goblins_goblin_dog_war_cry_cave",
attack = "goblins_goblin_dog_attack_cave",
damage = "goblins_goblin_dog_damage_cave",
death = "goblins_goblin_dog_death_cave",
replace = "goblins_goblin_dog_replace_cave",gain = 0.8,
},
follow = {
"goblins:goblins_goblin_bone","goblins:goblins_goblin_bone_meaty","group:meat"
},
drops = {
{name = "goblins:goblins_goblin_bone", chance = 1, min = 1, max = 3},
},
animation = {
speed_normal = 60,
stand_start = 0,
stand_end = 60,
walk_start = 70,
walk_end = 90,
run_start = 130,
run_end = 140,
run_speed =30,
jump_start = 160,
jump_end = 190,
jump_loop = true,
jump_speed = 30,
punch_start = 130,
punch_end = 140,
punch_speed = 30,
die_start = 140,
die_stop = 145,
die_speed = 30,
die_loop = false,
},
on_spawn = function(self)
minetest.sound_play("goblins_goblin_dog_war_cry_cave", {
object = self.object,
gain = .5,
max_hear_distance =30
})
self.groups = {"gobdog", "goblin"}
self.groups_defend = {"goblin","gobdog","gobdog_friend"}
if not self.secret_name then
local name_rules = {"list_a", "list_opt"}
self.secret_name = goblins.generate_name(gob_name_parts, name_rules)
end
--print (dump(self.secret_name))
local pos = vector.round(self.object:getpos())
if not pos then return end
if not self.secret_territory then
local territory = {goblins.territory(pos)}
self.secret_territory = {name = territory[1], vol = territory[2]}
--print(dump(self.secret_territory.name).." secret_territory assigned")
else
--print(dump(self.secret_territory.name).." secret_territory already assigned")
end
local color_var = "#"..math.random(10,50)..math.random(10,50)..math.random(10,50)
--print("COLOR_VAR: "..color_var)
self.object:set_texture_mod("^[colorize:"..color_var..":80")
if not self.size then
local s_mod = variance(1,20)
self.size = {x = (variance(90,100) - s_mod), y = (variance(80,110) - s_mod), z = (variance(90,100) - s_mod)}
end
local self_properties = self.object:get_properties()
self_properties.visual_size = self.size
self.object:set_properties(self_properties)
goblins.announce_spawn(self)
end,
on_rightclick = function(self, clicker)
if mobs:feed_tame(self, clicker, 4, true, true) then return end
if mobs:protect(self, clicker) then return end
if mobs:capture_mob(self, clicker, 0, 5, 50, false, nil) then return end
end,
---dog behaviors or not...
do_custom = function(self)
goblins.goblin_dog_behaviors(self)
end,
do_punch = function(self,hitter)
local pname = hitter:get_player_name()
local relations = goblins.relations(self, pname)
if not relations.aggro then
goblins.relations(self, pname, {aggro = 0})
relations = goblins.relations(self, pname)
end
--print(self.secret_name.." relations on click:\n"..dump(self.relations).."\n")
if self.relations[pname].aggro then
local adj = (self.relations[pname].aggro + 1) * 1.5
self.relations[pname].aggro = math.floor(adj)
goblins.relations(self, pname, {aggro = self.relations[pname].aggro} )
end
end,
spawning = goblins_spawning.gobdog
}
mobs:alias_mob("goblins:goblins_goblin_dog", "goblins:goblin_gobdog")
mobs:alias_mob("goblins:goblin_goblin_dog", "goblins:goblin_gobdog")
mobs:alias_mob("goblins:goblins_goblin_dog_aggro", "goblins:goblin_gobdog_aggro")
mobs:alias_mob("goblins:goblin_goblin_dog_aggro", "goblins:goblin_gobdog_aggro")

View File

@ -0,0 +1,806 @@
local debug_goblins_attack = minetest.settings:get_bool("debug_goblins_attack") or false
local debug_goblins_find = minetest.settings:get_bool("debug_goblins_find") or false
local debug_goblins_replace = minetest.settings:get_bool("debug_goblins_replace") or false
local debug_goblins_replace2 = minetest.settings:get_bool("debug_goblins_replace2") or false
local debug_goblins_search = minetest.settings:get_bool("debug_goblins_search") or false
local debug_goblins_tunneling = minetest.settings:get_bool("debug_goblins_tunneling") or false
local debug_goblins_trade = minetest.settings:get_bool("debug_goblins_trade") or false
--@trade_shrewdness increases trade difficulty
local goblins_trade_shrewdness = tonumber(minetest.settings:get("goblins_trade_shrewdness")) or 20
--@aggro_on_wield will goblins attack when a weapon is wielded?
local goblins_aggro_on_wield = minetest.settings:get_bool("goblins_aggro_on_wield") ~= false
--@goblins_defend_groups will goblins and gobdogs defend other mobs?
local goblins_defend_groups = minetest.settings:get_bool("goblins_defend_groups") ~= false
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
local goblins_node_protect_strict = minetest.settings:get_bool("goblins_node_protect_strict") ~= false
local peaceful_only = minetest.settings:get_bool("only_peaceful_mobs")
local S = minetest.get_translator("goblins")
local gob_name_parts = goblins.gob_name_parts
local function strip_escapes(input)
goblins.strip_escapes(input)
end
local function print_s(input)
print(goblins.strip_escapes(input))
end
function goblins.mixitup(pos)
pos.y = pos.y + math.random()
pos.x = pos.x + math.random()
pos.x = pos.x - math.random()
pos.z = pos.z + math.random()
pos.z = pos.z - math.random()
return pos
end
local function match_item_list(item, list)
for k,v in pairs(list) do
local found = string.find(item, v)
return found
end
end
local function match_only_list(item, list)
for k,v in pairs(list) do
if item == v then
return v
end
end
end
---Goblins will become aggro at range due to
-- wielded weapon, player aggression, defending defined mob groups
-- some attack code reused from Mobs Redo by TenPlus1
function goblins.attack(self, target, type)
if self.state == "runaway"
or self.state == "attack"
or self:day_docile()
or peaceful_only == true
then
--print_s(S("player not considered"))
return
end
local pos = vector.round(self.object:getpos())
local s = self.object:get_pos()
local objs = minetest.get_objects_inside_radius(s, self.view_range)
local aggro_wielded = {}
if goblins_aggro_on_wield then
aggro_wielded = self.aggro_wielded
end
local defend_groups = goblins_defend_groups
--print_s(S(dump(aggro_wielded)))
-- remove entities we aren't interested in
for n = 1, #objs do
local ent = objs[n]:get_luaentity()
-- are we a player?
if objs[n]:is_player() then
local pname = objs[n]:get_player_name()
local relations_self = {}
local relations = {}
local relations_adj = 0
--do we know this player?
local relations_self = goblins.relations(self, pname)
if not relations_self.trade then goblins.relations(self, pname,{trade = 0}) end
if not relations_self.aggro then goblins.relations(self, pname,{aggro = 0}) end
--tally the territorial scores
relations.aggro = goblins.relations_territory(self, pname, "aggro")
relations.trade = goblins.relations_territory(self, pname, "trade")
if debug_goblins_attack then
print_s(S("@1 of @2: ", self.secret_name, self.secret_territory.name))
print_s(S("mob relations = @1",dump(goblins.relations(self, pname))))
print_s(S("comparing territory trade @1 and aggro @2",dump(relations.trade),dump(relations.aggro)))
end
if relations.trade >= relations.aggro then
relations_adj = relations.trade - relations.aggro
end
if debug_goblins_attack then print_s(S("relations = @1",dump(relations))) end
if mobs.invis[pname]
or self.owner == pname then
local name = ""
else
local player = objs[n]
local name = "player"
end
-- if player invisible or mob not setup to attack then remove from list
local wielded = objs[n]:get_wielded_item():to_string()
--print(self.secret_name.." is loyal to " ..self.owner)
if debug_goblins_attack then print_s( S("player has @1 in hand",dump(objs[n]:get_wielded_item():to_string())))end
if self.attack_players == false
or relations_adj >= 100
or not self.owner == pname
--or not self.tamed
or mobs.invis[pname]
or self.specific_attack == "player" then
if debug_goblins_attack then print_s(S("found exempt player with score of @1 holding @2",relations_adj,dump(objs[n]:get_wielded_item():to_string()))) end
objs[n] = nil
--print("- pla", n)
else
if debug_goblins_attack then print_s(S("attackable player, @1 holding @2",pname,wielded)) end
--lets check if our friends in a fight with the player!
for n = 1, #objs do
local ent_other = objs[n]:get_luaentity()
if defend_groups and ent_other and ent_other.groups and self.groups_defend then
for k,v in pairs(self.groups_defend) do
if match_only_list(v, ent_other.groups) and
ent_other.state == "attack" and
ent_other.attack:is_player() and
ent_other.attack:get_player_name() == pname then
local xname = ent_other.attack:get_player_name()
if debug_goblins_attack then print_s( S(" ****Defending @1 from @2!",v,xname)) end
minetest.sound_play("goblins_goblin_war_cry", {
pos = pos,
gain = 1.0,
max_hear_distance = self.sounds.distance or 10
})
self:set_animation("run")
self:set_velocity(self.run_velocity)
self.state = "attack"
self.attack = ent_other.attack
end
end
end
end
if aggro_wielded and match_item_list(wielded, aggro_wielded)
then
if debug_goblins_attack then print_s(S("*** aggro triggered by @1 at @2 !! ***",wielded,minetest.pos_to_string(pos))) end
minetest.sound_play("goblins_goblin_war_cry", {
pos = pos,
gain = 1.0,
max_hear_distance = self.sounds.distance or 10
})
self:set_animation("run")
self:set_velocity(self.run_velocity)
self.state = "attack"
self.attack = (objs[n])
end
end
end --end of player eval
--what else do we care about?
--group_attack mobs nearby
end
end
--- Drops a special personlized item
function goblins.special_gifts(self, pname, drop_chance, max_drops)
if pname then
if self.drops then
if not drop_chance then drop_chance = 1000 end
if not max_drops then max_drops = 1 end
local rares = {}
for k,v in pairs(self.drops) do
--print_s(dump(v.name).." and "..dump(v.chance))
if v.chance >= drop_chance then
table.insert(rares,v.name)
end
end
if #rares > 0 then
--print_s("rares = "..dump(rares))
local pos = self.object:getpos()
pos.y = pos.y + 0.5
goblins.mixitup(pos)
if #rares > max_drops then
rares = rares[math.random(max_drops, #rares)]
if type(rares) ~= table then rares = {rares} end --
end
for k,v in pairs(rares) do
minetest.sound_play("goblins_goblin_cackle", {
pos = pos,
gain = 1.0,
max_hear_distance = self.sounds.distance or 10
})
local item_wear = math.random(5000,10000)
local stack = ItemStack({name = v, wear = item_wear })
local org_desc = minetest.registered_items[v].description
local meta = stack:get_meta()
local tool_adj = goblins.generate_name(goblins.words_desc, {"tool_adj"})
-- special thanks here to rubenwardy for showing me how translation works!
meta:set_string(
"description", S("@1's @2 @3", self.secret_name, tool_adj, org_desc)
)
local obj = minetest.add_item(pos, stack)
minetest.chat_send_player(
pname,S("@1 drops @2",self.secret_name, meta:get_string("description"))
)
end
end
end
end
end
---grab the score for a territory
--@rel_names are a table of relations to reference
function goblins.get_scores(self,player_name,rel_names)
local t_scores = {}
local player = minetest.get_player_by_name(player_name)
if not player then return end
local meta = player:get_meta()
local pdata = {}
--local pdata[self.secret_territory] = {}
for k,v in pairs(rel_names) do
t_scores[v] = goblins.relations_territory(self, player_name, v)
pdata[k] = v
end
--print_s(S("t_scores = @1",dump(t_scores)))
--write player data
meta:set_string(self.secret_territory.name, minetest.serialize(t_scores))
--print("*** player meta = "..meta:get_string(self.secret_territory.name))
meta:set_string("territory_current", self.secret_territory.name)
if self.secret_name_told and self.secret_name_told[player_name] then
meta:set_string("goblin_current", self.secret_name)
print("*** player meta = "..meta:get_string("goblin_current"))
else
meta:set_string("goblin_current", "unknown goblin")
end
goblins.update_hud(player)
return t_scores
end
--calculate tables of scores and get results
local function score_calc(add, sub)
local adds = 0
local subs = 0
local score = 0
if add then
for k,v in pairs(add) do
adds = v + adds
end
end
if sub then
for k,v in pairs(sub) do
subs = v + subs
end
end
if adds >= subs then
score = adds - subs
return score
else score = 0
end
return score
end
-- prepare trade score for giving a gift
local function trade_score(self, player_name)
local add = {}
local sub = {}
local rel_names = {"trade", "aggro"}
local rel_scores = goblins.get_scores(self,player_name,rel_names)
add[1] = rel_scores.trade
sub[1] = rel_scores.aggro
local result = score_calc(add, sub)
--print_s(S("calculated score = @1",dump(result)))
return result
end
--- You can give a gift, they *may* give something(s) in return, thats Goblin trading
function goblins.give_gift(self,clicker)
--if mobs:feed_tame(self, clicker, 14, false, false) then
local item = clicker:get_wielded_item()
local name = clicker:get_player_name()
local gift_accepted = nil
local gift_declined = nil
local pname = clicker:get_player_name()
local name_told = goblins.secret_name(self, pname)
local territory_told = goblins.secret_territory(self, pname)
local trade_shrewdness = goblins_trade_shrewdness
--establish trade if its not set
if not self.relations[pname] then
goblins.relations(self, pname, {trade = 0})
end
if debug_goblins_trade then print_s(dump(goblins.relations(self, pname))) end
local srp_trade = self.relations[pname].trade
local gift = item:get_name()
local gift_description = item:get_definition().description
if debug_goblins_trade then print_s("you offer: " ..dump(gift)) end
for k,v in pairs(self.follow) do
if v == gift then
local gift_value = k or 5 --higher number is less wanted gift, otherwise 10
gift_accepted = true
if debug_goblins_trade then print_s(S("@1 accepts @2 (@3)",self.name,dump(gift),k)) end
--increase trade rating on gifting - first item in follow list is worth more
srp_trade = srp_trade + math.ceil(5/k)
if gift == self.follow[1] then
srp_trade = srp_trade + 4
minetest.chat_send_player(pname,"Yessss! " .. gift_description.."!")
end
goblins.relations(self, pname,{trade = srp_trade})
if debug_goblins_trade then print_s("this goblins trade rating is now = " ..dump(srp_trade)) end
local gr_trade = trade_score(self, pname)
if debug_goblins_trade then print_s("Goblin adjusted (trade - aggro) relations for territory is = "..dump(gr_trade)) end
if not minetest.settings:get_bool("creative_mode") then
item:take_item()
clicker:set_wielded_item(item)
end
---appease an attacking goblin
if self.state == "attack" and self.attack == clicker then
if (self.relations[pname].aggro * 2) < self.relations[pname].trade then
self.state = "stand"
self:set_velocity(0)
self:set_animation("stand")
self.attack = nil
self.v_start = false
self.timer = 0
self.blinktimer = 0
self.path.way = nil
end
end
--print_s(dump(self.object:get_luaentity()).. " at " ..dump(self.object:getpos()).. " takes: " ..dump(item:get_name()))
if self.drops then
if debug_goblins_trade then
print_s("you may get some of "..dump(#self.drops).. " things such as: ")
for _,v in pairs(self.drops) do
print_s(dump(v.name).. " with a base drop chance of 1 in " ..dump(v.chance))
end
end
-- we can make some mobs extra stingy despite trade relations
if not self.shrewdness then self.shrewdness = 1 end
local pos = self.object:getpos()
pos.y = pos.y + 0.5
for _,v in pairs(self.drops) do
--@d_chance takes all the factors of trade into account for each item in drop list
local d_chance = 0
d_chance = ((gift_value + v.chance) * (self.shrewdness + trade_shrewdness)) / (gr_trade + 1)
--print(v.name.." d_chance = " ..d_chance)
--more likely to get something really rare , less likely to get something common
if gift == self.follow[1] then d_chance = self.shrewdness + trade_shrewdness end
d_chance = math.ceil(d_chance)
if math.random(d_chance) == 1 then
if debug_goblins_trade == true then
print_s(S("\n @1 dropped by @2 at an adjusted chance of 1 in @3", dump(v.name),dump(self.name),dump(d_chance)))
end
minetest.sound_play("goblins_goblin_cackle", {
pos = pos,
gain = 0.2,
max_hear_distance = self.sounds.distance or 10
})
--let it go already!
goblins.mixitup(pos)
minetest.add_item(pos, {
name = v.name
})
end
end
end
gift_accepted = true
if name_told and territory_told then
minetest.chat_send_player(pname,S("@1 of @2 takes your @3!",self.secret_name,self.secret_territory.name,gift_description))
elseif name_told then
minetest.chat_send_player(pname,S("@1 takes your @2!",self.secret_name,gift_description))
else
minetest.chat_send_player(pname,S("Goblin takes your @1!", gift_description))
end
return gift_accepted --acception of gift complete
else
if debug_goblins_trade == true then print_s("You did not offer " .. dump(string.split(v,":")[2]) ) end
end
end
local pos = self.object:getpos()
minetest.sound_play("goblins_goblin_damage", {
pos = pos,
gain = 0.2,
max_hear_distance = self.sounds.distance or 10
})
if name_told and territory_told then
minetest.chat_send_player(
pname,S("@1 of @2 does not want your @3",
self.secret_name,self.secret_territory.name,gift_description))
elseif name_told then
minetest.chat_send_player(pname,S("@1 does not want your @2",self.secret_name,gift_description))
else
minetest.chat_send_player(pname,S("Goblin does not want your @1",gift_description))
end
gift_declined = true
end
--- Replaces nodes with many params.
function goblins.search_replace(
self,
search_rate,
search_rate_above,
search_rate_below,
search_offset,
search_offset_above,
search_offset_below,
replace_rate,
replace_what,
replace_with,
replace_rate_secondary,
replace_with_secondary,
decorate, --this is for placing attached nodes like goblin mushrooms and torches
debug_me,
tools) -- {primary, secondary} based on index# of self.goblin_tools
local pos = self.object:getpos()
if mobs_griefing and not minetest.is_protected(pos, "") and math.random(1, search_rate) == 1 then
-- look for nodes
local pos = self.object:getpos()
local pos1 = self.object:getpos()
local pos2 = self.object:getpos()
--local pos = vector.round(self.object:getpos()) --will have to investigate these further
--local pos1 = vector.round(self.object:getpos())
--local pos2 = vector.round(self.object:getpos())
local tool_set = {}
if tools then
tool_set = tools
end
-- if we are looking, will we look below and by how much?
if math.random(1, search_rate_below) == 1 then
pos1.y = pos1.y - search_offset_below
end
-- if we are looking, will we look above and by how much?
if math.random(1, search_rate_above) == 1 then
pos2.y = pos2.y + search_offset_above
end
pos1.x = pos1.x - search_offset
pos1.z = pos1.z - search_offset
pos2.x = pos2.x + search_offset
pos2.z = pos2.z + search_offset
if debug_goblins_search and debug_me then
print (self.name:split(":")[2] .. " at\n "
.. minetest.pos_to_string(pos) .. " is searching between\n "
.. minetest.pos_to_string(pos1) .. " and\n "
.. minetest.pos_to_string(pos2))
end
local nodelist = minetest.find_nodes_in_area(pos1, pos2, replace_what)
if #nodelist > 0 then
if debug_goblins_find and debug_me then
print_s(#nodelist.." nodes found by " .. self.name:split(":")[2]..":")
for k,v in pairs(nodelist) do print_s(minetest.get_node(v).name:split(":")[2].. " found.") end
end
for key,value in pairs(nodelist) do
value = vector.round(value)
-- ok we see some nodes around us, are we going to replace them?
if minetest.is_protected(value, "") and goblins_node_protect_strict then break end
if math.random(1, replace_rate) == 1 then
local air_value = nil
if replace_rate_secondary and
math.random(1, replace_rate_secondary) == 1 then
if decorate then
value = minetest.find_node_near(value, 1, "air")
end
if value ~= nil then
-- print("decorating with "..replace_with_secondary..minetest.pos_to_string(value))
if tools and self.goblin_tools and type(self.goblin_tools) == "table" then
local replace = tools[2]
goblins.tool_attach(self,self.goblin_tools[replace])
--print("changing tool: "..self.goblin_tools[replace])
end
self:set_velocity(0)
self:set_animation("punch")
if decorate then
minetest.set_node(value, {name = replace_with_secondary})
else
minetest.set_node(value, {name = replace_with_secondary})
end
end
if debug_goblins_replace2 and debug_me then
print_s(replace_with_secondary.." secondary node placed by " .. self.name:split(":")[2])
end
else
if decorate then
value = minetest.find_node_near(value, 1, "air")
end
if value ~= nil then
if tools and self.goblin_tools and type(self.goblin_tools) == "table" then
local replace = tools[1]
goblins.tool_attach(self,self.goblin_tools[replace])
--print("changing tool: "..self.goblin_tools[replace])
end
-- print("decorating with "..replace_with..minetest.pos_to_string(value))
self:set_velocity(0)
self:set_animation("punch")
if decorate then
minetest.set_node(value, {name = replace_with})
else
minetest.set_node(value, {name = replace_with})
end
end
if debug_goblins_replace and debug_me then
print_s(replace_with.." placed by " .. self.name:split(":")[2])
end
end
minetest.sound_play(self.sounds.replace, {
object = self.object, gain = self.sounds.gain,
max_hear_distance = self.sounds.distance
})
end
end
end
end
end
--[[
"He destroys everything diggable in his path. It's too much trouble
to fudge around with particulars. Besides, I don't want them to
mine for me."
--From the tome __Of Goblinkind__ by duane-r
"The domain of stone is the Goblins home,
by metals might one thwart them from invasion."
--Epithet from __The Luanacy of Goblins__ by Persont Bachslachdi
--]]
--the following is built from duane-r's goblin tunnel digging:
local diggable_nodes = {"group:stone", "group:sand", "group:soil", "group:cracky", "group:crumbly"}
-- This translates yaw into vectors.
local cardinals = {{x=0,y=0,z=0.75}, {x=-0.75,y=0,z=0}, {x=0,y=0,z=-0.75}, {x=0.75,y=0,z=0}}
----------
-- Goblins Tunneling.
---------
-- @type are available for fine-tuning.
function goblins.tunneling(self, type)
--
if type == nil then
type = "digger"
end
local pos = self.object:getpos()
if mobs_griefing and not minetest.is_protected(pos, "") then
if self.state == "tunnel" then
self:set_animation("walk")
self:set_velocity(self.walk_velocity)
-- Yaw is stored as one of the four cardinal directions.
if not self.digging_dir then
self.digging_dir = math.random(0,3)
end
-- Turn him roughly in the right direction.
-- self.object:setyaw(self.digging_dir * math.pi * 0.5 + math.random() * 0.5 - 0.25)
self.object:setyaw(self.digging_dir * math.pi * 0.5)
-- Get a pair of coordinates that should cover what's in front of him.
local p = vector.add(pos, cardinals[self.digging_dir+1])
-- p.y = p.y - 1 -- What's this about?
local p1 = vector.add(p, .1)
local p2 = vector.add(p, 1.5)
-- Get any diggable nodes in that area.
local np_list = minetest.find_nodes_in_area(p1, p2, diggable_nodes)
if #np_list > 0 then
-- Dig it.
--print(" NP_LIST: ".. dump(np_list))
for _, np in pairs(np_list) do
if minetest.is_protected(np, "") and goblins_node_protect_strict then break end
local np_info = minetest.get_node(np)
--print(" np_name: "..np_info.name)
if np_info.name ~= "default:mossycobble" and np_info.name ~= "default:chest" then
self:set_animation("punch")
minetest.remove_node(np)
minetest.sound_play(self.sounds.replace, {
object = self.object, gain = self.sounds.gain,
max_hear_distance = self.sounds.distance
})
end
end
end
if math.random() < 0.2 then
local d = {-1,1}
self.digging_dir = (self.digging_dir + d[math.random(2)]) % 4
end
self.state = "walk"
self:set_animation("walk")
self:set_velocity(self.walk_velocity)
elseif self.state == "room" then -- Dig a room.
--[[first make sure player is not near by! (not quite ready yet)
goblins.must_hide = function()
end --]]
if not self.room_radius then
self.room_radius = 1
end
self:set_velocity(0)
self.state = "stand"
self:set_animation("stand")
-- Work from the inside, out.
for r = 1,self.room_radius do
-- Get a pair of coordinates that form a room.
local p1 = vector.add(pos, -r)
local p2 = vector.add(pos, r)
-- But not below him.
p1.y = pos.y
local np_list = minetest.find_nodes_in_area(p1, p2, diggable_nodes)
--FLG prefers a smaller room with a rougher look for goblin warrens. Maybe this should be a setting for users preference?
if r >= self.room_radius and #np_list == 0 then
--self.room_radius = math.random(1,2) + math.random(0,1)
self.room_radius = math.random(1,1.5) + math.random(0,0.5)
-- self.state = "stand"
-- self:set_velocity(0)
-- self:set_animation("stand")
break
end
if #np_list > 0 then -- dig it
if goblins_node_protect_strict then break end
self:set_animation("punch")
minetest.remove_node(np_list[math.random(#np_list)])
minetest.sound_play(self.sounds.replace, {
object = self.object, gain = self.sounds.gain,
max_hear_distance = self.sounds.distance
})
break
end
self.state = "walk"
self:set_animation("walk")
self:set_velocity(self.walk_velocity)
end
end
---the following values should be vars for settings...
--if we are standing, maybe make a tunnel or
--if we are tunneling, maybe make a room or
--if we are tunneling stand or maybe just end this function
--
if self.state == "stand" and math.random() < 0.1 then
self.state = "tunnel"
if debug_goblins_tunneling then print_s("goblineer is now tunneling") end
elseif self.state == "tunnel" and math.random() < 0.1 then
self.state = "room"
if debug_goblins_tunneling then print_s("goblineer is now making a room") end
elseif self.state == "tunnel" and math.random() < 0.1 then
self.state = "stand"
if debug_goblins_tunneling then print_s(dump(vector.round(self.object:getpos())).. "goblineer is thinking...") end
end
end
end
function goblins.danger_dig(self,freq,depth)
local pos = vector.round(self.object:getpos())
local lol = minetest.get_node_light(pos) or 0
local freq = freq or 0.1
local depth = depth or 1
local target = table.copy(pos)
target.y = target.y - depth
if self.light_damage_min and
lol >= self.light_damage_min and
mobs_griefing and
not minetest.is_protected(target, "") and
math.random() <= freq and
minetest.get_node(target).name ~="air" then
local target_node = minetest.get_node(target)
if self.state ~= "stand" then self.state = "stand" end
--find a pick among goblin tools if we can
if self.goblin_tools and type(self.goblin_tools) == "table" then
local tool = match_item_list("pick",self.goblin_tools)
if tool then
goblins.tool_attach(self,self.goblin_tools[tool])
--print("changing tool: "..self.goblin_tools[replace])
end
end
self:set_velocity(0)
self:set_animation("punch")
minetest.remove_node(target)
local node_above = vector.round(self.object:getpos())
node_above.y = node_above.y + 2
local nb_node1 = vector.round(self.object:getpos())
local nb_node2 = vector.round(self.object:getpos())
nb_node1.y = node_above.y
nb_node2.y = node_above.y
nb_node1.x = nb_node1.x - 1
nb_node1.z = nb_node1.z - 1
nb_node2.x = nb_node1.x + 1
nb_node2.z = nb_node1.z + 1
local air_nodes = minetest.find_nodes_in_area(nb_node1,nb_node2, "air")
--print(#nodes)
if #air_nodes == 1 then
minetest.set_node(node_above, {name = target_node.name})
end
end
end
function goblins.goblin_dog_behaviors(self)
local pos = self.object:getpos()
if math.random() < 0.1 then
goblins.attack(self)
--print("looking for a reason to fight")
end
if mobs_griefing and not minetest.is_protected(pos, "") then
if math.random() < 0.5 then
--consume meaty bones"
goblins.search_replace(
self,
100, --search_rate
100000, --search_rate_above
100000, --search_rate_below
1, --search_offset
1, --search_offset_above
1, --search_offset_below
5, --replace_rate
{"group:meat","group:food_meat","group:food_meat_raw"}, --replace_what
"goblins:goblins_goblin_bone", --replace_with
10, --replace_rate_secondary
"air", --replace_with_secondary --very hungry
nil, --decorate
false --debug_me if debugging also enabled in behaviors.lua
)
elseif math.random() < 0.5 then
--consume dry bones"
goblins.search_replace(
self,
100, --search_rate
100000, --search_rate_above
100000, --search_rate_below
1, --search_offset
1, --search_offset_above
1, --search_offset_below
5, --replace_rate
"goblins:goblins_goblin_bone", --replace_what
"air", --replace_with
nil, --replace_rate_secondary
nil, --replace_with_secondary
nil, --decorate
false--debug_me if debugging also enabled in behaviors.lua
)
elseif math.random() < 0.8 then
--dig and maybe bury bones if theres suitable terrain around
goblins.search_replace(
self,
100, --search_rate
100000, --search_rate_above
100, --search_rate_below
1, --search_offset
1, --search_offset_above
2, --search_offset_below
10, --replace_rate
{"group:soil",
"group:sand",
"default:gravel"}, --replace_what
"goblins:dirt_with_bone",
2, --replace_rate_secondary
"default:dirt", --replace_with_secondary
nil, --decorate
false --debug_me if debugging also enabled in behaviors.lua
)
else
--or maybe bury something more useful
goblins.search_replace(
self,
100, --search_rate
100000, --search_rate_above
100, --search_rate_below
1, --search_offset
1, --search_offset_above
2, --search_offset_below
10, --replace_rate
{"group:soil",
"group:sand",
"default:gravel"}, --replace_what
"goblins:dirt_with_stuff",
2, --replace_rate_secondary
"default:dirt", --replace_with_secondary
nil, --decorate
false --debug_me if debugging also enabled in behaviors.lua
)
end
end
--[[not quite ready yet...
if math.random() < 0.01 then
goblins.do_taunt_at(self)
print_s("are " ..dump(self.name).. " barking?" )
end
--]]
end

View File

@ -0,0 +1,26 @@
* Update 2020/07/12 Goblins dig to safety! Also, random tool selection weight fixes.
* Update 2020/07/06 added moss node and removed glow from cobblemoss node for better visuals
more variations for goblin tools and appropriate tools/nodes are chosen for tasks
* Update 2020/06/29 fixed a crash with protection enabled (such as with minecarts). Thanks to rusty-snake for reporting this!
* Update 2020/06/28 fixed a crashing bug in hud code, attached tools to goblins, new goblin animation and texture mods
* Update 2020/06/18 Optional HUD (enable in Minetest options menu)
* Update 2020/06/11 Custom Goblins and Gobdogs can be defined in goblins_custom.lua
attacking goblins can now be appeased through trade
* Update 2020/06/07 Goblin and Gobdog spawning is now configured from goblins_spawning.lua (at least until there is a way to easily change these with the setting menu :P )
* Update 2020/06/06 There are many settings now accessible from the minetest menu -> "settings" tab -> "all settings" -> "mods" -> "goblins" list! these can also be defined in the settingtypes.txt
* Update 2020/06/04 Mobs now defend each other (and will defend other mobs with a simple modification to those mobs)
* Update 2020/05/21 Aggro on player wielding a weapon. Trade affected by player aggression.
* Update 2020/05/09 Territorial enhancements:
The more goblins you trade with from a territory the better and more frequent drops you receive from trading!
Multiplayer works with territory and trade relations, each player has their own relations with goblins and territories.
Internationalization support begun to allow for translations
Personalized drops from goblins when you have learned their name and territory
* Update 2020/05/05 Goblins have territories defined by chunk!
Optional HungerNG and Bonemeal mod support thanks to orbea!
* Update 2020/05/01 Goblins have names!
* Update 2020/04/21: Gobdogs added!
* Update 2020/04/18: Implemented tunneling function from duane-r's goblins. Behavioral functions now get their own file.
* Update 2020/04/17: Added TenPlus1 [url=https://notabug.org/TenPlus1/ambience]ambience[/url] support as optional dependency. Weaker goblins are more timid around player.
more variety in sounds added.
* Update 2020/04/13: Goblin Fungiler added.
* Update 2020/04/11: Goblins get some updates and fixes. Should work with Minetest 5.30 (dev)[/spoiler]

View File

@ -0,0 +1,4 @@
default
mobs
ambience?
hunger_ng?

View File

@ -0,0 +1,902 @@
-- goblin namegen sets from https://github.com/LukeMS/lua-namegen
-- libtcod https://github.com/libtcod/libtcod name set format have been adapted for my Goblins gen_name function
local S = minetest.get_translator("goblins")
local gob_name_parts = goblins.gob_name_parts
local gob_words = goblins.words_desc
local function strip_escapes(input)
goblins.strip_escapes(input)
end
local function print_s(input)
print(goblins.strip_escapes(input))
end
local function variance(min,max)
local target = math.random(min,max) / 100
--print(target)
return target
end
-- you can use the goblins_spawning.lua or goblins_custom.lua to change spawning behavior
local goblins_spawning = goblins.spawning
-- this table defines the goblins with how they differ from the goblin template.
goblins.gob_types = {
digger = {
description = S("Cavedigger Goblin"),
lore = S("The digger burrows though stone to carve out the bowels of a goblin warren."),
damage = 1,
hp_min = 5,
hp_max = 10,
runaway_from = "player",
sounds = {
random = "goblins_goblin_breathing",
war_cry = "goblins_goblin_attack",
attack = "goblins_goblin_attack",
damage = "goblins_goblin_damage",
death = "goblins_goblin_death",
replace = "goblins_goblin_pick",
gain = .5,
distance = 15
},
textures = {
"goblins_goblin_digger.png"
},
-- if either digging style is set too close to "1", then the digging will go vertical!
-- best to set either of these less than 0.5 to give the gobs time to roam...
do_custom = function(self)
goblins.danger_dig(self)
if self.time_of_day > 0.2
and self.time_of_day < 0.8 then
if math.random() < 0.01 then -- higher values for more straight tunnels and room-like features
goblins.tunneling(self, "digger")
elseif math.random() < 0.5 then -- higher values more rough, tight and twisty digging
goblins.search_replace(
self,
15, --search_rate how often do we search?
10, --search_rate_above
10, --search_rate_below
.6,--search_offset
1.2, --search_offset_above
1, --search_offset_below
2, --replace_rate
{"group:soil",
"group:sand",
"default:gravel",
"default:stone",
"default:desert_stone",
"group:torch"}, --replace_what
"air", --replace_with
nil, --replace_rate_secondary
nil, --replace_with_secondary
nil, --decorate
{1}, -- primary and secondary (if used) tool index
nil --debug messages
)
end
elseif math.random() < 0.5 then
--and self.object:getpos().y < 0 then
goblins.search_replace(
self,
50, --search_rate how often do we search?
2, --search_rate_above
10000, --search_rate_below
.6,--search_offset
1.5, --search_offset_above
1, --search_offset_below
10, --replace_rate
{"group:soil",
"group:sand",
"default:gravel",
"default:stone",
"default:desert_stone",
"group:torch"}, --replace_what
"air", --replace_with
nil, --replace_rate_secondary
nil, --replace_with_secondary
nil, --decorate
{1}, -- primary and secondary (if used) tool index
nil --debug messages
)
end
end,
spawning = goblins_spawning.digger,
additional_properties = {
goblin_tools = {"goblins:pick_mossycobble","default:pick_stone","default:pick_wood"}
},
after_activate = function (self)
--self.goblin_tools = {"goblins:pick_mossycobble","default:pick_stone","default:pick_wood"}
goblins.tool_attach(self,self.goblin_tools)
end
},
cobble = {
description = S("Cobblemoss Goblin"),
lore = S("Cobbler crumbles walls infusing them with moss to collect moisture for a fetid, mushroom friendly habitat."),
damage = 1,
hp_min = 5,
hp_max = 10,
sounds = {
random = {"goblins_goblin_breathing",gain = 0.5},
war_cry = "goblins_goblin_war_cry",
attack = "goblins_goblin_attack",
damage = "goblins_goblin_damage",
death = "goblins_goblin_death",
replace = "default_place_node",gain = 0.8,
distance = 15
},
textures = {
{"goblins_goblin_cobble1.png"},
{"goblins_goblin_cobble2.png"},
},
runaway_from = "player",
do_custom = function(self)
goblins.danger_dig(self)
if math.random() < .2 then
goblins.search_replace(
self,
50, --search_rate
20, --search_rate_above
20, --search_rate_below
1, --search_offset
2, --search_offset_above
1, --search_offset_below
20, --replace_rate
"default:mossycobble", --replace_what
"goblins:moss", --replace_with
nil, --replace_rate_secondary
nil, --replace_with_secondary
nil, --decorate
{3}, -- primary and secondary tool index
nil --debug messages
)
else
goblins.search_replace(
self,
50, --search_rate
20, --search_rate_above
20, --search_rate_below
1, --search_offset
2, --search_offset_above
1, --search_offset_below
20, --replace_rate
{ "group:stone",
"group:torch"}, --replace_what
"default:mossycobble", --replace_with
90, --replace_rate_secondary
"goblins:mossycobble_trap", --replace_with_secondary
nil, --decorate
{1,2}, -- primary and secondary tool index
nil --debug messages
)
end
end,
additional_properties = {
goblin_tools = {
"default:mossycobble",
"default:axe_stone",
"goblins:moss",
"default:sword_stone"
}
},
after_activate = function (self)
goblins.tool_attach(self,self.goblin_tools)
end,
spawning = goblins.spawning.cobble
},
snuffer = {
description = S("Snuffer Goblin"),
lore = S("The Snuffer likes to put out pesky torches and steal them, collecting the fuel for trap makers."),
damage = 1,
hp_min = 5,
hp_max = 10,
textures = {
{"goblins_goblin_snuffer.png"}
},
stay_near = "group:torch",
runaway_from = "player",
do_custom = function(self)
goblins.danger_dig(self)
if math.random() < 0.01 then
goblins.tool_attach(self,self.goblin_tools)
end
goblins.search_replace(
self,
10, --search_rate
10, --search_rate_above
10, --search_rate_below
2, --search_offset
2, --search_offset_above
2, --search_offset_below
10, --replace_rate
{"group:torch"}, --replace_what
"air", --replace_with
1000, --replace_rate_secondary
"goblins:mossycobble_trap", --replace_with_secondary
nil, --decorate
{2}, -- primary and secondary tool index
nil --debug messages
)
end,
additional_properties = {
goblin_tools = {"default:axe_stone","default:stick"}
},
after_activate = function (self)
goblins.tool_attach(self,self.goblin_tools)
end,
spawning = goblins_spawning.snuffer
},
fungiler = {
description = S("Goblin Fungiler"),
lore = S("Fungilers keep the warren full of tasty mushrooms which are also fuel for pyromancy."),
damage = 1,
hp_min = 5,
hp_max = 10,
sounds = {
random = "goblins_goblin_breathing",
war_cry = "goblins_goblin_war_cry",
attack = "goblins_goblin_attack",
damage = "goblins_goblin_damage",
death = "goblins_goblin_death",
replace = "default_place_node", gain = 0.8,
distance = 15
},
textures = {
{"goblins_goblin_fungler1.png"},
{"goblins_goblin_fungler2.png"},
},
runaway_from = "player",
do_custom = function(self)
goblins.danger_dig(self)
if math.random() < 0.01 then
goblins.tool_attach(self,self.goblin_tools)
end
if math.random() < .4 then
goblins.search_replace(
self,
50, --search_rate
20, --search_rate_above
20, --search_rate_below
1.5, --search_offset
1.5, --search_offset_above
0, --search_offset_below
20, --replace_rate
"goblins:moss", --replace_what
"goblins:mushroom_goblin3", --replace_with
50, --replace_rate_secondary
"goblins:mushroom_goblin4", --replace_with_secondary
true, --decorate
{1,1}, -- primary and secondary tool index
nil --debug messages
)
else
goblins.search_replace(
self,
50, --search_rate
20, --search_rate_above
20, --search_rate_below
1.5, --search_offset
1.5, --search_offset_above
0, --search_offset_below
20, --replace_rate
"goblins:moss", --replace_what
"goblins:mushroom_goblin", --replace_with
50, --replace_rate_secondary
"goblins:mushroom_goblin2", --replace_with_secondary
true, --decorate
{1,1}, -- primary and secondary tool index
nil --debug messages
)
end
end,
additional_properties = {
goblin_tools = {"goblins:mushroom_goblin","goblins:pick_mossycobble"}
},
after_activate = function (self)
goblins.tool_attach(self,self.goblin_tools)
end,
spawning = goblins_spawning.fungiler
},
coal = {
description = S("Coalbreath Goblin"),
lore = S("Coal is essential for pyromantic pyrotechnics, the coalbreath goblin attuned to this."),
damage = 1,
hp_min = 5,
hp_max = 10,
textures = {
{"goblins_goblin_coal1.png"},
{"goblins_goblin_coal2.png"},
},
do_custom = function(self)
goblins.danger_dig(self)
if math.random() < 0.0001 then --vary rarely will attack and only if player looks like a threat
goblins.tool_attach(self,"default:sword_stone")
goblins.attack(self)
--print("looking for a reason to fight")
elseif math.random() < 0.01 then
goblins.tool_attach(self,self.goblin_tools)
end
goblins.search_replace(
self,
100, --search_rate
100, --search_rate_above
100, --search_rate_below
1, --search_offset
2, --search_offset_above
1, --search_offset_below
10, --replace_rate
{ "default:mossycobble",
"default:stone_with_coal",
"group:torch"}, --replace_what
"default:mossycobble", --replace_with
50, --replace_rate_secondary
"goblins:stone_with_coal_trap", --replace_with_secondary
nil, --decorate
{1,2}, -- primary and secondary tool index
nil --debug messages
)
end,
additional_properties = {
goblin_tools = {"goblins:pick_mossycobble","default:coal_lump"}
},
after_activate = function (self)
goblins.tool_attach(self,self.goblin_tools)
end,
spawning = goblins_spawning.coal
},
copper = {
description = S("Coppertooth Goblin"),
lore = S("Coppertooth seek metals to enhance their mining tools and are easily aggressive in defence."),
damage = 2,
hp_min = 10,
hp_max = 20,
textures = {
{"goblins_goblin_copper1.png"},
{"goblins_goblin_copper2.png"},
},
drops = {
{name = "default:pick_diamond",
chance = 1000, min = 0, max = 1},
{name = "default:shovel_diamond",
chance = 1000, min = 0, max = 1},
{name = "default:axe_diamond",
chance = 1000, min = 0, max = 1},
{name = "default:pick_bronze",
chance = 10, min = 0, max = 1},
{name = "default:bronze_ingot",
chance = 7, min = 0, max = 1},
{name = "default:axe_bronze",
chance = 5, min = 0, max = 1},
},
do_custom = function(self)
goblins.danger_dig(self)
if math.random() < 0.00001 then --may take a while to build courage
goblins.tool_attach(self,"default:sword_bronze")
goblins.attack(self)
--print("looking for a reason to fight")
elseif math.random() < 0.01 then
goblins.tool_attach(self,self.goblin_tools)
end
goblins.search_replace(
self,
100, --search_rate
100, --search_rate_above
100, --search_rate_below
1, --search_offset
2, --search_offset_above
1, --search_offset_below
10, --replace_rate
{ "default:mossycobble",
"default:stone_with_copper",
"group:torch"}, --replace_what
"default:mossycobble", --replace_with
50, --replace_rate_secondary
"goblins:stone_with_copper_trap", --replace_with_secondary
nil, --decorate
{1,2}, -- primary and secondary tool index
nil --debug messages
)
end,
additional_properties = {
goblin_tools = {"default:pick_bronze","default:sword_bronze"}
},
after_activate = function (self)
goblins.tool_attach(self,self.goblin_tools)
end,
spawning = goblins_spawning.copper
},
iron ={
description = S("Ironpick Goblin"),
lore = S("Most fey creatures avoid iron, but the ironpick goblins seem oddly immune."),
damage = 2,
hp_min = 10,
hp_max = 20,
textures = {
{"goblins_goblin_iron1.png"},
{"goblins_goblin_iron2.png"},
},
drops = {
{name = "default:pick_diamond",
chance = 1000, min = 0, max = 1},
{name = "default:shovel_diamond",
chance = 1000, min = 0, max = 1},
{name = "default:axe_diamond",
chance = 1000, min = 0, max = 1},
{name = "default:pick_steel",
chance = 10, min = 0, max = 1},
{name = "default:steel_ingot",
chance = 7, min = 0, max = 1},
{name = "default:axe_steel",
chance = 5, min = 0, max = 1},
},
do_custom = function(self)
goblins.danger_dig(self)
if math.random() < 0.01 then
goblins.tool_attach(self,"default:sword_steel")
goblins.attack(self)
--print("looking for a reason to fight")
elseif math.random() < 0.01 then
goblins.tool_attach(self,self.goblin_tools)
end
goblins.search_replace(
self,
100, --search_rate
100, --search_rate_above
100, --search_rate_below
1, --search_offset
2, --search_offset_above
1, --search_offset_below
10, --replace_rate
{ "default:mossycobble",
"default:stone_with_iron",
"group:torch"}, --replace_what
"default:mossycobble", --replace_with
50, --replace_rate_secondary
"goblins:stone_with_iron_trap", --replace_with_secondary
nil, --decorate
{1,2}, -- primary and secondary tool index
nil --debug messages
)
end,
additional_properties = {
goblin_tools = {"default:pick_steel","default:sword_steel","default:axe_steel"}
},
after_activate = function (self)
goblins.tool_attach(self,self.goblin_tools)
end,
spawning = goblins_spawning.iron
},
gold = {
description = S("Goldshiv Goblin"),
lore = S("Goldshiv goblins are jealous creatures and aggressive at the slightest provocation"),
damage = 3,
hp_min = 10,
hp_max = 30,
textures = {
{"goblins_goblin_gold1.png"},
{"goblins_goblin_gold2.png"},
},
drops = {
{name = "default:pick_diamond",
chance = 1000, min = 0, max = 1},
{name = "default:shovel_diamond",
chance = 1000, min = 0, max = 1},
{name = "default:axe_diamond",
chance = 1000, min = 0, max = 1},
{name = "default:pick_gold",
chance = 100, min = 0, max = 1},
{name = "default:gold_lump",
chance = 7, min = 0, max = 1},
{name = "default:pick_bronze",
chance = 5, min = 0, max = 1},
},
do_custom = function(self)
goblins.danger_dig(self)
if math.random() < 0.01 then
goblins.tool_attach(self,"default:sword_diamond")
goblins.attack(self)
--print("looking for a reason to fight")
elseif math.random() < 0.01 then
goblins.tool_attach(self,self.goblin_tools)
end
goblins.search_replace(
self,
100, --search_rate
100, --search_rate_above
100, --search_rate_below
1, --search_offset
2, --search_offset_above
1, --search_offset_below
10, --replace_rate
{ "default:mossycobble",
"default:stone_with_gold",
"group:torch"}, --replace_what
"default:mossycobble", --replace_with
30, --replace_rate_secondary
"goblins:stone_with_gold_trap", --replace_with_secondary
nil, --decorate
{1,2}, -- primary and secondary tool index
nil --debug messages
)
end,
additional_properties = {
goblin_tools = {"default:sword_steel","default:sword_diamond"}
},
after_activate = function (self)
goblins.tool_attach(self,self.goblin_tools)
end,
spawning = goblins_spawning.gold
},
diamond = {
description = S("Diamondagger Goblin"),
lore = S("Diamonddagger goblins know their blades are among the sharpest and seem eager to prove it."),
damage = 3,
hp_min = 20,
hp_max = 30,
textures = {
{"goblins_goblin_diamond1.png"},
{"goblins_goblin_diamond2.png"},
},
drops = {
{name = "default:pick_mese",
chance = 1000, min = 0, max = 1},
{name = "default:shovel_mese",
chance = 1000, min = 0, max = 1},
{name = "default:axe_mese",
chance = 1000, min = 0, max = 1},
{name = "default:pick_diamond",
chance = 100, min = 0, max = 1},
{name = "default:diamond",
chance = 7, min = 0, max = 1},
{name = "default:pick_bronze",
chance = 5, min = 0, max = 1},
},
follow = {"default:diamond", "default:apple", "default:torch", "default:blueberries"},
do_custom = function(self)
goblins.danger_dig(self)
if math.random() < 0.01 then
goblins.tool_attach(self,"default:sword_diamond")
goblins.attack(self)
--print("looking for a reason to fight")
elseif math.random() < 0.01 then
goblins.tool_attach(self,self.goblin_tools)
end
goblins.search_replace(
self,
100, --search_rate
100, --search_rate_above
100, --search_rate_below
1, --search_offset
2, --search_offset_above
1, --search_offset_below
10, --replace_rate
{ "default:mossycobble",
"default:stone_with_diamond",
"group:torch"}, --replace_what
"default:mossycobble", --replace_with
30, --replace_rate_secondary
"goblins:stone_with_diamond_trap", --replace_with_secondary
nil, --decorate
{2,1}, -- primary and secondary tool index
nil --debug messages
)
end,
additional_properties = {
goblin_tools = {"default:sword_diamond","default:pick_diamond"}
},
after_activate = function (self)
goblins.tool_attach(self,self.goblin_tools)
end,
spawning = goblins_spawning.diamond
},
hoarder = {
description = S("Goblin Hoarder"),
lore = S("Woe to the spelunker who encounters a hoarder unprepared!"),
type = "monster",
damage = 4,
hp_min = 20,
hp_max = 40,
textures = {
{"goblins_goblin_hoarder.png"},
},
drops = {
{name = "default:meselamp",
chance = 1000, min = 0, max = 1},
{name = "default:pick_mese",
chance = 1000, min = 0, max = 1},
{name = "default:shovel_mese",
chance = 10, min = 0, max = 1},
{name = "default:mese_crystal",
chance = 7, min = 0, max = 1},
{name = "default:pick_bronze",
chance = 5, min = 0, max = 1},
},
do_custom = function(self)
goblins.danger_dig(self)
if math.random() < 0.01 then
goblins.tool_attach(self,"default:sword_mese")
goblins.attack(self)
--print("looking for a reason to fight")
elseif math.random() < 0.01 then
goblins.tool_attach(self,self.goblin_tools)
end
goblins.search_replace(
self,
100, --search_rate
100, --search_rate_above
100, --search_rate_below
1, --search_offset
2, --search_offset_above
1, --search_offset_below
10, --replace_rate
{ "group:stone",
"group:torch"}, --replace_what
"default:mossycobble", --replace_with
10, --replace_rate_secondary
"goblins:mossycobble_trap", --replace_with_secondary
nil, --decorate
{2,3}, -- primary and secondary tool index
nil --debug messages
)
end,
additional_properties = {
goblin_tools = {"default:pick_mese","default:sword_diamond","default:sword_mese"}
},
after_activate = function (self)
goblins.tool_attach(self,self.goblin_tools)
end,
spawning = goblins_spawning.hoarder
},
}
--gob_types.king = gob_types.hoarder -- for compatability
mobs:alias_mob("goblins:goblin_king", "goblins:goblin_hoarder")
----------------------------------
--DEFAULT GOBLIN TEMPLATES
----------------------------------
-- these are drops all goblins will have
local gob_drops = {
{name = "default:torch",
chance = 4, min = 0, max = 10},
{name = "default:flint",
chance = 3, min = 0, max = 2},
{name = "default:mossycobble",
chance = 3, min = 0, max = 3},
{name = "goblins:goblins_goblin_bone_meaty",
chance = 3, min = 0, max = 1},
{name = "goblins:goblins_goblin_bone",
chance = 2, min = 0, max = 3},
{name = "goblins:mushroom_goblin",
chance = 2, min = 0, max = 5},
}
goblins.goblin_template = { --your average goblin,
description = "Basic Goblin",
lore = "This goblin has a story yet to be...",
type = "npc",
passive = false,
attack_type = "dogfight",
attack_monsters = false,
attack_npcs = false,
attack_players = true,
group_attack = true,
runaway = true,
damage = 1,
reach = 2,
knock_back = true,
hp_min = 5,
hp_max = 10,
armor = 100,
visual = "mesh",
mesh = "goblins_goblin.b3d",
textures = {
{"goblins_goblin_cobble1.png"},
{"goblins_goblin_cobble2.png"},
},
blood_texture = "goblins_blood.png",
collisionbox = {-0.25, -.01, -0.25, 0.25, .9, 0.25},
drawtype = "front",
makes_footstep_sound = true,
sounds = {
random = "goblins_goblin_breathing",
war_cry = "goblins_goblin_war_cry",
attack = "goblins_goblin_attack",
damage = "goblins_goblin_damage",
death = "goblins_goblin_death",
replace = "goblins_goblin_cackle", gain = 0.05,
distance = 15},
walk_velocity = 2,
run_velocity = 3,
pathfinding = 1,
jump = true,
jump_height = 5,
step_height = 1.5,
fear_height = 4,
water_damage = 0,
lava_damage = 2,
light_damage = 1,
lifetimer = 360,
view_range = 10,
stay_near = "group:stone",
order = "follow",
animation = {
stand_speed = 30,
stand_start = 0,
stand_end = 79,
walk_speed = 40,
walk_start = 168,
walk_end = 187,
run_speed = 60,
run_start = 168,
run_end = 187,
punch_speed = 60,
punch_start = 200,
punch_end = 219,
},
drops = {
{name = "default:pick_steel",
chance = 1000, min = 0, max = 1},
{name = "default:shovel_steel",
chance = 1000, min = 0, max = 1},
{name = "default:axe_steel",
chance = 1000, min = 0, max = 1},
{name = "default:pick_mossycobble",
chance = 10, min = 0, max = 1},
{name = "default:axe_stone",
chance = 5, min = 0, max = 1},
},
follow = {
"default:mese", "default:diamond", "default:gold_lump", "default:apple",
"default:blueberries", "default:torch", "default:cactus", "default:stick",
"flowers:mushroom_brown","flowers:mushroom_red"
},
on_spawn = function(self)
self.groups = {"goblin"}
self.groups_defend = {"goblin","gobdog","goblin_friend"}
if not self.shrewdness then self.shrewdness = 20 end
if not self.aggro_wielded then
self.aggro_wielded= {"sword","axe","bow","spear","knife"}
end -- some goblins are looking for a fight
if not self.secret_name then
self.secret_name = goblins.generate_name(gob_name_parts)
end
--print (dump(self.secret_name))
--print (dump(self.special_gifts).. " are precious to "..dump(self.secret_name).. "!")
local pos = vector.round(self.object:getpos())
if not pos then print(dump(self).."\n **position error!** \n") return end --something went wrong!
if not self.secret_territory then
local opt_data = {}
opt_data[self.secret_name] = os.time() --add this goblin as a key member of territory
local territory = {goblins.territory(pos,opt_data)}
--print("territory = "..dump(territory).."opt_data = "..dump(opt_data))
self.secret_territory = {name = territory[1], vol = territory[2]}
--print(dump(self.secret_territory.name).." secret_territory assigned")
else
--print(dump(self.secret_territory.name).." secret_territory already assigned")
end
local drop_list = {}
if not self.drops_set then
drop_list = table.copy(self.drops)
for _,v in ipairs(gob_drops) do
table.insert(drop_list,v)
end
self.drops = drop_list
self.drops_set = true
end
local color_var = "#"..math.random(10,50)..math.random(10,50)..math.random(10,50)
--print("COLOR_VAR: "..color_var)
self.object:set_texture_mod("^[colorize:"..color_var..":70")
--print_s(S(self.secret_name.." has "..dump(self.drops)))
if not self.size then
self.size = {x = variance(90,100), y = variance(65,105), z = variance(90,100)}
end
local self_properties = self.object:get_properties()
self_properties.visual_size = self.size
self.object:set_properties(self_properties)
goblins.announce_spawn(self)
--print_s(S(dump(minetest.registered_items[self.name])))
end,
do_punch = function(self,hitter)
local pname = hitter:get_player_name()
local relations = goblins.relations(self, pname)
if not relations.aggro then
goblins.relations(self, pname, {aggro = 0})
relations = goblins.relations(self, pname)
end
--print(self.secret_name.." relations on click:\n"..dump(self.relations).."\n")
if self.relations[pname].aggro then
local adj = (self.relations[pname].aggro + 1) * 1.5
self.relations[pname].aggro = math.floor(adj)
goblins.relations(self, pname, {aggro = self.relations[pname].aggro} )
end
local rel_names = {"trade", "aggro"}
goblins.get_scores(self,pname,rel_names)
end,
--By default the Goblins are willing to trade,
--this can be overridden in the table for any goblin.
on_rightclick = function(self,clicker)
local pname = clicker:get_player_name()
local relations = goblins.relations(self, pname)
if not relations.trade then
goblins.relations(self, pname, {trade = 0})
relations = goblins.relations(self, pname)
end
goblins.special_gifts(self)
--print(self.secret_name.." relations on click:\n"..dump(self.relations).."\n")
if self.relations[pname].trade >= 30 and not self.secret_territory_told[pname] then
goblins.secret_territory(self, pname, "tell")
goblins.special_gifts(self,pname)
--print(" these gifts lined up "..dump(self.special_gifts))
end
if self.relations[pname].trade >= 15 and not self.secret_name_told[pname] then
goblins.secret_name(self, pname, "tell")
goblins.special_gifts(self,pname)
--print(" these gifts lined up "..dump(self.special_gifts))
end
-- print(pname.." is about to make an offering")
goblins.give_gift(self,clicker)
end,
do_custom = function(self)
goblins.search_replace(
self,
5, --search_rate
10, --search_rate_above
10, --search_rate_below
3, --search_offset
2, --search_offset_above
2, --search_offset_below
10, --replace_rate
{"group:torch"}, --replace_what
"air", --replace_with
1000, --replace_rate_secondary
"goblins:mossycobble_trap", --replace_with_secondary
true, --decorate
nil, -- primary and secondary tool index
nil --debug messages
)
end,
spawning = {
nodes = {"default:mossycobble"},
min_light = 0,
max_light = 10,
chance = 500,
active_object_count = 4,
min_height = -31000,
max_height = -20,
day_toggle = nil,
a = nil
}
}

View File

@ -0,0 +1,99 @@
local S = minetest.get_translator("goblins")
-- Create new goblins here (or override existing ones).
-- This example just overrides the existing Cobble, feel to remove it
--[[
goblins.gob_types.cobble = {
description = S("Cobblemoss Goblin"),
lore = S("Cobbler crumbles walls infusing them with moss to collect moisture for a fetid, mushroom friendly habitat."),
damage = 1,
hp_min = 5,
hp_max = 10,
sounds = {
random = {"goblins_goblin_breathing",gain = 0.5},
war_cry = "goblins_goblin_war_cry",
attack = "goblins_goblin_attack",
damage = "goblins_goblin_damage",
death = "goblins_goblin_death",
replace = "default_place_node",gain = 0.8,
distance = 15
},
textures = {
{"goblins_goblin_cobble1.png"},
{"goblins_goblin_cobble2.png"},
},
runaway_from = "player",
do_custom = function(self)
if math.random() < .2 then
goblins.search_replace(
self,
50, --search_rate
20, --search_rate_above
20, --search_rate_below
1, --search_offset
2, --search_offset_above
1, --search_offset_below
20, --replace_rate
"default:mossycobble", --replace_what
"goblins:moss", --replace_with
nil, --replace_rate_secondary
nil, --replace_with_secondary
nil, --decorate
{3}, -- primary and secondary tool index
nil --debug messages
)
else
goblins.search_replace(
self,
50, --search_rate
20, --search_rate_above
20, --search_rate_below
1, --search_offset
2, --search_offset_above
1, --search_offset_below
20, --replace_rate
{ "group:stone",
"group:torch"}, --replace_what
"default:mossycobble", --replace_with
90, --replace_rate_secondary
"goblins:mossycobble_trap", --replace_with_secondary
nil, --decorate
{1,2}, -- primary and secondary tool index
nil --debug messages
)
end
end,
additional_properties = {
goblin_tools = {
"default:mossycobble",
"default:axe_stone",
"goblins:moss",
"default:sword_stone"
}
},
after_activate = function (self)
goblins.tool_attach(self,self.goblin_tools)
end,
spawning = goblins.spawning.cobble
--]]
--[[
goblins.gob_types.foo = {
description = S("foo Goblin"),
lore = S("foo bar baz"),
--you can leave as much as you want undefined as it will be added by the default template in goblins.lua
}
--]]
--[[
goblins.gobdog_types.zoo = {
description = S("zoo Gobdog"),
lore = S("zoo bar foo"),
--you can leave as much as you want undefined as it will be added by the default template in animals.lua
}
--]]

View File

@ -0,0 +1,179 @@
-- alter this file to adjust goblin/gobdog spawning
-- it may be overwritten on update so be sure to back up your changes
-- or use a custom file (explained at the bottom)
goblins.spawning = {
digger = {
nodes = {"group:stone"},
neighbors = "air",
min_light = 0,
max_light = 10,
interval = 30,
chance = 1000,
active_object_count = 2,
min_height = -31000,
max_height = -15,
day_toggle = nil,
on_spawn = nil,
},
cobble = {
nodes = {"group:stone"},
neighbors = "air",
min_light = 0,
max_light = 10,
interval = 30,
chance = 1000,
active_object_count = 2,
min_height = -31000,
max_height = -15,
day_toggle = nil,
on_spawn = nil,
},
snuffer = {
nodes = {"default:mossycobble"},
neighbors = "air",
min_light = 0,
max_light = 10,
interval = 30,
chance = 1000,
active_object_count = 2,
min_height = -31000,
max_height = -15,
day_toggle = nil,
on_spawn = nil,
},
fungiler = {
nodes = {"default:mossycobble"},
neighbors = "air",
min_light = 0,
max_light = 10,
interval = 30,
chance = 1500,
active_object_count = 1,
min_height = -31000,
max_height = -15,
day_toggle = nil,
on_spawn = nil,
},
coal = {
nodes = {"default:stone_with_coal", "default:mossycobble"},
neighbors = "air",
min_light = 0,
max_light = 10,
interval = 20,
chance = 500,
active_object_count = 3,
min_height = -31000,
max_height = -25,
day_toggle = nil,
on_spawn = nil,
},
copper = {
nodes = {"default:stone_with_copper", "default:mossycobble", "default:blueberries"},
neighbors = "air",
min_light = 0,
max_light = 10,
interval = 30,
chance = 500,
active_object_count = 2,
min_height = -31000,
max_height = -35,
day_toggle = nil,
on_spawn = nil,
},
iron = {
nodes = {"default:stone_with_iron", "default:mossycobble"},
neighbors = "air",
min_light = 0,
max_light = 10,
interval = 20,
chance = 500,
active_object_count = 3,
min_height = -31000,
max_height = -35,
day_toggle = nil,
on_spawn = nil,
},
gold = {
nodes = {"default:stone_with_gold", "default:mossycobble"},
neighbors = "air",
min_light = 0,
max_light = 10,
interval = 30,
chance = 500,
active_object_count = 2,
min_height = -31000,
max_height = -100,
day_toggle = nil,
on_spawn = nil,
},
diamond = {
nodes = {"default:stone_with_diamond", "default:mossycobble"},
neighbors = "air",
min_light = 0,
max_light = 10,
interval = 60,
chance = 1000,
active_object_count = 2,
min_height = -31000,
max_height = -200,
day_toggle = nil,
on_spawn = nil,
},
hoarder = {
nodes = {"default:mossycobble","default:chest"},
neighbors = "air",
min_light = 0,
max_light = 10,
interval = 90,
chance = 2000,
active_object_count = 1,
min_height = -31000,
max_height = -20,
day_toggle = nil,
on_spawn = nil,
},
gobdog = {
nodes = {"default:mossycobble", "group:sand"},
min_light = 0,
max_light = 14,
chance = 500,
active_object_count = 4,
min_height = -31000,
max_height = -20,
day_toggle = nil,
on_spawn = nil,
},
gobdog_aggro = {
nodes = {"default:mossycobble", "group:sand"},
min_light = 0,
max_light = 6,
chance = 500,
active_object_count = 1,
min_height = -31000,
max_height = -100,
day_toggle = nil,
on_spawn = nil,
}
}
---you can also override by declaring individual types here as follows:
goblins.spawning.gobdog = {
nodes = {"default:mossycobble", "group:sand"},
min_light = 0,
max_light = 14,
chance = 500,
active_object_count = 4,
min_height = -31000,
max_height = -20,
day_toggle = nil,
on_spawn = nil,
}
-- alternatively you can make a custom file with something like:
-- dofile(path .. "/goblins_spawning_custom.lua")
-- and declare your overrides there.

169
mods/mobs/goblins/hud.lua Normal file
View File

@ -0,0 +1,169 @@
local saved_huds = {}
local ght = tonumber(minetest.settings:get("goblins_hud_timer")) or 0
local function clear_hud(params)
local player = minetest.get_player_by_name(params[1])
local meta = player:get_meta()
--print ("testing"..dump(params))
if os.time() >= (meta:get_int("hud_time_start") + ght) then
player:hud_remove(params[2])
meta:set_int("hud_cleared",1)
--print ("removing"..dump(params))
end
end
function goblins.update_hud(player)
if ght > 0 then
--for _,player in ipairs(minetest.get_connected_players()) do
local player_name = player:get_player_name()
--print(player_name)
local meta = player:get_meta()
local goblin_current = meta:get_string("goblin_current")
local territory_current = meta:get_string("territory_current")
local known_territories = minetest.deserialize(meta:get_string("territory_list"))
local territory = "unknown"
if known_territories then
for k,v in pairs(known_territories) do
if territory_current == v then
territory = v
end
end
end
local text_goblin_current = goblin_current
local text_territory_current = "of " ..territory
local territory_score_table = minetest.deserialize(meta:get_string(territory_current))
local text_territory_scores = {}
if not territory_score_table then return end
for k,v in pairs(territory_score_table) do
text_territory_scores[k] = k..": "..v
end
local ids = saved_huds[player_name]
if not ids then
ids = {}
ids.territory_scores = {}
saved_huds[player_name] = ids
-- create HUD elements and set ids into `ids`
--[[
player:hud_add({
hud_elem_type = "text",
position = {x = 1, y = 0.5},
offset = {x = -120, y = -25},
text = "Goblin HUD",
alignment = -0,
scale = { x = 100, y = 30},
number = 0xFFFFFF,
})
--]]
ids.goblin_current = player:hud_add({
hud_elem_type = "text",
position = {x = 1, y = 0.5},
offset = {x = -120, y = -25},
text = text_goblin_current,
alignment = -1,
scale = { x = 50, y = 10},
number = 0xFFFFFF,
})
local params = {player_name,ids.goblin_current}
minetest.after((ght+5),clear_hud, params)
ids.territory_current = player:hud_add({
hud_elem_type = "text",
position = {x = 1, y = 0.5},
offset = {x = -120, y = 0},
text = text_territory_current,
alignment = -1,
scale = { x = 50, y = 10},
number = 0xFFFFFF,
})
local params = {player_name,ids.territory_current}
minetest.after((ght+5),clear_hud, params)
local tst_yo = 0
for k,v in pairs(territory_score_table) do
tst_yo = tst_yo + 20
ids.territory_scores[k] = player:hud_add({
hud_elem_type = "text",
position = {x = 1, y = 0.5},
offset = {x = -120, y = tst_yo},
text = text_territory_scores[k],
alignment = -1,
scale = { x = 50, y = 10},
number = 0xFFFFFF,
})
local params = {player_name,ids.territory_scores[k]}
minetest.after(ght+5,clear_hud, params)
end
--minetest.after(ght,clear_hud, params)
else
if meta:get_int("hud_cleared") == 1 then
--hud_params_title()
ids.goblin_current = player:hud_add({
hud_elem_type = "text",
position = {x = 1, y = 0.5},
offset = {x = -120, y = -25},
text = text_goblin_current,
alignment = -1,
scale = { x = 50, y = 10},
number = 0xFFFFFF,
})
local params = {player_name,ids.goblin_current}
minetest.after(ght,clear_hud, params)
ids.territory_current = player:hud_add({
hud_elem_type = "text",
position = {x = 1, y = 0.5},
offset = {x = -120, y = 0},
text = text_territory_current,
alignment = -1,
scale = { x = 50, y = 10},
number = 0xFFFFFF,
})
local params = {player_name,ids.territory_current}
minetest.after(ght,clear_hud, params)
local tst_yo = 0
for k,v in pairs(territory_score_table) do
tst_yo = tst_yo + 20
ids.territory_scores[k] = player:hud_add({
hud_elem_type = "text",
position = {x = 1, y = 0.5},
offset = {x = -120, y = tst_yo},
text = text_territory_scores[k],
alignment = -1,
scale = { x = 50, y = 10},
number = 0xFFFFFF,
})
local params = {player_name,ids.territory_scores[k]}
minetest.after(ght,clear_hud, params)
end
else
local params = {player_name,ids.goblin_current}
player:hud_change(ids["goblin_current"], "text", text_goblin_current)
minetest.after(ght,clear_hud, params)
local params = {player_name,ids.territory_current}
player:hud_change(ids["territory_current"], "text", text_territory_current)
minetest.after(ght,clear_hud, params)
for k,v in pairs(territory_score_table) do
local params = {player_name,ids.territory_scores[k]}
minetest.after(ght,clear_hud, params)
player:hud_change(ids.territory_scores[k],"text", text_territory_scores[k])
end
--player:hud_change(ids["bar_foreground"],
--"scale", { x = percent, y = 1 })
end
end
meta:set_int("hud_cleared",0)
meta:set_int("hud_time_start",os.time())
--minetest.after(ght, print,"ONE" )
-- minetest.after(ght-1, print,"TWO" )
-- minetest.after(ght-2, print,"THREE")
end
end
minetest.register_on_joinplayer(goblins.update_hud)
minetest.register_on_leaveplayer(function(player)
saved_huds[player:get_player_name()] = nil
end)

View File

@ -0,0 +1,18 @@
-- optional hunger api mod: https://gitlab.com/4w/hunger_ng
if minetest.get_modpath("hunger_ng") then
print("Hunger NG mod detected, looking for API..")
if hunger_ng and hunger_ng.add_hunger_data then
print("Hunger NG mod API loaded")
local add = hunger_ng.add_hunger_data
add("goblins:mushroom_goblin", { satiates = 1.5 })
add("goblins:mushroom_goblin2", { satiates = 2.0 })
add("goblins:mushroom_goblin3", { satiates = 2.2 })
add("goblins:mushroom_goblin4", { satiates = 2.5 })
else
print(dump( minetest.get_modpath("hunger_ng") ))
print("Hunger NG mod API not found")
print("Please check latest version of Hunger NG at")
print("https://gitlab.com/4w/hunger_ng")
end
end

113
mods/mobs/goblins/init.lua Normal file
View File

@ -0,0 +1,113 @@
local path = minetest.get_modpath("goblins")
goblins_db = minetest.get_mod_storage()
goblins_db:set_string("goblins mod start time", os.date() )
--set namespace for goblins functions
goblins = {}
goblins.version = "202000712"
-- Strips any kind of escape codes (translation, colors) from a string
-- https://github.com/minetest/minetest/blob/53dd7819277c53954d1298dfffa5287c306db8d0/src/util/string.cpp#L777
function goblins.strip_escapes(input)
local s = function(idx) return input:sub(idx, idx) end
local out = ""
local i = 1
while i <= #input do
if s(i) == "\027" then -- escape sequence
i = i + 1
if s(i) == "(" then -- enclosed
i = i + 1
while i <= #input and s(i) ~= ")" do
if s(i) == "\\" then
i = i + 2
else
i = i + 1
end
end
end
else
out = out .. s(i)
end
i = i + 1
end
--print(("%q -> %q"):format(input, out))
return out
end
local function print_s(input)
print(goblins.strip_escapes(input))
end
local S = minetest.get_translator("goblins")
local goblins_version = goblins.version
-- create the table if it does not exist!
local goblins_db_fields = goblins_db:to_table()["fields"]
local function goblins_db_write(key, table)
local data = minetest.serialize(table)
goblins_db:set_string(key, data)
return key, data
end
if not goblins_db_fields["territories"] then
print("-------------\nWe must Initialize!\n-------------")
goblins_db_write("territories", {test = {version = goblins_version, encode = minetest.encode_base64(os.date()), created = os.date() }})
end
if not goblins_db_fields["relations"] then
print("-------------\nWe must Initialize!\n-------------")
goblins_db_write("relations", {test = {version = goblins_version, encode = minetest.encode_base64(os.date()), created = os.date() }})
end
--compatability with minimal game
if not default.LIGHT_MAX then
default.LIGHT_MAX = 14
LIGHT_MAX = default.LIGHT_MAX
end
minetest.log("action", "[MOD] goblins " ..goblins.version.. " is lowdings....")
print_s(S("Please report issues at https://github.com/FreeLikeGNU/goblins/issues "))
if mobs.version then
if tonumber(mobs.version) >= tonumber(20200516) then
print_s(S("Mobs Redo 20200516 or greater found!"))
else
print_s(S("You should find a more recent version of Mobs Redo!"))
print_s(S("https://notabug.org/TenPlus1/mobs_redo"))
end
else
print_s(S("This mod requires Mobs Redo version 2020516 or greater!"))
print_s(S("https://notabug.org/TenPlus1/mobs_redo"))
end
dofile(path .. "/utilities.lua")
dofile(path .. "/traps.lua")
dofile(path .. "/nodes.lua")
dofile(path .. "/items.lua")
dofile(path .. "/soundsets.lua")
dofile(path .. "/behaviors.lua")
dofile(path .. "/goblins_spawning.lua")
dofile(path .. "/animals.lua")
dofile(path .. "/goblins.lua")
dofile(path .. "/hunger.lua")
dofile(path .. "/goblins_custom.lua") --allow for additional/replacement goblins created by user
dofile(path .. "/hud.lua")
-------------
--ASSEMBLE THE GOBLIN HORDES!!!!
-------------
local gob_types = goblins.gob_types
local gobdog_types = goblins.gobdog_types
local goblin_template = goblins.goblin_template
local gobdog_template = goblins.gobdog_template
local gob_name_parts = goblins.gob_name_parts
local gob_words = goblins.words_desc
goblins.generate(gob_types,goblin_template)
goblins.generate(gobdog_types,gobdog_template)
local function ggn(gob_name_parts,rules)
return goblins.generate_name(gob_name_parts,rules)
end
print_s(S("This diversion is dedicated to the memory of @1 the @2, @3 the @4, and @5 the @6... May their hordes be mine!",
ggn(gob_name_parts),ggn(gob_words, {"tool_adj"}),ggn(gob_name_parts),ggn(gob_words, {"tool_adj"}),ggn(gob_name_parts),ggn(gob_words, {"tool_adj"})))
print_s(S(" --@1 of the @2 clan.",ggn(gob_name_parts),ggn(gob_name_parts,{"list_a","list_opt","-","list_b"})))

141
mods/mobs/goblins/items.lua Normal file
View File

@ -0,0 +1,141 @@
--more things to craft soon...
local craft_ingreds = {
mossycobble = "default:mossycobble",
}
for name, mat in pairs(craft_ingreds) do
minetest.register_craft({
output = "goblins:pick_".. name,
recipe = {
{mat, mat, mat},
{"", "group:stick", ""},
{"", "group:stick", ""}
}
})
end
minetest.register_tool("goblins:pick_mossycobble", {
description = "Mossycobble Pickaxe",
inventory_image = "default_tool_stonepick.png",
tool_capabilities = {
full_punch_interval = 1.2,
max_drop_level=0,
groupcaps={
cracky = {times={[2]=1.8, [3]=0.90}, uses=25, maxlevel=1},
},
damage_groups = {fleshy=3},
},
sound = {breaks = "default_tool_breaks"},
groups = {pickaxe = 1}
})
local goblin_tool = {}
goblin_tool = {
initial_properties = {
--physical = true,
pointable = false,
collisionbox = {0,0,0,0,0,0},
visual = "wielditem",
visual_size = {x = 0.15, y = 0.15},
wield_item = "default:stick",
},
message = "Default message",
on_step = function(self)
if not self.owner or not self.owner:get_luaentity() then
self.object:remove()
else
if self.owner:get_luaentity().current_tool ~= self.tool_name then
--print("removing old tool: "..self.tool_name.." for "..self.owner:get_luaentity().current_tool)
self.object:set_detach()
self.object:remove()
end
end
end
}
local function tool_check(tool)
if not minetest.registered_entities[tool] and
not minetest.registered_tools[tool] and
not minetest.registered_items[tool] and
not minetest.registered_craftitems[tool] and
not minetest.registered_nodes[tool] then
return
else
return true
end
end
local function tool_gen_engine(tool)
local tool_table = string.split(tool,":")
local tool_string = tool_table[1].."_"..tool_table[2]
--print("tool string 1: "..tool_string)
local gen_tool = table.copy(goblin_tool)
gen_tool.initial_properties.wield_item = tool
--print("goblin tool "..dump(goblin_tool))
--tool_string = "default_stick"
local ent_name = "goblins:goblin_tool_"..tool_string
--print("trying "..ent_name )
if not minetest.registered_entities[ent_name] then
minetest.register_entity(ent_name,gen_tool)
-- print("registered "..ent_name )
else
-- print("*** goblin tool: "..ent_name.." already registered! ***")
end
--return tool_string
end
function goblins.tool_gen(tool)
-- if not tool_check(tool) then
-- tool = "default:stick"
-- end
if type(tool) == "table" then
for k,v in pairs(tool) do
tool_gen_engine(v)
end
else
tool_gen_engine(tool)
end
end
local function tool_attach_engine(self,tool)
local tool_table = string.split(tool,":")
local tool_string = tool_table[1].."_"..tool_table[2]
--print("tool string 2: "..tool_string)
--print("testing attach: "..dump(tool_gen(tool)))
local tool_name = "goblins:goblin_tool_"..tool_string
--..tool_gen(tool)
--print("tool name:"..tool_name)
local item = minetest.add_entity(self.object:get_pos(), tool_name)
item:set_attach(self.object, "Arm_Right", {x=0.15, y=2.0, z=1.75}, {x=-90, y=180, z=90})
--print(dump(self.goblin_tools))
item:get_luaentity().owner = self.object
self.current_tool = tool
item:get_luaentity().tool_name = tool
end
local function wrandom(wtable)
local chance = #wtable
for _,__ in pairs(wtable) do
local test = chance * math.random(chance)
if chance == math.random(test) then
return wtable[chance]
else chance = chance - 1
end
end
end
function goblins.tool_attach(self,tool)
--print(dump(tool))
-- if not tool_check(tool) then
-- tool = "default:stick"
-- end
if type(tool) == "table" then
local rnd_tool = wrandom(tool)
-- print("attaching "..rnd_tool)
-- local rnd_tool = tool[math.random(1,#tool)]
tool_attach_engine(self,rnd_tool)
--print("attaching "..rnd_tool)
else
tool_attach_engine(self,tool)
end
end

View File

@ -0,0 +1,8 @@
name = goblins
descriptions = add goblins that dig tunnels, set traps and create lairs.
depends = default, mobs
optional_depends = ambience, hunger_ng
release = 4547
author = FreeLikeGNU
description = (Respectfully) Destructive! Goblin NPCs burrow underground, build lairs, set traps and cultivate foodstuffs. They like to steal torches! This is a Work In Progress, but quite playable!
title = Goblins

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

327
mods/mobs/goblins/nodes.lua Normal file
View File

@ -0,0 +1,327 @@
minetest.register_node(":default:mossycobble", {
description = "Mossy Cobblestone",
tiles = {"default_mossycobble.png"},
is_ground_content = false,
groups = {cracky = 3, stone = 1},
sounds = default.node_sound_stone_defaults({
footstep = {name = "goblins_mossycobble_footstep", gain = 0.4},
}),
paramtype = "light",
light_source = 0,
})
minetest.register_node("goblins:moss", {
description = "Moss",
inventory_image = "default_moss.png",
tiles = {
"default_moss.png^[colorize:#222222:150",
},
paramtype = "light",
drawtype = "normal",
walkable = true,
buildable_to = true,
is_ground_content = false,
groups = {crumbly = 3,slippery = 2, falling_node = 1, },
sounds = default.node_sound_dirt_defaults({
footstep = {name = "goblins_mossycobble_footstep", gain = 0.4},
}),
light_source = 4,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
--print("node meta:"..dump(meta))
end
})
minetest.register_node("goblins:mossx", {
description = "Moss",
inventory_image = "default_moss.png",
tiles = {
"default_moss.png^[colorize:#222222:150",
"default_moss.png^[colorize:#222222:150",
"default_moss.png^[colorize:#222222:150",
"default_moss.png^[colorize:#222222:150",
"default_moss.png^[colorize:#222222:150",
"default_moss.png^[colorize:#222222:150",
},
paramtype2="wallmounted",
paramtype = "light",
drawtype = "nodebox",
node_box = {
type = "wallmounted",
wall_bottom = {-0.5, -0.5, -0.5, 0.5, -0.45, 0.5}, -- bottom
wall_top = {-0.5, 0.45, -0.5, 0.5, 0.5, 0.5}, -- top
wall_side = {-0.45, -0.5, -0.5, -0.5, 0.5, 0.5} -- side
},
walkable = false,
buildable_to = true,
is_ground_content = false,
groups = {crumbly = 3,slippery = 2, falling_node = 1, attached_node = 1},
sounds = default.node_sound_dirt_defaults({
footstep = {name = "goblins_mossycobble_footstep", gain = 0.4},
}),
light_source = 2,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
--print("node meta:"..dump(meta))
end
})
minetest.register_node("goblins:mushroom_goblin", {
description = "gobble mushroom",
tiles = {"goblins_mushroom_brown.png^(default_grass_1.png^[opacity:150)"},
inventory_image = "goblins_mushroom_brown.png",
wield_image = "goblins_mushroom_brown.png",
drawtype = "firelike",
paramtype = "light",
paramtype2="wallmounted",
light_source = 2,
sunlight_propagates = true,
walkable = false,
buildable_to = true,
climbable = true,
floodable = true,
groups = {float = 1, mushroom = 1, food_mushroom = 1, snappy = 3, attached_node = 1, flammable = 1},
sounds = default.node_sound_leaves_defaults(),
on_use = minetest.item_eat(2),
selection_box = {
type = "fixed",
fixed = {-3 / 16, -0.5, -3 / 16, 3 / 16, -2 / 16, 3 / 16},
}
})
minetest.register_node("goblins:mushroom_goblin2", {
description = "gobble mushroom",
tiles = {"goblins_mushroom_brown2.png^(default_grass_1.png^[opacity:150)"},
inventory_image = "goblins_mushroom_brown2.png",
wield_image = "goblins_mushroom_brown.png",
drawtype = "firelike",
paramtype = "light",
paramtype2="wallmounted",
light_source = 2,
sunlight_propagates = true,
walkable = false,
buildable_to = true,
climbable = true,
floodable = true,
groups = {float = 1, mushroom = 1, food_mushroom = 2, snappy = 3, attached_node = 1, flammable = 1},
sounds = default.node_sound_leaves_defaults(),
on_use = minetest.item_eat(2),
selection_box = {
type = "fixed",
fixed = {-3 / 16, -0.5, -3 / 16, 3 / 16, -2 / 16, 3 / 16},
}
})
minetest.register_node("goblins:mushroom_goblin3", {
description = "gobble mushroom",
tiles = {"goblins_mushroom_brown3.png^(default_grass_1.png^[opacity:150)"},
inventory_image = "goblins_mushroom_brown3.png",
wield_image = "goblins_mushroom_brown.png",
drawtype = "firelike",
paramtype = "light",
paramtype2="wallmounted",
light_source = 2,
sunlight_propagates = true,
walkable = false,
buildable_to = true,
climbable = true,
floodable = true,
groups = {float = 1, mushroom = 1, food_mushroom = 3, snappy = 3, attached_node = 1, flammable = 1},
sounds = default.node_sound_leaves_defaults(),
on_use = minetest.item_eat(2),
selection_box = {
type = "fixed",
fixed = {-3 / 16, -0.5, -3 / 16, 3 / 16, -2 / 16, 3 / 16},
}
})
minetest.register_node("goblins:mushroom_goblin4", {
description = "gobble mushroom",
tiles = {"goblins_mushroom_brown4.png^(default_grass_1.png^[opacity:150)"},
inventory_image = "goblins_mushroom_brown4.png",
wield_image = "goblins_mushroom_brown.png",
drawtype = "firelike",
paramtype = "light",
paramtype2="wallmounted",
light_source = 2,
sunlight_propagates = true,
walkable = false,
buildable_to = true,
climbable = true,
floodable = true,
groups = {float = 1, mushroom = 1, food_mushroom = 4, snappy = 3, attached_node = 1, flammable = 1},
sounds = default.node_sound_leaves_defaults(),
on_use = minetest.item_eat(2),
selection_box = {
type = "fixed",
fixed = {-3 / 16, -0.5, -3 / 16, 3 / 16, -2 / 16, 3 / 16},
}
})
function goblins.moss_spread(pos, node)
if minetest.get_node_light(pos, 0.5) > 6 then
if minetest.get_node_light(pos, nil) == 15 then
minetest.remove_node(pos)
end
return
end
--print("moss spread abm"..minetest.pos_to_string(pos))
if math.random() < 0.1 then
minetest.place_node(pos,{name = "goblins:mushroom_goblin"})
else
local positions = minetest.find_nodes_in_area_under_air(
{x = pos.x - 1, y = pos.y - 2, z = pos.z - 1},
{x = pos.x + 1, y = pos.y + 1, z = pos.z + 1},
{"default:mossycobble","group:choppy"})
if #positions == 0 or pos.y > -10 then
return
end
local pos2 = positions[math.random(#positions)]
pos2.y = pos2.y + 1
if minetest.get_node_light(pos2, 0.5) <= 5 then
minetest.set_node(pos2, {name = node.name})
end
end
end
function goblins.mushroom_spread(pos, node)
if minetest.get_node_light(pos, 0.5) > 6 then
if minetest.get_node_light(pos, nil) == 15 then
minetest.remove_node(pos)
end
return
end
--print("mushroom spread abm"..minetest.pos_to_string(pos))
local positions = minetest.find_nodes_in_area_under_air(
{x = pos.x - 1, y = pos.y - 2, z = pos.z - 1},
{x = pos.x + 1, y = pos.y + 1, z = pos.z + 1},
{"default:mossycobble"})
if #positions == 0 or pos.y > -10 then
return
end
local pos2 = positions[math.random(#positions)]
pos2.y = pos2.y + 1
if minetest.get_node_light(pos2, 0.5) <= 5 then
minetest.set_node(pos2, {name = node.name})
end
end
minetest.register_abm({
label = "Moss spread",
nodenames = {"goblins:moss"},
interval = 50,
chance = 50,
action = function(...)
goblins.moss_spread(...)
end,
})
minetest.register_abm({
label = "Mushroom spread",
nodenames = {"goblins:mushroom_goblin","goblins:mushroom_goblin2","goblins:mushroom_goblin3","goblins:mushroom_goblin4"},
interval = 100,
chance = 100,
action = function(...)
goblins.mushroom_spread(...)
end,
})
minetest.register_node("goblins:goblins_goblin_bone", {
description = "gnawed bone",
tiles = {"goblins_goblin_bone.png"},
inventory_image = "goblins_goblin_bone.png",
visual_scale = 0.7,
drawtype = "plantlike",
wield_image = "goblins_goblin_bone.png",
paramtype = "light",
walkable = false,
is_ground_content = true,
sunlight_propagates = true,
selection_box = {
type = "fixed",
fixed = {-0.2, -0.5, -0.2, 0.2, 0, 0.2}
},
groups = {bone = 1, meat = 1, snappy = 2, fleshy =1, dig_immediate = 3},
after_place_node = function(pos, placer, itemstack)
if placer:is_player() then
minetest.set_node(pos, {name = "goblins:goblins_goblin_bone", param2 = 1})
end
end,
})
minetest.register_node("goblins:goblins_goblin_bone_meaty", {
description = "meaty bone",
tiles = {"goblins_goblin_bone_meaty.png"},
inventory_image = "goblins_goblin_bone_meaty.png",
visual_scale = 0.7,
drawtype = "plantlike",
wield_image = "goblins_goblin_bone_meaty.png",
paramtype = "light",
walkable = false,
is_ground_content = true,
sunlight_propagates = true,
selection_box = {
type = "fixed",
fixed = {-0.2, -0.5, -0.2, 0.2, 0, 0.2}
},
groups = {meat = 1, snappy = 2, fleshy =3, dig_immediate = 3},
after_place_node = function(pos, placer, itemstack)
if placer:is_player() then
minetest.set_node(pos, {name = "goblins:goblins_goblin_bone_meaty", param2 = 1})
end
end,
})
---from minetest default mod: nodes
minetest.register_node("goblins:dirt_with_bone", {
description = "Dirt with something buried",
tiles = {"default_dirt.png^default_stones.png",
{name = "default_dirt.png^goblins_goblin_bone.png",
tileable_vertical = false}},
groups = {crumbly = 3, soil = 1,
--not_in_creative_inventory = 1
},
drop = {
max_items = 1, -- Only one set of item will be dropped.
items = {
{
items = {"default:flint 1", "goblins:goblins_goblin_bone 2","default:stick"},
rarity = 2, -- has 1 chance over 2 to be picked
},
{items = {"goblins:goblins_goblin_bone"}}
}
},
sounds = default.node_sound_dirt_defaults({
footstep = {name = "default_grass_footstep", gain = 0.25},
}),
})
minetest.register_node("goblins:dirt_with_stuff", {
description = "Dirt with something shiny buried",
tiles = {"default_dirt.png^default_mineral_tin.png^default_stones.png",
{name = "default_dirt.png^default_mineral_tin.png^goblins_goblin_bone.png",
tileable_vertical = false}},
groups = {crumbly = 3, soil = 1,
--not_in_creative_inventory = 1
},
drop = {
max_items = 2, -- Only one set of item will be dropped.
items = {
{
items = {"default:pick_steel", "default:meselamp"},
rarity = 16, -- has 1 chance over 16 to be picked
},
{items = {"goblins:goblins_goblin_bone","default:shovel_steel"}}
}
},
sounds = default.node_sound_dirt_defaults({
footstep = {name = "default_grass_footstep", gain = 0.25},
}),
})

View File

@ -0,0 +1,35 @@
Goblins Relations and Territory storage
Relations are tables that are stored in the mobs self.relations as tables and in the mod data storage as serialized strings
these tables are currently meant to provide a way to show the relations of:
* Mobs to a territory (defined by both the mob and territory secret names generated on spawn)
* Players to a mob (defined by playername and a table of keys/value pairs describing relationship parameters)
In this way way can jump into the table using only the keys of the mob, player and relavent parameters to avoid searches and iterations
sample relation table from self.relations with "Unkadz" as "self.secret_name" and "Khad-glat" as "self.secret_territory" :
Unkadz = {
ix = 1588945576,
Unkadz = "Khad-glat",
freelikegnu = {
trade = 52
aggro = 10
}
}
in functions:
Relations should always be checked by the goblins.relations(self, target_name, target_table) function.
With only the "self" variable defined, this function will check if a relations root table has been established
and create one if not yet existing in both the mobs self.relations table and in the mod storage.
This function will then return all existing relations as recorded. Use this as a basis to further alter the relations if needed.
With both self and target_name declared (with target_name being that of a player or even the self.secret_name of a mob)
The function will do as above and initialize a relation between self and the target_name entity if none already exists
This function will then return only the existing relations between self and target_name as recorded. Use this as a basis to further alter the relations between two entitities if needed to only edit the keys from the target_name to the self.
Any actual manipulations of existing relationships should be handled by goblins.relations(self, target_name, target_table) with all arguements declared. This will overwrite any existing value of the target_table that may have been stored in the self.relations[target_name][target_table] , so be sure that you have gathered and altered the table before invoking this way!
spawning a mob will generate its name and territory if these are not already present

View File

@ -0,0 +1,42 @@
# goblins_hud_timer: time in seconds the goblins HUD will be visible following interactions (0 disables HUD)
goblins_hud_timer (goblins HUD timer for interactions) int 0
# goblins_aggro_on_wield: will goblins attack when a weapon is wielded?
goblins_aggro_on_wield (goblins attack when a weapon is wielded) bool true
# goblins_defend_groups: will goblins and gobdogs defend other mobs?
goblins_defend_groups (goblins and gobdogs defend other mobs) bool true
# trade shrewdness increases trade difficulty
goblins_trade_shrewdness (trade shrewdness increases trade difficulty) int 20
# use strict protection if node protection is activated (otherwise mobs can replace nodes they are close to)
goblins_node_protect_strict (use strict protection if node protection is activated) bool true
# utilities debug output announce_spawning
debug_goblins_announce_spawning (utilities debug output announce_spawning) bool false
# utilities debug output goblins_relations
debug_goblins_relations (utilities debug output goblins_relations) bool false
# utilities debug output goblins_secret
debug_goblins_secret (utilities debug output goblins_secret) bool false
# utilities debug output goblins_territories
debug_goblins_territories (utilities debug output goblins_territories) bool false
# utilities debug output goblins_territory_relations
debug_goblins_territory_relations (utilities debug output goblins_territory_relations) bool false
# utilities debug output goblins_trade_relations
debug_goblins_trade_relations (utilities debug output goblins_trade_relations) bool false
# behaviors debug output attack
debug_goblins_attack (behaviors debug output attack) bool false
# behaviors debug output find
debug_goblins_find (behaviors debug output find) bool false
# behaviors debug output replace
debug_goblins_replace (behaviors debug output replace) bool false
# debug output replace2
debug_goblins_replace2 (behaviors debug output replace2) bool false
# behaviors debug output search
debug_goblins_search (behaviors debug output search) bool false
# behaviors debug output tunneling
debug_goblins_tunneling (behaviors debug output tunneling) bool false
# behaviors debug output trade
debug_goblins_trade (behaviors debug output trade) bool false

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,47 @@
-- optional ambience api mod: https://notabug.org/TenPlus1/ambience
if minetest.get_modpath("ambience") then
print("Ambience Lite mod detected, looking for API..")
if ambience then
print("Ambience mod API loaded")
if ambience.get_set("cave") then
ambience.del_set("cave")
end
ambience.add_set("underground", {
frequency = 1000,
sounds = {
{name = "goblins_ambient_underground", length = 25}
},
sound_check = function(def)
--[[ local c = (def.totals["default:mossycobble"] or 0)
+ (def.totals["default:stone"] or 0)
if c > 5 then--]]
if def.pos.y < -15 then
return "underground"
end
end,
})
--Does not seem to be working as expected, but maybe someday:
ambience.add_set("mossycobble", {
frequency = 100,
sounds = {
{name = "goblins_ambient_drip", length = 11.4}
},
sound_check = function(def)
local c = (def.totals["default:mossycobble"] or 0)
if c > 10 and def.pos.y < -15 then
return "mossycobble"
end
end,
})
else
print(dump( minetest.get_modpath("ambience") ))
print("Ambience Lite mod API not found")
print("Please check latest version of Ambience Lite at")
print("https://notabug.org/TenPlus1/ambience")
end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

View File

@ -0,0 +1,5 @@
goblins_lightning https://openclipart.org/detail/36343/lightning-08
goblins_molten_gold* http://commons.wikimedia.org/wiki/File:Villarrica_lava_fountain.jpg and VanessaE (HDX Textures)
mushrooms.xcf Copyright Francisco Athens (CC BY-SA 3.0) 2020
goblins_mushroom_brown.png, goblins_mushroom_brown2.png goblins_mushroom_brown3.png goblins_mushroom_brown4.png Copyright Francisco Athens (CC BY-SA 3.0) 2020

Binary file not shown.

360
mods/mobs/goblins/traps.lua Normal file
View File

@ -0,0 +1,360 @@
--[[some nasty things goblins can do]]
-- Many thanks for AntumDeluge https://github.com/AntumDeluge for replacing the depcrecated methods!
--Super thanks to duane-r for his work: https://github.com/duane-r/mobs_goblins/blob/work/goblin_traps.lua
minetest.register_node("goblins:mossycobble_trap", {
description = "Messy Gobblestone",
tiles = {"default_mossycobble.png"},
is_ground_content = false,
groups = {cracky = 2, stone = 1},
sounds = default.node_sound_stone_defaults(),
paramtype = "light",
light_source = 3,
})
minetest.register_node("goblins:stone_with_coal_trap", {
description = "Foul Coal",
tiles = {"default_cobble.png^default_mineral_coal.png"},
groups = {cracky = 1, level = 2},
drop = 'default:coal_lump',
is_ground_content = false,
sounds = default.node_sound_stone_defaults(),
on_punch = function(pos, node, puncher)
if puncher:is_player() then
if math.random(0,100) < 10 then -- chance player will get hurt mining this
if puncher:get_hp() > 0 then
puncher:set_hp(puncher:get_hp()-1)
minetest.sound_play("goblins_goblin_trap", {pos = pos, gain = 0.5, max_hear_distance = 10})
end
end
end
end,
})
minetest.register_node("goblins:stone_with_iron_trap", {
description = "Iron Gore",
tiles = {"default_cobble.png^default_mineral_iron.png"},
groups = {cracky = 1, level = 2},
drop = 'default:iron_lump',
is_ground_content = false,
sounds = default.node_sound_stone_defaults(),
on_punch = function(pos, node, puncher)
if puncher:is_player() then
if math.random(0,100) < 25 then -- chance player will get hurt mining this
if puncher:get_hp() > 0 then
puncher:set_hp(puncher:get_hp()-1)
minetest.sound_play("goblins_goblin_trap", {pos = pos, gain = 0.5, max_hear_distance = 10})
end
end
end
end,
})
minetest.register_node("goblins:stone_with_copper_trap", {
description = "Gobber Ore",
tiles = {"default_cobble.png^default_mineral_copper.png"},
groups = {cracky = 1, level = 2},
drop = 'default:copper_lump',
is_ground_content = false,
sounds = default.node_sound_stone_defaults(),
on_punch = function(pos, node, puncher)
if puncher:is_player() then
if math.random(0,100) < 50 then -- chance player will get hurt mining this
if puncher:get_hp() > 0 then
puncher:set_hp(puncher:get_hp()-1)
minetest.sound_play("goblins_goblin_trap", {pos = pos, gain = 0.5, max_hear_distance = 10})
end
end
end
end,
})
minetest.register_node("goblins:stone_with_gold_trap", {
description = "Gold Shinies",
tiles = {"default_cobble.png^default_mineral_gold.png"},
groups = {cracky = 1,level = 2},
drop = 'default:gold_lump',
is_ground_content = false,
sounds = default.node_sound_stone_defaults(),
on_punch = function(pos, node, puncher)
if puncher:is_player() then
if math.random(0,100) < 50 then -- chance player will get hurt mining this
if puncher:get_hp() > 0 then
puncher:set_hp(puncher:get_hp()-1)
minetest.sound_play("goblins_goblin_trap", {pos = pos, gain = 0.5, max_hear_distance = 10})
end
end
end
end,
})
minetest.register_node("goblins:stone_with_diamond_trap", {
description = "Pretty Diamonds",
tiles = {"default_cobble.png^default_mineral_diamond.png"},
groups = {cracky = 1, level = 3},
drop = 'default:diamond',
is_ground_content = false,
sounds = default.node_sound_stone_defaults(),
on_punch = function(pos, node, puncher)
if puncher:is_player() then
if math.random(0,100) < 75 then -- chance player will get hurt mining this
if puncher:get_hp() > 0 then
puncher:set_hp(puncher:get_hp()-1)
minetest.sound_play("goblins_goblin_trap", {pos = pos, gain = 0.5, max_hear_distance = 10})
end
end
end
end,
})
minetest.register_node("goblins:molten_gold_source", {
description = "Molten Gold Source",
inventory_image = minetest.inventorycube("default_lava.png"),
drawtype = "liquid",
tiles = {
{
name = "goblins_molten_gold_source_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 3.0,
},
},
},
special_tiles = {
-- New-style lava source material (mostly unused)
{
name = "goblins_molten_gold_source_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 3.0,
},
backface_culling = false,
},
},
paramtype = "light",
light_source = default.LIGHT_MAX - 1,
walkable = false,
pointable = false,
diggable = false,
buildable_to = true,
is_ground_content = false,
drop = "",
drowning = 1,
liquidtype = "source",
liquid_alternative_flowing = "goblins:molten_gold_flowing",
liquid_alternative_source = "goblins:molten_gold_source",
liquid_viscosity = 7,
liquid_renewable = false,
liquid_range = 3,
damage_per_second = 4 * 2,
post_effect_color = {a=192, r=255, g=64, b=0},
groups = {lava=3, liquid=2, hot=3, igniter=1},
})
minetest.register_node("goblins:molten_gold_flowing", {
description = "Flowing Molten Gold",
inventory_image = minetest.inventorycube("default_lava.png"),
drawtype = "flowingliquid",
tiles = {"default_lava.png"},
special_tiles = {
{
name = "goblins_molten_gold_flowing_animated.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 3.3,
},
},
{
name = "goblins_molten_gold_flowing_animated.png",
backface_culling = true,
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 3.3,
},
},
},
paramtype = "light",
paramtype2 = "flowingliquid",
light_source = default.LIGHT_MAX - 1,
walkable = false,
pointable = false,
diggable = false,
buildable_to = true,
is_ground_content = false,
drop = "",
drowning = 1,
liquidtype = "flowing",
liquid_alternative_flowing = "goblins:molten_gold_flowing",
liquid_alternative_source = "goblins:molten_gold_source",
liquid_viscosity = 7,
liquid_renewable = false,
liquid_range = 3,
damage_per_second = 4 * 2,
post_effect_color = {a=192, r=255, g=64, b=0},
groups = {lava=3, liquid=2, hot=3, igniter=1, not_in_creative_inventory=1},
})
--[[ too bad we can't keep track of what physics are set too by other mods...]]
minetest.register_abm({
nodenames = {"goblins:mossycobble_trap"},
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
for _,object in ipairs(minetest.get_objects_inside_radius(pos, 0.95)) do -- IDKWTF this is but it works
if object:is_player() then
object:set_physics_override({speed = 0.1})
minetest.after(1, function() -- this effect is temporary
object:set_physics_override({speed = 1}) -- we'll just set it to 1 and be done.
end)
end
end
end})
minetest.register_abm({
nodenames = {"goblins:stone_with_coal_trap"},
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
for _,object in ipairs(minetest.get_objects_inside_radius(pos, 3)) do
if object:is_player() then
minetest.set_node(pos, {name="fire:basic_flame"})
if object:get_hp() > 0 then
object:set_hp(object:get_hp()-2)
minetest.sound_play("default_dig_crumbly", {pos = pos, gain = 0.5, max_hear_distance = 10})
minetest.after(6, function() --this hell ends after a few seconds
minetest.set_node(pos, {name = "air"})
end)
end
end
end
end})
-- summon a metallic goblin?
-- pit of iron razors?
minetest.register_abm({
nodenames = {"goblins:stone_with_iron_trap"},
interval = 2,
chance = 2, --this may be a dud
action = function(pos, node, active_object_count, active_object_count_wider)
for _,object in ipairs(minetest.get_objects_inside_radius(pos, 2)) do
if object:is_player() then
if object:get_hp() > 0 then
object:set_hp(object:get_hp()-1)
minetest.sound_play("goblins_goblin_trap", {pos = pos, gain = 0.5, max_hear_distance = 10})
end
end
end
end})
local function lightning_effects(pos, radius)
minetest.add_particlespawner({
amount = 30,
time = 1,
minpos = vector.subtract(pos, radius / 2),
maxpos = vector.add(pos, radius / 2),
minvel = {x=-10, y=-10, z=-10},
maxvel = {x=10, y=10, z=10},
minacc = vector.new(),
maxacc = vector.new(),
minexptime = 1,
maxexptime = 3,
minsize = 16,
maxsize = 32,
texture = "goblins_lightning.png",
})
end
--[[ based on dwarves cactus]]
minetest.register_abm({
nodenames = {"goblins:stone_with_copper_trap"},
interval = 1,
chance = 2,
action = function(pos, node, active_object_count, active_object_count_wider)
for _,object in ipairs(minetest.get_objects_inside_radius(pos, 3)) do
if object:is_player() then
if object:get_hp() > 0 then
object:set_hp(object:get_hp()-1)
-- sprite
lightning_effects(pos, 3)
minetest.sound_play("goblins_goblin_trap", {pos = pos, gain = 0.5, max_hear_distance = 10})
end
end
end
end})
minetest.register_abm({
nodenames = {"goblins:stone_with_gold_trap"},
interval = 1,
chance = 2,
action = function(pos, node, active_object_count, active_object_count_wider)
for _,object in ipairs(minetest.get_objects_inside_radius(pos, 2)) do
if object:is_player() then
minetest.set_node(pos, {name="goblins:molten_gold_source"})
if object:get_hp() > 0 then
object:set_hp(object:get_hp()-2)
minetest.sound_play("default_dig_crumbly", {pos = pos, gain = 0.5, max_hear_distance = 10})
minetest.after(6, function() --this hell ends after a few seconds
minetest.set_node(pos, {name = "air"})
end)
end
end
end
end})
local setting = minetest.settings:get_bool("enable_tnt")
if setting == true then
print("enable_tnt = true")
else
print("enable_tnt ~= true")
end
if ( minetest.is_singleplayer() ~= true and setting ~= true) or (minetest.is_singleplayer() == true and setting == false) then
-- wimpier trap for non-tnt settings
minetest.register_abm({
nodenames = {"goblins:stone_with_diamond_trap"},
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
for _,object in ipairs(minetest.get_objects_inside_radius(pos, 3)) do
if object:is_player() then
minetest.set_node(pos, {name="default:lava_source"})
if object:get_hp() > 0 then
object:set_hp(object:get_hp()-2)
minetest.sound_play("default_dig_crumbly", {pos = pos, gain = 0.5, max_hear_distance = 10})
minetest.after(6, function() --this hell ends after a few seconds
minetest.set_node(pos, {name = "air"})
end)
end
end
end
end})
else
-- 5... 4... 3... 2... 1...
minetest.register_abm({
nodenames = {"goblins:stone_with_diamond_trap"},
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
for _,object in ipairs(minetest.get_objects_inside_radius(pos, 3)) do
if object:is_player() then
minetest.set_node(pos, {name="tnt:tnt_burning"})
minetest.get_node_timer(pos):start(5)
minetest.sound_play("tnt_ignite", {pos = pos, gain = 0.5, max_hear_distance = 10})
end
end
end})
end

View File

@ -0,0 +1,451 @@
local debug_goblins_announce_spawning = minetest.settings:get_bool("debug_goblins_announce_spawning") or false
local debug_goblins_relations = minetest.settings:get_bool("debug_goblins_relations") or false
local debug_goblins_secret = minetest.settings:get_bool("debug_goblins_secret") or false
local debug_goblins_territories = minetest.settings:get_bool("debug_goblins_territories") or false
local debug_goblins_territory_relations = minetest.settings:get_bool("debug_goblins_territory_relations") or false
local debug_goblins_trade_relations = minetest.settings:get_bool("debug_goblins_trade_relations") or false
local announce_spawning = debug_goblins_announce_spawning
local S = minetest.get_translator("goblins")
local function print_s(input)
print(goblins.strip_escapes(input))
end
function goblins.timer(target,timer_name,timeout)
local start = os.time()
local t_name = timer_name
if not t_name then t_name = default end
if target and minetest.is_player(target) then
local player = target
local meta = player:get_meta()
local timer = "timer_"..t_name
if not meta:get_int(timer) then
meta:set_int(timer,start)
elseif meta:get_int(timer) and
os.time() > (meta:get_int(timer) + timeout) then
meta:set_int(timer,0)
return timeout
end
end
end
goblins.gob_name_parts = {
list_a = "Ach Adz Ak Ark Az Balg Bilg Blid Blig Blok Blot Bolg Boor Bot Bug Burk Chu Dokh Drik Driz Drub Duf Flug Gaw Gad Gag Gah Gak Gar Gat Gaz Ghag Ghak Ghor Git Glag Glak Glat Glig Gliz Glok Gnat Gog Grak Grat Guk Hig Irk Kak Kav Khad Krig Lag Lak Lig Likk Loz Luk Lun Mak Maz Miz Mog Mub Mur Nad Nag Naz Nilg Nikk Nogg Nok Nukk Nur Pog Rag Rak Rat Rok Ronk Rot Shrig Shuk Skrag Skug Slai Slig Slog Sna Snag Snark Snat Snig Snik Snit Sog Spik Stogg Tog Unk Urf Vark Vog Yad Yagg Yak Yark Yarp Yig Yip Zat Zib Zit Ziz Zob Zord",
list_b = "ach adz ak ark awg az balg bilg blid blig blok blot bolg bot bug burk bus dokh drik driz duf ffy flug g ga gad gag gah gak gar gat gaz ghag ghak git glag glak glat glig gliz glok gnat gog grak grat gub guk hig irk kak khad krig lag lak lig likk loz luk mak maz miz mub murch nad nag naz nilg nikk nogg nok nukk og plus rag rak rat rkus rok shrig shuk skrag skug slai slig slog sna snag snark snat snig snik snit sog spik stogg thus tog un urf us vark yad yagg yak yark yarp yig yip zat zib zit ziz",
list_opt = "ah ay e ee gah ghy y ya"
}
--need to find more goblinly-sounding words than these..
goblins.words_desc = {
tool_adj = S("bent broken crusty dirty dull favorite gnarly grubbly happy moldy pointy ragged rusty sick sharp slimy trusty"),
motiv_adj = S("quickly quietly slowly stealthily"),
verbs = S("eats drinks hears moves feels smells sees")
}
local gob_name_parts = goblins.gob_name_parts
--- This can build all the mobs in our mod.
-- @gob_types is a table with the key used to build the subtype with values that are unique to that subtype
-- @goblin_template is the table with all params that a mob type would have defined
function goblins.generate(gob_types,goblin_template)
for k, v in pairs(gob_types) do
-- we need to get a fresh template to modify for every type or we get some carryover values:-P
local g_template = table.copy(goblin_template)
-- g_type should be different every time so no need to freshen
local g_type = v
for x, y in pairs(g_type) do
-- print_s("found template modifiers " ..dump(x).." = "..dump(y))
g_template[x] = g_type[x]
end
print_s("Assembling the "..g_template.description..":")
if g_template.lore then print_s(" "..g_template.lore) end
if g_template.additional_properties and g_template.additional_properties.goblin_tools then
--print("found in template:"..g_template.goblin_tool)
goblins.tool_gen(g_template.additional_properties.goblin_tools)
end
--print_s("resulting template: " ..dump(g_template))
mobs:register_mob("goblins:goblin_"..k, g_template)
mobs:register_egg("goblins:goblin_"..k, S("@1 Egg",g_template.description),"default_mossycobble.png", 1)
g_template.spawning.name = "goblins:goblin_"..k --spawn in the name of the key!
mobs:spawn(g_template.spawning)
if g_template.additional_properties then
for x,y in pairs(g_template.additional_properties) do
minetest.registered_entities["goblins:goblin_"..k][x] = y
end
end
--print(dump(minetest.registered_entities["goblins:goblin_"..k]))
g_template = {}
end
end
--- Our mobs, territories, etc can have randomly generated names.
-- @name_parts is the name parts table: {list_a = "foo bar baz"}
-- @rules are the list table key names in order of how they will be chosen
-- "-" and "\'" are rules that can be used to add a hyphen or apostrophe respectively
function goblins.generate_name(name_parts, rules)
-- print_s("generating name")
local name_arrays = {}
local r_parts = {}
local generated_name = {}
for k,v in pairs(name_parts) do
-- name_arrays.k = mysplit(v)
name_arrays.k = string.split(v," ")
-- print_s(dump(name_arrays.k))
r_parts[k] = k
r_parts[k] = name_arrays.k[math.random(1,#name_arrays.k)]
end
--local r_parts.k = name_arrays.k[math.random(1,#name_arrays.k)] did not work
--print_s(name_a)
if r_parts.list_opt and math.random() <= 0.5 then r_parts.list_opt = "" end
--print_s(r_parts.list_a..r_parts.list_b..r_parts.list_opt)
if rules then
--print_s(dump(rules))
local gen_name = ""
for i, v in ipairs(rules) do
if v == "-" then
gen_name = gen_name.."-"
elseif v == "\'" then
gen_name = gen_name.."\'"
else
gen_name = gen_name..r_parts[v]
end
end
generated_name = gen_name
--print_s(dump(generated_name))
return generated_name
else
generated_name = r_parts.list_a..r_parts.list_b..r_parts.list_opt
return generated_name
end
end
local function territory_list_update(player_name, territory_name)
local known_territories = {}
local recorded = false
local player = minetest.get_player_by_name(player_name)
local meta = player:get_meta()
known_territories = minetest.deserialize(meta:get_string("territory_list"))
if type(known_territories) == table then
for k,v in known_territories do
if territory_name == v then
recorded = true
end
end
else
known_territories = {}
--known_territories[1] = territory_name
end
if not recorded then
table.insert(known_territories,territory_name)
meta:set_string("territory_list", minetest.serialize(known_territories))
end
goblins.update_hud(player)
end
--- This will store the name of a player that learns the mobs territory in the mobs table.
function goblins.secret_territory(self, player_name, tell)
local pname = player_name
--self.nametag = self.secret_name.." of "..self.secret_territory.name
if not self.secret_territory_told then
self.secret_territory_told = {ix = os.time()}
end
if self.secret_territory_told[pname] then return self.secret_territory end
if not self.secret_territory_told[pname] and tell then
minetest.chat_send_player(pname,
S(" You have learned the secret territory name of @1!!",dump(self.secret_territory.name)))
self.secret_territory_told[pname] = os.time()
---self.nametag = self.secret_name.." of "..self.secret_territory.name
---player could also receive some kind of functional token for this territory
territory_list_update(pname, self.secret_territory.name)
return self.secret_territory
end
if debug_goblins_secret then
for k,v in pairs(self.secret_territory_told) do
print_s(self.secret_name.." revealed secret territories to: "..k.." "..v)
end
end
end
--- This will store the name of a player that learns the mobs name in the mobs table.
function goblins.secret_name(self, player_name,tell)
-- self.nametag = self.secret_name
local pname = player_name
if not self.secret_name_told then
self.secret_name_told = {[self.secret_name] = os.time()}
end
if self.secret_name_told[pname] then return self.secret_name end
if not self.secret_name_told[pname] and tell then
--The goblin is willing to share something special!
minetest.chat_send_player(pname,
S(" You have learned the secret name of @1!!",self.secret_name))
self.secret_name_told[pname] = os.time()
--self.nametag = self.secret_name
return self.secret_name
end
if debug_goblins_secret then
for k,v in pairs(self.secret_name_told) do
print_s(self.secret_name.." revealed secret name to: "..k.." "..v)
end
end
end
------------
-- Announce Spawn ...summon it with care, for the floods shall come!
-----------
-- Purely for debugging or curiosity it can be enabled at the top of this page
function goblins.announce_spawn(self)
if announce_spawning == true then
local pos = vector.round(self.object:getpos())
if not pos then return end
if self.secret_name then
print_s( self.name:split(":")[2].. ", "..self.secret_name.." spawned at: " .. minetest.pos_to_string(pos))
else
print_s( self.name:split(":")[2].. " spawned at: " .. minetest.pos_to_string(pos))
end
--goblins.territory(pos)
if self.secret_territory then
if self.secret_name then
print_s(self.secret_name.. " dwells in "..self.secret_territory["name"].." at " ..self.secret_territory["vol"].."!\n" )
else
print_s("A nameless creature inhabits"..self.secret_territory["name"].." at " ..self.secret_territory["vol"].."!\n" )
end
else
local territory = {goblins.territory(pos)}
if self.secret_name then
print_s(territory[1].." at "..territory[2].." festers with the lurking form of "..self.secret_name.."\n")
else
print_s(territory[1].." at "..territory[2].." becomses the domain of a nameless one\n")
end
end
end
end
-----
-- CREATE TERRITORIES
-----
-- Get the Minimum of a Chunk https://forum.minetest.net/viewtopic.php?p=351592#p351592
-- by duane » Sun Jul 14, 2019 00:20 and
-- by TalkLounge » Sun Jul 14, 2019 11:39
local function mapgen_min_max(pos)
local pos = vector.round(pos)
local chunksize = tonumber(type(minetest.settings) ~= "nil" and minetest.settings:get("chunksize") or minetest.setting_get("chunksize")) or 5
local chunk_offset = math.floor(chunksize / 2) * 16
local csize = {x = chunksize * 16, y = chunksize * 16, z = chunksize * 16}
local chunk = vector.floor(vector.divide(vector.add(pos, chunk_offset), csize))
local minp = vector.add(vector.multiply(chunk, 80), -chunk_offset)
local maxp = vector.add(minp, (chunksize * 16) - 1)
return minp, maxp
end
-- refer to https://rubenwardy.com/minetest_modding_book/en/map/storage.html
local goblins_db_fields = goblins_db:to_table()["fields"]
local function goblins_db_deser(table)
local data = minetest.deserialize(goblins_db_fields[table])
return data
end
local function goblins_db_read(table)
local data = minetest.deserialize(goblins_db:to_table()["fields"][table])
return data
end
local function goblins_db_write(key, table)
local data = minetest.serialize(table)
goblins_db:set_string(key, data)
return key, data
end
function goblins.territory_test(pos,territories)
local db_test = minetest.serialize({fieldtest = "initialized"})
print_s("\nBEGIN EXISTING LIST--------\n"..dump(goblins_db_read("territories")).."\n ------END LIST\n")
end --test
-- Provides a way to take a chunks position and use it a base for storing information in a mod.
-- it is dependant on the mapgen_min_max function above as well the goblins_db functions for storage.
-- @opt_data is just for adding information to this territories storage it expects a table
function goblins.territory(pos, opt_data)
-- this should be called on spawn but before a goblin gets its secret name
-- a handy chunk name key generator, should create a unique name for every chunk
local function cat_pos(chunk)
return "Xa"..chunk[1].x.."_Ya"..chunk[1].y.."_Za"..chunk[1].z.."_x_Xb"..chunk[2].x.."_Yb"..chunk[2].y.."_Zb"..chunk[2].z
end
-- get list of known territories and thier chunks or
local existing_territories = {}
existing_territories = goblins_db_read("territories")
local minp_maxp = {mapgen_min_max(pos)}
local this_territory = {}
local volume_cat_pos = cat_pos(minp_maxp)
--print_s(dump(volume_cat_pos).." cat paws!")
if debug_goblins_territories then
print_s("\n----Known territories")
for k,v in pairs(existing_territories) do
print_s(dump(k).." is known as "..dump(existing_territories[k].name))
end
print_s("----End Known territories\n")
end
local territories_table = table.copy(existing_territories)
-- print_s(dump(territories_table).."copied territories")
-- print_s("\nTERRITORY TEST TABLE READ:\n" ..dump(goblins_db_read("territories")).."\n")
if territories_table[volume_cat_pos] then
local t_vol = volume_cat_pos
local t_name = territories_table[volume_cat_pos]["name"]
if opt_data then --insert a table
for k,v in pairs(opt_data) do
--if not territories_table[volume_cat_pos][k] -- this is tricky...
territories_table[volume_cat_pos][k] = v
local territories_table_ser = minetest.serialize(territories_table)
goblins_db:set_string("territories", territories_table_ser )
if debug_goblins_territories then
print_s(k.." added to "..territories_table[volume_cat_pos]["name"])
end
--end
end
return t_name, t_vol
end
if debug_goblins_territories then
print_s(dump(t_name).." at "..dump(t_vol).." is already known!")
end
-- print_s(dump(territories_table[volume_cat_pos]).. "\n ---end details \n")
return t_name, t_vol
else
-- generate a name for this territory
local name_rules = {"list_a","list_opt","-","list_b"}
local territory_name = goblins.generate_name(gob_name_parts,name_rules)
-- print_s(dump(territory_name).." is a name whispered among those who dwell here")
-- set concatenated minp_maxp as the key for this territory and populate data
-- print_s(dump(volume_cat_pos)))
this_territory[(volume_cat_pos)] = {
["name"] = territory_name,
["flag"] = pos,
}
if debug_goblins_territories then
print_s("The territory of "..dump(this_territory[volume_cat_pos]["name"]).. " at "..volume_cat_pos.." will be recorded.")
end
territories_table[volume_cat_pos] = this_territory[volume_cat_pos]
-- print_s(dump(territories_table).. " is the new table \n")
-- print_s("\nTERRITORY TESTING SERIALIZED WRITE:\n"..dump(this_territory_ser).."\n")
-- prepare the territories_table for storage, unless we have something else to say..
if opt_data then --insert a table
for k,v in pairs(opt_data) do
--if not territories_table[volume_cat_pos][k] -- this is tricky...
territories_table[volume_cat_pos][k] = v
local territories_table_ser = minetest.serialize(territories_table)
goblins_db:set_string("territories", territories_table_ser )
if debug_goblins_territories then
print_s(k.." added to "..territories_table[volume_cat_pos]["name"])
end
--end
end
local t_name = territory_name
local t_vol = volume_cat_pos
return t_name, t_vol
end
local territories_table_ser = minetest.serialize(territories_table)
goblins_db:set_string("territories", territories_table_ser )
local t_name = territory_name
local t_vol = volume_cat_pos
return t_name, t_vol
end
--print_s("\nTERRITORY TEST:\n"..dump(this_territory.name).."\n")
end
-- Express and optionally store the relationship between a mob and a player or another mob.
-- Will return all known relationships if nothing is defined.
-- @self will return only relations know to that mob
-- @target_name will return the table of the self.relations (mobs) relations to the target if
-- optional target_table is not defined.
-- @target_table will set the value of a mobs relation to the target.
-- THIS SCRIPT IS DEPENDANT on secret_name and secret_territory!
function goblins.relations(self, target_name, target_table)
local existing_relations = {}
if not goblins_db_read("relations") then
print_s("relations DB not initialized from init.lua!!")
return
end
-- let's get all the facts, this query may have to get more specific if its too big...
local existing_relations = goblins_db_read("relations")
-- do we want to know something in particular?
if self then
local name = self.secret_name
--have we started keeping track of who we know?
if not self["relations"] then
self.relations = {ix = os.time()}
if debug_goblins_relations then print_s("self table: "..dump(self)) end
-- create an entry for ourselves with our territory as the value
if self.secret_name and self.secret_territory then
self.relations[name] = self.secret_territory.name
existing_relations[name] = self.relations
if debug_goblins_relations then print_s("self table updated: "..dump(self.relations).."\n") end
if debug_goblins_relations then print_s("adding mob to relations table: "..dump(existing_relations[self]).."\n")end
goblins_db_write("relations",existing_relations)
end
end
-- do we just want to know how we feel about the target?
if target_name and not target_table then
-- do we even know the target? If not, initialize relationship root for target
if not self.relations[target_name] then
self.relations[target_name] = {ix = os.time()}
existing_relations[name] = self.relations
goblins_db_write("relations",existing_relations)
end
return self.relations[target_name]
end
-- we have something to say about the target!
if target_name and target_table then
-- mob adds it to their entity..
for k,v in pairs(target_table) do
self.relations[target_name][k] = v
end
existing_relations[name] = self.relations
-- we add or modify this relationship in the mod storage "relations"
--existing_relations[name][target_name] = target_value
--print_s(dump(existing_relations))
goblins_db_write("relations",existing_relations)
if debug_goblins_relations then print_s("updated self table: "..dump(self.relations).."\n") end
return existing_relations[target_name]
end
end
-- we dont have a target just dump everything known about everone
if debug_goblins_relations then print_s("all relations"..dump(existing_relations).."\n") end
return existing_relations
end
--- Returns the status of a players relation throughout a territory
-- will initialize any new relation if required
-- at some point to support an array of relations to return
function goblins.relations_territory(self, player_name, rel_name)
local pname = player_name
local relations = goblins.relations(self)
local t_relation = 0
-- initialize tables if necessary
if not self["relations"] then self.relations = {ix = os.time()} end
if not self.relations[pname] then goblins.relations(self, pname) end
if not self.relations[pname][rel_name] then self.relations[pname][rel_name] = 0 end
--be sure that relations have been started with player before using this!
if debug_goblins_trade_relations then print_s(S("Individual mob trade relations: ")) end
for m_name,prop in pairs(relations) do
if self.secret_territory.name == relations[m_name][m_name] and
relations[m_name][pname] and relations[m_name][pname][rel_name] then
--add up the trade relations between the player and all goblins in this goblins territory
t_relation = t_relation + relations[m_name][pname][rel_name]
if debug_goblins_trade_relations then print_s(S("@1 = @2",m_name,relations[m_name][pname][rel_name]))end
end
end
if debug_goblins_territory_relations then
print_s("this mob's relation are "..dump(relations[self.secret_name]))
print_s(S("@1 territory @2 relation score = @3",self.secret_territory.name,rel_name,t_relation))
end
return t_relation
end
function goblins.player_relations_territory(player,territory)
end