diff --git a/mods/ENTITIES/mcl_mobs/animation.lua b/mods/ENTITIES/mcl_mobs/animation.lua new file mode 100644 index 000000000..5be7b569c --- /dev/null +++ b/mods/ENTITIES/mcl_mobs/animation.lua @@ -0,0 +1,56 @@ +local math,vector = math,vector + +local speed +local self_rotation +local currentvel +local goal = vector.new(0,0,0) +local acceleration +-- +mobs.create_animation_functions = function(def,mob_register) + if def.movement_type ~= "jump" then + mob_register.set_animation = function(self) + if self.speed == 0 or vector.equals(self.direction,vector.new(0,0,0)) then + self.current_animation = 0 + self.object:set_animation(def.standing_frame, 1, 0, true) + else + if self.current_animation ~= 1 then + self.object:set_animation(def.moving_frame, 1, 0, true) + self.current_animation = 1 + end + + speed = self.object:get_velocity() + speed.y = 0 + self.object:set_animation_frame_speed(vector.distance(vector.new(0,0,0),speed)*def.animation_multiplier) + end + end + end + + --this makes the mob rotate and then die + mob_register.manage_death_animation = function(self,dtime) + if self.death_animation_timer >= 0 and self.dead == true then + self.death_animation_timer = self.death_animation_timer - dtime + + self_rotation = self.object:get_rotation() + + if self.death_rotation == "x" then + if self_rotation.x < math.pi/2 then + self_rotation.x = self_rotation.x + (dtime*2) + self.object:set_rotation(self_rotation) + end + elseif self.death_rotation == "z" then + if self_rotation.z < math.pi/2 then + self_rotation.z = self_rotation.z + (dtime*2) + self.object:set_rotation(self_rotation) + end + end + + --print(self.death_animation_timer) + currentvel = self.object:get_velocity() + acceleration = vector.new(goal.x-currentvel.x,0,goal.z-currentvel.z) + acceleration = vector.multiply(acceleration, 0.05) + self.object:add_velocity(acceleration) + self.object:set_animation(def.standing_frame, 15, 0, true) + end + end + return(mob_register) +end diff --git a/mods/ENTITIES/mcl_mobs/api_hook.lua b/mods/ENTITIES/mcl_mobs/api_hook.lua new file mode 100644 index 000000000..1875a2048 --- /dev/null +++ b/mods/ENTITIES/mcl_mobs/api_hook.lua @@ -0,0 +1,250 @@ +local minetest = minetest +--class +mobs = {} + +local path = minetest.get_modpath(minetest.get_current_modname()).."/api/" +dofile(path.."movement.lua") +dofile(path.."interaction.lua") +dofile(path.."data_handling.lua") +dofile(path.."head_code.lua") +dofile(path.."animation.lua") +dofile(path.."timers.lua") + +mobs.register_mob = function(def) + + + +local mob_register = {} + +register_mob_spawner(def.mobname,def.textures,def.mesh) + +------------------------------------------------ +mob_register.initial_properties = { + physical = def.physical, + collide_with_objects = def.collide_with_objects, + collisionbox = def.collisionbox, + visual = def.visual, + visual_size = def.visual_size, + mesh = def.mesh, + textures = def.textures, + is_visible = def.is_visible, + pointable = def.pointable, + automatic_face_movement_dir = def.automatic_face_movement_dir, + automatic_face_movement_max_rotation_per_sec = def.automatic_face_movement_max_rotation_per_sec, + makes_footstep_sound = def.makes_footstep_sound, + static_save = false, +} + + +mob_register.hp = def.hp +mob_register.max_speed = def.max_speed +mob_register.jump_timer = 0 + + +if def.head_bone then + mob_register.head_bone = def.head_bone + mobs.create_head_functions(def,mob_register) + mob_register.debug_head_pos = def.debug_head_pos + mob_register.head_directional_offset = def.head_directional_offset + mob_register.head_height_offset = def.head_height_offset + mob_register.head_rotation_offset = def.head_rotation_offset + mob_register.head_position_correction = def.head_position_correction + mob_register.head_coord = def.head_coord + mob_register.flip_pitch = def.flip_pitch +else + --print("create some other functions to turn mob " .. def.mobname) +end + +mob_register.hurt_inside_timer = 0 +mob_register.death_animation_timer = 0 +mob_register.dead = false + +mob_register.mob = true +mob_register.mobname = def.mobname + +mob_register.hostile = def.hostile +if def.friendly_in_daylight == true then + mob_register.friendly_in_daylight = def.friendly_in_daylight + mob_register.friendly_in_daylight_timer = 0 +end + +mob_register.hostile_cooldown = def.hostile_cooldown + +mob_register.hostile_timer = 0 +mob_register.timer = 0 + +mob_register.state = def.state + +mob_register.hunger = 200 + +mob_register.view_distance = def.view_distance + +mob_register.punch_timer = 0 +mob_register.punched_timer = 0 +mob_register.group_attack = def.group_attack + +mob_register.death_rotation = def.death_rotation + +mob_register.head_mount = def.head_mount +mob_register.rotational_correction = def.rotational_correction or 0 + +mob_register.hurt_sound = def.hurt_sound +mob_register.die_sound = def.die_sound + +mob_register.attack_type = def.attack_type +if def.attack_type == "explode" then + mob_register.tnt_tick_timer = 0 + mob_register.explosion_type = def.explosion_type +end +mob_register.explosion_radius = def.explosion_radius +mob_register.explosion_power = def.explosion_power +mob_register.tnt_timer = nil +mob_register.explosion_time = def.explosion_time +mob_register.explosion_blink_color = def.explosion_blink_color or "white" +mob_register.explosion_blink_timer = def.explosion_blink_timer or 0.2 + +mob_register.custom_function_begin = def.custom_function_begin +mob_register.custom_function = def.custom_function +mob_register.custom_function_end = def.custom_function_end + +mob_register.projectile_timer_cooldown = def.projectile_timer_cooldown +mob_register.attacked_hostile = def.attacked_hostile +if not def.hostile and not def.attacked_hostile then + mob_register.scared = false + mob_register.scared_timer = 0 +end +mob_register.attack_damage = def.attack_damage + +mob_register.projectile_timer = 0 +mob_register.projectile_type = def.projectile_type + +mob_register.takes_fall_damage = def.takes_fall_damage or true +mob_register.make_jump_noise = def.make_jump_noise +mob_register.jump_animation = def.jump_animation +mob_register.jumping_frame = def.jumping_frame + +mob_register.item_drop = def.item_drop +mob_register.item_minimum = def.item_minimum or 1 +mob_register.item_max = def.item_max + +mob_register.die_in_light = def.die_in_light +mob_register.die_in_light_level = def.die_in_light_level + +mob_register.current_animation = 0 +mob_register.hurt_color_timer = 0 +mob_register.damage_color = def.damage_color or "red" +mob_register.custom_on_death = def.custom_on_death + +mob_register.custom_on_activate = def.custom_on_activate + +mob_register.custom_on_punch = def.custom_on_punch + +mob_register.c_mob_data = def.c_mob_data + +mob_register.deactivating = false + +mob_register.on_fire = false + +mob_register.fire_table = def.fire_table + +mob_register.sound_pitch_mod_min = def.sound_pitch_mod_min +mob_register.sound_pitch_mod_max = def.sound_pitch_mod_max + +mob_register.sound_pitch_mod_min_die = def.sound_pitch_mod_min_die +mob_register.sound_pitch_mod_max_die = def.sound_pitch_mod_max_die + + +if def.pathfinds then + --mob_register.path = {} + mob_register.pathfinding_timer = 0 +end + +if def.custom_timer then + mob_register.c_timer = 0 + mob_register.custom_timer = def.custom_timer + mob_register.custom_timer_function = def.custom_timer_function +end + +mobs.create_movement_functions(def,mob_register) +mobs.create_interaction_functions(def,mob_register) +mobs.create_data_handling_functions(def,mob_register) +mobs.create_animation_functions(def,mob_register) +mobs.create_timer_functions(def,mob_register) + + +mob_register.on_step = function(self, dtime,moveresult) + if self.custom_function_begin then + self.custom_function_begin(self,dtime) + end + + self.collision_detection(self) + if self.fall_damage then + self.fall_damage(self) + end + + if self.dead == false and self.death_animation_timer == 0 then + if self.do_custom_timer then + self.do_custom_timer(self,dtime) + end + + if self.custom_function then + self.custom_function(self,dtime,moveresult) + end + + self.move(self,dtime,moveresult) + + self.flow(self) + --self.debug_nametag(self,dtime) + + self.manage_hurt_color_timer(self,dtime) + + if self.manage_scared_timer then + self.manage_scared_timer(self,dtime) + end + + if self.set_animation then + self.set_animation(self) + end + + if self.look_around then + self.look_around(self,dtime) + end + + if self.pathfinding then + self.pathfinding(self,dtime) + end + + if self.handle_friendly_in_daylight_timer then + self.handle_friendly_in_daylight_timer(self,dtime) + end + + self.manage_punch_timer(self,dtime) + else + self.manage_death_animation(self,dtime) + if self.move_head then + self.move_head(self,nil,dtime) + end + end + + --fix zombie state again + if self.dead == true and self.death_animation_timer <= 0 then + self.on_death(self) + end + + if self.tnt_timer then + self.manage_explode_timer(self,dtime) + end + + if self.projectile_timer then + self.manage_projectile_timer(self,dtime) + end + + if self.custom_function_end then + self.custom_function_end(self,dtime) + end +end + +minetest.register_entity("mob:"..def.mobname, mob_register) +------------------------------------------------ + +end diff --git a/mods/ENTITIES/mcl_mobs/data_handling.lua b/mods/ENTITIES/mcl_mobs/data_handling.lua new file mode 100644 index 000000000..3b8753170 --- /dev/null +++ b/mods/ENTITIES/mcl_mobs/data_handling.lua @@ -0,0 +1,105 @@ +local +vector,minetest,math,pairs += +vector,minetest,math,pairs +-- +mobs.create_data_handling_functions = function(def,mob_register) + + --mob_register.get_staticdata = function(self) + --[[ + return minetest.serialize({ + --range = self.range, + hp = self.hp, + hunger = self.hunger, + hostile = self.hostile, + hostile_timer = self.hostile_timer, + death_animation_timer = self.death_animation_timer, + dead = self.dead, + tnt_timer = self.tnt_timer, + tnt_tick_timer = self.tnt_tick_timer, + tnt_mod_state = self.tnt_mod_state, + punch_timer = self.punch_timer, + projectile_timer = self.projectile_timer, + scared = self.scared, + scared_timer = self.scared_timer, + c_mob_data = self.c_mob_data, + on_fire = self.on_fire, + }) + ]]-- + --end + + mob_register.on_activate = function(self)--, staticdata, dtime_s) + --[[ + if string.sub(staticdata, 1, string.len("return")) == "return" then + local data = minetest.deserialize(staticdata) + if data and type(data) == "table" then + --self.range = data.range + self.hp = data.hp + self.hunger = data.hunger + self.hostile = data.hostile + self.hostile_timer = data.hostile_timer + self.death_animation_timer = data.death_animation_timer + self.dead = data.dead + self.tnt_timer = data.tnt_timer + self.tnt_tick_timer = data.tnt_tick_timer + self.tnt_mod_state = data.tnt_mod_state + self.punch_timer = data.punch_timer + self.projectile_timer = data.projectile_timer + self.scared = data.scared + self.scared_timer = data.scared_timer + self.c_mob_data = data.c_mob_data + self.on_fire = data.on_fire + end + end + ]]-- + --set up mob + self.object:set_armor_groups({immortal = 1}) + --self.object:set_velocity({x = math.random(-5,5), y = 5, z = math.random(-5,5)}) + self.object:set_acceleration(def.gravity) + self.object:set_animation(def.standing_frame, 0, 0, true) + self.current_animation = 0 + self.object:set_hp(self.hp) + self.direction = vector.new(math.random()*math.random(-1,1),0,math.random()*math.random(-1,1)) + + + --set the head up + if self.head_bone then + self.object:set_bone_position(self.head_bone, self.head_position_correction, vector.new(0,0,0)) + end + self.is_mob = true + self.object:set_armor_groups({immortal = 1}) + + if self.custom_on_activate then + self.custom_on_activate(self) + end + + if self.on_fire == true then + start_fire(self.object) + end + + --use this to handle the global mob table + --minetest.after(0,function() + -- self.deactivating = true + --end) + end + + --this is the info on the mob + mob_register.debug_nametag = function(self,dtime) + --we're doing this to the child because the nametage breaks the + --animation on the mob's body + + --we add in items we want to see in this list + local debug_items = {"hostile","hostile_timer"} + local text = "" + for _,item in pairs(debug_items) do + if self[item] ~= nil then + text = text..item..": "..tostring(self[item]).."\n" + end + end + self.object:set_nametag_attributes({ + color = "white", + text = text + }) + end + return(mob_register) +end diff --git a/mods/ENTITIES/mcl_mobs/head_code.lua b/mods/ENTITIES/mcl_mobs/head_code.lua new file mode 100644 index 000000000..d9d57e1f6 --- /dev/null +++ b/mods/ENTITIES/mcl_mobs/head_code.lua @@ -0,0 +1,287 @@ +local +minetest,math,vector += +minetest,math,vector + +--converts the degrees to radians +local degrees_to_radians = function(degrees) + --print(d) + return(degrees/180.0*math.pi) +end + +--converts yaw to degrees +local degrees = function(yaw) + return(yaw*180.0/math.pi) +end + +--rounds it up to an integer +local degree_round = function(degree) + return(degree + 0.5 - (degree + 0.5) % 1) +end +--turns radians into degrees - not redundant +--doesn't add math.pi +local radians_to_degrees = function(radians) + return(radians*180.0/math.pi) +end + +--make sure this is redefined as shown below aka +--don't run mob_rotation_degree_to_radians(rotation) +--run local radians = mob_rotation_degree_to_radians(rotation) +--or the mobs head rotation will become overwritten +local head_rotation_to_radians = function(rotation) + return{ + x = 0, --roll should never be changed + y = degrees_to_radians(180 - rotation.y)*-1, + z = degrees_to_radians(90 - rotation.z) + } +end + +-- + +local pos +local body_yaw +local head_yaw +local dir +local head_position +local head_rotation +mcl_mobs.create_head_functions = function(def,mob_register) + --a movement test to move the head + mob_register.move_head = function(self,pos2,dtime) + if self.head_bone then + if self.head_coord == "horizontal" then + --print(self.head_bone) + head_position,head_rotation = self.object:get_bone_position(self.head_bone) + --[[ debug + if rotation then + --print("--------------------------------") + --rotation.x = rotation.x + 1 + rotation.z = rotation.z + 1 + rotation.y = 0 + + if rotation.x > 90 then + rotation.x = -90 + end + if rotation.z > 90 then + rotation.z = -90 + end + + --print(rotation.x) + self.object:set_bone_position(self.head_bone, head_position, rotation) + end + ]]-- + + --if passed a direction to look + pos = self.object:get_pos() + body_yaw = self.object:get_yaw()-math.pi/2+self.rotational_correction + + dir = vector.multiply(minetest.yaw_to_dir(body_yaw),self.head_directional_offset) + + + body_yaw = minetest.dir_to_yaw(dir) + + --pos is where the head actually is + pos = vector.add(pos,dir) + pos.y = pos.y + self.head_height_offset + + --use this to literally look around + --self.head_pos = pos + + --if self.debug_head_pos == true then + -- minetest.add_particle({ + -- pos = pos, + -- velocity = {x=0, y=0, z=0}, + -- acceleration = {x=0, y=0, z=0}, + -- expirationtime = 0.2, + -- size = 1, + -- texture = "dirt.png", + -- }) + --end + + --if the function was given a pos + if pos2 then + --compare the head yaw to the body + --we must do a bunch of calculations to correct + --strange function returns + --for some reason get_yaw is offset 90 degrees + head_yaw = minetest.dir_to_yaw(vector.direction(pos,pos2)) + head_yaw = minetest.dir_to_yaw(minetest.yaw_to_dir(head_yaw)) + head_yaw = degrees(head_yaw)-degrees(body_yaw) + + if head_yaw < -180 then + head_yaw = head_yaw + 360 + elseif head_yaw > 180 then + head_yaw = head_yaw - 360 + end + + --if within range then do calculations + if head_yaw >= -90 and head_yaw <= 90 then + ---begin pitch calculation + --feed a 2D coordinate flipped into dir to yaw to calculate pitch + head_rotation.x = degrees(minetest.dir_to_yaw(vector.new(vector.distance(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z)),0,pos.y-pos2.y))+(math.pi/2)) + if self.flip_pitch then + head_rotation.x = head_rotation.x * -1 + end + head_rotation.z = head_yaw + self.object:set_bone_position(self.head_bone, head_position, head_rotation) + return(true) + --nothing to look at + else + self.return_head_to_origin(self,dtime) + return(false) + end + + --if nothing to look at + else + self.return_head_to_origin(self,dtime) + return(false) + end + + elseif self.head_coord == "vertical" then + --print(self.head_bone) + head_position,head_rotation = self.object:get_bone_position(self.head_bone) + --[[ debug + if rotation then + --print("--------------------------------") + --rotation.x = rotation.x + 1 + rotation.z = rotation.z + 1 + rotation.y = 0 + + if rotation.x > 90 then + rotation.x = -90 + end + if rotation.z > 90 then + rotation.z = -90 + end + + --print(rotation.x) + self.object:set_bone_position(self.head_bone, head_position, rotation) + end + ]]-- + + --print(self.head_rotation.y) + --if passed a direction to look + pos = self.object:get_pos() + body_yaw = self.object:get_yaw()-math.pi/2+self.rotational_correction + + dir = vector.multiply(minetest.yaw_to_dir(body_yaw),self.head_directional_offset) + + + body_yaw = minetest.dir_to_yaw(dir) + + --pos is where the head actually is + pos = vector.add(pos,dir) + pos.y = pos.y + self.head_height_offset + + --use this to literally look around + --self.head_pos = pos + + --if self.debug_head_pos == true then + -- minetest.add_particle({ + -- pos = pos, + -- velocity = {x=0, y=0, z=0}, + -- acceleration = {x=0, y=0, z=0}, + -- expirationtime = 0.2, + -- size = 1, + -- texture = "dirt.png", + -- }) + --end + + --if the function was given a pos + if pos2 then + --compare the head yaw to the body + --we must do a bunch of calculations to correct + --strange function returns + --for some reason get_yaw is offset 90 degrees + head_yaw = minetest.dir_to_yaw(vector.direction(pos,pos2)) + head_yaw = minetest.dir_to_yaw(minetest.yaw_to_dir(head_yaw)) + head_yaw = degrees(head_yaw)-degrees(body_yaw) + + if head_yaw < -180 then + head_yaw = head_yaw + 360 + elseif head_yaw > 180 then + head_yaw = head_yaw - 360 + end + + --if within range then do calculations + if head_yaw >= -90 and head_yaw <= 90 then + ---begin pitch calculation + --feed a 2D coordinate flipped into dir to yaw to calculate pitch + head_rotation.x = degrees(minetest.dir_to_yaw(vector.new(vector.distance(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z)),0,pos.y-pos2.y))+(math.pi/2)) + if self.flip_pitch then + head_rotation.x = head_rotation.x * -1 + end + head_rotation.y = -head_yaw + self.object:set_bone_position(self.head_bone, head_position, head_rotation) + return(true) + --nothing to look at + else + self.return_head_to_origin(self,dtime) + return(false) + end + + --if nothing to look at + else + self.return_head_to_origin(self,dtime) + return(false) + end + end + end + end + + + --this sets the mob to move it's head back to pointing forwards + + mob_register.return_head_to_origin = function(self,dtime) + head_position,head_rotation = self.object:get_bone_position(self.head_bone) + + if self.head_coord == "horizontal" then + --make the head yaw move back + if head_rotation.x > 0 then + head_rotation.x = head_rotation.x - (dtime*100) + elseif head_rotation.x < 0 then + head_rotation.x = head_rotation.x + (dtime*100) + end + + if math.abs(head_rotation.x) < (dtime*100) then + head_rotation.x = 0 + end + + + --move up down (pitch) back to center + if head_rotation.z > 0 then + head_rotation.z = head_rotation.z - (dtime*100) + elseif head_rotation.z < 0 then + head_rotation.z = head_rotation.z + (dtime*100) + end + + if math.abs(head_rotation.z) < (dtime*100) then + head_rotation.z = 0 + end + elseif self.head_coord == "vertical" then + --make the head yaw move back + if head_rotation.x > 0 then + head_rotation.x = head_rotation.x - (dtime*100) + elseif head_rotation.x < 0 then + head_rotation.x = head_rotation.x + (dtime*100) + end + + if math.abs(head_rotation.x) < (dtime*100) then + head_rotation.x = 0 + end + + + --move up down (pitch) back to center + if head_rotation.y > 0 then + head_rotation.y = head_rotation.y - (dtime*100) + elseif head_rotation.y < 0 then + head_rotation.y = head_rotation.y + (dtime*100) + end + + if math.abs(head_rotation.y) < (dtime*100) then + head_rotation.y = 0 + end + end + self.object:set_bone_position(self.head_bone, head_position, head_rotation) + end + return(mob_register) +end diff --git a/mods/ENTITIES/mcl_mobs/head_code0.lua b/mods/ENTITIES/mcl_mobs/head_code0.lua new file mode 100644 index 000000000..4211fb9b4 --- /dev/null +++ b/mods/ENTITIES/mcl_mobs/head_code0.lua @@ -0,0 +1,287 @@ +local +minetest,math,vector += +minetest,math,vector + +--converts the degrees to radians +local degrees_to_radians = function(degrees) + --print(d) + return(degrees/180.0*math.pi) +end + +--converts yaw to degrees +local degrees = function(yaw) + return(yaw*180.0/math.pi) +end + +--rounds it up to an integer +local degree_round = function(degree) + return(degree + 0.5 - (degree + 0.5) % 1) +end +--turns radians into degrees - not redundant +--doesn't add math.pi +local radians_to_degrees = function(radians) + return(radians*180.0/math.pi) +end + +--make sure this is redefined as shown below aka +--don't run mob_rotation_degree_to_radians(rotation) +--run local radians = mob_rotation_degree_to_radians(rotation) +--or the mobs head rotation will become overwritten +local head_rotation_to_radians = function(rotation) + return{ + x = 0, --roll should never be changed + y = degrees_to_radians(180 - rotation.y)*-1, + z = degrees_to_radians(90 - rotation.z) + } +end + +-- + +local pos +local body_yaw +local head_yaw +local dir +local head_position +local head_rotation +mobs.create_head_functions = function(def,mob_register) + --a movement test to move the head + mob_register.move_head = function(self,pos2,dtime) + if self.head_bone then + if self.head_coord == "horizontal" then + --print(self.head_bone) + head_position,head_rotation = self.object:get_bone_position(self.head_bone) + --[[ debug + if rotation then + --print("--------------------------------") + --rotation.x = rotation.x + 1 + rotation.z = rotation.z + 1 + rotation.y = 0 + + if rotation.x > 90 then + rotation.x = -90 + end + if rotation.z > 90 then + rotation.z = -90 + end + + --print(rotation.x) + self.object:set_bone_position(self.head_bone, head_position, rotation) + end + ]]-- + + --if passed a direction to look + pos = self.object:get_pos() + body_yaw = self.object:get_yaw()-math.pi/2+self.rotational_correction + + dir = vector.multiply(minetest.yaw_to_dir(body_yaw),self.head_directional_offset) + + + body_yaw = minetest.dir_to_yaw(dir) + + --pos is where the head actually is + pos = vector.add(pos,dir) + pos.y = pos.y + self.head_height_offset + + --use this to literally look around + --self.head_pos = pos + + --if self.debug_head_pos == true then + -- minetest.add_particle({ + -- pos = pos, + -- velocity = {x=0, y=0, z=0}, + -- acceleration = {x=0, y=0, z=0}, + -- expirationtime = 0.2, + -- size = 1, + -- texture = "dirt.png", + -- }) + --end + + --if the function was given a pos + if pos2 then + --compare the head yaw to the body + --we must do a bunch of calculations to correct + --strange function returns + --for some reason get_yaw is offset 90 degrees + head_yaw = minetest.dir_to_yaw(vector.direction(pos,pos2)) + head_yaw = minetest.dir_to_yaw(minetest.yaw_to_dir(head_yaw)) + head_yaw = degrees(head_yaw)-degrees(body_yaw) + + if head_yaw < -180 then + head_yaw = head_yaw + 360 + elseif head_yaw > 180 then + head_yaw = head_yaw - 360 + end + + --if within range then do calculations + if head_yaw >= -90 and head_yaw <= 90 then + ---begin pitch calculation + --feed a 2D coordinate flipped into dir to yaw to calculate pitch + head_rotation.x = degrees(minetest.dir_to_yaw(vector.new(vector.distance(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z)),0,pos.y-pos2.y))+(math.pi/2)) + if self.flip_pitch then + head_rotation.x = head_rotation.x * -1 + end + head_rotation.z = head_yaw + self.object:set_bone_position(self.head_bone, head_position, head_rotation) + return(true) + --nothing to look at + else + self.return_head_to_origin(self,dtime) + return(false) + end + + --if nothing to look at + else + self.return_head_to_origin(self,dtime) + return(false) + end + + elseif self.head_coord == "vertical" then + --print(self.head_bone) + head_position,head_rotation = self.object:get_bone_position(self.head_bone) + --[[ debug + if rotation then + --print("--------------------------------") + --rotation.x = rotation.x + 1 + rotation.z = rotation.z + 1 + rotation.y = 0 + + if rotation.x > 90 then + rotation.x = -90 + end + if rotation.z > 90 then + rotation.z = -90 + end + + --print(rotation.x) + self.object:set_bone_position(self.head_bone, head_position, rotation) + end + ]]-- + + --print(self.head_rotation.y) + --if passed a direction to look + pos = self.object:get_pos() + body_yaw = self.object:get_yaw()-math.pi/2+self.rotational_correction + + dir = vector.multiply(minetest.yaw_to_dir(body_yaw),self.head_directional_offset) + + + body_yaw = minetest.dir_to_yaw(dir) + + --pos is where the head actually is + pos = vector.add(pos,dir) + pos.y = pos.y + self.head_height_offset + + --use this to literally look around + --self.head_pos = pos + + --if self.debug_head_pos == true then + -- minetest.add_particle({ + -- pos = pos, + -- velocity = {x=0, y=0, z=0}, + -- acceleration = {x=0, y=0, z=0}, + -- expirationtime = 0.2, + -- size = 1, + -- texture = "dirt.png", + -- }) + --end + + --if the function was given a pos + if pos2 then + --compare the head yaw to the body + --we must do a bunch of calculations to correct + --strange function returns + --for some reason get_yaw is offset 90 degrees + head_yaw = minetest.dir_to_yaw(vector.direction(pos,pos2)) + head_yaw = minetest.dir_to_yaw(minetest.yaw_to_dir(head_yaw)) + head_yaw = degrees(head_yaw)-degrees(body_yaw) + + if head_yaw < -180 then + head_yaw = head_yaw + 360 + elseif head_yaw > 180 then + head_yaw = head_yaw - 360 + end + + --if within range then do calculations + if head_yaw >= -90 and head_yaw <= 90 then + ---begin pitch calculation + --feed a 2D coordinate flipped into dir to yaw to calculate pitch + head_rotation.x = degrees(minetest.dir_to_yaw(vector.new(vector.distance(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z)),0,pos.y-pos2.y))+(math.pi/2)) + if self.flip_pitch then + head_rotation.x = head_rotation.x * -1 + end + head_rotation.y = -head_yaw + self.object:set_bone_position(self.head_bone, head_position, head_rotation) + return(true) + --nothing to look at + else + self.return_head_to_origin(self,dtime) + return(false) + end + + --if nothing to look at + else + self.return_head_to_origin(self,dtime) + return(false) + end + end + end + end + + + --this sets the mob to move it's head back to pointing forwards + + mob_register.return_head_to_origin = function(self,dtime) + head_position,head_rotation = self.object:get_bone_position(self.head_bone) + + if self.head_coord == "horizontal" then + --make the head yaw move back + if head_rotation.x > 0 then + head_rotation.x = head_rotation.x - (dtime*100) + elseif head_rotation.x < 0 then + head_rotation.x = head_rotation.x + (dtime*100) + end + + if math.abs(head_rotation.x) < (dtime*100) then + head_rotation.x = 0 + end + + + --move up down (pitch) back to center + if head_rotation.z > 0 then + head_rotation.z = head_rotation.z - (dtime*100) + elseif head_rotation.z < 0 then + head_rotation.z = head_rotation.z + (dtime*100) + end + + if math.abs(head_rotation.z) < (dtime*100) then + head_rotation.z = 0 + end + elseif self.head_coord == "vertical" then + --make the head yaw move back + if head_rotation.x > 0 then + head_rotation.x = head_rotation.x - (dtime*100) + elseif head_rotation.x < 0 then + head_rotation.x = head_rotation.x + (dtime*100) + end + + if math.abs(head_rotation.x) < (dtime*100) then + head_rotation.x = 0 + end + + + --move up down (pitch) back to center + if head_rotation.y > 0 then + head_rotation.y = head_rotation.y - (dtime*100) + elseif head_rotation.y < 0 then + head_rotation.y = head_rotation.y + (dtime*100) + end + + if math.abs(head_rotation.y) < (dtime*100) then + head_rotation.y = 0 + end + end + self.object:set_bone_position(self.head_bone, head_position, head_rotation) + end + return(mob_register) +end diff --git a/mods/ENTITIES/mcl_mobs/interaction.lua b/mods/ENTITIES/mcl_mobs/interaction.lua new file mode 100644 index 000000000..fa8055834 --- /dev/null +++ b/mods/ENTITIES/mcl_mobs/interaction.lua @@ -0,0 +1,506 @@ +local +minetest,math,vector,ipairs += +minetest,math,vector,ipairs +-- +--for add sword wear +local itemstack +local wear +--for collision detection +local pos +local collision_count +local collisionbox +local collision_boundary +local radius +local pos2 +local object_collisionbox +local object_collision_boundary +local y_base_diff +local y_top_diff +local distance +local dir +local velocity +local vel1 +local vel2 +--for fall damage +local vel +local damage +--for on_punch +local hp +local hurt +local critical +local puncher_vel +--for item drop +local item +local data_item_amount +--for look around +local light_level +local player_found +local player_punch_timer +local line_of_sight +local obj +local sound_max +local sound_min +local head_pos +local light +local fire_it_up +mobs.create_interaction_functions = function(def,mob_register) + + mob_register.flow = function(self) + local flow_dir = flow(self.object:get_pos()) + if flow_dir then + flow_dir = vector.multiply(flow_dir,10) + local vel = self.object:get_velocity() + local acceleration = vector.new(flow_dir.x-vel.x,flow_dir.y-vel.y,flow_dir.z-vel.z) + acceleration = vector.multiply(acceleration, 0.01) + self.object:add_velocity(acceleration) + end + end + + --the pig will look for and at players + mob_register.look_around = function(self,dtime) + pos = self.object:get_pos() + + if self.die_in_light then + fire_it_up = false + if minetest.get_item_group(minetest.get_node(pos).name, "extinguish") > 0 then + fire_it_up = false + else + head_pos = table.copy(pos) + head_pos.y = head_pos.y + self.object:get_properties().collisionbox[5] + light = minetest.get_node_light(head_pos) + if light and light == 15 then + if weather_type == 2 then + fire_it_up = false + else + fire_it_up = true + end + end + end + if fire_it_up then + start_fire(self.object) + end + end + + if self.on_fire then + if minetest.get_item_group(minetest.get_node(pos).name, "extinguish") > 0 then + put_fire_out(self.object) + else + head_pos = table.copy(pos) + head_pos.y = head_pos.y + self.object:get_properties().collisionbox[5] + light = minetest.get_node_light(head_pos, 0.5) + if light and light == 15 then + if weather_type == 2 then + put_fire_out(self.object) + end + end + end + end + + --STARE O_O + --and follow! + self.following = false + player_found = false + + for _,object in ipairs(minetest.get_objects_inside_radius(pos, self.view_distance)) do + if object:is_player() and player_found == false and object:get_hp() > 0 then + --look at player's camera + pos2 = object:get_pos() + pos2.y = pos2.y + 1.625 + + player_found = true + + if self.head_bone then + self.move_head(self,pos2,dtime) + end + + --print(self.hostile) + if self.hostile == true then + distance = vector.distance(pos,pos2) + self.following_pos = vector.new(pos2.x,pos2.y-1.625,pos2.z) + + --punch the player + if self.attack_type == "punch" then + if distance < 2.5 and self.punch_timer <= 0 and object:get_hp() > 0 then + if player_can_be_punched(object) then + line_of_sight = minetest.line_of_sight(pos, pos2) + if line_of_sight == true then + self.punch_timer = 0.5 + object:punch(self.object, 2, + { + full_punch_interval=1.5, + damage_groups = {damage=self.attack_damage}, + },vector.direction(pos,pos2)) + --light the player on fire + if self.on_fire then + start_fire(object) + end + if is_player_on_fire(object) then + start_fire(self.object) + end + end + end + end + elseif self.attack_type == "explode" then + --mob will not explode if it cannot see you + if distance < self.explosion_radius and minetest.line_of_sight(vector.new(pos.x,pos.y+self.object:get_properties().collisionbox[5],pos.z), pos2) then + + if not self.tnt_timer then + minetest.sound_play("tnt_ignite", {object = self.object, gain = 1.0,}) + self.tnt_timer = self.explosion_time + self.tnt_tick_timer = 0.2 + self.tnt_mod_state = 1 + self.object:set_texture_mod("^[colorize:white:130") + end + end + elseif self.attack_type == "projectile" then + if not self.projectile_timer then + self.projectile_timer = self.projectile_timer_cooldown + end + if self.projectile_timer <= 0 then + self.projectile_timer = self.projectile_timer_cooldown + + obj = minetest.add_entity(vector.new(pos.x,pos.y+self.object:get_properties().collisionbox[5],pos.z), self.projectile_type) + if obj then + dir = vector.multiply(vector.direction(pos,vector.new(pos2.x,pos2.y-3,pos2.z)), 50) + obj:set_velocity(dir) + obj:get_luaentity().timer = 2 + obj:get_luaentity().owner = self.object + end + end + end + --smart + if self.path_data and table.getn(self.path_data) > 0 then + self.direction = vector.direction(vector.new(pos.x,0,pos.z), vector.new(self.path_data[1].x,0,self.path_data[1].z)) + --dumb + else + self.direction = vector.direction(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z)) + end + self.speed = self.max_speed + self.following = true + elseif self.scared == true then + self.speed = self.max_speed + self.direction = vector.direction(vector.new(pos2.x,0,pos2.z),vector.new(pos.x,0,pos.z)) + self.scared_timer = 10 + end + --only look at one player + break + end + end + --stare straight if not found + if player_found == false then + if self.move_head then + self.move_head(self,nil,dtime) + end + if self.following_pos then + self.following_pos = nil + end + if self.manage_hostile_timer then + self.manage_hostile_timer(self,dtime) + end + end + end + + + --this is what happens when a mob dies + mob_register.on_death = function(self, killer) + pos = self.object:get_pos() + if def.hp then + minetest.throw_experience(pos,math.ceil(def.hp/5)+math.random(0,1)) + end + --pos.y = pos.y + 0.4 + minetest.sound_play("mob_die", {pos = pos, gain = 1.0}) + minetest.add_particlespawner({ + amount = 40, + time = 0.001, + minpos = pos, + maxpos = pos, + minvel = vector.new(-5,-5,-5), + maxvel = vector.new(5,5,5), + minacc = {x=0, y=0, z=0}, + maxacc = {x=0, y=0, z=0}, + minexptime = 1.1, + maxexptime = 1.5, + minsize = 1, + maxsize = 2, + collisiondetection = false, + vertical = false, + texture = "smoke.png", + }) + + --only throw items if registered + if self.item_drop then + if type(self.item_drop) == "string" then + item = self.item_drop + elseif type(self.item_drop) == "table" then + item = self.item_drop[math.random(1,table.getn(self.item_drop))] + end + --detect if multiple items are going to be added + if self.item_max then + data_item_amount = math.random(self.item_minimum, self.item_max) + for i = 1 ,data_item_amount do + minetest.throw_item(vector.new(pos.x,pos.y+0.1,pos.z),item) + end + else + minetest.throw_item(vector.new(pos.x,pos.y+0.1,pos.z),item) + end + end + + + if self.custom_on_death then + self.custom_on_death(self) + end + + self.object:remove() + end + --this controls what happens when the mob gets punched + mob_register.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir) + hp = self.hp + vel = self.object:get_velocity() + hurt = tool_capabilities.damage_groups.damage + + if not hurt then + hurt = 1 + end + + critical = false + + --criticals + pos = self.object:get_pos() + if puncher:is_player() then + puncher_vel = puncher:get_player_velocity().y + if puncher_vel < 0 then + hurt = hurt * 1.5 + critical = true + end + end + + hp = hp-hurt + + if (self.punched_timer <= 0 and hp > 1) and not self.dead then + self.object:set_texture_mod("^[colorize:"..self.damage_color..":130") + self.hurt_color_timer = 0.25 + if puncher ~= self.object then + self.punched_timer = 0.25 + if self.attacked_hostile then + self.hostile = true + self.hostile_timer = 20 + if self.group_attack == true then + for _,object in ipairs(minetest.get_objects_inside_radius(pos, self.view_distance)) do + + if not object:is_player() and object:get_luaentity() and object:get_luaentity().mobname == self.mobname then + object:get_luaentity().hostile = true + object:get_luaentity().hostile_timer = 20 + end + end + end + else + self.scared = true + self.scared_timer = 10 + end + if self.custom_on_punch then + self.custom_on_punch(self) + end + end + + --critical effect + if critical == true then + self.do_critical_particles(pos) + minetest.sound_play("critical", {object=self.object, gain = 0.1, max_hear_distance = 10,pitch = math.random(80,100)/100}) + end + + sound_min = self.sound_pitch_mod_min or 100 + sound_max = self.sound_pitch_mod_max or 140 + + minetest.sound_play(self.hurt_sound, {object=self.object, gain = 1.0, max_hear_distance = 10,pitch = math.random(sound_min,sound_max)/100}) + + self.hp = hp + + dir = vector.multiply(dir,10) + if vel.y <= 0 then + dir.y = 4 + else + dir.y = 0 + end + + + self.object:add_velocity(dir) + self.add_sword_wear(self, puncher, time_from_last_punch, tool_capabilities, dir) + elseif (self.punched_timer <= 0 and self.death_animation_timer == 0) then + self.object:set_texture_mod("^[colorize:"..self.damage_color..":130") + self.hurt_color_timer = 0.25 + if puncher ~= self.object then + self.punched_timer = 0.25 + if self.attacked_hostile then + self.hostile = true + self.hostile_timer = 20 + if self.group_attack == true then + for _,object in ipairs(minetest.get_objects_inside_radius(pos, self.view_distance)) do + if not object:is_player() and object:get_luaentity() and object:get_luaentity().mobname == self.mobname then + object:get_luaentity().hostile = true + object:get_luaentity().hostile_timer = 20 + end + end + end + end + if self.custom_on_punch then + self.custom_on_punch(self) + end + end + self.death_animation_timer = 1 + self.dead = true + + --critical effect + if critical == true then + self.do_critical_particles(pos) + minetest.sound_play("critical", {object=self.object, gain = 0.1, max_hear_distance = 10,pitch = math.random(80,100)/100}) + end + + sound_min = self.sound_pitch_mod_min_die or 80 + sound_max = self.sound_pitch_mod_max_die or 100 + + minetest.sound_play(self.die_sound, {object=self.object, gain = 1.0, max_hear_distance = 10,pitch = math.random(sound_min,sound_max)/100}) + + self.add_sword_wear(self, puncher, time_from_last_punch, tool_capabilities, dir) + end + end + + mob_register.collision_detection = function(self) + pos = self.object:get_pos() + --do collision detection from the base of the mob + + collisionbox = self.object:get_properties().collisionbox + + pos.y = pos.y + collisionbox[2] + + collision_boundary = collisionbox[4] + + radius = collision_boundary + + if collisionbox[5] > collision_boundary then + radius = collisionbox[5] + end + collision_count = 0 + for _,object in ipairs(minetest.get_objects_inside_radius(pos, radius*1.25)) do + if object ~= self.object and (object:is_player() or object:get_luaentity().mob == true) and + --don't collide with rider, rider don't collide with thing + (not object:get_attach() or (object:get_attach() and object:get_attach() ~= self.object)) and + (not self.object:get_attach() or (self.object:get_attach() and self.object:get_attach() ~= object)) then + --stop infinite loop + collision_count = collision_count + 1 + if collision_count > 100 then + break + end + pos2 = object:get_pos() + + object_collisionbox = object:get_properties().collisionbox + + pos2.y = pos2.y + object_collisionbox[2] + + object_collision_boundary = object_collisionbox[4] + + + --this is checking the difference of the object collided with's possision + --if positive top of other object is inside (y axis) of current object + y_base_diff = (pos2.y + object_collisionbox[5]) - pos.y + + y_top_diff = (pos.y + collisionbox[5]) - pos2.y + + + distance = vector.distance(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z)) + + if distance <= collision_boundary + object_collision_boundary and y_base_diff >= 0 and y_top_diff >= 0 then + + dir = vector.direction(pos,pos2) + dir.y = 0 + + --eliminate mob being stuck in corners + if dir.x == 0 and dir.z == 0 then + dir = vector.new(math.random(-1,1)*math.random(),0,math.random(-1,1)*math.random()) + end + + velocity = vector.multiply(dir,1.1) + + vel1 = vector.multiply(velocity, -1) + vel2 = velocity + + self.object:add_velocity(vel1) + + if object:is_player() then + object:add_player_velocity(vel2) + if self.on_fire then + start_fire(object) + end + if is_player_on_fire(object) then + start_fire(self.object) + end + else + object:add_velocity(vel2) + if self.on_fire then + start_fire(object) + end + if object:get_luaentity().on_fire then + start_fire(self.object) + end + end + end + end + end + end + + --the sword wear mechanic + mob_register.add_sword_wear = function(self, puncher, time_from_last_punch, tool_capabilities, dir) + if puncher:is_player() then + itemstack = puncher:get_wielded_item() + wear = itemstack:get_definition().mob_hit_wear + if wear then + itemstack:add_wear(wear) + if itemstack:get_name() == "" then + minetest.sound_play("tool_break",{to_player = puncher:get_player_name(),gain=0.4}) + end + puncher:set_wielded_item(itemstack) + end + end + end + + --critical effect particles + mob_register.do_critical_particles = function(pos) + minetest.add_particlespawner({ + amount = 40, + time = 0.001, + minpos = pos, + maxpos = pos, + minvel = vector.new(-2,-2,-2), + maxvel = vector.new(2,8,2), + minacc = {x=0, y=4, z=0}, + maxacc = {x=0, y=12, z=0}, + minexptime = 1.1, + maxexptime = 1.5, + minsize = 1, + maxsize = 2, + collisiondetection = false, + vertical = false, + texture = "critical.png", + }) + end + + if def.takes_fall_damage == nil or def.takes_fall_damage == true then + mob_register.fall_damage = function(self) + vel = self.object:get_velocity() + if vel and self.oldvel then + if self.oldvel.y < -7 and vel.y == 0 then + damage = math.abs(self.oldvel.y + 7) + damage = math.floor(damage/1.5) + self.object:punch(self.object, 2, + { + full_punch_interval=1.5, + damage_groups = {damage=damage}, + }) + end + end + self.oldvel = vel + end + end + + return(mob_register) +end \ No newline at end of file diff --git a/mods/ENTITIES/mcl_mobs/movement.lua b/mods/ENTITIES/mcl_mobs/movement.lua new file mode 100644 index 000000000..dafe5dfb2 --- /dev/null +++ b/mods/ENTITIES/mcl_mobs/movement.lua @@ -0,0 +1,400 @@ +local +minetest,math,vector,pairs,table += +minetest,math,vector,pairs,table + +local pos +local node +local vel +local goal +local acceleration +local hurt +local fire +local currentvel +local goal +local y +local modifier +local pos2 +local ray +local pointed_thing + +local acute_pos +local height_diff +local acute_following_pos +local min +local max +local index_table +local path +local number +local pos1 +local pos3 +local can_cut +local _ + +--index all mods +local all_walkable_nodes = {} +minetest.register_on_mods_loaded(function() + for name in pairs(minetest.registered_nodes) do + if name ~= "air" and name ~= "ignore" then + if minetest.get_nodedef(name,"walkable") then + table.insert(all_walkable_nodes,name) + end + end + end +end) + +mobs.create_movement_functions = function(def,mob_register) + --makes the mob swim + mob_register.swim = function(self,dtime) + pos = self.object:get_pos() + pos.y = pos.y + 0.3 + node = minetest.get_node(pos).name + self.swimming = false + if node == "main:water" or node =="main:waterflow" then + vel = self.object:get_velocity() + goal = 3 + acceleration = vector.new(0,goal-vel.y,0) + --jump out of the water + if (vel.x == 0 and self.direction.x ~= 0) or (vel.z == 0 and self.direction.z ~= 0) then + self.object:set_velocity(vector.new(vel.x,5,vel.z)) + --else swim + else + self.object:add_velocity(acceleration) + end + self.swimming = true + end + end + + mob_register.hurt_inside = function(self,dtime) + if self.hp > 0 and self.hurt_inside_timer <= 0 then + pos = self.object:get_pos() + node = minetest.get_node(pos).name + hurt = minetest.get_item_group(node, "hurt_inside") + if hurt > 0 then + self.object:punch(self.object, 2, + { + full_punch_interval=1.5, + damage_groups = {damage=hurt}, + }) + end + fire = minetest.get_item_group(node, "fire") + if not self.on_fire and fire > 0 then + start_fire(self.object) + end + self.hurt_inside_timer = 0.25 + else + self.hurt_inside_timer = self.hurt_inside_timer - dtime + end + end + + --This makes the mob walk at a certain speed and jump + if def.movement_type == "walk" then + mob_register.move = function(self,dtime,moveresult) + self.manage_jump_timer(self,dtime) + self.timer = self.timer - dtime + + --jump + self.jump(self,moveresult) + + --swim + self.swim(self,dtime) + + --print(self.timer) + --direction state change + if self.timer <= 0 and not self.following == true then + --print("changing direction") + self.timer = math.random(2,7) + self.direction = vector.new(math.random()*math.random(-1,1),0,math.random()*math.random(-1,1)) + --local yaw = self.object:get_yaw() + dtime + self.speed = math.random(0,self.max_speed) + --self.object:set_yaw(yaw) + end + + self.hurt_inside(self,dtime) + + currentvel = self.object:get_velocity() + goal = vector.multiply(self.direction,self.speed) + acceleration = vector.new(goal.x-currentvel.x,0,goal.z-currentvel.z) + if self.whip_turn then + self.whip_turn = self.whip_turn - dtime + if self.whip_turn <= 0 then + self.whip_turn = nil + end + else + acceleration = vector.multiply(acceleration, 0.05) + end + self.object:add_velocity(acceleration) + end + mob_register.jump = function(self,moveresult) + if moveresult and moveresult.touching_ground and self.direction then + pos = self.object:get_pos() + pos.y = pos.y+0.1 + + if self.path_data and table.getn(self.path_data) > 0 then + --smart jump + y = math.floor(pos.y+0.5) + vel = self.object:get_velocity() + if y < self.path_data[1].y then + self.object:set_velocity(vector.new(vel.x,5,vel.z)) + elseif self.path_data[2] and y < self.path_data[2].y then + self.object:set_velocity(vector.new(vel.x,5,vel.z)) + elseif self.path_data[3] and y < self.path_data[3].y then + self.object:set_velocity(vector.new(vel.x,5,vel.z)) + elseif ((vel.x == 0 and self.direction.x ~= 0) or (vel.z == 0 and self.direction.z ~= 0)) then + self.object:set_velocity(vector.new(vel.x,5,vel.z)) + end + else + --assume collisionbox is even x and z + modifier = self.object:get_properties().collisionbox[4]*3 + + + pos2 = vector.add(vector.multiply(self.direction,modifier),pos) + + ray = minetest.raycast(pos, pos2, false, false) + + pointed_thing = nil + + if ray then + pointed_thing = ray:next() + end + + if pointed_thing then + if minetest.get_nodedef(minetest.get_node(pointed_thing.under).name, "walkable") then + --print("jump") + vel = self.object:get_velocity() + --self.jump_timer = 1+math.random() + self.object:set_velocity(vector.new(vel.x,5,vel.z)) + else + --print("velocity check") + vel = self.object:get_velocity() + if (vel.x == 0 and self.direction.x ~= 0) or (vel.z == 0 and self.direction.z ~= 0) then + self.object:set_velocity(vector.new(vel.x,5,vel.z)) + end + end + else + --print("velcheck 2") + vel = self.object:get_velocity() + if (vel.x == 0 and self.direction.x ~= 0) or (vel.z == 0 and self.direction.z ~= 0) then + self.object:set_velocity(vector.new(vel.x,5,vel.z)) + end + end + end + end + end + elseif def.movement_type == "jump" then + mob_register.move = function(self,dtime,moveresult) + self.manage_jump_timer(self,dtime) + self.timer = self.timer - dtime + + --jump + self.jump(self,moveresult) + + --swim + self.swim(self,dtime) + + --direction state change + if self.timer <= 0 and not self.following == true then + --print("changing direction") + self.timer = math.random(2,7) + self.direction = vector.new(math.random()*math.random(-1,1),0,math.random()*math.random(-1,1)) + --local yaw = self.object:get_yaw() + dtime + self.speed = math.random(0,self.max_speed) + --self.object:set_yaw(yaw) + end + + self.hurt_inside(self,dtime) + + currentvel = self.object:get_velocity() + if currentvel.y ~= 0 then + goal = vector.multiply(self.direction,self.speed) + acceleration = vector.new(goal.x-currentvel.x,0,goal.z-currentvel.z) + acceleration = vector.multiply(acceleration, 0.05) + self.object:add_velocity(acceleration) + end + end + + mob_register.jump = function(self,moveresult) + if moveresult and moveresult.touching_ground and self.direction then + if self.jump_timer <= 0 then + if self.make_jump_noise then + minetest.sound_play("slime_splat", {object=self.object, gain = 1.0, max_hear_distance = 10,pitch = math.random(80,100)/100}) + end + vel = self.object:get_velocity() + self.object:set_velocity(vector.new(vel.x,5,vel.z)) + if self.following == true then + self.jump_timer = 0.5 + else + self.jump_timer = 1+math.random() + end + else + self.object:set_velocity(vector.new(0,0,0)) + end + end + end + end + + if def.pathfinds then + mob_register.pathfinding = function(self,dtime) + acute_pos = vector.floor(vector.add(self.object:get_pos(),0.5)) + if self.following and self.following_pos then + self.pathfinding_timer = self.pathfinding_timer + dtime + height_diff = nil + if self.object:get_pos().y > self.following_pos.y then + height_diff = math.abs(self.object:get_pos().y-self.following_pos.y) + elseif self.object:get_pos().y <= self.following_pos.y then + height_diff = math.abs(self.following_pos.y-self.object:get_pos().y) + end + --delete path if height too far + if self.path_data and height_diff > self.view_distance/2 then + self.path_data = nil + self.old_path_pos = nil + self.old_acute_following_pos = nil + return + end + + if self.pathfinding_timer >= 0.5 and height_diff <= self.view_distance/2 then + acute_following_pos = vector.floor(vector.add(self.following_pos,0.5)) + + if (not self.old_path_pos or (self.old_path_pos and not vector.equals(acute_pos,self.old_path_pos))) and + (not self.old_acute_following_pos or (self.old_acute_following_pos and vector.distance(self.old_acute_following_pos,acute_following_pos) > 2)) then + + --if a player tries to hide in a node + if minetest.get_nodedef(minetest.get_node(acute_following_pos).name, "walkable") then + acute_following_pos.y = acute_following_pos.y + 1 + end + + --if a player tries to stand off the side of a node + if not minetest.get_nodedef(minetest.get_node(vector.new(acute_following_pos.x,acute_following_pos.y-1,acute_following_pos.z)).name, "walkable") then + min = vector.subtract(acute_following_pos,1) + max = vector.add(acute_following_pos,1) + + index_table = minetest.find_nodes_in_area_under_air(min, max, all_walkable_nodes) + --optimize this as much as possible + for _,i_pos in pairs(index_table) do + if minetest.get_nodedef(minetest.get_node(i_pos).name, "walkable") then + acute_following_pos = vector.new(i_pos.x,i_pos.y+1,i_pos.z) + break + end + end + end + + path = minetest.find_path(acute_pos,acute_following_pos,self.view_distance,1,1,"A*_noprefetch") + --if the path fails then raycast down to scare player or accidentally find new path + --disabled for extreme cpu usage + --[[ + if not path then + local ray = minetest.raycast(acute_following_pos, vector.new(acute_following_pos.x,acute_following_pos.y-self.view_distance,acute_following_pos.z), false, false) + for pointed_thing in ray do + if pointed_thing.above then + path = minetest.find_path(self.object:get_pos(),pointed_thing.above,self.view_distance,1,5,"A*_noprefetch") + break + end + end + end + ]]-- + if path then + self.whip_turn = 0.025 + self.path_data = path + + --remove the first element of the list + --shift whole list down + for i = 2,table.getn(self.path_data) do + self.path_data[i-1] = self.path_data[i] + end + self.path_data[table.getn(self.path_data)] = nil + + --cut corners (go diagonal) + if self.path_data and table.getn(self.path_data) >= 3 then + number = 3 + for i = 3,table.getn(self.path_data) do + pos1 = self.path_data[number-2] + pos2 = self.path_data[number] + + --print(number) + --check if diagonal and has direct line of sight + if pos1 and pos2 and pos1.x ~= pos2.x and pos1.z ~= pos2.z and pos1.y == pos2.y then + pos3 = vector.divide(vector.add(pos1,pos2),2) + pos3.y = pos3.y - 1 + can_cut,_ = minetest.line_of_sight(pos1, pos2) + if can_cut then + + if minetest.get_nodedef(minetest.get_node(pos3).name, "walkable") == true then + --shift whole list down + --print("removing"..number-1) + for z = number-1,table.getn(self.path_data) do + self.path_data[z-1] = self.path_data[z] + end + self.path_data[table.getn(self.path_data)] = nil + number = number + 2 + else + number = number + 1 + end + else + number = number + 1 + end + if number > table.getn(self.path_data) then + break + end + else + number = number + 1 + end + end + --if self.path_data and table.getn(self.path_data) <= 2 then + -- self.path_data = nil + --end + end + end + + self.old_path_pos = acute_pos + self.old_acute_following_pos = acute_following_pos + end + end + elseif (not self.following and self.path_data) or (self.path_data and height_diff > self.view_distance/2) then + self.path_data = nil + self.old_path_pos = nil + self.old_acute_following_pos = nil + end + --[[ + if self.path_data then + for index,pos_data in pairs(self.path_data) do + --print(dump(pos_data)) + minetest.add_particle({ + pos = pos_data, + velocity = {x=0, y=0, z=0}, + acceleration = {x=0, y=0, z=0}, + expirationtime = 0.01, + size = 1, + texture = "dirt.png", + }) + end + end + ]]-- + --this is the real time path deletion as it goes along it + if self.swimming == true then + self.path_data = nil + end + + if self.path_data and table.getn(self.path_data) > 0 then + if vector.distance(acute_pos,self.path_data[1]) <= 1 then + --shift whole list down + for i = 2,table.getn(self.path_data) do + self.path_data[i-1] = self.path_data[i] + end + self.path_data[table.getn(self.path_data)] = nil + self.whip_turn = 0.01 + --if table.getn(self.path_data) == 0 then + -- self.path_data = nil + --end + end + end + --charge at the player + if self.path_data and table.getn(self.path_data) < 2 then + self.path_data = nil + end + + end + end + + return(mob_register) +end + diff --git a/mods/ENTITIES/mcl_mobs/timers.lua b/mods/ENTITIES/mcl_mobs/timers.lua new file mode 100644 index 000000000..1e8135bf7 --- /dev/null +++ b/mods/ENTITIES/mcl_mobs/timers.lua @@ -0,0 +1,116 @@ +local +minetest,math += +minetest,math +local pos +local light +mobs.create_timer_functions = function(def,mob_register) + --this controls how fast the mob punches + mob_register.manage_punch_timer = function(self,dtime) + if self.punch_timer > 0 then + self.punch_timer = self.punch_timer - dtime + end + --this controls how fast you can punch the mob (punched timer reset) + if self.punched_timer > 0 then + --print(self.punched_timer) + self.punched_timer = self.punched_timer - dtime + end + end + + --this controls the hostile state + if def.hostile == true or def.attacked_hostile == true then + if def.hostile_cooldown == true then + mob_register.manage_hostile_timer = function(self,dtime) + if self.hostile_timer > 0 then + self.hostile_timer = self.hostile_timer - dtime + end + if self.hostile_timer <= 0 then + self.hostile = false + end + end + end + else + mob_register.manage_scared_timer = function(self,dtime) + if self.scared_timer > 0 then + self.scared_timer = self.scared_timer - dtime + end + if self.scared_timer <= 0 then + self.scared = false + end + end + end + + mob_register.manage_hurt_color_timer = function(self,dtime) + if self.hurt_color_timer > 0 then + self.hurt_color_timer = self.hurt_color_timer - dtime + if self.hurt_color_timer <= 0 then + self.hurt_color_timer = 0 + self.object:set_texture_mod("") + end + end + end + + mob_register.manage_explode_timer = function(self,dtime) + self.tnt_timer = self.tnt_timer - dtime + self.tnt_tick_timer = self.tnt_tick_timer - dtime + if self.tnt_tick_timer <= 0 and not self.dead then + self.tnt_tick_timer = self.explosion_blink_timer + self.tnt_mod_state = math.abs(self.tnt_mod_state-1) + if self.tnt_mod_state == 0 then + self.object:set_texture_mod("") + else + self.object:set_texture_mod("^[colorize:"..self.explosion_blink_color..":130") + end + --print(self.object:get_texture_mod()) + --self.object:set_texture_mod("^[colorize:red:130") + end + if self.tnt_timer <= 0 and not self.dead then + + self.object:set_texture_mod("^[colorize:red:130") + + pos = self.object:get_pos() + self.object:remove() + tnt(pos,self.explosion_power,self.explosion_type) + end + end + + if def.custom_timer then + mob_register.do_custom_timer = function(self,dtime) + self.c_timer = self.c_timer + dtime + if self.c_timer >= self.custom_timer then + self.c_timer = 0 + self.custom_timer_function(self,dtime) + end + end + end + + mob_register.manage_projectile_timer = function(self,dtime) + self.projectile_timer = self.projectile_timer - dtime + end + + if def.friendly_in_daylight then + mob_register.handle_friendly_in_daylight_timer = function(self,dtime) + self.friendly_in_daylight_timer = self.friendly_in_daylight_timer + dtime + if self.friendly_in_daylight_timer >= 2 then + self.friendly_in_daylight_timer = 0 + pos = self.object:get_pos() + light = minetest.get_node_light(pos) + if pos and light and light >= 13 then --1 greater than torch light + if self.following == false then + self.hostile = false + end + else + self.hostile = true + end + end + end + end + + --this stops the pig from flying into the air + mob_register.manage_jump_timer = function(self,dtime) + if self.jump_timer > 0 then + self.jump_timer = self.jump_timer - dtime + end + end + return(mob_register) +end