Merge pull request 'dev-jakklu' (#6) from dev-jakklu into master
Reviewed-on: #6
This commit is contained in:
commit
6c0074f52d
BIN
assets/atlas.png
BIN
assets/atlas.png
Binary file not shown.
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 20 KiB |
@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
import pygame as pg
|
import pygame as pg
|
||||||
|
|
||||||
for i in os.listdir('.'):
|
for i in os.listdir('.'):
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@ -3,9 +3,9 @@ import pygame
|
|||||||
from settings import SCREEN_WIDTH, SCREEN_HEIGHT
|
from settings import SCREEN_WIDTH, SCREEN_HEIGHT
|
||||||
from survival.camera import Camera
|
from survival.camera import Camera
|
||||||
from survival.game_map import GameMap
|
from survival.game_map import GameMap
|
||||||
from survival.player_generator import PlayerGenerator
|
from survival.generators.player_generator import PlayerGenerator
|
||||||
from survival.resource_generator import ResourceGenerator
|
from survival.generators.resource_generator import ResourceGenerator
|
||||||
from survival.world_generator import WorldGenerator
|
from survival.generators.world_generator import WorldGenerator
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
pygame.init()
|
pygame.init()
|
||||||
|
9
survival/biomes/biome_data.py
Normal file
9
survival/biomes/biome_data.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from survival.biomes.biome_preset import BiomePreset
|
||||||
|
|
||||||
|
|
||||||
|
class BiomeData:
|
||||||
|
def __init__(self, preset: BiomePreset):
|
||||||
|
self.biome = preset
|
||||||
|
|
||||||
|
def get_diff_value(self, height: float, moisture: float, heat: float):
|
||||||
|
return (height - self.biome.min_height) + (moisture - self.biome.min_moisture) + (heat - self.biome.min_heat)
|
22
survival/biomes/biome_preset.py
Normal file
22
survival/biomes/biome_preset.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
from survival.tile import Tile
|
||||||
|
|
||||||
|
|
||||||
|
class BiomePreset:
|
||||||
|
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
|
||||||
|
self.min_heat = min_heat
|
||||||
|
self.tiles = tiles
|
||||||
|
|
||||||
|
def get_new_tile(self):
|
||||||
|
tile = random.choice(self.tiles)
|
||||||
|
return Tile(origin=tile.origin, cost=tile.cost, biome=self)
|
||||||
|
|
||||||
|
def get_tile_sprite(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def match_conditions(self, height, moisture, heat):
|
||||||
|
return height >= self.min_height and moisture >= self.min_moisture and heat >= self.min_heat
|
6
survival/biomes/noise.py
Normal file
6
survival/biomes/noise.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from perlin_noise import PerlinNoise
|
||||||
|
|
||||||
|
|
||||||
|
def generate_noise(width: int, height: int, octaves, seed):
|
||||||
|
noise_map = PerlinNoise(octaves=octaves, seed=seed)
|
||||||
|
return [[noise_map([x / width, y / height]) for y in range(width)] for x in range(height)]
|
@ -1,13 +1,11 @@
|
|||||||
import time as _time
|
import time as _time
|
||||||
|
|
||||||
from functools import lru_cache as _lru_cache
|
from functools import lru_cache as _lru_cache
|
||||||
|
from typing import Any as _Any
|
||||||
|
from typing import Iterable as _Iterable
|
||||||
from typing import List as _List
|
from typing import List as _List
|
||||||
|
from typing import Tuple as _Tuple
|
||||||
from typing import Type as _Type
|
from typing import Type as _Type
|
||||||
from typing import TypeVar as _TypeVar
|
from typing import TypeVar as _TypeVar
|
||||||
from typing import Any as _Any
|
|
||||||
from typing import Tuple as _Tuple
|
|
||||||
from typing import Iterable as _Iterable
|
|
||||||
|
|
||||||
|
|
||||||
version = '1.3'
|
version = '1.3'
|
||||||
|
|
||||||
|
@ -24,3 +24,6 @@ class GameMap:
|
|||||||
|
|
||||||
def is_colliding(self, 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)
|
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)
|
||||||
|
|
||||||
|
def get_cost(self, pos):
|
||||||
|
return self.tile_layer.get_cost(pos)
|
||||||
|
73
survival/generators/tile_generator.py
Normal file
73
survival/generators/tile_generator.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
from survival.biomes.biome_data import BiomeData
|
||||||
|
from survival.biomes.biome_preset import BiomePreset
|
||||||
|
from survival.biomes.noise import generate_noise
|
||||||
|
from survival.tile import Tile
|
||||||
|
|
||||||
|
|
||||||
|
class TileGenerator:
|
||||||
|
Tiles = {
|
||||||
|
"Grass1": Tile(origin=(0, 0), cost=1),
|
||||||
|
"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),
|
||||||
|
}
|
||||||
|
|
||||||
|
TilesValues = list(Tiles.values())
|
||||||
|
|
||||||
|
Biomes = [
|
||||||
|
BiomePreset("Desert", min_height=0.2, min_moisture=0, min_heat=0.5, tiles=[Tiles["Grass1"], Tiles["Grass2"],
|
||||||
|
Tiles["Grass3"], Tiles["Grass4"]]),
|
||||||
|
BiomePreset("Forest", min_height=0.2, min_moisture=0.4, min_heat=0.4, tiles=[Tiles["Sand"]]),
|
||||||
|
BiomePreset("Grassland", min_height=0.2, min_moisture=0.5, min_heat=0.3, tiles=[Tiles["Sand"]]),
|
||||||
|
BiomePreset("Marsh", min_height=0.3, min_moisture=0.5, min_heat=0.62, tiles=[Tiles["Puddle"]]),
|
||||||
|
BiomePreset("Ocean", min_height=0, min_moisture=0, min_heat=0, tiles=[Tiles["Sand"]]),
|
||||||
|
BiomePreset("Tundra", min_height=0.2, min_moisture=0, min_heat=0, tiles=[Tiles["Puddle"]])
|
||||||
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_random_tile():
|
||||||
|
tile = random.choice(TileGenerator.TilesValues)
|
||||||
|
return Tile(origin=tile.origin, cost=tile.cost)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
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
|
||||||
|
def generate_biome_tiles(width: int, height: int):
|
||||||
|
seed = random.randint(0, 9999999)
|
||||||
|
octaves = 10
|
||||||
|
height_map = generate_noise(width, height, octaves, seed)
|
||||||
|
moisture_map = generate_noise(width, height, octaves, seed)
|
||||||
|
heat_map = generate_noise(width, height, octaves, seed)
|
||||||
|
|
||||||
|
return [[TileGenerator.get_biome(height_map[y][x], moisture_map[y][x], heat_map[y][x]).get_new_tile() for x in
|
||||||
|
range(width)] for y in range(height)]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_biome(height, moisture, heat) -> BiomePreset:
|
||||||
|
matching_biomes = list()
|
||||||
|
|
||||||
|
for biome in TileGenerator.Biomes:
|
||||||
|
if biome.match_conditions(height, moisture, heat):
|
||||||
|
matching_biomes.append(BiomeData(biome))
|
||||||
|
|
||||||
|
current_value = 0
|
||||||
|
found_biome = None
|
||||||
|
|
||||||
|
for biome in matching_biomes:
|
||||||
|
if found_biome is None:
|
||||||
|
found_biome = biome.biome
|
||||||
|
current_value = biome.get_diff_value(height, moisture, heat)
|
||||||
|
elif biome.get_diff_value(height, moisture, heat) < current_value:
|
||||||
|
found_biome = biome.biome
|
||||||
|
current_value = biome.get_diff_value(height, moisture, heat)
|
||||||
|
|
||||||
|
if found_biome is None:
|
||||||
|
found_biome = TileGenerator.Biomes[0]
|
||||||
|
|
||||||
|
return found_biome
|
@ -1,4 +1,5 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from queue import PriorityQueue
|
||||||
|
|
||||||
from survival import GameMap
|
from survival import GameMap
|
||||||
from survival.components.position_component import PositionComponent
|
from survival.components.position_component import PositionComponent
|
||||||
@ -12,57 +13,44 @@ class Action(Enum):
|
|||||||
|
|
||||||
|
|
||||||
class State:
|
class State:
|
||||||
def __init__(self, position, direction):
|
def __init__(self, position: tuple[int, int], direction: Direction):
|
||||||
self.position = position
|
self.position = position
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
|
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
def __init__(self, state: State, parent=None, action=None):
|
def __init__(self, state: State, parent=None, action=None, cost=None):
|
||||||
self.state = state
|
self.state = state
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.action = action
|
self.action = action
|
||||||
|
self.cost = cost
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.cost < other.cost
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.cost == other.cost
|
||||||
|
|
||||||
|
|
||||||
def get_moved_position(position, direction):
|
def get_moved_position(position: tuple[int, int], direction: Direction):
|
||||||
vector = Direction.get_vector(direction)
|
vector = Direction.get_vector(direction)
|
||||||
return position[0] + vector[0], position[1] + vector[1]
|
return position[0] + vector[0], position[1] + vector[1]
|
||||||
|
|
||||||
|
|
||||||
def get_states(state: State, game_map: GameMap):
|
def get_states(state: State, game_map: GameMap) -> list[tuple[Action, State, int]]:
|
||||||
states = list()
|
states = list()
|
||||||
|
|
||||||
states.append((Action.ROTATE_LEFT, State(state.position, state.direction.rotate_left(state.direction))))
|
states.append((Action.ROTATE_LEFT, State(state.position, state.direction.rotate_left(state.direction)), 1))
|
||||||
states.append((Action.ROTATE_RIGHT, State(state.position, state.direction.rotate_right(state.direction))))
|
states.append((Action.ROTATE_RIGHT, State(state.position, state.direction.rotate_right(state.direction)), 1))
|
||||||
|
|
||||||
target_state = get_moved_position(state.position, state.direction)
|
target_position = get_moved_position(state.position, state.direction)
|
||||||
if not game_map.is_colliding(target_state):
|
if not game_map.is_colliding(target_position):
|
||||||
states.append((Action.MOVE, State(target_state, state.direction)))
|
states.append((Action.MOVE, State(target_position, state.direction), game_map.get_cost(target_position)))
|
||||||
|
|
||||||
return states
|
return states
|
||||||
|
|
||||||
|
|
||||||
def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple):
|
def build_path(node: Node):
|
||||||
fringe = list()
|
|
||||||
explored = list()
|
|
||||||
|
|
||||||
explored_states = set()
|
|
||||||
fringe_states = set()
|
|
||||||
|
|
||||||
start = State(start.grid_position, start.direction)
|
|
||||||
fringe.append(Node(start))
|
|
||||||
fringe_states.add((tuple(start.position), start.direction))
|
|
||||||
|
|
||||||
while True:
|
|
||||||
# No solutions found
|
|
||||||
if not any(fringe):
|
|
||||||
return []
|
|
||||||
|
|
||||||
node = fringe.pop(0)
|
|
||||||
fringe_states.remove((tuple(node.state.position), node.state.direction))
|
|
||||||
|
|
||||||
# Check goal
|
|
||||||
if node.state.position == goal:
|
|
||||||
actions = [node.action]
|
actions = [node.action]
|
||||||
parent = node.parent
|
parent = node.parent
|
||||||
|
|
||||||
@ -74,15 +62,52 @@ def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple):
|
|||||||
actions.reverse()
|
actions.reverse()
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
|
|
||||||
|
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])
|
||||||
|
|
||||||
|
|
||||||
|
def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple):
|
||||||
|
fringe = PriorityQueue()
|
||||||
|
explored = list()
|
||||||
|
|
||||||
|
explored_states = set()
|
||||||
|
fringe_states = set() # Stores positions and directions of states
|
||||||
|
|
||||||
|
start = State(start.grid_position, start.direction)
|
||||||
|
fringe.put((0, Node(start, cost=0)))
|
||||||
|
fringe_states.add((tuple(start.position), start.direction))
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# No solutions found
|
||||||
|
if fringe.empty():
|
||||||
|
return []
|
||||||
|
|
||||||
|
node = fringe.get()
|
||||||
|
node_priority = node[0]
|
||||||
|
node = node[1]
|
||||||
|
fringe_states.remove((tuple(node.state.position), node.state.direction))
|
||||||
|
|
||||||
|
# Check goal
|
||||||
|
if node.state.position == goal:
|
||||||
|
return build_path(node)
|
||||||
|
|
||||||
explored.append(node)
|
explored.append(node)
|
||||||
explored_states.add((tuple(node.state.position), node.state.direction))
|
explored_states.add((tuple(node.state.position), node.state.direction))
|
||||||
|
|
||||||
# Get all possible states
|
# Get all possible states
|
||||||
for state in get_states(node.state, game_map):
|
for state in get_states(node.state, game_map):
|
||||||
sub_state = (tuple(state[1].position), state[1].direction)
|
sub_state = (tuple(state[1].position), state[1].direction)
|
||||||
if sub_state not in fringe_states and sub_state not in explored_states:
|
|
||||||
new_node = Node(state=state[1],
|
new_node = Node(state=state[1],
|
||||||
parent=node,
|
parent=node,
|
||||||
action=state[0])
|
action=state[0],
|
||||||
fringe.append(new_node)
|
cost=(state[2] + node.cost))
|
||||||
|
|
||||||
|
priority = new_node.cost + heuristic(new_node, goal)
|
||||||
|
if sub_state not in fringe_states and sub_state not in explored_states:
|
||||||
|
fringe.put((priority, new_node))
|
||||||
|
fringe_states.add((tuple(new_node.state.position), new_node.state.direction))
|
||||||
|
elif sub_state in fringe_states and node.cost > new_node.cost:
|
||||||
|
fringe.get(node)
|
||||||
|
fringe.put((priority, new_node))
|
||||||
fringe_states.add((tuple(new_node.state.position), new_node.state.direction))
|
fringe_states.add((tuple(new_node.state.position), new_node.state.direction))
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import pygame
|
|
||||||
from pygame.rect import Rect
|
from pygame.rect import Rect
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,14 +21,3 @@ class MovementSystem(esper.Processor):
|
|||||||
pos.position[1] - moving.target[1] * 32) < 0.1 * mov.speed:
|
pos.position[1] - moving.target[1] * 32) < 0.1 * mov.speed:
|
||||||
pos.position = [moving.target[0] * 32, moving.target[1] * 32]
|
pos.position = [moving.target[0] * 32, moving.target[1] * 32]
|
||||||
self.world.remove_component(ent, MovingComponent)
|
self.world.remove_component(ent, MovingComponent)
|
||||||
|
|
||||||
# if moving.direction[0] != 0:
|
|
||||||
# pos.position[0] += moving.direction[0] * mov.speed * dt / 100
|
|
||||||
# if abs(moving.movement_target[0] * 32 - pos.position[0]) < 0.1 * mov.speed:
|
|
||||||
# pos.position = [moving.movement_target[0] * 32, moving.movement_target[1] * 32]
|
|
||||||
# self.world.remove_component(ent, MovingComponent)
|
|
||||||
# else:
|
|
||||||
# pos.position[1] += moving.direction[1] * mov.speed * dt / 100
|
|
||||||
# if abs(pos.position[1] - moving.movement_target[1] * 32) < 0.1 * mov.speed:
|
|
||||||
# pos.position = [moving.movement_target[0] * 32, moving.movement_target[1] * 32]
|
|
||||||
# self.world.remove_component(ent, MovingComponent)
|
|
||||||
|
@ -5,7 +5,6 @@ from survival.components.moving_component import MovingComponent
|
|||||||
from survival.components.position_component import PositionComponent
|
from survival.components.position_component import PositionComponent
|
||||||
from survival.enums import Direction
|
from survival.enums import Direction
|
||||||
from survival.graph_search import graph_search, Action
|
from survival.graph_search import graph_search, Action
|
||||||
from survival.pathfinding import breadth_first_search
|
|
||||||
from survival.systems.input_system import PathfindingComponent
|
from survival.systems.input_system import PathfindingComponent
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
import random
|
|
||||||
|
|
||||||
|
|
||||||
class Tile:
|
class Tile:
|
||||||
origins = [(0, 0), (32, 0), (64, 0), (96, 0)]
|
def __init__(self, origin: tuple = (0, 0), cost: int = 1, biome=None):
|
||||||
|
self.origin = origin
|
||||||
def __init__(self, origin=(0, 0)):
|
self.cost = cost
|
||||||
self.origin = random.choice(Tile.origins)
|
self.biome = biome
|
||||||
self.image = None
|
self.image = None
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from survival.generators.tile_generator import TileGenerator
|
||||||
from survival.image import Image
|
from survival.image import Image
|
||||||
from survival.tile import Tile
|
from survival.tile import Tile
|
||||||
|
|
||||||
@ -6,7 +7,8 @@ class TileLayer:
|
|||||||
def __init__(self, width, height):
|
def __init__(self, width, height):
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
self.tiles = [[Tile() for x in range(self.width)] for y in range(self.height)]
|
self.tiles: list[list[Tile]] = TileGenerator.generate_biome_tiles(width, height)
|
||||||
|
# self.tiles: list[list[Tile]] = TileGenerator.generate_random_tiles(width, height)
|
||||||
self.image = Image('atlas.png')
|
self.image = Image('atlas.png')
|
||||||
|
|
||||||
def draw(self, camera, visible_area):
|
def draw(self, camera, visible_area):
|
||||||
@ -18,3 +20,6 @@ class TileLayer:
|
|||||||
self.image.pos = (x * 32, y * 32)
|
self.image.pos = (x * 32, y * 32)
|
||||||
self.image.origin = self.tiles[y][x].origin
|
self.image.origin = self.tiles[y][x].origin
|
||||||
camera.draw(self.image)
|
camera.draw(self.image)
|
||||||
|
|
||||||
|
def get_cost(self, pos):
|
||||||
|
return self.tiles[pos[1]][pos[0]].cost
|
||||||
|
Loading…
Reference in New Issue
Block a user