mobs_humans/init.lua
2018-07-27 22:31:01 +02:00

759 lines
16 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--[[
Mobs Humans - Adds human mobs.
Copyright (C) 2018 Hamlet
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
--]]
--
-- General variables
--
local mob_difficulty = tonumber(minetest.settings:get("mob_difficulty"))
if (mob_difficulty == nil) then
mob_difficulty = 1
end
--
-- Custom bones node
--
minetest.register_node("mobs_humans:human_bones", {
description = "Human Bones",
drawtype = "mesh",
mesh = "mobs_humans_bones_model.obj",
tiles = {"mobs_humans_bones.png"},
inventory_image = "mobs_humans_bones_inv.png",
wield_image = "mobs_humans_bones_inv.png",
selection_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0.0, 0.5}
},
},
walkable = false,
pointable = true,
diggable = true,
buildable_to = true,
paramtype = "light",
paramtype2 = "facedir",
groups = {dig_immediate = 2, falling_node = 1},
sounds = default.node_sound_gravel_defaults(),
on_construct = function(pos)
minetest.check_single_for_falling(pos)
minetest.get_node_timer(pos):start(math.random(300, 600))
end,
on_timer = function(pos, elapsed)
minetest.get_node_timer(pos):stop()
minetest.swap_node(pos, {name = "air"})
end
})
--
-- Chat messages
--
local MESSAGE_1 = "Saluton "
local MESSAGE_2 = ", mia nomo estas "
--
-- Functions
--
local function boolean()
if (math.random(0, 1) == 0) then
return false
else
return true
end
end
local function attack_type()
local number = math.random(1, 3)
local attack_name = nil
if (number == 1) then
attack_name = "dogfight"
elseif (number == 2) then
attack_name = "shoot"
else
attack_name = "dogshoot"
end
return attack_name
end
local function dps(self, element)
local damage_speed = nil
local hit_points = nil
local time_speed = nil
local in_game_day_length = nil
local five_in_game_minutes = nil
local damage_per_second = nil
hit_points = self.health
time_speed = tonumber(minetest.settings:get("time_speed"))
if (time_speed == nil) then
time_speed = 72
elseif (time_speed == 0) then
time_speed = 1
end
if (element == "water") then
damage_speed = 300
elseif (element == "lava") then
damage_speed = 100
end
in_game_day_length = 86400 / time_speed
five_in_game_minutes = (in_game_day_length * damage_speed) / 86400
damage_per_second = hit_points / five_in_game_minutes
return damage_per_second
end
local function experience(self)
if (self.attack == nil) and (self.engaged ~= true) then
return
elseif (self.attack ~= nil) and (self.engaged ~= true) then
self.engaged = true
self.object:set_properties({engaged = self.engaged})
elseif (self.attack == nil) and (self.engaged == true) then
self.engaged = false
self.object:set_properties({engaged = self.engaged})
if (self.damage < (8 * mob_difficulty)) then
self.damage = self.damage + 1
self.object:set_properties({damage = self.damage})
end
if (self.armor > 10) then
self.armor = self.armor - 1
self.object:set_properties({armor = self.armor})
end
end
end
local function heal_over_time(self, dtime)
-- For backward compatibility
if (self.heal_counter == nil)
or (self.initial_hp == nil)
or (self.injuried == nil)
then
self.heal_counter = 4.0
-- used for health recovery
self.initial_hp = math.random(self.hp_min, self.hp_max)
-- used as reference when recovering health
-- used to determine whether if in need of healing
if (self.health == self.initial_hp) then
self.injuried = false
else
self.injuried = true
end
self.object:set_properties({
heal_counter = self.heal_counter,
initial_hp = self.initial_hp,
injuried = self.injuried
})
end
if (self.injuried == true) then
if (self.state ~= "attack") and (self.state ~= "runaway") then
-- recover 1HP every 4 seconds
if (self.health < self.initial_hp) then
if (self.heal_counter > 0) then
self.heal_counter = self.heal_counter - dtime
self.object:set_properties({
heal_counter = self.heal_counter
})
else
self.heal_counter = 4.0
self.health = self.health + 1
self.object:set_hp(self.health)
self.object:set_properties({
heal_counter = self.heal_counter
})
end
elseif (self.health == self.initial_hp) then
self.injuried = false
self.object:set_properties({injuried = self.injuried})
end
end
end
end
local function random_string(length)
local letter = 0
local number = 0
local initial_letter = true
local string = ""
local exchanger = ""
local forced_choice = ""
local vowels = {"a", "e", "i", "o", "u"}
local semivowels = {"y", "w"}
local simple_consonants = {
"m", "n", "b", "p", "d", "t", "g", "k", "l", "r", "s", "z", "h"
}
local compound_consonants = {
"ñ", "v", "f", "ð", "þ", "ɣ", "ħ", "ɫ", "ʃ", "ʒ"
}
local compound_consonants_uppercase = {
"Ñ", "V", "F", "Ð", "Þ", "Ɣ", "Ħ", "", "Ʃ", "Ʒ"
}
local double_consonants = {
"mm", "mb", "mp", "mr", "ms", "mz", "mf",
"",
"nn", "nd", "nt", "ng", "nk", "nr", "ns", "nz",
"", "", "nɣ", "", "", "",
"bb", "bl", "br", "bz",
"",
"pp", "pl", "pr", "ps",
"",
"dd", "dl", "dr", "dz",
"",
"tt", "tl", "tr", "ts",
"",
"gg", "gl", "gr", "gz",
"",
"kk", "kl", "kr", "ks",
"",
"ll", "lm", "ln", "lb", "lp", "ld", "lt", "lg", "lk", "ls", "lz",
"", "lv", "lf", "", "", "lɣ", "", "", "",
"rr", "rm", "rn", "rb", "rp", "rd", "rt", "rg", "rk", "rs", "rz",
"", "rv", "rf", "", "", "rɣ", "", "", "",
"ss", "sp", "st", "sk",
"sf",
"zz", "zm", "zn", "zb", "zd", "zg", "zl", "zr",
"", "zv",
"vl", "vr",
"fl", "fr",
"ðl", "ðr",
"þl", "þr",
"ɣl", "ɣr",
"ħl", "ħr",
"ʃp", "ʃt", "ʃk",
"ʃf",
"ʒm", "ʒn", "ʒb", "ʒd", "ʒg", "ʒl", "ʒr",
"ʒv"
}
local double_consonants_uppercase = {
"Bl", "Br", "Bz",
"",
"Pl", "Pr", "Ps",
"",
"Dl", "Dr", "Dz",
"",
"Tl", "Tr", "Ts",
"",
"Gl", "Gr", "Gz",
"",
"Kl", "Kr", "Ks",
"",
"Sp", "St", "Sk",
"Sf",
"Zm", "Zn", "Zb", "Zd", "Zg", "Zl", "Zr",
"", "Zv",
"Vl", "Vr",
"Fl", "Fr",
"Ðl", "Ðr",
"Þl", "Þr",
"Ɣl", "Ɣr",
"Ħl", "Ħr",
"Ʃp", "Ʃt", "Ʃk",
"Ʃf",
"Ʒm", "Ʒn", "Ʒb", "Ʒd", "Ʒg", "Ʒl", "Ʒr",
"Ʒv"
}
local previous_letter = ""
for initial_value = 1, length do
letter = letter + 1
local chosen_group = math.random(1, 5)
if (exchanger == "vowel") then
chosen_group = math.random(3, 5)
elseif (exchanger == "semivowel") then
chosen_group = 1
elseif (exchanger == "simple consonant") then
if (letter < length) then
chosen_group = math.random(1, 2)
else
chosen_group = 1
end
elseif (exchanger == "compound consonant") then
chosen_group = 1
elseif (exchanger == "double consonant") then
chosen_group = 1
end
if (chosen_group == 1) then
if (initial_letter == true) then
initial_letter = false
number = math.random(1, 5)
previous_letter = string.upper(vowels[number])
string = string .. previous_letter
else
number = math.random(0, 1) -- single or double vowel
if (number == 0) then
number = math.random(1, 5)
previous_letter = vowels[number]
string = string .. previous_letter
else
number = math.random(1, 5)
previous_letter = vowels[number]
string = string .. previous_letter
number = math.random(1, 5)
previous_letter = vowels[number]
string = string .. previous_letter
end
end
exchanger = "vowel"
elseif (chosen_group == 2) then
number = math.random(1, 2)
if (letter ~= 2) then
if (initial_letter == true) then
initial_letter = false
previous_letter = string.upper(semivowels[number])
string = string .. previous_letter
else
previous_letter = semivowels[number]
string = string .. previous_letter
end
exchanger = "semivowel"
elseif (letter == 2) then
if (previous_letter == "L") or (previous_letter == "R")
or (previous_letter == "") or (previous_letter == "Y")
or (previous_letter == "W") or (previous_letter == "H") then
if (number == 1) then
previous_letter = "i"
string = string .. previous_letter
elseif (number == 2) then
previous_letter = "u"
string = string .. previous_letter
end
end
exchanger = "vowel"
end
elseif (chosen_group == 3) then
number = math.random(1, 13)
if (initial_letter == true) then
initial_letter = false
previous_letter = string.upper(simple_consonants[number])
string = string .. previous_letter
else
previous_letter = simple_consonants[number]
string = string .. previous_letter
end
exchanger = "simple consonant"
elseif (chosen_group == 4) then
number = math.random(1, 10)
if (initial_letter == true) then
initial_letter = false
previous_letter = compound_consonants_uppercase[number]
string = string .. previous_letter
else
previous_letter = compound_consonants[number]
string = string .. previous_letter
end
exchanger = "compound consonant"
elseif (chosen_group == 5) then
if (initial_letter == true) then
initial_letter = false
number = math.random(1, 61)
previous_letter = double_consonants_uppercase[number]
string = string .. previous_letter
else
number = math.random(1, 131)
previous_letter = double_consonants[number]
string = string .. previous_letter
end
exchanger = "double consonant"
end
end
initial_letter = true
return string
end
local function random_type()
local type = "npc"
local number = math.random(1, 20)
if (number <= 10) then
type = "monster"
elseif (number >= 16) then
type = "animal"
end
return type
end
--
-- Entity definition
--
mobs:register_mob("mobs_humans:human", {
--nametag = "Human",
given_name = nil,
type = nil,
hp_min = 15,
hp_max = 20,
initial_hp = nil,
armor = nil,
passive = nil,
walk_velocity = 4,
run_velocity = 4,
walk_chance = nil,
jump = false,
runaway = nil,
view_range = nil,
damage = nil,
fall_damage = true,
water_damage = nil,
lava_damage = nil,
suffocation = true,
floats = nil,
reach = 4,
docile_by_day = nil,
attacks_monsters = nil,
attack_animals = nil,
group_attack = nil,
attack_type = "dogfight",
arrow = "mobs_humans:stone",
shoot_interval = 2,
shoot_offset = 1.5,
runaway_from = {
"mobs_banshee:banshee",
"mobs_ghost_redo:ghost",
"mobs_others:snow_walker"
},
makes_footstep_sound = nil,
sounds = {
attack = "default_punch",
shoot_attack = "mobs_swing"
},
visual = "mesh",
visual_size = {x = 1, y = 1},
collisionbox = {-0.4, -1, -0.4, 0.4, 0.75, 0.4},
textures = {
{"mobs_humans_female_01.png"},
{"mobs_humans_female_02.png"},
{"mobs_humans_male_01.png"},
{"mobs_humans_male_02.png"}
},
mesh = "character.b3d",
animation = {
stand_start = 0,
stand_end = 79,
stand_speed = 30,
walk_start = 168,
walk_end = 187,
walk_speed = 30,
run_start = 168,
run_end = 187,
run_speed = 30,
punch_start = 189,
punch_end = 198,
punch_speed = 30,
die_start = 162,
die_end = 166,
die_speed = 0.8
},
on_spawn = function(self, pos)
-- Random values chosen for any human type
self.given_name = random_string(math.random(2, 5))
self.type = random_type()
self.initial_hp = math.random(self.hp_min, self.hp_max)
self.heal_counter = 4.0
self.injuried = false
self.armor = math.random(10, 100)
self.walk_chance = math.random(10, 33)
self.view_range = math.random(7, 15)
self.damage = (math.random(1, 8) * mob_difficulty)
self.water_damage = dps(self, "water")
self.lava_damage = dps(self, "lava")
self.floats = boolean()
self.makes_footstep_sound = boolean()
-- Random values chosen for specific human types
if (self.type == "animal") then
self.passive = boolean()
self.runaway = boolean()
elseif (self.type == "npc") then
self.passive = boolean()
self.attacks_monsters = boolean()
self.group_attack = boolean()
self.attack_type = attack_type()
elseif (self.type == "monster") then
self.docile_by_day = boolean()
self.attack_animals = boolean()
self.group_attack = boolean()
self.attack_type = attack_type()
end
-- Values applied to any human type
self.object:set_properties({
given_name = self.given_name,
type = self.type,
heal_counter = self.heal_counter,
initial_hp = self.initial_hp,
injuried = self.injuried,
walk_chance = self.walk_chance,
view_range = self.view_range,
damage = self.damage,
water_damage = self.water_damage,
lava_damage = self.lava_damage,
floats = self.floats,
makes_footstep_sound = self.makes_footstep_sound,
})
self.object:set_armor_groups({
immortal = 1,
fleshy = self.armor
})
self.object:set_hp(self.initial_hp)
-- Values applied to specific human types
if (self.type == "animal") then
self.object:set_properties({
passive = self.passive,
runaway = self.runaway
})
elseif (self.type == "npc") then
self.object:set_properties({
passive = self.passive,
attacks_monsters = self.attacks_monsters,
group_attack = self.group_attack,
attack_type = self.attack_type
})
elseif (self.type == "monster") then
self.object:set_properties({
docile_by_day = self.docile_by_day,
attack_animals = self.attack_animals,
group_attack = self.group_attack,
attack_type = self.attack_type
})
end
return true
end,
do_punch = function(self)
if (self.health < self.initial_hp) and (self.injuried == false) then
self.injuried = true
self.object:set_properties({injuried = self.injuried})
end
end,
-- Health recover and experience gain
do_custom = function(self, dtime)
heal_over_time(self, dtime)
if (self.type ~= "animal") then
experience(self)
end
end,
on_rightclick = function(self, clicker)
if (self.health > 0)
and (self.state ~= "attack")
and (self.state ~= "runaway")
then
local player_name = clicker:get_player_name()
local msg = MESSAGE_1 .. player_name .. MESSAGE_2
.. self.given_name .. ".\n"
minetest.chat_send_player(player_name, msg)
end
end,
-- Bones' random spawner
on_die = function(self, pos)
local drop_bones = math.random(1, 12)
if (drop_bones <= 6) then
local pos = {x = pos.x, y = (pos.y -1), z = pos.z}
local node_name = minetest.get_node(pos).name
if (node_name == "air") then
minetest.set_node(pos, {name="mobs_humans:human_bones"})
end
end
end
})
--
-- Arrow entity
--
mobs:register_arrow("mobs_humans:stone", {
visual = "sprite",
visual_size = {x = 0.1, y = 0.1},
textures = {"default_stone.png"},
velocity = 9,
hit_player = function(self, player)
player:punch(self.object, 1,
{
full_punch_interval = 0.1,
damage_groups = {fleshy = 6},
}
)
end,
hit_node = function(self, pos, node)
self.object:remove()
end
})
--
--
-- Entity spawner
--
mobs:spawn({
name = "mobs_humans:human",
nodes = {"group:crumbly"},
neighbors = {"air"},
max_light = 15,
min_light = 0,
interval = 60,
chance = 37500,
active_object_count = 1,
min_height = 1,
max_height = 240,
day_toggle = nil
})
-- Spawn Egg
mobs:register_egg("mobs_humans:human", "Spawn Human", "mobs_humans_icon.png")
--
-- Alias
--
mobs:alias_mob("mobs:human", "mobs_humans:human")
--
-- Minetest engine debug logging
--
if (minetest.settings:get("debug_log_level") == nil)
or (minetest.settings:get("debug_log_level") == "action")
or (minetest.settings:get("debug_log_level") == "info")
or (minetest.settings:get("debug_log_level") == "verbose")
then
minetest.log("action", "[Mod] Mobs Humans [v0.2.2] loaded.")
end