From d6d9b0a11248daeed004f118311fd613ef49ab50 Mon Sep 17 00:00:00 2001 From: benrob0329 Date: Sun, 30 May 2021 23:40:43 -0400 Subject: [PATCH] Pain Is Temporary --- .gitmodules | 3 + mods/ikea/api/defaults.lua | 2 +- mods/ikea_staff/corpse.lua | 115 --------- mods/ikea_staff/helpers.lua | 16 +- mods/ikea_staff/init.lua | 297 +++++++++++++----------- mods/ikea_staff/mod.conf | 2 +- mods/ikea_staff/spawning.lua | 43 ---- mods/mobkit | 1 + mods/player/init.lua | 2 +- mods/son_of_a_goap/init.lua | 90 ------- mods/son_of_a_goap/memorymanager.lua | 20 -- mods/son_of_a_goap/mod.conf | 1 - mods/son_of_a_goap/planner.lua | 82 ------- mods/son_of_a_luaentitysao/entity.lua | 73 ------ mods/son_of_a_luaentitysao/init.lua | 66 ------ mods/son_of_a_luaentitysao/mob.lua | 205 ---------------- mods/son_of_a_luaentitysao/mod.conf | 2 - mods/son_of_a_luaentitysao/pathfind.lua | 90 ------- 18 files changed, 165 insertions(+), 945 deletions(-) create mode 100644 .gitmodules delete mode 100644 mods/ikea_staff/corpse.lua delete mode 100644 mods/ikea_staff/spawning.lua create mode 160000 mods/mobkit delete mode 100644 mods/son_of_a_goap/init.lua delete mode 100644 mods/son_of_a_goap/memorymanager.lua delete mode 100644 mods/son_of_a_goap/mod.conf delete mode 100644 mods/son_of_a_goap/planner.lua delete mode 100644 mods/son_of_a_luaentitysao/entity.lua delete mode 100644 mods/son_of_a_luaentitysao/init.lua delete mode 100644 mods/son_of_a_luaentitysao/mob.lua delete mode 100644 mods/son_of_a_luaentitysao/mod.conf delete mode 100644 mods/son_of_a_luaentitysao/pathfind.lua diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..fa3d58d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "mods/mobkit"] + path = mods/mobkit + url = https://github.com/TheTermos/mobkit.git diff --git a/mods/ikea/api/defaults.lua b/mods/ikea/api/defaults.lua index 4555108..838423e 100644 --- a/mods/ikea/api/defaults.lua +++ b/mods/ikea/api/defaults.lua @@ -4,7 +4,7 @@ function ikea.default_furniture_def() paramtype2 = "facedir", drawtype = "mesh", wield_scale = {x = 2, y = 2, z = 2}, - groups = {carryable = 1, falling_node = 1, breakable = 10, pathable = 10, ikea_furniture = 1, size_x = 1, size_y = 1, size_z = 1}, + groups = {carryable = 1, falling_node = 1, breakable = 10, pathable = 0, ikea_furniture = 1, size_x = 1, size_y = 1, size_z = 1}, box_contents = {}, after_dig_node = util.leave_behind, } diff --git a/mods/ikea_staff/corpse.lua b/mods/ikea_staff/corpse.lua deleted file mode 100644 index 0d39384..0000000 --- a/mods/ikea_staff/corpse.lua +++ /dev/null @@ -1,115 +0,0 @@ --- Staff Corpse Node -- --- Populates with items from items.lua -- - -local function randomtext(length) - math.randomseed(math.random(os.clock() * os.time())) - local chars = "`~.,!%^*-:;'\" " - local str = string.rep("%s", length):gsub("%%s", function() - local idx = math.random(1, chars:len()) - return chars:sub(idx, idx) - end) - return str -end - -local items = { - function() -- Stickynote - local stack = ItemStack("staff:stickynote") - local meta = stack:get_meta() - meta:set_int("palette_index", math.random(0, 3) * 64) - stack:set_count(math.random(1, 30)) - return stack, 1 - end, - function() -- Used stickynote - local stack = ItemStack("staff:stickynote_used") - local meta = stack:get_meta() - local message = randomtext(math.random(4, 20)) - meta:set_string("description", "Sticky Note with Text:\n\"" .. message .. "\"") - meta:set_string("message", message) - meta:set_int("palette_index", math.random(0, 3) * 64) - return stack, 2 - end, - function() -- Pen - local stack = ItemStack("staff:pen") - stack:set_wear(math.random(1, 65535)) - return stack, 3 - end, - function() -- ID - return "staff:id", 25 - end, - function() -- Notepad - return "staff:notepad", 5 - end, - function() -- Used notepad - local stack = ItemStack("staff:notepad_used") - local meta = stack:get_meta() - local pages = {} - local pcount = math.random(1, 20) - for i = 1, pcount do - local content = "" - if math.random(1, pcount) == 1 then - content = minetest.formspec_escape(randomtext(math.random(10, 200 / 3))) - end - pages[i] = content - end - meta:set_int("current_page", math.random(1, #pages)) - meta:set_string("pages", minetest.serialize(pages)) - return stack, 10 - end, - function() -- Scanner - return "staff:scanner", 50 - end, -} - -minetest.register_node(":staff:corpse", { - description = "SCP-3008-2 (Deceased)", - drawtype = "mesh", - mesh = "ikea_staff_member_dead.obj", - tiles = {"ikea_staff_member.png"}, - selection_box = {type = "fixed", fixed = {-0.5, -0.5, 0, 0.5, 0, 3}}, - collision_box = {type = "fixed", fixed = {-0.5, -0.5, 0, 0.5, 0, 3}}, - walkable = false, - paramtype = "light", - paramtype2 = "facedir", - sunlight_propagates = true, - groups = {carryable = 1, falling_node = 1}, - on_construct = function(pos) - -- Inventory randomizer - local meta = minetest.get_meta(pos) - local inv = meta:get_inventory() - inv:set_size("main", 5) - - local contents = {} - - for i = 1, inv:get_size("main") do - math.randomseed(math.random(os.clock() * os.time())) - if math.random(1, 10) == 1 then -- An item will go in this slot - while not contents[i] do - local selection, chance = items[math.random(1, #items)]() - if math.random(1, chance) == 1 then - contents[i] = selection - end - end - else - contents[i] = "" - end - end - - meta:from_table({ - inventory = { - main = contents - }, - fields = { - formspec = [[ - size[8,7] - list[context;main;1.5,1;5,1] - list[current_player;main;0,3;8,4] - listring[context;main] - listring[current_player;main] - ]], - }, - }) - end, - allow_metadata_inventory_put = function() - return 0 - end, -}) diff --git a/mods/ikea_staff/helpers.lua b/mods/ikea_staff/helpers.lua index 3230329..94ea61d 100644 --- a/mods/ikea_staff/helpers.lua +++ b/mods/ikea_staff/helpers.lua @@ -7,8 +7,7 @@ function helpers.randompos(pos, radius) end function helpers.is_visible(pos, height, range) - height = height or 1 - height = math.max(1, height) + height = math.max(1, height or 0) for _, player in pairs(minetest.get_connected_players()) do local ppos = player:get_pos() -- Dont care about players way far away @@ -31,17 +30,4 @@ function helpers.is_visible(pos, height, range) return false end -function helpers.get_best_player_sighting(Memory, pos) - local best_score = math.huge - local best_memory - - for i, memory in Memory:memories() do - local score = (vector.distance(pos, memory.pos) / 10) + i -- Lower is Better - if score < best_score then - best_score, best_memory = score, memory - end - end - return best_memory or false -end - return helpers diff --git a/mods/ikea_staff/init.lua b/mods/ikea_staff/init.lua index daf40a4..8f2ca05 100644 --- a/mods/ikea_staff/init.lua +++ b/mods/ikea_staff/init.lua @@ -5,158 +5,175 @@ local PATH = minetest.get_modpath(minetest.get_current_modname()) .. "/" local helpers = dofile(PATH .. "helpers.lua") +local RANGE = 100 + --[[ Debug Entities ]]-- minetest.register_entity("ikea_staff:debug", { - visual = "sprite", - textures = {"blank.png^[invert:a^[colorize:red"}, - visual_size = {x = 0.3, y = 0.3}, - on_activate = function(self, data) - self.object:set_nametag_attributes({text = data}) - minetest.after(5, function() - if self.object then - self.object:remove() - end - end) - end, - on_punch = function(self) - self.object:remove() - end, - }) + visual = "sprite", + textures = {"blank.png^[invert:a^[colorize:red"}, + visual_size = {x = 0.3, y = 0.3}, + on_activate = function(self, data) + self.object:set_nametag_attributes({text = data}) + minetest.after(5, function() + if self.object then + self.object:remove() + end + end) + end, + on_punch = function(self) + self.object:remove() + end, +}) ---[[ Staff Entity ]]-- -local StaffMember = soal.Mob:new() - -StaffMember.initial_properties.mesh = "ikea_staff_member.b3d" -StaffMember.initial_properties.textures = {"ikea_staff_member.png"} -StaffMember.initial_properties.collisionbox = {-0.45, 0, -0.45, 0.45, 3.1, 0.45} -StaffMember.initial_properties.selectionbox = {-0.45, 0, -0.45, 0.45, 3.1, 0.45} - -StaffMember:addCustomProp("range", 40, false) -- How large an area staff members cover -StaffMember:addCustomProp("animations", { - idle = {range = {x = 0, y = 140}, speed = 15}, - moving = {range = {x = 141, y = 181}, speed = 45}, - carry = {range = {x = 204, y = 244}, speed = 45}, - dying = {range = {x = 182, y = 202}, speed = 10, loop = false}, - }, false) - --- Sight and Hive-Memory -- -local player_sightings = {} - -StaffMember:addCustomProp("worldstate", { - target = false, target_is_visible = false, target_is_close = false, - player_name = false, - wander = false, - }, false) - -StaffMember:addStep("sight", 1, function(self, dtime) +do --[[ Staff Entity ]]-- + -- Custom Functions -- + local function die(self) + local yaw = math.rad(90 * math.floor((math.deg(self.object:get_yaw()) / 90) + 0.5)) local pos = self.object:get_pos() - local range = self:getCustomProp("range") - local worldstate = self:getCustomProp("worldstate") - - worldstate.target_is_visible = false - local distance_to_target - local target_player - - -- Log Player Sightings -- - for _, player in ipairs(minetest.get_connected_players()) do - local player_pos = player and player:get_pos() - local player_name = player and player:get_player_name() - - if player_pos and self:canSeePlayer(pos, player) then - player_sightings[player_name] = player_pos - - if player_name == worldstate.player_name then - target_player = player - distance_to_target = vector.distance(pos, player_pos) - worldstate.target_is_visible = true + + mobkit.turn2yaw(self, yaw, 1) + mobkit.animate(self, "die") + self.object:set_pos({x = math.floor(pos.x + 0.5), y = pos.y, z = math.floor(pos.z + 0.5)}) + self.object:set_velocity({x = 0, y = 0, z = 0}) + + minetest.after((self.animation.die.range.y - self.animation.die.range.x) / self.animation.die.speed, function() + minetest.set_node(pos, {name = "staff:corpse", param2 = minetest.dir_to_facedir(minetest.yaw_to_dir(yaw))}) + self.object:remove() + end) + end + + -- Entity Registration -- + minetest.register_entity("ikea_staff:member", { + initial_properties = { + physical = true, + collide_with_objects = true, + visual = "mesh", + mesh = "ikea_staff_member.b3d", + textures = {"ikea_staff_member.png"}, + collisionbox = {-0.45, 0, -0.45, 0.45, 3.1, 0.45}, + selectionbox = {-0.45, 0, -0.45, 0.45, 3.1, 0.45}, + }, + + animation = { + stand = {range = {x = 0, y = 140}, speed = 15}, + walk = {range = {x = 141, y = 181}, speed = 80}, + attack = {range = {x = 204, y = 244}, speed = 45}, + die = {range = {x = 182, y = 202}, speed = 10, loop = false}, + }, + + -- Stats -- + armor_groups = {fleshy = 100}, + max_hp = 20, + timeout = 0, + buoyancy = 0, + max_speed = 4.5, + jump_height = 3, + view_range = RANGE, + attack = { + range = 2, + damage_groups = {fleshy = 5}, + }, + + -- Base Mobkit Functions -- + on_step = mobkit.stepfunc, + on_activate = mobkit.actfunc, + get_staticdata = mobkit.statfunc, + + -- Custom Logic -- + logic = function(self) + if mobkit.timer(self, 1) then + local priority = mobkit.get_queue_priority(self) + mobkit.vitals(self) + + if not mobkit.is_alive(self) then + mobkit.clear_queue_high(self) + mobkit.clear_queue_low(self) + die(self) + return + end + + local player = mobkit.get_nearby_player(self) + + if player then + mobkit.hq_hunt(self, 100, player) + end + + if mobkit.is_queue_empty_high(self) then + mobkit.hq_roam(self, 0) end end - end - - if worldstate.target == "player" then - if (not target_player) or distance_to_target > range then - worldstate.target = false - worldstate.player_name = false - worldstate.target_is_visible = false - end - end - - if not worldstate.target then - local closest_player_dist = math.huge - for k, v in pairs(player_sightings) do - local distance = vector.distance(pos, v) - if distance <= range and distance < closest_player_dist then - distance_to_target = distance - worldstate.target = "player" - worldstate.player_name = k - end - end - end - - worldstate.target_is_close = worldstate.target and distance_to_target < 5 - self:setCustomProp("worldstate", worldstate) - end) - --- GOAP -- -local Planner = soag.Planner:new() - -StaffMember:addCustomProp("wander_timer", 0, false) -Planner:addAction("wander", { - cost = 1, - requires = {}, - provides = {wander = true}, - func = function(self, dtime) - local timer = self:getCustomProp("wander_timer") - - if timer == 0 then - local pos = self.object:get_pos() - self:setState("move", { - target = helpers.randompos(pos, 10), - speed = 1, - range = 0, - }) - elseif timer >= 5 then - self:setCustomProp("wander_timer", 0) + end, + + on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) + if mobkit.is_alive(self) then + mobkit.hurt(self, tool_capabilities.damage_groups.fleshy or 0) return true end - - self:setCustomProp("wander_timer", timer + dtime) end, }) +end -Planner:addAction("find_player", { - cost = 1, - requires = {target = "player"}, - provides = {target_is_visible = true}, - func = function(self, dtime) - local worldstate = self:getCustomProp("worldstate") - if worldstate.target_is_visible then return true end - if not worldstate.target then return false end - - self:setState("move", {target = player_sightings[worldstate.player_name], speed = 2}) - end, - }) - -StaffMember:addCustomProp("goap_plan", false, false) - -StaffMember:addStep("ai", 1, function(self, dtime) - local worldstate = self:getCustomProp("worldstate") - local plan = self:getCustomProp("goap_plan") or Planner:getPlan(worldstate, {wander = true}) - - if plan and plan[1] then - local is_done = Planner.actions[plan[1]].func(self, dtime) - if is_done then - table.remove(plan, 1) - elseif is_done == false then - plan = false +do --[[ Corpse ]]-- + local function randtime() + return math.random(5, 120) + end + + -- Staff Corpse Node -- + minetest.register_node(":staff:corpse", { + description = "SCP-3008-2 (Deceased)", + drawtype = "mesh", + mesh = "ikea_staff_member_dead.obj", + tiles = {"ikea_staff_member.png"}, + selection_box = {type = "fixed", fixed = {-0.5, -0.5, 0, 0.5, 0, 3}}, + collision_box = {type = "fixed", fixed = {-0.5, -0.5, 0, 0.5, 0, 3}}, + walkable = false, + paramtype = "light", + paramtype2 = "facedir", + sunlight_propagates = true, + groups = {carryable = 1, falling_node = 1}, + + -- Corpse Mob Spawner -- + on_timer = function(pos, elapsed) + if not helpers.is_visible(pos, 3, RANGE) then + minetest.add_entity(helpers.randompos(pos, 5), "ikea_staff:member") end - end + minetest.get_node_timer(pos):start(randtime()) + return false + end, + on_construct = function(pos) + minetest.get_node_timer(pos):start(randtime()) + end, + }) +end - if plan and not plan[1] then - plan = false +do --[[ Rare "Thin Air" Spawning ]]-- + local TRIES = 20 -- How many times to try finding a valid spawn point before giving up + local timer = 0 + + minetest.register_globalstep(function(dt) + timer = timer + dt + if timer >= (5 * 60) then + for _, player in pairs(minetest.get_connected_players()) do + local pos = player:get_pos() + + local tries = 0 + while tries <= TRIES do + local spawnat = helpers.randompos(pos, RANGE) + if not minetest.registered_nodes[minetest.get_node(spawnat).name].walkable then + if helpers.is_visible(spawnat, 3, RANGE) then + tries = tries + 1 + else + spawnat.y = 2 + minetest.add_entity(spawnat, "ikea_staff:member") + break + end + else + tries = tries + 1 + end + end + end + + timer = 0 end - - self:setCustomProp("goap_plan", plan) end) - -minetest.register_entity("ikea_staff:member", StaffMember) +end diff --git a/mods/ikea_staff/mod.conf b/mods/ikea_staff/mod.conf index b01a4c0..a203526 100644 --- a/mods/ikea_staff/mod.conf +++ b/mods/ikea_staff/mod.conf @@ -1,2 +1,2 @@ name = ikea_staff -depends = ikea, son_of_a_goap, son_of_a_luaentitysao +depends = ikea, mobkit \ No newline at end of file diff --git a/mods/ikea_staff/spawning.lua b/mods/ikea_staff/spawning.lua deleted file mode 100644 index e826ff7..0000000 --- a/mods/ikea_staff/spawning.lua +++ /dev/null @@ -1,43 +0,0 @@ --- Staff Spawning -- - -local TRIES = 20 -- How many times to try finding a valid spawn point before giving up -local rate = 15 -local timer = 0 - -minetest.register_globalstep(function(dt) - timer = timer + dt - if timer >= rate then - for _, player in pairs(minetest.get_connected_players()) do - local pos = player:get_pos() - - local count = 0 - for _, entity in pairs(minetest.luaentities) do - if entity.name == "ikea_staff:member" then - if vector.distance(pos, entity.object:get_pos()) <= staff.RANGE then - count = count + 1 - end - end - end - - if count < 2 then -- math.floor(player:get_meta():get_float("age") / 7) - local tries = 0 - while tries <= TRIES do - local spawnat = staff.randompos(pos, staff.RANGE) - if not minetest.registered_nodes[minetest.get_node(spawnat).name].walkable then - if staff.is_visible(spawnat, 3) then - tries = tries + 1 - else - spawnat.y = 2 - minetest.add_entity(spawnat, "ikea_staff:member") - break - end - else - tries = tries + 1 - end - end - end - end - - timer = 0 - end -end) diff --git a/mods/mobkit b/mods/mobkit new file mode 160000 index 0000000..ddea141 --- /dev/null +++ b/mods/mobkit @@ -0,0 +1 @@ +Subproject commit ddea141b081e087900a6acc5a2a90e8d4e564295 diff --git a/mods/player/init.lua b/mods/player/init.lua index ea957c4..ca15ca2 100644 --- a/mods/player/init.lua +++ b/mods/player/init.lua @@ -18,6 +18,6 @@ minetest.register_item(":", { carryable = {times = {[1] = 1.00}, uses = 0}, }, - damage_groups = {whacking = 2}, + damage_groups = {fleshy = 2}, }, }) diff --git a/mods/son_of_a_goap/init.lua b/mods/son_of_a_goap/init.lua deleted file mode 100644 index 5b5e4b2..0000000 --- a/mods/son_of_a_goap/init.lua +++ /dev/null @@ -1,90 +0,0 @@ -local MODPATH = minetest.get_modpath(minetest.get_current_modname()) .. "/" - -soag = {} - -function soag.meets_requirements(a, b) - for k, v in pairs(a) do - if b[k] ~= v then - return false - end - end - return true -end - -soag.Planner = dofile(MODPATH .. "planner.lua") -soag.MemoryManager = dofile(MODPATH .. "memorymanager.lua") - --- [[ Tests ]] -- -do - local none = function() return true end - local Planner = soag.Planner:new() - Planner:addAction("look", { - cost = 1, - requires = {}, - provides = {sees_enemy = true}, - prerequisites = none, func = none - }) - - Planner:addAction("approach", { - cost = 1, - requires = {sees_enemy = true}, - provides = {near_enemy = true}, - prerequisites = none, func = none - }) - Planner:addAction("leap", { - cost = 5, - requires = {sees_enemy = true}, - provides = {near_enemy = true, on_top_of_enemy = true}, - prerequisites = none, func = none - }) - Planner:addAction("chase", { - cost = 10, - requires = {sees_enemy = true, enemy_moving = true}, - provides = {near_enemy = true}, - prerequisites = none, func = none - }) - - Planner:addAction("grapple", { - cost = 1, - requires = {sees_enemy = true, near_enemy = true}, - provides = {enemy_moving = false}, - prerequisites = none, func = none - }) - - Planner:addAction("punch", { - cost = 1, - requires = {sees_enemy = true, near_enemy = true, on_top_of_enemy = false}, - provides = {enemy_dead = true}, - prerequisites = none, func = none - }) - Planner:addAction("pound", { - cost = 1, - requires = {sees_enemy = true, near_enemy = true, on_top_of_enemy = true}, - provides = {enemy_dead = true}, - prerequisites = none, func = none - }) - Planner:addAction("headbutt", { - cost = 5, - requires = {enemy_moving = false, near_enemy = true, near_enemy = true}, - provides = {enemy_dead = true}, - prerequisites = none, func = none - }) - Planner:addAction("explode", { - cost = 10, - requires = {near_enemy = true}, - provides = {enemy_dead = true, self_dead = true}, - prerequisites = none, func = none - }) - - do - local plan = Planner:getPlan({self_dead = false, enemy_dead = false, on_top_of_enemy = false}, {enemy_dead = true, self_dead = false, on_top_of_enemy = false}) - assert(plan, "Getting Plan Failed! " .. dump(plan)) - - local test_string = "" - while #plan ~= 0 do - test_string = test_string .. " " .. table.remove(plan, 1) - end - - assert(test_string == " look approach punch", "Plan Is Wrong\n" .. test_string) - end -end diff --git a/mods/son_of_a_goap/memorymanager.lua b/mods/son_of_a_goap/memorymanager.lua deleted file mode 100644 index dcafc2e..0000000 --- a/mods/son_of_a_goap/memorymanager.lua +++ /dev/null @@ -1,20 +0,0 @@ -local MemoryManager = {} -function MemoryManager:new(o) - o = setmetatable(o or {}, {__index = self}) - return o -end - -function MemoryManager:addMemory(memory) - table.insert(self, 1, memory) - - if self[101] then - table.remove(self) - end -end - -function MemoryManager:memories() - return ipairs(self) -end - -return MemoryManager - diff --git a/mods/son_of_a_goap/mod.conf b/mods/son_of_a_goap/mod.conf deleted file mode 100644 index 479d301..0000000 --- a/mods/son_of_a_goap/mod.conf +++ /dev/null @@ -1 +0,0 @@ -depends = util diff --git a/mods/son_of_a_goap/planner.lua b/mods/son_of_a_goap/planner.lua deleted file mode 100644 index ae0e8f6..0000000 --- a/mods/son_of_a_goap/planner.lua +++ /dev/null @@ -1,82 +0,0 @@ -local Planner = {} -function Planner:new(o) - o = setmetatable(o or {}, self) - self.__index = self - o.actions = {} - return o -end - -function Planner:addAction(name, def) - assert( - type(name) == "string" - and type(def.cost) == "number" and def.cost > 0 - and type(def.requires) == "table" - and type(def.provides) == "table" - and (def.prerequisites == nil or type(def.prerequisites) == "function") - and type(def.func) == "function", - - "Incorrect Call To addAction()\n") - self.actions[name] = def -end - -function Planner:getActionsCopy() - local actions = {} - for k, v in pairs(self.actions) do - actions[k] = v - end - return actions -end - -local function reassemble_worldstate(path, nodes) - local worldstate = {} - for _, v in ipairs(path) do - for name, bool in pairs(nodes[v].provides) do - worldstate[name] = bool - end - end - return worldstate -end - -function Planner:getPlan(origin, target) - assert(origin and target, "Incorrect Call To getPlan") - - local start_token, end_token = {}, {} - local nodes = self:getActionsCopy() - nodes[start_token] = {cost = 0, provides = origin, requires = {}} - nodes[end_token] = {cost = 0, provides = {}, requires = target} - - local function get_neighbors(node, path) - local neighbors, worldstate = {}, reassemble_worldstate(path, nodes) - for k, v in pairs(nodes) do - if soag.meets_requirements(v.requires, worldstate) then - table.insert(neighbors, k) - end - end - return neighbors - end - - local function is_good_node(n) - return nodes[n].prerequisites and nodes[n].prerequisites() or true - end - - local function get_cost(a, b) - return nodes[b].cost or 1 - end - - local function get_heuristic(a, b, path) - local heuristic, worldstate = 0, reassemble_worldstate(path, nodes) - for k, v in pairs(target) do - heuristic = heuristic + (v ~= worldstate[k] and 1 or 0) - end - return heuristic - end - - local path = util.astar(start_token, end_token, get_neighbors, is_good_node, get_cost, get_heuristic, true) - if path then - table.remove(path, 1) - table.remove(path) - end - return path -end - -return Planner diff --git a/mods/son_of_a_luaentitysao/entity.lua b/mods/son_of_a_luaentitysao/entity.lua deleted file mode 100644 index 04ac851..0000000 --- a/mods/son_of_a_luaentitysao/entity.lua +++ /dev/null @@ -1,73 +0,0 @@ ---[[ Entity Class ]]-- -local Entity = {_initial_custom_props = {}, _steps = {}, initial_properties = {}} - -function Entity:new(o) - o = setmetatable(o or {}, {__index = self}) - o.initial_properties = table.copy(self.initial_properties) - o._initial_custom_props = table.copy(self._initial_custom_props) - o._custom_props = setmetatable({}, {__index = o._initial_custom_props}) - o._steps = table.copy(self._steps) - return o -end - -function Entity:addCustomProp(name, initial_value, persist) - assert(name and (initial_value ~= nil), "Incorrect call to Entity:addCustomProp") - self._initial_custom_props[name] = {value = initial_value, persist = persist} -end - -function Entity:setCustomProp(name, value) - assert(self._custom_props, "Something has gone very wrong!") - assert(self._custom_props[name] ~= nil, "Custom Property Must Be Defined!") - self._custom_props[name] = {value = value} -end - -function Entity:getCustomProp(name) - assert(self._custom_props, "Something has gone very wrong!") - assert(self._custom_props[name] ~= nil, "Custom Property Must Be Defined!") - return self._custom_props[name].value -end - -function Entity:addStep(name, interval, func) - assert(type(name) == "string" and type(interval) == "number" and type(func) == "function", "Incorrect call to Entity.addStep") - table.insert(self._steps, {name = name, interval = interval, timer = 0, func = func}) -end - -function Entity:doSteps(dtime, moveresult) - for i=1, #self._steps do - self._steps[i].timer = self._steps[i].timer + dtime - if self._steps[i].timer >= self._steps[i].interval then - self._steps[i].func(self, self._steps[i].timer, moveresult) - self._steps[i].timer = 0 - end - end -end - -function Entity:getPersistingProps() - local props = {} - for name,value in pairs(self._custom_props) do - if self._initial_custom_props[name].persist then - props[name] = value - end - end - return props -end - -function Entity:on_step(dtime, moveresult) - self:doSteps(dtime, moveresult) -end - -function Entity:get_staticdata() - local props = self.object:get_properties() - return minetest.serialize({props, self:getPersistingProps()}) -end - -function Entity:on_activate(staticdata) - local props, custom_props = unpack(minetest.deserialize(staticdata) or {{}, {}}) - self.object:set_properties(props) - - for name, prop in pairs(custom_props) do - self:setCustomProp(name, prop) - end -end - -return Entity diff --git a/mods/son_of_a_luaentitysao/init.lua b/mods/son_of_a_luaentitysao/init.lua deleted file mode 100644 index 155b391..0000000 --- a/mods/son_of_a_luaentitysao/init.lua +++ /dev/null @@ -1,66 +0,0 @@ -local PATH = minetest.get_modpath(minetest.get_current_modname()) .. "/" - -soal = {} -soal.Entity = dofile(PATH .. "entity.lua") - -do --[[ Entity Tests ]]-- - local TestEntity = soal.Entity:new() - - do -- Check Initial Props -- - local error_message = "Initial Props Not Right!\n" - TestEntity:addCustomProp("a", 123, false) - TestEntity:addCustomProp("b", 456, false) - TestEntity:addCustomProp("c", 789, true) - local expected = { - a = {value = 123, persist = false}, - b = {value = 456, persist = false}, - c = {value = 789, persist = true}, - } - local result = table.equals(TestEntity._initial_custom_props, expected) - assert(result, error_message..dump(TestEntity._initial_custom_props)) - end - - do -- Check Setting Props -- - local error_message = "Custom Props Not Right!\n" - TestEntity:setCustomProp("a", 987) - TestEntity:setCustomProp("b", 654) - local expected = { - a = {value = 987}, - b = {value = 654}, - c = {value = 789, persist = true}, - } - local result = table.equals(TestEntity._custom_props, expected) - assert(result, error_message..dump(TestEntity._custom_props)) - end - - do -- Check Getting Custom Props -- - local error_message = "Custom Props Not Getting Right!\n" - assert(TestEntity:getCustomProp("a") == 987, error_message.."a, 987") - assert(TestEntity:getCustomProp("b") == 654, error_message.."b, 654") - assert(TestEntity:getCustomProp("c") == 789, error_message.."c, 789") - end - - do -- Check addStep -- - local error_message = "addStep Not Right!\n" - local name, interval, func = "test", 123, function() - return "foo" - end - - TestEntity:addStep(name, interval, func) - assert(TestEntity._steps[1], error_message.."Not Defined") - assert(TestEntity._steps[1].name == name, error_message.."Name Incorrect") - assert(TestEntity._steps[1].interval == interval, error_message.."Interval Wrong!") - assert(TestEntity._steps[1].func() == "foo", error_message.."Func Wrong!") - end - - do -- Check Persisting Props -- - local error_message = "Persisting Props Not Right!\n" - TestEntity:setCustomProp("c", "test") - local expected = {c = {value = "test"}} - local result = table.equals(TestEntity:getPersistingProps(), expected) - assert(result, error_message..dump(TestEntity:getPersistingProps())) - end -end - -soal.Mob = dofile(PATH .. "mob.lua") - diff --git a/mods/son_of_a_luaentitysao/mob.lua b/mods/son_of_a_luaentitysao/mob.lua deleted file mode 100644 index f146f71..0000000 --- a/mods/son_of_a_luaentitysao/mob.lua +++ /dev/null @@ -1,205 +0,0 @@ -local PATH = minetest.get_modpath(minetest.get_current_modname()) .. "/" -local pathfind = dofile(PATH .. "pathfind.lua") - ---[[ Debug Entities ]]-- -minetest.register_entity("son_of_a_luaentitysao:debug", { - visual = "sprite", - textures = {"blank.png^[invert:a^[colorize:red"}, - visual_size = {x = 0.3, y = 0.3}, - on_activate = function(self, data) - self.object:set_nametag_attributes({text = data}) - minetest.after(1, function() - if self.object then - self.object:remove() - end - end) - end, - on_punch = function(self) - self.object:remove() - end, -}) - ---[[ Mob Class ]]-- -local Mob = soal.Entity:new() - -Mob.initial_properties.visual = "mesh" -Mob.initial_properties.mesh = "error.obj" -Mob.initial_properties.textures = {"unknown_node.png^[colorize:#ff0000:255"} -Mob.initial_properties.collide_with_objects = true -Mob.initial_properties.makes_footstep_sound = true -Mob.initial_properties.physical = true -Mob.initial_properties.max_hp = 100 - -Mob:addCustomProp("gravity", -9.8, true) - --- Velocity Handeling -- -Mob:addCustomProp("velocity", {x=0,y=0,z=0}, false) - -function Mob:setVelocity(velocity) - self.object:set_velocity(velocity) - self:setCustomProp("velocity", velocity) -end - --- Animation Handeling -- -Mob:addCustomProp("animation", "idle", false) -Mob:addCustomProp("animations", { - idle = {range = {x = 0, y = 100}, speed = 15}, - moving = {range = {x = 0, y = 100}, speed = 15, loop = true}, - }, false) - -function Mob:setAnimation(state, speed) - if self:getCustomProp("animation") ~= state then - local anim = self:getCustomProp("animations")[state] or {} - assert(anim.range, "Missing animation range for '" .. state .. "'.") - self.object:set_animation(anim.range, (anim.speed or 30) * (speed or 1), 0, anim.loop) - self:setCustomProp("animation", state) - end -end - --- Interpolated Turning -- -Mob:addCustomProp("turn_speed", 180, true) -Mob:addCustomProp("turn_target", false, false) - -function Mob:lookAt(pos, target, speed) - local lookat - if type(target) == "table" then - lookat = math.deg(minetest.dir_to_yaw(vector.direction(pos, target))) - -- Manual calculation: math.deg(math.atan2(target.z - pos.z, target.x - pos.x)) - 90 - else - lookat = math.deg(target) - end - - self:setCustomProp("turn_target", lookat) -end - -Mob:addStep("turn_interpolate", 0, function(self, dtime) - local look = self:getCustomProp("turn_target") - local turnspeed = self:getCustomProp("turn_speed") - - if look then - local yaw = math.deg(self.object:get_yaw()) - if yaw ~= look then - local diff = look - yaw - - while diff < -180 do diff = diff + 360 end - while diff > 180 do diff = diff - 360 end - - if math.abs(diff) < turnspeed * dtime then - self.object:set_yaw(math.rad(look)) - self:setCustomProp("turn_target", false) - end - - self.object:set_yaw(math.rad(yaw + math.abs(diff) / diff * turnspeed * dtime)) - end - end -end) - --- Misc -- -function Mob:hasDirectPath(pos, target) - if Raycast(vector.add(pos, {x = 0, y = 0.5, z = 0}), vector.add(target, {x = 0, y = 0.5, z = 0}), false):next() then - return false - end - return true -end - -function Mob:canSeePlayer(pos, player) - local props = self.object:get_properties() - local eye = vector.offset(pos, 0, props.collisionbox[5] - 0.3, 0) - - for i = 0, 2 do -- Check at feet, body, and head positions - for pointed in Raycast(eye, vector.offset(player:get_pos(), 0, i, 0)) do - if pointed.type == "object" and pointed.ref == player then - return true - elseif pointed.type == "object" and pointed.ref ~= self.object then - break - elseif pointed.type == "node" then - break - end - end - end - return false -end - -local parent_on_activate = Mob.on_activate -function Mob:on_activate(...) - parent_on_activate(self, ...) - self.object:set_acceleration(vector.new(0, self:getCustomProp("gravity"), 0)) -end - --- FSM -- -Mob:addCustomProp("fsm_current_state", false, false) -Mob:addCustomProp("fsm_context", {}, false) -Mob:addCustomProp("fsm_states", { - move = function(self, context, dtime) - if not context.target then - self:setAnimation("idle") - return context - end - - local pos = self.object:get_pos() - if vector.distance(pos, context.target) < 0.5 then - if context.path and #context.path > 0 then - context.target = table.remove(context.path, 1) - else - self:setAnimation("idle") - self:setVelocity(vector.new(0, 0, 0)) - context.target = nil - return context - end - end - - if not self:hasDirectPath(pos, context.target) then - local path = pathfind(pos, context.target, context.range or 20) - if path then - if context.path then - for i, v in ipairs(path) do - table.insert(context.path, i, v) - end - else - context.path = path - end - else - return context - end - end - - self:setAnimation("moving") - self:lookAt(pos, context.target, context.speed * 10) - local velocity = vector.multiply(vector.direction(pos, context.target), context.speed) - velocity.y = 0 - self:setVelocity(velocity) - return context - end, - - emote = function(self, context, dtime) - if context.delta then - context.delta = context.delta + dtime - else - context.delta = 0 - end - - if context.sequence and context.sequence[1] and context.sequence[1].time <= context.delta then - local keyframe = table.remove(context.sequence, 1) - if keyframe.sound then minetest.sound_play(unpack(keyframe.sound)) end - if keyframe.animation then self:setAnimation(keyframe.animation) end - if keyframe.callback then keyframe.callback(self, context.delta) end - end - - return context - end, -}) - -function Mob:setState(state, context) - self:setCustomProp("fsm_current_state", state) - self:setCustomProp("fsm_context", context) -end - -Mob:addStep("fsm_state", 0, function(self, dtime) - local current_state = self:getCustomProp("fsm_current_state") - if current_state then - local context = self:getCustomProp("fsm_context") - context = self:getCustomProp("fsm_states")[current_state](self, context, dtime) - self:setCustomProp("fsm_context", context) - end - end) -return Mob diff --git a/mods/son_of_a_luaentitysao/mod.conf b/mods/son_of_a_luaentitysao/mod.conf deleted file mode 100644 index 4d3eaab..0000000 --- a/mods/son_of_a_luaentitysao/mod.conf +++ /dev/null @@ -1,2 +0,0 @@ -name = son_of_a_luaentitysao -depends = util diff --git a/mods/son_of_a_luaentitysao/pathfind.lua b/mods/son_of_a_luaentitysao/pathfind.lua deleted file mode 100644 index fdb5d87..0000000 --- a/mods/son_of_a_luaentitysao/pathfind.lua +++ /dev/null @@ -1,90 +0,0 @@ -local walkable, cost = {}, {} -minetest.register_on_mods_loaded(function() - for k, v in pairs(minetest.registered_nodes) do - if v.walkable then - walkable[minetest.get_content_id(k)] = true - end - if v.groups.pathable and v.groups.pathable > 0 then - cost[minetest.get_content_id(k)] = v.groups.pathable - end - end -end) - -local function pathfind(origin, target, radius) - origin, target = vector.round(origin), vector.round(target) - target.y = 0 -- TODO: Jumping/Climbing - - local padding = vector.new(radius, 1, radius) - local padded_min, padded_max = vector.sort(origin, target) - padded_min, padded_max = vector.subtract(padded_min, padding), vector.add(padded_max, padding) - - local VM = minetest.get_voxel_manip() - local minp, maxp = VM:read_from_map(padded_min, padded_max) - local VA, node_data = VoxelArea:new{MinEdge = minp, MaxEdge = maxp}, VM:get_data() - - local x_width, y_width, z_width = maxp.x - minp.x, maxp.y - minp.y, maxp.z - minp.z - - local function get_neighbors(i) - -- Bounds check Y - 1 - if math.floor(((i - 1 - VA.ystride) % VA.zstride) / VA.ystride) < 0 then - return {} - end - - local north = math.floor((i - 1 + VA.zstride) / VA.zstride) <= z_width - local south = math.floor((i - 1 - VA.zstride) / VA.zstride) >= 0 - local east = math.floor((i - 1 + 1) % VA.zstride % VA.ystride) <= x_width - local west = math.floor((i - 1 - 1) % VA.zstride % VA.ystride) >= 0 - - local neighbors = {} - if north then - neighbors[#neighbors + 1] = i + VA.zstride - if east then neighbors[#neighbors + 1] = i + VA.zstride + 1 end - if west then neighbors[#neighbors + 1] = i + VA.zstride - 1 end - end - if south then - neighbors[#neighbors + 1] = i - VA.zstride - if east then neighbors[#neighbors + 1] = i - VA.zstride + 1 end - if west then neighbors[#neighbors + 1] = i - VA.zstride - 1 end - end - if east then neighbors[#neighbors + 1] = i + 1 end - if west then neighbors[#neighbors + 1] = i - 1 end - - return neighbors - end - - local function is_good_node(i) - return (not walkable[node_data[i]]) and walkable[node_data[i - VA.ystride]] - end - - local function get_cost(a, b) - return cost[node_data[b]] or 1 - end - - local function get_heuristic(a, b) - local a_pos, b_pos = VA:position(a), VA:position(b) - return vector.distance(a_pos, b_pos) - end - - local raw_path = util.astar(VA:indexp(origin), VA:indexp(target), get_neighbors, is_good_node, get_cost, get_heuristic) - if not raw_path then return false end - - table.remove(raw_path, 1) -- No need to walk to where we already are - local path = {} - for _, node_index in ipairs(raw_path) do - table.insert(path, VA:position(node_index)) - end - - local trimmed_path, old_direction, old_pos = {}, false, table.remove(path, 1) - for _, pos in ipairs(path) do - local direction = vector.direction(pos, old_pos) - if not old_direction or not vector.equals(direction, old_direction) then - table.insert(trimmed_path, old_pos) - old_pos, old_direction = pos, direction - end - end - table.insert(trimmed_path, table.remove(path)) - - return trimmed_path -end - -return pathfind