diff --git a/.gitea/issue_template/bug.md b/.gitea/issue_template/bug.md new file mode 100644 index 000000000..be76e6395 --- /dev/null +++ b/.gitea/issue_template/bug.md @@ -0,0 +1,36 @@ +--- + +name: "Bug report" +about: "File a bug report" +labels: + +- unconfirmed +- bug + +--- + + + + +MineClone2 version: + +### What happened? +Report about the bug! Please send large log snippets as an attachement file. + +### What should happen: +Tell us what should happen! + +### Steps to reproduce +Tell us how we can reproduce the bug! diff --git a/.gitea/issue_template/feature_request.md b/.gitea/issue_template/feature_request.md new file mode 100644 index 000000000..58a7437ab --- /dev/null +++ b/.gitea/issue_template/feature_request.md @@ -0,0 +1,26 @@ +--- + +name: "Feature request" +about: "File a feature request not in Minecraft" +labels: + +- "non-Minecraft feature" +- "needs discussion" + +--- + + + +### Feature +Tell us about your requested feature not in Minecraft! + +### Why +Tell us why should we implement it! diff --git a/.gitea/issue_template/missing_feature_request.md b/.gitea/issue_template/missing_feature_request.md new file mode 100644 index 000000000..b3e275c9b --- /dev/null +++ b/.gitea/issue_template/missing_feature_request.md @@ -0,0 +1,25 @@ +--- + +name: "Missing Feature request" +about: "File a missing feature request in Minecraft but not in MineClone2" +labels: + +- "missing feature" + +--- + + + +### Current feature in Minecraft +Tell us about the feature currently in Minecraft! What is it like on Minecraft? + +### Current feature in MineClone2 +Tell us about the feature currently in MineClone2! What is different? diff --git a/.gitea/pull_request_template.md b/.gitea/pull_request_template.md new file mode 100644 index 000000000..ec7207ee4 --- /dev/null +++ b/.gitea/pull_request_template.md @@ -0,0 +1,20 @@ +--- + +name: "Pull request" +about: "Submit a pull request" +labels: + +--- + + + +Tell us about your pull request! Reference related issues, if necessary + +### Testing +Tell us how to test your changes! diff --git a/mods/ENTITIES/mcl_mobs/pathfinding.lua b/mods/ENTITIES/mcl_mobs/pathfinding.lua index 495d23cd4..ee39d28ad 100644 --- a/mods/ENTITIES/mcl_mobs/pathfinding.lua +++ b/mods/ENTITIES/mcl_mobs/pathfinding.lua @@ -1,10 +1,24 @@ 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 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 LOG_MODULE = "[Mobs]" +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 minetest.log(LOG_MODULE .. " " .. message) @@ -20,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 @@ -30,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) @@ -42,11 +56,12 @@ 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("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 @@ -55,34 +70,26 @@ end -- 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 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"} - action = {} - action["type"] = "door" - action["action"] = "open" - action["target"] = cur_door_pos + action = {type = "door", action = "open", 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["target"] = cur_door_pos + action = {type = "door", action = "close", 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 = {} - action["type"] = "door" - action["action"] = "open" - action["target"] = cur_door_pos else cur_pos_to_add = cur_pos --mcl_log ("Pos doesn't match") @@ -100,106 +107,156 @@ 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 + 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") + return true + 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, 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 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 - --Path to door first - local wp = minetest.find_path(p,t,150,1,4) - if not wp then - mcl_log("No direct path. Path through door") + if cur_door_pos then + mcl_log("Found a door near: " .. minetest.pos_to_string(cur_door_pos)) - -- This could improve. There could be multiple doors. Check you can path from door to target first. - 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) + 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 - 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 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)) + + prospective_wp = minetest.find_path(p,pos_closest_to_door,150,1,4) + + if prospective_wp then + mcl_log("Found a path to next to door".. minetest.pos_to_string(pos_closest_to_door)) + mcl_log("Opposite is: ".. minetest.pos_to_string(other_side_of_door)) + + 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 - 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) + 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 - break else - mcl_log("This block next to door doesn't work.") + 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 else - mcl_log("Block is not air, it is: ".. n.name) + mcl_log("Cannot path to this air block next to door.") end - end - else - mcl_log("No door found") end else - mcl_log("We have a direct route") + 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 -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 self._pf_last_failed and (os.time() - self._pf_last_failed) < 30 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 - -- mcl_log("Not ready to path yet") - -- return - --end - --gopath_last = os.time() + if not self:ready_to_path() then return end self.order = nil 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 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.") + 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 + else + mcl_log("ok, we have a path through 1 door") + end + end + else + wp = generate_enriched_path(wp) + mcl_log("We have a direct route") + end + 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) @@ -268,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 @@ -291,7 +338,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.8 then mcl_log("Arrived at _target") self.waypoints = nil self._target = nil @@ -302,6 +349,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 @@ -314,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"] @@ -324,10 +373,11 @@ 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 >= 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 @@ -347,9 +397,22 @@ 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 self.waypoints then + mcl_log("WP: " .. tostring(self.waypoints)) + mcl_log("WP num: " .. tostring(#self.waypoints)) + 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 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 @@ -373,9 +436,9 @@ 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 - return end end diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index 9215416b8..bb23758c5 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 @@ -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 @@ -836,6 +836,7 @@ end local function take_bed (entity) if not entity then return end + if not entity: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?") @@ -1135,68 +1136,85 @@ 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) + +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" + + -- 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 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 return end + mcl_log("Go to town bell") local looking_for_type={} @@ -1208,8 +1226,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 target_point = get_ground_below_floating_object(n) - local gp = self:gopath(n,function(self) + local gp = self:gopath(target_point,function(self) if self then self.order = GATHERING mcl_log("Callback has a self") @@ -1277,22 +1296,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 @@ -1300,7 +1342,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) @@ -1309,13 +1351,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) diff --git a/mods/ENTITIES/mobs_mc/zombie.lua b/mods/ENTITIES/mobs_mc/zombie.lua index 358579dbd..a54f51dec 100644 --- a/mods/ENTITIES/mobs_mc/zombie.lua +++ b/mods/ENTITIES/mobs_mc/zombie.lua @@ -109,7 +109,7 @@ mcl_mobs.register_mob("mobs_mc:zombie", zombie) local baby_zombie = table.copy(zombie) baby_zombie.description = S("Baby Zombie") -baby_zombie.collisionbox = {-0.25, -0.01, -0.25, 0.25, 1, 0.25} +baby_zombie.collisionbox = {-0.25, -0.01, -0.25, 0.25, 0.98, 0.25} baby_zombie.xp_min = 12 baby_zombie.xp_max = 12 baby_zombie.walk_velocity = 1.2 diff --git a/mods/ENVIRONMENT/mcl_weather/skycolor.lua b/mods/ENVIRONMENT/mcl_weather/skycolor.lua index d8e89baf1..3163b0a9d 100644 --- a/mods/ENVIRONMENT/mcl_weather/skycolor.lua +++ b/mods/ENVIRONMENT/mcl_weather/skycolor.lua @@ -136,9 +136,16 @@ mcl_weather.skycolor = { local biomesky local biomefog if mg_name ~= "v6" and mg_name ~= "singlenode" then - local biome = minetest.get_biome_name(minetest.get_biome_data(player:get_pos()).biome) - biomesky = minetest.registered_biomes[biome]._mcl_skycolor - biomefog = minetest.registered_biomes[biome]._mcl_fogcolor + local biome_index = minetest.get_biome_data(player:get_pos()).biome + local biome_name = minetest.get_biome_name(biome_index) + local biome = minetest.registered_biomes[biome_name] + if biome then + --minetest.log("action", string.format("Biome found for number: %s in biome: %s", tostring(biome_index), biome_name)) + biomesky = biome._mcl_skycolor + biomefog = biome._mcl_fogcolor + else + --minetest.log("action", string.format("No biome for number: %s in biome: %s", tostring(biome_index), biome_name)) + end end if (mcl_weather.state == "none") then -- Clear weather 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_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) 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..bbb5cdc20 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", diff --git a/mods/ITEMS/mcl_farming/sweet_berry.lua b/mods/ITEMS/mcl_farming/sweet_berry.lua index aca5fadc2..8a91c0e79 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}, @@ -41,22 +51,20 @@ 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) - itemstack:take_item() + if not minetest.is_creative_enabled(pn) then + itemstack:take_item() + end 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 +84,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 diff --git a/mods/MAPGEN/mcl_villages/buildings.lua b/mods/MAPGEN/mcl_villages/buildings.lua index 67a0785ce..92a53ddaf 100644 --- a/mods/MAPGEN/mcl_villages/buildings.lua +++ b/mods/MAPGEN/mcl_villages/buildings.lua @@ -79,19 +79,27 @@ function settlements.create_site_plan(maxp, minp, pr) local settlement_info = {} local building_all_info local possible_rotations = {"0", "90", "180", "270"} + -- find center of chunk local center = { x=math.floor((minp.x+maxp.x)/2), y=maxp.y, z=math.floor((minp.z+maxp.z)/2) } + -- find center_surface of chunk local center_surface , surface_material = settlements.find_surface(center, true) local chunks = {} chunks[mcl_vars.get_chunk_number(center)] = true -- go build settlement around center - if not center_surface then return false end + if not center_surface then + minetest.log("action", "Cannot build village at: " .. minetest.pos_to_string(center)) + return false + else + minetest.log("action", "Village built.") + --minetest.log("action", "Build village at: " .. minetest.pos_to_string(center) .. " with surface material: " .. surface_material) + end -- initialize all settlement_info table local count_buildings, number_of_buildings, number_built = settlements.initialize_settlement_info(pr) @@ -190,6 +198,7 @@ local function construct_node(p1, p2, name) end local function spawn_iron_golem(pos) + --minetest.log("action", "Attempt to spawn iron golem.") local p = minetest.find_node_near(pos,50,"mcl_core:grass_path") if p then local l=minetest.add_entity(p,"mobs_mc:iron_golem"):get_luaentity() @@ -200,6 +209,7 @@ local function spawn_iron_golem(pos) end local function spawn_villagers(minp,maxp) + --minetest.log("action", "Attempt to spawn villagers.") local beds=minetest.find_nodes_in_area(vector.offset(minp,-20,-20,-20),vector.offset(maxp,20,20,20),{"mcl_beds:bed_red_bottom"}) for _,bed in pairs(beds) do local m = minetest.get_meta(bed) @@ -235,23 +245,6 @@ end function settlements.place_schematics(settlement_info, pr) local building_all_info - --attempt to place one belltower in the center of the village - this doesn't always work out great but it's a lot better than doing it first or last. - local belltower = table.remove(settlement_info,math.floor(#settlement_info/2)) - if belltower then - mcl_structures.place_schematic( - vector.offset(belltower["pos"],0,0,0), - settlements.modpath.."/schematics/belltower.mts", - belltower["rotation"], - nil, - true, - nil, - function(p1, p2, size, rotation, pr) - spawn_iron_golem(p1) - end, - pr - ) - end - for i, built_house in ipairs(settlement_info) do local is_last = i == #settlement_info @@ -262,6 +255,9 @@ function settlements.place_schematics(settlement_info, pr) end end + + + local pos = settlement_info[i]["pos"] local rotation = settlement_info[i]["rotat"] -- get building node material for better integration to surrounding @@ -313,8 +309,11 @@ function settlements.place_schematics(settlement_info, pr) -- format schematic string local schematic = loadstring(schem_lua)() + + local is_belltower = building_all_info["name"] == "belltower" + -- build foundation for the building an make room above - -- place schematic + mcl_structures.place_schematic( pos, schematic, @@ -323,8 +322,12 @@ function settlements.place_schematics(settlement_info, pr) true, nil, function(p1, p2, size, rotation, pr) - init_nodes(p1, p2, size, rotation, pr) - spawn_villagers(p1,p2) + if is_belltower then + spawn_iron_golem(p1) + else + init_nodes(p1, p2, size, rotation, pr) + spawn_villagers(p1,p2) + end end, pr ) diff --git a/mods/MAPGEN/mcl_villages/const.lua b/mods/MAPGEN/mcl_villages/const.lua index b844e6430..feff6a1d2 100644 --- a/mods/MAPGEN/mcl_villages/const.lua +++ b/mods/MAPGEN/mcl_villages/const.lua @@ -52,6 +52,7 @@ schem_path = settlements.modpath.."/schematics/" local basic_pseudobiome_villages = minetest.settings:get_bool("basic_pseudobiome_villages", true) settlements.schematic_table = { + {name = "belltower", mts = schem_path.."belltower.mts", hwidth = 5, hdepth = 5, hheight = 9, hsize = 14, max_num = 0 , rplc = basic_pseudobiome_villages }, {name = "large_house", mts = schem_path.."large_house.mts", hwidth = 12, hdepth = 12, hheight = 9, hsize = 14, max_num = 0.08 , rplc = basic_pseudobiome_villages }, {name = "blacksmith", mts = schem_path.."blacksmith.mts", hwidth = 8, hdepth = 11, hheight = 13, hsize = 13, max_num = 0.055, rplc = basic_pseudobiome_villages }, {name = "butcher", mts = schem_path.."butcher.mts", hwidth = 12, hdepth = 8, hheight = 10, hsize = 14, max_num = 0.03 , rplc = basic_pseudobiome_villages }, diff --git a/mods/MAPGEN/mcl_villages/utils.lua b/mods/MAPGEN/mcl_villages/utils.lua index 4ee2ccfbf..3b882af0c 100644 --- a/mods/MAPGEN/mcl_villages/utils.lua +++ b/mods/MAPGEN/mcl_villages/utils.lua @@ -40,22 +40,32 @@ function settlements.find_surface(pos, wait) -- go through nodes an find surface while cnt < cnt_max do -- Check Surface_node and Node above - -- - if settlements.surface_mat[surface_node.name] then + if surface_node and settlements.surface_mat[surface_node.name] then local surface_node_plus_1 = get_node({ x=p6.x, y=p6.y+1, z=p6.z}) - if surface_node_plus_1 and surface_node and - (string.find(surface_node_plus_1.name,"air") or - string.find(surface_node_plus_1.name,"snow") or - string.find(surface_node_plus_1.name,"fern") or - string.find(surface_node_plus_1.name,"flower") or - string.find(surface_node_plus_1.name,"bush") or - string.find(surface_node_plus_1.name,"tree") or - string.find(surface_node_plus_1.name,"grass")) + + if surface_node_plus_1 then + + local surface_node_minus_1 = get_node({ x=p6.x, y=p6.y-1, z=p6.z}) + local is_leaf_below = minetest.get_item_group(surface_node_minus_1, "leaves") ~= 0 or + string.find(surface_node_minus_1.name,"leaves") + + if not is_leaf_below and ((string.find(surface_node_plus_1.name,"air") or + string.find(surface_node_plus_1.name,"fern") or + string.find(surface_node_plus_1.name,"flower") or + string.find(surface_node_plus_1.name,"bush") or + string.find(surface_node_plus_1.name,"tree") or + string.find(surface_node_plus_1.name,"grass")) or + string.find(surface_node_plus_1.name,"snow")) then - settlements.debug("find_surface7: " ..surface_node.name.. " " .. surface_node_plus_1.name) + settlements.debug("find_surface success: " ..surface_node.name.. " " .. surface_node_plus_1.name) + settlements.debug("node below: " .. tostring(surface_node_minus_1.name)) + settlements.debug("node below leaves group: " .. tostring(minetest.get_item_group(surface_node_minus_1, "leaves"))) return p6, surface_node.name + else + settlements.debug("find_surface2: wrong surface+1") + end else - settlements.debug("find_surface2: wrong surface+1") + settlements.debug("find_surface8: missing node or plus_1") end else settlements.debug("find_surface3: wrong surface "..surface_node.name.." at pos "..minetest.pos_to_string(p6))