From 171ea82b8412f4bf77a24e13c30008f77f67e251 Mon Sep 17 00:00:00 2001 From: Elkien3 Date: Sat, 11 Apr 2020 11:38:01 -0500 Subject: [PATCH] Initial Commit --- README.md | 33 ++++++++++++ body.lua | 71 +++++++++++++++++++++++++ hitloc.lua | 139 ++++++++++++++++++++++++++++++++++++++++++++++++ init.lua | 15 ++++++ models/bone.b3d | Bin 0 -> 733 bytes vitals.lua | 56 +++++++++++++++++++ 6 files changed, 314 insertions(+) create mode 100644 README.md create mode 100644 body.lua create mode 100644 hitloc.lua create mode 100644 init.lua create mode 100644 models/bone.b3d create mode 100644 vitals.lua diff --git a/README.md b/README.md new file mode 100644 index 0000000..0b0b61d --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +Note: features are subject to change. This mod is WIP and may not be useable in it's current state. Intructions on use will come further in development. + +injury types: cuts, fractures, punctures, burns, bruises + +complications (injuries brought on from other injuries): obstructed airway, heart stopped, breathing stopped + +Injuries are treated linearly, but can have steps removed in they arent applicable. + +vitals: temperature, blood pressure/volume, oxygen, pulse, respiratory rate + +signs: color, injuries, level of alertness. + +symptoms: cold, dizzy, pain, immobility + +tools: blood pressure cuff, stethoscope, pulse oximeter, suction, AED, OPA and NPA, BVM, non-rebreather mask, oxygen tank, dressing, saline, c-collar, gloves, splint, triangle bandage, tourniquet, blanket, trauma shears, stopwatch/clock, blood bag, vital monitor, ventilator, oxygen concentrator + +perfusion rate is calculated based off breaths per minute, oxygen content of breaths taken, blood volume, and heart rate. also hunger and thirst, if applicable. +as the perfusion rate lowers, the patient will (in chronological order) get dizzy, pale, cold, confused, unconscious, stop breathing and pumping blood, and shortly after die. +perfusion will rapidly decrease if patient stops breathing due to drowning or suffocation. +perfusion will rapidly decrease if patient looses a lot of blood. +if patient has no nutrients (food) or no water, perfusion cannot take place. +patient can also can be dizzy, confused, or unconscious due to blunt force trauma, especially to the head. + +oxygen tanks will be able to be attached to bvms, non-rebreathers, and oxygen concentrator machines. +bags can be filled with saline (sterile water and salt) or blood from a donor. they can then be placed above the patient and given to the patient by gravity. +dressings can be used to clean, apply pressure, and bandage a wound. a tourniquet may be needed to stop major bleeding. +blankets can be placed on a patient to lower heat loss. +blood pressure cuff and stethoscope is used to attain a blood pressure. +areobic activity (running, jumping, swimming) will increase pulse and respiratory rate. but also has increased need for perfusion. +BVMing too quickly or with too much volume can cause the patient to vomit and cause an airway obstruction. + +need a very flexible api for injuries. would need to allow for special tools, vital sign changes, conditions that cause them, signs and symptoms they show, and be able to omit certain steps if needed. +would also need an api for vital sign management tools. \ No newline at end of file diff --git a/body.lua b/body.lua new file mode 100644 index 0000000..581a4bf --- /dev/null +++ b/body.lua @@ -0,0 +1,71 @@ +minetest.register_entity("medical:body", { + hp_max = 1, + physical = false, + weight = 5, + collisionbox = {-0.6, 0, -0.6, 0.6, .2, 0.6}, + visual = "mesh", + mesh = "character.b3d", + textures = {"character.png"}, + is_visible = true, + makes_footstep_sound = false, + automatic_rotate = false, + on_activate = function(self, staticdata, dtime_s) + self.object:set_animation({x=162,y=167}, 1) + self.object:set_armor_groups({immortal = 1}) + self.object:set_yaw(math.random(math.pi*-1, math.pi)) + end, + --[[get_staticdata = function(self) + --return minetest.serialize({owner = self.owner, sleeping = self.sleeping, expiretime = self.time, mesh = self.mesh, textures = self.textures, yaw = self.yaw, inv = serializeContents(self.inv)}) + end,--]] + on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir) + if not puncher:is_player() then return end + local wielditem = puncher:get_wielded_item() + local wieldname = wielditem:get_name() + local hitloc = medical.gethitloc(self, puncher, tool_capabilities, dir) + if medical.attachedtools[wieldname] then + medical.usedtools[wieldname](self, puncher, wielditem, hitloc) + end + -- attach things + end, + on_rightclick = function(self, clicker) + if not clicker:is_player() then return end + local wielditem = clicker:get_wielded_item() + local wieldname = wielditem:get_name() + local hitloc = medical.gethitloc(self, clicker, nil, nil) + if medical.attachedtools[wieldname] then + medical.usedtools[wieldname](self, clicker, wielditem, hitloc) + end + -- use things + end +}) + +--open fracture test +minetest.register_entity("medical:fracturetest", { + hp_max = 1, + physical = true, + weight = 5, + collisionbox = {-0.1,-0.1,-0.1, 0.1,0.1,0.1}, + visual = "mesh", + mesh = "bone.b3d", + visual_size = {x=1, y=1},--{x=.211, y=.211}, + textures = {"default_clay.png","default_clay.png","default_clay.png","default_clay.png","default_clay.png","default_clay.png"}, -- number of required textures depends on visual + colors = {}, -- number of required colors depends on visual + spritediv = {x=1, y=1}, + initial_sprite_basepos = {x=0, y=0}, + is_visible = true, + makes_footstep_sound = false, + automatic_rotate = false, + on_activate = function(self, staticdata, dtime_s) + minetest.after(1, function() + local all_objects = minetest.get_objects_inside_radius(self.object:get_pos(), 10) + local _,obj + for _,obj in ipairs(all_objects) do + if obj:get_entity_name() == "medical:body" then + minetest.chat_send_all(obj:get_entity_name()) + self.object:set_attach(obj, "Arm_Right", {x=0,y=4,z=0}, {x=1,y=0,z=math.random(-10, 10)}) + break + end + end + end) + end +}) \ No newline at end of file diff --git a/hitloc.lua b/hitloc.lua new file mode 100644 index 0000000..55ba629 --- /dev/null +++ b/hitloc.lua @@ -0,0 +1,139 @@ +local limb_location = {} + +--todo: make these change depending on what state the patient is in. +--[[ standing locations +limb_location.head = {x=0,y=1.6,z=0} +limb_location.torso = {x=0,y=1,z=0} +limb_location.rightarm = {x=.3,y=1,z=0} +limb_location.leftarm = {x=-.3,y=1,z=0} +limb_location.rightleg = {x=.1,y=.4,z=0} +limb_location.leftleg = {x=-.1,y=.4,z=0} +--]] +--[[ sitting locations +limb_location.head = {x=0,y=.9,z=0} +limb_location.torso = {x=0,y=.4,z=0} +limb_location.rightarm = {x=.3,y=.4,z=0} +limb_location.leftarm = {x=-.3,y=.4,z=0} +limb_location.rightleg = {x=.1,y=.1,z=.35} +limb_location.leftleg = {x=-.1,y=.1,z=.35} +--]] +-- laying locations +limb_location.head = {x=0,y=.1,z=-.65} +limb_location.torso = {x=0,y=.1,z=-.2} +limb_location.rightarm = {x=.4,y=.1,z=-.125} +limb_location.leftarm = {x=-.4,y=.1,z=-.125} +limb_location.rightleg = {x=.2,y=.1,z=.5} +limb_location.leftleg = {x=-.2,y=.1,z=.5} + +local DEBUG_WAYPOINT = true +local DEBUG_CHAT = true + +local function rotateVector(x, y, a) + local c = math.cos(a) + local s = math.sin(a) + return c*x - s*y, s*x + c*y +end + +function medical.gethitloc(player, hitter, tool_capabilities, dir) + if not player or not hitter then return end + local playerpos = player:get_pos() + local hitpos + local hitterpos = hitter:get_pos() + local adj_hitterpos = hitterpos + local isPlayer = hitter:is_player() + if isPlayer then + adj_hitterpos.y = adj_hitterpos.y + 1.45 -- eye offset + local offset, _ = hitter:get_eye_offset() + local hitteryaw = hitter:get_look_horizontal() + local x, z = rotateVector(offset.x, offset.z, hitteryaw) + offset = vector.multiply({x=x, y=offset.y, z=z}, .1) + adj_hitterpos = vector.add(adj_hitterpos, offset) + else + local properties = hitter:get_properties() + local offset = properties.eye_height or math.abs(properties.collisionbox[2] - properties.collisionbox[4]) + adj_hitterpos.y = adj_hitterpos.y + offset/2 + end + if tool_capabilities and dir and tool_capabilities.groupcaps.medical_dir ~= nil then + hitpos = vector.add(adj_hitterpos, vector.multiply(dir, vector.distance(playerpos, hitterpos))) + else + local pointdir = hitter:get_look_dir() or {} + if not pointdir or pointdir == nil or not isPlayer then + local yaw = hitter:getyaw() + local pitch = 0 + pointdir.x = -1*math.cos(yaw)*math.cos(pitch) + pointdir.z = -1*math.sin(yaw)*math.cos(pitch) + pointdir.y = math.sin(pitch) + end + hitpos = vector.add(adj_hitterpos, vector.multiply(pointdir, vector.distance(playerpos, hitterpos))) + end + if minetest.raycast then + local ray = minetest.raycast(adj_hitterpos, hitpos) -- it checks the players exact front before anything else because the default hit dir is weird, this may cause inaccuracies if a weapon with spread gives a look vector as a dir and the ray that goes stright ahead still hits the player + local pointed = ray:next() + if pointed and pointed.ref and pointed.ref == hitter then + pointed = ray:next() + end + if pointed and pointed.ref == player then + hitpos = pointed.intersection_point + end + end + if DEBUG_WAYPOINT then + local marker = hitter:hud_add({ + hud_elem_type = "waypoint", + name = "hit", + number = 0xFF0000, + world_pos = hitpos + }) + minetest.after(10, function() hitter:hud_remove(marker) end, hitter, marker) + end + return hitpos +end + +function medical.getlimb(player, hitter, tool_capabilities, dir, hitloc) + local hitpos + if hitloc then + hitpos = hitloc + else + hitpos = medical.gethitloc(player, hitter, tool_capabilities, dir) + if not hitpos then return end + end + local hitlimb + local hitdistance + local playeryaw + local playerpos = player:get_pos() + if player:is_player() then + playeryaw = player:get_look_horizontal() + else + playeryaw = player:get_yaw() + end + for id, pos in pairs(limb_location) do + local x, z = rotateVector(pos.x, pos.z, playeryaw) + local rot_pos = {x=x,y=pos.y,z=z} + local adj_pos = vector.add(playerpos, rot_pos) + local dist = vector.distance(adj_pos, hitpos) + if hitdistance == nil or dist < hitdistance then + hitdistance = dist + hitlimb = id + end + if DEBUG_WAYPOINT then + local mrker = hitter:hud_add({ + hud_elem_type = "waypoint", + name = id, + number = 0xFF0000, + world_pos = adj_pos + }) + minetest.after(5, function() hitter:hud_remove(mrker) end, hitter, mrker) + end + end + if DEBUG_CHAT then + minetest.chat_send_all(dump(hitlimb)) + end + return hitlimb +end + +minetest.register_on_player_hpchange(function(player, hp_change, reason) + if reason.type then minetest.chat_send_all(reason.type) end + return hp_change +end, true) +minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage) + medical.getlimb(player, hitter, tool_capabilities, dir) +end) \ No newline at end of file diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..498c60a --- /dev/null +++ b/init.lua @@ -0,0 +1,15 @@ +medical = {} +medical.mod_storage = minetest.get_mod_storage() +medical.usedtools = {} +medical.attachedtools = {} +medical.data = medical.mod_storage:to_table() or {} +if not medical.data.vitals then medical.data.vitals = {} end +if not medical.data.injuries then medical.data.injuries = {} end + +--mod_storage:from_table(medical) + +local modpath = minetest.get_modpath(minetest.get_current_modname()) + +dofile(modpath.."/vitals.lua") +dofile(modpath.."/hitloc.lua") +dofile(modpath.."/body.lua") \ No newline at end of file diff --git a/models/bone.b3d b/models/bone.b3d new file mode 100644 index 0000000000000000000000000000000000000000..cc5759635222276a05229426ac3823998a0a1eaa GIT binary patch literal 733 zcmZ>AGIqJj#K6GFz`)?=@8WtA$aXGGN(FL}L4!R512RU)`ML&sOaW^84+LRBA;E7z zCIJD60tz$y{=eVu#go+iX0tZ}%{X8OGUqQyOtaITNxc;)rT{h@B(~yDfqhNkET9^Y zm;+cH$GqnK8j5OAy|~5D^)fRY*oP{{U=I>QH4nEKHoa(WwqpgFhvqk|Vjz3b^ik3}|r#5`*YP_eV&OXYdrD22lKi z;u{+M49q|_hz|m6K+FonEI`Z-<%8H= 5 then + for _,player in ipairs(minetest.get_connected_players()) do + --player:set_bone_position("Head", {x=0,y=10,z=0}, {x=0,y=180,z=0}) + --local bonepos = player:get_bone_position("Head") + --[[local text = "" + for id, data in pairs (bonepos) do + text = text.." "..tostring(id)..":"..dump(data) + end + minetest.chat_send_all(text)--]] + local name = player:get_player_name() + if not medical.data.vitals[name] then medical.data.vitals[name] = default_vitals end + + if medical.data.injuries[name] then + --handle loss of vital signs due to injuries + end + + if hunger then + --handle hunger things + end + + if thirst then + --handle thirst things + end + + mv = medical.data.vitals[name] + local perfusion = ((mv.oxygen-60)/34) * ((mv.pulse-30)/40) * ((mv.volume-2000)/3000) * ((mv.temp-70)/28) + + if perfusion < .9 then --compensate by raising pulse and respiratory rate + + elseif perfusion < .7 then --subject gets cold and dizzy + + elseif perfusion < .5 then --subject is confused + + elseif perfusion < .3 then --subject is unconscious + + elseif perfusion < .1 then --subject stops breathing and pumping blood + + else --subject is ded + + end + end + timer = 0 + end +end) \ No newline at end of file