From 2ce9f1258df7874c005871281d20a7326971c5e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Walczak?= Date: Wed, 4 Feb 2026 20:52:00 +0100 Subject: [PATCH 1/2] Create .gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf From 81f8105c9742e23ed1a40f94bbe175db10391173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Walczak?= Date: Wed, 4 Feb 2026 21:01:46 +0100 Subject: [PATCH 2/2] Normalize line endings --- .gitattributes | 1 + .../LuaPhysics/PhysicsEngineGlobal.lua | 31 +++++++++++++- .../MaxYari/LuaPhysics/PhysicsEngineLocal.lua | 42 ++++++++++++++++++- .../LuaPhysics/PhysicsEnginePlayer.lua | 2 +- scripts/MaxYari/LuaPhysics/PhysicsObject.lua | 33 +++++++++++---- 5 files changed, 97 insertions(+), 12 deletions(-) diff --git a/.gitattributes b/.gitattributes index 6313b56..b5a9497 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ * text=auto eol=lf + \ No newline at end of file diff --git a/scripts/MaxYari/LuaPhysics/PhysicsEngineGlobal.lua b/scripts/MaxYari/LuaPhysics/PhysicsEngineGlobal.lua index 6b1c632..e3bfea3 100644 --- a/scripts/MaxYari/LuaPhysics/PhysicsEngineGlobal.lua +++ b/scripts/MaxYari/LuaPhysics/PhysicsEngineGlobal.lua @@ -250,6 +250,35 @@ return { [D.e.DetectCulpritResult] = function(...) if not crimeSystemActive then return end PhysAiSystem.onDetectCulpritResult(...) + end, + ["DropItem"] = function(data) + local newObject = data.object + print("Initializing physics for dropped item", newObject.recordId, "at position", newObject.position) + local mat = PhysMatSystem.getMaterialFromObject(newObject) + print("Material:", mat) + newObject:sendEvent(D.e.SetMaterial, { material = mat}) + newObject:sendEvent(D.e.SetPhysicsProperties, { + drag = 0.08, + bounce = 1.2, + isSleeping = false, + culprit = data.culprit, + mass = 1.2, + buoyancy = 0.3, + lockRotation = false, + angularDrag = 0.20, + resetOnLoad = false, + ignoreWorldCollisions = false, + collisionMode = "sphere", + realignWhenRested = false + }) + newObject:sendEvent(D.e.SetPositionUnadjusted, {position = newObject.position}) + local randomHorizontal = util.vector3( + (math.random() - 0.5) * 16, + (math.random() - 0.5) * 16, + -200 + ) + newObject:sendEvent(D.e.ApplyImpulse, {impulse = randomHorizontal, culprit = data.culprit}) + print("Applied impulse, physics initialized") end }, interfaceName = "LuaPhysics", @@ -260,4 +289,4 @@ return { getMaterialFromObject = PhysMatSystem.getMaterialFromObject, removeObject = removeObject }, -} +} \ No newline at end of file diff --git a/scripts/MaxYari/LuaPhysics/PhysicsEngineLocal.lua b/scripts/MaxYari/LuaPhysics/PhysicsEngineLocal.lua index 74482ad..34d3e5e 100644 --- a/scripts/MaxYari/LuaPhysics/PhysicsEngineLocal.lua +++ b/scripts/MaxYari/LuaPhysics/PhysicsEngineLocal.lua @@ -8,6 +8,7 @@ local vfs = require('openmw.vfs') local nearby = require('openmw.nearby') local omwself = require('openmw.self') +local types = require('openmw.types') local PhysicsObject = require(mp..'PhysicsObject') @@ -126,8 +127,48 @@ local function checkOutOfBounds() end +local function projectileCollisionFilter(hit) + if not hit or not hit.hitObject then return true end + + local obj = hit.hitObject + if obj.type == types.NPC or obj.type == types.Creature then + local health = types.Actor.stats.dynamic.health(obj).current + if health <= 0 then + -- Dead actor. Check height. + local relZ = hit.hitPos.z - obj.position.z + if relZ > 20 then + -- TOO HIGH. Ignore collision with the ghost upright capsule. + return false + end + end + end + return true +end + +physicsObject.collisionFilter = projectileCollisionFilter + + local function onCollision(hitResult) tryPlayCollisionSounds(hitResult) + + -- [[ CUSTOM LUA PROJECTILE PHYSICS HOOK ]] -- + if omwself.type == types.Weapon then + local record = types.Weapon.record(omwself) + if record and (record.type == types.Weapon.TYPE.Arrow or + record.type == types.Weapon.TYPE.Bolt or + record.type == types.Weapon.TYPE.MarksmanThrown) then + + core.sendGlobalEvent('LuaProjectilePhysics_ProjectileHit', { + projectile = omwself, + hitObject = hitResult.hitObject, + hitPos = hitResult.hitPos, + hitNormal = hitResult.hitNormal or hitResult.normal, + velocity = physicsObject.velocity + }) + end + end + -- [[ END CUSTOM HOOK ]] -- + if physicsObject.velocity:length() >= fenagledMinSpeed then core.sendGlobalEvent(D.e.ObjectFenagled, { object = omwself, @@ -271,4 +312,3 @@ return { - diff --git a/scripts/MaxYari/LuaPhysics/PhysicsEnginePlayer.lua b/scripts/MaxYari/LuaPhysics/PhysicsEnginePlayer.lua index ea96cab..e29814c 100644 --- a/scripts/MaxYari/LuaPhysics/PhysicsEnginePlayer.lua +++ b/scripts/MaxYari/LuaPhysics/PhysicsEnginePlayer.lua @@ -66,4 +66,4 @@ return { }, interfaceName = "LuaPhysics", interface = interface -} +} \ No newline at end of file diff --git a/scripts/MaxYari/LuaPhysics/PhysicsObject.lua b/scripts/MaxYari/LuaPhysics/PhysicsObject.lua index 6eb0b91..ea01f0e 100644 --- a/scripts/MaxYari/LuaPhysics/PhysicsObject.lua +++ b/scripts/MaxYari/LuaPhysics/PhysicsObject.lua @@ -405,18 +405,34 @@ function PhysicsObject:update(dt) if not self.ignoreWorldCollisions then if self.collisionMode == "sphere" then - hitResult = nearby.castRay(rayStart, rayEnd, { radius = self.radius }) + -- Use numeric bitmask 63 (AnyPhysical: World+Door+Actor+HeightMap+Projectile+Water) + hitResult = nearby.castRay(rayStart, rayEnd, { + radius = self.radius, + collisionType = 63 + }) elseif self.collisionMode == "aabb" then - hitResult = phUtils.customRaycastAABB(rayStart, rayEnd, self.radius, self.object:getBoundingBox(), self.rotation, { ignore = self.object }) + hitResult = phUtils.customRaycastAABB(rayStart, rayEnd, self.radius, self.object:getBoundingBox(), self.rotation, { ignore = self.object, collisionType = 63 }) end if hitResult and hitResult.hit then - collided = self:handleCollision(hitResult) - if self.collisionMode == "sphere" then - -- Update position on collision to get close to a surface without penetrating - if collided then self.position = phUtils.calcSpherePosAtHit(self.position, displacement, hitResult.hitPos, self.radius) end - elseif self.collisionMode == "aabb" then - -- Dont update position on collision, to ensure no penetration + local isHitValid = true + if self.collisionFilter then + isHitValid = self.collisionFilter(hitResult) + end + + if isHitValid then + collided = self:handleCollision(hitResult) + if self.collisionMode == "sphere" then + -- Update position on collision to get close to a surface without penetrating + if collided then self.position = phUtils.calcSpherePosAtHit(self.position, displacement, hitResult.hitPos, self.radius) end + elseif self.collisionMode == "aabb" then + -- Dont update position on collision, to ensure no penetration + end + else + -- Filter rejected this hit (e.g. ghost air above dead actor) + -- We act as if no hit occurred so displacement can continue. + hitResult = nil + collided = false end end end @@ -486,4 +502,3 @@ return PhysicsObject -