diff --git a/maidroid/api.lua b/maidroid/api.lua index 92351fc..6177f25 100644 --- a/maidroid/api.lua +++ b/maidroid/api.lua @@ -132,21 +132,34 @@ function maidroid.maidroid.set_yaw_by_direction(self, direction) self.object:setyaw(math.atan2(direction.z, direction.x) - math.pi / 2) end --- maidroid.maidroid.get_wield_item_info returns the maidroid's wield item's stack. +-- maidroid.maidroid.get_wield_item_stack returns the maidroid's wield item's stack. function maidroid.maidroid.get_wield_item_stack(self) local inv = self:get_inventory() return inv:get_stack("wield_item", 1) end +-- maidroid.maidroid.set_wield_item_stack sets maidroid's wield item stack. +function maidroid.maidroid.set_wield_item_stack(self, stack) + local inv = self:get_inventory() + inv:set_stack("wield_item", 1, stack) +end + +-- maidroid.maidroid.add_item_to_main add item to main slot. +-- and returns leftover. +function maidroid.maidroid.add_item_to_main(self, stack) + local inv = self:get_inventory() + return inv:add_item("main", stack) +end + -- maidroid.maidroid.move_main_to_wield moves itemstack from main to wield. -- if this function fails then returns false, else returns true. -function maidroid.maidroid.move_main_to_wield(self, itemname) +function maidroid.maidroid.move_main_to_wield(self, pred) local inv = self:get_inventory() local main_size = inv:get_size("main") for i = 1, main_size do local stack = inv:get_stack("main", i) - if stack:get_name() == itemname then + if pred(stack:get_name()) then local wield_stack = inv:get_stack("wield_item", 1) inv:set_stack("wield_item", 1, stack) inv:set_stack("main", i, wield_stack) @@ -161,6 +174,42 @@ function maidroid.maidroid.is_named(self) return self.nametag ~= "" end +-- maidroid.maidroid.has_item_in_main reports whether the maidroid has item. +function maidroid.maidroid.has_item_in_main(self, pred) + local inv = self:get_inventory() + local stacks = inv:get_list("main") + + for _, stack in ipairs(stacks) do + local itemname = stack:get_name() + if pred(itemname) then + return true + end + end +end + +-- maidroid.maidroid.change_direction change direction to destination and velocity vector. +function maidroid.maidroid.change_direction(self, destination) + local position = self.object:getpos() + local direction = vector.subtract(destination, position) + direction.y = 0 + local velocity = vector.multiply(vector.normalize(direction), 3) + + self.object:setvelocity(velocity) + self:set_yaw_by_direction(direction) +end + +-- maidroid.maidroid.change_direction_randomly change direction randonly. +function maidroid.maidroid.change_direction_randomly(self) + local direction = { + x = math.random(0, 5) * 2 - 5, + y = 0, + z = math.random(0, 5) * 2 - 5, + } + local velocity = vector.multiply(vector.normalize(direction), 3) + self.object:setvelocity(velocity) + self:set_yaw_by_direction(direction) +end + --------------------------------------------------------------------- -- maidroid.manufacturing_data represents a table that contains manufacturing data. @@ -545,8 +594,13 @@ function maidroid.register_maidroid(product_name, def) set_animation = maidroid.maidroid.set_animation, set_yaw_by_direction = maidroid.maidroid.set_yaw_by_direction, get_wield_item_stack = maidroid.maidroid.get_wield_item_stack, + set_wield_item_stack = maidroid.maidroid.set_wield_item_stack, + add_item_to_main = maidroid.maidroid.add_item_to_main, move_main_to_wield = maidroid.maidroid.move_main_to_wield, is_named = maidroid.maidroid.is_named, + has_item_in_main = maidroid.maidroid.has_item_in_main, + change_direction = maidroid.maidroid.change_direction, + change_direction_randomly = maidroid.maidroid.change_direction_randomly, }) -- register maidroid egg. diff --git a/maidroid_core/cores/_aux.lua b/maidroid_core/cores/_aux.lua new file mode 100644 index 0000000..2a4083c --- /dev/null +++ b/maidroid_core/cores/_aux.lua @@ -0,0 +1,20 @@ +------------------------------------------------------------ +-- Copyright (c) 2016 tacigar. All rights reserved. +-- https://github.com/tacigar/maidroid +------------------------------------------------------------ + +maidroid_core._aux = {} + +function maidroid_core._aux.search_surrounding(pos, pred, searching_range) + for x = -searching_range.x, searching_range.x do + for y = -searching_range.y, searching_range.y do + for z = -searching_range.z, searching_range.z do + local p = vector.add(pos, {x = x, y = y, z = z}) + if pred(p) then + return p + end + end + end + end + return nil +end diff --git a/maidroid_core/cores/farming.lua b/maidroid_core/cores/farming.lua new file mode 100644 index 0000000..5f71a76 --- /dev/null +++ b/maidroid_core/cores/farming.lua @@ -0,0 +1,290 @@ +------------------------------------------------------------ +-- Copyright (c) 2016 tacigar. All rights reserved. +-- https://github.com/tacigar/maidroid +------------------------------------------------------------ + +local state = { + WALK_RANDOMLY = 0, + WALK_TO_PLANT = 1, + WALK_TO_MOW = 2, + PLANT = 3, + MOW = 4, +} + +local target_plants = { + "farming:cotton_8", + "farming:wheat_8", +} + +local _aux = maidroid_core._aux + +local FIND_PATH_TIME_INTERVAL = 20 +local CHANGE_DIRECTION_TIME_INTERVAL = 30 +local MAX_WALK_TIME = 120 + +-- is_plantable_place reports whether maidroid can plant any seed. +local function is_plantable_place(pos) + local node = minetest.get_node(pos) + local lpos = vector.add(pos, {x = 0, y = -1, z = 0}) + local lnode = minetest.get_node(lpos) + return node.name == "air" + and minetest.get_item_group(lnode.name, "wet") > 0 +end + +-- is_mowable_place reports whether maidroid can mow. +local function is_mowable_place(pos) + local node = minetest.get_node(pos) + for _, plant in ipairs(target_plants) do + if plant == node.name then + return true + end + end + return false +end + + +local walk_randomly, walk_to_plant_and_mow_common, plant, mow +local to_walk_randomly, to_walk_to_plant, to_walk_to_mow, to_plant, to_mow + +local function on_start(self) + self.object:setacceleration{x = 0, y = -10, z = 0} + self.object:setvelocity{x = 0, y = 0, z = 0} + self.state = state.WALK_RANDOMLY + self.time_counters = {} + self.path = nil + to_walk_randomly(self) +end + +local function on_stop(self) + self.object:setvelocity{x = 0, y = 0, z = 0} + self.state = nil + self.time_counters = nil + self.path = nil + self:set_animation(maidroid.animation_frames.STAND) +end + +local function is_near(self, pos, distance) + local p = self.object:getpos() + -- p.y = p.y + 0.5 + return vector.distance(p, pos) < distance +end + +local searching_range = {x = 5, y = 2, z = 5} + +walk_randomly = function(self, dtime) + if self.time_counters[1] >= FIND_PATH_TIME_INTERVAL then + self.time_counters[1] = 0 + self.time_counters[2] = self.time_counters[2] + 1 + + local wield_stack = self:get_wield_item_stack() + if minetest.get_item_group(wield_stack:get_name(), "seed") > 0 + or self:has_item_in_main(function(itemname) return (minetest.get_item_group(itemname, "seed") > 0) end) then + local destination = _aux.search_surrounding(self.object:getpos(), is_plantable_place, searching_range) + if destination ~= nil then + local path = minetest.find_path(self.object:getpos(), destination, 10, 1, 1, "A*") + + if path ~= nil then -- to walk to plant state. + to_walk_to_plant(self, path, destination) + return + end + end + end + -- if couldn't find path to plant, try to mow. + local destination = _aux.search_surrounding(self.object:getpos(), is_mowable_place, searching_range) + if destination ~= nil then + local path = minetest.find_path(self.object:getpos(), destination, 10, 1, 1, "A*") + if path ~= nil then -- to walk to mow state. + to_walk_to_mow(self, path, destination) + return + end + end + -- else do nothing. + return + + elseif self.time_counters[2] >= CHANGE_DIRECTION_TIME_INTERVAL then + self.time_counters[1] = self.time_counters[1] + 1 + self.time_counters[2] = 0 + self:change_direction_randomly() + return + else + self.time_counters[1] = self.time_counters[1] + 1 + self.time_counters[2] = self.time_counters[2] + 1 + + local velocity = self.object:getvelocity() + if velocity.y == 0 then + local front_node = self:get_front_node() + if front_node.name ~= "air" and minetest.registered_nodes[front_node.name].walkable + and not (minetest.get_item_group(front_node.name, "fence") > 0) then + self.object:setvelocity{x = velocity.x, y = 6, z = velocity.z} + end + end + return + end +end + +to_walk_randomly = function(self) + self.state = state.WALK_RANDOMLY + self.time_counters[1] = 0 + self.time_counters[2] = 0 + self:change_direction_randomly() + self:set_animation(maidroid.animation_frames.WALK) +end + +to_walk_to_plant = function(self, path, destination) + self.state = state.WALK_TO_PLANT + self.path = path + self.destination = destination + self.time_counters[1] = 0 -- find path interval + self.time_counters[2] = 0 + self:change_direction(self.path[1]) + self:set_animation(maidroid.animation_frames.WALK) +end + +to_walk_to_mow = function(self, path, destination) + self.state = state.WALK_TO_MOW + self.path = path + self.destination = destination + self.time_counters[1] = 0 -- find path interval + self.time_counters[2] = 0 + self:change_direction(self.path[1]) + self:set_animation(maidroid.animation_frames.WALK) +end + +to_plant = function(self) + local wield_stack = self:get_wield_item_stack() + if minetest.get_item_group(wield_stack:get_name(), "seed") > 0 + or self:move_main_to_wield(function(itemname) return (minetest.get_item_group(itemname, "seed") > 0) end) then + self.state = state.PLANT + self.time_counters[1] = 0 + self.object:setvelocity{x = 0, y = 0, z = 0} + self:set_animation(maidroid.animation_frames.MINE) + return + else + to_walk_randomly(self) + return + end +end + +to_mow = function(self) + self.state = state.MOW + self.time_counters[1] = 0 + self.object:setvelocity{x = 0, y = 0, z = 0} + self:set_animation(maidroid.animation_frames.MINE) +end + +walk_to_plant_and_mow_common = function(self, dtime) + if is_near(self, self.destination, 1.5) then + if self.state == state.WALK_TO_PLANT then + to_plant(self) + return + elseif self.state == state.WALK_TO_MOW then + to_mow(self) + return + end + end + + if self.time_counters[2] >= MAX_WALK_TIME then -- time over. + to_walk_randomly(self) + return + end + + self.time_counters[1] = self.time_counters[1] + 1 + self.time_counters[2] = self.time_counters[2] + 1 + + if self.time_counters[1] >= FIND_PATH_TIME_INTERVAL then + self.time_counters[1] = 0 + local path = minetest.find_path(self.object:getpos(), self.destination, 10, 1, 1, "A*") + if path == nil then + to_walk_randomly(self) + return + end + self.path = path + end + + -- follow path + if is_near(self, self.path[1], 0.5) then + table.remove(self.path, 1) + + if #self.path == 0 then -- end of path + if self.state == state.WALK_TO_PLANT then + to_plant(self) + return + elseif self.state == state.WALK_TO_MOW then + to_mow(self) + return + end + else -- else next step, follow next path. + self:change_direction(self.path[1]) + end + + else + -- self:change_direction(self.path[1]) + -- if maidroid is stopped by obstacles, the maidroid must jump. + -- self:change_direction(self.path[1]) + local velocity = self.object:getvelocity() + if velocity.y == 0 then + local front_node = self:get_front_node() + if front_node.name ~= "air" and minetest.registered_nodes[front_node.name].walkable + and not (minetest.get_item_group(front_node.name, "fence") > 0) then + self.object:setvelocity{x = velocity.x, y = 6, z = velocity.z} + end + end + end +end + +plant = function(self, dtime) + if self.time_counters[1] >= 15 then + if is_plantable_place(self.destination) then + local stack = self:get_wield_item_stack() + local itemname = stack:get_name() + minetest.add_node(self.destination, {name = itemname, param2 = 1}) + stack:take_item(1) + self:set_wield_item_stack(stack) + end + to_walk_randomly(self) + return + else + self.time_counters[1] = self.time_counters[1] + 1 + end +end + +mow = function(self, dtime) + if self.time_counters[1] >= 15 then + if is_mowable_place(self.destination) then + local destnode = minetest.get_node(self.destination) + minetest.remove_node(self.destination) + local stacks = minetest.get_node_drops(destnode.name) + + for _, stack in ipairs(stacks) do + local leftover = self:add_item_to_main(stack) + minetest.add_item(self.destination, leftover) + end + end + to_walk_randomly(self) + return + else + self.time_counters[1] = self.time_counters[1] + 1 + end +end + +local function on_step(self, dtime) + if self.state == state.WALK_RANDOMLY then + walk_randomly(self, dtime) + elseif self.state == state.WALK_TO_PLANT or self.state == state.WALK_TO_MOW then + walk_to_plant_and_mow_common(self, dtime) + elseif self.state == state.PLANT then + plant(self, dtime) + elseif self.state == state.MOW then + mow(self, dtime) + end +end + +maidroid.register_core("maidroid_core:farming", { + description = "maidroid core : farming", + inventory_image = "maidroid_core_farming.png", + on_start = on_start, + on_stop = on_stop, + on_resume = on_start, + on_pause = on_stop, + on_step = on_step, +}) diff --git a/maidroid_core/init.lua b/maidroid_core/init.lua index 98f0535..a8af579 100644 --- a/maidroid_core/init.lua +++ b/maidroid_core/init.lua @@ -8,5 +8,7 @@ maidroid_core = {} maidroid_core.modname = "maidroid_core" maidroid_core.modpath = minetest.get_modpath(maidroid_core.modname) +dofile(maidroid_core.modpath .. "/cores/_aux.lua") dofile(maidroid_core.modpath .. "/cores/empty.lua") dofile(maidroid_core.modpath .. "/cores/basic.lua") +dofile(maidroid_core.modpath .. "/cores/farming.lua") diff --git a/maidroid_core/textures/maidroid_core_farming.png b/maidroid_core/textures/maidroid_core_farming.png new file mode 100644 index 0000000..cec53df Binary files /dev/null and b/maidroid_core/textures/maidroid_core_farming.png differ diff --git a/maidroid_core/textures/maidroid_core_farming.xcf b/maidroid_core/textures/maidroid_core_farming.xcf new file mode 100644 index 0000000..eb5cdb9 Binary files /dev/null and b/maidroid_core/textures/maidroid_core_farming.xcf differ diff --git a/maidroid_tool/core_writer.lua b/maidroid_tool/core_writer.lua index 1a4b7ec..2a73252 100644 --- a/maidroid_tool/core_writer.lua +++ b/maidroid_tool/core_writer.lua @@ -7,6 +7,7 @@ do -- register core writer local dye_item_map = { ["dye:red"] = "maidroid_core:basic", + ["dye:green"] = "maidroid_core:farming", } local node_box = {