135 lines
5.5 KiB
Python
135 lines
5.5 KiB
Python
import arcade
|
|
import constants
|
|
import random
|
|
|
|
# map consists of a series of different types of terrain (sea, land, city, etc.) called zones
|
|
# first field defines what tile the zone starts with; subsequent tiles are determined based on the transitions table below
|
|
# second field defines the number of tiles in the zone
|
|
# third field defines the total number of enemies to spawn in the zone (0 means no enemies)
|
|
# fourth field defines the list of possible enemy types and probabilities to spawn them (e.g. [("ship01", 1.0)] means 100% chance of spawning ship01)
|
|
ZONES = [
|
|
("sea", 50, 10, [("ship01", 0.5), ("ship02", 0.5),]),
|
|
("uphill", 160, 30, [("tank01", 0.5), ("tank02", 0.5),]),
|
|
("city", 20, 10, [("tank01", 0.5), ("tank02", 0.5),]),
|
|
("uphill", 30, 10, [("tank01", 0.5), ("tank02", 0.5),]),
|
|
("sea", 30, 5, [("ship01", 0.5), ("ship02", 0.5),])
|
|
]
|
|
|
|
TILE_TRANSITIONS = {
|
|
# sea always stays sea
|
|
"sea": [("sea", 1.0)],
|
|
# uphill can transition to flat or downhill with equal probability
|
|
"uphill": [("full", 0.75), ("downhill", 0.25)],
|
|
# flat can transition to uphill, flat, or downhill with equal probability
|
|
"full": [("uphill", 0.33), ("full", 0.33), ("downhill", 0.33)],
|
|
# downhill can transition to flat or uphill with equal probability
|
|
"downhill": [("uphill", 0.50), ("full", 0.50)],
|
|
# city always stays city
|
|
"city": [("city", 1.0)]
|
|
}
|
|
|
|
# how to change the height of the next tile when switching from first tile type to the second
|
|
# if combination is not specified here, terrain height doesn't change
|
|
HEIGHT_TRANSITIONS = {
|
|
("uphill", "uphill"): 1,
|
|
("full", "uphill"): 1,
|
|
("downhill", "downhill"): -1,
|
|
("downhill", "full"): -1,
|
|
}
|
|
|
|
# probabilities that a sequence of tiles of the same type (called run) will be of a certain length
|
|
RUN_LENGTHS = [
|
|
# 10% chance of 1 tile run, 20% chance of 2 tiles run, etc.
|
|
(0.1, 1),
|
|
(0.2, 2),
|
|
(0.25, 3),
|
|
(0.25, 4),
|
|
(0.2, 5),
|
|
]
|
|
|
|
|
|
def get_tile_at_x(sl: arcade.SpriteList[arcade.Sprite], x: int) -> float:
|
|
for sprite in sl:
|
|
if sprite.center_x == x * sprite.width:
|
|
return sprite.center_y
|
|
return 0.0
|
|
|
|
|
|
def generate_map(height: float) -> tuple[arcade.SpriteList[arcade.Sprite], arcade.SpriteList[arcade.Sprite]]:
|
|
r = arcade.SpriteList[arcade.Sprite]()
|
|
e = arcade.SpriteList[arcade.Sprite]()
|
|
|
|
cx = 0
|
|
cy = 0
|
|
dy = 0
|
|
prev_tile = ZONES[0][0] # start with the first tile in the first zone
|
|
|
|
for index, zone in enumerate(ZONES):
|
|
tile = zone[0]
|
|
length = zone[1]
|
|
zx = 0
|
|
enemies_x = random.choices(range(0, zone[1]), k=zone[2])
|
|
|
|
while zx < length:
|
|
run_len = random.choices(RUN_LENGTHS, weights=[t[0] for t in RUN_LENGTHS])[0][1]
|
|
|
|
for _ in range(min(run_len, length - zx)):
|
|
|
|
# if close to the end of ground, start descending to sea level
|
|
if index < len(ZONES)-1:
|
|
if ZONES[index+1][0] == "sea" and cy > 0 and zx >= length - cy - 2:
|
|
tile = "downhill"
|
|
run_len = cy
|
|
|
|
sprite = arcade.Sprite(f"assets/{tile}.png", scale=constants.SCALE_TERRAIN)
|
|
dy = HEIGHT_TRANSITIONS[(prev_tile, tile)] if (prev_tile, tile) in HEIGHT_TRANSITIONS else 0
|
|
prev_tile = tile
|
|
|
|
# break current run, forcing tile type change, if it's getting too high (5 tiles or less from the top of the screen) or too low
|
|
max_y = int((height - sprite.height / 2) / sprite.height) - 5
|
|
if (cy + dy) < 0 or (cy + dy) >= max_y:
|
|
break
|
|
|
|
r.append(sprite)
|
|
cy += dy
|
|
sprite.center_x = cx * sprite.width
|
|
sprite.center_y = (cy * sprite.height) + sprite.height / 2
|
|
|
|
# add underlying terrain, if needed
|
|
if cy > 0:
|
|
s = arcade.Sprite(f"assets/full.png", scale=constants.SCALE_TERRAIN)
|
|
s.center_x = cx * sprite.width
|
|
s.scale_y = s.scale_y * cy
|
|
s.center_y = s.height / 2
|
|
r.append(s)
|
|
|
|
# spawn enemies if any
|
|
if zx in enemies_x:
|
|
enemy_type = random.choices(zone[3], weights=[t[1] for t in zone[3]])[0][0]
|
|
enemy = arcade.Sprite(f"assets/{enemy_type}.png", scale=constants.SCALE_TERRAIN)
|
|
enemy.center_x = sprite.center_x
|
|
enemy.center_y = get_tile_at_x(r, cx) + enemy.height / (1 if tile == "full" else 2)
|
|
|
|
if tile == "downhill":
|
|
enemy.radians = 0.5
|
|
elif tile == "uphill":
|
|
enemy.radians = -0.5
|
|
|
|
enemy.properties["hit"] = False # custom property to track if the enemy has been hit
|
|
enemy.properties["weapon"] = "flak" # custom property to track the type of weapon
|
|
enemy.properties["last_fired"] = 0 # custom property to track the last time the enemy fired weapon
|
|
enemy.properties["value"] = 100 # custom property to track the score value for destroying the enemy
|
|
e.append(enemy)
|
|
|
|
cx += 1
|
|
zx += 1
|
|
|
|
while True:
|
|
tile = random.choices(TILE_TRANSITIONS[tile], weights=[t[1] for t in TILE_TRANSITIONS[tile]])[0][0]
|
|
if tile in ["downhill", "full"] and cy <= 2:
|
|
continue
|
|
else:
|
|
break
|
|
|
|
return r, e
|