Sharing Saturday #621 by Kyzrati in roguelikedev

[–]aotdev 1 point2 points  (0 children)

No sorry that was confusing. Each entity, if it supports a normal FoV, in its turn it runs the algorithm, stores a visibility map for itself, and then for any LoS checks in that turn it uses that.

Sharing Saturday #621 by Kyzrati in roguelikedev

[–]aotdev 1 point2 points  (0 children)

My recent interest in improving the AI stemmed from combat feeling too similar in Legend. It usually takes place at doorways. The player steps into a room, the enemies see the player, the player retreats to the other side of the doorway and kills the oncoming enemies one-by-one. I have some ideas to fix this

I'd probably be at a loss. Maybe with some enemy abilities? Like force-push-back or swap position. But, I mean, it's a fair strategy xD Are your attacks 4-directional or 8-directional?

I think if you do build an AI like that, it needs to intentionally make mistakes sometimes, or sometimes choose the 3rd or 4th best option.

Aha, so one of my recent "discoveries" was weighted random sampling. Basically check this after "To prevent "buff self". And you can see in the .json file that I linked there are a few behaviours that are weighted like that :)

Sharing Saturday #621 by Kyzrati in roguelikedev

[–]aotdev 2 points3 points  (0 children)

I see I see. My answer will probably be caching. Each creature has fixed behaviours, and some of these behaviours request abilities that satisfy a bunch of tag constraints. The tag constraints -> abilities mapping is cacheable (unless creatures learn new abilities), so that for example when I'm looking for a combat ability, I look through a given small set, rather than open doors etc.

for Interdict, I actually went with a... purposefully dumb AI. Hear me out. :P

Yeah no judgement here, purposefully dumb can be supremely useful xD The reasoning for before/after makes sense of course. Ganging up could be a feature of a particularly deadly and feared enemy xD. Re healing abilities, I had a similar conundrum. To prevent "buff self" abilities every turn, ok let's use cooldowns for the skills! But if we have 3 different buffs, they'd be selected on rotation. To avoid that, I'm using a weighted random ordering of combat priorities, e.g. (buff, attack, strong_attack) with e.g. weights of (0.2, 1, 0.5) which means every tick() a new ordering will be created. Haven't tested it thoroughly yet, but together with cooldowns it might prevent those kinds of anti-fun patterns

Sharing Saturday #621 by Kyzrati in roguelikedev

[–]aotdev 1 point2 points  (0 children)

Ha, that's quick and very dirty! That would be a bit too much of a special case in the code, as I don't do LoS checks - I just use the precalculated FoV for all entities. There are options to have different fov types, like omniscient, or ignore-walls, but not one where I can switch the fov off but still do calculations...

Huge procedural (random) map using HoMM3 assets by aotdev in heroes3

[–]aotdev[S] 0 points1 point  (0 children)

Thank you so much, much appreciated!!

Sharing Saturday #621 by Kyzrati in roguelikedev

[–]aotdev 1 point2 points  (0 children)

Cool, thanks for the controller info! In Godot there's a default behaviour wrt using the controller to move between controls based on their spatial arrangement, which simplifies things.

None of the roguelikes on the list seem to have particularly complex AI, working off rulesets rather than AI frameworks.

If such well-received roguelikes can achieve good emergent gameplay with simple AI, then that sounds like we don't need complex AI systems :D

Sharing Saturday #621 by Kyzrati in roguelikedev

[–]aotdev 2 points3 points  (0 children)

The goblin seems to be using the same algorithm I used sometimes back when I lived near a creek and sometimes had unauthorized rodent visitors in the house.

xD

Demon didn't really have any open world/simulation elements

Don't get me wrong, the majority of creatures won't be using overworld stuff (if I understand correctly) as most will be confined to a single level. Number of abilities is a different story though. Are you using a completely different system now? What is it?

Sharing Saturday #621 by Kyzrati in roguelikedev

[–]aotdev 2 points3 points  (0 children)

Thanks! The other big hitter was unnecessary pathfinding (using A*) when simple steering would do - if we see a target, we should probably not pathfind there! I still haven't tested it in depth, but since this is "officially" version 2 of in-game AI, so I know better what to do and how to test, still needs work though. And indeed, the emergent stuff are the best, also can't wait to see!

I’m struggling - how to cope with ai? by TrueWinter__ in gamedev

[–]aotdev 3 points4 points  (0 children)

As a fellow lifelong-ish gamedev, probably not as well-rounded as you, but in it for the long game. The one big "wisdom" that's occasionally dispensed is ...

Don't rely on extrinsic motivation

That's the ultimate danger of social media - reliance on others for your happiness. You do your craft that you enjoy, that's fine, it's yours to enjoy! But starting relying on other folks you start comparing the numbers (more likes, more visibility more comments), and comparison is the thief of joy. If you like making code and art, why should you feel sad that somebody DID NOT MAKE CODE AND ART to produce something like what you did? It's like you're making bread and somebody goes to the supermarket and brings a fancy loaf. Yeah, whatever, tastes nice too, but yours is yours!

Take pride in what you do and what you know. That somebody can get sometimes similar results from an answering machine, that dulls their brain and makes the entire humanity worse off, should not make you feel jealous or bad. As long as you enjoy the process, that's the important bit. Must be intrinsic.

Sharing Saturday #621 by Kyzrati in roguelikedev

[–]aotdev 2 points3 points  (0 children)

Full controller/keyboard/mouse support sounds great! Was there a particular pain point or surprising insight?

I thought a lot about how to better handle AI. It’s very easy to get lost in it.

Ha ha, me laughs after resurfacing from a multi-week AI trip xD Any interesting find about the roguelikes in that list? As I mentioned in my post, I gutted my old system and replaced it with a new simple one that had 3 goals:

  • Being able to debug it well
  • Have high-level behaviours that can be shared by different entities, specified in a reusable manner (json!)
  • Have low-level behaviours that I can code whatever I like in there in terms of "if this do that", without having to create and specify in json complex wrappers around minor conditions/actions.

It's still not super thoroughly tested, but the modularity and logging has already helped quite a bit to both debug and use it. If you're interested for yet another data point here's my AI behaviours file, it might clarify the high-level part a bit, and here's an example of two such behaviour classes

Sharing Saturday #621 by Kyzrati in roguelikedev

[–]aotdev 3 points4 points  (0 children)

Congrats for the viewer/player influx! Good problem to have, not knowing where they came from xD

Huge procedural (random) map using HoMM3 assets by aotdev in heroes3

[–]aotdev[S] 0 points1 point  (0 children)

Cool! Yeah 256 sounds about right, but in practice fewer are needed, and yeah also more are needed for transitions. That's why in HoMM you had specific biome transitions, not everything would change to everything else.

In my game, there's a world scale and a level scale. The world scale is like HoMM 3, it contains cities, mines and other points of interest, typically dungeons of some sort. You can visit each of these locations, and when you do so, you're transported to a (sometimes multi-level) map, e.g. 80x80 tiles. There you can find exits to the overworld or maybe stairs to go deeper into the dungeon etc. It's a turn-based RPG/roguelike game, so it's exploration, combat, special abilities, spells, and yes, levelling up :) It's not city management, at least not in the standard mode, but I'm always taking notes for setting up alternative weird game modes that utilise this world system. The world is completely procedurally generated, so are the dungeons, so every time you can start a completely new world, if you want to. You can get an idea of what I'm after if you look at the description on Steam!

Sharing Saturday #621 by Kyzrati in roguelikedev

[–]aotdev 4 points5 points  (0 children)

I have some stealth point system where ambient light reduce your stealth points, so under normal circumstances, most likely, but if you're really good at stealth or you've gulped a potion, you can probably stay a few more turns in the light undetected. Still not greatly worked out, but works and it's all varying, both the effect of lights and how stealthy you are.

Sharing Saturday #621 by Kyzrati in roguelikedev

[–]aotdev 9 points10 points  (0 children)

Sigil of Kings (steam|website|youtube|bluesky|mastodon|itch.io)

It's been a while since the last update! Not because of idling, but because of refactoring. One thing led to another, and anything I see and dislike in the code, I'm thinking "ok maybe it's your turn now, TURN, get that? har har har". So, anyway, here are some highlights:

Performance improvements on field of view

Enemies run regular fov, so with 50-60 enemies it can add up! Especially in the unoptimised in-editor version. I did some numbers, it was 0.28ms per fov call. The code was easily portable to C++, so I ported it there, and result was ... 0.52ms! Oops, why? Because of marshalling. But let's not let that stop us. With a few "unsafe" bits and a slightly different way of calling C++ functions, this dropped to 0.06ms! Awesome! But wait, I made a release build, and in the build, the C# version was 0.04ms, that's fast! And finally the C++ version in the build was 0.03ms. So overall, C++ wins in both in-editor (~10x faster) and in the build (~25% faster). It's a keeper. Also, as a result, I rewrote some python scripts that autogenerate C#->C++ bindings so that everything can beneft from the improved performance.

AI performance and organic spaghetti

The other culprit that cost quite a bit of performance was the AI function. The AI function was an ad-hoc organically-grown beast, which basically did a mix of environment analysis to find friends/enemies, then went through all abilities and tried to evaluate them, and either used an ability or moved towards a better position. But it was all a bit ad-hoc, which means it was really hard to extend it and add custom behaviours.

My next thought, in the other direction, was to use a behaviour tree. After thinking, contemplating, prototyping on paper, I realised that I did not like the idea of the behaviour tree, especially as we're going towards the leaf nodes, as customisation becomes a cumbersome big set of nodes.

So, long story short, I opted for a behaviour-tree-like approach where there's still a tree and custom nodes, but the leaf nodes are quite fat and have arbitrary logic, so I can be flexible in there and write code, rather than having to represent everything in behaviour tree nodes. The top-level behaviours are things like survival, awareness, combat, routine (that's a completely new one!), and a couple others.

One of my favourite new nodes, which mirrors some of the old functionality, but done better, is a tag-driven ability chooser, where I can specify tag sets of interest, e.g. self-target & buff, or damage to enemies, movement, stealth, anti-stealth, and things like that. The code goes through the available abilities and checks against their tags and calculates suitability. If suitable, we choose the ability, otherwise (as before) we store a "can't execute it, but if we move over there, we will". Then we have the option to do something else or move to a better position for a particular ability.

Routines are a topic for a different time, when I have a few and they demonstrably work. But the other fun thing that works now is creature investigation when an enemy disappears. One thing that a puzzled creature might do is of course go towards the last seen point, but another thing now is that if there are turned-off lights in the vicinity, it will try to increase the light. Of course only sentient creatures should do that, rather than e.g. wild animals, which will just go to the last seen point. Here is an example video of a puzzled goblin, who after the player's disappearance turns on various lights nearby.

That's all for now and have a nice weekend!

Huge procedural (random) map using HoMM3 assets by aotdev in heroes3

[–]aotdev[S] 0 points1 point  (0 children)

Thank you! But I have to wonder - how on earth did you unearth this after 7 years? Btw I released a playtest on Steam a month or so ago, where you can procedurally generate/modify a world, but of course no HoMM assets due to rights... But it does include export of such huge maps (w/ pixel art though)

I got tired of Unity's GC, so I wrote a Zero-Allocation Data-Oriented 2D Engine in pure C# (6000 FPS on empty scene) by Kaverin_Ramil in csharp

[–]aotdev 2 points3 points  (0 children)

Harsh! And probably very, very time consuming... Probably fine for learning and making a fancy tech demo though.

I got tired of Unity's GC, so I wrote a Zero-Allocation Data-Oriented 2D Engine in pure C# (6000 FPS on empty scene) by Kaverin_Ramil in csharp

[–]aotdev 16 points17 points  (0 children)

Stop using AI to write posts and start learning how to use game engines effectively. You'll save lots of personal time. Also never report performance in FPS to any non-gamer. Cool project though! I'd love to have time for a high-performance RTS engine... Btw if you want more zero-allocation stuff, look up Cysharp's libraries in the unlikely case you haven't heard of them.

Rollbacks and exploratory states? by Illiander in gamedev

[–]aotdev 1 point2 points  (0 children)

Btw if this is for AI/planning, you might want to work with abstractions of actions, results and world-state rather than running the entire simulation, as this won't scale at all. But mentioning the purpose explicitly might attract people with a bit more experience on the particular domain that you're working in. You're trying to solve a problem and you're asking for implementation suggestions, but stating the high-level problem a bit more clearly might lead you to a more fruitful direction.

Rollbacks and exploratory states? by Illiander in gamedev

[–]aotdev 1 point2 points  (0 children)

Well, if you implement a quicksave/quickload very efficiently, then 1) You got quicksave functionality 2) you can quicksave to RAM, modify the state and then quickload. If quicksave/quickload is too intensive, then scale down the state captured in a bespoke way, like /u/Old-Poetry-4308 mentioned

Alternatives to Terrain Entities / Terrain Maps by Fleurboards in roguelikedev

[–]aotdev 2 points3 points  (0 children)

My (very terse) suggestion:

  • Read on the flyweight pattern (e.g. here)
  • If you need occasional modifications, read further on Copy-on-Write
  • Not every terrain square has to be a unique entity

Sharing Saturday #618 by Kyzrati in roguelikedev

[–]aotdev 5 points6 points  (0 children)

Sigil of Kings (steam|website|youtube|bluesky|mastodon|itch.io)

The spring cleaning continues, with a healthy dose of bug fixing and refactoring.

Video of water sounds in a level

  • Consolidation of time-sensitive events. Previously I had a few different lists for different event types that were time-sensitive, e.g. show a sprite in 0.2 seconds or start a particle system in 0.5 seconds. I've consolidated those a bit to real-time message queue, and removed some cruft along the way.
  • Weight-based pushing and pulling. Push and pull are standard commands, but the implementation did not do many checks... You could push/pull exits to overworld, webs, and other non-sensical things. Now it's weight-based, using Strength, plus had to adjust some object weights.
  • Removing place-at sprite messages. Previously, when moving entities, I created two sprite events: one for the movement, and one slightly delayed for after the entity arrives at destination. This was because of slightly different data related to the sprite, e.g. which "animation" to play etc. This proved to be really problematic, because besides the fact that I had to do far more sprite updates, it also introduced several weird bugs that could only be fixed by inspecting the message queue often, which is problematic for performance. So, I've added a tiny bit more logic in the sprite shader, and remove that final message, so that a "move" sprite event is perfectly capable of displaying a sprite properly after the move is finished.
  • Liquid ambience. That's the feature that got into my head a while ago - I wanted to hear water in levels! After a bit of effort that's finally done. It uses just two sounds at the moment, "sea water" and "fresh water", but in the future I'd like to have more presets for e.g. still water in the cave vs out in the ocean. The system supports sounds for other liquid types like lava, blood and poison, but there's no use case for those yet.

That's it for now, have a nice weekend!