fix some file manager stuff, add sprite adding, convert values to float before using them, add TexturedRectangles so custom sprites work, remove morphing, fix DO blocks and some others not having vars, make blocks bigger, fix trash can not working most of the time, add more key inputs

This commit is contained in:
csd4ni3l
2025-12-07 22:43:07 +01:00
parent b74115b489
commit 3a7e40d833
5 changed files with 146 additions and 80 deletions

View File

@@ -6,10 +6,11 @@ from utils.preload import button_texture, button_hovered_texture
from arcade.gui.experimental.scroll_area import UIScrollArea, UIScrollBar from arcade.gui.experimental.scroll_area import UIScrollArea, UIScrollBar
class FileManager(arcade.gui.UIAnchorLayout): class FileManager(arcade.gui.UIAnchorLayout):
def __init__(self, width, allowed_extensions): def __init__(self, width, height, size_hint, allowed_extensions):
super().__init__(size_hint=(0.95, 0.875), vertical=False) super().__init__(size_hint=size_hint, vertical=False)
self.filemanager_width = width self.filemanager_width = width
self.filemanager_height = height
self.current_directory = os.path.expanduser("~") self.current_directory = os.path.expanduser("~")
self.allowed_extensions = allowed_extensions self.allowed_extensions = allowed_extensions
@@ -36,9 +37,9 @@ class FileManager(arcade.gui.UIAnchorLayout):
self.bottom_box = self.add(arcade.gui.UIBoxLayout(space_between=5), anchor_x="center", anchor_y="bottom", align_y=5) self.bottom_box = self.add(arcade.gui.UIBoxLayout(space_between=5), anchor_x="center", anchor_y="bottom", align_y=5)
self.filename_label = self.bottom_box.add(arcade.gui.UILabel(text="Filename:", font_name="Roboto", font_size=17)) self.filename_label = self.bottom_box.add(arcade.gui.UILabel(text="Filename:", font_name="Roboto", font_size=17))
self.filename_input = self.bottom_box.add(arcade.gui.UIInputText(width=self.filemanager_width * 0.35, height=self.filemanager_width * 0.02).with_border(color=arcade.color.WHITE)) self.filename_input = self.bottom_box.add(arcade.gui.UIInputText(width=self.filemanager_width * 0.35, height=self.filemanager_height * 0.05).with_border(color=arcade.color.WHITE))
self.submit_button = self.bottom_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="Submit", style=button_style, width=self.filemanager_width * 0.5, height=self.filemanager_width * 0.025)) self.submit_button = self.bottom_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="Submit", style=button_style, width=self.filemanager_width * 0.5, height=self.filemanager_height * 0.05))
self.submit_button.on_click = lambda event: self.submit(self.current_directory) self.submit_button.on_click = lambda event: self.submit(self.current_directory)
self.submit_button.visible = False self.submit_button.visible = False
@@ -55,9 +56,7 @@ class FileManager(arcade.gui.UIAnchorLayout):
def submit(self, content): def submit(self, content):
self.submitted_content = content if self.mode == "import" else f"{content}/{self.filename_input.text}" self.submitted_content = content if self.mode == "import" else f"{content}/{self.filename_input.text}"
self.disable()
def get_content(self, directory): def get_content(self, directory):
if not directory in self.content_cache or time.perf_counter() - self.content_cache[directory][-1] >= 30: if not directory in self.content_cache or time.perf_counter() - self.content_cache[directory][-1] >= 30:
try: try:
@@ -109,19 +108,16 @@ class FileManager(arcade.gui.UIAnchorLayout):
self.current_directory_label.text = self.current_directory self.current_directory_label.text = self.current_directory
self.file_buttons.append(self.files_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="Go up", style=button_style, width=self.filemanager_width / 1.5))) self.file_buttons.append(self.files_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text="Go up", style=button_style, width=self.filemanager_width / 1.5, height=self.filemanager_height * 0.05,)))
self.file_buttons[-1].on_click = lambda event, directory=self.current_directory: self.change_directory(os.path.dirname(directory)) self.file_buttons[-1].on_click = lambda event, directory=self.current_directory: self.change_directory(os.path.dirname(directory))
for file in self.get_content(self.current_directory): for file in self.get_content(self.current_directory):
self.file_buttons.append(self.files_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=file, style=button_style, width=self.filemanager_width / 1.5))) self.file_buttons.append(self.files_box.add(arcade.gui.UITextureButton(texture=button_texture, texture_hovered=button_hovered_texture, text=file, style=button_style, width=self.filemanager_width / 1.5, height=self.filemanager_height * 0.05,)))
if os.path.isdir(f"{self.current_directory}/{file}"): if os.path.isdir(f"{self.current_directory}/{file}"):
self.file_buttons[-1].on_click = lambda event, directory=f"{self.current_directory}/{file}": self.change_directory(directory) self.file_buttons[-1].on_click = lambda event, directory=f"{self.current_directory}/{file}": self.change_directory(directory)
else: else:
self.file_buttons[-1].on_click = lambda event, file=f"{self.current_directory}/{file}": self.submit(file) self.file_buttons[-1].on_click = lambda event, file=f"{self.current_directory}/{file}": self.submit(file)
def disable(self):
self.parent.parent.disable() # The FileManager UIManager. self.parent is the FileManager UIAnchorLayout
def change_directory(self, directory): def change_directory(self, directory):
if directory.startswith("//"): # Fix / paths if directory.startswith("//"): # Fix / paths
directory = directory[1:] directory = directory[1:]

View File

@@ -3,10 +3,10 @@ import arcade, arcade.gui, pyglet, random, json
from dataclasses import asdict from dataclasses import asdict
from utils.preload import SPRITE_TEXTURES, button_texture, button_hovered_texture from utils.preload import SPRITE_TEXTURES, button_texture, button_hovered_texture
from utils.constants import button_style, DO_RULES, IF_RULES, SHAPES, ALLOWED_INPUT from utils.constants import button_style, DO_RULES, IF_RULES, SPRITES, ALLOWED_INPUT
from game.rules import RuleUI, Block, VarBlock from game.rules import RuleUI, Block, VarBlock
from game.sprites import BaseShape, Rectangle, Circle, Triangle from game.sprites import BaseShape, Rectangle, Circle, Triangle, TexturedRectangle
from game.file_manager import FileManager from game.file_manager import FileManager
class Game(arcade.gui.UIView): class Game(arcade.gui.UIView):
@@ -23,7 +23,7 @@ class Game(arcade.gui.UIView):
self.rules_box = RuleUI(self.window) self.rules_box = RuleUI(self.window)
self.file_manager = FileManager(self.window.width * 0.95, [".json"]).with_border() self.file_manager = FileManager(self.window.width * 0.95, self.window.height * 0.875, (0.95, 0.875), [".json"]).with_border()
self.ui_selector_box = self.anchor.add(arcade.gui.UIBoxLayout(vertical=False, space_between=self.window.width / 100), anchor_x="left", anchor_y="bottom", align_y=5, align_x=self.window.width / 100) self.ui_selector_box = self.anchor.add(arcade.gui.UIBoxLayout(vertical=False, space_between=self.window.width / 100), anchor_x="left", anchor_y="bottom", align_y=5, align_x=self.window.width / 100)
self.add_ui_selector("Simulation", lambda event: self.simulation()) self.add_ui_selector("Simulation", lambda event: self.simulation())
@@ -39,8 +39,18 @@ class Game(arcade.gui.UIView):
self.rulesets = self.rules_box.rulesets self.rulesets = self.rules_box.rulesets
self.sprites_box = arcade.gui.UIAnchorLayout(size_hint=(0.95, 0.9)) self.sprite_add_filemanager = FileManager(self.window.width * 0.9, self.window.height * 0.75, (0.9, 0.75), [".png", ".jpg", ".jpeg", ".bmp", ".gif"])
self.sprite_types = SHAPES self.sprite_add_ui = arcade.gui.UIBoxLayout(size_hint=(0.95, 0.9), space_between=10)
self.sprite_add_ui.add(arcade.gui.UILabel(text="Add Sprite", font_size=24, text_color=arcade.color.WHITE))
self.sprite_add_ui.add(arcade.gui.UILabel(text="Sprite Name:", font_size=18, text_color=arcade.color.WHITE))
self.sprite_name_input = self.sprite_add_ui.add(arcade.gui.UIInputText(width=self.window.width * 0.4, height=self.window.height * 0.05).with_border(color=arcade.color.WHITE))
self.sprite_add_ui.add(arcade.gui.UILabel(text="Select a texture for the sprite:", font_size=18, text_color=arcade.color.WHITE))
self.sprite_add_ui.add(self.sprite_add_filemanager, anchor_x="center", anchor_y="bottom", align_y=25)
self.sprites_ui = arcade.gui.UIAnchorLayout(size_hint=(0.95, 0.9))
self.sprite_types = SPRITES
self.shapes = [] self.shapes = []
self.shape_batch = pyglet.graphics.Batch() self.shape_batch = pyglet.graphics.Batch()
@@ -52,6 +62,7 @@ class Game(arcade.gui.UIView):
button.on_click = on_click button.on_click = on_click
def move_x(self, a, shape): def move_x(self, a, shape):
a = float(a)
if isinstance(shape, Triangle): if isinstance(shape, Triangle):
shape.x += a shape.x += a
shape.x2 += a shape.x2 += a
@@ -60,6 +71,7 @@ class Game(arcade.gui.UIView):
shape.x += a shape.x += a
def move_y(self, a, shape): def move_y(self, a, shape):
a = float(a)
if isinstance(shape, Triangle): if isinstance(shape, Triangle):
shape.y += a shape.y += a
shape.y2 += a shape.y2 += a
@@ -68,6 +80,7 @@ class Game(arcade.gui.UIView):
shape.y += a shape.y += a
def change_x(self, a, shape): def change_x(self, a, shape):
a = float(a)
if isinstance(shape, Triangle): if isinstance(shape, Triangle):
offset_x2 = shape.x2 - shape.x offset_x2 = shape.x2 - shape.x
offset_x3 = shape.x3 - shape.x offset_x3 = shape.x3 - shape.x
@@ -79,6 +92,7 @@ class Game(arcade.gui.UIView):
shape.x = a shape.x = a
def change_y(self, a, shape): def change_y(self, a, shape):
a = float(a)
if isinstance(shape, Triangle): if isinstance(shape, Triangle):
offset_y2 = shape.y2 - shape.y offset_y2 = shape.y2 - shape.y
offset_y3 = shape.y3 - shape.y offset_y3 = shape.y3 - shape.y
@@ -90,18 +104,22 @@ class Game(arcade.gui.UIView):
shape.y = a shape.y = a
def change_x_velocity(self, a, shape): def change_x_velocity(self, a, shape):
a = float(a)
shape.x_velocity = a shape.x_velocity = a
self.triggered_events.append(["x_velocity_change", {"event_shape_type": shape.shape_type, "shape_size": shape.shape_size, "shape_x": shape.x, "shape_y": shape.y, "shape": shape, "shape_color": shape.shape_color}]) self.triggered_events.append(["x_velocity_change", {"event_shape_type": shape.shape_type, "shape_size": shape.shape_size, "shape_x": shape.x, "shape_y": shape.y, "shape": shape, "shape_color": shape.shape_color}])
def change_y_velocity(self, a, shape): def change_y_velocity(self, a, shape):
a = float(a)
shape.y_velocity = a shape.y_velocity = a
self.triggered_events.append(["y_velocity_change", {"event_shape_type": shape.shape_type, "shape_size": shape.shape_size, "shape_x": shape.x, "shape_y": shape.y, "shape": shape, "shape_color": shape.shape_color}]) self.triggered_events.append(["y_velocity_change", {"event_shape_type": shape.shape_type, "shape_size": shape.shape_size, "shape_x": shape.x, "shape_y": shape.y, "shape": shape, "shape_color": shape.shape_color}])
def change_x_gravity(self, a): def change_x_gravity(self, a):
a = float(a)
self.x_gravity = a self.x_gravity = a
self.triggered_events.append(["x_gravity_change", {}]) self.triggered_events.append(["x_gravity_change", {}])
def change_y_gravity(self, a): def change_y_gravity(self, a):
a = float(a)
self.y_gravity = a self.y_gravity = a
self.triggered_events.append(["y_gravity_change", {}]) self.triggered_events.append(["y_gravity_change", {}])
@@ -116,6 +134,7 @@ class Game(arcade.gui.UIView):
shape.delete() shape.delete()
def change_size(self, a, shape): def change_size(self, a, shape):
a = float(a)
if isinstance(shape, Circle): if isinstance(shape, Circle):
shape.radius = a shape.radius = a
elif isinstance(shape, Rectangle): elif isinstance(shape, Rectangle):
@@ -146,38 +165,56 @@ class Game(arcade.gui.UIView):
elif shape_type == "triangle": elif shape_type == "triangle":
self.shapes.append(Triangle(x, y, x + 10, y, x + 5, y + 10, color=arcade.color.WHITE, batch=self.shape_batch)) self.shapes.append(Triangle(x, y, x + 10, y, x + 5, y + 10, color=arcade.color.WHITE, batch=self.shape_batch))
else:
self.shapes.append(TexturedRectangle(shape_type, img=SPRITE_TEXTURES.get(shape_type, SPRITE_TEXTURES["rectangle"]), x=x, y=y, batch=self.shape_batch))
shape = self.shapes[-1] shape = self.shapes[-1]
self.triggered_events.append(["spawn", {"event_shape_type": shape.shape_type, "shape_size": shape.shape_size, "shape_x": shape.x, "shape_y": shape.y, "shape": shape, "shape_color": shape.shape_color}]) self.triggered_events.append(["spawn", {"event_shape_type": shape.shape_type, "shape_size": shape.shape_size, "shape_x": shape.x, "shape_y": shape.y, "shape": shape, "shape_color": shape.shape_color}])
def morph(self, a, shape): def add_sprite(self):
old_shape_x, old_shape_y, old_shape_size, old_shape_color = shape.x, shape.y, shape.shape_size, shape.shape_color self.disable_previous()
self.destroy(shape)
if a == "circle": self.mode = "sprite_add"
self.shapes.append(Circle(old_shape_x, old_shape_y, old_shape_size, color=getattr(arcade.color, old_shape_color), batch=self.shape_batch))
elif a == "rectangle": self.anchor.add(self.sprite_add_ui, anchor_x="center", anchor_y="center")
self.shapes.append(Rectangle(old_shape_x, old_shape_y, width=old_shape_size, height=old_shape_size, color=getattr(arcade.color, old_shape_color), batch=self.shape_batch))
def check_selection(delta_time):
elif a == "triangle": if self.sprite_add_filemanager.submitted_content:
self.shapes.append(Triangle(old_shape_x, old_shape_y, old_shape_x + old_shape_size, old_shape_y, old_shape_x + int(old_shape_size / 2), old_shape_y + old_shape_size, color=getattr(arcade.color, old_shape_color), batch=self.shape_batch)) texture = arcade.load_texture(self.sprite_add_filemanager.submitted_content)
SPRITE_TEXTURES[self.sprite_name_input.text] = texture
SPRITES[self.sprite_name_input.text] = self.sprite_add_filemanager.submitted_content
self.sprites_grid.clear()
for n, shape in enumerate(SPRITES):
row, col = n % 8, n // 8
box = self.sprites_grid.add(arcade.gui.UIBoxLayout(), row=row, column=col)
box.add(arcade.gui.UILabel(text=shape, font_size=16, text_color=arcade.color.WHITE))
box.add(arcade.gui.UIImage(texture=SPRITE_TEXTURES[shape], width=self.window.width / 15, height=self.window.width / 15))
self.anchor.remove(self.sprite_add_ui)
arcade.unschedule(check_selection)
arcade.schedule(check_selection, 0.1)
def on_show_view(self): def on_show_view(self):
super().on_show_view() super().on_show_view()
self.sprites_box.add(arcade.gui.UILabel(text="Sprites", font_size=24, text_color=arcade.color.WHITE), anchor_x="center", anchor_y="top") self.sprites_ui.add(arcade.gui.UILabel(text="Sprites", font_size=24, text_color=arcade.color.WHITE), anchor_x="center", anchor_y="top")
self.sprites_grid = self.sprites_box.add(arcade.gui.UIGridLayout(columns=8, row_count=8, align="left", vertical_spacing=10, horizontal_spacing=10, size_hint=(0.95, 0.85)), anchor_x="center", anchor_y="center").with_border() self.sprites_grid = self.sprites_ui.add(arcade.gui.UIGridLayout(columns=8, row_count=8, align="left", vertical_spacing=10, horizontal_spacing=10, size_hint=(0.95, 0.85), width=self.window.width * 0.95, height=self.window.height * 0.85), anchor_x="center", anchor_y="center")
for n, shape in enumerate(SHAPES): for n, shape in enumerate(SPRITES):
row, col = n % 8, n // 8 row, col = n % 8, n // 8
box = self.sprites_grid.add(arcade.gui.UIBoxLayout(), row=row, column=col) box = self.sprites_grid.add(arcade.gui.UIBoxLayout(), row=row, column=col)
box.add(arcade.gui.UILabel(text=shape, font_size=16, text_color=arcade.color.WHITE)) box.add(arcade.gui.UILabel(text=shape, font_size=16, text_color=arcade.color.WHITE))
box.add(arcade.gui.UIImage(texture=SPRITE_TEXTURES[shape], width=self.window.width / 15, height=self.window.width / 15)) box.add(arcade.gui.UIImage(texture=SPRITE_TEXTURES[shape], width=self.window.width / 15, height=self.window.width / 15))
self.sprites_box.add(arcade.gui.UITextureButton(text="Add Sprite", width=self.window.width / 2, height=self.window.height / 10, texture=button_texture, texture_hovered=button_hovered_texture, style=button_style)) add_sprite_button = self.sprites_ui.add(arcade.gui.UITextureButton(text="Add Sprite", width=self.window.width / 2, height=self.window.height / 10, texture=button_texture, texture_hovered=button_hovered_texture, style=button_style), anchor_x="center", anchor_y="bottom", align_y=10)
add_sprite_button.on_click = lambda event: self.add_sprite()
self.triggered_events.append(["start", {}]) self.triggered_events.append(["start", {}])
@@ -205,8 +242,7 @@ class Game(arcade.gui.UIView):
"change_y_velocity": self.change_y_velocity, "change_y_velocity": self.change_y_velocity,
"change_color": self.change_color, "change_color": self.change_color,
"change_size": self.change_size, "change_size": self.change_size,
"destroy": self.destroy, "destroy": self.destroy
"morph": self.morph
} }
} }
@@ -254,17 +290,23 @@ class Game(arcade.gui.UIView):
data = json.load(file) data = json.load(file)
self.triggered_events = [] self.triggered_events = []
self.rulesets = {}
if not data: if not data:
self.add_widget(arcade.gui.UIMessageBox(message_text="Invalid file. Could not import rules.", width=self.window.width * 0.5, height=self.window.height * 0.25)) self.add_widget(arcade.gui.UIMessageBox(message_text="Invalid file. Could not import rules.", width=self.window.width * 0.5, height=self.window.height * 0.25))
return return
for rule_num, ruleset in data.items(): for rule_num, ruleset in data["rulesets"].items():
kwargs = ruleset kwargs = ruleset
kwargs["children"] = [Block(**child) for child in ruleset["children"]] kwargs["children"] = [Block(**child) for child in ruleset["children"]]
kwargs["vars"] = [VarBlock(**var) for var in ruleset["vars"]] kwargs["vars"] = [VarBlock(**var) for var in ruleset["vars"]]
block = Block(**kwargs) block = Block(**kwargs)
self.rulesets[rule_num] = block self.rulesets[rule_num] = block
self.sprite_types = data.get("sprites", SPRITES)
for sprite_name, sprite_path in self.sprite_types.items():
if not sprite_name in SPRITE_TEXTURES:
SPRITE_TEXTURES[sprite_name] = arcade.load_texture(sprite_path)
self.rules_box.rulesets = self.rulesets self.rules_box.rulesets = self.rulesets
self.rules_box.current_rule_num = self.get_max_rule_num() + 1 self.rules_box.current_rule_num = self.get_max_rule_num() + 1
@@ -272,9 +314,19 @@ class Game(arcade.gui.UIView):
self.rules() self.rules()
if self.mode == "export" and self.file_manager.submitted_content: if self.mode == "export" and self.file_manager.submitted_content:
with open(self.file_manager.submitted_content, "w") as file: with open(self.file_manager.submitted_content, "w") as file:
file.write(json.dumps({rule_num: asdict(block) for rule_num, block in self.rulesets.items()}, indent=4)) file.write(json.dumps(
{
"rules": {
rule_num: asdict(block) for rule_num, block in self.rulesets.items()
},
"sprites": self.sprite_types
},
indent=4))
self.add_widget(arcade.gui.UIMessageBox(message_text="Rules and Sprites exported successfully!", width=self.window.width * 0.5, height=self.window.height * 0.25))
if not self.mode == "simulation": if not self.mode == "simulation":
return return
@@ -307,7 +359,7 @@ class Game(arcade.gui.UIView):
def on_key_press(self, symbol, modifiers): def on_key_press(self, symbol, modifiers):
if symbol == arcade.key.ESCAPE: if symbol == arcade.key.ESCAPE:
self.main_exit() self.main_exit()
elif self.mode == "simulation" and symbol in [ord(key) for key in ALLOWED_INPUT]: elif self.mode == "simulation" and symbol in [ord(key) if len(key) == 1 else getattr(arcade.key, key.upper()) for key in ALLOWED_INPUT]:
self.triggered_events.append(["on_input", {"event_key": chr(symbol)}]) self.triggered_events.append(["on_input", {"event_key": chr(symbol)}])
def on_mouse_press(self, x, y, button, modifiers): def on_mouse_press(self, x, y, button, modifiers):
@@ -339,7 +391,9 @@ class Game(arcade.gui.UIView):
elif self.mode == "rules": elif self.mode == "rules":
self.anchor.remove(self.rules_box) self.anchor.remove(self.rules_box)
elif self.mode == "sprites": elif self.mode == "sprites":
self.anchor.remove(self.sprites_box) self.anchor.remove(self.sprites_ui)
elif self.mode == "sprite_add":
self.anchor.remove(self.sprite_add_ui)
self.anchor.trigger_full_render() self.anchor.trigger_full_render()
@@ -371,7 +425,7 @@ class Game(arcade.gui.UIView):
self.mode = "sprites" self.mode = "sprites"
self.anchor.add(self.sprites_box, anchor_x="center", anchor_y="top") self.anchor.add(self.sprites_ui, anchor_x="center", anchor_y="top")
def simulation(self): def simulation(self):
self.disable_previous() self.disable_previous()

View File

@@ -124,10 +124,10 @@ class BlockRenderer:
def _build_block_with_vars(self, b: Block, x: int, y: int) -> None: def _build_block_with_vars(self, b: Block, x: int, y: int) -> None:
lx, ly = x, y - 42 lx, ly = x, y - 42
current_x = lx + 10 current_x = lx + 14
current_y = ly + 28 current_y = ly + 28
pattern = r' ([a-z]) ' pattern = r'(?:^| )([a-z])(?= |$)'
parts = re.split(pattern, b.label) parts = re.split(pattern, b.label)
var_index = 0 var_index = 0
@@ -145,19 +145,19 @@ class BlockRenderer:
self.text_objects.append(text_obj) self.text_objects.append(text_obj)
self.text_by_rule_num[b.rule_num].append(text_obj) self.text_by_rule_num[b.rule_num].append(text_obj)
current_x += len(part) * 10 current_x += len(part) * 12
else: else:
if var_index < len(b.vars): if var_index < len(b.vars):
var = b.vars[var_index] var = b.vars[var_index]
var_width, var_height = self._build_var_ui( var_width, var_height = self._build_var_ui(
var, current_x, current_y, b.rule_num var, current_x, current_y, b.rule_num
) )
current_x += var_width + 7 current_x += var_width + 10
var_index += 1 var_index += 1
def _build_block(self, b: Block, x: int, y: int) -> int: def _build_block(self, b: Block, x: int, y: int) -> int:
is_wrap = b.rule_type != "do" is_wrap = b.rule_type != "do"
h, w = 42, 280 h, w = 42, 380
if b.rule_type == "if": if b.rule_type == "if":
color = IF_COLOR color = IF_COLOR
@@ -467,7 +467,7 @@ class RuleUI(arcade.gui.UIAnchorLayout):
if block == self.dragged_rule_ui or (self.dragged_rule_ui.rule in NEEDS_SHAPE and block.rule not in PROVIDES_SHAPE): if block == self.dragged_rule_ui or (self.dragged_rule_ui.rule in NEEDS_SHAPE and block.rule not in PROVIDES_SHAPE):
continue continue
if arcade.LBWH(block.x, block.y - 44, 280, 44).intersection(arcade.LBWH(self.dragged_rule_ui.x, self.dragged_rule_ui.y - 44, 280, 44)): if arcade.LBWH(block.x, block.y - 44, 380, 44).intersection(arcade.LBWH(self.dragged_rule_ui.x, self.dragged_rule_ui.y - 44, 380, 44)):
block.children.append(self.dragged_rule_ui) block.children.append(self.dragged_rule_ui)
del self.rulesets[self.dragged_rule_ui.rule_num] del self.rulesets[self.dragged_rule_ui.rule_num]
self.block_renderer.refresh() self.block_renderer.refresh()
@@ -491,7 +491,7 @@ class RuleUI(arcade.gui.UIAnchorLayout):
continue continue
projected_vec = self.camera.unproject((event.x, event.y)) projected_vec = self.camera.unproject((event.x, event.y))
if arcade.LBWH(block.x, block.y - 44, 280, 44).point_in_rect((projected_vec.x, projected_vec.y)): if arcade.LBWH(block.x, block.y - 44, 380, 44).point_in_rect((projected_vec.x, projected_vec.y)):
if block not in list(self.rulesets.values()): # its children if block not in list(self.rulesets.values()): # its children
self.remove_from_parent(block, list(self.rulesets.values())) self.remove_from_parent(block, list(self.rulesets.values()))
self.block_renderer.refresh() self.block_renderer.refresh()
@@ -526,9 +526,22 @@ class RuleUI(arcade.gui.UIAnchorLayout):
elif isinstance(event, arcade.gui.UIMouseReleaseEvent): elif isinstance(event, arcade.gui.UIMouseReleaseEvent):
if self.dragged_rule_ui: if self.dragged_rule_ui:
block_vec = self.camera.unproject((self.dragged_rule_ui.x, self.dragged_rule_ui.y)) block_screen_pos = self.camera.project((self.dragged_rule_ui.x, self.dragged_rule_ui.y))
if self.trash_sprite.rect.intersection(arcade.LBWH(block_vec.x, block_vec.y, 280, 44)) and not self.trash_sprite._current_keyframe_index == self.trash_sprite.animation.num_frames - 1:
del self.rulesets[self.dragged_rule_ui.rule_num] block_rect = arcade.LBWH(block_screen_pos[0], block_screen_pos[1], 380, 44)
trash_rect = arcade.LBWH(
self.trash_sprite.center_x - self.trash_sprite.width / 2,
self.trash_sprite.center_y - self.trash_sprite.height / 2,
self.trash_sprite.width,
self.trash_sprite.height
)
if block_rect.intersection(trash_rect):
self.remove_from_parent(self.dragged_rule_ui, list(self.rulesets.values()))
if self.dragged_rule_ui.rule_num in self.rulesets:
del self.rulesets[self.dragged_rule_ui.rule_num]
self.dragged_rule_ui = None self.dragged_rule_ui = None
self.block_renderer.refresh() self.block_renderer.refresh()
return return
@@ -556,8 +569,8 @@ class RuleUI(arcade.gui.UIAnchorLayout):
def on_update(self, dt): def on_update(self, dt):
if self.dragged_rule_ui: if self.dragged_rule_ui:
block_vec = self.camera.unproject((self.dragged_rule_ui.x, self.dragged_rule_ui.y)) block_screen_pos = self.camera.project((self.dragged_rule_ui.x, self.dragged_rule_ui.y))
if self.trash_sprite.rect.intersection(arcade.LBWH(block_vec.x, block_vec.y, 280, 44)) and not self.trash_sprite._current_keyframe_index == self.trash_sprite.animation.num_frames - 1: if self.trash_sprite.rect.intersection(arcade.LBWH(block_screen_pos[0], block_screen_pos[1], 380, 44)) and not self.trash_sprite._current_keyframe_index == self.trash_sprite.animation.num_frames - 1:
self.trash_sprite.update_animation() self.trash_sprite.update_animation()
else: else:
self.trash_sprite.time = 0 self.trash_sprite.time = 0

View File

@@ -106,12 +106,11 @@ class Circle(pyglet.shapes.Circle, BaseShape):
return not (has_neg and has_pos) return not (has_neg and has_pos)
class Rectangle(pyglet.shapes.Rectangle, BaseShape):
def __init__(self, *args, **kwargs): class BaseRectangle(BaseShape):
super().__init__(*args, **kwargs) def __init__(self):
BaseShape.__init__(self) super().__init__()
self.shape_type = "rectangle"
@property @property
def shape_size(self): def shape_size(self):
return self.width return self.width
@@ -181,6 +180,18 @@ class Rectangle(pyglet.shapes.Rectangle, BaseShape):
return (ccw(x1, y1, x3, y3, x4, y4) != ccw(x2, y2, x3, y3, x4, y4) and return (ccw(x1, y1, x3, y3, x4, y4) != ccw(x2, y2, x3, y3, x4, y4) and
ccw(x1, y1, x2, y2, x3, y3) != ccw(x1, y1, x2, y2, x4, y4)) ccw(x1, y1, x2, y2, x3, y3) != ccw(x1, y1, x2, y2, x4, y4))
class Rectangle(pyglet.shapes.Rectangle, BaseRectangle):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
BaseRectangle.__init__(self)
self.shape_type = "rectangle"
class TexturedRectangle(pyglet.sprite.Sprite, BaseRectangle):
def __init__(self, shape_type, *args, **kwargs):
super().__init__(*args, **kwargs)
BaseRectangle.__init__(self)
self.shape_type = shape_type
class Triangle(pyglet.shapes.Triangle, BaseShape): class Triangle(pyglet.shapes.Triangle, BaseShape):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)

View File

@@ -1,12 +1,21 @@
import os
import arcade.color, operator import arcade.color, operator
from arcade.types import Color from arcade.types import Color
from arcade.gui.widgets.buttons import UITextureButtonStyle, UIFlatButtonStyle from arcade.gui.widgets.buttons import UITextureButtonStyle, UIFlatButtonStyle
from arcade.gui.widgets.slider import UISliderStyle from arcade.gui.widgets.slider import UISliderStyle
SHAPES = ["rectangle", "circle", "triangle"] # Get the directory where this module is located
_module_dir = os.path.dirname(os.path.abspath(__file__))
_assets_dir = os.path.join(os.path.dirname(_module_dir), 'assets')
SPRITES = {
os.path.splitext(file_name)[0]: os.path.join(_assets_dir, 'graphics', 'sprites', file_name)
for file_name in os.listdir(os.path.join(_assets_dir, 'graphics', 'sprites'))
}
VAR_NAMES = ["a", "b", "c", "d", "e", "f", "g"] VAR_NAMES = ["a", "b", "c", "d", "e", "f", "g"]
ALLOWED_INPUT = ["a", "b", "c", "d", "e", "q", "w", "s", "t"] ALLOWED_INPUT = ["a", "b", "c", "d", "e", "q", "w", "s", "t", "space", "left", "right", "up", "down"]
TRIGGER_COLOR = (255, 204, 102) TRIGGER_COLOR = (255, 204, 102)
DO_COLOR = (102, 178, 255) DO_COLOR = (102, 178, 255)
@@ -39,8 +48,8 @@ OPS = {
} }
VAR_DEFAULT = { VAR_DEFAULT = {
"shape_type": SHAPES[0], "shape_type": "rectangle",
"target_type": SHAPES[1], "target_type": "circle",
"variable": 0, "variable": 0,
"color": "WHITE", "color": "WHITE",
"size": 10, "size": 10,
@@ -49,8 +58,8 @@ VAR_DEFAULT = {
} }
VAR_OPTIONS = { VAR_OPTIONS = {
"shape_type": SHAPES, "shape_type": SPRITES,
"target_type": SHAPES, "target_type": SPRITES,
"variable": (-700, 700), "variable": (-700, 700),
"color": COLORS, "color": COLORS,
"size": (1, 200), "size": (1, 200),
@@ -118,13 +127,6 @@ TRIGGER_RULES = {
"vars": ["shape_type", "event_shape_type"], "vars": ["shape_type", "event_shape_type"],
"func": lambda *v: v[0] == v[1] "func": lambda *v: v[0] == v[1]
}, },
"morphs": {
"key": "morphs",
"description": "IF {a} shape morphs into {b}",
"user_vars": ["shape_type", "target_type"],
"vars": ["shape_type", "target_type", "event_a_type", "event_b_type"],
"func": lambda *v: (v[0] == v[2]) and (v[3] == v[1])
},
"collides": { "collides": {
"key": "collides", "key": "collides",
"description": "IF {a} shape collides with {b}", "description": "IF {a} shape collides with {b}",
@@ -289,14 +291,6 @@ DO_RULES = {
"vars": ["shape"] "vars": ["shape"]
}, },
"morph_into": {
"key": "morph_into",
"description": "Morph this into {a}",
"action": {"type": "shape_action", "name": "morph"},
"user_vars": ["shape_type"],
"vars": ["shape", "shape_type"]
},
"change_x_gravity": { "change_x_gravity": {
"key": "change_x_gravity", "key": "change_x_gravity",
"description": "Change X gravity to {a}", "description": "Change X gravity to {a}",
@@ -327,7 +321,6 @@ PROVIDES_SHAPE = [
"spawns", "spawns",
"color_changes", "color_changes",
"size_changes", "size_changes",
"morphs",
"collides", "collides",
# IFs, technically, these need and provide a shape to the next rule # IFs, technically, these need and provide a shape to the next rule
@@ -362,7 +355,6 @@ NEEDS_SHAPE = [
"change_y_velocity", "change_y_velocity",
"change_size", "change_size",
"destroy", "destroy",
"morph_into"
] ]
RULE_DEFAULTS = { RULE_DEFAULTS = {