Shattered Islands - DevLog

DevLog#1 - Too much things to say

Hello everyone, a few months ago, with my friend we started to dev a multiplayer game. It’s yet another autobattler, but this one is our own, and I hope this one might be different.

8 players fight each other for a dozen rounds. The twist is that each round, you might fight a 1v1, 2v2, 4v4 or some other combinations. Your last turn allies might be your future enemies. But in the end, only one player will be the winner.

A absurd lore for an twisted game
After a cataclysm shattered the floating continent into countless islands, war was banned to prevent further ruin. Instead, all conflicts are now settled in brutal tavern-hosted battles known as the Death Games. From shady dives to grand halls, taverns have become the arenas where glory, gold, and power are won—or lost. Adventurers flock to these chaotic brawls, each seeking to carve their name into legend. In the Shattered Islands, the tavern is the battlefield.

Small disclaimer
My mate and I aren’t artists and don’t pretend to be. I (Breep) myself am a dev. It’s been more than 10 years that some people pay me to develop things for them so I think I can claim that title. In this project I’m in charge of the dev and game design. My mate (Twiins) is in charge of VFX, 3D assets and game design. All that speech to say, for the moment, we use a lot of AI to modelize our 3D assets. In the future, when we will have a playable game, we plan to find an artist to work with.

The gameplay
Each round is divided in 2 phases, first you buy upgrades for your hero, then you fight against one or several other heroes.

Buying phase
During this phase, to make it easy (it’s not so much), you buy upgrades for your hero !
What changes with other autobattler is that during this phase you are not alone, you see other players in the tavern with you. Also, you can interact with them :slight_smile:


Fighting phase
During this phase, all heroes automatically fight each other. This phase is divided into several sub phases. First you have some time to place your hero on your side (a fog of war hides the other sides). Then the player doesn’t control anything and the fight happens automatically. At the end of this phase, loser players take damage according to winners’ damages.

A short history
This project is already a few months old, so a lot of code has been written, and a lot has been deleted. The project keeps changing with the time, also I learned a lot the last months so I discover other ways to do the same things but better/easier/cleaner so that changes the code.

What is done
Card shop is on the server side, all client can buy cards from this shop
Card shop visual on the buying area: you can see how many cards of each type remain on the bar
Coin piles: you can see how much gold each player has on the table in front of their hero
Card shop UI: you can buy cards
FightingArea: characters from differents team fight each other
Basic behaviour of bots

BUT
A lot of things have to change. As the project is getting old, we are more aware of the challenges, the main challenge being the multiplayer. So my friend and I changed our vision of this game. First it was possible to move during the fighting phase, the code design was: the server runs the fight on its side, receives demand for movement from players and replicates to all other players if ok. This made the server send a lot of information to the players constantly (quite a normal behaviour if the game was a real time multiplayer but it was hard to handle, also the game design was not well finished and we decided to remove this movement feature. So now, I can code a real autobattler, which means that I only need to send the same random seed to all my players and they can generate the same battle on their side. But this also brings some challenges, the main being to have a deterministic code.

This week devlog
Anyway, this challenge, I will work on it later (very soon). First, let’s talk about this week’s job.
This week, I changed all my Character Nodes. The main issue was that my Character node was not very easy to use for testing and also configuring. It was generic, which is good, but also increased the complexity of the code. So I changed it, now all my characters (heroes) are now independent, they inherit from the Character class which inherit from the Entity class. They can be instantiated easily by drag and drop the node on my scenes so I can test them easily. Also it will be easier to write some specific code for each hero this way.

Then

Now

I also created a scene PrototypingMap to be able to test directly my heroes and cards without having to launch a full game (connect clients or bot, find the cards inside the card shop, wait for fighting phase to start). In the inspector on the right you can see that I can select cards for each player deck.

I am starting to write my devlog waaaay too late on the project so I have a lot of things to say and I can’t tell everything in one post because it would obviously be way too long. But, if you are interested, I can present you how I made my TargetManager (how all character can choose their target) or how the card shop work in multiplayer for example, or anything you want :slightly_smiling_face:

I hope next week I will present how I successfully made my game deterministic (or how I failed to :o)

xoxo

Breep

5 Likes

DevLog#2 - Spaghettification

When you work on a project, you often have to make this choice, going fast and getting shit done, or taking hours of doing a nice clean reusable code. I know it is a very debatable subject, but I think both are good. If I am not 100% sure of the global vision of my code for some subject, I often choose the fast way to try things, then I rework directly. But as you know, we all have temporary lines of code that are sitting strong for months. In this game. I was most of the time going blind, trying things because I didn’t have a clear global picture of what I am doing. Anyway, all that speech for trying to excuse myself for writing such shitty code :stuck_out_tongue:
This week, I was supposed to look at the “deterministic” way of coding my autobattler. For that I wanted to use my PrototypingScene, but I thought it is an ugly Scene so wanted to make it an Island. But then I tried my FightingScene in the PrototypingScene but I noticed that for my FightingScene to work I had to launch my game from the start to initiate the right things. I puked a little, then I read my code and puked again. I didn’t know I could write such a spaghetti code (even worst, it was a ninja spaghetti code with hidden reference needed everywhere), but I have to admit that I fucked up there. Anyway, this week, it was rework time.

My objective: I want my Scenes to be as independent as they can be and most importantly I want to be able to test my Scenes alone using F6. Finally, I want explicit dependence with export variables when needed.

FloatingIsland
I created a new Scene that holds a Map (Visual of the Island) and a CenterPointCamera (Camera that rotate)

  • BuyingIsland
    Children of the FloatingIsland, add a BuyingArea inside
  • FightingIsland
    Children of the FloatingIsland, add a FightingArea inside

FightingScene
I made my FightingArea independent of the rest of the game, they can start a fight with FightingArea.start() function that takes an array of characters and a battle type for parameters and that’s it. The fighting area will spawn characters on the spawn points, then configure the characters so they can spawn entities and projectiles using the right spawn manager embedded in the fighting area.

PrototypingScene
My prototyping scene now use my FloatingIslands and FightingArea


SceneSwitcher
An easy Scene that reads player input to make a FloatinIsland active and all others inactive. (Make you switch camera, change UI and later change the aspect of the BuyingArea)

HeroSelector
I removed the hero selection code from the BuyingArea and made an independant Scene for it.

Characters
I changed my character scene because, even if I like the idea of being able to just drag and drop my heroes on a map to test them, I was doing the same stuff (copy pasting also) on each character scene so I factorised a little bit of logic.
I changed some logic so they don’t try too much crazy things on ready, but they need reference to the fighting_area they are on toi me able to fight, I can’t see for the moment how to make them independent of that.

By the way what I mean by dependency is that for example. Here my auto-attack module, which is responsable for triggering an auto-attack needs to know where to spawn the projectile (SpawnPoint), also it needs to know what stats to apply on the projectile (AttackManager) and finally it needs to be able to do an attack (ActionModule).

Game
I can just launch by Game Scene even with no players just to see what it looks like (not much but you can get some idea of the future visual)

I hope to be able to present better things next week. In the meantime, don’t hesitate to ask me any question about this project :slightly_smiling_face:

xoxo

Breep

3 Likes

DevLog#3 - Pick and Collect

What a time to be alive, to be able to pick and drop things on your game…
Well, I’m not shitting you, I feel awkward this week. Truly, it felt like I climbed a mountain, but the result is, at most, a tiny hill. But this hill brings me some very useful self made tools. Selecting things in £D is not that obvious so I’m quite happy about this

2 modules that you can add on any RigidBody/StaticBody:
ClickableModule: Make the owner react when the mouse over it. By default, it shines when focused/unfocused. Also when clicked it triggers a selected callback and an unselected callback when clicked elsewhere.
PickableModule: Make the owner able to be picked and dropped.

1 module that makes your mouse react with the the above modules:
ClickerModule: Need to be attached to a camera, make the camera raycast in front of it and react with the two above modules

2 modules to add constraints to the PickableModule drop check:
PickableCollider: Triggers an overlapped signal when 1 or more other PickableCollider collide. Also show itself when it happens.
SpawnAreaChecker: Tells if the owner is on a specific SpawnArea, also tells if it overlapped onto another SpawnArea.

All the default behaviour can be overridden by the owner if using the correct functions. This makes these modules more flexible.

Also I added a quick debug boolean in the ClickerModule to help visualise the raycast and hit point.

Here are some demos:



What do you think of this ? :slight_smile:

xoxo

Breep

4 Likes

DevLog #4 - GameLoop

This week I worked on making the game loop work again for a game with bots only. I launch only one instance of the game so there is no multiplayer constraint and I want a game to be able to run from beginning to end.

Also, I reorganised all my project folders. I started my organisation following some others I saw on tutorials but I wonder why so many people organise their files per type instead of logics. My files were organised by type also before (.gd in a Script folder, .tscn in a Scene folder) but it’s not a working way of doing things. I don’t know what you think about it, but I find it better this way.

Some ideas I’m quite proud of and think it’s a little bit cool

  • A pile of coin in front of the players on the table show how much money the player has (so you can see which one is saving money to gain interest and adapt your strategy)
  • Piles of cards on the counter show how much cards of each family remains so you can adapt your strategy
  • You can see other fights from your island in the background



We also slightly improved the visual of a fighting island.

Here is an example of a game loop. It’s still messy af and buggy but it’s starting to lok like a game :slight_smile:

You can join our Discord if you want too share your ideas :wink:

xoxo

Breep

DevLog #5 - Fixing tons of errors

From Hundreds of warnings to 4
It’s been a while since the first wrnings appears in the console as soon as I lanch the game, but I always said to myself that i’d take care of them later. As the warnings / non critical errors kept rising, the more lazy i get to fix those things because my game was working. Or so I thaught. Because there were a lot of random issues really bothering me, and I noticed that in fact, godot was trying to help me. Some of those warnings were actually really code errors that made my game behave wrongly.

Now there only have like 4 shader errors on start, I’ll fix them later :stuck_out_tongue:

Entire game loop
The game loop is full, I mean the game start with 8 playersand slowly they get eliminated and at the end only 2 remains. The end of game is not handled yet.

Battle Log
The main point of this dev log is about the battle log. I was wondering how to handle the network part since a long time. I tried differents things.

  • First, I fully used the godot multiplayer api (MultiplayerSpawner, MultiplayerSynchronizers and some rpc for resources). It worked well but was really network heavy for not that much information to share in the end. It was not scalable for my use.
  • Then I tried the deterministioc approach. After searching a little about it, I figured it was too hard to have a deterministic code for my game using godot (or any other engine anyway) because of my way of coding. I use a lot of signals everywhere because they are really handy, also because it helps keeping my code clean. The problem is that signals aren’t deterministic. For example, if on a ObjA you have a signal bomb_exploded() and ObjB and ObjC you listen to it, there is no way of knowing who will notice the signal first, and that inconstitancy in the game will make every player see different things.
  • The third idea, and the one I’m currently working on is a mix of those ideas. The idea is that only the server simulate the battle and nothing is synchronized. This way, I no longer need to focus on how to make my game deterministic or sending too much stuff through the network. Instead, I keep a log of every action done during the battle in a very condensed data structure. When the battle ends on the server, I send all the data to the clients. For not making the client wait 30s that the battle ends I speed up a lot the server during this phase. The difficulty is that during the start of the fighting phase, I need to sync the entities because the players can place their units. Then I deactivate the MultiplayerSynchronizer, I speedup the server and simulate the fight on it. During this time a 3s countdown start on the clients. At the end they ask the server for the battle result. Then they all simulate the fight on their side. This way of doing is network light and doesn’t imply deterministic coding.

It is not fully operational yet but the start looks promising. I hope to have something to show you next time about this.

Our Discord

Xoxo

Breep

2 Likes