From ee255099753712cfac19c4022a9bd550e5d34e9f Mon Sep 17 00:00:00 2001 From: LukeSkiiWalker <58610760+LukeSkiiWalker@users.noreply.github.com> Date: Fri, 21 May 2021 20:14:00 +0200 Subject: [PATCH 1/7] added OnCollisionComponent class and changed all tuples and lists to Upper case --- survival/__init__.py | 2 ++ survival/biomes/biome_preset.py | 3 ++- survival/components/OnCollisionComponent.py | 10 ++++++++++ survival/components/inventory_component.py | 2 +- survival/entity_layer.py | 3 +++ survival/game_map.py | 3 +++ survival/generators/building_generator.py | 2 +- survival/generators/player_generator.py | 2 ++ survival/generators/resource_generator.py | 3 +++ survival/generators/tile_generator.py | 3 ++- survival/graph_search.py | 9 +++++---- survival/systems/collision_system.py | 8 +++++++- 12 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 survival/components/OnCollisionComponent.py diff --git a/survival/__init__.py b/survival/__init__.py index 6a9abf7..0adb37e 100644 --- a/survival/__init__.py +++ b/survival/__init__.py @@ -3,6 +3,7 @@ import pygame from settings import SCREEN_WIDTH, SCREEN_HEIGHT from survival.camera import Camera from survival.game_map import GameMap +from survival.generators.building_generator import BuildingGenerator from survival.generators.player_generator import PlayerGenerator from survival.generators.resource_generator import ResourceGenerator from survival.generators.world_generator import WorldGenerator @@ -20,6 +21,7 @@ if __name__ == '__main__': world = WorldGenerator().create_world(camera, game_map) player = PlayerGenerator().create_player(world, game_map) + building = BuildingGenerator().create_home(world, game_map) ResourceGenerator(world, game_map).generate_resources() diff --git a/survival/biomes/biome_preset.py b/survival/biomes/biome_preset.py index b11cd3c..f1289b3 100644 --- a/survival/biomes/biome_preset.py +++ b/survival/biomes/biome_preset.py @@ -1,10 +1,11 @@ import random +from typing import List from survival.tile import Tile class BiomePreset: - def __init__(self, name, min_height: float, min_moisture: float, min_heat: float, tiles: list[Tile]): + def __init__(self, name, min_height: float, min_moisture: float, min_heat: float, tiles: List[Tile]): self.name = name self.min_height = min_height self.min_moisture = min_moisture diff --git a/survival/components/OnCollisionComponent.py b/survival/components/OnCollisionComponent.py new file mode 100644 index 0000000..629967a --- /dev/null +++ b/survival/components/OnCollisionComponent.py @@ -0,0 +1,10 @@ +class OnCollisionComponent: + def __init__(self, callbacks: [] = []): + self.callbacks = callbacks + + def callAll(self): + for func in self.callbacks: + func() + + def addCallback(self, fn): + self.callbacks.append(fn) \ No newline at end of file diff --git a/survival/components/inventory_component.py b/survival/components/inventory_component.py index 18d39f9..d321739 100644 --- a/survival/components/inventory_component.py +++ b/survival/components/inventory_component.py @@ -1,5 +1,5 @@ class InventoryComponent: - def __init__(self, maxitems): + def __init__(self, maxitems = 10): self.maxitems = maxitems self.items = {} diff --git a/survival/entity_layer.py b/survival/entity_layer.py index d934b6f..cd7a9cd 100644 --- a/survival/entity_layer.py +++ b/survival/entity_layer.py @@ -18,5 +18,8 @@ class EntityLayer: def remove_entity(self, pos): self.tiles[pos[1]][pos[0]] = None + def get_entity(self, pos) -> int: + return self.tiles[pos[1]][pos[0]] + def is_colliding(self, pos): return self.tiles[pos[1]][pos[0]] is not None diff --git a/survival/game_map.py b/survival/game_map.py index f8dca9e..36c04af 100644 --- a/survival/game_map.py +++ b/survival/game_map.py @@ -22,6 +22,9 @@ class GameMap: def remove_entity(self, pos): self.entity_layer.remove_entity(pos) + def get_entity(self, pos) -> int: + return self.entity_layer.get_entity(pos) + def is_colliding(self, pos): return pos[0] < 0 or pos[0] >= self.width or pos[1] < 0 or pos[1] >= self.height or self.entity_layer.is_colliding(pos) diff --git a/survival/generators/building_generator.py b/survival/generators/building_generator.py index bdfeacf..79ce688 100644 --- a/survival/generators/building_generator.py +++ b/survival/generators/building_generator.py @@ -12,7 +12,7 @@ class BuildingGenerator: world.add_component(home, InventoryComponent()) game_map.add_entity(home, pos) - sprite = SpriteComponent('stone.png') + sprite = SpriteComponent('tree.png') sprite.set_scale(2) world.add_component(home, sprite) world.add_component(home, CollisionComponent()) diff --git a/survival/generators/player_generator.py b/survival/generators/player_generator.py index 4a24660..1dc8014 100644 --- a/survival/generators/player_generator.py +++ b/survival/generators/player_generator.py @@ -1,3 +1,4 @@ +from survival.components.OnCollisionComponent import OnCollisionComponent from survival.components.camera_target_component import CameraTargetComponent from survival.components.input_component import InputComponent from survival.components.movement_component import MovementComponent @@ -14,6 +15,7 @@ class PlayerGenerator: world.add_component(player, pos) world.add_component(player, MovementComponent()) world.add_component(player, InputComponent()) + world.add_component(player, OnCollisionComponent()) camera_target = CameraTargetComponent(pos) world.add_component(player, camera_target) game_map.add_entity(player, pos) diff --git a/survival/generators/resource_generator.py b/survival/generators/resource_generator.py index fe36c9a..a625709 100644 --- a/survival/generators/resource_generator.py +++ b/survival/generators/resource_generator.py @@ -1,5 +1,6 @@ import random +from survival.components.OnCollisionComponent import OnCollisionComponent from survival.components.position_component import PositionComponent from survival.components.sprite_component import SpriteComponent from survival.settings import RESOURCES_AMOUNT @@ -20,8 +21,10 @@ class ResourceGenerator: pos = PositionComponent(empty_pos, empty_grid_pos) sprite = SpriteComponent(random.choice(sprites)) + col = OnCollisionComponent() self.world.add_component(obj, pos) self.world.add_component(obj, sprite) + self.world.add_component(obj, col) self.map.add_entity(obj, pos) def get_empty_grid_position(self): diff --git a/survival/generators/tile_generator.py b/survival/generators/tile_generator.py index 3fc133d..57305a8 100644 --- a/survival/generators/tile_generator.py +++ b/survival/generators/tile_generator.py @@ -1,4 +1,5 @@ import random +from typing import List from survival.biomes.biome_data import BiomeData from survival.biomes.biome_preset import BiomePreset @@ -34,7 +35,7 @@ class TileGenerator: return Tile(origin=tile.origin, cost=tile.cost) @staticmethod - def generate_random_tiles(width: int, height: int) -> list[list[Tile]]: + def generate_random_tiles(width: int, height: int) -> List[List[Tile]]: return [[TileGenerator.get_random_tile() for _ in range(width)] for _ in range(height)] @staticmethod diff --git a/survival/graph_search.py b/survival/graph_search.py index 77b4138..76c9f6e 100644 --- a/survival/graph_search.py +++ b/survival/graph_search.py @@ -1,5 +1,6 @@ from enum import Enum from queue import PriorityQueue +from typing import Tuple, List from survival import GameMap from survival.components.position_component import PositionComponent @@ -13,7 +14,7 @@ class Action(Enum): class State: - def __init__(self, position: tuple[int, int], direction: Direction): + def __init__(self, position: Tuple[int, int], direction: Direction): self.position = position self.direction = direction @@ -32,12 +33,12 @@ class Node: return self.cost == other.cost -def get_moved_position(position: tuple[int, int], direction: Direction): +def get_moved_position(position: Tuple[int, int], direction: Direction): vector = Direction.get_vector(direction) return position[0] + vector[0], position[1] + vector[1] -def get_states(state: State, game_map: GameMap) -> list[tuple[Action, State, int]]: +def get_states(state: State, game_map: GameMap) -> List[Tuple[Action, State, int]]: states = list() states.append((Action.ROTATE_LEFT, State(state.position, state.direction.rotate_left(state.direction)), 1)) @@ -63,7 +64,7 @@ def build_path(node: Node): return actions -def heuristic(new_node: Node, goal: tuple[int, int]): +def heuristic(new_node: Node, goal: Tuple[int, int]): return abs(new_node.state.position[0] - goal[0]) + abs(new_node.state.position[1] - goal[1]) diff --git a/survival/systems/collision_system.py b/survival/systems/collision_system.py index 5e0bea2..8e44d41 100644 --- a/survival/systems/collision_system.py +++ b/survival/systems/collision_system.py @@ -1,6 +1,7 @@ import operator from survival import esper +from survival.components.OnCollisionComponent import OnCollisionComponent from survival.components.moving_component import MovingComponent from survival.components.position_component import PositionComponent from survival.enums import Direction @@ -11,7 +12,7 @@ class CollisionSystem(esper.Processor): self.map = game_map def process(self, dt): - for ent, (pos, moving) in self.world.get_components(PositionComponent, MovingComponent): + for ent, (pos, moving, onCol) in self.world.get_components(PositionComponent, MovingComponent, OnCollisionComponent): if moving.target is not None: continue @@ -22,6 +23,11 @@ class CollisionSystem(esper.Processor): moving.direction_vector = vector if self.check_collision(moving.target): self.world.remove_component(ent, MovingComponent) + onCol.callAll() + colliding_object : int = self.map.get_entity(moving.target) + if self.world.has_component(colliding_object, OnCollisionComponent): + self.world.component_for_entity(colliding_object, OnCollisionComponent).callAll() + else: self.map.move_entity(pos.grid_position, moving.target) pos.grid_position = moving.target From f449cec09098f319a8746c62e7126c6cf000b958 Mon Sep 17 00:00:00 2001 From: LukeSkiiWalker <58610760+LukeSkiiWalker@users.noreply.github.com> Date: Sat, 22 May 2021 18:32:36 +0200 Subject: [PATCH 2/7] BuildingGenerator creates house with 2 chests. --- assets/atlas.png | Bin 20010 -> 21480 bytes assets/chest.png | Bin 0 -> 885 bytes assets/home.png | Bin 0 -> 1672 bytes survival/generators/building_generator.py | 25 ++++++++++++++++------ 4 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 assets/chest.png create mode 100644 assets/home.png diff --git a/assets/atlas.png b/assets/atlas.png index 46a2db2673e20396319541e4dc6060511946d7be..f4c46822875d1040ced5825db8f6cdbe6b925df7 100644 GIT binary patch literal 21480 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 ?G#{TFL<>*v0j(U-F|fEGU*zM1sQ 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 FV1lQOe98a- diff --git a/assets/chest.png b/assets/chest.png new file mode 100644 index 0000000000000000000000000000000000000000..563166c43304b38cc4daf1ae583f17b938debfce GIT binary patch literal 885 zcmV-*1B(2KP)Lo6=3rVl*@No_oIgo$s7`BT2kZTYKAtcWrk7?f~3> z)8NVRk^Z?jFTS^1yKkCaX9op)^*F8= zIZ_pf=yo4CIX(i=>5G?3lPBX*?gj`HUV)@m2cSO~{>P`;NC7x}{-l(NG6qV6vk;`p zI^CXs341y?XcuFC&1EJ^Q*ikFN!jmrVx<787|89*XPI@mZy> zfWyxpFa56+VV=tfXoNfxbRg$2a{$N;Gxyf?ASgR`N&$2_J!Z2h>v0^^Kewb-Cw}m- z2>9U#<$?QyVJ=l7Al*(^2heOhWNqypz*5<*$1$fbUY6=4r@zw)VhnQI+iN2UGh6bh zOu@Y5=9bjz1)%SL__@T(16)+Ug@+_;RR#do;{+s#=0sjx4*Wl&P~R$cNlBTC5F8uX)^@#iH zUK0`E%rWQ>5s=jDa5JLY?a|!!)pBsq#>}ttcjt_L|1~DqYCc_xwFE#?fk1-S*Ao=^ zLsLMjk4=7F^LS~wG#6WJ;N%nZ|<3DX?V)y<@+>y3>X0)G4b zF905;Tb%WV?C-a*%+I&TFavYoWK6Sc6#&A_FEE)-koi<}&o9WUsF6M(n`QKeH*BVx z?C{6&P^lFcag^IMl9ZW2d=Y0Em;s>!FnZkU5|52v7tH z^vl2`&w^k&TLpk~I1~5c1d~_`0D`7`J&T1}S-2bIjYnWIqzESuL})b;y3Fy_vrobT zo<@ZLc`w&Oz~URfOBC`hFFNOhGs8k^Zrjq0=>N9&ek;BMa0lRDn}6#>e*=ma00000 LNkvXXu0mjfY6YRv literal 0 HcmV?d00001 diff --git a/assets/home.png b/assets/home.png new file mode 100644 index 0000000000000000000000000000000000000000..68837dd3e42666d2ba651a72f57a43d626aca3f0 GIT binary patch literal 1672 zcmV;326y?1P)A6O6~}+)-uvcz#�hansmt1guzrf?&f6sR$$%AhAKH zNT?DYY7tTppt6AqsGx-v5~vHHAVQ!?s>J!QW6yYI-sioC#havd?Pk(S6&wDer}y6I z-E;o;fBxs*E2_%>5^mfF_rux8mK7B$R(S97qI~n2Uyd8H1M+qiICElIopY#ywL*~> zEX~i#51xB*+|X@!TLexWKdO<7n46xUSv3sPz|FNDX`Zn(KPS)r{J}|>aUZ-9r;jgd zHL5Vbf0B3@v(b+!vV^6B)3jT4u3o#TpE>u$xG?wdmI$0av8=5|m7|Z$;(g9Hp8Eqg z*7|(q!iyxykmZFr+RZwbUb~^6{`mXHg@d<3;Pi=Q?KEn<>ya4-!vSBt@H=|_9<6$f z*KV!y#b;iiyU}H7ewKE zbYe8~N@TIdV2$PB*%`j|xsNc}YBEf7=BKCl_W2KUU~U=_*xRX6iyVjNXKAG?Xr(L0+1gteU3)ffHKolR~=%M{|H!}jzX*g7s z^?pW{c`jbLNq-n~WN}Wub76Zqb|rA;j^3~!_&}{9y!?-A^oI!{An#r}z*9f^73@}mGb6*z&+VntbO=fy z5JF(BmyqWL^$60mAWkzP8%WZEEH9`#s5>YMkI&0X6%4}Sfqm4gRf4Mi_0qL_`L-!= z>d|FwHme+*nZi25zIMbg2Wt$9f(in(YKC6yUuP}lZK7`3Ov30@5CK(a)eUzxGm0W` z`T8n@IOXudoP7UhzkQv+spChrQLkYIJOPW~oWY1efV(ue7EIU$QvNRjGJ5eo5EOh+ zLQoJ$lbqlG=@q%R8y;EQF983(dQ(wFL=Y>Kzfm?FU9#oAHSvg@h-@PeLLdYPswl9u zFe{Oh1a@@62hTpLs&HU#ipi$K8AqHeR!X-QL1{G{yQS{RwnBa#t6eqnQBJ# z;sT^}dEu3;DEQB{+X%uhUV8c7Om-Ydj4d0(S;N(p4Rpjh%U$98*Ct67b@1W^gao)myhv6|jb~{8F~_7VLT^ z6oH4P_weMo#}N^nDP{CtIWoVOX01XcayaWS7Ept=4zP&8;=?mMdG2uv9}pC09m|Ji z_{+r`J96zxzz9?-mO}?;nQTSGsSOEA$U6e-?&@;oEY3s$=WvQ$tN zvdoiv4qxr|1|Dq=h1Q;v{A0AkGyGII*NjM(#c9gFvln7^FkuVS-xAaF8;H z3kIoD4W5AurFtSbF{D{Rk^6Efz8`_ydkD~vbDTg@C`JVFLO(4iGEd>FtoDXPVi?9b zUi+*MQ#OY=aiSE3l4Sv71Y;pi1r3T1f)8xN&Jq;mf)l4Xc~O*$hX{iNLQsmr)7?xM z#89s~vOKV{5|idil6gwi14_WEp;L1Vl7I#swcvII9_Uc|PGan#^qt243H%p#{{32q SZhX`L0000 Date: Sun, 23 May 2021 14:00:30 +0200 Subject: [PATCH 3/7] Don't check collision when entity is not found --- survival/systems/collision_system.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/survival/systems/collision_system.py b/survival/systems/collision_system.py index 8e44d41..267b278 100644 --- a/survival/systems/collision_system.py +++ b/survival/systems/collision_system.py @@ -12,7 +12,8 @@ class CollisionSystem(esper.Processor): self.map = game_map def process(self, dt): - for ent, (pos, moving, onCol) in self.world.get_components(PositionComponent, MovingComponent, OnCollisionComponent): + for ent, (pos, moving, onCol) in self.world.get_components(PositionComponent, MovingComponent, + OnCollisionComponent): if moving.target is not None: continue @@ -24,8 +25,8 @@ class CollisionSystem(esper.Processor): if self.check_collision(moving.target): self.world.remove_component(ent, MovingComponent) onCol.callAll() - colliding_object : int = self.map.get_entity(moving.target) - if self.world.has_component(colliding_object, OnCollisionComponent): + colliding_object: int = self.map.get_entity(moving.target) + if colliding_object is not None and self.world.has_component(colliding_object, OnCollisionComponent): self.world.component_for_entity(colliding_object, OnCollisionComponent).callAll() else: From d4b340ba229613c3e0f07585530ee4092cbb7b16 Mon Sep 17 00:00:00 2001 From: Kanewersa <30356293+Kanewersa@users.noreply.github.com> Date: Sun, 23 May 2021 14:33:05 +0200 Subject: [PATCH 4/7] Add arguments to collision callbacks --- survival/components/OnCollisionComponent.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/survival/components/OnCollisionComponent.py b/survival/components/OnCollisionComponent.py index 629967a..49e3feb 100644 --- a/survival/components/OnCollisionComponent.py +++ b/survival/components/OnCollisionComponent.py @@ -1,10 +1,15 @@ +from functools import partial + + class OnCollisionComponent: - def __init__(self, callbacks: [] = []): + def __init__(self, callbacks=None): + if callbacks is None: + callbacks = [] self.callbacks = callbacks def callAll(self): for func in self.callbacks: func() - def addCallback(self, fn): - self.callbacks.append(fn) \ No newline at end of file + def addCallback(self, fn, **kwargs): + self.callbacks.append(partial(fn, **kwargs)) From 5ea20bd1c0bdefd511898584e1cde2832076d529 Mon Sep 17 00:00:00 2001 From: Kanewersa <30356293+Kanewersa@users.noreply.github.com> Date: Sun, 23 May 2021 14:33:21 +0200 Subject: [PATCH 5/7] Remove resources on collision --- survival/generators/resource_generator.py | 9 +++++++++ survival/systems/collision_system.py | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/survival/generators/resource_generator.py b/survival/generators/resource_generator.py index a625709..ada4820 100644 --- a/survival/generators/resource_generator.py +++ b/survival/generators/resource_generator.py @@ -1,8 +1,10 @@ import random +from survival import GameMap from survival.components.OnCollisionComponent import OnCollisionComponent from survival.components.position_component import PositionComponent from survival.components.sprite_component import SpriteComponent +from survival.esper import World from survival.settings import RESOURCES_AMOUNT @@ -22,6 +24,7 @@ class ResourceGenerator: pos = PositionComponent(empty_pos, empty_grid_pos) sprite = SpriteComponent(random.choice(sprites)) col = OnCollisionComponent() + col.addCallback(self.remove_resource, world=self.world, game_map=self.map, entity=obj) self.world.add_component(obj, pos) self.world.add_component(obj, sprite) self.world.add_component(obj, col) @@ -32,3 +35,9 @@ class ResourceGenerator: while self.map.is_colliding(free_pos): free_pos = [random.randrange(self.map.width), random.randrange(self.map.height)] return free_pos + + @staticmethod + def remove_resource(world: World, game_map: GameMap, entity: int): + pos = world.component_for_entity(entity, PositionComponent) + game_map.remove_entity(pos.grid_position) + world.delete_entity(entity, immediate=True) diff --git a/survival/systems/collision_system.py b/survival/systems/collision_system.py index 267b278..b9cc0f7 100644 --- a/survival/systems/collision_system.py +++ b/survival/systems/collision_system.py @@ -26,7 +26,11 @@ class CollisionSystem(esper.Processor): self.world.remove_component(ent, MovingComponent) onCol.callAll() colliding_object: int = self.map.get_entity(moving.target) - if colliding_object is not None and self.world.has_component(colliding_object, OnCollisionComponent): + + if colliding_object is None or not self.world.entity_exists(colliding_object): + continue + + if self.world.has_component(colliding_object, OnCollisionComponent): self.world.component_for_entity(colliding_object, OnCollisionComponent).callAll() else: From d051f68e52058c1f48f90f89c21d511f1fffbaf5 Mon Sep 17 00:00:00 2001 From: jakand Date: Sun, 23 May 2021 18:03:16 +0200 Subject: [PATCH 6/7] =?UTF-8?q?Posta=C4=87=20=20zwalnia=20gdy=20wchodzi=20?= =?UTF-8?q?na=20p=C5=82ytk=C4=99=20o=20wi=C4=99kszym=20koszcie.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- survival/generators/tile_generator.py | 4 ++-- survival/generators/world_generator.py | 2 +- survival/settings.py | 4 ++-- survival/systems/movement_system.py | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/survival/generators/tile_generator.py b/survival/generators/tile_generator.py index 3fc133d..31abaea 100644 --- a/survival/generators/tile_generator.py +++ b/survival/generators/tile_generator.py @@ -12,8 +12,8 @@ class TileGenerator: "Grass2": Tile(origin=(32, 0), cost=1), "Grass3": Tile(origin=(64, 0), cost=1), "Grass4": Tile(origin=(96, 0), cost=1), - "Sand": Tile(origin=(64, 64), cost=20), - "Puddle": Tile(origin=(96, 64), cost=20), + "Sand": Tile(origin=(64, 64), cost=4), + "Puddle": Tile(origin=(96, 64), cost=5), } TilesValues = list(Tiles.values()) diff --git a/survival/generators/world_generator.py b/survival/generators/world_generator.py index 586564b..bb9b59e 100644 --- a/survival/generators/world_generator.py +++ b/survival/generators/world_generator.py @@ -15,7 +15,7 @@ class WorldGenerator: world = esper.World() world.add_processor(InputSystem(camera)) world.add_processor(CameraSystem(camera)) - world.add_processor(MovementSystem(), priority=1) + world.add_processor(MovementSystem(game_map), priority=1) world.add_processor(CollisionSystem(game_map), priority=2) world.add_processor(DrawSystem(camera)) world.add_processor(TimeSystem()) diff --git a/survival/settings.py b/survival/settings.py index fd6e84f..0deb510 100644 --- a/survival/settings.py +++ b/survival/settings.py @@ -1,4 +1,4 @@ -SCREEN_WIDTH = 1920 -SCREEN_HEIGHT = 1080 +SCREEN_WIDTH = 1000 +SCREEN_HEIGHT = 600 RESOURCES_AMOUNT = 300 DIRECTION_CHANGE_DELAY = 200 diff --git a/survival/systems/movement_system.py b/survival/systems/movement_system.py index 47a5ad1..9299d29 100644 --- a/survival/systems/movement_system.py +++ b/survival/systems/movement_system.py @@ -1,4 +1,4 @@ -from survival import esper +from survival import esper, GameMap from survival.components.movement_component import MovementComponent from survival.components.moving_component import MovingComponent from survival.components.position_component import PositionComponent @@ -6,16 +6,16 @@ from survival.components.sprite_component import SpriteComponent class MovementSystem(esper.Processor): - def __init__(self): - self.map = None + def __init__(self, game_map: GameMap): + self.map = game_map def process(self, dt): for ent, (mov, pos, moving, sprite) in self.world.get_components(MovementComponent, PositionComponent, MovingComponent, SpriteComponent): - - pos.position[0] += moving.direction_vector[0] * mov.speed * dt / 100 - pos.position[1] += moving.direction_vector[1] * mov.speed * dt / 100 + cost = self.map.get_cost(moving.target) + pos.position[0] += moving.direction_vector[0] * mov.speed * dt / 100 / cost + pos.position[1] += moving.direction_vector[1] * mov.speed * dt / 100 / cost if abs(moving.target[0] * 32 - pos.position[0]) < 0.1 * mov.speed and abs( pos.position[1] - moving.target[1] * 32) < 0.1 * mov.speed: From be25865123b1d1541efc26b31321f65281ef6388 Mon Sep 17 00:00:00 2001 From: Kanewersa <30356293+Kanewersa@users.noreply.github.com> Date: Mon, 24 May 2021 13:10:10 +0200 Subject: [PATCH 7/7] Add ui and inventory --- assets/ui.png | Bin 0 -> 712 bytes survival/__init__.py | 7 +++- survival/components/inventory_component.py | 19 ++++------ survival/components/resource_component.py | 3 ++ survival/generators/player_generator.py | 2 + survival/generators/resource_generator.py | 29 +++++++++++--- survival/image.py | 11 ++++-- survival/systems/draw_system.py | 7 ++++ survival/user_interface.py | 42 +++++++++++++++++++++ 9 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 assets/ui.png create mode 100644 survival/components/resource_component.py create mode 100644 survival/user_interface.py diff --git a/assets/ui.png b/assets/ui.png new file mode 100644 index 0000000000000000000000000000000000000000..999de9f8104e67dcca297ab1e591097a75cd66f0 GIT binary patch literal 712 zcmV;(0yq7MP);Dz!+ucz_d_$I~e*d~*SvjR0#0t6+10xLLw=iX+sX@kMQDwT?x zgODSD1rPu(77LqBr#2dmY_(eXLuqye0{{Uv$U*{C5a&VbZnx`$*b%_t5cnBp2N4be zaRUhuB#h`HVLWLTgXZ?b5FdFE7L!0Mr(-0- zxw?P^AUOiN-Of$$1Q|^~Y{14?K`D~d@3bHTI_HAWKQv!TbGjvFffTrjyLi6O0_MD? zTS&kHe?Jq1sr5bvSo@U(1MgM96bU`^#szx0q%?mk0Y+n3*X>D-AIXeZrg#EcY~G=1 z5m7z=h5$)?N^V^67k&@5&&5D0-QFQuCsOkN9TDp@Yd^=w2%y$O&?tZe37Oo8_F@qV z_o#wAHv_N|__zgPmxKnU-sweFc(f3O>wFgQjz&4=H@0Js5V$8{nfC;EFKfqpPqSAg zR^~kcd_X|f>vi9RAST$o-XoK80SjHNR&#?v?=3)cBM4iqmi7C6HzxrG>AedoRm8L< z!~X#&sF~*JKvOJ02H+0_JDxBNLy)4ft(^}*VSz@Yktg7NAq;sTX+shch=IjOXcn=_ ujaopv;Mj$fB9L15h=m;P&6iJ#nSB8(auo=48D?4l0000 self.maxitems: self.items[item] = self.maxitems - def removeItem(self, item, count): - if self.items: + def remove_item(self, item, count): + if item in self.items: self.items[item] = self.items[item] - count - if self.items[item] < 0: - self.items[item] = 0 + if self.items[item] < 0: + self.items[item] = 0 - def hasItem(self, item): - if self.items[item] != 0: - return True - else: - return False + def has_item(self, item): + return item in self.items and self.items[item] != 0 diff --git a/survival/components/resource_component.py b/survival/components/resource_component.py new file mode 100644 index 0000000..0a95b73 --- /dev/null +++ b/survival/components/resource_component.py @@ -0,0 +1,3 @@ +class ResourceComponent: + def __init__(self, resource_type): + self.resource_type = resource_type diff --git a/survival/generators/player_generator.py b/survival/generators/player_generator.py index 1dc8014..46561c1 100644 --- a/survival/generators/player_generator.py +++ b/survival/generators/player_generator.py @@ -1,6 +1,7 @@ from survival.components.OnCollisionComponent import OnCollisionComponent from survival.components.camera_target_component import CameraTargetComponent from survival.components.input_component import InputComponent +from survival.components.inventory_component import InventoryComponent from survival.components.movement_component import MovementComponent from survival.components.position_component import PositionComponent from survival.components.sprite_component import SpriteComponent @@ -16,6 +17,7 @@ class PlayerGenerator: world.add_component(player, MovementComponent()) world.add_component(player, InputComponent()) world.add_component(player, OnCollisionComponent()) + world.add_component(player, InventoryComponent()) camera_target = CameraTargetComponent(pos) world.add_component(player, camera_target) game_map.add_entity(player, pos) diff --git a/survival/generators/resource_generator.py b/survival/generators/resource_generator.py index ada4820..b9a8309 100644 --- a/survival/generators/resource_generator.py +++ b/survival/generators/resource_generator.py @@ -1,33 +1,47 @@ import random +from enum import Enum from survival import GameMap from survival.components.OnCollisionComponent import OnCollisionComponent +from survival.components.inventory_component import InventoryComponent from survival.components.position_component import PositionComponent +from survival.components.resource_component import ResourceComponent from survival.components.sprite_component import SpriteComponent from survival.esper import World from survival.settings import RESOURCES_AMOUNT +class ResourceType(Enum): + FOOD = 1 + WATER = 2 + WOOD = 3 + + class ResourceGenerator: def __init__(self, world, game_map): self.world = world self.map = game_map - def generate_resources(self): + def generate_resources(self, player: int): for x in range(RESOURCES_AMOUNT): obj = self.world.create_entity() - sprites = ['apple.png', 'water.png', 'wood.png'] + sprites = { + ResourceType.FOOD: 'apple.png', + ResourceType.WATER: 'water.png', + ResourceType.WOOD: 'wood.png' + } empty_grid_pos = self.get_empty_grid_position() empty_pos = [empty_grid_pos[0] * 32, empty_grid_pos[1] * 32] - pos = PositionComponent(empty_pos, empty_grid_pos) - sprite = SpriteComponent(random.choice(sprites)) + resource_type = random.choice(list(ResourceType)) + sprite = SpriteComponent(sprites[resource_type]) col = OnCollisionComponent() - col.addCallback(self.remove_resource, world=self.world, game_map=self.map, entity=obj) + col.addCallback(self.remove_resource, world=self.world, game_map=self.map, entity=obj, player=player) self.world.add_component(obj, pos) self.world.add_component(obj, sprite) self.world.add_component(obj, col) + self.world.add_component(obj, ResourceComponent(resource_type)) self.map.add_entity(obj, pos) def get_empty_grid_position(self): @@ -37,7 +51,10 @@ class ResourceGenerator: return free_pos @staticmethod - def remove_resource(world: World, game_map: GameMap, entity: int): + def remove_resource(world: World, game_map: GameMap, entity: int, player: int): pos = world.component_for_entity(entity, PositionComponent) + resource = world.component_for_entity(entity, ResourceComponent) + inventory = world.component_for_entity(player, InventoryComponent) + inventory.add_item(resource.resource_type, 1) game_map.remove_entity(pos.grid_position) world.delete_entity(entity, immediate=True) diff --git a/survival/image.py b/survival/image.py index 3c93b9c..c1727cf 100644 --- a/survival/image.py +++ b/survival/image.py @@ -4,12 +4,12 @@ import pygame class Image: - def __init__(self, filename): + def __init__(self, filename, pos=(0, 0), scale=1): self.texture = pygame.image.load(os.path.join('..', 'assets', filename)).convert_alpha() self.image = self.texture self.origin = (0, 0) - self.pos = (0, 0) - self.scale = 1 + self.pos = pos + self.set_scale(scale) def set_scale(self, scale): self.image = pygame.transform.scale(self.texture, @@ -20,3 +20,8 @@ class Image: window.blit(self.image, camera.apply(self.pos), pygame.Rect(self.origin[0] * self.scale, self.origin[1] * self.scale, 32 * self.scale, 32 * self.scale)) + + def draw_static(self, window): + window.blit(self.image, self.pos, + pygame.Rect(self.origin[0] * self.scale, self.origin[1] * self.scale, 32 * self.scale, + 32 * self.scale)) diff --git a/survival/systems/draw_system.py b/survival/systems/draw_system.py index 2cb35ae..38aac3e 100644 --- a/survival/systems/draw_system.py +++ b/survival/systems/draw_system.py @@ -1,14 +1,21 @@ from survival import esper from survival.components.position_component import PositionComponent from survival.components.sprite_component import SpriteComponent +from survival.user_interface import UserInterface class DrawSystem(esper.Processor): def __init__(self, camera): self.camera = camera + self.ui = None + + def initialize_interface(self, inventory): + self.ui = UserInterface(self.camera.window, inventory) def process(self, dt): for ent, (sprite, pos) in self.world.get_components(SpriteComponent, PositionComponent): sprite.image.pos = pos.position sprite.image.origin = (32 * pos.direction.value, 0) self.camera.draw(sprite.image) + self.ui.update() + self.ui.draw() diff --git a/survival/user_interface.py b/survival/user_interface.py new file mode 100644 index 0000000..33e8841 --- /dev/null +++ b/survival/user_interface.py @@ -0,0 +1,42 @@ +import pygame.font + +from survival import settings +from survival.components.inventory_component import InventoryComponent +from survival.generators.resource_generator import ResourceType +from survival.image import Image + + +class UserInterface: + def __init__(self, window, inventory: InventoryComponent): + self.width = settings.SCREEN_WIDTH + self.height = settings.SCREEN_HEIGHT + self.window = window + self.pos = (self.width - 240, 50) + self.scale = 2 + self.inventory = inventory + self.images = { + ResourceType.FOOD: Image('apple.png', self.pos, self.scale), + ResourceType.WATER: Image('water.png', self.pos, self.scale), + ResourceType.WOOD: Image('wood.png', self.pos, self.scale) + } + i = 0 + for key, value in self.images.items(): + self.images[key].pos = (self.pos[0] + i * 32 * self.scale + 8 * i, self.pos[1]) + i += 1 + self.slot_image = Image('ui.png', self.pos, scale=2) + self.font = pygame.font.SysFont('Comic Sans MS', 20) + + def update(self): + pass + + def draw(self): + for key, image in self.images.items(): + items_count = self.inventory.items[key] if self.inventory.has_item(key) else 0 + + self.slot_image.pos = image.pos + self.slot_image.draw_static(self.window) + image.draw_static(self.window) + + textsurface = self.font.render(str(items_count), False, (255, 255, 255)) + self.window.blit(textsurface, (image.pos[0] + 48, image.pos[1] + 36)) +