From 17d66d5da04dac7c598b1a725f58a059c12f9bf1 Mon Sep 17 00:00:00 2001 From: csd4ni3l Date: Fri, 7 Nov 2025 18:16:57 +0100 Subject: [PATCH] Make cells modular, add crosses and t junctions and houses which have example texture and dont work yet, improve code quality, fix cell texture not being the same when pressed --- assets/graphics/powered_lines/cross/cross.png | Bin 0 -> 314 bytes .../t_junction/left_right_bottom.png | Bin 0 -> 271 bytes .../t_junction/left_right_top.png | Bin 0 -> 257 bytes .../t_junction/top_bottom_left.png | Bin 0 -> 269 bytes .../t_junction/top_bottom_right.png | Bin 0 -> 276 bytes .../graphics/unpowered_lines/cross/cross.png | Bin 0 -> 292 bytes .../t_junction/left_right_bottom.png | Bin 0 -> 251 bytes .../t_junction/left_right_top.png | Bin 0 -> 244 bytes .../t_junction/top_bottom_left.png | Bin 0 -> 258 bytes .../t_junction/top_bottom_right.png | Bin 0 -> 259 bytes game/cell.py | 62 +++++++++++ game/cells.py | 17 +++ game/play.py | 40 ++++--- game/power_line.py | 104 ------------------ utils/constants.py | 24 ++++ utils/preload.py | 50 ++++++--- 16 files changed, 163 insertions(+), 134 deletions(-) create mode 100644 assets/graphics/powered_lines/cross/cross.png create mode 100644 assets/graphics/powered_lines/t_junction/left_right_bottom.png create mode 100644 assets/graphics/powered_lines/t_junction/left_right_top.png create mode 100644 assets/graphics/powered_lines/t_junction/top_bottom_left.png create mode 100644 assets/graphics/powered_lines/t_junction/top_bottom_right.png create mode 100644 assets/graphics/unpowered_lines/cross/cross.png create mode 100644 assets/graphics/unpowered_lines/t_junction/left_right_bottom.png create mode 100644 assets/graphics/unpowered_lines/t_junction/left_right_top.png create mode 100644 assets/graphics/unpowered_lines/t_junction/top_bottom_left.png create mode 100644 assets/graphics/unpowered_lines/t_junction/top_bottom_right.png create mode 100644 game/cell.py create mode 100644 game/cells.py delete mode 100644 game/power_line.py diff --git a/assets/graphics/powered_lines/cross/cross.png b/assets/graphics/powered_lines/cross/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..86ef4f93a0016a94b7be4bb8d073fa3f9f4fb883 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLH@YFfw|&IEGZrd3)25tJy%HHF3Gx zJLYW-XJ-OH|uZyD8UIo=;`UCp^)h;{0^*IhPx)1zbELj^DH z+uxV``0paEZL4K+j{lWsz97+XjKPF$fgwXLV+M~yGJ_PV(5H$kGu_$^rapaLYgTe~DWM4fwm^Ua literal 0 HcmV?d00001 diff --git a/assets/graphics/powered_lines/t_junction/left_right_bottom.png b/assets/graphics/powered_lines/t_junction/left_right_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..5c12728e7ac39f43f4c0a4fca746af07ea42c4db GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJMYo0ESAr*7pUOmX!>>%KlsQ#u( zd{V>OnPNdLz7b7ZH8q~dh^m!8JYLBt`L!I#kl zJ7aU|&P&!RhUHbQCI|1nkexK;KTF%gpNlWXnwy0U*&hXouDV|;a z=%vM>%KhuMXJ)*IR-Qr1iKKa?qti69u;`^?9Nq08O@3faqDz{$0XL9%diJA?| k7-lhLa4o=Oh{}Ip+)`}lALF;W1?WQtPgg&ebxsLQ0N`+JjQ{`u literal 0 HcmV?d00001 diff --git a/assets/graphics/powered_lines/t_junction/left_right_top.png b/assets/graphics/powered_lines/t_junction/left_right_top.png new file mode 100644 index 0000000000000000000000000000000000000000..92d36004fcde52fd3da3074a4b5020f721bc2196 GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJM6P_-PAr*7p-f-k6q}Y%0-|W-= zR|glKdOP32OL*^L&De9yAdwy@DqMcm&-N%rtbC_BrkfNo;yNZ^AwOSDWn`-!e%aH=J|XV9)JsE4n)TE@#K? zp0aAyjdewvWuMD4zTj|hXRue%yn!U{`upU=9Kwomep o*~gDJ{Pz8Eb;D=RboM_CM_22=UuL^}InbL7p00i_>zopr0HQN(lmGw# literal 0 HcmV?d00001 diff --git a/assets/graphics/unpowered_lines/cross/cross.png b/assets/graphics/unpowered_lines/cross/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..65e48bf7beef98ca6e6e4954d0fc4c7b5a5a32cb GIT binary patch literal 292 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJM*PbqpAr*7p-aN?JVj$4^P}Y~Z zi7R-v?h=mf4XnF`i^`f(_?xmBC)cvvD?gQasNdz_iCydFTW!m=?sInW(lm4nH8mFe zcz5Y%+3)o{4#^Bs%oijYjxm_9Eih#0Wz0YoI%&~2)ouBdb+30_KWy-&Ha>Ilw$9)D zzuh&T?)6<7J4@a0nAhU(wf>rS%}q{kI`cW^ahuwTRqv17e<3jMtBLQbs(IHrI7s{-k$y(Kzui3DSVHQ&c*8&_2-P2C* zKjfsPec?Nw_Iuy|j4ze*u9wb?%+`IrCwKO4{{0_j1W8$cw6!``7kAxsX^0nR;)mVh zcWd7qS-17t`K@8)>$mppZ~R|#TAu$rC+L+)M$X3{{J+;< zyY+sCUvQiFQjx0m=w)+^_a4>_UOV;P!)cjco}2z@KZDIMu<335I~a@>N$>OC_01pX O4F*qFKbLh*2~7aizGOTA literal 0 HcmV?d00001 diff --git a/assets/graphics/unpowered_lines/t_junction/top_bottom_left.png b/assets/graphics/unpowered_lines/t_junction/top_bottom_left.png new file mode 100644 index 0000000000000000000000000000000000000000..12d1b2ab6229669293811f50cedefca2841bc6bd GIT binary patch literal 258 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJMlb$Y)Ar*7p-aN>8*g(ML;@u6t zE8ImNDqU&XE&O6#caZ^0(e*5YKj8;-?kDhc#$QjhjLP0yULK!)HOgDF*suJ4_0!Ge z?`)W^a0Dnj^fH)YXLMhSy7Wcj@R@5-p1=EF>|HK*LhN?^hW{t)zV%xatNCAvn!N1& zZ@FZf{})u|8f?~Bn&RuOqWeE~X0e*Dd=>krxJSlu{}u6=UtN1ZJXl+IPWOf86riUV NJYD@<);T3K0RX12W3vDN literal 0 HcmV?d00001 diff --git a/assets/graphics/unpowered_lines/t_junction/top_bottom_right.png b/assets/graphics/unpowered_lines/t_junction/top_bottom_right.png new file mode 100644 index 0000000000000000000000000000000000000000..504f13679909be22da6106d24f967ece0ae058c0 GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1SD0tpLGJMQ=Tr4Ar*7p-aN?Lq9EdW@u!up zhg0sB1VOPx?y~elQV|dGj93rU8}8iR;O^&9(kOfUs$cWoh)zg P&{qteu6{1-oD!M<{(ETI literal 0 HcmV?d00001 diff --git a/game/cell.py b/game/cell.py new file mode 100644 index 0000000..45183bc --- /dev/null +++ b/game/cell.py @@ -0,0 +1,62 @@ +import arcade, arcade.gui + +from utils.constants import ROTATIONS, NEIGHBOURS +from utils.preload import TEXTURE_MAP + +def get_opposite(direction): + if direction == "l": + return "r" + elif direction == "r": + return "l" + elif direction == "t": + return "b" + elif direction == "b": + return "t" + +class Cell(arcade.gui.UITextureButton): + def __init__(self, cell_type, left_neighbour, top_neighbour): + super().__init__(texture=TEXTURE_MAP[cell_type, ROTATIONS[cell_type][0], cell_type == "power_source"]) + + self.rotation = ROTATIONS[cell_type][0] + self.cell_type = cell_type + self.powered = False + self.left_neighbour, self.top_neighbour = left_neighbour, top_neighbour + self.right_neighbour, self.bottom_neighbour = None, None + + def get_neighbour(self, name): + if name == "l": + return self.left_neighbour + elif name == "r": + return self.right_neighbour + elif name == "b": + return self.bottom_neighbour + elif name == "t": + return self.top_neighbour + + def get_connected_neighbours(self): + return [ + self.get_neighbour(neighbour_direction) for neighbour_direction in NEIGHBOURS[self.rotation] + if ( + self.get_neighbour(neighbour_direction) and + get_opposite(neighbour_direction) in NEIGHBOURS[self.get_neighbour(neighbour_direction).rotation] + ) + ] + + def update_value(self): + self.powered = any([neighbour.powered for neighbour in self.get_connected_neighbours()]) + + def update_visual(self): + self.texture = TEXTURE_MAP[(self.cell_type, self.rotation, self.powered)] + self.texture_hovered = TEXTURE_MAP[(self.cell_type, self.rotation, self.powered)] + self.texture_pressed = TEXTURE_MAP[(self.cell_type, self.rotation, self.powered)] + self._requires_render = True + + def next_rotation(self): + current_index = ROTATIONS[self.cell_type].index(self.rotation) + + if current_index + 1 == len(ROTATIONS[self.cell_type]): + self.rotation = ROTATIONS[self.cell_type][0] + else: + self.rotation = ROTATIONS[self.cell_type][current_index + 1] + + self.update_visual() \ No newline at end of file diff --git a/game/cells.py b/game/cells.py new file mode 100644 index 0000000..08f2495 --- /dev/null +++ b/game/cells.py @@ -0,0 +1,17 @@ +from game.cell import Cell + +class House(Cell): + def __init__(self, left_neighbour, top_neighbour): + super().__init__("house", left_neighbour, top_neighbour) + +class PowerSource(Cell): + def __init__(self, left_neighbour, top_neighbour): + super().__init__("power_source", left_neighbour, top_neighbour) + self.on_click = lambda e: self.next_rotation() + +class PowerLine(Cell): + def __init__(self, cell_type, left_neighbour, top_neighbour): + super().__init__(cell_type, left_neighbour, top_neighbour) + + if not cell_type == "cross": + self.on_click = lambda e: self.next_rotation() \ No newline at end of file diff --git a/game/play.py b/game/play.py index 5543ca3..b2cdaac 100644 --- a/game/play.py +++ b/game/play.py @@ -5,7 +5,7 @@ from utils.preload import button_texture, button_hovered_texture from collections import deque -from game.power_line import PowerLine +from game.cells import * class Game(arcade.gui.UIView): def __init__(self, pypresence_client, difficulty): @@ -15,8 +15,9 @@ class Game(arcade.gui.UIView): self.pypresence_client.update(state='In Game', start=self.pypresence_client.start_time) self.difficulty = difficulty - self.power_lines = [] + self.cells = [] self.power_sources = [] + self.houses = [] self.anchor = self.add_widget(arcade.gui.UIAnchorLayout(size_hint=(1, 1))) self.grid_size = list(map(int, difficulty.split("x"))) @@ -30,31 +31,36 @@ class Game(arcade.gui.UIView): self.anchor.add(self.back_button, anchor_x="left", anchor_y="top", align_x=5, align_y=-5) for row in range(self.grid_size[0]): - self.power_lines.append([]) + self.cells.append([]) for col in range(self.grid_size[1]): - left_neighbour = self.power_lines[row][col - 1] if col > 0 else None - top_neighbour = self.power_lines[row - 1][col] if row > 0 else None + left_neighbour = self.cells[row][col - 1] if col > 0 else None + top_neighbour = self.cells[row - 1][col] if row > 0 else None - line_type = random.choice(["line", "corner", "power_source"]) - power_line = PowerLine(line_type, left_neighbour, top_neighbour) + cell_type = random.choice(["line", "corner", "t_junction", "cross", "power_source", "house"]) - if line_type == "power_source": - self.power_sources.append(power_line) + if cell_type in ["line", "corner", "t_junction", "cross"]: + cell = PowerLine(cell_type, left_neighbour, top_neighbour) + elif cell_type == "power_source": + cell = PowerSource(left_neighbour, top_neighbour) + self.power_sources.append(cell) + elif cell_type == "house": + cell = House(left_neighbour, top_neighbour) + self.houses.append(cell) - self.power_grid.add(power_line, row=row, column=col) - self.power_lines[row].append(power_line) + self.power_grid.add(cell, row=row, column=col) + self.cells[row].append(cell) if left_neighbour: - left_neighbour.right_neighbour = power_line + left_neighbour.right_neighbour = cell if top_neighbour: - top_neighbour.bottom_neighbour = power_line + top_neighbour.bottom_neighbour = cell - arcade.schedule(self.update_grid, 1 / 10) + arcade.schedule(self.update_grid, 1 / 8) def update_grid(self, _): - for row in self.power_lines: + for row in self.cells: for power_line in row: - if power_line.line_type != "power_source": + if power_line.cell_type != "power_source": power_line.powered = False queue = deque(self.power_sources) @@ -74,7 +80,7 @@ class Game(arcade.gui.UIView): if id(connected_neighbour) not in visited: queue.append(connected_neighbour) - for row in self.power_lines: + for row in self.cells: for power_line in row: power_line.update_visual() diff --git a/game/power_line.py b/game/power_line.py deleted file mode 100644 index f648694..0000000 --- a/game/power_line.py +++ /dev/null @@ -1,104 +0,0 @@ -import arcade, arcade.gui - -from utils.preload import * - -TEXTURE_MAP = { - ("line", "vertical", True): vertical_powered, - ("line", "vertical", False): vertical_unpowered, - - ("line", "horizontal", True): horizontal_powered, - ("line", "horizontal", False): horizontal_unpowered, - - ("corner", "left_bottom", True): left_bottom_powered, - ("corner", "left_bottom", False): left_bottom_unpowered, - - ("corner", "left_top", True): left_top_powered, - ("corner", "left_top", False): left_top_unpowered, - - ("corner", "right_bottom", True): right_bottom_powered, - ("corner", "right_bottom", False): right_bottom_unpowered, - - ("corner", "right_top", True): right_top_powered, - ("corner", "right_top", False): right_top_unpowered, - - ("power_source", "all", True): power_source, -} - -ROTATIONS = { - "line": ["vertical", "horizontal"], - "corner": ["right_bottom", "left_bottom", "left_top", "right_top"], - "power_source": ["all"] -} - -NEIGHBOURS = { - "vertical": ["b", "t"], - "horizontal": ["l", "r"], - "left_bottom": ["l", "b"], - "right_bottom": ["r", "b"], - "left_top": ["l", "t"], - "right_top": ["r", "t"], - "all": ["l", "r", "t", "b"] -} - -def get_opposite(direction): - if direction == "l": - return "r" - elif direction == "r": - return "l" - - elif direction == "t": - return "b" - elif direction == "b": - return "t" - -class PowerLine(arcade.gui.UITextureButton): - def __init__(self, line_type, left_neighbour, top_neighbour): - super().__init__(texture=TEXTURE_MAP[line_type, ROTATIONS[line_type][0], line_type == "power_source"]) - - self.line_type = line_type - self.rotation = ROTATIONS[line_type][0] - self.powered = self.line_type == "power_source" - - self.left_neighbour, self.top_neighbour = left_neighbour, top_neighbour - self.right_neighbour, self.bottom_neighbour = None, None - - self.on_click = lambda e: self.next_rotation() - - self.update_visual() - - def next_rotation(self): - current_index = ROTATIONS[self.line_type].index(self.rotation) - - if current_index + 1 == len(ROTATIONS[self.line_type]): - self.rotation = ROTATIONS[self.line_type][0] - else: - self.rotation = ROTATIONS[self.line_type][current_index + 1] - - self.update_visual() - - def get_neighbour(self, name): - if name == "l": - return self.left_neighbour - elif name == "r": - return self.right_neighbour - elif name == "b": - return self.bottom_neighbour - elif name == "t": - return self.top_neighbour - - def get_connected_neighbours(self): - return [ - self.get_neighbour(neighbour_direction) for neighbour_direction in NEIGHBOURS[self.rotation] - if ( - self.get_neighbour(neighbour_direction) and - get_opposite(neighbour_direction) in NEIGHBOURS[self.get_neighbour(neighbour_direction).rotation] - ) - ] - - def update_value(self): - self.powered = any([neighbour.powered for neighbour in self.get_connected_neighbours()]) - - def update_visual(self): - self.texture = TEXTURE_MAP[(self.line_type, self.rotation, self.powered)] - self.texture_hovered = TEXTURE_MAP[(self.line_type, self.rotation, self.powered)] - self._requires_render = True \ No newline at end of file diff --git a/utils/constants.py b/utils/constants.py index 41c9ae0..d60fed6 100644 --- a/utils/constants.py +++ b/utils/constants.py @@ -3,6 +3,30 @@ from arcade.types import Color from arcade.gui.widgets.buttons import UITextureButtonStyle, UIFlatButtonStyle from arcade.gui.widgets.slider import UISliderStyle +ROTATIONS = { + "line": ["vertical", "horizontal"], + "corner": ["right_bottom", "left_bottom", "left_top", "right_top"], + "t_junction": ["top_bottom_right", "left_right_bottom", "top_bottom_left", "left_right_top"], + "cross": ["cross"], + "power_source": ["cross"], + "house": ["cross"] +} + +NEIGHBOURS = { + "vertical": ["b", "t"], + "horizontal": ["l", "r"], + "left_bottom": ["l", "b"], + "right_bottom": ["r", "b"], + "left_top": ["l", "t"], + "right_top": ["r", "t"], + "top_bottom_right": ["t", "b", "r"], + "top_bottom_left": ["t", "b", "l"], + "left_right_bottom": ["l", "r", "b"], + "left_right_top": ["l", "r", "t"], + "cross": ["l", "r", "t", "b"] +} + + menu_background_color = (30, 30, 47) log_dir = 'logs' discord_presence_id = 1435687634960777266 diff --git a/utils/preload.py b/utils/preload.py index 43fe057..84721a6 100644 --- a/utils/preload.py +++ b/utils/preload.py @@ -3,18 +3,42 @@ import arcade.gui, arcade button_texture = arcade.gui.NinePatchTexture(64 // 4, 64 // 4, 64 // 4, 64 // 4, arcade.load_texture("assets/graphics/button.png")) button_hovered_texture = arcade.gui.NinePatchTexture(64 // 4, 64 // 4, 64 // 4, 64 // 4, arcade.load_texture("assets/graphics/button_hovered.png")) -vertical_powered = arcade.load_texture("assets/graphics/powered_lines/line/vertical.png") -horizontal_powered = arcade.load_texture("assets/graphics/powered_lines/line/horizontal.png") -left_bottom_powered = arcade.load_texture("assets/graphics/powered_lines/corner/left_bottom.png") -left_top_powered = arcade.load_texture("assets/graphics/powered_lines/corner/left_top.png") -right_bottom_powered = arcade.load_texture("assets/graphics/powered_lines/corner/right_bottom.png") -right_top_powered = arcade.load_texture("assets/graphics/powered_lines/corner/right_top.png") +TEXTURE_MAP = { + ("line", "vertical", True): arcade.load_texture("assets/graphics/powered_lines/line/vertical.png"), + ("line", "vertical", False): arcade.load_texture("assets/graphics/unpowered_lines/line/vertical.png"), + + ("line", "horizontal", True): arcade.load_texture("assets/graphics/powered_lines/line/horizontal.png"), + ("line", "horizontal", False): arcade.load_texture("assets/graphics/unpowered_lines/line/horizontal.png"), -vertical_unpowered = arcade.load_texture("assets/graphics/unpowered_lines/line/vertical.png") -horizontal_unpowered = arcade.load_texture("assets/graphics/unpowered_lines/line/horizontal.png") -left_bottom_unpowered = arcade.load_texture("assets/graphics/unpowered_lines/corner/left_bottom.png") -left_top_unpowered = arcade.load_texture("assets/graphics/unpowered_lines/corner/left_top.png") -right_bottom_unpowered = arcade.load_texture("assets/graphics/unpowered_lines/corner/right_bottom.png") -right_top_unpowered = arcade.load_texture("assets/graphics/unpowered_lines/corner/right_top.png") + ("corner", "left_bottom", True): arcade.load_texture("assets/graphics/powered_lines/corner/left_bottom.png"), + ("corner", "left_bottom", False): arcade.load_texture("assets/graphics/unpowered_lines/corner/left_bottom.png"), + + ("corner", "left_top", True): arcade.load_texture("assets/graphics/powered_lines/corner/left_top.png"), + ("corner", "left_top", False): arcade.load_texture("assets/graphics/unpowered_lines/corner/left_top.png"), + + ("corner", "right_bottom", True): arcade.load_texture("assets/graphics/powered_lines/corner/right_bottom.png"), + ("corner", "right_bottom", False): arcade.load_texture("assets/graphics/unpowered_lines/corner/right_bottom.png"), + + ("corner", "right_top", True): arcade.load_texture("assets/graphics/powered_lines/corner/right_top.png"), + ("corner", "right_top", False): arcade.load_texture("assets/graphics/unpowered_lines/corner/right_top.png"), -power_source = arcade.load_texture("assets/graphics/power_source.png") + ("t_junction", "left_right_bottom", True): arcade.load_texture("assets/graphics/powered_lines/t_junction/left_right_bottom.png"), + ("t_junction", "left_right_bottom", False): arcade.load_texture("assets/graphics/unpowered_lines/t_junction/left_right_bottom.png"), + + ("t_junction", "left_right_top", True): arcade.load_texture("assets/graphics/powered_lines/t_junction/left_right_top.png"), + ("t_junction", "left_right_top", False): arcade.load_texture("assets/graphics/unpowered_lines/t_junction/left_right_top.png"), + + ("t_junction", "top_bottom_left", True): arcade.load_texture("assets/graphics/powered_lines/t_junction/top_bottom_left.png"), + ("t_junction", "top_bottom_left", False): arcade.load_texture("assets/graphics/unpowered_lines/t_junction/top_bottom_left.png"), + + ("t_junction", "top_bottom_right", True): arcade.load_texture("assets/graphics/powered_lines/t_junction/top_bottom_right.png"), + ("t_junction", "top_bottom_right", False): arcade.load_texture("assets/graphics/unpowered_lines/t_junction/top_bottom_right.png"), + + ("cross", "cross", True): arcade.load_texture("assets/graphics/powered_lines/cross/cross.png"), + ("cross", "cross", False): arcade.load_texture("assets/graphics/unpowered_lines/cross/cross.png"), + + ("power_source", "cross", True): arcade.load_texture("assets/graphics/power_source.png"), + + ("house", "cross", False): button_texture, + ("house", "cross", True): button_texture, +}