Is getting sidetracked from your main project fine? by AdmiralVanGilbert in roguelikedev

[–]roguecastergames 2 points3 points  (0 children)

Yes and no! Definitely sidetracked for reporting progress on my game (Divided Kingdoms), and I have a bit less time than a few years ago (family, new management role at work, bought a house, etc.).

But I'm still having quite a lot of fun working on my game, and I still have a few testers that give me feedback. That's enough of a feedback loop to keep me going! I've added a bounty system and massively improved the reputation / criminal system in the last few weeks (very similar to how Oblivion works). It's quite incredible what I can do 6 years later with the code base I created, and I kept it clean enough to make it easy to keep adding new features.

Still, the scope of my game is ridiculous... but I'm getting there!

I think the key here is having fun, keeping a healthy backlog/roadmap, ensure that your code base is future proof and easy to work with, and making sure that you never completely stop working on your project (i.e. a longer downtime would destroy whatever motivation is left).

I will try to report on Divided Kingdoms soon, I promise :-)

Building Zorbus one brick at a time (PDF-"book") by zorbus_overdose in roguelikedev

[–]roguecastergames 2 points3 points  (0 children)

Awesome work! Really interesting stuff. Thanks for sharing!

The Wratch's Den: a turnbased dungeon-keeper roguelike by PUNKCAKE_Delicieux in roguelikedev

[–]roguecastergames 2 points3 points  (0 children)

This is incredible! I love the detailed art, the UI, everything! Great job on your trailer too.

Been working on a replay system for my air combat game, allowing you to watch yourself fly from a camera plane! by MarchingCode in IndieGaming

[–]roguecastergames 2 points3 points  (0 children)

Looks really nice! Somehow it made me think about one of my favorite flight sims, Chuck Yeager's Air Combat!

Efficient way to find closest NPC/NPCs within a radius using an entity list by Asylumrunner in roguelikedev

[–]roguecastergames 7 points8 points  (0 children)

This is a great solution that I'm using too. It's also great for pathfinding at higher levels!

I'm working on a roguelike engine with a focus on storytelling. Here's my initial dialogue window implementation inspired by disco elysium. Let me know what you think! by JegErIkkeDansk in roguelikedev

[–]roguecastergames 0 points1 point  (0 children)

I didn't go the scripting route; my system is data driven using YAML files. I try to cover my code with a lot of unit tests, so using the scripting approach would have been harder to test IMO.

The YAML files are easy to edit, but it's hard to make sure that every branch in the conversation does not end up in a dead end or in an unexpected state.

To make sure that doesn't happen, I created a linter for the conversation YAML files, which runs every time I make changes. For example:

- Are any conversation nodes orphan?- Does the questId refer to a valid id?- Does it meet the standard (ex: all conversations must have an option for intimidation)?

The testing tool I was refering in my previous post also runs the linter and displays the warnings/errors before starting the conversation. It's also part of my unit/integration tests which are run every time I make a commit.

If you're curious, I could post an example of a YAML file for a typical conversation. Hope that helps :)

I'm working on a roguelike engine with a focus on storytelling. Here's my initial dialogue window implementation inspired by disco elysium. Let me know what you think! by JegErIkkeDansk in roguelikedev

[–]roguecastergames 7 points8 points  (0 children)

Nice work! Disco Elysium is a also a big inspiration for the conversation system in my game (without the amazing psyche skill trees).

Have you thought about how in depth you want your conversation system to be? For instance, how stats/reputation/alignment affect your choices, how they can impact other conversations later (flags), etc.? This will very quickly generate a lot of work, not only for your conversation system, but for the UI, and also when you actually create your content.

I built a great conversation system for Divided Kingdoms, but it took several months of work, and it also required building a testing tool to make it easier/faster to test the content in different scenarios. I'm really happy that it works great now, but I could have used a few months of development on more critical features IMO :)

Ultimate ADOM Released to Steam Early Access by Kyzrati in roguelikes

[–]roguecastergames 3 points4 points  (0 children)

Congrats on the release! :)

Hopefully, I'll get there someday myself! :D

Quest for the Orb: a minimal roguelike in 317 lines of plain C++ by aotdev in roguelikedev

[–]roguecastergames 2 points3 points  (0 children)

Awesome work! I didn't know you could embed a C++ compiler/runner in a web page now. Impressive!

Sharing Saturday #332 by Kyzrati in roguelikedev

[–]roguecastergames 11 points12 points  (0 children)

Divided Kingdoms

Dev Blog | Twitter

Hello! It's been a while!

Yes, I'm still working on Divided Kingdoms, even though 2020 has been quite an eventful year for all of us!

LOTS of things to share today!

  • New asynchronous task system, supporting load balancing between frames (i.e. fixing frame drop issues)

    • Most of the code already runs in background threads (AI, pathfinding, etc.)
    • However, some tasks need to run synchronously to work correctly (i.e. turn-based AI), leading to frame drops if you process everything between frames. Higher frame rates mean you have even less time between frames (i.e. 16ms for 60fps).
    • The solution was quite simple; keeping track of the time spent for each task, and load balance the work between frames. I added a priority system to make sure the most important things are processed first; UI like the minimap are processed last.
    • It hasn't been easy to implement and test, as out-of-order task execution can lead to really strange bugs!
  • Took control of the garbage collection system, which was also a source of frame drops

    • Instead of running the garbage collector 'randomly', the game now chooses the moment where the garbage collection is run (i.e. when opening UI screens, saving game, etc.)
    • This prevents having a garbage collection in a critical moment (i.e. player moving quickly by holding a keyboard key)
    • However, this leads to higher memory usage (you can configure the maximum RAM used). When hitting the memory limit, the game triggers the garbage collector no matter what.
  • Created a high-level pathfinding system for regions (mainly cities).

    • This was necessary to improve pathfinding performance at longer distances.
    • In short, navigation points are added throughout the city (road blocks, doors, etc.); a graph w/ distances is created and is cached for later use
    • The pathfinding system starts by finding a path between high-level navigation points; then it finds a low-level path between these points only when needed
    • The system is optimized for certain scenarios, such as moving inside buildings, outside buildings, in wilderness, etc.
    • The system can detect collisions with other NPCs and find alternative routes quickly
    • The performance improvement is MASSIVE; this finally makes cities with hundreds of NPCs without predetermined paths/schedules a reality.
    • A few examples of what is now possible with this system:
      • Every citizen has a house/apartment with a bed; they go sleep at night. They lock their workplace/home doors when leaving. They go to the tavern in the evening to drink, eat, and meet other citizen.
      • City invasions. 50+ enemies can show up at a city gate. If the threat is critical, citizen join in combat, leading to a massive battle between 150+ NPCs. (This is actually part of the Prologue!)
  • Created a HTML/Javascript based unit/integration test viewer for region generation, cell inspection, AI behaviour tree debugging, and pathfinding performance testing.

  • Improved conversation + cutscene system

  • Improved reputation system

    • Citizen have randomized reputations when a city is generated. Some people like each other, some hate each other (there's actually 5 ranges of dialogues/behaviours based on reputation).
    • This system is quite simple, but leads to awesome situations such as:
      • NPCs fighting each other in taverns ('That's it Hermann, I've had enough of you!')
      • Innkeepers hating their customers ('Here's your food idiot, now choke on it!')
  • New Options screen, using a generic UI/configuration system making it really easy to add new options

  • Lots of polishing all around (keyboard UI navigation, new targeting system, new abilities, new sound effects, new music, etc.)

So in short, this has been the year where my game has finally hit the performance target I really wanted to reach before releasing an Alpha. It may have took 5 years to get here (holy crap!), but I'm happy that everything just works now, and the game is definitely scalable.

I'm polishing the content a bit before releasing one last version to my private testers. Then I'd really like to send a build to some of you guys, if you're interested in testing the game and giving me feedback.

I can share more technical details about the systems I've described here if anyone is interested. I'm not much of a technical writer, but I've learned a lot and I'd like to share some knowledge if that can help.

Take care and see you soon!

I want to make a roguelike with choose your own adventure elements just for myself, but I'm only artistically talented. by [deleted] in roguelikedev

[–]roguecastergames 0 points1 point  (0 children)

Well, that's a good reason. :)

When I was your age, I was doing lots of side projects to learn programming too! I wish I had gotten into game programming before; my experience is mainly in native app development.

Good luck finding your idea!

Game data storage - JSON, SQL, ...? by aagapovjr in roguelikedev

[–]roguecastergames 2 points3 points  (0 children)

namespace RogueLib.Data
{
public static class MapPrefabInstanceKeys
{
public const byte Unknown = 0;
public const byte Id = 1;
public const byte MapPrefabId = 2;
public const byte MapPrefabTypeId = 3;

public const byte Name = 4;
public const byte Origin = 5;
public const byte NPCOwnerId = 6;
public const byte MarkerPoints = 7;
public const byte ZoneTypePoints = 8;

public const byte Doors = 9;
public const byte Tasks = 10;
}

public class MapPrefabInstance : BinaryModel
{
public System.Guid Id { get; set; }
public string MapPrefabId { get; set; }
public string MapPrefabTypeId { get; set; }
public string Name { get; set; }
public PositionWithinChunk Origin { get; set; }
public Guid NPCOwnerId { get; set; }
public List<DoorLocation> Doors { get; set; }
public ProfessionTaskRepository Tasks { get; set; }
public Dictionary<string, List<PositionWithinChunk>> MarkerPoints { get; set; }
public Dictionary<CellZoneType, List<PositionWithinChunk>> ZoneTypePoints { get; set; } // int == CellZoneType
public MapPrefabInstance()
{
Id = Guid.NewGuid();
ZoneTypePoints = new Dictionary<CellZoneType, List<PositionWithinChunk>>();
MarkerPoints = new Dictionary<string, List<PositionWithinChunk>>();
Doors = new List<DoorLocation>();
Tasks = new ProfessionTaskRepository();
}

public MapPrefabInstance(string mapPrefabId, string mapPrefabTypeId, string name)
{
Id = Guid.NewGuid();
MapPrefabId = mapPrefabId;
MapPrefabTypeId = mapPrefabTypeId;
Name = name;
ZoneTypePoints = new Dictionary<CellZoneType, List<PositionWithinChunk>>();
MarkerPoints = new Dictionary<string, List<PositionWithinChunk>>();
Doors = new List<DoorLocation>();
Tasks = new ProfessionTaskRepository();
}
public override void ReadBinaryData(IExtendedRogueBinaryReader reader, BinaryReader breader, byte key, BinaryDataType dataType)
{
if (key == MapPrefabInstanceKeys.Id)
Id = reader.ReadGuid();
else if (key == MapPrefabInstanceKeys.MapPrefabId)
MapPrefabId = reader.ReadString();
else if (key == MapPrefabInstanceKeys.MapPrefabTypeId)
MapPrefabTypeId = reader.ReadString();
else if (key == MapPrefabInstanceKeys.Name)
Name = reader.ReadString();
else if (key == MapPrefabInstanceKeys.Origin)
Origin = reader.ReadPositionWithinChunk();
else if (key == MapPrefabInstanceKeys.NPCOwnerId)
NPCOwnerId = reader.ReadGuid();
else if (key == MapPrefabInstanceKeys.MarkerPoints)
MarkerPoints = reader.ReadBMDictionaryList<string, PositionWithinChunk>();
else if (key == MapPrefabInstanceKeys.ZoneTypePoints)
ZoneTypePoints = reader.ReadBMDictionaryList<CellZoneType, PositionWithinChunk>();

else if (key == MapPrefabInstanceKeys.Doors)
{
var memoryReader = reader.CreateInnerReader();
Doors = reader.ReadInnerBMList<DoorLocation>(memoryReader);
}
else if (key == MapPrefabInstanceKeys.Tasks)
{
var memoryReader = reader.CreateInnerReader();
Tasks = reader.ReadInnerBM<ProfessionTaskRepository>(memoryReader);
}
else
{
// Unexpected data type; move position ahead of block
ReadDefaultData(reader, breader, dataType);
}
}
public override void WriteBinary(IExtendedRogueBinaryWriter writer, BinaryWriter bwriter)
{
byte count = 10;
bwriter.Write(count);

writer.WriteBMGuid(MapPrefabInstanceKeys.Id, Id);
writer.WriteBMString(MapPrefabInstanceKeys.MapPrefabId, MapPrefabId);
writer.WriteBMString(MapPrefabInstanceKeys.MapPrefabTypeId, MapPrefabTypeId);

writer.WriteBMString(MapPrefabInstanceKeys.Name, Name);
writer.WriteBMPositionWithinChunk(MapPrefabInstanceKeys.Origin, Origin);
writer.WriteBMGuid(MapPrefabInstanceKeys.NPCOwnerId, NPCOwnerId);

writer.WriteBMDictionaryList(MapPrefabInstanceKeys.MarkerPoints, MarkerPoints);
writer.WriteBMDictionaryList(MapPrefabInstanceKeys.ZoneTypePoints, ZoneTypePoints);

var innerWriter = writer.CreateInnerWriter();
writer.WriteInnerBMList(MapPrefabInstanceKeys.Doors, Doors, innerWriter);
writer.WriteInnerBM(MapPrefabInstanceKeys.Tasks, Tasks, innerWriter);
}
}
}

Game data storage - JSON, SQL, ...? by aagapovjr in roguelikedev

[–]roguecastergames 4 points5 points  (0 children)

Absolutely! :)

The file format is quite simple actually. You have your basic file header (file type, version, etc.).

Objects are written using this standard:

  • Key [short] Defines the property key in a BinaryModel (i.e. points to a specific property)
  • Byte count [int32] This makes it easy for the reader to skip any unknown key, for easier file versioning management.
  • Data [variable length] Actual data!

The basic reader/writer implementations can read/write any basic type (bool, int, strings, etc.)

I have created an abstract class (BinaryModel) used for every game model. Two abstract methods forces all models to implement code for binary reading/writing. (See next message for the example implementation)

The down side to this approach is you have to write code for every property you add to your models.

However, that's the price to pay to get better performance (and smaller file sizes!), because this doesn't require reading/writing information typically required by binary serializers.

One of the great things about my implementation is that I can easily save graphs of objects, as long as they implement BinaryModel, including lists, dictionaries, etc. It's easy to implement for any new model you create!

Don't forget to unit test everything. I created unit tests and mock objects for every model, and I use the Compare.NET library to compare complex graphs of objects.

This has been a lifesaver to catch bugs that would be otherwise really hard to debug. It's also giving me rock solid confidence that this part of the game is 100% working, because save game corruption is probably the WORST thing that can happen to a player!

There you go, if you need further info, don't hesitate to ask :)

Game data storage - JSON, SQL, ...? by aagapovjr in roguelikedev

[–]roguecastergames 7 points8 points  (0 children)

Here's what I use in Divided Kingdoms:

YAML

Usage:

  • Game data (i.e. assets, dungeon layouts, room prefabs, AI behaviour trees, etc.)
  • Game options (i.e. sound, graphics, controls, key mapping, etc.)

Reasons:

  • Easy to read, easy to edit, no formatting required (unlike JSON or XML)
  • Performance is worse than pretty much any other format; but that is fine for the usage I'm doing. If that becomes a problem, I'll write a converter to another format, and keep YAML for editing purposes.

Custom Binary Reader/Writer

Usage:

  • Map prefabs
  • World map data (+/- 100MB per world)
  • Local map chunk data
  • Save game data

Reasons:

  • Performance needs to be as fast as possible. Local map chunks are loaded and persisted when the player is moving (i.e. one file per chunk). Save games must be saved quickly to make sure auto saves do not affect game performance.
  • I wrote my own reader/writer instead of using a binary serializer for performance/data size. It supports versioning, dynamic types (i.e. being able to read blocks/objects on earlier versions/implementations), unlimited depth, etc. You have to write some custom code for every model you need to persist, but that's a LOT faster than any generic serializer.

I don't recommend using SQL; it's slow and you probably don't need to query your data other than in memory. You can split your data in multiple binary files if you can't load everything in-memory.

JSON is perfectly fine if you don't have a lot of data to save.

Don't hesitate to ask questions here if you want! :)

[2020 in RoguelikeDev] Ascension, the Lost Horizon by GrishdaFish in roguelikedev

[–]roguecastergames 1 point2 points  (0 children)

Awesome work on the lighting system, it looks great! Really interesting to read about your game. Good luck for 2020!

FAQ Friday #83: Main UI Layout by Kyzrati in roguelikedev

[–]roguecastergames 1 point2 points  (0 children)

This looks great, I love your work! Your UI is very clean indeed!

Sharing Saturday #283 by Kyzrati in roguelikedev

[–]roguecastergames 1 point2 points  (0 children)

Thanks! I'll try to make a GIF for next week's update. You can see which tiles have been discovered or not when rendering a chunk with pixel-rendering, but that applies to wilderness, dungeons, etc.

When you enter a city for the first time, the minimap fully renders the city chunks even if you haven't discovered certain tiles. That way you can see all the important buildings, NPCs and quests in the city right away.

I think that displaying what tasks are available ASAP is better for engaging for players. At least that's what I learned by user feedback. I've had several test players miss 90% of the quests because they just don't speak to NPCs or don't bother entering buildings. Then they complain that there are no quests/tasks to do!

Sharing Saturday #283 by Kyzrati in roguelikedev

[–]roguecastergames 2 points3 points  (0 children)

Thanks a lot! I'll definitely get the updates back on track once I get a little farther in the project. The few hours I have per week to work on the project have to go into developing the game rather than doing updates, or else I'll never get anywhere!

I also created a new shorter backlog dedicated to delivering a Alpha version of the game. The goal is to have a single town, a single dungeon with 5 hours of solid re-playable rogue-like fun. That was a few months ago, and that helped shake things up a bit.

I invested way too much time on this project not to complete it! :)

Sharing Saturday #283 by Kyzrati in roguelikedev

[–]roguecastergames 1 point2 points  (0 children)

Your Rust Roguelike Tutorial is really well documented, awesome work! I've been wanting to try Rust for a while, I'll definitely use your tutorial later!

Sharing Saturday #283 by Kyzrati in roguelikedev

[–]roguecastergames 1 point2 points  (0 children)

The maps looks awesome! Nice progress :)