Another mixed dev and business day. Though, I had a special treat today!
I had a nice lunch with another indie who used to work at BioWare with me. Over beers and lunch, we chatted about such things as Pokemon Go, sales tax, indie publishers, motivation, and our current projects. We also had a really interesting talk about procedural drama. Dusty had some really cool insight into ways to string together characters' stories, as well as persistence of personalities and named characters vs. strictly procedural characters. Food for thought!
In the dev time I had this morning, I tackled a bit more of the item GUI system. I think what I'm aiming at is a system where each interaction has an optional "RaiseGUI" property. If the player selects an AI that's in an interaction with this property set, the game will show that named GUI. The widgets in that GUI will be wired to in-game data through the interaction's settings.
So if Akiko interacts with the battery using the "inspect" interaction, it can have "RaiseGUI" set to show a meter, and the meter's value will be set to the battery's current charge. So far, this seems pretty easy to do with some data files and Unity's component-based architecture. We'll see next week!
Lastly, more business stuff. Now that I'm incorporated, I have to update all of my distribution agreements with Valve, GOG, etc. So lots of paperwork to rifle through. Fortunately, it's pretty straightforward stuff. Just takes a while to sort out.
Anyway, that's all for this week. Have a good weekend, all!
Hey Folks! Short dev day today as I had to take care of some business banking stuff. But I think I've figured out where to "jam" next: equipment UIs.
Part of the experience I want to give players in this space prototype is the joy of flipping switches and turning knobs that actually mean something in the game world. I want them to enjoy examining the controls like the owner of a new car, and design/choose ships based on their "feel," and not just stats. Manual override on an airlock is actually by the airlock, not some translucent menu floating in one's field of view. And like Ripley in Alien, if you're going to self-destruct a mobile factory, you'll have to navigate the annoying self-destruct sequence through a series of failsafes, not just through a cascading on-screen menu.
For this to work, AI is going to have to be able to raise a UI when interacting with an object. E.g. if it goes to the ship's helm, it'll show the helm controls. If they approach the navigation console, they'll see the System map, orbital projections, and possibly RADAR/LADAR readings.
To work towards this, I setup a simple bar meter UI to see if I could attach it to the battery to monitor charge. And without too much effort, that's up and running. It's hooked up to a debug hotkey, and has lots of hard-coded values, but it works.
The next step is to find a way to launch the GUI when the AI reaches a control panel. And I'm thinking this will be in the interaction system. I.e. the interaction should have an optional UI it raises while running, and that UI closes when the AI finishes the interaction.
This raises a few questions, though. For one thing, what's the AI doing when using the UI? Can the AI bail out before the user is done with the UI? What if the user switches AIs?
Also, how will UI layouts and data-mapping work? If I want a UI to monitor temperatures around the ship, how to I define all the gauges' positions on-screen, their data sources, and what various buttons and knobs do when clicked? Making stuff data-driven isn't too hard, but what data format to use so it doesn't get too complex is a harder question. Especially for something like this. Which is complex :)
Anyway, that's where I'm headed next. I'll start simple, and hopefully some patterns will emerge that show me how to solidify the system. More on that later!
Hey Folks! Got the batteries working today. And also a slight change to the power transmission system.
Yesterday's post prompted some useful discussion of the power system, and Malocodor's suggestion turned out to be a better approach than mine. I originally had each of the powered items check surroundings and try to draw power from them, and each of those surroundings would chain-request power back towards the source. This worked, but required the system to re-check each request.
Malocodor's recommendation was to instead map out all the power connections once, and just update it whenever conduits are added or removed. This way, powered objects just checked their last known connections without all the back-tracing. And since I already had a lot of the grid-walking code to trace rooms, I could easily make a tweaked copy to trace conduits.
And once I had setup the air pump to use power, it (correctly) stopped operating since none of the connected conduits had power. Time to add some batteries!
Yesterday's post showed the battery art I added as a placeholder, so the bones of the item were already done. All that remained was to setup the various properties on the battery to make it store power. Namely, it has a stored power limit and some placement rules that prevent stacking, etc.
Then, it was just a matter of hooking up the air pump and battery with appropriate stats. Since I'm trying to be near-future, hard sci-fi, I want the numbers to make sense. So as a baseline, I'm going with some current-tech values for things that NASA and industry use. So the battery is assumed to be a big nickel-hydride bank, like NASA uses. And for the in-game size of about 0.25 m3, that means it stores about 41kWh.
Similarly, a typical, 2-stage vacuum pump used industrially consumes about 100W. So each time "use power" is triggered, I made it consume 0.1 kW. Hooked up to the above battery, it'll run continuously for about 410 hours, or 17 days. I have no idea if that's accurate, but it seems reasonable at first glance.
Then, it was time to try it out. And I'm happy to report that it seems to work! Cutting down the battery capacity for testing to last about 15 seconds, I saw the pump run for 15 seconds, then cut out. And when I added a second battery to the network of conduits, the running time capped at 30s.
Mission successful! Probably some bugs yet to find, but good enough. Jam on!
What's next? Currently, I'm leaning towards item UIs. Each item that the AI can interact with is probably going to have a UI the player can tinker with to make stuff happen on the ship. E.g. AI can sit down at a nav panel on the ship, and it opens a UI where the player can plot their route. Or an AI standing at the outer airlock control panel can control pressurization to exit/enter the ship.
The idea here is that pressing buttons, looking at meters and flashing lights, and throwing switches that actually do stuff can be fun. Won't know until we try it, though!
I think I've got a working set of code to emulate power needs on a ship, and I'm now in the process of filling-in some content to test it. And since seeing it makes it easier to explain, here's what I have so far:
The above room is a continuation of the air pump simulation from a few days ago. To recap: an air tank (orange cylinder) is to the right of an air pump in the wall, which gathers gas from the room to its left, and pumps it into the tank. The new part are the snaking blue conduits and the battery in the lower right (below the light).
As described yesterday, ship items are going to need power, and to get it they'll request power from points specified in their data. If the thing under that request point has power, it's delivered to the requester, and it runs for one more turn. If not, the requester shuts down until it can have its power needs fulfilled again.
In the example room above, the air pump needs some kWh per "turn," something like 1 kW/h. So when the pump is ready, it checks its nearby points for power input. Assuming one of those points is the tile to the right, we find a conduit, air tank, and floor there. Since the conduit is the right type, it tries to get power from that.
The conduit, however, has no power stored, so it requests power from its nearby points. Since this conduit is an "L" shape, it tries to get power from the tile "south" of it. That, in turn, tries to get power from the conduit to the right, and so on until we reach the battery. Since the battery actually has power stored, it deducts from its charge (if any), and sends a message back up the chain about how much it gave. Assuming we have enough to fulfill the pump's request, the pump triggers another "turn's" worth of action until it makes the next request.
So far, I think this'll work. It'll allow distant power sources to supply components around the ship. It also allows power to reroute around faults in the path, in case one of the conduits is damaged. More importantly, I'm hoping this will be one of the "fun" bits of ship building: planning system layouts, power needs, redundancies, etc. Then, setting the ship loose and watching it succeed, or more likely, fail.
One of the risks of this system is performance. Each power cycle is a series of connection requests, possibly many for complex layouts. And this could eat into performance. However, I'm hoping that by keeping the rate of requests for power limited, it won't kill performance. E.g. if each item on the ship requests power once per second, that shouldn't be too bad. And if power fails on the ship, a 1-second lag time to shut-down of connected equipment won't be unrealistic.
The other, more scary, risk is that this won't be fun. I was reading Tynan Sylvester's AMA today on reddit, and one of the things he discusses is the core of a game's design. He warned against adding complexity to compensate for lack of fun, or not identifying the fun soon enough. He explained that core game concepts should be identifiable as fun early on, and can be reached by trimming out excess fat over and over until there's one irresistible experience.
I'm not sure that I'm doing this very well. I mean, I expect to enjoy this, but it's not enjoyable right now. Heck, there's not much to do besides build. And maybe watch funny AI bugs. It's no more a game than a Lego set. An unfinished, glitchy Lego set.
Then again, I think I'm building towards an experience I already know I like. Namely, Prison Architect or Rimworld within the context of a spaceship. It'll have differences, but a lot of the fun I have in those games is coming up with a new design for a prison/colony, and testing to see how well it works in practice. That's the core experience, and it may be that it cannot exist without a certain minimum number of interacting systems.
Which I guess Tynan also admits in his StarCraft example. One can strip out a lot in SC to reach the core, but the core still has several parts. 4 unit types, AI for the units, a map with resources, and an opponent. I can probably narrow down the number of systems I have to a minimum, and add code to test it with a crew in space. Maybe that should be a "game jam" goal I set for the near future?
Hey Folks! Hope everyone had a good weekend. Pretty low-key, here. Yard work, a park visit, Skyping with family. Maybe a quintessential summer weekend?
Back at the office, Tiago's shared his latest mobile port work, and still looking good! One of the bigger features he's working on is a zoom lens for making it easier to interact with tiny objects/UI items on small screens (or for those of us suffering from "fat finger"). This will also be a chance to look at the context menu and pop-up tooltips for things, and hopefully make them all play nice together and make the users' lives easier.
On the space prototype, I've decided to try tackling power as my next task. Ship equipment is going to require power to work. And though the gas stuff I added has many similarities with power (drawing from a source, depositing in a destination, and changing status based on whether any is available), it's different enough to warrant a closer look.
The current thinking is that I'll be doing everything in Watt-hours (or kWh, or MWh), since that's a pretty universal measure of a machine's power needs. Batteries will store kWh, machines will consume kWh while running, and generators will create kWh while running.
That can all be done using the ICS stat system, so I'm not too worried. The trickier part, though, is transporting power from the source to the destination. How will a player be able to define which areas of the ship are powered? And how machines get their power from local surroundings? If a chunk of the ship is damaged, which things lose power?
For that, I need some more spatial code, like the gas stuff. And since power travels differently than a gas, it'll probably use separate (but similar) special code.
So far, I'm working on a system where objects can have two points specified: a point from where they draw power, and a point to which other, downstream things can tap for power. Only the first is required for powered objects. The latter will be optional. Sort of like this:
The source will generate power, object1 will grab power from point "A" which overlaps the source, and object2 will grab power from object1's point "B." I'm hoping that this allows for a variety of powered system designs. E.g. object1 could be a simple conduit with input and output points, or it could be a working machine that has a way to transmit power downstream, like a daisy chain or string of lights.
When purchasing, you can choose how much to pay, with a minimum of $3.74 USD. You can also choose to give 10% of the purchase to the Child's Play charity. Plus, you'll be supporting Indie Game Stand, who are all-around good folks.
So as usual, if you've been holding out, now may be your time :)
Hey Folks! I think I've got the air tanks up and running now. I spent most of the day fixing up the code for gas pumping to be compatible with both rooms and items, and then setup an "Air Canister" item to test pumping gas into a non-room item. Here's how it looks right now:
In the screenshot above, we're looking at a couple of rooms separated by walls and doors, an air pump, an air tank, and our trusty crewman Mann. Each grid tile has a debug overlay to help me see partial pressures of each gas present, so that's what all the numbers and red/green tint are. (Green means pressure is close to atmospheric, or breathable. Red means near vacuum.)
The air tank is the orange circle with the white bracket around it, in the "red" room. Immediately to it's left is an air pump in the wall drawing air from the room to the pump's left ("orange" room) and into the air tank on its right. Mann is standing in the "green" room, which is sealed off from both the red and orange rooms.
There are a few things going on here.
First, you can see the pressure is high in that air tank. The numbers are partial pressure of each gas in kPa, and atmospheric pressure is 101.3 kPa. So that tank is quite a bit higher than normal air pressure on Earth. Though, probably still only about half of an industrial air canister pressure when full.
That air tank is being filled from the orange room. It was originally higher pressure (like the green room), but has been sealed off with the pump running a few minutes. Given enough time, the orange room would turn closer and closer to red, though probably never reaching it due to diminishing pump performance at low pressure.
Speaking of red, the red room is a total vacuum, and is demonstrating that the air pump and air tank are sealed off from it.
Finally, we see that the pressurized rooms have mostly N2 (nitrogen), some O2 (oxygen), and trace CO2 (carbon dioxide). When the simulation started, I pumped in roughly Earth-like atmospheric constituents (so about 80/20 N2/O2). All of the CO2 you see in the image was generated by our trusty windbag, Mann. Given enough time, the ship's air will have more and more CO2 and less O2. That's where future equipment will come in, such as CO2 scrubbers, ducting, and either O2 reservoirs or O2 generation beds, to create a life support system (at least the breathable component).
The question now is, what next?
I see a few options. I could go ahead with the aforementioned life support equipment to try and create a closed system. I could also start thinking about power and electricity for the systems, and how they interact with that. There needs to be some new data added to handle asphyxiation in crew (and what happens when a crew dies).
There are also higher-level systems I haven't touched yet. I want there to be a console the crew can interact with to load a UI for navigating the Solar System. And for that matter, a system to open a UI based on crew interacting with it.
So lots to choose from. I'll let that stew over the weekend, and see what I'm leaning towards on Monday. Until then, have a good weekend!
I did some refactoring today, to try and get the recently-added respiration code to work for things like air pumps and filters, too. I think I've got most of the code and data for it in place now, and I'm in the process of debugging it.
Basically, I took all the remaining gas functions and stats and moved them from Room to Condition Owners (COs). The idea is that life support systems will need to pump gas to and from ship equipment (like O2 canisters). And since equipment and rooms both have COs attached to them, I just made those the home for all that logic and data. COs are getting a bit heavy, but I can optimize and organize later.
So now, my COs have a few tools in their tool belt for managing thermodynamics:
GasExchange() - takes params for point A, point B, and flow rate. The function looks for rooms at A and B, and performs a gas exchange between them as if there is a giant hole between each. The flow rate controls the rate of exchange, so it's possible to slow or speed the equalization, if needed.
TempExchange() - like GasExchange, except it equalizes temperatures between A and B over time.
GasRespire() - Takes a huge param list defined in a new data type. Basically, this handles the transfer and/or processing/filtering of a single gas within a mixture. Things like a human breathing O2 and exhaling CO2, or a CO2 scrubber pulling CO2 from a gas mixture. Source and destination for gases can be either a room or CO (like a canister), and is defined by filters and coordinates in the param list.
GasPump() - Like GasRespire above, except is does the entire gas mixture and no processing/filtering. Basically, just moves gas from container A to B.
Together, this should cover breathing, leaks, room atmo mixing, scrubbing, filtering, and just about any other gas process on a ship.
I'm also hoping that this is a good starting point for interoperating ship equipment. In theory, this could allow a chain of rooms and COs to function as a complex machine. Room A could feed an air pump which moves gas to an adsorbent bed for scrubbing CO2, and another air pump pulls the scrubbed air into room B. And since the scrubber would use GasRespire() to pull CO2 out, it could be setup to gradually increase a "saturated" stat on itself each time it activated. Once this "saturated" stat reached a threshold, the scrubber would shut down or lose efficiency.
In other words, we may have our first ship repair situation! Granted, it's pretty janitorial in nature. But hey, blue-collar sci-fi isn't exactly glamorous :)
I tried the "just game jam it" approach today, and so far, it's paid off. I now have a respiration command I can attach to my condition owners (COs) that lets them take a gas from the room, use it to pump up one of their stats, and expel another gas in its place. And the data files can control which rooms the gas comes from and goes to, which gas to use, the volume of gas to process, and the conversion rate of the gas. Those last two are useful for making the breathing process scale with gas pressure/concentrations, and modeling inefficiencies.
So now, I'm able to plop an AI into a room, pump it full of N2 and O2 at air-like proportions, and watch as the O2 partial pressure ticks down slowly, being replaced by CO2. Neat!
In the process, I also had to setup the network of data files for things like breathing stats, timers, and critical conditions (e.g. hypoxia), and they seem to be working correctly. Nothing fancy yet. The AI doesn't dramatically keel over when asphyxiated. But the chain of "bad-to-worse" conditions for not getting O2 are there and working, just like hunger would in NEO Scavenger.
It's not the most extensible code, but it works. And it has a lot of flexibility. So score one for game jamming?
In fact, my next task, air pumps and filters, may already be partially done since the function above is customizable. Air pumps need to move air from point a to point b, and are more or less effective based on the pressure of the source container. The above breathing function already does that. And if I choose the output gas to be the same as the input, it's basically just a pump. And if I wanted to make it a filter, I could make the output gas a nonexistent one, so input gas gets converted to nothing (and using the condition stat build-up, maybe I could convert the filter/scrubber to a used version that needs to be replaced by crew).
So far so good, but there are a few catches.
First, pumps work on the mixed gas, not just a constituent of it. I could work around this by just calling the pump command for all possible gases, but that's a bit inefficient (and hard to maintain). I could possibly add a parameter to the breathing function to work on all gas and not just one.
Another catch is that not all gas is going to come from or go to a whole room. There are going to be containers to store gas, and more than likely, there will be multiple of these in a single room. (Otherwise, there would have to be walled rooms for each container, which is a huge waste of deck space.) The breathing code could be made to check input and output points for containers first, and if none found, then use the room.
Finally, I'd have to tweak a few parameters in the code so that it is more customizable. E.g. the pump needs to add or subtract stats, not just subtract, the stat to be changed needs to be exposed as a param, and the timer condition that triggers it would need to be a param. These last few are easy to do, but do inflate the function param list quite a bit.
This is pretty doable. But the other approach might be to just have a separate pumping command. It just feels weird making it separate when so much of it is identical code.
More than anything, though, I should probably sort out that container issue. That seems like it's the biggest unknown in terms of architecture, and if solved, also opens up the most useful options. (E.g. more complex ship machinery, like water systems, propulsion, reactors, etc.)
Hey Folks! I went ahead and hard-coded the gas exchange stuff today, as mentioned yesterday.
Basically, I removed the special "Door/Portal" class from the game, which previously handled gas exchange, and just made Condition Owners (COs) able to do this instead. They now have a list of "Update Commands," which are basically just function calls with parameters that are in the data definitions for each CO. Surprisingly, it wasn't hard to swap out this old code for new, and the room gases were updating normally again pretty soon after. It's even more extensible now, since it allows the data to define input and output points relative to the CO, instead of assuming hard-coded tile positions for the Portal/Door class.
I then set about coding a bespoke function for respiration, and quickly bogged-down again.
At first, I tried to make this generic enough to handle both breathing and air pumps. However, I soon decided that these were different enough that I should just make this first one breathing-only. That helped for a bit, and I was able to code the parts where it takes in a volume of gas, and based on room pressure, would figure out the partial pressure of the desired gas being inhaled. It then removed that gas from room A, and deposited the same amount of a different gas into room B. All stuff the parameters would control.
Unfortunately, I got stuck in the middle of these two pieces, where the inhaled gas is applied to the CO (e.g. a crew). Do I just add an equivalent amount of O2 to the CO's stats? That'd probably work, but how would it handle other gases? What about toxic gases, for example? Should I write this function to specifically check certain gases that have an effect on crew? Does that mean this function is only used by crew? Should I just have the function iterate through all gases present, try applying them to CO stats, and let the CO sort out which it accepts?
And how does this all relate to air pumps, which basically take everything in room A and pump it into room B? And for that matter, what about filtering/scrubbing air?
Paralyzed by thought again. I'm thinking the best approach is to "just game jam it" and worry about it later. But I think my brain was so fried by this afternoon I couldn't even do that. Hopefully, tomorrow is better.
Hey Folks! Hope everyone had a good weekend. Pretty low-key here. Though, we finally got a new bed. Yay! No more sleeping on layers of scavenged mattress topper foam and a box spring! I know, I know. How un-NEO Scavenger of me. But it was finally time to have a grown-up bed :)
Most of today was non-dev stuff. I spent some time meeting with the web developers, who are now about to start the visual design phase of the new site. We discussed colors, fonts, imagery, and navigation metaphors, and I'm excited to see what they come up with. Priority #1 is still usability, so simplicity will win over flashiness. But I'm hoping we can have both.
I also caught-up with Tiago's mobile port work, and it's moving along nicely! He fixed some major issues in the map, battle system, and even save files. It officially saves non-Flash cookies now! Well, the files are still .sol files, but no longer in the precarious Flash cookie folder. They now live in the user's AppData folder along with other real-live app data files, so are safe from cookie-cleaners. We may have a non-Flash PC version yet!
On the space prototype, I picked up where I left off working on the air pump data, and I'm still unsure if it'll work as intended. I'm starting to think this may be a case where I should just do it the cheap way and have hard-coded bits for things like air transfer, pumping, heating, etc. Like the room gas exchange is now. Trying to make this use the interaction system exclusively is getting out of hand and confusing, and maybe the interaction system can have certain keywords that spawn hard-coded functions to do complex stuff like this.
NEO Scavenger did this a lot with the conditions calling functions in the code by using keywords. I originally thought I should move away from this to enhance moddability. But I'm thinking some of this stuff is basic physics, and shouldn't need to be changed by data.
Also, the game may never exist if I try to make everything too perfect and extensible. Remember "game jam mode," Dan? Get on it!