Initial Commit
This commit is contained in:
parent
a944ab0ba1
commit
171ea82b84
33
README.md
Normal file
33
README.md
Normal file
@ -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.
|
71
body.lua
Normal file
71
body.lua
Normal file
@ -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
|
||||
})
|
139
hitloc.lua
Normal file
139
hitloc.lua
Normal file
@ -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)
|
15
init.lua
Normal file
15
init.lua
Normal file
@ -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")
|
BIN
models/bone.b3d
Normal file
BIN
models/bone.b3d
Normal file
Binary file not shown.
56
vitals.lua
Normal file
56
vitals.lua
Normal file
@ -0,0 +1,56 @@
|
||||
local timer = 0
|
||||
local default_vitals = {}
|
||||
default_vitals.temp = 98 --farhenhiet
|
||||
default_vitals.oxygen = 94 --percent
|
||||
default_vitals.respiratory = 12 --breaths per minute
|
||||
default_vitals.pulse = 70 --beats per minute
|
||||
default_vitals.volume = 5000 --milliliters
|
||||
default_vitals.systolic = 110 --mmHg
|
||||
default_vitals.diastolic = 70 --mmHg
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime;
|
||||
if timer >= 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)
|
Loading…
x
Reference in New Issue
Block a user