Block TD is a simple Tower Defense game developed by Keegan Smith, Michael Morris, and Will Heeney for the 2023 Capsher ACC Coding Challenge.
python -m pip install -U pygame --user
Download the "[temp] Clock TD.zip" file and extract it's contents. Then simply run the executable. In the game you will buy towers and place them around the map to stop each wave of blocks from reaching the end of the track!
main.py is the heart of our game. main keeps track of the current game state, of which there are 5: start screen, game screen before round, game screen during wave, victory screen, and defeat screen. main also has to keep track of all of the buttons on the current screen. If the player is currently on the start screen, the program checks whether a mouse click has happened, and if it has, it checks whether or not the mouse click happened within the bounds of the start button.
Once the start button has been pressed, the program changes the screen to the game screen before round. On this screen the program must keep track of the play button, the shop button, and whether or not the player has clicked on a tower. If the player has clicked on a tower then the range of the tower as well as the sell button are displayed, when the player clicks off the tower the "clicked tower" state is set to false and as a result the range and sell button disappear. If the "clicked tower" state is true and the player clicks on the sell button, then the program sets the "clicked tower" state to false and also gets rid of the tower (removes the tower from the current list of towers on the screen) as well as refunds the player half of the tower's initial cost by updating the money display. When the shop button is pressed, the screen displays the shop panel. All of the buttons within the shop panel are handled by shop.py. If the shop panel is active and the player presses the shop button again, the shop panel vanishes. When the play button is pressed the program loads the next round, (stored in round.py) and advances to the game screen during wave screen.
The game during wave screen has to keep track of a lot things. It must keep track of 1.) the enemies currently on the screen 2.) the enemies yet to be loaded onto the screen, and when to deploy them 3.) the projectiles currently on the screen 4.) the towers on the screen, telling them to shoot 5.) any collisions that may happen 6.) felled enemies and updating the players cash 7.) player's remaining lives. The collision handling is done by looping through all of the enemies and then calling the collision function in collisions.py file which loops through all of the projectiles and checks if there is a collision between the current enemy and any of the projectiles on screen. This algorithm runs in
The defeat screen only keeps track of the "again" button. Upon being clicked the player is redirected back to the start screen.
The victory screen is pretty much the same as the defeat screen. Again it just keeps track of the "again" button which upon being pressed redirects the player back to the start screen.
The enemy class keeps track of enemy pathing, health points, status affects, and speed. There are also child classes of Enemy such as Fast Enemy, Tank Enemy, Super Fast Enemy, and Super Tank Enemy which simply adjust the speed and health points attributes of the enemy class.
Enemy pathing is kept track of by simply having a list of points which represent each place where the enemy has to turn a corner. The enemy starts at the initial point and then heads either up, down, left, or right depending on the location of the next way point. This would make it easy if we wanted to add a new map as updating the enemy pathing is just a matter of changing the set of waypoints
As of now there is only one status affect in the game which is slow. To ensure that only one slow is applied at a time, we also include the id of the tower that applied the slow to the enemy. This way we can check if the current slow tower's id matches the tower that slowed the enemy to begin with, and can update the slow affect as needed.
Each enemy stores it's current health. When this value reaches 0 the enemy has been felled.
The enemy speed represents the number of pixels the enemy travels in each frame. The higher this number, the faster an enemy travels.
This enemy is colored blue and has 100 health and 5 speed. This means that it dies in two shots from the normal tower.
This enemy is colored purple and has 500 health and 2 speed. This means it is very slow and dies in two shots from a sniper tower.
This enemy is colored white and has 100 health and 8 speed. This means that it travels more than 50% faster than the regular enemy and also dies in two hits from a normal tower
This enemy is colored black and has 750 health and 4 speed. This means that it travels at nearly the same speed as a regular enemy, but has 7.5x the health making this a formidable foe.
This enemy is colored gray and has 150 health and 12 speed. This means that it has 50% more health than the regular enemy and more than double the speed, making this possibly the toughest enemy in the game.
The Shop functionality of the game is implemented with 3 classes being shop_button, Shop, and Shop_Item. These 3 classes work together to smoothly allow the player to buy towers in the shop and place them on the map.
The shop_button class is used to create a button on the main screen so that the event listener for loop in main can detect when the user wants to open and close the shop and properly change the display.
The main purpose of the shop class is to hold the different Shop_Items which are the towers that you can buy as well as also detect when a user clicks on an item in the shop and calls the appropriate function to complete that action. The Shop class also checks to see if the user is hovering over a tower in the shop and displays the description for that item if it is.
Shop_Item holds the information about each tower that is in the shop mainly the type of tower as well as the description and price. The main function in Shop_Item is the addtower() function which allows the user to click somewhere on the map and then returns the location of the click so that the appropriate tower can be placed in that location.
The Towers are your only line of defense against the enemies. They shoot, slow or burn the enemies to prevent them from escaping. They can be placed anywhere besides on another tower, out of bounds, or on the path the enemies walk. The normal and sniper tower both shoot projectiles, which aim at the furthest enemy down the path that has its centerpoint within the radius of the tower. The angle is calculated by using the centerpoint of the enemy and the tower as the two points on a right triangle
The slow tower doesn't shoot projectiles however, as it slows down all enemies within its radius. The flame tower also doesn't not "shoot" projectiles, but it still attack by burning all enemies within close proximity with its flamethrower. Each tower has its own seperate cost, radius, damage, and shot speed for projectiles.
The projectiles are also their own class, following in a straight line when shoot in whichever angle chose for it until it either hits an enemy or reached the boujndary of the game.
The projectiles are updates every frame by their shot speed by the tower class
The normal tower bullet and sniper bullet have their own classes but do similar things with differing and damage
While the flame from the flame doesn't actually travel it is considered a projectile so the flame wouldn't need to be implemented within the flame tower class
All of the artwork in our game is made of pixel art using an online pixel art editor.
Art Credit:
- Start Screen - Will Heeney
- Map - Will Heeney
- Game Start Button - Will Heeney
- Round Start Button - Keegan Smith
- Shop Button - Will Heeney
- Win Screen - Keegan Smith
- Lose Screen - Keegan Smith