dev-jakklu #6
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 pygame as pg
|
||||
|
||||
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 survival.camera import Camera
|
||||
from survival.game_map import GameMap
|
||||
from survival.player_generator import PlayerGenerator
|
||||
from survival.resource_generator import ResourceGenerator
|
||||
from survival.world_generator import WorldGenerator
|
||||
from survival.generators.player_generator import PlayerGenerator
|
||||
from survival.generators.resource_generator import ResourceGenerator
|
||||
from survival.generators.world_generator import WorldGenerator
|
||||
|
||||
if __name__ == '__main__':
|
||||
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
|
||||
|
||||
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 Tuple as _Tuple
|
||||
from typing import Type as _Type
|
||||
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'
|
||||
|
||||
|
@ -24,3 +24,6 @@ class GameMap:
|
||||
|
||||
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)
|
||||
|
||||
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 queue import PriorityQueue
|
||||
|
||||
from survival import GameMap
|
||||
from survival.components.position_component import PositionComponent
|
||||
@ -12,57 +13,44 @@ class Action(Enum):
|
||||
|
||||
|
||||
class State:
|
||||
def __init__(self, position, direction):
|
||||
def __init__(self, position: tuple[int, int], direction: Direction):
|
||||
self.position = position
|
||||
self.direction = direction
|
||||
|
||||
|
||||
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.parent = parent
|
||||
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)
|
||||
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.append((Action.ROTATE_LEFT, State(state.position, state.direction.rotate_left(state.direction))))
|
||||
states.append((Action.ROTATE_RIGHT, State(state.position, state.direction.rotate_right(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)), 1))
|
||||
|
||||
target_state = get_moved_position(state.position, state.direction)
|
||||
if not game_map.is_colliding(target_state):
|
||||
states.append((Action.MOVE, State(target_state, state.direction)))
|
||||
target_position = get_moved_position(state.position, state.direction)
|
||||
if not game_map.is_colliding(target_position):
|
||||
states.append((Action.MOVE, State(target_position, state.direction), game_map.get_cost(target_position)))
|
||||
|
||||
return states
|
||||
|
||||
|
||||
def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple):
|
||||
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:
|
||||
def build_path(node: Node):
|
||||
actions = [node.action]
|
||||
parent = node.parent
|
||||
|
||||
@ -74,15 +62,52 @@ def graph_search(game_map: GameMap, start: PositionComponent, goal: tuple):
|
||||
actions.reverse()
|
||||
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_states.add((tuple(node.state.position), node.state.direction))
|
||||
|
||||
# Get all possible states
|
||||
for state in get_states(node.state, game_map):
|
||||
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],
|
||||
parent=node,
|
||||
action=state[0])
|
||||
fringe.append(new_node)
|
||||
action=state[0],
|
||||
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))
|
||||
|
@ -1,4 +1,3 @@
|
||||
import pygame
|
||||
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 = [moving.target[0] * 32, moving.target[1] * 32]
|
||||
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.enums import Direction
|
||||
from survival.graph_search import graph_search, Action
|
||||
from survival.pathfinding import breadth_first_search
|
||||
from survival.systems.input_system import PathfindingComponent
|
||||
|
||||
|
||||
|
@ -1,9 +1,6 @@
|
||||
import random
|
||||
|
||||
|
||||
class Tile:
|
||||
origins = [(0, 0), (32, 0), (64, 0), (96, 0)]
|
||||
|
||||
def __init__(self, origin=(0, 0)):
|
||||
self.origin = random.choice(Tile.origins)
|
||||
def __init__(self, origin: tuple = (0, 0), cost: int = 1, biome=None):
|
||||
self.origin = origin
|
||||
self.cost = cost
|
||||
self.biome = biome
|
||||
self.image = None
|
||||
|
@ -1,3 +1,4 @@
|
||||
from survival.generators.tile_generator import TileGenerator
|
||||
from survival.image import Image
|
||||
from survival.tile import Tile
|
||||
|
||||
@ -6,7 +7,8 @@ class TileLayer:
|
||||
def __init__(self, width, height):
|
||||
self.width = width
|
||||
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')
|
||||
|
||||
def draw(self, camera, visible_area):
|
||||
@ -18,3 +20,6 @@ class TileLayer:
|
||||
self.image.pos = (x * 32, y * 32)
|
||||
self.image.origin = self.tiles[y][x].origin
|
||||
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