Virion studios devlogs

Blogging stuff about life, projects, etc.
Some devlogs too.
User avatar
Site Admin
Posts: 234
Joined: Wed Oct 10, 2018 3:48 am

Virion studios devlogs

Post by dendiz » Wed Oct 10, 2018 7:41 pm

title: "devlog #1"
date: 2018-04-19

  • game mechanics
    enemy spawns
    map generation
test playing the new turn based tower defense game which I have not yet dubbed and shall refer to as TBTD for short I came to the
conclusion that levels were too easy. Adding more enemies is the first thing that popped into my mind but that doesn't make the game interesting,
it only makes it tedious. In the first round of the prototype I had the targeting set to all neighbor tile at a distance of 1 tile.
So diagonal attacks were possible. Changing this rule to only cardinal directions increased the difficulty quite a bit. Also I came up
with the following mechanic: The player has 2 types of attacks. Ranged and melee. If you are standing next to an enemy you default to a melee attack.
You cannot use a ranged attack. If you are not next to the enemy it's a ranged weapon attack (if you have one). Since ranged attack will not displace
the enemy it becomes a tactical decision to use a ranged attack and keep you Mech safe or go up to the enemy to displace it. This did add a variety to
the tactical decision needed. GO refactored the attack code into a weapons class, so we have the foundation to add different types of weapons into the game.
The pilot weapons are the Punch and Auto-cannon with different damages / ranges.

As the turns progress new enemies spawn. The current method for doing this is a probabilistic method and the chances to spawn an enemy increase with each
turn that an enemy is not spawn. If an enemy does spawn, the chance reduces. So if the game drags on your chances of clearing the level decrease quite
a bit which forces the player to act quickly. I'm also thinking of adding a turn counter to make some levels survival mode levels.

One feedback I got was that the levels become repetitive after a certain while, especially if there is going to be the concept of permanent death, which I
actually find appealing. Random level generation is out of the question as random is random, and not nice to play. So going through some procedural level
content designs, I came up with the idea of constructing the levels in chunks. Each level consists of a combination of 4 chunks to choose from N.
This can yield different level topology and game play. We'll need to test this to see how it goes.

We discussed the concept of salvaging enemies at the end of each level (looting) for better weapons and upgrades. This also looks like a promising mechanic.
For this to be meaningful, I will also need to implement some kind of procedural weapon generator which I need look into.

User avatar
Site Admin
Posts: 234
Joined: Wed Oct 10, 2018 3:48 am

Re: Virion studios devlogs

Post by dendiz » Wed Oct 10, 2018 7:44 pm

title: "devlog #2"
date: 2018-04-20

  • map generation
Generating chunks was a good idea but not all chunks are perfect. There are cases that some chunks may leave isolated tiles and/or
buildings after the generation. E.g

Code: Select all

 0 0 0 0
 0 0 0 0
 1 1 0 0
 0 1 1 0
this chunk is totally OK as long as it's not placed at the bottom left quadrant. If it is the bottom left tile is an island
and pointless. Since we are playing on an limit 8 x 8 board, we can't have useless islands taking up precious space. So to battle this
I set out the write a connectivity check from tile to tile and tile to building (all building need to be reachable as well).
Tile to tile checking is easy with a DFS and check if every tile node is visited. Build to tile check required a pairwise reach-ability check.
It takes about 1 ~ 2 seconds to generate a valid map, which is acceptable. Next up the movement of pieces and enemies where happening with
place swaps, so it was impossible to tell who went where and what happened. I decided to draw a route line that indicated what happened to
understand the movement of pieces better. This lead to me realizing a bug in the path finding code and I changed that completely.
Previously I was using a BFS with a parent/child relationship map and back tracking the nodes to build the path. I switched over to Lee's
algorithm which is similar in concept but simpler. There is a more detailed approach on this algorithm somewhere on the blog (try searching for graph path finding).
With the path finder working again I completed the line drawing showing the route the pieces had taken and also prevented pieces from
jump over other pieces blocking their way. This made the game a bit harder and more restricting,
I don't know how it will impact the feedback during test plays.

User avatar
Site Admin
Posts: 234
Joined: Wed Oct 10, 2018 3:48 am

Re: Virion studios devlogs

Post by dendiz » Wed Oct 10, 2018 7:47 pm

title: "devlog #3"
date: 2018-04-21
  • enemy spawns
  • game mechanics, turns
  • targeting, game difficulty
A tactical feature that was missing after the enemy spawns was an indication of the tile of the spawn and the time.
Enemies suddenly appearing on the map and random places doesn't benefit the strategical and
tactical aspect of the game as the player has no chance to plan and prepare for the new wave of enemies.
I implemented this ETA and location indication on the map. If the player can get a unit on the tile for the new spawn, it will void.
This is a nice way for the player to prevent new enemies and gives to opportunity for a trade-off. Should I defend a building or prevent a new spawn?
tbtd1.png (149.52 KiB) Viewed 547 times
The turn mechanics for enemies started out as alternating between movement and attack. This made the game a bit static,
but it was the basis for the push strategy to save buildings. If an enemy was next to a target at the end of the movement phase,
the next turn would mean a damage to a building, so it was wise to push the enemy away from the building.
But during the movement turns towards the targets, the attack phase made the game progression slow. Turns were wasted doing
nothing as there wasn't anything to attack. I changed this around to the following structure: A turn for an enemy is
either attack or move. If the enemy reaches it's target after a movement turn, then it is committed to attacking that target
even it's pushed away and will not move for that turn. Currently this means doing nothing as the target is not in range,
but I have a 2 ideas on how to stop this attack turn being wasted. Either store the direction the attack would have been and do an
attack in that direction. If there is anything (un)lucky enough to be on that tile, it will receive the damage (maybe including friendlies?)
Or select a target in range and just attack that target. I will have to discuss these options and get some feedback on them.

The game is suffering from being to easy. Go up to the enemies and hit them on the head. Or even don't go up to them and attack them from range.
Well attacking from a distance is nice and realistic but infinite ammo for range attacks is not. That's why I implemented a feature to
limit the number of ranged attacks per unit per level. This makes the level progressively harder as you have to start walking around to hit the enemies after a while.
Also the chunk level generator wasn't paying attention to the number of buildings placed on the level. Having 9 buildings on a level
makes it easier to survive (think of buildings as player units). I added a pretty simple function that tries to adjust the number
of buildings on the level based on the current level the player is at.

User avatar
Site Admin
Posts: 234
Joined: Wed Oct 10, 2018 3:48 am

Re: Virion studios devlogs

Post by dendiz » Wed Oct 10, 2018 7:49 pm

title: "devlog #4"
date: 2018-04-22
  • AI, support units
  • weapons, targeting arcs
I though about different AI enemy types today. Currently the damage, and the health/armor for the AI was being set within an
interval appropriate with the current level, but this doesn't really give the sense that you are fighting against different
enemies. The AI also tries to compensate for the static nature by introducing some non-determinism into target selection
but still not enough. What I need is to have different types of units. E.g a unit that stuns the enemy, like the half snake
monsters from XCOM2. Or support units that boost the health of the all the enemies in the level unless it's destroyed. These
types of units will need their own AI implementations. E.g a support unit won't go around chasing enemies, it will try to stay
as far away from the player as it can and evade the player. So to facilitate this, I moved the AI into the enemy base class and
a portion of it (target selection/movement) to the current type of enemy. (called type A, because I haven't yet thought about
names or a story line etc.). This turned to be a bit tedious with subtle variable shadowing bugs creeping in an took a good
portion of my day.

This also lead the way for letting the enemies use the same types of weapons the player was using, for the
time being which means that the enemies will soon get ranged weapons. This actually has an implication on the user interface,
the player should be able to see which enemy has a ranged weapon and the area that it covers. Maybe if this were a PC game
a mouse-over the enemy would reveal it's range and coverage but this doesn't work when you are targeting handheld devices.
I also don't want to complicate the selection states further by revealing this information when a player is not selected and
an enemy is selected etc. So the idea I came up with is to display the ranged attack arc of the enemy when the player selects
his own unit and that unit can actually move, as it only matters when moving. This refactoring lead me to the decision to
also move the targeting code from the player and enemy classes into the weapon classes themselves, using the wielder information
for target selection (you don't want to shoot at friendlies). Coming from a server-side programming background I am used to
using lists, arrays etc to use composition in objects, but with the scene tree there, it actually makes more sense to use a
node in the scene tree to hold stuff like weapons, as you get the holder of that item from the scene tree way more easier.
Otherwise you have to pass around the reference of the parent etc. This model of programming should also find it's way into
other domains. You should be able to freely traverse the object graph.

I was also thinking that all player units would carry a ranged weapon but this doesn't seem optimal to me now. Some units
should only have a melee attack, and loot a ranged weapon after progressing in the game? One thing is for sure though
you can't have 2 ranged weapons, as that would require the player to select which one to fire. I want to take a
convention over configuration type of approach for a mobile game. The convention here being that if you are in melee
range you can only do a melee attack. This is also kind of more realistic. If you were in a close brawl with someone
would you leave that and try to shoot at something far away?

I was able to squeeze a couple of hours more out of the day (mostly because my SO is away on an MBA class reunion in Nashville)
to implement the range arc display for enemies that are equipped with a ranged weapon. I also changed the sprite they
use for a visual distinction between the current 2 types of enemies.
tbtd2.png (176.96 KiB) Viewed 548 times

User avatar
Site Admin
Posts: 234
Joined: Wed Oct 10, 2018 3:48 am

Re: Virion studios devlogs

Post by dendiz » Wed Oct 10, 2018 7:54 pm

title: "devlog #5"
date: 2018-04-23

  • firing arcs
  • AI target selection
  • support units
I put off working on indirect/direct firing weapons for a couple of days to get the basics of targeting and arc display
going, but now that I have that down, I added types of different range selection. Indirect firing weapons don't require
a direct line of sight, these will be weapons like rockets, or attack drones. Direct fire weapons are your classical AC and
laser type weapons. L.O.S is normally a tricky thing to implement when you are doing it for an arc F.O.V but the weapons in this
game are simple, and only have effect on files and ranks. So no diagonals means easier L.O.S sigh implementation. Just keep
looking at the next tile in the direction you are scanning in and if you hit a target or blocker stop progressing in that

Targeting got some love today as well. I had already implemented ranged weapons for enemies and their targeting arc was
visible on the map when the player was moving, but the units did not engage the target even it if came into range. I
changed that today. If an enemy detects a player unit in firing range it will drop it's current target and go after the
player unit. If it was committed to an attack when the player unit jumped into range, it will still carry out the committed
attack then pursue the player unit. The adds a bit of variety to the AI units. It is still relatively easy to tip-toe
around the firing arc of the enemies but may a smarter AI that guards choke points on the map (like a mountain passage
with a single tile width way to access an area of the map) would make for some interesting tactics and game play.

Not all units in an army are there to engage and attack. Well at least not until it's a last resort so I added a support
unit that will increase the armor of the enemy units as long as it is alive. It will try to center itself relative to it's
team mates, but I may change this movement heuristic to a more evasion biased heuristic, like run away from the player.
I'm kind of worried if this will actually lead to all the support units grouping in one corner of the map while trying to
stay away from the player. The logic behind trying to center them was to simulate an area of effect range for the armor

The number of unit types are increasing so here is a a little info on the types so far with some of their stats:

Basic enemy

alien1.png (1.57 KiB) Viewed 548 times
This guy only has melee attack with damage 1-3, armor 2 + X (based on level), speed 1 - 4

Advanced enemy
alien2.png (1.28 KiB) Viewed 548 times

This guy has a melee weapon and a ranged weapon. These can get pretty dangerous as they can
switch targets and start going after the player units if they can lock onto the player.

Armor support enemy
alien3.png (954 Bytes) Viewed 548 times

These guys don't attack the player, but provide an armor boost +1 to all enemy unit while
they are alive.

User avatar
Site Admin
Posts: 234
Joined: Wed Oct 10, 2018 3:48 am

Re: Virion studios devlogs

Post by dendiz » Wed Oct 10, 2018 7:58 pm

title: "devlog #6"
date: 2018-04-24


  • firing arcs
  • support units
  • water expansion

When displaying enemy firing arcs I didn't want any water tiles to be considered in range, as it made it look like
water tiles were traversable, which is false. So I just declared them as obstacles and excluded from the arc. This
lead to an undesired side effect of players not being able to shoot enemies on the other side of the river. So I had
to remedy the situation by just not displaying the arc if the tile is a water tile.

Another thing that was bugging me was that the armor support units boost was effecting the enemy units only after
the first enemy moves were made. I changed this by giving the boost in the `_ready` function call with gets called
every frame. I like to refrain from using this function as it consumes a lot of CPU cycles. Another option would be
to implement some kind of post enemy creation hook that runs after all enemies are spawned and then distribute the
boosts. I would have to run this code again after each in-level spawn has happened which I thought would be unnecessary
complication in the code. One optimization I did was instead of executing the call every frame I added a check to
execute it one every 0.5 seconds which is good enough for my purposes.

I was thinking about a type of enemy unit that would manipulate the terrain, e.g expand the water tiles or make the
volcano's erupt and make lava flow around the map having the same effect as water. When I set out to implement this
I realized that the backing array that I used when creating the map initially was kind of limiting. If I wanted to
add a new lava tile, I would have to come up with a constant that represented that tile, and then add manage path
finding code, etc to account for this new tile type. Besides this I was maintaining 2 data structures: the scene tree
and matrix containing redundant data. Since I can't dump the scene tree, I had to dump the matrix. The first iteration
constructs a new matrix when needed from the scene tree on the fly, the use of a 2D matrix makes path finding
code a lot more simple. For the second iteration of refactoring I'm thinking about getting rid of the constant that
I had been using to compare tile types, but I'm not sure this can be done. I was thinking about comparing class types
to get to tile types, but some tiles share a class type, and GD script class type comparison isn't that good. For
instance I could not find a way to compare a class to it self. Say I have a function that returns all the enemy units
on the map and in the support unit I want to skip over the enemy types "support unit". You can load the support
unit within itself, so you are kind of stuck with the constant comparisons.

User avatar
Site Admin
Posts: 234
Joined: Wed Oct 10, 2018 3:48 am

Re: Virion studios devlogs

Post by dendiz » Wed Oct 10, 2018 8:00 pm

title: "devlog #7"
date: 2018-04-25

  • performance/code improvements
  • jump jet mechanics, game mechanics
I have been skimming through the code so far, which is around 2K lines right now, and I have noticed some
inefficiencies and some simplification that could be made. GD Script doesn't really provide an extensive
language so a lot of things have to be done the old fashioned way. I guess their motto is, C doesn't have
those fancy constructs and they're doing fine, why shouldn't we? So I did a couple of one-liner fixes and
some code reduction here and there, nothing too big. A couple of bug crawled into the code making it possible
for the player to gain super Mech attack powers, like attacking even after the attack action had been consumed.
Like that wasn't enough the ranged weapons could fire beyond their ammo limit. I fixed that too, so no more
Bruce Lee mechs, kicks every bodies ass.

One of the items that I want to add is a jump jet, that will get the player beyond blocked tiles. This will
give the ability to jump over the enemy to their behind and attack them. And also get out of hairy situations
like this

tbtd3.png (143.27 KiB) Viewed 548 times
The item isn't pick-able yet, but the movement mechanics are there. So the item should be ready within the
coming days.

I also wanted the player to prioritize building protection as this is supposed to be a tower defense game.
Now if you lose more than X building across all levels, the game is over.

I'm thinking of a turn counter so that you may just have to protects the buildings for X turns, but this also
brings the mechanic of running away from the monsters if you have high speed or a jump jet. This is kind of
refuted by the newly spawning enemies. But I also wanted to try out the mechanic of water flooding, killing
buildings, enemies, mountains or anything else in it's path eventually cover the whole map.

User avatar
Site Admin
Posts: 234
Joined: Wed Oct 10, 2018 3:48 am

Re: Virion studios devlogs

Post by dendiz » Wed Oct 10, 2018 8:02 pm

title: "devlog #8"
date: 2018-04-26

  • bug fixes
  • loot generation
I had implemented the duplicates check for the spawn queue in a inefficient way, so I corrected that. No need
to scan the queue every time, just keep track of the element in the queue in a set. I also realized during
test playing that newly spawned water tiles could lead to islands in the map. So a check for connectivity
was needed before adding that new water tile to the queue. This lead me to fix a performance issue in the
connectivity checking code. A split the tile-tile checking code from the slower building - tile checking
code. I'm still not sure if I should let water tiles spawn on building tiles. This is frustrating for the
player because most of the time there is nothing you can do to stop it. It's simply not possible to kill
the remaining enemies in the number of turn given to save the building and this does reduce the global
building preservation count. On the other hand it does make the game a bit more difficult and exciting.

A couple of days ago I had noticed that some enemies froze occasionally. Investigation into this led me
to discover that it was possible for the enemies to select a building/mountain tile as the location for
an attack on the player in some configurations. I implemented a fix for this and also implemented a fix
for selecting unreachable tiles. I'm wondering if I should just let the enemies do a random walk if they
don't get a movement tile or just let them sit frozen for a couple of turns?

During one test game I was blessed (!) with a mech that had 5 speed. This made it super easy to go around
killing all the enemies very fast before a wave came in to their rescue. After progressing about 5 levels
the game crashed with a NPE because the level generator could not place the number of required units
on the deployment zone for the enemies. There just wasn't enough free tiles!. What this means is this

* The weapon/item generation system is flawed. Why would I get a +5 speed mech in the first level?
* Not just more enemies each level, we also need stronger enemies.
* There has to be a trade-off between speed, weapon strength, range.

These were issues I was previously aware of, and had in my to do list. The solution is a points
system which measures the strength of the units and adjusts the level difficulty based on this.
Also I'm thinking of introducing a weight and maybe a heat system to implement the trade off between
items. To install an AC with +2 damage means your speed is reduced by 1. So if you have a mech that's
only speed 1 you can't install that weapon. With the current setup of only 1 item per mech this also
means that if can't have armor + speed. So if your mechs initial speed is 1 you cannot have AC2 + armor
which may have a negative effect on game play.

I also saw an issue where the supports unit would surround a normal enemy unit and would not budge
effectively freezing that unit in place. I set an upper limit of 1 for support units on the map.

Now that the unit lab development is complete and we can equip the units with items and weapons things
are looking a lot better. We also implemented a budgeting system for generating the weapon stats based
on level. Each level you get a budget of say 5 units and this budget is distributed randomly to the
stats of the loot being generated. This type of generation also brought a balance to the game. No
more Bruce Lee'ing around the map killing everybody.

User avatar
Site Admin
Posts: 234
Joined: Wed Oct 10, 2018 3:48 am

Re: Virion studios devlogs

Post by dendiz » Wed Oct 10, 2018 8:05 pm

title: "devlog #9"
date: 2018-04-27

  • how to use the mech lab
We now have the unit lab fully functional. Actually we already had it for a couple of days but it's
only now that I had a chance to write about it. The unit lab is the place where you can add/remove
items and weapons from your units. You can modify and deploy up to 4 units into battle. Going over
a screen shot will make it easier to under stand what's going on:
tbtd4.png (61.2 KiB) Viewed 547 times

This is the main screen. The unit as listed on the left with their equipment. The middle section
contains the inventory and the right section contains the stats of the selected item.
tbtd-mechbay.png (2.86 KiB) Viewed 547 times
this little tool here allows to to select which unit you want to change.

tbtd-weapon.png (3.24 KiB) Viewed 547 times
the "+" button allows you to equip that weapon to the currently selected unit. The "D" button
drops the item from your inventory. Currently there is no limit to the items you can have in your
inventory but that may change. It's only purpose is to empty the trash items if it gets to crowded.

Each unit can only carry a single ranged weapon/melee weapon/item, so when you add an item it will
get swapped with the existing item.

When a unit has a chassis, melee weapon and a melee weapon it will be deployed automatically. Currently
we are debating the issue of an option to not deploy a mech that is fully equipped but there are no
strong cases where a player would not want a mech on the battlefield.

User avatar
Site Admin
Posts: 234
Joined: Wed Oct 10, 2018 3:48 am

Re: Virion studios devlogs

Post by dendiz » Wed Oct 10, 2018 8:15 pm

title: "devlog #10"
date: 2018-05-01

  • difficulty mechanics
  • attack mechanics
The difficulty of the level was being determined by the number of enemies, but the enemy stats
were being generated randomly. This meant that some games level 2 could be much more harder than
other games. To balance this I came up with a budgeting system. Each level has a budget that is a
function of the level number. The more advanced the level, the higher the budget. At the start of
each level, this budget is consumed according to the stats of the randomly generated enemy. So if
say the level 2 budget is 10, you could have 3 monsters with 3 stats, or 2 with 5 each. This keeps
the game dynamic while keeping the levels balanced. For the in-level enemy spawns, the budget is
increased each turn according to the current level. Further on the game the in-level budget
increase is much high therefore more/stronger enemies spawn.

I also changed the end of level loots to a random number between one and two. Sometimes you'll be
lucky and get two, but this should not affect the game progression that much.

I also increased enemy melee damage with level and building armor too. This provides a way to decide
on which buildings to protect from which enemy. Also it's now a good idea to check an enemies
damage before deciding to go in for a melee or ranged attack.

I tried playing around with the committed attack directions. This means that once the enemy is
committed to an attack, they will keep the relative direction of the attack, and do the attack
on that square even if it means damaging a friendly. I though this would lead to more tactical
game play, by pushing enemies into tiles that will be attacked for sure the next turn, but that
doesn't happen as much I as thought it would, so I reverted the code.

An peculiar thing with Godot is that freed objects are not set to null. This lead to a subtle bug:
If the player dies, and you click on an enemy to see some info about the guy that killed you
the game would crash. This was happening because the player object had been freed and
there was a reference in the info label to the player object. There was a null check if place,
but that didn't cover it. The current solution is the wrap that object with a weak reference,
but that means changing every access of the target variable with a weak reference. Meh. What
I came up with is checking the the target object is in the scene tree during the label update.
The label update is done every frame, so this is potentially a performance bottleneck, but since
it is only run when the enemy is selected it is an acceptable trade-off for a clean approach.

Post Reply