Skip to content

Commit 47b5023

Browse files
Merge pull request #2 from JeanVArdent/master
Multi-Avatar
2 parents 8aba07f + 549a3c4 commit 47b5023

32 files changed

Lines changed: 275 additions & 716 deletions

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,12 @@ Changes made in 2023.
123123
* Avatar VS Player Classes
124124
* These two classes are often confused with each other. Here are their differences.
125125
* Avatar:
126-
* The Avatar class represents the character that is in the game. The Avatar is
126+
* The Avatar class represents the character or characters that are in the game. The Avatar is
127127
what moves around, interacts with other GameObjects, gains points, and whatever else
128128
the developers implement.
129129

130130
* Player:
131-
* The Player object encapsulates the Avatar class and represents the physical
131+
* The Player object encapsulates the Avatar class or classes and represents the physical
132132
people competing in the competition. This is why it has parameters such as team_name.
133133
It is also why the controllers take a Player object as a parameter and not an Avatar
134134
object; it helps manage the information gathered for the individual competitors/teams for
@@ -150,6 +150,8 @@ Changes made in 2023.
150150
* Movement Controller Class
151151
* This class manages moving the player through the game board by using the given enums.
152152

153+
* Avatar Selection Controller Class
154+
* This class is used to select different avatars for the player.
153155

154156
* Generate Game File
155157
* This file has a single method that's used to generate the game map. The
@@ -185,6 +187,13 @@ Changes made in 2023.
185187
* Lastly, documentation is a great method to stay organized too. Having someone to
186188
write what is discussed in meetings can be useful; you won't easily lose track of
187189
what's discussed.
190+
* Tools
191+
* Excalidraw - https://excalidraw.com/
192+
* Excalidraw is a collaborative whiteboarding tool which is useful for creating diagrams on the fly
193+
* Mermaid - https://mermaid.js.org/
194+
* Mermaid is a tool to create diagrams, including UML diagrams using text and code
195+
* PlantUML - https://plantuml.com/
196+
* PlantUML is another diagramming tool, similar to mermaid.
188197

189198

190199

base_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def team_name(self):
1515
return 'Team 1'
1616

1717
# This is where your AI will decide what to do
18-
def take_turn(self, turn, actions, world, avatar):
18+
def take_turn(self, turn, actions, world, avatars, avatar):
1919
"""
2020
This is where your AI will decide what to do.
2121
:param turn: The current turn of the game.

base_client_2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def team_name(self):
1515
return 'Team 2'
1616

1717
# This is where your AI will decide what to do
18-
def take_turn(self, turn, actions, world, avatar):
18+
def take_turn(self, turn, actions, world, avatars, avatar):
1919
"""
2020
This is where your AI will decide what to do.
2121
:param turn: The current turn of the game.

game/client/user_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ def debug(self, *args):
1717
def team_name(self):
1818
return "No_Team_Name_Available"
1919

20-
def take_turn(self, turn, actions, world, avatar):
20+
def take_turn(self, turn, actions, world, avatars, selected_avatar):
2121
raise NotImplementedError("Implement this in subclass")

game/common/avatar.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,6 @@ def held_item(self) -> Item | None:
160160
self.__clean_inventory()
161161
return self.inventory[self.__held_index]
162162

163-
@property
164-
def score(self) -> int:
165-
return self.__score
166-
167163
@property
168164
def position(self) -> Vector | None:
169165
return self.__position
@@ -194,14 +190,6 @@ def held_item(self, item: Item | None) -> None:
194190
# If the item is contained in the inventory, set the held_index to that item's index
195191
self.__held_index = self.inventory.index(item)
196192

197-
@score.setter
198-
def score(self, score: int) -> None:
199-
if score is None or not isinstance(score, int):
200-
raise ValueError(
201-
f'{self.__class__.__name__}.score must be an int. It is a(n) {score.__class__.__name__} and has the value of '
202-
f'{score}')
203-
self.__score: int = score
204-
205193
@position.setter
206194
def position(self, position: Vector | None) -> None:
207195
if position is not None and not isinstance(position, Vector):

game/common/enums.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ class ActionType(Enum):
5959
PLACE_ITEM_DOWN = auto()
6060
PLACE_ITEM_LEFT = auto()
6161
PLACE_ITEM_RIGHT = auto()
62+
SELECT_DEFAULT_AVATAR = auto()
6263
"""
6364
These last 10 enums are for selecting a slot from the Avatar class' inventory.
6465
You can add/remove these as needed for the purposes of your game.
65-
"""
66+
"""

game/common/map/game_board.py

Lines changed: 15 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ class GameBoard(GameObject):
2020
2121
Map Size:
2222
---------
23-
map_size is a Vector object, allowing you to specify the size of the (x, y) plane of the game board.
24-
For example, a Vector object with an 'x' of 5 and a 'y' of 7 will create a board 5 tiles wide and
23+
`map_size` is a Vector object, allowing you to specify the size of the (x, y) plane of the game board.
24+
For example, a Vector object with an 'x' of 5 and a 'y' of 7 will set a boarder of 5 tiles wide and
2525
7 tiles long.
2626
2727
Example:
@@ -39,70 +39,21 @@ class GameBoard(GameObject):
3939
4040
Locations:
4141
----------
42-
This is the bulkiest part of the generation.
43-
44-
The locations field is a dictionary with a key of a tuple of Vectors, and the value being a list of
45-
GameObjects (the key **must** be a tuple instead of a list because Python requires dictionary keys to be
46-
immutable).
47-
48-
This is used to assign the given GameObjects the given coordinates via the Vectors. This is done in two ways:
49-
50-
Statically:
51-
If you want a GameObject to be at a specific coordinate, ensure that the key-value pair is
52-
*ONE* Vector and *ONE* GameObject.
53-
An example of this would be the following:
54-
::
55-
locations = { (vector_2_4) : [station_0] }
56-
57-
In this example, vector_2_4 contains the coordinates (2, 4). (Note that this naming convention
58-
isn't necessary, but was used to help with the concept). Furthermore, station_0 is the
59-
GameObject that will be at coordinates (2, 4).
60-
61-
Dynamically:
62-
If you want to assign multiple GameObjects to different coordinates, use a key-value
63-
pair of any length.
64-
65-
**NOTE**: The length of the tuple and list *MUST* be equal, otherwise it will not
66-
work. In this case, the assignments will be random. An example of this would be the following:
67-
::
68-
locations =
69-
{
70-
(vector_0_0, vector_1_1, vector_2_2) : [station_0, station_1, station_2]
71-
}
72-
73-
(Note that the tuple and list both have a length of 3).
74-
75-
When this is passed in, the three different vectors containing coordinates (0, 0), (1, 1), or
76-
(2, 2) will be randomly assigned station_0, station_1, or station_2.
77-
78-
If station_0 is randomly assigned at (1, 1), station_1 could be at (2, 2), then station_2 will be at (0, 0).
79-
This is just one case of what could happen.
80-
81-
Lastly, another example will be shown to explain that you can combine both static and
82-
dynamic assignments in the same dictionary:
83-
::
84-
locations =
85-
{
86-
(vector_0_0) : [station_0],
87-
(vector_0_1) : [station_1],
88-
(vector_1_1, vector_1_2, vector_1_3) : [station_2, station_3, station_4]
89-
}
90-
91-
In this example, station_0 will be at vector_0_0 without interference. The same applies to
92-
station_1 and vector_0_1. However, for vector_1_1, vector_1_2, and vector_1_3, they will randomly
93-
be assigned station_2, station_3, and station_4.
42+
The locations field is a dictionary with a key of a vector with a value of GameObjectContainer.
43+
Every object in a GameObjectContainer will be store at the specified location.
44+
Each container will function like a stack, where you can only place a new object at the top of the stack.
9445
9546
-----
9647
9748
Walled:
9849
-------
9950
This is simply a bool value that will create a wall barrier on the boundary of the game_board. If
100-
walled is True, the wall will be created for you.
51+
walled is True, the wall will be generated.
10152
102-
For example, let the dimensions of the map be (5, 7). There will be wall Objects horizontally across
103-
x = 0 and x = 4. There will also be wall Objects vertically at y = 0 and y = 6
53+
For example, let the dimensions of the map be (5, 7). There will be Wall objects horizontally across
54+
x = 0 and x = 4. There will also be Wall objects vertically at y = 0 and y = 6
10455
105-
Below is a visual example of this, with 'x' being where the wall Objects are.
56+
Below is a visual example of this, with 'x' being where the Wall objects are.
10657
10758
Example:
10859
::
@@ -206,10 +157,10 @@ def walled(self, walled: bool) -> None:
206157
self.__walled = walled
207158

208159
def generate_map(self) -> None:
209-
# Dictionary Init
210-
self.game_map = self.__map_init()
211-
212-
def __map_init(self) -> dict[Vector, GameObjectContainer]:
160+
"""
161+
Populates the game map based off self.locations.
162+
:return: None
163+
"""
213164
output: dict[Vector, GameObjectContainer] = dict()
214165

215166
# Update all Avatar positions if they are to be placed on the map
@@ -229,7 +180,7 @@ def __map_init(self) -> dict[Vector, GameObjectContainer]:
229180

230181
# convert locations dict to go_container
231182
output.update({vec: GameObjectContainer(objs) for vec, objs in self.locations.items()})
232-
return output
183+
self.game_map = output
233184

234185
def get(self, coords: Vector) -> GameObjectContainer | None:
235186
"""
@@ -322,7 +273,7 @@ def is_occupiable(self, coords: Vector) -> bool:
322273
isinstance(self.get(coords).get_top(), Occupiable))
323274

324275
# Returns the Vector and a list of GameObject for whatever objects you are trying to get
325-
# CHANGE RETURN TYPE TO BE A DICT NOT A LIST OF TUPLES
276+
# TODO: CHANGE RETURN TYPE TO BE A DICT NOT A LIST OF TUPLES
326277
def get_objects(self, look_for: ObjectType) -> list[tuple[Vector, list[GameObject]]]:
327278
"""
328279
Zips together the game map's keys and values. A nested for loop then iterates through the zipped lists, and

game/common/map/game_object_container.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
class GameObjectContainer(GameObject):
1313
"""
1414
This class encapsulates all objects that are to be stored at a coordinate in the GameBoard.
15+
The methods in this class handle the logic for storing and removing objects in the GameBoard.
16+
This helps with abstraction and ensures that the GameBoard has cleaner code.
1517
"""
1618

1719
def __init__(self, objects: list[GameObject] | None = None):
@@ -21,6 +23,11 @@ def __init__(self, objects: list[GameObject] | None = None):
2123
self.place_all(objects)
2224

2325
def place_all(self, game_objs: list[GameObject] | None) -> bool:
26+
"""
27+
Populates this GameObjectContainer with all given objects.
28+
:param game_objs:
29+
:return bool: True if placing all objects is successful, False otherwise
30+
"""
2431
if game_objs is None:
2532
return False
2633

@@ -101,6 +108,10 @@ def remove(self, object_type: ObjectType) -> GameObject | None:
101108
return None
102109

103110
def get_top(self) -> GameObject | None:
111+
"""
112+
Returns the last object in self.__sublist or None if the list is empty.
113+
:return: GameObject or None
114+
"""
104115
return self.__sublist[-1] if self.__sublist is not None \
105116
and len(self.__sublist) > 0 else None
106117

game/common/map/occupiable.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1+
from typing import Self
2+
13
from game.common.enums import ObjectType
24
from game.common.game_object import GameObject
3-
from game.common.items.item import Item
4-
from typing import Self, Type
5-
from game.common.avatar import Avatar
65

76

87
class Occupiable(GameObject):

game/common/map/tile.py

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,18 @@
11
from game.common.map.occupiable import Occupiable
22
from game.common.enums import ObjectType
3-
from game.common.game_object import GameObject
4-
from game.common.avatar import Avatar
5-
from game.common.stations.occupiable_station import OccupiableStation
6-
from game.common.stations.station import Station
7-
from game.common.map.wall import Wall
83
from typing import Self
94

105

116
class Tile(Occupiable):
127
"""
138
`Tile Class Notes:`
149
15-
Tiles are used as a placeholder object when generating the GameBoard.
10+
Tiles are used as a placeholder object when visualizing the GameBoard.
1611
17-
When the GameBoard is generated, it's specified where certain objects need to go. However, not every space on
18-
the map may have something there. If that is the case, a Tile is used to ensure `None` values are avoided.
19-
20-
When something is meant to be placed on a Tile object, it will simply be appended to it.
21-
22-
Here is an example:
23-
24-
Current: [Tile]
25-
Object to place: Station
26-
Result: [Tile, Station]
27-
28-
The additional objects are appended since it's more time efficient.
29-
30-
Tiles are a great way to represent the floor when nothing else is present, but once something else should be
31-
there, that should be the focus.
32-
33-
If the game being developed requires different tiles with special properties, future classes may be added and
34-
inherit from this class, and the rule about the Tile objects being replaced can always be removed.
12+
Tile objects are never stored in the gameboard to ensure a faster generation.
13+
Since this is the case, if there is no GameObjectContainer at a specific coordinate, the visualizer will default
14+
to a tile sprite. Therefore, this file is still necessary for tile objects to be written to and from the json.
15+
⚠️⚠️⚠️DO NOT DELETE THIS FILE⚠️⚠️⚠️
3516
"""
3617
def __init__(self):
3718
super().__init__()

0 commit comments

Comments
 (0)