--Find handles and load sound function init() fan = FindBody("fan") pos = GetBodyTransform(fan).pos fanSound = LoadLoop("MOD/snd/fan.ogg") end --Return random number between mi and ma function rnd(mi, ma) return math.random()*(ma-mi)+mi end --Limit linear and angular velocity of a dynamic body function limitVelocity(body, lin, ang) local linVel = GetBodyVelocity(body) local linVelLength = VecLength(linVel) if linVelLength > lin then linVel = VecScale(linVel, lin/linVelLength) SetBodyVelocity(body, linVel) end local angVel = GetBodyAngularVelocity(body) local angVelLength = VecLength(angVel) if angVelLength > ang then angVel = VecScale(angVel, ang/angVelLength) SetBodyAngularVelocity(body, angVel) end end function tick(dt) --Spin fan, play sound and spawn particles --It's just a visual effect has no impact on behavior) SetBodyAngularVelocity(fan, Vec(0, 20, 0)) PlayLoop(fanSound, pos, 0.3) ParticleRadius(1,3) ParticleAlpha(0.3, 0) ParticleCollide(0) SpawnParticle(VecAdd(pos, Vec(rnd(-1,1), 1, rnd(-1,1))), Vec(rnd(-5,5), 10, rnd(-5,5)), 1.0) --Collect all dynamic shapes close to the fan (but exclude the fan itself) local strength = 200 local maxDist = 7.0 QueryRejectBody(fan) QueryRequire("dynamic") local aabbMin = VecAdd(pos, Vec(-maxDist, 0, -maxDist)) local aabbMax = VecAdd(pos, Vec(maxDist, maxDist, maxDist)) local shapes = QueryAabbShapes(aabbMin, aabbMax) --Apply force to dynamic shapes for i=1, #shapes do local s = shapes[i] local _, cp = GetShapeClosestPoint(s, pos) local d = VecSub(cp, pos) local l = VecLength(d) if l > 0 and l < maxDist then --Scale factor based on distance to fan local scale = 1.0-l/maxDist --Create an upwards impulse local imp = Vec(0, strength*scale, 0) --Apply impulse to the shape's body local b = GetShapeBody(s) ApplyBodyImpulse(b, cp, imp) --This is a bit of a hack (but quite useful method) to avoid dynamic --bodies to spin out of control limitVelocity(b, 10, 10) end end end