Hey Folks! Productive day today. Website fixes and game dev :)
The website fix is something some of you may have run into. Apparently, the user contact forms would never work due to a missing Subject field. So probably, nobody's been able to contact each other since the new site went live. I've got this fixed now, and thanks to James Murchie for the heads-up!
Back on the game dev front, I've been further fleshing-out the new doors feature to work with other systems.
First order of business was to get the pathfinding code to use the new tile conditions, instead of the old tile socket/passable flags. Once that was done, doors could affect the pathfinding cost of a tile differently depending on their state.
Then, I updated the pathfinding heatmap code to give open and closed doors higher path costs, so AIs would prefer routes without doors first, then with open doors, and finally with closed doors. So far, so good.
The trick now is to see if I can get AIs to interact with doors if they are in the way. I could just make the doors auto-open like in Star Trek, but I think it's just about the same amount of work to make AIs pause, manually open doors, and then continue when pathfinding. And since I want situations where the doors auto-lock during depressurization or other emergencies, AI manual override is going to have to be done eventually.
Plus, this gives me the opportunity to have el-cheapo doors that require manual opening, and luxury doors that auto-open, later on. (I'm also not entirely sure how I want to handle things like onboard ship equipment that detects things like nearby AIs, pressure, and other situational stuff. But that's coming up soon, methinks.)
Anyway, we're most of the way there as of this evening. The code is in place, as are the necessary data files. I just need to work out the bugs. More on that tomorrow!
Hey Folks! Unfortunately, slow day today. Doctor's appointment in the middle of the afternoon. And some paperwork catch-up in the morning.
However, I did manage to make a little progress on the room atmosphere simulator. In fact, I think I got it working just right when only two rooms were interacting. However, if a room had more than one open door in it (like a corridor or galley), things got weird. Like, pressure would ping-pong around the rooms, spike, and suddenly drop to negative or positive infinity.
Some of it was down to my code processing the same room multiple times in a simulation cycle, and that was pretty easy to fix. However, I think part of it has to do with my hacky attempt at mixing temperatures.
I'm not interested in doing complicated integrations or calculus for extremely accurate gas exchange. (At least, not here.) I just want rough approximations "good enough" to convince the player. Unfortunately, pressure is related to temperature for a gas. So is volume. And in the case where an airlock opens between two rooms with potentially different temperatures and pressures, we have all of those variables changing.
I think one of the ways to work out such a problem is to do things one step at a time. E.g. pressure first, then heat exchange. And I'm going to try that next. Though, probably a part of my problem is that I'm also not very realistic in my calculations, and I'm creating or destroying energy/matter in the process.
Anyway, I'll try again tomorrow to see if I can get something "good enough." Game jam mentality calls, after all!
Hey Folks! Hope everyone had a good weekend. We visited some friends for the first time since leaving Edmonton, and had a great time eating and playing board games. I think we are all set to do that again sometime soon. We had all but written-off board games since the little one was born. But it appears to be possible to wrangle and play at the same time!
Back on the space frontier, work continues on the room system. Last week, I almost got the rooms hooked-up in such a way that they could talk to each other and exchange their gases when I clicked on the door between them. Of course, it turned out not to be that simple. Clicking on the door did nothing.
Once I sorted that issue out, it opened up a new can of worms. It turns out the rooms didn't actually see each other through the doors because the doors acted like walls until opened. In essence, that's all they were: walls that magically vanished when clicked, so the room detection script didn't notice anything special about them.
I decided to create a new object type: portals. This way, the game could understand the special nature of the door when it encountered them during room calculations. Each room now stores a list of doors it has, and each door lists the rooms it connects. Thus, rooms can check doors for being opened or closed, and simulate gas exchange accordingly.
One problem down. And in fact, this worked pretty well. I was able to get rooms to fill with a gas, and spread it to adjoining rooms when the door between them opened.
However, this didn't account at all for different room sizes. If a supply closet had 1 atmosphere of pressure in it, and it opened into a giant cargo bay with no atmosphere, the closet and bay would end up at about 0.5 atmospheres each. Not really accurate. Plus, I'd like to work temperature in here somehow. Even if it doesn't figure into the gas exchange (which it would), I at least want there to be a danger of freezing to death or overheating if environmental controls fail. And this temperature issue should be able to move into adjoining rooms if there are no sealed doors.
So I've started working on that now. And I've actually got it running! Except, the math is pretty wrong, as it turns out. I have a few calculations in there that cause gas to be spontaneously created and/or destroyed when doors open, and I have to find out where.
Still trying to move quickly through features, with an eye toward getting stuff up and running, even if crude.
Today, I finally wrote room-detection code. It's not very sophisticated, and basically treats walls like doors. Small openings/bulkheads are ignored, so rooms will pass through them. Unclosed rooms that adjoin empty space are also "rooms." However, each separate room has an object to track data, and it can set properties on the tiles it covers. I've even ported my tile-based gas exchange over to room-based gas exchange.
For the unclosed rooms, I've just added a boolean flag to say "yes, this room is unsealed." I'll see if it needs more than that later. For now, I'm thinking all it'll do is tell us whether to do a regular gas exchange or treat this room as an infinite volume into space.
In the process, I also finally fixed an AI pathfinding bug. Since there may be tiles that are both walkable yet unreachable, I needed to do a pathfinding check in the AI for such cases. If found, they'll bail out instead of setting it as a pathfinding goal.
I've also started implementing some rough code to swap out closed doors for open ones, and vice versa, when they're clicked in the crew sim mode. It's just for testing purposes, but it might be how I'll handle mode-switching items like doors. Just delete mode 1 and add mode 2 in exactly the same place. (Like NEO Scavenger's item mode switching.) The benefit of this is that I can piggyback on the ship's code which handles tile data for adding/removing parts.
It doesn't work yet, however. Doesn't crash either, though, which is weird. Monday, I'll have to see what's up, and then start pumping air into rooms to see if toggling the doors does anything.
Continuing yesterday's thrust toward faster prototyping, I worked on some more gas expansion code today.
First, however, I had to fix something I broke in my haste. I accidentally made all UI overlays use a material for tinting, and it caused selection brackets and AI thought bubbles to be washed out. Splitting the GUI items into tile and not-tile allowed me to restore the different materials for different usages.
Once that was done, I made a few more tweaks to the gas expansion code, and I think it's functional enough to use. The next step will be tricky, though: making use of this simulation.
At first, I was thinking I should make an atmospheric processing unit to spew gas into the surrounding areas. But that isn't very realistic, as a life support system would have the unit pipe air into distant rooms and back again, with various ducts and filters along the way. And my gas expansion code seems to break down at long range. E.g. as soon as you get 15-20 tiles away from the source, the gas takes forever to expand.
This is a limitation of the expansion code, which uses a type of cellular automata code. It checks each tile's neighbors and averages the tiles' pressures so they are more alike. As a result, gas can only move so fast through a system, while in reality, it can move close to Mach 1 (and usually does).
More than a few of you have suggested steering away from per-tile calculations and using a room-based system instead. I've been resistant until now since it's a sometimes complicated problem to solve. However, this may be the straw on the proverbial camel's back. It's come up enough times that maybe I should just do it. Everything seemingly might use it, including gas expansion, lighting, privacy, and designated social usage (e.g. head, canteen, storage, etc.).
Before I go down that route, though, I need to do something about doors. Without them, there are no discrete rooms (unless you have a ship of ghosts or teleporting Trekkies). And doors...well, doors are a famous problem in games.
For now, I'm going to create a simple sliding door. 5x1 tiles in size, 3x1 opening. One version closed, the other open. I'll figure out some quick and dirty way to toggle the different modes, some way for pathfinding to cooperate with them, and then, some way for the game to figure out what a room is. Then, convert my tile-based gas expansion code to room-based, and see if that helps speed, performance, realism, and eventually, hooking up an HVAC/Life Support system.
In other news, I'm also trying to figure out an oft-reported NEO Scavenger bug. People say that Autosave is causing all kinds of issues when enabled.
The trouble is, I can't seem to reproduce it in my debugger. Hours of play on vanilla produce nothing. I've just tried an hour of playing with a mod, and still nothing. If anyone can help pin down the trigger, let me know! I'd love to get this sorted out once and for all.
Okay, I've pulled the trigger on "Gamejam Mode." I wrote a script to yank out all my lights from the big ship layout, cranked the ambient lighting back up, and I'll just ignore that for now. And in it's place, I'll start banging out as many new features as I can. No polish. Just quick and dirty.
First up: ship atmosphere.
I want the ship's HVAC system to be something the player has to carefully design (and maintain) to keep occupants alive. And this means keeping track of O2 pressure on the ship. The simplest solution I can think of is to just have a per-tile pressure that gets smeared to neighboring tiles each update cycle (or some other time period).
To help visualize what's going on, and debug it more easily, I've hooked up the tile grid overlay to the pressure stats of each tile, and they tint green when the pressure is high, and red when it is low. And for testing purposes, tile underneath the cursor is set to 100% pressure as long as the right mouse button is down.
Here's a snapshot of it in action:
The white dot in the image above is the source of the gas input, and you can see it expanding outward from there. Walls stop propagation, and the gas slowly expands around corners.
One thing I'm immediately noticing is that this method takes a long time to fill a ship. (Remember 2x2 grid squares is one person in size.) I've been holding the mouse down for close to a minute in that shot, and the small room under the cursor is barely breathable. And this is with each tile updating once per frame.
Per-frame updates on almost 5000 tiles is pretty crazy expensive, so I may have to ramp that frequency down a bit. And that means even slower propagation.
Of course, maybe this is fine. It'd take a while to pressurize a whole ship. Heck, even pressurizing an airlock takes a while with modern tech. And doing so too fast is dangerous for the occupants. So maybe this will work?
In the spirit of gamejamming, I'll probably move on from this to the next thing, since it's "good enough" for now. Maybe the next thing is an air processor, or airlocks, or even AI reacting to lack of pressure. Whatever it is, though, it should be the shortest path from here to some playable system.
Not much to report in gamedev-land today, as pretty much the entirety of the day was spent filling out forms, mailing things, calling offices, and sorting out other business tasks. The good news is that the bulk of it involved the final steps to incorporating Blue Bottle Games, LLC. It's pretty much a done deal now, and I'm just waiting for the various parties to sync-up. (I.e. lawyers, government)
I did arrive at a decision last night about what to do next in the prototype, though. I think I'm going to switch to a sort-of "jam mentality" for a little while. Basically, start acting like I've only got days or weeks to get this thing playable, and start hammering out quick-and-dirty (QnD) solutions for all the pieces of the game.
So far, I have the following bare bones pieces:
Crew AI Simulation (Both needs-fulfillment and pathfinding)
Total rough draft here. But I think that'll get the game to a more playable state. Basically a large game loop of designing the ship, loading it with crew and supplies, take-off, fly around, deal with encounter, land at destination, repair ship, repeat.
I'll sort out lighting, fancy graphics, AI emotional realism, and all that other nonsense later when there's more game in place :)
Hey Folks! Hope everyone had a good weekend. Not a lot to report from mine. Mostly a quiet one, which can often be just fine!
I continued pushing the limits of the ship editor today, finishing up the largest layout yet. It's the same 80x50 tile ship from last week, now with floor grates and lighting. As of the end of the day, the ship has (roughly):
Wall Tiles: 750
Floor Tiles: 500
And we're definitely over our limit. Framerate is lucky to be ~30fps when zoomed into a room, and down near 15-20 when viewing the whole ship. I was able to find and fix a few bottlenecks and performance hogs in my scripting, but I think this new limit is rendering. Specifically, dynamic shadows.
80 lights casting shadows is a lot. Probably more than reasonable. I can see there being more lights on a ship in practice, but not all of them need to cast shadows.
Even so, I'm not sure there is much I can do to improve this number. I've tried reducing the light ranges, which helps a lot. But it also undermines the point of the lights: illuminating the rooms. Turning off shadows works, too, but then we have light spilling into unlit rooms and around corners. None of these things are terrible, they would just mean giving up nice lighting.
On the other hand, this is good info. Part of the reason I spent time building a large ship layout was to see what pitfalls I'd be facing. And now I have a better idea. I can also see how zooming and scrolling around the ship feels, which is currently tedious. (Though, made worse by low framerate.)
I'll have to give this some thought, and see what my options are. E.g. go back to larger tiles, no dynamic lighting, or some sort of clever lightmapping/culling/quadtree magic.
Hey Folks! As mentioned yesterday, my brain was seriously losing traction on the AI task. By midday, it was getting to the point where all my energy was being spent keeping focused, rather than doing productive work. This usually means it's time for a change of task.
So today was all about the grid system. I resumed yesterday's beginnings of switching the game from a 32-pixel grid to a 16-pixel one. I'll still be keeping the 32-pixel crew for now, though, because my main motivation for the smaller grid is to have more room for ship layout detail. Namely, thinner walls and angled/curved sections.
It took most of the morning to sort out the last of the 32-pixel settings and make it work at a smaller grid size, but I eventually did it. And using a new set of smaller tiles with angled pieces, I was able to get this:
It's not a perfect circle or anything, but I think it gets the point across. And nicely shows that non-orthogonal shapes are possible.
In theory, one could even go a step further and make perfectly curved pieces if they wanted. But the elegance of this type of layout is that it only uses a handful of modular tile types. Basically, a 1x1 box, a 1x1 wedge, and a pair of mirrored 1x2 wedge shapes. And using these to make 12-sided rings means each ring is cocentric with room to walk between each.
It's not without issues, though. For one thing, a crew is still 32x32, so it takes up 2 of these tiles. And when it walks close to a wall, it clips into said wall. I think this can be solved by making the wall block pathfinding on grid squares that border solid wall, though.
Another issue is performance. I had a total of 7 of these cocentric rings in a ship's layout at once, plus some decorative outer shapes. And the overall ship dimensions were 80x58 tiles. Assuming this was a working ship, this would mean wall tiles, floor tiles, ship's equipment and hardware, and lighting. I'd estimate I've got about 1000 tiles in this file, and that's just walls. And performance is certainly struggling. I get about 45fps when I zoom out to see the whole thing.
And this is just in building mode. I was kinda hoping to have AIs doing their thing, and physical simulations going on to handle power, air, water, and other ship systems.
That said, I have barely bothered to optimize anything yet. I found a memory leak by accident, and was able to fix that. And there seems to be a bug with the way the tile grid overlay works, because hiding it kills framerate, and showing it again fixes framerate. (The opposite of what one would expect.)
And there's probably some optimization to be had in simulating periodically, rather than continuously. The AI already does this by ticking once per second, instead of per-frame. And maybe the ship simulator can do the same.
Anyway, I feel like today's major accomplishment was a few things: proving that a non-square layout can be drawn, revealing the limits of performance, and getting a view of what a smaller grid looks like with a larger crew sprite.
More to come next week, but this was at least a high note to end on! Have a good weekend, all!
I'm starting to burn out on this AI stuff. I spent another morning looking over potential interactions, cribbing example dramatic interactions from summaries of Hamlet, Dr. No, and Casablanca. I was planning on comparing this to my existing list of "payloads" as well as existing interactions, in the hopes of grouping as many as possible into a short list for easier payload creation.
Somehow, doing this put a bullet right through my motivation. Which is weird. I was feeling really good about this process last night. I think I reached a point where I wasn't sure what to do next.
I decided to turn back to the code, to fix up the half-edited changes I was making to support this new master list of stats, needs, and ticks. It involved changing the incremental (integer-based) condition system into a fractional (floating-point-based) system. This allows me to say the blood restores at a rate of 0.0006 liters per hour, as opposed to 6 liters per 10000 hours as I would if decimals weren't allowed. Plus, I can have smaller modifiers/buffs this way.
I got that change made, then ripped out an old feature that added a list of new conditions each time an existing condition incremented (since they don't increment anymore). Plus, I found and fixed a few bugs that regressed somewhere along the way (mostly involving pathfinding breaking the reply logic).
After all this, I still balked at doing any more emotional/drama design. I think my mind is just spent.
So I switched to graphics for a bit. I've been meaning to try a new set of 16x16 tiles to go with my 32x32 crew. The artist I worked with had some compelling reasons to try smaller tiles than 32x32, and what's more, a smaller tile size would allow me to do more detailed ship layouts. (Basically, more granularity for edges, curves, and angles.)
I hacked together some 16x16 tiles for walls, diagonals, and floors, and modified the existing toilet, fridge, and other items to work with the new grid size. I even found a few places to spruce up the furniture, by adding heights and shadows to them. (I think anyway. Let's see if it works.)
And now, I'm in the process of changing the game's grid size. Which, as it turns out, is not as simple as I thought. I must've hard-coded more pieces than I realized. But, this is way easier on the brain right now. Maybe I just needed to switch from conceptual mode to spatial mode, because I feel much fresher on these problems.
Didn't get it finished yet, though. I'll have to resume tomorrow morning. Until then, have a good one!
The first part was spent reviewing conditions from NEO Scavenger for those that would apply to the new space game. Things like nausea, diarrhea, some diseases, headaches, and the various physiological stages such as hunger, hypothermia, etc. These were extremely handy in NS because I could have standardized symptoms and other effects used across multiple situations. Diseases and fumes could both apply the "headache" condition without worrying about double-penalizing or maintaining two sets of headache data.
I have a nice, long list of discomforts now :)
I also converted most of the NEO Scavenger stats and physiology rates (blood restore, healing, temperature, insulation, etc.) to IS units for use in the space prototype. This way, I've got a lot of the same simulation data and algorithms already done and can use it right away in the space game.
Plus, I did a little code-digging, and was able to verify that AI would remember secondary effects of an interaction. At least to a degree. Things which immediately affected an AI as the result of an interaction (e.g. a headache, or being winded) would be logged, but things which took place later (e.g. gastroenteritis, increased metabolic rate leading to hunger) wouldn't be logged. This actually makes sense, as we tend to disassociate results that are too far removed from the cause. (See: smoking, drinking, overeating, making babies.)
This discovery means that my idea yesterday might actually work. Namely, that I can make standardized emotional "payloads" for most interactions. I could have, for example, a standard "praise" payload, which makes an AI feel more self-assured and well-liked, more socially-fulfilled, and a bit closer to needing privacy and altruism. Then, any of the interactions that fall into that category could just point to "praise" instead of having to copy data from one to the next, and update them all in case of a data change.
This approach was further reinforced as I reviewed yesterday's work and started noticing patterns in "payloads." While not exactly the same, most of my interactions seemed to fit into a smaller number of categories with very similar benefit/drawback patterns. So far, this list consists of:
And probably some form of commiserate, surrender, and comfort, too. Probably still more as the list of interactions grows. And so far, most of these have one set of values for the subject, and another for the object. (I.e. "us" vs. "them" in an interaction) This will make the data a lot more manageable and consistent, and if I really need it, I can still tweak individual interactions with plus and minus extras.
Once I have this done for the emotional interactions, I can follow-up with the mixed social/physical ones such as push-ups and toilet use, and the complete socio-physiological picture will be in place.
Then, I think I need to look into the AI conditions system to see if I can get it to support floating-point value changes, instead of simple integers. Ints were sufficient before I started having things like blood and immune system restoration rates, which are fractional values. I could try multiplying those rates by 1000 to shift the decimal point, but even then, adding a RateBloodRestore modifier of +25%, is hard to do if the base rate is non-divisible by 4. We'll see how doable that is, though.
Anyway, I feel like data made some significant steps forward today, despite not having much to show for it. Hopefully, getting this into the game will prove pretty easy, and I can start testing the simulation again soon!
Hey Folks! Still plugging away at a complete stat list for AIs. And let me say, this is getting complex:
In the above screenshot, the rows are individual interactions (chat, snub, eat, perform trick, etc.), while the columns are stats. As mentioned yesterday, there are now three types of stats. Needs are things like emotional needs, oxygen, and hydration, stats are things like encumbrance, radiation exposure, and poison, and ticks are rates at which each of these needs/stats change over time. The selected columns on the right are the ticks.
The list has expanded slightly since yesterday, and we now have StatAtrophy and NeedHygiene (both of which will be important on long space voyages in a confined area).
As you can probably tell by the coloration, I'm about 3/4 done with choosing values for the list. And the left-most highlighted column shows the net positive or negative effect of the interaction. So far, it seems like a pretty good spread of positive and negative outcomes, so emotions should ebb and flow as AIs do their thing.
I've also made the interactions a bit more modular, as many of the petitioner->granter chains can be broken into component parts and mixed and matched. E.g. "Insult target" is a perfectly valid response for a number of interactions, from chatting to flirting, and even offers to help. This way, we should start to see more variable interaction chains and get more data re-use.
Also, as mentioned earlier, this expanded stat list gives me a chance to tackle some of the more mixed emotional/physical interactions. Things like exercising, sleeping, etc. And as I'm discovering, it raises a lot of interesting questions.
For one thing, I can go two different ways when applying effects of a physical interaction.
One way is to apply stat changes directly, like NeedRest increases when exercising, while NeedAchievement decreases. This has the benefit of working within the current AI decision-making code, since the AI is carefully remembering the direct effects of an interaction for future reference. However, the down side is that the effects are more or less instant and permanent. They get flicked on like a switch, and decay over time with normal AI rates. Not very natural. Also, it means a lot of redundant data as interactions that have similar effects must repeat similar data.
A second way is to make an interaction apply some sort of condition that contains several other conditions. In the above example, maybe the exercise interaction applies "exhausted" and "inspired" conditions, the former affecting NeedRest and the latter NeedAchievement. Doing it this way means those "exhausted" and "inspired" conditions can disappear over time, like temporary buffs. And other interactions with similar effects can just reuse those, making data management easier. The down side, however, is that the AI probably won't remember them as effectively, since they're setup to remember direct effects.
There are ways to make AIs remember things better in the second way, but I've been noodling for so long I'm starting to get antsy. I haven't made visible progress in a while, and my inner project manager is saying "put this on the back burner, or cut it." I feel like I've been refactoring over and over. Of course, a lot of this is the result of seeing how the data is actually being used now that there are more mechanics in place, as compared to the original design which was more theoretical.
I dunno. I'm going to have to take a hard look at this soon. But I might be close enough to finishing this interaction iteration to see if it makes progress, and decide after that.
Hey folks! Hope everyone had a good weekend. Ours was quite busy! One might say it filled our "NeedContact" and "Need Friendship" stats nicely :)
Back at work, Tiago sent me his latest progress on the mobile port of NEO Scavenger. Have a look!
Not bad! It loads the main screen, options, etc., and can start a new game. The player can select skills, and then load an encounter. Though, currently it's loading the wrong encounter (and if you look carefully, you'll notice the next encounter is even wrong-er :)
Also, there are still plenty of smaller issues to sort out with rendering, GUI sizing, etc. But this is an awesome step forward. I was able to wander the hex map, and this is even running on my old, crappy cheap-o tablet. So there's hope that it will work well for many users! Nice work, Tiago!
Back on the space prototype, I think I'm finally starting to make sense of my AI stats. Last week, we got a sneak peek into the full list and naming convention for AI stats. And I think I was finally able to finish standardizing them.
Basically, all stats are good when lower, and bad when higher. And depending on the concept, some are "needs" while others are "stats."
"Needs" are things which, if we have too little, spell trouble for a human. And they're easier to explain as a deficit rather than an abundance. Examples of "needs" include achievement, contact, and hydration.
"Stats," on the other hand, are things which are harmful in large doses, and are easier to explain as a positive than a lack thereof. Examples of "stats" include encumbrance, radiation, and infection.
So far, I've been able to classify all of the numbers into one of those two categories. And as mentioned already, all of them are on a scale where more is worse. One wrench in the works, however, is body temperature. This is an exception where too much and too little are problems. There are some different ways to approach this, but I'll have to give this some thought.
Also, I'm starting to realize there may be a "third" category to track. "Ticks" or "rates." Certain things about humans increase or decrease over time, and these rates change based on conditions. Examples of this include the rate at which we generate body heat, metabolize food, consume oxygen, etc. The above "needs/stats" measure how much we've lacked, but if we're running a marathon, for example, the rate at which we lack oxygen should be faster.
NEO Scavenger already had this with things like "m_fImmuneRestoreRate," and "fWaterConsumptionRate," so I may just be able to copy a lot of this over. (Both values and code/logic.) There are some benefits to having created a survival simulator before a spaceship crew simulator :)
I took a break from AI code today to reexamine the data. And as a result of some analysis, some chatting in the comments, and some testing, made some interesting discoveries.
First of all, I need to standardize my AI stats better. I'm having trouble conceptualizing them when coming up with values for how they're affected in an interaction. And a lot of this stems from having stat names which are not only abstract, but struggle to keep a standard of "more of this thing" vs. "lack of this thing." So I'm working on that.
I also realized that many of my interactions make an AI's life worse. Bear in mind that an AI is using an interaction to solve a problem, usually a deficit in some need. If they are hungry, they eat. If they want social contact, they chat. But with many of these interactions, they ended up worse off than when they started. And when I multiplied the effects of interactions, as I did yesterday, that problem just got amplified. AIs were experiencing crippling emotional distress in their attempts to satisfy needs, and quickly bottomed-out.
That said, interactions shouldn't be "freebies," either. If they don't come with a cost, there's no reason not to do them. A "freebie" interaction AI would probably just cycle through all their needs in a boring, machine-like list, and there'd be no emergent drama.
Probably the sweet spot, as far as my thinking has gotten me, is to make the overall net result of an interaction close to zero, if not zero. Probably slightly better than zero, to compensate for emotional and physiological decline, and to leave room for ship's duties in their schedules. This way, an AI has to keep doing interactions at a certain rate to stay satisfied, and the AI's job is to choose the most efficient ones. And since each interaction has pluses and minuses, it'll be a constant whack-a-mole.
Related to the above, I think my simulation so far has struggled a bit since it was only a partial picture. I only had emotional needs, food, sleep, and excretion simulated. If an interaction caused an increase in fatigue, say, there would be no way for me to balance that in the data without a fatigue stat. So the go-to Achievement interaction, furious push-ups, would have costs that never appeared in the game, making it hard to have a net-zero effect.
SOooo, I decided to finally take a crack at expanding the list of AI stats to something more like the final game will require. What are all the inputs and outputs for an AI? How does the rise and fall of stat A affect stat B?
Now, bearing in mind that this is a work in progress on both standardized naming convention and complete emotional and physiological simulation, here's the current thinking:
How much am I craving achievement?
How much am I craving altruism?
How stuffed do I feel?
How much autonomy do I crave?
How much blood have I lost?
How much do I need social contact?
How much do I need to defecate?
Can I carry more?
How much admiration do I need from others?
How much do I miss family?
How fatigued am I?
How malnourished am I?
How much do I lack friendship?
How dehydrated am I?
Am I more sick or infected?
Do I feel a deep connection?
Does this fill me with purpose?
Does this wind me?
Does this hurt?
Does this isolate me?
Does this isolate me?
How many rads do I have?
Do I feel safer?
Do I feel proud?
Am I getting restful sleep?
Does this make me sleepy?
What is my core temperature?
As you can see, my latest iteration only got about as far as hydration. And with things like Encumbrance and Rads, this may require a separate type of stat that is not a "Need." Unless we count things like "need to remove Rads" or "need to get help carrying something."
There's also the concept of "Ticks," which are timers that gradually increase certain Needs over time. NeedHydration, for example, would get worse over time, and better each time the AI drank something. And after adding some of these new physical Needs, I may need to have "Ticks" for several of these, as well.
It's a lot of data to work with. But having worked on AI in NEO Scavenger, it's not that much worse :)
In the middle of the night, an idea occurred to me: maybe the reason my AIs aren't doing predictable things is because the interactions produce such small effects. And in particular, the results they're trying to get are not much greater than the side-effects. Basically, the signal-to-noise ratio is too small.
So I set about amping-up the interaction effects. My goal was to make a 1-3 iterations of an interaction create or remove a serious condition on an AI. E.g. three insults create a self-esteem crisis, or a single lost family member creates a family crisis.
In the process, however, I was reviewing some of the old effects, and starting to question whether they made sense. Does doing push-ups have an effect on one's need for contact with others? What about one's sense of altruism? What does it even mean to have more or less altruism? Is this a measure of the AI's likelihood of doing something altruistic next? Or is it just a measure of their discomfort with their current state of altruism?
As I dug deeper, I decided to map out the existing stats and their meanings, for easier reference. And upon doing so, I realized that I had positive traits, like self esteem, but they were being used to measure discomforts and crises, like hopelessness and futility. I started thinking of ways to express these same positive needs as the equivalent lack thereof, and turned to twitter for help.
And as a result of that discussion, Lars pointed out an interesting article about Dwarf Fortress. In it, they discuss how the Adams brothers built the AI/narrative system in DF. And their approach was pretty much the opposite of mine. Instead of setting up a series of variables and trying to make stories out of their random interplay, the DF devs came up with example stories first, and built a system to generate those stories.
I apologize if someone already told me about this, as it kind of sounds familiar. But anyway, it's got me thinking. Thinking AI thoughts.
What I may do tomorrow (or soon, anyway), is perform a similar exercise. What are some example interactions and dramas I want to see play out on the ship? And can they be broken down into components or a system?
It's something I've kind of wanted to do anyway, to see if my system could generate such stories. So maybe it's getting to be time to do so.
Hey Folks! Quick update today, as dinner's in the oven and will need tending.
Today was about watching the AI do its thing, and taking notes to see what needs fixing or improvement. The AI is pretty stable now, and what I'm looking for is whether it:
a) makes sense
b) is entertaining to watch
As mentioned way back when, part of this game is about procedural drama and crew management during longer voyages, so the AI needs to do interesting things. So far, it's interesting to watch, but probably not dramatic. More like watching insects. I'm not sure I'm seeing much reason in their behavior apart from initial attempts to satisfy needs. And part of that is because AIs are only planning their next move if they are the ones starting a conversation/interaction.
During the mid-interaction, AIs are just responding in the way that satisfies needs best, without any regard for the feelings or relationship they have with the other AI. This means Abner can chat with Bruce, and Bruce might insult Abner, but if Abner later flirts with Bruce, Bruce may flirt back if he needs intimacy. There's no consideration for the previous negativity. Bruce just needs intimacy, and doesn't care whom from.
That may be tricky to solve. Though, I may already have some data stored by each AI that helps. (Basically, the same data they use to start a conversation/interaction.)
I also noticed a few smaller issues with the AI, now that it's running (mostly) smoothly. AI was accidentally using replies as opening statements in conversations, and this turned out to be a bug in the code that remembers conversation results. It was storing replies as opening statements and then deciding to use the ones it liked.
I also noticed some missing data in a few negative replies, causing AIs to continue trying interactions that resulted in denial. They just weren't getting punished, so didn't care.
I think there's also an issue in there where AI's are allowing themselves to be interacted with too one-sidedly. Basically, Abner keeps starting interactions with Bruce, and Bruce keeps turning him away, and there's no end in sight. This might be the result of that missing denial penalty, but it also might mean AIs need to have a way to bail out.
Finally, I started looking into ways of making AI do more than just say "no" when they are done with another AI. I think it's possible for AIs to pathfind to a new point as part of their reply, which might make for an interesting way to get insulted and walk off. And it might also solve the above problem of not being able to bail out.
Hey Folks! Hope everyone had a good weekend. It was a scorcher here (relatively speaking), and it would appear sweating is in the forecast for the near term. The upstairs semi-attic-office is, shall we say, not the coolest room in the house :)
I managed to make some progress on the AI/container issue I found last week. In the end, I decided to go the bookkeeping route, despite my reservations. It wasn't as hard as I anticipated, and it's the best way to be sure Bruce doesn't agree to do something for Abner if Bruce doesn't have the goods to do it.
As of now, I can see my AI trying out different containers in the ship, and successfully taking items from them. And in some cases, even consuming those items afterward! So I think it's working at a basic level.
Now the question is one of training. This AI would also be at risk of forming some pretty weird ideas about food-gathering. Things like searching toilets for food because it makes them feel productive, or not bothering with fridges after failing to find food in a toilet, because both fridges and toilets are containers and the toilet failed.
The good news is that I don't think this is a coding issue anymore. This is a data issue. My AIs have literally zero experience with the world, so their data points are, like, a list of 2. They'll try 1-2 things, and their world view will consist of 100% those two experiences.
One way I was thinking this could be solved is to just setup some AIs in an area and let them learn over time until some come out acting like relatively normal people. Then, I can save their knowledge as templates and load them into future AIs as a starting point.
The other way would be to manually generate some knowledge, either in the native AI knowledge tree format, or some special hints file that's easier to hand-edit.
Since the former sounds more fun, maybe I'll reward myself by trying that first :)
Hey Folks! I got the food interactions hooked up today, and that revealed a new obstacle: containers.
My first order of business was to get AI to be able to find food again, now that I have this new "learning" system in place. I had to separate the old "SeekFood" into two distinct interactions: one for seeking a food item directly, and one for checking a container for food. And this revealed a problem with my system and containers.
Basically, AI had no way of associating success/failure of their food-seeking with the container it came from.
To solve this, I decided to add a couple of markers to any item that gets transferred during an interaction. This info includes who the original owner was, and what interaction caused the transfer. Then, when the AI later uses that item and gets a benefit (or drawback), they can associate the effects with that original owner+interaction combo, like they do for other direct interactions.
I think this piece is actually working now, after a few hang-ups and bugs were sorted. However, I think I'm noticing a new problem. Two, in fact.
First, my AI gets pretty pessimistic if it fails the first time it checks a container for food. Since that's the only data point it has, future attempts are avoided because the first attempt actually made them feel worse. Oops. I'll have to figure out a way around this.
The second issue is possibly bigger. My containers no longer think they contain anything when asked by the AI for an item. I used to have a hack to get around this problem, but I eventually removed it because it interfered with the AI learning code. The hack basically gave each AI omniscience about the contents of any container.
Instead, the AI shouldn't know what's in a container until checking, and then the container should either allow or deny the AI based on its contents. But the way I've setup items and conditions, there's no handy way for the container to know what it has..
One solution might be to include a loot check when the game is checking for valid interaction targets. If the interaction requires giving/taking loot, the validator would check that the petitioner or granter have the necessary loot. This solution should work, but is a bit brutish and may use lots of cpu.
Another approach is to do something like what I did in NEO Scavenger. That is, items impart certain conditions upon their owners when added to their inventories, and remove those conditions when the item is lost. This way, I could have something like "HasFood" imparted on a container when a food item is added to it, and the interaction filter is already setup to check for necessary conditions.
The down side to both approaches is that loot can sometimes mean a random amount of items, and there would be no way to know what that random number is in advance without saving that info between the query and the actual interaction. That's more bookkeeping than I want.
I think I'll need to do this NEO Scavenger approach anyway, since I some day want items to impart benefits to the owner. Things like warmth, oxygen supply, or social status. But I'll need to think on it a bit.
That's what weekends are for, right? Have a good one, all!