GameDev Diary: Buying Units, Getting Buffs, and Defense Bonuses

Andrew Lukes
4 min readSep 3, 2023

--

Adding depth to my Python game

I warmly welcome everyone to another installment of my game dev diary. I have added a lot of new features to the game, which I will share with you in this article.

  • I wanted to include a demo file, but sadly there seems to be a problem with the compilation.

Buying Units
Money Settings
Cursor Improvements
Defense Bonuses
Improved Cities
· Conclusion

Buying Units

So far the player was only able to play with the units he was given at the start of the game. Now I am changing that. In the new version, you are able to pick up a unit from the buy menu at the bottom of the screen and place it somewhere inside your buy area. The unit price will be subtracted from your current balance, which replenishes at 10 supplies per turn. You will be able to buy pikemen, musketeers, canons, shields, and medics.

def buy_unit(self, click_pos):
def check_unit_overlap(unit, player, bought_unit):
if unit == bought_unit:
return False
# create a copy of the unit.rect object
rect_copy = unit.rect.copy()
# inflate the copy of the rect object
rect_copy.inflate_ip(bought_unit.size, bought_unit.size)
# check if the inflated copy of the rect object collides with the click position
if rect_copy.collidepoint(click_pos):

return True
def check_valid_placement_position():
if background_screen.get_at((click_pos[0], click_pos[1])) == RIVER_BLUE:
abort_placement_mode(player, bought_unit)
return False
buy_area_rect = pygame.Rect(*player.buy_area)
buy_area_rect.inflate_ip(-bought_unit.size //
2, -bought_unit.size//2)
if not buy_area_rect.collidepoint(click_pos):
print("Cannot place unit outside of buy area.")
return False
return True

def point_in_occupied_town():
for town in player.occupied_towns:
if town.rect.collidepoint((cursor_x,cursor_y)):
return True
return False
player = game_state.players[game_state.cur_player]
bought_unit = player.preview_unit
cursor_x, cursor_y = pygame.mouse.get_pos()
print("PREVIEW UNIT", bought_unit)
for unit in game_state.living_units.array:
res = check_unit_overlap(unit, player, bought_unit)
if res:
return
if bought_unit:
check_valid_placement_position()
if not check_valid_placement_position() and not point_in_occupied_town():
# abort_placement_mode(bought_unit)
return
else:
player.preview_unit = None
game_state.unit_placement_mode = False
game_state.money_spent += bought_unit.cost
else:
print(
f"Error: Unit type {bought_unit} not found.")

Money Settings

Inside the settings bar, you can now increase the money the player starts with up to 1000$ and the per-turn income to 100$.

new settings screen

Cursor Improvements

When you now hover on interactable objects like buttons, units, or sliders. Your cursor will change to a hand.

def set_cursor():
global hovered_slider
if game_state.hovered_button != None or game_state.hovered_unit != None or hovered_slider != None:
pygame.mouse.set_cursor(pygame.SYSTEM_CURSOR_HAND)
else:
pygame.mouse.set_cursor(pygame.SYSTEM_CURSOR_ARROW)

Defense Bonuses

When Inside a forest, city, or near your commander, your troops will get an additional bonus to their attack resistance, which is the percentage chance of them being hit by an attack. Currently, forest decreases hit chance by 25%, town by 20% and commander proximity by 33%.

 def apply_modifiers(self):
self.attack_resistances = {"base_resistance": 1 }
new_pos_color = game_state.pixel_colors[self.center[0]][self.center[1]]
if new_pos_color == FORREST_GREEN:
self.attack_resistances["IN FORREST" ] = 0.3

for town in game_state.battle_ground.towns:
if town.rect.collidepoint(self.center):
print("IN TOWN RECT", self)
self.attack_resistances["IN TOWN" ] = 0.1
break

for commander in game_state.living_units.dict["Commanders"]:
if commander.color == self.color and commander != self:
commander.give_deffense_boost(self)

Improved Cities

Now, cities actually have a functionality. When you leave your unit inside an uncontested city for a turn, it will capture it for you (signified by a change in the outline), and the city will start producing by default 10$ per turn for you. On top of that, you can buy units in those cities, allowing you to deploy them closer to the frontline. However, once you leave the city, it will automatically change to being neutral at the end of your turn.

 def get_occupied_towns(self):
def get_units_inside_town():
units_inside_town = []
for unit in game_state.living_units.array:
if town.rect.collidepoint(unit.center):
units_inside_town.append(unit)
return units_inside_town
def check_controlled_by_one_team():
first_unit_color = units_inside_town[0].color
for unit in units_inside_town:
if unit.color != first_unit_color:
return False
return True
def occupy_town():
first_unit_color = units_inside_town[0].color
town.occupied_by = first_unit_color
def update_players_occupied_towns():
for player in game_state.players:
player.occupied_towns = []
for town in towns:

if player.color == town.occupied_by:
player.occupied_towns.append(town)
print("players occupied towns", player.occupied_towns)

towns = game_state.battle_ground.towns

# get the units, whose centers are inside of the town rect
for town in towns:
units_inside_town = get_units_inside_town()
if len(units_inside_town) < 1:
town.occupied_by = None
continue

units_are_same_color = check_controlled_by_one_team()
if units_are_same_color:
occupy_town()
update_players_occupied_towns()
 player.supplies += game_state.money_per_turn + len(player.occupied_towns)* game_state.city_turn_revenue

Conclusion

In this article, we have discussed the latest improvements to Battleforge such as:

  • Buying units from the bottom bar
  • Functional cities give bonuses to the players occupying them
  • UI improvements like a dynamic cursor

Check out the previous article here:

Check out the next article here

Photo by JOSHUA COLEMAN on Unsplash

--

--

Andrew Lukes

Introducing Andrew Lukes: a Prague web dev & language enthusiast who shares his ideas on Medium. You can visit andrewebdev.online to see some of my projects