606 lines
17 KiB
Python
606 lines
17 KiB
Python
import arcade
|
|
import arcade.gui
|
|
import random
|
|
# =====================================================
|
|
# USTAWIENIA
|
|
# =====================================================
|
|
|
|
CELL_SIZE = 5
|
|
|
|
GRID_WIDTH = 240
|
|
GRID_HEIGHT = 160
|
|
|
|
SCREEN_WIDTH = GRID_WIDTH * CELL_SIZE
|
|
SCREEN_HEIGHT = GRID_HEIGHT * CELL_SIZE
|
|
|
|
TITLE = "Silnik Piaskownicy"
|
|
|
|
FPS = 60
|
|
|
|
# =====================================================
|
|
# MATERIAŁY
|
|
# =====================================================
|
|
|
|
PUSTE = 0
|
|
PIASEK = 1
|
|
ZWIR = 2
|
|
WODA = 3
|
|
KAMIEN = 4
|
|
MOKRY_PIASEK = 5
|
|
AIR=6
|
|
LAWA=7
|
|
|
|
KOLORY = {
|
|
PUSTE: arcade.color.BLACK,
|
|
PIASEK: (235, 210, 70),
|
|
ZWIR: (140, 140, 140),
|
|
WODA: (50, 120, 255),
|
|
KAMIEN: (80, 80, 80),
|
|
MOKRY_PIASEK: (205,180,40),
|
|
AIR:(248,248,248),
|
|
LAWA: (200,40,40)
|
|
}
|
|
|
|
# =====================================================
|
|
# KLASA ŚWIATA
|
|
# =====================================================
|
|
|
|
class World:
|
|
|
|
def __init__(self):
|
|
|
|
self.width = GRID_WIDTH
|
|
self.height = GRID_HEIGHT
|
|
|
|
# Szybka tablica 1D
|
|
self.grid = [PUSTE] * (self.width * self.height)
|
|
|
|
def index(self, x, y):
|
|
x = int(x)
|
|
y = int(y)
|
|
|
|
return y * self.width + x
|
|
|
|
def inside(self, x, y):
|
|
return (
|
|
0 <= x < self.width and
|
|
0 <= y < self.height
|
|
)
|
|
|
|
def get(self, x, y):
|
|
|
|
if not self.inside(x, y):
|
|
return PUSTE
|
|
|
|
return self.grid[self.index(x, y)]
|
|
|
|
def set(self, x, y, value):
|
|
|
|
if not self.inside(x, y):
|
|
return
|
|
|
|
self.grid[self.index(x, y)] = value
|
|
|
|
def clear(self):
|
|
|
|
self.grid[:] = [PUSTE] * (self.width * self.height)
|
|
|
|
def swap(self, x1, y1, x2, y2):
|
|
|
|
i1 = self.index(x1, y1)
|
|
i2 = self.index(x2, y2)
|
|
|
|
self.grid[i1], self.grid[i2] = (
|
|
self.grid[i2],
|
|
self.grid[i1]
|
|
)
|
|
|
|
# =====================================================
|
|
# GŁÓWNE OKNO
|
|
# =====================================================
|
|
|
|
class Game(arcade.Window):
|
|
|
|
def __init__(self):
|
|
|
|
super().__init__(
|
|
SCREEN_WIDTH,
|
|
SCREEN_HEIGHT,
|
|
TITLE,
|
|
update_rate=1 / FPS
|
|
)
|
|
|
|
arcade.set_background_color(arcade.color.BLACK)
|
|
|
|
self.world_old = World()
|
|
self.world_new = World()
|
|
|
|
# Wybrany materiał
|
|
self.current_material = PIASEK
|
|
|
|
# Rozmiar pędzla
|
|
self.brush_size = 3
|
|
|
|
# Czy mysz jest wciśnięta
|
|
self.mouse_down = False
|
|
|
|
# Pozycja myszy
|
|
self.mouse_x = 0
|
|
self.mouse_y = 0
|
|
|
|
# Stan gry
|
|
self.paused = False
|
|
|
|
# Tekst interfejsu
|
|
self.info_text = arcade.Text(
|
|
"",
|
|
10,
|
|
SCREEN_HEIGHT - 25,
|
|
arcade.color.WHITE,
|
|
14
|
|
)
|
|
self.manager = arcade.gui.UIManager()
|
|
self.manager.enable()
|
|
|
|
self.manager = arcade.gui.UIManager()
|
|
self.manager.enable()
|
|
|
|
# Główny układ pozycjonujący (zastępuje UIAnchorWidget)
|
|
self.anchor_layout = arcade.gui.UIAnchorLayout()
|
|
|
|
# Układ pionowy dla elementów wewnątrz menu
|
|
self.v_box = arcade.gui.UIBoxLayout()
|
|
|
|
# Poprawione: definicja opcji pisana małą literą
|
|
opcje = ["Piasek", "Żwir", "Woda","Kamień","Mokry piasek","Powietrze","Lawa"]
|
|
|
|
# Tworzenie widgetu listy wybieranej
|
|
self.dropdown = arcade.gui.UIDropdown(
|
|
default=opcje[0], # Wartość domyślna (pierwszy element)
|
|
options=opcje, # Lista dostępnych opcji
|
|
width=200
|
|
)
|
|
|
|
# Przypisanie akcji po zmianie wyboru
|
|
self.dropdown.on_change = self.on_dropdown_change
|
|
|
|
# Dodanie listy wybieranej do pionowego kontenera
|
|
self.v_box.add(self.dropdown)
|
|
|
|
self.anchor_layout.add(
|
|
child=self.v_box,
|
|
anchor_x="right",
|
|
anchor_y="top"
|
|
)
|
|
|
|
# Dodanie całego układu do menedżera UI
|
|
self.manager.add(self.anchor_layout)
|
|
|
|
def can_move(self, x, y):
|
|
return self.world_old.get(x, y) == PUSTE and self.world_new.get(x, y) == PUSTE
|
|
|
|
def on_dropdown_change(self, event: arcade.gui.UIOnChangeEvent):
|
|
print(f"Zmieniono z: {event.old_value} na: {event.new_value}")
|
|
if event.new_value=="Piasek":
|
|
self.current_material = PIASEK
|
|
elif event.new_value=="Żwir":
|
|
self.current_material = ZWIR
|
|
elif event.new_value=="Woda":
|
|
self.current_material = WODA
|
|
elif event.new_value=="Kamień":
|
|
self.current_material = KAMIEN
|
|
elif event.new_value=="Mokry piasek":
|
|
self.current_material = MOKRY_PIASEK
|
|
elif event.new_value=="Powietrze":
|
|
self.current_material = AIR
|
|
elif event.new_value=="Lawa":
|
|
self.current_material = LAWA
|
|
|
|
|
|
def update_info(self):
|
|
|
|
self.info_text.text = (
|
|
f"Materiał: {self.material_name()} | "
|
|
f"Pędzel: {self.brush_size} | "
|
|
f"Pauza: {self.paused}"
|
|
)
|
|
|
|
def material_name(self):
|
|
|
|
nazwy = {
|
|
PUSTE: "Puste",
|
|
PIASEK: "Piasek",
|
|
ZWIR: "Żwir",
|
|
WODA: "Woda",
|
|
KAMIEN: "Kamień",
|
|
MOKRY_PIASEK: "Mokry piasek",
|
|
AIR:"Powietrze",
|
|
LAWA:"Lawa"
|
|
}
|
|
|
|
return nazwy[self.current_material]
|
|
# ==============================================
|
|
|
|
def on_update(self, delta_time):
|
|
|
|
if self.paused:
|
|
return
|
|
|
|
world_old = self.world_old
|
|
world_new = self.world_new
|
|
|
|
# Najpierw kopiujemy cały stan starego świata
|
|
world_new.grid[:] = world_old.grid[:]
|
|
|
|
for y in range(1, world_old.height):
|
|
for x in range(1, world_old.width):
|
|
|
|
material = world_old.get(x, y)
|
|
|
|
if material == PIASEK:
|
|
self.update_sand(x, y)
|
|
|
|
elif material == ZWIR:
|
|
self.update_zwir(x, y)
|
|
|
|
elif material == KAMIEN:
|
|
self.update_rock(x, y)
|
|
|
|
elif material == WODA:
|
|
self.update_woda(x, y)
|
|
|
|
elif material == MOKRY_PIASEK:
|
|
self.update_wet_sand(x,y)
|
|
|
|
elif material == AIR:
|
|
self.update_air(x,y)
|
|
|
|
elif material == LAWA:
|
|
self.update_lawa(x,y)
|
|
|
|
# Zamiana światów po zakończeniu całej klatki
|
|
self.world_old, self.world_new = (
|
|
self.world_new,
|
|
self.world_old
|
|
)
|
|
|
|
if self.mouse_down:
|
|
gx = int(self.mouse_x // CELL_SIZE)
|
|
gy = int(self.mouse_y // CELL_SIZE)
|
|
|
|
self.paint(gx, gy)
|
|
|
|
# ==============================================
|
|
|
|
def on_draw(self):
|
|
|
|
self.clear()
|
|
|
|
world = self.world_old
|
|
|
|
for y in range(world.height):
|
|
for x in range(world.width):
|
|
|
|
material = world.get(x, y)
|
|
|
|
if material == PUSTE:
|
|
continue
|
|
|
|
arcade.draw_rect_filled(
|
|
arcade.LBWH(
|
|
x * CELL_SIZE,
|
|
y * CELL_SIZE,
|
|
CELL_SIZE,
|
|
CELL_SIZE
|
|
),
|
|
KOLORY[material]
|
|
)
|
|
|
|
# reszta rysowania...
|
|
|
|
gx = self.mouse_x // CELL_SIZE
|
|
gy = self.mouse_y // CELL_SIZE
|
|
|
|
brush_pixels = self.brush_size * CELL_SIZE
|
|
|
|
center_x = gx * CELL_SIZE + CELL_SIZE / 2
|
|
center_y = gy * CELL_SIZE + CELL_SIZE / 2
|
|
|
|
rect = arcade.XYWH(
|
|
center_x,
|
|
center_y,
|
|
brush_pixels,
|
|
brush_pixels,
|
|
)
|
|
|
|
arcade.draw_rect_outline(
|
|
rect,
|
|
arcade.color.WHITE,
|
|
border_width=2,
|
|
)
|
|
|
|
self.update_info()
|
|
|
|
self.info_text.draw()
|
|
|
|
self.manager.draw()
|
|
|
|
self.world_old=self.world_new
|
|
|
|
def czy_dotyka(self,x,y,czego,na_co):
|
|
world_old = self.world_old
|
|
world_new = self.world_new
|
|
if world_old.get(x, y - 1) == czego:
|
|
world_new.set(x, y, na_co)
|
|
elif world_old.get(x - 1, y - 1) == czego:
|
|
world_new.set(x, y, na_co)
|
|
elif world_old.get(x + 1, y - 1) == czego:
|
|
world_new.set(x, y, na_co)
|
|
elif world_old.get(x + 1, y) == czego:
|
|
world_new.set(x, y, na_co)
|
|
elif world_old.get(x - 1, y) == czego:
|
|
world_new.set(x, y, na_co)
|
|
elif world_old.get(x - 1, y + 1) == czego:
|
|
world_new.set(x, y, na_co)
|
|
elif world_old.get(x, y + 1) == czego:
|
|
world_new.set(x, y, na_co)
|
|
elif world_old.get(x + 1, y + 1) == czego:
|
|
world_new.set(x, y, na_co)
|
|
|
|
def update_sand(self, x, y):
|
|
|
|
world_old = self.world_old
|
|
world_new = self.world_new
|
|
|
|
kierunek = random.choice([-1, 1])
|
|
szansa_w_wodzie=random.choice([True, False])
|
|
# dół
|
|
if world_old.get(x, y - 1) == PUSTE:
|
|
world_new.swap(x,y,x,y - 1)
|
|
|
|
|
|
# dół-lewo / dół-prawo
|
|
elif PUSTE == world_old.get(x + kierunek, y - 1):
|
|
if self.can_move(x + kierunek, y - 1):
|
|
world_new.swap(x, y, x + kierunek, y - 1)
|
|
elif world_old.get( x - kierunek, y - 1) == PUSTE:
|
|
if self.can_move(x - kierunek, y - 1):
|
|
world_new.swap(x, y, x - kierunek, y - 1)
|
|
|
|
# W wodzie
|
|
elif world_old.get(x, y - 1) == WODA and szansa_w_wodzie:
|
|
world_new.swap(x,y,x,y - 1)
|
|
world_new.set(x,y-1,MOKRY_PIASEK)
|
|
elif world_old.get( x + kierunek, y - 1) == WODA and szansa_w_wodzie:
|
|
world_new.swap(x, y, x + kierunek, y - 1)
|
|
world_new.set(x+kierunek, y - 1, MOKRY_PIASEK)
|
|
elif world_old.get( x - kierunek, y - 1) == WODA and szansa_w_wodzie:
|
|
world_new.swap(x, y, x - kierunek, y - 1)
|
|
world_new.set(x - kierunek, y - 1, MOKRY_PIASEK)
|
|
elif world_old.get( x +1, y-1) == WODA or world_old.get( x , y - 1) == WODA or world_old.get( x-1, y-1) == WODA or world_old.get( x - 1, y) == WODA or world_old.get( x - 1, y + 1) == WODA or world_old.get( x, y+1) == WODA or world_old.get( x +1, y +1) == WODA or world_old.get( x +1, y) == WODA:
|
|
world_new.set(x, y, MOKRY_PIASEK)
|
|
szansa_na_znikniencie_wody=random.choice([True, False])
|
|
if szansa_na_znikniencie_wody:
|
|
self.czy_dotyka(x,y,WODA,MOKRY_PIASEK)
|
|
|
|
def update_zwir(self,x,y):
|
|
world_old = self.world_old
|
|
world_new = self.world_new
|
|
if world_old.get(x, y - 1) == PUSTE:
|
|
world_new.swap(x,y,x, y - 1)
|
|
for kierunek in random.choice([-1, 1]), random.choice([1, -1]):
|
|
skos_x = x + kierunek
|
|
skos_y = y - 1
|
|
|
|
dol_x = x + kierunek
|
|
dol_y = y - 2
|
|
|
|
if world_old.get(skos_x, skos_y) == PUSTE and world_old.get(dol_x, dol_y) == PUSTE:
|
|
if self.can_move(skos_x, skos_y):
|
|
world_new.swap( x, y, skos_x, skos_y)
|
|
break
|
|
def update_rock(self,x,y):
|
|
world_new = self.world_new
|
|
world_old = self.world_old
|
|
if world_old.get(x, y - 1) == PUSTE and self.can_move(x,y-1):
|
|
world_new.swap(x, y, x, y - 1)
|
|
|
|
def update_woda(self, x, y):
|
|
world_new = self.world_new
|
|
world_old = self.world_old
|
|
kierunek = random.choice([-1, 1])
|
|
# dół
|
|
if world_old.get(x, y - 1) == PUSTE:
|
|
world_new.swap(x,y,x,y - 1)
|
|
|
|
# dół-lewo / dół-prawo
|
|
elif world_old.get( x +1, y - 1) == PUSTE:
|
|
world_new.swap(x, y, x + 1, y - 1)
|
|
elif world_old.get( x - 1, y - 1) == PUSTE:
|
|
world_new.swap(x, y, x - 1, y - 1)
|
|
|
|
# lewo/prawo
|
|
elif (
|
|
world_old.get(x - 1, y) == PUSTE
|
|
and world_old.get(x + 1, y) == PUSTE
|
|
):
|
|
world_new.swap(x, y, x + 1, y)
|
|
|
|
elif world_old.get(x + kierunek, y) == PUSTE:
|
|
world_new.swap(x, y, x + kierunek, y)
|
|
|
|
elif world_old.get(x - kierunek, y) == PUSTE:
|
|
world_new.swap(x, y, x - kierunek, y)
|
|
|
|
self.czy_dotyka(x,y,LAWA,AIR)
|
|
|
|
def update_wet_sand(self, x, y):
|
|
|
|
world_old = self.world_old
|
|
world_new = self.world_new
|
|
|
|
kierunek = random.choice([-1, 1])
|
|
szansa_w_wodzie=random.choice([True, False])
|
|
if world_old.get(x, y - 1) == PUSTE:
|
|
world_new.swap(x,y,x,y - 1)
|
|
|
|
|
|
# dół-lewo / dół-prawo
|
|
elif PUSTE == world_old.get(x + kierunek, y - 1):
|
|
if self.can_move(x + kierunek, y - 1):
|
|
world_new.swap(x, y, x + kierunek, y - 1)
|
|
elif world_old.get( x + kierunek, y - 1) == PUSTE:
|
|
if self.can_move(x + kierunek, y - 1):
|
|
world_new.swap(x, y, x + kierunek, y - 1)
|
|
# W wodzie
|
|
elif world_old.get(x, y - 1) == WODA and szansa_w_wodzie:
|
|
world_new.swap(x,y,x,y - 1)
|
|
elif world_old.get( x + kierunek, y - 1) == WODA and szansa_w_wodzie:
|
|
world_new.swap(x, y, x + kierunek, y - 1)
|
|
elif world_old.get( x + kierunek, y - 1) == WODA and szansa_w_wodzie:
|
|
world_new.swap(x, y, x + kierunek, y - 1)
|
|
|
|
def update_air(self,x,y):
|
|
world_old = self.world_old
|
|
world_new = self.world_new
|
|
kierunekx = random.choice([-1,0, 1])
|
|
kieruneky = random.choice([-1,0, 1])
|
|
lista = [False] * 10 + [True]
|
|
czy_ruszyc = random.choice(lista)
|
|
if world_old.get(x, y + 1) != PUSTE:
|
|
world_new.swap(x, y, x, y + 1)
|
|
elif self.can_move(x + kierunekx, y +kieruneky) and czy_ruszyc:
|
|
world_new.swap(x, y, x + kierunekx, y+kieruneky)
|
|
|
|
def update_lawa(self,x,y):
|
|
world_new = self.world_new
|
|
world_old = self.world_old
|
|
kierunek = random.choice([-1, 1])
|
|
if random.random() < 0.9:
|
|
return
|
|
# dół
|
|
if world_old.get(x, y - 1) == PUSTE:
|
|
world_new.swap(x, y, x, y - 1)
|
|
|
|
# dół-lewo / dół-prawo
|
|
elif world_old.get(x + 1, y - 1) == PUSTE:
|
|
world_new.swap(x, y, x + 1, y - 1)
|
|
elif world_old.get(x - 1, y - 1) == PUSTE:
|
|
world_new.swap(x, y, x - 1, y - 1)
|
|
|
|
# lewo/prawo
|
|
elif (
|
|
world_old.get(x - 1, y) == PUSTE
|
|
and world_old.get(x + 1, y) == PUSTE
|
|
):
|
|
world_new.swap(x, y, x + 1, y)
|
|
|
|
elif world_old.get(x + kierunek, y) == PUSTE:
|
|
world_new.swap(x, y, x + kierunek, y)
|
|
|
|
elif world_old.get(x - kierunek, y) == PUSTE:
|
|
world_new.swap(x, y, x - kierunek, y)
|
|
|
|
self.czy_dotyka(x,y,WODA,KAMIEN)
|
|
|
|
def on_key_press(self,key, modifiers):
|
|
|
|
if key == arcade.key.KEY_1:
|
|
self.current_material = PIASEK
|
|
|
|
elif key == arcade.key.KEY_2:
|
|
self.current_material = ZWIR
|
|
|
|
elif key == arcade.key.KEY_3:
|
|
self.current_material = WODA
|
|
|
|
elif key == arcade.key.KEY_4:
|
|
self.current_material = KAMIEN
|
|
|
|
elif key == arcade.key.KEY_5:
|
|
self.current_material = MOKRY_PIASEK
|
|
|
|
elif key == arcade.key.KEY_6:
|
|
self.current_material = AIR
|
|
|
|
elif key == arcade.key.KEY_7:
|
|
self.current_material = LAWA
|
|
|
|
elif key == arcade.key.C:
|
|
self.world_old.clear()
|
|
|
|
elif key == arcade.key.SPACE:
|
|
self.paused = not self.paused
|
|
# ==============================================
|
|
|
|
def on_mouse_press(self, x, y, button, modifiers):
|
|
|
|
self.mouse_down = True
|
|
|
|
self.mouse_x = x
|
|
self.mouse_y = y
|
|
|
|
gx = x // CELL_SIZE
|
|
gy = y // CELL_SIZE
|
|
|
|
self.paint(gx, gy)
|
|
|
|
def on_mouse_release(self, x, y, button, modifiers):
|
|
|
|
self.mouse_down = False
|
|
|
|
def on_mouse_motion(self, x, y, dx, dy):
|
|
|
|
self.mouse_x = x
|
|
self.mouse_y = y
|
|
|
|
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
|
|
|
|
self.mouse_x = x
|
|
self.mouse_y = y
|
|
|
|
def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
|
|
|
|
self.brush_size += scroll_y
|
|
|
|
if self.brush_size < 1:
|
|
self.brush_size = 1
|
|
|
|
if self.brush_size > 20:
|
|
self.brush_size = 20
|
|
|
|
def paint(self, grid_x, grid_y):
|
|
|
|
grid_x = int(grid_x)
|
|
grid_y = int(grid_y)
|
|
|
|
r = int(self.brush_size)
|
|
|
|
for dy in range(-r, r + 1):
|
|
for dx in range(-r, r + 1):
|
|
|
|
if dx * dx + dy * dy > r * r:
|
|
continue
|
|
|
|
x = grid_x + dx
|
|
y = grid_y + dy
|
|
|
|
self.world_old.set(
|
|
x,
|
|
y,
|
|
self.current_material
|
|
)
|
|
|
|
# =====================================================
|
|
# START PROGRAMU
|
|
# =====================================================
|
|
|
|
def main():
|
|
|
|
Game()
|
|
|
|
arcade.run()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |