--Place a configurable smoke emitter in the world #include "ui/ui_helpers.lua" ui = nil uix = 0 uiy = 0 uiScale = 0 local CLICK_TO = "loc@CLICK_TO" local UI_SECONDS = "loc@UI_SECONDS" local INFINITE = "loc@INFINITE" local A_LITTLE = "loc@A_LITTLE" local A_LOT = "loc@A_LOT" local RED = "loc@RED" local GREEN = "loc@GREEN" local LOW = "loc@LOW" local HIGH = "loc@HIGH" local DOWNWARDS = "loc@DOWNWARDS" local UPWARDS = "loc@UPWARDS" local UI_TEXT_PRESS_P_2 = "loc@UI_TEXT_PRESS_P_2" local SMOKE_GUN_TIME_SECONDS_DURATION = "loc@SMOKE_GUN_TIME_SECONDS_DURATION" local MEDIUM = "loc@MEDIUM" local BROWN = "loc@BROWN" local NONE = "loc@NONE" function init() RegisterTool("smokegun", "SMOKEGUN", "MOD/vox/smokegun.vox") SetBool("game.tool.smokegun.enabled", true) snd = LoadLoop("MOD/snd/gas.ogg") emit = false uiScale = 0 end --Helper to return a random vector of particular length function rndVec(length) local v = VecNormalize(Vec(math.random(-100,100), math.random(-100,100), math.random(-100,100))) return VecScale(v, length) end --Helper to return a random number in range mi to ma function rnd(mi, ma) return math.random(1000)/1000*(ma-mi) + mi end --Main tick function handles tool logic function tick(dt) SetString("game.tool.smokegun.ammo.display", CLICK_TO) --Check if smokegun is selected if GetString("game.player.tool") == "smokegun" then if GetBool("game.player.canusetool") and InputPressed("usetool") then local ct = GetCameraTransform(); local pos = ct.pos local dir = TransformToParentVec(ct, Vec(0, 0, -1)) local hit, dist, normal, shape = QueryRaycast(pos, dir, 500) if hit then local hitPoint = VecAdd(pos, VecScale(dir, dist)) local b = GetShapeBody(shape) local t = GetBodyTransform(b) emitBody = b emitPos = TransformToLocalPoint(t, hitPoint) emitDir = TransformToLocalVec(t, normal) emitTimer = 0 emit = true end end end end --Update function handles smoke emission --It is important to put it in update and not tick for constant emission rate function update(dt) if emit then local radius = 0.4 local life = 5.0 local emitPeriod = 1 local count = 6 local vel = 8 local drag = 0.3 local gravity = 0.0 local red = 0.55 local green = 0.5 local blue = 0.45 local vol = 0.3 local alpha = 0.7 --Modify according to settings local propTime = GetString("savegame.mod.time") if propTime == "3 " .. UI_SECONDS then emitPeriod = 3 elseif propTime == INFINITE then emitPeriod = 1000000 end local propAmount = GetString("savegame.mod.amount") if propAmount == A_LITTLE then count = 4 vel = 5 radius = 0.3 vol = 0.1 alpha = 0.4 elseif propAmount == A_LOT then count = 8 vel = 10 radius = 0.5 vol = 0.5 alpha = 1.0 end local propColor = GetString("savegame.mod.color") if propColor == RED then red = 0.7 green = 0.5 blue = 0.5 elseif propColor == GREEN then red = 0.6 green = 0.7 blue = 0.6 end local propDrag = GetString("savegame.mod.drag") if propDrag == LOW then drag = 0.1 elseif propDrag == HIGH then drag = 1.0 end local propGravity = GetString("savegame.mod.gravity") if propGravity == DOWNWARDS then gravity = -5 elseif propGravity == UPWARDS then gravity = 3 end local bt = GetBodyTransform(emitBody) local pos = TransformToParentPoint(bt, emitPos) local dir = TransformToParentVec(bt, emitDir) PlayLoop(snd, pos, vol) --Set up the particle state ParticleReset() ParticleType("smoke") ParticleRadius(radius) ParticleAlpha(alpha, alpha, "constant", 0.1/life, 0.5) -- Ramp up fast, ramp down after 50% ParticleGravity(gravity * rnd(0.5, 1.5)) -- Slightly randomized gravity looks better ParticleDrag(drag) ParticleColor(red, green, blue, 0.9, 0.9, 0.9) -- Animating color towards white --Emit particles for i=1, count do --Randomize position slightly. This is important when spawning multiple particles at the same time local p = VecAdd(pos, VecAdd(VecScale(dir, radius), rndVec(radius))) --Randomize velocity slightly local v = VecScale(VecAdd(dir, rndVec(0.2)), vel) --Include some of the movement of the attachment body v = VecAdd(v, VecScale(GetBodyVelocityAtPos(emitBody, pos), 0.5)) --Randomize lifetime local l = rnd(life*0.5, life*1.5) --Spawn particle into the world SpawnParticle(p, v, l) end --Check if we should stop emitting emitTimer = emitTimer + dt if emitTimer > emitPeriod then emit = false end end end --Helper function for UI to configure properties function property(name, list, key) local current = GetString(key) if current == "" then current = list[1] end UiPush() UiFont("regular.ttf", 22) UiText(name) UiTranslate(180, 0) UiFont("bold.ttf", 22) if UiTextButton(current) then local new = nil for i=1, #list-1 do if list[i] == current then new = list[i+1] end end if new then SetString(key, new) else SetString(key, list[1]) end end UiPop() end --Configuration UI function draw() local shouldHide = GetBool("game.ui.hidemods") if shouldHide then return end if GetString("game.player.tool") == "smokegun" then UiFont("regular.ttf", 22) UiTextShadow(0, 0, 0, 0.5, 0.5) local buttonText = "P" if LastInputDevice() == UI_DEVICE_GAMEPAD then buttonText = "[[player::gamepad_button_r3;iconsize=42\\,42]]+[[player::gamepad_dpad_left;iconsize=42\\,42]]"; end local textPressP = GetTranslatedStringByKey("UI_TEXT_PRESS_P_2," .. buttonText); local rightOffset = UiGetTextSize(textPressP) + 30 local topOffset = 50 if not ui then uix = UiWidth()-rightOffset uiy = topOffset end UiTranslate(uix, uiy) if InputPressed("p") or InputPressed("extra1") then ui = not ui local value = 0.25 if ui then SetValue("uix", UiCenter()-100, "cosine", value) SetValue("uiy", UiMiddle()-100, "cosine", value) else SetValue("uix", UiWidth()-rightOffset, "cosine", value) SetValue("uiy", topOffset, "cosine", value) end end if ui then UiMakeInteractive() UiText("loc@UI_TEXT_PRESS_P," .. buttonText) UiTranslate(0, 30) property("loc@EMIT_TIME", {"loc@SMOKE_GUN_TIME_SECONDS_DURATION,1", "loc@SMOKE_GUN_TIME_SECONDS_DURATION,3", "loc@INFINITE"}, "savegame.mod.time") UiTranslate(0, 22) property("loc@AMOUNT", {"loc@MEDIUM", "loc@A_LOT", "loc@A_LITTLE"}, "savegame.mod.amount") UiTranslate(0, 22) property("loc@COLOR", {"loc@BROWN", "loc@RED", "loc@GREEN"}, "savegame.mod.color") UiTranslate(0, 22) property("loc@GRAVITY", {"loc@NONE", "loc@UPWARDS", "loc@DOWNWARDS"}, "savegame.mod.gravity") UiTranslate(0, 22) property("loc@DRAG", {"loc@MEDIUM", "loc@HIGH", "loc@LOW"}, "savegame.mod.drag") UiTranslate(0, 22) else UiText(textPressP) end end end