Skip to content

Commit

Permalink
Trap defusal active ability (#54)
Browse files Browse the repository at this point in the history
* Balancing

Added values to the already existing active abilities to add basic balancing

* Ability setup

Started the setup for creating the new active ability

* Ability tests

Created tests that should've been there for other abilities and a test for trap defusal

* Create test_defuse_controller.py

Tested the defuse controller

* Mining cap

Implemented check in the mine controller that prevents the avatar from mining when inventory is full
  • Loading branch information
KingPhilip14 authored Dec 28, 2023
1 parent 32baf64 commit 307eaaa
Show file tree
Hide file tree
Showing 16 changed files with 318 additions and 35 deletions.
4 changes: 3 additions & 1 deletion game/common/avatar.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from game.common.game_object import GameObject
from game.quarry_rush.ability.emp_active_ability import EMPActiveAbility
from game.quarry_rush.ability.landmine_active_ability import LandmineActiveAbility
from game.quarry_rush.ability.trap_defusal_active_ability import TrapDefusalActiveAbility
from game.quarry_rush.tech.tech import TechInfo
from game.utils.vector import Vector
from game.quarry_rush.tech.tech_tree import TechTree
Expand Down Expand Up @@ -154,6 +155,7 @@ def __init__(self, company: Company = Company.CHURCH, position: Vector | None =
self.dynamite_active_ability: DynamiteActiveAbility = DynamiteActiveAbility()
self.landmine_active_ability: LandmineActiveAbility = LandmineActiveAbility()
self.emp_active_ability: EMPActiveAbility = EMPActiveAbility()
self.trap_defusal_active_ability: TrapDefusalActiveAbility = TrapDefusalActiveAbility()

@property
def company(self) -> Company:
Expand Down Expand Up @@ -361,7 +363,7 @@ def can_place_emp(self) -> bool:
return self.abilities['EMPs'] and self.emp_active_ability.is_usable and not self.abilities['Landmines']

def can_defuse_trap(self) -> bool:
return self.abilities['Trap Defusal']
return self.abilities['Trap Defusal'] and self.trap_defusal_active_ability.is_usable

# method to return the opposing team based on the avatar's company
def get_opposing_team(self) -> Company:
Expand Down
1 change: 1 addition & 0 deletions game/common/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class ObjectType(Enum):
TRAP = auto() # 31
LANDMINE = auto() # 32
EMP = auto() # 33
TRAP_DEFUSAL_ACTIVE_ABILITY = auto() # 34


class ActionType(Enum):
Expand Down
3 changes: 3 additions & 0 deletions game/controllers/defuse_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ def __init__(self) -> None:
super().__init__()

def handle_actions(self, action: ActionType, client: Player, world: GameBoard):
if not client.avatar.can_defuse_trap(): # return if not usable
return

temp_vector: Vector
match action:
case ActionType.DEFUSE_UP:
Expand Down
4 changes: 4 additions & 0 deletions game/controllers/mine_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ def handle_actions(self, action: ActionType, client: Player, world: GameBoard) -
will be taken, and it stops there. If no copium is found, None will be returned.
"""

# don't mine anything if the inventory is full
if len(world.inventory_manager.get_inventory(client.avatar.company)) == 50:
return

match action:
case ActionType.MINE:
client.avatar.state = 'mining'
Expand Down
4 changes: 3 additions & 1 deletion game/quarry_rush/ability/dynamite_active_ability.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

class DynamiteActiveAbility(ActiveAbility):

def __init__(self, cooldown: int = 1, fuse: int = 0):
def __init__(self, cooldown: int = 4, fuse: int = 0):
super().__init__()
self.object_type = ObjectType.DYNAMITE_ACTIVE_ABILITY
self.cooldown = cooldown
self.fuse = fuse # default = 0 to be available right after purchase
4 changes: 2 additions & 2 deletions game/quarry_rush/ability/emp_active_ability.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

class EMPActiveAbility(ActiveAbility):

def __init__(self, cooldown: int = 1, fuse: int = 0):
def __init__(self, cooldown: int = 4, fuse: int = 0):
super().__init__()
self.object_type: ObjectType = ObjectType.EMP_ACTIVE_ABILITY
self.cooldown: int = cooldown
self.fuse: int = fuse
self.fuse: int = fuse # default = 0 to be available right after purchase
4 changes: 2 additions & 2 deletions game/quarry_rush/ability/landmine_active_ability.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

class LandmineActiveAbility(ActiveAbility):

def __init__(self, cooldown: int = 1, fuse: int = 0):
def __init__(self, cooldown: int = 6, fuse: int = 0):
super().__init__()
self.object_type: ObjectType = ObjectType.LANDMINE_ACTIVE_ABILITY
self.cooldown: int = cooldown
self.fuse: int = fuse
self.fuse: int = fuse # default = 0 to be available right after purchase
10 changes: 10 additions & 0 deletions game/quarry_rush/ability/trap_defusal_active_ability.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from game.quarry_rush.ability.active_ability import ActiveAbility
from game.common.enums import ObjectType


class TrapDefusalActiveAbility(ActiveAbility):
def __init__(self, cooldown: int = 0, fuse: int = 0):
super().__init__()
self.object_type = ObjectType.TRAP_DEFUSAL_ACTIVE_ABILITY
self.cooldown = cooldown # default = 0 to always be available
self.fuse = fuse # default = 0 to be available right after purchase
9 changes: 9 additions & 0 deletions game/test_suite/tests/test_avatar.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ def test_unlock_dynamite(self):
self.avatar.buy_new_tech('Dynamite')
self.assertTrue(self.avatar.is_researched('Improved Mining'))
self.assertTrue(self.avatar.is_researched('Dynamite'))
self.assertTrue(self.avatar.can_place_dynamite())

# Buying Landmines will return False if the tree hasn't developed to it yet
def test_unlock_landmines_fail(self):
Expand All @@ -181,6 +182,7 @@ def test_unlock_landmines(self):
self.assertTrue(self.avatar.is_researched('Improved Mining'))
self.assertTrue(self.avatar.is_researched('Dynamite'))
self.assertTrue(self.avatar.is_researched('Landmines'))
self.assertTrue(self.avatar.can_place_landmine())

# Buying EMPs will return False if the tree hasn't developed to it yet
def test_unlock_emps_fail(self):
Expand All @@ -197,6 +199,7 @@ def test_unlock_emps(self):
self.assertTrue(self.avatar.is_researched('Landmines'))
self.assertTrue(self.avatar.is_researched('EMPs'))
self.assertFalse(self.avatar.is_researched('Trap Defusal'))
self.assertTrue(self.avatar.can_place_emp())

# Buying Trap Detection will return False if the tree hasn't developed to it yet
def test_unlock_trap_defusal_fail(self):
Expand All @@ -213,6 +216,7 @@ def test_unlock_trap_defusal(self):
self.assertTrue(self.avatar.is_researched('Landmines'))
self.assertTrue(self.avatar.is_researched('Trap Defusal'))
self.assertFalse(self.avatar.is_researched('EMPs'))
self.assertTrue(self.avatar.can_defuse_trap())

# Tests getting the researched techs
def test_get_researched_techs(self):
Expand Down Expand Up @@ -246,6 +250,11 @@ def test_avatar_json(self):
self.assertEqual(self.avatar.emp_active_ability.cooldown, avatar.emp_active_ability.cooldown)
self.assertEqual(self.avatar.emp_active_ability.is_usable, avatar.emp_active_ability.is_usable)

self.assertEqual(self.avatar.trap_defusal_active_ability.fuse, avatar.trap_defusal_active_ability.fuse)
self.assertEqual(self.avatar.trap_defusal_active_ability.cooldown, avatar.trap_defusal_active_ability.cooldown)
self.assertEqual(self.avatar.trap_defusal_active_ability.is_usable,
avatar.trap_defusal_active_ability.is_usable)

# other_tree: dict = self.avatar.get_tech_tree().to_json()
# for tech in self.avatar.get_tech_tree().tech_names():
# self.assertEqual(other_tree[tech], self.avatar.get_tech_tree().is_researched(tech))
Expand Down
63 changes: 63 additions & 0 deletions game/test_suite/tests/test_defuse_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import unittest

from game.common.action import ActionType
from game.common.avatar import Avatar
from game.common.game_object import GameObject
from game.common.map.game_board import GameBoard
from game.common.player import Player
from game.controllers.defuse_controller import DefuseController
from game.quarry_rush.entity.placeable.traps import Landmine, EMP
from game.utils.vector import Vector


class TestDefuseController(unittest.TestCase):
def setUp(self):
self.avatar: Avatar = Avatar()
self.avatar.science_points = 5000 # to unlock trap defusal
self.avatar.buy_new_tech('Improved Mining')
self.avatar.buy_new_tech('Dynamite')
self.avatar.buy_new_tech('Landmines')
self.avatar.buy_new_tech('Trap Defusal')

self.defuse_controller: DefuseController = DefuseController()
self.locations: dict[tuple[Vector], list[GameObject]] = {
(Vector(1, 0),): [Landmine()], # above avatar
(Vector(0, 1),): [EMP()], # left of avatar
(Vector(1, 1),): [self.avatar], # center
(Vector(2, 1),): [EMP()], # right of avatar
(Vector(1, 2),): [Landmine()] # below avatar
}

# make a 3x3 game board
self.world: GameBoard = GameBoard(0, Vector(3, 3), self.locations, False)
self.client = Player(None, None, [], self.avatar)
self.world.generate_map()

# test defusing a trap above works
def test_defuse_trap_above(self):
self.assertTrue(self.world.game_map[0][1].is_occupied_by_game_object(Landmine))
self.defuse_controller.handle_actions(ActionType.DEFUSE_UP, self.client, self.world)
self.assertFalse(self.world.game_map[0][1].is_occupied_by_game_object(Landmine)) # remember (y, x) coordinates

# test defusing a trap to the right works
def test_defuse_trap_to_right(self):
self.assertTrue(self.world.game_map[1][2].is_occupied_by_game_object(EMP))
self.defuse_controller.handle_actions(ActionType.DEFUSE_RIGHT, self.client, self.world)
self.assertFalse(self.world.game_map[1][2].is_occupied_by_game_object(EMP))

# test defusing a trap below works
def test_defuse_trap_below(self):
self.assertTrue(self.world.game_map[2][1].is_occupied_by_game_object(Landmine))
self.defuse_controller.handle_actions(ActionType.DEFUSE_DOWN, self.client, self.world)
self.assertFalse(self.world.game_map[2][1].is_occupied_by_game_object(Landmine))

# test defusing a trap to the left works
def test_defuse_trap_to_left(self):
self.assertTrue(self.world.game_map[1][0].is_occupied_by_game_object(EMP))
self.defuse_controller.handle_actions(ActionType.DEFUSE_LEFT, self.client, self.world)
self.assertFalse(self.world.game_map[1][0].is_occupied_by_game_object(EMP))

# test defusing the same spot causes no errors
def test_defuse_twice(self):
self.test_defuse_trap_above()
self.assertFalse(self.world.game_map[0][1].is_occupied_by_game_object(Landmine))
28 changes: 2 additions & 26 deletions game/test_suite/tests/test_dynamite_active_ability.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
import unittest
from game.quarry_rush.ability.dynamite_active_ability import DynamiteActiveAbility
from game.utils.vector import Vector


class TestDynamiteActiveAbility(unittest.TestCase):

"""
This is class that tests the Active Ability
This is class that tests the Dynamite Active Ability
"""

# set up
def setUp(self) -> None:
self.dynamite_active_ability = DynamiteActiveAbility()
self.name: str = ""
self.cooldown: int = 1

# test: name
def test_name(self):
self.name = ""
self.dynamite_active_ability.name = ""
self.assertEqual(self.dynamite_active_ability.name, self.name)

# test: cooldown
def test_cooldown(self):
self.cooldown = 1
Expand Down Expand Up @@ -68,22 +60,6 @@ def test_fuse_fail_negative(self):
self.dynamite_active_ability.fuse = -1
self.assertEqual(str(e.exception), 'DynamiteActiveAbility.fuse cannot be negative')

# test: position
def test_dynamite_active_ability_set_position(self):
self.dynamite_active_ability.position = Vector(10, 10)
self.assertEqual(str(self.dynamite_active_ability.position), str(Vector(10, 10)))

# test: position none
def test_dynamite_active_ability_set_position_None(self):
self.dynamite_active_ability.position = None
self.assertEqual(self.dynamite_active_ability.position, None)

# fail test: position cannot be anything else
# def test_dynamite_active_ability_set_position_fail(self):
# with self.assertRaises(ValueError) as e:
# self.dynamite_active_ability.position = 10
# self.assertEqual(str(e.exception), 'DynamiteActiveAbility.position must be a Vector or None.')

# test: for placing dynamite
def test_placing_dynamite(self):
self.dynamite_active_ability.placing_dynamite = False
Expand Down
67 changes: 67 additions & 0 deletions game/test_suite/tests/test_emp_active_ability.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import unittest
from game.quarry_rush.ability.emp_active_ability import EMPActiveAbility


class TestEMPActiveAbility(unittest.TestCase):
"""
This is class that tests the Trap Defusal Active Ability
"""

# set up
def setUp(self) -> None:
self.emp_active_ability: EMPActiveAbility = EMPActiveAbility()

# test: cooldown
def test_cooldown(self):
self.emp_active_ability.cooldown = 1
self.assertEqual(self.emp_active_ability.cooldown, 1)

# fail test: cooldown CANT be null
def test_cooldown_fail_null(self):
with self.assertRaises(ValueError) as e:
self.emp_active_ability.cooldown = None
self.assertEqual(str(e.exception), 'EMPActiveAbility.cooldown must be an int')

# fail test: cooldown cant be anything else
def test_cooldown_fail_str(self):
with self.assertRaises(ValueError) as e:
self.emp_active_ability.cooldown = ""
self.assertEqual(str(e.exception), 'EMPActiveAbility.cooldown must be an int')

# fail test: cooldown cannot be negative
def test_cooldown_fail_negative(self):
with self.assertRaises(ValueError) as e:
self.emp_active_ability.cooldown = -1
self.assertEqual(str(e.exception), 'EMPActiveAbility.cooldown cannot be negative')

# test: fuse
def test_fuse(self):
self.fuse = 1
self.emp_active_ability.fuse = 1
self.assertEqual(self.emp_active_ability.fuse, self.fuse)

# fail test: fuse CANT be null
def test_fuse_fail_null(self):
with self.assertRaises(ValueError) as e:
self.emp_active_ability.fuse = None
self.assertEqual(str(e.exception), 'EMPActiveAbility.fuse must be an int')

# fail test: fuse cannot be anything else
def test_fuse_fail_str(self):
with self.assertRaises(ValueError) as e:
self.emp_active_ability.fuse = ""
self.assertEqual(str(e.exception), 'EMPActiveAbility.fuse must be an int')

# fail test: fuse cannot be negative
def test_fuse_fail_negative(self):
with self.assertRaises(ValueError) as e:
self.emp_active_ability.fuse = -1
self.assertEqual(str(e.exception), 'EMPActiveAbility.fuse cannot be negative')

# test: json
def test_emp_active_ability_json(self):
data: dict = self.emp_active_ability.to_json()
emp_active_ability: EMPActiveAbility = EMPActiveAbility().from_json(data)
self.assertEqual(self.emp_active_ability.cooldown, emp_active_ability.cooldown)
self.assertEqual(self.emp_active_ability.fuse, emp_active_ability.fuse)
self.assertEqual(self.emp_active_ability.object_type, emp_active_ability.object_type)
68 changes: 68 additions & 0 deletions game/test_suite/tests/test_landmine_active_ability.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import unittest
from game.quarry_rush.ability.landmine_active_ability import LandmineActiveAbility
from game.utils.vector import Vector


class TestLandmineActiveAbility(unittest.TestCase):
"""
This is class that tests the Landmine Active Ability
"""

# set up
def setUp(self) -> None:
self.landmine_active_ability: LandmineActiveAbility = LandmineActiveAbility()

# test: cooldown
def test_cooldown(self):
self.landmine_active_ability.cooldown = 1
self.assertEqual(self.landmine_active_ability.cooldown, 1)

# fail test: cooldown CANT be null
def test_cooldown_fail_null(self):
with self.assertRaises(ValueError) as e:
self.landmine_active_ability.cooldown = None
self.assertEqual(str(e.exception), 'LandmineActiveAbility.cooldown must be an int')

# fail test: cooldown cant be anything else
def test_cooldown_fail_str(self):
with self.assertRaises(ValueError) as e:
self.landmine_active_ability.cooldown = ""
self.assertEqual(str(e.exception), 'LandmineActiveAbility.cooldown must be an int')

# fail test: cooldown cannot be negative
def test_cooldown_fail_negative(self):
with self.assertRaises(ValueError) as e:
self.landmine_active_ability.cooldown = -1
self.assertEqual(str(e.exception), 'LandmineActiveAbility.cooldown cannot be negative')

# test: fuse
def test_fuse(self):
self.fuse = 1
self.landmine_active_ability.fuse = 1
self.assertEqual(self.landmine_active_ability.fuse, self.fuse)

# fail test: fuse CANT be null
def test_fuse_fail_null(self):
with self.assertRaises(ValueError) as e:
self.landmine_active_ability.fuse = None
self.assertEqual(str(e.exception), 'LandmineActiveAbility.fuse must be an int')

# fail test: fuse cannot be anything else
def test_fuse_fail_str(self):
with self.assertRaises(ValueError) as e:
self.landmine_active_ability.fuse = ""
self.assertEqual(str(e.exception), 'LandmineActiveAbility.fuse must be an int')

# fail test: fuse cannot be negative
def test_fuse_fail_negative(self):
with self.assertRaises(ValueError) as e:
self.landmine_active_ability.fuse = -1
self.assertEqual(str(e.exception), 'LandmineActiveAbility.fuse cannot be negative')

# test: json
def test_landmine_active_ability_json(self):
data: dict = self.landmine_active_ability.to_json()
landmine_active_ability: LandmineActiveAbility = LandmineActiveAbility().from_json(data)
self.assertEqual(self.landmine_active_ability.cooldown, landmine_active_ability.cooldown)
self.assertEqual(self.landmine_active_ability.fuse, landmine_active_ability.fuse)
self.assertEqual(self.landmine_active_ability.object_type, landmine_active_ability.object_type)
Loading

0 comments on commit 307eaaa

Please sign in to comment.