Hi dear readers!
This entry is the first in a playful bi-weekly series about developing a game called Tanais Online. Don't expect it to be very detailed. I'm a lone dev who has minimal time and super high ambitions so the focus is on devving. If you have any questions feel free to ask in the comments.
First of all, what is Tanais Online? It's a browser game that aims to be a cross between Total War: Attila — the last good historical TW game (yes, feel free to go at me in the comments, idc 😝) — and Supremacy 1914.
I played the former an awful lot (1303,6 hours to be exact) and found it awesome. Classical Antiquity rocks in general. Idk why there are no more games set in that age. If nobody creates them then it should be a good market for new releases, right? RIGHT? I hope so hah. We will see. In the worst case, I will create a game that I can play for the rest of my life, even if CA keeps failing with all its historical releases forever.
I started two weeks ago by sitting down to plan my game in Milanote. They had an ad on YT and it looked like the best tool for the job. I quickly added a gazillion small notes and hit the free limit. I realized the unlimited plan cost $10 a month. Whhaaaat? So I just did some Sulla-like purges between those ideas hopefully keeping the most successful ones.
I exported & uploaded the full plan to here. You will see excerpts from it all around in the next episodes.
After I got a plan I started to work on the hardest part, the graphical representation. I have the advantage that I'm working on a web game, so if I get lucky, I can just render everything in the browser. What a brilliant idea! 😅 After some thinking and research I decided that my target should be something like what's present here (check the last image). If I can get something like that and I can show some armies marching on it I will be 98% done (aka almost). After some Gimping, headaches, and a few laughs I ended up with this:
I know it looks terrible, but honestly, it still looks better than I expected. If I buy David Baumgart's terrain pack, it will look okay!
Other than the map UI, I started working on the backend code as well. Login & registration is done with the home screen that shows the running and soon starting games.
This was harder than I initially thought it to be. Adding websocket support & MySQL-based saving was easy, but coming up with the game's initializing logic was not.
I ended up creating 4 tables. The player table stores the users, the game table stores the games, the nations table stores the nations in those games, and the settlement table stores the settlements owned by those nations. Maybe using document storage (Mongo?) would have been easier, but I wanted to have "proper", easy-to-use (doesn't exist anywhere but ok) transaction support.
<changeSet id="2" author="laxika">
<createTable tableName="game">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true"/>
</column>
<column name="scenarioId" type="int"/>
<column name="status" type="varchar(16)"/>
<column name="startTime" type="bigint"/>
<column name="lastUpdated" type="bigint"/>
</createTable>
<createTable tableName="nation">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true"/>
</column>
<column name="gameId" type="bigint"/>
<column name="ownedBy" type="varchar(16)"/>
<column name="ownerId" type="bigint"/>
<column name="nation" type="varchar(32)"/>
</createTable>
<createTable tableName="settlement">
<column name="id" type="bigint" autoIncrement="true">
<constraints primaryKey="true"/>
</column>
<column name="gameId" type="bigint"/>
<column name="settlementInScenarioId" type="int"/>
<column name="ownerId" type="bigint"/>
</createTable>
</changeSet>
Now in the code, I have scenarios (actually just one) that are blueprints for games. There is an update logic that runs every 30 seconds, update every game and if there are less than five games under preparation it creates as many as needed to match five. The nations and settlements are spawned at game initialization as they are in the scenario. Then once a player joins, it overtakes an AI's place.
@Scheduled(fixedRate = 30000)
public void scheduleGameRefresh() {
//TODO: Create as many as needed to match 5 :)
if (activeGameContainer.getPreparingGames().size() < 5) {
log.info("Less than 5 active games. Spawning a new one.");
final Game game = gameFactory.newGame();
activeGameContainer.registerGame(game);
}
activeGameContainer.getActiveGames()
.forEach(game -> log.info("Updating game {}.", game.getId()));
}
This is all for now. In the next two weeks, I plan on adding the logic for actually starting a game, showing cities and nations to the in-game players, and maybe resource generation.
Top comments (0)