made several changes working on zoom
added proper particle count there is a settings menu some keybindings have been added ESC to exit Glowing particles are now a thing Gas effects can be toggled on and off for performance
This commit is contained in:
parent
05e37a0bd5
commit
b9a280208b
3
.gitignore
vendored
3
.gitignore
vendored
@ -163,3 +163,6 @@ cython_debug/
|
|||||||
current_stations.html
|
current_stations.html
|
||||||
forecast_data.json
|
forecast_data.json
|
||||||
openapi.json
|
openapi.json
|
||||||
|
__pycache__/sim.cpython-312.pyc
|
||||||
|
__pycache__/sim.cpython-312.pyc
|
||||||
|
__pycache__/rendering.cpython-312.pyc
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
111
rendering.py
111
rendering.py
@ -1,6 +1,7 @@
|
|||||||
#File Name: rendering.py
|
#File Name: rendering.py
|
||||||
|
|
||||||
from settings import pygame, random, particle_properties
|
from settings import pygame, random, particle_properties, engine_settings
|
||||||
|
|
||||||
|
|
||||||
class Rendering:
|
class Rendering:
|
||||||
|
|
||||||
@ -69,31 +70,68 @@ class Rendering:
|
|||||||
|
|
||||||
base_color = particle_colors.get(particle.particle_type, (255, 255, 255))
|
base_color = particle_colors.get(particle.particle_type, (255, 255, 255))
|
||||||
color = list(base_color)
|
color = list(base_color)
|
||||||
|
|
||||||
if particle.is_gas:
|
|
||||||
# Enhanced gas visibility
|
|
||||||
alpha = random.randint(128, 200)
|
|
||||||
color = list(color)
|
|
||||||
if len(color) < 4:
|
|
||||||
color.append(alpha)
|
|
||||||
else:
|
|
||||||
color[3] = alpha
|
|
||||||
|
|
||||||
# Add subtle movement effect
|
|
||||||
offset_x = random.randint(-1, 1)
|
|
||||||
offset_y = random.randint(-1, 1)
|
|
||||||
rect = (x * particle_size + offset_x, y * particle_size + offset_y,
|
|
||||||
particle_size, particle_size)
|
|
||||||
else:
|
|
||||||
rect = (x * particle_size, y * particle_size,
|
|
||||||
particle_size, particle_size)
|
|
||||||
|
|
||||||
|
if engine_settings['enable_glow']:
|
||||||
|
glow_color = (255, 255, 255)
|
||||||
|
glow_radius = 0.5 * particle_size
|
||||||
|
glow_surface = pygame.Surface((glow_radius * 2, glow_radius * 2), pygame.SRCALPHA)
|
||||||
|
pygame.draw.circle(glow_surface, glow_color, (glow_radius, glow_radius), glow_radius)
|
||||||
|
glow_surface.set_alpha(100)
|
||||||
|
self.particle_surface.blit(glow_surface, (x * particle_size - glow_radius, y * particle_size - glow_radius))
|
||||||
|
|
||||||
|
if engine_settings['enable_gas_effect']:
|
||||||
|
if particle.is_gas:
|
||||||
|
# Enhanced gas visibility
|
||||||
|
alpha = random.randint(128, 200)
|
||||||
|
color = list(color)
|
||||||
|
if len(color) < 4:
|
||||||
|
color.append(alpha)
|
||||||
|
else:
|
||||||
|
color[3] = alpha
|
||||||
|
|
||||||
|
# Add subtle movement effect
|
||||||
|
offset_x = random.randint(-1, 1)
|
||||||
|
offset_y = random.randint(-1, 1)
|
||||||
|
rect = (x * particle_size + offset_x, y * particle_size + offset_y,
|
||||||
|
particle_size, particle_size)
|
||||||
|
|
||||||
|
rect = (x * particle_size, y * particle_size,
|
||||||
|
particle_size, particle_size)
|
||||||
|
|
||||||
pygame.draw.rect(self.particle_surface, color, rect)
|
pygame.draw.rect(self.particle_surface, color, rect)
|
||||||
|
|
||||||
self.screen.blit(self.background, (0, 0))
|
self.screen.blit(self.background, (0, 0))
|
||||||
self.screen.blit(self.particle_surface, (0, 0))
|
self.screen.blit(self.particle_surface, (0, 0))
|
||||||
|
|
||||||
|
|
||||||
|
def draw_zoom_window(self, particles, particle_size, particle_colors, mouse_pos, zoom_factor=4):
|
||||||
|
print(f"Drawing zoom window.")
|
||||||
|
zoom_size = 100 # Size of zoom window
|
||||||
|
zoom_surface = pygame.Surface((zoom_size, zoom_size))
|
||||||
|
zoom_surface.fill((0, 0, 0))
|
||||||
|
|
||||||
|
# Get area around mouse to zoom
|
||||||
|
mouse_x, mouse_y = mouse_pos
|
||||||
|
view_x = mouse_x - zoom_size/(2*zoom_factor)
|
||||||
|
view_y = mouse_y - zoom_size/(2*zoom_factor)
|
||||||
|
print(f"Viewing area: {view_x}, {view_y}")
|
||||||
|
|
||||||
|
# Draw zoomed particles
|
||||||
|
for x in range(int(view_x), int(view_x + zoom_size/zoom_factor)):
|
||||||
|
for y in range(int(view_y), int(view_y + zoom_size/zoom_factor)):
|
||||||
|
if 0 <= x < len(particles) and 0 <= y < len(particles[0]):
|
||||||
|
particle = particles[x][y]
|
||||||
|
if particle:
|
||||||
|
color = particle_colors.get(particle.particle_type, (255, 255, 255))
|
||||||
|
rect = ((x - view_x) * zoom_factor,
|
||||||
|
(y - view_y) * zoom_factor,
|
||||||
|
particle_size * zoom_factor,
|
||||||
|
particle_size * zoom_factor)
|
||||||
|
pygame.draw.rect(zoom_surface, color, rect)
|
||||||
|
print(f"Drawing zoom window at {mouse_pos}{zoom_surface}")
|
||||||
|
return zoom_surface
|
||||||
|
|
||||||
|
|
||||||
def draw_debug_overlay(self, fps, particles): # this is the function that draws the debug overlay
|
def draw_debug_overlay(self, fps, particles): # this is the function that draws the debug overlay
|
||||||
# Get mouse position and convert to grid coordinates
|
# Get mouse position and convert to grid coordinates
|
||||||
mouse_x, mouse_y = pygame.mouse.get_pos()
|
mouse_x, mouse_y = pygame.mouse.get_pos()
|
||||||
@ -124,13 +162,16 @@ class Rendering:
|
|||||||
|
|
||||||
# Draw debug information
|
# Draw debug information
|
||||||
font = pygame.font.SysFont(None, 24)
|
font = pygame.font.SysFont(None, 24)
|
||||||
|
particle_count = sum(1 for row in particles for cell in row if cell is not None)
|
||||||
|
|
||||||
debug_info = [
|
debug_info = [
|
||||||
f"FPS: {int(fps)}",
|
f"FPS: {int(fps)}",
|
||||||
f"Mouse: ({mouse_x}, {mouse_y})",
|
f"Mouse: ({mouse_x}, {mouse_y})",
|
||||||
f"Grid: ({grid_x}, {grid_y})",
|
f"Grid: ({grid_x}, {grid_y})",
|
||||||
f"Particle: {particle_info}"
|
f"Particle: {particle_info}",
|
||||||
|
#f"Active Particle Count: {sim.Simulation.get_active_particle_count(sim.Simulation)}",
|
||||||
|
f"Particle Count: {particle_count}"
|
||||||
]
|
]
|
||||||
|
|
||||||
y_offset = 10
|
y_offset = 10
|
||||||
for info in debug_info:
|
for info in debug_info:
|
||||||
debug_text = font.render(info, True, (255, 255, 255))
|
debug_text = font.render(info, True, (255, 255, 255))
|
||||||
@ -175,6 +216,13 @@ class Rendering:
|
|||||||
label = font.render("Clear", True, (255, 255, 255))
|
label = font.render("Clear", True, (255, 255, 255))
|
||||||
self.screen.blit(label, (self.clear_screen_button.x + 5, self.clear_screen_button.y + 5))
|
self.screen.blit(label, (self.clear_screen_button.x + 5, self.clear_screen_button.y + 5))
|
||||||
|
|
||||||
|
# Draw Settings menu directly below the buttons
|
||||||
|
self.settings_button = pygame.Rect(x_offset, y_offset + 50, 80, 25)
|
||||||
|
pygame.draw.rect(self.screen, (255, 255, 255), self.settings_button)
|
||||||
|
font = pygame.font.SysFont(None, 24)
|
||||||
|
label = font.render("Settings", True, (0, 0, 0))
|
||||||
|
self.screen.blit(label, (self.settings_button.x + 5, self.settings_button.y + 5))
|
||||||
|
|
||||||
|
|
||||||
def render_brush_curser(self, x, y, radius): # this is the function that draws the brush curser but isn't used yet so unkown if works
|
def render_brush_curser(self, x, y, radius): # this is the function that draws the brush curser but isn't used yet so unkown if works
|
||||||
# Draw a circle cursor for brushsize
|
# Draw a circle cursor for brushsize
|
||||||
@ -189,8 +237,27 @@ class Rendering:
|
|||||||
label = pygame.font.SysFont(None, 24).render(f"Brush Size: {brush_size}", True, (255, 255, 255))
|
label = pygame.font.SysFont(None, 24).render(f"Brush Size: {brush_size}", True, (255, 255, 255))
|
||||||
self.screen.blit(label, (500, 10))
|
self.screen.blit(label, (500, 10))
|
||||||
|
|
||||||
|
def draw_settings_menu(self):
|
||||||
|
settings_surface = pygame.Surface((300, 400))
|
||||||
|
settings_surface.fill((50, 50, 50))
|
||||||
|
|
||||||
|
y_offset = 10
|
||||||
|
font = pygame.font.SysFont(None, 24)
|
||||||
|
|
||||||
|
for setting, value in engine_settings.items():
|
||||||
|
# Create toggle button
|
||||||
|
button_rect = pygame.Rect(10, y_offset, 20, 20)
|
||||||
|
pygame.draw.rect(settings_surface, (0, 255, 0) if value else (255, 0, 0), button_rect)
|
||||||
|
|
||||||
|
# Draw setting name
|
||||||
|
label = font.render(setting.replace('_', ' ').title(), True, (255, 255, 255))
|
||||||
|
settings_surface.blit(label, (40, y_offset))
|
||||||
|
|
||||||
|
y_offset += 30
|
||||||
|
|
||||||
|
return settings_surface
|
||||||
|
|
||||||
def clear_screen(self, sim): ## this is the function that clears the screen
|
def clear_screen(self, sim): # this is the function that clears the screen
|
||||||
# Store current particle type
|
# Store current particle type
|
||||||
current_type = sim.current_particle_type
|
current_type = sim.current_particle_type
|
||||||
|
|
||||||
|
|||||||
62
sandpypi.py
62
sandpypi.py
@ -5,27 +5,36 @@
|
|||||||
# This is my most functional system for falling sand in python yet i took some things i learned in JS.
|
# This is my most functional system for falling sand in python yet i took some things i learned in JS.
|
||||||
# This needs further optimizations to core performance sections.
|
# This needs further optimizations to core performance sections.
|
||||||
|
|
||||||
from settings import pygame
|
from settings import pygame, engine_settings
|
||||||
from rendering import Rendering
|
from rendering import Rendering
|
||||||
from sim import Simulation
|
from sim import Simulation
|
||||||
|
|
||||||
def main(): # Main function to run the program
|
def main(): # Main function to run the program
|
||||||
pygame.init()
|
pygame.init()
|
||||||
clock = pygame.time.Clock()
|
clock = pygame.time.Clock()
|
||||||
pygame.display.set_mode((1024, 768), pygame.HWSURFACE | pygame.DOUBLEBUF)
|
width = 1024
|
||||||
sim = Simulation(1024, 768)
|
height = 768
|
||||||
rendering = Rendering(1024, 768)
|
screen = pygame.display.set_mode((width, height), pygame.HWSURFACE | pygame.DOUBLEBUF)
|
||||||
|
sim = Simulation(width, height)
|
||||||
|
rendering = Rendering(width, height)
|
||||||
mouse_down_left = False
|
mouse_down_left = False
|
||||||
mouse_down_right = False
|
mouse_down_right = False
|
||||||
mouse_down_middle = False
|
mouse_down_middle = False
|
||||||
mouse_down_wheel_up = False
|
mouse_down_wheel_up = False
|
||||||
mouse_down_wheel_down = False
|
mouse_down_wheel_down = False
|
||||||
over_button = False
|
over_button = False
|
||||||
|
zoom_active = False
|
||||||
|
zoom_locked = False
|
||||||
|
zoom_pos = None
|
||||||
|
settings_visible = False
|
||||||
|
settings_menu_y = 100
|
||||||
running = True
|
running = True
|
||||||
|
|
||||||
while running:
|
while running:
|
||||||
fps = clock.get_fps()
|
fps = clock.get_fps()
|
||||||
dt = clock.tick(60) / 1000
|
dt = clock.tick(60) / 1000
|
||||||
|
keys = pygame.key.get_pressed()
|
||||||
|
zoom_active = keys[pygame.K_z]
|
||||||
|
|
||||||
|
|
||||||
# Handle events
|
# Handle events
|
||||||
@ -38,6 +47,11 @@ def main(): # Main function to run the program
|
|||||||
sim.brush_size = max(sim.brush_size - 1, 1)
|
sim.brush_size = max(sim.brush_size - 1, 1)
|
||||||
elif event.button == 1: # Left click
|
elif event.button == 1: # Left click
|
||||||
over_button = False
|
over_button = False
|
||||||
|
|
||||||
|
if zoom_active:
|
||||||
|
zoom_locked = not zoom_locked
|
||||||
|
if zoom_locked:
|
||||||
|
zoom_pos = mouse_pos
|
||||||
|
|
||||||
# Check category buttons
|
# Check category buttons
|
||||||
for category, button in rendering.category_buttons.items():
|
for category, button in rendering.category_buttons.items():
|
||||||
@ -59,6 +73,18 @@ def main(): # Main function to run the program
|
|||||||
rendering.clear_screen(sim)
|
rendering.clear_screen(sim)
|
||||||
over_button = True
|
over_button = True
|
||||||
|
|
||||||
|
if rendering.settings_button.collidepoint(event.pos):
|
||||||
|
settings_visible = not settings_visible
|
||||||
|
elif settings_visible:
|
||||||
|
# Handle settings toggles
|
||||||
|
mouse_pos = pygame.mouse.get_pos()
|
||||||
|
relative_y = mouse_pos[1] - settings_menu_y
|
||||||
|
setting_index = relative_y // 30
|
||||||
|
if 0 <= setting_index < len(engine_settings):
|
||||||
|
setting_name = list(engine_settings.keys())[setting_index]
|
||||||
|
engine_settings[setting_name] = not engine_settings[setting_name]
|
||||||
|
|
||||||
|
|
||||||
if not over_button:
|
if not over_button:
|
||||||
mouse_down_left = True
|
mouse_down_left = True
|
||||||
|
|
||||||
@ -74,6 +100,23 @@ def main(): # Main function to run the program
|
|||||||
mouse_down_right = False
|
mouse_down_right = False
|
||||||
elif event.type == pygame.MOUSEBUTTONUP and event.button == 2:
|
elif event.type == pygame.MOUSEBUTTONUP and event.button == 2:
|
||||||
mouse_down_middle = False
|
mouse_down_middle = False
|
||||||
|
elif event.type == pygame.KEYDOWN:
|
||||||
|
if event.key == pygame.K_ESCAPE:
|
||||||
|
print("Escape button pressed")
|
||||||
|
print(f"Exiting Program {__file__}")
|
||||||
|
running = False
|
||||||
|
elif event.key == pygame.K_SPACE:
|
||||||
|
print(f"Pause button pressed but not functional 'Space'")
|
||||||
|
pass
|
||||||
|
elif event.key == pygame.K_c:
|
||||||
|
rendering.clear_screen(sim)
|
||||||
|
elif event.key == pygame.K_z:
|
||||||
|
zoom_active = not zoom_active
|
||||||
|
if zoom_active:
|
||||||
|
zoom_locked = False
|
||||||
|
zoom_pos = pygame.mouse.get_pos()
|
||||||
|
|
||||||
|
|
||||||
elif event.type == pygame.QUIT:
|
elif event.type == pygame.QUIT:
|
||||||
running = False
|
running = False
|
||||||
|
|
||||||
@ -94,6 +137,17 @@ def main(): # Main function to run the program
|
|||||||
rendering.draw_particles(sim.particles, sim.active_particles, sim.particle_size, rendering.particle_colors)
|
rendering.draw_particles(sim.particles, sim.active_particles, sim.particle_size, rendering.particle_colors)
|
||||||
rendering.draw_buttons()
|
rendering.draw_buttons()
|
||||||
rendering.draw_brush_size_slider(sim.brush_size)
|
rendering.draw_brush_size_slider(sim.brush_size)
|
||||||
|
if zoom_active or zoom_locked:
|
||||||
|
mouse_pos = zoom_pos if zoom_locked else pygame.mouse.get_pos()
|
||||||
|
zoom_surface = rendering.draw_zoom_window(sim.particles, sim.particle_size, rendering.particle_colors, mouse_pos)
|
||||||
|
# Position zoom window
|
||||||
|
zoom_x = 10 if mouse_pos[0] > width/2 else width - 110
|
||||||
|
zoom_y = 10 if mouse_pos[1] > height/2 else height - 110
|
||||||
|
screen.blit(zoom_surface, (zoom_x, zoom_y))
|
||||||
|
|
||||||
|
if settings_visible:
|
||||||
|
settings_menu = rendering.draw_settings_menu()
|
||||||
|
rendering.screen.blit(settings_menu, (rendering.width - 320, 100))
|
||||||
rendering.draw_debug_overlay(fps, sim.particles)
|
rendering.draw_debug_overlay(fps, sim.particles)
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,13 @@ import json
|
|||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
engine_settings = {
|
||||||
|
'enable_glow': True,
|
||||||
|
'enable_gas_effect': True
|
||||||
|
# 'settings': True/False
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load particle properties from JSON file
|
||||||
def load_particle_properties():
|
def load_particle_properties():
|
||||||
try:
|
try:
|
||||||
with open('particles.json') as f:
|
with open('particles.json') as f:
|
||||||
@ -16,3 +23,4 @@ def load_particle_properties():
|
|||||||
|
|
||||||
# Load particle properties once when module is imported
|
# Load particle properties once when module is imported
|
||||||
particle_properties = load_particle_properties()
|
particle_properties = load_particle_properties()
|
||||||
|
|
||||||
|
|||||||
27
sim.py
27
sim.py
@ -100,10 +100,14 @@ class Simulation:
|
|||||||
|
|
||||||
def update_spatial_grid(self): # this is where we update the spatial grid.
|
def update_spatial_grid(self): # this is where we update the spatial grid.
|
||||||
"""Update spatial grid for optimized collision detection"""
|
"""Update spatial grid for optimized collision detection"""
|
||||||
self.spatial_grid.clear()
|
if len(self.active_particles) > 100: # Threshold for rebuild
|
||||||
for x, y in self.active_particles:
|
self.spatial_grid.clear()
|
||||||
self.add_to_spatial_grid(self.particles[x][y], x, y)
|
for x, y in self.active_particles:
|
||||||
|
cell_key = self.get_cell_key(x, y)
|
||||||
|
if cell_key not in self.spatial_grid:
|
||||||
|
self.spatial_grid[cell_key] = set()
|
||||||
|
self.spatial_grid[cell_key].add((x, y))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def handle_phase_transitions(self, particle, x, y): # this is where we handle all the phase transitions.
|
def handle_phase_transitions(self, particle, x, y): # this is where we handle all the phase transitions.
|
||||||
@ -593,6 +597,21 @@ class Simulation:
|
|||||||
liquid2.color = self.calculate_color(liquid2.temperature)
|
liquid2.color = self.calculate_color(liquid2.temperature)
|
||||||
|
|
||||||
|
|
||||||
|
def get_particle_count(self):
|
||||||
|
"""Returns the number of particles in the simulation for the Debug display and for performance analysis."""
|
||||||
|
count = 0
|
||||||
|
for x in range(self.width):
|
||||||
|
for y in range(self.height):
|
||||||
|
if self.particles[x][y] is not None:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
def get_active_particle_count(self):
|
||||||
|
"""Returns the number of active particles in the simulation for the Debug display and for performance analysis."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def simulate_step(self, dt):
|
def simulate_step(self, dt):
|
||||||
"""Run a single step of the simulation"""
|
"""Run a single step of the simulation"""
|
||||||
# Update particle positions and physics
|
# Update particle positions and physics
|
||||||
|
|||||||
80
template_particles.json
Normal file
80
template_particles.json
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"Template": {
|
||||||
|
"description": "This is a template for particles.",
|
||||||
|
"description2": "Remove this for your own particle mods to work"
|
||||||
|
},
|
||||||
|
"sand": {
|
||||||
|
"name": "Sand",
|
||||||
|
"size": 1,
|
||||||
|
"hardness": 0.5,
|
||||||
|
"color": [255, 255, 0, 255],
|
||||||
|
"velocity": 0.5,
|
||||||
|
"mass": 0.5,
|
||||||
|
"conductivity": 0,
|
||||||
|
"heat_capacity": 1,
|
||||||
|
"flamability": 0.8,
|
||||||
|
"temperature": 0,
|
||||||
|
"explosive": false,
|
||||||
|
"explosion_radius": 0,
|
||||||
|
"explosion_color": [0, 0, 0],
|
||||||
|
"friction": 0.5,
|
||||||
|
"viscosity": 0,
|
||||||
|
"pressure": 0,
|
||||||
|
"melt": "molten-Glass",
|
||||||
|
"melt_temperature": 1000,
|
||||||
|
"conductive": false,
|
||||||
|
"liquid": false,
|
||||||
|
"solid": true,
|
||||||
|
"is_gas": false
|
||||||
|
},
|
||||||
|
"water": {
|
||||||
|
"name": "Water",
|
||||||
|
"size": 1,
|
||||||
|
"hardness": 0.2,
|
||||||
|
"velocity": 0.3,
|
||||||
|
"conductivity": 1,
|
||||||
|
"heat_capacity": 1,
|
||||||
|
"color": [0, 0, 255, 255],
|
||||||
|
"mass": 1,
|
||||||
|
"flamability": 0,
|
||||||
|
"temperature": 22,
|
||||||
|
"explosive": false,
|
||||||
|
"explosion_radius": 0,
|
||||||
|
"explosion_color": [0, 0, 0],
|
||||||
|
"friction": 1,
|
||||||
|
"viscosity": 1,
|
||||||
|
"pressure": 0.5,
|
||||||
|
"evaporate": "steam",
|
||||||
|
"evaporate_temperature": 145,
|
||||||
|
"freeze": "ice",
|
||||||
|
"freeze_temperature": 0,
|
||||||
|
"melt": "water",
|
||||||
|
"melt_temperature": 20,
|
||||||
|
"liquid": true,
|
||||||
|
"solid": false,
|
||||||
|
"is_gas": false
|
||||||
|
},
|
||||||
|
"steam": {
|
||||||
|
"name": "Steam",
|
||||||
|
"size": 1,
|
||||||
|
"hardness": 0.0,
|
||||||
|
"velocity": 0.2,
|
||||||
|
"conductivity": 1,
|
||||||
|
"heat_capacity": 1,
|
||||||
|
"color": [255, 255, 255, 255],
|
||||||
|
"mass": 0.01,
|
||||||
|
"flamability": 0,
|
||||||
|
"temperature": 100,
|
||||||
|
"solidify_temperature": 98,
|
||||||
|
"solidify": "water",
|
||||||
|
"explosive": false,
|
||||||
|
"explosion_radius": 0,
|
||||||
|
"explosion_color": [0, 0, 0],
|
||||||
|
"friction": 0.5,
|
||||||
|
"viscosity": 0.5,
|
||||||
|
"liquid": false,
|
||||||
|
"solid": false,
|
||||||
|
"is_gas": true,
|
||||||
|
"conductive": false
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user