I didn’t write yesterday. Yesterday I spent most of the day writing this flare launcher GUI for Stormworks via https://lua.flaffipony.rocks/
-- IO definitions flareCount = property.getNumber("flareCount") cfIOIndex = 2 -- the Microcontroller output index which is meant to be wired to a CrispyFlare IO Microcontroller -- vars c1 = {255,255,255} -- white c2 = {217, 90, 0} -- Persian orange isFiring = false flaresFired = 0 qFlareCount = 0 qFlaresFired = 0 qFlareDelay = 0 qStartTime = 0 tickCounter = 0 millis = 0 actDuration = 250 -- xCoord, yCoord, height, width, label, isActive, color, activeColor, clickTime buttons = { {["x"]=0, ["y"]=7, ["h"]=7, ["w"]=8, ["label"]="1", ["act"]=false, ["c"]={207,19,0}, ["ac"]={120,120,120}, ["ct"]=0 }, {["x"]=8, ["y"]=7, ["h"]=7, ["w"]=8, ["label"]="2", ["act"]=false, ["c"]={207,19,0}, ["ac"]={120,120,120}, ["ct"]=0 }, {["x"]=16, ["y"]=7, ["h"]=7, ["w"]=8, ["label"]="3", ["act"]=false, ["c"]={207,19,0}, ["ac"]={120,120,120}, ["ct"]=0 }, {["x"]=24, ["y"]=7, ["h"]=7, ["w"]=8, ["label"]="R", ["act"]=false, ["c"]={207,19,0}, ["ac"]={70,0,120}, ["ct"]=0 }, {["x"]=0, ["y"]=13, ["h"]=7, ["w"]=8, ["label"]="4", ["act"]=false, ["c"]={207,19,0}, ["ac"]={120,120,120}, ["ct"]=0 }, {["x"]=8, ["y"]=13, ["h"]=7, ["w"]=8, ["label"]="5", ["act"]=false, ["c"]={207,19,0}, ["ac"]={120,120,120}, ["ct"]=0 }, {["x"]=16, ["y"]=13, ["h"]=7, ["w"]=8, ["label"]="6", ["act"]=false, ["c"]={207,19,0}, ["ac"]={120,120,120}, ["ct"]=0 }, {["x"]=24, ["y"]=13, ["h"]=7, ["w"]=8, ["label"]="D", ["act"]=false, ["c"]={207,19,0}, ["ac"]={20,30,255}, ["ct"]=0 }, {["x"]=0, ["y"]=19, ["h"]=7, ["w"]=8, ["label"]="7", ["act"]=false, ["c"]={207,19,0}, ["ac"]={120,120,120}, ["ct"]=0 }, {["x"]=8, ["y"]=19, ["h"]=7, ["w"]=8, ["label"]="8", ["act"]=false, ["c"]={207,19,0}, ["ac"]={120,120,120}, ["ct"]=0 }, {["x"]=16, ["y"]=19, ["h"]=7, ["w"]=8, ["label"]="9", ["act"]=false, ["c"]={207,19,0}, ["ac"]={120,120,120}, ["ct"]=0 }, {["x"]=24, ["y"]=19, ["h"]=7, ["w"]=8, ["label"]="", ["act"]=false, ["c"]={120,80,0}, ["ac"]={120,120,120}, ["ct"]=0 }, {["x"]=0, ["y"]=25, ["h"]=7, ["w"]=8, ["label"]="*", ["act"]=false, ["c"]={0,120,0}, ["ac"]={120,120,120}, ["ct"]=0 }, {["x"]=8, ["y"]=25, ["h"]=7, ["w"]=8, ["label"]="0", ["act"]=false, ["c"]={207,19,0}, ["ac"]={120,120,120}, ["ct"]=0 }, {["x"]=16, ["y"]=25, ["h"]=7, ["w"]=8, ["label"]="#", ["act"]=false, ["c"]={207,19,0}, ["ac"]={20,30,255}, ["ct"]=0 }, {["x"]=24, ["y"]=25, ["h"]=7, ["w"]=8, ["label"]="X", ["act"]=false, ["c"]={207,19,0}, ["ac"]={120,0,0}, ["ct"]=0 } } --[[ * Converts an HSV color value to RGB. Conversion formula * adapted from http://en.wikipedia.org/wiki/HSV_color_space. * Assumes h, s, and v are contained in the set [0, 1] and * returns r, g, and b in the set [0, 255]. * Greetz https://github.com/EmmanuelOga/columns/blob/master/utils/color.lua * * @param Number h The hue * @param Number s The saturation * @param Number v The value * @return Array The RGB representation ]] function hsvToRgb(h, s, v, a) local r, g, b local i = math.floor(h * 6); local f = h * 6 - i; local p = v * (1 - s); local q = v * (1 - f * s); local t = v * (1 - (1 - f) * s); i = i % 6 if i == 0 then r, g, b = v, t, p elseif i == 1 then r, g, b = q, v, p elseif i == 2 then r, g, b = p, v, t elseif i == 3 then r, g, b = p, q, v elseif i == 4 then r, g, b = t, p, v elseif i == 5 then r, g, b = v, p, q end return r * 255, g * 255, b * 255, a * 255 end function isClickWithin(clickX, clickY, button) leftBoundary = button['x'] topBoundary = button['y'] rightBoundary = button['x']+button['w'] bottomBoundary = button['y']+button['h'] if (clickX > leftBoundary and clickX < rightBoundary) then if (clickY > topBoundary and clickY < bottomBoundary) then return true end end return false end function ceaseFire() isFiring = false end function resetQFlareCount() qFlareCount = 0 end function resetQFlareDelay() qFlareDelay = 0 end function resetHash() buttons[15]["act"] = false end function resetD() buttons[8]["act"] = false end function resetQ() -- reset qFlareCount and qFlareDelay resetQFlareCount() resetQFlareDelay() -- reset the D & # button state resetD() resetHash() -- stop the ongoing queue ceaseFire() end function processClick(clickX, clickY) for index, button in next, buttons do if (isClickWithin(clickX, clickY, button)) then -- A button was clicked if (button["label"] == "*") then -- asterisk button was clicked print("firing with qFlareCount:"..qFlareCount.." and qFlareDelay:"..qFlareDelay) isFiring = true qStartTime = millis qFlaresFired = 0 resetHash() resetD() elseif (button["label"] == "R") then -- R button was clicked -- reset the counter flaresFired = 0 elseif (button["label"] == "D") then -- D (delay) button was pressed -- deactivate # button state buttons[15]["act"] = false elseif (button["label"] == "X") then -- X button was pressed resetQ() elseif (button["label"] == "#") then -- # (Flare Count) button was pressed -- deactivate D button state resetQ() buttons[8]["act"] = false elseif ( button["label"] == "0" or button["label"] == "1" or button["label"] == "2" or button["label"] == "3" or button["label"] == "4" or button["label"] == "5" or button["label"] == "6" or button["label"] == "7" or button["label"] == "8" or button["label"] == "9" ) then -- A numbered button was clicked -- if the "#"" button is active, the number presses are setting the queue flare count if (buttons[15]["act"]) then qFlareCount = tonumber(tostring(qFlareCount)..button["label"]) print("qFlareCount:"..qFlareCount) end -- if the D button is active, the number presses are setting the queue flare delay if (buttons[8]["act"]) then qFlareDelay = tonumber(tostring(qFlareDelay)..button["label"]) print("qFlareDelay:"..qFlareDelay) end end -- Toggle the active state on D or #. All other buttons get set to active state. if (button["label"] == "D" or button["label"] == "#") then button["act"] = not button["act"] else button["act"] = true end -- set the clickTime button["ct"] = millis end end end function processFiring() -- Only continue if there's a flare queue if (qFlareCount == 0) then ceaseFire() return end timeToFire = qStartTime + qFlareDelay -- wait until it's time to fire if (timeToFire < millis) then -- fire if flares are in stock -- and the qflaresFired is less than the target if flaresFired < flareCount and qFlaresFired < qFlareCount then -- there are flares in stock -- increment the flaresFired counter flaresFired = flaresFired + 1 output.setNumber(cfIOIndex, flaresFired) -- increment the qFlaresFired counter qFlaresFired = qFlaresFired + 1 -- queue the next firing time qStartTime = millis else -- there are no flares in stock -- stop firing ceaseFire() end end end function onTick() tickCounter = tickCounter + 1 millis = math.floor(tickCounter / 60 * 1000) isP1 = input.getBool(1) in1X = input.getNumber(3) in1Y = input.getNumber(4) if (isFiring) then processFiring() end if pre and not isP1 then -- click just occured processClick(preInX, preInY) end pre = input.getBool(1) preInX = input.getNumber(3) preInY = input.getNumber(4) end function onDraw() flaresRemaining = math.floor(flareCount - flaresFired) percentageRemaining = flaresRemaining / flareCount -- draw the main bg which will covered and form a border setC(c1[1], c1[2], c1[3]) screen.drawRectF(0,0,32,32) -- draw the black backgrounds setC(0,0,0) screen.drawRectF(0, 0, 20, 7) -- heading screen.drawRectF(20, 0, 12, 7) -- No. remaining indicator screen.drawRectF(0, 7, 24, 32) -- keypad screen.drawRectF(24, 7, 10, 32) -- function buttons -- determine the color for the heading text if (percentageRemaining == 0) then setC(120,0,0) else setC(0,120,0) end screen.drawTextBox(0, 0, 20, 7, "FLR", 0, 0) -- determine the color of remaining indicator based on remaining percentage -- 0.35 is green hue (on HSV wheel [0..1]) local hue = 0.35*percentageRemaining local r, g, b = hsvToRgb(hue, 1, 0.45, 1) setC(r, g, b) screen.drawTextBox(21, 0, 11, 7, flaresRemaining, 0, 0) -- draw the buttons for index, button in next, buttons do if (button["act"]) then setC(button["ac"][1], button["ac"][2], button["ac"][3]) else setC(button["c"][1], button["c"][2], button["c"][3]) end -- screen.drawRectF(button["x"], button["y"], button["w"], button["h"]) -- setC(0, 0, 0) screen.drawTextBox(button["x"], button["y"], button["w"], button["h"], button["label"], 0, 0) end -- clear button active states for index, button in next, buttons do if ( button["act"] and button["ct"]+actDuration < millis and not(button["label"] == "#" or button["label"] == "D") ) then button["act"] = false end end end function setC(r,g,b,a) if a==nil then a=255 end screen.setColor(r,g,b,a) end
So this is version 2, which is the advanced GUI. Version 1 has a simpler GUI.
Here’s a sample of version 2 which adds a queue and a timer.
User input is as follows.
The user clicks # followed by the number of flares they want to launch. Then, the user clicks D followed by the millisecond delay between each flare launch. Then, the user presses * to execute the queue.
An example for this is the entry #1*
which tells the system to fire 1 flare. It omits the D button click because there is only 1 flare, so a delay between flare launches is unecessary.
The next example input is #2D500*
which tells CrispyFlare to launch 2 flares with a 500 ms delay between launches.
The final example input is #3D150*
which tells CrispyFlare to launch 3 flares with a 150 ms delay between launches.
The count and delay are saved between launches, which means that once programmed, the user can press * a second time to launch the same fire mission as last time. The existing fire mission can be cleared using the X button. The fire mission can be stopped during execution by pressing the X button.
The flare counter can be reset by pressing R. R is meant for “Reloading” in the case that the vessel returns to base and the flares are physically reloaded by the game.
Pretty cool, eh? I have yet to test version 2 in game. I think the script might be too large, and I might have to do some aggressive optimization to fit into the game. That is of course if Pony IDE isn’t lying when it says the script limit is 4096.
I have tested CrispyFlare V1.0 in game. It’s pretty cool! I’ll remember to record some in-game footage of these working when I play next.
This UI is super simple. Press anywhere on the screen, and a flare will be launched, if able.
I now know that there is a problem with this script– it has no reload button! A user would not be able to fire again after reloading their flare tubes, unless they find a way to power-cycle the Microcontroller which drives this UI.
My next goal for writing code in Stormworks is to write code for someone else. I’ve done enough code writing for myself to where I have a good handle on Lua scripting in Stormworks.
This is my new master plan for the end of 2020 and the start of 2021– Generate value for other people.
I’ve long dreamt about making software development my job. Heck, I was so determined to make this a reality that I shortly had the saying, “I will become a paid software developer, or I will die trying.”
It’s still a good saying, although I’m no longer in the headspace where I would end my life if I couldn’t make it work out.
I’m going to make it work out. I might die trying, but that’s going to be from an accident, not an intentional act.
The big thing that’s changed from how I used to do things is that I care about moderation now. I do a lot to ensure that I don’t get burnt out with my work. When I do overdo it, I’m gentle and I take time off to recover. Moderation is really important to me now.
I’ve gone off on a tangent. That’s okay, but let’s bring it back to the point I was trying to make.
Generating value for other people. This is a big mission of my Twitch streaming idea.
I can generate value for people by
- keeping them company
- providing them with software that they couldn’t write themselves
- being nice to them
- giving them attention
- teaching them something
In doing this, I get something in return, as well.
- having someone to talk to
- being a “guru”
- having an opportunity to teach
- learning through making mistakes
- confidence practice
- learning through teaching
I think what I get out streaming on Twitch might actually be more than what a viewer would get. I suppose that’s what I could call a harmonious relationship!
I’ve been scared to stream lately. The last stream I did ended up with me being pretty embarrased by my behavior. I started out so confident and calm, then I was surprised by a viewer who interacted with me. I prioritized my focus on their words, and what I was researching and reading aloud became secondary.
I had a codependent relapse of sorts. I suddenly felt the need to perform, and entertain. I lost my cool and reverted to a childlike state where everything was a joke and my thought process was completely illogical. I laughed uncomfortably and I ended the stream feeling like a fool.
I haven’t gone back to it since then. I can do it again, and I want to do it again, but I’ve set a milestone for myself which I need to reach before I go back.
The milestone is the completion of my chat bot, “CrispyBot” and the !request command.
I think I wrote about the request command before… Maybe…
Ok I’ll summarize. A viewer in my Twitch chat could send the message, !request I would like a bot which has the command !fortune and it tells me a fortune.
This input would create a line in my google docs spreadsheet which tracks the requests that I get from viewers.
The columns in that spreadsheet are Requestor, Request Description, and Donation Amount. I am using https://sheetsu.com/ to integrate the bot with the spreadhseet.
It’s a milestone I set for myself, but it’s probably not worth holding off on the stream for. It’s not like I can’t stream unless I have the request bot. In reality, I’m avoiding streaming because it’s uncomfortable.
It’s uncomfortable to stream via cloud workspace which has little familiarity to my existing development environment. It’s uncomfortable to spin-up the cloud workspace and log into Mumble and X2Go so my voice and keyboard/mouse inputs are sent to the cloud workspace. It’s uncomfortable to engage with strangers and carry a conversation.
What do I really want?
I want money so I can move to Idaho. I want money so I can hire a home cleaner. I want money so I can buy a house. I want money so I can buy a car. I want money so I can rent an office space.
I want money, and I want a good life. I want a life where I earn what money I make. I don’t want a life where I’m given things by a higher power such as a state government. That kind of life is fragile… Again, Washington state is not the place for me anymore. They’ve long been against individual freedom and I don’t want to support that anymore.
So um, yeah.
What to write about now?
Did I finish that previous tangent? Is it time to move on or no?
I think I finished it.
For now, I wrap up this writing and I go do laundry!
I me do this it what huh?
asdf

Affy & Gratty
15. I do what I want, I follow my own path, because it makes me me.
92. I allow myself to take a break and do something I enjoy.
91. I make a difference in the world.
90. My spirit is beautiful.
I’m grateful for my sister A. Did I write about her gifting me a new pair of shoes? I haven’t had new shoes in so long! I forgot what new shoes felt like! I’m really grateful that she would buy me new shoes after seeing my old shoes and how holey they were. Super stoked to have Vibram shoes!
I’m grateful for my cell phone camera which I used to take card photos. I use OpenCamera and it’s been really helpful in locking orientation and doing Exposure Bracketing. I end up with a variety of differently exposed photos, one of which I ultimately choose, depending on which turns out best. Also I lock the format to portrait mode which is super helpful. Super nice app.
I’m grateful for vegan pumpkin pie! I’m so grateful that I realize that eating pie for 4 days straight is not a good idea! My poops have been so painful and difficult to pass! I really scarfed down the last of the pie I had left, so I won’t have to worry about the sugar content or the temptation anymore. In the future, I’m going to just say no to sugary food, even if it’s free! That’s not food, that’s digestive system suffering in a container which happens to trick my tongue into thinking it’s a good time!
ktnxbai