From 8c648d1fc3e627abd5e8371e07832bb92915205a Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Mon, 2 Jan 2023 00:00:40 +0000 Subject: [PATCH 01/22] Refactor mob_step and do_states --- mods/ENTITIES/mcl_mobs/api.lua | 126 ++--- mods/ENTITIES/mcl_mobs/combat.lua | 394 +++++++++++++++ mods/ENTITIES/mcl_mobs/effects.lua | 1 + mods/ENTITIES/mcl_mobs/movement.lua | 756 +++++++--------------------- mods/ENTITIES/mcl_mobs/physics.lua | 30 ++ 5 files changed, 666 insertions(+), 641 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index a898ed979..46e113c54 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -297,51 +297,67 @@ function mob_class:mob_activate(staticdata, def, dtime) end end +-- execute current state (stand, walk, run, attacks) +-- returns true if mob has died +function mob_class:do_states(dtime) + --if self.can_open_doors then check_doors(self) end + if self.state == "stand" then + self:do_states_stand() + elseif self.state == PATHFINDING then + self:check_gowp(dtime) + elseif self.state == "walk" then + self:do_state_walk() + elseif self.state == "runaway" then + -- runaway when punched + self:do_states_runaway() + elseif self.state == "attack" then + -- attack routines (explode, dogfight, shoot, dogshoot) + if self:do_state_attack(dtime) then + return true + end + end +end + +local function update_timers (self, dtime) + -- knockback timer + if self.pause_timer > 0 then + self.pause_timer = self.pause_timer - dtime + return true + end + + -- attack timer + self.timer = self.timer + dtime + + if self.state ~= "attack" and self.state ~= PATHFINDING then + if self.timer < 1 then + return true + end + self.timer = 0 + end + + -- never go over 100 + if self.timer > 100 then + self.timer = 1 + end +end -- main mob function function mob_class:on_step(dtime) self.lifetimer = self.lifetimer - dtime + local pos = self.object:get_pos() if not pos then return end + if self:check_despawn(pos) then return true end - local d = 0.85 - if self:check_dying() then d = 0.92 end - - local v = self.object:get_velocity() - if v then - --diffuse object velocity - self.object:set_velocity({x = v.x*d, y = v.y, z = v.z*d}) - end - + self:slow_mob() if self:falling(pos) then return end self:check_suspend() self:check_water_flow() - local yaw = 0 - if self:is_at_water_danger() and self.state ~= "attack" then - if math.random(1, 10) <= 6 then - self:set_velocity(0) - self.state = "stand" - self:set_animation( "stand") - yaw = yaw + math.random(-0.5, 0.5) - yaw = self:set_yaw( yaw, 8) - end - else - if self.move_in_group ~= false then - self:check_herd(dtime) - end - end - - if self:is_at_cliff_or_danger() then - self:set_velocity(0) - self.state = "stand" - self:set_animation( "stand") - local yaw = self.object:get_yaw() or 0 - yaw = self:set_yaw( yaw + 0.78, 8) - end + self:env_danger_movement_checks (dtime) if not self.fire_resistant then mcl_burning.tick(self.object, dtime, self) @@ -378,63 +394,23 @@ function mob_class:on_step(dtime) -- run custom function (defined in mob lua file) if self.do_custom then - - -- when false skip going any further if self.do_custom(self, dtime) == false then return end end - -- knockback timer - if self.pause_timer > 0 then + if update_timers(self, dtime) then return end - self.pause_timer = self.pause_timer - dtime - - return - end - - -- attack timer - self.timer = self.timer + dtime - - if self.state ~= "attack" and self.state ~= PATHFINDING then - if self.timer < 1 then - return - end - self.timer = 0 - end self:check_particlespawners(dtime) self:check_item_pickup() - -- never go over 100 - if self.timer > 100 then - self.timer = 1 - end - - -- mob plays random sound at times + -- mob plays random sound at times. Should be 120. Zombie and mob farms are ridiculous if math.random(1, 70) == 1 then self:mob_sound("random", true) end - -- environmental damage timer (every 1 second) - self.env_damage_timer = self.env_damage_timer + dtime - - if (self.state == "attack" and self.env_damage_timer > 1) - or self.state ~= "attack" then - self:check_entity_cramming() - self.env_damage_timer = 0 - - -- check for environmental damage (water, fire, lava etc.) - if self:do_env_damage() then - return - end - - -- node replace check (cow eats grass etc.) - self:replace(pos) - end - - if self:do_states(dtime) then - return - end + if self:env_damage (dtime, pos) then return end + if self:do_states(dtime) then return end if not self.object:get_luaentity() then return false diff --git a/mods/ENTITIES/mcl_mobs/combat.lua b/mods/ENTITIES/mcl_mobs/combat.lua index 5577a4361..24eb2f290 100644 --- a/mods/ENTITIES/mcl_mobs/combat.lua +++ b/mods/ENTITIES/mcl_mobs/combat.lua @@ -794,3 +794,397 @@ function mob_class:check_aggro(dtime) end self._check_aggro_timer = self._check_aggro_timer + dtime end + +function mob_class:do_state_attack (dtime) + local yaw = self.object:get_yaw() or 0 + + local s = self.object:get_pos() + local p = self.attack:get_pos() or s + + -- stop attacking if player invisible or out of range + if not self.attack + or not self.attack:get_pos() + or not self:object_in_range(self.attack) + or self.attack:get_hp() <= 0 + or (self.attack:is_player() and mcl_mobs.invis[ self.attack:get_player_name() ]) then + + self.state = "stand" + self:set_velocity( 0) + self:set_animation( "stand") + self.attack = nil + self.v_start = false + self.timer = 0 + self.blinktimer = 0 + self.path.way = nil + + return + end + + -- calculate distance from mob and enemy + local dist = vector.distance(p, s) + + if self.attack_type == "explode" then + + local vec = { + x = p.x - s.x, + z = p.z - s.z + } + + yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate + + if p.x > s.x then yaw = yaw +math.pi end + + yaw = self:set_yaw( yaw, 0, dtime) + + local node_break_radius = self.explosion_radius or 1 + local entity_damage_radius = self.explosion_damage_radius + or (node_break_radius * 2) + + -- start timer when in reach and line of sight + if not self.v_start + and dist <= self.reach + and self:line_of_sight( s, p, 2) then + + self.v_start = true + self.timer = 0 + self.blinktimer = 0 + self:mob_sound("fuse", nil, false) + + -- stop timer if out of reach or direct line of sight + elseif self.allow_fuse_reset + and self.v_start + and (dist >= self.explosiontimer_reset_radius + or not self:line_of_sight( s, p, 2)) then + self.v_start = false + self.timer = 0 + self.blinktimer = 0 + self.blinkstatus = false + self:remove_texture_mod("^[brighten") + end + + -- walk right up to player unless the timer is active + if self.v_start and (self.stop_to_explode or dist < self.reach) then + self:set_velocity( 0) + else + self:set_velocity( self.run_velocity) + end + + if self.animation and self.animation.run_start then + self:set_animation( "run") + else + self:set_animation( "walk") + end + + if self.v_start then + + self.timer = self.timer + dtime + self.blinktimer = (self.blinktimer or 0) + dtime + + if self.blinktimer > 0.2 then + + self.blinktimer = 0 + + if self.blinkstatus then + self:remove_texture_mod("^[brighten") + else + self:add_texture_mod("^[brighten") + end + + self.blinkstatus = not self.blinkstatus + end + + if self.timer > self.explosion_timer then + + local pos = self.object:get_pos() + + if mobs_griefing and not minetest.is_protected(pos, "") then + mcl_explosions.explode(mcl_util.get_object_center(self.object), self.explosion_strength, { drop_chance = 1.0 }, self.object) + else + minetest.sound_play(self.sounds.explode, { + pos = pos, + gain = 1.0, + max_hear_distance = self.sounds.distance or 32 + }, true) + self:entity_physics(pos,entity_damage_radius) + mcl_mobs.effect(pos, 32, "mcl_particles_smoke.png", nil, nil, node_break_radius, 1, 0) + end + mcl_burning.extinguish(self.object) + self.object:remove() + + return true + end + end + + elseif self.attack_type == "dogfight" + or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 2) and (dist >= self.avoid_distance or not self.shooter_avoid_enemy) + or (self.attack_type == "dogshoot" and dist <= self.reach and self:dogswitch() == 0) then + + if self.fly + and dist > self.reach then + + local p1 = s + local me_y = math.floor(p1.y) + local p2 = p + local p_y = math.floor(p2.y + 1) + local v = self.object:get_velocity() + + if self:flight_check( s) then + + if me_y < p_y then + + self.object:set_velocity({ + x = v.x, + y = 1 * self.walk_velocity, + z = v.z + }) + + elseif me_y > p_y then + + self.object:set_velocity({ + x = v.x, + y = -1 * self.walk_velocity, + z = v.z + }) + end + else + if me_y < p_y then + + self.object:set_velocity({ + x = v.x, + y = 0.01, + z = v.z + }) + + elseif me_y > p_y then + + self.object:set_velocity({ + x = v.x, + y = -0.01, + z = v.z + }) + end + end + + end + + -- rnd: new movement direction + if self.path.following + and self.path.way + and self.attack_type ~= "dogshoot" then + + -- no paths longer than 50 + if #self.path.way > 50 + or dist < self.reach then + self.path.following = false + return + end + + local p1 = self.path.way[1] + + if not p1 then + self.path.following = false + return + end + + if math.abs(p1.x-s.x) + math.abs(p1.z - s.z) < 0.6 then + -- reached waypoint, remove it from queue + table.remove(self.path.way, 1) + end + + -- set new temporary target + p = {x = p1.x, y = p1.y, z = p1.z} + end + + local vec = { + x = p.x - s.x, + z = p.z - s.z + } + + yaw = (atan(vec.z / vec.x) + math.pi / 2) - self.rotate + + if p.x > s.x then yaw = yaw + math.pi end + + yaw = self:set_yaw( yaw, 0, dtime) + + -- move towards enemy if beyond mob reach + if dist > self.reach then + + -- path finding by rnd + if self.pathfinding -- only if mob has pathfinding enabled + and enable_pathfinding then + + self:smart_mobs(s, p, dist, dtime) + end + + if self:is_at_cliff_or_danger() then + + self:set_velocity( 0) + self:set_animation( "stand") + local yaw = self.object:get_yaw() or 0 + yaw = self:set_yaw( yaw + 0.78, 8) + else + + if self.path.stuck then + self:set_velocity( self.walk_velocity) + else + self:set_velocity( self.run_velocity) + end + + if self.animation and self.animation.run_start then + self:set_animation( "run") + else + self:set_animation( "walk") + end + end + + else -- rnd: if inside reach range + + self.path.stuck = false + self.path.stuck_timer = 0 + self.path.following = false -- not stuck anymore + + self:set_velocity( 0) + + if not self.custom_attack then + + if self.timer > 1 then + + self.timer = 0 + + if self.double_melee_attack + and math.random(1, 2) == 1 then + self:set_animation( "punch2") + else + self:set_animation( "punch") + end + + local p2 = p + local s2 = s + + p2.y = p2.y + .5 + s2.y = s2.y + .5 + + if self:line_of_sight( p2, s2) == true then + + -- play attack sound + self:mob_sound("attack") + + -- punch player (or what player is attached to) + local attached = self.attack:get_attach() + if attached then + self.attack = attached + end + self.attack:punch(self.object, 1.0, { + full_punch_interval = 1.0, + damage_groups = {fleshy = self.damage} + }, nil) + end + end + else -- call custom attack every second + if self.custom_attack + and self.timer > 1 then + + self.timer = 0 + + self.custom_attack(self, p) + end + end + end + + elseif self.attack_type == "shoot" + or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 1) + or (self.attack_type == "dogshoot" and (dist > self.reach or dist < self.avoid_distance and self.shooter_avoid_enemy) and self:dogswitch() == 0) then + + p.y = p.y - .5 + s.y = s.y + .5 + + local dist = vector.distance(p, s) + local vec = { + x = p.x - s.x, + y = p.y - s.y, + z = p.z - s.z + } + + yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate + + if p.x > s.x then yaw = yaw +math.pi end + + yaw = self:set_yaw( yaw, 0, dtime) + + local stay_away_from_player = vector.new(0,0,0) + + --strafe back and fourth + + --stay away from player so as to shoot them + if dist < self.avoid_distance and self.shooter_avoid_enemy then + self:set_animation( "shoot") + stay_away_from_player=vector.multiply(vector.direction(p, s), 0.33) + end + + if self.strafes then + if not self.strafe_direction then + self.strafe_direction = 1.57 + end + if math.random(40) == 1 then + self.strafe_direction = self.strafe_direction*-1 + end + self.acc = vector.add(vector.multiply(vector.rotate_around_axis(vector.direction(s, p), vector.new(0,1,0), self.strafe_direction), 0.3*self.walk_velocity), stay_away_from_player) + else + self:set_velocity( 0) + end + + local p = self.object:get_pos() + p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2 + + if self.shoot_interval + and self.timer > self.shoot_interval + and not minetest.raycast(vector.add(p, vector.new(0,self.shoot_offset,0)), vector.add(self.attack:get_pos(), vector.new(0,1.5,0)), false, false):next() + and math.random(1, 100) <= 60 then + + self.timer = 0 + self:set_animation( "shoot") + + -- play shoot attack sound + self:mob_sound("shoot_attack") + + -- Shoot arrow + if minetest.registered_entities[self.arrow] then + + local arrow, ent + local v = 1 + if not self.shoot_arrow then + self.firing = true + minetest.after(1, function() + self.firing = false + end) + arrow = minetest.add_entity(p, self.arrow) + ent = arrow:get_luaentity() + if ent.velocity then + v = ent.velocity + end + ent.switch = 1 + ent.owner_id = tostring(self.object) -- add unique owner id to arrow + + -- important for mcl_shields + ent._shooter = self.object + ent._saved_shooter_pos = self.object:get_pos() + end + + local amount = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) ^ 0.5 + -- offset makes shoot aim accurate + vec.y = vec.y + self.shoot_offset + vec.x = vec.x * (v / amount) + vec.y = vec.y * (v / amount) + vec.z = vec.z * (v / amount) + if self.shoot_arrow then + vec = vector.normalize(vec) + self:shoot_arrow(p, vec) + else + arrow:set_velocity(vec) + end + end + end + else + + end +end diff --git a/mods/ENTITIES/mcl_mobs/effects.lua b/mods/ENTITIES/mcl_mobs/effects.lua index 476c13469..81ebdbd2e 100644 --- a/mods/ENTITIES/mcl_mobs/effects.lua +++ b/mods/ENTITIES/mcl_mobs/effects.lua @@ -116,6 +116,7 @@ function mob_class:mob_sound(soundname, is_opinion, fixed_pitch) -- randomize the pitch a bit pitch = pitch + math.random(-10, 10) * 0.005 end + -- Should be 0.1 to 0.2 for mobs. Cow and zombie farms loud. At least have cool down. minetest.sound_play(sound, { object = self.object, gain = 1.0, diff --git a/mods/ENTITIES/mcl_mobs/movement.lua b/mods/ENTITIES/mcl_mobs/movement.lua index 2e8b4198c..753f79281 100644 --- a/mods/ENTITIES/mcl_mobs/movement.lua +++ b/mods/ENTITIES/mcl_mobs/movement.lua @@ -269,6 +269,31 @@ function mob_class:is_at_water_danger() return false end +function mob_class:env_danger_movement_checks(dtime) + local yaw = 0 + if self:is_at_water_danger() and self.state ~= "attack" then + if math.random(1, 10) <= 6 then + self:set_velocity(0) + self.state = "stand" + self:set_animation( "stand") + yaw = yaw + math.random(-0.5, 0.5) + yaw = self:set_yaw( yaw, 8) + end + else + if self.move_in_group ~= false then + self:check_herd(dtime) + end + end + + if self:is_at_cliff_or_danger() then + self:set_velocity(0) + self.state = "stand" + self:set_animation( "stand") + local yaw = self.object:get_yaw() or 0 + yaw = self:set_yaw( yaw + 0.78, 8) + end +end + -- jump if facing a solid node (not fences or gates) function mob_class:do_jump() if not self.jump @@ -769,591 +794,190 @@ function mob_class:teleport(target) end end --- execute current state (stand, walk, run, attacks) --- returns true if mob has died -function mob_class:do_states(dtime) - --if self.can_open_doors then check_doors(self) end - +function mob_class:do_state_walk() local yaw = self.object:get_yaw() or 0 - if self.state == "stand" then - if math.random(1, 4) == 1 then + local s = self.object:get_pos() + local lp = nil + + -- is there something I need to avoid? + if (self.water_damage > 0 + and self.lava_damage > 0) + or self.breath_max ~= -1 then + lp = minetest.find_node_near(s, 1, {"group:water", "group:lava"}) + elseif self.water_damage > 0 then + lp = minetest.find_node_near(s, 1, {"group:water"}) + elseif self.lava_damage > 0 then + lp = minetest.find_node_near(s, 1, {"group:lava"}) + elseif self.fire_damage > 0 then + lp = minetest.find_node_near(s, 1, {"group:fire"}) + end + + local is_in_danger = false + if lp then + -- If mob in or on dangerous block, look for land + if (self:is_node_dangerous(self.standing_in) or + self:is_node_dangerous(self.standing_on)) or (self:is_node_waterhazard(self.standing_in) or self:is_node_waterhazard(self.standing_on)) and (not self.fly) then + is_in_danger = true + + -- If mob in or on dangerous block, look for land + if is_in_danger then + -- Better way to find shore - copied from upstream + lp = minetest.find_nodes_in_area_under_air( + {x = s.x - 5, y = s.y - 0.5, z = s.z - 5}, + {x = s.x + 5, y = s.y + 1, z = s.z + 5}, + {"group:solid"}) + + lp = #lp > 0 and lp[math.random(#lp)] + + -- did we find land? + if lp then + + local vec = { + x = lp.x - s.x, + z = lp.z - s.z + } + + yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate + + + if lp.x > s.x then yaw = yaw +math.pi end + + -- look towards land and move in that direction + yaw = self:set_yaw( yaw, 6) + self:set_velocity(self.walk_velocity) - local s = self.object:get_pos() - local objs = minetest.get_objects_inside_radius(s, 3) - local lp - for n = 1, #objs do - if objs[n]:is_player() then - lp = objs[n]:get_pos() - break end end - -- look at any players nearby, otherwise turn randomly - if lp and self.look_at_players then - - local vec = { - x = lp.x - s.x, - z = lp.z - s.z - } - - yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate - - if lp.x > s.x then yaw = yaw +math.pi end - else - yaw = yaw + math.random(-0.5, 0.5) - end - - yaw = self:set_yaw( yaw, 8) - end - if self.order == "sit" then - self:set_animation( "sit") - self:set_velocity(0) - else - self:set_animation( "stand") - self:set_velocity(0) - end - - -- npc's ordered to stand stay standing - if self.order == "stand" or self.order == "sleep" or self.order == "work" then - - else - if self.walk_chance ~= 0 - and self.facing_fence ~= true - and math.random(1, 100) <= self.walk_chance - and self:is_at_cliff_or_danger() == false then - - self:set_velocity(self.walk_velocity) - self.state = "walk" - self:set_animation( "walk") - end - end - - elseif self.state == PATHFINDING then - self:check_gowp(dtime) - - elseif self.state == "walk" then - local s = self.object:get_pos() - local lp = nil - - -- is there something I need to avoid? - if (self.water_damage > 0 - and self.lava_damage > 0) - or self.breath_max ~= -1 then - - lp = minetest.find_node_near(s, 1, {"group:water", "group:lava"}) - - elseif self.water_damage > 0 then - - lp = minetest.find_node_near(s, 1, {"group:water"}) - - elseif self.lava_damage > 0 then - - lp = minetest.find_node_near(s, 1, {"group:lava"}) - - elseif self.fire_damage > 0 then - - lp = minetest.find_node_near(s, 1, {"group:fire"}) - - end - - local is_in_danger = false - if lp then - -- If mob in or on dangerous block, look for land - if (self:is_node_dangerous(self.standing_in) or - self:is_node_dangerous(self.standing_on)) or (self:is_node_waterhazard(self.standing_in) or self:is_node_waterhazard(self.standing_on)) and (not self.fly) then - is_in_danger = true - - -- If mob in or on dangerous block, look for land - if is_in_danger then - -- Better way to find shore - copied from upstream - lp = minetest.find_nodes_in_area_under_air( - {x = s.x - 5, y = s.y - 0.5, z = s.z - 5}, - {x = s.x + 5, y = s.y + 1, z = s.z + 5}, - {"group:solid"}) - - lp = #lp > 0 and lp[math.random(#lp)] - - -- did we find land? - if lp then - - local vec = { - x = lp.x - s.x, - z = lp.z - s.z - } - - yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate - - - if lp.x > s.x then yaw = yaw +math.pi end - - -- look towards land and move in that direction - yaw = self:set_yaw( yaw, 6) - self:set_velocity(self.walk_velocity) - - end - end - -- A danger is near but mob is not inside - else + else - -- Randomly turn - if math.random(1, 100) <= 30 then - yaw = yaw + math.random(-0.5, 0.5) - yaw = self:set_yaw( yaw, 8) - end + -- Randomly turn + if math.random(1, 100) <= 30 then + yaw = yaw + math.random(-0.5, 0.5) + yaw = self:set_yaw( yaw, 8) end + end - yaw = self:set_yaw( yaw, 8) + yaw = self:set_yaw( yaw, 8) -- otherwise randomly turn - elseif math.random(1, 100) <= 30 then - yaw = yaw + math.random(-0.5, 0.5) - yaw = self:set_yaw( yaw, 8) - end + elseif math.random(1, 100) <= 30 then + yaw = yaw + math.random(-0.5, 0.5) + yaw = self:set_yaw( yaw, 8) + end - -- stand for great fall or danger or fence in front - local cliff_or_danger = false - if is_in_danger then - cliff_or_danger = self:is_at_cliff_or_danger() - end - if self.facing_fence == true - or cliff_or_danger - or math.random(1, 100) <= 30 then + -- stand for great fall or danger or fence in front + local cliff_or_danger = false + if is_in_danger then + cliff_or_danger = self:is_at_cliff_or_danger() + end + if self.facing_fence == true + or cliff_or_danger + or math.random(1, 100) <= 30 then - self:set_velocity(0) - self.state = "stand" - self:set_animation( "stand") - local yaw = self.object:get_yaw() or 0 - yaw = self:set_yaw( yaw + 0.78, 8) + self:set_velocity(0) + self.state = "stand" + self:set_animation( "stand") + local yaw = self.object:get_yaw() or 0 + yaw = self:set_yaw( yaw + 0.78, 8) + else + + self:set_velocity(self.walk_velocity) + + if self:flight_check() + and self.animation + and self.animation.fly_start + and self.animation.fly_end then + self:set_animation( "fly") else - - self:set_velocity(self.walk_velocity) - - if self:flight_check() - and self.animation - and self.animation.fly_start - and self.animation.fly_end then - self:set_animation( "fly") - else - self:set_animation( "walk") - end - end - - -- runaway when punched - elseif self.state == "runaway" then - - self.runaway_timer = self.runaway_timer + 1 - - -- stop after 5 seconds or when at cliff - if self.runaway_timer > 5 - or self:is_at_cliff_or_danger() then - self.runaway_timer = 0 - self:set_velocity(0) - self.state = "stand" - self:set_animation( "stand") - local yaw = self.object:get_yaw() or 0 - yaw = self:set_yaw( yaw + 0.78, 8) - else - self:set_velocity( self.run_velocity) - self:set_animation( "run") - end - - -- attack routines (explode, dogfight, shoot, dogshoot) - elseif self.state == "attack" then - - local s = self.object:get_pos() - local p = self.attack:get_pos() or s - - -- stop attacking if player invisible or out of range - if not self.attack - or not self.attack:get_pos() - or not self:object_in_range(self.attack) - or self.attack:get_hp() <= 0 - or (self.attack:is_player() and mcl_mobs.invis[ self.attack:get_player_name() ]) then - - self.state = "stand" - self:set_velocity( 0) - self:set_animation( "stand") - self.attack = nil - self.v_start = false - self.timer = 0 - self.blinktimer = 0 - self.path.way = nil - - return - end - - -- calculate distance from mob and enemy - local dist = vector.distance(p, s) - - if self.attack_type == "explode" then - - local vec = { - x = p.x - s.x, - z = p.z - s.z - } - - yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate - - if p.x > s.x then yaw = yaw +math.pi end - - yaw = self:set_yaw( yaw, 0, dtime) - - local node_break_radius = self.explosion_radius or 1 - local entity_damage_radius = self.explosion_damage_radius - or (node_break_radius * 2) - - -- start timer when in reach and line of sight - if not self.v_start - and dist <= self.reach - and self:line_of_sight( s, p, 2) then - - self.v_start = true - self.timer = 0 - self.blinktimer = 0 - self:mob_sound("fuse", nil, false) - - -- stop timer if out of reach or direct line of sight - elseif self.allow_fuse_reset - and self.v_start - and (dist >= self.explosiontimer_reset_radius - or not self:line_of_sight( s, p, 2)) then - self.v_start = false - self.timer = 0 - self.blinktimer = 0 - self.blinkstatus = false - self:remove_texture_mod("^[brighten") - end - - -- walk right up to player unless the timer is active - if self.v_start and (self.stop_to_explode or dist < self.reach) then - self:set_velocity( 0) - else - self:set_velocity( self.run_velocity) - end - - if self.animation and self.animation.run_start then - self:set_animation( "run") - else - self:set_animation( "walk") - end - - if self.v_start then - - self.timer = self.timer + dtime - self.blinktimer = (self.blinktimer or 0) + dtime - - if self.blinktimer > 0.2 then - - self.blinktimer = 0 - - if self.blinkstatus then - self:remove_texture_mod("^[brighten") - else - self:add_texture_mod("^[brighten") - end - - self.blinkstatus = not self.blinkstatus - end - - if self.timer > self.explosion_timer then - - local pos = self.object:get_pos() - - if mobs_griefing and not minetest.is_protected(pos, "") then - mcl_explosions.explode(mcl_util.get_object_center(self.object), self.explosion_strength, { drop_chance = 1.0 }, self.object) - else - minetest.sound_play(self.sounds.explode, { - pos = pos, - gain = 1.0, - max_hear_distance = self.sounds.distance or 32 - }, true) - self:entity_physics(pos,entity_damage_radius) - mcl_mobs.effect(pos, 32, "mcl_particles_smoke.png", nil, nil, node_break_radius, 1, 0) - end - mcl_burning.extinguish(self.object) - self.object:remove() - - return true - end - end - - elseif self.attack_type == "dogfight" - or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 2) and (dist >= self.avoid_distance or not self.shooter_avoid_enemy) - or (self.attack_type == "dogshoot" and dist <= self.reach and self:dogswitch() == 0) then - - if self.fly - and dist > self.reach then - - local p1 = s - local me_y = math.floor(p1.y) - local p2 = p - local p_y = math.floor(p2.y + 1) - local v = self.object:get_velocity() - - if self:flight_check( s) then - - if me_y < p_y then - - self.object:set_velocity({ - x = v.x, - y = 1 * self.walk_velocity, - z = v.z - }) - - elseif me_y > p_y then - - self.object:set_velocity({ - x = v.x, - y = -1 * self.walk_velocity, - z = v.z - }) - end - else - if me_y < p_y then - - self.object:set_velocity({ - x = v.x, - y = 0.01, - z = v.z - }) - - elseif me_y > p_y then - - self.object:set_velocity({ - x = v.x, - y = -0.01, - z = v.z - }) - end - end - - end - - -- rnd: new movement direction - if self.path.following - and self.path.way - and self.attack_type ~= "dogshoot" then - - -- no paths longer than 50 - if #self.path.way > 50 - or dist < self.reach then - self.path.following = false - return - end - - local p1 = self.path.way[1] - - if not p1 then - self.path.following = false - return - end - - if math.abs(p1.x-s.x) + math.abs(p1.z - s.z) < 0.6 then - -- reached waypoint, remove it from queue - table.remove(self.path.way, 1) - end - - -- set new temporary target - p = {x = p1.x, y = p1.y, z = p1.z} - end - - local vec = { - x = p.x - s.x, - z = p.z - s.z - } - - yaw = (atan(vec.z / vec.x) + math.pi / 2) - self.rotate - - if p.x > s.x then yaw = yaw + math.pi end - - yaw = self:set_yaw( yaw, 0, dtime) - - -- move towards enemy if beyond mob reach - if dist > self.reach then - - -- path finding by rnd - if self.pathfinding -- only if mob has pathfinding enabled - and enable_pathfinding then - - self:smart_mobs(s, p, dist, dtime) - end - - if self:is_at_cliff_or_danger() then - - self:set_velocity( 0) - self:set_animation( "stand") - local yaw = self.object:get_yaw() or 0 - yaw = self:set_yaw( yaw + 0.78, 8) - else - - if self.path.stuck then - self:set_velocity( self.walk_velocity) - else - self:set_velocity( self.run_velocity) - end - - if self.animation and self.animation.run_start then - self:set_animation( "run") - else - self:set_animation( "walk") - end - end - - else -- rnd: if inside reach range - - self.path.stuck = false - self.path.stuck_timer = 0 - self.path.following = false -- not stuck anymore - - self:set_velocity( 0) - - if not self.custom_attack then - - if self.timer > 1 then - - self.timer = 0 - - if self.double_melee_attack - and math.random(1, 2) == 1 then - self:set_animation( "punch2") - else - self:set_animation( "punch") - end - - local p2 = p - local s2 = s - - p2.y = p2.y + .5 - s2.y = s2.y + .5 - - if self:line_of_sight( p2, s2) == true then - - -- play attack sound - self:mob_sound("attack") - - -- punch player (or what player is attached to) - local attached = self.attack:get_attach() - if attached then - self.attack = attached - end - self.attack:punch(self.object, 1.0, { - full_punch_interval = 1.0, - damage_groups = {fleshy = self.damage} - }, nil) - end - end - else -- call custom attack every second - if self.custom_attack - and self.timer > 1 then - - self.timer = 0 - - self.custom_attack(self, p) - end - end - end - - elseif self.attack_type == "shoot" - or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 1) - or (self.attack_type == "dogshoot" and (dist > self.reach or dist < self.avoid_distance and self.shooter_avoid_enemy) and self:dogswitch() == 0) then - - p.y = p.y - .5 - s.y = s.y + .5 - - local dist = vector.distance(p, s) - local vec = { - x = p.x - s.x, - y = p.y - s.y, - z = p.z - s.z - } - - yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate - - if p.x > s.x then yaw = yaw +math.pi end - - yaw = self:set_yaw( yaw, 0, dtime) - - local stay_away_from_player = vector.new(0,0,0) - - --strafe back and fourth - - --stay away from player so as to shoot them - if dist < self.avoid_distance and self.shooter_avoid_enemy then - self:set_animation( "shoot") - stay_away_from_player=vector.multiply(vector.direction(p, s), 0.33) - end - - if self.strafes then - if not self.strafe_direction then - self.strafe_direction = 1.57 - end - if math.random(40) == 1 then - self.strafe_direction = self.strafe_direction*-1 - end - self.acc = vector.add(vector.multiply(vector.rotate_around_axis(vector.direction(s, p), vector.new(0,1,0), self.strafe_direction), 0.3*self.walk_velocity), stay_away_from_player) - else - self:set_velocity( 0) - end - - local p = self.object:get_pos() - p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2 - - if self.shoot_interval - and self.timer > self.shoot_interval - and not minetest.raycast(vector.add(p, vector.new(0,self.shoot_offset,0)), vector.add(self.attack:get_pos(), vector.new(0,1.5,0)), false, false):next() - and math.random(1, 100) <= 60 then - - self.timer = 0 - self:set_animation( "shoot") - - -- play shoot attack sound - self:mob_sound("shoot_attack") - - -- Shoot arrow - if minetest.registered_entities[self.arrow] then - - local arrow, ent - local v = 1 - if not self.shoot_arrow then - self.firing = true - minetest.after(1, function() - self.firing = false - end) - arrow = minetest.add_entity(p, self.arrow) - ent = arrow:get_luaentity() - if ent.velocity then - v = ent.velocity - end - ent.switch = 1 - ent.owner_id = tostring(self.object) -- add unique owner id to arrow - - -- important for mcl_shields - ent._shooter = self.object - ent._saved_shooter_pos = self.object:get_pos() - end - - local amount = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) ^ 0.5 - -- offset makes shoot aim accurate - vec.y = vec.y + self.shoot_offset - vec.x = vec.x * (v / amount) - vec.y = vec.y * (v / amount) - vec.z = vec.z * (v / amount) - if self.shoot_arrow then - vec = vector.normalize(vec) - self:shoot_arrow(p, vec) - else - arrow:set_velocity(vec) - end - end - end - else - + self:set_animation( "walk") end end end +function mob_class:do_states_stand() + local yaw = self.object:get_yaw() or 0 + + if math.random(1, 4) == 1 then + + local s = self.object:get_pos() + local objs = minetest.get_objects_inside_radius(s, 3) + local lp + for n = 1, #objs do + if objs[n]:is_player() then + lp = objs[n]:get_pos() + break + end + end + + -- look at any players nearby, otherwise turn randomly + if lp and self.look_at_players then + + local vec = { + x = lp.x - s.x, + z = lp.z - s.z + } + + yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate + + if lp.x > s.x then yaw = yaw +math.pi end + else + yaw = yaw + math.random(-0.5, 0.5) + end + + yaw = self:set_yaw( yaw, 8) + end + if self.order == "sit" then + self:set_animation( "sit") + self:set_velocity(0) + else + self:set_animation( "stand") + self:set_velocity(0) + end + + -- npc's ordered to stand stay standing + if self.order == "stand" or self.order == "sleep" or self.order == "work" then + + else + if self.walk_chance ~= 0 + and self.facing_fence ~= true + and math.random(1, 100) <= self.walk_chance + and self:is_at_cliff_or_danger() == false then + + self:set_velocity(self.walk_velocity) + self.state = "walk" + self:set_animation( "walk") + end + end +end + +function mob_class:do_states_runaway() + local yaw = self.object:get_yaw() or 0 + + self.runaway_timer = self.runaway_timer + 1 + + -- stop after 5 seconds or when at cliff + if self.runaway_timer > 5 + or self:is_at_cliff_or_danger() then + self.runaway_timer = 0 + self:set_velocity(0) + self.state = "stand" + self:set_animation( "stand") + local yaw = self.object:get_yaw() or 0 + yaw = self:set_yaw( yaw + 0.78, 8) + else + self:set_velocity( self.run_velocity) + self:set_animation( "run") + end +end + + + + + + function mob_class:check_smooth_rotation(dtime) -- smooth rotation by ThomasMonroe314 if self._turn_to then diff --git a/mods/ENTITIES/mcl_mobs/physics.lua b/mods/ENTITIES/mcl_mobs/physics.lua index af206c41c..0dfc3a800 100644 --- a/mods/ENTITIES/mcl_mobs/physics.lua +++ b/mods/ENTITIES/mcl_mobs/physics.lua @@ -183,6 +183,17 @@ function mob_class:collision() return({x,z}) end +function mob_class:slow_mob() + local d = 0.85 + if self:check_dying() then d = 0.92 end + + local v = self.object:get_velocity() + if v then + --diffuse object velocity + self.object:set_velocity({x = v.x*d, y = v.y, z = v.z*d}) + end +end + -- move mob in facing direction function mob_class:set_velocity(v) local c_x, c_y = 0, 0 @@ -778,6 +789,25 @@ function mob_class:do_env_damage() return self:check_for_death("", {type = "unknown"}) end +function mob_class:env_damage (dtime, pos) + -- environmental damage timer (every 1 second) + self.env_damage_timer = self.env_damage_timer + dtime + + if (self.state == "attack" and self.env_damage_timer > 1) + or self.state ~= "attack" then + self:check_entity_cramming() + self.env_damage_timer = 0 + + -- check for environmental damage (water, fire, lava etc.) + if self:do_env_damage() then + return true + end + + -- node replace check (cow eats grass etc.) + self:replace(pos) + end +end + function mob_class:damage_mob(reason,damage) if not self.health then return end damage = math.floor(damage) From 5faf06012289eb3f3431a284e0e200c306b092ae Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Mon, 2 Jan 2023 00:54:08 +0000 Subject: [PATCH 02/22] Fix crash and remove unused duplicate variables --- mods/ENTITIES/mcl_mobs/api.lua | 14 -------------- mods/ENTITIES/mcl_mobs/breeding.lua | 1 - mods/ENTITIES/mcl_mobs/combat.lua | 12 ++++++++++++ mods/ENTITIES/mcl_mobs/movement.lua | 1 - mods/ENTITIES/mcl_mobs/pathfinding.lua | 1 - mods/ENTITIES/mcl_mobs/physics.lua | 3 +-- 6 files changed, 13 insertions(+), 19 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index 46e113c54..e280bc5d8 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -2,8 +2,6 @@ local mob_class = mcl_mobs.mob_class local mob_class_meta = {__index = mcl_mobs.mob_class} local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs -- API for Mobs Redo: MineClone 2 Edition (MRM) -local MAX_MOB_NAME_LENGTH = 30 -local DEFAULT_FALL_SPEED = -9.81*1.5 local PATHFINDING = "gowp" @@ -17,21 +15,9 @@ local function mcl_log (message) end end - -- Invisibility mod check mcl_mobs.invis = {} --- localize math functions -local atann = math.atan - -local function atan(x) - if not x or x ~= x then - return 0 - else - return atann(x) - end -end - local remove_far = true local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false local spawn_protected = minetest.settings:get_bool("mobs_spawn_protected") ~= false diff --git a/mods/ENTITIES/mcl_mobs/breeding.lua b/mods/ENTITIES/mcl_mobs/breeding.lua index 9284163cd..33e05f23b 100644 --- a/mods/ENTITIES/mcl_mobs/breeding.lua +++ b/mods/ENTITIES/mcl_mobs/breeding.lua @@ -1,7 +1,6 @@ local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs local mob_class = mcl_mobs.mob_class -local MAX_MOB_NAME_LENGTH = 30 local HORNY_TIME = 30 local HORNY_AGAIN_TIME = 300 local CHILD_GROW_TIME = 60*20 diff --git a/mods/ENTITIES/mcl_mobs/combat.lua b/mods/ENTITIES/mcl_mobs/combat.lua index 24eb2f290..f60384517 100644 --- a/mods/ENTITIES/mcl_mobs/combat.lua +++ b/mods/ENTITIES/mcl_mobs/combat.lua @@ -8,6 +8,18 @@ local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching local stuck_path_timeout = 10 -- how long will mob follow path before giving up +local enable_pathfinding = true + +local atann = math.atan +local function atan(x) + if not x or x ~= x then + return 0 + else + return atann(x) + end +end + + -- check if daytime and also if mob is docile during daylight hours function mob_class:day_docile() if self.docile_by_day == false then diff --git a/mods/ENTITIES/mcl_mobs/movement.lua b/mods/ENTITIES/mcl_mobs/movement.lua index 753f79281..11a1de092 100644 --- a/mods/ENTITIES/mcl_mobs/movement.lua +++ b/mods/ENTITIES/mcl_mobs/movement.lua @@ -4,7 +4,6 @@ local DEFAULT_FALL_SPEED = -9.81*1.5 local FLOP_HEIGHT = 6 local FLOP_HOR_SPEED = 1.5 local PATHFINDING = "gowp" -local enable_pathfinding = true local node_ice = "mcl_core:ice" local node_snowblock = "mcl_core:snowblock" diff --git a/mods/ENTITIES/mcl_mobs/pathfinding.lua b/mods/ENTITIES/mcl_mobs/pathfinding.lua index 6cb37434f..495d23cd4 100644 --- a/mods/ENTITIES/mcl_mobs/pathfinding.lua +++ b/mods/ENTITIES/mcl_mobs/pathfinding.lua @@ -3,7 +3,6 @@ local mob_class = mcl_mobs.mob_class local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false) local PATHFINDING = "gowp" -local enable_pathfinding = true local LOG_MODULE = "[Mobs]" local function mcl_log (message) diff --git a/mods/ENTITIES/mcl_mobs/physics.lua b/mods/ENTITIES/mcl_mobs/physics.lua index 0dfc3a800..0617fd1e8 100644 --- a/mods/ENTITIES/mcl_mobs/physics.lua +++ b/mods/ENTITIES/mcl_mobs/physics.lua @@ -5,8 +5,7 @@ local ENTITY_CRAMMING_MAX = 24 local CRAMMING_DAMAGE = 3 local DEATH_DELAY = 0.5 local DEFAULT_FALL_SPEED = -9.81*1.5 -local FLOP_HEIGHT = 6 -local FLOP_HOR_SPEED = 1.5 + local PATHFINDING = "gowp" local mobs_debug = minetest.settings:get_bool("mobs_debug", false) local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false From f8ae702ce4c3d21ffbea44f3c439f86a5a1f06c6 Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Mon, 2 Jan 2023 00:58:23 +0000 Subject: [PATCH 03/22] Function name consistency --- mods/ENTITIES/mcl_mobs/api.lua | 4 ++-- mods/ENTITIES/mcl_mobs/combat.lua | 2 +- mods/ENTITIES/mcl_mobs/movement.lua | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index e280bc5d8..e9265d36b 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -293,13 +293,13 @@ function mob_class:do_states(dtime) elseif self.state == PATHFINDING then self:check_gowp(dtime) elseif self.state == "walk" then - self:do_state_walk() + self:do_states_walk() elseif self.state == "runaway" then -- runaway when punched self:do_states_runaway() elseif self.state == "attack" then -- attack routines (explode, dogfight, shoot, dogshoot) - if self:do_state_attack(dtime) then + if self:do_states_attack(dtime) then return true end end diff --git a/mods/ENTITIES/mcl_mobs/combat.lua b/mods/ENTITIES/mcl_mobs/combat.lua index f60384517..1886c7ccc 100644 --- a/mods/ENTITIES/mcl_mobs/combat.lua +++ b/mods/ENTITIES/mcl_mobs/combat.lua @@ -807,7 +807,7 @@ function mob_class:check_aggro(dtime) self._check_aggro_timer = self._check_aggro_timer + dtime end -function mob_class:do_state_attack (dtime) +function mob_class:do_states_attack (dtime) local yaw = self.object:get_yaw() or 0 local s = self.object:get_pos() diff --git a/mods/ENTITIES/mcl_mobs/movement.lua b/mods/ENTITIES/mcl_mobs/movement.lua index 11a1de092..df00a42a7 100644 --- a/mods/ENTITIES/mcl_mobs/movement.lua +++ b/mods/ENTITIES/mcl_mobs/movement.lua @@ -793,7 +793,7 @@ function mob_class:teleport(target) end end -function mob_class:do_state_walk() +function mob_class:do_states_walk() local yaw = self.object:get_yaw() or 0 local s = self.object:get_pos() From fb51067c782fe2c42e0a873d204d41159cb706a7 Mon Sep 17 00:00:00 2001 From: CyberMango Date: Fri, 23 Dec 2022 16:05:23 +0200 Subject: [PATCH 04/22] Created a shared function for planting a seed that can also be consumed. --- mods/ITEMS/mcl_farming/carrots.lua | 9 +-------- mods/ITEMS/mcl_farming/potatoes.lua | 9 +-------- mods/ITEMS/mcl_farming/shared_functions.lua | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/mods/ITEMS/mcl_farming/carrots.lua b/mods/ITEMS/mcl_farming/carrots.lua index 1c3ebcdfa..b76606be2 100644 --- a/mods/ITEMS/mcl_farming/carrots.lua +++ b/mods/ITEMS/mcl_farming/carrots.lua @@ -89,14 +89,7 @@ minetest.register_craftitem("mcl_farming:carrot_item", { groups = {food = 2, eatable = 3, compostability = 65}, _mcl_saturation = 3.6, on_secondary_use = minetest.item_eat(3), - on_place = function(itemstack, placer, pointed_thing) - local new = mcl_farming:place_seed(itemstack, placer, pointed_thing, "mcl_farming:carrot_1") - if new then - return new - else - return minetest.do_item_eat(3, nil, itemstack, placer, pointed_thing) - end - end, + on_place = mcl_farming:get_seed_or_eat_callback("mcl_farming:carrot_1", 3), }) minetest.register_craftitem("mcl_farming:carrot_item_gold", { diff --git a/mods/ITEMS/mcl_farming/potatoes.lua b/mods/ITEMS/mcl_farming/potatoes.lua index 78532c0c0..50bb66a3b 100644 --- a/mods/ITEMS/mcl_farming/potatoes.lua +++ b/mods/ITEMS/mcl_farming/potatoes.lua @@ -95,14 +95,7 @@ minetest.register_craftitem("mcl_farming:potato_item", { _mcl_saturation = 0.6, stack_max = 64, on_secondary_use = minetest.item_eat(1), - on_place = function(itemstack, placer, pointed_thing) - local new = mcl_farming:place_seed(itemstack, placer, pointed_thing, "mcl_farming:potato_1") - if new then - return new - else - return minetest.do_item_eat(1, nil, itemstack, placer, pointed_thing) - end - end, + on_place = mcl_farming:get_seed_or_eat_callback("mcl_farming:potato_1", 1), }) minetest.register_craftitem("mcl_farming:potato_item_baked", { diff --git a/mods/ITEMS/mcl_farming/shared_functions.lua b/mods/ITEMS/mcl_farming/shared_functions.lua index e2e42dd25..989274c43 100644 --- a/mods/ITEMS/mcl_farming/shared_functions.lua +++ b/mods/ITEMS/mcl_farming/shared_functions.lua @@ -469,6 +469,21 @@ function mcl_farming:stem_color(startcolor, endcolor, step, step_count) return colorstring end +--[[Get a callback that either eats the item or plants it. + +Used for on_place callbacks for craft items which are seeds that can also be consumed. +--]] +function mcl_farming:get_seed_or_eat_callback(plantname, hp_change) + return function(itemstack, placer, pointed_thing) + local new = mcl_farming:place_seed(itemstack, placer, pointed_thing, plantname) + if new then + return new + else + return minetest.do_item_eat(hp_change, nil, itemstack, placer, pointed_thing) + end + end +end + minetest.register_lbm({ label = "Add growth for unloaded farming plants", name = "mcl_farming:growth", From 8a7fcfde82692a78e83c12f76e21518347194907 Mon Sep 17 00:00:00 2001 From: CyberMango Date: Sat, 24 Dec 2022 19:38:55 +0200 Subject: [PATCH 05/22] Fixed sweet berries bugs. Now they can only be placed a tile's upper part. Also when destroyed bushes at stage 2 drop 1 or 2 berries, while bushes at stage 3 drop 2 or 3 (instead of fixed numbers of 1 and 3 respectively). Also harvesting sweet berries at stage 3 (final) brings them to stage 1 instead of just to stage 2. Number of dropped berries was adjusted. --- mods/ITEMS/mcl_farming/sweet_berry.lua | 34 ++++++++++++++++---------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/mods/ITEMS/mcl_farming/sweet_berry.lua b/mods/ITEMS/mcl_farming/sweet_berry.lua index aca5fadc2..4682447df 100644 --- a/mods/ITEMS/mcl_farming/sweet_berry.lua +++ b/mods/ITEMS/mcl_farming/sweet_berry.lua @@ -9,6 +9,9 @@ for i=0, 3 do if i > 0 then groups.sweet_berry_thorny = 1 end + local drop_berries = (i >= 2) + local berries_to_drop = drop_berries and {i - 1, i} or nil + minetest.register_node(node_name, { drawtype = "plantlike", tiles = {texture}, @@ -24,7 +27,14 @@ for i=0, 3 do liquid_renewable = false, liquid_range = 0, walkable = false, - drop = (i>=2) and ("mcl_farming:sweet_berry" .. (i==3 and " 3" or "")) or "", + -- Dont even create a table if no berries are dropped. + drop = not drop_berries and "" or { + max_items = 1, + items = { + { items = {"mcl_farming:sweet_berry " .. berries_to_drop[1] }, rarity = 2 }, + { items = {"mcl_farming:sweet_berry " .. berries_to_drop[2] } } + } + }, selection_box = { type = "fixed", fixed = {-6 / 16, -0.5, -6 / 16, 6 / 16, (-0.30 + (i*0.25)), 6 / 16}, @@ -46,17 +56,12 @@ for i=0, 3 do itemstack:take_item() return end - local stage - if node.name:find("_2") then - stage = 2 - elseif node.name:find("_3") then - stage = 3 - end - if stage then - for i=1,math.random(stage) do - minetest.add_item(pos,"mcl_farming:sweet_berry") + + if drop_berries then + for j=1, berries_to_drop[math.random(2)] do + minetest.add_item(pos, "mcl_farming:sweet_berry") end - minetest.swap_node(pos,{name = "mcl_farming:sweet_berry_bush_" .. stage - 1 }) + minetest.swap_node(pos, {name = "mcl_farming:sweet_berry_bush_" .. 1 }) end return itemstack end, @@ -76,8 +81,11 @@ minetest.register_craftitem("mcl_farming:sweet_berry", { minetest.record_protection_violation(pointed_thing.above, pn) return itemstack end - if pointed_thing.type == "node" and table.indexof(planton,minetest.get_node(pointed_thing.under).name) ~= -1 and minetest.get_node(pointed_thing.above).name == "air" then - minetest.set_node(pointed_thing.above,{name="mcl_farming:sweet_berry_bush_0"}) + if pointed_thing.type == "node" and + table.indexof(planton, minetest.get_node(pointed_thing.under).name) ~= -1 and + pointed_thing.above.y > pointed_thing.under.y and + minetest.get_node(pointed_thing.above).name == "air" then + minetest.set_node(pointed_thing.above, {name="mcl_farming:sweet_berry_bush_0"}) if not minetest.is_creative_enabled(placer:get_player_name()) then itemstack:take_item() end From 0f569fdbaad89768fec63ee506782164cf99a7ba Mon Sep 17 00:00:00 2001 From: CyberMango Date: Sat, 24 Dec 2022 20:38:32 +0200 Subject: [PATCH 06/22] Bone meal on sweet berries in creative mode is no longer wasted and minor fixes. removed an unnecessary -- in a comment. Removed a redundant concatination. --- mods/ITEMS/mcl_farming/shared_functions.lua | 2 +- mods/ITEMS/mcl_farming/sweet_berry.lua | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mods/ITEMS/mcl_farming/shared_functions.lua b/mods/ITEMS/mcl_farming/shared_functions.lua index 989274c43..bbb5cdc20 100644 --- a/mods/ITEMS/mcl_farming/shared_functions.lua +++ b/mods/ITEMS/mcl_farming/shared_functions.lua @@ -472,7 +472,7 @@ end --[[Get a callback that either eats the item or plants it. Used for on_place callbacks for craft items which are seeds that can also be consumed. ---]] +]] function mcl_farming:get_seed_or_eat_callback(plantname, hp_change) return function(itemstack, placer, pointed_thing) local new = mcl_farming:place_seed(itemstack, placer, pointed_thing, plantname) diff --git a/mods/ITEMS/mcl_farming/sweet_berry.lua b/mods/ITEMS/mcl_farming/sweet_berry.lua index 4682447df..86c27c182 100644 --- a/mods/ITEMS/mcl_farming/sweet_berry.lua +++ b/mods/ITEMS/mcl_farming/sweet_berry.lua @@ -53,7 +53,9 @@ for i=0, 3 do end if mcl_dye and clicker:get_wielded_item():get_name() == "mcl_bone_meal:bone_meal" then mcl_dye.apply_bone_meal({under=pos},clicker) - itemstack:take_item() + if not minetest.is_creative_enabled(pn) then + itemstack:take_item() + end return end @@ -61,7 +63,7 @@ for i=0, 3 do for j=1, berries_to_drop[math.random(2)] do minetest.add_item(pos, "mcl_farming:sweet_berry") end - minetest.swap_node(pos, {name = "mcl_farming:sweet_berry_bush_" .. 1 }) + minetest.swap_node(pos, {name = "mcl_farming:sweet_berry_bush_1"}) end return itemstack end, From fb28e192e6b39790aa3c4bbdd03ab6ad973e1c2e Mon Sep 17 00:00:00 2001 From: CyberMango Date: Sun, 1 Jan 2023 22:55:44 +0200 Subject: [PATCH 07/22] Bone meal applied on sweet berries only grows them by 1 stage now. The interface of the grow plant is a bit confusing since even with stages set to 0 the plant grows by 1 stage (if other conditions such as light are met). Therefore changing it to 0 makes the plant grow by 1. --- mods/ITEMS/mcl_dye/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_dye/init.lua b/mods/ITEMS/mcl_dye/init.lua index 80219d298..16672b554 100644 --- a/mods/ITEMS/mcl_dye/init.lua +++ b/mods/ITEMS/mcl_dye/init.lua @@ -275,7 +275,7 @@ local function apply_bone_meal(pointed_thing,user) if n.name == "mcl_farming:sweet_berry_bush_3" then return minetest.add_item(vector.offset(pos,math.random()-0.5,math.random()-0.5,math.random()-0.5),"mcl_farming:sweet_berry") else - return mcl_farming:grow_plant("plant_sweet_berry_bush", pos, n, 1, true) + return mcl_farming:grow_plant("plant_sweet_berry_bush", pos, n, 0, true) end elseif n.name == "mcl_cocoas:cocoa_1" or n.name == "mcl_cocoas:cocoa_2" then mcl_dye.add_bone_meal_particle(pos) From 17e02aec3c96bbfd21e7ecfa1e13ca86977665fd Mon Sep 17 00:00:00 2001 From: CyberMango Date: Sun, 1 Jan 2023 23:02:38 +0200 Subject: [PATCH 08/22] Applying bone meal on a stage 3 sweet berry no longer grows it. It now ignores the bone meal and harvests the sweet berry as normal. --- mods/ITEMS/mcl_farming/sweet_berry.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mods/ITEMS/mcl_farming/sweet_berry.lua b/mods/ITEMS/mcl_farming/sweet_berry.lua index 86c27c182..9033f5077 100644 --- a/mods/ITEMS/mcl_farming/sweet_berry.lua +++ b/mods/ITEMS/mcl_farming/sweet_berry.lua @@ -51,7 +51,8 @@ for i=0, 3 do minetest.record_protection_violation(pos, pn) return itemstack end - if mcl_dye and clicker:get_wielded_item():get_name() == "mcl_bone_meal:bone_meal" then + if 3 ~= i and mcl_dye and + clicker:get_wielded_item():get_name() == "mcl_bone_meal:bone_meal" then mcl_dye.apply_bone_meal({under=pos},clicker) if not minetest.is_creative_enabled(pn) then itemstack:take_item() From 5e969ba9287c6328cdd387f8b9b4be9a9463101a Mon Sep 17 00:00:00 2001 From: CyberMango Date: Sun, 1 Jan 2023 23:52:35 +0200 Subject: [PATCH 09/22] Added sweet berries death messages. --- mods/HUD/mcl_death_messages/init.lua | 5 +++++ mods/ITEMS/mcl_farming/sweet_berry.lua | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mods/HUD/mcl_death_messages/init.lua b/mods/HUD/mcl_death_messages/init.lua index 91e13995b..13ed23668 100644 --- a/mods/HUD/mcl_death_messages/init.lua +++ b/mods/HUD/mcl_death_messages/init.lua @@ -149,6 +149,11 @@ mcl_death_messages = { plain = "@1 went off with a bang", item = "@1 went off with a bang due to a firework fired from @3 by @2", -- order is intentional }, + sweet_berry = { + _translator = S, + plain = "@1 died a sweet death", + assist = "@1 was poked to death by a sweet berry bush whilst trying to escape @2", + }, -- Missing snowballs: The Minecraft wiki mentions them but the MC source code does not. }, } diff --git a/mods/ITEMS/mcl_farming/sweet_berry.lua b/mods/ITEMS/mcl_farming/sweet_berry.lua index 9033f5077..8a91c0e79 100644 --- a/mods/ITEMS/mcl_farming/sweet_berry.lua +++ b/mods/ITEMS/mcl_farming/sweet_berry.lua @@ -51,7 +51,7 @@ for i=0, 3 do minetest.record_protection_violation(pos, pn) return itemstack end - if 3 ~= i and mcl_dye and + if 3 ~= i and mcl_dye and clicker:get_wielded_item():get_name() == "mcl_bone_meal:bone_meal" then mcl_dye.apply_bone_meal({under=pos},clicker) if not minetest.is_creative_enabled(pn) then From 9b1ceebf0d3db46d8f3f40f478e9af121cf2decd Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Wed, 7 Dec 2022 23:38:44 +0000 Subject: [PATCH 10/22] Villagers will now pathfind to town bell that isn't on the ground --- mods/ENTITIES/mcl_mobs/pathfinding.lua | 7 ++++--- mods/ENTITIES/mobs_mc/villager.lua | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/pathfinding.lua b/mods/ENTITIES/mcl_mobs/pathfinding.lua index 6cb37434f..04138bed0 100644 --- a/mods/ENTITIES/mcl_mobs/pathfinding.lua +++ b/mods/ENTITIES/mcl_mobs/pathfinding.lua @@ -35,19 +35,20 @@ function append_paths (wp1, wp2) end local function output_enriched (wp_out) - mcl_log("Output enriched path") + --mcl_log("Output enriched path") local i = 0 for _,outy in pairs (wp_out) do i = i + 1 - mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"])) + --mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"])) local action = outy["action"] if action then + mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"])) mcl_log("type: " .. action["type"]) mcl_log("action: " .. action["action"]) mcl_log("target: " .. minetest.pos_to_string(action["target"])) end - mcl_log("failed attempts: " .. outy["failed_attempts"]) + --mcl_log("failed attempts: " .. outy["failed_attempts"]) end end diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index 9215416b8..b1f73247c 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -1190,6 +1190,19 @@ local function do_work (self) end end +local below_vec = vector.new(0, -1, 0) + +local function get_ground_below_floating_object (float_pos) + local pos = float_pos + repeat + mcl_log("Current pos: " .. minetest.pos_to_string(pos)) + pos = vector.add(pos, below_vec) + local node = minetest.get_node(pos) + mcl_log("First non air materials: ".. tostring(node.name)) + until node.name ~= "air" + return pos +end + local function go_to_town_bell(self) if self.order == GATHERING then mcl_log("Already gathering") @@ -1208,8 +1221,9 @@ local function go_to_town_bell(self) --Ideally should check for closest available. It'll make pathing easier. for _,n in pairs(nn) do mcl_log("Found bell") - - local gp = self:gopath(n,function(self) + local target_point = get_ground_below_floating_object(n) + + local gp = self:gopath(target_point,function(self) if self then self.order = GATHERING mcl_log("Callback has a self") From 5c0a763b836798530da5a624fe0f99f9d466fe83 Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Fri, 9 Dec 2022 19:07:45 +0000 Subject: [PATCH 11/22] Optimisation - Only check for town bell if ready to path --- mods/ENTITIES/mcl_mobs/pathfinding.lua | 31 ++++++++++++++++++++------ mods/ENTITIES/mobs_mc/villager.lua | 10 +++++++-- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/pathfinding.lua b/mods/ENTITIES/mcl_mobs/pathfinding.lua index 04138bed0..5f70760da 100644 --- a/mods/ENTITIES/mcl_mobs/pathfinding.lua +++ b/mods/ENTITIES/mcl_mobs/pathfinding.lua @@ -120,22 +120,27 @@ local function calculate_path_through_door (p, t, target) local pos_closest_to_door = nil local other_side_of_door = nil - --Path to door first + --Check direct route local wp = minetest.find_path(p,t,150,1,4) + + --Path to door first if not wp then mcl_log("No direct path. Path through door") -- This could improve. There could be multiple doors. Check you can path from door to target first. + + -- target could be pos local cur_door_pos = minetest.find_node_near(target,16,{"group:door"}) if cur_door_pos then mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos)) for _,v in pairs(plane_adjacents) do pos_closest_to_door = vector.add(cur_door_pos,v) - local n = minetest.get_node(pos_closest_to_door) if n.name == "air" then + wp = minetest.find_path(p,pos_closest_to_door,150,1,4) if wp then + mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door)) other_side_of_door = vector.add(cur_door_pos,-v) mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door)) @@ -154,7 +159,7 @@ local function calculate_path_through_door (p, t, target) mcl_log("This block next to door doesn't work.") end else - mcl_log("Block is not air, it is: ".. n.name) + --mcl_log("Block is not air, it is: ".. n.name) end end @@ -165,21 +170,33 @@ local function calculate_path_through_door (p, t, target) mcl_log("We have a direct route") end + -- If not, get door near pos + --path from pos to door, path from otherside to target + if wp and not enriched_path then enriched_path = generate_enriched_path(wp) end return enriched_path end -local gopath_last = os.time() +--local gopath_last = os.time() + +function mob_class:ready_to_path() + mcl_log("Check ready to path") + if self._pf_last_failed and (os.time() - self._pf_last_failed) < 30 then + return false + else + mcl_log("We are ready to pathfind, no previous fail or we are past threshold") + return true + end +end + function mob_class:gopath(target,callback_arrived) if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end - if self._pf_last_failed and (os.time() - self._pf_last_failed) < 30 then + if not self:ready_to_path() then mcl_log("We are not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed)) return - else - mcl_log("We are ready to pathfind, no previous fail or we are past threshold") end --if os.time() - gopath_last < 5 then diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index b1f73247c..c9a8ab602 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -1205,11 +1205,17 @@ end local function go_to_town_bell(self) if self.order == GATHERING then - mcl_log("Already gathering") + --mcl_log("Already gathering") return else mcl_log("Current order" .. self.order) end + + if not self:ready_to_path() then + mcl_log("Negative response to go_path. Do not bother") + return + end + mcl_log("Go to town bell") local looking_for_type={} @@ -1222,7 +1228,7 @@ local function go_to_town_bell(self) for _,n in pairs(nn) do mcl_log("Found bell") local target_point = get_ground_below_floating_object(n) - + local gp = self:gopath(target_point,function(self) if self then self.order = GATHERING From 29cd73cb84f641267b52ddcff54fa65a64dfe6c1 Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Fri, 30 Dec 2022 04:15:59 +0000 Subject: [PATCH 12/22] Pathfinding through door should also check door closest to position so villager can leave current house --- mods/ENTITIES/mcl_mobs/pathfinding.lua | 154 +++++++++++++------------ 1 file changed, 81 insertions(+), 73 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/pathfinding.lua b/mods/ENTITIES/mcl_mobs/pathfinding.lua index 5f70760da..f89f4a323 100644 --- a/mods/ENTITIES/mcl_mobs/pathfinding.lua +++ b/mods/ENTITIES/mcl_mobs/pathfinding.lua @@ -35,11 +35,11 @@ function append_paths (wp1, wp2) end local function output_enriched (wp_out) - --mcl_log("Output enriched path") + mcl_log("Output enriched path") local i = 0 for _,outy in pairs (wp_out) do i = i + 1 - --mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"])) + mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"])) local action = outy["action"] if action then @@ -109,76 +109,6 @@ local plane_adjacents = { vector.new(0,0,-1), } --- This function is used to see if we can path. We could use to check a route, rather than making people move. -local function calculate_path_through_door (p, t, target) - -- target is the same as t, just 1 square difference. Maybe we don't need target - mcl_log("Plot route from mob: " .. minetest.pos_to_string(p) .. ", to target: " .. minetest.pos_to_string(t)) - - local enriched_path = nil - - local cur_door_pos = nil - local pos_closest_to_door = nil - local other_side_of_door = nil - - --Check direct route - local wp = minetest.find_path(p,t,150,1,4) - - --Path to door first - if not wp then - mcl_log("No direct path. Path through door") - - -- This could improve. There could be multiple doors. Check you can path from door to target first. - - -- target could be pos - local cur_door_pos = minetest.find_node_near(target,16,{"group:door"}) - if cur_door_pos then - mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos)) - for _,v in pairs(plane_adjacents) do - pos_closest_to_door = vector.add(cur_door_pos,v) - local n = minetest.get_node(pos_closest_to_door) - if n.name == "air" then - - wp = minetest.find_path(p,pos_closest_to_door,150,1,4) - if wp then - - mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door)) - other_side_of_door = vector.add(cur_door_pos,-v) - mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door)) - - local wp_otherside_door_to_target = minetest.find_path(other_side_of_door,t,150,1,4) - if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then - table.insert(wp, cur_door_pos) - append_paths (wp, wp_otherside_door_to_target) - enriched_path = generate_enriched_path(wp, pos_closest_to_door, other_side_of_door, cur_door_pos) - mcl_log("We have a path from outside door to target") - else - mcl_log("We cannot path from outside door to target") - end - break - else - mcl_log("This block next to door doesn't work.") - end - else - --mcl_log("Block is not air, it is: ".. n.name) - end - - end - else - mcl_log("No door found") - end - else - mcl_log("We have a direct route") - end - - -- If not, get door near pos - --path from pos to door, path from otherside to target - - if wp and not enriched_path then - enriched_path = generate_enriched_path(wp) - end - return enriched_path -end - --local gopath_last = os.time() function mob_class:ready_to_path() @@ -191,6 +121,63 @@ function mob_class:ready_to_path() end end +-- This function is used to see if we can path. We could use to check a route, rather than making people move. +local function calculate_path_through_door (p, t, cur_door_pos) + -- target is the same as t, just 1 square difference. Maybe we don't need target + mcl_log("Plot route from mob: " .. minetest.pos_to_string(p) .. ", to target: " .. minetest.pos_to_string(t)) + + local enriched_path = nil + + --local cur_door_pos = nil + local pos_closest_to_door = nil + local other_side_of_door = nil + + local wp + + if cur_door_pos then + mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos)) + for _,v in pairs(plane_adjacents) do + pos_closest_to_door = vector.add(cur_door_pos,v) + local n = minetest.get_node(pos_closest_to_door) + + if n.name == "air" then + mcl_log("We have air space next to door at: " .. minetest.pos_to_string(pos_closest_to_door)) + + wp = minetest.find_path(p,pos_closest_to_door,150,1,4) + + if wp then + mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door)) + other_side_of_door = vector.add(cur_door_pos,-v) + mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door)) + + local wp_otherside_door_to_target = minetest.find_path(other_side_of_door,t,150,1,4) + if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then + table.insert(wp, cur_door_pos) + append_paths (wp, wp_otherside_door_to_target) + enriched_path = generate_enriched_path(wp, pos_closest_to_door, other_side_of_door, cur_door_pos) + mcl_log("We have a path from outside door to target") + else + mcl_log("We cannot path from outside door to target") + end + break + else + mcl_log("This block next to door doesn't work.") + end + end + end + else + mcl_log("No door found") + end + + if wp and not enriched_path then + mcl_log("Wp but not enriched") + enriched_path = generate_enriched_path(wp) + end + return enriched_path +end + + + function mob_class:gopath(target,callback_arrived) if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end @@ -210,7 +197,28 @@ function mob_class:gopath(target,callback_arrived) local p = self.object:get_pos() local t = vector.offset(target,0,1,0) - local wp = calculate_path_through_door(p, t, target) + --Check direct route + local wp = minetest.find_path(p,t,150,1,4) + + if not wp then + mcl_log("No direct path. Path through door") + -- target could be pos + local cur_door_pos = minetest.find_node_near(target, 16, {"group:door"}) + wp = calculate_path_through_door(p, t, cur_door_pos) + + if not wp then + mcl_log("No path though door closest to target. Try door closest to origin.") + cur_door_pos = minetest.find_node_near(p, 16, {"group:door"}) + wp = calculate_path_through_door(p, t, cur_door_pos) + end + else + wp = generate_enriched_path(wp) + mcl_log("We have a direct route") + end + + --path from pos to door, path from otherside to target + + if not wp then mcl_log("Could not calculate path") self._pf_last_failed = os.time() From e3307d647b720cfea3c6d4f6833bb2f075a07ae7 Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Fri, 30 Dec 2022 14:17:24 +0000 Subject: [PATCH 13/22] Fix pathing to bell that is sat on the ground --- mods/ENTITIES/mobs_mc/villager.lua | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index c9a8ab602..dcc16e16f 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -641,7 +641,7 @@ function get_activity(tod) else activity = "chill" end - mcl_log("Time is " .. tod ..". Activity is: ".. activity) + --mcl_log("Time is " .. tod ..". Activity is: ".. activity) return activity end @@ -1200,6 +1200,16 @@ local function get_ground_below_floating_object (float_pos) local node = minetest.get_node(pos) mcl_log("First non air materials: ".. tostring(node.name)) until node.name ~= "air" + + -- If pos is 1 below float_pos, then just return float_pos as there is no air below it + if pos.y == float_pos.y -1 then + --mcl_log("pos is only 1 lower than float pos so no air below") + return float_pos + else + --mcl_log("pos is more than 1 lower than float pos so air is below") + return pos + end + return pos end From 87f04bdd9f5eb9de2c2162a32a86bf3540ad63ad Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Fri, 30 Dec 2022 15:10:55 +0000 Subject: [PATCH 14/22] Prevent attempting to path through 2 doors until code supports it. Clean up also. --- mods/ENTITIES/mcl_mobs/pathfinding.lua | 46 +++++++++++++------------- mods/ENTITIES/mobs_mc/villager.lua | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/pathfinding.lua b/mods/ENTITIES/mcl_mobs/pathfinding.lua index f89f4a323..db58ff777 100644 --- a/mods/ENTITIES/mcl_mobs/pathfinding.lua +++ b/mods/ENTITIES/mcl_mobs/pathfinding.lua @@ -3,7 +3,6 @@ local mob_class = mcl_mobs.mob_class local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false) local PATHFINDING = "gowp" -local enable_pathfinding = true local LOG_MODULE = "[Mobs]" local function mcl_log (message) @@ -64,26 +63,27 @@ local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_ local cur_pos_to_add = vector.add(cur_pos, one_down) if door_open_pos and vector.equals (cur_pos, door_open_pos) then mcl_log ("Door open match") - --action = {type = "door", action = "open"} - action = {} - action["type"] = "door" - action["action"] = "open" + action = {type = "door", action = "open"} + --action = {} + --action["type"] = "door" + --action["action"] = "open" action["target"] = cur_door_pos cur_pos_to_add = vector.add(cur_pos, one_down) elseif door_close_pos and vector.equals(cur_pos, door_close_pos) then mcl_log ("Door close match") - --action = {type = "door", action = "closed"} - action = {} - action["type"] = "door" - action["action"] = "close" + action = {type = "door", action = "close"} + --action = {} + --action["type"] = "door" + --action["action"] = "close" action["target"] = cur_door_pos cur_pos_to_add = vector.add(cur_pos, one_down) elseif cur_door_pos and vector.equals(cur_pos, cur_door_pos) then mcl_log("Current door pos") cur_pos_to_add = vector.add(cur_pos, one_down) - action = {} - action["type"] = "door" - action["action"] = "open" + action = {type = "door", action = "open"} + --action = {} + --action["type"] = "door" + --action["action"] = "open" action["target"] = cur_door_pos else cur_pos_to_add = cur_pos @@ -132,7 +132,7 @@ local function calculate_path_through_door (p, t, cur_door_pos) local pos_closest_to_door = nil local other_side_of_door = nil - local wp + local wp, prospective_wp if cur_door_pos then mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos)) @@ -143,18 +143,19 @@ local function calculate_path_through_door (p, t, cur_door_pos) if n.name == "air" then mcl_log("We have air space next to door at: " .. minetest.pos_to_string(pos_closest_to_door)) - wp = minetest.find_path(p,pos_closest_to_door,150,1,4) + prospective_wp = minetest.find_path(p,pos_closest_to_door,150,1,4) - if wp then + if prospective_wp then mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door)) other_side_of_door = vector.add(cur_door_pos,-v) mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door)) local wp_otherside_door_to_target = minetest.find_path(other_side_of_door,t,150,1,4) if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then - table.insert(wp, cur_door_pos) - append_paths (wp, wp_otherside_door_to_target) - enriched_path = generate_enriched_path(wp, pos_closest_to_door, other_side_of_door, cur_door_pos) + table.insert(prospective_wp, cur_door_pos) + append_paths (prospective_wp, wp_otherside_door_to_target) + enriched_path = generate_enriched_path(prospective_wp, pos_closest_to_door, other_side_of_door, cur_door_pos) + wp = prospective_wp mcl_log("We have a path from outside door to target") else mcl_log("We cannot path from outside door to target") @@ -202,7 +203,6 @@ function mob_class:gopath(target,callback_arrived) if not wp then mcl_log("No direct path. Path through door") - -- target could be pos local cur_door_pos = minetest.find_node_near(target, 16, {"group:door"}) wp = calculate_path_through_door(p, t, cur_door_pos) @@ -218,15 +218,14 @@ function mob_class:gopath(target,callback_arrived) --path from pos to door, path from otherside to target - if not wp then mcl_log("Could not calculate path") self._pf_last_failed = os.time() - -- Cover for a flaw in pathfind where it chooses the wrong door and gets stuck. Take a break, allow others. + -- If cannot path, don't immediately try again end - --output_table(wp) if wp and #wp > 0 then + --output_table(wp) self._target = t self.callback_arrived = callback_arrived local current_location = table.remove(wp,1) @@ -318,7 +317,7 @@ function mob_class:check_gowp(dtime) -- arrived at location, finish gowp local distance_to_targ = vector.distance(p,self._target) --mcl_log("Distance to targ: ".. tostring(distance_to_targ)) - if distance_to_targ < 2 then + if distance_to_targ < 1.5 then mcl_log("Arrived at _target") self.waypoints = nil self._target = nil @@ -400,6 +399,7 @@ function mob_class:check_gowp(dtime) self:go_to_pos(self._current_target) else mcl_log("close to current target: ".. minetest.pos_to_string(self.current_target["pos"])) + mcl_log("target is: ".. minetest.pos_to_string(self._target)) self.current_target = nil end diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index dcc16e16f..fc12899cd 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -1202,7 +1202,7 @@ local function get_ground_below_floating_object (float_pos) until node.name ~= "air" -- If pos is 1 below float_pos, then just return float_pos as there is no air below it - if pos.y == float_pos.y -1 then + if pos.y == float_pos.y - 1 then --mcl_log("pos is only 1 lower than float pos so no air below") return float_pos else From d6804bf4b7a7967df5f6614c5f6be17ab2dc42cd Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Fri, 30 Dec 2022 15:59:02 +0000 Subject: [PATCH 15/22] Fix distance to target issue, and refactor pathing actions --- mods/ENTITIES/mcl_mobs/pathfinding.lua | 39 ++++++++++++++------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/pathfinding.lua b/mods/ENTITIES/mcl_mobs/pathfinding.lua index db58ff777..31b98bbba 100644 --- a/mods/ENTITIES/mcl_mobs/pathfinding.lua +++ b/mods/ENTITIES/mcl_mobs/pathfinding.lua @@ -63,28 +63,19 @@ local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_ local cur_pos_to_add = vector.add(cur_pos, one_down) if door_open_pos and vector.equals (cur_pos, door_open_pos) then mcl_log ("Door open match") - action = {type = "door", action = "open"} - --action = {} - --action["type"] = "door" - --action["action"] = "open" - action["target"] = cur_door_pos + action = {type = "door", action = "open", target = cur_door_pos} + --action["target"] = cur_door_pos cur_pos_to_add = vector.add(cur_pos, one_down) elseif door_close_pos and vector.equals(cur_pos, door_close_pos) then mcl_log ("Door close match") - action = {type = "door", action = "close"} - --action = {} - --action["type"] = "door" - --action["action"] = "close" - action["target"] = cur_door_pos + action = {type = "door", action = "close", target = cur_door_pos} + --action["target"] = cur_door_pos cur_pos_to_add = vector.add(cur_pos, one_down) elseif cur_door_pos and vector.equals(cur_pos, cur_door_pos) then mcl_log("Current door pos") + action = {type = "door", action = "open", target = cur_door_pos} cur_pos_to_add = vector.add(cur_pos, one_down) - action = {type = "door", action = "open"} - --action = {} - --action["type"] = "door" - --action["action"] = "open" - action["target"] = cur_door_pos + --action["target"] = cur_door_pos else cur_pos_to_add = cur_pos --mcl_log ("Pos doesn't match") @@ -202,7 +193,7 @@ function mob_class:gopath(target,callback_arrived) local wp = minetest.find_path(p,t,150,1,4) if not wp then - mcl_log("No direct path. Path through door") + mcl_log("No direct path. Path through door closest to target.") local cur_door_pos = minetest.find_node_near(target, 16, {"group:door"}) wp = calculate_path_through_door(p, t, cur_door_pos) @@ -317,7 +308,7 @@ function mob_class:check_gowp(dtime) -- arrived at location, finish gowp local distance_to_targ = vector.distance(p,self._target) --mcl_log("Distance to targ: ".. tostring(distance_to_targ)) - if distance_to_targ < 1.5 then + if distance_to_targ < 1.8 then mcl_log("Arrived at _target") self.waypoints = nil self._target = nil @@ -328,6 +319,8 @@ function mob_class:check_gowp(dtime) self.object:set_acceleration({x = 0, y = 0, z = 0}) if self.callback_arrived then return self.callback_arrived(self) end return true + elseif not self.current_target then + mcl_log("Not close enough to targ: ".. tostring(distance_to_targ)) end -- More pathing to be done @@ -373,9 +366,19 @@ function mob_class:check_gowp(dtime) -- Is a little sensitive and could take 1 - 7 times. A 10 fail count might be a good exit condition. mcl_log("We don't have waypoints or a current target. Let's try to path to target") + + if wp then + mcl_log("WP: " .. tostring(wp)) + mcl_log("WP num: " .. tostring(#wp)) + else + mcl_log("No wp set") + end + + mcl_log("Current target: " .. tostring(self.current_target)) + local final_wp = minetest.find_path(p,self._target,150,1,4) if final_wp then - mcl_log("We might be able to get to target here.") + mcl_log("We can get to target here.") -- self.waypoints = final_wp self:go_to_pos(self._target) else From 4324fe24895774b45835124af37f1f1530b8f732 Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Sat, 31 Dec 2022 01:21:16 +0000 Subject: [PATCH 16/22] Villager will now path from one house to another in search of available job or bed --- mods/ENTITIES/mcl_mobs/pathfinding.lua | 131 ++++++++++++++++++------- 1 file changed, 96 insertions(+), 35 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/pathfinding.lua b/mods/ENTITIES/mcl_mobs/pathfinding.lua index 31b98bbba..b92486465 100644 --- a/mods/ENTITIES/mcl_mobs/pathfinding.lua +++ b/mods/ENTITIES/mcl_mobs/pathfinding.lua @@ -42,7 +42,7 @@ local function output_enriched (wp_out) local action = outy["action"] if action then - mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"])) + --mcl_log("Pos ".. i ..":" .. minetest.pos_to_string(outy["pos"])) mcl_log("type: " .. action["type"]) mcl_log("action: " .. action["action"]) mcl_log("target: " .. minetest.pos_to_string(action["target"])) @@ -51,31 +51,33 @@ local function output_enriched (wp_out) end end +local one_down = vector.new(0,-1,0) +local one_up = vector.new(0,1,0) + -- This function will take a list of paths, and enrich it with: -- a var for failed attempts -- an action, such as to open or close a door where we know that pos requires that action local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_door_pos) local wp_out = {} + + local current_door_index = -1 + for i, cur_pos in pairs(wp_in) do local action = nil - local one_down = vector.new(0,-1,0) local cur_pos_to_add = vector.add(cur_pos, one_down) if door_open_pos and vector.equals (cur_pos, door_open_pos) then mcl_log ("Door open match") action = {type = "door", action = "open", target = cur_door_pos} - --action["target"] = cur_door_pos cur_pos_to_add = vector.add(cur_pos, one_down) elseif door_close_pos and vector.equals(cur_pos, door_close_pos) then mcl_log ("Door close match") action = {type = "door", action = "close", target = cur_door_pos} - --action["target"] = cur_door_pos cur_pos_to_add = vector.add(cur_pos, one_down) elseif cur_door_pos and vector.equals(cur_pos, cur_door_pos) then mcl_log("Current door pos") action = {type = "door", action = "open", target = cur_door_pos} cur_pos_to_add = vector.add(cur_pos, one_down) - --action["target"] = cur_door_pos else cur_pos_to_add = cur_pos --mcl_log ("Pos doesn't match") @@ -100,8 +102,6 @@ local plane_adjacents = { vector.new(0,0,-1), } ---local gopath_last = os.time() - function mob_class:ready_to_path() mcl_log("Check ready to path") if self._pf_last_failed and (os.time() - self._pf_last_failed) < 30 then @@ -113,22 +113,26 @@ function mob_class:ready_to_path() end -- This function is used to see if we can path. We could use to check a route, rather than making people move. -local function calculate_path_through_door (p, t, cur_door_pos) - -- target is the same as t, just 1 square difference. Maybe we don't need target - mcl_log("Plot route from mob: " .. minetest.pos_to_string(p) .. ", to target: " .. minetest.pos_to_string(t)) +local function calculate_path_through_door (p, cur_door_pos, t) + if t then + mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p) .. ", to target: " .. minetest.pos_to_string(t)) + else + mcl_log("Plot route through door from pos: " .. minetest.pos_to_string(p)) + end local enriched_path = nil + local wp, prospective_wp - --local cur_door_pos = nil local pos_closest_to_door = nil local other_side_of_door = nil - local wp, prospective_wp - if cur_door_pos then mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos)) + for _,v in pairs(plane_adjacents) do pos_closest_to_door = vector.add(cur_door_pos,v) + other_side_of_door = vector.add(cur_door_pos,-v) + local n = minetest.get_node(pos_closest_to_door) if n.name == "air" then @@ -138,22 +142,34 @@ local function calculate_path_through_door (p, t, cur_door_pos) if prospective_wp then mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door)) - other_side_of_door = vector.add(cur_door_pos,-v) mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door)) - local wp_otherside_door_to_target = minetest.find_path(other_side_of_door,t,150,1,4) - if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then - table.insert(prospective_wp, cur_door_pos) - append_paths (prospective_wp, wp_otherside_door_to_target) - enriched_path = generate_enriched_path(prospective_wp, pos_closest_to_door, other_side_of_door, cur_door_pos) - wp = prospective_wp - mcl_log("We have a path from outside door to target") + table.insert(prospective_wp, cur_door_pos) + + if t then + mcl_log("We have t, lets go from door to target") + local wp_otherside_door_to_target = minetest.find_path(other_side_of_door,t,150,1,4) + + if wp_otherside_door_to_target and #wp_otherside_door_to_target > 0 then + append_paths (prospective_wp, wp_otherside_door_to_target) + + wp = prospective_wp + mcl_log("We have a path from outside door to target") + else + mcl_log("We cannot path from outside door to target") + end else - mcl_log("We cannot path from outside door to target") + mcl_log("No t, just add other side of door") + table.insert(prospective_wp, other_side_of_door) + wp = prospective_wp + end + + if wp then + enriched_path = generate_enriched_path(wp, pos_closest_to_door, other_side_of_door, cur_door_pos) + break end - break else - mcl_log("This block next to door doesn't work.") + mcl_log("Cannot path to this air block next to door.") end end end @@ -168,7 +184,7 @@ local function calculate_path_through_door (p, t, cur_door_pos) return enriched_path end - +--local gopath_last = os.time() function mob_class:gopath(target,callback_arrived) if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end @@ -193,15 +209,55 @@ function mob_class:gopath(target,callback_arrived) local wp = minetest.find_path(p,t,150,1,4) if not wp then - mcl_log("No direct path. Path through door closest to target.") - local cur_door_pos = minetest.find_node_near(target, 16, {"group:door"}) - wp = calculate_path_through_door(p, t, cur_door_pos) + mcl_log("### No direct path. Path through door closest to target.") + local door_near_target = minetest.find_node_near(target, 16, {"group:door"}) + wp = calculate_path_through_door(p, door_near_target, t) if not wp then - mcl_log("No path though door closest to target. Try door closest to origin.") - cur_door_pos = minetest.find_node_near(p, 16, {"group:door"}) - wp = calculate_path_through_door(p, t, cur_door_pos) + mcl_log("### No path though door closest to target. Try door closest to origin.") + local door_closest = minetest.find_node_near(p, 16, {"group:door"}) + wp = calculate_path_through_door(p, door_closest, t) + + -- Path through 2 doors + if not wp then + mcl_log("### Still not wp. Need to path through 2 doors.") + local path_through_closest_door = calculate_path_through_door(p, door_closest) + + if path_through_closest_door and #path_through_closest_door > 0 then + mcl_log("We have path through first door") + mcl_log("Number of pos in path through door: " .. tostring(#path_through_closest_door)) + + local pos_after_door_entry = path_through_closest_door[#path_through_closest_door] + if pos_after_door_entry then + local pos_after_door = vector.add(pos_after_door_entry["pos"], one_up) + mcl_log("pos_after_door: " .. minetest.pos_to_string(pos_after_door)) + local path_after_door = calculate_path_through_door(pos_after_door, door_near_target, t) + if path_after_door and #path_after_door > 1 then + mcl_log("We have path after first door") + table.remove(path_after_door, 1) -- Remove duplicate + wp = path_through_closest_door + append_paths (wp, path_after_door) + else + mcl_log("Path after door is not good") + end + else + mcl_log("No pos after door") + end + + else + mcl_log("Path through closest door empty or null") + end + -- Path to and through door + -- Path from otherside of door through door to next target + else + mcl_log("ok, we have a path through 1 door") + end + end + + -- Path through door closest to target (starting at square before door) + -- Path to that starting point directly + -- or path through door to that starting point else wp = generate_enriched_path(wp) mcl_log("We have a direct route") @@ -367,14 +423,19 @@ function mob_class:check_gowp(dtime) mcl_log("We don't have waypoints or a current target. Let's try to path to target") - if wp then - mcl_log("WP: " .. tostring(wp)) - mcl_log("WP num: " .. tostring(#wp)) + if self.waypoints then + mcl_log("WP: " .. tostring(self.waypoints)) + mcl_log("WP num: " .. tostring(#self.waypoints)) else mcl_log("No wp set") end - mcl_log("Current target: " .. tostring(self.current_target)) + if self.current_target then + mcl_log("Current target: " .. tostring(self.current_target)) + else + mcl_log("No current target") + end + local final_wp = minetest.find_path(p,self._target,150,1,4) if final_wp then From e9b54e85c28a2a1d0f2a67c067035176b0b87db2 Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Sat, 31 Dec 2022 17:52:09 +0000 Subject: [PATCH 17/22] Pathfinding clean up --- mods/ENTITIES/mcl_mobs/pathfinding.lua | 28 ++++++++------------------ 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/pathfinding.lua b/mods/ENTITIES/mcl_mobs/pathfinding.lua index b92486465..273e9952d 100644 --- a/mods/ENTITIES/mcl_mobs/pathfinding.lua +++ b/mods/ENTITIES/mcl_mobs/pathfinding.lua @@ -1,16 +1,19 @@ local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs local mob_class = mcl_mobs.mob_class -local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false) +local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_pathfinding",false) local PATHFINDING = "gowp" -local LOG_MODULE = "[Mobs]" +local LOG_MODULE = "[Mobs Pathfinding]" local function mcl_log (message) if LOGGING_ON and message then minetest.log(LOG_MODULE .. " " .. message) end end +local one_down = vector.new(0,-1,0) +local one_up = vector.new(0,1,0) + function output_table (wp) if not wp then return end mcl_log("wp items: ".. tostring(#wp)) @@ -51,15 +54,13 @@ local function output_enriched (wp_out) end end -local one_down = vector.new(0,-1,0) -local one_up = vector.new(0,1,0) - -- This function will take a list of paths, and enrich it with: -- a var for failed attempts -- an action, such as to open or close a door where we know that pos requires that action local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_door_pos) local wp_out = {} + -- TODO Just pass in door position and the index before is open, the index after is close local current_door_index = -1 for i, cur_pos in pairs(wp_in) do @@ -105,6 +106,7 @@ local plane_adjacents = { function mob_class:ready_to_path() mcl_log("Check ready to path") if self._pf_last_failed and (os.time() - self._pf_last_failed) < 30 then + mcl_log("Not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed)) return false else mcl_log("We are ready to pathfind, no previous fail or we are past threshold") @@ -188,11 +190,7 @@ end function mob_class:gopath(target,callback_arrived) if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end - - if not self:ready_to_path() then - mcl_log("We are not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed)) - return - end + if not self:ready_to_path() then return end --if os.time() - gopath_last < 5 then -- mcl_log("Not ready to path yet") @@ -243,28 +241,18 @@ function mob_class:gopath(target,callback_arrived) else mcl_log("No pos after door") end - else mcl_log("Path through closest door empty or null") end - -- Path to and through door - -- Path from otherside of door through door to next target else mcl_log("ok, we have a path through 1 door") end - end - - -- Path through door closest to target (starting at square before door) - -- Path to that starting point directly - -- or path through door to that starting point else wp = generate_enriched_path(wp) mcl_log("We have a direct route") end - --path from pos to door, path from otherside to target - if not wp then mcl_log("Could not calculate path") self._pf_last_failed = os.time() From 325a666c622e6a77ed9ae3f03ef4adbf2091d107 Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Sat, 31 Dec 2022 19:39:58 +0000 Subject: [PATCH 18/22] Added pathfinder constants to make it easier to tweak --- mods/ENTITIES/mcl_mobs/pathfinding.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/pathfinding.lua b/mods/ENTITIES/mcl_mobs/pathfinding.lua index 273e9952d..c8855364d 100644 --- a/mods/ENTITIES/mcl_mobs/pathfinding.lua +++ b/mods/ENTITIES/mcl_mobs/pathfinding.lua @@ -4,6 +4,9 @@ local mob_class = mcl_mobs.mob_class local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_pathfinding",false) local PATHFINDING = "gowp" +local PATHFINDING_FAIL_THRESHOLD = 100 -- no. of ticks to fail before giving up. 20p/s. 5s helps them get through door +local PATHFINDING_FAIL_WAIT = 30 -- how long to wait before trying to path again + local LOG_MODULE = "[Mobs Pathfinding]" local function mcl_log (message) if LOGGING_ON and message then @@ -105,7 +108,7 @@ local plane_adjacents = { function mob_class:ready_to_path() mcl_log("Check ready to path") - if self._pf_last_failed and (os.time() - self._pf_last_failed) < 30 then + if self._pf_last_failed and (os.time() - self._pf_last_failed) < PATHFINDING_FAIL_WAIT then mcl_log("Not ready to path as last fail is less than threshold: " .. (os.time() - self._pf_last_failed)) return false else @@ -390,7 +393,7 @@ function mob_class:check_gowp(dtime) -- No waypoints left, but have current target. Potentially last waypoint to go to. self.current_target["failed_attempts"] = self.current_target["failed_attempts"] + 1 local failed_attempts = self.current_target["failed_attempts"] - if failed_attempts >= 50 then + if failed_attempts >= PATHFINDING_FAIL_THRESHOLD then mcl_log("Failed to reach position (" .. minetest.pos_to_string(self.current_target["pos"]) .. ") too many times. Abandon route. Times tried: " .. failed_attempts) self.state = "stand" self.current_target = nil From 465a919f6b77048c7cd9903ae5c3965f651976ec Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Sat, 31 Dec 2022 19:41:42 +0000 Subject: [PATCH 19/22] Villager will now reclaim job during the day even when it isn't work time --- mods/ENTITIES/mobs_mc/villager.lua | 136 +++++++++++++++-------------- 1 file changed, 70 insertions(+), 66 deletions(-) diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index fc12899cd..ce1e76c72 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -770,7 +770,7 @@ local function check_bed (entity) local n = minetest.get_node(b) local is_bed_bottom = string.find(n.name,"_bottom") - mcl_log("" .. tostring(is_bed_bottom)) + --mcl_log("is bed bottom: " .. tostring(is_bed_bottom)) if n and not is_bed_bottom then mcl_log("Where did my bed go?!") entity._bed = nil --the stormtroopers have killed uncle owen @@ -1135,59 +1135,54 @@ local function validate_jobsite(self) end local function do_work (self) - --debug_trades(self) - if self.child then - mcl_log("A child so don't send to work") + + if not self or self.child then + mcl_log("No self, or a child so don't work") return end + --mcl_log("Time for work") + local jobsite_node = retrieve_my_jobsite (self) - -- Don't try if looking_for_work, or gowp possibly - if validate_jobsite(self) then - --mcl_log("My jobsite is valid. Do i need to travel?") - - local jobsite2 = retrieve_my_jobsite (self) + if jobsite_node then local jobsite = self._jobsite - if self and jobsite2 and self._jobsite then - local distance_to_jobsite = vector.distance(self.object:get_pos(),self._jobsite) - --mcl_log("Villager: ".. minetest.pos_to_string(self.object:get_pos()) .. ", jobsite: " .. minetest.pos_to_string(self._jobsite) .. ", distance to jobsite: ".. distance_to_jobsite) + local distance_to_jobsite = vector.distance(self.object:get_pos(), jobsite) + --mcl_log("Villager: ".. minetest.pos_to_string(self.object:get_pos()) .. ", jobsite: " .. minetest.pos_to_string(self._jobsite) .. ", distance to jobsite: ".. distance_to_jobsite) - if distance_to_jobsite < 2 then - if self.state ~= PATHFINDING and self.order ~= WORK then - mcl_log("Setting order to work.") - self.order = WORK - unlock_trades(self) - else - --mcl_log("Still pathfinding.") - end + if distance_to_jobsite < 2 then + if self.state ~= PATHFINDING and self.order ~= WORK then + mcl_log("Setting order to work.") + self.order = WORK + unlock_trades(self) else - mcl_log("Not at job block. Need to commute.") - if self.order == WORK then - self.order = nil - return - end - self:gopath(jobsite, function(self,jobsite) - if not self then - --mcl_log("missing self. not good") - return false - end - if not self._jobsite then - --mcl_log("Jobsite not valid") - return false - end - if vector.distance(self.object:get_pos(),self._jobsite) < 2 then - --mcl_log("Made it to work ok callback!") - return true - else - --mcl_log("Need to walk to work. Not sure we can get here.") - end - end) + --mcl_log("Still pathfinding.") end + else + mcl_log("Not at job block. Need to commute.") + if self.order == WORK then + self.order = nil + return + end + self:gopath(jobsite, function(self, jobsite) + if not self then + --mcl_log("missing self. not good") + return false + end + if not self._jobsite then + --mcl_log("Jobsite not valid") + return false + end + if vector.distance(self.object:get_pos(),self._jobsite) < 2 then + --mcl_log("Made it to work ok callback!") + return true + else + --mcl_log("Need to walk to work. Not sure we can get here.") + end + end) end - elseif self._profession == "unemployed" or has_traded(self) then - get_a_job(self) end + end local below_vec = vector.new(0, -1, 0) @@ -1214,17 +1209,10 @@ local function get_ground_below_floating_object (float_pos) end local function go_to_town_bell(self) - if self.order == GATHERING then - --mcl_log("Already gathering") - return - else - mcl_log("Current order" .. self.order) - end + if self.order == GATHERING then return + else mcl_log("Current order" .. self.order) end - if not self:ready_to_path() then - mcl_log("Negative response to go_path. Do not bother") - return - end + if not self:ready_to_path() then return end mcl_log("Go to town bell") @@ -1307,22 +1295,45 @@ local function validate_bed(self) end local function do_activity (self) - -- Maybe just check we're pathfinding first? + if self.following then mcl_log("Following, so do not do activity.") return end + if self.state == PATHFINDING then + mcl_log("Pathfinding, so do not do activity.") + return + end - if not validate_bed(self) and self.state ~= PATHFINDING then + local jobsite_valid = false + + if not mcl_beds.is_night() then if self.order == SLEEP then self.order = nil end - mcl_log("Villager has no bed. Currently at location: "..minetest.pos_to_string(self.object:get_pos())) - take_bed (self) + + if not validate_jobsite(self) then + --debug_trades(self) + if self._profession == "unemployed" or has_traded(self) then + get_a_job(self) + return + end + else + jobsite_valid = true + --mcl_log("My jobsite is valid. Do i need to travel?") + end + else + if self.order == WORK then self.order = nil end + + if not validate_bed(self) then + if self.order == SLEEP then self.order = nil end + mcl_log("Villager at this location has no bed: " .. minetest.pos_to_string(self.object:get_pos())) + take_bed (self) + end end -- Only check in day or during thunderstorm but wandered_too_far code won't work local wandered_too_far = false if check_bed (self) then - wandered_too_far = ( self.state ~= PATHFINDING ) and (vector.distance(self.object:get_pos(),self._bed) > 50 ) + wandered_too_far = vector.distance(self.object:get_pos(),self._bed) > 50 end if wandered_too_far then @@ -1330,7 +1341,7 @@ local function do_activity (self) go_home(self, false) elseif get_activity() == SLEEP then go_home(self, true) - elseif get_activity() == WORK then + elseif get_activity() == WORK and jobsite_valid then do_work(self) elseif get_activity() == GATHERING then go_to_town_bell(self) @@ -1339,13 +1350,6 @@ local function do_activity (self) self.order = nil end - -- Daytime is work and play time - if not mcl_beds.is_night() then - if self.order == SLEEP then self.order = nil end - else - if self.order == WORK then self.order = nil end - end - end local function update_max_tradenum(self) From 85f7bbdb807c5db1387a5d422b048cbc4d7ecf14 Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Sat, 31 Dec 2022 23:47:22 +0000 Subject: [PATCH 20/22] Optimisation. Don't even prepare to path if not ready to path --- mods/ENTITIES/mobs_mc/villager.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index ce1e76c72..0cd3a6c52 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -836,6 +836,7 @@ end local function take_bed (entity) if not entity then return end + if not self:ready_to_path() then return end local p = entity.object:get_pos() @@ -1059,9 +1060,9 @@ local function look_for_job(self, requested_jobsites) end - local function get_a_job(self) if self.order == WORK then self.order = nil end + if not self:ready_to_path() then return end mcl_log("I'm unemployed or lost my job block and have traded. Can I get a job?") From 2527479401aeaa3911e38975186e2140bf685620 Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Sat, 31 Dec 2022 23:48:15 +0000 Subject: [PATCH 21/22] Clean up on isle 5, please --- mods/ENTITIES/mcl_mobs/pathfinding.lua | 57 +++++++++----------------- 1 file changed, 19 insertions(+), 38 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/pathfinding.lua b/mods/ENTITIES/mcl_mobs/pathfinding.lua index c8855364d..ee39d28ad 100644 --- a/mods/ENTITIES/mcl_mobs/pathfinding.lua +++ b/mods/ENTITIES/mcl_mobs/pathfinding.lua @@ -1,12 +1,23 @@ local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs local mob_class = mcl_mobs.mob_class -local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_pathfinding",false) -local PATHFINDING = "gowp" - local PATHFINDING_FAIL_THRESHOLD = 100 -- no. of ticks to fail before giving up. 20p/s. 5s helps them get through door local PATHFINDING_FAIL_WAIT = 30 -- how long to wait before trying to path again +local PATHFINDING = "gowp" + +local one_down = vector.new(0,-1,0) +local one_up = vector.new(0,1,0) + +local plane_adjacents = { + vector.new(1,0,0), + vector.new(-1,0,0), + vector.new(0,0,1), + vector.new(0,0,-1), +} + +local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_pathfinding",false) + local LOG_MODULE = "[Mobs Pathfinding]" local function mcl_log (message) if LOGGING_ON and message then @@ -14,9 +25,6 @@ local function mcl_log (message) end end -local one_down = vector.new(0,-1,0) -local one_up = vector.new(0,1,0) - function output_table (wp) if not wp then return end mcl_log("wp items: ".. tostring(#wp)) @@ -26,7 +34,7 @@ function output_table (wp) end function append_paths (wp1, wp2) - mcl_log("Start append") + --mcl_log("Start append") if not wp1 or not wp2 then mcl_log("Cannot append wp's") return @@ -36,7 +44,7 @@ function append_paths (wp1, wp2) for _,a in pairs (wp2) do table.insert(wp1, a) end - mcl_log("End append") + --mcl_log("End append") end local function output_enriched (wp_out) @@ -99,13 +107,6 @@ local function generate_enriched_path(wp_in, door_open_pos, door_close_pos, cur_ return wp_out end -local plane_adjacents = { - vector.new(1,0,0), - vector.new(-1,0,0), - vector.new(0,0,1), - vector.new(0,0,-1), -} - function mob_class:ready_to_path() mcl_log("Check ready to path") if self._pf_last_failed and (os.time() - self._pf_last_failed) < PATHFINDING_FAIL_WAIT then @@ -189,18 +190,10 @@ local function calculate_path_through_door (p, cur_door_pos, t) return enriched_path end ---local gopath_last = os.time() - function mob_class:gopath(target,callback_arrived) if self.state == PATHFINDING then mcl_log("Already pathfinding, don't set another until done.") return end if not self:ready_to_path() then return end - --if os.time() - gopath_last < 5 then - -- mcl_log("Not ready to path yet") - -- return - --end - --gopath_last = os.time() - self.order = nil local p = self.object:get_pos() @@ -332,17 +325,7 @@ function mob_class:do_pathfind_action(action) end end -local gowp_etime = 0 - function mob_class:check_gowp(dtime) - gowp_etime = gowp_etime + dtime - - -- 0.1 is optimal. - --less frequently = villager will get sent back after passing a point. - --more frequently = villager will fail points they shouldn't they just didn't get there yet - - --if gowp_etime < 0.05 then return end - --gowp_etime = 0 local p = self.object:get_pos() -- no destination @@ -380,7 +363,7 @@ function mob_class:check_gowp(dtime) -- 0.8 is optimal for 0.025 frequency checks and also 1... Actually. 0.8 is winning -- 0.9 and 1.0 is also good. Stick with unless door open or closing issues if self.waypoints and #self.waypoints > 0 and ( not self.current_target or not self.current_target["pos"] or distance_to_current_target < 0.9 ) then - -- We have waypoints, and no current target, or we're at it. We need a new current_target. + -- We have waypoints, and are at current_target or have no current target. We need a new current_target. self:do_pathfind_action (self.current_target["action"]) local failed_attempts = self.current_target["failed_attempts"] @@ -390,7 +373,8 @@ function mob_class:check_gowp(dtime) self:go_to_pos(self.current_target["pos"]) return elseif self.current_target and self.current_target["pos"] then - -- No waypoints left, but have current target. Potentially last waypoint to go to. + -- No waypoints left, but have current target and not close enough. Potentially last waypoint to go to. + self.current_target["failed_attempts"] = self.current_target["failed_attempts"] + 1 local failed_attempts = self.current_target["failed_attempts"] if failed_attempts >= PATHFINDING_FAIL_THRESHOLD then @@ -420,14 +404,12 @@ function mob_class:check_gowp(dtime) else mcl_log("No wp set") end - if self.current_target then mcl_log("Current target: " .. tostring(self.current_target)) else mcl_log("No current target") end - local final_wp = minetest.find_path(p,self._target,150,1,4) if final_wp then mcl_log("We can get to target here.") @@ -457,7 +439,6 @@ function mob_class:check_gowp(dtime) mcl_log("target is: ".. minetest.pos_to_string(self._target)) self.current_target = nil end - return end end From e4db91d35c4b6f8dfec11b5153fc32096f7540dc Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Sun, 1 Jan 2023 03:19:45 +0000 Subject: [PATCH 22/22] Fix crash bug --- mods/ENTITIES/mobs_mc/villager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index 0cd3a6c52..bb23758c5 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -836,7 +836,7 @@ end local function take_bed (entity) if not entity then return end - if not self:ready_to_path() then return end + if not entity:ready_to_path() then return end local p = entity.object:get_pos()