Kogmind: Devlog #3June 13, 2020
With the new animation layer now in place I thought that I’d tweak the animations a bit more. The object explosion animation was not fading out properly so I added a color lerping function that fades the background from the explosion color to black. This makes for a better, more realistic effect.
All the animation related code was living in the BeamBlaster class as it was convenient to modify stuff, I moved those to their respective classes. Not much problems since no refactoring really required for this reorganization.
Next up was setting the scene for more the first neutral bot. I had only implemented a concrete class for AI agents so I went ahead and extracted a protocol and a base class for NPCs. This process was pretty straight forward as XCode provides a way to automatically rename things across the project. An interesting swift quirk is that array.removeAll(where:) doesn’t work with protocols. I had to refactor there into array = array.filter(where:) and all was good again. With the basics in place I implemented the WallRepairBot. The idea for this bot is that it will search the level for walls that were destroyed and now lead into the void and repair these. They become a wall themselves to it’s an ‘honorable’ mission for them, they are martyrs. Initially I thought that each bot would search the map to find a wall that needs repair but that turned out to be super slow and a noticable performance bottle neck as scanning the map to get a tile, then checking the bots to see if anyone is already decided to repair that tile takes a long time. This made me think that a class that is responsible for assigning work to these janitors of the level could be a better way to go. I also set up some code that keeps tracks of the tiles that are destroyed so I don’t have to scan the whole level each turn. This architecture also requires the need for synchronization between the bots as they are racing to find a tile to repair. After some play testing I realized that assigning a wall to repair to an agent also needs some additional checks as the player may kill the bot assigned to that task and the BotMind should reassign that repair task to another bot if that happens.
A funny bug I found was that when I destroyed a floor a repair bot would go there and build a wall. The reason was that I wasn’t filtering based on the tile type that was destroyed and adding them all to the destroyed objects list. It was an easy fix but I did chuckle when I first saw it.
I also ran into an issue where the player was aiming a bot and even though the weapon had a 100% hit chance it was missing the bot and the bot continued along it’s path. I figured that the bot was being scheduled to move before the player but I was pretty certain that I had put the player as the first actor in the scheduler queue. After checking multiple places in the code I found a call from the older version of the scheduler that was shuffling the queue.
I realized after watching some Cogmind videos that I wasn’t labeling the bots but just the props. I dived into get section and saw that Props were embedded into the level tile data but bots weren’t. So I had to given them their own labeling pass. There is an issue of overlapping labels that needs some thought on a good solution and is something that I’m not really keen on doing right now. It’ll have to wait in the backlog.
Another issue was in tne fog reveal. If an unexplored area was behind a prop and not in the players FOV it would not be revealed even though most of the tiles around it would. This seemed inconsistent so I switched to revealing every tile that’s in the rectangle around the player within the fovRadius parameter. It’s way better like this.
Caching for the FOV calls was in the back of my mind for some time now so I added that. You can cache it in definitely as props and walls get blown up so they change and adding something too sophisticated that recalculates based on this seemed overkill. The simple cache just keeps the calcuted values around for a single turn as there multiple calls to this function per turn and invalidates the cache after a move in the scheduler. I didn’t see a super gain in performance though as the calculations are pretty simple.
A huge annoyance was the keyboard beeping after each key press because nobody was registered as a responder to the event. I spent a couple of hours a couple of days ago researching a solution for this but only got to a working one today. The trick was to override keyDown and not call the super method. Such a hacky workaround but gets the job done.
I also added ammo tracking to the ranged weapon. Melee weapons don’t have ammo so they are a special case. I added the ammo status to the info pane and disabled ranged targeting if none of the weapons have any ammo.
It’s amazing how many bugs related to clamping and out of bounds coordinates have surfaced in the last couple of days. Another on was when the roll for a ranged weapon failed a new target location selected randomly from the neighboring tiles could turn up to be a void tile and the npcs trying to navigate there would error out because a path could not be found. The fix was simple, just filter out those bad location before picking the new one.
I also came across another issue where a cell that was illuminated could not be picked as a target because it was reported to not be in the LOS. Thid was happening because LOS is calculated using Bresenham and in some cases the line would need to pass through a neighboring wall tile and that was not considered to be in LOS. I solved this by changing the definition of LOS to be a cell in the FOV. With the the targeting line can sometimes appear to cross walls but that’s okay.