mirror of
https://git.minetest.land/VoxeLibre/VoxeLibre.git
synced 2024-09-12 08:42:10 +02:00
In minecraft horses are tamed by trying to ride them and they can also be fed to speed up taming. This commit implements both of those features and disables the old and broken taming system for horses.
523 lines
15 KiB
Lua
523 lines
15 KiB
Lua
--MCmobs v0.4
|
|
--maikerumine
|
|
--made for MC like Survival game
|
|
--License for code WTFPL and otherwise stated in readmes
|
|
|
|
local S = minetest.get_translator("mobs_mc")
|
|
|
|
--###################
|
|
--################### HORSE
|
|
--###################
|
|
|
|
-- Return overlay texture for horse/donkey/mule, e.g. chest, saddle or horse armor
|
|
local horse_extra_texture = function(horse)
|
|
local base = horse._naked_texture
|
|
local saddle = horse._saddle
|
|
local chest = horse._chest
|
|
local armor = horse._horse_armor
|
|
local textures = {}
|
|
if armor and minetest.get_item_group(armor, "horse_armor") > 0 then
|
|
textures[2] = base .. "^" .. minetest.registered_items[armor]._horse_overlay_image
|
|
else
|
|
textures[2] = base
|
|
end
|
|
if saddle then
|
|
textures[3] = base
|
|
else
|
|
textures[3] = "blank.png"
|
|
end
|
|
if chest then
|
|
textures[1] = base
|
|
else
|
|
textures[1] = "blank.png"
|
|
end
|
|
return textures
|
|
end
|
|
|
|
-- Helper functions to determine equipment rules
|
|
local can_equip_horse_armor = function(entity_id)
|
|
return entity_id == "mobs_mc:horse" or entity_id == "mobs_mc:skeleton_horse" or entity_id == "mobs_mc:zombie_horse"
|
|
end
|
|
local can_equip_chest = function(entity_id)
|
|
return entity_id == "mobs_mc:mule" or entity_id == "mobs_mc:donkey"
|
|
end
|
|
local can_breed = function(entity_id)
|
|
return entity_id == "mobs_mc:horse" or "mobs_mc:mule" or entity_id == "mobs_mc:donkey"
|
|
end
|
|
|
|
--[[ Generate all possible horse textures.
|
|
Horse textures are a combination of a base texture and an optional marking overlay. ]]
|
|
-- The base horse textures (fur) (fur)
|
|
local horse_base = {
|
|
"mobs_mc_horse_brown.png",
|
|
"mobs_mc_horse_darkbrown.png",
|
|
"mobs_mc_horse_white.png",
|
|
"mobs_mc_horse_gray.png",
|
|
"mobs_mc_horse_black.png",
|
|
"mobs_mc_horse_chestnut.png",
|
|
"mobs_mc_horse_creamy.png",
|
|
}
|
|
-- Horse marking texture overlay, to be appended to the base texture string
|
|
local horse_markings = {
|
|
"", -- no markings
|
|
"mobs_mc_horse_markings_whitedots.png", -- snowflake appaloosa
|
|
"mobs_mc_horse_markings_blackdots.png", -- sooty
|
|
"mobs_mc_horse_markings_whitefield.png", -- paint
|
|
"mobs_mc_horse_markings_white.png", -- stockings and blaze
|
|
}
|
|
|
|
local horse_textures = {}
|
|
for b=1, #horse_base do
|
|
for m=1, #horse_markings do
|
|
local fur = horse_base[b]
|
|
if horse_markings[m] ~= "" then
|
|
fur = fur .. "^" .. horse_markings[m]
|
|
end
|
|
table.insert(horse_textures, {
|
|
"blank.png", -- chest
|
|
fur, -- base texture + markings and optional armor
|
|
"blank.png", -- saddle
|
|
})
|
|
end
|
|
end
|
|
|
|
-- Horse
|
|
local horse = {
|
|
type = "animal",
|
|
spawn_class = "passive",
|
|
visual = "mesh",
|
|
mesh = "mobs_mc_horse.b3d",
|
|
visual_size = {x=3.0, y=3.0},
|
|
collisionbox = {-0.69825, -0.01, -0.69825, 0.69825, 1.59, 0.69825},
|
|
animation = {
|
|
stand_speed = 25,
|
|
stand_start = 0,
|
|
stand_end = 0,
|
|
walk_speed = 25,
|
|
walk_start = 0,
|
|
walk_end = 40,
|
|
run_speed = 60,
|
|
run_start = 0,
|
|
run_end = 40,
|
|
},
|
|
textures = horse_textures,
|
|
sounds = {
|
|
random = "mobs_mc_horse_random",
|
|
-- TODO: Separate damage sound
|
|
damage = "mobs_mc_horse_death",
|
|
death = "mobs_mc_horse_death",
|
|
eat = "mobs_mc_animal_eat_generic",
|
|
distance = 16,
|
|
},
|
|
fear_height = 4,
|
|
fly = false,
|
|
walk_chance = 60,
|
|
view_range = 16,
|
|
follow = mobs_mc.follow.horse,
|
|
passive = true,
|
|
hp_min = 15,
|
|
hp_max = 30,
|
|
xp_min = 1,
|
|
xp_max = 3,
|
|
floats = 1,
|
|
makes_footstep_sound = true,
|
|
jump = true,
|
|
jump_height = 5.75, -- can clear 2.5 blocks
|
|
drops = {
|
|
{name = mobs_mc.items.leather,
|
|
chance = 1,
|
|
min = 0,
|
|
max = 2,
|
|
looting = "common",},
|
|
},
|
|
|
|
do_custom = function(self, dtime)
|
|
|
|
-- set needed values if not already present
|
|
if not self._regentimer then
|
|
self._regentimer = 0
|
|
end
|
|
if not self.v2 then
|
|
self.v2 = 0
|
|
self.max_speed_forward = 7
|
|
self.max_speed_reverse = 2
|
|
self.accel = 6
|
|
self.terrain_type = 3
|
|
self.driver_attach_at = {x = 0, y = 4.17, z = -1.75}
|
|
self.driver_eye_offset = {x = 0, y = 3, z = 0}
|
|
self.driver_scale = {x = 1/self.visual_size.x, y = 1/self.visual_size.y}
|
|
end
|
|
|
|
-- Slowly regenerate health
|
|
self._regentimer = self._regentimer + dtime
|
|
if self._regentimer >= 4 then
|
|
if self.health < self.hp_max then
|
|
self.health = self.health + 1
|
|
end
|
|
self._regentimer = 0
|
|
end
|
|
|
|
-- Some weird human is riding. Buck them off?
|
|
if self.driver and not self.tamed and self.buck_off_time <= 0 then
|
|
if math.random() < 0.2 then
|
|
mobs.detach(self.driver, {x = 1, y = 0, z = 1})
|
|
-- TODO bucking animation
|
|
else
|
|
-- Nah, can't be bothered. Think about it again in one second
|
|
self.buck_off_time = 20
|
|
end
|
|
end
|
|
|
|
-- Tick the timer for trying to buck the player off
|
|
if self.buck_off_time then
|
|
if self.driver then
|
|
self.buck_off_time = self.buck_off_time - 1
|
|
else
|
|
-- Player isn't riding anymore so no need to count
|
|
self.buck_off_time = nil
|
|
end
|
|
end
|
|
|
|
-- if driver present and horse has a saddle allow control of horse
|
|
if self.driver and self._saddle then
|
|
|
|
mobs.drive(self, "walk", "stand", false, dtime)
|
|
|
|
return false -- skip rest of mob functions
|
|
end
|
|
|
|
return true
|
|
end,
|
|
|
|
on_die = function(self, pos)
|
|
|
|
-- drop saddle when horse is killed while riding
|
|
if self._saddle then
|
|
minetest.add_item(pos, mobs_mc.items.saddle)
|
|
end
|
|
-- also detach from horse properly
|
|
if self.driver then
|
|
mobs.detach(self.driver, {x = 1, y = 0, z = 1})
|
|
end
|
|
|
|
end,
|
|
|
|
on_rightclick = function(self, clicker)
|
|
|
|
-- make sure player is clicking
|
|
if not clicker or not clicker:is_player() then
|
|
return
|
|
end
|
|
|
|
local item = clicker:get_wielded_item()
|
|
local iname = item:get_name()
|
|
local heal = 0
|
|
|
|
-- Taming
|
|
self.temper = self.temper or (math.random(1,100))
|
|
|
|
if not self.tamed then
|
|
local temper_increase = 0
|
|
|
|
-- Feeding, intentionally not using mobs:feed_tame because horse taming is
|
|
-- different and more complicated
|
|
if (iname == mobs_mc.items.sugar) then
|
|
temper_increase = 3
|
|
elseif (iname == mobs_mc.items.wheat) then
|
|
temper_increase = 3
|
|
elseif (iname == mobs_mc.items.apple) then
|
|
temper_increase = 3
|
|
elseif (iname == mobs_mc.items.golden_carrot) then
|
|
temper_increase = 5
|
|
elseif (iname == mobs_mc.items.golden_apple) then
|
|
temper_increase = 10
|
|
|
|
-- Trying to ride
|
|
elseif not self.driver then
|
|
self.object:set_properties({stepheight = 1.1})
|
|
mobs.attach(self, clicker)
|
|
self.buck_off_time = 40 -- TODO how long does it take in minecraft?
|
|
if self.temper > 100 then
|
|
self.tamed = true -- NOTE taming can only be finished by riding the horse
|
|
if not self.owner or self.owner == "" then
|
|
self.owner = clicker:get_player_name()
|
|
end
|
|
end
|
|
temper_increase = 5
|
|
|
|
-- Clicking on the horse while riding ==> unmount
|
|
elseif self.driver and self.driver == clicker then
|
|
mobs.detach(clicker, {x = 1, y = 0, z = 1})
|
|
end
|
|
|
|
-- If nothing happened temper_increase = 0 and addition does nothing
|
|
self.temper = self.temper + temper_increase
|
|
|
|
return
|
|
end
|
|
|
|
if can_breed(self.name) then
|
|
-- Breed horse with golden apple or golden carrot
|
|
if (iname == mobs_mc.items.golden_apple) then
|
|
heal = 10
|
|
elseif (iname == mobs_mc.items.golden_carrot) then
|
|
heal = 4
|
|
end
|
|
if heal > 0 and mobs:feed_tame(self, clicker, heal, true, false) then
|
|
return
|
|
end
|
|
end
|
|
-- Feed with anything else
|
|
-- TODO heal amounts don't work
|
|
if (iname == mobs_mc.items.sugar) then
|
|
heal = 1
|
|
elseif (iname == mobs_mc.items.wheat) then
|
|
heal = 2
|
|
elseif (iname == mobs_mc.items.apple) then
|
|
heal = 3
|
|
elseif (iname == mobs_mc.items.hay_bale) then
|
|
heal = 20
|
|
end
|
|
if heal > 0 and mobs:feed_tame(self, clicker, heal, false, false) then
|
|
return
|
|
end
|
|
|
|
if mobs:protect(self, clicker) then
|
|
return
|
|
end
|
|
|
|
-- Make sure tamed horse is mature and being clicked by owner only
|
|
if self.tamed and not self.child and self.owner == clicker:get_player_name() then
|
|
|
|
local inv = clicker:get_inventory()
|
|
|
|
-- detatch player already riding horse
|
|
if self.driver and clicker == self.driver then
|
|
|
|
mobs.detach(clicker, {x = 1, y = 0, z = 1})
|
|
|
|
-- Put on saddle if tamed
|
|
elseif not self.driver and not self._saddle
|
|
and iname == mobs_mc.items.saddle then
|
|
|
|
-- Put on saddle and take saddle from player's inventory
|
|
local w = clicker:get_wielded_item()
|
|
self._saddle = true
|
|
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
|
w:take_item()
|
|
clicker:set_wielded_item(w)
|
|
end
|
|
|
|
-- Update texture
|
|
if not self._naked_texture then
|
|
-- Base horse texture without chest or saddle
|
|
self._naked_texture = self.base_texture[2]
|
|
end
|
|
local tex = horse_extra_texture(self)
|
|
self.base_texture = tex
|
|
self.object:set_properties({textures = self.base_texture})
|
|
minetest.sound_play({name = "mcl_armor_equip_leather"}, {gain=0.5, max_hear_distance=12, pos=self.object:get_pos()}, true)
|
|
|
|
-- Put on horse armor if tamed
|
|
elseif can_equip_horse_armor(self.name) and not self.driver and not self._horse_armor
|
|
and minetest.get_item_group(iname, "horse_armor") > 0 then
|
|
|
|
|
|
-- Put on armor and take armor from player's inventory
|
|
local armor = minetest.get_item_group(iname, "horse_armor")
|
|
self._horse_armor = iname
|
|
local w = clicker:get_wielded_item()
|
|
if not minetest.is_creative_enabled(clicker:get_player_name()) then
|
|
w:take_item()
|
|
clicker:set_wielded_item(w)
|
|
end
|
|
|
|
-- Set horse armor strength
|
|
self.armor = armor
|
|
local agroups = self.object:get_armor_groups()
|
|
agroups.fleshy = self.armor
|
|
self.object:set_armor_groups(agroups)
|
|
|
|
-- Update texture
|
|
if not self._naked_texture then
|
|
-- Base horse texture without chest or saddle
|
|
self._naked_texture = self.base_texture[2]
|
|
end
|
|
local tex = horse_extra_texture(self)
|
|
self.base_texture = tex
|
|
self.object:set_properties({textures = self.base_texture})
|
|
local def = w:get_definition()
|
|
if def.sounds and def.sounds._mcl_armor_equip then
|
|
minetest.sound_play({name = def.sounds._mcl_armor_equip}, {gain=0.5, max_hear_distance=12, pos=self.object:get_pos()}, true)
|
|
end
|
|
|
|
-- Mount horse
|
|
elseif not self.driver and self._saddle then
|
|
|
|
self.object:set_properties({stepheight = 1.1})
|
|
mobs.attach(self, clicker)
|
|
|
|
-- Used to capture horse
|
|
elseif not self.driver and iname ~= "" then
|
|
mobs:capture_mob(self, clicker, 0, 5, 60, false, nil)
|
|
end
|
|
end
|
|
end,
|
|
|
|
on_breed = function(parent1, parent2)
|
|
local pos = parent1.object:get_pos()
|
|
local child = mobs:spawn_child(pos, parent1.name)
|
|
if child then
|
|
local ent_c = child:get_luaentity()
|
|
local p = math.random(1, 2)
|
|
local child_texture
|
|
-- Randomly pick one of the parents for the child texture
|
|
if p == 1 then
|
|
if parent1._naked_texture then
|
|
child_texture = parent1._naked_texture
|
|
else
|
|
child_texture = parent1.base_texture[2]
|
|
end
|
|
else
|
|
if parent2._naked_texture then
|
|
child_texture = parent2._naked_texture
|
|
else
|
|
child_texture = parent2.base_texture[2]
|
|
end
|
|
end
|
|
local splt = string.split(child_texture, "^")
|
|
if #splt >= 2 then
|
|
-- Randomly mutate base texture (fur) and markings
|
|
-- with chance of 1/9 each
|
|
local base = splt[1]
|
|
local markings = splt[2]
|
|
local mutate_base = math.random(1, 9)
|
|
local mutate_markings = math.random(1, 9)
|
|
if mutate_base == 1 then
|
|
local b = math.random(1, #horse_base)
|
|
base = horse_base[b]
|
|
end
|
|
if mutate_markings == 1 then
|
|
local m = math.random(1, #horse_markings)
|
|
markings = horse_markings[m]
|
|
end
|
|
child_texture = base
|
|
if markings ~= "" then
|
|
child_texture = child_texture .. "^" .. markings
|
|
end
|
|
end
|
|
ent_c.base_texture = { "blank.png", child_texture, "blank.png" }
|
|
ent_c._naked_texture = child_texture
|
|
|
|
child:set_properties({textures = ent_c.base_texture})
|
|
return false
|
|
end
|
|
end,
|
|
}
|
|
|
|
mobs:register_mob("mobs_mc:horse", horse)
|
|
|
|
-- Skeleton horse
|
|
local skeleton_horse = table.copy(horse)
|
|
skeleton_horse.breath_max = -1
|
|
skeleton_horse.armor = {undead = 100, fleshy = 100}
|
|
skeleton_horse.textures = {{"blank.png", "mobs_mc_horse_skeleton.png", "blank.png"}}
|
|
skeleton_horse.drops = {
|
|
{name = mobs_mc.items.bone,
|
|
chance = 1,
|
|
min = 0,
|
|
max = 2,},
|
|
}
|
|
skeleton_horse.sounds = {
|
|
random = "mobs_mc_skeleton_random",
|
|
death = "mobs_mc_skeleton_death",
|
|
damage = "mobs_mc_skeleton_hurt",
|
|
eat = "mobs_mc_animal_eat_generic",
|
|
base_pitch = 0.95,
|
|
distance = 16,
|
|
}
|
|
skeleton_horse.harmed_by_heal = true
|
|
mobs:register_mob("mobs_mc:skeleton_horse", skeleton_horse)
|
|
|
|
-- Zombie horse
|
|
local zombie_horse = table.copy(horse)
|
|
zombie_horse.breath_max = -1
|
|
zombie_horse.armor = {undead = 100, fleshy = 100}
|
|
zombie_horse.textures = {{"blank.png", "mobs_mc_horse_zombie.png", "blank.png"}}
|
|
zombie_horse.drops = {
|
|
{name = mobs_mc.items.rotten_flesh,
|
|
chance = 1,
|
|
min = 0,
|
|
max = 2,},
|
|
}
|
|
zombie_horse.sounds = {
|
|
random = "mobs_mc_horse_random",
|
|
-- TODO: Separate damage sound
|
|
damage = "mobs_mc_horse_death",
|
|
death = "mobs_mc_horse_death",
|
|
eat = "mobs_mc_animal_eat_generic",
|
|
base_pitch = 0.5,
|
|
distance = 16,
|
|
}
|
|
zombie_horse.harmed_by_heal = true
|
|
mobs:register_mob("mobs_mc:zombie_horse", zombie_horse)
|
|
|
|
-- Donkey
|
|
local d = 0.86 -- donkey scale
|
|
local donkey = table.copy(horse)
|
|
donkey.textures = {{"blank.png", "mobs_mc_donkey.png", "blank.png"}}
|
|
donkey.animation = {
|
|
speed_normal = 25,
|
|
stand_start = 0, stand_end = 0,
|
|
walk_start = 0, walk_end = 40,
|
|
}
|
|
donkey.sounds = {
|
|
random = "mobs_mc_donkey_random",
|
|
damage = "mobs_mc_donkey_hurt",
|
|
death = "mobs_mc_donkey_death",
|
|
eat = "mobs_mc_animal_eat_generic",
|
|
distance = 16,
|
|
}
|
|
donkey.visual_size = { x=horse.visual_size.x*d, y=horse.visual_size.y*d }
|
|
donkey.collisionbox = {
|
|
horse.collisionbox[1] * d,
|
|
horse.collisionbox[2] * d,
|
|
horse.collisionbox[3] * d,
|
|
horse.collisionbox[4] * d,
|
|
horse.collisionbox[5] * d,
|
|
horse.collisionbox[6] * d,
|
|
}
|
|
donkey.jump = true
|
|
donkey.jump_height = 3.75 -- can clear 1 block height
|
|
|
|
mobs:register_mob("mobs_mc:donkey", donkey)
|
|
|
|
-- Mule
|
|
local m = 0.94
|
|
local mule = table.copy(donkey)
|
|
mule.textures = {{"blank.png", "mobs_mc_mule.png", "blank.png"}}
|
|
mule.visual_size = { x=horse.visual_size.x*m, y=horse.visual_size.y*m }
|
|
mule.sounds = table.copy(donkey.sounds)
|
|
mule.sounds.base_pitch = 1.15
|
|
mule.collisionbox = {
|
|
horse.collisionbox[1] * m,
|
|
horse.collisionbox[2] * m,
|
|
horse.collisionbox[3] * m,
|
|
horse.collisionbox[4] * m,
|
|
horse.collisionbox[5] * m,
|
|
horse.collisionbox[6] * m,
|
|
}
|
|
mobs:register_mob("mobs_mc:mule", mule)
|
|
|
|
--===========================
|
|
--Spawn Function
|
|
mobs:spawn_specific("mobs_mc:horse", mobs_mc.spawn.grassland_savanna, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 4, mobs_mc.spawn_height.water+3, mobs_mc.spawn_height.overworld_max)
|
|
mobs:spawn_specific("mobs_mc:donkey", mobs_mc.spawn.grassland_savanna, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 15000, 4, mobs_mc.spawn_height.water+3, mobs_mc.spawn_height.overworld_max)
|
|
|
|
-- spawn eggs
|
|
mobs:register_egg("mobs_mc:horse", S("Horse"), "mobs_mc_spawn_icon_horse.png", 0)
|
|
mobs:register_egg("mobs_mc:skeleton_horse", S("Skeleton Horse"), "mobs_mc_spawn_icon_horse_skeleton.png", 0)
|
|
--mobs:register_egg("mobs_mc:zombie_horse", S("Zombie Horse"), "mobs_mc_spawn_icon_horse_zombie.png", 0)
|
|
mobs:register_egg("mobs_mc:donkey", S("Donkey"), "mobs_mc_spawn_icon_donkey.png", 0)
|
|
mobs:register_egg("mobs_mc:mule", S("Mule"), "mobs_mc_spawn_icon_mule.png", 0)
|