From c580c86ac049d73d60aa77ff766d486c209cbaf4 Mon Sep 17 00:00:00 2001 From: tacigar Date: Tue, 13 Sep 2016 09:48:17 +0900 Subject: [PATCH] space_to_tab --- _aux.lua | 8 +- api.lua | 256 ++++++++++++------------ maidroids.lua | 22 +-- modules/_aux.lua | 104 +++++----- modules/chasing_player_module.lua | 114 +++++------ modules/empty_module.lua | 10 +- modules/farming_module.lua | 306 ++++++++++++++--------------- modules/lumberjack_module.lua | 314 +++++++++++++++--------------- recipe.lua | 84 ++++---- util.lua | 18 +- 10 files changed, 618 insertions(+), 618 deletions(-) diff --git a/_aux.lua b/_aux.lua index 5f8f123..63c34ea 100644 --- a/_aux.lua +++ b/_aux.lua @@ -7,8 +7,8 @@ maidroid._aux = {} -- get inventory of the maidroid function maidroid._aux.get_maidroid_inventory(self) - return minetest.get_inventory{ - type = "detached", - name = self.invname, - } + return minetest.get_inventory{ + type = "detached", + name = self.invname, + } end diff --git a/api.lua b/api.lua index 93ff68c..7ff5cdc 100644 --- a/api.lua +++ b/api.lua @@ -7,11 +7,11 @@ local _aux = maidroid._aux -- aux function to generate serialnumber for inventories local gen_inv_serialnumber = (function () - local serialnumber = 0 - return function () - serialnumber = serialnumber + 1 - return serialnumber - 1 - end + local serialnumber = 0 + return function () + serialnumber = serialnumber + 1 + return serialnumber - 1 + end end) () local main_invsize = 16 @@ -23,140 +23,140 @@ local module_invname = "module" maidroid.registered_modules = {} function maidroid.register_module(module_name, def) - maidroid.registered_modules[module_name] = def - minetest.register_craftitem(module_name, { - description = def.description, - stack_max = 1, - inventory_image = def.inventory_image, - }) + maidroid.registered_modules[module_name] = def + minetest.register_craftitem(module_name, { + description = def.description, + stack_max = 1, + inventory_image = def.inventory_image, + }) end -- animation frame maidroid.animations = { - stand = {x = 0, y = 79}, - lay = {x = 162, y = 166}, - walk = {x = 168, y = 187}, - mine = {x = 189, y = 198}, - walk_mine = {x = 200, y = 219}, - sit = {x = 81, y = 160}, + stand = {x = 0, y = 79}, + lay = {x = 162, y = 166}, + walk = {x = 168, y = 187}, + mine = {x = 189, y = 198}, + walk_mine = {x = 200, y = 219}, + sit = {x = 81, y = 160}, } function maidroid.register_maidroid(product_name, def) - minetest.register_entity(product_name, { - hp_max = def.hp_max or 1, - physical = true, - weight = def.weight or 5, - collistionbox = def.collistionbox or { -0.35, -0.5, -0.35, 0.35, 1.1, 0.35 }, - visual = "mesh", - visual_size = {x = 10, y = 10}, - mesh = def.mesh or "maidroid.b3d", - textures = def.textures or {"maidroid.png"}, - is_visible = true, - makes_footstep_sound = true, - module = nil, - invname = "", + minetest.register_entity(product_name, { + hp_max = def.hp_max or 1, + physical = true, + weight = def.weight or 5, + collistionbox = def.collistionbox or { -0.35, -0.5, -0.35, 0.35, 1.1, 0.35 }, + visual = "mesh", + visual_size = {x = 10, y = 10}, + mesh = def.mesh or "maidroid.b3d", + textures = def.textures or {"maidroid.png"}, + is_visible = true, + makes_footstep_sound = true, + module = nil, + invname = "", - on_activate = function(self, staticdata) - self.invname = "maidroid"..tostring(gen_inv_serialnumber()) - local inv = minetest.create_detached_inventory(self.invname, { - on_put = function(inv, listname, index, stack, player) - if listname == module_invname then - local module_name = stack:get_name() - local module_def = maidroid.registered_modules[module_name] - self.module = module_def - module_def.initialize(self) - end - end, - allow_put = function(inv, listname, index, stack, player) - local item_name = stack:get_name() - local is_module = maidroid.registered_modules[item_name] - if listname == main_invname - or (listname == module_invname and is_module) then - return stack:get_count() - end - return 0 - end, - on_take = function(inv, listname, index, stack, player) - if listname == module_invname then - local module_name = stack:get_name() - local module_def = maidroid.registered_modules[module_name] - self.module = nil - module_def.finalize(self) - end - end, - }) - inv:set_size(main_invname, main_invsize) - inv:set_size(module_invname, module_invsize) - -- process staticdata - if staticdata ~= "" then - local data = minetest.deserialize(staticdata) - if data.inv.module ~= "" then - module_stack = ItemStack(data.inv.module) - module_stack:set_count(1) - inv:add_item(module_invname, module_stack) - self.module = maidroid.registered_modules[data.inv.module] - end - for _, item in ipairs(data.inv.main) do - local itemstack = ItemStack(item.name) - itemstack:set_count(item.count) - inv:add_item(main_invname, itemstack) - end - end - -- initialize module - if self.module then self.module.initialize(self) - else - self.object:setvelocity{x = 0, y = 0, z = 0} - self.object:setacceleration{x = 0, y = -10, z = 0} - end - end, + on_activate = function(self, staticdata) + self.invname = "maidroid"..tostring(gen_inv_serialnumber()) + local inv = minetest.create_detached_inventory(self.invname, { + on_put = function(inv, listname, index, stack, player) + if listname == module_invname then + local module_name = stack:get_name() + local module_def = maidroid.registered_modules[module_name] + self.module = module_def + module_def.initialize(self) + end + end, + allow_put = function(inv, listname, index, stack, player) + local item_name = stack:get_name() + local is_module = maidroid.registered_modules[item_name] + if listname == main_invname + or (listname == module_invname and is_module) then + return stack:get_count() + end + return 0 + end, + on_take = function(inv, listname, index, stack, player) + if listname == module_invname then + local module_name = stack:get_name() + local module_def = maidroid.registered_modules[module_name] + self.module = nil + module_def.finalize(self) + end + end, + }) + inv:set_size(main_invname, main_invsize) + inv:set_size(module_invname, module_invsize) + -- process staticdata + if staticdata ~= "" then + local data = minetest.deserialize(staticdata) + if data.inv.module ~= "" then + module_stack = ItemStack(data.inv.module) + module_stack:set_count(1) + inv:add_item(module_invname, module_stack) + self.module = maidroid.registered_modules[data.inv.module] + end + for _, item in ipairs(data.inv.main) do + local itemstack = ItemStack(item.name) + itemstack:set_count(item.count) + inv:add_item(main_invname, itemstack) + end + end + -- initialize module + if self.module then self.module.initialize(self) + else + self.object:setvelocity{x = 0, y = 0, z = 0} + self.object:setacceleration{x = 0, y = -10, z = 0} + end + end, - on_step = function(self, dtime) - if self.module then self.module.on_step(self, dtime) end - end, + on_step = function(self, dtime) + if self.module then self.module.on_step(self, dtime) end + end, - on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir) - end, + on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir) + end, - on_rightclick = function(self, clicker) - local formspec = "size[8,9]" - .."list[detached:"..self.invname..";"..main_invname..";0,0;4,4;]" - .."label[5,0;MODULE]" - .."list[detached:"..self.invname..";"..module_invname..";6,0;1,1;]" - .."list[current_player;"..main_invname..";0,5;8,1;]" - .."list[current_player;"..main_invname..";0,6.2;8,3;8]" - minetest.show_formspec(clicker:get_player_name(), self.invname, formspec) - end, + on_rightclick = function(self, clicker) + local formspec = "size[8,9]" + .."list[detached:"..self.invname..";"..main_invname..";0,0;4,4;]" + .."label[5,0;MODULE]" + .."list[detached:"..self.invname..";"..module_invname..";6,0;1,1;]" + .."list[current_player;"..main_invname..";0,5;8,1;]" + .."list[current_player;"..main_invname..";0,6.2;8,3;8]" + minetest.show_formspec(clicker:get_player_name(), self.invname, formspec) + end, - get_staticdata = function(self) - local inv = _aux.get_maidroid_inventory(self) - local staticdata = {} - staticdata.inv = {} - local module_name = inv:get_list(module_invname)[1]:get_name() - staticdata.inv.module = module_name or "" - staticdata.inv.main = {} - for _, item in ipairs(inv:get_list(main_invname)) do - local count = item:get_count() - local itemname = item:get_name() - if count ~= 0 then - local itemdata = { count = count, name = itemname } - table.insert(staticdata.inv.main, itemdata) - end - end - return minetest.serialize(staticdata) - end, - }) + get_staticdata = function(self) + local inv = _aux.get_maidroid_inventory(self) + local staticdata = {} + staticdata.inv = {} + local module_name = inv:get_list(module_invname)[1]:get_name() + staticdata.inv.module = module_name or "" + staticdata.inv.main = {} + for _, item in ipairs(inv:get_list(main_invname)) do + local count = item:get_count() + local itemname = item:get_name() + if count ~= 0 then + local itemdata = { count = count, name = itemname } + table.insert(staticdata.inv.main, itemdata) + end + end + return minetest.serialize(staticdata) + end, + }) - -- register spawn egg - minetest.register_craftitem(product_name.."_spawn_egg", { - description = def.description.." Spawn Egg", - inventory_image = def.inventory_image, - stack_max = 1, - on_use = function(itemstack, user, pointed_thing) - if pointed_thing.above ~= nil then - minetest.add_entity(pointed_thing.above, product_name) - return itemstack - end - return nil - end - }) + -- register spawn egg + minetest.register_craftitem(product_name.."_spawn_egg", { + description = def.description.." Spawn Egg", + inventory_image = def.inventory_image, + stack_max = 1, + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.above ~= nil then + minetest.add_entity(pointed_thing.above, product_name) + return itemstack + end + return nil + end + }) end diff --git a/maidroids.lua b/maidroids.lua index 5f5d1dc..4522b7e 100644 --- a/maidroids.lua +++ b/maidroids.lua @@ -4,21 +4,21 @@ ------------------------------------------------------------ maidroid.register_maidroid("maidroid:maidroid", { - hp_max = 10, - description = "Maidroid : Maidroid", - inventory_image = "maidroid_maidroid.png", + hp_max = 10, + description = "Maidroid : Maidroid", + inventory_image = "maidroid_maidroid.png", }) maidroid.register_maidroid("maidroid:maidroid_mk2", { - hp_max = 10, - description = "Maidroid : Maidroid MkII", - textures = {"maidroid_mk2.png"}, - inventory_image = "maidroid_maidroid_mk2.png", + hp_max = 10, + description = "Maidroid : Maidroid MkII", + textures = {"maidroid_mk2.png"}, + inventory_image = "maidroid_maidroid_mk2.png", }) maidroid.register_maidroid("maidroid:maidroid_mk3", { - hp_max = 10, - description = "Maidroid : Maidroid MkIII", - textures = {"maidroid_mk3.png"}, - inventory_image = "maidroid_maidroid_mk3.png", + hp_max = 10, + description = "Maidroid : Maidroid MkIII", + textures = {"maidroid_mk3.png"}, + inventory_image = "maidroid_maidroid_mk3.png", }) diff --git a/modules/_aux.lua b/modules/_aux.lua index 65cac25..df6cfb4 100644 --- a/modules/_aux.lua +++ b/modules/_aux.lua @@ -9,84 +9,84 @@ local velocity = 3 -- change direction to destination and velocity vector function maidroid.modules._aux.change_dir_to(self, dest) - local pos = self.object:getpos() - local dir = vector.subtract(dest, pos) - local vel = vector.multiply(vector.normalize(dir), velocity) - self.object:setvelocity(vel) - self.object:setyaw(math.atan2(vel.z, vel.x) + math.pi / 2) + local pos = self.object:getpos() + local dir = vector.subtract(dest, pos) + local vel = vector.multiply(vector.normalize(dir), velocity) + self.object:setvelocity(vel) + self.object:setyaw(math.atan2(vel.z, vel.x) + math.pi / 2) end -- change direction and velocity vector function maidroid.modules._aux.change_dir(self) - local rnd = function() return math.random(0, 5) * 2 - 5 end - local dir = {x = rnd(), y = 0, z = rnd()} - local vel = vector.multiply(vector.normalize(dir), velocity) - self.object:setvelocity(vel) - self.object:setyaw(math.atan2(vel.z, vel.x) + math.pi / 2) + local rnd = function() return math.random(0, 5) * 2 - 5 end + local dir = {x = rnd(), y = 0, z = rnd()} + local vel = vector.multiply(vector.normalize(dir), velocity) + self.object:setvelocity(vel) + self.object:setyaw(math.atan2(vel.z, vel.x) + math.pi / 2) end -- get direction vector by yaw function maidroid.modules._aux.get_forward(yaw) - return { x = math.sin(yaw), y = 0.0, z = -math.cos(yaw) } + return { x = math.sin(yaw), y = 0.0, z = -math.cos(yaw) } end -- round direction vector function maidroid.modules._aux.get_round_forward(forward) - local rforward = { x = 0, y = 0, z = 0} - if math.abs((forward.x / (math.abs(forward.x) + math.abs(forward.z)))) > 0.5 then - if forward.x > 0 then rforward.x = 1 - else rforward.x = -1 end - end - if math.abs((forward.z / (math.abs(forward.x) + math.abs(forward.z)))) > 0.5 then - if forward.z > 0 then rforward.z = 1 - else rforward.z = -1 end - end - return rforward + local rforward = { x = 0, y = 0, z = 0} + if math.abs((forward.x / (math.abs(forward.x) + math.abs(forward.z)))) > 0.5 then + if forward.x > 0 then rforward.x = 1 + else rforward.x = -1 end + end + if math.abs((forward.z / (math.abs(forward.x) + math.abs(forward.z)))) > 0.5 then + if forward.z > 0 then rforward.z = 1 + else rforward.z = -1 end + end + return rforward end function maidroid.modules._aux.get_under_pos(vec) - return { x = vec.x, y = vec.y - 1, z = vec.z } + return { x = vec.x, y = vec.y - 1, z = vec.z } end function maidroid.modules._aux.get_upper_pos(vec) - return { x = vec.x, y = vec.y + 1, z = vec.z } + return { x = vec.x, y = vec.y + 1, z = vec.z } end -- pickup droped items function maidroid.modules._aux.pickup_item(self, radius, target_pred) - local pos = self.object:getpos() - local pred = target_pred or (function(itemstring) return true end) - local all_objects = minetest.get_objects_inside_radius(pos, radius) - for _, obj in ipairs(all_objects) do - if not obj:is_player() and obj:get_luaentity() then - local itemstring = obj:get_luaentity().itemstring - if itemstring then - if pred(itemstring) then - local inv = maidroid._aux.get_maidroid_inventory(self) - local stack = ItemStack(itemstring) - local leftover = inv:add_item("main", stack) - minetest.add_item(obj:getpos(), leftover) - obj:get_luaentity().itemstring = "" - obj:remove() - end - end - end - end + local pos = self.object:getpos() + local pred = target_pred or (function(itemstring) return true end) + local all_objects = minetest.get_objects_inside_radius(pos, radius) + for _, obj in ipairs(all_objects) do + if not obj:is_player() and obj:get_luaentity() then + local itemstring = obj:get_luaentity().itemstring + if itemstring then + if pred(itemstring) then + local inv = maidroid._aux.get_maidroid_inventory(self) + local stack = ItemStack(itemstring) + local leftover = inv:add_item("main", stack) + minetest.add_item(obj:getpos(), leftover) + obj:get_luaentity().itemstring = "" + obj:remove() + end + end + end + end end -- search surrounding nodes function maidroid.modules._aux.search_surrounding(self, lenvec, pred) - local pos = vector.round(self.object:getpos()) - for xi = -lenvec.x, lenvec.x do - for yi = -lenvec.y, lenvec.y do - for zi = -lenvec.z, lenvec.z do - local p = {x = pos.x + xi, y = pos.y + yi, z = pos.z + zi} - local node = minetest.get_node(p) - if pred(self, p, node) then return true, p, node end - end - end - end - return false, nil, nil + local pos = vector.round(self.object:getpos()) + for xi = -lenvec.x, lenvec.x do + for yi = -lenvec.y, lenvec.y do + for zi = -lenvec.z, lenvec.z do + local p = {x = pos.x + xi, y = pos.y + yi, z = pos.z + zi} + local node = minetest.get_node(p) + if pred(self, p, node) then return true, p, node end + end + end + end + return false, nil, nil end diff --git a/modules/chasing_player_module.lua b/modules/chasing_player_module.lua index a0304d7..76be091 100644 --- a/modules/chasing_player_module.lua +++ b/modules/chasing_player_module.lua @@ -11,65 +11,65 @@ local view_of_range = 7 local stop_of_range = 2 maidroid.register_module("maidroid:chasing_player_module", { - description = "Maidroid Module : Chasing Player", - inventory_image = "maidroid_chasing_player_module.png", + description = "Maidroid Module : Chasing Player", + inventory_image = "maidroid_chasing_player_module.png", - initialize = function(self) - self.state = state.idle - self.object:setacceleration{x = 0, y = -10, z = 0} - self.object:setvelocity{x = 0, y = 0, z = 0} - end, + initialize = function(self) + self.state = state.idle + self.object:setacceleration{x = 0, y = -10, z = 0} + self.object:setvelocity{x = 0, y = 0, z = 0} + end, - finalize = function(self) - self.state = nil - self.object:setvelocity{x = 0, y = 0, z = 0} - end, + finalize = function(self) + self.state = nil + self.object:setvelocity{x = 0, y = 0, z = 0} + end, - on_step = function(self, dtime) - local pos = self.object:getpos() - local all_objects = minetest.get_objects_inside_radius(pos, view_of_range) - local player = nil - for _, obj in pairs(all_objects) do - if obj:is_player() then player = obj; break end - end - if not player then - self.object:set_animation(maidroid.animations.stand, 15, 0) - self.state = state.idle - return - end - local ppos = player:getpos() - local dir = vector.subtract(ppos, pos) - local vel = self.object:getvelocity() - if (vector.length(dir) < stop_of_range) then - if self.state == state.chase then - self.object:set_animation(maidroid.animations.stand, 15, 0) - self.state = state.idle - self.object:setvelocity({x = 0, y = vel.y, z = 0}) - end - else - if self.state == state.idle then - self.object:set_animation(maidroid.animations.walk, 15, 0) - self.state = state.chase - end - self.object:setvelocity({x = dir.x, y = vel.y, z = dir.z}) - end - local yaw = math.atan2(dir.z, dir.x) + math.pi/2 - self.object:setyaw(yaw) + on_step = function(self, dtime) + local pos = self.object:getpos() + local all_objects = minetest.get_objects_inside_radius(pos, view_of_range) + local player = nil + for _, obj in pairs(all_objects) do + if obj:is_player() then player = obj; break end + end + if not player then + self.object:set_animation(maidroid.animations.stand, 15, 0) + self.state = state.idle + return + end + local ppos = player:getpos() + local dir = vector.subtract(ppos, pos) + local vel = self.object:getvelocity() + if (vector.length(dir) < stop_of_range) then + if self.state == state.chase then + self.object:set_animation(maidroid.animations.stand, 15, 0) + self.state = state.idle + self.object:setvelocity({x = 0, y = vel.y, z = 0}) + end + else + if self.state == state.idle then + self.object:set_animation(maidroid.animations.walk, 15, 0) + self.state = state.chase + end + self.object:setvelocity({x = dir.x, y = vel.y, z = dir.z}) + end + local yaw = math.atan2(dir.z, dir.x) + math.pi/2 + self.object:setyaw(yaw) - -- jump process - if vel.y == 0 and self.state == state.chase then - local rdir = vector.round(dir) - local front_vec = { x = 0, y = 0, z = 0 } - if math.abs((rdir.x / (math.abs(rdir.x) + math.abs(rdir.z)))) > 0.5 then - if rdir.x > 0 then front_vec.x = 1 else front_vec.x = -1 end - end - if math.abs((rdir.z / (math.abs(rdir.x) + math.abs(rdir.z)))) > 0.5 then - if rdir.z > 0 then front_vec.z = 1 else front_vec.z = -1 end - end - local front_pos = vector.add(vector.round(pos), front_vec) - if minetest.get_node(front_pos).name ~= "air" then - self.object:setvelocity({x = dir.x, y = 5, z = dir.z}) - end - end - end, + -- jump process + if vel.y == 0 and self.state == state.chase then + local rdir = vector.round(dir) + local front_vec = { x = 0, y = 0, z = 0 } + if math.abs((rdir.x / (math.abs(rdir.x) + math.abs(rdir.z)))) > 0.5 then + if rdir.x > 0 then front_vec.x = 1 else front_vec.x = -1 end + end + if math.abs((rdir.z / (math.abs(rdir.x) + math.abs(rdir.z)))) > 0.5 then + if rdir.z > 0 then front_vec.z = 1 else front_vec.z = -1 end + end + local front_pos = vector.add(vector.round(pos), front_vec) + if minetest.get_node(front_pos).name ~= "air" then + self.object:setvelocity({x = dir.x, y = 5, z = dir.z}) + end + end + end, }) diff --git a/modules/empty_module.lua b/modules/empty_module.lua index a11592e..df23fdf 100644 --- a/modules/empty_module.lua +++ b/modules/empty_module.lua @@ -4,9 +4,9 @@ ------------------------------------------------------------ maidroid.register_module("maidroid:empty_module", { - description = "Maidroid Module : Empty Module", - inventory_image = "maidroid_empty_module.png", - initialize = function(self) end, - finalize = function(self) end, - on_step = function(self, dtime) end, + description = "Maidroid Module : Empty Module", + inventory_image = "maidroid_empty_module.png", + initialize = function(self) end, + finalize = function(self) end, + on_step = function(self, dtime) end, }) diff --git a/modules/farming_module.lua b/modules/farming_module.lua index c29fa3b..c5261d6 100644 --- a/modules/farming_module.lua +++ b/modules/farming_module.lua @@ -6,12 +6,12 @@ local _aux = maidroid.modules._aux local state = { - walk = 0, - punch = 1, - plant = 2, - walk_to_plant = 3, - walk_to_soil = 4, - walk_avoid = 5, + walk = 0, + punch = 1, + plant = 2, + walk_to_plant = 3, + walk_to_soil = 4, + walk_avoid = 5, } local max_punch_time = 20 local max_plant_time = 15 @@ -22,188 +22,188 @@ local search_lenvec = {x = 3, y = 0, z = 3} -- find max size of each plants local target_plants_list = {} minetest.after(0, function() - local max = {} - for name, node in pairs(minetest.registered_nodes) do - if minetest.get_item_group(name, "plant") > 0 then - local s, i = string.match(name, "(.+)_(%d+)") - if (s and i) and (max[s] == nil or max[s] < i) then max[s] = i end - end - end - for s, i in pairs(max) do - table.insert(target_plants_list, s.."_"..i) - end + local max = {} + for name, node in pairs(minetest.registered_nodes) do + if minetest.get_item_group(name, "plant") > 0 then + local s, i = string.match(name, "(.+)_(%d+)") + if (s and i) and (max[s] == nil or max[s] < i) then max[s] = i end + end + end + for s, i in pairs(max) do + table.insert(target_plants_list, s.."_"..i) + end end) -- check the maidroid has seed items local function has_seed_item(self) - local inv = maidroid._aux.get_maidroid_inventory(self) - local stacks = inv:get_list("main") - for _, stack in ipairs(stacks) do - local item_name = stack:get_name() - if minetest.get_item_group(item_name, "seed") > 0 then - return true - end - end - return false + local inv = maidroid._aux.get_maidroid_inventory(self) + local stacks = inv:get_list("main") + for _, stack in ipairs(stacks) do + local item_name = stack:get_name() + if minetest.get_item_group(item_name, "seed") > 0 then + return true + end + end + return false end -- check can plant plants. local function can_plant(self, pos) - local node = minetest.get_node(pos) - local upos = _aux.get_under_pos(pos) - local unode = minetest.get_node(upos) - return node.name == "air" - and minetest.get_item_group(unode.name, "wet") > 0 - and has_seed_item(self) + local node = minetest.get_node(pos) + local upos = _aux.get_under_pos(pos) + local unode = minetest.get_node(upos) + return node.name == "air" + and minetest.get_item_group(unode.name, "wet") > 0 + and has_seed_item(self) end -- check can punch plant local function can_punch(self, pos) - local node = minetest.get_node(pos) - return maidroid.util.table_find_value(target_plants_list, node.name) + local node = minetest.get_node(pos) + return maidroid.util.table_find_value(target_plants_list, node.name) end -- change state to walk local function to_walk(self) - self.state = state.walk - self.destination = nil - self.object:set_animation(maidroid.animations.walk, 15, 0) - self.time_count = 0 - _aux.change_dir(self) + self.state = state.walk + self.destination = nil + self.object:set_animation(maidroid.animations.walk, 15, 0) + self.time_count = 0 + _aux.change_dir(self) end local function to_walk_avoid(self) - to_walk(self) - self.state = state.walk_avoid + to_walk(self) + self.state = state.walk_avoid end maidroid.register_module("maidroid:farming_module", { - description = "Maidroid Module : Farming", - inventory_image = "maidroid_farming_module.png", + description = "Maidroid Module : Farming", + inventory_image = "maidroid_farming_module.png", - initialize = function(self) - self.object:set_animation(maidroid.animations.walk, 15, 0) - self.object:setacceleration{x = 0, y = -10, z = 0} - self.state = state.walk - self.preposition = self.object:getpos() - self.time_count = 0 - self.destination = nil -- for walk_to_* - _aux.change_dir(self) - end, + initialize = function(self) + self.object:set_animation(maidroid.animations.walk, 15, 0) + self.object:setacceleration{x = 0, y = -10, z = 0} + self.state = state.walk + self.preposition = self.object:getpos() + self.time_count = 0 + self.destination = nil -- for walk_to_* + _aux.change_dir(self) + end, - finalize = function(self) - self.state = nil - self.preposition = nil - self.time_count = nil - self.destination = nil - self.object:setvelocity{x = 0, y = 0, z = 0} - end, + finalize = function(self) + self.state = nil + self.preposition = nil + self.time_count = nil + self.destination = nil + self.object:setvelocity{x = 0, y = 0, z = 0} + end, - on_step = function(self, dtime) - local pos = self.object:getpos() - local rpos = vector.round(pos) - local upos = _aux.get_under_pos(pos) - local yaw = self.object:getyaw() + on_step = function(self, dtime) + local pos = self.object:getpos() + local rpos = vector.round(pos) + local upos = _aux.get_under_pos(pos) + local yaw = self.object:getyaw() - _aux.pickup_item(self, 1.5, function(itemstring) -- pickup droped seed items - return minetest.get_item_group(itemstring, "seed") > 0 - end) - if self.state == state.walk then -- searching plants or spaces - local b1, dest1 = _aux.search_surrounding(self, search_lenvec, can_plant) - local b2, dest2 = _aux.search_surrounding(self, search_lenvec, can_punch) - -- search soil node near - if b1 then -- to soil - self.state = state.walk_to_soil - self.destination = dest1 - _aux.change_dir_to(self, dest1) - elseif b2 then - self.state = state.walk_to_plant - self.destination = dest2 - _aux.change_dir_to(self, dest2) - elseif pos.x == self.preposition.x or pos.z == self.preposition.z then - _aux.change_dir(self) - end + _aux.pickup_item(self, 1.5, function(itemstring) -- pickup droped seed items + return minetest.get_item_group(itemstring, "seed") > 0 + end) + if self.state == state.walk then -- searching plants or spaces + local b1, dest1 = _aux.search_surrounding(self, search_lenvec, can_plant) + local b2, dest2 = _aux.search_surrounding(self, search_lenvec, can_punch) + -- search soil node near + if b1 then -- to soil + self.state = state.walk_to_soil + self.destination = dest1 + _aux.change_dir_to(self, dest1) + elseif b2 then + self.state = state.walk_to_plant + self.destination = dest2 + _aux.change_dir_to(self, dest2) + elseif pos.x == self.preposition.x or pos.z == self.preposition.z then + _aux.change_dir(self) + end - elseif self.state == state.punch then - if self.time_count >= max_punch_time then - if can_punch(self, self.destination) then - local destnode = minetest.get_node(self.destination) - minetest.remove_node(self.destination) - local inv = minetest.get_inventory{type = "detached", name = self.invname} - local stacks = minetest.get_node_drops(destnode.name) - for _, stack in ipairs(stacks) do - local leftover = inv:add_item("main", stack) - minetest.add_item(self.destination, leftover) - end - end - to_walk(self) - else - self.time_count = self.time_count + 1 - end + elseif self.state == state.punch then + if self.time_count >= max_punch_time then + if can_punch(self, self.destination) then + local destnode = minetest.get_node(self.destination) + minetest.remove_node(self.destination) + local inv = minetest.get_inventory{type = "detached", name = self.invname} + local stacks = minetest.get_node_drops(destnode.name) + for _, stack in ipairs(stacks) do + local leftover = inv:add_item("main", stack) + minetest.add_item(self.destination, leftover) + end + end + to_walk(self) + else + self.time_count = self.time_count + 1 + end - elseif self.state == state.plant then - if self.time_count >= max_plant_time then - if can_plant(self, self.destination) then - local inv = minetest.get_inventory{type = "detached", name = self.invname} - local stacks = inv:get_list("main") - for idx, stack in ipairs(stacks) do - local item_name = stack:get_name() - if minetest.get_item_group(item_name, "seed") > 0 then - minetest.add_node(self.destination, {name = item_name, param2 = 1}) - stack:take_item(1) - inv:set_stack("main", idx, stack) - break - end - end - end - to_walk(self) - else - self.time_count = self.time_count + 1 - end + elseif self.state == state.plant then + if self.time_count >= max_plant_time then + if can_plant(self, self.destination) then + local inv = minetest.get_inventory{type = "detached", name = self.invname} + local stacks = inv:get_list("main") + for idx, stack in ipairs(stacks) do + local item_name = stack:get_name() + if minetest.get_item_group(item_name, "seed") > 0 then + minetest.add_node(self.destination, {name = item_name, param2 = 1}) + stack:take_item(1) + inv:set_stack("main", idx, stack) + break + end + end + end + to_walk(self) + else + self.time_count = self.time_count + 1 + end - elseif self.state == state.walk_to_soil then - if vector.distance(pos, self.destination) < 1.5 then -- to plant state - local destnode = minetest.get_node(self.destination) - if (can_plant(self, self.destination)) then - self.state = state.plant - self.object:set_animation(maidroid.animations.mine, 15, 0) - self.object:setvelocity{x = 0, y = 0, z = 0} - else to_walk(self) end - else - if pos.x == self.preposition.x or pos.z == self.preposition.z then - to_walk_avoid(self) - end - end + elseif self.state == state.walk_to_soil then + if vector.distance(pos, self.destination) < 1.5 then -- to plant state + local destnode = minetest.get_node(self.destination) + if (can_plant(self, self.destination)) then + self.state = state.plant + self.object:set_animation(maidroid.animations.mine, 15, 0) + self.object:setvelocity{x = 0, y = 0, z = 0} + else to_walk(self) end + else + if pos.x == self.preposition.x or pos.z == self.preposition.z then + to_walk_avoid(self) + end + end - elseif self.state == state.walk_to_plant then - if vector.distance(pos, self.destination) < 1.5 then - local destnode = minetest.get_node(self.destination) - if maidroid.util.table_find_value(target_plants_list, destnode.name) then - self.state = state.punch - self.object:set_animation(maidroid.animations.mine, 15, 0) - self.object:setvelocity{x = 0, y = 0, z = 0} - else to_walk(self) end - else - if pos.x == self.preposition.x or pos.z == self.preposition.z then - to_walk_avoid(self) - end - end + elseif self.state == state.walk_to_plant then + if vector.distance(pos, self.destination) < 1.5 then + local destnode = minetest.get_node(self.destination) + if maidroid.util.table_find_value(target_plants_list, destnode.name) then + self.state = state.punch + self.object:set_animation(maidroid.animations.mine, 15, 0) + self.object:setvelocity{x = 0, y = 0, z = 0} + else to_walk(self) end + else + if pos.x == self.preposition.x or pos.z == self.preposition.z then + to_walk_avoid(self) + end + end - elseif self.state == state.walk_avoid then - if self.time_count > max_avoid_time then - self.state = state.walk - self.time_count = 0 - else - self.time_count = self.time_count + 1 - end - end - self.preposition = pos - return - end + elseif self.state == state.walk_avoid then + if self.time_count > max_avoid_time then + self.state = state.walk + self.time_count = 0 + else + self.time_count = self.time_count + 1 + end + end + self.preposition = pos + return + end }) diff --git a/modules/lumberjack_module.lua b/modules/lumberjack_module.lua index 99a8f05..c496ccc 100644 --- a/modules/lumberjack_module.lua +++ b/modules/lumberjack_module.lua @@ -7,11 +7,11 @@ local util = maidroid.util local _aux = maidroid.modules._aux local state = { - walk = 0, - plant = 1, - punch = 2, - walk_to_tree = 3, - walk_avoid = 4, + walk = 0, + plant = 1, + punch = 2, + walk_to_tree = 3, + walk_avoid = 4, } local find_lenvec = {x = 3, y = 0, z = 3} local plant_lenvec = {x = 2, y = 0, z = 2} @@ -23,170 +23,170 @@ local target_sapling_list = { "default:sapling" } -- check the maidroid need to start to punch local function check_punch_flag(forward_pos) - local forward_upper_pos = util.table_shallow_copy(forward_pos) - while true do - local forward_upper_node = minetest.get_node(forward_upper_pos) - if util.table_find_value(target_tree_list, forward_upper_node.name) then - return true, forward_upper_pos, forward_upper_node - elseif forward_upper_node.name ~= "air" then break end - forward_upper_pos.y = forward_upper_pos.y + 1 - end - return false, nil, nil + local forward_upper_pos = util.table_shallow_copy(forward_pos) + while true do + local forward_upper_node = minetest.get_node(forward_upper_pos) + if util.table_find_value(target_tree_list, forward_upper_node.name) then + return true, forward_upper_pos, forward_upper_node + elseif forward_upper_node.name ~= "air" then break end + forward_upper_pos.y = forward_upper_pos.y + 1 + end + return false, nil, nil end -- check the maidroid has sapling items local function has_sapling_item(self) - local inv = maidroid._aux.get_maidroid_inventory(self) - local stacks = inv:get_list("main") - for _, stack in ipairs(stacks) do - local item_name = stack:get_name() - if util.table_find_value(target_sapling_list, item_name) then - return true - end - end - return false + local inv = maidroid._aux.get_maidroid_inventory(self) + local stacks = inv:get_list("main") + for _, stack in ipairs(stacks) do + local item_name = stack:get_name() + if util.table_find_value(target_sapling_list, item_name) then + return true + end + end + return false end maidroid.register_module("maidroid:lumberjack_module", { - description = "Maidroid Module : Lumberjack", - inventory_image = "maidroid_lumberjack_module.png", + description = "Maidroid Module : Lumberjack", + inventory_image = "maidroid_lumberjack_module.png", - initialize = function(self) - self.state = state.walk - self.time_count = 0 - self.object:setacceleration{x = 0, y = -10, z = 0} - self.object:set_animation(maidroid.animations.walk, 15, 0) - self.preposition = self.object:getpos() - self.destination = nil - _aux.change_dir(self) - end, + initialize = function(self) + self.state = state.walk + self.time_count = 0 + self.object:setacceleration{x = 0, y = -10, z = 0} + self.object:set_animation(maidroid.animations.walk, 15, 0) + self.preposition = self.object:getpos() + self.destination = nil + _aux.change_dir(self) + end, - finalize = function(self) - self.state = nil - self.time_count = nil - self.preposition = nil - self.object:setvelocity{x = 0, y = 0, z = 0} - self.destination = nil - end, + finalize = function(self) + self.state = nil + self.time_count = nil + self.preposition = nil + self.object:setvelocity{x = 0, y = 0, z = 0} + self.destination = nil + end, - on_step = function(self, dtime) - local pos = self.object:getpos() - local rpos = vector.round(pos) - local yaw = self.object:getyaw() - local forward = _aux.get_forward(yaw) - local rforward = _aux.get_round_forward(forward) - local forward_pos = vector.add(rpos, rforward) - local forward_node = minetest.get_node(forward_pos) - local forward_under_pos = _aux.get_under_pos(forward_pos) - local forward_under_node = minetest.get_node(forward_under_pos) + on_step = function(self, dtime) + local pos = self.object:getpos() + local rpos = vector.round(pos) + local yaw = self.object:getyaw() + local forward = _aux.get_forward(yaw) + local rforward = _aux.get_round_forward(forward) + local forward_pos = vector.add(rpos, rforward) + local forward_node = minetest.get_node(forward_pos) + local forward_under_pos = _aux.get_under_pos(forward_pos) + local forward_under_node = minetest.get_node(forward_under_pos) - if self.state == state.walk then - local b, dest = _aux.search_surrounding(self, find_lenvec, function(self, pos, node) - return util.table_find_value(target_tree_list, node.name) - end) - if b then -- walk to tree - self.state = state.walk_to_tree - self.destination = dest - _aux.change_dir_to(self, dest) - -- to plant sapling - elseif forward_node.name == "air" - and minetest.get_item_group(forward_under_node.name, "soil") > 0 - and not _aux.search_surrounding(self, plant_lenvec, function(self, pos, node) -- no tree around - return util.table_find_value(target_tree_list, node.name) - or util.table_find_value(target_sapling_list, node.name) - end) - and has_sapling_item(self) then - self.state = state.plant - self.object:set_animation(maidroid.animations.mine, 15, 0) - self.object:setvelocity{x = 0, y = 0, z = 0} - -- else continue to walk - else - if pos.x == self.preposition.x or pos.z == self.preposition.z then - _aux.change_dir(self) - end - end - -- pickup sapling items - _aux.pickup_item(self, 1.5, function(itemstring) - return util.table_find_value(target_sapling_list, itemstring) - end) + if self.state == state.walk then + local b, dest = _aux.search_surrounding(self, find_lenvec, function(self, pos, node) + return util.table_find_value(target_tree_list, node.name) + end) + if b then -- walk to tree + self.state = state.walk_to_tree + self.destination = dest + _aux.change_dir_to(self, dest) + -- to plant sapling + elseif forward_node.name == "air" + and minetest.get_item_group(forward_under_node.name, "soil") > 0 + and not _aux.search_surrounding(self, plant_lenvec, function(self, pos, node) -- no tree around + return util.table_find_value(target_tree_list, node.name) + or util.table_find_value(target_sapling_list, node.name) + end) + and has_sapling_item(self) then + self.state = state.plant + self.object:set_animation(maidroid.animations.mine, 15, 0) + self.object:setvelocity{x = 0, y = 0, z = 0} + -- else continue to walk + else + if pos.x == self.preposition.x or pos.z == self.preposition.z then + _aux.change_dir(self) + end + end + -- pickup sapling items + _aux.pickup_item(self, 1.5, function(itemstring) + return util.table_find_value(target_sapling_list, itemstring) + end) - elseif self.state == state.punch then - if self.time_count >= max_punch_time then - local punch_flag, forward_upper_pos, forward_upper_node = check_punch_flag(self.destination) - if punch_flag then - minetest.remove_node(forward_upper_pos) - local inv = minetest.get_inventory{type = "detached", name = self.invname} - local stacks = minetest.get_node_drops(forward_upper_node.name) - for _, stack in ipairs(stacks) do - local leftover = inv:add_item("main", stack) - minetest.add_item(forward_pos, leftover) - end - end - if (not forward_upper_pos) or (forward_upper_pos and - not check_punch_flag(_aux.get_upper_pos(forward_upper_pos))) then - self.state = state.walk - self.object:set_animation(maidroid.animations.walk, 15, 0) - _aux.change_dir(self) - end - self.time_count = 0 - else - self.time_count = self.time_count + 1 - end + elseif self.state == state.punch then + if self.time_count >= max_punch_time then + local punch_flag, forward_upper_pos, forward_upper_node = check_punch_flag(self.destination) + if punch_flag then + minetest.remove_node(forward_upper_pos) + local inv = minetest.get_inventory{type = "detached", name = self.invname} + local stacks = minetest.get_node_drops(forward_upper_node.name) + for _, stack in ipairs(stacks) do + local leftover = inv:add_item("main", stack) + minetest.add_item(forward_pos, leftover) + end + end + if (not forward_upper_pos) or (forward_upper_pos and + not check_punch_flag(_aux.get_upper_pos(forward_upper_pos))) then + self.state = state.walk + self.object:set_animation(maidroid.animations.walk, 15, 0) + _aux.change_dir(self) + end + self.time_count = 0 + else + self.time_count = self.time_count + 1 + end - elseif self.state == state.plant then - if self.time_count > max_plant_time then - if forward_node.name == "air" - and minetest.get_item_group(forward_under_node.name, "soil") > 0 then - local inv = minetest.get_inventory{type = "detached", name = self.invname} - local stacks = inv:get_list("main") - for i, stack in ipairs(stacks) do - local itemname = stack:get_name() - if util.table_find_value(target_sapling_list, itemname) then - minetest.add_node(forward_pos, {name = itemname, param2 = 1}) - stack:take_item(1) - inv:set_stack("main", i, stack) - break - end - end - end - self.state = state.walk - self.object:set_animation(maidroid.animations.walk, 15, 0) - self.time_count = 0 - _aux.change_dir(self) - else - self.time_count = self.time_count + 1 - end + elseif self.state == state.plant then + if self.time_count > max_plant_time then + if forward_node.name == "air" + and minetest.get_item_group(forward_under_node.name, "soil") > 0 then + local inv = minetest.get_inventory{type = "detached", name = self.invname} + local stacks = inv:get_list("main") + for i, stack in ipairs(stacks) do + local itemname = stack:get_name() + if util.table_find_value(target_sapling_list, itemname) then + minetest.add_node(forward_pos, {name = itemname, param2 = 1}) + stack:take_item(1) + inv:set_stack("main", i, stack) + break + end + end + end + self.state = state.walk + self.object:set_animation(maidroid.animations.walk, 15, 0) + self.time_count = 0 + _aux.change_dir(self) + else + self.time_count = self.time_count + 1 + end - elseif self.state == state.walk_to_tree then - if vector.distance(pos, self.destination) < 1.5 then -- to punch state - local destnode = minetest.get_node(self.destination) - if (util.table_find_value(target_tree_list, destnode.name)) then - self.state = state.punch - self.object:set_animation(maidroid.animations.mine, 15, 0) - self.object:setvelocity{x = 0, y = 0, z = 0} - else - self.state = state.walk - self.object:set_animation(maidroid.animations.walk, 15, 0) - self.time_count = 0 - _aux.change_dir(self) - end - else - if pos.x == self.preposition.x or pos.z == self.preposition.z then - self.state = state.walk_avoid - self.object:set_animation(maidroid.animations.walk, 15, 0) - self.time_count = 0 - _aux.change_dir(self) - end - end + elseif self.state == state.walk_to_tree then + if vector.distance(pos, self.destination) < 1.5 then -- to punch state + local destnode = minetest.get_node(self.destination) + if (util.table_find_value(target_tree_list, destnode.name)) then + self.state = state.punch + self.object:set_animation(maidroid.animations.mine, 15, 0) + self.object:setvelocity{x = 0, y = 0, z = 0} + else + self.state = state.walk + self.object:set_animation(maidroid.animations.walk, 15, 0) + self.time_count = 0 + _aux.change_dir(self) + end + else + if pos.x == self.preposition.x or pos.z == self.preposition.z then + self.state = state.walk_avoid + self.object:set_animation(maidroid.animations.walk, 15, 0) + self.time_count = 0 + _aux.change_dir(self) + end + end - elseif self.state == state.walk_avoid then - if self.time_count > max_avoid_time then - self.state = state.walk - self.time_count = 0 - else - self.time_count = self.time_count + 1 - end - end - self.preposition = pos - end - }) + elseif self.state == state.walk_avoid then + if self.time_count > max_avoid_time then + self.state = state.walk + self.time_count = 0 + else + self.time_count = self.time_count + 1 + end + end + self.preposition = pos + end + }) diff --git a/recipe.lua b/recipe.lua index 3d3d58c..453edbc 100644 --- a/recipe.lua +++ b/recipe.lua @@ -5,65 +5,65 @@ -- maidroids minetest.register_craft{ - output = "maidroid:maidroid_spawn_egg", - recipe = { - {"default:diamond", "default:diamond", "default:diamond"}, - {"default:steel_ingot", "default:mese_crystal", "default:steel_ingot"}, - {"default:papyrus", "default:mese_crystal", "default:papyrus"}, - }, + output = "maidroid:maidroid_spawn_egg", + recipe = { + {"default:diamond", "default:diamond", "default:diamond"}, + {"default:steel_ingot", "default:mese_crystal", "default:steel_ingot"}, + {"default:papyrus", "default:mese_crystal", "default:papyrus"}, + }, } minetest.register_craft{ - output = "maidroid:maidroid_mk2_spawn_egg", - recipe = { - {"dye:blue", "dye:blue", "dye:blue"}, - {"dye:blue", "maidroid:maidroid_spawn_egg", "dye:blue"}, - {"dye:blue", "dye:blue", "dye:blue"}, - }, + output = "maidroid:maidroid_mk2_spawn_egg", + recipe = { + {"dye:blue", "dye:blue", "dye:blue"}, + {"dye:blue", "maidroid:maidroid_spawn_egg", "dye:blue"}, + {"dye:blue", "dye:blue", "dye:blue"}, + }, } minetest.register_craft{ - output = "maidroid:maidroid_mk3_spawn_egg", - recipe = { - {"dye:pink", "dye:pink", "dye:pink"}, - {"dye:pink", "maidroid:maidroid_spawn_egg", "dye:pink"}, - {"dye:pink", "dye:pink", "dye:pink"}, - }, + output = "maidroid:maidroid_mk3_spawn_egg", + recipe = { + {"dye:pink", "dye:pink", "dye:pink"}, + {"dye:pink", "maidroid:maidroid_spawn_egg", "dye:pink"}, + {"dye:pink", "dye:pink", "dye:pink"}, + }, } -- modules minetest.register_craft{ - output = "maidroid:empty_module", - recipe = { - {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, - {"default:steel_ingot", "default:obsidian", "default:steel_ingot"}, - {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, - }, + output = "maidroid:empty_module", + recipe = { + {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, + {"default:steel_ingot", "default:obsidian", "default:steel_ingot"}, + {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, + }, } minetest.register_craft{ - output = "maidroid:chasing_player_module", - recipe = { - {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, - {"default:steel_ingot", "maidroid:empty_module", "default:steel_ingot"}, - {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, - }, + output = "maidroid:chasing_player_module", + recipe = { + {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, + {"default:steel_ingot", "maidroid:empty_module", "default:steel_ingot"}, + {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, + }, } minetest.register_craft{ - output = "maidroid:farming_module", - recipe = { - {"default:gold_ingot", "default:gold_ingot", "default:gold_ingot"}, - {"default:gold_ingot", "maidroid:empty_module", "default:gold_ingot"}, - {"default:gold_ingot", "default:gold_ingot", "default:gold_ingot"}, - }, + output = "maidroid:farming_module", + recipe = { + {"default:gold_ingot", "default:gold_ingot", "default:gold_ingot"}, + {"default:gold_ingot", "maidroid:empty_module", "default:gold_ingot"}, + {"default:gold_ingot", "default:gold_ingot", "default:gold_ingot"}, + }, } minetest.register_craft{ - output = "maidroid:lumberjack_module", - recipe = { - {"default:diamond", "default:diamond", "default:diamond"}, - {"default:diamond", "maidroid:empty_module", "default:diamond"}, - {"default:diamond", "default:diamond", "default:diamond"}, - }, + output = "maidroid:lumberjack_module", + recipe = { + {"default:diamond", "default:diamond", "default:diamond"}, + {"default:diamond", "maidroid:empty_module", "default:diamond"}, + {"default:diamond", "default:diamond", "default:diamond"}, + }, } diff --git a/util.lua b/util.lua index 4e235ac..46ad170 100644 --- a/util.lua +++ b/util.lua @@ -7,17 +7,17 @@ maidroid.util = {} -- check that the table has the value function maidroid.util.table_find_value(tbl, value) - for k, v in ipairs(tbl) do - if v == value then return true, k end - end - return false, nil + for k, v in ipairs(tbl) do + if v == value then return true, k end + end + return false, nil end -- table shallow copy function maidroid.util.table_shallow_copy(source) - local copy = {} - for key, value in pairs(source) do - copy[key] = value - end - return copy + local copy = {} + for key, value in pairs(source) do + copy[key] = value + end + return copy end