123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- local modname = minetest.get_current_modname()
- local function get_random_colormod()
- local r = math.random(256) - 1
- local g = math.random(256) - 1
- local b = math.random(256) - 1
- local hex = string.format("%.2X%.2X%.2X", r, g, b)
- return "^[multiply:#" .. hex
- end
- local function get_dragonfly_textures(colormod)
- return {
- "eg_bugs_dragonfly_thorax.png" .. colormod,
- "eg_bugs_dragonfly_wings.png",
- "eg_bugs_dragonfly_head.png" .. colormod,
- "eg_bugs_dragonfly_eye.png",
- }
- end
- local function get_firebreath_toolcaps(size)
- return {
- full_punch_interval = 10,
- damage_groups = {
- fire = math.ceil(size)
- }
- }
- end
- minetest.register_entity(modname .. ":firebreath", {
- initial_properties = {
- visual = "sprite",
- textures = {"eg_bugs_firebreath.png"},
- glow = 10, --BROKEN?
- collide_with_objects = true,
- physical = true,
- collisionbox = {-0.25, -0.35, -0.25, 0.25, 0.25, 0.25},
- },
- on_activate = function(self, staticdata)
- local data = minetest.deserialize(staticdata)
- -- Don't brick the world with crashes if no staticdata is passed for some reason.
- if not data then
- self.object:remove()
- return
- end
- self.object:set_velocity(data.vel)
- self.size = data.size
- end,
- get_staticdata = function(self)
- local data = {
- vel = self.object:get_velocity(),
- size = self.size,
- }
- if not vel then return end
- return minetest.serialize(data)
- end,
- on_step = function(self, dtime, moveresult)
- self.size = self.size - dtime
- local pos = self.object:get_pos()
- if self.size < 0.1 or
- minetest.get_item_group(minetest.get_node(pos).name, "water") > 0 then
- self.object:remove()
- return
- end
- if moveresult.collides then
- -- Spawn a smoke puff
- minetest.add_particlespawner({
- amount = 10,
- time = 0.1,
- minpos = pos,
- maxpos = pos,
- glow = 15,
- minvel = vector.new(-0.3, 1, -0.3),
- maxvel = vector.new(0.3, 3, 0.3),
- minsize = 1,
- maxsize = 5,
- texture = "eg_bugs_smokepuff.png",
- minexptime = 0.3,
- maxexptime = 0.5,
- })
- -- Damage collided objects
- for i, v in ipairs(moveresult.collisions) do
- if v.type == "object" then
- v.object:punch(self.object, 10, get_firebreath_toolcaps(self.size))
- end
- end
- --disappear
- self.object:remove()
- return
- end
- local props = self.object:get_properties()
- local collbox_offset = self.size * 0.125
- props.collisionbox = {
- -collbox_offset,
- -collbox_offset,
- -collbox_offset,
- collbox_offset,
- collbox_offset,
- collbox_offset,
- }
- props.visual_size = vector.new(self.size, self.size, self.size)
- self.object:set_properties(props)
- end,
- })
- minetest.register_craftitem(modname .. ":firebreath_debug", {
- inventory_image = "eg_bugs_firebreath.png",
- on_use = function(itemstack, user)
- local dir = user:get_look_dir()
- local pos = user:get_pos()
- pos.y = pos.y + 1.4
- pos = vector.add(pos, dir)
- local data = {vel = vector.multiply(dir, 5), size = math.random(3)}
- minetest.add_entity(pos, modname .. ":firebreath", minetest.serialize(data))
- end
- })
- local function lq_hover_to_pos(self, pos, turn_speed, tolerance, speed_factor)
- speed_factor = speed_factor or 1
- tolerance = tolerance or 1
- turn_speed = turn_speed or 1
- local oldpos = pos
- local func = function()
- local selfpos = self.object:get_pos()
- -- Stop when there or stuck
- if vector.distance(selfpos, pos) < tolerance or
- vector.distance(selfpos, oldpos) < self.max_speed * speed_factor * self.dtime then
- return true
- end
- oldpos = selfpos
- local dir = vector.direction(selfpos, pos)
- mobkit.turn2yaw(self, minetest.dir_to_yaw(dir), turn_speed)
- self.object:set_velocity(vector.multiply(dir, self.max_speed * speed_factor))
- end
- mobkit.queue_low(self, func)
- end
- local function lq_turn2yaw_hovering(self, desired_yaw, rate)
- local func = function()
- self.object:set_velocity(vector.new(0, 0, 0)) -- TODO: maybe small random movement
- return mobkit.turn2yaw(self, desired_yaw, rate)
- end
- mobkit.queue_low(self, func)
- end
- local function lq_turn2pos_hovering(self, destpos, rate)
- local yaw = minetest.dir_to_yaw(vector.direction(self.object:get_pos(), destpos))
- lq_turn2yaw_hovering(self, yaw, rate)
- end
- local function flyable(selfpos, tgtpos)
- local raycast = Raycast(selfpos, tgtpos, true, true)
- raycast:next() --Get self out of list
- return not raycast:next()
- end
- local function attack(self, tgt)
- local data = {}
- local selfpos = self.object:get_pos()
- local tgtpos = tgt:get_pos()
- tgtpos.y = tgtpos.y + 1
- local dir = vector.direction(selfpos, tgtpos)
- local pos = vector.add(selfpos, dir)
- data.vel = vector.multiply(dir, 10)
- data.size = math.random(3)
- minetest.add_entity(pos, modname .. ":firebreath", minetest.serialize(data))
- mobkit.make_sound(self, "breathefire")
- end
- local function lq_strafe(self, tgtpos)
- local selfpos = self.object:get_pos()
- local dir = vector.direction(selfpos, tgtpos)
- local dest
- for i = 1, 4 do
- dest = vector.add(selfpos, vector.multiply(vector.rotate_around_axis(
- vector.new(0, 0, 1),
- dir,
- math.random() * 2 * math.pi),
- math.random() * 2 + 1))
- if flyable(selfpos, dest) then
- break
- end
- end
- lq_hover_to_pos(self, dest, 6)
- end
- minetest.register_on_joinplayer(function(player)
- player:set_armor_groups({fire = 100})
- end)
- local function hq_dragonfly_roam(self, prty)
- local func = function()
- if mobkit.timer(self, 0.5) then
- if not mobkit.is_queue_empty_low(self) then
- return
- end
- local rand = math.random(10)
- if rand == 1 then
- lq_turn2yaw_hovering(self, math.random() * 2 * math.pi, 4)
- elseif rand > 1 then
- --find position
- local rand_vec = vector.normalize(vector.new(math.random() - 0.5, math.random() - (not self.isonground and 0.55 or 0), math.random() - 0.5))
- if vector.length(rand_vec) == 0 then
- return
- end
- rand_vec = vector.multiply(rand_vec, math.random(self.view_range))
- local pos = self.object:get_pos()
- local dest = vector.add(pos, rand_vec)
- --fly to position if possible
- if flyable(pos, dest) then
- lq_hover_to_pos(self, dest, 6)
- end
- end
- end
- end
- mobkit.queue_high(self, func, prty)
- end
- local function hq_dragonfly_hunt(self, tgtobj, prty)
- local tgt = tgt or mobkit.get_nearby_player(self)
- local func = function()
- if not mobkit.timer(self, 0.5) then
- return
- end
- if not mobkit.exists(tgt) then
- --TODO: test if gives up dead player
- return true
- end
- mobkit.clear_queue_low(self)
- local selfpos = self.object:get_pos()
- local tgtpos = tgt:get_pos()
- tgtpos.y = tgtpos.y + 1 --TODO: un-hardcode
- local dist = vector.distance(selfpos, tgtpos)
- if dist > 16 then
- local cpos = vector.divide(vector.add(tgtpos, selfpos), 2)
- if not flyable(selfpos, cpos) then
- cpos = vector.add(selfpos, vector.new(math.random() * 2 - 1,
- math.random() * 2 - 1,
- math.random() * 2 - 1))
- end
- lq_hover_to_pos(self, cpos, 6)
- elseif dist < 5 then
- local dir = vector.direction(tgtpos, selfpos)
- local cpos = vector.add(tgtpos, vector.multiply(dir, 6))
- for i = 1, 5 do
- if flyable(selfpos, cpos) then
- break
- end
- cpos = vector.add(selfpos, vector.new(math.random() * 8 - 4,
- math.random() * 8 - 3,
- math.random() * 8 - 4))
- end
- lq_hover_to_pos(self, cpos, 6)
- elseif math.random(2) == 1 then
- lq_turn2pos_hovering(self, tgtpos, 6)
- attack(self, tgt)
- else
- lq_strafe(self, tgtpos)
- end
- end
- mobkit.queue_high(self, func, prty)
- end
- minetest.register_entity(modname .. ":dragonfly",
- {
- initial_properties =
- {
- visual = "mesh",
- mesh = "eg_bugs_dragonfly.b3d",
- physical = true,
- collisionbox = {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25},
- glow = 5,
- backface_culling = false,
- },
- timeout = 50,
- buoyancy = 0.2,
- lung_capacity = 10,
- max_hp = 10,
- sounds = {
- breathefire =
- "eg_bugs_dragonfly_firebreath",
- },
- on_step = mobkit.stepfunc,
- on_activate = function(self, staticdata, dtime_s)
- mobkit.actfunc(self, staticdata, dtime_s)
- local props = self.object:get_properties()
- -- set texture
- local colormod = mobkit.recall(self, "colormod")
- if not colormod then
- colormod = get_random_colormod()
- mobkit.remember(self, "colormod", colormod)
- end
- props.textures = get_dragonfly_textures(colormod)
-
- -- set size
- local size = mobkit.recall(self, "size")
- if not size then
- size = math.random()
- -- hypercube size so small bugs are more common
- size = (size * size * size * size * 3) + 1
- mobkit.remember(self, "size", size)
- end
- props.visual_size = vector.new(size, size, size)
- for i, v in ipairs(props.collisionbox) do
- props.collisionbox[i] = v * size
- end
- self.object:set_properties(props)
- hq_dragonfly_roam(self, 1)
- end,
- get_staticdata = mobkit.statfunc,
- logic = function(self)
- if mobkit.is_queue_empty_low(self) then
- self.object:set_acceleration({x = 0, y = -0.2, z = 0})
- end
- if mobkit.get_queue_priority(self) < 5 then
- local p = mobkit.get_nearby_player(self)
- if p then
- hq_dragonfly_hunt(self, p, 10)
- end
- end
- end,
- max_speed = 5,
- jump_height = 1,
- view_range = 16,
- attack = {range = 1, damage_groups = {fleshy = 2}},
- armor_groups = {fleshy = 3}
- })
|