--------------------- -- HQ/LQ Functions -- --------------------- ------ Ver 2.0 ------ ------------ -- Locals -- ------------ local abs = math.abs local vec_dist = vector.distance local function anim_length(self, anim) if self.animation and self.animation[anim] then local frame1 = self.animation[anim].range.x local frame2 = self.animation[anim].range.y local frame_length = frame2-frame1 local total_length = frame_length/self.animation[anim].speed return abs(total_length) end end local function hitbox(object) if type(object) == "table" then object = object.object end return object:get_properties().collisionbox end local function find_closest_pos(tbl, pos) local iter = 2 if #tbl < 2 then return end local closest = tbl[1] while iter < #tbl do if vec_dist(pos, closest) < vec_dist(pos, tbl[iter + 1]) then iter = iter + 1 else closest = tbl[iter] iter = iter + 1 end end if iter >= #tbl and closest then return closest end end ------------------ -- LQ Functions -- ------------------ -- Dumb Punch -- function paleotest.lq_dumb_punch(self, target) local func = function(self) mobkit.animate(self, "stand") local vel = self.object:get_velocity() self.object:set_velocity({x=0,y=vel.y,z=0}) local pos = self.object:get_pos() local yaw = self.object:get_yaw() local tpos = target:get_pos() local tyaw = minetest.dir_to_yaw(vector.direction(pos, tpos)) if abs(tyaw-yaw) > 0.1 then mobkit.turn2yaw(self, tyaw, 4) elseif vec_dist(pos, tpos) < self.reach-hitbox(target)[4] and self.punch_timer <= 0 then mobkit.animate(self, "punch") target:punch(self.object, 1.0, { full_punch_interval = 0.1, damage_groups = {fleshy = self.damage} }, nil) mob_core.punch_timer(self, self.punch_cooldown) return true end end mobkit.queue_low(self, func) end ------------------ -- HQ Functions -- ------------------ -- Latch Attack -- function paleotest.hq_latch(self,prty,target) local timer = 4 local func = function(self) if not mobkit.is_alive(target) then if self.latching == target then mobkit.animate(self,"stand") self.object:set_detach() mobkit.clear_queue_high(self) self.latching = nil self.object:set_properties({ visual_size = self.base_size }) end return true end local pos = mobkit.get_stand_pos(self) local tpos = mobkit.get_stand_pos(target) local dist = vec_dist(pos,tpos) if not self.latching then timer = timer-1 end if dist <= self.collisionbox[4]+self.reach then if not self.latching then self.latching = target self.base_size = self.visual_size local target_scale = self.visual_size.x/target:get_luaentity().visual_size.x self.object:set_properties({ visual_size = {x=target_scale,y=target_scale} }) self.object:set_attach(target, "", {x=0,y=target:get_luaentity().collisionbox[5],z=0}, {x=0,y=0,z=0}) timer = 4 end mobkit.animate(self,"latch") if mobkit.timer(self,1) then target:punch(self.object, 1.0, { full_punch_interval = 0.1, damage_groups = {fleshy = self.damage} }, nil) end end if timer <= 0 then mobkit.clear_queue_high(self) return true end end mobkit.queue_high(self,func,prty) end -- Fight and Flee -- function paleotest.hq_fight_and_flee(self, prty, target) local func = function(self) if not mobkit.is_alive(target) then return true end local pos = mobkit.get_stand_pos(self) local tpos = target:get_pos() if mobkit.is_queue_empty_low(self) then mob_core.punch_timer(self) if vec_dist(pos, tpos) < 8 and self.punch_timer <= 0 then paleotest.lq_dumb_punch(self, target) end if vec_dist(pos, tpos) < self.view_range and (vec_dist(pos, tpos) > 8 or self.punch_timer > 0) then local fpos = {x=2*pos.x - tpos.x,y=tpos.y,z=2*pos.z - tpos.z} mob_core.goto_next_waypoint(self, fpos) elseif vec_dist(pos, tpos) > self.view_range then mobkit.clear_queue_high(self) mobkit.lq_idle(self, 1) self.object:set_velocity({x=0,y=0,z=0}) return true end end end mobkit.queue_high(self, func, prty) end local function eat_from_feeder(self, feeder) if not feeder then return end local node = minetest.get_node(feeder) local on_feed = minetest.registered_nodes[node.name].on_feed local meta = minetest.get_meta(feeder) local food_level = meta:get_int("food_level") local hunger = self.max_hunger - self.hunger if food_level - hunger >= 0 then self.hunger = mobkit.remember(self,"hunger",self.hunger+hunger) meta:set_int("food_level",food_level - hunger) on_feed(feeder, hunger) if self.hunger > self.max_hunger then self.hunger = mobkit.remember(self, "hunger", self.max_hunger) end else self.hunger = mobkit.remember(self,"hunger",self.hunger + food_level) meta:set_int("food_level", 0) on_feed(feeder, hunger) if self.hunger > self.max_hunger then self.hunger = mobkit.remember(self, "hunger", self.max_hunger) end end end -- Eat from Feeder -- function paleotest.hq_go_to_feeder(self, prty, search_for) local timer = 16 local func = function(self) local pos = mobkit.get_stand_pos(self) local pos1 = {x=pos.x -self.view_range,y=pos.y-self.view_range,z=pos.z-self.view_range} local pos2 = {x=pos.x +self.view_range,y=pos.y+self.view_range,z=pos.z+self.view_range} local nodes = minetest.find_nodes_in_area(pos1, pos2, search_for) if mobkit.is_queue_empty_low(self) and self.isonground then local feeder if #nodes < 1 then return true end for _,i in ipairs(nodes) do local meta = minetest.get_meta(i) local food_level = meta:get_int("food_level") if food_level > 1 then feeder = i break end end if not feeder then return true end timer = timer - self.dtime if timer <= 0 then mobkit.clear_queue_high(self) end local meta = minetest.get_meta(feeder) local food_level = meta:get_int("food_level") if food_level < 1 then return true end if vec_dist(pos,feeder) > self.collisionbox[4]+2 then mob_core.goto_next_waypoint(self,feeder) else eat_from_feeder(self,feeder) paleotest.feeder_update_formspec(meta) paleotest.particle_spawner(feeder, "farming_bread.png", "splash") mobkit.lq_idle(self,1) return true end end end mobkit.queue_high(self,func,prty) end -- Eat from Aquatic Feeder -- function paleotest.hq_aqua_go_to_feeder(self,prty,search_for) local func = function(self) local pos = mobkit.get_stand_pos(self) local pos1 = {x=pos.x -self.view_range,y=pos.y-1,z=pos.z-self.view_range} local pos2 = {x=pos.x +self.view_range,y=pos.y+1,z=pos.z+self.view_range} local nodes = minetest.find_nodes_in_area(pos1,pos2,search_for) if mobkit.is_queue_empty_low(self) and self.isinliquid then local feeder if #nodes < 1 then return true end for _,i in ipairs(nodes) do local meta = minetest.get_meta(i) local food_level = meta:get_int("food_level") if food_level > 1 then feeder = i break end end if not feeder then return true end local meta = minetest.get_meta(feeder) local food_level = meta:get_int("food_level") if food_level < 1 then return true end if vec_dist(pos,feeder) > self.collisionbox[4]+3 then mob_core.swim_to_next_waypoint(self,feeder) else eat_from_feeder(self,feeder) paleotest.feeder_update_formspec(meta) paleotest.particle_spawner(feeder, "farming_bread.png", "splash") return true end end end mobkit.queue_high(self,func,prty) end -- Eat dropped items -- function paleotest.hq_eat_items(self, prty) local func = function(self) local pos = self.object:get_pos() local objs = minetest.get_objects_inside_radius(pos, self.view_range) if #objs < 1 then return true end for _, obj in ipairs(objs) do local ent = obj:get_luaentity() if mobkit.is_queue_empty_low(self) then for i = 1, #self.follow do if ent and ent.name == "__builtin:item" and ent.itemstring:match(self.follow[i]) then local food = obj:get_pos() if vec_dist(pos, food) > self.collisionbox[4] + 2 then mob_core.goto_next_waypoint(self, food) else mobkit.lq_turn2pos(self, food) mobkit.lq_idle(self, 1, "punch") local stack = ItemStack(ent.itemstring) local max_count = stack:get_stack_max() local count = math.min(stack:get_count(), max_count) self.hunger = mobkit.remember(self, "hunger", self.hunger + count) obj:punch(self.object, 1.0, { full_punch_interval = 0.1, damage_groups = {} }, nil) return true end else return true end end end end end mobkit.queue_high(self,func,prty) end -- Graze from Ground -- function paleotest.hq_graze(self, prty, search_for) local timer = 8 local func = function(self) local pos = mobkit.get_stand_pos(self) local pos1 = {x=pos.x-self.view_range,y=pos.y-self.view_range,z=pos.z-self.view_range} local pos2 = {x=pos.x+self.view_range,y=pos.y+self.view_range,z=pos.z+self.view_range} local food = minetest.find_nodes_in_area(pos1, pos2, search_for) if #food < 1 then return true end local go_to = find_closest_pos(pos, food) if mobkit.is_queue_empty_low(self) and self.isonground and go_to then if vec_dist(pos, go_to) > self.collisionbox[4]+1 then timer = timer - self.dtime mob_core.goto_next_waypoint(self, go_to) if timer <= 0 then return true end else mobkit.lq_idle(self,0.1) minetest.set_node(go_to, {name="air"}) return true end end end mobkit.queue_high(self,func,prty) end -- Graze from Trees -- function paleotest.hq_graze_high(self,prty,search_for,height) local timer = 12 local func = function(self) local pos = mobkit.get_stand_pos(self) local pos1 = {x=pos.x-self.view_range,y=pos.y-1,z=pos.z-self.view_range} local pos2 = {x=pos.x+self.view_range,y=pos.y+height,z=pos.z+self.view_range} local food = minetest.find_nodes_in_area(pos1, pos2, search_for) if #food < 1 then return true end local go_to = find_closest_pos(pos, food) if mobkit.is_queue_empty_low(self) and self.isonground and go_to then if vec_dist(pos, go_to) > self.collisionbox[4] + self.reach then timer = timer - self.dtime mob_core.goto_next_waypoint(self, go_to) if timer <= 0 then return true end else self.object:set_velocity({x=0,y=0,z=0}) minetest.set_node(go_to, {name="air"}) return true end end end mobkit.queue_high(self,func,prty) end -- Flop when out of water -- function paleotest.hq_flop(self, prty) local init = true local func = function(self) if self.isinliquid then return true end if not mobkit.is_alive(self) then return true end if init then mobkit.lq_fallover(self) init = false end local pos = mobkit.get_stand_pos(self) local yaw = self.object:get_yaw() local vel = self.object:get_velocity() local pos1 = {x=pos.x-self.view_range,y=pos.y-self.view_range/4,z=pos.z-self.view_range} local pos2 = {x=pos.x+self.view_range,y=pos.y+self.view_range/4,z=pos.z+self.view_range} local water = minetest.find_nodes_in_area(pos1, pos2, paleotest.global_liquid) if #water < 1 then if self.isonground then vel.y = vel.y + 6 self.object:add_velocity(vector.multiply(vel, minetest.yaw_to_dir(yaw+math.random(0.1, 6.3)))) end else if self.isonground then vel.y = vel.y + 6 self.object:add_velocity(vector.multiply(vel, vector.direction(pos, water[1]))) end end end mobkit.queue_high(self, func, prty) end -- Animated Roar -- function paleotest.hq_roar(self, prty) local init = true local duration = anim_length(self, "roar") local func=function(self) if mobkit.is_queue_empty_low(self) then if init then mobkit.animate(self, "roar") init=false mobkit.make_sound(self,"roar") end duration = duration-self.dtime if duration <= 0 then return true end end end mobkit.queue_high(self,func,prty) end ----------- -- Sleep -- ----------- local function get_time() local time local timeofday = minetest.get_timeofday() if not timeofday then return nil end timeofday = timeofday * 24000 if timeofday < 4500 or timeofday > 19500 then time = "night" else time = "day" end return time end function paleotest.lq_go_to_sleep(self) local func = function(self) mobkit.animate(self, "sleep") self.status = mobkit.remember(self, "status", "sleeping") if not self.child or (self.child and not self.child_sleep_overlay) then self.object:set_texture_mod("^"..self.sleep_overlay) else self.object:set_texture_mod("^"..self.child_sleep_overlay) end mobkit.clear_queue_low(self) return true end mobkit.queue_low(self,func) end function paleotest.lq_wakeup(self) local func = function(self) mobkit.animate(self, "stand") self.status = mobkit.remember(self, "status", "") self.object:set_texture_mod("") mobkit.clear_queue_high(self) mobkit.clear_queue_low(self) return true end mobkit.queue_low(self,func) end function paleotest.hq_sleep(self, prty) local func = function(self) local time = get_time() if self.status ~= "sleeping" and time ~= self.sleeps_at then self.sleep_timer = mobkit.remember(self, "sleep_timer", 10) return true end if self.status ~= "sleeping" and time == self.sleeps_at then paleotest.lq_go_to_sleep(self) end if self.status == "sleeping" and time == self.sleeps_at then if not self.child or (self.child and not self.child_sleep_overlay) then self.object:set_texture_mod("^"..self.sleep_overlay) else self.object:set_texture_mod("^"..self.child_sleep_overlay) end mobkit.animate(self, "sleep") end if self.status == "sleeping" and time ~= self.sleeps_at then paleotest.lq_wakeup(self) end end mobkit.queue_high(self, func, prty) end ------------- -- Playing -- ------------- function paleotest.logic_play_with_ball(self, prty) local func = function(self) local pos = self.object:get_pos() local objs = minetest.get_objects_inside_radius(pos, self.view_range) if #objs < 1 then return true end for _, obj in ipairs(objs) do if obj:get_luaentity() and obj:get_luaentity().name == "paleotest:pursuit_ball_ent" then if mobkit.is_queue_empty_low(self) then local obj_pos = obj:get_pos() if vec_dist(pos, obj_pos) > self.collisionbox[4] + self.reach then mob_core.goto_next_waypoint(self, obj_pos) else mobkit.lq_turn2pos(self, obj_pos) mobkit.lq_idle(self, 1, "punch") obj:punch(self.object, 1.0, { full_punch_interval = 0.1, damage_groups = {} }, nil) if self.mood < 100 then self.mood = mobkit.remember(self, "mood", self.mood + 5) else self.mood = mobkit.remember(self, "mood", 100) end return true end end else return true end end end mobkit.queue_high(self,func,prty) end function paleotest.logic_play_with_post(self,prty) local func = function(self) local pos = mobkit.get_stand_pos(self) local pos1 = {x=pos.x -self.view_range,y=pos.y-1,z=pos.z-self.view_range} local pos2 = {x=pos.x +self.view_range,y=pos.y+1,z=pos.z+self.view_range} local post = minetest.find_nodes_in_area(pos1,pos2,"paleotest:scratching_post") if #post < 1 then return end local go_to = find_closest_pos(pos, post) if mobkit.is_queue_empty_low(self) and self.isonground and go_to then if vec_dist(pos, go_to) > self.collisionbox[4]+1.5 then mob_core.goto_next_waypoint(self, go_to) else mobkit.lq_idle(self, 1, "punch") paleotest.particle_spawner(self.object:get_pos(), "heart.png", "float") if self.mood < 100 then self.mood = mobkit.remember(self,"mood",self.mood+5) else self.mood = mobkit.remember(self,"mood",100) end return true end end return true end mobkit.queue_high(self,func,prty) end -------------------- -- Fight and Flee -- -------------------- function paleotest.logic_flee_or_fight(self, prty) -- Attack specified mobs if self.predators then for i = 1, #self.predators do local predator = mobkit.get_closest_entity(self, self.predators[i]) if predator and vec_dist(self.object:get_pos(), predator:get_pos()) < self.view_range and mobkit.is_alive(predator) then if (self.tamed == true and predator:get_luaentity().owner ~= self.owner) or not self.tamed then paleotest.hq_fight_and_flee(self, prty, predator) return end end end end end ------------------ -- Aerial Mount -- ------------------ function paleotest.hq_aerial_mount_logic(self, prty) local forward_speed = minetest.registered_entities[self.name].max_speed_forward local y = 0 local tvel = 0 local jump_meter = 0 local last_pos = {} local mount_state = "ground" local anim = "stand" local timer = 0.25 local init = false local func = function(self) if not self.driver then return true end if not init then self.driver:set_attach(self.object, "Torso.2", self.driver_attach_at, self.player_rotation) end local pos = mobkit.get_stand_pos(self) if timer <= 0 then last_pos = pos timer = 0.25 end local ctrl = self.driver:get_player_control() local tyaw = self.driver:get_look_horizontal() or 0 local yaw = self.object:get_yaw() local cur_vel = self.object:get_velocity() if math.abs(tyaw - yaw) > 0.1 then self.object:set_yaw(tyaw) end local vel = vector.multiply(minetest.yaw_to_dir(yaw), tvel) vel.y = y self.object:set_velocity(vel) if mount_state == "ground" then -- Move Forward if ctrl.up then tvel = forward_speed/3 end -- Jump if ctrl.jump then if self.isonground then y = (self.jump_height) + 4 end jump_meter = jump_meter + self.dtime if jump_meter > 0.5 then -- Takeoff y = 6 mount_state = "flight" end else jump_meter = 0 y = cur_vel.y end if tvel > 0 then anim = "walk" else anim = "stand" end end if mount_state == "flight" then tvel = forward_speed if ctrl.down then y = -forward_speed pos.y = pos.y - 1 timer = timer - self.dtime if timer <= 0 and last_pos and last_pos.y == pos.y then mount_state = "ground" end elseif ctrl.jump then y = forward_speed elseif not ctrl.jump and not ctrl.down then y = 0 end if self.object:get_acceleration().y < 0 then self.object:set_acceleration({x = 0, y = 0, z = 0}) -- Defy Gravity end anim = "fly" end mobkit.animate(self, anim) -- Velocity Control if mount_state == "ground" and tvel ~= 0 and not ctrl.up then tvel = 0 end if not ctrl.down and not ctrl.jump then if mount_state == "ground" then y = cur_vel.y else y = 0 end end if ctrl.sneak then mobkit.clear_queue_low(self) mobkit.clear_queue_high(self) mob_core.detach(self.driver, {x = -1, y = 0, z = 0}) end end mobkit.queue_high(self, func, prty) end ------------------- -- Aerial Follow -- ------------------- function paleotest.hq_aerial_follow(self, prty, target) local center = self.object:get_pos() local timer = 5 local func = function(self) if not mobkit.is_alive(target) then return true end if mobkit.is_queue_empty_low(self) and not self.isinliquid then local pos = mobkit.get_stand_pos(self) local pos2 = target:get_pos() if vector.distance(pos, pos2) > 12 then timer = timer - self.dtime end if timer <= 0 and vector.distance(pos, center) < 12 then self.object:add_velocity({x = 0, y = 2, z = 0}) end if self.isonground then if pos2.y - pos.y > 4 then self.object:add_velocity({x = 0, y = 2, z = 0}) end if vector.distance(pos, pos2) > 1.5*self.growth_stage then mob_core.goto_next_waypoint(self, pos2) else mobkit.lq_idle(self, 1) end end if not self.isonground then mob_core.fly_to_next_waypoint(self, pos2) end end end mobkit.queue_high(self, func, prty) end