Skip to content

Commit

Permalink
feat: Two artefacts (#1480)
Browse files Browse the repository at this point in the history
* fix: load correct worksheet in aimmo-game

* Merge branch 'development' into two-artefacts

* Merge branch 'development' into two-artefacts

* added 2 types of artefacts

PickupUpdater now accepts types

* fix wrong arefact model being shown sometimes

* Merge branch 'development' into two-artefacts

* fix failing game test

* test key and chest artefacts

* test worksheet 3 avatar state

* test editing interactable with the same type

* fix interactable flicker when editing

* test artefact repr

* artefact -> yellow_orb

* group all artefacts into one file
  • Loading branch information
razvan-pro committed Mar 16, 2021
1 parent cd27590 commit 69fbd60
Show file tree
Hide file tree
Showing 25 changed files with 583 additions and 66 deletions.
2 changes: 1 addition & 1 deletion aimmo-game-creator/game_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ def _create_game_server_allocation(
},
},
)
if result["status"]["state"] == "UnAllocated" and retry_count < 5:
if result["status"]["state"] == "UnAllocated" and retry_count < 60:
LOGGER.warning(
f"Failed to create game, retrying... retry_count={retry_count}"
)
Expand Down
4 changes: 2 additions & 2 deletions aimmo-game/simulation/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
PickedUpEvent,
ReceivedAttackEvent,
)
from simulation.interactables.pickups import Artefact
from simulation.interactables.pickups.artefacts import _Artefact

if TYPE_CHECKING:
from simulation.world_map import WorldMap
Expand Down Expand Up @@ -76,7 +76,7 @@ def __init__(self, avatar):

def _is_legal(self, world_map):
current_cell = world_map.get_cell(self.avatar.location)
cell_has_artefact = issubclass(type(current_cell.interactable), Artefact)
cell_has_artefact = issubclass(type(current_cell.interactable), _Artefact)
return cell_has_artefact and self.avatar.backpack_has_space()

def _apply(self, world_map):
Expand Down
4 changes: 2 additions & 2 deletions aimmo-game/simulation/avatar/avatar_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
LOGGER = logging.getLogger(__name__)

if TYPE_CHECKING:
from simulation.interactables.pickups.artefact import Artefact
from simulation.interactables.pickups.artefacts import _Artefact


class AvatarWrapper(object):
Expand All @@ -23,7 +23,7 @@ def __init__(self, player_id, initial_location, avatar_appearance):
self.orientation = "north"
self.health = 5
self.score = 0
self.backpack: "List[Artefact]" = []
self.backpack: "List[_Artefact]" = []
self.BACKPACK_SIZE = 10
self.events = []
self.avatar_appearance = avatar_appearance
Expand Down
2 changes: 1 addition & 1 deletion aimmo-game/simulation/cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def __init__(self, location: Location, partially_fogged=False):
self.actions = []

def __repr__(self):
return "Cell({} h={} a={} i={} f{})".format(
return "Cell({} h={} a={} i={} f={})".format(
self.location,
self.habitable,
self.avatar,
Expand Down
39 changes: 25 additions & 14 deletions aimmo-game/simulation/game_logic/map_updaters.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import math
import random
from abc import ABCMeta, abstractmethod
from collections import namedtuple
from collections import Counter, namedtuple
from logging import getLogger
from typing import List, Type

from simulation.cell import Cell
from simulation.interactables.pickups import Artefact
from simulation.interactables.interactable import _Interactable
from simulation.interactables.score_location import ScoreLocation
from simulation.location import Location

Expand Down Expand Up @@ -50,19 +51,29 @@ class PickupUpdater(_MapUpdater):
Generates artefacts based on the TARGET_NUM_PICKUPS_PER_AVATAR setting.
"""

def __init__(self, pickup_types: List[Type[_Interactable]]) -> None:
super().__init__()
self.pickup_types = pickup_types

def update(self, world_map, context):
target_num_pickups = int(
math.ceil(
context.num_avatars
* world_map.settings["TARGET_NUM_PICKUPS_PER_AVATAR"]
)
target_total_num_pickups = (
context.num_avatars * world_map.settings["TARGET_NUM_PICKUPS_PER_AVATAR"]
)
max_num_pickups_to_add = target_num_pickups - len(
list(world_map.pickup_cells())
target_num_pickups_per_type = int(
math.ceil(target_total_num_pickups / len(self.pickup_types))
)
locations = world_map._spawn_location_finder.get_random_spawn_locations(
max_num_pickups_to_add
current_num_pickups = Counter(
type(cell.interactable) for cell in world_map.pickup_cells()
)
for cell in locations:
cell.interactable = Artefact(cell)
LOGGER.info("Adding new pickup at %s of type %s", cell, cell.interactable)
for pickup_type in self.pickup_types:
max_num_pickups_to_add = (
target_num_pickups_per_type - current_num_pickups[pickup_type]
)
locations = world_map._spawn_location_finder.get_random_spawn_locations(
max_num_pickups_to_add
)
for cell in locations:
cell.interactable = pickup_type(cell)
LOGGER.info(
"Adding new pickup at %s of type %s", cell, cell.interactable
)
22 changes: 13 additions & 9 deletions aimmo-game/simulation/interactables/pickups/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from simulation.interactables.pickups.artefact import Artefact
from simulation.interactables.pickups.artefacts import (
ChestArtefact,
KeyArtefact,
YellowOrbArtefact,
)
from simulation.interactables.pickups.damage_boost_pickup import DamageBoostPickup
from simulation.interactables.pickups.health_pickup import HealthPickup
from simulation.interactables.pickups.invulnerability_pickup import (
Expand All @@ -10,11 +14,11 @@ def serialize_pickups(world_map):
return [cell.interactable.serialize() for cell in world_map.pickup_cells()]


ALL_PICKUPS = (DamageBoostPickup, InvulnerabilityPickup, HealthPickup, Artefact)
__all__ = [
"ALL_PICKUPS",
"DamageBoostPickup",
"InvulnerabilityPickup",
"HealthPickup",
"Artefact",
]
ALL_PICKUPS = (
DamageBoostPickup,
InvulnerabilityPickup,
HealthPickup,
YellowOrbArtefact,
ChestArtefact,
KeyArtefact,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,27 @@
from simulation.interactables.interactable import _Interactable


class Artefact(_Interactable):
class _Artefact(_Interactable):
"""
Base artefact class. self._type should be overridden in __init__.
"""

def __init__(self, cell):
super(Artefact, self).__init__(cell)
super().__init__(cell)
self.delete_after_effects_applied = True
self.in_backpack = False
self.conditions = [avatar_on_cell, in_backpack]
self.effects.append(ArtefactEffect)
self._type = "_artefact"

def get_targets(self):
return [self.cell.avatar]

def __repr__(self):
return "Artefact(Location={})".format(self.cell.location)
return f"{type(self).__name__}(Location={self.cell.location})"

def serialize(self):
serialized_artefact = {"type": "artefact"}
serialized_artefact = {"type": self._type}

if not self.in_backpack:
serialized_artefact["location"] = {
Expand All @@ -27,3 +32,21 @@ def serialize(self):
}

return serialized_artefact


class ChestArtefact(_Artefact):
def __init__(self, cell):
super().__init__(cell)
self._type = "chest"


class KeyArtefact(_Artefact):
def __init__(self, cell):
super().__init__(cell)
self._type = "key"


class YellowOrbArtefact(_Artefact):
def __init__(self, cell):
super().__init__(cell)
self._type = "yellow_orb"
4 changes: 2 additions & 2 deletions aimmo-game/simulation/simulation_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ def update(self, num_avatars, game_state):

def _update_map(self, num_avatars):
context = MapContext(num_avatars=num_avatars)
for MapUpdater in self.worksheet.map_updaters:
MapUpdater().update(self.game_state.world_map, context=context)
for map_updater in self.worksheet.map_updaters:
map_updater.update(self.game_state.world_map, context=context)

def add_avatar(self, player_id, location=None):
with self._lock:
Expand Down
9 changes: 9 additions & 0 deletions aimmo-game/simulation/worksheet/avatar_state_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,12 @@ def worksheet2_avatar_state_serializer(avatar: AvatarWrapper) -> Dict:
"orientation": avatar.orientation,
"backpack": [artefact.serialize() for artefact in avatar.backpack],
}


def worksheet3_avatar_state_serializer(avatar: AvatarWrapper) -> Dict:
return {
"location": avatar.location.serialize(),
"id": avatar.player_id,
"orientation": avatar.orientation,
"backpack": [artefact.serialize() for artefact in avatar.backpack],
}
18 changes: 16 additions & 2 deletions aimmo-game/simulation/worksheet/worksheet.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@
from typing import TYPE_CHECKING

from simulation.game_logic.map_updaters import PickupUpdater
from simulation.interactables.interactable import _Interactable
from simulation.interactables.pickups import (
YellowOrbArtefact,
ChestArtefact,
KeyArtefact,
)
from .avatar_state_serializers import (
worksheet1_avatar_state_serializer,
worksheet2_avatar_state_serializer,
worksheet3_avatar_state_serializer,
)

if TYPE_CHECKING:
Expand All @@ -37,17 +44,24 @@ class WorksheetData:
1: WorksheetData(
worksheet_id=1,
era="future",
map_updaters=[PickupUpdater],
map_updaters=[PickupUpdater(pickup_types=[YellowOrbArtefact])],
number_of_obstacle_textures=1,
avatar_state_serializer=worksheet1_avatar_state_serializer,
),
2: WorksheetData(
worksheet_id=2,
era="future",
map_updaters=[PickupUpdater],
map_updaters=[PickupUpdater(pickup_types=[YellowOrbArtefact])],
number_of_obstacle_textures=1,
avatar_state_serializer=worksheet2_avatar_state_serializer,
),
3: WorksheetData(
worksheet_id=3,
era="ancient",
map_updaters=[PickupUpdater(pickup_types=[ChestArtefact, KeyArtefact])],
number_of_obstacle_textures=1,
avatar_state_serializer=worksheet3_avatar_state_serializer,
),
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import pytest

from simulation.action import PickupAction
from simulation.interactables.pickups import Artefact
from simulation.interactables.pickups import (
ChestArtefact,
KeyArtefact,
YellowOrbArtefact,
)
from simulation.location import Location
from tests.test_simulation.dummy_avatar import CustomLiveDummy
from .mock_world import MockWorld


testdata = [
(ChestArtefact, "chest"),
(KeyArtefact, "key"),
(YellowOrbArtefact, "yellow_orb"),
]


@pytest.fixture
def game() -> "MockWorld":
world = MockWorld(dummies_list=[CustomLiveDummy])
Expand All @@ -19,21 +30,23 @@ def cell(game):
return game.game_state.world_map.get_cell(Location(1, 0))


def test_artefact_serialization(cell):
artefact = Artefact(cell)
@pytest.mark.parametrize("artefact_class, artefact_type", testdata)
def test_artefact_serialization(cell, artefact_class, artefact_type):
artefact = artefact_class(cell)
assert artefact.serialize() == {
"type": "artefact",
"type": artefact_type,
"location": {"x": cell.location.x, "y": cell.location.y},
}

artefact.in_backpack = True
assert artefact.serialize() == {"type": "artefact"}
assert artefact.serialize() == {"type": artefact_type}


@pytest.mark.asyncio
async def test_artefact_applies_correctly(game, cell):
@pytest.mark.parametrize("artefact_class, artefact_type", testdata)
async def test_artefact_applies_correctly(game, cell, artefact_class, artefact_type):
avatar: "CustomLiveDummy" = game.avatar_manager.get_avatar(1)
artefact = Artefact(cell)
artefact = artefact_class(cell)
cell.interactable = artefact

# Move to the cell with the artefact
Expand All @@ -56,3 +69,9 @@ async def test_artefact_applies_correctly(game, cell):
assert cell.interactable is None
assert len(avatar.backpack) == 1
assert avatar.backpack == [artefact]


@pytest.mark.parametrize("artefact_class, artefact_type", testdata)
def test_artefact_repr(cell, artefact_class, artefact_type):
artefact = artefact_class(cell)
assert repr(artefact) == f"{type(artefact).__name__}(Location={cell.location})"
12 changes: 7 additions & 5 deletions aimmo-game/tests/test_simulation/test_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from simulation.avatar.avatar_manager import AvatarManager
from simulation.direction import EAST
from simulation.game_state import GameState
from simulation.interactables.pickups import Artefact
from simulation.interactables.pickups.artefacts import YellowOrbArtefact
from simulation.location import Location
from .dummy_avatar import MoveDummy
from .maps import InfiniteMap, EmptyMap, AvatarMap, PickupMap
Expand Down Expand Up @@ -129,15 +129,15 @@ def test_no_move_in_wait(self):
assert self.avatar.location == ORIGIN

def test_successful_pickup_action(self):
game_state = GameState(PickupMap(Artefact), self.avatar_manager)
game_state = GameState(PickupMap(YellowOrbArtefact), self.avatar_manager)
game_state.world_map.setup_cell(self.avatar.location)
artefact = game_state.world_map.get_cell(self.avatar.location).interactable

assert artefact.in_backpack == False

action.PickupAction(self.avatar).process(game_state.world_map)

assert self.avatar.events == [event.PickedUpEvent({"type": "artefact"})]
assert self.avatar.events == [event.PickedUpEvent({"type": "yellow_orb"})]
assert artefact.in_backpack == True

def test_failed_pickup_action(self):
Expand All @@ -148,11 +148,13 @@ def test_failed_pickup_action(self):
assert self.avatar.events == [event.FailedPickupEvent()]

def test_failed_pickup_action_if_backpack_full(self):
game_state = GameState(PickupMap(Artefact), self.avatar_manager)
game_state = GameState(PickupMap(YellowOrbArtefact), self.avatar_manager)
game_state.world_map.setup_cell(self.avatar.location)
artefact = game_state.world_map.get_cell(self.avatar.location).interactable

self.avatar.backpack = [Artefact for _ in range(self.avatar.BACKPACK_SIZE)]
self.avatar.backpack = [
YellowOrbArtefact for _ in range(self.avatar.BACKPACK_SIZE)
]

action.PickupAction(self.avatar).process(game_state.world_map)

Expand Down
Loading

0 comments on commit 69fbd60

Please sign in to comment.